NodeJS tạo pdf

Nút. js là thời gian chạy JavaScript hướng sự kiện không đồng bộ và hiệu quả nhất khi xây dựng các ứng dụng mạng có thể mở rộng. Nút. js không có khóa, vì vậy không có cơ hội khóa chết bất kỳ quy trình nào. , Chrome & Docker không đầu

Tiểu sử. Vài tháng trước, một trong những khách hàng của RisingStack đã yêu cầu chúng tôi phát triển một tính năng mà người dùng có thể yêu cầu trang React ở định dạng PDF. Trang đó về cơ bản là báo cáo/kết quả cho bệnh nhân có trực quan hóa dữ liệu, chứa rất nhiều SVG. Hơn nữa, có một số yêu cầu đặc biệt để thao tác bố cục và thực hiện một số sắp xếp lại các phần tử HTML. Vì vậy, PDF phải có kiểu dáng và bổ sung khác so với trang React ban đầu

NodeJS tạo pdf

Vì nhiệm vụ phức tạp hơn một chút so với những gì có thể được giải quyết bằng các quy tắc CSS đơn giản, trước tiên chúng tôi khám phá các triển khai khả thi. Về cơ bản, chúng tôi tìm thấy 3 giải pháp chính. Bài đăng trên blog này sẽ hướng dẫn bạn về các khả năng này và các triển khai cuối cùng

Nhận xét cá nhân trước khi chúng tôi bắt đầu. nó khá rắc rối, vì vậy hãy thắt dây an toàn

Mục lục

  • Tạo PDF phía máy khách hay phía phụ trợ?
  • lựa chọn 1. Tạo ảnh chụp màn hình từ DOM
  • Lựa chọn 2. Chỉ sử dụng thư viện PDF
  • Phương án cuối cùng 3. Puppeteer, Chrome không đầu với Node. js
    • Thao tác kiểu
    • Gửi tệp cho khách hàng và lưu nó
  • Sử dụng Puppeteer với Docker
  • Tùy chọn 3 +1. Quy tắc in CSS
  • Tóm lược

Tạo PDF phía máy khách hay phía máy chủ?

Có thể tạo tệp PDF ở cả phía máy khách và phía máy chủ. Tuy nhiên, có lẽ sẽ hợp lý hơn nếu để phần phụ trợ xử lý nó, vì bạn không muốn sử dụng hết tài nguyên mà trình duyệt của người dùng có thể cung cấp

Mặc dù vậy, tôi vẫn sẽ đưa ra các giải pháp cho cả hai phương pháp

lựa chọn 1. Tạo ảnh chụp màn hình từ DOM

Thoạt nhìn, giải pháp này có vẻ đơn giản nhất, hóa ra lại đúng, nhưng nó có những hạn chế riêng. Nếu bạn không có nhu cầu đặc biệt, chẳng hạn như văn bản có thể chọn hoặc có thể tìm kiếm trong PDF, thì đó là một cách hay và đơn giản để tạo một văn bản

Phương pháp này là đơn giản và đơn giản. tạo ảnh chụp màn hình từ trang và đặt nó vào tệp PDF. khá đơn giản. Chúng tôi đã sử dụng hai gói cho phương pháp này

Html2canvas, để tạo ảnh chụp màn hình từ DOM
jsPdf, một thư viện để tạo PDF

Hãy bắt đầu mã hóa

doc = new PDFDocument
doc.pipe fs.createWriteStream('output.pdf')
doc.font('fonts/PalatinoBold.ttf')
   .fontSize(25)
   .text('Some text with an embedded font!', 100, 100)
 
doc.image('path/to/image.png', {
   fit: [250, 300],
   align: 'center',
   valign: 'center'
});
 
doc.addPage()
   .fontSize(25)
   .text('Here is some vector graphics...', 100, 100)
 
doc.end()
1

import html2canvas from 'html2canvas'
import jsPdf from 'jspdf'
 
function printPDF () {
    const domElement = document.getElementById('your-id')
    html2canvas(domElement, { onclone: (document) => {
      document.getElementById('print-button').style.visibility = 'hidden'
    }})
    .then((canvas) => {
        const img = canvas.toDataURL('image/png')
        const pdf = new jsPdf()
        pdf.addImage(imgData, 'JPEG', 0, 0, width, height)
        pdf.save('your-filename.pdf')
})

Và đó là nó

Hãy chắc chắn rằng bạn đã xem phương pháp 

doc = new PDFDocument
doc.pipe fs.createWriteStream('output.pdf')
doc.font('fonts/PalatinoBold.ttf')
   .fontSize(25)
   .text('Some text with an embedded font!', 100, 100)
 
doc.image('path/to/image.png', {
   fit: [250, 300],
   align: 'center',
   valign: 'center'
});
 
doc.addPage()
   .fontSize(25)
   .text('Here is some vector graphics...', 100, 100)
 
doc.end()
doc = new PDFDocument
doc.pipe fs.createWriteStream('output.pdf')
doc.font('fonts/PalatinoBold.ttf')
   .fontSize(25)
   .text('Some text with an embedded font!', 100, 100)
 
doc.image('path/to/image.png', {
   fit: [250, 300],
   align: 'center',
   valign: 'center'
});
 
doc.addPage()
   .fontSize(25)
   .text('Here is some vector graphics...', 100, 100)
 
doc.end()
3. Nó có thể tỏ ra tiện dụng khi bạn cần nhanh chóng chụp ảnh nhanh và thao tác với DOM (e. g. ẩn nút in) trước khi chụp ảnh. Tôi có thể thấy khá nhiều trường hợp sử dụng cho gói này. Thật không may, của chúng tôi không phải là một, vì chúng tôi cần xử lý việc tạo PDF ở phía phụ trợ

Lựa chọn 2. Chỉ sử dụng Thư viện PDF

Có một số thư viện trên NPMnpm là sổ đăng ký phần mềm phục vụ hơn 1. 3 triệu gói. npm được sử dụng bởi các nhà phát triển nguồn mở từ khắp nơi trên thế giới để chia sẻ và mượn mã, cũng như nhiều doanh nghiệp. Có ba thành phần để npm. trang web Giao diện dòng lệnh (CLI) sổ đăng ký Sử dụng trang web để khám phá và tải xuống các gói, tạo hồ sơ người dùng và. cho mục đích này, chẳng hạn như jsPDF (đã đề cập ở trên) hoặc PDFKit. Vấn đề với chúng là tôi sẽ phải tạo lại cấu trúc trang nếu tôi muốn sử dụng các thư viện này. Điều đó chắc chắn ảnh hưởng đến khả năng bảo trì, vì tôi cần áp dụng tất cả các thay đổi tiếp theo cho cả mẫu PDF và trang React.

Hãy nhìn vào đoạn mã dưới đây. Bạn cần tự tạo tài liệu PDF bằng tay. Bây giờ bạn có thể duyệt qua DOM và tìm ra cách dịch từng phần tử sang PDF, nhưng đó là một công việc tẻ nhạt. Nó phải là một con đường dễ dàng hơn

doc = new PDFDocument
doc.pipe fs.createWriteStream('output.pdf')
doc.font('fonts/PalatinoBold.ttf')
   .fontSize(25)
   .text('Some text with an embedded font!', 100, 100)
 
doc.image('path/to/image.png', {
   fit: [250, 300],
   align: 'center',
   valign: 'center'
});
 
doc.addPage()
   .fontSize(25)
   .text('Here is some vector graphics...', 100, 100)
 
doc.end()

Đoạn mã này là từ tài liệu PDFKit. Tuy nhiên, nó có thể hữu ích nếu mục tiêu của bạn là tệp PDF ngay lập tức chứ không phải chuyển đổi trang HTML đã tồn tại (và luôn thay đổi)

Lựa chọn cuối cùng 3. Người múa rối, Chrome không đầu có nút. js

Người múa rối là gì?

Puppeteer là thư viện Node cung cấp API cấp cao để kiểm soát Chrome hoặc Chromium qua Giao thức DevTools. Puppeteer chạy không đầu theo mặc định, nhưng có thể được định cấu hình để chạy Chrome hoặc Chromium đầy đủ (không đầu)

Về cơ bản, đây là một trình duyệt mà bạn có thể chạy từ Node. js. Nếu bạn đọc tài liệu, điều đầu tiên nó nói về Puppeteer là bạn có thể sử dụng nó để Tạo ảnh chụp màn hình và tệp PDF của các trang'. Xuất sắc. Đó là những gì chúng tôi đang tìm kiếm

Hãy cài đặt Puppeteer với 

doc = new PDFDocument
doc.pipe fs.createWriteStream('output.pdf')
doc.font('fonts/PalatinoBold.ttf')
   .fontSize(25)
   .text('Some text with an embedded font!', 100, 100)
 
doc.image('path/to/image.png', {
   fit: [250, 300],
   align: 'center',
   valign: 'center'
});
 
doc.addPage()
   .fontSize(25)
   .text('Here is some vector graphics...', 100, 100)
 
doc.end()
4 và triển khai trường hợp sử dụng của chúng ta

const puppeteer = require('puppeteer')
 
async function printPDF() {
  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();
  await page.goto('https://blog.risingstack.com', {waitUntil: 'networkidle0'});
  const pdf = await page.pdf({ format: 'A4' });
 
  await browser.close();
  return pdf
})

Đây là một chức năng đơn giản điều hướng đến một URL và tạo tệp PDF của trang web

Đầu tiên, chúng tôi khởi chạy trình duyệt (chỉ hỗ trợ tạo PDF ở chế độ trình duyệt không đầu), sau đó chúng tôi mở một trang mới, đặt kích thước khung nhìn và điều hướng đến URL được cung cấp

Đặt tùy chọn 

doc = new PDFDocument
doc.pipe fs.createWriteStream('output.pdf')
doc.font('fonts/PalatinoBold.ttf')
   .fontSize(25)
   .text('Some text with an embedded font!', 100, 100)
 
doc.image('path/to/image.png', {
   fit: [250, 300],
   align: 'center',
   valign: 'center'
});
 
doc.addPage()
   .fontSize(25)
   .text('Here is some vector graphics...', 100, 100)
 
doc.end()
5 có nghĩa là Puppeteer coi việc điều hướng kết thúc khi không có kết nối mạng trong ít nhất 500 mili giây. (Xem tài liệu API để biết thêm thông tin. )

Sau đó, chúng tôi lưu tệp PDF vào một biến, chúng tôi đóng trình duyệt và trả lại tệp PDF

Ghi chú. Phương thức 

doc = new PDFDocument
doc.pipe fs.createWriteStream('output.pdf')
doc.font('fonts/PalatinoBold.ttf')
   .fontSize(25)
   .text('Some text with an embedded font!', 100, 100)
 
doc.image('path/to/image.png', {
   fit: [250, 300],
   align: 'center',
   valign: 'center'
});
 
doc.addPage()
   .fontSize(25)
   .text('Here is some vector graphics...', 100, 100)
 
doc.end()
6 nhận một đối tượng 
doc = new PDFDocument
doc.pipe fs.createWriteStream('output.pdf')
doc.font('fonts/PalatinoBold.ttf')
   .fontSize(25)
   .text('Some text with an embedded font!', 100, 100)
 
doc.image('path/to/image.png', {
   fit: [250, 300],
   align: 'center',
   valign: 'center'
});
 
doc.addPage()
   .fontSize(25)
   .text('Here is some vector graphics...', 100, 100)
 
doc.end()
7, nơi bạn cũng có thể lưu tệp vào đĩa bằng tùy chọn 'đường dẫn'. Nếu đường dẫn không được cung cấp, tệp PDF sẽ không được lưu vào đĩa, thay vào đó, bạn sẽ nhận được một bộ đệm. Lát nữa mình bàn bạn xử lý thế nào. )

Trong trường hợp trước tiên bạn cần đăng nhập để tạo tệp PDF từ một trang được bảo vệ, trước tiên bạn cần điều hướng đến trang đăng nhập, kiểm tra các thành phần của biểu mẫu để tìm ID hoặc tên, điền vào, sau đó gửi biểu mẫu

await page.type('#email', process.env.PDF_USER)
await page.type('#password', process.env.PDF_PASSWORD)
await page.click('#submit')

Luôn lưu trữ thông tin đăng nhập trong các biến môi trường, không mã hóa chúng

Thao tác phong cách

Puppeteer cũng có một giải pháp cho thao tác kiểu này. Bạn có thể chèn các thẻ kiểu trước khi tạo PDF và Puppeteer sẽ tạo một tệp có các kiểu đã sửa đổi

await page.addStyleTag({ content: '.nav { display: none} .navbar { border: 0px} #print-button {display: none}' })

Gửi tệp cho khách hàng và lưu nó

Được rồi, bây giờ bạn đã tạo một tệp PDF trên phần phụ trợ. Làm gì bây giờ?

Như tôi đã đề cập ở trên, nếu bạn không lưu tệp vào đĩa, bạn sẽ nhận được một bộ đệm. Bạn chỉ cần gửi bộ đệm đó với loại nội dung phù hợp tới giao diện người dùng

printPDF().then(pdf => {
	res.set({ 'Content-Type': 'application/pdf', 'Content-Length': pdf.length })
	res.send(pdf)
})

Giờ đây, bạn có thể chỉ cần gửi yêu cầu đến máy chủ để nhận tệp PDF đã tạo

function getPDF() {
 return axios.get(`${API_URL}/your-pdf-endpoint`, {
   responseType: 'arraybuffer',
   headers: {
     'Accept': 'application/pdf'
   }
 })

Khi bạn đã gửi yêu cầu, bộ đệm sẽ bắt đầu tải xuống. Bây giờ, bước cuối cùng là chuyển đổi bộ đệm thành tệp PDF

savePDF = () => {
    this.openModal(‘Loading…’) // open modal
   return getPDF() // API call
     .then((response) => {
       const blob = new Blob([response.data], {type: 'application/pdf'})
       const link = document.createElement('a')
       link.href = window.URL.createObjectURL(blob)
       link.download = `your-file-name.pdf`
       link.click()
       this.closeModal() // close modal
     })
   .catch(err => /** error handling **/)
 }

Điều đó là vậy đó. Nếu bạn nhấp vào nút lưu, tệp PDF sẽ được lưu bởi trình duyệt

Sử dụng Puppeteer với Docker

Tôi nghĩ đây là phần khó nhất trong quá trình triển khai – vì vậy hãy để tôi giúp bạn tiết kiệm vài giờ tra Google

Tài liệu chính thức nêu rõ rằng "việc thiết lập và chạy headless Chrome trong Docker có thể khó khăn". Các tài liệu chính thức có một phần, tại thời điểm viết bài này, bạn có thể tìm thấy tất cả thông tin cần thiết về cách cài đặt nghệ sĩ múa rối với Docker

Nếu bạn cài đặt Puppeteer trên ảnh Alpine, hãy đảm bảo rằng bạn cuộn xuống một chút để. Mặt khác, bạn có thể che đậy sự thật rằng bạn không thể chạy phiên bản Puppeteer mới nhất và bạn cũng cần tắt sử dụng shm, sử dụng cờ

const browser = await puppeteer.launch({
  headless: true,
  args: ['--disable-dev-shm-usage']
});

Nếu không, quy trình con Puppeteer có thể hết bộ nhớ trước khi nó được bắt đầu đúng cách. Thông tin thêm về điều đó trên liên kết khắc phục sự cố ở trên

Tùy chọn 3 + 1. Quy tắc in CSS

Người ta có thể nghĩ rằng chỉ cần sử dụng các quy tắc in CSS là dễ dàng từ quan điểm của nhà phát triển. Không có mô-đun NPM hoặc nút, chỉ có CSS ​​thuần túy. Nhưng làm thế nào để họ có giá khi nói đến khả năng tương thích giữa các trình duyệt?

Khi chọn quy tắc in CSS, bạn phải kiểm tra kết quả trong mọi trình duyệt để đảm bảo rằng nó cung cấp bố cục giống nhau và điều đó không đúng 100%

Ví dụ: chèn dấu ngắt sau một phần tử nhất định không thể được coi là một trường hợp sử dụng bí truyền, nhưng bạn có thể ngạc nhiên rằng mình cần sử dụng các giải pháp thay thế

Trừ khi bạn là một nhà ảo thuật CSS thiện chiến với nhiều kinh nghiệm trong việc tạo các trang có thể in được, nếu không thì việc này có thể tốn nhiều thời gian

Các quy tắc in sẽ rất tuyệt nếu bạn có thể giữ cho các biểu định kiểu in đơn giản

Hãy xem một ví dụ

doc = new PDFDocument
doc.pipe fs.createWriteStream('output.pdf')
doc.font('fonts/PalatinoBold.ttf')
   .fontSize(25)
   .text('Some text with an embedded font!', 100, 100)
 
doc.image('path/to/image.png', {
   fit: [250, 300],
   align: 'center',
   valign: 'center'
});
 
doc.addPage()
   .fontSize(25)
   .text('Here is some vector graphics...', 100, 100)
 
doc.end()
0

CSS ở trên ẩn nút in và chèn dấu ngắt trang sau mỗi _______0_______8 với lớp 

doc = new PDFDocument
doc.pipe fs.createWriteStream('output.pdf')
doc.font('fonts/PalatinoBold.ttf')
   .fontSize(25)
   .text('Some text with an embedded font!', 100, 100)
 
doc.image('path/to/image.png', {
   fit: [250, 300],
   align: 'center',
   valign: 'center'
});
 
doc.addPage()
   .fontSize(25)
   .text('Here is some vector graphics...', 100, 100)
 
doc.end()
9 Có một bài viết hay tóm tắt những gì bạn có thể làm với quy tắc in và những khó khăn với chúng, bao gồm cả khả năng tương thích với trình duyệt

Cân nhắc mọi thứ, các quy tắc in CSS rất tuyệt vời và hiệu quả nếu bạn muốn tạo PDF từ một trang không quá phức tạp

Làm cách nào để tạo PDF từ HTML trong nodejs?

Bắt đầu .
Tạo một nút mới. dự án js. Tạo một thư mục mới cho dự án của bạn và đi đến thư mục. .
Cài đặt người múa rối. Chạy lệnh bên dưới để cài đặt Puppeteer. npm tôi nghệ sĩ múa rối. .
Tạo một tệp mới. Trong cùng một dự án, tạo chỉ mục. tập tin js. .
Nghệ nhân múa rối nhập khẩu. Bên trong chỉ mục. js, nhập người múa rối

Làm cách nào để tạo PDF động trong nodejs?

Thiết lập dự án .
Tạo một thư mục mới. mkdir pdfGenerator && cd pdfGenerator
Tạo một ứng dụng React mới với. ứng dụng khách tạo phản ứng. .
Tạo một máy chủ Express với. máy chủ mkdir && máy chủ cd && chỉ mục cảm ứng. js && npm init. .
Mở hai thiết bị đầu cuối khác nhau. Đầu tiên. đi vào thư mục máy khách và chạy npm start

Làm cách nào để tạo PDF của trang web bằng nút js?

HTML cơ bản sang PDF. const fs = yêu cầu('fs'); . thế hệ const = { html. 'mẫu. html', };

Làm cách nào để chuyển đổi văn bản thành PDF trong nút js?

Các bước chuyển đổi TXT sang PDF qua nút. .
Tải Khóa và bí mật ứng dụng của bạn từ tệp JSON hoặc đặt thông tin đăng nhập theo cách khác
Tạo một đối tượng để kết nối với Cloud API
Tải lên tệp tài liệu của bạn
Thực hiện chuyển đổi bằng putMarkdownInStorageToPdf
Tải xuống kết quả nếu cần