了解更多信息,請訪問 pdf-lib.js.org
pdf-lib
的創建是為了解決 JavaScript 生態系統缺乏對 PDF 操作(尤其是 PDF修改)的強大支援的問題。
pdf-lib
的兩個顯著特徵是:
還有其他優秀的開源 JavaScript PDF 函式庫可用。然而,他們中的大多數只能創建文檔,不能修改現有文檔。其中許多只能在特定環境下工作。
此範例產生此 PDF。
嘗試 JSFiddle 演示
import { PDFDocument , StandardFonts , rgb } from 'pdf-lib'
// Create a new PDFDocument
const pdfDoc = await PDFDocument . create ( )
// Embed the Times Roman font
const timesRomanFont = await pdfDoc . embedFont ( StandardFonts . TimesRoman )
// Add a blank page to the document
const page = pdfDoc . addPage ( )
// Get the width and height of the page
const { width , height } = page . getSize ( )
// Draw a string of text toward the top of the page
const fontSize = 30
page . drawText ( 'Creating PDFs in JavaScript is awesome!' , {
x : 50 ,
y : height - 4 * fontSize ,
size : fontSize ,
font : timesRomanFont ,
color : rgb ( 0 , 0.53 , 0.71 ) ,
} )
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc . save ( )
// For example, `pdfBytes` can be:
// • Written to a file in Node
// • Downloaded from the browser
// • Rendered in an <iframe>
此範例產生此 PDF (當此 PDF 用於existingPdfBytes
變數時)。
嘗試 JSFiddle 演示
import { degrees , PDFDocument , rgb , StandardFonts } from 'pdf-lib' ;
// This should be a Uint8Array or ArrayBuffer
// This data can be obtained in a number of different ways
// If your running in a Node environment, you could use fs.readFile()
// In the browser, you could make a fetch() call and use res.arrayBuffer()
const existingPdfBytes = ...
// Load a PDFDocument from the existing PDF bytes
const pdfDoc = await PDFDocument . load ( existingPdfBytes )
// Embed the Helvetica font
const helveticaFont = await pdfDoc . embedFont ( StandardFonts . Helvetica )
// Get the first page of the document
const pages = pdfDoc . getPages ( )
const firstPage = pages [ 0 ]
// Get the width and height of the first page
const { width , height } = firstPage . getSize ( )
// Draw a string of text diagonally across the first page
firstPage . drawText ( 'This text was added with JavaScript!' , {
x : 5 ,
y : height / 2 + 300 ,
size : 50 ,
font : helveticaFont ,
color : rgb ( 0.95 , 0.1 , 0.1 ) ,
rotate : degrees ( - 45 ) ,
} )
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc . save ( )
// For example, `pdfBytes` can be:
// • Written to a file in Node
// • Downloaded from the browser
// • Rendered in an <iframe>
此範例產生此 PDF。
嘗試 JSFiddle 演示
另請參閱建立和填寫表格
import { PDFDocument } from 'pdf-lib'
// Create a new PDFDocument
const pdfDoc = await PDFDocument . create ( )
// Add a blank page to the document
const page = pdfDoc . addPage ( [ 550 , 750 ] )
// Get the form so we can add fields to it
const form = pdfDoc . getForm ( )
// Add the superhero text field and description
page . drawText ( 'Enter your favorite superhero:' , { x : 50 , y : 700 , size : 20 } )
const superheroField = form . createTextField ( 'favorite.superhero' )
superheroField . setText ( 'One Punch Man' )
superheroField . addToPage ( page , { x : 55 , y : 640 } )
// Add the rocket radio group, labels, and description
page . drawText ( 'Select your favorite rocket:' , { x : 50 , y : 600 , size : 20 } )
page . drawText ( 'Falcon Heavy' , { x : 120 , y : 560 , size : 18 } )
page . drawText ( 'Saturn IV' , { x : 120 , y : 500 , size : 18 } )
page . drawText ( 'Delta IV Heavy' , { x : 340 , y : 560 , size : 18 } )
page . drawText ( 'Space Launch System' , { x : 340 , y : 500 , size : 18 } )
const rocketField = form . createRadioGroup ( 'favorite.rocket' )
rocketField . addOptionToPage ( 'Falcon Heavy' , page , { x : 55 , y : 540 } )
rocketField . addOptionToPage ( 'Saturn IV' , page , { x : 55 , y : 480 } )
rocketField . addOptionToPage ( 'Delta IV Heavy' , page , { x : 275 , y : 540 } )
rocketField . addOptionToPage ( 'Space Launch System' , page , { x : 275 , y : 480 } )
rocketField . select ( 'Saturn IV' )
// Add the gundam check boxes, labels, and description
page . drawText ( 'Select your favorite gundams:' , { x : 50 , y : 440 , size : 20 } )
page . drawText ( 'Exia' , { x : 120 , y : 400 , size : 18 } )
page . drawText ( 'Kyrios' , { x : 120 , y : 340 , size : 18 } )
page . drawText ( 'Virtue' , { x : 340 , y : 400 , size : 18 } )
page . drawText ( 'Dynames' , { x : 340 , y : 340 , size : 18 } )
const exiaField = form . createCheckBox ( 'gundam.exia' )
const kyriosField = form . createCheckBox ( 'gundam.kyrios' )
const virtueField = form . createCheckBox ( 'gundam.virtue' )
const dynamesField = form . createCheckBox ( 'gundam.dynames' )
exiaField . addToPage ( page , { x : 55 , y : 380 } )
kyriosField . addToPage ( page , { x : 55 , y : 320 } )
virtueField . addToPage ( page , { x : 275 , y : 380 } )
dynamesField . addToPage ( page , { x : 275 , y : 320 } )
exiaField . check ( )
dynamesField . check ( )
// Add the planet dropdown and description
page . drawText ( 'Select your favorite planet*:' , { x : 50 , y : 280 , size : 20 } )
const planetsField = form . createDropdown ( 'favorite.planet' )
planetsField . addOptions ( [ 'Venus' , 'Earth' , 'Mars' , 'Pluto' ] )
planetsField . select ( 'Pluto' )
planetsField . addToPage ( page , { x : 55 , y : 220 } )
// Add the person option list and description
page . drawText ( 'Select your favorite person:' , { x : 50 , y : 180 , size : 18 } )
const personField = form . createOptionList ( 'favorite.person' )
personField . addOptions ( [
'Julius Caesar' ,
'Ada Lovelace' ,
'Cleopatra' ,
'Aaron Burr' ,
'Mark Antony' ,
] )
personField . select ( 'Ada Lovelace' )
personField . addToPage ( page , { x : 55 , y : 70 } )
// Just saying...
page . drawText ( `* Pluto should be a planet too!` , { x : 15 , y : 15 , size : 15 } )
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc . save ( )
// For example, `pdfBytes` can be:
// • Written to a file in Node
// • Downloaded from the browser
// • Rendered in an <iframe>
此範例產生此 PDF (當此 PDF 用於formPdfBytes
變數時,此影像用於marioImageBytes
變量,此影像用於emblemImageBytes
變數)。
嘗試 JSFiddle 演示
另請參閱建立和填寫表格
import { PDFDocument } from 'pdf-lib'
// These should be Uint8Arrays or ArrayBuffers
// This data can be obtained in a number of different ways
// If your running in a Node environment, you could use fs.readFile()
// In the browser, you could make a fetch() call and use res.arrayBuffer()
const formPdfBytes = ...
const marioImageBytes = ...
const emblemImageBytes = ...
// Load a PDF with form fields
const pdfDoc = await PDFDocument . load ( formPdfBytes )
// Embed the Mario and emblem images
const marioImage = await pdfDoc . embedPng ( marioImageBytes )
const emblemImage = await pdfDoc . embedPng ( emblemImageBytes )
// Get the form containing all the fields
const form = pdfDoc . getForm ( )
// Get all fields in the PDF by their names
const nameField = form . getTextField ( 'CharacterName 2' )
const ageField = form . getTextField ( 'Age' )
const heightField = form . getTextField ( 'Height' )
const weightField = form . getTextField ( 'Weight' )
const eyesField = form . getTextField ( 'Eyes' )
const skinField = form . getTextField ( 'Skin' )
const hairField = form . getTextField ( 'Hair' )
const alliesField = form . getTextField ( 'Allies' )
const factionField = form . getTextField ( 'FactionName' )
const backstoryField = form . getTextField ( 'Backstory' )
const traitsField = form . getTextField ( 'Feat+Traits' )
const treasureField = form . getTextField ( 'Treasure' )
const characterImageField = form . getButton ( 'CHARACTER IMAGE' )
const factionImageField = form . getTextField ( 'Faction Symbol Image' )
// Fill in the basic info fields
nameField . setText ( 'Mario' )
ageField . setText ( '24 years' )
heightField . setText ( `5' 1"` )
weightField . setText ( '196 lbs' )
eyesField . setText ( 'blue' )
skinField . setText ( 'white' )
hairField . setText ( 'brown' )
// Fill the character image field with our Mario image
characterImageField . setImage ( marioImage )
// Fill in the allies field
alliesField . setText (
[
`Allies:` ,
` • Princess Daisy` ,
` • Princess Peach` ,
` • Rosalina` ,
` • Geno` ,
` • Luigi` ,
` • Donkey Kong` ,
` • Yoshi` ,
` • Diddy Kong` ,
`` ,
`Organizations:` ,
` • Italian Plumbers Association` ,
] . join ( 'n' ) ,
)
// Fill in the faction name field
factionField . setText ( `Mario's Emblem` )
// Fill the faction image field with our emblem image
factionImageField . setImage ( emblemImage )
// Fill in the backstory field
backstoryField . setText (
`Mario is a fictional character in the Mario video game franchise, owned by Nintendo and created by Japanese video game designer Shigeru Miyamoto. Serving as the company's mascot and the eponymous protagonist of the series, Mario has appeared in over 200 video games since his creation. Depicted as a short, pudgy, Italian plumber who resides in the Mushroom Kingdom, his adventures generally center upon rescuing Princess Peach from the Koopa villain Bowser. His younger brother and sidekick is Luigi.` ,
)
// Fill in the traits field
traitsField . setText (
[
`Mario can use three basic three power-ups:` ,
` • the Super Mushroom, which causes Mario to grow larger` ,
` • the Fire Flower, which allows Mario to throw fireballs` ,
` • the Starman, which gives Mario temporary invincibility` ,
] . join ( 'n' ) ,
)
// Fill in the treasure field
treasureField . setText ( [ '• Gold coins' , '• Treasure chests' ] . join ( 'n' ) )
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc . save ( )
// For example, `pdfBytes` can be:
// • Written to a file in Node
// • Downloaded from the browser
// • Rendered in an <iframe>
此範例產生此 PDF (當此 PDF 用於formPdfBytes
變數時)。
嘗試 JSFiddle 演示
import { PDFDocument } from 'pdf-lib'
// This should be a Uint8Array or ArrayBuffer
// This data can be obtained in a number of different ways
// If your running in a Node environment, you could use fs.readFile()
// In the browser, you could make a fetch() call and use res.arrayBuffer()
const formPdfBytes = ...
// Load a PDF with form fields
const pdfDoc = await PDFDocument . load ( formPdfBytes )
// Get the form containing all the fields
const form = pdfDoc . getForm ( )
// Fill the form's fields
form . getTextField ( 'Text1' ) . setText ( 'Some Text' ) ;
form . getRadioGroup ( 'Group2' ) . select ( 'Choice1' ) ;
form . getRadioGroup ( 'Group3' ) . select ( 'Choice3' ) ;
form . getRadioGroup ( 'Group4' ) . select ( 'Choice1' ) ;
form . getCheckBox ( 'Check Box3' ) . check ( ) ;
form . getCheckBox ( 'Check Box4' ) . uncheck ( ) ;
form . getDropdown ( 'Dropdown7' ) . select ( 'Infinity' ) ;
form . getOptionList ( 'List Box6' ) . select ( 'Honda' ) ;
// Flatten the form's fields
form . flatten ( ) ;
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc . save ( )
// For example, `pdfBytes` can be:
// • Written to a file in Node
// • Downloaded from the browser
// • Rendered in an <iframe>
此範例產生此 PDF (當此 PDF 用於firstDonorPdfBytes
變數且此 PDF 用於secondDonorPdfBytes
變數時)。
嘗試 JSFiddle 演示
import { PDFDocument } from 'pdf-lib'
// Create a new PDFDocument
const pdfDoc = await PDFDocument . create ( )
// These should be Uint8Arrays or ArrayBuffers
// This data can be obtained in a number of different ways
// If your running in a Node environment, you could use fs.readFile()
// In the browser, you could make a fetch() call and use res.arrayBuffer()
const firstDonorPdfBytes = ...
const secondDonorPdfBytes = ...
// Load a PDFDocument from each of the existing PDFs
const firstDonorPdfDoc = await PDFDocument . load ( firstDonorPdfBytes )
const secondDonorPdfDoc = await PDFDocument . load ( secondDonorPdfBytes )
// Copy the 1st page from the first donor document, and
// the 743rd page from the second donor document
const [ firstDonorPage ] = await pdfDoc . copyPages ( firstDonorPdfDoc , [ 0 ] )
const [ secondDonorPage ] = await pdfDoc . copyPages ( secondDonorPdfDoc , [ 742 ] )
// Add the first copied page
pdfDoc . addPage ( firstDonorPage )
// Insert the second copied page to index 0, so it will be the
// first page in `pdfDoc`
pdfDoc . insertPage ( 0 , secondDonorPage )
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc . save ( )
// For example, `pdfBytes` can be:
// • Written to a file in Node
// • Downloaded from the browser
// • Rendered in an <iframe>
此範例產生此 PDF (當此圖像用於jpgImageBytes
變數且此圖像用於pngImageBytes
變數時)。
嘗試 JSFiddle 演示
import { PDFDocument } from 'pdf-lib'
// These should be Uint8Arrays or ArrayBuffers
// This data can be obtained in a number of different ways
// If your running in a Node environment, you could use fs.readFile()
// In the browser, you could make a fetch() call and use res.arrayBuffer()
const jpgImageBytes = ...
const pngImageBytes = ...
// Create a new PDFDocument
const pdfDoc = await PDFDocument . create ( )
// Embed the JPG image bytes and PNG image bytes
const jpgImage = await pdfDoc . embedJpg ( jpgImageBytes )
const pngImage = await pdfDoc . embedPng ( pngImageBytes )
// Get the width/height of the JPG image scaled down to 25% of its original size
const jpgDims = jpgImage . scale ( 0.25 )
// Get the width/height of the PNG image scaled down to 50% of its original size
const pngDims = pngImage . scale ( 0.5 )
// Add a blank page to the document
const page = pdfDoc . addPage ( )
// Draw the JPG image in the center of the page
page . drawImage ( jpgImage , {
x : page . getWidth ( ) / 2 - jpgDims . width / 2 ,
y : page . getHeight ( ) / 2 - jpgDims . height / 2 ,
width : jpgDims . width ,
height : jpgDims . height ,
} )
// Draw the PNG image near the lower right corner of the JPG image
page . drawImage ( pngImage , {
x : page . getWidth ( ) / 2 - pngDims . width / 2 + 75 ,
y : page . getHeight ( ) / 2 - pngDims . height ,
width : pngDims . width ,
height : pngDims . height ,
} )
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc . save ( )
// For example, `pdfBytes` can be:
// • Written to a file in Node
// • Downloaded from the browser
// • Rendered in an <iframe>
此範例產生此 PDF (當此 PDF 用於americanFlagPdfBytes
變數且此 PDF 用於usConstitutionPdfBytes
變數時)。
嘗試 JSFiddle 演示
import { PDFDocument } from 'pdf-lib'
// These should be Uint8Arrays or ArrayBuffers
// This data can be obtained in a number of different ways
// If your running in a Node environment, you could use fs.readFile()
// In the browser, you could make a fetch() call and use res.arrayBuffer()
const americanFlagPdfBytes = ...
const usConstitutionPdfBytes = ...
// Create a new PDFDocument
const pdfDoc = await PDFDocument . create ( )
// Embed the American flag PDF bytes
const [ americanFlag ] = await pdfDoc . embedPdf ( americanFlagPdfBytes )
// Load the U.S. constitution PDF bytes
const usConstitutionPdf = await PDFDocument . load ( usConstitutionPdfBytes )
// Embed the second page of the constitution and clip the preamble
const preamble = await pdfDoc . embedPage ( usConstitutionPdf . getPages ( ) [ 1 ] , {
left : 55 ,
bottom : 485 ,
right : 300 ,
top : 575 ,
} )
// Get the width/height of the American flag PDF scaled down to 30% of
// its original size
const americanFlagDims = americanFlag . scale ( 0.3 )
// Get the width/height of the preamble clipping scaled up to 225% of
// its original size
const preambleDims = preamble . scale ( 2.25 )
// Add a blank page to the document
const page = pdfDoc . addPage ( )
// Draw the American flag image in the center top of the page
page . drawPage ( americanFlag , {
... americanFlagDims ,
x : page . getWidth ( ) / 2 - americanFlagDims . width / 2 ,
y : page . getHeight ( ) - americanFlagDims . height - 150 ,
} )
// Draw the preamble clipping in the center bottom of the page
page . drawPage ( preamble , {
... preambleDims ,
x : page . getWidth ( ) / 2 - preambleDims . width / 2 ,
y : page . getHeight ( ) / 2 - preambleDims . height / 2 - 50 ,
} )
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc . save ( )
// For example, `pdfBytes` can be:
// • Written to a file in Node
// • Downloaded from the browser
// • Rendered in an <iframe>
pdf-lib
依賴一個姊妹模組來支援嵌入自訂字體: @pdf-lib/fontkit
。在嵌入自訂字體之前,您必須將@pdf-lib/fontkit
模組新增至您的專案中並使用pdfDoc.registerFontkit(...)
註冊它。
有關將
@pdf-lib/fontkit
安裝為 UMD 或 NPM 模組的詳細安裝說明,請參閱下文。
此範例產生此 PDF (當此字體用於fontBytes
變數時)。
嘗試 JSFiddle 演示
import { PDFDocument , rgb } from 'pdf-lib'
import fontkit from '@pdf-lib/fontkit'
// This should be a Uint8Array or ArrayBuffer
// This data can be obtained in a number of different ways
// If you're running in a Node environment, you could use fs.readFile()
// In the browser, you could make a fetch() call and use res.arrayBuffer()
const fontBytes = ...
// Create a new PDFDocument
const pdfDoc = await PDFDocument . create ( )
// Register the `fontkit` instance
pdfDoc . registerFontkit ( fontkit )
// Embed our custom font in the document
const customFont = await pdfDoc . embedFont ( fontBytes )
// Add a blank page to the document
const page = pdfDoc . addPage ( )
// Create a string of text and measure its width and height in our custom font
const text = 'This is text in an embedded font!'
const textSize = 35
const textWidth = customFont . widthOfTextAtSize ( text , textSize )
const textHeight = customFont . heightAtSize ( textSize )
// Draw the string of text on the page
page . drawText ( text , {
x : 40 ,
y : 450 ,
size : textSize ,
font : customFont ,
color : rgb ( 0 , 0.53 , 0.71 ) ,
} )
// Draw a box around the string of text
page . drawRectangle ( {
x : 40 ,
y : 450 ,
width : textWidth ,
height : textHeight ,
borderColor : rgb ( 1 , 0 , 0 ) ,
borderWidth : 1.5 ,
} )
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc . save ( )
// For example, `pdfBytes` can be:
// • Written to a file in Node
// • Downloaded from the browser
// • Rendered in an <iframe>
此範例產生此 PDF (當此圖像用於jpgAttachmentBytes
變數且此 PDF 用於pdfAttachmentBytes
變數時)。
嘗試 JSFiddle 演示
import { PDFDocument } from 'pdf-lib'
// These should be Uint8Arrays or ArrayBuffers
// This data can be obtained in a number of different ways
// If your running in a Node environment, you could use fs.readFile()
// In the browser, you could make a fetch() call and use res.arrayBuffer()
const jpgAttachmentBytes = ...
const pdfAttachmentBytes = ...
// Create a new PDFDocument
const pdfDoc = await PDFDocument . create ( )
// Add the JPG attachment
await pdfDoc . attach ( jpgAttachmentBytes , 'cat_riding_unicorn.jpg' , {
mimeType : 'image/jpeg' ,
description : 'Cool cat riding a unicorn! ???️' ,
creationDate : new Date ( '2019/12/01' ) ,
modificationDate : new Date ( '2020/04/19' ) ,
} )
// Add the PDF attachment
await pdfDoc . attach ( pdfAttachmentBytes , 'us_constitution.pdf' , {
mimeType : 'application/pdf' ,
description : 'Constitution of the United States ???' ,
creationDate : new Date ( '1787/09/17' ) ,
modificationDate : new Date ( '1992/05/07' ) ,
} )
// Add a page with some text
const page = pdfDoc . addPage ( ) ;
page . drawText ( 'This PDF has two attachments' , { x : 135 , y : 415 } )
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc . save ( )
// For example, `pdfBytes` can be:
// • Written to a file in Node
// • Downloaded from the browser
// • Rendered in an <iframe>
此範例產生此 PDF 。
嘗試 JSFiddle 演示
import { PDFDocument , StandardFonts } from 'pdf-lib'
// Create a new PDFDocument
const pdfDoc = await PDFDocument . create ( )
// Embed the Times Roman font
const timesRomanFont = await pdfDoc . embedFont ( StandardFonts . TimesRoman )
// Add a page and draw some text on it
const page = pdfDoc . addPage ( [ 500 , 600 ] )
page . setFont ( timesRomanFont )
page . drawText ( 'The Life of an Egg' , { x : 60 , y : 500 , size : 50 } )
page . drawText ( 'An Epic Tale of Woe' , { x : 125 , y : 460 , size : 25 } )
// Set all available metadata fields on the PDFDocument. Note that these fields
// are visible in the "Document Properties" section of most PDF readers.
pdfDoc . setTitle ( '? The Life of an Egg ?' )
pdfDoc . setAuthor ( 'Humpty Dumpty' )
pdfDoc . setSubject ( ' An Epic Tale of Woe ' )
pdfDoc . setKeywords ( [ 'eggs' , 'wall' , 'fall' , 'king' , 'horses' , 'men' ] )
pdfDoc . setProducer ( 'PDF App 9000 ?' )
pdfDoc . setCreator ( 'pdf-lib (https://github.com/Hopding/pdf-lib)' )
pdfDoc . setCreationDate ( new Date ( '2018-06-24T01:58:37.228Z' ) )
pdfDoc . setModificationDate ( new Date ( '2019-12-21T07:00:11.000Z' ) )
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc . save ( )
// For example, `pdfBytes` can be:
// • Written to a file in Node
// • Downloaded from the browser
// • Rendered in an <iframe>
嘗試 JSFiddle 演示
import { PDFDocument } from 'pdf-lib'
// This should be a Uint8Array or ArrayBuffer
// This data can be obtained in a number of different ways
// If your running in a Node environment, you could use fs.readFile()
// In the browser, you could make a fetch() call and use res.arrayBuffer()
const existingPdfBytes = ...
// Load a PDFDocument without updating its existing metadata
const pdfDoc = await PDFDocument . load ( existingPdfBytes , {
updateMetadata : false
} )
// Print all available metadata fields
console . log ( 'Title:' , pdfDoc . getTitle ( ) )
console . log ( 'Author:' , pdfDoc . getAuthor ( ) )
console . log ( 'Subject:' , pdfDoc . getSubject ( ) )
console . log ( 'Creator:' , pdfDoc . getCreator ( ) )
console . log ( 'Keywords:' , pdfDoc . getKeywords ( ) )
console . log ( 'Producer:' , pdfDoc . getProducer ( ) )
console . log ( 'Creation Date:' , pdfDoc . getCreationDate ( ) )
console . log ( 'Modification Date:' , pdfDoc . getModificationDate ( ) )
此腳本輸出以下內容(當此 PDF 用於existingPdfBytes
變數時):
Title: Microsoft Word - Basic Curriculum Vitae example.doc
Author: Administrator
Subject: undefined
Creator: PScript5.dll Version 5.2
Keywords: undefined
Producer: Acrobat Distiller 8.1.0 (Windows)
Creation Date: 2010-07-29T14:26:00.000Z
Modification Date: 2010-07-29T14:26:00.000Z
import {
PDFDocument ,
StandardFonts ,
NonFullScreenPageMode ,
ReadingDirection ,
PrintScaling ,
Duplex ,
PDFName ,
} from 'pdf-lib'
// Create a new PDFDocument
const pdfDoc = await PDFDocument . create ( )
// Embed the Times Roman font
const timesRomanFont = await pdfDoc . embedFont ( StandardFonts . TimesRoman )
// Add a page and draw some text on it
const page = pdfDoc . addPage ( [ 500 , 600 ] )
page . setFont ( timesRomanFont )
page . drawText ( 'The Life of an Egg' , { x : 60 , y : 500 , size : 50 } )
page . drawText ( 'An Epic Tale of Woe' , { x : 125 , y : 460 , size : 25 } )
// Set all available viewer preferences on the PDFDocument:
const viewerPrefs = pdfDoc . catalog . getOrCreateViewerPreferences ( )
viewerPrefs . setHideToolbar ( true )
viewerPrefs . setHideMenubar ( true )
viewerPrefs . setHideWindowUI ( true )
viewerPrefs . setFitWindow ( true )
viewerPrefs . setCenterWindow ( true )
viewerPrefs . setDisplayDocTitle ( true )
// Set the PageMode (otherwise setting NonFullScreenPageMode has no meaning)
pdfDoc . catalog . set ( PDFName . of ( 'PageMode' ) , PDFName . of ( 'FullScreen' ) )
// Set what happens when fullScreen is closed
viewerPrefs . setNonFullScreenPageMode ( NonFullScreenPageMode . UseOutlines )
viewerPrefs . setReadingDirection ( ReadingDirection . L2R )
viewerPrefs . setPrintScaling ( PrintScaling . None )
viewerPrefs . setDuplex ( Duplex . DuplexFlipLongEdge )
viewerPrefs . setPickTrayByPDFSize ( true )
// We can set the default print range to only the first page
viewerPrefs . setPrintPageRange ( { start : 0 , end : 0 } )
// Or we can supply noncontiguous ranges (e.g. pages 1, 3, and 5-7)
viewerPrefs . setPrintPageRange ( [
{ start : 0 , end : 0 } ,
{ start : 2 , end : 2 } ,
{ start : 4 , end : 6 } ,
] )
viewerPrefs . setNumCopies ( 2 )
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc . save ( )
// For example, `pdfBytes` can be:
// • Written to a file in Node
// • Downloaded from the browser
// • Rendered in an <iframe>
import { PDFDocument } from 'pdf-lib'
// This should be a Uint8Array or ArrayBuffer
// This data can be obtained in a number of different ways
// If your running in a Node environment, you could use fs.readFile()
// In the browser, you could make a fetch() call and use res.arrayBuffer()
const existingPdfBytes = ...
// Load a PDFDocument without updating its existing metadata
const pdfDoc = await PDFDocument . load ( existingPdfBytes )
const viewerPrefs = pdfDoc . catalog . getOrCreateViewerPreferences ( )
// Print all available viewer preference fields
console . log ( 'HideToolbar:' , viewerPrefs . getHideToolbar ( ) )
console . log ( 'HideMenubar:' , viewerPrefs . getHideMenubar ( ) )
console . log ( 'HideWindowUI:' , viewerPrefs . getHideWindowUI ( ) )
console . log ( 'FitWindow:' , viewerPrefs . getFitWindow ( ) )
console . log ( 'CenterWindow:' , viewerPrefs . getCenterWindow ( ) )
console . log ( 'DisplayDocTitle:' , viewerPrefs . getDisplayDocTitle ( ) )
console . log ( 'NonFullScreenPageMode:' , viewerPrefs . getNonFullScreenPageMode ( ) )
console . log ( 'ReadingDirection:' , viewerPrefs . getReadingDirection ( ) )
console . log ( 'PrintScaling:' , viewerPrefs . getPrintScaling ( ) )
console . log ( 'Duplex:' , viewerPrefs . getDuplex ( ) )
console . log ( 'PickTrayByPDFSize:' , viewerPrefs . getPickTrayByPDFSize ( ) )
console . log ( 'PrintPageRange:' , viewerPrefs . getPrintPageRange ( ) )
console . log ( 'NumCopies:' , viewerPrefs . getNumCopies ( ) )
此腳本輸出以下內容(當此 PDF 用於existingPdfBytes
變數時):
HideToolbar: true
HideMenubar: true
HideWindowUI: false
FitWindow: true
CenterWindow: true
DisplayDocTitle: true
NonFullScreenPageMode: UseNone
ReadingDirection: R2L
PrintScaling: None
Duplex: DuplexFlipLongEdge
PickTrayByPDFSize: true
PrintPageRange: [ { start: 1, end: 1 }, { start: 3, end: 4 } ]
NumCopies: 2
此範例產生此 PDF 。
嘗試 JSFiddle 演示
import { PDFDocument , rgb } from 'pdf-lib'
// SVG path for a wavy line
const svgPath =
'M 0,20 L 100,160 Q 130,200 150,120 C 190,-40 200,200 300,150 L 400,90'
// Create a new PDFDocument
const pdfDoc = await PDFDocument . create ( )
// Add a blank page to the document
const page = pdfDoc . addPage ( )
page . moveTo ( 100 , page . getHeight ( ) - 5 )
// Draw the SVG path as a black line
page . moveDown ( 25 )
page . drawSvgPath ( svgPath )
// Draw the SVG path as a thick green line
page . moveDown ( 200 )
page . drawSvgPath ( svgPath , { borderColor : rgb ( 0 , 1 , 0 ) , borderWidth : 5 } )
// Draw the SVG path and fill it with red
page . moveDown ( 200 )
page . drawSvgPath ( svgPath , { color : rgb ( 1 , 0 , 0 ) } )
// Draw the SVG path at 50% of its original size
page . moveDown ( 200 )
page . drawSvgPath ( svgPath , { scale : 0.5 } )
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc . save ( )
// For example, `pdfBytes` can be:
// • Written to a file in Node
// • Downloaded from the browser
// • Rendered in an <iframe>
pdf-lib
完全支援令人興奮的新 Deno 運行時!所有使用範例都適用於 Deno。您唯一需要做的就是更改pdf-lib
和@pdf-lib/fontkit
的導入以使用 Skypack CDN,因為 Deno 要求透過 URL 引用所有模組。
另請參閱如何使用 pdf-lib 在 Deno 中建立和修改 PDF 文件
以下是為 Deno 修改的建立文件範例:
import {
PDFDocument ,
StandardFonts ,
rgb ,
} from 'https://cdn.skypack.dev/pdf-lib@^1.11.1?dts' ;
const pdfDoc = await PDFDocument . create ( ) ;
const timesRomanFont = await pdfDoc . embedFont ( StandardFonts . TimesRoman ) ;
const page = pdfDoc . addPage ( ) ;
const { width , height } = page . getSize ( ) ;
const fontSize = 30 ;
page . drawText ( 'Creating PDFs in JavaScript is awesome!' , {
x : 50 ,
y : height - 4 * fontSize ,
size : fontSize ,
font : timesRomanFont ,
color : rgb ( 0 , 0.53 , 0.71 ) ,
} ) ;
const pdfBytes = await pdfDoc . save ( ) ;
await Deno . writeFile ( 'out.pdf' , pdfBytes ) ;
如果將此腳本儲存為create-document.ts
,則可以使用 Deno 透過以下命令執行它:
deno run --allow-write create-document.ts
產生的out.pdf
檔案將類似於此 PDF。
以下是一個稍微複雜的範例,示範如何在 Deno 中嵌入字體和測量文字:
import {
degrees ,
PDFDocument ,
rgb ,
StandardFonts ,
} from 'https://cdn.skypack.dev/pdf-lib@^1.11.1?dts' ;
import fontkit from 'https://cdn.skypack.dev/@pdf-lib/fontkit@^1.0.0?dts' ;
const url = 'https://pdf-lib.js.org/assets/ubuntu/Ubuntu-R.ttf' ;
const fontBytes = await fetch ( url ) . then ( ( res ) => res . arrayBuffer ( ) ) ;
const pdfDoc = await PDFDocument . create ( ) ;
pdfDoc . registerFontkit ( fontkit ) ;
const customFont = await pdfDoc . embedFont ( fontBytes ) ;
const page = pdfDoc . addPage ( ) ;
const text = 'This is text in an embedded font!' ;
const textSize = 35 ;
const textWidth = customFont . widthOfTextAtSize ( text , textSize ) ;
const textHeight = customFont . heightAtSize ( textSize ) ;
page . drawText ( text , {
x : 40 ,
y : 450 ,
size : textSize ,
font : customFont ,
color : rgb ( 0 , 0.53 , 0.71 ) ,
} ) ;
page . drawRectangle ( {
x : 40 ,
y : 450 ,
width : textWidth ,
height : textHeight ,
borderColor : rgb ( 1 , 0 , 0 ) ,
borderWidth : 1.5 ,
} ) ;
const pdfBytes = await pdfDoc . save ( ) ;
await Deno . writeFile ( 'out.pdf' , pdfBytes ) ;
如果將此腳本儲存為custom-font.ts
,則可以使用下列命令執行它:
deno run --allow-write --allow-net custom-font.ts
產生的out.pdf
檔案將類似於此 PDF。
使用範例提供了簡潔明了的程式碼,展示了pdf-lib
的不同功能。您可以在apps/
目錄中找到完整的工作範例。這些應用程式用於在每次發布之前對pdf-lib
進行手動測試(除了自動測試之外)。
目前有四個應用程式:
node
- 包含 Node 環境中pdf-lib
的測試。當嘗試從檔案系統中使用pdf-lib
儲存/載入 PDF、字體或圖像時,這些測試是一個方便的參考。它們還允許您在不同的檢視器(Acrobat、Preview、Foxit、Chrome、Firefox 等)中快速開啟 PDF,以確保相容性。web
- 包含瀏覽器環境中pdf-lib
的測試。當嘗試在瀏覽器環境中使用pdf-lib
保存/載入 PDF、字體或圖像時,這些測試是一個方便的參考。rn
- 包含 React Native 環境中pdf-lib
的測試。當嘗試在 React Native 環境中使用pdf-lib
保存/載入 PDF、字體或圖像時,這些測試是一個方便的參考。deno
- 包含 Deno 環境中pdf-lib
的測試。當嘗試從檔案系統中使用pdf-lib
儲存/載入 PDF、字體或圖像時,這些測試是一個方便的參考。 要安裝最新的穩定版本:
# With npm
npm install --save pdf-lib
# With yarn
yarn add pdf-lib
這假設您使用 npm 或yarn 作為套件管理器。
您也可以從 unpkg 或 jsDelivr 下載pdf-lib
作為 UMD 模組。 UMD 版本已編譯為 ES5,因此它們應該可以在任何現代瀏覽器中運行。如果您不使用套件管理器或模組捆綁器,UMD 建置非常有用。例如,您可以直接在 HTML 頁面的<script>
標籤中使用它們。
以下版本可用:
注意:如果您在生產中使用 CDN 腳本,則應在 URL 中包含特定版本號,例如:
- https://unpkg.com/[email protected]/dist/pdf-lib.min.js
- https://cdn.jsdelivr.net/npm/[email protected]/dist/pdf-lib.min.js
使用 UMD 建置時,您將可以存取全域window.PDFLib
變數。此變數包含pdf-lib
導出的所有類別和函數。例如:
// NPM module
import { PDFDocument , rgb } from 'pdf-lib' ;
// UMD module
var PDFDocument = PDFLib . PDFDocument ;
var rgb = PDFLib . rgb ;
pdf-lib
依賴一個姊妹模組來支援嵌入自訂字體: @pdf-lib/fontkit
。您必須將@pdf-lib/fontkit
模組新增至您的專案中,並在嵌入自訂字體之前使用pdfDoc.registerFontkit(...)
註冊它(請參閱字體嵌入範例)。預設不包含此模組,因為並非所有使用者都需要它,而且它會增加捆綁包的大小。
安裝該模組很容易。就像pdf-lib
本身一樣, @pdf-lib/fontkit
可以與npm
/ yarn
一起安裝或作為 UMD 模組安裝。
# With npm
npm install --save @pdf-lib/fontkit
# With yarn
yarn add @pdf-lib/fontkit
註冊fontkit
實例:
import { PDFDocument } from 'pdf-lib'
import fontkit from '@pdf-lib/fontkit'
const pdfDoc = await PDFDocument . create ( )
pdfDoc . registerFontkit ( fontkit )
以下版本可用:
注意:如果您在生產中使用 CDN 腳本,則應在 URL 中包含特定版本號,例如:
- https://unpkg.com/@pdf-lib/[email protected]/dist/fontkit.umd.min.js
- https://cdn.jsdelivr.net/npm/@pdf-lib/[email protected]/dist/fontkit.umd.min.js
使用 UMD 建置時,您將可以存取全域window.fontkit
變數。註冊fontkit
實例:
var pdfDoc = await PDFLib . PDFDocument . create ( )
pdfDoc . registerFontkit ( fontkit )
API 文件可在專案網站 https://pdf-lib.js.org/docs/api/ 上找到。
專案網站的儲存庫(以及產生的文件檔案)位於此處:https://github.com/Hopding/pdf-lib-docs。
在處理 PDF 時,您會經常遇到術語「字元編碼」和「字體」。如果您有 Web 開發經驗,您可能想知道為什麼這些如此受歡迎。它們不是您不需要擔心的煩人的細節嗎?難道 PDF 庫和閱讀器不應該能夠像 Web 瀏覽器一樣為您處理所有這些嗎?不幸的是,事實並非如此。 PDF 文件格式的性質使得在處理 PDF 時很難避免考慮字元編碼和字體。
pdf-lib
盡力為您簡化事情。但它無法施展魔法。這意味著您應該了解以下事項:
import { PDFDocument , StandardFonts } from 'pdf-lib'
const pdfDoc = await PDFDocument . create ( )
const courierFont = await pdfDoc . embedFont ( StandardFonts . Courier )
const page = pdfDoc . addPage ( )
page . drawText ( 'Some boring latin text in the Courier font' , {
font : courierFont ,
} )
embedFont
方法。當您嵌入自己的字體時,您可以使用它支援的任何 Unicode 字元。此功能使您擺脫了標準字體的限制。大多數 PDF 檔案使用嵌入字體。您可以嵌入並使用自訂字體,如下所示(另請參閱): import { PDFDocument } from 'pdf-lib'
import fontkit from '@pdf-lib/fontkit'
const url = 'https://pdf-lib.js.org/assets/ubuntu/Ubuntu-R.ttf'
const fontBytes = await fetch ( url ) . then ( ( res ) => res . arrayBuffer ( ) )
const pdfDoc = await PDFDocument . create ( )
pdfDoc . registerFontkit ( fontkit )
const ubuntuFont = await pdfDoc . embedFont ( fontBytes )
const page = pdfDoc . addPage ( )
page . drawText ( 'Some fancy Unicode text in the ŪЬȕǹƚü font' , {
font : ubuntuFont ,
} )
請注意,如果您嘗試使用不支援該字體的字符,則會引發編碼錯誤。例如, Ω
不在 WinAnsi 字元集中。因此嘗試在使用標準 Helvetica 字體的頁面上繪製它會拋出以下錯誤:
Error: WinAnsi cannot encode "Ω" (0x03a9)
at Encoding.encodeUnicodeCodePoint
在 PDF 文件中嵌入字體通常會增加文件的大小。您可以透過對字體進行子集化來減少檔案大小的增加,以便僅嵌入必要的字元。您可以透過將subset
選項設為true
來對字體進行子集化。例如:
const font = await pdfDoc . embedFont ( fontBytes , { subset : true } ) ;
請注意,子集化並不適用於所有字體。有關更多詳細信息,請參閱#207(評論)。
pdf-lib
可以建立、填寫和讀取 PDF 表單欄位。支援以下欄位類型:
有關程式碼範例,請參閱表單建立和表單填寫使用範例。完整範例中的測試 1、14、15、16 和 17 包含用於在各種不同 JS 環境中建立和填寫表單的工作範例程式碼。
重要提示:用於在按鈕、下拉清單、選項清單和文字欄位中顯示文字的預設字體是標準 Helvetica 字體。此字體僅支援拉丁字母表中的字元(有關詳細信息,請參閱字體和 Unicode)。這意味著,如果建立或修改任何這些欄位類型以包含拉丁字母以外的文字(通常是這種情況),您將需要嵌入並使用自訂字體來更新欄位外觀。否則將引發錯誤(可能在儲存PDFDocument
時)。
您可以在填寫表單欄位時使用嵌入字體,如下所示:
import { PDFDocument } from 'pdf-lib' ;
import fontkit from '@pdf-lib/fontkit' ;
// Fetch the PDF with form fields
const formUrl = 'https://pdf-lib.js.org/assets/dod_character.pdf' ;
const formBytes = await fetch ( formUrl ) . then ( ( res ) => res . arrayBuffer ( ) ) ;
// Fetch the Ubuntu font
const fontUrl = 'https://pdf-lib.js.org/assets/ubuntu/Ubuntu-R.ttf' ;
const fontBytes = await fetch ( fontUrl ) . then ( ( res ) => res . arrayBuffer ( ) ) ;
// Load the PDF with form fields
const pdfDoc = await PDFDocument . load ( formBytes ) ;
// Embed the Ubuntu font
pdfDoc . registerFontkit ( fontkit ) ;
const ubuntuFont = await pdfDoc . embedFont ( fontBytes ) ;
// Get two text fields from the form
const form = pdfDoc . getForm ( ) ;
const nameField = form . getTextField ( 'CharacterName 2' ) ;
const ageField = form . getTextField ( 'Age' ) ;
// Fill the text fields with some fancy Unicode characters (outside
// the WinAnsi latin character set)
nameField . setText ( 'Ӎӑȑїõ' ) ;
ageField . setText ( '24 ŷȇȁŗš' ) ;
// **Key Step:** Update the field appearances with the Ubuntu font
form . updateFieldAppearances ( ubuntuFont ) ;
// Save the PDF with filled form fields
const pdfBytes = await pdfDoc . save ( ) ;
可以使用PDFForm
的以下方法存取現有表單欄位:
PDFForm.getButton
PDFForm.getCheckBox
PDFForm.getDropdown
PDFForm.getOptionList
PDFForm.getRadioGroup
PDFForm.getTextField
可以使用PDFForm
的以下方法建立新的表單欄位:
PDFForm.createButton
PDFForm.createCheckBox
PDFForm.createDropdown
PDFForm.createOptionList
PDFForm.createRadioGroup
PDFForm.createTextField
以下是讀取和填充上述PDFField
類別的一些最常用方法:
PDFCheckBox.check
PDFCheckBox.uncheck
PDFCheckBox.isChecked
PDFDropdown.select
PDFDropdown.clear
PDFDropdown.getSelected
PDFDropdown.getOptions
PDFDropdown.addOptions
PDFOptionList.select
PDFOptionList.clear
PDFOptionList.getSelected
PDFOptionList.getOptions
PDFOptionList.addOptions
PDFRadioGroup.select
PDFRadioGroup.clear
PDFRadioGroup.getSelected
PDFRadioGroup.getOptions
PDFRadioGroup.addOptionToPage
PDFTextField.setText
PDFTextField.getText
PDFTextField.setMaxLength
PDFTextField.getMaxLength
PDFTextField.removeMaxLength
pdf-lib
可以提取文字欄位的內容(請參閱PDFTextField.getText
),但它無法提取表單欄位以外的頁面上的純文字。這是一個很難實現的功能,但它在這個庫的範圍內,並且將來可能會添加到pdf-lib
中。請參閱#93、#137、#177、#329 和#380。pdf-lib
可以刪除和編輯文字欄位的內容(請參閱PDFTextField.setText
),但它不提供用於刪除或編輯表單欄位之外頁面上的文字的 API。這也是一個很難實現的功能,但在pdf-lib
的範圍內,並且將來可能會添加。請參閱#93、#137、#177、#329 和#380。pdf-lib
不支援使用 HTML 或 CSS。同樣, pdf-lib
無法將 HTML/CSS 內容嵌入 PDF 中。儘管這樣的功能可能很方便,但實現起來卻非常困難,並且遠遠超出了這個庫的範圍。如果您需要此功能,請考慮使用 Puppeteer。 討論是與我們聊天、提出問題和了解有關 pdf-lib 的更多資訊的最佳場所!
另請參閱 MAINTAINERSHIP.md#communication 和 MAINTAINERSHIP.md#discord。
pdf-lib
目前不支援加密文檔。您不應該將pdf-lib
與加密文件一起使用。但是,這是一個可以添加到pdf-lib
中的功能。如果您發現此功能有幫助,請建立一個問題!
當加密文件傳遞給PDFDocument.load(...)
時,會拋出錯誤:
import { PDFDocument , EncryptedPDFError } from 'pdf-lib'
const encryptedPdfBytes = ...
// Assignment fails. Throws an `EncryptedPDFError`.
const pdfDoc = PDFDocument . load ( encryptedPdfBytes )
這種預設行為通常是您想要的。它允許您輕鬆檢測給定文件是否已加密,並防止您嘗試修改它。但是,如果您確實想要載入文檔,可以使用{ ignoreEncryption: true }
選項:
import { PDFDocument } from 'pdf-lib'
const encryptedPdfBytes = ...
// Assignment succeeds. Does not throw an error.
const pdfDoc = PDFDocument . load ( encryptedPdfBytes , { ignoreEncryption : true } )
請注意,使用此選項不會解密文件。這表示您嘗試對傳回的PDFDocument
進行的任何修改都可能會失敗,或產生意外結果。
您不應該使用此選項。它的存在只是出於向後相容性的原因。
我們歡迎開源社群的貢獻!如果您有興趣為pdf-lib
做出貢獻,請查看 CONTRIBUTING.md 檔案。它包含可協助您在電腦上安裝並執行pdf-lib
資訊。 (我們盡力使這盡可能簡單和快速!)
請參閱 MAINTAINERSHIP.md,以了解有關如何維護此儲存庫以及我們如何使用問題、PR 和討論的詳細資訊。
pdfkit
是 Node 和瀏覽器的 PDF 產生庫。在創建pdf-lib
時,該庫作為參考和存在證明非常有用。 pdfkit
的字體嵌入、PNG 嵌入和 JPG 嵌入程式碼特別有用。pdf.js
是瀏覽器的 PDF 渲染庫。在編寫pdf-lib
的解析器時,該函式庫作為參考非常有用。一些流解碼程式碼直接移植到 TypeScript 以在pdf-lib
中使用。pdfbox
是一個用Java寫的PDF產生和修改函式庫。在為pdf-lib
實作表單建立和填入 API 時,該函式庫是非常寶貴的參考。jspdf
是瀏覽器的 PDF 產生庫。pdfmake
是瀏覽器的 PDF 產生庫。hummus
是一個用於 Node 環境的 PDF 生成和修改庫。 hummus
是 C++ 函式庫的 Node 包裝器,因此它無法在許多 JavaScript 環境中運作 - 例如瀏覽器或 React Native。react-native-pdf-lib
是一個用於 React Native 環境的 PDF 產生和修改函式庫。 react-native-pdf-lib
是 C++ 和 Java 函式庫的包裝器。pdfassembler
是一個用於 Node 和瀏覽器的 PDF 產生和修改庫。它需要一些有關 PDF 文件邏輯結構的知識才能使用。 該儲存庫過去在根目錄中包含一個名為pdf_specification.pdf
的檔案。這是 PDF 1.7 規範的副本,由 Adobe 免費提供。 2021 年 8 月 30 日,我們收到了 DMCA 投訴,要求我們從此儲存庫中刪除該文件。簡單地透過新提交到master
來刪除文件不足以滿足投訴。該檔案需要從儲存庫的 git 歷史記錄中完全刪除。不幸的是,該文件是在兩年多前添加的,這意味著我們必須重寫儲存庫的 git 歷史記錄並強制推送到master
?。
我們刪除了該檔案並使用 BFG Repo-Cleaner 重寫了儲存庫的歷史記錄,如此處所述。為了完全透明,以下是我們運行的確切命令:
$ git clone [email protected]:Hopding/pdf-lib.git
$ cd pdf-lib
$ rm pdf_specification.pdf
$ git commit -am 'Remove pdf_specification.pdf'
$ bfg --delete-files pdf_specification.pdf
$ git reflog expire --expire=now --all && git gc --prune=now --aggressive
$ git push --force
如果您是pdf-lib
的用戶,您不應該在意!繼續像平常一樣使用pdf-lib
嗎? !
如果您是pdf-lib
開發人員(表示您已經分叉了pdf-lib
和/或擁有開放 PR),那麼這確實會對您產生影響。如果您在 2021 年 8 月 30 日之前分叉或克隆了該儲存庫,那麼您的分叉的 git 歷史記錄與該儲存庫的master
分支不同步。不幸的是,這可能會讓您感到頭痛。對不起!我們不想改寫歷史,但確實別無選擇。
需要注意的是,pdf-lib 的源碼根本沒有改變。和 git 歷史重寫之前一模一樣。該儲存庫仍然具有完全相同的提交數量(甚至相同的提交內容,除了添加pdf_specification.pdf
的提交)。發生變化的是這些提交的 SHA。
處理這個事實最簡單的方法是:
請參閱 StackOverflow 答案,以了解 git 歷史記錄重寫的詳細、深入的解釋。
麻省理工學院