0

PDF.js Khám phá sâu từ Demo 1: Render PDF với viewer tùy chỉnh

Lời nói đầu

hi, đây là loạt bài phân tích mã nguồn PDF.js.

Loạt bài này bắt đầu từ mã demo, dần dần theo chuỗi gọi của mã đi sâu vào bên trong PDF.js. Đồng thời sẽ giải thích nguyên lý mã từ hai khía cạnh:

  • Giải thích demo: giải thích theo từng trường hợp, liên kết từ nông đến sâu, khám phá chuyên sâu
  • Giải thích kho mã: giải thích khái quát, có thể đọc theo từng khối, mỗi bài đứng trên cơ sở riêng của mình

Hy vọng mang lại cho bạn trải nghiệm đọc khác biệt, happy reading!

Render PDF với viewer tùy chỉnh

Chúng ta hãy thử tự xây dựng một viewer tối giản để render PDF, sau đó theo dõi từng dòng mã để đi sâu vào bên trong PDF.js. Trong quá trình theo dõi, chúng ta sẽ thay thế giá trị biến vào mã để giúp luồng thực thi dễ hiểu hơn.

import * as pdfjsLib from 'https://cdn.jsdelivr.net/npm/pdfjs-dist@6.3.289/build/pdf.mjs';
pdfjsLib.GlobalWorkerOptions.workerSrc =
  'https://cdn.jsdelivr.net/npm/pdfjs-dist@6.3.289/build/pdf.worker.mjs';
const loadingTask = pdfjsLib.getDocument({
  url: 'https://assets-blog.streampdf.com/PDF32000_2008.pdf',
});
const pdf = await loadingTask.promise;
// Lấy dữ liệu render của trang đầu tiên
const page = await pdf.getPage(1);
// Lấy dữ liệu viewport của trang đầu tiên
const viewport = page.getViewport({ scale: 0.5 });
// Lấy canvas mà chúng ta sẽ render lên đó
const canvas = document.getElementById('canvas-id');
const context = canvas.getContext('2d');
// Đồng bộ kích thước canvas với kích thước viewport PDF
canvas.width = Math.floor(viewport.width);
canvas.height = Math.floor(viewport.height);
canvas.style.width = Math.floor(viewport.width) + 'px';
canvas.style.height = Math.floor(viewport.height) + 'px';
// Chuẩn bị tham số render
const renderContext = {
  canvasContext: context,
  viewport,
};
// Gọi phương thức render để thực hiện render thực tế, await đợi render hoàn tất
await page.render(renderContext);

demo: https://streampdf.com/blog/demos/a-simple-pdf-render-demo.html

Dòng 1-3: Cấu hình đường dẫn tệp Worker

Nhập tệp mục vào của thư viện PDF.js và đặt đường dẫn của tệp Worker.

Điều này có nghĩa là khi sử dụng PDF.js, chúng ta thậm chí có thể thay thế hoàn toàn Worker bằng Worker của riêng mình, miễn là tuân thủ quy chuẩn giao diện.

Tại đây chúng ta đặt nó thành đường dẫn tệp Worker mặc định:

GlobalWorkerOptions.workerSrc = https://cdn.jsdelivr.net/npm/pdfjs-dist@6.3.289/build/pdf.worker.mjs

Dòng 4-7: Tải tài liệu PDF

Gọi phương thức getDocument để tải tài liệu PDF, truyền vào tham số đối tượng:

  • url: URL của tài liệu PDF. Giá trị là https://assets-blog.streampdf.com/PDF32000_2008.pdf

Phương thức getDocument trả về một Promise, khi tài liệu PDF tải xong sẽ resolve một đối tượng tài liệu PDF.

const loadingTask = pdfjsLib.getDocument(...) là lời gọi đồng bộ: nhấp để xem lời gọi

loadingTask.promise là một getter, trả về Promise được tạo bởi phương thức Promise.withResolvers().

Tại đây chúng ta await loadingTask.promise, đợi tài liệu PDF tải xong và gán nó cho biến pdf.

Dòng 8-9: Lấy trang đầu tiên của tài liệu PDF

Sau khi tài liệu khởi tạo xong, chúng ta có thể gọi phương thức getPage để lấy dữ liệu render của trang đầu tiên.

Phương thức getPage trả về một Promise, khi dữ liệu trang tải xong sẽ resolve một đối tượng trang.

const page = await pdf.getPage(1); là lời gọi bất đồng bộ: nhấp để xem lời gọi

Sau khi dữ liệu trang đầu tiên tải xong, gán nó cho biến page.

Dòng 10-11: Lấy dữ liệu viewport của trang đầu tiên

Sau khi dữ liệu trang tải xong, chúng ta có thể gọi phương thức getViewport để lấy dữ liệu viewport của trang đầu tiên.

const viewport = page.getViewport({ scale: 0.5 }); là lời gọi đồng bộ: nhấp để xem lời gọi

Lấy dữ liệu viewport của trang đầu tiên, truyền vào tham số đối tượng:

  • scale: tỷ lệ thu phóng. Giá trị là 0.5

Viewport trả về là một đối tượng, chứa chiều rộng và chiều cao của viewport. Giá trị là:

{
  width: 297.5,
  height: 421,
  // các cái khác, ở đây không dùng nên tạm lược bỏ...
}

Gán nó cho biến viewport.

Dòng 12-19: Lấy canvas mà chúng ta sẽ render lên đó

Sau khi có biến viewport, đồng bộ width và height của canvas với kích thước viewport PDF.

canvas.width = 297; // Math.floor(viewport.width);
canvas.height = 421; // Math.floor(viewport.height);

Cũng đặt CSS width và CSS height của canvas bằng kích thước viewport PDF.

canvas.style.width = 297 + 'px'; // Math.floor(viewport.width) + 'px';
canvas.style.height = 421 + 'px'; // Math.floor(viewport.height) + 'px';

Tại sao cần đặt CSS width và CSS height của canvas? Thuộc tính width và height của canvas kiểm soát kích thước pixel của canvas, trong khi CSS width và height là kích thước hiển thị canvas trên trang. Nói chung, hai cái này giống nhau. Nhưng trên màn hình độ phân giải cao, chúng khác nhau. Ví dụ khi window.devicePixelRatio là 2, kích thước pixel của canvas nên đặt là 594, 842, còn CSS width và height giữ nguyên 297, 421 để tránh render bị mờ.

Dòng 20-26: Render trang đầu tiên

Sau khi canvas đã sẵn sàng, chúng ta có thể gọi phương thức render để render trang đầu tiên.

Chuẩn bị ngữ cảnh canvas, dữ liệu viewport và tham số render để có thể gọi phương thức render.

await page.render(renderContext); là lời gọi bất đồng bộ: nhấp để xem lời gọi

Đến đây, việc thực thi mã đã hoàn tất.

Tổng kết

Trong chương này, chúng ta đã theo dõi một viewer tùy chỉnh để render trang đầu tiên của tệp PDF.

Điểm khác biệt lớn nhất so với các bài viết khác là chúng ta đã thay thế giá trị biến vào chuỗi mã đang phân tích, từ đó có thể xác định các nhánh thực thi khác nhau. Các tệp và bài viết phân tích khác nhau dẫn đến các nhánh phân nhánh khác nhau, quá trình đọc bài viết giống như đang debug mã vậy.

Hy vọng điều này mang lại cho bạn trải nghiệm đọc mã nguồn khác biệt.

happy coding!


About me: Người sáng tạo StreamPDF, tập trung vào nghiên cứu và phát triển trong lĩnh vực AI và PDF.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí