פונקציות PDF (או PDF Functions) הן אחת הפינות השקטות יותר במפרט. רוב המפתחים נתקלים בהן פעם אחת, כדבר שהצללה צירית מטיפוס 2 זקוקה לו כדי להשתנות בין שני צבעים, ולא מביטים בהן שוב. חבל, מכיוון שמנגנון הפונקציות הוא מעריך (evaluator) קטן לשימוש כללי שהפורמט עושה בו שימוש חוזר להצללות, פונקציות העברה (transfer functions), פונקציות נקודת רשת של גווני ביניים, גווני הפרדה (separation tints) ועקומות העברה של מסיכה רכה. מבין ארבעת טיפוסי הפונקציות, טיפוס 0 הוא החזק ביותר והפחות מובן. זוהי פונקציה דגומה (sampled function): רשת רב-ממדית של ערכי פלט שהקורא מציב ביניהם באינטרפולציה. מכיוון שהרשת יכולה להחזיק כל מספר שתשים בה, פונקציה מטיפוס 0 יכולה לבטא מיפוי לא ליניארי שרירותי, שהוא בדיוק הצורה של טבלת חיפוש צבעים (color lookup table)
מאמר זה עובר על מילון טיפוס 0 כפי שמוגדר בתקן ISO 32000-1 בסעיף 7.10.2, ולאחר מציג את שני המקרים החשובים ביותר בצינור עבודה של מסמכים: טבלת LUT לתיקון צבע RGB-ל-RGB עם שלוש כניסות, והמרת גוון צבע ספוט (spot-color tint transform) עם כניסה אחת. אותו בונה פונקציות דגומות משרת את שניהם, וההבדל ביניהם הוא לחלוטין שאלה של כמה כניסות יש לרשת
פונקציה דגומה היא רשת שהקורא מבצע עבורה אינטרפולציה
פונקציה מטיפוס 0 ממפה וקטור בעל m כניסות לוקטור בעל n יציאות על ידי שמירת דגימות על רשת קבועה וביצוע אינטרפולציה ביניהן. תקן ISO 32000-1 §7.10.2 מפרט את המפתחות המתארים את הרשת הזו. /Domain מחזיק שני מספרים לכל כניסה, הגבול הנמוך והגבוה של כל ציר כניסה. /Range מחזיק שני מספרים לכל רכיב פלט. /Size הוא מערך של m מספרים שלמים המציין את מספר הדגימות לאורך כל ציר כניסה, כך שרשת של 12 דגימות לכל כיוון בשלושה ממדים היא בעלת /Size [12 12 12] ושומרת 1,728 נקודות רשת. /BitsPerSample קובע את הדיוק של כל ערך שמור; HotPDF מקבל 1, 2, 4, 8, 12, 16, 24 ו-32 סיביות, בהתאמה לערכים שטבלה 38 מאפשרת
זרם הדגימות נקרא בסדר קבוע. ממד הכניסה הראשון משתנה הכי מהר, ואז השני, וכן הלאה, ובכל נקודת רשת רכיבי הפלט ה-n נשמרים לפי הסדר. עבור טבלת RGB-ל-RGB זה שלושה בתים לכל נקודת רשת בשמונה סיביות, המסודרים כפלט אדום, פלט ירוק, פלט כחול, כשהסריקה מתחילה על פני הכניסה של האדום תחילה. שני מפתחות נוספים ממפים את העולם הרציף על גבי רשת המספרים השלמים. /Encode ממפה כל כניסה ממרווח ה-/Domain שלה לטווח אינדקס הדגימות 0 עד Size[i] - 1, ו-/Decode ממפה את המספרים השלמים הגולמיים השמורים בחזרה למרווחי /Range. כאשר אתה משאיר אותם בברירת המחדל שלהם, כניסה המשתרעת על [0 1] נוחתת בצורה נקייה על הרשת המלאה ובית שמור של 255 מפענח לקצה העליון של טווח הפלט שלו, וזה מה שטבלת LUT מנורמלת ל-[0,1] רוצה
סדר 1 מול סדר 3
בין נקודות הרשת הקורא צריך לבצע אינטרפולציה, והמפתח /Order בוחר כיצד. /Order 1 הוא אינטרפולציה רב-ליניארית: ליניארית לאורך ציר אחד, דו-ליניארית על פני שניים, ותלת-ליניארית על פני שלושה. זה מהיר, זה בדיוק מה שהחומרה ברוב המציגים עושה, ועבור המרת צבע חלקה זה בדרך כלל בלתי ניתן להבחנה מכל דבר מורכב יותר. /Order 3 מבקש אינטרפולציית cubic-spline, המתאימה עקומה חלקה יותר דרך הדגימות במחיר של יותר עבודה ואזור תמיכה רחב יותר סביב כל נקודה מוערכת
הפשרה היא צפיפות הרשת מול חלקות העקומה. סדר מעוקב (cubic) מוכיח את עצמו כאשר הרשת היא גסה ולמיפוי יש קימור נראה לעין, מכיוון שקו ישר בין שתי דגימות מרוחקות יכול לשטח עקומת גוון באופן שהעין תופסת במעברי צבע. ברגע שהרשת צפופה, המקש המקטעים קצר מספיק כדי שאינטרפולציה ליניארית תעקוב אחר העקומה בצורה הדוקה וסדר מעוקב יוסיף מעט מאוד. כלל מעשי הוא לבחור ב-/Order 3 רק עם רשתות קטנות או המרות תלולות, ובמקרים אחרים להשאיר אותו בברירת המחדל הליניארית. שים לב ש-/Order חל על פונקציות מטיפוס 0 בלבד, ו-HotPDF דוחה כל ערך שאינו 1 או 3
ה-3D LUT: שלוש כניסות, שלושה פלטים
תיקון צבע של RGB-ל-RGB הוא מקרה ספרותי קלאסי עבור רשת בעלת שלוש כניסות, ה-3D LUT הקלאסי המשמש בדירוג צבעים (color grading) והתאמת מכשירים. כל ציר של הקובייה הוא ערוץ כניסה אחד, כל נקודת רשת שומרת את שלשת ה-RGB המתוקנת עבור קואורדינטת כניסה זו, והקורא מבצע אינטרפולציה תלת-ליניארית על דגימות הפינה סביב כל צבע נכנס. שלוש כניסות הן בלתי נמנעות כאן מכיוון שהאדום המתוקן יכול להיות תלוי בירוק ובכחול שנכנסו, ולא רק באדום שנכנס; עקומה לכל ערוץ אינה יכולה לבטא השפעה הדדית בין ערוצים (crosstalk), אך קובייה יכולה
HotPDF בונה את זרם טיפוס 0 דרך RegisterSampledFunction, המקבל את ה-/Domain, /Range, /Size, /BitsPerSample, ואת בתיי הדגימה ישירות ומחזיר את אובייקט הפונקציה. עבור קובייה מנורמלת סטנדרטית אתה מעביר גבולות [0,1] בכל שלושת צירי הכניסה וכל שלושת הפלטים, גודל N x N x N, ואת טבלת הדגימות השטוחה. הבונה מאמת שספירת הבתים תואמת לרשת: עבור עומק מיושר לבתים הוא מצפה ל-OutputCount x (BitsPerSample div 8) x מכפלת הגדלים, ומעלה שגיאה אם המערך הוא באורך הלא נכון, כך שפסיעה (stride) שחושבה לא נכון נכשלת ברעש בזמן הרישום במקום להתרנדר כזבל מאוחר יותר
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;
התקינות הצבעונית (colorimetric) של הקובייה תלויה באופן שבו אתה ממלא אותה, ולא בפונקציית ה-PDF. הנתיב הנכון הוא לחשב כל נקודת רשת דרך המרת ICC מנוהלת, אותו מנוע שמניע הגהת מסך (soft-proof), כך שהמספרים ברשת יביעו משהו מול פרופיל מקור ויעד מוגדרים. רשום את הפרופילים המגבילים את ההמרה עם RegisterICCProfile, המתעד מרחב צבע ICCBased (בעל 1, 3 או 4 רכיבים) ומחזיר שם משאב שאתה יכול לחבר לתוכן שספק ה-LUT מזין. הפונקציה מטיפוס 0 נושאת את טבלת האינטרפולציה; פרופיל ה-ICC נושא את המשמעות של נקודות הקצה
המקרה החד-ממדי: המרת גוון צבע ספוט
מרחבי צבע של הפרדה (Separation color spaces) נשענים על אותו מנגנון עבור עבודה שונה לחלוטין. מרחב הפרדה, המוגדר בתקן ISO 32000-1 §8.6.6.4, מייצג צבען (colorant) יחיד, דיו ספוט כגון 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 והקריאה תדחה זאת, אותו אימות הגנתי שבונה התלת-ממד מיישם, וזה מה שאתה רוצה מפונקציה שאחרת תיכשל בשקט בזמן ההדפסה
היכן שאותו מנגנון מופיע שוב
ברגע שאתה רואה את טיפוס 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 של מילון מצב-גרפי (graphics-state dictionary), שכל אחד מהם הוא עקומה בעלת כניסה בודדת מהאפור המחושב לכמות שחורה. אלו הן פונקציות מטיפוס 0 גם כאשר אתה רוצה עקומה נמדדת ולא עקומה אנליטית, הבנויות באותו אופן דרך RegisterSampledFunction ומונחות במצב הגרפי. הלקח שראוי לזכור הוא שפונקציית ה-PDF היא לעולם לא המקום שבו מתרחש ניהול הצבע; זוהי טבלת החיפוש הנושאת החלטה שקיבלת עם מנוע צבע אמיתי, וטיפוס 0 הוא סוג הפונקציה היחיד שגמיש מספיק כדי לשאת כל החלטה שהיא
עבור התמונה הרחבה יותר של אופן פליטת גופנים, תמונות ומשאבי צבע למסמך גמור, ראה את המדריך שלנו על פלט דוחות עם גופנים ותמונות. כאשר הפלט חייב לשרוד בדיקת תאימות לארכיון או להדפסה, חוקי מרחב הצבע וכוונת הפלט (output-intent) המכוסים במדריך אימות ה-PDF/A, PDF/X, ו-PDF/UA קובעים אילו פונקציות מותרות וכיצד יש לתייג צבעי מכשיר. כל זה נשלח בתוך HotPDF Component עבור Delphi ו-C++Builder, לצד ממשקי ה-API להצללה, ICC והפרדה הנבנים על אותה ליבה של טיפוס 0