pdfkit/lib/structure_element.js

128 lines
3.0 KiB
JavaScript

/*
PDFStructureElement - represents an element in the PDF logical structure tree
By Ben Schmidt
*/
import PDFStructureContent from "./structure_content";
class PDFStructureElement {
constructor(document, type, options = {}, children = null) {
this.document = document;
this._ended = false;
this._flushed = false;
this.dictionary = document.ref({
// Type: "StructElem",
S: type
});
const data = this.dictionary.data;
if (Array.isArray(options)) {
children = options;
options = {};
}
if (typeof options.title !== 'undefined') {
data.T = new String(options.title);
}
if (typeof options.lang !== 'undefined') {
data.Lang = new String(options.lang);
}
if (typeof options.alt !== 'undefined') {
data.Alt = new String(options.alt);
}
if (typeof options.expanded !== 'undefined') {
data.E = new String(options.expanded);
}
if (typeof options.actual !== 'undefined') {
data.ActualText = new String(options.actual);
}
this.structElemChildren = [];
if (children) {
children.forEach((child) => this.add(child));
this.end();
}
}
add(child) {
if (this._ended) {
throw new Error(`Cannot add child to already-ended structure element`);
}
if (!this.dictionary.data.K) {
this.dictionary.data.K = [];
}
if (!(child instanceof PDFStructureElement || child instanceof PDFStructureContent)) {
throw new Error(`Invalid structure element child`);
}
if (child instanceof PDFStructureElement) {
child.setParent(this.dictionary);
this.structElemChildren.push(child);
this.dictionary.data.K.push(child.dictionary);
}
if (child instanceof PDFStructureContent) {
child.refs.forEach(({ pageRef, mcid }) => {
if (!this.dictionary.data.Pg) {
this.dictionary.data.Pg = pageRef;
}
if (this.dictionary.data.Pg === pageRef) {
this.dictionary.data.K.push(mcid);
} else {
this.dictionary.data.K.push({
Type: "MCR",
Pg: pageRef,
MCID: mcid
});
}
const pageStructParents = this.document.getStructParentTree()
.get(pageRef.data.StructParents);
pageStructParents[mcid] = this.dictionary;
});
}
return this;
}
setParent(parentRef) {
this.dictionary.data.P = parentRef;
if (this._ended) {
this._flush();
}
}
end() {
if (this._ended) {
return;
}
this.structElemChildren.forEach((child) => child.end());
this._ended = true;
if (this.dictionary.data.P) {
this._flush();
}
}
_flush() {
this.dictionary.end();
// free memory used by children; the dictionary itself may still be
// referenced by a parent structure element or root, but we can
// at least trim the tree here
this.structElemChildren = [];
this.dictionary.data.K = null;
}
}
export default PDFStructureElement;