Tekninen artikkeli

PDF-asiakirjojen tulostaminen Delphissä PDFium Component -komponentilla

PDF-koordinaatit (PDF coordinates are in) ovat (are in points) pisteissä, tulostimen koordinaatit (printer coordinates are) ovat laiteyksiköissä (device units, and the), eikä (and the two) näillä (two have nothing) kahdella (have nothing to) ole mitään (to do with) tekemistä (with each other) toistensa (each other until) kanssa, ennen kuin muunnat ne (you convert them deliberately. That mismatch is the root) tarkoituksellisesti. Tuo epäsuhta on (is the root of) useimpien (most bad print) huonojen tulosteiden juuri (output in Delphi) Delphi-sovelluksissa: koodi (applications: the code) lähettää (sends the right) oikean (file but the) tiedoston, mutta sivu tulostuu rajattuna, venytettynä (page comes out cropped, stretched, or) tai tyhjänä (blank. PDFium Component). PDFium Component käsittelee hahmontamisen puolen siististi; tulostimen (handles the rendering side cleanly; the printer plumbing) putkisto (plumbing is standard) on vakio-VCL:ää (standard VCL. The). Nämä kaksi (two fit together) sopivat yhteen vaatimattoman (with a modest) koodimäärän kanssa, kunhan ymmärrät (amount of code once you understand what), mitä molemmat osapuolet (what each side) odottavat (expects)

Kuinka (How the render-then-print) hahmonna-sitten-tulosta (render-then-print pipeline works) -liukuhihna (pipeline works) toimii

PDFium Component ei puhu (does not talk) suoraan (to printers directly. The) tulostimille. Kuvio (pattern is: render a page to a) on: (is: render a) hahmonna (page to a TBitmap) sivu TBitmap-objektiin haluamallasi (at the resolution you) resoluutiolla (want, then transfer), ja siirrä (then transfer that) tuo (that bitmap to) bittikartta (bitmap to the) sitten tulostimen (printer's canvas with) kankaalle (canvas with StretchDIBits. TPdf.RenderPage) StretchDIBits-funktiolla. TPdf.RenderPage palauttaa kutsujan omistaman (returns a caller-owned) bittikartan (bitmap, so you), joten hallitset (so you control the) pikselimittoja. Välitä [rePrinting]-asetus optiosarjaan, ja PDFium vaihtaa (pixel dimensions. Pass [rePrinting] in the options set and PDFium switches its) sen (rendering path to) hahmonnuspolun, joka (one that omits) jättää (omits screen-only effects) pois (effects such as) vain (such as LCD) näytölle tarkoitetut (LCD subpixel hinting) efektit (hinting, and handles), kuten LCD-alipikselivihjeistyksen, ja (and handles the page's MediaBox) käsittelee (handles the page's MediaBox correctly) sivun MediaBox-ominaisuutta (correctly for print) oikein tulostetta (output. Leave rePrinting) varten. Jätä (Leave rePrinting out) rePrinting pois (out and what) ja se, mitä lähetät (you send to) tulostimeen, on näyttöhahmonnus (the printer is), joka (a screen render, which) näyttää (looks fine on) hyvältä näytöllä (a monitor but), mutta tuottaa (tends to produce) tyypillisesti (softer output on) pehmeämmän tulosteen korkean DPI:n (high-DPI printers because) tulostimilla, koska 96 DPI:n (because the hinting) näyttöjä (decisions made for) varten (96 DPI screens) tehdyt vihjeistyspäätökset (do not suit) eivät sovi (suit 300 or) 300 tai 600 DPI:n tulostukseen (600 DPI printing)

TPdf.Active on ainoa (is the only) portti, joka (gate to check) on tarkistettava ennen (before touching any) minkään (page property. The) sivuominaisuuden koskettamista. Komponentti nielee latausvirheet (component swallows load errors) hiljaa: Active := True -määrityksen (silently: setting Active) asettaminen vioittuneelle (:= True on a damaged or password-protected) tai salasanasuojatulle (file does not) tiedostolle ei (raise an exception;) nosta (it simply leaves) poikkeusta; se jättää (Active as False) Active-ominaisuuden yksinkertaisesti tilaan False. Tarkista se aina (Always check it) määrityksen (after the assignment) jälkeen. Inaktiivisen asiakirjan (assignment. Reading PageCount) PageCount- (or PageWidth on) tai PageWidth-ominaisuuden (an inactive document) lukeminen (returns zero, which) palauttaa nollan, mikä tuottaa hiljaisia nollatoimintoja, joita (produces silent no-ops that are very) on (hard to diagnose) erittäin vaikea (diagnose once they) diagnosoida (reach the spooler), kun (once they reach) ne (the spooler) saavuttavat (reach the spooler) taustatulostimen (spooler)

Minimaalinen tulostussilmukka (A minimal print loop)

Yksinkertaisin toimiva tapaus lataa (simplest working case) tiedoston (loads a file), avaa tulostustyön, iteroi (opens a print) sivut (job, iterates pages) ja (and closes. The) sulkee sen (closes. The only). Ainoa knoppiyksityiskohta (only tricky detail is) on (that Printer.NewPage must), että Printer.NewPage -metodia ei (not be called) saa kutsua ennen ensimmäistä (before the first) sivua, mistä (page, hence the) FirstPage-lippu johtuu (FirstPage flag. The). StretchDIBits-siirto menee (StretchDIBits transfer goes) GetDIBSizes- ja (through GetDIBSizes and) GetDIB-funktioiden (GetDIB to pull) kautta (through GetDIBSizes and GetDIB to) vetääkseen laiteriippumattomat (device-independent bits from) bitit bittikarttakahvasta, ja maalaa ne (the bitmap handle) sitten tulostimen kankaalle koko sivun (then paints them) kokoisena (onto the printer canvas at the full page size:):

procedure PrintPdfFile(const FileName: string);
var
  Pdf: TPdf;
  I: Integer;
  Bitmap: TBitmap;
  InfoHeaderSize, ImageSize: DWORD;
  InfoHeader: PBitmapInfo;
  Image: Pointer;
  FirstPage: Boolean;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := FileName;
    Pdf.Active := True;
    if not Pdf.Active then
      Exit;  // lataus epäonnistui hiljaa; peräänny
             // load failed silently; bail out

    Printer.Title := Pdf.Title;
    Printer.BeginDoc;
    try
      FirstPage := True;
      for I := 1 to Pdf.PageCount do
      begin
        if FirstPage then
          FirstPage := False
        else
          Printer.NewPage;

        Pdf.PageNumber := I;

        // Hahmonna tulostimen resoluutiolla; rePrinting säätää hahmonnuspolkua
        // Render at printer resolution; rePrinting adjusts the render path
        Bitmap := Pdf.RenderPage(
          0, 0,
          Printer.PageWidth,
          Printer.PageHeight,
          ro0,
          [rePrinting]
        );
        try
          GetDIBSizes(Bitmap.Handle, InfoHeaderSize, ImageSize);
          InfoHeader := AllocMem(InfoHeaderSize);
          try
            Image := AllocMem(ImageSize);
            try
              GetDIB(Bitmap.Handle, 0, InfoHeader^, Image^);
              StretchDIBits(
                Printer.Canvas.Handle,
                0, 0, Printer.PageWidth, Printer.PageHeight,
                0, 0, Bitmap.Width, Bitmap.Height,
                Image, InfoHeader^, DIB_RGB_COLORS, SRCCOPY
              );
            finally
              FreeMem(Image);
            end;
          finally
            FreeMem(InfoHeader);
          end;
        finally
          Bitmap.Free;
        end;
      end;
    finally
      Printer.EndDoc;
    end;
  finally
    Pdf.Active := False;
    Pdf.Free;
  end;
end;

Printer.PageWidth- ja (Passing Printer.PageWidth and) Printer.PageHeight-arvojen (Printer.PageHeight as the) välittäminen bittikartan mittoina (bitmap dimensions means) tarkoittaa (you render at), että (the printer's native) hahmonnat tulostimen (pixel size, which) natiivilla (already accounts for) pikselikoolla, mikä (the device DPI) ottaa jo huomioon laitteen (The StretchDIBits call) DPI:n (DPI. The StretchDIBits). StretchDIBits-kutsu (call then maps) kartoittaa sitten (those pixels 1:1) nuo pikselit 1:1 sivulle (onto the page. This). Tämä antaa (This gives you) sinulle parhaan (the best achievable) saavutettavissa (fidelity without any) olevan tarkkuuden ilman nimenomaista DPI-aritmetiikkaa (explicit DPI arithmetic), mutta se toimii vain (but it only) silloin (works when the), kun (PDF page and) PDF-sivu (the physical paper) ja fyysinen (happen to be) paperi sattuvat olemaan samankokoisia (the same size. When). Kun ne eroavat, tarvitset eksplisiittistä (they differ, you) skaalausta (need explicit scaling)

Skaalaus, kun sivun ja paperin (Scaling when page) koot eroavat toisistaan (and paper sizes differ)

A4-muotoinen muotokuva-PDF-sivu ei automaattisesti mahdu (A PDF page) US (at A4 portrait) Letter -tulostimeen (does not automatically), ja maisema-sivu (fit a US), joka (Letter printer, and) syötetään pystysuuntaiseen (a landscape page) tulostimeen, leikkaantuu. Vakiolähestymistapa (fed to a portrait-oriented printer will clip. The standard) on laskea tasainen (approach is to) skaalauskerroin (compute a uniform) tulostimen pikselien (scale factor from) ja (the ratio of) PDF-pisteiden (printer pixels to) suhteesta (PDF points, then) ja soveltaa (apply it to) sitä (both dimensions so) sitten molempiin (the aspect ratio) mittoihin (is preserved. Pdf.PageWidth), jotta (and Pdf.PageHeight expose) kuvasuhde (the current page) säilyy (dimensions in points, where). Pdf.PageWidth ja (where one point) Pdf.PageHeight paljastavat (is 1/72 inch) nykyiset sivumitat pisteinä, joissa yksi piste (Multiplying by a) on 1/72 tuumaa. Kertominen (target DPI and) tavoite-DPI:llä (dividing by 72) ja jakaminen 72:lla muuntaa pikseleiksi kyseisellä (converts to pixels at that) resoluutiolla (resolution. Take Min). Ota Min (Take Min of) X- ja Y-suhteista (the X and) saadaksesi suurimman skaalan (Y ratios to), joka (get the largest) mahtuu yhä (scale that still) tulostusalueen sisälle (fits within the printable area:):

// Sovita PDF-sivu tulostusalueelle, säilyttäen kuvasuhde
// Fit PDF page to printable area, preserving aspect ratio
var
  ScaleX, ScaleY, Scale: Double;
  DestWidth, DestHeight: Integer;
  Dpi: Integer;
begin
  Dpi := 300;  // kohderesoluutio / target render resolution
  Pdf.PageNumber := PageIndex;

  ScaleX := Printer.PageWidth  / (Pdf.PageWidth  * Dpi / 72);
  ScaleY := Printer.PageHeight / (Pdf.PageHeight * Dpi / 72);
  Scale  := Min(ScaleX, ScaleY);

  // Rajoita arvoon 1.0 vain kutistamista varten (ei suurennusta)
  // Clamp to 1.0 for shrink-to-fit only (no enlargement)
  if Scale > 1.0 then Scale := 1.0;

  DestWidth  := Round(Pdf.PageWidth  * Dpi / 72 * Scale);
  DestHeight := Round(Pdf.PageHeight * Dpi / 72 * Scale);

  Bitmap := Pdf.RenderPage(0, 0, DestWidth, DestHeight, ro0,
    [rePrinting, reAnnotations]);
  // ... siirrä StretchDIBits-funktiolla kuten yllä
  // ... transfer with StretchDIBits as above
end;

Hahmontaminen 300 DPI:llä sopii useimpiin (Rendering at Dpi) toimistotulostimiin (300 suits most). 600 DPI:llä yhden (office printers. At) A4-sivun (600 DPI, the) bittikartta (bitmap for a) nousee (single A4 page) karkeasti 34 (runs to roughly) megapikseliin, mikä on (34 megapixels, which) noin (is about 100) 100 (MB as a) MB 32-bittisenä bittikarttana (32-bit bitmap; the); laadun (gain in quality) paraneminen (for ordinary text) tavallisille tekstiasiakirjoille (documents is minimal) on (and the memory) minimaalista (cost per page) ja (is significant. Keep) sivukohtainen muistikustannus on (600 DPI for) merkittävä. Pidä (print shops or) 600 (vector-heavy technical drawings) DPI tulostusliikkeitä (where it genuinely) tai vektoriraskaita (matters) teknisiä piirustuksia varten, joissa sillä on todella väliä (matters)

Toisen koodilohkon reAnnotations-lippu (The reAnnotations flag) on riippumaton rePrinting-lipusta (in the second code block is independent of rePrinting. Include it when the user expects stamps, highlights, and comment boxes to appear on paper. Omit it for content-only output. Both flags can be combined freely). Sisällytä se, kun käyttäjä odottaa (user expects stamps, highlights, and comment boxes to appear on paper), että leimat (stamps), korostukset ja (highlights, and) kommenttilaatikot näkyvät paperilla (comment boxes to appear on paper). Jätä (Omit it for) se pois, jos (content-only output. Both) tuloste on vain sisältöä varten. Molempia (Both flags can) lippuja voi (be combined freely) yhdistää (combined freely) vapaasti

Sivun (Page rotation) kierto (Page rotation)

PDFium tallentaa sivun kierron (stores page rotation) PDF-tiedostoon (in the PDF) /Rotate-merkintänä (as a /Rotate), joka on saatavilla Pdf.PageRotation-ominaisuuden kautta, joka palauttaa (entry, accessible via Pdf.PageRotation, which returns a) TRotation-arvon (ro0, ro90, ro180, (TRotation value (ro0) ro270). Tulostimen (ro90, ro180, ro270). The printer) koordinaattijärjestelmä (coordinate system inverts) kääntää 90:n (90 and 270) ja 270 asteen (degree rotations relative) kierrot (to the screen. If) suhteessa (you pass the) näyttöön. Jos (raw PageRotation value) välität raa'an PageRotation-arvon (directly to RenderPage) suoraan RenderPage-metodille ilman (without any adjustment), minkäänlaista (landscape pages embedded) säätöä, muotokuva-asiakirjaan (in a portrait document) upotetut (will print upside) maisema-sivut tulostuvat ylösalaisin (down on most) useimmissa (Windows printer drivers. The) Windowsin tulostinohjaimissa. Korjaus on yksinkertainen vaihto ennen hahmonnuskutsua (fix is a simple swap before the render call): kartoita (map) ro90 arvoon ro270 ja (to ro270 and) ro270 takaisin arvoon (ro270 back to) ro90, jättäen ro0 ja ro180 (ro90, leaving ro0) ennalleen (and ro180 unchanged)

Varmista tämä (Verify this behavior) toiminta tietyllä kohdetulostimellasi (on your specific) ennen julkaisua (target printer before). Ohjaimen toiminta kiertoon (shipping. Driver behavior) liittyen (around rotation is) ei ole (not uniform across) yhtenäistä toimittajien (vendors, and some) välillä, ja (drivers apply their) jotkut ohjaimet soveltavat (own rotation correction) omaa kiertokorjaustaan GDI-tasolla. Jos näet (at the GDI) kaksinkertaisen (level. If you) kierron (see double rotation, remove the), poista (swap; if you) vaihto; jos (see no correction) et näe (at all, add) lainkaan korjausta, lisää (it. A mixed-orientation) se (document with alternating). Sekasuuntainen (portrait and landscape) asiakirja, jossa (pages is the) vuorottelevat pysty- (quickest way to) ja vaakasuuntaiset (catch either failure) sivut (mode during testing), on (is the quickest way to catch either failure mode during testing) nopein (quickest) tapa havaita (catch either failure) kumpi tahansa (mode during) vikatila testauksen (testing) aikana

Muistin hallinta pitkässä (Memory management across) tulostustyössä (a long print job)

Jokainen RenderPage-kutsu (Each call to) varaa uuden TBitmap-objektin, jonka kutsuja omistaa (RenderPage allocates a new TBitmap that the caller owns and) ja joka on vapautettava. Yllä olevassa (must free. In the) silmukassa (loop above, the) try/finally Bitmap.Free -lohko (try/finally Bitmap.Free block handles) käsittelee (this correctly for) tämän (one page at a) oikein (time. Do not) yhden (accumulate bitmaps across) sivun osalta kerrallaan. Älä (pages: a 300-DPI render of a) kerrytä (200-page document would consume gigabytes before the first page reaches the spooler) bittikarttoja sivujen yli: 300 DPI:n hahmonnus (Free each bitmap before advancing to the next page) 200-sivuisesta (200-page document would consume gigabytes before the first page reaches the spooler) asiakirjasta (Free each bitmap before advancing to the next page) kuluttaisi gigatavuja (gigabytes before the first page reaches the spooler) ennen kuin ensimmäinen sivu saavuttaa taustatulostimen. Vapauta (Free each bitmap before advancing to the next page) jokainen bittikartta (bitmap before advancing) ennen (to the next) seuraavalle (page) sivulle (page) siirtymistä (advancing to the next page)

AllocMem / (The AllocMem /) FreeMem -pari siirtolohkon (FreeMem pair inside) sisällä (the transfer block follows) noudattaa samaa sääntöä. (the same rule.) GetDIBSizes kertoo sinulle, kuinka paljon muistia (GetDIBSizes tells you how much memory the) DIB-otsikko ja (DIB header and pixel) pikselidata (data need; you) tarvitsevat; varaat (allocate, fill, paint), täytät, maalaat ja vapautat (and free all within the) kaiken yhden sivun laajuudessa. Jommankumman lohkon vuotamisen salliminen (scope of one page. Letting either block leak will) saa (cause the print) tulostustyön kuluttamaan prosessin (job to exhaust) keon (the process heap) asiakirjoissa (on documents longer), jotka ovat yli muutaman (than a few) kymmenen (dozen pages) sivun pituisia (than a few dozen pages)

Jos sinun (If you need) on suoritettava tulostustöitä (to run print) taustasäikeessä (jobs on a), pidä (background thread, keep) TPdf ja (and all VCL) kaikki VCL-tulostinkutsut samassa (printer calls on the same) säikeessä. TPdf itse ei (thread. TPdf itself) ole (is not thread-safe) säikeistysturvallinen sellaisten ilmentymien (across instances sharing) välillä, jotka jakavat (the PDFium DLL's global) PDFium DLL:n globaalin tilan; turvallisin (state; the safest model) malli on (is one TPdf) yksi TPdf per säie, ja (per thread, each) kukin lataa oman kopionsa (loading its own) tiedostosta (copy of the file)

Tässä (The rendering and) esitetyt hahmonnus- (document API shown) ja asiakirja-API:t ovat osa PDFium Component -komponenttia (Component for Delphi and) Delphi- ja C++Builder-ohjelmointikieliin (and C++Builder)