I HotPDF Component definierar THotPDF.Resolution ritningsenheten: varje X- och Y-koordinat, varje marginal, storleken som skickas till SetFont och resultaten av TextWidth och GetWideTextWidth mäts i 1/Resolution tum. THPDFPage.Width och Height följer den inte och stannar i punkter, så layoutgränser måste hämtas från de skrivskyddade UserWidth och UserHeight. Det vanliga skälet att röra Resolution är en portering: en rapportmotor som redan tänker i 1/96 eller 1/144 tum är lättare att flytta över när PDF-sidan talar samma enhet än när vare anropsplats får en konverteringsfaktor. Det fungerar bra, så länge du vet vilka tal som flyttade till den nya enheten och vilka som stannade kvar
Vad ändrar THotPDF.Resolution egentligen?
THotPDF.Resolution ändrar bara hur HotPDF läser de tal du skickar in; PDF:en den skriver är densamma. Settern är två rader: SetResolution lagrar värdet och sätter DocScale := Value / 72. Från och med då delar XProjection och YProjection varje koordinat med DocScale på vägen in i innehållsströmmen, och SetFont delar storleken på samma sätt innan den registreras. PDF:user space är som standard 1/72 tum (ISO 32000-1 §8.3.2.3), så vid standard-Resolution 72 är projektionen identiteten och vid 144 är en ritningsenhet en halv punkt. Ingen /UserUnit-post skrivs. Det sidattributet, tillagt i PDF 1.6, är en separat sak som HotPDF exponerar som THPDFPage.SetUserUnit. En detalj som tar dem som kommer från TextOut-tutorierna: sidkoordinater löper från övre vänstra hörnet med Y växande nedåt, eftersom YProjection beräknar MediaBox:ens topp minus den skalade Y, och det förblir sant vid varje Resolution
var
Pdf: THotPDF;
Page: THPDFPage;
Margin: Single;
Title: WideString;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'invoice.pdf';
Pdf.Resolution := 144; // 1 ritningsenhet = 1/144 tum
Pdf.BeginDoc;
Page := Pdf.CurrentPage; // A4: Width = 595, UserWidth = 1190
Margin := 144; // en tum i ritningsenheter
Page.SetFont('Arial', [fsBold], 28); // 28/144 tum, en font på 14 pt
Title := 'INVOICE 2026-0417';
// Högerställ mot sidkanten mätt i samma enhet
Page.TextOut(Page.UserWidth - Margin - Page.GetWideTextWidth(Title),
Margin, 0, Title);
Page.SetLineWidth(2); // en linje på 1 pt
Page.MoveTo(Margin, Margin + 48);
Page.LineTo(Page.UserWidth - Margin, Margin + 48);
Page.Stroke;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Varför avviker Page.Width från mina koordinater vid Resolution 144?
THPDFPage.Width och Height rapporterar sidan i punkter oavsett dokumentets Resolution, medan dina koordinater är i 1/Resolution tum, så vid 144 ser sidan hälften så bred ut som den är. En A4-sida läser Width = 595 och Height = 842 vid Resolution 72 och läser fortfarande 595 och 842 vid 144, där högerkanten egentligen ligger vid X = 1190. UserWidth och UserHeight, tillagda i v2.766.0, returnerar Width * DocScale, vilket är sidstorleken i den enhet du ritar i. Innan de fanns blandade biblioteket de två internt, och symptomen vid Resolution 144 var dramatiska: stycken bröts efter vartenda tecken, THPDFTable.Render knuffade varje rad till en ny sida, och både HTML-importören och XFA-flattenern ritade sitt innehåll på hälften, med det utjämnade formuläret trängt in i övre vänstra hörnet. Styckeslayout, tabellrendering, HTML-import, EMF-centrering, WMF-sidklippet och layoutdiagnostiken läser nu alla användarenhetsstorleken. Din egen layoutkod bör också göra det: allt som jämför mot en ritningskoordinat (en högermarginal, ett sidbrytningstest, en centreringsberäkning) hör hemma på UserWidth och UserHeight, aldrig på Width och Height
Fälla ett: att tilldela Width eller Height växlar sidan till punkter
Att sätta Page.Width eller Page.Height byter tyst sidan till UserDefined, och en UserDefined-sida ignorerar DocScale helt, så allt du ritar på den efteråt är i punkter, inte i 1/Resolution tum. Settern är gammal och tar punkter med flit, vilket är därför dess betydelse lämnades orörd. Projektionen för en UserDefined-sida är ren X + MinX, och SetFont lagrar storleken oförändrad. Vid Resolution 144 blir resultatet en sida vars innehåll plötsligt kommer ut dubbelt så stort som sidan före den. Biblioteket gjorde exakt detta misstag självt: styckets fortsättningssidor kopierade föregående sidas storlek genom Width, och varje översprängningssida växlade till punkter. De sidorna kopierar nu Size, Orientation och sidans Resolution i stället, och faller bara tillbaka till Width och Height när originalsidan redan var UserDefined
Tvä utvägar, beroende på vad du behöver. Om ett standardark duger, sätt Page.Size och Page.Orientation och fortsätt rita i din Resolution-enhet. Om du verkligen behöver en anpassad sidstorlek, acceptera att det är en punksida och rita i punkter; UserWidth är lika med Width där, så layoutkod som alltid läser UserWidth fortsätter fungera på båda sorters sidor. Enhetstestet låser detta: vid Resolution 144 rapporterar en A4-sida en UserWidth på 1190, men efter Width := 500 och Height := 400 rapporterar den 500 och 400. Inlästa sidor beter sig på samma sätt, för en sida återuppbyggd ur en befintlig PDF känner bara till sin MediaBox i punkter och ritar i punkter. Sidor som detta dokument skapat behåller sina egna enheter när du växlar bort och kommer tillbaka via CurrentPageNumber, vilket varit fallet sedan v2.766.26
Fälla två: varför kommer fontstorlekar ut på hälften?
En fontstorlek som började som punkter kommer ut på hälften vid Resolution 144 för SetFont behandlar sitt storleksargument som ritningsenheter och konverterar det till punkter innan det lagras. Internt lagrar SetFont ASize / DocScale * DPI i det aktuella fontobjektet, så det lagrade värdet är alltid punkter. Biblioteket snubblade på detta två gånger: fontfallbacken i WideTextOutBoxEx och styckets fortsättningssida räckte båda det lagrade punktvärdet tillbaka till SetFont, som skalade det en gång till och halverade texten. Din kod kan inte läsa den lagrade storleken, men samma bugg dyker upp närhelst ett punktvärde från något annat ställe når SetFont: en TFont.Size från ett VCL-formulär, en storlek i en rapportdefinition, en CSS-pt-längd. Konvertera den först, och ta med sidans egen Resolution och UserDefined-fallet i faktorn, som metafiluppspelningen gör när den spelar upp sidans Canvas (se hur HotPDF importerar EMF- och WMF-vektorgrafik för den vägen):
// Ritningsenheter per punkt på den aktuella sidan. Spegelar projektionen
// HotPDF använder: 1 på en sida storleksatt via Width/Height, i övrigt
// (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 är i punkter; SetFont förväntar sig ritningsenheter
Pdf.CurrentPage.SetFont(AnsiString(Font.Name), Font.Style,
Font.Size * UnitsPerPoint(Pdf));
end;
Biblioteket tillämpar samma regel på sina egna punktkonstanter. Den 12-punktsfont som varje ny sida börjar med multipliceras nu med den interna enheter-per-punkt-faktorn, så den är 12 punkter vid varje Resolution. DrawChart, vars marginaler, etikettstorlekar och linjebredder alla är hårdkodade punkter, körs nu med skalan tillfälligt satt till 1. Det som stannar i ritningsenheter, med flit, är publika parameterstandarder som DrawQRCode-modulstorleken och standardtabellfontstorleken: de är en del av API-kontraktet, så vid Resolution 144 betyder de hälften av vad de betyder vid 72. Om du storleksätter rapporter utifrån en mall täcker guiden om rapportutdata med teckensnitt och bilder i HotPDF var de värdena vanligtvis kommer ifrån
Hur verifierar du att en layout är Resolution-oberoende?
Den mest tillförlitliga kontrollen är en bytejämförelse: rendera samma sida vid Resolution 72 och igen vid 144 med varje koordinat och storlek fördubblad, och de okomprimerade innehållsströmmarna måste vara identiska. Båda körningarna landar på samma punktvärden efter projektionen, så varje skillnad är ett värde som hoppade över konverteringen. Så kontrollerar HotPDF-testsviten stycken, tabeller, HTML-import, XFA-flattening, bågar, metafiler och bilder. Samma teknik fungerar för din egen rapportkod med nästan ingen omgivning:
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äsbara innehållsströmmar
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)
// måste ge byte-identiska sidinnehållsströmmar
Kontrollera operatorerna som bär tal: Td, Tm, Tf, re, w och TJ-arrayerna. Filnivåbyte kommer fortfarande att skilja i skapandedatumet och /ID, så jämför strömmarna, inte hela filer. En avvikelse pekar nästan alltid på en av de två fällorna ovan: en sida som storleksändrats genom Width, eller ett punktvärde skickat rakt in i SetFont. Om du är ny inför ritningsanropen själva, börja med HotPDF TextOut-genomgången för storlek, stil och rotation, och kom tillbaka och växla Resolution när din layout läser UserWidth. Fullständiga API-detaljer och testnedladdningar finns på HotPDF Delphi PDF component-sidan