Eksportuotas PDF kiekvieną stulpelio ribą pastato per pusę simbolio į kairę nuo ten, kur piešia Excel, ir kiekvienas laužomas langelis dabar laužiasi kitoje vietoje. Excel stulpelio plotis nematuojamas simboliais ar taškais. Jis matuojamas darbo knygos Normal šrifto Max Digit Width (MDW) vienetais, o HotXLS tą šriftą su GDI pamatuoja prieš kiekvieną puslapiavimo buildą. Gedimo režimas tylus: niekas nemeta, saugojami pločiai grįžta baitas po baito, o geometrija vis tiek klysta keliais procentais per stulpelį, kol sukauptas dreifas nustumia vieno puslapio lentelę ant dviejų
Kokiais vienetais matuojamas Excel stulpelio plotis?
Stulpelio plotis darbalapyje yra darbo knygos Normal šrifto skaitmenų simbolių skaičius, o ne absoliutus matas. ECMA-376 §18.3.1.13 apibrėžia <col> atributą width per to šrifto 96 dpi Maximum Digit Width ir duoda konversiją iš saugoto pločio atgal į pikselius kaip apipjaustančią išraišką ant MDW. Kalbant apie Calibri 11, kurį Excel siunčia kaip Normal stilių, MDW yra 7 pikseliai. Įleiskite numatytąjį 8.43 vienetų plotį per specifikacijos formulę su MDW 7 ir gausite tiksliai 64 pikselius, tai yra 48 taškus 96 dpi. Tie skaičiai yra tai, ką pats Excel praneša, tad jie naudinga patikra: jei jūsų konversija atkuria 8.43 vienetus į 64 pikselius, aritmetika teisinga, ir tik MDW įvestis dar gali būti bloga
const
// Numatytojo pagrindinio šrifto Maximum digit width (MDW) pikseliais 96 dpi.
// Calibri 11 yra 7 px, kas atkuria tikslius pikselių pločius,
// kuriuos saugojo Excel (8.43 vienetai -> 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 pikseliai -> taškai
if Result < MinimumColumnWidth then
Result := MinimumColumnWidth;
end;
HotXLS laiko tą aritmetiką tiksliai vienoje funkcijoje, lxPagination modulyje, tad yra vienintelė vieta, kur liniuotė gali klysti. + 5 yra tarpas, kurį Excel prideda tinkleliams ir langelio paraštėms, * 0.75 konvertuoja 96 dpi pikselius į PostScript taškus, o grindis ties MinimumColumnWidth egzistuoja tam, kad patologiškai siauras stulpelis vis tiek paliktų juostą, į kurią rendereris gali nupiešti rėmelį. Viešas įėjimo taškas ColumnWidthToPoints išlaiko seną vieno argumento signatūrą ir perduoda pamatuotą MDW šiai funkcijai, kas leido elgesio pokyčiui atsirasti neliečiant nė vieno kvietimo vietos
Kodėl ne Calibri Normal šriftas pastumia kiekvieną ribą
Dreifas multiplikatyvus, todėl jis skaitomas kaip atvaizdavimo klaida, o ne vienetų klaida. MDW yra daugiklis ant pločio, o ne poslinkis. Pastumkite MDW nuo 7 iki 8, ir numatytasis 8.43 vienetų stulpelis eina nuo 64 pikselių iki 72 — šuolis 8 pikseliais arba 6 taškais viename stulpelyje. Dešimt tokių stulpelių, ir dešinysis lentelės kraštas pajudėjo beveik coliu. Darbo knygos, kurios užkabina tai, visiškai paprastos: bet kas, sugeneruota ataskaitų įrankio, kuris įspaudžia Arial ar Segoe UI į Normal stilių, bet kas, išsaugota iš ERP eksporto šablono, bet kas, ką klientas kartą perstilizavo ir užmiršo
Dvi susijusios maketo sistemos paveldi klaidą, o ne sukelia jos. Sujungti regionai sumuoja savo nario stulpelių taškų pločius, tad sujungimas, kuris tilpo viename puslapyje Excel, gali perpilti po MDW dreifo — verta prisiminti, kai statote sujungtų langelių ataskaitų šablonus. Shrink-to-fit lygina pamatuotą teksto plotį su tokiu pačiu stulpelio pločiu, tad blogas MDW taip pat keičia, kurie langeliai traukiasi ir kiek. Ta pati vienetų sumaišies šeima atsiranda piešimo inkaruose, kur vaizdo geometrija ir EMU mastelis turi savą konversijos grandinę, kurią galima sugadinti
Kaip HotXLS matuoja MDW vykdymo metu
HotXLS išsprendžia MDW iš pačios darbo knygos, o ne mano konstantą, ir dvi procedūros atlieka darbą. PaginationApplyNormalFont perskaito Normal stiliaus šriftą iš darbo knygos ir suveikia puslapiavimo buildo viršuje, prieš skaičiuojant bet kokią stulpelių geometriją; jis pirmiausia atstatomas į Calibri 11, tad darbo knyga be šriftų lentelės negali paveldėti pasenusios būsenos iš ankstesnio buildo. Normal stiliaus šriftas yra fonts[0] styles.xml, komponento atskleistas kaip Workbook.Fonts[0]
// Skaito fonts[0] (Normal stiliaus šriftą) iš darbalapio darbo knygos.
// Klasikiniai darbalapiai be šriftų lentelės laiko Calibri 11 numatytąjį.
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;
Antra procedūra, PaginationMeasureMdW, klauso GDI dėl vieno simbolio '0' išmatavimo per GetTextExtentPoint32W ant bendros už ekrano esančios bitmap drobės, grįžta prie tmAveCharWidth iš GetTextMetricsW, kai išmatavimo iškvietimas kliesta, ir grįžta prie DefaultMDW, kai nė vieno nėra. Jo talpykla yra vienas lizdas, raktuotas pagal (name, size), kas skamba primityviai, kol nepažiūrite į prieigos modelį: puslapiavimo buildas klauso to paties Normal šrifto ant kiekvieno kiekvieno puslapio stulpelio, tad vienas lizdas turi beveik tobulą pataikymo dažnį ir kainuoja tris lyginimus per iškvietimą
Kas nutinka be šriftų lentelės, be GUI ar su trūkstamu šriftu?
HotXLS sunyksta iki Calibri 11 konstantos kiekvienu atveju, kai tikrojo Normal šrifto negalima nustatyti, ir daro tai tyliai pagal dizainą. Klasikiniai BIFF darbalapiai yra dažniausias: paveldėti formatai neneša XLSX šriftų baseino, į kurį fonts[0] galėtų rodyti, tad tipo apsauga išeina anksti ir numatytasis MDW 7 lieka galiojęs. Tai ne pataisymas, tai ankstesnis elgesys, sąmoningai išsaugotas, kad matavimo pridėjimas į XLSX kelią negalėtų regresuoti klasikinio formato išvesties
GDI priklausomybė yra sąžiningas įspėjimas. Matavimas vyksta prieš Windows device context, tad kelias mano Windows prieglobą su įdiegtu šriftu. Servise arba be GUI buildo agente GDI teksto metrika paprastai vis tiek išsprendžia, bet šriftas, neįdiegtas toje mašinoje, pakeičiamas font mapperio ir jūs matuojate pakaitalą. Tai niekada nesvyla garsiai; jis grąžina tikėtiną skaičių neteisingam šriftui. Jei serverio pusės eksportai turi atitikti darbalaukio atskaitą, įdiekite šriftus, kuriuos įvardija jūsų šablonai, eksporto priegloboje, arba užfiksuokite Normal šriftą prieš kviesdami darbalapio PDF eksporto kelią
var
Book: TXLSXWorkbook;
Exporter: TXLSPDFExport;
begin
Book := TXLSXWorkbook.Create;
Exporter := TXLSPDFExport.Create;
try
Book.Open('quarterly-report.xlsx');
// Užfiksuokite Normal šriftą, kad šiame priegloboje pamatuotas MDW būtų
// tas, prieš kurį buvo suprojektuotas maketas, o ne font mapperio pakaital.
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;
Matavimo talpyklos ir ta, kuri užgriuvo ant Win64
Kai teksto matavimas tampa GDI apvaliuoju keliu, o ne dauginimu, jį reikia talpinti, o talpinimas atvaizdavimo perėjimo viduje yra vieta, kur šis darbas praliejo kraujo. Shrink-to-fit ciklas žingsniuoja šrifto dydžiu žemyn 0.5 pt žingsniais ir perrašuoja po kiekvieno žingsnio, tad vienas langelis gali kviesti PaginationMeasureTextWidth tuziną kartų su ta pačia eilute, o žodžių laužymas kviečia jį dar kartą kiekvienai kandidatinei eilutei. Memo, raktuotas pagal šrifto vardą, dydį ir tekstą, suslegia tai į vieną GDI iškvietimą skirtingai eilutei, saugomą TStringList kaip name/value poros
Kita šalia pridėta talpykla nebuvo tokia tvarkinga. Atvaizdavimo perėjimas 5 išsprendžia šriftų baseiną per langelį pagal FontIndex, o jo memo naudojo lygiagrečius dinaminius masyvus su rankomis prižiūrimu FontMemoCount. Pirmoji versija užmiršo kviesti ResetFontMemo kiekvieno puslapio pradžioje, tad skaitiklis toliau lipa per puslapius, kol masyvai ne, ir kodas rašė pro visų jų galą. Ant Win32 tai tyliai subraižė į gretimą krūvą ir baigėsi; ant Win64 tai iškart kėlė access violation rašydamas į 0x538. Apibendrinama pamoka: masyvo pagrįsta talpykla, laikoma modulio lygio kintamajame, turi būti atstatoma kiekvieno perėjimo, kuris ją naudoja, įėjime, nes eilučių sąrašas ar žodynas atleidžia trūkstamą atstatymą augdamas, o lygiagretūs masyvai neatleidžia
Patikrinkite savo konversiją
Jums nereikia komponento, kad patvirtintumėte bet kurį iš šito. Paimkite darbo knygą, kurios Normal šriftas nėra Calibri 11, perskaitykite plotį iš <col width="..."/> ir paleiskite jį per specifikacijos formulę du kartus — kartą su MDW 7 ir kartą su MDW, kurį jūsų rendereris iš tikrųjų matuoja tam šriftui; jei atsakymai skiriasi ir jūsų išvestis atitinka pirmąjį, radote dreifą. Stulpelių geometrija yra viena iš tų skaičiuoklės variklio dalių, kurios arba nematoma, arba vienintelis dalykas, kurį kas nors pastebi, ir teisingas jos gavimas reiškia Normal šrifto traktavimą kaip maketo įvestį, o ne stiliavimo detalę. Jei statote Delphi ar C++Builder programas, skaitančias, rašančias, atvaizduojančias ir spausdinančias Excel darbo knygas be įdiegto Office, HotXLS Delphi Excel komponentas apdoroja MDW matavimą, puslapiavimo modelį ir PDF konvejerį už vieno VCL klasių rinkinio