Vykreslenie PDF stránky do Windows kontextu zariadenia (device context) pre náhľad tlače spája tri súradnicové systémy v rovnakom riadku kódu, ktoré sa však málokedy zhodujú. PDF stránka sa meria v bodoch s počiatkom vľavo dole. Obrazovkový DC (device context) sa meria v pixeloch s počiatkom vľavo hore a zvoleným faktorom priblíženia. Tlačový DC, ktorého správanie má náhľad predpovedať, meria pixely v rozlíšení zariadenia, ale jeho počiatok je v rohu tlačiteľnej oblasti, nie v rohu hárku. Ak urobíte chybu v ktoromkoľvek z nich, náhľad bude vyzerať v poriadku, zatiaľ čo vytlačená stránka bude posunutá, zmenšená alebo orezaná na okraji. Obvyklým príznakom je formulár s ohraničením, ktorý sa v náhľade zobrazuje vycentrovaný, ale pri tlači má odrezaný horný a ľavý okraj, pretože laserová tlačiareň nedokáže tlačiť na posledných niekoľkých milimetroch okraja a náhľad o tom nevie. Knižnica losLab PDF Library (PDFlibPas) pokrýva celý tento proces pomocou volaní na vykresľovanie do kontextu zariadenia, konfiguračnej vrstvy virtuálnej tlačiarne a náhľadových bitmap generovaných priamo z metrík tlačiarne, vďaka čomu náhľad presne zohľadňuje daný okraj.
Geometria papiera nie je to isté ako tlačiteľná geometria
Akýkoľvek cieľ tlače opisujú dva obdĺžniky a odsadenie medzi nimi je miestom, kde vzniká väčšina chýb v náhľade. Obdĺžnik papiera predstavuje fyzický hárok. Tlačiteľný obdĺžnik je menšia oblasť, ktorú dokáže tlačové zariadenie skutočne dosiahnuť, zmenšená o hardvérový okraj, ktorý sa líši podľa modelu tlačiarne a niekedy aj podľa podávača. Tlačová vrstva knižnice meria obidva tieto rozmery. Interná trieda TPLPrinter poskytuje vlastnosti PageWidth a PageHeight pre tlačiteľnú oblasť, FullPageWidth a FullPageHeight pre celý hárok a PrintOffsetX s PrintOffsetY pre medzeru medzi ich počiatkami, všetko v pixeloch zariadenia pri rozlíšení, ktoré vracia funkcia GetDPI. Presný náhľad prepočíta tieto hodnoty na rozlíšenie obrazovky, namiesto toho, aby vykreslil stránku do akéhokoľvek obdĺžnika, ktorý má daný komponent k dispozícii. Ak tento krok vynecháte, náhľad bude potichu predpokladať nulový okraj, čo je hodnota, ktorú žiadna skutočná tlačiareň nepoužíva.
Obrazovkový náhľad pomocou RenderPageToDC
V prípade komponentu pre obrazovkový náhľad metóda RenderPageToDC(DPI, Page, DC) vykreslí stránku načítaného dokumentu priamo do akéhokoľvek GDI kontextu zariadenia, či už ide o plátno TPaintBox, bitmapu v pamäti alebo DC metasúboru. Argument DPI nastavuje priblíženie. Hodnota 96 zodpovedá približne 100 % zobrazeniu na klasickom monitore a jej zdvojnásobenie zdvojnásobí veľkosť vykreslenia.
procedure TPreviewForm.PreviewBoxPaint(Sender: TObject);
begin
// these three are sticky library state, not per-call parameters:
FPdf.SetRenderDCOffset(FOffsetX, FOffsetY);
FPdf.SetRenderDCErasePage(1);
FPdf.SetRenderCropType(0);
FPdf.RenderPageToDC(FPreviewDpi, FCurrentPage, PreviewBox.Canvas.Handle);
end;
Pascou je, že vykresľovacia cesta pre DC je riadená trvalým stavom knižnice, nie parametrami jednotlivých volaní. Nastavenia SetRenderDCOffset, SetRenderDCErasePage a SetRenderCropType zostávajú aktívne dovtedy, kým ich niečo nezmení, takže slučka pre generovanie miniatúr, ktorá sa spustí po tom, čo používateľ upravil priblíženie zobrazenia, zdedí akékoľvek odsadenie alebo orezanie z predchádzajúceho volania. Príznakom je náhľad, ktorý sa posúva iba pri určitých sekvenciách navigácie, čo je chyba, ktorá sa reprodukuje mimoriadne ťažko. Nastavenie všetkých dôležitých stavov na začiatku obslužnej procedúry prekreslenia (ako je uvedené vyššie) nestojí takmer nič a odstraňuje celú túto triedu chýb. V blízkosti sa skrýva aj druhý násobiteľ. Efektívne výstupné rozlíšenie je dané mierkou vykresľovania vynásobenou argumentom DPI, a hoci je predvolená hodnota SetRenderScale 1.0, po zmene pretrváva, takže exportná funkcia, ktorá ju zvýšila, potichu zmení mierku každého neskoršieho náhľadu, kým ju niečo nevráti späť.
Pre rolovateľné zobrazenia a čiastočné prekreslenia je k dispozícii špeciálny variant. Metóda RenderPageToDCClip prijíma okrem kontextu zariadenia aj špecifikáciu orezania, takže pri zneplatnení (invalidate) jednej časti okna sa prekreslí iba táto časť namiesto opätovného rastrovania celej stránky. Pri vysokom priblížení na veľkoformátových stránkach je to rozdiel medzi zobrazením, ktoré plynule nasleduje posuvník, a zobrazením, ktoré za ním viditeľne zaostáva.
Tlačová úloha, ktorá zodpovedá náhľadu
Tlačová strana funguje prostredníctvom virtuálnej tlačiarne. Funkcia NewCustomPrinter naklonuje systémovú tlačiareň do privátnej konfigurácie knižnice a SetupPrinter upraví tento klon bez ovplyvnenia globálneho nastavenia DevMode v systéme: papier sa definuje ako nastavenie 1 (konštanta DMPAPER_*) a orientácia ako nastavenie 11. Výhodou je izolácia. Služba môže tlačiť štítky na formát A4, zatiaľ čo predvolená tlačiareň hostiteľa zostáva nastavená na formát Letter, pričom po dokončení nie je potrebné nič vracať 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); // setting 1 = paper, DMPAPER_A4
Pdf.SetupPrinter(Virt, 11, 1); // setting 11 = orientation, 1 = portrait
Opt := Pdf.PrintOptions(1, 1, 'Monthly Report'); // fit to paper, auto-rotate + center
Pdf.PrintDocument(Virt, 1, Pdf.PageCount, Opt);
finally
Pdf.Free;
end;
end;
Funkcia PrintOptions si vyžaduje pozornosť. Vracia handle (odkaz) nastavení, ktorý musíte odovzdať metóde PrintDocument alebo PrintPages; nejde o globálny stav. Ak vytvoríte nastavenia a potom zabudnete odovzdať tento handle, operácia zlyhá bez chybového hlásenia. Úloha sa vytlačí s predvolenými hodnotami, čo si nikto nevšimne, kým sa neočakáva prispôsobenie veľkosti papiera a nadrozmerná stránka sa namiesto toho nevytlačí orezaná. Tento prístup definuje parameter mierky stránky. Možnosť bez zmeny mierky zachováva presnosť rozmerov, čo je dôležité pre formuláre, ktoré sa merajú pravítkom. Prispôsobenie na veľkosť papiera zmení veľkosť všetkého podľa hárku. Zmenšenie veľkých stránok ponechá bežné stránky bez zmeny a zasiahne iba vtedy, keď stránka presiahne tlačiteľnú oblasť, čo je zvyčajne správne predvolené nastavenie pre zmiešanú sadu dokumentov. Príznak automatického otáčania a centrovania spracováva stránky na šírku bez potreby ďalšej vetvy kódu.
Aplikácie, ktoré už spravujú objekt TPrinter prostredníctvom dialógov VCL, ho môžu odovzdať priamo. Metódy PrintDocumentToPrinterObject a PrintPagesToPrinterObject prijímají nakonfigurovanú inštanciu TPrinter, vďaka čomu zostáva štandardný dialóg tlače používateľským rozhraním pre konfiguráciu, zatiaľ čo knižnica spracováva vykresľovanie stránok. Miešanie oboch prístupov v jednej vetve kódu zvyčajne opäť spôsobí posun geometrie, ktorému sme sa chceli vyhnúť, preto si vyberte iba jeden z nich. Cesta virtuálnej tlačiarne je vhodná pre automatizované služby na pozadí, zatiaľ čo prístup s TPrinter je určený pre interaktívne aplikácie.
Výberový výstup funguje rovnakým spôsobom. Metóda PrintPages prijíma reťazec s rozsahom strán, takže odovzdaním názvu virtuálnej tlačiarne, rozsahu '2-5,12' a odkazu na nastavenia vytlačíte strany 2 až 5 a stranu 12 so zachovaním geometrie, pričom rovnaká syntax riadi aj varianty tlače do súboru. Tieto varianty tlače do súboru sú praktickým riešením pre automatizované prostredia bez pripojeného fyzického zariadenia, napríklad na regresné testovanie geometrie tlače na zostavovacom serveri, ktorý nemá žiadny rad tlačových úloh. Ak pri každom zostavení vykreslíte rovnaký dokument s rovnakými nastaveniami do súboru, regresia geometrie sa prejaví ako rozdiel v porovnaní súborov (diff) a nie ako hlásenie od zákazníka o tri týždne neskôr.
Náhľadové bitmapy s vlastnými metrikami tlačiarne
Náhľad vykreslený pri 96 DPI oproti predpokladanej veľkosti stránky odpovedá na nesprávnu otázku. Ukazuje, ako stránka vyzerá, nie to, čo táto konkrétna tlačiareň vytlačí na tento papier. Metóda GetPrintPreviewBitmapToString odstraňuje tento rozdiel tým, že zostaví náhľad z rovnakej vlastnej tlačiarne a s rovnakým odkazom na nastavenia ako pri výslednej úlohe, takže do bitmapy vstupuje veľkosť papiera, orientácia, politika zmeny mierky, otočenie a hardvérové odsadenie. To, čo sa vráti, presne zodpovedá výsledku na papieri.
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 obmedzuje dlhšiu hranu bitmapy. Rozlíšenie 1200 pixelov zostáva dostatočne ostré pre dialógové okno náhľadu a udržiava nízke nároky na pamäť aj pri technických výkresoch formátu E, kde by vykreslenie v plnom rozlíšení pri 600 DPI tlačiarne vyžadovalo gigabajty pamäte.
Ukladanie voľby tlačiarne používateľa
Dialógové okná tlače, ktoré si nepamätajú svoje nastavenia medzi reláciami, generujú ďalšie požiadavky na podporu. Dvojica funkcií DevMode, GetPrinterDevModeToString a SetPrinterDevModeFromString, serializuje kompletnú konfiguráciu ovládača tlačiarne do textového reťazeca, ktorý môžete uložiť do predvolieb používateľa a obnoviť pri ďalšom spustení, vrátane špecifických volieb ovládača, ktoré žiadne všeobecné API nerieši. Ukladajte tlačiareň podľa názvu z GetPrinterNames, nikdy nie podľa indexu v zozname. Poradie indexov sa mení pri každom pridaní alebo odobraní tlačiarne, takže uložený index by pri zmene zoznamu ukazoval na nesprávne zariadenie. Funkcia GetDefaultPrinterName slúži ako záloha v prípade, že zapamätané zariadenie úplne zmizlo.
Výber podávača dopĺňa možnosti ukladania nastavení. Funkcia GetPrinterBins vracia zdroje papiera, ktoré ovládač ponúka, čo je dôležité pri pracovných postupoch s hlavičkovým papierom, kde sa prvá strana berie z podávača s hlavičkovým papierom a ostatné z bežného papiera. Túto voľbu používatelia očakávajú, že si aplikácia zapamätá spolu so všetkým ostatným, a tlačová úloha, ktorá skončí na nesprávnom papieri, sa považuje za chybu, aj keď bol každý bajt PDF dokumentu správny.
Používajte rovnaké vykresľovacie jadro pre náhľad aj tlač
Posledné rozhodnutie nenápadne ovplyvňuje vernosť zobrazenia. Výber vykresľovacieho jadra (rendering engine) sa vzťahuje na obrazovku aj na tlačiareň, takže môže byť lákavé zobraziť náhľad pomocou rýchleho jadra a tlačiť pomocou presného. Odolajte tomu. Spracovanie náhľadu a samotnej tlače rôznymi jadrami opäť prináša presne ten rozdiel vo vernosti, ktorému mal presný náhľad zabrániť, pričom sa to prejaví až na papieri. Kompromisy medzi vstavaným jadrom, Cairo a PDFium sú podrobne opísané v článku viacjadrové vykresľovanie PDF v Delphi; vyberte si jedno z nich a používajte ho na oboch stranách.
Dokumenty, ktoré sú príliš veľké na bezproblémové načítanie pred tlačou, je možné otvoriť prostredníctvom cesty priameho prístupu opísanej v článku spájanie, rozdeľovanie a priamy prístup k veľkým PDF, ktorý vykresľuje stránky do kontextu zariadenia priamo z handle súboru bez budovania stromu dokumentu. Kompletná referenčná príručka pre tlačové API sa nachádza na produktovej stránke losLab PDF Library pre Delphi.