UNPKG

@cantoo/pdf-lib

Version:

Create and modify PDF files with JavaScript

135 lines 7.45 kB
import { __awaiter } from "tslib"; import PDFHeader from '../document/PDFHeader.js'; import PDFTrailer from '../document/PDFTrailer.js'; import PDFInvalidObject from '../objects/PDFInvalidObject.js'; import PDFName from '../objects/PDFName.js'; import PDFNumber from '../objects/PDFNumber.js'; import PDFRawStream from '../objects/PDFRawStream.js'; import PDFRef from '../objects/PDFRef.js'; import PDFStream from '../objects/PDFStream.js'; import PDFCatalog from '../structures/PDFCatalog.js'; import PDFPageTree from '../structures/PDFPageTree.js'; import PDFPageLeaf from '../structures/PDFPageLeaf.js'; import PDFCrossRefStream from '../structures/PDFCrossRefStream.js'; import PDFObjectStream from '../structures/PDFObjectStream.js'; import PDFWriter from './PDFWriter.js'; import { last, waitForTick } from '../../utils/index.js'; import { defaultDocumentSnapshot } from '../../api/snapshot/index.js'; import PDFDict from '../objects/PDFDict.js'; class PDFStreamWriter extends PDFWriter { constructor(context, objectsPerTick, snapshot, encodeStreams, objectsPerStream) { super(context, objectsPerTick, snapshot); // the process of saving uses references numbers, and creates a new indirect object, that has to be deleted after saving this._refToDeleteAfterSave = 0; this.encodeStreams = encodeStreams; this.objectsPerStream = objectsPerStream; } computeBufferSize(incremental) { return __awaiter(this, void 0, void 0, function* () { this._refToDeleteAfterSave = 0; const header = PDFHeader.forVersion(1, 7); let size = this.snapshot.pdfSize; if (!incremental) { size += header.sizeInBytes() + 1; } size += 1; const xrefStream = PDFCrossRefStream.create(this.createTrailerDict(), this.encodeStreams); const uncompressedObjects = []; const compressedObjects = []; const objectStreamRefs = []; const security = this.context.security; const indirectObjects = this.context.enumerateIndirectObjects(); for (let idx = 0, len = indirectObjects.length; idx < len; idx++) { const indirectObject = indirectObjects[idx]; const [ref, object] = indirectObject; if (!this.snapshot.shouldSave(ref.objectNumber)) { continue; } // Old XRef stream objects from the parsed PDF must be skipped: a new // XRef stream will be generated below. If we kept them, the xref would // reference an object that serializeToBuffer() (inherited from // PDFWriter) also skips, producing an offset mismatch. if (object instanceof PDFRawStream && object.dict.lookup(PDFName.of('Type')) === PDFName.of('XRef')) { continue; } const shouldNotCompress = ref === this.context.trailerInfo.Encrypt || object instanceof PDFStream || object instanceof PDFInvalidObject || object instanceof PDFCatalog || object instanceof PDFPageTree || object instanceof PDFPageLeaf || ref.generationNumber !== 0 || (object instanceof PDFDict && object.lookup(PDFName.of('Type')) === PDFName.of('Sig')); if (shouldNotCompress) { uncompressedObjects.push(indirectObject); if (security) this.encrypt(ref, object, security); xrefStream.addUncompressedEntry(ref, size); size += this.computeIndirectObjectSize(indirectObject); if (this.shouldWaitForTick(1)) yield waitForTick(); } else { let chunk = last(compressedObjects); let objectStreamRef = last(objectStreamRefs); if (!chunk || chunk.length % this.objectsPerStream === 0) { chunk = []; compressedObjects.push(chunk); objectStreamRef = this.context.nextRef(); this._refToDeleteAfterSave += 1; objectStreamRefs.push(objectStreamRef); } xrefStream.addCompressedEntry(ref, objectStreamRef, chunk.length); chunk.push(indirectObject); } } for (let idx = 0, len = compressedObjects.length; idx < len; idx++) { const chunk = compressedObjects[idx]; const ref = objectStreamRefs[idx]; const objectStream = PDFObjectStream.withContextAndObjects(this.context, chunk, this.encodeStreams); this.context.assign(ref, objectStream); if (security) this.encrypt(ref, objectStream, security); xrefStream.addUncompressedEntry(ref, size); size += this.computeIndirectObjectSize([ref, objectStream]); uncompressedObjects.push([ref, objectStream]); if (this.shouldWaitForTick(chunk.length)) yield waitForTick(); } const xrefStreamRef = this.context.nextRef(); this._refToDeleteAfterSave += 1; xrefStream.dict.set(PDFName.of('Size'), PDFNumber.of(this.context.largestObjectNumber + 1)); if (this.snapshot.prevStartXRef) { xrefStream.dict.set(PDFName.of('Prev'), PDFNumber.of(this.snapshot.prevStartXRef)); } xrefStream.addUncompressedEntry(xrefStreamRef, size); const xrefOffset = size; size += this.computeIndirectObjectSize([xrefStreamRef, xrefStream]); uncompressedObjects.push([xrefStreamRef, xrefStream]); const trailer = PDFTrailer.forLastCrossRefSectionOffset(xrefOffset); size += trailer.sizeInBytes(); size -= this.snapshot.pdfSize; return { size, header, indirectObjects: uncompressedObjects, trailer }; }); } serializeToBuffer() { const _super = Object.create(null, { serializeToBuffer: { get: () => super.serializeToBuffer } }); return __awaiter(this, void 0, void 0, function* () { const buffer = yield _super.serializeToBuffer.call(this); const firstTempRef = this.context.largestObjectNumber - this._refToDeleteAfterSave + 1; for (let i = firstTempRef; i < firstTempRef + this._refToDeleteAfterSave - 1; i++) { this.context.delete(PDFRef.of(i)); } this.context.largestObjectNumber -= this._refToDeleteAfterSave; return buffer; }); } } PDFStreamWriter.forContext = (context, objectsPerTick, encodeStreams = true, objectsPerStream = 50) => new PDFStreamWriter(context, objectsPerTick, defaultDocumentSnapshot, encodeStreams, objectsPerStream); PDFStreamWriter.forContextWithSnapshot = (context, objectsPerTick, snapshot, encodeStreams = true, objectsPerStream = 50) => new PDFStreamWriter(context, objectsPerTick, snapshot, encodeStreams, objectsPerStream); export default PDFStreamWriter; //# sourceMappingURL=PDFStreamWriter.js.map