Tekninen artikkeli

Esikatselu ja device-context-tuloste PDFlibPasilla

PDF-sivun renderöinti Windowsin device contextiin tulostuksen esikatselua varten asettaa kolme koordinaatistoa samalle koodiriville, ja ne harvoin ovat samaa mieltä. PDF-sivua mitataan pisteissä, origo alavasemmalla. Näyttö-DC mitataan pikseleissä, origo ylävasemmalla ja zoom-tekijä, jonka valitset. Tulostin-DC, se jota esikatselun pitäisi ennustaa, mitaa pikseleitä laitteen resoluutiossa mutta asettaa origonsa tulostettavan alueen kulmaan, ei arkin kulmaan. Saat minkä tahansa yhden väärin ja esikatselu näyttää hyvältä kun tulostettu sivu tulee ulos siirtyneenä, skaalattuna tai leikattuna reunasta. Tavallinen oire on reunallinen lomake, joka esikatselussa on keskitetty ja tulostuu ylä- ja vasensäännöt leikattuina, koska lasertulostin ei voi laittaa mustetta ulompiin muutamaan millimetriin eikä kukaan kertonut esikatselulle. losLab PDF Library (PDF Library for Delphi) kattaa koko polun device-context-renderöintikutsuilla, virtuaalitulostimen konfigurointikerroksella, ja esikatselubitmapeilla, jotka generoidaan tulostimen omista metrisistä, mikä on se osa joka tekee esikatselusta rehellisen tuon marginaalin suhteen

Paperigeometria ei ole tulostettava geometriaa

Kaksi suorakulmiota kuvaa mitä tahansa tulostuskohdetta, ja niiden välinen siirtymä on siellä, missä useimmat esikatseluviat asuvat. Paperi-suorakulmio on fyysinen arkki. Tulostettava-suorakulmio on pienempi alue, johon tulostuskone oikeasti yltää, sisennetty laitemarginaalilla, joka eroaa tulostinmallittain ja joskus lokeroittain. Kirjaston tulostuskerros mittaa molemmat. Taustalla oleva TPLPrinter-luokka paljastaa PageWidth:n ja PageHeight:n tulostettavalle alueelle, FullPageWidth:n ja FullPageHeight:n koko arkille, ja PrintOffsetX:n kanssa PrintOffsetY:n niiden origojen väliselle kuilulle, kaikki laitteen pikseleissä resoluutiossa, jonka GetDPI raportoi. Rehellinen esikatselu skaalaa nuo samat numerot näyttöresoluutioon ennemmin kuin maalaa sivua mihin tahansa suorakulmioon, joka kontrolli sattuu omistamaan. Ohita tuo vaihe ja esikatselu olettaa hiljaisesti nollamarginaalin, mikä on se yksi arvo, jota yksikään todellinen tulostin ei käytä

PDF Library for Delphi -kaavio koko paperiarkasta ja pienemmästä tulostuskelpoisesta suorakulmiosta, jossa PrintOffsetX ja PrintOffsetY merkitsevät laitteiston marginaalia niiden origojen välillä
Paperisuorakulmio on fyysinen arkki, kun taas tulostettava suorakulmio on se, mihin tulostusmoottori yltää, ja origojen välinen ero on paikka, jossa useimmat esikatselun viat asuvat

Näyttöesikatselu RenderPageToDC:n kautta

Näytönesikatselukontrollia varten RenderPageToDC(DPI, Page, DC) piirtää ladatun dokumentin sivun suoraan mille tahansa GDI-device contextille, olkoon se TPaintBox-kangas, off-screen-bitmap tai metafile-DC. DPI-argumentti asettaa zoomin. 96 likimäärää 100 % näkymää klassisella näytöllä, ja sen kaksinkertaistaminen kaksinkertaistaa renderöidyn koon

procedure TPreviewForm.PreviewBoxPaint(Sender: TObject);
begin
  // nämä kolme ovat pysyvää kirjastotilaa, eivät kutsukohtaisia parametreja:
  FPdf.SetRenderDCOffset(FOffsetX, FOffsetY);
  FPdf.SetRenderDCErasePage(1);
  FPdf.SetRenderCropType(0);
  FPdf.RenderPageToDC(FPreviewDpi, FCurrentPage, PreviewBox.Canvas.Handle);
end;

Ansa on, että DC-renderöintipolkua ohjaavat tarttuvat kirjastotilat, eivät per-kutsu-parametrit. SetRenderDCOffset, SetRenderDCErasePage ja SetRenderCropType kukin säilyvät kunnes jokin muuttaa niitä, joten thumbnail-silmukka, joka ajetaan kun käyttäjä on säätänyt zoomatun näkymän, perii minkä tahansa offsetin tai rajauksen, jonka edellinen koodipolku jätti jälkeensä. Oire on esikatselu, joka harhailee vain tietyissä navigointisekvensseissä, mikä on suunnilleen yhtä kurja toistettava kuin vika voi olla. Kaiken olennaisen tilan asettaminen piirrokäsittelijän yläosaan, kuten yllä, ei maksa mitään ja poistaa koko luokan. Toinen kertojin piiloutuu lähelle. Todellinen tulostusresoluutio on renderöintiskaala kertaa DPI-argumentti, ja vaikka SetRenderScale on oletuksena 1.0, se myös säilyy kerran muutettuna, joten vientiominaisuus, joka sitä nosti, hiljaa skaalaa jokaisen myöhemmän esikatselun uudelleen kunnes jokin asettaa sen takaisin

Vierityskatselimet ja osittaiset uudelleenmaalaukset ovat omat varianttinsa. RenderPageToDCClip ottaa leikkausmäärittelyn device contextin ohella, joten yhden nauhan invalidointi ikkunassa maalaa vain sen nauhan uudelleen kokonaisen sivun uudelleenrasteroinnin sijaan. Suurella zoomilla large-format-sivuilla se on ero katselimen, joka seuraa vierityspalkkia, ja sellaisen, joka sivelyttää sen perässä

Tulostustö, joka vastaa esikatselua

Tulostuspuoli toimii virtuaalitulostimen kautta. NewCustomPrinter kloonaa järjestelmätulostimen kirjaston-yksityiseksi konfiguraatioksi, ja SetupPrinter säätää tuota kloonia koskematta koneen laajaan DevModeen: paperi menee asetuksena 1 (DMPAPER_*-vakio) ja suunta asetuksena 11. Hyöty on eristäminen. Palvelu voi tulostaa A4-etikettejä kun isännän oletustulostin pysyy Letter-asetuksella, eikä mitään tarvitse palauttaa sen jälkeen

PDF Library for Delphi: virta järjestelmän oletustulostimesta NewCustomPrinterin ja SetupPrinterin kautta eristettyyn tulostustyöhön, joka ei koskaan koske koneenlaajuista DevModea
SetupPrinter uudelleenkohdentaa kirjaston yksityisen kloonaan, joten palvelu voi tulostaa A4:lle, kun taas isännän oletustulostimen Letter-DevMode säilyy koskemattomana
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');  // sovita paperille, kierrä automaattisesti + keskitä
    Pdf.PrintDocument(Virt, 1, Pdf.PageCount, Opt);
  finally
    Pdf.Free;
  end;
end;

PrintOptions ansaitsee huolellisen lukemisen. Se palauttaa options-kahvan, joka sinun on välitettävä PrintDocument:lle tai PrintPages:lle; se ei ole ympäristötila. Optionsien rakentaminen ja sitten kahvan unohtaminen epäonnistuu hiljaa. Työ tulostuu oletuksilla, eikä kukaan huomaa ennen kuin fit-to-paper-käytäntöä odotettiin ja ylikokoinen sivu tuli leikattuna sen sijaan. Sivun skaalaus -argumentti on siellä, missä tuo käytäntö elää. Ei skaalausta säilyttää ulottuvuustarkkuuden, mikä merittää lomakkeille, joita mitataan viivainta vasten. Fit-to-paper skaalaa kaiken arkille. Shrink-large-pages jättää normaalit sivut rauhaan ja astuu sisään vain kun sivu ylittää tulostettavan alueen, mikä on yleensä oikea oletus sekalaiselle dokumenttijoukolle. Auto-rotate-and-center-lippu käsittelee landscape-sivut ilman toista koodipolkua

Sovellukset, jotka ylläpitävät jo TPrinter:ää VCL-dialogivuon kautta, voivat luovuttaa sen suoraan. PrintDocumentToPrinterObject ja PrintPagesToPrinterObject hyväksyvät konfiguroidun TPrinter-instanssin, mikä pitää standardin tulostusdialogin käyttäjän kohtaamana konfigurointipintana kun kirjasto hoitaa sivun renderöinnin. Kahden lähestymistavan sekoittaminen yhteen koodipolkuu pyrkii palauttamaan geometriaharhailun, jonka muu työ tässä oli tarkoitettu tappamaan, joten valitse yksi. Virtuaalitulostinreitti sopii valvomattomille palveluille; TPrinter-reitti interaktiivisille sovelluksille

Valikoiva tuloste toimii samalla tavalla. PrintPages ottaa välijonon, joten virtuaalitulostinnimen, '2-5,12':n ja options-kahvan välittäminen tulostaa sivut 2–5 ja 12 geometriasopimuksen ehjänä, ja sama syntaksi ajaa print-to-file-variantit. Nuo tiedostovariantit ovat käytännön vastaus valvomattomaan ympäristöön ilman fyysistä laitetta: tulostusgeometrian regressiotestaus build-palvelimella, jolla ei ole driver-jonoa lainkaan. Renderöi sama dokumentti samojen optionsien kautta tiedostoartefaktiksi jokaisessa buildissa, ja geometriaregressio muuttuu diffiksi ennemmin kuin asiakasraportiksi kolme viikkoa myöhemmin

Esikatselubitmapit tulostimen omilla metrisillä

Esikatselu, joka renderöidään 96 DPI:ssä oletettua sivukokoa vasten, vastaa väärään kysymykseen. Se näyttää, miltä sivu näyttää, ei sitä, mitä tämä tulostin laittaa tälle paperille. GetPrintPreviewBitmapToString sulkee tuon kuilun rakentamalla esikatselun samasta mukautetusta tulostimesta ja samasta options-kahvasta kuin lopullinen työ, joten paperikoko, suunta, skaalauskäytäntö, kierto ja laite-offset kaikki syöttävät bitmapiin. Se mikä palaa, on se, mitä arkki näyttää

PDF Library for Delphi: vastakohta oletetun sivukoken näytönesikatselun, joka ilmoittaa marginaalit väärin, ja tulostimen mukaisen bittikartan välillä, joka rakennetaan työn omasta tulostimesta ja asetuskahvasta
GetPrintPreviewBitmapToString piirtää esikatselun samalla omalla tulostimella ja asetusten handlella kuin lopullinenkin työ, joten bittikartta näyttää marginaalit ja kierron, jotka arkki todella saa
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;

MaxDimension-argumentti rajoittaa bitmapin pitkän reunan. 1200 pikseliä pysyy terävänä esikatseludialogissa ja pitää muistin kohtuullisena edes E-kokoisilla insinööripiirustuksilla, missä täyden resoluution renderöinti tulostimen 600 DPI:ssä juoksisi gigatavuihin

Käyttäjän tulostinvalintojen muistaminen

Tulostusdialogit, jotka unohtavat asetuksensa istuntojen välillä, generoivat omat tukilippunsa. DevMode-pari, GetPrinterDevModeToString ja SetPrinterDevModeFromString, sarjallistaa tulostimen täyden driver-konfiguraation epäläpinäkyväksi merkkijonoksi, jonka voit piilottaa käyttäjäasetuksiin ja palauttaa seuraavalla istunnolla, mukaan lukien driver-kohtaiset vaihtoehdot joita yksikään yleinen API vaivautuu mallintamaan. Pysäytä tulostin nimellä GetPrinterNames:stä, ei koskaan luettelo-indeksillä. Indeksijärjestys muuttuu joka kerta kun tulostin lisätään tai poistetaan, joten tallennettu indeksi osoittaa hiljaa väärään laitteeseen kun luettelo seuraavan kerran siirtyy. GetDefaultPrinterName kattaa varatoimen kun muistettu laite on hävinnyt kokonaan

Lokeron valinta täydentää pysyvyystarinan. GetPrinterBins raportoi paperilähteet, jotka driver paljastaa, mikä merkitsee kirjeotsake-vuoksiin, joissa sivu yksi vetää otsakelokeroista ja loput tavallisesta paperista. Se on käytäntö, jonka käyttäjät odottavat sovelluksen muistavan yhdessä kaiken muun kanssa, ja tulostustyö, joka laskeutuu väärälle paperille, luetaan bugina silloinkin kun jokainen PDF:n tavu oli oikein

Pidä yksi moottori esikatselun ja tulostuksen yli

Yksi viimeinen päätös hallitsee hiljaa tarkkuutta. Renderöintimoottorin valinta koskee molempia, näyttö- ja tulostinkohteita, joten kiusaus on esikatsella nopealla moottorilla ja tulostaa tarkalla. Vastusta sitä. Esikatselun ja työn ajaminen eri moottoreiden kautta palauttaa juuri sen tarkkuusharhailun, jonka tulostin-tosi-esikatselu oli rakennettu poistamaan, ja tekee sen tavalla, joka näkyy vain paperilla. Sisäänrakennetun, Cairon ja PDFiumin moottoreiden väliset kompromissit punnitaan monimoottorisessa PDF-renderöinnissä Delphissä; valitse yksi ja käytä sitä molemmilla puolilla

Dokumentit, jotka ovat liian suuria ladata mukavasti ennen tulostusta, voidaan avata suoran pääsyn polun kautta, joka on kuvattu suurten PDF-tiedostojen yhdistämisessä, jakamisessa ja suorassa pääsyssä, joka renderöi sivut device contextiin tiedostokahvasta ilman dokumenttipuun rakentamista. Täydellinen tulostus-API-viite on losLab PDF Library for Delphi -tuotesivulla