HotPDF tegner Separation- og DeviceN-dekkfarger (spot colors) på lastede PDF-sider ved å løse opp fargerommet via HPDFResolveColorSpace, evaluere tint-transformeringsfunksjonen med HPDFEvalTintTransform, og konverterere resultatet via det alternative fargerommet til RGB for skjermen. Siden v2.375.0 evaluerer den rørledningen alle fire PDF-funksjonstyper, inkludert Type 4 PostScript-kalkulatorer, slik at en trykklar fil med Pantone-blekk viser sine virkelige farger i stedet for en plassholder. Denne artikkelen går gjennom hvordan rørledningen fungerer, og like nyttig, hvordan den feilet mens vi bygde den
Scenarioet som motiverer alt dette er alltid det samme: En kunde mottar en PDF fra et trykkeri, der overskriften er satt i en navngitt dekkfarge, emballasje-kunsten bruker en DeviceN-blanding med to blekktyper, og brødteksten er vanlig svart. I Acrobat ser det perfekt ut. I din Delphi-applikasjon tegnes overskriften som svart, eller enda verre, den tegnes ikke i det hele tatt, og kunden rapporterer en feil mot din programvare i stedet for mot filen. Filen er fin; tegningsmotoren snakker bare ikke fargerommene filen bruker
Hvorfor tegnes dekkfarger som svart i en PDF-viser?
Dekkfarger tegnes som svart, eller forsvinner, når tegningsmotoren kun implementerer enhetsfargeoperatorene (rg, g, k) og ignorerer de generiske. ISO 32000-1 §8.6 definerer tre grupper av fargerom: enhetsrom (DeviceGray, DeviceRGB, DeviceCMYK), CIE-baserte rom (CalGray, CalRGB, Lab, ICCBased) og spesielle rom (Indexed, Separation, DeviceN, Pattern). Alt utenfor enhetsgruppen velges med de generiske operatorene: cs og CS velger et rom etter navn fra sidens ressursordbok, og deretter leverer sc, SC, scn og SCN komponentverdiene. En tegningsmotor som hopper over disse operatorene beholder fargen som sist ble satt, noe som for en side som starter med en overskrift i dekkfarge, betyr den opprinnelige DeviceGray-svartfargen
HotPDF la til det generiske operatorsettet i sin sidetegner i v2.333.0, sammen med en enhetlig oppløsningsbane: hver /ColorSpace-ressursoppføring, enten det er et enkelt navn, en innebygd tabell eller en indirekte referanse, analyseres inn i en THPDFColorSpace-post, og hver forespørsel om fyll- eller strekfarge traktes gjennom et enkelt HPDFResolveColor-kall. Familie-oppregningen viser dekningen på et øyeblikk
type
THPDFColorSpaceFamily = (csfDeviceGray, csfDeviceRGB, csfDeviceCMYK,
csfIndexed, csfCalGray, csfCalRGB, csfLab,
csfICCBased, csfSeparation, csfDeviceN,
csfUnsupported);
function HPDFResolveColorSpace(Obj: THPDFObject): THPDFColorSpace;
function HPDFResolveColor(const CS: THPDFColorSpace;
const Comps: THPDFColorComps; CompCount: Integer): THPDFRenderColor;
En designbeslutning viser sin verdi gang på gang: csfUnsupported er en førsteklasses familie, ikke en feil. Et fargerom tegneprosessen ikke kan tolke, faller tilbake på en definert reserve i stedet for å avbryte siden, noe som samsvarer med hvordan vanlige visere oppfører seg og forhindrer at et enkelt eksotisk fyll blanker ut et ellers tegnbart dokument
Hvordan gjør en tint-transformering en blekkverdi om til en ekte farge?
Et Separation-rom bærer tre deler med informasjon: blekknavnet, et alternativt fargerom og en tint-transformeringsfunksjon. Tabellen [/Separation /PANTONE485 /DeviceCMYK f] sier: når innholdsstrømmen skriver 0.8 scn, send tint-verdien 0,8 inn i funksjonen f og tegn den resulterende CMYK-kvadrupelen. DeviceN generaliserer dette til N blekktyper med en funksjon med N inndata. Selve blekknavnet er kun rådgivende på skjermen; tint-transformeringen utgjør hele tegningssemantikken, so en tegningsmotor som analyserer fargerommet men hopper over funksjonen, har ikke gjort noe nyttig ennå
HPDFEvalTintTransform er funksjonsmotoren bak dette trinnet. Den ble introdusert i v2.334.0 og evaluerer Type 2 eksponensielle funksjoner (C0 + x^N * (C1 - C0) med Domain- og Range-fastlåsing), Type 3 sammensydde funksjoner (grense-valgt underfunksjonsrekursjon med Encode-omkartlegging), og Type 0 samplede funksjoner med 8-, 16- og 32-bit sampler. Type 0 er det samme oppslagstabell-maskineriet vi dekket fra opprettelsessiden i artikkelen om oppbygging av Type 0 farge-LUT-er; tegnesiden går gjennom den identiske strukturen i omvendt rekkefølge, fra dekodede sample-bytes tilbake til komponentverdier
Type 4 PostScript-kalkulatorfunksjoner var den siste skansen. Frem til v2.375.0 ble de degradert pent til en nøytral plassholder; siden v2.375.0 kjører HPDFEvalPostScriptCalculator det fulle operatorsettet fra ISO 32000-1 §7.10.5 på en begrenset operand-stakk: aritmetikk, sammenligning, boolske og bitvise operatorer, stakk-manipulasjon inkludert roll, og if/ifelse-betingelser. Grensetilfellene er strengere enn de ser ut. PostScripts round runder halvdeler oppover mot den større verdien, så Delphis bankavrunding Round kan ikke brukes; de trigonometriske operatorene fungerer i grader, der atan returnerer verdier i [0, 360); og exp er en potens med to operander, ikke den naturlige eksponentialfunksjonen. Den samme evaluatoren driver også funksjonsbaserte gradientfyll, noe som er grunnen til at aksial og radiell skyggeleggingstegning fikk kalkulatordrevne ramper i samme utgivelse
// Evaluate a Separation/DeviceN tint transform (Type 0/2/3/4).
function HPDFEvalTintTransform(FuncObj: THPDFObject;
const Inputs: THPDFColorComps; InputCount: Integer;
out AltComps: THPDFColorComps): Boolean;
// Type 4 PostScript calculator, ISO 32000-1 7.10.5 operator set.
function HPDFEvalPostScriptCalculator(const Prog: TBytes;
const Inputs: THPDFColorComps; InputCount: Integer;
var Outputs: THPDFColorComps; OutCount: Integer): Boolean;
Ærlighet om presisjon: en programvarekalkulator evaluert med dobbel presisjon vil ikke matche en RIP bit for bit, og fastlåsing ved Range-grensen kan avvike med en minst signifikant bit fra en annen implementasjon. For skjermvisning og regresjonstegning er dette irrelevant; hvis du bygger fargestyrt prøvetrykk, er tint-transformeringen bare det første trinnet, og du trenger fremdeles en ekte CMM nedstrøms
CalGray, CalRGB, Lab og ICCBased uten en ICC-motor
De CIE-baserte familiene tar den andre grenen av samme oppløser. HotPDF konverterer Lab-verdier gjennom den standard kjeden Lab til XYZ til sRGB, inkludert 6/29-knekkpunktkuben i den omvendte overføringsfunksjonen, og håndterer CalRGB med sin gamma per kanal pluss en 3x3 lineær matrise, samt CalGray med sin enkelt-gamma. Den ytelsesrelevante detaljen er håndtering av hvitpunkt: konverteringsmatrisen fra XYZ til sRGB is Bradford-tilpasset hvitpunktet deklarert i fargerommet og lagret i den oppløste THPDFColorSpace-posten, slik at arbeidet per piksel forblir en enkelt 3x3-multiplikasjon uansett hvor eksotisk den deklarerte lyskilden er
ICCBased-fargerom får en bevisst pragmatisk behandling. PDF-spesifikasjonen krever at hver ICCBased-strøm deklarerer et alternativt fargerom /Alternate, eller et underforstått ett via sitt komponentantall /N, nettopp slik at visere uten en fargestyringsmotor fremdeles kan tegne fornuftig. HotPDF løser opp ICCBased via dette alternativet, eller gjetter DeviceGray, DeviceRGB eller DeviceCMYK ut fra at /N er 1, 3 eller 4 når oppføringen mangler, og analyserer aldri profil-bytene. Det betyr ingen lcms-avhengighet og ingen kostnad for profiloppslag, til prisen av kolorimetrisk nøyaktighet: et ICCBased-rom hvis profil avviker sterkt fra sitt alternativ, vil vise den alternative tegningen. For skjermvisning og miniatyrbilder er dette den samme avveiningen som alle lette visere gjør, og det er begrensningen du bør oppgi tydelig i din egen dokumentasjon
Feilen som gjorde at ethvert navngitt fargeromsoppslag bommet
Operator-koblingen i v2.333.0 ble levert med en feil som forble usynlig i førtito utgivelser: håndtererne for cs og CS slo opp operanden sin med den ledende skråstreken intakt (/CS0) mot resursordbok-nøkler lagret uten skråstreken (CS0). Ethvert navngitt fargeromsoppslag bommet hundre prosent av tiden, og koden falt lydløst tilbake på standard DeviceGray. Det synlige symptomet var subtilt på verst tenkelig måte: et Separation-fyll på 1 scn ble til DeviceGray 1.0, som tegner hvitt, og hvitt blekk på en hvit side er ikke en tegningsfeil noen tar skjermdump av. Løsningen, i v2.375.0, var en felles navnenormaliseringshjelper som ble brukt ved hvert oppslag fra operand til resurs
To beslektede feil falt ut av den samme undersøkelsen. For det første ble indirekte referanser til tabellverdige objekter returnert uoppløste: tegneprosessens dokumenttilgang hadde typede oppløsere kun for strømmer og ordbøker, så /CS0 5 0 R som pekte på en frittstående [/Separation ...]-tabell kom tilbake som den rå referansen, og fargerommet ble tolket som ikke-støttet. For det andre håndhevet HPDFReadNumericArray streng lengdesemantikk, som krevde at PDF-tabellen var minst like lang som den leverte bufferen. Å lese en Type 2-funksjons /C0 og /C1 inn i en fire-elements buffer feilet derfor for alternativer med én og tre komponenter, noe som etterlot begge tabellene nullstilt, og hver ikke-CMYK eksponensiell fargenyanse ble tegnet som svart inntil v2.376.0 introduserte den tolerante leseren HPDFReadNumericArrayUpTo. Lærdommen man kan ta med seg: enhver PDF-nøkkel som er dokumentert å inneholde en numerisk tabell med variabel lengde må leses med en leser som fyller det som eksisterer, fordi en buffer med fast størrelse med streng matching gjør gyldige filer til lydløse nuller
Hvordan tester du dekkfargetegning uten å lure deg selv?
Det ubehagelige spørsmålet er hvorfor testpakken forble grønn gjennom alt dette. Den opprinnelige regresjonstesten, SeparationRendersDistinguishable, hevdet bare at det tegnede bitmapet ikke var helt svart. En rørledning som slapp hver dekkfarge ned til DeviceGray ga grå og hvite utdata, noe som ikke er svart, så påstanden gikk igjennom selv om hele funksjonen var død. Svake påstander av formen "ikke tom", "ikke helt svart" eller "sammendraget er ikke-null" kan ikke skille en fungerende tegneprosess fra en ødelagt, fordi nesten enhver feilmodus fremdeles produserer noen piksler
Påstands-stilen som faktisk fanger disse feilene, låser den forventede fargen: tegn en håndbygd, minimal PDF der Separation-blekket løses opp til en kjent nyanse, og tell deretter pikslene som er dominerende i den nyansen. Tegning til et bitmap for inspeksjon bruker det samme inngangspunktet RenderLoadedPageToBitmap som er beskrevet i veiledningen for sidetegning til bitmap
var
Pdf: THotPDF;
Bmp: TBitmap;
X, Y, RedHits: Integer;
Px: TColor;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('spot-red-fixture.pdf', '') > 0 then
begin
Bmp := Pdf.RenderLoadedPageToBitmap(0, 96);
try
RedHits := 0;
for Y := 0 to Bmp.Height - 1 do
for X := 0 to Bmp.Width - 1 do
begin
Px := Bmp.Canvas.Pixels[X, Y];
if (GetRValue(Px) > 180) and (GetGValue(Px) < 100) and
(GetBValue(Px) < 100) then
Inc(RedHits);
end;
// Lock the expected ink: demand a real area of red-dominant
// pixels, never settle for "not all black".
Assert(RedHits > 500);
finally
Bmp.Free;
end;
end;
finally
Pdf.Free;
end;
end;
Fiksturen betyr like mye som påstanden. En håndbygd PDF på noen få hundre byte med ett Separation-fyll og ingenting annet, etterlater ingen tvil om hva de forventede utdataene er; en fil fra den virkelige verden kjører mer kode, men kan ikke fortelle deg hvilket trinn som feilet. Vi behandler nå pikseltelling av forventet farge som minimumskravet for enhver røyksjekk av tegning, fordi det er den eneste påstands-stilen som tvang disse feilene frem i lyset
Tegning av dekkfarger og CIE-fargerom er en del av rørledningen for lastede dokumenter i HotPDF Component for Delphi og C++Builder, sammen med funksjonsmotoren, skyggeleggingstegning og bitmap-eksportbanene vist over