Teknisk artikkel

Rendring av PDF-gradienter i Delphi: Aksial og radial

En PDF med en Word- eller Excel-aktig gradient-bakgrunn i toppteksten, rendret til et bilde, pleide tidligere å komme tilbake med et tomt hvitt rektangel der fargeblandingen skulle ha vært. HotPDFs siderendrer respekterer nå PDFs sh (shading)-operator for Type 2 aksiale og Type 3 radiale gradienter: den evaluerer skyggeleggingsfunksjonen langs gradientaksen og tegner fargeblandingen, der tidligere versjoner i det stille hoppet over operatoren og lot området forbli papirhvitt. De to skyggeleggingstypene som dekker nesten alle kontordokumentgradienter er inkludert; nettingtyper (mesh types) er ikke det, og denne artikkelen er ærlig om hvor den grensen går

Hvorfor rendres en PDF-gradient som en hvit boks?

En PDF-gradient er en vektorkonstruksjon, ikke et punktgrafikkbilde (bitmap) rendreren kan kopiere (blit). En skyggeleggingsordbok (shading dictionary) (ISO 32000-1 §8.7.4.5) beskriver geometrien — en akse eller et par sirkler i Coords-tabellen — pluss en fargefunksjon, og sh-operatoren tegner denne skyggeleggingen over det gjeldende klippeområdet (clip region), det samme grafikkmodellen som HotPDF eksponerer når du tegner stier og fyll på et sidelerret (canvas). HotPDFs siderendrer går gjennom innholdsstrømmen operator for operator, og alle operatorer den ikke har en kodebane for hoppes over, slik at resten av siden fortsatt rendres. Før versjon 2.335.0 lå sh i dette overhoppede settet: en topptekst- eller helsidesgradient bidro ikke med noe til utdataene, slik at bakgrunnen skinte igjennom som hvit. Filen var aldri korrupt. Rendreren hadde rett og slett ingen håndterer for akkurat den operatoren, og en hvit boks er akkurat slik en manglende håndterer ser ut

Funksjonsevalueringskjeden bak en skyggelegging

En skyggelegging kan ikke tegnes før funksjonen dens er evaluert, så det maskineriet måtte på plass først. Både type 2 aksiale og type 3 radiale skyggelegginger har en /Function som kartlegger en parametrisk posisjon langs gradienten til en farge (ISO 32000-1 §7.10), og HotPDFs funksjonsevaluator løser opp FunctionType og ruter arbeidet videre basert på dette. Type 2 eksponentiell interpolasjon (§7.10.3) beregner C0 + t^N * (C1 - C0) med Domain- og Range-begrensninger; Type 0 samplede funksjoner (§7.10.2) leser et rutenett av 8-, 16- eller 32-bits sampler og velger den nærmeste via Encode-kartet, et tilfelle som dekkes i dybden i vår gjennomgang av Type 0 samplede fargeoppslag; Type 3 sammensydde funksjoner (§7.10.4) velger en underfunksjon via Bounds-tabellen og kaller seg selv rekursivt. Type 4 PostScript-kalkulatorfunksjoner (§7.10.5) blir ikke evaluert — de degraderer pent til en mislykket evaluering fremfor en feil farge, noe som samsvarer med den konservative policyen HotPDF har gjennom hele rendringsbanen. Denne evaluatoren ble levert i versjon 2.334.0, én utgivelse før selve operatoren, fordi skyggeleggingstegneren bare er så god som den underliggende funksjonen

Hvordan gjør HotPDF en kontinuerlig gradient om til piksler?

HotPDF tilnærmer den kontinuerlige overgangen med et endelig antall fargebånd. Skyggeleggingstegneren går langs gradientaksen, deler den inn i opptil 64 bånd — færre når aksen er kort i enhetspiksler — evaluerer funksjonen ved hvert bånds parametriske posisjon, og fyller et rektangel vinkelrett på aksen med en solid fargebørste (brush). Avveiningen står mellom antall bånd og jevnhet: flere bånd følger fargeovergangen mer nøyaktig, men koster mer å tegne (fylle), og 64 er det bevisste taket. Siden antallet også skalerer med akselengden i enhetspiksler, har oppløsningen betydning. Rendring av samme side ved en høyere DPI gjør aksen lengre, slik at en kort gradient får flere bånd og stripene mykes opp til taket nås

// The shading discretizer uses up to 64 color bands along the gradient
// axis, but never more than the axis is long in device pixels. Rendering
// the same page at a higher DPI lengthens that axis, so a short gradient
// gains bands and the banding softens until the 64-band ceiling is hit.
Thumb := Pdf.RenderLoadedPageToBitmap(PageIndex, 96);   // coarser bands
Proof := Pdf.RenderLoadedPageToBitmap(PageIndex, 300);  // up to 64 bands

Rendre en skyggelagt side til et punktgrafikkbilde

Det offentlige inngangspunktet er uendret: RenderLoadedPageToBitmap tar en nullbasert sideindeks og en DPI-verdi, returnerer et oppkaller-eid 24-biters punktgrafikkbilde (bitmap), og returnerer nil ved feil i stedet for å utløse et unntak (exception), slik at støtten for skyggelegging er transparent for koden du allerede har. En side som pleide å rendre gradient-toppteksten blank, returnerer nå det samme punktgrafikkbildet med fargeblandingen tegnet inn, og ingen API-endring er nødvendig for å få det til. Det eneste som er verdt å huske på er eierskap: oppkalleren frigjør det returnerte punktgrafikkbildet. Hele rendrings-pipelinen — tolking av innholdsstrøm, innebygde glyfer og side-hurtigbufring — dekkes i veiledningen for rendring av PDF-sider til et punktgrafikkbilde i Delphi

var
  Pdf: THotPDF;
  Bmp: TBitmap;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('quarterly-report.pdf') > 0 then
    begin
      // Page 1 (zero-based) at 150 DPI. A gradient-filled header that
      // used to come back blank now carries its color blend.
      Bmp := Pdf.RenderLoadedPageToBitmap(0, 150);
      if Assigned(Bmp) then
      try
        Bmp.SaveToFile('page1.bmp');
      finally
        Bmp.Free;
      end;
    end;
  finally
    Pdf.Free;
  end;
end;

En enkel regresjonskontroll faller ut av det samme kallet. Rendre siden, ta en stikkprøve av en piksel der gradientbåndet hører hjemme, og bekreft at den ikke lenger er papirhvit, som er det rendreren pleide å falle tilbake til — en enkel sjekk på én linje som fanger opp en lydløs retur til den gamle oppførselen uten handling

Bmp := Pdf.RenderLoadedPageToBitmap(0, 150);
if Assigned(Bmp) then
try
  // True when the sh operator painted; False if the region fell back to white.
  HeaderPainted := Bmp.Canvas.Pixels[Bmp.Width div 2, 8] <> clWhite;
finally
  Bmp.Free;
end;

Hva HotPDF ikke tegner: mesh-skyggelegginger og klippekanter

De fire mesh-skyggeleggingstypene er utenfor omfanget, og et dokument bør vite det på forhånd. Friforms Gouraud-trekantmesh (Type 4), gitterforms Gouraud-mesh (Type 5), Coons-lappmesh (Coons patch meshes) (Type 6) og tensorprodukt-lappmesh (Type 7), definert i ISO 32000-1 §8.7.4.5.5 til §8.7.4.5.8, blir ikke rendret; en side som bruker en av disse vil fortsatt rendre, bare at den aktuelle regionen blir stående som den er. To ytterligere begrensninger er verdt å si tydelig. Båndrektanglene dekker skyggeleggingens stripe fremfor å bli klippet nøyaktig til den gjeldende klippebanen (clip path), så en gradient maskert inn i en avrundet eller uregelmessig form tegnes som sin avgrensede stripe inntil presis regionklipping kommer i en slower runde. HotPDF antar også at funksjonens utdata er RGB eller gråtoner, slik at skyggelegging opprettet i CMYK eller et eksotisk fargerom blir tilnærmet i stedet for fargehåndtert. For dokumentene de fleste Delphi- og C++Builder-utviklere faktisk behandler — rapporter, fakturaer, kontoutskrifter og lysbildepresentasjoner eksportert til PDF — aksial og radial dekker i praksis nesten alle gradienter du vil møte, og mesh-tunge designutkast er noe som bør behandles som en forhåndsvisning snarere enn et prøvetrykk

HotPDFs siderendrer, støtten for sh-operatoren beskrevet her, og funksjonsevaluatoren av type 0/2/3 under den leveres som en del av HotPDF Component for Delphi og C++Builder — et innfødt VCL-bibliotek uten eksterne DLL-avhengigheter, som dekker PDF-oppretting, redigering, tekstuttrekk og siderendring i én pakke