U HotPDF Component THotPDF.Resolution definira crtaću jedinicu: svaka X i Y koordinata, svaka margina, veličina predana SetFontu i rezultati TextWidtha i GetWideTextWidtha mjere se u 1/Resolution inča. THPDFPage.Width i Height ne slijede je i ostaju u točkama, pa granice rasporeda moraju doći iz read-only UserWidtha i UserHeighta. Uobičajen razlog za diranje Resolutiona jest port: engine izvještaja koji već misli u 1/96 ili 1/144 inča lakše se preseli kad PDF strana govori istu jedinicu, nego kad svaki call site dobije faktor pretvorbe. To radi dobro, dok god znate koji su se brojevi preselili u novu jedinicu, a koji su ostali
Što THotPDF.Resolution zapravo mijenja?
THotPDF.Resolution mijenja samo način na koji HotPDF čita brojeve koje predate; PDF koji zapiše isti je. Setter su dva retka: SetResolution pohrani vrijednost i postavi DocScale := Value / 72. Od tada XProjection i YProjection dijele svaku koordinatu s DocScaleom na putu u content stream, a SetFont dijeli veličinu isto prije nego je zabilježi. PDF user space zadan je na 1/72 inča (ISO 32000-1 §8.3.2.3), pa na zadanom Resolutionu 72 projekcija je identitet, a na 144 jedna crtaća jedinica je pola točke. Nijedan unos /UserUnit ne zapisuje se. To svojstvo stranice, dodano u PDF 1.6, zasebna je stvar koju HotPDF izlaže kao THPDFPage.SetUserUnit. Jedan detalj koji uhvati ljude koji dolaze s TextOut vodiča: koordinate stranice teku od gornjeg lijevog kuta s Y koji raste prema dolje, jer YProjection računa vrh MediaBoxa minus skalirani Y, i to vrijedi na 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';
// Poravnajte desno uz rub stranice izmjeren 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 s mojim koordinatama na Resolutionu 144?
THPDFPage.Width i Height javljaju stranicu u točkama ma što dokumentni Resolution bio, dok su vaše koordinate u 1/Resolution inča, pa na 144 stranica izgleda upola uža nego što jest. A4 stranica čita Width = 595 i Height = 842 na Resolutionu 72 i i dalje čita 595 i 842 na 144, gdje je desni rub zapravo na X = 1190. UserWidth i UserHeight, dodani u v2.766.0, vraćaju Width * DocScale, što je veličina stranice u jedinici kojom crtate. Prije njih biblioteka je interno miješala dvoje, i simptomi na Resolutionu 144 bili su dramatični: odlomci su se lomili iza svakog znaka, THPDFTable.Render gurao je svaki redak na novu stranicu, i i HTML uvoznik i XFA flattener crtali su sadržaj upola manje, spljoštena forma ugurana u gornji lijevi kut. Raspored odlomaka, renderiranje tablica, HTML uvoz, EMF centriranje, WMF clip stranice i dijagnostika rasporeda sada svi čitaju veličinu u user jedinicama. I vaš rasporedni kod treba isto: sve što uspoređuje s crtaćom koordinatom (desna margina, test prijeloma stranice, izračun centriranja) pripada na UserWidth i UserHeight, nikad na Width i Height
Zamka prva: dodjela Width ili Height prebacuje stranicu na točke
Postavljanje Page.Width ili Page.Height tiho prebaci stranicu na UserDefined, a UserDefined stranica potpuno ignorira DocScale, pa je sve što na nju poslije nacrtate u točkama, ne u 1/Resolution inča. Setter je star i po dizajnu prima točke, zato mu je značenje ostavljeno na miru. Projekcija za UserDefined stranicu je puki X + MinX, a SetFont pohranjuje veličinu nepromijenjenu. Na Resolutionu 144 rezultat je stranica čiji sadržaj odjednom izađe dvostruko veći nego na stranici prije nje. Biblioteka je napravila upravo ovu pogrešku sama: stranice nastavka odlomaka kopirale su veličinu prethodne stranice kroz Width, i svaka overflow stranica prebacivala se na točke. Te stranice sada kopiraju Size, Orientation i Resolution stranice, i vraćaju se na Width i Height samo kad je izvorna stranica već bila UserDefined
Dva izlaza, ovisno o tome što trebate. Ako će standardni list poslužiti, postavite Page.Size i Page.Orientation i nastavite crtati u svojoj Resolution jedinici. Ako stvarno trebate vlastitu veličinu stranice, prihvatite da je to stranica u točkama i crtajte u točkama; UserWidth je tamo jednak Widthu, pa rasporedni kod koji uvijek čita UserWidth i dalje radi na objema vrstama stranica. Unit test to pribija: na Resolutionu 144 A4 stranica javlja UserWidth od 1190, ali nakon Width := 500 i Height := 400 javlja 500 i 400. Učitane stranice ponašaju se isto, jer stranica ponovno izgrađena iz postojećeg PDF-a zna samo svoj MediaBox u točkama i crta u točkama. Stranice koje je ovaj dokument stvorio čuvaju vlastite jedinice kad prebacite van i vratite se kroz CurrentPageNumber, što je slučaj od v2.766.26
Zamka druga: zašto veličine fontova izlaze upola manje?
Veličina fonta koja je počela kao točke izlazi upola manja na Resolutionu 144 jer SetFont svoj argument veličine tretira kao crtaće jedinice i pretvara ga u točke prije pohrane. Iznutra SetFont pohranjuje ASize / DocScale * DPI u trenutni font objekt, pa je pohranjena vrijednost uvijek točke. Biblioteka je o tome sapela dvaput: fontovski fallback u WideTextOutBoxExu i stranica nastavka odlomaka oboje su vratili tu pohranjenu točkovnu vrijednost u SetFont, koji ju je skalirao drugi put i upolaio tekst. Vaš kod ne može pročitati pohranjenu veličinu, ali isti bug iskoči svaki put kad točkovna vrijednost odnekud drugamo stigne do SetFonta: TFont.Size iz VCL forme, veličina u definiciji izvještaja, CSS pt duljina. Pretvorite je najprije, i u faktor uključite vlastiti Resolution stranice i slučaj UserDefined, kako čini playback metafila kad ponavlja Canvas stranice (vidite kako HotPDF uvozi EMF i WMF vektorsku grafiku za taj put):
// Crtaće jedinice po točki na trenutnoj stranici. Ogledala projekciju
// koju HotPDF koristi: 1 na stranici dimenzioniranoj 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 točkama; SetFont očekuje crtaće jedinice
Pdf.CurrentPage.SetFont(AnsiString(Font.Name), Font.Style,
Font.Size * UnitsPerPoint(Pdf));
end;
Biblioteka primjenjuje isto pravilo na vlastite točkovne konstante. Font od 12 točaka s kojim kreće svaka nova stranica sada se množi internim faktorom jedinica-po-točki, pa je 12 točaka na svakom Resolutionu. DrawChart, čije su margine, veličine oznaka i debljine linija sve hardkodirane točke, sada radi sa skalom privremeno postavljenom na 1. Ono što ostaje u crtaćim jedinicama, namjerno, javni su zadani parametri poput veličine modula DrawQRCodea i zadanog fonta tablice: dio su API ugovora, pa na Resolutionu 144 znače polovicu onoga što znače na 72. Ako dimenzionirate izvještaje iz predloška, vodič izlazu izvještaja s fontovima i slikama u HotPDF-u pokriva odakle te vrijednosti obično dolaze
Kako provjerite da je raspored neovisan o Resolutionu?
Najpouzdanija je provjera usporedba bajtova: renderirajte istu stranicu na Resolutionu 72, pa opet na 144 s udvostručenom svakom koordinatom i veličinom, i nekompresirani se content streamovi moraju poklapati. Oba runa slijeću na iste točkovne vrijednosti iza projekcije, pa je svaka razlika vrijednost koja je preskočila pretvorbu. Tako test suite HotPDF-a provjerava odlomke, tablice, HTML uvoz, XFA spljoštavanje, lukove, metafile i slike. Ista tehnika radi za vaš vlastiti izvještajni kod uz gotovo nikakav testni 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) and RenderPage('r144.pdf', 144, 2)
// mora proizvesti bajtno identične content streamove stranice
Provjerite operatore koji nose brojeve: Td, Tm, Tf, re, w i polja TJ. Bajtovi na razini datoteke i dalje će se razlikovati u datumu stvaranja i /IDu, pa uspoređujte streamove, ne cijele datoteke. Nepoklapanje gotovo uvijek upućuje na jednu od dviju zamki gore: stranicu promijenjene veličine kroz Width ili točkovnu vrijednost predanu ravno u SetFont. Ako ste novi u samim crtaćim pozivima, počnite s HotPDF TextOut proputovanjem za veličinu, stil i rotaciju, pa se vratite i prebacite Resolution jednom kad vaš raspored čita UserWidth. Potpune API pojedinosti i trial preuzimanja su na stranici HotPDF Delphi PDF komponente