Artykuł techniczny

Spójność farb spot NChannel w Delphi z HotPDF

Drukarnia przygotowalni odsyła zlecenie: ta sama farba spot rozseparowana na dwie płyty. HotPDF zapobiega temu w czasie tworzenia, zapisując NChannel jako pięcioelementową postać DeviceN z ISO 32000-2 i trzymając jedną kanoniczną przestrzeń alternatywną i transformację tinty na nazwę spot dla całego dokumentu, odrzucając drugą, sprzeczną definicję zamiast jej emitować

NChannel nie jest nazwą rodziny przestrzeni barw

Pierwszą rzeczą do odwykania jest sama nazwa. NChannel nie jest rodziną przestrzeni barw tak, jak rodzinami są Separation i DeviceN. ISO 32000-2 §8.6.6.5 opisuje go jako podtyp DeviceN, więc zgodna przestrzeń NChannel jest zapisywana jako pięcioelementowa tablica [/DeviceN names alternateSpace tintTransform attributes], a słownik atrybutów niesie /Subtype /NChannel. Tablicy [/NChannel ...] nie ma w specyfikacji. Jeśli kiedykolwiek zbudowałeś jedną ręcznie i patrzyłeś, jak RIP wzrusza ramionami, to dlatego

HotPDF zapisuje przestrzeń NChannel jako pięcioelementową tablicę DeviceN, której słownik atrybutów niesie Subtype NChannel razem z wpisami Process, Colorants i MixingHints, bo tablicy z nazwą rodziny NChannel w ogóle nie ma w specyfikacji
Zgodna przestrzeń NChannel to tablica DeviceN ze słownikiem atrybutów, dlatego zapisująca strona emituje wyłącznie tę postać, podczas gdy czytająca nadal przyjmuje stare tokeny

HotPDF popełnił tu kiedyś błąd i potem go naprawił, co warto powiedzieć wprost, bo kształtuje to, jak komponent zachowuje się dziś. Starsze wydania HotPDF emitowały postać z nazwą rodziny. THotPDF.RegisterNChannelColorSpace emituje teraz wyłącznie standardową postać DeviceN-plus-atrybuty, a ponieważ podtyp NChannel przyszedł w PDF 1.6, punkt wejścia producenta strzeże RequirePDFVersion(pdf16, ...) i po prostu odmawia na starszych celach. Strona renderująca jest celowo wyrozumialsza niż zapisująca: HPDFResolveColorSpace nadal przyjmuje stary token /NChannel jako rodzinę DeviceN, więc pliki ze starego zapisu renderują się dalej, ale cokolwiek HotPDF wypisuje z powrotem używa standardowego kodowania. Pobłażliwie na wejściu, surowo na wyjściu to właściwa asymetria, bo twój czytnik musi radzić sobie z plikami, których nie tworzył, a twój zapis nie ma takiej wymówki

Dlaczego jedna nazwa spot ląduje na dwóch płytach?

Bo nazwa coloranta spot to tożsamość płyty w skali dokumentu, nie lokalny argument. Dwa wywołania, które obie nazywają Orange, ale przekazują inną przestrzeń alternatywną albo tę samą przestrzeń z inną transformacją tinty, opisują dwie różne farby, które przypadkiem dzielą etykietę. RIP budujący separacje nie ma jak tego pogodzić, więc robi jedyną uczciwą rzecz i daje ci dwie płyty. HotPDF prowadzi więc kanoniczną sygnaturę per nazwa coloranta w skali dokumentu. RegisterSpotColorantDefinition składa tę sygnaturę z alternatywnej przestrzeni barw i kształtu funkcji tinty, a każde RegisterSeparation, RegisterSeparationFunc, RegisterSeparationLUT i definicja spot NChannel przez nią przechodzi. Gdy druga definicja się nie zgadza, wywołanie rzuca wyjątek zamiast po cichu rejestrować drugi wariant, a komunikat jest celowo konkretny co do trybu awarii, bo alternatywą jest odkrycie tego na dowodzie płyty trzy tygodnie później

Każde wywołanie rejestracji spot HotPDF przechodzi lejkiem przez RegisterSpotColorantDefinition, które zapisuje jedną kanoniczną sygnaturę przestrzeni alternatywnej i tinty per nazwa coloranta dla dokumentu i rzuca wyjątek, gdy przychodzi druga, sprzeczna definicja tej samej nazwy
Jedna nazwa, jedna sygnatura: druga, sprzeczna definicja spot jest odrzucana w czasie tworzenia, zamiast być odkrywana na dowodzie płyty
// Orange był już zarejestrowany względem DeviceCMYK
// z transformacją tinty 0 / 0.55 / 1 / 0 przy pełnej tintie.
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;

Rozdział nazw głównych na process i spot

Kompletny NChannel musi rozliczyć każdą ze swoich głównych nazw colorantów dokładnie raz, jako komponent process albo colorant spot. Zaawansowany przeciążony RegisterNChannelColorSpace bierze główne ColorantNames, ProcessColorantNames, przestrzeń alternatywną, ogólną transformację tinty, tablicę rekordów THPDFNChannelSpotColorant i opcjonalną kolejność druku. Każdy rekord spot niesie własną nazwę, własną jedno-wejściową transformację tinty Separation, opcjonalną solidność i opcjonalną funkcję rozrostu punktu. Ogólna transformacja tinty musi mapować N wejść na liczbę komponentów przestrzeni alternatywnej; każda tinta spot musi mapować jedno wejście na tę samą liczbę

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 tego wywołania HotPDF emituje słownik atrybutów, o który prosi specyfikacja: /Subtype /NChannel, słownik /Process, którego /ColorSpace to przestrzeń process i którego /Components wymienia nazwy process w kolejności komponentów tej przestrzeni, słownik /Colorants trzymający prawdziwą tablicę [/Separation name alternate tintfn] dla każdego spot oraz słownik /MixingHints niosący /Solidities, /PrintingOrder i /DotGain, jeśli je podałeś. Zwrócona nazwa zasobu idzie do SetFillColorSpace albo SetStrokeColorSpace dokładnie jak prostsze przestrzenie omówione w artykule o renderowaniu barw spot Separation i DeviceN

HotPDF dzieli główne nazwy colorantów przestrzeni NChannel na słownik Process dla czterech komponentów CMYK i słownik Colorants trzymający po jednej tablicy Separation na spot, z każdą arity sprawdzoną przed zapisem
Każda główna nazwa jest zajmowana dokładnie raz, a ogólna transformacja tinty, tinty per spot i kolejność druku są wszystkie walidowane, zanim jakikolwiek obiekt zostanie wyemitowany

Co tak naprawdę odrzuca kontrola spójności?

Odrzuca strukturalną niespójność wewnątrz przestrzeni i robi to, zanim jakikolwiek obiekt zostanie zapisany. Nazwy colorantów muszą być unikalne i nie mogą być puste, All ani None. Definicje process i spot razem muszą pokrywać nazwy główne dokładnie, bez coloranta w obu rolach i bez żadnego niezdefiniowanego. Gdy przestrzeń alternatywna to DeviceCMYK, komponenty process muszą być Cyan, Magenta, Yellow, Black w tej kolejności, a liczba nazw process musi zgadzać się z liczbą komponentów alternatywnej. Każda transformacja tinty i funkcja rozrostu punktu musi być pośrednim obiektem funkcji o właściwej arity wejścia i wyjścia. Solidność musi być skończoną wartością w 0..1. Kolejność druku musi być pusta albo pełną permutacją nazw głównych, nigdy listą częściową. Czego nie robi, to osądzania barwy: nic tutaj nie sprawdza, czy twoja transformacja tinty Orange faktycznie przypomina farbę w puszce, czy jej budowa CMYK jest znośnym przybliżeniem albo czy podana solidność zgadza się ze zmierzonym zachowaniem na podłożu. To pytania maszyny drukarskiej i pomiaru, a komponent nie ma mandatu, by na nie odpowiadać. Prostsze przeciążenie tylko-process jest z designu jeszcze surowsze: produkuje NChannel wyłącznie process i celowo odmawia przyjęcia nazw spot, bo zapisanie spot w tablicy nazw bez pasującego wpisu /Colorants wyprodukowałoby strukturalnie niezgodny plik, a fabrykowanie domyślnej definicji byłoby gorsze niż porażka

Output intenty PDF/X-6n muszą pokrywać każdy zarejestrowany spot

Plik PDF/X-6n z N colorantami deklaruje swoje coloranty dwukrotnie i obie deklaracje muszą się zgadzać. AddPDFX6ExternalOutputIntent zapisuje referencję zewnętrznego profilu ICC z jego ColorantTable, a zanim to zrobi, ValidateRegisteredSpotOutputColorants przechodzi każdy spot zarejestrowany przez dokument i rzuca wyjątek, jeśli któregoś brakuje w tabeli. Kontrola działa w obie strony: gdy output intent opublikuje już swoją listę colorantów, późniejsza rejestracja spot dla nazwy spoza tej listy jest także odrzucana. AddPDFX6ExternalOutputIntentSpotData dokłada na to metadane per farba i egzekwuje własne reguły, w szczególności że colorant niesie albo wartość solidności, albo dane spektralne CxF/X-4, nigdy oba. To ta sama powierzchnia zgodności omówiona w tekście o walidacji PDF/A, PDF/X i PDF/UA

Spectral := TMemoryStream.Create;
try
  LoadCxFForInk('Orange', Spectral);   // ładunek 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],                  // solidność dla jednego coloranta
    Order,
    ['Orange'], [Spectral]);           // dane spektralne dla innego
finally
  Spectral.Free;
end;

Dwa operacyjne detale łatwo przegapić. Rejestr, który to wszystko podpiera, jest per dokument i jest czyszczony na granicach dokumentów, więc wczytanie nowego pliku do tej samej instancji THotPDF nie dziedziczy tożsamości spot poprzedniego dokumentu; ta izolacja jest sensem, bo przeciekła sygnatura odrzuciłaby całkiem poprawną pracę w kolejnym zleceniu. A strona profili ma własne twarde limity: zewnętrzny output intent wymaga celu PDF 2.0, absolutnego URL profilu HTTP albo HTTPS, czterobajtowej sygnatury przestrzeni barw ICC, a dla PDF/X-6n od 2 do 15 colorantów z pasującą sygnaturą od 2CLR do FCLR

Gdzie kontrola się kończy

Obsługa CxF/X-4 to część, o której trzeba mówić uczciwie. HotPDF nakłada ograniczone strukturalne kontrole bezpieczeństwa na strumień spektralny i potwierdza, że tożsamość farby wewnątrz niego zgadza się z nazwanym przez ciebie colorantem. Zasuwa rozmiar ładunku, odrzuca wbudowane bajty null, odmawia każdego strumienia zawierającego deklarację DOCTYPE albo ENTITY, wymaga rozpoznawalnego korzenia CxF i wymaga dokładnie jednego elementu SpotInkCharacterisation niosącego dokładnie jedno SpotInkName równe nazwie twojego coloranta. To brama przeciw zniekształconemu i wrogemu wejściu, nie walidator schematu. Nie jest pełną implementacją ISO 17972-4, nie weryfikuje twoich pomiarów spektralnych i nie audytuje wstecz dowolnych wcześniej istniejących grafów obiektów stron trzecich ani wewnętrznych tabel colorantów wewnątrz osadzonego profilu ICC. Jeśli twój przepływ pracy zależy od pełnej zgodności CxF, zwaliduj plik dedykowanym narzędziem, zanim oddasz go komponentowi

Jedno sąsiednie ograniczenie kąsi ludzi, którzy nigdy nie spodziewali się go spotkać. Miękka maska luminacyjna nie może używać Separation, DeviceN ani NChannel jako /CS swojej grupy przezroczystości; przestrzeń mieszania grupy musi być urządzeniowa albo oparta na CIE, więc farba spot wewnątrz grupy jest rozwiązywana przez swoją transformację tinty do przestrzeni alternatywnej, zanim zostanie policzona luminancja. RegisterLuminositySoftMaskState buduje grupę DeviceGray dokładnie po to, żeby to nie poszło źle przypadkiem. Praktyczna konsekwencja: projekt bogaty w spot maskowany w ten sposób jest oceniany przez swoje przybliżenie CMYK, nie przez swoją farbę, co ma znaczenie, gdy porównujesz go z dowodem separacji, jak opisano w notkach o proofingu overprint i urządzeniach renderujących

Nic z tego nie usuwa potrzeby dowodu płyty, ale przenosi całą klasę odrzuceń przygotowalni z sali maszyn z powrotem do budowania. API NChannel, Separation i output intentów PDF/X-6n opisane tutaj przychodzą w standardowym HotPDF Delphi Component dla Delphi i C++Builder, gdzie referencja dokumentuje pełne układy rekordów i dokładne warunki, w których każde wywołanie zawodzi bezpiecznie