I HotPDF Component definerer THotPDF.Resolution tegneenheden: hver X- og Y-koordinat, hver margin, størrelsen givet til SetFont og resultaterne af TextWidth og GetWideTextWidth måles i 1/Resolution tomme. THPDFPage.Width og Height følger den ikke og forbliver i points, så layoutgrænser skal komme fra de skrivebeskyttede UserWidth og UserHeight. Den sædvanlige grund til at røre Resolution er en portering: en rapportmotor, der allerede tænker i 1/96 eller 1/144 tomme, er lettere at flytte over, når PDF-siden taler samme enhed, end når hvert kaldsted får en konverteringsfaktor. Det virker fint, så længe du ved, hvilke tal der flyttede til den nye enhed, og hvilke der blev tilbage
Hvad ændrer THotPDF.Resolution egentlig?
THotPDF.Resolution ændrer kun, hvordan HotPDF læser de tal, du giver den; den PDF, den skriver, er den samme. Setteren er to linjer: SetResolution gemmer værdien og sætter DocScale := Value / 72. Derefter deler XProjection og YProjection hver koordinat med DocScale på vej ind i content streamen, og SetFont deler størrelsen på samme måde, inden den gemmes. PDF user space er som default 1/72 tomme (ISO 32000-1 §8.3.2.3), så ved default Resolution på 72 er projektionen identitetsafbildningen, og ved 144 er én tegneenhed en halv point. Intet /UserUnit-indslag skrives. Den sideattribut, tilføjet i PDF 1.6, er noget separat, som HotPDF eksponerer som THPDFPage.SetUserUnit. Én detalje, der fanger folk, der kommer fra TextOut-tutorials: sidekoordinater løber fra øverste venstre hjørne med Y voksende nedad, fordi YProjection beregner MediaBox-top minus den skalerede Y, og det gælder ved hver Resolution
var
Pdf: THotPDF;
Page: THPDFPage;
Margin: Single;
Title: WideString;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'invoice.pdf';
Pdf.Resolution := 144; // 1 tegneenhed = 1/144 tomme
Pdf.BeginDoc;
Page := Pdf.CurrentPage; // A4: Width = 595, UserWidth = 1190
Margin := 144; // én tomme i tegneenheder
Page.SetFont('Arial', [fsBold], 28); // 28/144 tomme, en 14 pt font
Title := 'INVOICE 2026-0417';
// Højrejustér mod sidekanten målt i samme enhed
Page.TextOut(Page.UserWidth - Margin - Page.GetWideTextWidth(Title),
Margin, 0, Title);
Page.SetLineWidth(2); // 1 pt streg
Page.MoveTo(Margin, Margin + 48);
Page.LineTo(Page.UserWidth - Margin, Margin + 48);
Page.Stroke;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Hvorfor er Page.Width uenig med mine koordinater ved Resolution 144?
THPDFPage.Width og Height rapporterer siden i points uanset dokumentets Resolution, mens dine koordinater er i 1/Resolution tomme, så ved 144 ser siden ud til at være halvt så bred, som den er. En A4-side viser Width = 595 og Height = 842 ved Resolution 72 og viser stadig 595 og 842 ved 144, hvor højrekanten faktisk ligger ved X = 1190. UserWidth og UserHeight, tilføjet i v2.766.0, returnerer Width * DocScale, hvilket er sidestørrelsen i den enhed, du tegner i. Før de fandtes blandede biblioteket de to internt, og symptomerne ved Resolution 144 var dramatiske: afsnit brød efter hvert tegn, THPDFTable.Render skubbede hver række på en ny side, og både HTML-importøren og XFA-flatteneren tegnede deres content i halv størrelse, den udjævnede form trængt ind i øverste venstre hjørne. Afsnitslayout, tabel-rendering, HTML-import, EMF-centering, WMF-sideklippet og layoutdiagnostik læser nu alle brugerenheds-størrelsen. Din egen layoutkode bør også: alt, der sammenlignes med en tegnekoordinat (en højre margin, et sideskift-tjek, en centreringsberegning), hører hjemme på UserWidth og UserHeight, aldrig på Width og Height
Fælde én: at tildele Width eller Height skifter siden til points
Sætter man Page.Width eller Page.Height, skifter siden i stilhed til UserDefined, og en UserDefined-side ignorerer DocScale fuldstændigt, så alt, hvad du tegner på den bagefter, er i points, ikke i 1/Resolution tomme. Setteren er gammel og tager points af design, hvilket er grunden til, at dens betydning blev efterladt i fred. Projektionen for en UserDefined-side er ren X + MinX, og SetFont gemmer størrelsen uændret. Ved Resolution 144 er resultatet en side, hvis content pludselig kommer ud dobbelt så stor som sidens før. Biblioteket begik selv præcis denne fejl: afsnit-fortsættelsessider kopierede tidligere forrige sides størrelse gennem Width, og hver overflow-side skiftede til points. De sider kopierer nu Size, Orientation og side-Resolutionen i stedet og falder kun tilbage til Width og Height, når den oprindelige side allerede var UserDefined
To veje ud, afhængigt af hvad du har brug for. Hvis et standardark duer, så sæt Page.Size og Page.Orientation og bliv ved at tegne i din Resolution-enhed. Hvis du virkelig har brug for en brugerdefineret sidestørrelse, så acceptér, at det er en point-side, og tegn i points; UserWidth svarer til Width der, så layoutkode, der altid læser UserWidth, fortsætter med at virke på begge slags sider. Unit-testen låser det fast: ved Resolution 144 rapporterer en A4-side en UserWidth på 1190, men efter Width := 500 og Height := 400 rapporterer den 500 og 400. Indlæste sider opfører sig på samme måde, for en side genopbygget fra en eksisterende PDF kender kun sin MediaBox i points og tegner i points. Sider, som dokumentet selv har oprettet, beholder deres egne enheder, når du skifter væk og kommer tilbage gennem CurrentPageNumber, hvilket har været tilfældet siden v2.766.26
Fælde to: hvorfor kommer fontstørrelser ud i halv størrelse?
En fontstørrelse, der startede som points, kommer ud i halv størrelse ved Resolution 144, fordi SetFont behandler sit størrelsesargument som tegneenheder og konverterer det til points, inden det gemmes. Internt gemmer SetFont ASize / DocScale * DPI i det aktuelle font-objekt, så den gemte værdi er altid points. Biblioteket snublede over dette to gange: font-fallbacken i WideTextOutBoxEx og afsnit-fortsættelsessiden gav begge den gemte point-værdi tilbage til SetFont, som skalerede den en gang til og halverede teksten. Din kode kan ikke læse den gemte størrelse, men samme fejl dukker op, hver gang en point-værdi et sted fra når SetFont: en TFont.Size fra en VCL-formular, en størrelse i en rapportdefinition, en CSS-pt-længde. Konvertér den først, og tag sidens egen Resolution og UserDefined-tilfældet med i faktoren, som metafile-afspilningen gør, når den afspiller sidens Canvas (se hvordan HotPDF importerer EMF- og WMF-vektorgrafik for den sti):
// Tegneenheder pr. point på den aktuelle side. Spejler projektionen
// HotPDF bruger: 1 på en side størrelsessat gennem Width/Height, ellers
// (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 er i points; SetFont forventer tegneenheder
Pdf.CurrentPage.SetFont(AnsiString(Font.Name), Font.Style,
Font.Size * UnitsPerPoint(Pdf));
end;
Biblioteket anvender samme regel på sine egne point-konstanter. Den 12 points-font, som hver ny side starter med, ganges nu med den interne enheder-pr-point-faktor, så den er 12 points ved enhver Resolution. DrawChart, hvis margins, label-størrelser og linjebredder alle er hårdkodede points, kører nu med skalaen midlertidigt sat til 1. Det, der med vilje forbliver i tegneenheder, er offentlige parameter-defaults som DrawQRCode-modulstørrelsen og standard tabelfont-størrelsen: de er en del af API-kontrakten, så ved Resolution 144 betyder de halvdelen af, hvad de betyder ved 72. Hvis du sætter størrelse på rapporter ud fra en skabelon, dækker guiden om rapportoutput med fonts og billeder i HotPDF, hvor de værdier normalt kommer fra
Hvordan verificerer du, at et layout er Resolution-uafhængigt?
Det mest pålidelige tjek er en byte-sammenligning: render samme side ved Resolution 72 og igen ved 144 med hver koordinat og størrelse fordoblet, og de ukomprimerede content streams skal være identiske. Begge kørsler lander på samme point-værdier efter projektionen, så enhver forskel er en værdi, der sprang konverteringen over. Sådan tjekker HotPDFs test-suite afsnit, tabeller, HTML-import, XFA-udjævning, buer, metafiler og billeder. Samme teknik virker for din egen rapportkode med næsten intet harness:
procedure RenderPage(const FileName: string; Res: Integer; K: Single);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.AutoLaunch := False;
Pdf.Compression := cmNone; // læselige content streams
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)
// skal give byte-identiske side-content streams
Tjek de operatorer, der bærer tal: Td, Tm, Tf, re, w og TJ-arrayene. Filniveau-bytes vil stadig differere i oprettelsesdatoen og /ID, så sammenlign streams, ikke hele filer. Et mismatch peger næsten altid på én af de to fælder ovenfor: en side, der fik ændret størrelse gennem Width, eller en point-værdi givet direkte til SetFont. Hvis du er ny til tegnekaldene selv, så start med HotPDF TextOut-gennemgangen for størrelse, stil og rotation, kom derefter tilbage og skift Resolutionen, så snart dit layout læser UserWidth. Fuld API-detaljer og trial-downloads ligger på HotPDF Delphi PDF component-siden