: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
xml.documentElement.dump(buffer);
async function updateAcroform({
if (hasXfa && !hasXfaDatasetsEntry && !xfaDatasetsRef) {
warn("XFA - Cannot save it");
if (!needAppearances && (!hasXfa || !xfaDatasetsRef || hasXfaDatasetsEntry)) {
const dict = acroForm.clone();
if (hasXfa && !hasXfaDatasetsEntry) {
const newXfa = acroForm.get("XFA").slice();
newXfa.splice(2, 0, "datasets");
newXfa.splice(3, 0, xfaDatasetsRef);
dict.set("NeedAppearances", true);
await writeObject(acroFormRef, dict, buffer, xref);
const datasets = xref.fetchIfRef(xfaDatasetsRef);
xfaData = writeXFADataForAcroform(datasets.getString(), newRefs);
const encrypt = xref.encrypt;
const transform = encrypt.createCipherTransform(xfaDatasetsRef.num, xfaDatasetsRef.gen);
xfaData = transform.encryptString(xfaData);
const data = `${xfaDatasetsRef.num} ${xfaDatasetsRef.gen} obj\n` + `<< /Type /EmbeddedFile /Length ${xfaData.length}>>\nstream\n` + xfaData + "\nendstream\nendobj\n";
async function getXRefTable(xrefInfo, baseOffset, newRefs, newXref, buffer) {
const indexes = getIndexes(newRefs);
if (ref.num === indexes[indexesPosition]) {
buffer.push(`${indexes[indexesPosition]} ${indexes[indexesPosition + 1]}\n`);
buffer.push(`${baseOffset.toString().padStart(10, "0")} ${Math.min(ref.gen, 0xffff).toString().padStart(5, "0")} n\r\n`);
baseOffset += data.length;
buffer.push(`0000000000 ${Math.min(ref.gen + 1, 0xffff).toString().padStart(5, "0")} f\r\n`);
computeIDs(baseOffset, xrefInfo, newXref);
buffer.push("trailer\n");
await writeDict(newXref, buffer);
buffer.push("\nstartxref\n", baseOffset.toString(), "\n%%EOF\n");
function getIndexes(newRefs) {
if (ref.num === indexes.at(-2) + indexes.at(-1)) {
indexes[indexes.length - 1] += 1;
indexes.push(ref.num, 1);
async function getXRefStreamTable(xrefInfo, baseOffset, newRefs, newXref, buffer) {
const xrefTableData = [];
maxOffset = Math.max(maxOffset, baseOffset);
gen = Math.min(ref.gen, 0xffff);
xrefTableData.push([1, baseOffset, gen]);
baseOffset += data.length;
gen = Math.min(ref.gen + 1, 0xffff);
xrefTableData.push([0, 0, gen]);
maxGen = Math.max(maxGen, gen);
newXref.set("Index", getIndexes(newRefs));
const offsetSize = getSizeInBytes(maxOffset);
const maxGenSize = getSizeInBytes(maxGen);
const sizes = [1, offsetSize, maxGenSize];
computeIDs(baseOffset, xrefInfo, newXref);
const structSize = sizes.reduce((a, x) => a + x, 0);
const data = new Uint8Array(structSize * xrefTableData.length);
const stream = new Stream(data);
for (const [type, objOffset, gen] of xrefTableData) {
offset = writeInt(type, sizes[0], offset, data);
offset = writeInt(objOffset, sizes[1], offset, data);
offset = writeInt(gen, sizes[2], offset, data);
await writeObject(xrefInfo.newRef, stream, buffer, {});
buffer.push("startxref\n", baseOffset.toString(), "\n%%EOF\n");
function computeIDs(baseOffset, xrefInfo, newXref) {
if (Array.isArray(xrefInfo.fileIds) && xrefInfo.fileIds.length > 0) {
const md5 = computeMD5(baseOffset, xrefInfo);
newXref.set("ID", [xrefInfo.fileIds[0], md5]);
function getTrailerDict(xrefInfo, newRefs, useXrefStream) {
const newXref = new Dict(null);
newXref.set("Prev", xrefInfo.startXRef);
const refForXrefTable = xrefInfo.newRef;
newXref.set("Size", refForXrefTable.num + 1);
newXref.set("Type", Name.get("XRef"));
newXref.set("Size", refForXrefTable.num);
if (xrefInfo.rootRef !== null) {
newXref.set("Root", xrefInfo.rootRef);
if (xrefInfo.infoRef !== null) {
newXref.set("Info", xrefInfo.infoRef);
if (xrefInfo.encryptRef !== null) {
newXref.set("Encrypt", xrefInfo.encryptRef);
async function incrementalUpdate({
hasXfaDatasetsEntry = false,
let baseOffset = originalData.length;
const lastByte = originalData.at(-1);
if (lastByte !== 0x0a && lastByte !== 0x0d) {
const newXref = getTrailerDict(xrefInfo, newRefs, useXrefStream);
newRefs = newRefs.sort((a, b) => a.ref.num - b.ref.num);
await (useXrefStream ? getXRefStreamTable(xrefInfo, baseOffset, newRefs, newXref, buffer) : getXRefTable(xrefInfo, baseOffset, newRefs, newXref, buffer));
const totalLength = buffer.reduce((a, str) => a + str.length, originalData.length);
const array = new Uint8Array(totalLength);
let offset = originalData.length;
for (const str of buffer) {
writeString(str, offset, array);
;// CONCATENATED MODULE: ./src/core/struct_tree.js
const StructElementType = {
constructor(rootDict, rootRef) {
this.ref = rootRef instanceof Ref ? rootRef : null;
this.roleMap = new Map();
this.structParentIds = null;
#addIdToPage(pageRef, id, type) {
if (!(pageRef instanceof Ref) || id < 0) {
this.structParentIds ||= new RefSetCache();
let ids = this.structParentIds.get(pageRef);
this.structParentIds.put(pageRef, ids);
addAnnotationIdToPage(pageRef, id) {
this.#addIdToPage(pageRef, id, StructElementType.ANNOTATION);
const roleMapDict = this.dict.get("RoleMap");
if (!(roleMapDict instanceof Dict)) {
roleMapDict.forEach((key, value) => {
if (!(value instanceof Name)) {
this.roleMap.set(key, value.name);
static async canCreateStructureTree({
if (!(catalogRef instanceof Ref)) {
warn("Cannot save the struct tree: no catalog reference.");
let hasNothingToUpdate = true;
for (const [pageIndex, elements] of newAnnotationsByPage) {
} = await pdfManager.getPage(pageIndex);
if (!(pageRef instanceof Ref)) {
warn(`Cannot save the struct tree: page ${pageIndex} has no ref.`);
hasNothingToUpdate = true;
for (const element of elements) {
if (element.accessibilityData?.type) {
element.parentTreeId = nextKey++;
hasNothingToUpdate = false;
if (hasNothingToUpdate) {
for (const elements of newAnnotationsByPage.values()) {
for (const element of elements) {
delete element.parentTreeId;
static async createStructureTree({
const root = pdfManager.catalog.cloneDict();
const cache = new RefSetCache();
cache.put(catalogRef, root);
const structTreeRootRef = xref.getNewTemporaryRef();
root.set("StructTreeRoot", structTreeRootRef);
const structTreeRoot = new Dict(xref);
structTreeRoot.set("Type", Name.get("StructTreeRoot"));
const parentTreeRef = xref.getNewTemporaryRef();
structTreeRoot.set("ParentTree", parentTreeRef);
structTreeRoot.set("K", kids);
cache.put(structTreeRootRef, structTreeRoot);
const parentTree = new Dict(xref);
parentTree.set("Nums", nums);
const nextKey = await this.#writeKids({
structTreeRoot.set("ParentTreeNextKey", nextKey);
cache.put(parentTreeRef, parentTree);
for (const [ref, obj] of cache.items()) {
await writeObject(ref, obj, buffer, xref);
async canUpdateStructTree({
warn("Cannot update the struct tree: no root reference.");
let nextKey = this.dict.get("ParentTreeNextKey");
if (!Number.isInteger(nextKey) || nextKey < 0) {
warn("Cannot update the struct tree: invalid next key.");
const parentTree = this.dict.get("ParentTree");
if (!(parentTree instanceof Dict)) {
warn("Cannot update the struct tree: ParentTree isn't a dict.");
const nums = parentTree.get("Nums");
if (!Array.isArray(nums)) {
warn("Cannot update the struct tree: nums isn't an array.");
const numberTree = new NumberTree(parentTree, xref);
for (const pageIndex of newAnnotationsByPage.keys()) {
} = await pdfManager.getPage(pageIndex);
if (!pageDict.has("StructParents")) {
const id = pageDict.get("StructParents");
if (!Number.isInteger(id) || !Array.isArray(numberTree.get(id))) {
warn(`Cannot save the struct tree: page ${pageIndex} has a wrong id.`);
let hasNothingToUpdate = true;
for (const [pageIndex, elements] of newAnnotationsByPage) {
} = await pdfManager.getPage(pageIndex);
StructTreeRoot.#collectParents({
for (const element of elements) {
if (element.accessibilityData?.type) {
element.parentTreeId = nextKey++;
hasNothingToUpdate = false;
if (hasNothingToUpdate) {
for (const elements of newAnnotationsByPage.values()) {
for (const element of elements) {
delete element.parentTreeId;
delete element.structTreeParent;
async updateStructureTree({
const xref = this.dict.xref;
const structTreeRoot = this.dict.clone();
const structTreeRootRef = this.ref;
const cache = new RefSetCache();
cache.put(structTreeRootRef, structTreeRoot);
let parentTreeRef = structTreeRoot.getRaw("ParentTree");
if (parentTreeRef instanceof Ref) {
parentTree = xref.fetch(parentTreeRef);
parentTree = parentTreeRef;
parentTreeRef = xref.getNewTemporaryRef();
structTreeRoot.set("ParentTree", parentTreeRef);
parentTree = parentTree.clone();
cache.put(parentTreeRef, parentTree);
let nums = parentTree.getRaw("Nums");
if (nums instanceof Ref) {
nums = xref.fetch(numsRef);
parentTree.set("Nums", nums);
const newNextkey = await StructTreeRoot.#writeKids({
structTreeRoot.set("ParentTreeNextKey", newNextkey);
cache.put(numsRef, nums);
for (const [ref, obj] of cache.items()) {
await writeObject(ref, obj, buffer, xref);
static async #writeKids({
const objr = Name.get("OBJR");
for (const [pageIndex, elements] of newAnnotationsByPage) {
} = await pdfManager.getPage(pageIndex);
const isPageRef = pageRef instanceof Ref;