Izvezeni PDF stavlja svaku granicu kolone pola znaka levo od mesta gde je Excel crta, i svaka omotana ćelija sada lomi na drugom mestu. Excel širina kolone se ne meri u znakovima ni u poenima. Meri se u Max Digit Width (MDW) jedinicama Normal fonta radne sveske, i HotXLS meri taj font GDI-jem pre svakog izgrađivanja paginacije. Režim greške je tih: ništa ne baca izuzetak, sačuvane širine kruže bajt po bajt, i geometrija je i dalje van za nekoliko procenata po koloni dok nakupljeni pomak ne gurne tabelu sa jedne stranice na dve
U kojoj je jedinici Excel širina kolone?
Širina kolone u radnom listu je broj znakova cifara Normal fonta radne sveske, ne apsolutno merenje. ECMA-376 §18.3.1.13 definiše width atribut <col> preko Maximum Digit Width tog fonta na 96 dpi, i daje konverziju sačuvane širine nazad u piksele kao odscajući izraz nad MDW. Za Calibri 11, koji Excel isporučuje kao Normal stil, MDW meri 7 piksela. Propustite podrazumevanu širinu od 8.43 jedinica kroz formulu specifikacije sa MDW 7 i dobijate tačno 64 piksela, što je 48 poena na 96 dpi. To su brojevi koje sam Excel prijavljuje, pa čine korisnu proveru: ako vaša konverzija reprodukuje 8.43 jedinice u 64 piksela, aritmetika je ispravna i samo MDW ulaz može još biti pogrešan
const
// Maximum digit width (MDW) podrazumevanog fonta teksta u pikselima na 96 dpi.
// Calibri 11 meri 7 px, što reprodukuje tačne piksel širine
// koje Excel čuva (8.43 jedinice -> 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 -> poeni
if Result < MinimumColumnWidth then
Result := MinimumColumnWidth;
end;
HotXLS drži tu aritmetiku u tačno jednoj funkciji, u lxPagination jedinici, pa postoji jedno jedino mesto gde lenjir može pogrešiti. + 5 je punjenje koje Excel dodaje za mrežne linije i margine ćelija, * 0.75 pretvara 96 dpi piksele u PostScript poene, i pod u MinimumColumnWidth postoji da patološki uska kolona i dalje ostavi traku u koju render može da nacrta ivicu. Javna ulazna tačka ColumnWidthToPoints zadržava svoj stari potpis sa jednim argumentom i prosleđuje izmereni MDW ovoj funkciji, što je omogućilo da promena ponašanja sleti bez dodirivanja ijednog mesta poziva
Zašto Normal font koji nije Calibri pomera svaku granicu
Pomak je multiplikativan, i zato izgleda kao greška renderovanja a ne greška jedinica. MDW je faktor na širini, ne ofset. Podignite MDW sa 7 na 8 i podrazumevana kolona od 8.43 jedinice prelazi sa 64 na 72 piksela, skok od 8 piksela ili 6 poena na jednoj koloni. Deset takvih kolona i desna ivica tabele se pomerila za skoro ceo inč. Radne sveske koje na ovo naiđu su potpuno obične: sve što je izgenerisao izveštajni alat koji utiskuje Arial ili Segoe UI u Normal stil, sve što je sačuvano iz ERP šablona za izvoz, sve što je kupac jednom prestilio i zaboravio
Dva srodna sistema rasporeda nasleđuju grešku umesto da je prouzrokuju. Spojene regije sabiraju poenske širine svojih članskih kolona, pa spajanje koje je stalo na jednu stranicu u Excel-u može da prekorači posle MDW pomaka, što vredi zapamtiti kad gradite šablone izveštaja sa spojenim ćelijama. Shrink-to-fit poredi izmerenu širinu teksta sa istom širinom kolone, pa pogrešan MDW takođe menja koje se ćelije skupljaju i za koliko. Ista porodica zabune jedinica javlja se u sidrima crteža, gde geometrija slika i EMU razmera ima svoj lanac konverzije koji može da se pokvari
Kako HotXLS meri MDW u vreme izvršavanja
HotXLS razrešava MDW iz same radne sveske umesto da pretpostavi konstantu, i dve procedure rade taj posao. PaginationApplyNormalFont čita Normal stil fonta sa radne sveske i izvršava se na vrhu izgrađivanja paginacije, pre nego što se bilo kakva geometrija kolone izračuna; prvo se resetuje na Calibri 11, pa radna sveska bez tabele fontova ne može da nasledi zastarelo stanje od prethodne izgradnje. Normal stil font je fonts[0] u styles.xml, izložen od komponente kao Workbook.Fonts[0]
procedure PaginationApplyNormalFont(Worksheet: TObject);
var
Sh: TXLSXWorksheet;
Fnt: TXLSFont;
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, pita GDI za obim jednog znaka '0' kroz GetTextExtentPoint32W na deljenom vanekranskom bitmap platnu, pada nazad na tmAveCharWidth iz GetTextMetricsW kada poziv obima ne uspe, i pada nazad na DefaultMDW kada nijedno nije dostupno. Njen keš je jedan slot sa ključem (ime, veličina), što zvuči grubo dok ne pogledate obrazac pristupa: izgrađivanje paginacije traži isti Normal font za svaku kolonu svake stranice, pa jedan slot ima skoro savršenu stopu pogodaka i košta tri poređenja po pozivu
Šta se dešava bez tabele fontova, bez GUI-ja ili sa fontom koji nedostaje?
HotXLS degradira na Calibri 11 konstantu u svakom slučaju gde se pravi Normal font ne može odrediti, i čini to tiho po dizajnu. Klasični BIFF radni listovi su uobičajeni slučaj: zastareli formati ne nose XLSX bazen fontova na koji fonts[0] može da se pozove, pa tip-guard izlazi rano i podrazumevani MDW od 7 stoji. To nije popravka, to je prethodno ponašanje sačuvano namerno, tako da dodavanje merenja na XLSX putanju nije moglo da regredira izlaz klasičnog formata
GDI zavisnost je iskrena napomena. Merenje se izvršava nad Windows device context, pa putanja pretpostavlja Windows domaćina sa instaliranim fontom. U servisu ili headless build agentu, GDI tekst metrike obično i dalje rešavaju, ali font koji nije instaliran na toj mašini font mapper zamenjuje i vi merite zamenicu. Nikad ne pada glasno; vraća uverljiv broj za pogrešan tip slova. Ako izvozi na strani servera moraju da se poklapaju sa referencom sa radne površine, instalirajte fontove koje vaši šabloni imenuju na export host, ili zakačite Normal font pre poziva putanje izvoza radnog lista u PDF
var
Book: TXLSXWorkbook;
Exporter: TXLSPDFExport;
begin
Book := TXLSXWorkbook.Create;
Exporter := TXLSPDFExport.Create;
try
Book.Open('quarterly-report.xlsx');
// Zakačite Normal font da MDW izmeren na ovom domaćinu bude onaj
// prema kojem je raspored dizajniran, ne font-mapper zamena.
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;
Keševi merenja, i onaj koji je pukao na Win64
Kada je merenje teksta GDI odlazak i povratak umesto množenja, mora se keširati, a keširanje unutar prolaza renderovanja je mesto gde je ovaj posao prokrvario. Shrink-to-fit petlja spušta veličinu fonta u koracima od 0.5 pt i ponovo meri posle svakog koraka, pa jedna ćelija može da pozove PaginationMeasureTextWidth desetak puta sa istim stringom, i prelom reči ga poziva ponovo za svaku kandidatsku liniju. Memo sa ključem ime fonta, veličina, tekst svodi to na jedan GDI poziv po jedinstvenom stringu, čuvan u TStringList kao parovi ime/vrednost
Drugi keš dodat uz njega nije bio tako uredan. Render prolaz 5 razrešava bazen fontova po ćeliji preko FontIndex, i njegov memo je koristio paralelne dinamičke nizove sa ručno održavanim FontMemoCount. Prva verzija zaboravila je da pozove ResetFontMemo na početku svake stranice, pa je brojač nastavio da raste kroz stranice dok nizovi nisu, i kod je pisao preko kraja svih njih. Na Win32 je to tiho izgrebalo susedni heap i završilo; na Win64 diglo je access violation pri upisu na 0x538 odmah. Generalizovana lekcija: keš podržan nizom držan u unit nivoa promenljivoj mora se resetovati na ulazu u svaki prolaz koji ga koristi, jer string lista ili rečnik oprašta nedostajući reset rastom, a paralelni nizovi ne
Provera sopstvene konverzije
Nije vam potrebna komponenta da biste ovo proverili. Uzmite radnu svesku čiji Normal font nije Calibri 11, pročitajte širinu iz <col width="..."/>, i provucite je kroz formulu specifikacije dva puta, jednom sa MDW 7 i jednom sa MDW koji vaš render stvarno meri za taj font; ako se odgovori razlikuju i vaš izlaz se poklapa sa prvim, našli ste pomak. Geometrija kolone je jedan od onih delova spreadsheet engine-a koji je ili nevidljiv ili jedina stvar koju neko primeti, i njeno ispravno urađivanje znači tretiranje Normal fonta kao ulaza u raspored a ne detalja stila. Ako gradite Delphi ili C++Builder aplikacije koje čitaju, pišu, renderuju i štampaju Excel radne sveske bez instaliranog Office-a, HotXLS Delphi Excel komponenta rukuje MDW merenjem, modelom paginacije i PDF cevovodom iza jednog skupa VCL klasa