Teknik Makale

PDFium Component ile Delphi'de Yan Yana PDF Karşılaştırma

Aynı anda açık iki belge, aynı sayfa numarası, her biri kendi kaydırılabilir panelinde: bir karşılaştırma görüntüleyicisinin özü budur. PDFium Component bunu, TPdf bileşeninin dosyaya, TPdfView bileşeninin ise görüntüye sahip olduğu yalın bir nesne modeliyle sunar. Bir belge, bir TPdf, bir TPdfView. Üç panel isterseniz üç çiftiniz olur. Zor kısımlar API çağrıları değildir; pencere yeniden boyutlandığındaki yerleşim aritmetiği ve hangi görünümün hangisini izleyeceğine karar verdiğinizdeki sayfa eşitleme mantığıdır

Form Yerleşimi

VCL formu yan yana üç TScrollBox kapsayıcı barındırır; her birinin içinde bir TPdfView vardır ve kutuyu doldurması için alClient ile hizalanmıştır. Kullanıcının sütun genişliklerini çalışma zamanında ayarlayabilmesi için kutuların arasında iki TSplitter bileşeni durur. Panellerin üstündeki bir araç çubuğu açma düğmelerini, yakınlaştırma denetimlerini ve iki görünüm / üç görünüm anahtarını taşır

Üç görünüm kipi, formun içeride izlediği bir mantıksal değerdir. Değiştiğinde genişlikleri yeniden hesaplar ve üçüncü sütunu gösterir ya da gizlersiniz. En basit yaklaşım, tüm Align özelliklerini temizlemek, ayırıcıları gizlemek ve sonra mutlak konumları ayarlamaktır:

PDFium Component ile kurulmuş bir Delphi yan yana PDF karşılaştırma görüntüleyicisinin form yerleşim şeması: bir araç çubuğu, TPdfView panelleri içeren üç kaydırma kutusu ve iki görünüm ile üç görünüm kiplerindeki ayırıcılar
Her panel, içinde bir TPdfView bulunan bir kaydırma kutusudur ve iki görünüm ile üç görünüm arasında geçiş yalnızca farklı bir genişlik atamaları kümesidir
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;
    // Aynı (ClientHeight - araç çubuğu yüksekliği) değerini üç Height alanına da uygula
  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;

Tamsayı aritmetiğinden önce üç kutuda da Align := alNone ayarlamak, VCL kısıt motorunun atamalarınızla boğuşmasını önler. İki görünüm kipinde sürükleyerek boyutlandırma istiyorsanız konumlandırmadan sonra ayırıcı görünürlüğünü geri verin

Her kaydırma kutusunun yüksekliği, istemci alanından araç çubuğu paneli yüksekliği çıkarılmış değerdir. Araç çubuğu alTop ile üste yerleştiği için ClientHeight - PanelButtons.Height size kullanılabilir dikey alanı verir. Bunu üç kutuya da aynı UpdateLayout çağrısı içinde atayın ki bir kutunun diğerlerinden uzun olduğu ve yerleşim titremesine yol açtığı bir kare hiç oluşmasın

Belge Açma

Her panel çiftinin kendi açma yordamına ihtiyacı vardır. Desen kısadır: bileşeni devre dışı bırakın, dosya adını ayarlayın, etkinleştirin, sonra Active değerini denetleyin; False kaldıysa parola isteyip yeniden deneyin. Şunu unutmayın: işlemeyi denetleyen TPdfView.Active değeridir ama dosyayı gerçekten açan TPdf.Active değeridir; ikisi bağımsızdır. Bağlı TPdf bileşeni henüz etkin değilken PdfView.Active := True ayarlamak zararsızdır ama hiçbir şey göstermez

Delphi'de PDFium Component ile bir PDF belgesi açmanın akış şeması: sessiz Active denetimi, tek bir parola yeniden denemesi ve hasarlı ya da parola korumalı dosyalar için bir hata iletişim kutusu
Başarısız bir yükleme istisna fırlatmadan Active değerini False bırakır; bu yüzden akış onu denetler, parolayla bir kez yeniden dener ve boş bir panel göstermek yerine sonunda sorunu bildirir
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;

  // Yükleme hataları sessizdir: istisna yerine Active False kalır.
  if not PdfComponent.Active then
  begin
    // Büyük olasılıkla parola korumalı bir dosya; kullanıcıya bir hak ver.
    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;

Atamadan sonra PdfComponent.Active değerini her zaman denetleyin; hasarlı bir dosya ya da yanlış bir parola, varsayılan yolda istisna fırlatmadan yüklemenin sessizce başarısız olmasına yol açar. Başarılı bir açmanın ardından PdfViewComponent.PageNumber := 1 ayarını açıkça yapmak, önceki belgeden kalan bayat bir sayfa numarasını önler

Sondaki mesaj iletişim kutusu bilinçlidir: bozuk ya da desteklenmeyen dosyaların sessiz bir boş panel olarak yutulması yerine hemen yüzeye çıkmasını istersiniz. Hiçbir şey görmeyen bir kullanıcı, dosyanın yüklenip gerçekten boş mu olduğunu yoksa bileşenin onu reddedip reddetmediğini bilemez. Hatayı bildirmek sorunu görünür tutar

Etkin Panel Takibi

Kullanıcı bir panelin içine tıkladığında o panel etkin olur. Form, özel bir FActivePdfView: TPdfView alanını izler. Görsel geri bildirim, kapsayan TScrollBox üzerindeki bir kenarlık rengi değişimidir: etkin olan için clHighlight, diğerleri için clWindow ayarlayın. Bunu her TPdfView.OnClick olayına ve açma yordamına bağlayın ki odak, az önce açtığınız belgeyi izlesin

Bazı işlemler yalnızca etkin panele değil görünür panellerin hepsine uygulanır. Formdaki mantıksal bir FAllViewsMode alanı bu dalı sürer. Değeri true olduğunda yakınlaştırma değişiklikleri ve sayfa gezinmesi, etkin belgesi olan her panele dağılır:

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;

Eşzamanlı Sayfa Gezinmesi

Eşzamanlı gezinme isteğe bağlıdır ama her iki dosyanın aynı sayfa aralığını kapsadığı belge revizyon akışlarında yararlıdır. Mantık, kullanıcı bir görünümde gezindikten sonra tetiklenen bir olay işleyicisine aittir. Bir kaynak görünüm PageNumber değerini değiştirdiğinde işleyici bu numarayı diğer görünümlere yayar; tek bir koruma vardır: hedef görünümün en az o kadar sayfası olmalıdır, yoksa atlanır

TPdfView üzerindeki PageNumber ile TPdf üzerindeki bağımsızdır. TPdf.PageNumber, belge bileşeninin geçerli saydığı sayfayı izler; TPdfView.PageNumber ise ekranda görüntülenen sayfayı izler. Gezinme amacıyla belge özelliğini değil görünüm özelliğini istersiniz

"Sayfaları eşitle" gibi bir etiket taşıyan bir onay kutusu denetimi kullanıcıya verir. İşaretli değilken her panel bağımsız gezinir ve işleyici hemen çıkar. Bu bağımsızlık, iki belgenin sayfa sayılarının farklı olduğu ya da kullanıcının farklı bir sayfada başlayan bir çeviride eşdeğer bölümü bulmak istediği kullanım durumları için önemlidir. Eşitlemeyi her zaman zorlamak, aracı iki pencereli basit bir masaüstü düzeninden daha kullanışsız kılar

Dikkat edilecek bir nokta: eşitleme işleyicisi içinde PdfView.PageNumber değerini programla ayarlamak, o görünümde değişim olayını da tetikler. Atamadan önce ayarlayıp hemen sonra temizlediğiniz bir mantıksal bayrakla sonsuz özyinelemeye karşı korunun. Bayrak görünüm başına değil form başınadır; çünkü üç görünüm de aynı işleyiciyi paylaşır

PDFium Component kullanan bir Delphi PDF karşılaştırma görüntüleyicisinde eşzamanlı sayfa gezinmesinin şeması: eşitleme onay kutusu, hedef görünüm başına sayfa sayısı koruması ve bir özyineleme koruma bayrağı
Sayfa numarası kaynak görünümden diğer her görünüme yalnızca eşitleme etkinken ve her hedef görünüm o sayfayı gerçekten içerdiğinde gider

Panel Başına Yakınlaştırma

Her TPdfView kendi Zoom özelliğini taşır; bu, yüzde cinsinden bir Double değerdir ve Zoom := 100 gerçek boyut (%100) demektir. Onu ayarlamak etkin herhangi bir FitMode ayarını geçersiz kılar. Etkin panelde genişliğe sığdır düğmesi için sığdırma yakınlaştırmasını PdfView.PageWidthZoom[PdfView.PageNumber] değerinden okuyup atayın. Sayfaya sığdır için PageZoom[PageNumber] kullanın. İkisi de 1 tabanlı sayfa numarasıyla dizinlenen dizi özellikleridir; bu yüzden onlara erişmeden önce sıfır sayfa numarasına karşı koruma koyun

Geçerli sayfayı bir görüntüye aktardığınızda döndürmeyi görünümden okuyun ama RenderPage çağrısını görünümde değil TPdf bileşeninde yapın. TPdf.RenderPage çağrısının bit eşlem biçimi açık piksel boyutlarının yanı sıra bir TRotation değeri ve bir TRenderOptions kümesi alır. İşlev biçimi, kaydettikten sonra kendiniz serbest bırakacağınız, çağırana ait bir TBitmap döndürür:

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;

Genişlik ve yükseklikteki 2 kat çarpan, ince metinli belgeler için daha keskin çıktı verir. Bit eşlem serbest bırakmasının çevresindeki try/finally isteğe bağlı değildir; bir TSaveDialog iptali de finally bloğuna girer ve kullanıcı ne yapmış olursa olsun bit eşlemin bırakılmasını istersiniz

DLL Gereksinimleri

PDFium Component yerel pdfium kütüphanesini sarmalar. 32 bitlik bir barındırıcı süreç pdfium32.dll ister; 64 bitlik bir barındırıcı pdfium64.dll ister. V8 JavaScript motorunu içeren çeşitler v8 sonekini alır ve 5-6 MB boyutundaki standart yapılara karşılık kabaca 23-27 MB ağırlığındadır. Form doldurmayı devre dışı bırakan (Pdf.FormFill := False) bir karşılaştırma görüntüleyicisi için standart, V8 içermeyen yapı yeterlidir ve dağıtımı daha küçük tutar

DLL dosyasını yürütülebilir dosyayla aynı dizine ya da sistem PATH değerindeki herhangi bir dizine koyun. Bileşen onu ilk TPdf etkinleştirildiğinde istek üzerine yükler; dolayısıyla eksik bir DLL uygulama başlangıcında değil o noktada ortaya çıkar. Bir kurulum paketi gönderiyorsanız en güvenilir yaklaşım, bir yöneticinin sonradan temizleyebileceği bir sistem dizinine güvenmek yerine DLL dosyasını kurulum sırasında uygulama klasörüne kopyalamaktır

V8 yapıları esas olarak PDF JavaScript eylemleriyle etkileşmeniz gerektiğinde, örneğin hesaplama alanlarını ya da gönderme işleyicilerini tetiklemek için yararlıdır. Edilgen bir karşılaştırma görüntüleyicisinin JavaScript çalıştırmak için hiçbir nedeni yoktur; Active := True öncesinde Pdf.FormFill := False ayarlamak form doldurma ortamını tümüyle atlar ve bu, standart yapı kullanılsa bile hiçbir JS motorunun başlatılmaması demektir. Hangi DLL çeşidini gönderirseniz gönderin, salt okunur bir görüntüleyici için doğru varsayılan budur

PDFium Component ve tam API arayüzü hakkında ayrıntılar için Delphi PDFium Component ürün sayfasını ziyaret edin