Technisch artikel

NChannel-steunkleurenconsistentie in Delphi met HotPDF

Een prepress-bedrijf stuurt de klus terug: dezelfde steunkleur die op twee platen is gescheiden. HotPDF voorkomt dat op auteursmoment door NChannel te schrijven als de ISO 32000-2 vijfelementige DeviceN-vorm en door per spotnaam voor het hele document één canonieke alternatieve ruimte en tinttransformatie aan te houden, waarbij de tweede, conflicterende definitie wordt geweigerd in plaats van geëmitteerd

NChannel is geen familienaam voor kleurruimten

Het eerste wat u moet afleren is de naam zelf. NChannel is geen kleurruimtefamilie zoals Separation en DeviceN families zijn. ISO 32000-2 §8.6.6.5 beschrijft het als een subtype van DeviceN, dus een conforme NChannel-ruimte wordt geschreven als de vijfelementige array [/DeviceN names alternateSpace tintTransform attributes], en het attributes-dictionary draagt /Subtype /NChannel. Er bestaat geen [/NChannel ...]-array in de specificatie. Als u er ooit zelf een met de hand hebt gebouwd en een RIP hebt zien met de schouders ophalen, dat is de reden

HotPDF schrijft een NChannel-ruimte als de vijfelementige DeviceN-array waarvan het attributes-dictionary Subtype NChannel draagt samen met de entries Process, Colorants en MixingHints, omdat er in de specificatie helemaal geen NChannel-familienaamarray bestaat
Een conforme NChannel-ruimte is een DeviceN-array met een attributes-dictionary, daarom emitteert de writer alleen deze vorm terwijl de reader het legacy-token nog steeds accepteert

HotPDF heeft dit één keer fout gedaan en het daarna gerepareerd, en dat is het eerlijk zeggen waard omdat het bepaalt hoe de component zich vandaag gedraagt. Oudere HotPDF-releases emitteerden de familienaamvorm. THotPDF.RegisterNChannelColorSpace emitteert nu alleen de standaard DeviceN-plus-attributes-vorm, en omdat het NChannel-subtype in PDF 1.6 kwam, gate het producer-entrypoint op RequirePDFVersion(pdf16, ...) en weigert het gewoon op oudere targets. De renderkant is opzettelijk vergevingsgezinder dan de writer: HPDFResolveColorSpace accepteert het legacy-token /NChannel nog steeds als een DeviceN-familie zodat bestanden uit de oude writer blijven renderen, maar alles wat HotPDF terugschrijft gebruikt de standaardencoding. Soepel op input, streng op output is hier de juiste asymmetrie, want uw reader moet omgaan met bestanden die het niet heeft gemaakt, terwijl uw writer die excuus niet heeft

Waarom belandt één spotnaam op twee platen?

Omdat een spotcolorantnaam een documentbrede plaatidentiteit is, geen lokaal argument. Twee aanroepen die allebei Orange noemen maar een andere alternatieve ruimte overhandigen, of dezelfde alternatieve ruimte met een andere tinttransformatie, beschrijven twee verschillende inkten die toevallig een label delen. Een RIP die separations bouwt heeft geen manier om dat te reconciliëren, dus doet het het enige eerlijke en geeft u twee platen. HotPDF houdt daarom per document een canonieke signature per colorantnaam bij. RegisterSpotColorantDefinition stelt die signature samen uit de alternatieve kleurruimte en de vorm van de tintfunctie, en elke RegisterSeparation, RegisterSeparationFunc, RegisterSeparationLUT en NChannel-spotdefinitie gaat erdoorheen. Wanneer een tweede definitie ervan afwijkt, gooit de aanroep een exceptie in plaats van stilletjes een tweede variant te registreren, en de melding is opzettelijk specifiek over de faalmodus, want het alternatief is het drie weken later op een plaatproef ontdekken

Elke HotPDF-spotregistratieaanroep loopt via RegisterSpotColorantDefinition, dat per colorantnaam voor het document één canonieke alternatieve ruimte en tintsignature vastlegt en een exceptie gooit zodra een tweede, afwijkende definitie van dezelfde naam binnenkomt
Eén naam, één signature: de tweede, conflicterende definitie van een spot wordt op auteursmoment geweigerd in plaats van op een plaatproef ontdekt
// Orange was al geregistreerd tegen DeviceCMYK met de
// tinttransformatie 0 / 0.55 / 1 / 0 bij volledige 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;

De master-namen opsplitsen in process en spot

Een complete NChannel moet voor elk van zijn master-colorantnamen exact één keer verantwoording afleggen, als procescomponent of als spotcolorant. De geavanceerde overload van RegisterNChannelColorSpace neemt de master-ColorantNames, de ProcessColorantNames, de alternatieve ruimte, de algehele tinttransformatie, een array met THPDFNChannelSpotColorant-records, en een optionele drukvolgorde. Elk spotrecord draagt zijn eigen naam, zijn eigen Separation-tinttransformatie met één input, een optionele solidity, en een optionele dot gain-functie. De algehele tinttransformatie moet N inputs mappen op het componentaantal van de alternatieve ruimte; elke spottint moet één input op datzelfde aantal mappen

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

Uit die aanroep emitteert HotPDF het attributes-dictionary dat de spec vraagt: /Subtype /NChannel, een /Process-dictionary waarvan /ColorSpace de procesruimte is en waarvan /Components de procesnamen in de componentvolgorde van die ruimte opsomt, een /Colorants-dictionary met voor elke spot een echte [/Separation name alternate tintfn]-array, en een /MixingHints-dictionary met /Solidities, /PrintingOrder en /DotGain wanneer u die leverde. De teruggegeven resourcenaam gaat naar SetFillColorSpace of SetStrokeColorSpace, precies zoals bij de simpelere ruimten die het artikel over Separation- en DeviceN-spotkleurrendering behandelt

HotPDF splitst de master-colorantnamen van een NChannel-ruimte in een Process-dictionary voor de vier CMYK-componenten en een Colorants-dictionary met per spot één Separation-array, waarbij elke ariteit wordt gecontroleerd vóór het schrijven
Elke master-naam wordt exact één keer geclaimd, en de algehele tinttransformatie, de tints per spot en de drukvolgorde worden allemaal gevalideerd voordat er ook maar één object wordt geëmitteerd

Wat wijst de consistentiecontrole werkelijk af?

Hij wijst structurele incoherentie binnen de ruimte af, en doet dat voordat er ook maar één object is geschreven. Colorantnamen moeten uniek zijn en mogen niet leeg zijn, All of None. Proces- en spotdefinities moeten samen de master-namen exact dekken, zonder dat een colorant in beide rollen voorkomt en zonder dat er een ongedefinieerd blijft. Wanneer de alternatieve ruimte DeviceCMYK is, moeten de procescomponenten Cyan, Magenta, Yellow, Black in die volgorde zijn, en het procesnaamaantal moet matchen met het componentaantal van de alternatieve ruimte. Elke tinttransformatie en dot gain-functie moet een indirect functieobject zijn met de juiste input- en outputariteit. Solidity moet een eindige waarde binnen 0..1 zijn. De drukvolgorde moet leeg zijn of een volledige permutatie van de master-namen, nooit een partiële lijst. Wat hij niet doet is kleur beoordelen: niks hier controleert of uw Orange-tinttransformatie werkelijk lijkt op de inkt in de blik, of zijn CMYK-opbouw een zinnige proxy is, of dat de solidity die u leverde matcht met gemeten gedrag op het substraat. Dat zijn pers- en meetvragen, en de component heeft geen standing om ze te beantwoorden. De simpelere process-only overload is by design nog strenger: hij produceert een process-only NChannel en weigert bewust spotnamen te accepteren, want een spot in de namenarray schrijven zonder matchende /Colorants-entry zou een structureel niet-conform bestand opleveren, en een standaarddefinitie verzinnen is erger dan falen

PDF/X-6n output intents moeten elke geregistreerde spot dekken

Een N-colorant PDF/X-6n-bestand declareert zijn colorants twee keer, en de twee declaraties moeten overeenkomen. AddPDFX6ExternalOutputIntent schrijft de externe ICC-profielreferentie met zijn ColorantTable, en voordat het dat doet, loopt ValidateRegisteredSpotOutputColorants elke spot na die het document heeft geregistreerd en gooit het een exceptie als er één in de tabel ontbreekt. De controle loopt in beide richtingen: zodra een output intent zijn colorantlijst heeft gepubliceerd, wordt een latere spotregistratie voor een naam buiten die lijst ook geweigerd. AddPDFX6ExternalOutputIntentSpotData voegt daarbovenop de per-inkt-metadata toe, en handhaaft zijn eigen regels, onder andere dat een colorant óf een solidity-waarde óf CxF/X-4-spectrale data draagt, nooit beide. Dit is hetzelfde conformantievlak als besproken in het stuk over PDF/A-, PDF/X- en PDF/UA-validatie

Spectral := TMemoryStream.Create;
try
  LoadCxFForInk('Orange', Spectral);   // ISO 17972-4-payload
  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],                  // solidity voor één colorant
    Order,
    ['Orange'], [Spectral]);           // spectrale data voor een andere
finally
  Spectral.Free;
end;

Twee operationele details zijn makkelijk te missen. Het register dat dit alles onderbouwt is per document en wordt gewist bij documentgrenzen, dus het laden van een nieuw bestand in dezelfde THotPDF-instantie erft de spotidentiteiten van het vorige document niet; die isolatie is het punt, want een gelekte signature zou volkomen geldig werk in de volgende klus weigeren. En de profielkant heeft eigen harde limieten: een externe output intent vereist een PDF 2.0-target, een absolute HTTP- of HTTPS-profiel-URL, een vierbyte ICC-kleurruimtesignature, en voor PDF/X-6n tussen 2 en 15 colorants met een matchende 2CLR tot FCLR-signature

Waar de controles stoppen

De CxF/X-4-afhandeling is het deel waar u eerlijk over moet zijn. HotPDF past begrensde structurele veiligheidscontroles toe op de spectrale stream en bevestigt dat de inktidentiteit erin matcht met de colorant die u noemde. Het capst de payloadgrootte, wijst ingesloten null-bytes af, weigert elke stream met een DOCTYPE- of ENTITY-declaratie, vereist een herkenbare CxF-root, en vereist exact één SpotInkCharacterisation-element dat exact één SpotInkName draagt die gelijk is aan uw colorantnaam. Dat is een poort tegen misvormde en vijandige input, geen schema-validator. Het is geen complete ISO 17972-4-implementatie, het verifieert uw spectrale metingen niet, en het her-audit geen willekeurige reeds bestaande third-party objectgraphs of de interne coloranttabellen binnen een ingesloten ICC-profiel. Als uw workflow van volledige CxF-conformance afhangt, valideer het bestand met een speciaal gereedschap voordat u het aan de component geeft

Eén aangrenzende beperking bijt mensen die haar nooit verwachtten te ontmoeten. Een luminosity soft mask kan Separation, DeviceN of NChannel niet gebruiken als /CS van zijn transparantiegroep; de groep-blending-ruimte moet een device- of CIE-based-ruimte zijn, dus spotverf binnen de groep wordt via zijn tinttransformatie naar de alternatieve ruimte opgelost voordat de luminosity wordt berekend. RegisterLuminositySoftMaskState bouwt een DeviceGray-groep juist zodat dit niet per ongeluk mis kan gaan. De praktische consequentie is dat een spotrijk ontwerp dat op deze manier gemaskd is wordt beoordeeld via zijn CMYK-proxy, niet via zijn inkt, wat telt wanneer u hem vergelijkt met een gescheiden proef zoals beschreven in de notities over overprint-proofing en render devices

Geen van dit alles haalt de noodzaak van een plaatproef weg, maar het verplaatst een hele klasse prepress-afwijzingen van de drukzaal terug naar de build. De hier beschreven NChannel-, Separation- en PDF/X-6n output intent-API's zitten in de standaard HotPDF Delphi Component voor Delphi en C++Builder, waar de referentie de volledige recordlayouts documenteert en de exacte voorwaarden waaronder elke aanroep faalt en dichtklapt