Technical Article

Skaliranje PDF stranica na 70% uz losLab PDF biblioteku

Dimenzije PDF stranice su fiksne u trenutku kreiranja stranice, pa sadrzaj ne mozete jednostavno prerazmерiti na mestu kao sto biste promenili velicinu slike. Model biblioteke koji cini smanjivanje prakticnim je hvatanje i ponovno crtanje: izvucete sadrzaj svake stranice iz dokumenta u rukovaoс, kreirate novu praznu stranicu na originalnoj velicini medija, zatim nacrtate uhvaceni sadrzaj nazad u smanjenom ogranicavajucem pravougaoniku. Okolni prazni prostor postaje margina. Pri skali od 70% na A4 stranici, na primer, 15% sirine pada na svaku stranu i isti udeo gore i dole, sto je tacno ono sto aritmetika granica dole proizvodi.

Kako CapturePage radi

CapturePage uzima broj stranice, unapreduje sadrzaj te stranice u objekat hvatanja u memoriji i uklanja stranicu iz stabla stranica dokumenta. To uklanjanje je namerno i razlog zbog kojeg petlja uvek bira stranicu 1 bez obzira na indeks iteracije: kada se stranica 1 uhvati i izbrise, ono sto je bila stranica 2 postaje nova stranica 1, i tako dalje. Ako povecavate selektor stranica zajedno sa brojacem petlje, prескасете svaku drugu stranicu i zavrsavate s upola ocekivanog izlaza.

Rukovaoс hvatanja koji vraca CapturePage nije referenca na stranicu; vise lici na snimak sadrzaja. Ostaje vazecan dok ne pozovete DrawCapturedPage ili ga eksplicitno oslobodite. DrawCapturedPage uzima taj rukovaoс plus odredbeni pravougaonik zadat kao levi pomak, donji pomak, sirina i visina, sve u tackama. Biblioteka skalira uhvaceni sadrzaj da tacno stane u taj pravougaonik, cuvajuci odnos stranica samo ako vас pravougaonik slucajno odgovara originalnim proporcijama. Za uniformno skaliranje zelite da pravougaonik bude originalna velicina pomnozena faktorom skale, centrirana na stranici.

Matematika centriranja

Sa faktorom skale od 70% preostalih 30% svake dimenzije se ravnomerno deli izmedju dve strane. Dakle, horizontalni uvlak je pageWidth * (1.0 - 0.70) / 2, sto je 15% sirine, a vertikalni uvlak sledi istu formulu koristeci visinu stranice. Odredbeni pravougaonik za DrawCapturedPage zatim pocinje od (horizBorder, vertBorder) i prostire se pageWidth - 2 * horizBorder puta pageHeight - 2 * vertBorder. Ta aritmetika nije specificna za biblioteku; to je samo geometrija umetanja manjeg pravougaonika simetricno unutar veceg.

Jedna stvar vredna napomene: SetOrigin(1) postavlja ishodiste koordinata u gornji levi ugao umesto donjeg levog. Vrednosti granica koje prosledjujete DrawCapturedPage mere se od kojeg god ishodista ste postavili, pa ako menjate rezime ishodista izmedju ucitavanja i crtanja, centriranje ce biti pogresno.

C# primer

Sledeci kod obradjuje svaku stranicu Pages.pdf kroz ciklus hvatanja i ponovnog crtanja i zapisuje rezultat u newpages.pdf. PDFL je ActiveX/COM objekat omotaca dodat projektu iz 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++)
    {
        // Always select page 1: CapturePage removes the page, so page 2
        // becomes page 1 on the next 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 primer

Delphi verzija koristi TPDFlib direktno umesto putem COM sloja, ali je redosled poziva identicaн. Jedna prakticna razlika je cuvar izlazne datoteke: FileExists plus DeleteFile umesto File.Delete, jer ce SaveToFile biti neuspesan ako je odrediste zakljucano prethodnim pokretanjem jos otvorenim u pregledacu.

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;

Sta faktor skale zaista kontrolise

Vrednost 0,70 ovde znaci da renderovani sadrzaj zauzima 70% svake dimenzije stranice, ne da je datoteka 70% svoje originalne velicine u bajtovima. Velicina datoteke posle ove operacije zavisi od slozеnosti originalnog sadrzaja; stranica s velikim slikama nece se srazmerno smanjiti jer se podaci piksela ponovo crtaju pri istoj rezoluciji u manjem prostoru. Ako je kompresija na nivou bajtova cilj, pravi pristup je LinearizeFile ili ponovno snimanje s kompresijom toka, a ne geometrijsko skaliranje.

Vrednost 70% takodje nije tvrda granica. Bilo koja vrednost izmedju 0,0 i 1,0 funkcionise, a vrednosti iznad 1,0 povecavaju sadrzaj izvan originalne granice stranice, sto se iseca na ivici medijskog okvira osim ako takodje ne povecate dimenzije stranice. Dokumenti mesovite velicine se obradjuju prirodno jer se PageWidth i PageHeight upituju po stranici pre izracunavanja granica, pa ce dokument gde su neparne stranice A4 a parne A3 producirati ispravno centrirani izlaz na svakoj velicini stranice bez posebnog obradjivanja.

Gde stvari mogu posrnutiiti

U praksi se javljaju dva oblika kvara. Prvi je izlazna datoteka ostavljena otvorenom u PDF pregledacu iz prethodnog pokretanja: SaveToFile ce biti neuspesan ili ce zapisati nula bajtova zavisno od platforme, i novi izlaz nikada ne slete. Cuvar brisanja datoteka na vrhu funkcije to obraduje za razvoj, ali u proizvodnom cevovodu pisanje na privremenu putanju i preimenoavanje pri uspehu je sigurnije.

Drugi je neslaganje broja stranica. Buduci da CapturePage uklanja stranice iz dokumenta dok ih obraduje, brojac koji citате iz PageCount() pre petlje je ispravna granica za iteraciju. Pozivanje PageCount()

unutar petlje bi vracalo opadajuci broj pri svakom prolazu i izlazilo rano, ostavljajuci poslednje stranice neobradjenim. Promenljiva petlje u primerima sluzi samo kao brojac preostalih iteracija; nikada se ne koristi za odabir stranice, jer je stranica za odabir uvek 1 iz razloga objasnjenog ranije.

Pozivi za manipulaciju stranicama prikazani ovde, ukljucujuci CapturePage, DrawCapturedPage i SetPageDimensions, deo su losLab PDF biblioteke za Delphi, C#, VB.NET i C++.