مقاله فنی

سازگاری جوهر نقطه‌ای NChannel در Delphi با HotPDF

چاپخانه پیش‌مطبوعات کار را پس می‌فرستد: یک جوهر نقطه‌ای یکسان روی دو پلیت جدا شده است. HotPDF این را در زمان تألیف مسدود می‌کند؛ NChannel را به شکل پنج‌عضوی DeviceN استاندارد ISO 32000-2 می‌نویسد و برای هر نام جوهر نقطه‌ای در کل سند یک فضای جایگزین و تبدیل تینت مرجع واحد نگه می‌دارد و به‌جای انتشار تعریف دومِ متناقض، آن را رد می‌کند

NChannel نام یک خانواده فضای رنگی نیست

اولین چیزی که باید از یاد ببرید خودِ نام است. NChannel آن‌طور که Separation و DeviceN خانواده‌اند، یک خانواده فضای رنگی نیست. ISO 32000-2 §8.6.6.5 آن را یک زیرنوع از DeviceN توصیف می‌کند، پس یک فضای NChannel منطبق به‌صورت آرایه پنج‌عضوی [/DeviceN names alternateSpace tintTransform attributes] نوشته می‌شود و دیکشنری attributes حامل /Subtype /NChannel است. در مشخصه هیچ آرایه [/NChannel ...]‌ای وجود ندارد. اگر تا کنون یکی را دستی ساخته‌اید و شانه بالا انداختن RIP در برابرش را دیده‌اید، دلیلش همین است

HotPDF فضای NChannel را به‌صورت آرایه پنج‌عضوی DeviceN می‌نویسد که دیکشنری attributes آن Subtype NChannel را همراه با مدخل‌های Process و Colorants و MixingHints حمل می‌کند، چون در مشخصه اصلاً هیچ آرایه‌ای با نام خانواده NChannel وجود ندارد
یک فضای NChannel منطبق، آرایه‌ای DeviceN با دیکشنری attributes است؛ به همین دلیل نویسنده فقط این شکل را منتشر می‌کند در حالی که خواننده همچنان توکن قدیمی را می‌پذیرد

HotPDF یک بار این را اشتباه انجام داد و بعد درستش کرد؛ گفتنش به‌صورت صریح ارزش دارد چون رفتار امروز کامپوننت را شکل می‌دهد. نسخه‌های قدیمی‌تر HotPDF شکل با نام خانواده را منتشر می‌کردند. THotPDF.RegisterNChannelColorSpace اکنون فقط شکل استاندارد DeviceN به‌علاوه attributes را منتشر می‌کند و چون زیرنوع NChannel در PDF 1.6 آمد، نقطه ورود تولیدکننده روی RequirePDFVersion(pdf16, ...) گیت می‌گذارد و روی اهداف قدیمی‌تر به‌سادگی انصراف می‌دهد. سمت رندر عمداً بخشنده‌تر از نویسنده است: HPDFResolveColorSpace همچنان توکن قدیمی /NChannel را به‌عنوان خانواده DeviceN می‌پذیرد تا فایل‌های حاصل از نویسنده قدیمی به رندر ادامه دهند، اما هر چیزی که HotPDF دوباره بیرون می‌نویسد از کدگذاری استاندارد استفاده می‌کند. سخت‌گیری در خروجی و آسان‌گیری در ورودی همین‌جا عدم‌تقارن درستی است، چون خواننده شما باید با فایل‌هایی کنار بیاید که خودش نساخته، اما نویسنده شما چنین بهانه‌ای ندارد

چرا یک نام جوهر نقطه‌ای سر از دو پلیت درمی‌آورد؟

چون نام یک رنگدانه نقطه‌ای یک هویت پلیت در مقیاس کل سند است، نه یک آرگومان محلی. دو فراخوانی که هر دو نام Orange را می‌گویند اما فضای جایگزین متفاوتی تحویل می‌دهند، یا همان فضای جایگزین با یک تبدیل تینت متفاوت، دو جوهر متفاوت را توصیف می‌کنند که اتفاقاً برچسب مشترکی دارند. RIP‌ای که جداسازی‌ها می‌سازد راهی برای آشتی دادن این دو ندارد، پس تنها کار صادقانه را می‌کند و دو پلیت به شما می‌دهد. HotPDF بنابراین برای هر نام رنگدانه یک امضای مرجع در سطح سند نگه می‌دارد. RegisterSpotColorantDefinition آن امضا را از فضای رنگ جایگزین و شکل تابع تینت می‌سازد و هر RegisterSeparation، RegisterSeparationFunc، RegisterSeparationLUT و تعریف جوهر نقطه‌ای NChannel از آن می‌گذرد. وقتی تعریف دوم ناهم‌خوان باشد، فراخوانی به‌جای ثبت بی‌سروصدای واریانت دوم استثنا پرتاب می‌کند و پیام عمداً درباره حالت خرابی خاص است، چون جایگزینش این است که سه هفته بعد روی پروف پلیت کشفش کنید

هر فراخوانی ثبت جوهر نقطه‌ای در HotPDF از RegisterSpotColorantDefinition عبور می‌کند؛ جایی که برای هر نام رنگدانه یک فضای جایگزین مرجع و امضای تینت در سند ثبت می‌شود و با رسیدن تعریف دومِ ناهم‌خوان برای همان نام استثنا پرتاب می‌شود
یک نام، یک امضا: تعریف دومِ متناقض یک جوهر نقطه‌ای در زمان تألیف رد می‌شود، نه آن‌که روی پروف پلیت کشف شود
// 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;

تقسیم نام‌های اصلی میان رنگ process و جوهر نقطه‌ای

یک NChannel کامل باید برای هر یک از نام‌های رنگدانه اصلی خود دقیقاً یک‌بار حساب بدهد؛ یا به‌عنوان مؤلفه process یا به‌عنوان رنگدانه نقطه‌ای. اورلود پیشرفته RegisterNChannelColorSpace نام‌های اصلی ColorantNames، نام‌های ProcessColorantNames، فضای جایگزین، تبدیل تینت کلی، آرایه‌ای از رکوردهای THPDFNChannelSpotColorant و یک ترتیب چاپ اختیاری می‌گیرد. هر رکورد نقطه‌ای نام خودش، تبدیل تینت Separation تک‌ورودی خودش، یک solidity اختیاری و یک تابع dot-gain اختیاری را حمل می‌کند. تبدیل تینت کلی باید 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 دیکشنری attributes ای را که مشخصه خواسته منتشر می‌کند: /Subtype /NChannel، یک دیکشنری /Process که /ColorSpace آن فضای process است و /Components آن نام‌های process را به همان ترتیب مؤلفه‌های آن فضا فهرست می‌کند، یک دیکشنری /Colorants که برای هر spot یک آرایه واقعی [/Separation name alternate tintfn] نگه می‌دارد، و یک دیکشنری /MixingHints که وقتی شما فراهمشان کرده باشید /Solidities، /PrintingOrder و /DotGain را حمل می‌کند. نام منبع برگشتی دقیقاً مثل فضاهای ساده‌ترِ پوشش‌داده‌شده در مقاله رندر رنگ‌های نقطه‌ای Separation و DeviceN به SetFillColorSpace یا SetStrokeColorSpace می‌رود

HotPDF نام‌های رنگدانه اصلی یک فضای NChannel را به یک دیکشنری Process برای چهار مؤلفه CMYK و یک دیکشنری Colorants که برای هر spot یک آرایه Separation نگه می‌دارد تقسیم می‌کند، با بررسی همه arityها پیش از نوشتن
هر نام اصلی دقیقاً یک‌بار ادعا می‌شود و تبدیل تینت کلی، تینت‌های هر spot و ترتیب چاپ همگی پیش از انتشار هر شیء اعتبارسنجی می‌شوند

بررسی سازگاری واقعاً چه چیزی را رد می‌کند؟

این بررسی ناهم‌خوانی ساختاری درون فضا را رد می‌کند و این کار را پیش از نوشتن حتی یک شیء انجام می‌دهد. نام‌های رنگدانه باید یکتا باشند و نباید خالی، All یا None باشند. تعریف‌های process و spot با هم باید نام‌های اصلی را دقیقاً پوشش دهند؛ هیچ رنگدانه‌ای در هر دو نقش ظاهر نشود و هیچ‌کدام بی‌تعریف نماند. وقتی فضای جایگزین DeviceCMYK است، مؤلفه‌های process باید به همان ترتیب Cyan، Magenta، Yellow، Black باشند و شمار نام‌های process باید با شمار مؤلفه‌های فضای جایگزین بخواند. هر تبدیل تینت و تابع dot-gain باید یک شیء تابع غیرمستقیم با arity ورودی و خروجی درست باشد. Solidity باید مقداری متناهی درون 0..1 باشد. ترتیب چاپ باید خالی یا یک جایگشت کامل از نام‌های اصلی باشد، هرگز فهرستی ناقص. آن‌چه انجام نمی‌دهد قضاوت درباره رنگ است: هیچ‌چیز این‌جا بررسی نمی‌کند که تبدیل تینت Orange شما واقعاً به جوهر داخل قوطی شبیه باشد، که ساخت CMYK آن پروکسی معقولی باشد یا اینکه solidity‌ای که داده‌اید با رفتار اندازه‌گیری‌شده روی بستر بخواند. آن‌ها پرسش‌های ماشین چاپ و اندازه‌گیری‌اند و کامپوننت حق اظهارنظری در آن‌ها ندارد. اورلود ساده‌ترِ مخصوص process از نظر طراحی سخت‌گیرتر هم هست: یک NChannel فقط با مؤلفه‌های process تولید می‌کند و عمداً از پذیرش نام‌های spot امتناع می‌ورزد، چون نوشتن یک spot در آرایه نام‌ها بدون مدخل /Colorants متناظر فایلی ساختاراً نامنطبق تولید می‌کند و جعل یک تعریف پیش‌فرض از شکست بدتر است

Output intentهای PDF/X-6n باید هر spot ثبت‌شده را پوشش دهند

یک فایل PDF/X-6n با رنگدانه‌های N رنگدانه‌هایش را دو بار اعلان می‌کند و این دو اعلان باید با هم بخوانند. AddPDFX6ExternalOutputIntent ارجاع پروفایل ICC بیرونی را همراه با ColorantTable آن می‌نویسد و پیش از آن، ValidateRegisteredSpotOutputColorants همه spotهایی که سند ثبت کرده را مرور می‌کند و اگر یکی در جدول نباشد استثنا پرتاب می‌کند. بررسی در هر دو جهت اجرا می‌شود: وقتی یک output intent فهرست رنگدانه‌هایش را منتشر کرد، ثبت بعدی یک spot برای نامی خارج از آن فهرست هم رد می‌شود. 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 هویت‌های spot سند قبلی را به ارث نمی‌برد؛ این جداسازی همان نکته است، چون امضای باقی‌مانده کار کاملاً معتبر کار بعدی را رد می‌کرد. و سمت پروفایل هم حد و مرزهای سختی خودش را دارد: یک output intent بیرونی به هدف PDF 2.0، یک URL پروفایل مطلق HTTP یا HTTPS، یک امضای فضای رنگ ICC چهاربایتی و برای PDF/X-6n بین 2 تا 15 رنگدانه با امضای متناظر 2CLR تا FCLR نیاز دارد

جایی که بررسی متوقف می‌شود

برخورد با CxF/X-4 بخشی است که باید درباره‌اش صادق بود. HotPDF بررسی‌های ایمنی ساختاری محدودی روی استریم طیفی اعمال می‌کند و تأیید می‌کند که هویت جوهر درونش با رنگدانه‌ای که نام برده‌اید بخواند. اندازه بار داده را سقف می‌زند، بایت‌های null جاسازی‌شده را رد می‌کند، هر استریمی را که حاوی اعلان DOCTYPE یا ENTITY باشد نمی‌پذیرد، یک ریشه CxF قابل تشخیص می‌خواهد و دقیقاً یک عنصر SpotInkCharacterisation می‌خواهد که دقیقاً یک SpotInkName حمل کند که با نام رنگدانه شما برابر باشد. این یک گیت در برابر ورودی خراب و خصمانه است، نه یک اعتبارسنج schema. یک پیاده‌سازی کامل ISO 17972-4 نیست، اندازه‌گیری‌های طیفی شما را تأیید نمی‌کند و گراف‌های شیء شخص ثالث از پیش موجود را ممیزی معکوس نمی‌کند و جدول‌های رنگدانه داخلی داخل یک پروفایل ICC جاسازی‌شده را هم نه. اگر گردش کارتان به انطباق کامل CxF وابسته است، پیش از تحویل فایل به کامپوننت آن را با ابزار اختصاصی اعتبارسنجی کنید

یک محدودیت همسایه کسانی را گاز می‌گیرد که هرگز انتظار دیدار با آن را نداشتند. یک ماسک نرم luminosity نمی‌تواند Separation، DeviceN یا NChannel را به‌عنوان /CS گروه شفافیت خودش استفاده کند؛ فضای blending گروه باید یک فضای دستگاهی یا مبتنی بر CIE باشد، پس رنگ spot درون گروه پیش از محاسبه luminosity از طریق تبدیل تینتش به فضای جایگزین حل می‌شود. RegisterLuminositySoftMaskState دقیقاً برای همین یک گروه DeviceGray می‌سازد تا این نتواند به‌تصادف خراب شود. نتیجه عملی این است که طراحی سنگین بر spot که این‌طور ماسک شده از طریق پروکسی CMYK‌اش ارزیابی می‌شود، نه جوهرش؛ و این وقتی اهمیت دارد که آن را با یک پروف جداسازی‌شده مقایسه می‌کنید، همان‌طور که در یادداشت‌های پروف overprint و دستگاه‌های رندر توضیح داده شده

هیچ‌کدام از این‌ها نیاز به پروف پلیت را از بین نمی‌برد، اما یک کلاس کامل از ردهای پیش‌مطبوعات را از اتاق ماشین چاپ به مرحله build برمی‌گرداند. APIهای NChannel و Separation و output intent PDF/X-6n توصیف‌شده در این مقاله در HotPDF Delphi Component استاندارد برای Delphi و C++Builder عرضه می‌شوند؛ جایی که مستندات مرجع چیدمان کامل رکوردها و شرایط دقیقی را که هر فراخوانی با شکست امن تمام می‌شود مستند می‌کند