Tekninen artikkeli

Vierekkäinen PDF-vertailu Delphissä PDFium Component -komponentilla

Kaksi asiakirjaa auki yhtä aikaa, sama sivunumero, kumpikin omassa vieritettävässä paneelissaan: se on vertailukatseluohjelman ydin. PDFium Component tarjoaa tämän suoraviivaisella objektimallilla, jossa TPdf omistaa tiedoston ja TPdfView omistaa näytön. Yksi asiakirja, yksi TPdf, yksi TPdfView. Haluat kolme paneelia, sinulla on kolme paria. Vaikeat osat eivät ole API-kutsut; ne ovat asettelu-aritmetiikkaa, kun ikkunan koko muuttuu, ja sivusynkronoinnin logiikka, kun päätät, minkä näkymän tulisi seurata mitäkin

Lomakkeen asettelu

VCL-lomake sisältää kolme TScrollBox-säilöä vierekkäin, kussakin sisällä TPdfView kohdistettuna arvoon alClient, jotta se täyttää laatikon. Kaksi TSplitter-komponenttia sijaitsee laatikoiden välissä, jotta käyttäjä voi säätää sarakeleveyksiä suoritusaikana. Paneelien yläpuolella oleva työkalurivi sisältää avauspainikkeet, zoomaussäätimet sekä kaksi-/kolminäkymä-kytkimen

Kolminäkymätila on totuusarvo, jota lomake seuraa sisäisesti. Kun se kytketään, lasket leveydet uudelleen ja näytät tai piilotat kolmannen sarakkeen. Yksinkertaisin tapa on tyhjentää kaikki Align-ominaisuudet, piilottaa jakajat ja asettaa sitten absoluuttiset sijainnit:

Lomakeasettelukaavio Delphi-rinnakkaisesta PDF-vertailukatselimesta, joka rakentuu PDFium Componentilla, ja näyttää työkalupalkin, kolme vierityslaatikkoa TPdfView-paneelien kanssa ja jakajat kaksinäkymä- ja kolminäkymätiloissa
Jokainen paneeli on scroll box, jonka sisällä on TPdfView, ja kaksinäkymän ja kolminäkymän välillä vaihtaminen on vain eri joukko leveyssijoituksia
procedure TFormMain.UpdateLayout;
var
  TotalWidth: Integer;
begin
  TotalWidth := ClientWidth;

  if ThreeViewMode then
  begin
    ScrollBox3.Visible := True;
    ScrollBox1.Left   := 0;
    ScrollBox1.Width  := TotalWidth div 3;
    ScrollBox2.Left   := ScrollBox1.Width;
    ScrollBox2.Width  := TotalWidth div 3;
    ScrollBox3.Left   := ScrollBox2.Left + ScrollBox2.Width;
    ScrollBox3.Width  := TotalWidth - ScrollBox3.Left;
    // Sovella samaa (ClientHeight - työkalurivin korkeus) kaikkiin kolmeen Height-arvoon
  end
  else
  begin
    ScrollBox3.Visible := False;
    ScrollBox1.Left   := 0;
    ScrollBox1.Width  := TotalWidth div 2;
    ScrollBox2.Left   := ScrollBox1.Width;
    ScrollBox2.Width  := TotalWidth - ScrollBox2.Left;
  end;
end;

Määrityksen Align := alNone tekeminen kaikkiin kolmeen laatikkoon ennen kokonaislukuaritmetiikkaa estää VCL:n rajoitemoottoria taistelemasta määrityksiäsi vastaan. Palauta jakajien näkyvyys sijoittelun jälkeen, jos haluat vetämällä tapahtuvaa koon muuttamista kaksinäkymätilassa

Kunkin vierityslaatikon korkeus on asiakasalue miinus työkalurivipaneelin korkeus. Koska työkalurivi on telakoitu ylös arvolla alTop, ClientHeight - PanelButtons.Height antaa sinulle käyttökelpoisen pystysuoran tilan. Määritä tämä kaikille kolmelle laatikolle saman UpdateLayout-kutsun sisällä, jotta ei koskaan ole ruutua, jossa yksi laatikko on pidempi kuin muut ja aiheuttaa asettelun välkynnän

Asiakirjan avaaminen

Jokainen paneelipari tarvitsee oman avausmenettelynsä. Kuvio on lyhyt: deaktivoi komponentti, aseta tiedostonimi, aktivoi, ja tarkista sitten Active; jos se pysyi tilassa False, kysy salasanaa ja yritä uudelleen. Huomaa, että TPdfView.Active ohjaa hahmontamista, mutta TPdf.Active on se, mikä oikeasti avaa tiedoston; ne ovat itsenäisiä. Määrityksen PdfView.Active := True tekeminen silloin, kun siihen linkitetty TPdf ei ole vielä aktiivinen, on vaaratonta, mutta ei näytä mitään

Vuokaavio PDF-asiakirjan avaamisesta PDFium Componentilla Delphissä, joka näyttää hiljaisen Active-tarkistuksen, yhden salasanayrityksen ja virhevalintaikkunan vaurioituneille tai salasanasuojatuille tiedostoille
Epäonnistunut lataus jättää Active-arvoksi False ilman poikkeusta, joten virta tarkistaa sen, yrittää uudelleen kerran salasanalla ja raportoi lopuksi ongelman tyhjän paneelin näyttämisen sijaan
procedure TFormMain.OpenPdfFile(PdfComponent: TPdf;
  PdfViewComponent: TPdfView);
var
  Password: string;
begin
  if not OpenDialog.Execute then
    Exit;

  PdfComponent.Active   := False;
  PdfComponent.FileName := OpenDialog.FileName;
  PdfComponent.Password := '';
  PdfComponent.Active   := True;

  // Latauksen epäonnistumiset ovat hiljaisia: Active pysyy False-tilassa poikkeuksen sijaan.
  if not PdfComponent.Active then
  begin
    // Todennäköisesti salasanasuojattu tiedosto; anna käyttäjälle yksi uusi yritys.
    if InputQuery('Password', 'Enter document password:', Password) then
    begin
      PdfComponent.Password := Password;
      PdfComponent.Active   := True;
    end;
  end;

  if not PdfComponent.Active then
  begin
    ShowMessage('Could not open ' + OpenDialog.FileName +
      ' (damaged file or wrong password)');
    Exit;
  end;

  PdfViewComponent.PageNumber := 1;
  SetActivePdfView(PdfViewComponent);
end;

Tarkista aina PdfComponent.Active määrityksen jälkeen; vioittunut tiedosto tai väärä salasana saa latauksen epäonnistumaan hiljaisesti nostamatta poikkeusta oletuspolulla. Määrityksen PdfViewComponent.PageNumber := 1 tekeminen eksplisiittisesti onnistuneen avaamisen jälkeen välttää vanhentuneen sivunumeron jäämisen edellisestä asiakirjasta

Lopussa oleva viesti-valintaikkuna on tarkoituksellinen: haluat, että vioittuneet tai tukemattomat tiedostot tulevat pintaan välittömästi sen sijaan, että ne nieltäisiin hiljaisena tyhjänä paneelina. Käyttäjällä, joka ei näe mitään, ei ole aavistustakaan siitä, latautuiko tiedosto ja onko se yksinkertaisesti tyhjä, vai hylkäsikö komponentti sen. Epäonnistumisesta raportoiminen pitää virheen näkyvänä

Aktiivisen paneelin seuranta

Kun käyttäjä napsauttaa paneelin sisällä, tuosta paneelista tulee aktiivinen. Lomake seuraa yksityistä FActivePdfView: TPdfView -kenttää. Visuaalinen palaute on reunaviivan värin muutos sisältävässä TScrollBox-laatikossa: aseta se arvoon clHighlight aktiiviselle ja clWindow muille. Kytke tämä jokaiseen TPdfView.OnClick-tapahtumaan ja avausmenettelyyn, jotta kohdistus seuraa juuri avaamaasi asiakirjaa

Jotkin toiminnot koskevat kaikkia näkyviä paneeleja pelkän aktiivisen sijaan. Totuusarvo FAllViewsMode lomakkeessa ohjaa tuota haaraa. Kun se on tosi, zoomausmuutokset ja sivunavigointi leviävät jokaiseen paneeliin, jossa on aktiivinen asiakirja:

procedure TFormMain.ApplyZoomToAll(NewZoom: Double);
begin
  if PdfView1.Active then PdfView1.Zoom := NewZoom;
  if PdfView2.Active then PdfView2.Zoom := NewZoom;
  if ThreeViewMode and PdfView3.Active then PdfView3.Zoom := NewZoom;
end;

Synkronoitu sivunavigointi

Synkronoitu navigointi on valinnainen, mutta hyödyllinen asiakirjojen versiotyönkuluille, joissa molemmat tiedostot kattavat saman sivualueen. Logiikka kuuluu tapahtumankäsittelijään, joka laukeaa sen jälkeen, kun käyttäjä navigoi yhtä näkymää. Kun lähdenäkymä muuttaa PageNumber-arvoaan, käsittelijä välittää tuon numeron muihin näkymiin, yhden suojan varassa: kohdenäkymässä on oltava vähintään yhtä monta sivua, muuten se ohitetaan

PageNumber-ominaisuudet luokissa TPdfView ja TPdf ovat itsenäisiä. TPdf.PageNumber seuraa, mitä sivua asiakirjakomponentti pitää nykyisenä; TPdfView.PageNumber seuraa, mitä näytöllä näytetään. Navigointitarkoituksiin haluat näkymän ominaisuuden, et asiakirjan ominaisuutta

Valintaruutu, jonka nimi on esimerkiksi "Synkronoi sivut", antaa käyttäjälle hallinnan. Kun sen valinta poistetaan, kukin paneeli navigoi itsenäisesti ja käsittelijä poistuu välittömästi. Tuo itsenäisyys on tärkeää käyttötapauksille, joissa kahdessa asiakirjassa on eri sivumäärät, tai joissa käyttäjä haluaa löytää vastaavan kohdan käännöksestä, joka alkaa eri sivulta. Synkronoinnin pakottaminen aina tekisi työkalusta vaikeamman käyttää kuin yksinkertainen kaksi-ikkunainen työpöytäjärjestely

Yksi asia, jota kannattaa varoa: PdfView.PageNumber-ominaisuuden asettaminen ohjelmallisesti synkronointikäsittelijän sisällä laukaisee itsessään muutostapahtuman kyseisessä näkymässä. Suojaudu ääretöntä rekursiota vastaan totuusarvoisella lipulla, jonka asetat ennen määritystä ja tyhjennät heti sen jälkeen. Lippu on lomakekohtainen, ei näkymäkohtainen, koska kaikki kolme näkymää jakavat saman käsittelijän

Kaavio synkronoidusta sivunavigoinnista Delphi-PDF-vertailukatselimessa PDFium Componentilla, synkronointivalintaruudulla, sivumääräsuojalla kohdenäkymää kohden ja rekursion suojalipulla
Sivunumero matkustaa lähtönäkymästä jokaiseen muuhun näkymään vain, kun synkronointi on käytössä ja jokainen kohdenäkymä oikeasti sisältää kyseisen sivun

Zoomaus paneelia kohti

Jokainen TPdfView kantaa oman Zoom-ominaisuutensa, Double-arvon prosentteina, jossa Zoom := 100 tarkoittaa todellista kokoa (100 %). Sen asettaminen kumoaa minkä tahansa aktiivisen FitMode-tilan. Fit-to-width-painikkeelle aktiivisessa paneelissa lue fit-zoomaus ominaisuudesta PdfView.PageWidthZoom[PdfView.PageNumber] ja määritä se. Fit-to-page-toiminnolle käytä PageZoom[PageNumber]-ominaisuutta. Molemmat ovat taulukko-ominaisuuksia, jotka on indeksoitu 1-pohjaisella sivunumerolla, joten suojaudu nollasivunumeroa vastaan ennen niiden käyttämistä

Kun viet nykyisen sivun kuvaksi, lue kierto näkymästä, mutta kutsu RenderPage-metodia TPdf-komponentilla, ei näkymällä. TPdf.RenderPage-metodin bittikarttamuoto ottaa eksplisiittiset pikselimitat sekä TRotation-arvon ja TRenderOptions-joukon. Funktion variantti palauttaa kutsujan omistaman TBitmap-objektin, jonka vapautat itse tallentamisen jälkeen:

procedure TFormMain.SaveActiveViewAsImage;
var
  Pdf: TPdf;
  Bmp: TBitmap;
  Jpeg: TJpegImage;
begin
  if not Assigned(FActivePdfView) or not FActivePdfView.Active then
    Exit;

  Pdf := FActivePdfView.Pdf;
  Pdf.PageNumber := FActivePdfView.PageNumber;

  Bmp := Pdf.RenderPage(
    0, 0,
    Round(Pdf.PageWidth * 2),
    Round(Pdf.PageHeight * 2),
    FActivePdfView.Rotation, [], clWhite);
  try
    if SavePictureDialog.Execute then
    begin
      Jpeg := TJpegImage.Create;
      try
        Jpeg.Assign(Bmp);
        Jpeg.CompressionQuality := 90;
        Jpeg.SaveToFile(SavePictureDialog.FileName);
      finally
        Jpeg.Free;
      end;
    end;
  finally
    Bmp.Free;
  end;
end;

2x-kerroin leveydessä ja korkeudessa antaa terävämmän tulosteen asiakirjoille, joissa on hienoa tekstiä. try/finally-lohko bittikartan vapauttamisen ympärillä ei ole valinnainen; TSaveDialog-peruutus osuu silti finally-lohkoon, ja haluat, että bittikartta vapautetaan riippumatta siitä, mitä käyttäjä teki

DLL-vaatimukset

PDFium Component kietoo natiivin pdfium-kirjaston. 32-bittinen isäntäprosessi tarvitsee pdfium32.dll-kirjaston; 64-bittinen isäntä tarvitsee pdfium64.dll-kirjaston. Variantit, joissa on V8 JavaScript -moottori, lisäävät v8-liitteen ja painavat karkeasti 23-27 Mt verrattuna 5-6 Mt:n standardiversioihin. Vertailukatseluohjelmalle, joka poistaa lomakkeiden täytön käytöstä (Pdf.FormFill := False), standardi ei-V8-koontiversio riittää ja pitää jakelun pienempänä

Sijoita DLL samaan hakemistoon kuin suoritettava tiedosto, tai mihin tahansa hakemistoon järjestelmän PATH-muuttujassa. Komponentti lataa sen tarpeen mukaan, kun ensimmäinen TPdf aktivoidaan, joten puuttuva DLL nousee pintaan vasta siinä vaiheessa sovelluksen käynnistyksen sijaan. Jos toimitat asennusohjelman, luotettavin tapa on kopioida DLL sovelluskansioon asennuksen aikana sen sijaan, että luottaisit järjestelmähakemistoon, jonka järjestelmänvalvoja saattaa myöhemmin siivota

V8-koontiversiot ovat ensisijaisesti hyödyllisiä, kun sinun on oltava vuorovaikutuksessa PDF:n JavaScript-toimintojen kanssa, esimerkiksi laskentakenttien liipaisemiseksi tai lähetyskäsittelijöiden käynnistämiseksi. Passiivisella vertailukatseluohjelmalla ei ole mitään syytä suorittaa JavaScriptiä; määrityksen Pdf.FormFill := False tekeminen ennen Active := True -kutsua ohittaa lomakkeen täyttöympäristön kokonaan, mikä tarkoittaa myös, ettei JS-moottoria alusteta, vaikka käytettäisiin standardiversiota. Se on oikea oletus vain luku -katseluohjelmalle riippumatta siitä, minkä DLL-variantin toimitat

Lisätietoja PDFium Component -komponentista ja sen täydestä API:sta löydät Delphi PDFium Component -tuotesivulta