Препрес-бюро повертає роботу: та сама плашкова фарба розділилася на дві форми. HotPDF запобігає цьому ще на етапі авторингу, записуючи NChannel у пʼятиелементній формі DeviceN з ISO 32000-2 і тримаючи один канонічний альтернативний простір та одне перетворення відтінку на кожну назву фарби в межах усього документа; друге конфліктне визначення відхиляється, а не записується
NChannel — це не назва сімейства колірних просторів
Перше, що варто забути, — сама назва. NChannel — не сімейство колірних просторів, як Separation і DeviceN. ISO 32000-2 §8.6.6.5 описує його як підтип DeviceN, тож коректний простір NChannel записується пʼятиелементним масивом [/DeviceN names alternateSpace tintTransform attributes], а словник атрибутів несе /Subtype /NChannel. Масиву [/NChannel ...] у специфікації немає. Якщо ви колись зібрали такий вручну й бачили, як RIP байдуже дивиться на нього, — ось чому
HotPDF колись помилявся в цьому, а потім виправив, і про це варто сказати прямо, бо це визначає, як компонент поводиться сьогодні. Старіші випуски HotPDF видавали форму з назвою сімейства. THotPDF.RegisterNChannelColorSpace тепер видає лише стандартну форму DeviceN зі словником атрибутів, і оскільки підтип NChannel зʼявився в PDF 1.6, точка входу продюсера спирається на RequirePDFVersion(pdf16, ...) і просто відмовляє на старіших цілях. Рендерна сторона свідомо поблажливіша за записувача: HPDFResolveColorSpace досі приймає застарілий токен /NChannel як сімейство DeviceN, тож файли від старого записувача продовжують рендеритися, але все, що HotPDF записує назад, використовує стандартне кодування. Поблажливість на вході, строгість на виході — правильна асиметрія тут, бо ваш читач мусить справлятися з файлами, яких він не створював, а ваш записувач такого виправдання не має
Чому одна назва плашкової фарби опиняється на двох формах?
Бо назва плашкового колоранта — це ідентичність форми в масштабі документа, а не локальний аргумент. Два виклики, що обидва називають Orange, але передають різний альтернативний простір, або той самий простір з іншим перетворенням відтінку, описують дві різні фарби, які випадково поділяють етикетку. RIP, що будує цвітоділення, не має способу це узгодити, тож робить єдине чесне — дає вам дві форми. Тому HotPDF веде канонічний підпис на кожну назву колоранта в межах документа. RegisterSpotColorantDefinition складає цей підпис з альтернативного колірного простору та форми функції відтінку, і кожне RegisterSeparation, RegisterSeparationFunc, RegisterSeparationLUT і визначення плашки NChannel проходить через нього. Коли друге визначення розходиться, виклик кидає виняток замість того, щоб мовчки зареєструвати другий варіант, і повідомлення свідомо конкретне щодо режиму відмови, бо альтернатива — виявити це на кольоропробі форми за три тижні
// Orange уже було зареєстровано з DeviceCMYK і перетворенням
// відтінку 0 / 0.55 / 1 / 0 при повному відтінку.
Conflicting := Pdf.RegisterExponentialFunction(
Domain1, NoInk, OtherOrangeCMYK, 1, []);
try
Pdf.RegisterSeparationFunc('Orange', 'DeviceCMYK', Conflicting);
except
on E: Exception do
// 'Плашковий колорант "Orange" має неузгоджений альтернативний
// колірний простір або визначення відтінку в цьому документі'
LogPrepressWarning(E.Message);
end;
Розподіл головних назв між процесними компонентами та плашками
Повний NChannel мусить врахувати кожну зі своїх головних назв колорантів рівно один раз — як процесний компонент або як плашковий колорант. Розширене перевантаження RegisterNChannelColorSpace приймає головні ColorantNames, ProcessColorantNames, альтернативний простір, загальне перетворення відтінку, масив записів THPDFNChannelSpotColorant і необовʼязковий порядок друку. Кожен запис плашки несе власну назву, власне перетворення відтінку Separation з одним входом, необовʼязкове значення solidity та необовʼязкову функцію растискивання. Загальне перетворення відтінку мусить відображати N входів у кількість компонентів альтернативного простору; кожна плашкова функція відтінку мусить відображати один вхід у ту саму кількість
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);
З цього виклику HotPDF видає словник атрибутів, якого вимагає специфікація: /Subtype /NChannel, словник /Process, чий /ColorSpace — процесний простір, а /Components перелічує процесні назви у порядку компонентів того простору, словник /Colorants, що тримає справжній масив [/Separation name alternate tintfn] для кожної плашки, і словник /MixingHints з /Solidities, /PrintingOrder і /DotGain, якщо ви їх подали. Повернуту назву ресурсу передаєте в SetFillColorSpace або SetStrokeColorSpace точно так, як у простіших просторах зі статті про рендеринг плашкових кольорів Separation і DeviceN
Що насправді відхиляє перевірка узгодженості?
Вона відхиляє структурну неузгодженість усередині простору і робить це до того, як буде записано хоч один обʼєкт. Назви колорантів мусять бути унікальними й не мусять бути порожніми, All чи None. Процесні та плашкові визначення разом мусять покривати головні назви точно, без колоранта, що зʼявляється в обох ролях, і без залишеного невизначеним. Коли альтернативний простір — DeviceCMYK, процесні компоненти мусять бути Cyan, Magenta, Yellow, Black саме в цьому порядку, а кількість процесних назв мусить збігатися з кількістю компонентів альтернативного простору. Кожне перетворення відтінку й кожна функція растискивання мусять бути непрямим обʼєктом-функцією з правильною арністю входів і виходів. Solidity мусить бути скінченним значенням у межах 0..1. Порядок друку мусить бути порожнім або повною перестановкою головних назв, ніколи не частковим списком. Чого вона не робить, то не судить колір: тут ніщо не перевіряє, що ваше перетворення відтінку для Orange справді схоже на фарбу з банки, що його CMYK-суміш — розумний проксі, або що подана вами solidity збігається з виміряною поведінкою на матеріалі. Це питання друкарні та вимірювань, і компонент не має мандату їх вирішувати. Простіше перевантаження лише для процесних компонентів за проєктом суворіше ще й тому: воно видає NChannel лише з процесними компонентами і свідомо відмовляється приймати плашкові назви, бо запис плашки в масив назв без відповідного запису /Colorants дав би структурно некоректний файл, а вигадування визначення за замовчуванням було б гіршим за відмову
Output intent PDF/X-6n мусить покривати кожну зареєстровану плашку
Файл PDF/X-6n з N колорантами декларує свої колоранти двічі, і обидві декларації мусять збігатися. AddPDFX6ExternalOutputIntent записує посилання на зовнішній ICC-профіль із його ColorantTable, а перед цим ValidateRegisteredSpotOutputColorants обходить кожну плашку, яку зареєстрував документ, і кидає виняток, якщо якоїсь немає в таблиці. Перевірка працює в обидва боки: щойно output intent опублікував свій список колорантів, пізніша реєстрація плашки з назвою поза цим списком також відмовляється. AddPDFX6ExternalOutputIntentSpotData додає до цього метадані по кожній фарбі та має власні правила, зокрема те, що колорант несе або значення solidity, або спектральні дані CxF/X-4, ніколи не обидва. Це та сама поверхня відповідності, що обговорюється у матеріалі про валідацію PDF/A, PDF/X і PDF/UA
Spectral := TMemoryStream.Create;
try
LoadCxFForInk('Orange', Spectral); // корисне навантаження 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], // solidity для одного колоранта
Order,
['Orange'], [Spectral]); // спектральні дані для іншого
finally
Spectral.Free;
end;
Дві експлуатаційні дрібниці легко пропустити. Реєстр, на якому тримається все це, належить документу і очищається на межах документів, тож завантаження нового файлу в той самий екземпляр THotPDF не успадковує плашкові ідентичності попереднього документа; ізоляція — це і є мета, бо витік підсунув би відмову цілком валідній роботі в наступному завданні. А сторона профілю має власні жорсткі межі: зовнішній output intent вимагає цілі PDF 2.0, абсолютного URL профілю HTTP або HTTPS, чотирибайтового підпису колірного простору ICC, а для PDF/X-6n — від 2 до 15 колорантів зі збіжним підписом від 2CLR до FCLR
Де перевірка зупиняється
Обробка CxF/X-4 — та частина, про яку варто говорити чесно. HotPDF застосовує до спектрального потоку обмежені структурні перевірки безпеки і підтверджує, що ідентичність фарби всередині нього збігається з названим вами колорантом. Він обмежує розмір корисного навантаження, відхиляє вбудовані нульові байти, відмовляє будь-якому потоку з декларацією DOCTYPE або ENTITY, вимагає впізнаваного кореня CxF і вимагає рівно одного елемента SpotInkCharacterisation, що несе рівно один SpotInkName, який дорівнює назві вашого колоранта. Це заслінка від пошкодженого й ворожого вводу, а не валідатор схеми. Це не повна реалізація ISO 17972-4, він не перевіряє ваших спектральних вимірювань і не робить зворотного аудиту довільних уже наявних сторонніх графів обʼєктів чи внутрішніх таблиць колорантів усередині вбудованого ICC-профілю. Якщо ваш робочий процес залежить від повної відповідності CxF, провалідуйте файл окремим інструментом, перш ніж передавати його компоненту
Одне сусіднє обмеження кусає людей, які ніколи не чекали з ним зустрітися. Мʼяка маска світності (luminosity soft mask) не може використовувати Separation, DeviceN або NChannel як /CS групи прозорості; простір змішування групи мусить бути апаратним або CIE-простором, тож плашкову фарбу всередині групи розвʼязують через її перетворення відтінку в альтернативний простір до обчислення світності. RegisterLuminositySoftMaskState будує групу DeviceGray саме для того, щоб це не могло зламатися випадково. Практичний наслідок: дизайн, багатий на плашки і замаскований так, оцінюється через свій CMYK-проксі, а не через фарбу, що має значення, коли ви порівнюєте його з цвітоділеною кольоропробою, як описано в нотатках про препрес-проби та пристрої рендерингу з урахуванням overprint
Ніщо з цього не прибирає потреби в кольоропробі форми, але воно пересуває цілий клас препрес-відмов із друкарні назад у збірку. Описані тут API NChannel, Separation та output intent PDF/X-6n постачаються зі стандартним HotPDF Delphi Component для Delphi і C++Builder, де довідник документує повні структури записів і точні умови, за яких кожен виклик зазнає відмови