HotPDF merender warna spot Separation dan DeviceN pada halaman PDF yang dimuat dengan menyelesaikan ruang warna melalui HPDFResolveColorSpace, mengevaluasi fungsi transformasi warna (tint-transform) dengan HPDFEvalTintTransform, dan mengonversi hasilnya melalui ruang warna alternatif ke RGB untuk layar. Sejak versi 2.375.0, saluran tersebut mengevaluasi keempat jenis fungsi PDF, termasuk kalkulator PostScript Type 4, sehingga berkas siap cetak dengan tinta Pantone menampilkan warna aslinya alih-alih placeholder. Artikel ini membahas bagaimana saluran tersebut bekerja dan, yang juga berguna, bagaimana ia gagal saat kami membangunnya
Kasus pemicunya selalu sama. Pelanggan menerima PDF dari percetakan: tajuk utama diatur dengan tinta spot bernama, karya seni kemasan menggunakan campuran dua tinta DeviceN, dan teks isi berwarna hitam polos. Di Acrobat, tampilannya tampak sempurna. Di aplikasi Delphi Anda, tajuk utama dirender sebagai hitam, atau lebih buruk lagi, tidak dirender sama sekali, dan pelanggan mengajukan bug untuk perangkat lunak Anda alih-alih berkas tersebut. Berkasnya baik-baik saja. Perender hanya tidak mengerti ruang warna yang digunakan berkas tersebut
Mengapa warna spot dirender sebagai hitam di viewer PDF?
Warna spot dirender sebagai hitam, atau lenyap, ketika perender hanya mengimplementasikan operator warna perangkat (rg, g, k) dan mengabaikan operator generik. ISO 32000-1 §8.6 mendefinisikan tiga kelompok ruang warna: ruang perangkat (DeviceGray, DeviceRGB, DeviceCMYK), ruang berbasis CIE (CalGray, CalRGB, Lab, ICCBased), dan ruang khusus (Indexed, Separation, DeviceN, Pattern). Segala sesuatu di luar kelompok perangkat dipilih dengan operator generik: cs dan CS memilih ruang warna berdasarkan nama dari kamus sumber daya halaman, kemudian sc, SC, scn, dan SCN memasok nilai-nilai komponen. Perender yang melewatkan operator tersebut akan mempertahankan warna apa pun yang terakhir ditetapkan, yang untuk halaman yang diawali dengan tajuk utama warna spot adalah hitam DeviceGray awal
HotPDF menambahkan set operator generik ke perender halamannya di versi 2.333.0, bersama dengan jalur penyelesaian terpadu: setiap entri sumber daya /ColorSpace, baik berupa nama kosong, array inline, atau referensi tidak langsung, diurai menjadi satu rekaman THPDFColorSpace, dan setiap permintaan warna isi atau garis disalurkan melalui panggilan HPDFResolveColor tunggal. Enumerasi keluarga menunjukkan cakupan sekilas
type
THPDFColorSpaceFamily = (csfDeviceGray, csfDeviceRGB, csfDeviceCMYK,
csfIndexed, csfCalGray, csfCalRGB, csfLab,
csfICCBased, csfSeparation, csfDeviceN,
csfUnsupported);
function HPDFResolveColorSpace(Obj: THPDFObject): THPDFColorSpace;
function HPDFResolveColor(const CS: THPDFColorSpace;
const Comps: THPDFColorComps; CompCount: Integer): THPDFRenderColor;
Satu keputusan desain sangat berharga: csfUnsupported adalah keluarga kelas satu, bukan sebuah kesalahan. Ruang warna yang tidak dapat ditafsirkan perender akan diturunkan ke fallback yang ditentukan alih-alih membatalkan halaman, yang sesuai dengan perilaku sebagian besar viewer populer dan mencegah satu warna isi yang eksotis membuat dokumen yang seharusnya dapat dirender menjadi kosong
Bagaimana transformasi warna (tint transform) mengubah satu nilai tinta menjadi warna nyata?
Ruang Separation membawa tiga informasi: nama tinta, ruang warna alternatif, dan fungsi transformasi warna (tint-transform). Array [/Separation /PANTONE485 /DeviceCMYK f] menyatakan: ketika aliran konten menulis 0.8 scn, masukkan nilai warna 0.8 ke dalam fungsi f dan cat hasil CMYK segi empat tersebut. DeviceN menggeneralisasikan ini ke N tinta dengan fungsi N-masukan. Nama tinta itu sendiri hanya bersifat anjuran di layar; transformasi warna adalah seluruh semantik rendering, sehingga perender yang mengurai ruang warna tetapi melewatkan fungsinya belum melakukan sesuatu yang berguna
HPDFEvalTintTransform adalah mesin fungsi di balik langkah tersebut. Hadir di versi 2.334.0, ia mengevaluasi fungsi eksponensial Type 2 (C0 + x^N * (C1 - C0) dengan penjepitan Domain dan Range), fungsi penggabungan Type 3 (rekursi sub-fungsi yang dipilih Bounds dengan pemetaan ulang Encode), dan fungsi sampel Type 0 dengan sampel 8-, 16-, dan 32-bit. Type 0 adalah mekanisme tabel pencarian yang sama yang kami bahas dari sisi pembuatan dalam artikel tentang membangun LUT warna Type 0; degan sisi render menyusuri struktur identik tersebut secara terbalik, dari byte sampel yang didekodekan kembali ke nilai komponen
Fungsi kalkulator PostScript Type 4 adalah pertahanan terakhir. Hingga versi 2.375.0, mereka turun performanya secara anggun menjadi placeholder netral; sejak versi 2.375.0, HPDFEvalPostScriptCalculator mengeksekusi set operator ISO 32000-1 §7.10.5 lengkap pada tumpukan operan yang dibatasi: operasi aritmatika, perbandingan, boolean dan bitwise, manipulasi tumpukan termasuk roll, dan kondisional if/ifelse. Semantik sudutnya lebih ketat daripada yang terlihat. Operator round PostScript membulatkan setengah ke arah nilai yang lebih besar, sehingga fungsi pembulatan bankir Delphi Round tidak dapat digunakan; operator trigonometri bekerja dalam derajat, dengan atan mengembalikan nilai dalam [0, 360); dan exp adalah pangkat dua operan, bukan eksponensial alami. Evaluator yang sama juga mendorong warna isi gradien berbasis fungsi, itulah sebabnya rendering bayangan aksial dan radial menggunakan tanjakan yang didorong kalkulator dalam rilis yang sama
// Evaluasi transformasi warna Separation/DeviceN (Type 0/2/3/4).
function HPDFEvalTintTransform(FuncObj: THPDFObject;
const Inputs: THPDFColorComps; InputCount: Integer;
out AltComps: THPDFColorComps): Boolean;
// Kalkulator PostScript Type 4, set operator ISO 32000-1 7.10.5.
function HPDFEvalPostScriptCalculator(const Prog: TBytes;
const Inputs: THPDFColorComps; InputCount: Integer;
var Outputs: THPDFColorComps; OutCount: Integer): Boolean;
Jujur tentang presisi: kalkulator perangkat lunak yang dievaluasi dalam presisi ganda (double precision) tidak akan cocok dengan RIP bit per bit, dan penjepitan pada batas Range dapat berbeda sebesar bit terkecil yang signifikan (least significant bit) dari implementasi lainnya. Untuk tampilan layar dan rendering regresi hal itu tidak relevan; jika Anda membangun pemeriksaan warna terkelola, transformasi warna hanyalah tahap pertama dan Anda masih memerlukan CMM nyata di hilir
CalGray, CalRGB, Lab, dan ICCBased tanpa mesin ICC
Keluarga berbasis CIE mengambil cabang lain dari resolver yang sama. HotPDF mengonversi nilai Lab melalui rantai standar Lab ke XYZ ke sRGB, termasuk kubus titik henti 6/29 (breakpoint cube) dalam fungsi transfer terbalik, dan menangani CalRGB dengan gamma per saluran ditambah matriks linier 3x3 dan CalGray dengan gamma tunggalnya. Detail yang relevan dengan kinerja adalah penanganan white point: matriks konversi XYZ ke sRGB diadaptasi Bradford ke white point yang dideklarasikan di ruang warna dan di-cache dalam rekaman THPDFColorSpace yang diselesaikan, sehingga pekerjaan per piksel tetap berupa perkalian 3x3 tunggal tidak peduli seberapa eksotis iluminan yang dideklarasikan
Ruang warna ICCBased mendapatkan perlakuan pragmatis yang sengaja diambil. Spesifikasi PDF mengharuskan setiap aliran ICCBased untuk mendeklarasikan ruang warna /Alternate atau ruang warna tersirat melalui jumlah komponennya /N, tepatnya agar viewer tanpa mesin manajemen warna tetap dapat merender dengan wajar. HotPDF menyelesaikan ICCBased melalui alternatif tersebut, atau menebak DeviceGray, DeviceRGB, or DeviceCMYK dari /N bernilai 1, 3, atau 4 ketika entri tersebut tidak ada, dan tidak pernah mengurai byte profil. Itu berarti tidak ada dependensi lcms dan tidak ada biaya pencarian profil, dengan mengorbankan akurasi kolorimetri: ruang ICCBased yang profilnya sangat menyimpang dari alternatifnya akan menampilkan rendering alternatif. Untuk tampilan di layar dan miniatur (thumbnail), ini adalah pertukaran yang sama yang dilakukan oleh setiap viewer ringan, dan ini adalah batasan yang harus dinyatakan dengan jelas dalam dokumentasi Anda sendiri
Bug yang membuat setiap pencarian ruang warna bernama meleset
Sistem operator versi 2.333.0 dikirimkan dengan cacat yang tetap tidak terlihat selama empat puluh dua rilis: handler cs dan CS mencari operan mereka, dengan garis miring utama yang utuh (/CS0), terhadap kunci kamus sumber daya yang disimpan tanpa garis miring (CS0). Setiap pencarian ruang warna bernama meleset, seratus persen dari waktu yang ada, dan kode beralih secara diam-diam ke DeviceGray default. Gejala yang terlihat sangat samar dalam artian buruk: warna isi Separation dari 1 scn menjadi DeviceGray 1.0, yang mengecat putih, dan tinta putih pada halaman putih bukanlah kesalahan rendering yang akan discreenshot orang. Perbaikan di versi 2.375.0 adalah pembantu normalisasi nama bersama yang diterapkan pada setiap pencarian operan-ke-sumber daya
Dua cacat terkait ditemukan dari penyelidikan yang sama. Pertama, referensi tidak langsung ke objek bernilai array dikembalikan tanpa diselesaikan: akses dokumen perender memiliki resolver tipe untuk aliran dan kamus saja, sehingga /CS0 5 0 R yang menunjuk ke array [/Separation ...] mandiri dikembalikan sebagai referensi mentah dan ruang warna tersebut diurai sebagai tidak didukung. Kedua, HPDFReadNumericArray memberlakukan semantik panjang yang ketat, mengharuskan array PDF minimal sepanjang buffer yang disediakan. Membaca fungsi Type 2 /C0 dan /C1 ke dalam buffer empat elemen gagal untuk alternatif satu dan tiga komponen, membiarkan kedua array nol, dan setiap warna eksponensial non-CMYK dirender sebagai hitam hingga versi 2.376.0 memperkenalkan pembaca HPDFReadNumericArrayUpTo yang toleran. Pelajaran yang dapat dipetik: setiap kunci PDF yang didokumentasikan sebagai penampung array numerik dengan panjang variabel harus dibaca dengan pembaca isi-yang-ada (fill-what-exists), karena buffer ukuran tetap dengan pencocokan ketat mengubah berkas valid menjadi nol secara diam-diam
Bagaimana cara menguji rendering warna spot tanpa membodohi diri sendiri?
Pertanyaan yang tidak nyaman adalah mengapa rangkaian pengujian tetap hijau di tengah semua masalah ini. Regresi asli, SeparationRendersDistinguishable, menegaskan (assert) bahwa bitmap yang dirender tidak sepenuhnya hitam. Saluran yang menjatuhkan setiap warna spot ke DeviceGray menghasilkan output abu-abu dan putih, yang bukan hitam, sehingga penegasan lulus sementara seluruh fitur mati. Penegasan yang lemah berupa "tidak kosong", "tidak semuanya hitam", atau "digest bukan nol" tidak dapat membedakan perender yang berfungsi dari perender yang rusak, karena hampir semua mode kegagalan tetap menghasilkan beberapa piksel
Gaya penegasan yang benar-benar menangkap kegagalan ini mengunci warna yang diharapkan: render PDF minimal yang dibuat sendiri yang tinta Separation-nya diselesaikan ke rona warna yang diketahui, lalu hitung piksel yang dominan dalam rona tersebut. Rendering ke bitmap untuk pemeriksaan menggunakan titik masuk RenderLoadedPageToBitmap yang sama yang dijelaskan dalam panduan rendering halaman-ke-bitmap
var
Pdf: THotPDF;
Bmp: TBitmap;
X, Y, RedHits: Integer;
Px: TColor;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('spot-red-fixture.pdf', '') > 0 then
begin
Bmp := Pdf.RenderLoadedPageToBitmap(0, 96);
try
RedHits := 0;
for Y := 0 to Bmp.Height - 1 do
for X := 0 to Bmp.Width - 1 do
begin
Px := Bmp.Canvas.Pixels[X, Y];
if (GetRValue(Px) > 180) and (GetGValue(Px) < 100) and
(GetBValue(Px) < 100) then
Inc(RedHits);
end;
// Kunci tinta yang diharapkan: tuntut area nyata piksel dominan merah,
// jangan pernah puas dengan "tidak semuanya hitam".
Assert(RedHits > 500);
finally
Bmp.Free;
end;
end;
finally
Pdf.Free;
end;
end;
Fixture sama pentingnya dengan penegasan. PDF buatan tangan sepanjang beberapa ratus byte, dengan satu warna isi Separation dan tidak ada yang lain, tidak meninggalkan keraguan tentang output yang diharapkan; berkas dunia nyata melatih lebih banyak kode tetapi tidak dapat memberi tahu Anda tahap mana yang gagal. Kami sekarang memperlakukan penghitungan piksel warna yang diharapkan sebagai batas minimum untuk uji asap (smoke test) rendering apa pun, karena itu adalah satu-satunya gaya penegasan yang memaksa cacat ini terungkap
Rendering warna spot dan ruang warna CIE adalah bagian dari saluran dokumen yang dimuat dalam HotPDF Component untuk Delphi and C++Builder, bersama dengan mesin fungsi, rendering bayangan, dan jalur ekspor bitmap yang ditunjukkan di atas