Tekninen artikkeli

PDF-sivujen skaalaaminen 70 %:iin losLab PDF Librarylla

PDF-sivujen mitat (dimensions) kiinnitetään (are fixed) sillä hetkellä (at the time), kun (a page is created) sivu luodaan (created), joten et voi (cannot) yksinkertaisesti (simply rescale content in place the way you might resize an) skaalata (rescale) sisältöä (content) paikoillaan sillä tavalla kuin ehkä (might resize an image) muuttaisit kuvan kokoa. Kirjastomalli (library model), joka tekee pienentämisestä (shrinking) käytännöllistä, on (is capture-and-redraw) nappaa ja piirrä (capture-and-redraw): nosta kunkin (each page's) sivun (page's content) sisältö (content) asiakirjasta (document) kahvaan (handle), luo (create a new blank) uusi tyhjä sivu (page) alkuperäisessä kossa (at the original media size), ja piirrä sitten napattu sisältö (captured content) takaisin (back in at a reduced bounding box) pienennettyyn (reduced) rajauslaatikkoon (bounding box). Ympäröivästä valkoisesta (surrounding whitespace becomes the) tilasta tulee (becomes the) marginaali (margin). 70 %:n (At 70% scale on an A4 page, for instance, 15% of the) mittakaavassa esimerkiksi (for instance) A4-sivulla 15 % leveydestä osuu (falls on each side) kummallekin puolelle ja sama (same fraction top and) murtoluku (fraction top and bottom) ylös ja alas (bottom), mikä on tismalleen se tulos (exactly what the), jonka (which is exactly what the) alla oleva (below) reunojen laskutoimitus (border arithmetic below produces) tuottaa (produces)

Kuinka CapturePage toimii (works)

CapturePage ottaa (takes a) sivunumeron (page number), siirtää (promotes that page's content into an) kyseisen sivun sisällön (content) muistissa olevaan (in-memory capture object) sieppausobjektiin ja poistaa (removes the) sivun asiakirjan sivupuusta (document's page tree). Poisto on (That removal is intentional) tarkoituksellinen ja (and is the) on syy siihen (reason the), miksi (reason the loop always) silmukka valitsee aina (always selects) sivun 1 riippumatta (regardless of the) iteraatioindeksistä: kun sivu (once page 1) 1 on (is captured and deleted) napattu (captured and deleted) ja poistettu, entisestä (what was page 2 becomes the) sivusta 2 tulee (becomes the) uusi sivu (new page 1) 1, ja (and so on) niin (so on) edelleen. Jos (If you increment the) kasvatat sivuvalitsijaa (page selector along with the) silmukan laskurin mukana (along with the loop counter), ohitat joka toisen sivun (skip every other page) ja (and end up with) tuloksena on vain (half the expected) puolet (half) odotetusta (expected output) tulosteesta

CapturePage-kutsun (returned by) palauttama sieppauskahva (capture handle returned by CapturePage is not a page reference) ei ole sivuviittaus (page reference); se on enemmänkin kuin (more like a) tilannekuva (content snapshot) sisällöstä (snapshot). Se (It stays valid until you) pysyy voimassa (valid), kunnes (until you call) kutsut DrawCapturedPage-komentoa tai vapautat (release it) sen (it) nimenomaisesti (explicitly). DrawCapturedPage ottaa (takes that) tuon kahvan ja lisäksi kohdesuorakulmion (destination rectangle given as left), joka (given as) annetaan vasempana (left offset) siirtymänä (offset), alasiirtymänä (bottom offset), leveytenä (width) ja korkeutena, kaikki (all in) pisteinä (points). Kirjasto (The library scales the captured content to fit that) skaalaa napatun sisällön (content) sovittaakseen sen tuohon suorakulmioon tismalleen, säilyttäen (preserving aspect) kuvasuhteen (ratio only) vain, jos (if your rectangle happens to match the) suorakulmiosi sattuu (happens to) vastaamaan alkuperäisiä (original) mittasuhteita (proportions). Tasaisen skaalaamisen (uniform scaling you want the) saavuttamiseksi haluat (you want the rectangle to be the), että suorakulmio (rectangle) on (is the) alkuperäinen (original size) koko (size multiplied by the) kerrottuna skaalauskertoimella (scale factor), keskitettynä sivulle (centered on the page)

Keskittämisen (centering) matematiikka

70 %:n skaalauskertoimella (scale factor the remaining 30% of) jokaisen (each dimension gets split equally between the) ulottuvuuden (dimension) jäljelle jäävä (remaining 30%) 30 % jaetaan tasan kahden (two sides) puolen (sides) kesken. Vaakasuora (horizontal inset is) sisennys (inset is) on siis pageWidth * (1.0 - 0.70) / 2, mikä (which is 15% of the) on 15 % leveydestä (width), ja (and the) pystysuora (vertical inset follows the) sisennys noudattaa samaa kaavaa käyttäen (using the) sivun korkeutta (height). DrawCapturedPage-kutsun (The destination rectangle for DrawCapturedPage then) kohdesuorakulmio alkaa siis pisteestä (starts at) (horizBorder, vertBorder) ja ulottuu (spans) arvoon pageWidth - 2 * horizBorder leveyssuunnassa (by) ja pageHeight - 2 * vertBorder pituussuunnassa (by). Tämä aritmetiikka ei (That arithmetic is not library-specific; it is just the) ole kirjastokohtaista (library-specific); se on vain (is just the geometry of) sen (geometry of fitting a smaller rectangle symmetrically inside a larger one) geometriaa (geometry of fitting a), kuinka pienempi suorakulmio (rectangle) sovitetaan (fitting a) symmetrisesti (symmetrically inside a larger) suuremman (larger) sisälle (inside a larger one)

Yksi (One thing worth noting) huomionarvoinen (worth noting) seikka: SetOrigin(1) sijoittaa (puts the) koordinaatiston (coordinate origin at the) origon vasempaan (top-left rather than the) yläkulmaan (top-left) vasemman (rather than the bottom-left) alakulman (bottom-left) sijaan (rather than). DrawCapturedPage-kutsulle välittämäsi (pass to) reuna-arvot (border values you pass to DrawCapturedPage are) mitataan (measured from whichever origin you set) siitä (whichever origin you set) origosta (origin you set), jonka asetat, joten jos (so if you switch origin modes between loading and) vaihdat origotiloja (origin modes) lataamisen ja (loading and drawing) piirtämisen (drawing) välillä (between), keskitys (centering will be off) menee pieleen (will be off)

C#-esimerkki (example)

Seuraava (The following code) koodi (code processes every page of) käsittelee jokaisen Pages.pdf-tiedoston sivun (page of Pages.pdf through the) nappaa ja piirrä -jakson läpi ja kirjoittaa (writes the result to) tuloksen (result) tiedostoon (to) newpages.pdf. PDFL on ActiveX/COM-kääreobjekti (wrapper object added to the project from), joka (wrapper object added to the) lisättiin (added to the) projektiin (project from) tiedostosta (from) PDFlibDLL64.dll

private void ScalePages_Click(object sender, EventArgs e)
{
    File.Delete("newpages.pdf");

    double pageWidth, pageHeight, horizBorder, vertBorder;
    double scaleFactor = 0.70;
    int capturedPageId, ret;

    PDFL.LoadFromFile("Pages.pdf", "");
    PDFL.SetOrigin(1);

    int numPages = PDFL.PageCount();

    for (int i = 1; i <= numPages; i++)
    {
        // Valitse (Always select page 1: CapturePage removes the page, so page 2) aina sivu (page) 1: CapturePage poistaa (removes the page) sivun (page), joten sivu (page 2) 2
        // muuttuu (becomes page 1 on the next iteration) sivuksi (page 1) 1 seuraavassa (next) iteraatiossa (iteration).
        PDFL.SelectPage(1);

        pageWidth  = PDFL.PageWidth();
        pageHeight = PDFL.PageHeight();

        horizBorder = pageWidth  * (1.0 - scaleFactor) / 2;
        vertBorder  = pageHeight * (1.0 - scaleFactor) / 2;

        capturedPageId = PDFL.CapturePage(1);

        PDFL.NewPage();
        PDFL.SetPageDimensions(pageWidth, pageHeight);

        ret = PDFL.DrawCapturedPage(
            capturedPageId,
            horizBorder, vertBorder,
            pageWidth  - 2 * horizBorder,
            pageHeight - 2 * vertBorder);
    }

    PDFL.SaveToFile("newpages.pdf");
}

Delphi-esimerkki (example)

Delphi-versio käyttää TPDFlib:iä suoraan COM-kerroksen (layer) sijaan, mutta (but the call sequence is) kutsusarja (call sequence) on (is identical) identtinen (identical). Yksi (One practical difference is the) käytännön ero (practical difference) on tulostetiedoston (output file guard) suojaus (guard): FileExists ja (plus) DeleteFile komennon (instead of) File.Delete sijaan, koska (because) SaveToFile epäonnistuu (will fail), jos katseluohjelmassa yhä avoinna (open in a) oleva edellinen ajokerta (previous run still open in a viewer) lukitsee (locked by a previous run) kohteen (destination)

procedure TForm1.ScalePagesClick(Sender: TObject);
var
  PDFLib: TPDFlib;
  pageWidth, pageHeight, horizBorder, vertBorder: Double;
  scaleFactor: Double;
  capturedPageId, ret, numPages, i: Integer;
begin
  if FileExists('newpages.pdf') then
    DeleteFile('newpages.pdf');

  scaleFactor := 0.70;

  PDFLib := TPDFlib.Create;
  try
    PDFLib.LoadFromFile('Pages.pdf', '');
    PDFLib.SetOrigin(1);

    numPages := PDFLib.PageCount();

    for i := 1 to numPages do
    begin
      PDFLib.SelectPage(1);

      pageWidth  := PDFLib.PageWidth();
      pageHeight := PDFLib.PageHeight();

      horizBorder := pageWidth  * (1.0 - scaleFactor) / 2;
      vertBorder  := pageHeight * (1.0 - scaleFactor) / 2;

      capturedPageId := PDFLib.CapturePage(1);

      PDFLib.NewPage();
      PDFLib.SetPageDimensions(pageWidth, pageHeight);

      ret := PDFLib.DrawCapturedPage(
        capturedPageId,
        horizBorder, vertBorder,
        pageWidth  - 2 * horizBorder,
        pageHeight - 2 * vertBorder);
    end;

    PDFLib.SaveToFile('newpages.pdf');
  finally
    PDFLib.Free;
  end;
end;

Mitä (What the) skaalauskerroin (scale factor actually controls) itse asiassa ohjaa (controls)

Arvo 0.70 tarkoittaa (means the) tässä (here), että (that the) renderöity sisältö (rendered content occupies) vie (occupies) 70 % (70% of) kustakin (each) sivun ulottuvuudesta (dimension), ei sitä, että tiedosto on 70 % sen (its) alkuperäisestä tavukoosta (byte size). Tiedoston koko (File size) tämän (after this operation) toiminnon jälkeen riippuu (depends on the) alkuperäisen (original) sisällön monimutkaisuudesta (complexity); sivu, jolla on (page with large images will not shrink proportionally because the) suuria kuvia (large images), ei (will not shrink proportionally) kutistu (shrink) suhteellisesti, koska (because the) pikselidata piirretään (pixel data is redrawn at the same resolution into a smaller area) uudelleen samalla resoluutiolla pienemmälle alueelle (area). Jos tavoitteena (If byte-level compression is the goal) on (is the goal, the) tavutason (byte-level compression) pakkaus (compression), oikea (right approach is) lähestymistapa (approach) on (is) LinearizeFile tai (or) uudelleentallennus (re-saving with) virtapakkauksella (stream compression), ei (not geometric scaling) geometrinen (geometric) skaalaus (scaling)

70 % luku ei (figure is also not a hard limit) myöskään ole ehdoton (hard limit) raja. Mikä (Any value between) tahansa arvo 0.0:n ja (and) 1.0:n väliltä toimii (works), ja yli (above) 1.0:n (1.0 enlarge content beyond the) arvot (values) suurentavat sisällön alkuperäisen sivun rajan yli (beyond the original page boundary), mikä (which) rajautuu (clips) mediaboxin (media box edge unless you also) reunaan (edge), ellet (unless you also) kasvata (increase the page dimensions) myös (also) sivun (page) mittoja (dimensions). Erikokoisia (Mixed-size documents are handled naturally because) sivuja sisältävät (documents are handled naturally) asiakirjat (documents) käsitellään (handled) luonnollisesti, koska PageWidth ja (and) PageHeight kysytään sivukohtaisesti (per page) ennen reunan laskemista (border calculation), joten asiakirja, jossa (document where odd pages are A4 and even pages are A3 will produce correctly centered) parittomat sivut ovat A4-kokoisia ja (and even pages are A3 will produce) parilliset (even) sivut A3-kokoisia, tuottaa oikein keskitetyn (correctly centered) tulosteen (output) jokaiselle (on each) sivukoolle (page size) ilman minkäänlaista (without any special casing) erikoiskäsittelyä (special casing)

Missä (Where things can go wrong) asiat voivat mennä (go wrong) pieleen (wrong)

Kaksi epäonnistumistilaa (Two failure modes come up in) nousee esiin (come up in) käytännössä (practice). Ensimmäinen on (first is an output file left open in a PDF viewer from a previous run) tulostetiedosto (output file), joka on (left open in a PDF viewer from a previous run) jätetty (left) auki (open) PDF-katseluohjelmassa edelliseltä ajokerralta (run): SaveToFile epäonnistuu (will fail) tai (or) kirjoittaa nolla (write zero bytes depending on the platform) tavua (bytes) alustasta (platform) riippuen (depending on the), eikä (and the) uutta (new output) tulostetta koskaan tallenneta (lands). Funktion (function handles that for) alussa (top of the function) oleva (file-delete guard at the) tiedoston poistosuojaus (file-delete guard) käsittelee (handles that for) tämän (that for development) kehitysvaiheessa (development), mutta tuotantoputkessa (in a production pipeline) kirjoittaminen tilapäiseen (temporary) polkuun (path and renaming on success is safer) ja nimeäminen (renaming) uudelleen (renaming) onnistuessa on turvallisempaa

Toinen (second) on sivumäärän (page count mismatch) epäsuhta (mismatch). Koska CapturePage poistaa (removes pages from the document as it processes them) sivuja asiakirjasta sitä (as it) mukaa (it) kun (as it) niitä käsitellään, PageCount()-kutsusta (count you read from PageCount() before the loop is the correct bound to iterate against) ennen (before the loop) silmukkaa (loop) luettu (read from) määrä on (is the) oikea raja (bound), jota vasten (against) iteroidaan (iterate against). PageCount()-komennon (Calling PageCount() inside the) kutsuminen silmukan (loop) sisällä (inside the) palauttaisi (would return a) pienenevän numeron (decreasing number on each pass and exit early) jokaisella kierroksella (pass) ja (and exit early) lopettaisi ajoissa (early), jättäen viimeiset sivut (last pages unprocessed) käsittelemättä (unprocessed). Esimerkkien (examples serves only as a remaining-iterations counter) silmukkamuuttuja (loop variable in the) toimii (serves) vain jäljellä (remaining-iterations counter) olevien iteraatioiden laskurina; sitä (it is never used to select a page) ei koskaan (never) käytetä (used) sivun (page) valitsemiseen, koska valittava sivu (page to select) on (is always) aina (always) 1 edellä (earlier) selitetystä (explained) syystä (reason)

Tässä näytetyt (shown here, including) sivun (page) manipulointikutsut (manipulation calls), mukaan (including) lukien CapturePage, DrawCapturedPage ja SetPageDimensions, ovat osa losLab PDF -kirjastoa (Library) Delphille, C#:lle, VB.NET:ille ja C++:lle