Ένα prepress στέλνει τη δουλειά πίσω: το ίδιο spot μελάνι διαχωρισμένο σε δύο πλάκες. Το HotPDF το αποτρέπει τη στιγμή της συγγραφής γράφοντας το NChannel ως τη μορφή DeviceN πέντε στοιχείων του ISO 32000-2 και κρατώντας έναν κανονικό εναλλακτικό χώρο και tint transform ανά όνομα spot για όλο το έγγραφο, απορρίπτοντας τον δεύτερο, συγκρουόμενο ορισμό αντί να τον εκπέμπει
Το NChannel δεν είναι όνομα οικογένειας χρωματικών χώρων
Το πρώτο πράγμα που πρέπει να ξεμάθετε είναι το ίδιο το όνομα. Το NChannel δεν είναι οικογένεια χρωματικών χώρων όπως οι οικογένειες Separation και DeviceN. Το ISO 32000-2 §8.6.6.5 το περιγράφει ως subtype του DeviceN, ώστε ένα συμμορφούμενο NChannel space γράφεται ως ο πενταμελής πίνακας [/DeviceN names alternateSpace tintTransform attributes], και το λεξικό attributes κουβαλά /Subtype /NChannel. Δεν υπάρχει πίνακας [/NChannel ...] στην προδιαγραφή. Αν έχετε χτίσει ποτέ ένα χειροκίνητα και έχετε δει ένα RIP να το προσπερνά, αυτός είναι ο λόγος
Το HotPDF το έκανε λάθος μία φορά και μετά το διόρθωσε, κάτι που αξίζει να ειπωθεί ευθέως επειδή καθορίζει πώς συμπεριφέρεται σήμερα το component. Παλαιότερες εκδόσεις HotPDF εξέπεμπαν τη μορφή ονόματος οικογένειας. Το THotPDF.RegisterNChannelColorSpace τώρα εκπέμπει μόνο τη μορφή DeviceN-συν-attributes, και επειδή το subtype NChannel έφτασε στο PDF 1.6, το entry point του producer ελέγχει μέσω RequirePDFVersion(pdf16, ...) και απλώς αρνείται σε παλαιότερους στόχους. Η πλευρά render είναι σκόπιμα πιο επιεικής από τον writer: το HPDFResolveColorSpace δέχεται ακόμα το legacy token /NChannel ως οικογένεια DeviceN ώστε αρχεία από τον παλιό writer να συνεχίζουν να αποδίδονται, αλλά οτιδήποτε το HotPDF ξαναγράφει χρησιμοποιεί τη standard κωδικοποίηση. Επιεικής στην είσοδο, αυστηρός στην έξοδο είναι η σωστή ασυμμετρία εδώ, επειδή ο reader σας πρέπει να αντιμετωπίσει αρχεία που δεν δημιούργησε ενώ ο writer σας δεν έχει τέτοια δικαιολογία
Γιατί ένα όνομα spot καταλήγει σε δύο πλάκες;
Επειδή ένα όνομα spot colorant είναι ταυτότητα πλάκας σε επίπεδο εγγράφου, όχι τοπικό όρισμα. Δύο κλήσεις που και οι δύο ονομάζουν Orange αλλά παραδίδουν διαφορετικό εναλλακτικό χώρο, ή τον ίδιο εναλλακτικό χώρο με διαφορετικό tint transform, περιγράφουν δύο διαφορετικά μελάνια που τυχαίνει να μοιράζονται ετικέτα. Ένα RIP που χτίζει διαχωρισμούς δεν έχει τρόπο να συμφιλιώσει αυτό, οπότε κάνει το μόνο ειλικρινές πράγμα και σας δίνει δύο πλάκες. Το HotPDF λοιπόν διατηρεί μια κανονική υπογραφή ανά όνομα colorant σε επίπεδο εγγράφου. Το RegisterSpotColorantDefinition συνθέτει αυτή την υπογραφή από τον εναλλακτικό χρωματικό χώρο και το σχήμα της tint function, και κάθε RegisterSeparation, RegisterSeparationFunc, RegisterSeparationLUT, και ορισμός spot NChannel περνά από αυτό. Όταν ένας δεύτερος ορισμός διαφωνεί, η κλήση πετάει exception αντί να εγγράψει σιωπηλά δεύτερη παραλλαγή, και το μήνυμα είναι σκόπιμα συγκεκριμένο για τη μορφή αποτυχίας, επειδή η εναλλακτική είναι να την ανακαλύψετε σε plate proof τρεις εβδομάδες αργότερα
// Το Orange είχε ήδη εγγραφεί κατά DeviceCMYK με το
// tint transform 0 / 0.55 / 1 / 0 σε πλήρες tint.
Conflicting := Pdf.RegisterExponentialFunction(
Domain1, NoInk, OtherOrangeCMYK, 1, []);
try
Pdf.RegisterSeparationFunc('Orange', 'DeviceCMYK', Conflicting);
except
on E: Exception do
// 'Spot colourant "Orange" έχει ασυνεπή εναλλακτικό χρωματικό
// χώρο ή ορισμό tint σε αυτό το έγγραφο'
LogPrepressWarning(E.Message);
end;
Διαχωρίζοντας τα master ονόματα σε process και spot
Ένα πλήρες NChannel πρέπει να λογαριάζει κάθε ένα από τα master ονόματα colorant του ακριβώς μία φορά, είτε ως process component είτε ως spot colorant. Το προχωρημένο overload RegisterNChannelColorSpace παίρνει τα master ColorantNames, τα ProcessColorantNames, τον εναλλακτικό χώρο, τον συνολικό tint transform, έναν πίνακα εγγραφών THPDFNChannelSpotColorant, και προαιρετική σειρά εκτύπωσης. Κάθε εγγραφή spot κουβαλά το δικό της όνομα, το δικό της single-input Separation tint transform, προαιρετική solidity, και προαιρετική συνάρτηση dot-gain. Ο συνολικός tint transform πρέπει να αντιστοιχίζει N εισόδους στο πλήθος components του εναλλακτικού χώρου· κάθε spot tint πρέπει να αντιστοιχίζει μία είσοδο στο ίδιο εκείνο πλήθος
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 ονόματα στη σειρά components εκείνου του χώρου, ένα λεξικό /Colorants που κρατά έναν πραγματικό πίνακα [/Separation name alternate tintfn] για κάθε spot, και ένα λεξικό /MixingHints που κουβαλά /Solidities, /PrintingOrder, και /DotGain όταν τα προμηθεύσατε. Το επιστραφέν όνομα resource πηγαίνει σε SetFillColorSpace ή SetStrokeColorSpace ακριβώς όπως οι απλούστεροι χώροι που καλύπτονται στο άρθρο για το Separation and DeviceN spot color rendering
Τι απορρίπτει πραγματικά ο έλεγχος συνέπειας;
Απορρίπτει τη δομική ασυνέπεια μέσα στον χώρο, και το κάνει πριν γραφεί έστω ένα object. Τα ονόματα colorant πρέπει να είναι μοναδικά και δεν επιτρέπεται να είναι κενά, All, ή None. Οι ορισμοί process και spot μαζί πρέπει να καλύπτουν τα master ονόματα ακριβώς, χωρίς colorant να εμφανίζεται και στα δύο roles και χωρίς κανένα να μένει αόριστο. Όταν ο εναλλακτικός χώρος είναι DeviceCMYK, τα process components πρέπει να είναι Cyan, Magenta, Yellow, Black σε αυτή τη σειρά, και το πλήθος process ονομάτων πρέπει να ταιριάζει με το πλήθος components του εναλλακτικού. Κάθε tint transform και συνάρτηση dot-gain πρέπει να είναι έμμεσο function object με σωστό arity εισόδου και εξόδου. Η solidity πρέπει να είναι πεπερασμένη τιμή μέσα στο 0..1. Η σειρά εκτύπωσης πρέπει να είναι κενή ή πλήρης μετάθεση των master ονομάτων, ποτέ μερική λίστα. Αυτό που δεν κάνει είναι να κρίνει το χρώμα: τίποτα εδώ δεν ελέγχει ότι το tint transform του Orange σας όντως μοιάζει με το μελάνι στο δοχείο, ότι το CMYK build του είναι λογικός proxy, ή ότι η solidity που προμηθεύσατε ταιριάζει με μετρούμενη συμπεριφορά στο υπόστρωμα. Αυτά είναι ερωτήσεις πρέσας και μέτρησης, και το component δεν έχει αρμοδιότητα να απαντήσει σε αυτά. Το απλούστερο overload μόνο-process είναι αυστηρότερο ακόμα by design: παράγει ένα NChannel μόνο-process και αρνείται σκόπιμα να δεχτεί spot ονόματα, επειδή η γραφή ενός spot στον πίνακα ονομάτων χωρίς αντίστοιχη εγγραφή /Colorants θα παρήγαγε δομικά μη συμμορφούμενο αρχείο, και η κατασκευή προεπιλεγμένου ορισμού θα ήταν χειρότερη από την αποτυχία
Τα output intents PDF/X-6n πρέπει να καλύπτουν κάθε εγγεγραμμένο spot
Ένα αρχείο PDF/X-6n με N colorants δηλώνει τα colorants του δύο φορές, και οι δύο δηλώσεις πρέπει να συμφωνούν. Το AddPDFX6ExternalOutputIntent γράφει την αναφορά εξωτερικού ICC profile με το ColorantTable του, και πριν το κάνει, το ValidateRegisteredSpotOutputColorants περπατά κάθε spot που έχει εγγράψει το έγγραφο και πετάει exception αν ένα λείπει από τον πίνακα. Ο έλεγχος τρέχει και προς τις δύο κατευθύνσεις: μόλις ένα output intent έχει δημοσιεύσει τη λίστα colorants του, μια μεταγενέστερη εγγραφή spot για όνομα εκτός εκείνης της λίστας απορρίπτεται επίσης. Το AddPDFX6ExternalOutputIntentSpotData προσθέτει τα metadata ανά μελάνι από πάνω, και επιβάλλει δικούς του κανόνες, κυρίως ότι ένα colorant κουβαλά είτε τιμή solidity είτε φασματικά δεδομένα CxF/X-4, ποτέ και τα δύο. Αυτή είναι η ίδια επιφάνεια συμμόρφωσης που συζητιέται στο κομμάτι για το PDF/A, PDF/X, and PDF/UA validation
Spectral := TMemoryStream.Create;
try
LoadCxFForInk('Orange', Spectral); // payload 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 για ένα colorant
Order,
['Orange'], [Spectral]); // φασματικά δεδομένα για άλλο
finally
Spectral.Free;
end;
Δύο λειτουργικές λεπτομέρειες είναι εύκολο να ξεφύγουν. Το registry που στηρίζει όλα αυτά είναι ανά έγγραφο και καθαρίζεται στα όρια εγγράφων, ώστε η φόρτωση νέου αρχείου στο ίδιο instance THotPDF δεν κληρονομεί τις ταυτότητες spot του προηγούμενου εγγράφου· αυτή η απομόνωση είναι το νόημα, αφού μια διαρρεύσασα υπογραφή θα απέρριπτε απολύτως έγκυρη δουλειά στην επόμενη εργασία. Και η πλευρά profile έχει σκληρά όρια δικά της: ένα εξωτερικό output intent απαιτεί στόχο PDF 2.0, απόλυτο URL profile HTTP ή HTTPS, υπογραφή χρωματικού χώρου ICC τεσσάρων byte, και για PDF/X-6n από 2 έως 15 colorants με αντίστοιχη υπογραφή 2CLR έως FCLR
Πού σταματά ο έλεγχος
Ο χειρισμός CxF/X-4 είναι το μέρος για ειλικρινή συζήτηση. Το HotPDF εφαρμόζει οριοθετημένους δομικούς ελέγχους ασφαλείας στο φασματικό stream και βεβαιώνει ότι η ταυτότητα μελανιού μέσα του ταιριάζει με το colorant που ονομάσατε. Περιορίζει το μέγεθος payload, απορρίπτει ενσωματωμένα null bytes, αρνείται κάθε stream που περιέχει δήλωση DOCTYPE ή ENTITY, απαιτεί αναγνωρίσιμη CxF ρίζα, και απαιτεί ακριβώς ένα στοιχείο SpotInkCharacterisation που κουβαλά ακριβώς ένα SpotInkName ίσο με το όνομα colorant σας. Αυτό είναι φράγμα ενάντια σε κακοδιατύπωτη και εχθρική είσοδο, όχι schema validator. Δεν είναι πλήρης υλοποίηση ISO 17972-4, δεν επαληθεύει τις φασματικές μετρήσεις σας, και δεν κάνει reverse-audit σε αυθαίρετα προϋπάρχοντα object graphs τρίτων ούτε στους εσωτερικούς πίνακες colorant μέσα σε ενσωματωμένο ICC profile. Αν το workflow σας εξαρτάται από πλήρη συμμόρφωση CxF, επαληθεύστε το αρχείο με εξειδικευμένο εργαλείο πριν το παραδώσετε στο component
Ένας γειτονικός περιορισμός δαγκώνει ανθρώπους που δεν περίμεναν ποτέ να τον συναντήσουν. Μια luminosity soft mask δεν μπορεί να χρησιμοποιήσει Separation, DeviceN, ή NChannel ως /CS της ομάδας διαφάνειας· ο blending χώρος της ομάδας πρέπει να είναι device ή CIE-based χώρος, ώστε το spot χρώμα μέσα στην ομάδα επιλύεται μέσω του tint transform του στον εναλλακτικό χώρο πριν υπολογιστεί η luminosity. Το RegisterLuminositySoftMaskState χτίζει ομάδα DeviceGray ακριβώς ώστε αυτό να μην μπορεί να πάει στραβά κατά λάθος. Η πρακτική συνέπεια είναι ότι ένα design πλούσιο σε spot που μασκάρεται έτσι αξιολογείται μέσω του CMYK proxy του, όχι του μελανιού του, κάτι που μετράει όταν το συγκρίνετε με διαχωρισμένο proof όπως περιγράφεται στις σημειώσεις για το overprint proofing and render devices
Κανένα από αυτά δεν αφαιρεί την ανάγκη για plate proof, αλλά μετακινεί μια ολόκληρη κατηγορία απορρίψεων prepress από την αίθουσα πρέσας πίσω στο build. Τα API NChannel, Separation, και PDF/X-6n output intent που περιγράφονται εδώ στέλνονται στο standard HotPDF Delphi Component για Delphi και C++Builder, όπου η αναφορά τεκμηριώνει τα πλήρη record layouts και τις ακριβείς συνθήκες κάτω από τις οποίες κάθε κλήση αποτυγχάνει κλειστά