Teknisk artikel

Rendering av PDF-gradienter i Delphi: Axiella och radiella

En PDF vars rubrik har en gradientbakgrund i Word- eller Excel-style, renderad till en bild, brukade förr komma tillbaka med en tom vit rektangel där färgtoningen borde vara. HotPDF:s sidrenderare stöder nu PDF-operatören sh (shading) för axiella gradienter av typ 2 och radiella av typ 3: den utvärderar toningens funktion längs gradientaxeln och ritar färgövergången, där tidigare versioner tyst hoppade över operatören och lämnade ytan pappersvit. De två toningstyperna som täcker nästan alla kontorsdokumentgradienter stöds; mesh-typerna gör det inte, och denna artikel är ärlig om var den gränsen går

Varför renderas en PDF-gradient som en vit ruta?

En PDF-gradient är en vektorkonstruktion, inte en bitmap som renderaren kan överföra. En toningsordbok (shading dictionary) (ISO 32000-1 §8.7.4.5) beskriver geometrin — en axel eller ett par cirklar i matrisen Coords — plus en färgfunktion, och operatören sh ritar den toningen över det aktuella klippområdet, samma grafikmodell som HotPDF exponerar när du ritar banor och fyllningar på en sidas rityta. HotPDF:s sidrenderare går igenom innehållsströmmen operatör för operatör, och alla operatörer som saknar kodväg hoppas över så att resten av sidan fortfarande renderas. Före version 2.335.0 tillhörde sh den överhoppade uppsättningen: en rubrik- eller helsidgradient gav ingenting till utdata, så bakgrunden lyste igenom som vit. Filen var aldrig korrupt. Renderaren saknade helt enkelt en hanterare för den operatören, och en vit ruta är precis vad en saknad hanterare resulterar i

Funktionsutvärderingskedjan bakom en toning

En toning kan inte ritas förrän dess funktion har utvärderats, så den mekanismen var tvungen att komma först. Axiella toningarna av typ 2 och radiella av typ 3 har båda en /Function som mappar en parametrisk position längs gradienten till en färg (ISO 32000-1 §7.10), och HotPDF:s funktionsutvärderare löser denna FunctionType och skickar vidare den. Exponentiell interpolation av typ 2 (§7.10.3) beräknar C0 + t^N * (C1 - C0) med begränsning av Domain och Range; samplade funktioner av typ 0 (§7.10.2) läser ett rutnät av 8-, 16- eller 32-bitars sampel och väljer det närmaste via Encode-mappningen, fallet som täcks i detalj i vår genomgång av typ 0 samplade färguppslagningar; sammansatta stitching-funktioner av typ 3 (§7.10.4) väljer en delfunktion med matrisen Bounds och anropar den rekursivt. PostScript-kalkylatorfunktioner av typ 4 (§7.10.5) utvärderas inte — de degraderas graciöst till en misslyckad utvärdering snarare än en felaktig färg, vilket matchar den konservativa policy HotPDF har genom hela renderingsbanan. Denna utvärderare levererades i version 2.334.0, en version före själva operatören, eftersom toningsritaren bara är så bra som funktionen därunder

Hur förvandlar HotPDF en kontinuerlig gradient till pixlar?

HotPDF uppskattar den kontinuerliga övergången med en ändlig följd av enfärgade band. Toningsritaren går längs gradientaxeln, delar upp den i upp till 64 band — färre när axeln är kort i enhetspixlar — utvärderar funktionen vid varje bands parametriska position och fyller en rektangel vinkelrät mot axeln med en solid pensel. Avvägningen är antal band mot mjukhet: fler band spårar övergången mer exakt men kostar fler fyllningar, och 64 är det medvetna taket. Eftersom antalet också skalar med axelns längd i enhetspixlar spelar upplösningen roll. Att rendera samma sida vid en högre DPI förlänger axeln, så en kort gradient får fler band och dess ränder mjukas upp tills 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

Rendering av en tonad sida till en bitmap

Den offentliga startpunkten är oförändrad: RenderLoadedPageToBitmap tar ett nollbaserat sidindex och ett DPI-värde, returnerar en användarägd 24-bitars bitmap och ger tillbaka nil vid fel snarare än att kasta undantag, så toningsstödet är transparent för kod du redan har. En sida som tidigare renderade sin gradientsrubrik tom returnerar nu samma bitmap med toningen ritad, och ingen API-ändring krävs för att få det. Det enda som är värt att komma ihåg är ägarskapet: anroparen frigör den returnerade bitmappen. Hela renderingskedjan — tolkning av innehållsström, inbäddade tecken och sidcachning — täcks i guiden för att rendera PDF sidor till en bitmap 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;

Ett enkelt regressionsskydd kan göras från samma anrop. Rendera sidan, sampla en pixel där gradientbandet hör hemma och bekräfta att det inte längre är pappersvitt som renderaren brukade falla tillbaka på — en kontroll på en rad som fångar en tyst återgång till det gamla beteendet utan operation

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;

Vad HotPDF inte ritar: mesh-toningar och klippkanter

De fyra mesh-toningstyperna ligger utanför tillämpningsområdet, och ett dokument bör känna till det i förväg. Frihands Gouraud-triangelmesh (typ 4), gallerformade Gouraud-mesh (typ 5), Coons patch-mesh (typ 6) och tensorprodukt-patchmesh (typ 7), definierade i ISO 32000-1 §8.7.4.5.5 till §8.7.4.5.8, renderas inte; en sida som använder en sådan renderas fortfarande, men det området lämnas som det är. Ytterligare två begränsningar är värda att nämna. Bandrektanglarna täcker toningens remsa snarare än att klippas exakt till den aktuella klippbanan, så en gradient som maskeras till en rundad eller oregelbunden form ritas som sin omslutande remsa tills exakt områdesklippning anländer i en senare version. HotPDF antar också att funktionens utdata är RGB or gråskala, så en toning skapad i CMYK eller en exotic färgrymd uppskattas snarare än färgstyrs. För de dokument som de flesta Delphi- och C++Builder-team faktiskt bearbetar — rapporter, fakturor, kontoutdrag och bildspel exporterade till PDF — täcker axiella och radiella i princip alla gradienter du stöter på, och mesh-tunga designkorrektur är fall som bör behandlas som en förhandsgranskning snarare än ett färgäkta korrektur

HotPDF:s sidrenderare, stödet för sh-operatören som beskrivs här och funktionsutvärderaren för typ 0/2/3 därunder levereras som en del av HotPDF Component för Delphi och C++Builder — ett inbyggt VCL-bibliotek utan externa DLL-beroenden som täcker PDF-skapande, redigering, textextraktion och sidrendering i ett paket