@cantoo/pdf-lib
Version:
Create and modify PDF files with JavaScript
127 lines • 6.17 kB
JavaScript
;
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