Techninis straipsnis

Dokumentų PDF palyginimas greta „Delphi“ programoje naudojant „PDFium Component“

Vienu metu atidaryti du dokumentai, tas pats puslapio numeris, kiekvienas savo slenkamame skydelyje: tai yra palyginimo peržiūros programos pagrindas. „PDFium Component“ tai užtikrina naudodamas paprastą objektų modelį, kuriame TPdf priklauso failas, o TPdfView – ekranas. Vienas dokumentas, vienas TPdf, vienas TPdfView. Norite trijų skydelių – turite tris poras. Sunkiausia dalis nėra API iškvietimai; tai yra maketo aritmetika, kai keičiasi lango dydis, ir puslapių sinchronizavimo logika, kai nusprendžiate, kuris rodinys kurį turėtų sekti

Formos maketas

VCL formoje greta vienas kito yra trys TScrollBox konteineriai, kurių kiekviename yra TPdfView ir kurie sulygiuoti pagal alClient, kad užpildytų laukelį. Tarp laukelių yra du TSplitter komponentai, kad naudotojas galėtų reguliuoti stulpelių plotį vykdymo metu. Virš skydelių esančioje įrankių juostoje yra atidarymo mygtukai, mastelio keitimo valdikliai ir dviejų / trijų rodinių perjungiklis

Trijų rodinių režimas yra loginė reikšmė (boolean), kurią forma seka viduje. Kai ji perjungiama, perskaičiuojate pločius ir parodote arba paslepiate trečiąjį stulpelį. Paprasčiausias būdas yra išvalyti visas Align savybes, paslėpti skirstytuvus ir nustatyti absoliučias pozicijas:

Formos išdėstymo diagrama Delphi gretutinės PDF palyginimo peržiūros programos, sukurtos su PDFium Component, rodanti įrankių juostą, tris slinkimo langelius su TPdfView skydais ir skirtukus dvi-rodinio ir trijų-rodinių režimuose
Kiekvienas skydas yra slinkimo dėžutė su TPdfView viduje, o perjungimas tarp dviejų peržiūrų ir trijų peržiūrų yra tik kitas pločių priskyrimų rinkinys
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;
    // Pritaikykite tą pačią (ClientHeight - įrankių juostos aukštis) reikšmę visoms trims Height reikšmėms
  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;

Nustatę Align := alNone visiems trims laukeliams prieš atliekant sveikųjų skaičių aritmetiką, išvengsite VCL apribojimų variklio prieštaravimų jūsų priskyrimams. Po pozicionavimo atkurkite skirstytuvų matomumą, jei norite, kad dviejų rodinių režimu būtų galima keisti dydį vilkimo būdu

Kiekvieno slinkties laukelio aukštis yra kliento sritis minus įrankių juostos skydelio aukštis. Kadangi įrankių juosta yra priglausta viršuje naudojant alTop, ClientHeight - PanelButtons.Height suteikia jums naudingą vertikalią erdvę. Priskirkite tai visiems trims laukeliams tame pačiame UpdateLayout iškvietime, kad niekada nebūtų kadro, kai vienas laukelis yra aukštesnis už kitus ir sukelia maketo mirgėjimą

Dokumento atidarymas

Kiekvienai skydelių porai reikia atskiros atidarymo procedūros. Šablonas yra trumpas: išjunkite komponentą, nustatykite failo pavadinimą, įjunkite, tada patikrinkite Active; jei jis liko False, paprašykite slaptažodžio ir bandykite dar kartą. Atkreipkite dėmesį, kad TPdfView.Active valdo atvaizdavimą, tačiau TPdf.Active iš tikrųjų atidaro failą; jie yra nepriklausomi. Nustačius PdfView.Active := True, kai su juo susietas TPdf dar nėra aktyvus, yra nekenksminga, bet nieko nerodo

PDF dokumento atvėrimo su PDFium Component Delphi blokinė schema, rodanti tylų Active patikrinimą, vieną slaptažodžio pakartojimą ir klaidos dialogą sugadintiems arba slaptažodžiu apsaugotiems failams
Nesėkmingasis įkėlimas palieka Active False nekeliant išimties, todėl srautas jį tikrina, bando kartą su slaptažodžiu ir galiausiai praneša problemą vietoje rodyti tuščiąjį skydą
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;

  // Įkėlimo klaidos yra tylios: Active lieka False, o ne iškeliama išimtis.
  if not PdfComponent.Active then
  begin
    // Greičiausiai slaptažodžiu apsaugotas failas; suteikite naudotojui vieną bandymą pakartoti.
    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;

Visada patikrinkite PdfComponent.Active po priskyrimo; dėl sugadinto failo ar neteisingo slaptažodžio įkėlimas nepavyksta tyliai, nesukeliant išimties numatytajame kelyje. Aiškus PdfViewComponent.PageNumber := 1 nustatymas po sėkmingo atidarymo leidžia išvengti pasenusio puslapio numerio iš ankstesnio dokumento

Pranešimų dialogo langas pabaigoje yra tyčinis: norite, kad sugadinti ar nepalaikomi failai iškiltų iš karto, o ne būtų praryti kaip tylus tuščias skydelis. Naudotojas, kuris nieko nemato, neįsivaizduoja, ar failas buvo įkeltas ir yra tiesiog tuščias, ar komponentas jį atmetė. Pranešus apie gedimą, klaida lieka matoma

Aktyvaus skydelio sekimas

Kai naudotojas spusteli skydelio viduje, tas skydelis tampa aktyvus. Forma stebi privačių FActivePdfView: TPdfView lauką. Vizualinis grįžtamasis ryšys yra rėmelio spalvos pasikeitimas turinčiame TScrollBox: nustatykite jį į clHighlight aktyviajam ir clWindow kitiems. Susiekite tai su kiekvienu TPdfView.OnClick ir su atidarymo procedūra, kad fokusavimas sektų dokumentą, kurį ką tik atidarėte

Kai kurios operacijos taikomos visiems matomiems skydeliams, o ne tik aktyviajam. Loginė reikšmė FAllViewsMode formoje valdo šią šaką. Kai ji yra teisinga (true), mastelio keitimai ir puslapių naršymas išsiskirsto į kiekvieną skydelį, turintį aktyvų dokumentą:

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;

Sinchronizuotas puslapių naršymas

Sinchronizuotas naršymas yra neprivalomas, bet naudingas dokumentų peržiūros darbo eigose, kai abu failai apima tą patį puslapių diapazoną. Logika priklauso įvykių apdorojimo programai, kuri suveikia naudotojui naršant viename rodinyje. Kai šaltinio rodinys pakeičia savo PageNumber, apdorojimo programa perduoda tą numerį kitiems rodiniams su viena apsauga: tikslinis rodinys turi turėti bent tiek puslapių, kitaip jis praleidžiamas

PageNumber TPdfView ir TPdf komponentuose yra nepriklausomi. TPdf.PageNumber seka, kurį puslapį dokumento komponentas laiko dabartiniu; TPdfView.PageNumber seka, kas rodoma ekrane. Naršymo tikslais jums reikia rodinio savybės, o ne dokumento savybės

Žymimasis langelis, pažymėtas kažkuo panašiu į „Sinchronizuoti puslapius“, suteikia naudotojui valdymą. Kai jis atžymėtas, kiekvienas skydelis naršomas atskirai ir apdorojimo programa nedelsiant išeina. Šis nepriklausomumas yra svarbus naudojimo atvejais, kai du dokumentai turi skirtingą puslapių skaičių, arba kai naudotojas nori rasti atitinkamą ištrauką vertime, kuris prasideda kitame puslapyje. Nuolatinis sinchronizavimo priverstinis taikymas padarytų įrankį sunkiau naudojamą nei paprastas dviejų langų darbalaukio išdėstymas

Vienas dalykas, į kurį reikia atkreipti dėmesį: programinis PdfView.PageNumber nustatymas sinchronizavimo apdorojimo programos viduje savaime sukels to rodinio pakeitimo įvykį. Apsisaugokite nuo begalinės rekursijos naudodami loginę vėliavėlę, kurią nustatote prieš priskyrimą ir išvalote iškart po jo. Vėliavėlė yra skirta formai, o ne rodiniui, nes visi trys rodiniai dalijasi ta pačia apdorojimo programa

Sinchronizuotos puslapių navigacijos Delphi PDF palyginimo peržiūros programoje su PDFium Component diagrama, su sinchronizavimo žymimuoju langeliu, puslapių skaičiaus apsauga kiekvienam tiksliniam rodiniui ir rekursijos apsaugos vėliavėle
Puslapio numeris keliauja iš šaltinio peržiūros į kiekvieną kitą peržiūrą tik tada, kai įgalintas sinchronizavimas ir kiekviena paskirties peržiūra iš tiesų turi tą puslapį

Kiekvieno skydelio mastelio keitimas

Kiekvienas TPdfView turi savo Zoom savybę – Double procentais, kur Zoom := 100 reiškia tikrąjį dydį (100 %). Ją nustačius, nepaisoma jokio aktyvaus FitMode. Mygtukui „pritaikyti prie pločio“ aktyviame skydelyje nuskaitykite pritaikymo mastelį iš PdfView.PageWidthZoom[PdfView.PageNumber] ir jį priskirkite. Norėdami pritaikyti prie puslapio, naudokite PageZoom[PageNumber]. Abi yra masyvo savybės, indeksuojamos pagal nuo 1 prasidedantį puslapio numerį, todėl prieš pasiekdami jas, apsisaugokite nuo nulinio puslapio numerio

Kai eksportuojate dabartinį puslapį į vaizdą, nuskaitykite pasukimą iš rodinio, bet iškvieskite RenderPage TPdf komponente, o ne rodinyje. Taškinės grafikos (bitmap) TPdf.RenderPage forma priima aiškius pikselių matmenis, plius TRotation reikšmę ir TRenderOptions rinkinį. Funkcijos variantas grąžina iškvietėjui priklausantį TBitmap, kurį atlaisvinate patys po išsaugojimo:

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;

2 kartų daugiklis pločiui ir aukščiui suteikia ryškesnį išvesties rezultatą dokumentams su smulkiu tekstu. try/finally blokas aplink taškinės grafikos atlaisvinimą nėra neprivalomas; TSaveDialog atšaukimas vis tiek pasiekia finally bloką, o jūs norite, kad taškinė grafika būtų atlaisvinta neatsižvelgiant į tai, ką padarė naudotojas

DLL reikalavimai

„PDFium Component“ apgaubia natūralią (native) „pdfium“ biblioteką. 32 bitų prieglobos (host) procesui reikia pdfium32.dll; 64 bitų prieglobai reikia pdfium64.dll. Variantai su V8 „JavaScript“ varikliu prideda v8 priesagą ir sveria maždaug 23-27 MB, palyginti su 5-6 MB standartinėmis versijomis. Palyginimo peržiūros programai, kuri išjungia formų pildymą (Pdf.FormFill := False), pakanka standartinės ne V8 versijos ir tai išlaiko mažesnį platinimo paketą

Įdėkite DLL į tą patį katalogą kaip ir vykdomasis failas arba į bet kurį katalogą, esantį sistemos PATH. Komponentas įkelia jį pagal pareikalavimą, kai aktyvuojamas pirmasis TPdf, todėl trūkstamas DLL iškyla tuo momentu, o ne paleidžiant programą. Jei tiekiate diegimo programą, patikimiausias būdas yra diegimo metu nukopijuoti DLL į programos aplanką, o ne pasikliauti sistemos katalogu, kurį administratorius vėliau gali išvalyti

V8 versijos pirmiausia naudingos, kai reikia sąveikauti su PDF „JavaScript“ veiksmais, pavyzdžiui, paleisti skaičiavimo laukus arba pateikti tvarkykles. Pasyviai palyginimo peržiūros programai nėra jokios priežasties vykdyti „JavaScript“; nustačius Pdf.FormFill := False prieš Active := True, visiškai praleidžiama formų pildymo aplinka, o tai taip pat reiškia, kad joks JS variklis nėra inicijuojamas net ir naudojant standartinę versiją. Tai yra teisinga numatytoji reikšmė tik skaitymo peržiūros programai, neatsižvelgiant į tai, kurį DLL variantą tiekiate

Norėdami gauti daugiau informacijos apie „PDFium Component“ ir visą jo API, apsilankykite Delphi PDFium Component produkto puslapyje