Teknisk artikel

Konsekventa NChannel-spotfärger i Delphi med HotPDF

Ett reproföretag skickar tillbaka jobbet: samma spotfärg separerad på två plåtar. HotPDF förhindrar det redan vid framställningen genom att skriva NChannel som ISO 32000-2:s DeviceN-form med fem element och genom att hålla en kanonisk alternativrymd och nyansfunktion per spotnamn för hela dokumentet, och avvisar den andra, motstridiga definitionen i stället för att skriva ut den

NChannel är inte ett färgrymdsfamiljenamn

Det första man måste göra sig av med är själva namnet. NChannel är inte en färgrymdsfamilj på det sätt som Separation och DeviceN är familjer. ISO 32000-2 §8.6.6.5 beskriver det som en undertyp av DeviceN, så en konform NChannel-rymd skrivs som femelementsarrayen [/DeviceN names alternateSpace tintTransform attributes], och attributordlistan bär /Subtype /NChannel. Det finns ingen [/NChannel ...]-array i specifikationen. Om du någon gång har byggt en för hand och sett en RIP rycka på axlarna åt den, är det därför

HotPDF skriver en NChannel-rymd som femelementsarrayen DeviceN vars attributordlista bär Subtype NChannel tillsammans med posterna Process, Colorants och MixingHints, eftersom det inte finns någon NChannel-familjenamnsarray i specifikationen alls
En konform NChannel-rymd är en DeviceN-array med en attributordlista, vilket är därför skrivaren bara skriver ut denna form medan läsaren fortfarande accepterar den äldre token

HotPDF gjorde fel här en gång och rättade sedan till det, vilket är värt att säga rakt ut eftersom det formar hur komponenten beter sig i dag. Äldre HotPDF-versioner skrev ut familjenamnsformen. THotPDF.RegisterNChannelColorSpace skriver nu bara ut standardformen DeviceN plus attribut, och eftersom undertypen NChannel kom i PDF 1.6 gateas producentens ingångspunkt på RequirePDFVersion(pdf16, ...) och avböjer helt enkelt mot äldre mål. Rendersidan är avsiktligt mer förlåtande än skrivaren: HPDFResolveColorSpace accepterar fortfarande den äldre token /NChannel som en DeviceN-familj så att filer från den gamla skrivaren fortsätter att renderas, men allt HotPDF skriver ut använder standardkodningen. Förlåtande på ingången, strikt på utgången är rätt asymmetri här, eftersom din läsare måste klara av filer den inte skapade medan din skrivare inte har någon sådan ursäkt

Varför hamnar ett spotnamn på två plåtar?

Eftersom ett spotfärgsnamn är en dokumentomfattande plåtidentitet, inte ett lokalt argument. Två anrop som båda namnger Orange men lämnar över en annan alternativrymd, eller samma alternativrymd med en annan nyansfunktion, beskriver två olika färger som råkar dela en etikett. En RIP som bygger separationer har inget sätt att förena det, så den gör det enda ärliga och ger dig två plåtar. HotPDF upprätthåller därför en kanonisk signatur per dokument och färgstoffnamn. RegisterSpotColorantDefinition sammansätter signaturen av den alternativa färgrymden och nyansfunktionens form, och varje RegisterSeparation, RegisterSeparationFunc, RegisterSeparationLUT och NChannel-spotdefinition passerar genom den. När en andra definition inte stämmer överens kastar anropet ett undantag i stället för att tyst registrera en andra variant, och meddelandet är avsiktligt specifikt om felläget, eftersom alternativet är att upptäcka det på ett plåtprov tre veckor senare

Varje HotPDF-spotregistreringsanrop kanaliseras genom RegisterSpotColorantDefinition, som spelar in en kanonisk alternativrymd och nyansignatur per färgstoffnamn för dokumentet och kastar undantag när en andra, avvikande definition av samma namn anländer
Ett namn, en signatur: den andra, motstridiga definitionen av en spotfärg avvisas vid framställningen i stället för att upptäckas på ett plåtprov
// Orange var redan registrerad mot DeviceCMYK med
// nyansfunktionen 0 / 0.55 / 1 / 0 vid full nyans.
Conflicting := Pdf.RegisterExponentialFunction(
  Domain1, NoInk, OtherOrangeCMYK, 1, []);
try
  Pdf.RegisterSeparationFunc('Orange', 'DeviceCMYK', Conflicting);
except
  on E: Exception do
    // 'Spotfärgsstoffet "Orange" har inkonsekvent alternativ
    //  färgrymd eller nyansdefinition i detta dokument'
    LogPrepressWarning(E.Message);
end;

Uppdelning av masternamnen i process och spot

En komplett NChannel måste redogöra för vart och ett av sina masterfärgstoffnamn exakt en gång, antingen som en processkomponent eller ett spotfärgsstoff. Den avancerade överlagringen RegisterNChannelColorSpace tar masterlistan ColorantNames, ProcessColorantNames, den alternativa rymden, den övergripande nyansfunktionen, en array av THPDFNChannelSpotColorant-poster och en valfri tryckordning. Varje spotpost bär sitt eget namn, sin egen Separation-nyansfunktion med en ingång, en valfri soliditet och en valfri punktförstoringsfunktion. Den övergripande nyansfunktionen måste avbilda N ingångar på den alternativa rymdens komponentantal; varje spotnyans måste avbilda en ingång på samma antal

const
  Colorants: array[0..4] of AnsiString =
    ('Cyan', 'Magenta', 'Yellow', 'Black', 'Orange');
  ProcessNames: array[0..3] of AnsiString =
    ('Cyan', 'Magenta', 'Yellow', 'Black');
  Order: array[0..4] of AnsiString =
    ('Yellow', 'Magenta', 'Cyan', 'Orange', 'Black');
  Domain5: array[0..9] of Single = (0, 1, 0, 1, 0, 1, 0, 1, 0, 1);
  Range4: array[0..7] of Single = (0, 1, 0, 1, 0, 1, 0, 1);
  Domain1: array[0..1] of Single = (0, 1);
  NoInk: array[0..3] of Single = (0, 0, 0, 0);
  OrangeCMYK: array[0..3] of Single = (0, 0.55, 1, 0);
  GainC0: array[0..0] of Single = (0);
  GainC1: array[0..0] of Single = (1);
var
  Pdf: THotPDF;
  Spots: array[0..0] of THPDFNChannelSpotColorant;
  CSName: AnsiString;
begin
  Pdf.Version := pdf20;
  Pdf.BeginDoc;
  Spots[0].Name := 'Orange';
  Spots[0].TintTransform := Pdf.RegisterExponentialFunction(
    Domain1, NoInk, OrangeCMYK, 1, []);
  Spots[0].HasSolidity := True;
  Spots[0].Solidity := 0.82;
  Spots[0].DotGainFunction := Pdf.RegisterExponentialFunction(
    Domain1, GainC0, GainC1, 1, []);
  CSName := Pdf.RegisterNChannelColorSpace(Colorants, ProcessNames,
    'DeviceCMYK',
    Pdf.RegisterPostScriptFunction(Domain5, Range4,
      '{ pop pop pop pop pop 0 0 0 0 }'),
    Spots, Order);

Från det anropet skriver HotPDF ut den attributordlista som specifikationen kräver: /Subtype /NChannel, en /Process-ordlista vars /ColorSpace är processrymden och vars /Components listar processnamnen i den rymdens komponentordning, en /Colorants-ordlista som håller en riktig [/Separation name alternate tintfn]-array för varje spotfärg, och en /MixingHints-ordlista som bär /Solidities, /PrintingOrder och /DotGain när du angett dem. Det returnerade resursnamnet går till SetFillColorSpace eller SetStrokeColorSpace precis som de enklare rymderna i artikeln om rendering av Separation- och DeviceN-spotfärger

HotPDF delar upp masterfärgstoffnamnen i en NChannel-rymd i en Process-ordlista för de fyra CMYK-komponenterna och en Colorants-ordlista som håller en Separation-array per spotfärg, med varje aritet kontrollerad före skrivning
Varje masternamn tas i anspråk exakt en gång, och den övergripande nyansfunktionen, spotnyanserna och tryckordningen valideras alla innan något objekt skrivs ut

Vad avvisar konsekvenskontrollen egentligen?

Den avvisar strukturell inkonsekvens inom rymden, och den gör det innan ett enda objekt skrivs. Färgstoffnamn måste vara unika och får inte vara tomma, All eller None. Process- och spotdefinitioner måste tillsammans täcka masternamnen exakt, utan att något färgstoff förekommer i båda rollerna och utan att något lämnas odefinierat. När den alternativa rymden är DeviceCMYK måste processkomponenterna vara Cyan, Magenta, Yellow, Black i den ordningen, och antalet processnamn måste matcha antalet komponenter i den alternativa rymden. Varje nyansfunktion och punktförstoringsfunktion måste vara ett indirekt funktionsobjekt med rätt in- och utgångsaritet. Soliditet måste vara ett ändligt värde inom 0..1. Tryckordning måste vara tom eller en fullständig permutation av masternamnen, aldrig en partiell lista. Vad den inte gör är att bedöma färg: inget här kontrollerar att din Orange-nyansfunktion faktiskt liknar färgen i burken, att dess CMYK-uppbyggnad är en vettig proxy eller att soliditeten du angav matchar uppmätt beteende på substratet. Det är tryck- och mätfrågor, och komponenten har ingen behörighet att svara på dem. Den enklare överlagringen med bara process är by design ännu striktare: den producerar en NChannel med enbart process och vägrar avsiktligt att ta emot spotnamn, eftersom att skriva en spotfärg i namnarrayen utan en matchande /Colorants-post skulle ge en strukturellt icke-konform fil, och att fabricera en standarddefinition vore värre än att misslyckas

PDF/X-6n-utdataavsikter måste täcka varje registrerad spotfärg

En PDF/X-6n-fil med N färgstoff deklarerar sina färgstoff två gånger, och de två deklarationerna måste stämma överens. AddPDFX6ExternalOutputIntent skriver den externa ICC-profilreferensen med sin ColorantTable, och innan den gör det går ValidateRegisteredSpotOutputColorants igenom varje spotfärg som dokumentet har registrerat och kastar undantag om någon saknas i tabellen. Kontrollen körs i båda riktningarna: när en utdataavsikt väl har publicerat sin färgstofflista avvisas också en senare spotregistrering för ett namn utanför listan. AddPDFX6ExternalOutputIntentSpotData lägger till metadata per färg ovanpå det, och den upprätthåller sina egna regler, framför allt att ett färgstoff bär antingen ett soliditetsvärde eller CxF/X-4-spektraldata, aldrig båda. Det är samma konformitetsyta som diskuteras i artikeln om validering av PDF/A, PDF/X och PDF/UA

Spectral := TMemoryStream.Create;
try
  LoadCxFForInk('Orange', Spectral);   // ISO 17972-4-nyttolast
  Pdf.AddPDFX6ExternalOutputIntentSpotData(
    'ECG-5', 'Five-colour output condition',
    'https://profiles.example.com/ecg-5.icc', '5CLR',
    Colorants,
    '00112233445566778899AABBCCDDEEFF', #4#3#0#0,
    ['Cyan'], [0.70],                  // soliditet för ett färgstoff
    Order,
    ['Orange'], [Spectral]);           // spektraldata för ett annat
finally
  Spectral.Free;
end;

Två operativa detaljer är lätta att missa. Registret som ligger bakom allt detta är per dokument och rensas vid dokumentgränser, så att läsa in en ny fil i samma THotPDF-instans ärver inte föregående dokuments spotidentiteter; isoleringen är själva poängen, eftersom en läckt signatur skulle avvisa fullt giltigt arbete i nästa jobb. Och profilsidan har sina egna hårda gränser: en extern utdataavsikt kräver ett PDF 2.0-mål, en absolut HTTP- eller HTTPS-profil-URL, en ICC-färgrymdssignatur på fyra byte, och för PDF/X-6n mellan 2 och 15 färgstoff med en matchande signatur 2CLR till FCLR

Där kontrollen tar slut

CxF/X-4-hanteringen är den del man bör vara ärlig om. HotPDF tillämpar avgränsade strukturella säkerhetskontroller på spektralströmmen och bekräftar att färgidentiteten i den matchar det färgstoff du namngav. Den begränsar nyttolastens storlek, avvisar inbäddade nullbyte, vägrar alla strömmar som innehåller en DOCTYPE- eller ENTITY-deklaration, kräver en igenkännbar CxF-rot och kräver exakt ett SpotInkCharacterisation-element som bär exakt ett SpotInkName som motsvarar ditt färgstoffnamn. Det är en grind mot felaktig och fientlig indata, inte en schemavalidator. Det är inte en komplett ISO 17972-4-implementation, den verifierar inte dina spektrala mätningar, och den granskar inte i efterhand godtyckliga befintliga tredjepartsobjektgrafer eller de interna färgstofftabellerna i en inbäddad ICC-profil. Om ditt arbetsflöde är beroende av full CxF-konformitet, validera filen med ett dedikerat verktyg innan du lämnar den till komponenten

En närliggande begränsning biter på folk som aldrig väntat sig att stöta på den. En luminositetsbaserad soft mask kan inte använda Separation, DeviceN eller NChannel som sin transparensgrupps /CS; gruppens blandningsrymd måste vara en enhets- eller CIE-baserad rymd, så spotfärg inuti gruppen löses upp via sin nyansfunktion till den alternativa rymden innan luminositeten beräknas. RegisterLuminositySoftMaskState bygger en DeviceGray-grupp just så att detta inte kan gå fel av misstag. Den praktiska konsekvensen är att en design dominerad av spotfärger som maskeras på detta sätt utvärderas via sin CMYK-proxy, inte sin färg, vilket spelar roll när du jämför den mot ett separerat prov enligt beskrivningen i anteckningarna om övertrycksprovning och renderingsenheter

Inget av detta tar bort behovet av ett plåtprov, men det flyttar en hel klass av reproavslag från trycksalen tillbaka till bygget. De API:er för NChannel, Separation och PDF/X-6n-utdataavsikter som beskrivs här levereras i standardkomponenten HotPDF Delphi Component för Delphi och C++Builder, där referensen dokumenterar de fullständiga postlayouterna och de exakta villkor under vilka varje anrop misslyckas stängt