In HotPDF Component definieert THotPDF.Resolution de tekeneenheid: elke X- en Y-coördinaat, elke marge, de grootte die aan SetFont doorgegeven wordt, en de resultaten van TextWidth en GetWideTextWidth worden gemeten in 1/Resolution inch. THPDFPage.Width en Height volgen hem niet en blijven in punten, dus layoutgrenzen moeten komen van de read-only UserWidth en UserHeight. De gebruikelijke reden om Resolution aan te raken is een portering: een rapport-engine die al in 1/96 of 1/144 inch denkt verhuist makkelijker als de PDF-kant dezelfde eenheid spreekt dan wanneer elke call site een conversiefactor krijgt. Dat werkt goed, zolang u maar weet welke getallen naar de nieuwe eenheid zijn verhuisd en welke achterbleven
Wat verandert THotPDF.Resolution eigenlijk?
THotPDF.Resolution verandert alleen hoe HotPDF de getallen leest die u doorgeeft; de PDF die hij schrijft is dezelfde. De setter is twee regels: SetResolution bewaart de waarde en zet DocScale := Value / 72. Vanaf dat moment delen XProjection en YProjection elke coördinaat door DocScale op weg naar de content stream, en SetFont deelt de grootte op dezelfde manier voordat hij hem opslaat. PDF user space is standaard 1/72 inch (ISO 32000-1 §8.3.2.3), dus bij de default Resolution van 72 is de projectie de identiteit en bij 144 is één tekeneenheid een halve punt. Er wordt geen /UserUnit-entry geschreven. Dat pagina-attribuut, toegevoegd in PDF 1.6, is een apart iets dat HotPDF aanbiedt als THPDFPage.SetUserUnit. Eén detail dat mensen vangt die vanuit de TextOut-tutorials komen: pagina-coördinaten lopen vanaf de linkerbovenhoek met Y naar beneden groeiend, want YProjection berekent de MediaBox-top min de geschaalde Y, en dat blijft bij elke Resolution zo
var
Pdf: THotPDF;
Page: THPDFPage;
Margin: Single;
Title: WideString;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'invoice.pdf';
Pdf.Resolution := 144; // 1 tekeneenheid = 1/144 inch
Pdf.BeginDoc;
Page := Pdf.CurrentPage; // A4: Width = 595, UserWidth = 1190
Margin := 144; // één inch in tekeneenheden
Page.SetFont('Arial', [fsBold], 28); // 28/144 inch, een font van 14 pt
Title := 'INVOICE 2026-0417';
// Rechts uitlijnen op de paginarand gemeten in dezelfde eenheid
Page.TextOut(Page.UserWidth - Margin - Page.GetWideTextWidth(Title),
Margin, 0, Title);
Page.SetLineWidth(2); // lijn van 1 pt
Page.MoveTo(Margin, Margin + 48);
Page.LineTo(Page.UserWidth - Margin, Margin + 48);
Page.Stroke;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Waarom klopt Page.Width bij Resolution 144 niet met mijn coördinaten?
THPDFPage.Width en Height rapporteren de pagina in punten, wat de document-Resolution ook is, terwijl uw coördinaten in 1/Resolution inch zijn, dus bij 144 oogt de pagina half zo breed als hij werkelijk is. Een A4-pagina leest Width = 595 en Height = 842 bij Resolution 72 en leest bij 144 nog steeds 595 en 842, terwijl de rechterrand in werkelijkheid op X = 1190 ligt. UserWidth en UserHeight, toegevoegd in v2.766.0, geven Width * DocScale terug, de paginagrootte in de eenheid waarin u tekent. Vóór ze bestonden, mengde de library de twee intern door elkaar, en de symptomen bij Resolution 144 waren dramatisch: alinea's braken na elk teken af, THPDFTable.Render duwde elke rij naar een nieuwe pagina, en zowel de HTML-importeur als de XFA-flattener tekende zijn content op halve grootte, met het afgeplatte formulier in de linkerbovenhoek gedrongen. Alinealayout, tabelrendering, HTML-import, EMF-centering, de WMF-paginaclip en layoutdiagnostiek lezen nu allemaal de user-unit-grootte. Uw eigen layoutcode hoort dat ook te doen: alles wat tegen een tekencoördinaat vergelijkt (een rechtermarge, een paginabreak-test, een centeringberekening) hoort op UserWidth en UserHeight, nooit op Width en Height
Valkuil één: Width of Height toewijzen zet de pagina om naar punten
Page.Width of Page.Height zetten stilletjes de pagina om naar UserDefined, en een UserDefined-pagina negeert DocScale volledig, dus alles wat u er daarna op tekent is in punten, niet in 1/Resolution inch. De setter is oud en neemt punten bij ontwerp, daarom is zijn betekenis met rust gelaten. De projectie voor een UserDefined-pagina is kaal X + MinX, en SetFont slaat de grootte ongewijzigd op. Bij Resolution 144 is het resultaat een pagina waarvan de content plotseling twee keer zo groot uitkomt als de pagina ervoor. De library maakte precies deze fout zelf: vervolgpagina's van alinea's kopieerden vroeger de grootte van de vorige pagina via Width, en elke overflow-pagina schoof door naar punten. Die pagina's kopiëren nu in plaats daarvan Size, Orientation en de pagina-Resolution, en vallen alleen terug op Width en Height als de oorspronkelijke pagina al UserDefined was
Twee uitwegen, afhankelijk van wat u nodig heeft. Als een standaardvel volstaat, zet Page.Size en Page.Orientation en blijf tekenen in uw Resolution-eenheid. Als u echt een eigen paginaformaat nodig heeft, accepteer dan dat het een puntenpagina is en teken in punten; UserWidth is daar gelijk aan Width, dus layoutcode die altijd UserWidth leest blijft op beide soorten pagina's werken. De unittest zet dit vast: bij Resolution 144 rapporteert een A4-pagina een UserWidth van 1190, maar na Width := 500 en Height := 400 rapporteert hij 500 en 400. Geladen pagina's gedragen zich hetzelfde, want een pagina die uit een bestaande PDF is opgebouwd kent alleen zijn MediaBox in punten en tekent in punten. Pagina's die dit document zelf maakte houden hun eigen eenheden aan als u wegswitcht en via CurrentPageNumber terugkomt, en dat is zo sinds v2.766.26
Valkuil twee: waarom komen fontgroottes op de halve grootte uit?
Een fontgrootte die als punten begon, komt bij Resolution 144 op de halve grootte uit, omdat SetFont zijn grootte-argument als tekeneenheden behandelt en hem naar punten omzet voordat hij hem opslaat. Intern slaat SetFont ASize / DocScale * DPI op in het huidige fontobject, dus de opgeslagen waarde is altijd in punten. De library struikelde hier twee keer over: de fontfallback in WideTextOutBoxEx en de alinea-vervolgpagina gaven die opgeslagen puntwaarde allebei terug aan SetFont, die hem een tweede keer schaalde en de tekst halveerde. Uw code kan de opgeslagen grootte niet lezen, maar dezelfde bug verschijnt zodra een puntwaarde van ergens anders SetFont bereikt: een TFont.Size uit een VCL-formulier, een grootte in een rapportdefinitie, een CSS-pt-lengte. Zet hem eerst om, en neem de eigen Resolution van de pagina en het UserDefined-geval op in de factor, zoals metafile playback doet wanneer hij de paginanvas afspeelt (zie hoe HotPDF EMF- en WMF-vectorafbeeldingen importeert voor dat pad):
// Tekeneenheden per punt op de huidige pagina. Spiegelt de projectie
// die HotPDF gebruikt: 1 op een pagina van formaat via Width/Height, anders
// (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 is in punten; SetFont verwacht tekeneenheden
Pdf.CurrentPage.SetFont(AnsiString(Font.Name), Font.Style,
Font.Size * UnitsPerPoint(Pdf));
end;
De library past dezelfde regel toe op zijn eigen puntconstanten. Het font van 12 punten waarmee elke nieuwe pagina begint, wordt nu vermenigvuldigd met de interne eenheden-per-punt-factor, dus het is 12 punten bij elke Resolution. DrawChart, waarvan marges, labelgroottes en lijnbreedtes allemaal hardgecodeerde punten zijn, draait nu met de schaal tijdelijk op 1. Wat bewust in tekeneenheden blijft, zijn publieke parameterdefaults zoals de DrawQRCode-modulegrootte en de standaardtabelfontgrootte: ze horen bij het API-contract, dus bij Resolution 144 betekenen ze de helft van wat ze bij 72 betekenen. Als u rapporten van een template voorziet van maten, behandelt de gids over rapportuitvoer met fonts en afbeeldingen in HotPDF waar die waarden meestal vandaan komen
Hoe controleert u dat een layout Resolution-onafhankelijk is?
De betrouwbaarste controle is een bytevergelijking: render dezelfde pagina bij Resolution 72 en opnieuw bij 144 met elke coördinaat en grootte verdubbeld, en de ongecomprimeerde content streams moeten identiek zijn. Beide runs komen na projectie op dezelfde puntwaarden uit, dus elk verschil is een waarde die de conversie overgeslagen heeft. Zo controleert de HotPDF-testsuite alinea's, tabellen, HTML-import, XFA-flattening, bogen, metafiles en afbeeldingen. Dezelfde techniek werkt voor uw eigen rapportcode met bijna geen testopstelling:
procedure RenderPage(const FileName: string; Res: Integer; K: Single);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.AutoLaunch := False;
Pdf.Compression := cmNone; // leesbare 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) en RenderPage('r144.pdf', 144, 2)
// moeten byte-identieke paginastreams opleveren
Controleer de operators die getallen meedragen: Td, Tm, Tf, re, w en de TJ-arrays. Bytes op bestandsniveau verschillen nog steeds in de aanmaakdatum en de /ID, dus vergelijk de streams, niet hele bestanden. Een mismatch wijst bijna altijd naar een van de twee valkuilen hierboven: een pagina die via Width van grootte is veranderd, of een puntwaarde die rechtstreeks in SetFont is gestopt. Als u nieuw bent in de tekenaanroepen zelf, begin dan met de HotPDF TextOut-walkthrough voor grootte, stijl en rotatie, en kom daarna terug om de Resolution om te zetten zodra uw layout UserWidth leest. De volledige API-details en trial-downloads staan op de HotPDF Delphi PDF component-pagina