Technický článek

Konzistence náplní spot NChannel v Delphi s HotPDF

Předtiskárna posílá zakázku zpět: tatáž náplň spot se oddělila na dvě desky. HotPDF tomu brání už při autorování, když píše NChannel jako pětiprvkovou formu DeviceN podle ISO 32000-2 a drží jediný kanonický alternativní prostor a tint transformaci na název spotu pro celý dokument; druhou, kolidující definici odmítá místo emitování

NChannel není název rodiny barevných prostorů

První věc, kterou se odnaučit, je samotný název. NChannel není rodina barevných prostorů tak, jak rodinami jsou Separation a DeviceN. ISO 32000-2 §8.6.6.5 ho popisuje jako podtyp DeviceN, takže korektní NChannel prostor se zapisuje jako pětiprvkové pole [/DeviceN names alternateSpace tintTransform attributes] a slovník atributů nese /Subtype /NChannel. Pole [/NChannel ...] ve specifikaci neexistuje. Stavili jste-li si někdy jedno ručně a sledovali, jak na něj RIP krčí rameny, tohle je důvod

HotPDF zapisuje prostor NChannel jako pětiprvkové pole DeviceN, jehož slovník atributů nese Subtype NChannel spolu s položkami Process, Colorants a MixingHints, protože pole s názvem rodiny NChannel ve specifikaci vůbec neexistuje
Korektní prostor NChannel je pole DeviceN se slovníkem atributů, a proto zapisovač emituje jen tuto formu, zatímco čtenář stále přijímá starý token

HotPDF si to jednou vykládal špatně a pak to opravil, což stojí za vyslovení nahlas, protože to formuje chování komponenty dnes. Starší verze HotPDF emitovaly formu s názvem rodiny. THotPDF.RegisterNChannelColorSpace dnes emituje jen standardní formu DeviceN plus atributy a protože podtyp NChannel přišel s PDF 1.6, vstupní bod producenta braní na RequirePDFVersion(pdf16, ...) a na starších cílech prostě odpadá. Renderovací strana je záměrně shovívavější než zapisovač: HPDFResolveColorSpace stále přijímá starý token /NChannel jako rodinu DeviceN, takže soubory ze starého zapisovače se dál renderují, ale vše, co HotPDF zapisuje zpět, používá standardní kódování. Shovívavý vstup, přísný výstup je tady správná asymetrie, protože váš čtenář musí zvládnout soubory, které nevytvořil, kdežto váš zapisovač takovou výmluvu nemá

Proč končí jeden název spotu na dvou deskách?

Protože název colorantu spotu je identita desky v rozsahu celého dokumentu, ne lokální argument. Dvě volání, která obě jmenují Orange, ale předávají jiný alternativní prostor, nebo tentýž alternativní prostor s jinou tint transformací, popisují dvě různé náplně, kterým se shodou okolností pletou štítek. RIP stavěcí separace nemá jak to smířit, takže udělá jedinou poctivou věc a dá vám dvě desky. HotPDF si proto drží kanonickou signaturu na colorant název pro dokument. RegisterSpotColorantDefinition tu signaturu skládá z alternativního barevného prostoru a tvaru tint funkce a skrz ni prochází každé RegisterSeparation, RegisterSeparationFunc, RegisterSeparationLUT i definice spotu pro NChannel. Když se druhá definice rozchází, volání vyhazuje místo tichého zaregistrování druhé varianty a hlášení je záměrně konkrétní ohledně režimu selhání, protože alternativa je objevit to na zkouškové desce o tři týdny později

Každé registrační volání spotu HotPDF protéká přes RegisterSpotColorantDefinition, které si na dokument zaznamená jediný kanonický alternativní prostor a tint signaturu na colorant název a vyhazuje, když dorazí druhá, nesouhlasící definice téhož názvu
Jeden název, jedna signatura: druhá, kolidující definice spotu je odmítnuta při autorování místo objevení na zkouškové desce
// Orange už byl zaregistrován proti DeviceCMYK s tint transformací
// 0 / 0.55 / 1 / 0 při plné tintě.
Conflicting := Pdf.RegisterExponentialFunction(
  Domain1, NoInk, OtherOrangeCMYK, 1, []);
try
  Pdf.RegisterSeparationFunc('Orange', 'DeviceCMYK', Conflicting);
except
  on E: Exception do
    // 'Spot colourant "Orange" has inconsistent alternate colour
    //  space or tint definition in this document'
    LogPrepressWarning(E.Message);
end;

Rozdělení master názvů na process a spot

Úplný NChannel musí započítat každý svůj master colorant název přesně jednou, buď jako process komponentu, nebo jako colorant spotu. Pokročilé přetížení RegisterNChannelColorSpace přijímá master ColorantNames, ProcessColorantNames, alternativní prostor, celkovou tint transformaci, pole záznamů THPDFNChannelSpotColorant a volitelné tiskové pořadí. Každý záznam spotu nese vlastní název, vlastní jednovstupovou tint transformaci Separation, volitelnou soliditu a volitelnou funkci dot-gain. Celková tint transformace musí mapovat N vstupů na počet komponent alternativního prostoru; každá tint spotu musí mapovat jeden vstup na tentýž počet

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);

Z tohoto volání emituje HotPDF slovník atributů, jaký žádá specifikace: /Subtype /NChannel, slovník /Process, jehož /ColorSpace je process prostor a jehož /Components seznam process názvů v pořadí komponent toho prostoru, slovník /Colorants držící skutečné pole [/Separation name alternate tintfn] pro každý spot a slovník /MixingHints nesoucí /Solidities, /PrintingOrder a /DotGain, když jste je dodali. Vrácený název zdroje jde do SetFillColorSpace nebo SetStrokeColorSpace úplně stejně jako u jednodušších prostorů z článku o renderování spot barev Separation a DeviceN

HotPDF rozdělí master colorant názvy prostoru NChannel do slovníku Process pro čtyři komponenty CMYK a slovníku Colorants držícího jedno pole Separation na spot, se zkontrolovanou aritou před každým zápisem
Každý master název je nárokován přesně jednou a celková tint transformace, tinty na spot i tiskové pořadí se validují, než se emituje jakýkoli objekt

Co kontrola konzistence skutečně odmítá?

Odmítá strukturální nesourodost uvnitř prostoru a dělá to, než se zapíše jediný objekt. Colorant názvy musejí být unikátní a nesmí být prázdné, All ani None. Process a spot definice dohromady musejí pokrýt master názvy přesně, bez colorantu v obou rolích a bez nedefinovaného zbytku. Je-li alternativním prostorem DeviceCMYK, musejí být process komponenty Cyan, Magenta, Yellow, Black v tomto pořadí a počet process názvů musí odpovídat počtu komponent alternativního prostoru. Každá tint transformace i dot-gain funkce musí být nepřímý funkční objekt se správnou aritou vstupů a výstupů. Solidita musí být konečná hodnota uvnitř 0..1. Tiskové pořadí musí být prázdné nebo úplná permutace master názvů, nikdy částečný seznam. Co nedělá, je soudit barvu: nic tady nekontroluje, že vaše tint transformace Orange skutečně připomíná náplň v kanystru, že její CMYK sestava je zdravý zástupný proxy, nebo že dodaná solidita odpovídá naměřenému chování na substrátu. To jsou otázky tisku a měření a komponenta nemá mandát je odpovídat. Jednodušší přetížení jen pro process je designem ještě přísnější: vyrábí NChannel jen s process a záměrně odmítá přijímat názvy spotů, protože zápis spotu do pole názvů bez odpovídající položky /Colorants by vyrobil strukturálně nekorektní soubor a vymýšlení výchozí definice by bylo horší než selhání

Output intents PDF/X-6n musí pokrýt každý zaregistrovaný spot

Soubor PDF/X-6n s N coloranty deklaruje své coloranty dvakrát a obě deklarace se musí shodovat. AddPDFX6ExternalOutputIntent zapisuje referenci externího ICC profilu s jeho ColorantTable a předtím ValidateRegisteredSpotOutputColorants projde každý spot, který si dokument zaregistroval, a vyhodí, když v tabulce chybí. Kontrola běží oběma směry: jakmile output intent publikoval svůj seznam colorantů, pozdější registrace spotu na název mimo ten seznam je odmítnuta také. AddPDFX6ExternalOutputIntentSpotData přidává metadata po inku navrch a vymáhá vlastní pravidla, zejména že colorant nese buď hodnotu solidity, nebo spektrální data CxF/X-4, nikdy obojí. To je týž povrch shody, o němž pojednává článek o validaci PDF/A, PDF/X a PDF/UA

Spectral := TMemoryStream.Create;
try
  LoadCxFForInk('Orange', Spectral);   // payload ISO 17972-4
  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],                  // solidita pro jeden colorant
    Order,
    ['Orange'], [Spectral]);           // spektrální data pro jiný
finally
  Spectral.Free;
end;

Dva provozní detaily se lehce minou. Registr, který tohle všechno podepírá, je per dokument a maže se na dokumentových hranicích, takže načtení nového souboru do téže instance THotPDF nezdědí identity spotů předchozího dokumentu; ta izolace je smyslem věci, protože uniklá signatura by odmítla naprosto korektní práci v další zakázce. Strana profilu má navíc vlastní tvrdé limity: externí output intent vyžaduje cíl PDF 2.0, absolutní URL profilu HTTP nebo HTTPS, čtyřbajtovou signaturu ICC barevného prostoru a pro PDF/X-6n mezi 2 a 15 coloranty s odpovídající signaturou 2CLR až FCLR

Kde kontrola končí

Zpracování CxF/X-4 je kus, u něhož být upřímný. HotPDF aplikuje ohraničené strukturální bezpečnostní kontroly na spektrální proud a potvrdí, že identita inku uvnitř odpovídá colorantu, který jste pojmenovali. Uzavírá velikost payloadu, odmítá vložené nulové bajty, zavrhuje jakýkoli proud obsahující deklaraci DOCTYPE nebo ENTITY, vyžaduje rozeznatelný kořen CxF a vyžaduje přesně jeden element SpotInkCharacterisation nesoucí přesně jedno SpotInkName rovnající se názvu vašeho colorantu. To je brána proti deformovanému a nepřátelskému vstupu, ne schéma validátor. Není to kompletní implementace ISO 17972-4, neověřuje vaše spektrální měření a neodaudituje zpětně libovolné předem existující objektové grafy třetích stran ani vnitřní tabulky colorantů uvnitř vloženého ICC profilu. Závisí-li vaše workflow na úplné shodě CxF, validujte soubor specializovaným nástrojem, než ho předáte komponentě

Jedno sousedící omezení kousne lidi, kteří by ho nikdy nečekali. Měkká maska luminosity nemůže použít Separation, DeviceN ani NChannel jako svou /CS průhlednostní skupiny; míchací prostor skupiny musí být device nebo CIE-based prostor, takže barva spotu uvnitř skupiny se rozliší přes tint transformaci do alternativního prostoru, než se spočítá luminosity. RegisterLuminositySoftMaskState staví skupinu DeviceGray přesně proto, aby se to nemohlo pokazit omylem. Praktický důsledek je, že design plný spotů maskovaný tímto způsobem se hodnotí přes svůj CMYK proxy, ne přes svou náplň, což zvážíte, když ho srovnáváte se separovanou zkouškou, jak popisují poznámky o overprint proofingu a render zařízeních

Nic z toho neodbývá potřebu zkouškové desky, ale přemisťuje celou třídu předtiskových odmítnutí z tiskárny zpátky do buildu. API NChannel, Separation a output intentu PDF/X-6n popsaná zde putují ve standardní HotPDF Delphi Component pro Delphi a C++Builder, kde reference dokumentuje úplná rozložení záznamů a přesné podmínky, za nichž každé volání selhává zavřeně