Izvezeni PDF postavlja svaku granicu stupca pola znaka lijevo od mjesta gdje je Excel iscrtava, a svaka omotana ćelija sada se lomi na drugom mjestu. Širina stupca u Excelu ne mjeri se u znakovima ni u točkama. Mjeri se u jedinicama Max Digit Width (MDW) Normalnog fonta radne knjige, a HotXLS taj font mjeri putem GDI-ja prije svake izgradnje paginacije. Način otkazivanja je tih: ništa ne baca iznimku, pohranjene širine vraćaju se bajt-po-bajt netaknute, a geometrija je ipak odmaknuta za nekoliko postotaka po stupcu sve dok nakupljeni pomak tablicu s jedne stranice ne pogurne na dvije
U kojim se jedinicama mjeri širina stupca u Excelu?
Širina stupca u radnom listu jest broj znamenkastih znakova Normalnog fonta radne knjige, a ne apsolutna mjera. ECMA-376 §18.3.1.13 definira atribut width elementa <col> preko Maximum Digit Width tog fonta pri 96 dpi i daje pretvorbu iz pohranjene širine natrag u piksele kao odsječajući izraz nad MDW-om. Za Calibri 11, koji Excel isporučuje kao Normalni stil, MDW iznosi 7 piksela. Ako zadanu širinu od 8.43 jedinice provedete kroz formulu iz specifikacije s MDW-om 7, dobijete točno 64 piksela, što je 48 točaka pri 96 dpi. To su brojevi koje sam Excel prijavljuje, pa služe kao korisna provjera: ako vaša pretvorba reproducira 8.43 jedinice u 64 piksela, aritmetika je ispravna i jedino ulaz MDW još može biti kriv
const
// Najveća širina znamenke (MDW) zadanog fonta teksta u pikselima pri 96 dpi.
// Calibri 11 iznosi 7 px, što reproducira točne pikselne širine
// koje Excel pohranjuje (8.43 jedinica -> 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 pikseli -> točke
if Result < MinimumColumnWidth then
Result := MinimumColumnWidth;
end;
HotXLS tu aritmetiku drži u točno jednoj funkciji, u jedinici lxPagination, pa postoji samo jedno mjesto na kojem mjerilo može biti krivo. + 5 je dopuna koju Excel dodaje za mrežne linije i margine ćelija, * 0.75 pretvara piksele pri 96 dpi u PostScript točke, a pod od MinimumColumnWidth postoji zato patološki uzak stupac i dalje ostavlja traku u koju iscrtivač može nacrtati rub. Javna ulazna točka ColumnWidthToPoints zadržava svoj stari potpis s jednim argumentom i prosljeđuje izmjereni MDW ovoj funkciji, što je promjenu ponašanja omogućilo bez diranja ijednog mjesta poziva
Zašto Normalni font koji nije Calibri pomjera svaku granicu
Pomak je multiplikativan, pa se stoga čita kao greška iscrtavanja, a ne greška jedinica. MDW je faktor na širinu, a ne pomak. Gurnite li MDW sa 7 na 8, zadani stupac od 8.43 jedinice ide s 64 piksela na 72, skok od 8 piksela ili 6 točaka na jednom stupcu. Deset takvih stupaca i desni rub tablice pomaknuo se za gotovo cijeli palec. Radne knjige koje ovo okidaju posve su obične: sve što je generirao alat za izvještaje koji u Normalni stil utiskuje Arial ili Segoe UI, sve što je spremljeno iz ERP predloška za izvoz, sve što je kupac jednom preoblikovao pa zaboravio
Dva povezana sustava rasporeda nasljeđuju grešku umjesto da je uzrokuju. Spojene regije zbrajaju širine u točkama svojih sastavnih stupaca, pa spoj koji je u Excelu stao na jednu stranicu može se prelijeti nakon MDW pomaka, što vrijedi zapamtiti kad gradite predloške izvještaja sa spojenim ćelijama. Shrink-to-fit uspoređuje izmjerenu širinu teksta s istom širinom stupca, pa pogrešan MDW mijenja i to koje se ćelije sažimaju i za koliko. Ista obitelj zabune jedinica pojavljuje se u sidrima crteža, gdje geometrija slika i EMU skaliranje ima svoj lanac pretvorbe u kojem se može pogriješiti
Kako HotXLS mjeri MDW tijekom izvođenja
HotXLS razrješuje MDW iz same radne knjige umjesto da pretpostavi konstantu, a dva postupka obavljaju posao. PaginationApplyNormalFont čita font Normalnog stila iz radne knjige i izvodi se na vrhu izgradnje paginacije, prije nego što se bilo koja geometrija stupca izračuna; prvo se vraća na Calibri 11, pa radna knjiga bez tablice fontova ne može naslijediti zastarjelo stanje od prethodne izgradnje. Font Normalnog stila jest fonts[0] u styles.xml, kojega komponenta izlaže kao Workbook.Fonts[0]
// Čita fonts[0] (font Normalnog stila) iz radne knjige lista.
// Klasični listovi bez tablice fontova zadržavaju zadanu vrijednost 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;
Drugi postupak, PaginationMeasureMdW, od GDI-ja traži opseg pojedinačnog znaka '0' putem GetTextExtentPoint32W na dijeljenom izvanekranskom platnu bitmape, vraća se na tmAveCharWidth iz GetTextMetricsW kad poziv opsega ne uspije, a na DefaultMDW kad ništa od toga nije dostupno. Njegova predmemorija jest jedan utor ključan po (name, size), što zvuči grubo dok ne pogledate obrazac pristupa: izgradnja paginacije traži isti Normalni font na svakom stupcu svake stranice, pa jedan utor ima gotovo savršenu stopu pogodaka i košta tri usporedbe po pozivu
Što se događa bez tablice fontova, bez GUI-ja ili uz font koji nedostaje?
HotXLS se u svakom slučaju u kojem stvarni Normalni font ne može utvrditi vraća na konstantu Calibri 11, i to po dizajnu tiho. Klasični BIFF listovi uobičajeni su slučaj: naslijeđeni formati nemaju XLSX bazen fontova na koji bi se fonts[0] mogao pozvati, pa provjera tipa rano izlazi i zadani MDW od 7 ostaje na snazi. To nije popravak, to je namjerno sačuvano prethodno ponašanje, tako da dodavanje mjerenja XLSX putanji nije moglo regresirati izlaz klasičnog formata
GDI ovisnost jest iskrena napomena. Mjerenje se izvodi nad Windows kontekstom uređaja, pa putanja pretpostavlja Windows računalo s instaliranim fontom. U servisu ili agentu za gradnju bez sučelja GDI metrike teksta u pravilu se i dalje razrješuju, ali font koji nije instaliran na tom stroju zamjenjuje font mapper i vi mjerite zamjenu. Nikad ne otkazuje glasno; vraća uvjerljiv broj za pogrešan tip slova. Ako izvozi na poslužiteljskoj strani moraju odgovarati referenci sa stolnog računala, instalirajte fontove koje vaši predlošci navode na stroj s kojeg izvozite, ili zaključajte Normalni font prije poziva putanje PDF izvoza radnog lista
var
Book: TXLSXWorkbook;
Exporter: TXLSPDFExport;
begin
Book := TXLSXWorkbook.Create;
Exporter := TXLSPDFExport.Create;
try
Book.Open('quarterly-report.xlsx');
// Zaključajte Normalni font da MDW izmjeren na ovom stroju bude onaj
// prema kojemu je raspored dizajniran, a ne zamjena font-mappera.
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;
Predmemorije mjerenja i ona koja je srušila Win64
Kad je mjerenje teksta odlazak do GDI-ja i natrag, a ne množenje, mora se predmemorirati, a predmemoriranje unutar prolaza iscrtavanja mjesto je na kojem je ovaj posao najviše zabolio. Petlja shrink-to-fit snižava veličinu fonta u koracima od 0.5 pt i ponovno mjeri nakon svakog koraka, pa jedna ćelija može pozvati PaginationMeasureTextWidth desetak puta s istim nizom, a prelamanje riječi poziva ga ponovno za svaki kandidatski redak. Bilješka ključana po imenu fonta, veličini i tekstu to svodi na jedan GDI poziv po različitom nizu, pohranjen u TStringList kao parovi ime/vrijednost
Druga predmemorija dodana uz nju nije bila tako uredna. Prolaz iscrtavanja 5 razrješuje bazen fontova po ćeliji preko FontIndex, a njegova je bilješka koristila paralelna dinamička polja s ručno vođenim FontMemoCount. Prva verzija zaboravila je pozvati ResetFontMemo na početku svake stranice, pa je brojač nastavio rasti kroz stranice dok polja nisu, i kod je pisao preko kraja svih njih. Na Win32 to je tiho šaralo po susjednoj hrpi i završilo; na Win64 odmah je podignuta iznimka pristupa pri pisanju na 0x538. Pouka koja se generalizira: predmemorija oslonjena na polje, držana u varijabli razine jedinice, mora se resetirati na ulazu u svaki prolaz koji je koristi, jer popis nizova ili rječnik izostavljeni reset oprašta rastom, a paralelna polja ne
Provjera vlastite pretvorbe
Ne trebate komponentu da biste ovo provjerili. Uzmite radnu knjigu čiji Normalni font nije Calibri 11, pročitajte širinu iz <col width="..."/> i provedite je dvaput kroz formulu iz specifikacije, jednom s MDW-om 7 i jednom s MDW-om koji vaš iscrtivač stvarno mjeri za taj font; ako se odgovori razlikuju i vaš izlaz odgovara prvom, našli ste pomak. Geometrija stupaca jedan je od onih dijelova motora proračunskih tablica koji je ili nevidljiv ili jedina stvar koju itko primijeti, a ispravno postaviti znači tretirati Normalni font kao ulaz u raspored, a ne detalj oblikovanja. Ako gradite Delphi ili C++Builder aplikacije koje čitaju, pišu, prikazuju i ispisuju Excel radne knjige bez instaliranog Officea, HotXLS Delphi Excel komponenta rješava MDW mjerenje, model paginacije i PDF cjevovod iza jednog skupa VCL klasa