How to Burn Annotations to a PDF Page with JavaScript
Burning (flattening) annotations means including them as vector graphics in the content of a PDF page, leaving them uneditable. Dynamsoft Document Viewer is a JavaScript SDK for document scanning and viewing, which can add annotations and export PDF files. In this article, we are going to explore how to use it.
Use Dynamsoft Document Viewer to Open a PDF File with Annotations Support
-
Create a new HTML file with the following template.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Burn PDF Annotation</title> <style> </style> </head> <body> </body> <script> </script> </html>
-
Include Dynamsoft Document Viewer’s files.
<script src="https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@2.1.0/dist/ddv.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@2.1.0/dist/ddv.css">
-
Initialize Dynamsoft Document Viewer with a license. You can apply for one here.
Dynamsoft.DDV.Core.license = "LICENSE-KEY"; Dynamsoft.DDV.Core.engineResourcePath = "https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@2.1.0/dist/engine";// Lead to a folder containing the distributed WASM files await Dynamsoft.DDV.Core.init();
-
Create a new document instance.
const docManager = Dynamsoft.DDV.documentManager; const doc = docManager.createDocument();
-
Create an instance of edit viewer, bind it to a container and use it to view the document we just created. The buttons on it can be configured using a
UIConfig
object. Here, we add the annotation button to enable adding annotations.HTML:
<div id="viewer"></div>
JavaScript:
Dynamsoft.DDV.setProcessingHandler("imageFilter", new Dynamsoft.DDV.ImageFilter()); let uiConfig = { type: "Layout", flexDirection: "column", className: "ddv-edit-viewer-desktop", children: [ { type: "Layout", className: "ddv-edit-viewer-header-desktop", children: [ { type: "Layout", children: [ "ThumbnailSwitch", "FitMode", "DisplayMode", "RotateLeft", "Crop", "Filter", "Undo", "Redo", "DeleteCurrent", "DeleteAll", "Pan", "SeparatorLine", "AnnotationSet" ], enableScroll: true }, { type: "Layout", children: [ { "type": "Pagination", "className": "ddv-edit-viewer-pagination-desktop" }, { type: Dynamsoft.DDV.Elements.Button, className: "ddv-button-download", events: { click: "exportPDFWithOptions", }, }, ] } ] }, "MainView" ] } editViewer = new Dynamsoft.DDV.EditViewer({ uiConfig: uiConfig, container: document.getElementById("viewer") });
CSS:
#viewer { width: 320px; height: 480px; }
-
Use
input
to select an image or PDF file and load it into the document instance.HTML:
<label> Select a file to load: <br/> <input type="file" id="files" name="files" onchange="filesSelected()"/> </label>
JavaScript:
async function filesSelected(){ let filesInput = document.getElementById("files"); let files = filesInput.files; if (files.length>0) { const file = files[0]; const blob = await readFileAsBlob(file); await doc.loadSource(blob); // load the file } } function readFileAsBlob(file){ return new Promise((resolve, reject) => { const fileReader = new FileReader(); fileReader.onload = async function(e){ const response = await fetch(e.target.result); const blob = await response.blob(); resolve(blob); }; fileReader.onerror = function () { reject('oops, something went wrong.'); }; fileReader.readAsDataURL(file); }) }
We’ll be able to see a viewer like the following:
Burn the Annotations and Save the PDF
Dynamsoft Document Viewer supports four ways to handle the PDF annotations:
none
: Discard all the annotationsimage
: Merge all the content into a raster imageflatten
: Flatten all the annotationsannotation
: Save the annotations in an editable form. Individual annotations marked as flattened will still be flattened
We can save the PDF file and burn the annotations using the flatten
option.
let blob = await doc.saveToPdf({
saveAnnotation: "flatten"
})
If we want to keep some annotations editable while making some annotations flattened, we can use the flatten attribute of annotations and save the PDF using the annotation
option.
let annotations = Dynamsoft.DDV.annotationManager.getAnnotationsByDoc(doc.uid);
let annotation = annotations[0];
annotation.flattened = true;
let blob = await doc.saveToPdf({
saveAnnotation: "annotation"
})
How Does it Work Internally
PDF files are described using PostScript. We are going to use some examples to show how the flattening works internally.
A PDF file contains a list of dictionaries and here is an example of a page dictionary:
4 0 obj
<<
/Type/Page % Specifies that this dictionary defines a page.
/Annots[ 8 0 R ] % A list of references to annotation objects on this page.
/Contents 7 0 R % Reference to page content stream.
/MediaBox[ 0 0 147 143.25] % Page dimensions.
% Other page properties
>>
endobj
7 0 obj
<</Filter/FlateDecode/Length 44>>stream
x??41W0 BCc=#S039椝 ?J缫w媹0Tp蒞? 卵
endstream
endobj
The above page has a reference to the following annotation dictionary:
8 0 obj
<<
/Type/Annot
/AP<<
/Contents(annotation)
/CreationDate(D:20241227135119+08'00')
/DA(0.9411764705882353 0.07450980392156863 0.0784313725490196 rg /Helvetica 16 Tf)
/DS(font: 'Helvetica' 16pt; text-align:left; color:#F01314)
/F 4
/IT/FreeTextTypeWriter/M(D:20241227135125+08'00')/NM(m56c4eb9uq)/RC(<?xml version="1.0"?><body xmlns="http://www.w3.org/1999/xhtml" xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/" xfa:APIVersion="Acrobat:18.11.0" xfa:spec="2.0.2" style="font-size:16pt;text-align:left;color:#f01314;font-weight:normal;font-style:normal;font-family:'Helvetica';font-stretch:normal;"><p dir="ltr"><span>annotation</span></p></body>)
/Rect[ 22.1854 112.467 98.423 129.217]
/Subj()
/Subtype
/FreeText
/T()
>>
endobj
After flattening, the page dictionary will become the following. It no longer has the annotation node and a node of the converted annotation graphics is appended to its content.
4 0 obj
<<
/Type/Page
/Contents 13 0 R
/MediaBox[ 0 0 147 143.25]
>>
endobj
7 0 obj
<</Filter/FlateDecode/Length 48>>stream
x???41W0 BCc=#S039椝 ?J缫w媹0Tp蒞溻
靔 ?
endstream
endobj
13 0 obj
[ 7 0 R 14 0 R ]
endobj
14 0 obj
<</Filter/FlateDecode/Length 29>>stream
x?T0T0 B櫆珷镦b犩挴 M+?
endstream
endobj
Source Code
Get the source code of the demo to have a try: