Vykreslení stránky PDF do kontextu zařízení Windows kvůli náhledu tisku sráží do jednoho řádku kódu tři souřadné soustavy a ty se málokdy shodnou. Stránka PDF se měří v bodech s počátkem vlevo dole. Obrazovkový DC se měří v pixelech s počátkem vlevo nahoře a s faktorem přiblížení, který si zvolíte. Tiskový DC, tedy ten, jejž má náhled předpovídat, měří pixely v rozlišení zařízení, ale klade počátek do rohu tisknutelné oblasti, ne do rohu archu. Spleťte kteroukoli z nich a náhled vypadá dobře, zatímco vytištěná stránka vyjde posunutá, přeškálovaná nebo u okraje oříznutá. Obvyklým příznakem je orámovaný formulář, jenž se v náhledu jeví vystředěný a vytiskne se s uříznutou horní a levou linkou, protože laserová tiskárna nedokáže dát toner do vnějších pár milimetrů a náhledu to nikdo neřekl. losLab PDF Library (PDF Library for Delphi) pokrývá celou tu cestu voláními pro vykreslení do kontextu zařízení, konfigurační vrstvou virtuální tiskárny a náhledovými bitmapami generovanými z metrik samotné tiskárny, což je ta část, díky níž je náhled o oné marži upřímný
Geometrie papíru není geometrie tisknutelné plochy
Každý tiskový cíl popisují dva obdélníky a posun mezi nimi je místem, kde žije většina chyb náhledu. Obdélník papíru je fyzický arch. Tisknutelný obdélník je menší oblast, na kterou tiskový engine skutečně dosáhne, zmenšená o hardwarovou marži lišící se podle modelu tiskárny a někdy i podle zásobníku. Tisková vrstva knihovny měří obojí. Podkladová třída TPLPrinter zpřístupňuje PageWidth a PageHeight pro tisknutelnou oblast, FullPageWidth a FullPageHeight pro celý arch a PrintOffsetX s PrintOffsetY pro mezeru mezi jejich počátky, vše v pixelech zařízení při rozlišení, které hlásí GetDPI. Poctivý náhled tato čísla přeškáluje dolů na rozlišení obrazovky, místo aby stránku namaloval do jakéhokoli obdélníku, jejž má zrovna ovládací prvek k dispozici. Vynechte tento krok a náhled tiše předpokládá nulovou marži, což je jediná hodnota, kterou žádná skutečná tiskárna nemá
Náhled na obrazovce přes RenderPageToDC
Pro náhledový ovládací prvek na obrazovce vykreslí RenderPageToDC(DPI, Page, DC) stránku načteného dokumentu rovnou do libovolného kontextu zařízení GDI, ať už jde o plátno TPaintBox, bitmapu mimo obrazovku nebo DC metasouboru. Argument DPI nastavuje přiblížení. Hodnota 96 přibližně odpovídá zobrazení na 100 % na klasickém displeji a její zdvojnásobení zdvojnásobí vykreslenou velikost
procedure TPreviewForm.PreviewBoxPaint(Sender: TObject);
begin
// tyto tři jsou lepivý stav knihovny, ne parametry jednotlivého volání:
FPdf.SetRenderDCOffset(FOffsetX, FOffsetY);
FPdf.SetRenderDCErasePage(1);
FPdf.SetRenderCropType(0);
FPdf.RenderPageToDC(FPreviewDpi, FCurrentPage, PreviewBox.Canvas.Handle);
end;
Past spočívá v tom, že cestu vykreslení do DC řídí lepivý stav knihovny, nikoli parametry jednotlivých volání. SetRenderDCOffset, SetRenderDCErasePage i SetRenderCropType vydrží, dokud je něco nezmění, takže smyčka generující náhledy, jež poběží poté, co uživatel upravil přiblížené zobrazení, zdědí libovolný posun či ořez, který po sobě předchozí cesta kódem nechala. Příznakem je náhled ujíždějící jen při konkrétních posloupnostech navigace, což je asi tak nejmizernější reprodukovatelnost, jaké může chyba dosáhnout. Nastavení veškerého relevantního stavu na začátku obsluhy překreslení, jak je uvedeno výše, nestojí nic a celou tuto třídu chyb odstraní. Poblíž se skrývá druhý násobitel. Efektivní výstupní rozlišení je měřítko vykreslení krát argument DPI, a přestože SetRenderScale má výchozí hodnotu 1.0, po změně rovněž vydrží, takže exportní funkce, jež jej nadzvedla, tiše přeškáluje každý další náhled, dokud jej něco nevrátí zpět
Rolovací prohlížeče a částečná překreslení mají vyhrazenou variantu. RenderPageToDCClip bere vedle kontextu zařízení i specifikaci ořezu, takže zneplatnění jednoho pásu okna překreslí jen tento pás místo opětovné rasterizace celé stránky. Při vysokém přiblížení na velkoformátových stránkách je to rozdíl mezi prohlížečem, který sleduje posuvník, a prohlížečem, jenž se za ním rozmazává
Tisková úloha, která odpovídá náhledu
Tisková strana pracuje přes virtuální tiskárnu. NewCustomPrinter naklonuje systémovou tiskárnu do konfigurace privátní pro knihovnu a SetupPrinter tento klon upravuje, aniž by sáhl na celosystémový DevMode: papír vstupuje jako nastavení 1 (konstanta DMPAPER_*) a orientace jako nastavení 11. Výhodou je izolace. Služba může tisknout štítky A4, zatímco výchozí tiskárna hostitele zůstává na Letter, a poté není třeba nic vracet do původního stavu
var
Pdf: TPDFlib;
Virt: WideString;
Opt: Integer;
begin
Pdf := TPDFlib.Create;
try
if Pdf.LoadFromFile('report.pdf', '') <> 1 then
raise Exception.Create('load failed');
Virt := Pdf.NewCustomPrinter(Pdf.GetDefaultPrinterName);
Pdf.SetupPrinter(Virt, 1, 9); // nastavení 1 = papír, DMPAPER_A4
Pdf.SetupPrinter(Virt, 11, 1); // nastavení 11 = orientace, 1 = na výšku
Opt := Pdf.PrintOptions(1, 1, 'Monthly Report'); // na papír, otočit + vystředit
Pdf.PrintDocument(Virt, 1, Pdf.PageCount, Opt);
finally
Pdf.Free;
end;
end;
PrintOptions si zaslouží pozorné čtení. Vrací handle voleb, který musíte předat do PrintDocument nebo PrintPages; nejde o okolní stav. Sestavit volby a pak zapomenout handle předat selže tiše. Úloha se vytiskne s výchozími hodnotami a nikdo si toho nevšimne, dokud se čekalo na politiku přizpůsobení papíru a místo toho vyjela předimenzovaná stránka oříznutá. Právě v argumentu škálování stránky tato politika sídlí. Žádné škálování zachovává rozměrovou přesnost, na čemž záleží u formulářů, které se přeměřují pravítkem. Přizpůsobení papíru přeškáluje na arch všechno. Zmenšení velkých stránek nechá běžné stránky být a zasáhne jen tehdy, když stránka přesáhne tisknutelnou oblast, což bývá u smíšené sady dokumentů správná výchozí volba. Příznak automatického otočení a vystředění vyřídí stránky na šířku bez druhé cesty kódem
Aplikace, které už spravují TPrinter přes dialogový tok VCL, jej mohou předat rovnou. PrintDocumentToPrinterObject a PrintPagesToPrinterObject přijímají nakonfigurovanou instanci TPrinter, čímž zůstává standardní tiskový dialog konfigurační plochou obrácenou k uživateli, zatímco knihovna obstará vykreslení stránek. Míchání obou přístupů v jedné cestě kódem má sklon znovu zavést tu geometrickou odchylku, kterou měl zbytek téhle práce zabít, takže si jeden vyberte. Cesta přes virtuální tiskárnu se hodí bezobslužným službám; cesta přes TPrinter interaktivním aplikacím
Výběrový výstup funguje stejně. PrintPages bere řetězec s rozsahem, takže předání názvu virtuální tiskárny, '2-5,12' a handlu voleb vytiskne stránky 2 až 5 a 12 s neporušenou geometrickou smlouvou, a stejná syntaxe řídí i varianty tisku do souboru. Tyto souborové varianty jsou praktickou odpovědí pro bezobslužné prostředí bez připojeného fyzického zařízení: regresní testování geometrie tisku na sestavovacím serveru, jenž nemá vůbec žádnou frontu ovladače. Vykreslujte při každém sestavení tentýž dokument přes tytéž volby do souborového artefaktu a z regrese geometrie se stane rozdíl místo zákaznického hlášení o tři týdny později
Náhledové bitmapy podle metrik samotné tiskárny
Náhled vykreslený při 96 DPI proti předpokládané velikosti stránky odpovídá na špatnou otázku. Ukazuje, jak stránka vypadá, ne co tato tiskárna položí na tento papír. GetPrintPreviewBitmapToString tu mezeru zavírá tím, že náhled staví ze stejné vlastní tiskárny a stejného handlu voleb jako pozdější úloha, takže do bitmapy vstupuje velikost papíru, orientace, politika škálování, otočení i hardwarový posun. Co se vrátí, je to, co arch ukáže
procedure ShowPrinterTruePreview(Pdf: TPDFlib; const Virt: WideString; Opt: Integer);
var
Data: AnsiString;
Strm: TMemoryStream;
Bmp: TBitmap;
begin
Data := Pdf.GetPrintPreviewBitmapToString(Virt, 1, Opt, 1200, 0);
Strm := TMemoryStream.Create;
try
Strm.WriteBuffer(PAnsiChar(Data)^, Length(Data));
Strm.Position := 0;
Bmp := TBitmap.Create;
try
Bmp.LoadFromStream(Strm);
PreviewImage.Picture.Assign(Bmp);
finally
Bmp.Free;
end;
finally
Strm.Free;
end;
end;
Argument MaxDimension omezuje delší hranu bitmapy. Hodnota 1200 pixelů zůstává pro náhledový dialog ostrá a drží paměť při zemi i u konstrukčních výkresů velikosti E, kde by vykreslení v plném rozlišení při 600 DPI tiskárny šlo do gigabajtů
Zapamatování uživatelských voleb tiskárny
Tiskové dialogy, které mezi sezeními zapomínají svá nastavení, generují vlastní požadavky na podporu. Dvojice pro DevMode, GetPrinterDevModeToString a SetPrinterDevModeFromString, serializuje úplnou konfiguraci ovladače tiskárny do neprůhledného řetězce, jejž můžete schovat do uživatelských předvoleb a příští sezení obnovit, včetně voleb specifických pro ovladač, které se žádné obecné API nenamáhá modelovat. Tiskárnu si ukládejte podle názvu z GetPrinterNames, nikdy podle indexu v seznamu. Pořadí indexů se mění pokaždé, když se tiskárna přidá nebo odebere, takže uložený index tiše ukazuje na nesprávné zařízení, jakmile se seznam posune. GetDefaultPrinterName pokrývá záložní řešení pro případ, že zapamatované zařízení zmizelo úplně
Příběh o uchovávání nastavení dokresluje výběr zásobníku. GetPrinterBins hlásí zdroje papíru, které ovladač nabízí, na čemž záleží u postupů s hlavičkovým papírem, kde se první stránka tahá ze zásobníku s hlavičkou a zbytek z běžného papíru. To je politika, u níž uživatelé očekávají, že si ji aplikace zapamatuje spolu se vším ostatním, a tisková úloha, jež dosedne na nesprávný papír, se čte jako chyba, i kdyby byl každý bajt PDF správný
Držte jeden engine pro náhled i tisk
Věrnost tiše řídí ještě jedno poslední rozhodnutí. Výběr vykreslovacího enginu platí pro cíle na obrazovce i na tiskárně, takže je lákavé dělat náhled rychlým enginem a tisknout přesným. Odolejte tomu. Hnát náhled a úlohu různými enginy znovu zavádí přesně tu odchylku věrnosti, kterou měl náhled věrný tiskárně odstranit, a dělá to způsobem, jenž se projeví až na papíře. Kompromisy mezi vestavěným enginem, Cairo a PDFium váží článek vícemotorové vykreslování PDF v Delphi; vyberte si jeden a používejte jej na obou stranách
Dokumenty příliš velké na to, aby se před tiskem pohodlně načetly, lze otevřít cestou přímého přístupu popsanou v článku slučování, dělení a přímý přístup u velkých PDF, která vykresluje stránky do kontextu zařízení z handlu souboru bez stavby stromu dokumentu. Kompletní referenční příručka tiskového API je na produktové stránce losLab PDF Library for Delphi