U HotPDF komponenti, THotPDF.Resolution definiše crtaću jedinicu: svaka X i Y koordinata, svaka margina, veličina data u SetFont, i rezultati TextWidth-a i GetWideTextWidth-a mere se u 1/Resolution inča. THPDFPage.Width i Height ih ne prate i ostaju u poenima, pa granice rasporeda moraju da dolaze iz read-only UserWidth-a i UserHeight-a. Uobičajen razlog da se Resolution pipa je port: engine izveštaja koji već razmišlja u 1/96 ili 1/144 inča lakše se preseli kad PDF strana govori istu jedinicu nego kad svako mesto poziva dobije faktor konverzije. To dobro radi, dokle znate koji su se brojevi preselili u novu jedinicu a koji ostali
Šta THotPDF.Resolution zapravo menja?
THotPDF.Resolution menja samo to kako HotPDF čita brojeve koje uputite; PDF koji napiše je isti. Seter ima dve linije: SetResolution sačuva vrednost i postavi DocScale := Value / 72. Od tada XProjection i YProjection dele svaku koordinatu sa DocScale-om na ulazu u content stream, a SetFont veličinu deli na isti način pre nego što je zapiše. PDF user space podrazumeva 1/72 inča (ISO 32000-1 §8.3.2.3), pa je pri podrazumevanom Resolutionu od 72 projekcija identitet, a na 144 jedna crtaća jedinica je pola poena. Ne upisuje se /UserUnit unos. Taj page atribut, dodat u PDF 1.6, odvojena je stvar koju HotPDF izlaže kao THPDFPage.SetUserUnit. Detalj koji uhvati ljude sa TextOut tutorijala: koordinate stranice idu od gornjeg levog ugla sa Y koji raste nadole, jer YProjection računa vrh MediaBox-a minus skalirani Y, i to važi pri svakom Resolutionu
var
Pdf: THotPDF;
Page: THPDFPage;
Margin: Single;
Title: WideString;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'invoice.pdf';
Pdf.Resolution := 144; // 1 crtaća jedinica = 1/144 inča
Pdf.BeginDoc;
Page := Pdf.CurrentPage; // A4: Width = 595, UserWidth = 1190
Margin := 144; // jedan inč u crtaćim jedinicama
Page.SetFont('Arial', [fsBold], 28); // 28/144 inča, font od 14 pt
Title := 'INVOICE 2026-0417';
// Desno poravnaj uz ivicu stranice izmerenu u istoj jedinici
Page.TextOut(Page.UserWidth - Margin - Page.GetWideTextWidth(Title),
Margin, 0, Title);
Page.SetLineWidth(2); // linija od 1 pt
Page.MoveTo(Margin, Margin + 48);
Page.LineTo(Page.UserWidth - Margin, Margin + 48);
Page.Stroke;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Zašto se Page.Width ne slaže sa mojim koordinatama na Resolutionu 144?
THPDFPage.Width i Height prijavljuju stranicu u poenima ma šta bio Resolution dokumenta, dok su vaše koordinate u 1/Resolution inča, pa na 144 stranica deluje upola uža nego što jeste. A4 stranica čita Width = 595 i Height = 842 na Resolutionu 72 i i dalje čita 595 i 842 na 144, gde je desna ivica zapravo na X = 1190. UserWidth i UserHeight, dodati u v2.766.0, vraćaju Width * DocScale, što je veličina stranice u jedinici kojom crtate. Pre nego što su postojali, biblioteka je mešala dve stvari interno, a simptomi na Resolutionu 144 bili su dramatični: pasusi su se lomili posle svakog znaka, THPDFTable.Render je svaki red gurao na novu stranicu, i HTML uvoznik i XFA poravnavač crtali su svoj sadržaj upola, sa poravnatom formom uguranom u gornji levi ugao. Raspored pasusa, render tabela, HTML uvoz, EMF centriranje, WMF klip stranice i dijagnostika rasporeda sada svi čitaju veličinu u korisničkim jedinicama. I vaš rasporedni kod treba isto: sve što se poredi sa crtaćom koordinatom (desna margina, test preloma stranice, račun centriranja) pripada UserWidth-u i UserHeight-u, nikada Width-u i Height-u
Zamka prva: dodela Width ili Height prebacuje stranicu na poene
Postavljanje Page.Width ili Page.Height tiho prebacuje stranicu na UserDefined, a UserDefined stranica potpuno ignoriše DocScale, pa je sve što na nju nakon toga nacrtate u poenima, ne u 1/Resolution inča. Seter je star i po dizajnu prima poene, zato mu je značenje i ostavljeno. Projekcija za UserDefined stranicu je čist X + MinX, a SetFont veličinu upisuje nepromenjenu. Na Resolutionu 144 rezultat je stranica čiji sadržaj odjednom izađe duplo veći od prethodne. Biblioteka je napravila baš ovu grešku: stranice nastavka pasusa kopirale su veličinu prethodne stranice kroz Width, i svaka stranica prekoračenja prebacivala se na poene. Te stranice sada kopiraju Size, Orientation i Resolution stranice, i na Width i Height padaju tek kad je originalna stranica već bila UserDefined
Dva izlaza, zavisno od toga šta vam treba. Ako posluži standardni list, postavite Page.Size i Page.Orientation i nastavite da crtate u svojoj Resolution jedinici. Ako baš treba sopstvena veličina stranice, prihvatite da je to page u poenima i crtajte u poenima; UserWidth je tu jednak Width-u, pa rasporedni kod koji uvek čita UserWidth radi na obe vrste stranica. Jedinični test ovo kucava: na Resolutionu 144 A4 stranica prijavljuje UserWidth od 1190, ali posle Width := 500 i Height := 400 prijavljuje 500 i 400. Učitane stranice rade isto, jer stranica ponovo izgrađena iz postojećeg PDF-a zna samo svoj MediaBox u poenima i crta u poenima. Stranice koje je ovaj dokument sam napravio čuvaju svoje jedinice kad se odete i vratite kroz CurrentPageNumber, što je slučaj od v2.766.26
Zamka druga: zašto veličine fontova izlaze prepolovljene?
Veličina fonta koja je počela kao poeni izlazi prepolovljena na Resolutionu 144 jer SetFont svoj argument veličine tretira kao crtaću jedinicu i pretvara ga u poene pre upisa. Interno, SetFont upisuje ASize / DocScale * DPI u tekući font objekat, pa je sačuvana vrednost uvek u poenima. Biblioteka je ovde posrnula dva puta: font zamena u WideTextOutBoxEx-u i stranica nastavka pasusa obe su tu sačuvanu poentnu vrednost vraćale u SetFont, koji bi je skalirao drugi put i prepolovio tekst. Vaš kod ne može da pročita sačuvanu veličinu, ali se isti bug javlja svaki put kad poentna vrednost odaslane negde stigne do SetFont-a: TFont.Size sa VCL forme, veličina u definiciji izveštaja, CSS pt dužina. Prvo je pretvorite, i u faktor uključite i Resolution same stranice i UserDefined slučaj, kao što radi reprodukcija metafajla kad reprodukuje Canvas stranice (za tu putanju vidi kako HotPDF uvozi EMF i WMF vektorsku grafiku):
// Crtaća jedinica po poenu na tekućoj stranici. Ogleda projekciju
// koju HotPDF koristi: 1 na stranici dimenzionisanoj kroz Width/Height, inače
// (document Resolution / 72) * (page Resolution / 72)
function UnitsPerPoint(Pdf: THotPDF): Single;
begin
if Pdf.CurrentPage.Size = UserDefined then
Result := 1
else
Result := (Pdf.Resolution / 72) * (Pdf.CurrentPage.Resolution / 72);
end;
procedure SetFontFromVcl(Pdf: THotPDF; Font: TFont);
begin
// TFont.Size je u poenima; SetFont očekuje crtaća jedinice
Pdf.CurrentPage.SetFont(AnsiString(Font.Name), Font.Style,
Font.Size * UnitsPerPoint(Pdf));
end;
Biblioteka primenjuje isto pravilo na sopstvene poentne konstante. Font od 12 poena s kojim svaka nova stranica počinje sada se množi internim faktorom jedinica-po-poenu, pa je 12 poena pri bilo kom Resolutionu. DrawChart, čije su margine, veličine oznaka i debljine linija sve ukucane poene, sada radi sa skalom privremeno postavljenom na 1. Ono što ostaje u crtaćim jedinicama, namerno, jesu javni podrazumevani parametri poput veličine modula DrawQRCode-a i podrazumevane veličine fonta tabele: deo su API ugovora, pa na Resolutionu 144 znače polovinu onoga što znače na 72. Ako dimenzionišete izveštaje iz šablona, vodič o izlazu izveštaja sa fontovima i slikama u HotPDF-u pokriva odakle te vrednosti obično dolaze
Kako proveriti da li je raspored nezavisan od Resolutiona?
Najpouzdanija provera je poređenje bajtova: renderujte istu stranicu na Resolutionu 72 pa opet na 144 sa svakom koordinatom i veličinom udvostručenom, i nekompresovani content streamovi moraju biti identični. Oba prolaza slijeću na iste poentne vrednosti posle projekcije, pa je svaka razlika vrednost koja je preskočila konverziju. Tako HotPDF test skup proverava pasuse, tabele, HTML uvoz, XFA poravnavanje, lukove, metafajlove i slike. Isti trik radi za vaš vlastiti kod izveštaja uz gotovo nikakav test okvir:
procedure RenderPage(const FileName: string; Res: Integer; K: Single);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.AutoLaunch := False;
Pdf.Compression := cmNone; // čitljivi content streamovi
Pdf.FileName := FileName;
Pdf.Resolution := Res;
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 10 * K);
Pdf.CurrentPage.TextOut(36 * K, 36 * K, 0, 'Line 1');
Pdf.CurrentPage.Rectangle(36 * K, 60 * K, 200 * K, 40 * K);
Pdf.CurrentPage.Stroke;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
// RenderPage('r72.pdf', 72, 1) i RenderPage('r144.pdf', 144, 2)
// moraju dati bajt-identične content streamove stranice
Proverite operatore koji nose brojeve: Td, Tm, Tf, re, w i TJ nizove. Bajtovi na nivou fajla i dalje će se razlikovati u datumu kreiranja i /ID-ju, pa poredite streamove, ne cele fajlove. Neslaganje skoro uvek ukazuje na jednu od dve zamke iznad: stranicu promenjenu kroz Width, ili poentnu vrednost prosleđenu pravo u SetFont. Ako ste novi u samim crtaćim pozivima, počnite sa HotPDF TextOut propitkom za veličinu, stil i rotaciju, pa se vratite i prebacite Resolution kad vaš raspored čita UserWidth. Potpune API pojedinosti i probna preuzimanja su na stranici HotPDF Delphi PDF komponente