Функції PDF — це один із найменш помітних розділів специфікації. Більшість розробників стикаються з ними лише один раз, коли осьовому затіненню типу 2 (Type 2 axial shading) потрібно виконати плавний перехід між двома кольорами, і більше до них не повертаються. Це прикро, оскільки механізм функцій є невеликим інтерпретатором загального призначення, який формат повторно використовує для затінення, передавальних функцій, півтонових точкових функцій, плашкових відтінків та кривих передачі м'яких масок. З чотирьох типів функцій тип 0 є найпотужнішим і водночас найменш зрозумілим. Це семплована функція (sampled function): багатовимірна сітка вихідних значень, між якими програма читання виконує інтерполяцію. Оскільки сітка може містити будь-які числа, які ви туди запишете, функція типу 0 може виражати довільне нелінійне відображення, що є точною формою таблиці пошуку кольорів (color lookup table).
У цій статті ми розглянемо словник типу 0, як його визначає параграф §7.10.2 стандарту ISO 32000-1, а потім покажемо два випадки, які мають найбільше значення в конвеєрі обробки документів: коригування кольору RGB-в-RGB з трьома входами та перетворення відтінку плашкового кольору з одним входом. Один і той самий конструктор семплованих функцій обслуговує обидва випадки, і різниця між ними полягає виключно в кількості входів сітки.
Вибіркова функція — це сітка, яку інтерполює програма читання
Функція типу 0 відображає вектор з m входів у вектор з n виходів шляхом збереження семплів на регулярній сітці та інтерполяції між ними. Параграф §7.10.2 стандарту ISO 32000-1 перераховує ключі, що описують цю сітку. /Domain містить по два числа на кожен вхід — нижню та верхню межі кожної осі входу. /Range містить по два числа для кожного вихідного компонента. /Size — це масив з m цілих чисел, який вказує кількість семплів уздовж кожної осі входу; наприклад, сітка з дванадцятьма семплами на кожній стороні у трьох вимірах має /Size [12 12 12] і зберігає 1728 точок сітки. /BitsPerSample задає точність кожного збереженого значення; HotPDF приймає 1, 2, 4, 8, 12, 16, 24 та 32 біти, що відповідає значенням, дозволеним таблицею 38.
Потік семплів зчитується у фіксованому порядку. Перший вимір входу змінюється найшвидше, потім другий і так далі, і в кожній точці сітки послідовно зберігаються n вихідних компонентів. Для таблиці RGB-в-RGB це три байти на точку сітки при 8 бітах, які розташовані в порядку: червоний вихід, зелений вихід, синій вихід, починаючи з першого кроку по червоному входу. Еще два ключі відображають безперервний світ на цілочисельну сітку. /Encode відображає кожен вхід з його інтервалу /Domain у діапазон індексів семплів від 0 до Size[i] - 1, а /Decode відображає вихідні збережені цілі числа назад в інтервали /Range. Якщо залишити їх за замовчуванням, вхід у діапазоні [0 1] акуратно лягає на всю сітку, а збережений байт 255 декодується у верхню межу свого вихідного діапазону, що і потрібно для [0,1]-нормалізованого Color LUT.
Порядок 1 проти Порядку 3
Між точками сітки програма читання має виконувати інтерполяцію, і ключ /Order вибирає спосіб її виконання. /Order 1 — це мультилінійна інтерполяція: лінійна за однією віссю, білінійна за двома, трилінійна за трьома. Вона швидка, це саме те, що робить апаратне забезпечення у більшості засобів перегляду, і для плавного перетворення кольору її зазвичай не відрізнити від складніших методів. /Order 3 запитує кубічну сплайн-інтерполяцію, яка будує плавнішу криву через семпли за рахунок більшого обсягу обчислень і ширшої області підтримки навколо кожної обчислюваної точки.
Компроміс полягає між щільністю сітки та плавністю кривої. Кубічний порядок виправдовує себе, коли сітка рідка, а відображення має помітну кривизну, оскільки пряма лінія між двома віддаленими семплами може спотворити тонову криву так, що це буде помітно на градієнтах. Коли сітка щільна, сегменти досить короткі, щоб лінійна інтерполяція точно повторювала криву, і кубічна дає мало переваг. Практичне правило полягає в тому, щоб використовувати /Order 3 лише для малих сіток або крутих перетворень, а в інших випадках залишати лінійне значення за замовчуванням. Зверніть увагу, що /Order застосовується лише до функцій типу 0, і HotPDF відхиляє будь-які значення, крім 1 або 3.
3D LUT: три входи, три виходи
Корекція кольору RGB-в-RGB є класичним прикладом для сітки з трьома входами — це традиційний 3D LUT, що використовується для градації кольорів та калібрування пристроїв. Кожна вісь куба є одним вхідним каналом, кожна точка сітки зберігає скориговану трійку RGB для цієї координати входу, а програма читання трилінійно інтерполює кутові семпли навколо будь-якого вхідного кольору. Три входи тут є неминучими, оскільки скоригований червоний колір може залежати від вхідних зеленого та синього, а не лише від червоного; крива для окремого каналу не може виразити взаємний вплив каналів, а куб може.
HotPDF створює потік типу 0 за допомогою методу RegisterSampledFunction, який приймає безпосередньо /Domain, /Range, /Size, /BitsPerSample та байти семплів і повертає об'єкт функції. Для стандартного нормалізованого куба ви передаєте межі [0,1] для всіх трьох вхідних і трьох вихідних осей, розмір N x N x N та лінійну таблицю семплів. Конструктор перевіряє сумісність кількості байтів із сіткою: для вирівняної по байтах глибини він очікує OutputCount x (BitsPerSample div 8) x добуток розмірів, і видає помилку, якщо масив має неправильну довжину, тому помилково обчислений крок призведе до збою на етапі реєстрації, а не до відображення сміття згодом.
const
N = 17; // 17 x 17 x 17 cube, the common ICC LUT resolution
var
LutFn: THPDFStreamObject;
Samples: TBytes;
begin
// Fill Samples with N*N*N grid points, 3 bytes each (R,G,B output),
// red input varying fastest. Build the corrected triple for each
// grid coordinate with your ICC-managed conversion, then store it.
SetLength(Samples, N * N * N * 3);
BuildCorrectedCube(Samples, N); // your color-managed fill
LutFn := Pdf.RegisterSampledFunction(
[0,1, 0,1, 0,1], // /Domain: three input axes on [0,1]
[0,1, 0,1, 0,1], // /Range: three output channels on [0,1]
[N, N, N], // /Size: the cube resolution per axis
8, // /BitsPerSample
Samples,
1); // /Order 1 = trilinear
end;
Колориметрична коректність куба залежить від того, як ви його заповнюєте, а не від функції PDF. Найнадійніший шлях — обчислити кожну точку сітки за допомогою перетворення під керуванням ICC (такого ж двигуна, який керує програмною кольоропробою), щоб числа в сітці відповідали визначеним вхідним і вихідним профілям. Зареєструйте профілі, що обмежують перетворення, за допомогою RegisterICCProfile, який створює колірний простір ICCBased (1, 3 або 4 компоненти) і повертає ім'я ресурсу, яке ви можете призначити вмісту, який використовує LUT. Функція типу 0 містить таблицю інтерполяції; профіль ICC визначає значення кінцевих точок.
Одновимірний випадок: перетворення відтінку плашкового кольору
Плашкаві (Separation) колірні простори використовують той самий механізм для зовсім іншого завдання. Колірний простір Separation, визначений в ISO 32000-1 §8.6.6.4, представляє один барвник — плашкову фарбу на кшталт Pantone або лак, пов'язуючи ім'я з перетворенням відтінку (tint transform): функцією, яка відображає одновимірне значення відтінку (від 0 для відсутності фарби до 1 для повної заливки) в альтернативний колірний простір, який пристрій може реально відтворити, зазвичай CMYK. Тобто перетворення відтінку часто реалізується як функція типу 0, і в цьому випадку сітка має рівно одну вісь входу.
Це чіткий контраст із 3D LUT. Плашкова фарба — це один ступінь свободи, тому її перетворенню відтінку потрібен один вхід, а сітка є лінією семплів, кожен з яких містить значення CMYK (або інше альтернативне значення) для цього рівня відтінку. Куб RGB потребує трьох входів, оскільки його область визначення є тривимірною і канали взаємодіють між собою. Один і той самий тип функції, однакові правила інтерполяції, але різна розмірність; специфікація повторно використовує один обчислювач і дозволяє /Size визначати, чи ви працюєте з лінією, площиною чи кубом. HotPDF обгортає все розділення в RegisterSeparationLUT, який створює одновхідне перетворення відтінку типу 0 із плоскаго масиву байтів всередині системи та повертає назву ресурсу колірного простору.
var
SpotCS: AnsiString;
begin
// Four CMYK output bytes per tint grid point, tint domain [0..1].
// Here 0% ink -> all zero, 100% ink -> a rich spot build,
// with two interior steps; the tint transform interpolates between.
SpotCS := Pdf.RegisterSeparationLUT(
'PANTONE 286 C', // colorant name
'DeviceCMYK', // alternate color space
[ 0, 0, 0, 0, // tint 0.00 -> 0,0,0,0
90, 60, 0, 0, // tint 0.33
100, 80, 0, 10, // tint 0.66
100, 72, 0, 18]); // tint 1.00 -> full ink build
// Use SpotCS with SetFillColorSpace / SetFillColor on a page.
end;
Кількість семплів має бути цілою кількістю точок сітки: позитивним кратним кількості компонентів альтернативного простору і мати щонайменше дві точки, щоб існував сегмент для інтерполяції. Передайте три байти на точку для CMYK-альтернативи, і виклик буде відхилено — це той самий захисний механізм перевірки сумісності, який застосовує 3D-конструктор, що дозволяє уникнути непомітного збою під час друку.
Де цей самий механізм з'являється знову
Коли ви почнете сприймати тип 0 як універсальну таблицю інтерполяції, ще дві функції керування пристроєм перестануть виглядати як окремі випадки. Передавальна функція (transfer function) коригує значення компонентів на їхньому шляху до пристрою виводу, і це просто окрема функція для кожного журналу; HotPDF реєструє її як ExtGState за допомогою RegisterTransferFunctionState, яка приймає або одну комбіновану функцію, або масив функцій для кожного каналу. Оскільки ці функції є звичайними об'єктами функцій, ви можете передати їй той самий THPDFStreamObject, який повертає RegisterSampledFunction, і керувати кривою передачі на основі таблиці семплів, а не математичної формули.
var
ToneFn: THPDFStreamObject;
GsName: AnsiString;
begin
// A single-input, single-output sampled tone curve on [0,1].
ToneFn := Pdf.RegisterSampledFunction(
[0,1], [0,1], [256], 8, ToneCurveBytes, 1);
// Apply it to all channels as a combined /TR2 transfer function.
GsName := Pdf.RegisterTransferFunctionState(ToneFn, []);
// Select GsName on the page before drawing the affected content.
end;
Генерація чорного кольору (black generation) та вилучення підкладкових кольорів (undercolor removal) належуть до того ж сімейства. Коли пристрій перетворює RGB в CMYK, він вирішує, яка частина сірої складової має бути виведена як чорна фарба, і специфікація виражає це рішення як функцію — записи /BG2 та /UCR2 у словнику стану графіки, кожна з яких є кривою з одним входом від обчисленого сірого до кількості чорного. Вони також є функціями типу 0, якщо вам потрібна виміряна крива замість аналітичної, і створюються так само через RegisterSampledFunction та розміщуються у стані графіки. Важливий урок полягає в тому, що керування кольором ніколи не відбувається всередині функції PDF; функція — це просто таблиця пошуку кольорів, яка переносить рішення, прийняте вами за допомогою справжнього колірного двигуна, а тип 0 є єдиним типом функції, достатньо гнучким, щоб перенести будь-яке рішення взагалі.
Для отримання ширшого уявлення про те, як шрифти, зображення та колірні ресурси виводяться у готовий документ, див. нашу інструкцію з виведення звітів із шрифтами та зображеннями. Коли вихідний файл має успішно пройти перевірку відповідності архівним стандартам або стандартам друку, правила колірних просторів та намірів виводу (output intent), опис яких наведено в посібнику з перевірки PDF/A, PDF/X та PDF/UA, визначають, які з цих функцій є дозволеними та як мають бути теговані кольори пристрою. Усе це постачається у складі HotPDF Component для Delphi та C++Builder разом з API затінення, ICC та Separation, які побудовані на одному ядрі типу 0.