Izvoženi PDF postavi vsako mejo stolpca za pol znaka levo od tistega, kjer jo nariše Excel, vsaka ovita celica pa se zdaj prelomi na drugem mestu. Širina stolpca v Excelu se ne meri v znakih ali točkah. Meri se v enotah Max Digit Width (MDW) pisave Normal delovnega zvezka, HotXLS pa to pisavo izmeri z GDI, preden vsakič zgradi paginacijo. Način odpovedi je tih: nič ne sproži izjeme, shranjene širine preživijo pot naprej in nazaj bajt za bajtom, geometrija pa je vseeno odmaknjena za nekaj odstotkov na stolpec, dokler nakopičen zdrs enostrani tabele ne potisne na dve strani
V kakšni enoti je širina stolpca v Excelu?
Širina stolpca v delovnem listu je štetje znakov števk pisave Normal delovnega zvezka, ne absolutna meritev. ECMA-376 §18.3.1.13 definira atribut width elementa <col> prek Maximum Digit Width te pisave pri 96 dpi in poda pretvorbo iz shranjene širine nazaj v slikovne pike kot odrezni izraz nad MDW. Za Calibri 11, ki ga Excel dobavlja kot stil Normal, MDW meri 7 slikovnih pik. Privzeto širino 8.43 enot pognite skozi formulo specifikacije z MDW 7 in dobite točno 64 slikovnih pik, kar je 48 točk pri 96 dpi. To so številke, ki jih poroča sam Excel, zato so koristen preizkus: če vaša pretvorba ponovi 8.43 enot v 64 slikovnih pik, je aritmetika prava in narobe je lahko le še vhod MDW
const
// Max digit width (MDW) privzete pisave telesa, v slikovnih pikah pri 96 dpi.
// Calibri 11 meri 7 px, kar ponovi točne širine v slikovnih pikah,
// ki jih shrani Excel (8.43 enot -> 64 px -> 48 pt).
DefaultMDW = 7;
MinimumColumnWidth = 24.0;
function ColumnWidthToPointsMdW(Value: Double; MdW: Integer): Double;
var
Pixels: Integer;
begin
if Value <= 0 then
Value := 8.43;
if MdW <= 0 then
MdW := DefaultMDW;
Pixels := Trunc(((256 * Value + Trunc(128 / MdW)) / 256) * MdW) + 5;
Result := Pixels * 0.75; // 96 dpi slikovne pike -> točke
if Result < MinimumColumnWidth then
Result := MinimumColumnWidth;
end;
HotXLS hrani to aritmetiko v natančno eni funkciji, v enoti lxPagination, zato obstaja en sam prostor, kjer se lahko ravnilo pokaže napačno. + 5 je oblazinjenje, ki ga Excel doda za črte mreže in robove celic, * 0.75 pretvori 96 dpi slikovne pike v točke PostScript, spodnja meja pri MinimumColumnWidth pa obstaja zato, da patološko ozek stolpec še vedno pusti pas, v katerega izrisovalnik lahko nariše obrobo. Javna vstopna točka ColumnWidthToPoints ohrani star podpis z enim argumentom in tej funkciji posreduje izmerjen MDW, kar je omogočilo, da je sprememba vedenja pristala, ne da bi se dotaknila enega samega mesta klica
Zakaj pisava Normal, ki ni Calibri, premakne vsako mejo
Zdrs je multiplikativen, zato se bere kot hrošč izrisovanja in ne kot hrošč enot. MDW je faktor na širini, ne odmik. Pomaknite MDW s 7 na 8 in privzeti stolpec 8.43 enot gre s 64 slikovnih pik na 72, skok 8 slikovnih pik ali 6 točk na enem stolpcu. Deset takih stolpcev in desni rob tabele se je premaknil za skoraj palec. Delovni zvezki, ki to sprožijo, so povsem običajni: vse, kar je ustvarilo orodje za poročila, ki v stil Normal odtisne Arial ali Segoe UI, vse, kar je shranjeno iz predloge za izvoz ERP, vse, kar je stranka enkrat preoblikovala in pozabila
Dva sorodna postavitvena sistema napako podedujeta in je ne povzročata. Združena območja seštejejo točkovne širine svojih članskih stolpcev, zato se lahko združitev, ki je v Excelu stala na eni strani, po zdrsu MDW prelije — tega je vredno pomniti, ko gradite predloge poročil z združenimi celicami. Shrink-to-fit primerja izmerjeno širino besedila z isto širino stolpca, zato napačni MDW spremeni tudi to, katere celice se skrčijo in za koliko. Isti družini zmede enot se približa pri sidrih risb, kjer ima geometrija slik in merilo EMU svojo verigo pretvorb, v kateri se gre zmotiti
Kako HotXLS meri MDW med tekom
HotXLS razreši MDW iz delovnega zvezka samega, namesto da bi domneval konstanto, delo pa opravita dve proceduri. PaginationApplyNormalFont prebere pisavo stila Normal z delovnega zvezka in teče na vrhu gradnje paginacije, preden se izračuna katera koli geometrija stolpcev; najprej se ponastavi na Calibri 11, zato delovni zvezek brez tabele pisav ne more podedovati zastarelega stanja prejšnje gradnje. Pisava stila Normal je fonts[0] v styles.xml, komponenta pa jo izpostavi kot Workbook.Fonts[0]
// Prebere fonts[0] (pisavo stila Normal) z delovnega zvezka delovnega lista.
// Klasični delovni listi brez tabele pisav obdržijo privzeti Calibri 11.
procedure PaginationApplyNormalFont(Worksheet: TObject);
var
Sh: TXLSXWorksheet;
Fnt: TXLSXFont;
begin
PaginationNormalFontName := 'Calibri';
PaginationNormalFontSize := 11;
if not (Worksheet is TXLSXWorksheet) then
Exit;
Sh := TXLSXWorksheet(Worksheet);
if (Sh.Workbook = nil) or (Sh.Workbook.Fonts.Count < 1) then
Exit;
Fnt := Sh.Workbook.Fonts[0];
if Fnt.Name <> '' then
PaginationNormalFontName := Fnt.Name;
if Fnt.Size > 0 then
PaginationNormalFontSize := Fnt.Size;
end;
Druga procedura, PaginationMeasureMdW, vpraša GDI po obsegu enega samega znaka '0' prek GetTextExtentPoint32W na skupnem platnu bitne slike izven zaslona, ob neuspehu tega klica se vrne k tmAveCharWidth iz GetTextMetricsW, kadar pa ni na voljo nič od tega, k DefaultMDW. Njen predpomnilnik je en sam slot s ključem (name, size), kar zveni grobo, dokler ne pogledate vzorca dostopov: gradnja paginacije sprašuje po isti pisavi Normal na vsakem stolpcu vsake strani, zato ima en slot skoraj popolno stopnjo zadetkov in stane tri primerjave na klic
Kaj se zgodi brez tabele pisav, brez GUI ali z manjkajočo pisavo?
HotXLS propade do konstante Calibri 11 v vsakem primeru, ko prave pisave Normal ni mogoče določiti, in to po zasnovi tiho. Klasični delovni listi BIFF so pogosti primer: zapuščinski formati ne nosijo zbirk pisav XLSX, na katero bi se skliceval fonts[0], zato tipski varoval zgodaj izstopi in privzeti MDW 7 ostane veljaven. To ni popravek, to je prejšnje vedenje, namenoma ohranjeno, tako da dodajanje meritve na pot XLSX ni moglo regresirati izhoda klasičnega formata
Odvisnost od GDI je iskren pripombnik. Meritev teče proti kontekstu naprave Windows, zato pot domneva gostitelja Windows z nameščeno pisavo. V storitvi ali brezglavem gradbenem agentu se metrike besedil GDI splošno še vedno razrešijo, pisava, ki ni nameščena na tistem stroju, pa jo zamenja mapirnik pisav in vi merite zamenjavo. Nikoli ne odpove glasno; vrne verodostojno številko za napačno pisavo. Če morajo izvozi na strani strežnika ustrezati namizni referenci, namestite na izvoznega gostitelja pisave, ki jih poimenujejo vaše predloge, ali pa pripnite pisavo Normal, preden pokličete pot izvoza delovnega lista v PDF
var
Book: TXLSXWorkbook;
Exporter: TXLSPDFExport;
begin
Book := TXLSXWorkbook.Create;
Exporter := TXLSPDFExport.Create;
try
Book.Open('quarterly-report.xlsx');
// Pripni pisavo Normal, da je MDW, izmerjeno na tem gostitelju, tisto,
// proti kateremu je postavitev zasnovana, in ne zamenjava iz mapirnika pisav.
if Book.Fonts.Count > 0 then
begin
Book.Fonts[0].Name := 'Calibri';
Book.Fonts[0].Size := 11;
end;
Exporter.UseWorksheetPageSetup := True;
Exporter.SaveAsPDF(Book, 'quarterly-report.pdf');
finally
Exporter.Free;
Book.Free;
end;
end;
Predpomnilniki meritev in tisti, ki je sesul Win64
Ko je merjenje besedila GDI-potovanje naprej in nazaj in ne množenje, ga je treba predpomniti, predpomnjenje znotraj prehoda izrisovanja pa je mesto, kjer je to delo krvavelo. Zanka shrink-to-fit stopnjuje velikost pisave navzdol v korakih 0.5 pt in po vsakem koraku znova meri, zato lahko ena celica PaginationMeasureTextWidth pokliče ducatkrat z istim nizom, prelom besed pa ga pokliče še enkrat na kandidatno vrstico. Zapomnilnik s ključem ime pisave, velikost in besedilo to strne v en GDI-klic na različen niz, shranjen v TStringList kot pari ime/vrednost
Drugi predpomnilnik, dodan ob njem, pa ni bil tako urejen. Prehod izrisovanja 5 razreši zbirko pisav na celico po FontIndex, njegov zapomnilnik pa je uporabljal vzporedna dinamična polja z ročno vzdrževanim FontMemoCount. Prva različica je pozabila poklicati ResetFontMemo na začetku vsake strani, zato je števec čez strani stalno rastel, polja pa ne, koda pa je zapisala čez konec vseh. Na Win32 je to tiho črtalo po sosednji kopici in končalo; na Win64 pa je takoj sprožilo kršitev dostopa ob zapisu na 0x538. Generalizirana lekcija: predpomnilnik, ki ga nosi polje in živi v spremenljivki na ravni enote, je treba ponastaviti na vstopu vsakega prehoda, ki ga uporablja, ker seznam nizov ali slovar manjkajočo ponastavitev odpusti z rastjo, vzporedna polja pa ne
Preverjanje lastne pretvorbe
Te komponente ne potrebujete, da preverite katero koli od teh stvari. Vzemite delovni zvezek, čigar pisava Normal ni Calibri 11, preberite širino iz <col width="..."/> in jo dvakrat zaženite skozi formulo specifikacije, enkrat z MDW 7 in enkrat z MDW, ki ga vaš izrisovalnik za to pisavo dejansko izmeri; če se odgovora razlikujeta in vaš izhod ustreza prvemu, ste zdrs našli. Geometrija stolpcev je ena tistih delov pogona preglednic, ki je bodisi nevidna bodisi edina stvar, ki jo kdorkoli opazi, pravilna pa pomeni, da pisavo Normal obravnavate kot vhod v postavitev in ne kot podrobnost oblikovanja. Če gradite aplikacije Delphi ali C++Builder, ki berejo, pišejo, izrisujejo in tiskajo delovne zvezke Excel brez nameščenega Officea, komponenta Excel HotXLS za Delphi opravi merjenje MDW, model paginacije in cevovod PDF za enim naborom razredov VCL