Artykuł techniczny

Kontrola kolorów PDF/X-1a w HotPDF: wyłap RGB w Delphi

Gdy PDFXCompliance jest ustawione na 'X-1a', HotPDF waliduje kolor treści na samym końcu pracy: EndDoc woła THotPDF.EnsurePDFX1aContentColorSpaces, które odrzuca dowolną treść DeviceRGB, CalRGB albo ICCBased RGB gdziekolwiek na dowolnej stronie i rzuca wyjątek nazywający winny zasób. Plik nigdy nie zostaje zapisany jako dokument PDF/X-1a, który drukarnia odbije

Ta ostatnia część jest sednem. Plik PDF/X-1a z jednym zabłąkanym logo RGB wygląda na ekranie perfekcyjnie, przechodzi pobieżny przegląd i pada na preflight w drukarni dwa dni przed slotem prasy. Wyłapanie tego w buildzie Delphi, z komunikatem mówiącym, która strona i który obraz, jest o wiele tańsze niż dowiedzenie się od wściekłego operatora prepress

Dlaczego PDF/X-1a odrzuca treść RGB?

PDF/X-1a odrzuca RGB, bo format obiecuje, że każdy kolor jest już ostateczny dla jednego konkretnego warunku druku CMYK. ISO 15930-1:2001 §6.2.3 ogranicza kolor treści do DeviceCMYK, DeviceGray i farb spot, czyli przestrzeni Separation i DeviceN, których alternatywy są CMYK albo Gray. OutputIntent GTS_PDFX osadzony w pliku nazywa ten warunek druku, na przykład FOGRA39 coated offset, i odbierający workflow ma prawo wysłać liczby wprost na płyty bez żadnej konwersji barw

RGB łamie ten kontrakt. Wartość RGB opisuje kolor względem wyświetlacza albo przestrzeni roboczej, nie receptę farby, więc ktoś downstream musiałby zdecydować, jak ją wyseparować, a X-1a istnieje właśnie po to, by odebrać tę decyzję odbiorcy. ISO 32000-1 §8.6 grupuje przestrzenie barw PDF w rodziny device, oparte na CIE i specjalne, i członkowie RGB pojawiają się w więcej niż jednej rodzinie: DeviceRGB to przestrzeń device, CalRGB i ICCBased o trzech komponentach są oparte na CIE, a przestrzeń Indexed albo Separation może schować bazę lub alternatywę RGB pod spodem. Kontroler patrzący wyłącznie na operator rg przeoczy większość z nich. Późniejsza edycja PDF/X-1a:2003 (ISO 15930-4) trzyma tę samą regułę barwną; zauważ, że wartość 'X-1a' HotPDF-a zapisuje identyfikację PDF/X-1a:2001 do pakietu XMP

Jeśli twoja treść naprawdę jest RGB i taka ma zostać, X-1a to zły cel. HotPDF ogranicza tę kontrolę wyłącznie do 'X-1a', więc 'X-3' i 'X-4', które pozwalają barwę opartą na ICC i kalibrowaną, pozostają nietknięte

Co faktycznie sprawdza EnsurePDFX1aContentColorSpaces

EnsurePDFX1aContentColorSpaces łączy flagę z czasu rysowania z rekurencyjnym przejściem po zasobach każdej strony i działa ostatnie w bramce PDF/X. EndDoc najpierw wymusza ogólne wymagania PDF/X: Title musi być ustawione, Trapped musi być 'True', 'False' albo 'Unknown', OutputIntent GTS_PDFX musi istnieć (EnsurePDFXOutputIntent), a każda strona potrzebuje TrimBox albo ArtBox (EnsurePDFXPageBoxes). Dopiero po ich zaliczeniu rusza kontrola koloru

Sekwencja bramki PDF/X wewnątrz HotPDF EndDoc dla Delphi: najpierw metadane, OutputIntent i pola stron, potem kontrola koloru treści RGB
kontrola koloru działa ostatnia w bramce X-1a, a flaga z czasu rysowania znaczy, że RGB narysowane przed przypisaniem zgodności też zostanie wyłapane

Pierwsza ścieżka to flaga dla całego dokumentu. Setter kolorów RGB HotPDF-a, osadzanie bitmap i wpisy kolorów adnotacji rejestrują użycie DeviceRGB w czasie rysowania, co obejmuje inline operatory rg i RG, które nigdy nie pojawiają się w słowniku zasobów. Druga ścieżka przechodzi przez /Resources każdej strony i zagląda w:

  • Nazwane przestrzenie barw w /ColorSpace, łącznie ze skrótem obrazów inline RGB jako synonimem DeviceRGB
  • /ColorSpace każdego Image XObject
  • Form XObjects i wzory tiling, przez rekurencję w ich własne zagnieżdżone /Resources
  • Shading Patterns i wpisy w słowniku /Shading strony, przez /ColorSpace shadingu
  • Bazy przestrzeni Indexed, alternatywy Separation i DeviceN oraz bazy wzorów, śledzone w dół do zagnieżdżenia 8

Odrzucane są trzy rzeczy: DeviceRGB, CalRGB oraz przestrzenie ICCBased, których strumień profilu deklaruje N = 3. Słowniki zasobów współdzielone między Form XObjects i wzorami są odwiedzane raz, więc wzajemnie się referujący zestaw form nie zapętla przejścia. Metoda jest publiczna na THotPDF, ale w normalnym użyciu nigdy sam jej nie wołasz, bo robi to za ciebie EndDoc

Gdzie RGB się chowa w zasobach stron PDF: zagnieżdżone formy, wzory, shadingi i alternatywy prowadzą przejście HotPDF w Delphi do DeviceRGB, CalRGB albo ICCBased RGB
przejście idzie za zagnieżdżonymi zasobami do głębokości 8 i odrzuca DeviceRGB, CalRGB i przestrzenie ICCBased z N = 3, gdziekolwiek się pokażą

Rysowanie strony X-1a w Delphi przechodzącej kontrolę

Zgodny dokument X-1a HotPDF potrzebuje OutputIntent CMYK, TrimBox na każdej stronie i koloru ustawianego wyłącznie przez API CMYK, Gray i spot. Szkielet poniżej używa profilu FOGRA39; AddPDFXOutputIntent bierze identyfikator warunku wyjściowego, string info, strumień profilu ICC, liczbę jego komponentów i przestrzeń alternatywną

var
  Pdf: THotPDF;
  Profile: TFileStream;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'brochure-x1a.pdf';
    Pdf.Title := 'Spring brochure, press edition';
    Pdf.Trapped := 'False';
    Pdf.PDFXCompliance := 'X-1a';
    Pdf.BeginDoc;

    Profile := TFileStream.Create('CoatedFOGRA39.icc', fmOpenRead or fmShareDenyWrite);
    try
      Pdf.AddPDFXOutputIntent('FOGRA39 (ISO 12647-2:2004)',
        'Coated FOGRA39 offset', Profile, 4, 'DeviceCMYK');
    finally
      Profile.Free;
    end;
    Pdf.CurrentPage.SetTrimBox(0, 0, 612, 792);   // wymagany na każdej stronie

    Pdf.CurrentPage.SetCMYKFillColor(0, 0.5, 0.8, 0.1);
    Pdf.CurrentPage.Rectangle(72, 600, 468, 120);
    Pdf.CurrentPage.Fill;

    Pdf.CurrentPage.SetGrayFillColor(0.2);         // tekst DeviceGray
    Pdf.CurrentPage.SetFont('Arial', [], 14);
    Pdf.CurrentPage.TextOut(72, 560, 0, 'Spring catalog, press edition');
    Pdf.EndDoc;                                    // tu działa kontrola koloru
  finally
    Pdf.Free;
  end;
end;

Każde wywołanie oparte na TColor to pułapka w tym kontekście. SetRGBFillColor, SetRGBStrokeColor i SetRGBColor wszystkie biorą kolor GDI i emitują DeviceRGB, więc helper napisany lata temu dla raportów pod ekran zatru zadanie drukowane w chwili, gdy zostanie ponownie użyty. Używaj SetCMYKFillColor, SetCMYKStrokeColor albo SetCMYKColor z komponentami w zakresie od 0 do 1, albo odpowiedników Gray

Farby spot i gradienty CMYK

Farby spot są dozwolone w X-1a, dopóki ich alternatywa jest CMYK albo Gray. RegisterSeparation buduje przestrzeń Separation i zwraca jej nazwę zasobu, a RegisterAxialGradient dobiera przestrzeń barwną shadingu z liczby komponentów, którą podasz, przy czym cztery komponenty znaczy DeviceCMYK

var
  SpotCS, Ramp: AnsiString;
begin
  // Farba spot z przybliżonym składem CMYK jako alternate
  SpotCS := Pdf.RegisterSeparation('Corporate Red', 'DeviceCMYK',
    [0, 1, 0.8, 0.05]);
  Pdf.CurrentPage.SetFillColorSpace(SpotCS);
  Pdf.CurrentPage.SetFillColor([1.0]);           // tint 100%
  Pdf.CurrentPage.Rectangle(72, 420, 200, 80);
  Pdf.CurrentPage.Fill;

  // 4 komponenty na stop: shading to DeviceCMYK, nie DeviceRGB
  Ramp := Pdf.RegisterAxialGradient(72, 300, 540, 300,
    [0, 0, 0, 0], [1, 0.6, 0, 0.2]);
  Pdf.CurrentPage.SetFillPattern(Ramp);
  Pdf.CurrentPage.Rectangle(72, 260, 468, 80);
  Pdf.CurrentPage.Fill;
end;

Podaj temu samemu wywołaniu gradientu trzy komponenty, a dostaniesz shading DeviceRGB, który przejście po zasobach odrzuci pod nazwą wzoru shadingu. To samo dotyczy Separacji zarejestrowanej z 'DeviceRGB' jako alternatywą: nazwa farby spot wygląda niewinnie, ale przejście idzie za alternatywą i znajduje RGB pod spodem. Jak takie farby spot są komponowane, gdy przeglądarka albo RIP spada na alternatywę, opisuje artykuł renderowaniu Separation i DeviceN farb spot w Delphi

Obrazy rastrowe potrzebują tej samej dyscypliny. HotPDF przybija bitmapy skompresowane Flate do DeviceRGB, podczas gdy JPEG osadzony z icJpeg trzyma własną liczbę komponentów, więc czterokanałowy CMYK JPEG albo skala szarości jest w porządku, a zwykły RGB JPEG nie. Konwertuj grafikę do profilu prasowego w swoim pipeline obrazowania, zanim dotrze do PDF-a

Dlaczego /DefaultRGB nie ratuje pliku PDF/X-1a?

/DefaultRGB nie pomaga w X-1a, bo reguła dotyczy tego, co dostaje prasa, nie tego, czy RGB jest skarakteryzowane. Dla PDF/A HotPDF pozwala, by RegisterDefaultColorSpace('DefaultRGB', ...) wchłonęło użycie DeviceRGB: gdy domyślna przestrzeń barw mapuje DeviceRGB na profil ICC, treść jest niezależna od urządzenia i kontrola spójności PDF/A ją przyjmuje. Deweloperzy, którzy przeszli przez ten workflow, mają tendencję do próbowania tego samego lekarstwa tutaj

EnsurePDFX1aContentColorSpaces celowo ignoruje to mapowanie. Skarakteryzowana wartość RGB nadal jest RGB, a OutputIntent X-1a to warunek druku CMYK, więc ktoś i tak musiałby ją skonwertować. Jeśli masz zapamiętać jedną różnicę między tymi dwoma profilami archiwalnym i drukowym, niech będzie ta: PDF/A pyta, czy kolor jest jednoznaczny, PDF/X-1a pyta, czy jest już farbą

Kolejność rysowania i setup strony, który się nie liczy

Ponieważ kontrola działa przy EndDoc na już zapisanej treści, kolejność, w jakiej sobie konfigurujesz, nie ma znaczenia. RGB narysowane przed przypisaniem PDFXCompliance albo przed rejestracją OutputIntent nadal zostanie wyłapane. To ma znaczenie dla kodu budującego strony we współdzielonych procedurach i decydującego o profilu wyjściowym późno, czasem na podstawie ustawienia użytkownika wybranego, gdy pół dokumentu już istnieje

Pdf.BeginDoc;
// ... OutputIntent i TrimBox jak wyżej ...
Pdf.CurrentPage.SetRGBFillColor(clRed);   // stary helper, emituje DeviceRGB
Pdf.CurrentPage.Rectangle(72, 72, 200, 100);
Pdf.CurrentPage.Fill;

Pdf.PDFXCompliance := 'X-1a';             // zadecydowane po rysowaniu
Pdf.EndDoc;                               // nadal rzuca: DeviceRGB zapisany

Flaga z czasu rysowania ma jedną pułapkę wartą znajomości, jeśli budujesz podobną kontrolę sam. Każda nowa strona jest inicjalizowana czarnym stanem graficznym przez SetRGBColor(0), co emituje parę 0 0 0 rg i 0 0 0 RG, i gdyby ten boilerplate setupu strony mógł ustawić flagę DeviceRGB, każdy dokument wyglądałby na zatruty RGB — zarówno dla tej kontroli, jak i dla kontroli spójności barw PDF/A. HotPDF zapisuje i przywraca flagę wokół tej inicjalizacji, więc liczy się tylko kolor, który faktycznie narysujesz

Jak znaleźć treść RGB, gdy EndDoc rzuca?

Czytaj lokalizację z początku komunikatu wyjątku, bo mówi ci, która z dwóch ścieżek znalazła problem. Komunikat ma formę PDFXCompliance 'X-1a': <where> uses <what>, po której idzie referencja ISO 15930-1 i rada, by skonwertować do CMYK albo Gray albo przejść na X-3 lub X-4. Walidator rzuca zwykły Exception przy pierwszym naruszeniu, więc dopasowuj na tekst, nie na klasę wyjątku

Anatomia wyjątku RGB z HotPDF EndDoc w Delphi: flaga z czasu rysowania raportuje użycie w całym dokumencie, a przejście po zasobach nazywa dokładną stronę i klucz
czytaj lokalizację z początku komunikatu — raport dla całego dokumentu wysyła cię do kodu rysowania, strona i nazwa zasobu wskazują jeden klucz
  • document content uses DeviceRGB (rg/RG operators, RGB images or annotation colours recorded during drawing) znaczy, że zapaliła się flaga z czasu rysowania. Nie ma numeru strony, bo flaga jest ogólnodokumentowa i sprawdzana przed przejściem. Przeszukaj kod rysowania za wywołaniami SetRGB*, bitmapami Flate, JPEG-ami RGB i kolorami adnotacji TColor
  • page 3 image XObject /Im2 uses DeviceRGB content albo page 1 shading Pattern /P1 uses DeviceRGB content przychodzi z przejścia po zasobach i wskazuje dokładny klucz zasobu
  • uses an ICCBased RGB colour space (N=3) po lokalizacji ColorSpace /... to typowy wynik rejestracji profilu sRGB przez RegisterICCProfile i wybrania go przez SetFillColorSpace, ścieżki, której flaga z czasu rysowania nie widzi
  • Zagnieżdżone lokalizacje się łańcuchują, na przykład page 2 Form XObject /Fm1 ColorSpace /CS1, więc możesz iść ścieżką od strony w dół do zasobu niosącego przestrzeń RGB
try
  Pdf.EndDoc;
except
  on E: Exception do
  begin
    if Pos('X-1a', E.Message) > 0 then
      LogPrepressFailure(Pdf.FileName, E.Message);  // twój własny logger
    raise;
  end;
end;

Ponieważ raportowane jest tylko pierwsze naruszenie, naprawianie zadania z kilkoma źródłami RGB jest iteracyjne: napraw, przebuduj, przeczytaj następny komunikat. Dla zadań wsadowych opłaca się logować pełny komunikat per plik i trzymać krótką listę znanych sprawców, jak bitmapa logo albo helper wykresów, który nadal bierze TColor

Gdzie bramka RGB pasuje do twojego preflightu

EnsurePDFX1aContentColorSpaces to celowana bramka RGB działająca wewnątrz twojego builda i stanowi parę z pełnym preflightem PDF/X-1a, zamiast go zastępować. Pokrywa rodziny RGB — przez przejście po zasobach od /Resources każdej strony i przez flagę z czasu rysowania, którą prowadzą settery kolorów HotPDF-a dla inline operatorów rg i RG; zagnieżdżone przestrzenie barw są śledzone do głębokości 8 jako zabezpieczenie przed zepsutymi cyklami, a pierwsze naruszenie jest raportowane per EndDoc. Inne rodziny barw jak Lab albo CalGray to materiał dla pełnego preflightu, więc przegon skończony plik przez zewnętrzny walidator jako część builda, by dostać pełny werdykt zgodności. Szerszy obraz wyników zgodności jest w przewodniku po wyjściu PDF/A, PDF/X i PDF/UA z HotPDF, a podpięcie walidatora do pipeline'u nienadzorowanego opisuje automatyzacja kontroli preflight PDF w Delphi

HotPDF to natywny komponent VCL PDF dla Delphi i C++Builder z wyjściem PDF/X, PDF/A i PDF/UA, API kolorów CMYK i spot oraz wsparciem ICC OutputIntent. Szczegóły, licencjonowanie i pobranie próbne są na stronie produktu HotPDF Delphi PDF component