@cantoo/pdf-lib
Version:
Create and modify PDF files with JavaScript
169 lines • 6.77 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const PDFArray_1 = tslib_1.__importDefault(require("../objects/PDFArray"));
const PDFDict_1 = tslib_1.__importDefault(require("../objects/PDFDict"));
const PDFName_1 = tslib_1.__importDefault(require("../objects/PDFName"));
const PDFNumber_1 = tslib_1.__importDefault(require("../objects/PDFNumber"));
const PDFPageLeaf_1 = tslib_1.__importDefault(require("./PDFPageLeaf"));
const errors_1 = require("../errors");
class PDFPageTree extends PDFDict_1.default {
Parent() {
return this.lookup(PDFName_1.default.of('Parent'));
}
Kids() {
return this.lookup(PDFName_1.default.of('Kids'), PDFArray_1.default);
}
Count() {
return this.lookup(PDFName_1.default.of('Count'), PDFNumber_1.default);
}
pushTreeNode(treeRef) {
const Kids = this.Kids();
Kids.push(treeRef);
}
pushLeafNode(leafRef) {
const Kids = this.Kids();
this.insertLeafKid(Kids.size(), leafRef);
}
/**
* Inserts the given ref as a leaf node of this page tree at the specified
* index (zero-based). Also increments the `Count` of each page tree in the
* hierarchy to accomodate the new page.
*
* Returns the ref of the PDFPageTree node into which `leafRef` was inserted,
* or `undefined` if it was inserted into the root node (the PDFPageTree upon
* which the method was first called).
*/
insertLeafNode(leafRef, targetIndex) {
const Kids = this.Kids();
const Count = this.Count().asNumber();
if (targetIndex > Count) {
throw new errors_1.InvalidTargetIndexError(targetIndex, Count);
}
let leafsRemainingUntilTarget = targetIndex;
for (let idx = 0, len = Kids.size(); idx < len; idx++) {
if (leafsRemainingUntilTarget === 0) {
// Insert page and return
this.insertLeafKid(idx, leafRef);
return undefined;
}
const kidRef = Kids.get(idx);
const kid = this.context.lookup(kidRef);
if (kid instanceof PDFPageTree) {
if (kid.Count().asNumber() > leafsRemainingUntilTarget) {
// Dig in
return (kid.insertLeafNode(leafRef, leafsRemainingUntilTarget) || kidRef);
}
else {
// Move on
leafsRemainingUntilTarget -= kid.Count().asNumber();
}
}
if (kid instanceof PDFPageLeaf_1.default) {
// Move on
leafsRemainingUntilTarget -= 1;
}
}
if (leafsRemainingUntilTarget === 0) {
// Insert page at the end and return
this.insertLeafKid(Kids.size(), leafRef);
return undefined;
}
// Should never get here if `targetIndex` is valid
throw new errors_1.CorruptPageTreeError(targetIndex, 'insertLeafNode');
}
/**
* Removes the leaf node at the specified index (zero-based) from this page
* tree. Also decrements the `Count` of each page tree in the hierarchy to
* account for the removed page.
*
* If `prune` is true, then intermediate tree nodes will be removed from the
* tree if they contain 0 children after the leaf node is removed.
*/
removeLeafNode(targetIndex, prune = true) {
const Kids = this.Kids();
const Count = this.Count().asNumber();
if (targetIndex >= Count) {
throw new errors_1.InvalidTargetIndexError(targetIndex, Count);
}
let leafsRemainingUntilTarget = targetIndex;
for (let idx = 0, len = Kids.size(); idx < len; idx++) {
const kidRef = Kids.get(idx);
const kid = this.context.lookup(kidRef);
if (kid instanceof PDFPageTree) {
if (kid.Count().asNumber() > leafsRemainingUntilTarget) {
// Dig in
kid.removeLeafNode(leafsRemainingUntilTarget, prune);
if (prune && kid.Kids().size() === 0)
Kids.remove(idx);
return;
}
else {
// Move on
leafsRemainingUntilTarget -= kid.Count().asNumber();
}
}
if (kid instanceof PDFPageLeaf_1.default) {
if (leafsRemainingUntilTarget === 0) {
// Remove page and return
this.removeKid(idx);
return;
}
else {
// Move on
leafsRemainingUntilTarget -= 1;
}
}
}
// Should never get here if `targetIndex` is valid
throw new errors_1.CorruptPageTreeError(targetIndex, 'removeLeafNode');
}
ascend(visitor) {
visitor(this);
const Parent = this.Parent();
if (Parent)
Parent.ascend(visitor);
}
/** Performs a Post-Order traversal of this page tree */
traverse(visitor) {
const Kids = this.Kids();
for (let idx = 0, len = Kids.size(); idx < len; idx++) {
const kidRef = Kids.get(idx);
const kid = this.context.lookup(kidRef);
if (kid instanceof PDFPageTree)
kid.traverse(visitor);
visitor(kid, kidRef);
}
}
insertLeafKid(kidIdx, leafRef) {
const Kids = this.Kids();
this.ascend((node) => {
const newCount = node.Count().asNumber() + 1;
node.set(PDFName_1.default.of('Count'), PDFNumber_1.default.of(newCount));
});
Kids.insert(kidIdx, leafRef);
}
removeKid(kidIdx) {
const Kids = this.Kids();
const kid = Kids.lookup(kidIdx);
if (kid instanceof PDFPageLeaf_1.default) {
this.ascend((node) => {
const newCount = node.Count().asNumber() - 1;
node.set(PDFName_1.default.of('Count'), PDFNumber_1.default.of(newCount));
});
}
Kids.remove(kidIdx);
}
}
PDFPageTree.withContext = (context, parent) => {
const dict = new Map();
dict.set(PDFName_1.default.of('Type'), PDFName_1.default.of('Pages'));
dict.set(PDFName_1.default.of('Kids'), context.obj([]));
dict.set(PDFName_1.default.of('Count'), context.obj(0));
if (parent)
dict.set(PDFName_1.default.of('Parent'), parent);
return new PDFPageTree(dict, context);
};
PDFPageTree.fromMapWithContext = (map, context) => new PDFPageTree(map, context);
exports.default = PDFPageTree;
//# sourceMappingURL=PDFPageTree.js.map