UNPKG

@cantoo/pdf-lib

Version:

Create and modify PDF files with JavaScript

127 lines 6.17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createPDFAcroField = exports.createPDFAcroFields = void 0; const tslib_1 = require("tslib"); const PDFNumber_1 = tslib_1.__importDefault(require("../objects/PDFNumber")); const PDFDict_1 = tslib_1.__importDefault(require("../objects/PDFDict")); const PDFName_1 = tslib_1.__importDefault(require("../objects/PDFName")); const PDFArray_1 = tslib_1.__importDefault(require("../objects/PDFArray")); const PDFRef_1 = tslib_1.__importDefault(require("../objects/PDFRef")); const PDFAcroTerminal_1 = tslib_1.__importDefault(require("./PDFAcroTerminal")); const PDFAcroNonTerminal_1 = tslib_1.__importDefault(require("./PDFAcroNonTerminal")); const PDFAcroSignature_1 = tslib_1.__importDefault(require("./PDFAcroSignature")); const PDFAcroText_1 = tslib_1.__importDefault(require("./PDFAcroText")); const PDFAcroPushButton_1 = tslib_1.__importDefault(require("./PDFAcroPushButton")); const PDFAcroRadioButton_1 = tslib_1.__importDefault(require("./PDFAcroRadioButton")); const PDFAcroCheckBox_1 = tslib_1.__importDefault(require("./PDFAcroCheckBox")); const PDFAcroComboBox_1 = tslib_1.__importDefault(require("./PDFAcroComboBox")); const PDFAcroListBox_1 = tslib_1.__importDefault(require("./PDFAcroListBox")); const flags_1 = require("./flags"); const createPDFAcroFields = (kidDicts) => { if (!kidDicts) return []; const kids = []; for (let idx = 0, len = kidDicts.size(); idx < len; idx++) { const ref = kidDicts.get(idx); const dict = kidDicts.lookup(idx); // if (dict instanceof PDFDict) kids.push(PDFAcroField.fromDict(dict)); if (ref instanceof PDFRef_1.default && dict instanceof PDFDict_1.default) { kids.push([(0, exports.createPDFAcroField)(dict, ref), ref]); } } return kids; }; exports.createPDFAcroFields = createPDFAcroFields; const createPDFAcroField = (dict, ref) => { const isNonTerminal = isNonTerminalAcroField(dict); if (isNonTerminal) return PDFAcroNonTerminal_1.default.fromDict(dict, ref); return createPDFAcroTerminal(dict, ref); }; exports.createPDFAcroField = createPDFAcroField; // TODO: Maybe just check if the dict is *not* a widget? That might be better. // According to the PDF spec: // // > A field's children in the hierarchy may also include widget annotations // > that define its appearance on the page. A field that has children that // > are fields is called a non-terminal field. A field that does not have // > children that are fields is called a terminal field. // // The spec is not entirely clear about how to determine whether a given // dictionary represents an acrofield or a widget annotation. So we will assume // that a dictionary is an acrofield if it is a member of the `/Kids` array // and it contains a `/T` entry (widgets do not have `/T` entries). This isn't // a bullet proof solution, because the `/T` entry is technically defined as // optional for acrofields by the PDF spec. But in practice all acrofields seem // to have a `/T` entry defined. const isNonTerminalAcroField = (dict) => { const kids = dict.lookup(PDFName_1.default.of('Kids')); if (kids instanceof PDFArray_1.default) { for (let idx = 0, len = kids.size(); idx < len; idx++) { const kid = kids.lookup(idx); const kidIsField = kid instanceof PDFDict_1.default && kid.has(PDFName_1.default.of('T')); if (kidIsField) return true; } } return false; }; const createPDFAcroTerminal = (dict, ref) => { const ftNameOrRef = getInheritableAttribute(dict, PDFName_1.default.of('FT')); const type = dict.context.lookup(ftNameOrRef, PDFName_1.default); if (type === PDFName_1.default.of('Btn')) return createPDFAcroButton(dict, ref); if (type === PDFName_1.default.of('Ch')) return createPDFAcroChoice(dict, ref); if (type === PDFName_1.default.of('Tx')) return PDFAcroText_1.default.fromDict(dict, ref); if (type === PDFName_1.default.of('Sig')) return PDFAcroSignature_1.default.fromDict(dict, ref); // We should never reach this line. But there are a lot of weird PDFs out // there. So, just to be safe, we'll try to handle things gracefully instead // of throwing an error. return PDFAcroTerminal_1.default.fromDict(dict, ref); }; const createPDFAcroButton = (dict, ref) => { var _a; const ffNumberOrRef = getInheritableAttribute(dict, PDFName_1.default.of('Ff')); const ffNumber = dict.context.lookupMaybe(ffNumberOrRef, PDFNumber_1.default); const flags = (_a = ffNumber === null || ffNumber === void 0 ? void 0 : ffNumber.asNumber()) !== null && _a !== void 0 ? _a : 0; if (flagIsSet(flags, flags_1.AcroButtonFlags.PushButton)) { return PDFAcroPushButton_1.default.fromDict(dict, ref); } else if (flagIsSet(flags, flags_1.AcroButtonFlags.Radio)) { return PDFAcroRadioButton_1.default.fromDict(dict, ref); } else { return PDFAcroCheckBox_1.default.fromDict(dict, ref); } }; const createPDFAcroChoice = (dict, ref) => { var _a; const ffNumberOrRef = getInheritableAttribute(dict, PDFName_1.default.of('Ff')); const ffNumber = dict.context.lookupMaybe(ffNumberOrRef, PDFNumber_1.default); const flags = (_a = ffNumber === null || ffNumber === void 0 ? void 0 : ffNumber.asNumber()) !== null && _a !== void 0 ? _a : 0; if (flagIsSet(flags, flags_1.AcroChoiceFlags.Combo)) { return PDFAcroComboBox_1.default.fromDict(dict, ref); } else { return PDFAcroListBox_1.default.fromDict(dict, ref); } }; const flagIsSet = (flags, flag) => (flags & flag) !== 0; const getInheritableAttribute = (startNode, name) => { let attribute; ascend(startNode, (node) => { if (!attribute) attribute = node.get(name); }); return attribute; }; const ascend = (startNode, visitor) => { visitor(startNode); const Parent = startNode.lookupMaybe(PDFName_1.default.of('Parent'), PDFDict_1.default); if (Parent) ascend(Parent, visitor); }; //# sourceMappingURL=utils.js.map