Technical Article

PDF puslapių mastelio keitimas iki 70 % naudojant losLab PDF biblioteką

PDF puslapio matmenys yra užfiksuojami jo sukūrimo metu, todėl negalite tiesiog pakeisti turinio mastelio vietoje taip, kaip keičiamas paveikslėlio dydis. Šios bibliotekos modelis, leidžiantis praktiškai sumažinti puslapį, yra paremtas fiksavimo ir perbraižymo (capture-and-redraw) principu: kiekvieno puslapio turinys perkeliamas į rodyklę (handle), sukuriamas naujas tuščias originalaus dydžio puslapis, o užfiksuotas turinys įbraižomas atgal sumažintame ribojančiame rėmelyje. Aplink likusi tuščia sritis tampa paraštėmis. Pavyzdžiui, keičiant A4 puslapio mastelį iki 70 %, po 15 % pločio lieka iš abiejų šonų ir tiek pat viršuje bei apačioje. Tai tiksliai atitinka toliau pateiktus paraščių skaičiavimus.

Kaip veikia CapturePage

Metodas CapturePage priima puslapio numerį, perkelia to puslapio turinį į atmintyje esantį fiksavimo objektą ir pašalina puslapį iš dokumento puslapių medžio. Šis pašalinimas yra suplanuotas ir būtent dėl to ciklas visada pasirenka 1-ąjį puslapį, nepriklausomai nuo iteracijos indekso: kai 1-asis puslapis yra užfiksuojamas ir ištrinamas, buvęs 2-asis puslapis tampa naujuoju 1-uoju puslapiu ir t. t. Jeigu didinsite puslapio selektoriaus indeksą kartu su ciklo skaitikliu, praleisite kas antrą puslapį ir gausite tik pusę tikėtino rezultato.

Fiksavimo rodyklė (handle), kurią grąžina CapturePage, nėra nuoroda į puslapį; tai labiau primena turinio momentinę nuotrauką. Ji lieka galioti tol, kol iškviečiate DrawCapturedPage or atlaisvinate ją rankiniu būdu. Metodas DrawCapturedPage priima šią rodyklę bei tikslo stačiakampį, kurį sudaro poslinkis iš kairės, poslinkis iš apačios, plotis ir aukštis (visi matmenys nurodomi taškais). Biblioteka tiksliai pritaiko užfiksuoto turinio mastelį prie nurodyto stačiakampio, išlaikydama proporcijas tik tuo atveju, jei stačiakampio proporcijos sutampa su originaliomis. Norint proporcingo mastelio keitimo, stačiakampis turi būti lygus originaliam dydžiui, padaugintam iš mastelio koeficiento, ir centruotas puslapyje.

Centravimo matematika

Pasirinkus 70 % mastelio koeficientą, likę 30 % kiekvieno matmens tolygiai padalijami abiem pusėms. Taigi, horizontalus poslinkis yra pageWidth * (1.0 - 0.70) / 2, kas sudaro 15 % pločio, o vertikalus poslinkis skaičiuojamas pagal tą pačią formulę, naudojant puslapio aukštį. Tikslo stačiakampis metodui DrawCapturedPage prasideda koordinačių taške (horizBorder, vertBorder) ir apima plotą nuo pageWidth - 2 * horizBorder iki pageHeight - 2 * vertBorder. Ši aritmetika nėra būdinga tik šiai bibliotekai; tai paprasta geometrija, skirta mažesniam stačiakampiui simetriškai talpinti didesniame.

Verta atkreipti dėmesį: metodas SetOrigin(1) nustato koordinačių pradžią viršutiniame kairiajame, o ne apatiniame kairiajame kampe. Rėmelių reikšmės, kurias perduodate metodui DrawCapturedPage, yra matuojamos nuo nustatytos koordinačių pradžios, todėl, jeigu pakeisite pradžios režimą tarp dokumento įkėlimo ir braižymo etapų, centravimas bus neteisingas.

C# pavyzdys

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 pavyzdys

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;

Ką iš tikrųjų valdo mastelio koeficientas

Reikšmė 0,70 čia reiškia, kad atvaizduojamas turinys užima 70 % kiekvieno puslapio matmens, o ne tai, kad failo baitų dydis sumažėja iki 70 % pradinio dydžio. Failo dydis po šios operacijos priklauso nuo originalaus turinio sudėtingumo. Puslapis su dideliais paveikslėliais nesumažės proporcingai, nes taškų (pikselių) duomenys yra perbraižomi ta pačia raiška mažesniame plote. Jei tikslas yra failo dydžio suspaudimas baitų lygiu, tinkamas sprendimas būtų naudoti LinearizeFile arba pakartotinį išsaugojimą su srauto suspaudimu, o ne geometrinį mastelio keitimą.

Nurodyti 70 % nėra griežta riba. Veikia bet kokia reikšmė nuo 0,0 iki 1,0, o reikšmės virš 1,0 padidina turinį už pradinio puslapio ribų (turinys bus apkirptas ties puslapio kraštais, nebent padidinsite paties puslapio matmenis). Skirtingų matmenų dokumentai apdorojami automatiškai, nes PageWidth ir PageHeight savybės tikrinamos kiekvienam puslapiui atskirai prieš skaičiuojant paraštes. Todėl dokumente, kur nelyginiai puslapiai yra A4 formato, o lyginiai – A3 formato, turinys bus teisingai centruojamas kiekviename puslapyje be papildomų sąlygų kode.

Kur gali kilti problemų

Praktikoje dažniausiai pasitaiko du klaidų scenarijai. Pirmasis – tikslo failas yra paliktas atidarytas PDF peržiūros programoje iš ankstesnio paleidimo. Tokiu atveju metodas SaveToFile nepavyks arba įrašys nulinį baitų skaičių (priklausomai nuo platformos), o naujas rezultatas nebus išsaugotas. Failo trynimo sąlyga funkcijos pradžioje padeda išspręsti šią problemą kūrimo etape, tačiau gamybinėje aplinkoje saugiau rašyti į laikiną failą ir sėkmės atveju jį pervadinti.

Antrasis scenarijus – puslapių skaičiaus neatitikimas. Kadangi CapturePage pašalina apdorojamus puslapius iš dokumento, prieš ciklą gautas puslapių skaičius per PageCount() yra teisinga iteracijų riba. Ciklo viduje kviečiant PageCount(), kiekvieno žingsnio metu būtų grąžinamas mažėjantis skaičius, todėl ciklas baigtųsi per anksti ir dalis puslapių liktų neapdoroti. Pavyzdžiuose ciklo kintamasis naudojamas tik kaip likusių iteracijų skaitiklis. Jis niekada nenaudojamas puslapio pasirinkimui, nes pasirinktas puslapis visada turi būti 1 dėl anksčiau paaiškintos priežasties.

Čia demonstruojami puslapių manipuliavimo iškvietimai, įskaitant CapturePage, DrawCapturedPage ir SetPageDimensions, yra losLab PDF Library bibliotekos dalis, skirta Delphi, C#, VB.NET ir C++.