@cantoo/pdf-lib
Version:
Create and modify PDF files with JavaScript
179 lines • 8.43 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EntryType = void 0;
const tslib_1 = require("tslib");
const PDFName_1 = tslib_1.__importDefault(require("../objects/PDFName"));
const PDFRef_1 = tslib_1.__importDefault(require("../objects/PDFRef"));
const PDFFlateStream_1 = tslib_1.__importDefault(require("./PDFFlateStream"));
const utils_1 = require("../../utils");
var EntryType;
(function (EntryType) {
EntryType[EntryType["Deleted"] = 0] = "Deleted";
EntryType[EntryType["Uncompressed"] = 1] = "Uncompressed";
EntryType[EntryType["Compressed"] = 2] = "Compressed";
})(EntryType = exports.EntryType || (exports.EntryType = {}));
/**
* Entries should be added using the [[addDeletedEntry]],
* [[addUncompressedEntry]], and [[addCompressedEntry]] methods
* **in order of ascending object number**.
*/
class PDFCrossRefStream extends PDFFlateStream_1.default {
constructor(dict, entries, encode = true) {
super(dict, encode);
// Returns an array of integer pairs for each subsection of the cross ref
// section, where each integer pair represents:
// firstObjectNumber(OfSection), length(OfSection)
this.computeIndex = () => {
const subsections = [];
let subsectionLength = 0;
for (let idx = 0, len = this.entries.length; idx < len; idx++) {
const currEntry = this.entries[idx];
const prevEntry = this.entries[idx - 1];
if (idx === 0) {
subsections.push(currEntry.ref.objectNumber);
}
else if (currEntry.ref.objectNumber - prevEntry.ref.objectNumber > 1) {
subsections.push(subsectionLength);
subsections.push(currEntry.ref.objectNumber);
subsectionLength = 0;
}
subsectionLength += 1;
}
subsections.push(subsectionLength);
return subsections;
};
this.computeEntryTuples = () => {
const entryTuples = new Array(this.entries.length);
for (let idx = 0, len = this.entries.length; idx < len; idx++) {
const entry = this.entries[idx];
if (entry.type === EntryType.Deleted) {
const { type, nextFreeObjectNumber, ref } = entry;
entryTuples[idx] = [type, nextFreeObjectNumber, ref.generationNumber];
}
if (entry.type === EntryType.Uncompressed) {
const { type, offset, ref } = entry;
entryTuples[idx] = [type, offset, ref.generationNumber];
}
if (entry.type === EntryType.Compressed) {
const { type, objectStreamRef, index } = entry;
entryTuples[idx] = [type, objectStreamRef.objectNumber, index];
}
}
return entryTuples;
};
this.computeMaxEntryByteWidths = () => {
const entryTuples = this.entryTuplesCache.access();
const widths = [0, 0, 0];
for (let idx = 0, len = entryTuples.length; idx < len; idx++) {
const [first, second, third] = entryTuples[idx];
const firstSize = (0, utils_1.sizeInBytes)(first);
const secondSize = (0, utils_1.sizeInBytes)(second);
const thirdSize = (0, utils_1.sizeInBytes)(third);
if (firstSize > widths[0])
widths[0] = firstSize;
if (secondSize > widths[1])
widths[1] = secondSize;
if (thirdSize > widths[2])
widths[2] = thirdSize;
}
return widths;
};
this.entries = entries || [];
this.entryTuplesCache = utils_1.Cache.populatedBy(this.computeEntryTuples);
this.maxByteWidthsCache = utils_1.Cache.populatedBy(this.computeMaxEntryByteWidths);
this.indexCache = utils_1.Cache.populatedBy(this.computeIndex);
dict.set(PDFName_1.default.of('Type'), PDFName_1.default.of('XRef'));
}
addDeletedEntry(ref, nextFreeObjectNumber) {
const type = EntryType.Deleted;
this.entries.push({ type, ref, nextFreeObjectNumber });
this.entryTuplesCache.invalidate();
this.maxByteWidthsCache.invalidate();
this.indexCache.invalidate();
this.contentsCache.invalidate();
}
addUncompressedEntry(ref, offset) {
const type = EntryType.Uncompressed;
this.entries.push({ type, ref, offset });
this.entryTuplesCache.invalidate();
this.maxByteWidthsCache.invalidate();
this.indexCache.invalidate();
this.contentsCache.invalidate();
}
addCompressedEntry(ref, objectStreamRef, index) {
const type = EntryType.Compressed;
this.entries.push({ type, ref, objectStreamRef, index });
this.entryTuplesCache.invalidate();
this.maxByteWidthsCache.invalidate();
this.indexCache.invalidate();
this.contentsCache.invalidate();
}
clone(context) {
const { dict, entries, encode } = this;
return PDFCrossRefStream.of(dict.clone(context), entries.slice(), encode);
}
getContentsString() {
const entryTuples = this.entryTuplesCache.access();
const byteWidths = this.maxByteWidthsCache.access();
let value = '';
for (let entryIdx = 0, entriesLen = entryTuples.length; entryIdx < entriesLen; entryIdx++) {
const [first, second, third] = entryTuples[entryIdx];
const firstBytes = (0, utils_1.reverseArray)((0, utils_1.bytesFor)(first));
const secondBytes = (0, utils_1.reverseArray)((0, utils_1.bytesFor)(second));
const thirdBytes = (0, utils_1.reverseArray)((0, utils_1.bytesFor)(third));
for (let idx = byteWidths[0] - 1; idx >= 0; idx--) {
value += (firstBytes[idx] || 0).toString(2);
}
for (let idx = byteWidths[1] - 1; idx >= 0; idx--) {
value += (secondBytes[idx] || 0).toString(2);
}
for (let idx = byteWidths[2] - 1; idx >= 0; idx--) {
value += (thirdBytes[idx] || 0).toString(2);
}
}
return value;
}
getUnencodedContents() {
const entryTuples = this.entryTuplesCache.access();
const byteWidths = this.maxByteWidthsCache.access();
const buffer = new Uint8Array(this.getUnencodedContentsSize());
let offset = 0;
for (let entryIdx = 0, entriesLen = entryTuples.length; entryIdx < entriesLen; entryIdx++) {
const [first, second, third] = entryTuples[entryIdx];
const firstBytes = (0, utils_1.reverseArray)((0, utils_1.bytesFor)(first));
const secondBytes = (0, utils_1.reverseArray)((0, utils_1.bytesFor)(second));
const thirdBytes = (0, utils_1.reverseArray)((0, utils_1.bytesFor)(third));
for (let idx = byteWidths[0] - 1; idx >= 0; idx--) {
buffer[offset++] = firstBytes[idx] || 0;
}
for (let idx = byteWidths[1] - 1; idx >= 0; idx--) {
buffer[offset++] = secondBytes[idx] || 0;
}
for (let idx = byteWidths[2] - 1; idx >= 0; idx--) {
buffer[offset++] = thirdBytes[idx] || 0;
}
}
return buffer;
}
getUnencodedContentsSize() {
const byteWidths = this.maxByteWidthsCache.access();
const entryWidth = (0, utils_1.sum)(byteWidths);
return entryWidth * this.entries.length;
}
updateDict() {
super.updateDict();
const byteWidths = this.maxByteWidthsCache.access();
const index = this.indexCache.access();
const { context } = this.dict;
this.dict.set(PDFName_1.default.of('W'), context.obj(byteWidths));
this.dict.set(PDFName_1.default.of('Index'), context.obj(index));
}
}
PDFCrossRefStream.create = (dict, encode = true) => {
const stream = new PDFCrossRefStream(dict, [], encode);
stream.addDeletedEntry(PDFRef_1.default.of(0, 65535), 0);
return stream;
};
PDFCrossRefStream.of = (dict, entries, encode = true) => new PDFCrossRefStream(dict, entries, encode);
exports.default = PDFCrossRefStream;
//# sourceMappingURL=PDFCrossRefStream.js.map