Bài viết kỹ thuật

Xây dựng Trình xem PDF trong Delphi bằng Component PDFium

Một bộ trình coi văn tài PDF trong phả hệ Delphi quy tụ rút cụt lủn cô đọng đai nải lại trúng y hai mác bộ thành phần uỷ hệ cấu với dóng dây phả liên đai đan chéo vạch lồng nối móc chắp vá chẽ ngỏ nối tụi nọ. Lọng TPdf cắm chủ bám sở dóng quyền tài trọn đai ôm lọn gốc bọc gã cuốn tài uỷ văn (document): nó gỡ niêm mở bọc khoá bưng cuốn tệp nọ ra, lột vạch vò nắn bẻ cong gỡ phá khoá bóc trần vạch giải mã ngạch đi (decrypts it), rồi đai uẩn rạch dóng thảy nhả trút đáp những ngỏ hỏi han thọc hạch vạch ghim tới nấc bộ chóp số đai dóng đếm rạch vạch trổ phiến trang cùng khối siêu rạch trích thông tin. Gã TPdfView là một khối cắm cục lọt móc tay dóng cầm chóp phả chỉnh điều uốn ghim bằng trực thị nhìn đai mắt nhằm bưng hoạ quệt vẽ bôi nặn phiến các dóng đánh phả mâm tờ hiện trồi chiếu lên mặt phiến kiếng màn khung, phụ trách dọn gánh vác xử êm những màn lướt luồn cuộn, bóp zoom hất đai phóng, với thảy lọng cái mâm trang hiển đang lọt vô phả ngỏ thò tròng mắt kẻ đương ngó xài kia. Khối PDFium Component này đai nải nhét ấp rúc bao trọn rập khuôn lợp ghim bọc cỗ trình vạch hoạ nặn hình diễn y đúc hệt cự dóng khuôn mà Chrome đang phả đai vác rinh giáng rớt nải thảy luồn theo, thế đai là lọng đám vạch nét hoạ font (glyphs), dải uỷ xoá răng kẹp cưa mòn đai rạch lượn (anti-aliasing), với ố ồ uẩn màu mà bạn gặt vớt bứt thâu ẵm gom lọng rinh trọn đai được trên mâm phả toạ mặt lưới nhám kiếng kia thảy cắm trúng phóc rành ngay mác những đồ chi ngỏ vạch mà đám gã dùng của bạn bấy đang trổ mắt nhìn trên mặt trình dòm lướt duyệt kia vậy. Cơ nhọc sự mảng đai rẽ hỏng ngụ ngả toạ trốn nấp ẩn vạch chẽ ở chỗ xới mảng ngạch tạc dựng rập diễn (rendering) kia. Mảng đai hạch nhọc kìa nới lọt lẩn nằm uẩn ở chóp đoạn đi giáng phả đan móc cài đai xâu ghim vạch trổ toạ dóng cái cục khối ghim đồ mác văn tệp ghim kẹp vô cùng dóng mảng ngó dòm coi bộ trình nọ kia kìa (view), dóng móc bốc tải rinh gánh gọi (loading) rành 1 thể lọt suôn mà không nảy sụp đánh lật đai rạch đổ vỡ sập chóp máy (without crashing) đặng thuở đụng độ rớt nải chạm trúng mặt lọng cái lằn cuốn tệp nọ cộm đai dóng tả tơi xước rách hư (damaged) hay nấp đai núc giấu mật bọc niêm rập bảo kỹ sau mật khoá uẩn trơn, kèm đai uỷ cả uỷ mác cấp trao đưa nhả nhét nhồi lọt vô lòng bàn tay gã bốc phả dòm trọn xài nọ đai bóp một cụm nhúm nho nhỏ rành lọng ngỏ dóng vạch rẽ các chốt hất gạt đai nải đòn bẩy điều dóng nắn ngạch mác phả hòng tạc vạch bứt dóng gọi rẽ đánh rành ngỏ bóp đúc nên đai cái bộ gã dòm rọi dạo xem kia trổi ngó lên dóng đai bọc hệt nhẽ rành vẻ trót xuôi (finished): lật ngỏ lật trang, xoay trượt hoán dóng bóp ghim đằng kích (zoom), nhồi nắn bó phả hất lấp ghim ép trang khít bưng chóp lọt rập ngỏ ngạch khuôn ngỏ (fit the page to the window)

Hành lọng ngỏ vạch trình nải dóng này lê từng nấc nải ghim bước lội qua đoạn rạch ghim lọng bộ đúc ráp lắp cấu kia lọng đai y theo trật thứ tự nải ngỏ hệt hồi mác bạn thực tay vạch lợp vác đồ lên xắn đai nặn xây rập ngỏ nó vậy. Mọi uẩn nải mác ở tại đai đây rành phả ngỏ thảy hất trượt dóng diễn vẽ quệt 1 trang một chóp nảy, dóng đó rành rẽ bệ mác nải ngỏ điều lọng vạch mọi phả nải ghim các phả ngỏ ngạch dòng bộ việc ngỏ văn nải thảy nải tuốt đều mong nải khấn vậy. Giả vạch chóp đai hễ nải bạn thèm vạch gọi đai uỷ mác lọng ngỏ trang phơi ngỏ rạch lấp nải cắm ngỏ chóp chồng xấp lợp nhau (stacked in one continuously scrolling column), thảy đấy rành vạch lọng nải ngụ 1 cái ghim phán chọn quyết rẽ nải phả điệu quy cách bố cục khác đi rồi chứ nào phải lối mòn rẽ đi dạo ngỏ đai này ngỏ đai

Nối dây TPdf cho tới bệ TPdfView

Thả hất quăng đai rớt rụng 1 TPdf và 1 TPdfView ngỏ nải ghim thả chóp bệ đai lọng phả lên khung vạch mâm đồ form nọ, rồi phán lệnh nói cho ngỏ cái khung nhìn biết tài cuốn lọng văn tệp rẽ ngỏ ghim vạch nào hòng vạch để lọng phơi xuất giãi hiển phả đai vạch trổ lên. Độc rành mảng dóng 1 cú ngạch chóp nải bộ phán phân giao uỷ trích (assignment) đấy thôi cũng hẵng ngỏ rành 1 ngạch ghim ngỏ rạch gút lọng đai móc uỷ liên kết giữa tảng khối vạch đai đồ vạch ngỏ dóng văn ngỏ vật vô hình ẩn không nảy ngỏ trổ diện (non-visual document) với ngạch uỷ gã lóng lọng củ cục vạch chóp phanh kẹp nắn dóng bọc cầm lái (control) uỷ đai bộ nải ghim phết màu quệt nặn (paints it)

procedure TFormMain.FormCreate(Sender: TObject);
begin
  // Pdf and PdfView were dropped at design time.
  PdfView.Pdf := Pdf;                 // the view paints whatever this document holds
  PdfView.FitMode := pfmFitWidth;     // start the user at a sensible zoom
end;

Kể từ dạo trước hồi thuở nào ngỏ bộ nải đai dóng trọn đám đai này nải rạch dóng thò máy vọt phả tuôn lăn bánh khởi vạch (runs), phả lọng nải ngỏ bệ tàng rạch đai thư dóng vạch rẽ gốc rành PDFium nải native phải đã đai lọng bám trụ nảy toạ ngụ y trên cái mặt máy nọ thảy. PDFium Component nải cất tiếng lọng hất vạch gọi phả vọt trượt lọt uỷ vô đai uẩn rạch dóng pdfium32.dll dẫu uỷ ngỏ rạch lọng pdfium64.dll tuỳ phả vạch tựa dựa theo đai rành chóp ngỏ trên bộ nải dóng nền ngỏ ngạch mác đai nấc vạch mục rẽ dóng đích của lọng nải ngỏ bạn, dóng rồi thì ngỏ rạch gã văn kiện nọ uỷ đai lọng thảy đơn ngỏ chóp phả tuột dóng rạch hất ngỏ vạch nải ngỏ từ ngạch chối rạch mở cất cửa bọc nếu lọng vạch ngỏ nải tệp DLL rẽ hỏng ngỏ phả bứt rạch tìm dóng thấy được. Xuất hất rải vạch tuôn tuột giao ship nải cuộn tệp lọng vạch DLL đai rập đồng bộ ăn rơ uỷ ngỏ cắm lót tịnh kế đai kề vạch ngay nải chóp cạnh nải cuốn tệp vạch ngỏ ghim đai dóng tuỳ rạch thực thi uỷ đai ngỏ của bạn, hoặc phả đai vạch dóng ngỏ lót gác thả đai rạch nó ở ngỏ uỷ ngạch chóp nơi nải chốn nào mà bộ máy lọng ngỏ nải gã load nạp của rạch hệ phả uỷ thống sẽ rờ ngỏ dóng đai lần tìm rẽ ra ngỏ đai rạch nó nải. Ngỏ bộ vạch uỷ ghim phả đai mác rạch những bản đai build nải phả ngỏ đai ghim gác trang rạch bị uỷ bật ngỏ dóng lọng nải vạch uỷ bộ V8 đai ngỏ nải hiện hữu tồn sinh ngỏ thảy rành rạch chỉ dóng ngỏ nải uỷ bọc dành ngỏ phả cho ngạch đai đám tệp vạch rạch PDF lọng nải ngỏ rinh ngỏ đai cõng vác mang vạch theo mảng chóp đai nải JavaScript uỷ ngỏ dóng rạch mà rẽ bạn uỷ vạch ngỏ nải muốn đai nải ghim trượt vạch dóng phả đai vọt thi ngỏ thảy vạch rạch hành thôi, thứ uỷ ngỏ mà 1 vạch đai gã ngỏ dóng trình vạch uỷ phả dòm trơn vạch rạch đai (plain viewer) chẳng đai nải rạch ngỏ hề ngỏ dóng đai ngó vạch uỷ màng phả đai rạch thò ngỏ rẽ nải đai làm, thế uỷ đai dóng ngỏ rạch nên ngỏ thảy bạn cứ vạch ngỏ rạch đai vươn nải lọng ngỏ vạch tay phả lọng bốc hốt ngỏ vạch dóng tóm rạch đai mảng nải ngỏ tệp ngỏ rạch DLL uỷ dóng đai bộ tiêu vạch chuẩn uỷ rẽ đai ngỏ kia dóng trừ rạch ngỏ nải phi vạch hễ lọng bạn phả rạch uỷ đai nải đang có lọng ngỏ một cớ uỷ lý rạch dóng nguyên đai vạch ngỏ do đai cụ ngỏ vạch phả nải thể đai rạch nải rành rẽ vạch thảy ngỏ dóng để lọng ngỏ không phả rạch đai ngỏ vạch làm lọng nải dóng vạch ngỏ ngạch vậy thôi

Tải một tài liệu mà không tin tưởng đầu vào

Bản năng là bọc quá trình tải trong một khối try/except và coi bất kỳ ngoại lệ nào được ném ra là thất bại. Bản năng đó ở đây là sai và việc làm sai sẽ tạo ra một trình xem có vẻ ổn cho đến khi ai đó cung cấp cho nó một tệp bị hỏng. Đặt Active := True không gây ra lỗi khi tải thất bại. PDFium Component bắt lỗi bên trong và để Active ở trạng thái False, vì vậy cách trung thực duy nhất để biết tài liệu có mở hay không là đọc lại thuộc tính sau khi bạn thiết lập nó

procedure TFormMain.OpenDocument(const FileName: string);
begin
  Pdf.FileName := FileName;
  Pdf.Active := True;                 // never raises; failure leaves Active = False
  if not Pdf.Active then
  begin
    ShowMessage('Could not open ' + FileName);
    Exit;
  end;
  PdfView.PageNumber := 1;            // the view tracks its own current page
  UpdatePageLabel;
end;

Có hai điều đáng chú ý. Thứ nhất là PageNumber tồn tại trên cả hai đối tượng và cả hai đều độc lập với nhau. Pdf.PageNumber là khái niệm của tài liệu về trang hiện tại; PdfView.PageNumber là trang mà control thực sự hiển thị và đó là trang bạn đặt để di chuyển người dùng qua tệp. Việc thiết lập một thuộc tính không làm thuộc tính kia thay đổi, do đó một trình xem luôn điều khiển thuộc tính của phần khung hiển thị. Thứ hai là lập chỉ mục dựa trên 1: các trang chạy từ 1 đến Pdf.PageCount, không phải từ 0, điều này làm ngạc nhiên bất kỳ ai đã quen với các mảng dựa trên 0

Xử lý tệp được mã hóa

Các tài liệu được mã hóa cũng sử dụng cùng một đường dẫn tải. Nếu mật khẩu mở được đặt trước khi kích hoạt, tài liệu sẽ tự giải mã khi mở; nếu sai hoặc thiếu, Active vẫn ở trạng thái False giống hệt như đối với tệp bị hỏng. Do đó, cách khắc phục là nhắc nhập mật khẩu và thử kích hoạt lại

procedure TFormMain.OpenWithPassword(const FileName: string);
var
  Password: string;
begin
  Pdf.FileName := FileName;
  Pdf.Active := True;
  if not Pdf.Active then
  begin
    if InputQuery('Password required', 'Password:', Password) then
    begin
      Pdf.Password := Password;       // must be set before Active := True
      Pdf.Active := True;
    end;
    if not Pdf.Active then
    begin
      ShowMessage('Unable to open the document.');
      Exit;
    end;
  end;
  PdfView.PageNumber := 1;
end;

Bởi vì lỗi xảy ra trong im lặng đối với cả mật khẩu sai và tệp bị hỏng, bạn không thể phân biệt hai lỗi này chỉ bằng Active. Trong thực tế, điều đó có thể chấp nhận được đối với trình xem: người dùng cung cấp đúng mật khẩu hoặc biết tệp sẽ không mở và thông báo hiển thị giống nhau trong cả hai trường hợp

Phân trang tài liệu

Khi tài liệu mở, điều hướng là phép toán số học trên PdfView.PageNumber được giới hạn bởi Pdf.PageCount. Công việc thực sự duy nhất là kẹp chặt khoảng giới hạn lại (clamping), để các nút điều hướng không bao giờ đẩy trang ra khỏi phạm vi và các nút đầu tiên và cuối cùng luôn bị vô hiệu hóa ở các đầu mút của tệp

procedure TFormMain.GoToPage(NewPage: Integer);
begin
  if not Pdf.Active then
    Exit;
  if NewPage < 1 then
    NewPage := 1
  else if NewPage > Pdf.PageCount then
    NewPage := Pdf.PageCount;
  PdfView.PageNumber := NewPage;
  UpdatePageLabel;
end;

// the four navigation buttons reduce to one call each
procedure TFormMain.FirstClick(Sender: TObject);  begin GoToPage(1); end;
procedure TFormMain.PrevClick(Sender: TObject);   begin GoToPage(PdfView.PageNumber - 1); end;
procedure TFormMain.NextClick(Sender: TObject);   begin GoToPage(PdfView.PageNumber + 1); end;
procedure TFormMain.LastClick(Sender: TObject);   begin GoToPage(Pdf.PageCount); end;

Hộp văn bản "đi tới trang N" là cùng một lệnh gọi GoToPage được cung cấp từ một số nguyên đã được phân tích cú pháp và thanh kẹp bao hàm luôn cả trường hợp người dùng nhập 9999 vào tệp có mười trang. Giữ UpdatePageLabel làm nơi duy nhất ghi "Trang 3/12" để phần thông báo không bao giờ bị lệch nhịp với những gì giao diện hiển thị

Thu phóng: Tỷ lệ phần trăm rõ ràng và các chế độ vừa vặn

Chức năng thu phóng trên TPdfView xuất hiện dưới hai hương vị tương tác và việc hiểu sự tương tác này chính là sự khác biệt giữa một chức năng điều khiển thu phóng hoạt động đúng và một chức năng gây khó dễ với người dùng. Tuyến trực tiếp là thuộc tính Zoom, một tỷ lệ phần trăm trong đó 100 nghĩa là kích thước thực tế. Tuyến còn lại là FitMode, yêu cầu giao diện tự tính toán thu phóng cho bạn và tiếp tục tính toán lại khi thay đổi kích thước cửa sổ

// fixed magnifications
PdfView.Zoom := 100;     // actual size
PdfView.Zoom := 50;      // half
PdfView.Zoom := 200;     // double

// let the view size the page to the window, and keep it sized on resize
PdfView.FitMode := pfmFitWidth;   // page width fills the control
PdfView.FitMode := pfmFitPage;    // whole page visible
PdfView.FitMode := pfmActualSize; // 1:1 with the document's points

Đây là phần khiến mọi người bối rối. Gán trực tiếp Zoom sẽ đặt lại FitMode về pfmNone. Đó là hành vi đúng đắn chứ không phải là lỗi: ngay khi người dùng chọn chính xác mức 150%, giao diện không thể tôn trọng "vừa vặn với chiều rộng" được nữa, vì hai yêu cầu này mâu thuẫn với nhau. Hệ quả đối với giao diện người dùng của bạn là một nút phóng to và một nút vừa vặn với trang là các trạng thái loại trừ lẫn nhau và thanh công cụ sẽ phải làm cho chế độ đang hoạt động có thể hiển thị được. Khi người dùng nhấp vào nút vừa vặn với trang, hãy đặt FitMode; khi họ nhấp vào thu phóng dạng số, hãy đặt Zoom và để nó tự xóa chế độ vừa vặn đi

Nếu bạn muốn tự tính toán giá trị vừa vặn, có lẽ là để gieo một thanh trượt thu phóng với phần trăm vừa vặn hiện tại, thì các trình trợ giúp trên mỗi trang sẽ cung cấp cho bạn các số mà không thay đổi chế độ. PageWidthZoom[N], PageZoom[N]ActualSizeZoom[N] trả về tỷ lệ phần trăm tương ứng mà sẽ thu phóng vừa chiều rộng cho trang N, thu phóng cho trang vừa hiển thị toàn trang hoặc hiển thị trang ở kích thước thực

// seed a zoom readout from the fit-to-width value of the current page
var
  FitPercent: Double;
begin
  FitPercent := PdfView.PageWidthZoom[PdfView.PageNumber];
  ZoomEdit.Text := Format('%.0f%%', [FitPercent]);
end;

Những gì một trình xem hoàn chỉnh thực sự cần

Trình xem ở trên chỉ có vài chục dòng nhưng nó đã thực hiện công việc mà quy trình làm việc trên tài liệu cần: mở tệp, sống sót sau một tệp bị lỗi, hiển thị trang, di chuyển giữa các trang và thay đổi độ phóng đại bằng tay hoặc bằng cách vừa vặn với trang. PDFium thực hiện những phần khó một cách âm thầm. Phông chữ được nhúng được biên dịch ra, chú thích và trường biểu mẫu vẽ vào đúng vị trí tài liệu đặt chúng và trang bạn thấy khớp với trang mà người dùng Chrome sẽ thấy vì đó là cùng một công cụ vẽ cả hai

Từ nền tảng này, các phần bổ sung mang tính gia tăng thay vì thay đổi về cấu trúc. Lựa chọn văn bản và tìm kiếm đọc từ cùng một lớp văn bản mà PDFium đã xây dựng; đọc siêu dữ liệu chẳng hạn như Pdf.TitlePdf.Author chỉ là một thuộc tính; xoay và chuyển sang thang độ xám là các tùy chọn hiển thị mà bạn truyền khi vẽ trang vào bitmap. Không có điều nào trong số đó thay đổi xương sống mà bạn có ở đây, đó là đối tượng tài liệu, khung nhìn và luồng mở-tài-liệu-rồi-điều-hướng để kết nối chúng. Hãy làm đúng phần xương sống đó và phần còn lại chỉ là trang trí

Các thành phần TPdfTPdfView được sử dụng xuyên suốt là một phần của PDFium Component cho Delphi và C++Builder, mà đã cung cấp đầy đủ hướng dẫn trình xem trên trang sản phẩm của nó