survey-pdf
Version:
Renders JSON-driven SurveyJS forms and their responses as PDF documents in the browser or Node.js: fillable interactive PDF forms (AcroForm) or static printouts.
194 lines (185 loc) • 7.42 kB
JavaScript
/*!
* surveyjs - SurveyJS PDF library v3.0.2
* Copyright (c) 2015-2026 Devsoft Baltic OÜ - http://surveyjs.io/
* License: MIT (http://www.opensource.org/licenses/mit-license.php)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('fs')) :
typeof define === 'function' && define.amd ? define(['exports', 'fs'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.PDFFormFiller = {}, global.fs));
})(this, (function (exports, fs) { 'use strict';
class FormsMap {
constructor(map) {
this.map = map;
this.res = {};
}
mapDataRecursive(data, map) {
if (data === undefined || data === null)
return;
if (typeof (data) !== 'object') {
if (Array.isArray(map)) {
map.forEach((m) => {
this.mapDataRecursive(data, m);
});
return;
}
if (typeof (map) !== 'object') {
this.res[map] = data;
}
else {
this.res[map[data].field] = map[data].value;
}
return;
}
if (Array.isArray(data)) {
data.forEach((d, i) => {
this.mapDataRecursive(d, Array.isArray(map) ? map[i] : map);
});
return;
}
Object.keys(data).forEach(key => {
this.mapDataRecursive(data[key], map[key]);
});
}
mapData(data) {
this.res = {};
this.mapDataRecursive(data, this.map);
return this.res;
}
}
/**
* A base class for the `PDFFormFiller` plugin.
* @since 2.0.9
*/
class PDFFormFillerBase {
constructor(options) {
if (options) {
this.data = options.data;
this.fieldMap = options.fieldMap;
this.pdfTemplate = options.pdfTemplate;
this.pdfLibraryAdapter = options.pdfLibraryAdapter;
}
}
async getPDFBytes() {
var _a;
const map = new FormsMap(this.fieldMap);
const plainData = map.mapData(this.data);
return await ((_a = this.pdfLibraryAdapter) === null || _a === void 0 ? void 0 : _a.fillForm(this.pdfTemplate, plainData));
}
fromCharCode(array) {
const strings = [];
const chunkSize = 0xffff;
for (let i = 0; i < array.length; i += chunkSize) {
const chunk = Array.prototype.slice.call(array, i, i + chunkSize);
strings.push(String.fromCharCode.apply(null, chunk));
}
return strings.join('');
}
/**
* An asynchronous method that allows you to get PDF content in different formats.
* @param type *(Optional)* One of `"blob"`, `"bloburl"`, `"dataurlstring"`. Do not specify this parameter if you want to get raw PDF content as a string value.
* @since 2.0.9
*/
async raw(type) {
const pdfBytes = await this.getPDFBytes();
if (!type || !pdfBytes)
return pdfBytes;
if (type == 'dataurlstring')
return 'data:application/pdf;base64,' + btoa(this.fromCharCode(pdfBytes));
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
if (type == 'blob')
return blob;
if (type == 'bloburl')
return URL.createObjectURL(blob);
return pdfBytes;
}
/**
* An asynchronous method that starts to download the filled PDF form in the web browser.
*
* [View pdf-lib Demo](https://surveyjs.io/pdf-generator/examples/map-survey-responses-to-pdf-fields-using-pdflib/ (linkStyle))
*
* [View PDF.js Demo](https://surveyjs.io/pdf-generator/examples/fill-in-pdf-form-fields-with-dynamic-survey-data-using-pdfjs/ (linkStyle))
* @param fileName *(Optional)* A file name with the ".pdf" extension. Default value: `"FilledForm.pdf"`.
* @since 2.0.9
*/
async save(fileName = 'FilledForm.pdf') {
const pdfBytes = await this.getPDFBytes();
if (!pdfBytes)
return;
await this.saveToFile(pdfBytes, fileName);
}
}
class PDFFormFiller extends PDFFormFillerBase {
async saveToFile(pdfBytes, fileName) {
return new Promise((resolve, reject) => {
fs.writeFile(fileName, pdfBytes, (err) => {
if (err)
reject(err);
else
resolve();
});
});
}
}
class PDFLibAdapter {
constructor(pdfLibrary) {
this.pdfLibrary = pdfLibrary;
}
async fillForm(template, data) {
const { PDFDocument, PDFTextField, PDFCheckBox, PDFRadioGroup, PDFDropdown } = this.pdfLibrary;
const pdfDoc = await PDFDocument.load(template);
const form = pdfDoc.getForm();
const fields = form.getFields();
fields.forEach((field) => {
const fieldName = field.getName();
const value = data[fieldName];
if (value === null || value === undefined)
return;
if (field instanceof PDFTextField) {
field.setText(value);
}
else if (field instanceof PDFCheckBox) {
if (value)
field.check();
else
field.uncheck();
}
else if (field instanceof PDFRadioGroup || field instanceof PDFDropdown) {
field.select(value.toString());
}
});
return await pdfDoc.save();
}
}
class PDFJSAdapter {
constructor(pdfLibrary) {
this.pdfLibrary = pdfLibrary;
}
async fillForm(template, data) {
const pdfjsLib = this.pdfLibrary;
const doc = await pdfjsLib.getDocument(template).promise;
const numPages = doc.numPages;
// Process all pages
for (let pageNum = 1; pageNum <= numPages; pageNum++) {
const page = await doc.getPage(pageNum);
const annotations = await page.getAnnotations();
annotations.forEach((field) => {
if (field.fieldType == undefined)
return;
const value = data[field.fieldName];
if (value) {
if (field.radioButton && field.buttonValue != value) {
return;
}
doc.annotationStorage.setValue(field.id, { value });
}
});
}
return await doc.saveDocument();
}
}
exports.PDFFormFiller = PDFFormFiller;
exports.PDFJSAdapter = PDFJSAdapter;
exports.PDFLibAdapter = PDFLibAdapter;
}));
//# sourceMappingURL=pdf-form-filler.node.js.map