Cyjanowy prostokąt siedzi na żółtym tle, a stan graficzny, który go maluje, niesie /op true i /OPM 1. Na ekranie nałożenie to płaski cyjan. Na arkuszu drukarskim te same nałożenie wychodzi zielone, bo RIP zostawił płytę żółtą w spokoju i położył cyjan na jej wierzchu. Oba wyniki trzymają się pliku co do litery; render ekranowy po prostu nigdy nie zapytał, co urządzenie odtwarzające robi z tą flagą — więc akceptacja zrobiona na ekranie przykrywa kolor, który nigdy nie dotrze do papieru
Dlaczego strona drukuje się inaczej, niż podgląda?
Overprint to instrukcja dla urządzenia wyjściowego o płytach, a ekran RGB nie ma płyt. ISO 32000-1 §8.6.7 definiuje, co się dzieje, gdy wpis OP (kreskowanie) albo op (wypełnianie) ExtGState jest prawdziwy: coloranty, których bieżąca farba nie nazywa, zachowują to, co już leżało na ich płycie, zamiast być wymazywane do zera. Obiekt cyjanowy malowany tak nie zdejmuje niczego z płyty żółtej. Przeglądarka składająca w RGB nie ma gdzie trzymać żółtego, więc maluje cyjan na wierzchu i jedzie dalej
Żadno z wyjść nie jest zepsute. Rozjazd siedzi wbudowany w dwa modele odtwarzania i pozostaje niewidoczny, dopóki ktoś nie przyłoży drukowanej próbki do monitora. To późno w pracy dla projektanta, który ustawił czarny tekst na knockout przez pomyłkę albo zostawił białe logo na overprinccie i liczył, że się pokaże
Włączanie proofu przez RenderOverprintPreview
HotPDF wystawia proof jako jedną wartość Boolean na dokumencie, THotPDF.RenderOverprintPreview, domyślnie False. Po ustawieniu render bitmapowy śledzi płyty procesowe pod każdą nakładającą farbą i nakłada reguły §8.6.7 przed zapisem piksela, więc nałożenia pokazują kombinację farb, którą wyprodukowałaby maszyna. Zostawienie domyślnego wyłączenia jest właściwe do zwykłego oglądania, bo czytelnicy biurowego PDF oczekują, że będzie wyglądał tak, jak pokazuje go ich zwykła przeglądarka. Włącz go w kroku, którego wyjście idzie do operatora druku przygotowawczego
var
Pdf: THotPDF;
Device: THPDFBitmapRenderDevice;
Proof: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.LoadFromFile('cover-cmyk.pdf');
Pdf.RenderOverprintPreview := True; // proofuj farby, domyślnie False
Device := THPDFBitmapRenderDevice.Create;
try
if Pdf.RenderLoadedPageToDevice(0, 150, Device) then
begin
Proof := Device.TakeBitmap; // wywołujący posiada Proof od tego momentu
try
Image1.Picture.Assign(Proof);
finally
Proof.Free;
end;
end;
finally
Device.Free;
end;
finally
Pdf.Free;
end;
end;
Reguły płyt zależą od przestrzeni kolorów nakładającej farby i warto je znać, bo każda wyjaśnia inne zaskoczenie na próbce:
- DeviceCMYK pod
/OPM 1: składnik zerowy zostawia swoją płytę nietkniętą, składnik niezerowy ją zastępuje. Cyjan 100 / żółty 0 na żółtym tle zachowuje żółty i robi zielony - DeviceGray: farba nazywa wyłącznie płytę czarną, więc cyjan, magenta i żółty pod spodem przeżywają. Białe logo DeviceGray ustawione na overprint na tle CMY dlatego znika
- Separation i DeviceN: colorant nazwany
Cyan,Magenta,YellowalboBlackzastępuje płytę procesową wprost,/Nonenie maluje niczego, a/Allzapisuje każdą płytę procesową - Coloranty spot: każda inna nazwa jest traktowana jako farba spot; jej transformacja tintu daje kolor alternatywny, który jest mnożony przez płyty procesowe pod spodem, więc spot zaciemnia to, co pokrywa, zamiast tego zastępować
Proof obejmuje wypełniane i kreskowane ścieżki oraz tekst, włącznie z tekstem w trybie renderowania kreskowania, gdzie decyduje flaga kreskowania OP, a nie op. Obrazy, obrazy inline i cieniowania sh przechodzą dalej przez swoje pełne rastrowe potoki kolorów i malują ze zwykłą nieprzezroczystością. W praktyce pokrywa to to, co raporty preflight flagują najczęściej: reguły overprintu, keyline'y, tekst i płaskie tinty
Przezroczystość zostaje żywa, póki proof jest włączony. Grupa przezroczystości z ustawionym knockout nadal wycina, a grupa jest składana w tym samym przebiegu co reguły overprintu, więc plik gotowy do druku z przezroczystą grafiką na nakładającym się czarnym tle proofuje oba efekty razem. Trafienie w jedno i pudło w drugie wyprodukowałoby po prostu inny zły proof
Ustawienie bierze też udział w tożsamości renderów w cache. RenderLoadedPageToBitmapCached kluczuje swoje wpisy na stronie, DPI i wariancie zbudowanym z ustawień renderowania, do których należy RenderOverprintPreview, a warstwa dyskowa używa tego samego wariantu. Przeglądarka przełączająca się między podglądem zwykłym a proofem dostaje dwa osobne wpisy w cache i nigdy nie potrzebuje wywołania InvalidateRenderedPageCache tylko dlatego, że przełącznik się zmienił
Pułapka /OPM 0 w plikach DeviceCMYK
Oto przypadek, który wysyła ludzi szukać błędu. Plik ustawia /op true na cyjanowym wypełnieniu DeviceCMYK, ale zostawia OPM na domyślnym 0 — proof nadal pokazuje cyjan wycinający żółty. Proof ma rację. Pod trybem overprintu 0 §8.6.7 mówi, że farba DeviceCMYK ustawia wszystkie cztery coloranty procesowe, zera włącznie, więc zerowy składnik żółty zastępuje płytę żółtą dokładnie tak, jak zrobiłby to knockout. Tryb overprintu 0 chroni tylko płyty, których farba nie nazywa — co dla koloru czteroskładnikowego CMYK znaczy płyty spot i nic więcej
Na płytach procesowych, które pokazuje proof składany, tryb 0 i zwykły knockout dają te same piksele, więc HotPDF maluje te obiekty DeviceCMYK jako zwykłe knockouti. Żeby obiekt CMYK nakładał się na inne farby procesowe, ExtGState niosący /op true potrzebuje też /OPM 1. Gdy proof i zamiar projektanta rozmijają się na obiekcie DeviceCMYK, otwórz ten ExtGState i poszukaj wpisu OPM, zanim podejrzysz cokolwiek innego
Odwrócone zaskoczenie też istnieje. Pod /OPM 1 biel CMYK to cztery zera, a cztery zery pod overprintem zostawiają każdą płytę taką, jaka była. Biały tekst ustawiony na overprint znika z arkusza, a proof pokazuje jego znikanie — i to jest cały powód, żeby go odpalać
Trzy urządzenia za RenderLoadedPageToDevice
THotPDF.RenderLoadedPageToDevice(PageIndex, DPI, Device) to pojedynczy punkt wejścia, którego cel rozstrzyga obiekt urządzenia, który podasz. Podnosi wyjątek dla urządzenia nil, traktuje DPI poniżej 1 jako 150 i zwraca to, co urządzenie raportuje z metody Execute. THPDFRenderDevice to abstrakcyjna baza z dwoma wirtualnymi składnikami: Kind, zwracającym rdkBitmap, rdkDeviceContext albo rdkEnhancedMetafile, oraz Execute(Document, PageIndex, DPI)
Trzy dostarczone urządzenia posiadają swoje wyjścia po różnemu:
THPDFBitmapRenderDevicetrzymaTBitmapwe właściwościBitmapi zwalnia go w destruktorze, chyba żeTakeBitmapoddał go ci najpierwTHPDFDeviceContextRenderDevice.Create(ADC, AWidth, AHeight)rysuje doHDC, który już masz, i nie posiada niczego; konstruktor podnosi wyjątek na zerowym DC albo niedodatnim rozmiarze, a tło nie jest malowane, więc ekranowy DC potrzebuje własnego białego wypełnieniaTHPDFMetafileRenderDevicetrzymaTMetafile, ażTakeMetafilego przekaże. Tekst i wektorowe ścieżki zostają skalowalne w rozszerzonym metapliku, a image XObject przychodzą zrastrowane
Własność podąża za zwykłymi regułami Delphi. Czytaj właściwość Bitmap albo Metafile, gdy obiekt musi żyć tylko tak długo jak urządzenie; wołaj metodę Take, gdy ma je przeżyć. Weź bitmapę, zwolnij urządzenie, zapomnij o bitmapie — a wyciek rośnie z liczbą stron: niewidoczny na próbce pięciostronicowej, oczywisty na partii pięciuset stron. Daj każdemu obiektowi własne try/finally, a pytanie kto co zwalnia odpowie się samo
Dyspatchuj po Kind w kodzie aplikacji zamiast testować is THPDFBitmapRenderDevice. Skoro Execute i Kind są publicznymi metodami wirtualnymi, możesz napisać własną klasę urządzenia, a kod sprawdzający kind dalej działa, gdy któraś z twoich zastępuje dostarczone urządzenie
Dlaczego render drukarkowy ląduje w rogu?
Ścieżka kontekstu urządzenia wymiaruje stronę z argumentu DPI, nie z szerokości i wysokości, które podajesz. Renderer skaluje jednostki PDF przez DPI / 72 i używa wysokości wyłącznie do zakotwiczenia początku strony na dole powierzchni. Zahardkoduj 300 i wyślij zadanie do drukarki 600 dpi, a strona wyjdzie w połowie rozmiaru, wepchnięta w lewy dolny róg arkusza. Rozdzielczość drukarki to własność sterownika, więc zapytaj o nią sterownika:
procedure PrintPage(Pdf: THotPDF; PageIndex: Integer);
var
Device: THPDFDeviceContextRenderDevice;
PrinterDPI, W, H, Tmp: Integer;
L, B, R, T: Single;
begin
if not Pdf.GetLoadedPageVisibleBox(PageIndex, L, B, R, T) then
Exit;
Printer.BeginDoc;
try
// zapytaj sterownik; 300, 600 i 1200 dpi to wszystko częste wartości
PrinterDPI := GetDeviceCaps(Printer.Handle, LOGPIXELSX);
W := Round((R - L) * PrinterDPI / 72);
H := Round((T - B) * PrinterDPI / 72);
if (Pdf.GetLoadedPageRotation(PageIndex) mod 180) = 90 then
begin
Tmp := W; W := H; H := Tmp; // zamiana dla stron 90 / 270
end;
Device := THPDFDeviceContextRenderDevice.Create(
Printer.Canvas.Handle, W, H);
try
Pdf.RenderLoadedPageToDevice(PageIndex, PrinterDPI, Device);
finally
Device.Free;
end;
finally
Printer.EndDoc;
end;
end;
Szerokość i wysokość pochodzą z widocznego pola strony — CropBox przyciętego do MediaBox — w własnej rozdzielczości drukarki, a wywołujący zamienia je miejscami dla obróconych stron przed utworzeniem urządzenia. To odpowiada temu, czego renderer oczekuje przy kotwiczeniu początku
Proofing overprintu nie jedzie tą drogą. Urządzenia kontekstu urządzenia i metapliku emitują wywołania GDI wprost do celu, a reguły płyt potrzebują pikseli już leżących pod każdą farbą — co ma tylko render bitmapowy. Dla próbki na papierze wyrenderuj proof bitmapowy i rozciągnij go na kanwę drukarki. Małe własne urządzenie trzyma to za tym samym punktem wejścia:
type
// urządzenie własne: proof bitmapowy, potem rozciągnięty na drukarkę
TPrinterProofDevice = class(THPDFRenderDevice)
public
function Kind: THPDFRenderDeviceKind; override;
function Execute(Document: THotPDF; PageIndex, DPI: Integer): boolean;
override;
end;
function TPrinterProofDevice.Kind: THPDFRenderDeviceKind;
begin
Result := rdkDeviceContext;
end;
function TPrinterProofDevice.Execute(Document: THotPDF;
PageIndex, DPI: Integer): boolean;
var
Proof: TBitmap;
OldProof: Boolean;
begin
OldProof := Document.RenderOverprintPreview;
Document.RenderOverprintPreview := True;
try
Proof := Document.RenderLoadedPageToBitmap(PageIndex, DPI);
finally
Document.RenderOverprintPreview := OldProof; // zostaw ustawienie wywołujące takie, jakie było
end;
Result := Proof <> nil;
if Result then
try
Printer.Canvas.StretchDraw(
Rect(0, 0, Printer.PageWidth, Printer.PageHeight), Proof);
finally
Proof.Free;
end;
end;
Wołaj go między Printer.BeginDoc a Printer.EndDoc z DPI dającym bitmapie dość pikseli na arkusz — około 200 do 300 dla próbki wielkości listu. Prostokąt rozciągania wyżej wypełnia obszar drukowalny; zachowaj w nim proporcje strony, jeśli strona i papier różnią się kształtem
Gdzie proofing pasuje obok preflight
Proof odpowiada na jedno pytanie: które farby przeżywają tam, gdzie obiekty się nakładają. Jest składany na tej samej bitmapie RGB 24-bitowej co każdy inny render, więc jego kolory pokazują kombinację farb, a nie zmierzoną barwę arkusza. Warunki maszynowe to pytanie o zarządzanie kolorem i dokument załatwia je przez ConfigureRenderICCWorkflow, które bierze profil proofu, profil wyjściowy i flagę kompensacji czerni, oraz ClearRenderICCWorkflow, żeby je z powrotem zrzucić
Najbardziej produktywnym miejscem dla renderu proofu jest sąsiedztwo kontroli, które już odpalasz. Przebieg preflight raportuje, że czarny tekst jest ustawiony na overprint; render proofu pokazuje operatorowi, co to robi na stronie; oba lądują w tym samym raporcie. Praca opakowaniowa miesza overprint z farbami spot, a przejście przez artykuł renderowanie farb spot Separation i DeviceN pokrywa stronę colorantów tej samej karty, włącznie z widokami pojedynczej płyty przez RenderLoadedPageSeparationPlate. Artykuły o renderowaniu strony PDF do bitmapy i drukowaniu wczytanego PDF przez TPrinter pokrywają oba cele urządzeń w ich gołej formie
Całość biegnie w natywnym kodzie Delphi i C++Builder bez żadnej DLL renderującej do wysyłki obok pliku wykonywalnego. Strona komponentu HotPDF Delphi wypisuje cechy renderowania i przygotowania do druku oraz ma build próbny, na którym odpalisz te przykłady na własnych plikach drukowych