Az exportált PDF minden oszlophatárt fél karakterrel az Excel általi rajzolástól balra tesz, és minden tördelt cella most más helyen törik. Az Excel oszlopszélessége nem karakterekben vagy pontokban mérődik. A munkafüzet Normal betűtípusának Max Digit Width (MDW) egységeiben mérődik, és a HotXLS minden lapozási build előtt GDI-vel méri azt a betűtípust. A hibamód csendes: semmi nem dob, a tárolt szélességek bájtonként visszautaznak, és a geometria oszloponként még mindig néhány százalékkal eltér, amíg a felgyülemlett sodródás egy egyoldalas táblát kettőre nem nyom
Milyen egységben mérődik az Excel oszlopszélessége?
Egy munkalap oszlopszélessége a munkafüzet Normal betűtípusának számjegykaraktereinek száma, nem abszolút mérték. Az ECMA-376 §18.3.1.13 a <col> width attribútumát az adott betűtípus 96 dpi-nél mért Maximum Digit Width-jával definiálja, és a tárolt szélességről pixelekre való visszakonverziót MDW feletti lefelé csonkító kifejezésként adja. Calibri 11 esetén — amely az Excel Normal stílusának szállított betűtípusa — az MDW 7 pixel. Ha az alapértelmezett 8,43 egységes szélességet a specifikációs képleten átengedi MDW 7-tel, pontosan 64 pixelt kap, ami 96 dpi-nél 48 pont. Ezek azok a számok, amelyeket maga az Excel jelent, így hasznos ellenőrzést adnak: ha a konverziója 8,43 egységből 64 pixelt reprodukál, az aritmetika helyes, és csak az MDW bemenet lehet még rossz
const
// Az alapértelmezett szövegtörzs betűtípusának MDW-ja pixelekben 96 dpi-nél.
// A Calibri 11 7 px, ami reprodukálja a pontos pixel szélességeket,
// amelyeket az Excel tárol (8.43 egység -> 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 pixelek -> pontok
if Result < MinimumColumnWidth then
Result := MinimumColumnWidth;
end;
A HotXLS azt az aritmetikát pontosan egy függvényben tartja, a lxPagination egységben, így egyetlen hely van, ahol a vonalzó tévedhet. A + 5 az a kitöltés, amelyet az Excel a rácsvonalak és cellamargók miatt ad hozzá, a * 0.75 a 96 dpi-s pixeleket PostScript pontokká alakítja, a MinimumColumnWidth-nál lévő plafon pedig azért létezik, hogy egy patológiusan keskeny oszlop is hagyjon sávot, amelybe a renderelő szegélyt rajzolhat. A nyilvános belépési pont, a ColumnWidthToPoints, megtartja régi egyargumentumos szignatúráját, és mért MDW-t továbbít ennek a függvénynek, ami tette lehetővé, hogy a viselkedésváltozás egyetlen hívóhely érintése nélkül landoljon
Miért mozgat minden határt egy nem Calibri Normal betűtípus?
A sodródás multiplikatív, ezért olvastatik renderelési hibaként, nem egységhibaként. Az MDW a szélesség tényezője, nem eltolása. Tolja az MDW-t 7-ről 8-ra, és az alapértelmezett 8,43 egységes oszlop 64 pixelről 72-re megy, 8 pixel vagy 6 pont ugrás egyetlen oszlopon. Tíz ilyen oszlop, és a tábla jobb széle jóformán egy hüvelyknyit mozdult. Az ezen fennakadó munkafüzetek teljesen hétköznapik: bármi, amelyet olyan jelentéskészítő eszköz generált, amely Arial vagy Segoe UI-t pecsétel a Normal stílusba, bármi, amelyet ERP export sablonból mentettek, bármi, amelyet egy ügyfél egyszer átstyírt, majd elfelejtett
Két kapcsolódó elrendezési rendszer örökli a hibát, nem okozza. Az összevont régiók tagoszlopaik pontszélességeit összegzik, így egy Excelben egy oldalra férő összevonás MDW-sodródás után túllőhet, ami emlékezetre érdemes, amikor összevont cellás jelentéssablonokat épít. A zsugorításhoz igazítás a mért szövegszélességet ugyanahhoz az oszlopszélességhez méri, így a rossz MDW azt is megváltoztatja, mely cellák zsugorodnak és mennyit. Ugyanez az egységzavar család rajzolási horgonyoknál bukkan fel, ahol a képgeometria és EMU skálázás saját konverziólánccal rendelkezik, amelyet el lehet rontani
Hogyan méri a HotXLS az MDW-t futásidőben
A HotXLS az MDW-t magából a munkafüzetből oldja fel, konstans feltételezése helyett, és két eljárás végzi a munkát. A PaginationApplyNormalFont leolvassa a Normal stílus betűtípusát a munkafüzetről, és a lapozási build tetején fut, bármilyen oszlopgeometria kiszámítása előtt; először Calibri 11-re áll vissza, így egy betűtípus-tábla nélküli munkafüzet nem örökölhet elavult állapotot egy korábbi buildből. A Normal stílus betűtípusa a styles.xml fonts[0]-ja, amelyet a komponens Workbook.Fonts[0]-ként tár fel
// Leolvassa a fonts[0]-t (a Normal stílus betűtípusát) a munkalap-munkafüzetről.
// A betűtípus-tábla nélküli klasszikus munkalapok megtartják a Calibri 11 alapértelmezést.
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;
A második eljárás, a PaginationMeasureMdW, a GDI-től kéri az egyetlen '0' karakter kiterjedését a GetTextExtentPoint32W-n át egy megosztott, képernyőn kívüli bitképvásznon, a tmAveCharWidth-ra esik vissza a GetTextMetricsW-ből, amikor a kiterjedési hívás elbukik, és a DefaultMDW-ra, amikor egyik sem elérhető. A gyorsítótára egyetlen hely, amelyet a (name, size) kulcsol, ami durvának hangzik, amíg meg nem nézi a hozzáférési mintát: egy lapozási build minden oldal minden oszlopánál ugyanazt a Normal betűtípust kéri, így egy hely közel tökéletes találati aránnyal bír, és hívásonként három összehasonlításba kerül
Mi történik betűtípus-tábla, GUI vagy betűtípus nélkül?
A HotXLS minden olyan esetben a Calibri 11 konstansra degradálódik, amikor a valódi Normal betűtípus nem határozható meg, és ezt tervezetten csendesen teszi. A klasszikus BIFF munkalapok a gyakori eset: a legacy formátumok nem hordoznak XLSX betűtípus-készletet, amelyre a fonts[0] hivatkozhat, így a típusőr korán kilép, és a 7-es alapértelmezett MDW áll. Ez nem javítás, hanem a korábbi viselkedés szándékos megőrzése, hogy a mérés XLSX útvonalra vétele ne regresszálhassa a klasszikus formátumú kimenetet
A GDI-függés az őszinte kikötés. A mérés Windows eszközkontextus ellen fut, így az útvonal telepített betűtípussal rendelkező Windows gazdát tételez fel. Szolgáltatásban vagy fej nélküli build agenten a GDI szövegmetrikák általában még feloldódnak, de egy azon a gépen telepítetlen betűtípust a betűtípus-leképező helyettesít, és Ön a helyettesítőt méri. Soha nem hibázik hangosan; hihető számot ad vissza rossz betűtípusra. Ha a kiszolgáló oldali exportnak asztali referenciához kell illeszkednie, telepítse a sablonjai által nevezett betűtípusokat az export gazdagépre, vagy rögzítse a Normal betűtípust, mielőtt meghívja a munkalap PDF export útvonalát
var
Book: TXLSXWorkbook;
Exporter: TXLSPDFExport;
begin
Book := TXLSXWorkbook.Create;
Exporter := TXLSPDFExport.Create;
try
Book.Open('quarterly-report.xlsx');
// Rögzítse a Normal betűtípust, hogy az ezen a gazdagépen mért MDW az legyen,
// amelyre az elrendezést tervezték, nem betűtípus-leképező helyettesítő.
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;
Mérési gyorsítótárak, és az, amely Win64-en összeomlott
Amint a szövegmérés GDI oda-vissza út, nem szorzás, gyorsítótárazni kell, és a gyorsítótárazás egy render meneten belül az a hely, ahol ez a munka vért ejtett. A zsugorításhoz igazítás ciklusa 0,5 pt lépésekben csökkenti a betűméretet, és minden lépés után újra mér, így egyetlen cella tucatszor hívhatja a PaginationMeasureTextWidth-et ugyanazzal a sztringgel, és a szótördelés jelöltsoronként újra hívja. Egy memo, amelyet betűtípusnév, méret és szöveg kulcsol, ezt egyetlen GDI hívásra zsugorolja különálló sztringenként, TStringList-ben tárolva név/érték párokként
A mellette hozzáadott másik gyorsítótár nem volt ilyen rendben. Az 5. render menet cellánként oldja fel a betűkészletet FontIndex alapján, és a memoja párhuzamos dinamikus tömböket használt kézzel karbantartott FontMemoCount-tal. Az első verzió elfelejtette meghívni a ResetFontMemo-t minden oldal elején, így a számláló oldalakon át tovább mászott, miközben a tömbök nem, és a kód mindet túlírt a végén. Win32-n ez csendben a szomszédos kupacba kacsintott be, és befejeződött; Win64-en azonnal hozzáférési sértést dobott egy 0x538-ra íráskor. A általánosítható tanulság: egységszintű változóban tartott tömbalapú gyorsítótár minden őt használó menet belépésénél visszaállítandó, mert egy sztringlista vagy szótár a hiányzó visszaállítást növekedéssel megbocsátja, a párhuzamos tömbök nem
A saját konverziójának ellenőrzése
Nincs szüksége a komponensre, hogy bármelyikét ellenőrizze. Végyen egy munkafüzetet, amelynek Normal betűtípusa nem Calibri 11, olvasson egy szélességet a <col width="..."/>-ből, és futtassa át a specifikációs képleten kétszer, egyszer MDW 7-tel, egyszer azzal az MDW-vel, amelyet a renderelője ténylegesen mér az adott betűtípusra; ha a válaszok eltérnek, és a kimenete az elsőt egyezi, megtalálta a sodródást. Az oszlopgeometria a táblázatkezelő motor azon részei közé tartozik, amelyek vagy láthatatlanok, vagy az egyetlen dolog, amelyet bárki észrevesz, és helyesen eltalálni azt jelenti, hogy a Normal betűtípust az elrendezés bemeneteként kezeli, nem stílusrészletként. Ha olyan Delphi vagy C++Builder alkalmazásokat épít, amelyek Office telepítése nélkül olvasnak, írnak, renderelnek és nyomtatnak Excel munkafüzeteket, a HotXLS Delphi Excel komponens kezeli az MDW mérést, a lapozási modellt és a PDF pipeline-t egy VCL osztálykészlet mögött