Teknisk artikkel

HotPDF Resolution i Delphi: tegneenheter og UserWidth

I HotPDF Component definerer THotPDF.Resolution tegneenheten: hver X- og Y-koordinat, hver margin, størrelsen sendt til SetFont og resultatene av TextWidth og GetWideTextWidth måles i 1/Resolution tomme. THPDFPage.Width og Height følger den ikke og forblir i punkter, så layoutgrenser må komme fra de skrivebeskyttede UserWidth og UserHeight. Den vanlige grunnen til å røre Resolution er en port: en rapportmotor som allerede tenker i 1/96 eller 1/144 tomme er lettere å flytte over når PDF-siden snakker samme enhet enn når hvert kallsted får en konverteringsfaktor. Det fungerer godt, så lenge du vet hvilke tall som flyttet til den nye enheten og hvilke som ble igjen

Hva endrer THotPDF.Resolution egentlig?

THotPDF.Resolution endrer bare hvordan HotPDF leser tallene du sender inn; PDF-en den skriver, er den samme. Setteren er to linjer: SetResolution lagrer verdien og setter DocScale := Value / 72. Fra da av deler XProjection og YProjection hver koordinat med DocScale på vei inn i content stream-en, og SetFont deler størrelsen på samme måte før den lagres. PDF user space er som standard 1/72 tomme (ISO 32000-1 §8.3.2.3), så ved standard Resolution på 72 er projeksjonen identiteten, og ved 144 er én tegneenhet en halv punkt. Ingen /UserUnit-oppføring skrives. Den sideattributten, lagt til i PDF 1.6, er en egen ting HotPDF eksponerer som THPDFPage.SetUserUnit. Én detalj som fanger folk som kommer fra TextOut-veiledningene: sidekoordinater løper fra øvre venstre hjørne med Y voksende nedover, for YProjection beregner MediaBox-topp minus den skalerte Y-en, og det holder ved enhver Resolution

Hvordan THotPDF.Resolution definerer tegneenheten i Delphi: setteren lagrer DocScale som Resolution delt på 72, så deler XProjection, YProjection og SetFont hver koordinat og størrelse på vei inn i content stream-en, slik at Resolution 72 er en identitetsmapping og Resolution 144 gjør én tegneenhet til en halv punkt mens siden fortsatt løper fra øvre venstre med Y nedover
Ingenting i outputfilen flytter seg — bare betydningen av tallene du sender inn, endres, og det er derfor samme content stream vises ved 72 og 144
var
  Pdf: THotPDF;
  Page: THPDFPage;
  Margin: Single;
  Title: WideString;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'invoice.pdf';
    Pdf.Resolution := 144;               // 1 tegneenhet = 1/144 tomme
    Pdf.BeginDoc;
    Page := Pdf.CurrentPage;             // A4: Width = 595, UserWidth = 1190
    Margin := 144;                       // én tomme i tegneenheter
    Page.SetFont('Arial', [fsBold], 28); // 28/144 tomme, en 14 pt-font
    Title := 'INVOICE 2026-0417';
    // Høyrejuster mot sidekanten målt i samme enhet
    Page.TextOut(Page.UserWidth - Margin - Page.GetWideTextWidth(Title),
      Margin, 0, Title);
    Page.SetLineWidth(2);                // 1 pkt-strek
    Page.MoveTo(Margin, Margin + 48);
    Page.LineTo(Page.UserWidth - Margin, Margin + 48);
    Page.Stroke;
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Hvorfor strider Page.Width med koordinatene mine ved Resolution 144?

THPDFPage.Width og Height rapporterer siden i punkter uansett hva dokumentets Resolution er, mens koordinatene dine er i 1/Resolution tomme, så ved 144 ser siden halvparten så bred ut som den er. En A4-side leser Width = 595 og Height = 842 ved Resolution 72 og leser fortsatt 595 og 842 ved 144, der høyre kant faktisk ligger ved X = 1190. UserWidth og UserHeight, lagt til i v2.766.0, returnerer Width * DocScale, som er sidestørrelsen i enheten du tegner med. Før de fantes, blandet biblioteket de to internt, og symptomene ved Resolution 144 var dramatiske: avsnitt brytet etter hvert tegn, THPDFTable.Render dyttet hver rad over på en ny side, og både HTML-importøren og XFA-flatteneren tegnet innholdet sitt i halv størrelse, det flattede skjemaet klemt sammen i øvre venstre hjørne. Avsnittlayout, tabellrendering, HTML-import, EMF-sentrering, WMF-sideklippet og layoutdiagnostikk leser nå alle brukerenhetsstørrelsen. Din egen layoutkode bør gjøre det samme: alt som sammenlignes mot en tegnecoordinat (en høyre margin, en sideskift-test, en sentreringsberegning) hører hjemme på UserWidth og UserHeight, aldri på Width og Height

Felle én: å tilordne Width eller Height bytter siden til punkter

Å sette Page.Width eller Page.Height bytter stille siden til UserDefined, og en UserDefined-side ignorerer DocScale helt, så alt du tegner på den etterpå er i punkter, ikke i 1/Resolution tomme. Setteren er gammel og tar punkter med hensikt, og det er derfor betydningen dens ble latt i fred. Projeksjonen for en UserDefined-side er ren X + MinX, og SetFont lagrer størrelsen uendret. Ved Resolution 144 er resultatet en side hvis innhold plutselig kommer ut dobbelt så stort som siden før. Biblioteket gjorde nøyaktig denne feilen selv: avsnittsfortsettelsessider pleide å kopiere forrige sides størrelse gjennom Width, og hver overflyt-side byttet til punkter. De sidene kopierer nå Size, Orientation og side-Resolution i stedet og faller bare tilbake til Width og Height når originalsiden allerede var UserDefined

To veier ut, avhengig av hva du trenger. Hvis et standardark holder, sett Page.Size og Page.Orientation og fortsett å tegne i Resolution-enheten din. Hvis du virkelig trenger en egendefinert sidestørrelse, aksepter at det er en punktside og tegn i punkter; UserWidth er lik Width der, så layoutkode som alltid leser UserWidth fortsetter å fungere på begge sidetypene. Unit-testen fester dette: ved Resolution 144 rapporterer en A4-side en UserWidth på 1190, men etter Width := 500 og Height := 400 rapporterer den 500 og 400. Lastede sider oppfører seg likt, for en side gjenoppbygd fra en eksisterende PDF kjenner bare sin MediaBox i punkter og tegner i punkter. Sider dette dokumentet opprettet, beholder egne enheter når du bytter bort og tilbake gjennom CurrentPageNumber, noe som har vært tilfelle siden v2.766.26

Hvorfor Page.Width strider mot koordinatene dine ved Resolution 144 i HotPDF: Width og Height forblir i punkter mens tegning bruker 1/144 tomme, så en A4-side leser 595 men høyre kanten ligger ved UserWidth 1190, og å tilordne Width bytter siden til UserDefined, som ignorerer DocScale, så avsnitt bryter per tegn, tabeller bryter per rad og SetFont-størrelser halveres
Alt som sammenlignes mot en tegnecoordinat hører hjemme på UserWidth og UserHeight — på en UserDefined-punktside faller de to sammen, så samme layoutkode overlever begge

Felle to: hvorfor kommer fontstørrelser ut i halv størrelse?

En fontstørrelse som startet som punkter kommer ut i halv størrelse ved Resolution 144 fordi SetFont behandler størrelsesargumentet sitt som tegneenheter og konverterer det til punkter før lagring. Internt lagrer SetFont ASize / DocScale * DPI i det gjeldende fontobjektet, så den lagrede verdien er alltid punkter. Biblioteket snublet i dette to ganger: font-fallbacken i WideTextOutBoxEx og avsnittsfortsettelsessiden ga begge den lagrede punktverdien tilbake til SetFont, som skalerte den en gang til og halverte teksten. Koden din kan ikke lese den lagrede størrelsen, men samme bug dukker opp hver gang en punktverdi fra et annet sted når SetFont: en TFont.Size fra et VCL-skjema, en størrelse i en rapportdefinisjon, en CSS-pt-lengde. Konverter den først, og ta med sidens egen Resolution og UserDefined-tilfellet i faktoren, slik metafile-avspilling gjør når den spiller av side-Canvas-en (se hvordan HotPDF importerer EMF- og WMF-vektorgrafikk for den stien):

// Tegneenheter per punkt på gjeldende side. Speiler projeksjonen
// HotPDF bruker: 1 på en side dimensjonert gjennom 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 punkter; SetFont forventer tegneenheter
  Pdf.CurrentPage.SetFont(AnsiString(Font.Name), Font.Style,
    Font.Size * UnitsPerPoint(Pdf));
end;

Biblioteket anvender samme regel på sine egne punktkonstanter. 12-punktsfonten som hver ny side starter med, multipliseres nå med den interne enheter-per-punkt-faktoren, så den er 12 punkter ved enhver Resolution. DrawChart, hvis marginer, etikettstørrelser og linjebredder alle er hardkodede punkter, kjører nå med skalaen midlertidig satt til 1. Det som med vilje blir i tegneenheter, er offentlige parameterstandarder som DrawQRCode-modulstørrelsen og standard tabellfontstørrelse: de er en del av API-kontrakten, så ved Resolution 144 betyr de halvparten av det de betyr ved 72. Hvis du dimensjonerer rapporter fra en mal, dekker guiden om rapportoutput med fonter og bilder i HotPDF hvor de verdiene vanligvis kommer fra

Hvordan verifiserer du at et oppsett er Resolution-uavhengig?

Den mest pålitelige sjekken er en bytesammenligning: render samme side ved Resolution 72 og igjen ved 144 med hver koordinat og størrelse doblet, og de ukomprimerte content streams må være identiske. Begge kjøringene lander på de samme punktverdiene etter projeksjon, så enhver forskjell er en verdi som hoppet over konverteringen. Slik sjekker HotPDF-testsuiten avsnitt, tabeller, HTML-import, XFA-flatting, buer, metafiler og bilder. Samme teknikk fungerer for din egen rapportkode med nesten ingen rigg:

procedure RenderPage(const FileName: string; Res: Integer; K: Single);
var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.AutoLaunch := False;
    Pdf.Compression := cmNone;       // lesbare 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) og RenderPage('r144.pdf', 144, 2)
// må produsere byteidentiske side-content streams
Hvordan verifisere Resolution-uavhengighet i HotPDF Delphi-kode: render det identiske oppsettet to ganger, én gang ved Resolution 72 med skala 1 og én gang ved 144 med hver koordinat og fontstørrelse doblet, og krev deretter byteidentiske ukomprimerte content streams — et avvik peker på en side byttet til UserDefined gjennom Width eller en ukonvertert punktverdi som når SetFont
Begge kjøringene lander på de samme punktverdiene etter projeksjon, så enhver forskjell er et tall som hoppet over konverteringen — samme rigg HotPDF-testsuiten støtter seg på

Sjekk operatorene som bærer tall: Td, Tm, Tf, re, w og TJ-arrayene. Filnivå-byte vil fortsatt avvike i opprettelsesdatoen og /ID-en, så sammenlign strømmene, ikke hele filer. Et avvik peker nesten alltid på én av de to fellene over: en side som ble gjenstørrelsesatt gjennom Width, eller en punktverdi sendt rett inn i SetFont. Hvis du er ny til tegnekallene selv, start med HotPDF TextOut-gjennomgangen for størrelse, stil og rotasjon, kom så tilbake og bytt Resolution når oppsettet ditt leser UserWidth. Full API-detaljer og prøvenedlastinger ligger på HotPDF Delphi PDF-komponentsiden