UNPKG

pdf-lib

Version:

Library for creating and modifying PDF files in JavaScript

114 lines (113 loc) 6.05 kB
import isFunction from 'lodash/isFunction'; import last from 'lodash/last'; import sortBy from 'lodash/sortBy'; import { PDFDictionary, PDFIndirectObject, PDFNumber, PDFStream, } from '../pdf-objects'; import { PDFCatalog, PDFObjectStream, PDFTrailer } from '../pdf-structures'; import PDFXRefStreamFactory from '../pdf-structures/factories/PDFXRefStreamFactory'; import PDFXRefTableFactory from '../pdf-structures/factories/PDFXRefTableFactory'; import { error } from '../../utils'; var createIndirectObjectsFromIndex = function (_a) { var index = _a.index; var catalogRef; var streamObjects = []; var nonStreamObjects = []; index.forEach(function (object, ref) { if (object instanceof PDFCatalog) catalogRef = ref; var array = object instanceof PDFStream ? streamObjects : nonStreamObjects; array.push(PDFIndirectObject.of(object).setReference(ref)); }); return { catalogRef: catalogRef, streamObjects: streamObjects, nonStreamObjects: nonStreamObjects }; }; var computeOffsets = function (startingOffset, indirectObjects) { return indirectObjects.map(function (object) { return ({ objectNumber: object.reference.objectNumber, generationNumber: object.reference.generationNumber, startOffset: startingOffset, endOffset: startingOffset += object.bytesSize(), }); }); }; var PDFDocumentWriter = /** @class */ (function () { function PDFDocumentWriter() { } /** * Converts a [[PDFDocument]] object into the raw bytes of a PDF document. * These raw bytes could, for example, be saved as a file and opened in a * PDF reader. * * `options.useObjectStreams` controls whether or not to use Object Streams * when saving the document. Using Object Streams will result in a smaller * file size for many documents. This option is `true` by default. If set to * `false`, then Object Streams will not be used. * * @param pdfDoc The [[PDFDocument]] to be converted to bytes. * @param options An options object. * * @returns A `Uint8Array` containing the raw bytes of a PDF document. */ PDFDocumentWriter.saveToBytes = function (pdfDoc, options) { if (options === void 0) { options = { useObjectStreams: true }; } return options.useObjectStreams === false ? PDFDocumentWriter.saveToBytesWithXRefTable(pdfDoc) : PDFDocumentWriter.saveToBytesWithObjectStreams(pdfDoc); }; PDFDocumentWriter.saveToBytesWithXRefTable = function (pdfDoc) { /* ===== (1) Compute indirect object offsets and sort by objectnumber ===== */ var _a = createIndirectObjectsFromIndex(pdfDoc.index), catalogRef = _a.catalogRef, streamObjects = _a.streamObjects, nonStreamObjects = _a.nonStreamObjects; if (!catalogRef) error('Missing PDFCatalog'); streamObjects.forEach(function (streamObj) { if (isFunction(streamObj.pdfObject.encode)) streamObj.pdfObject.encode(); }); var merged = streamObjects.concat(nonStreamObjects); var offsets = computeOffsets(pdfDoc.header.bytesSize(), merged); var sortedOffsets = sortBy(offsets, 'objectNumber'); /* ===== (2) Create XRefTable and Trailer ===== */ var table = PDFXRefTableFactory.forOffsets(sortedOffsets); var tableOffset = last(offsets).endOffset; var trailer = PDFTrailer.from(tableOffset, PDFDictionary.from({ Size: PDFNumber.fromNumber(last(sortedOffsets).objectNumber + 1), Root: catalogRef, }, pdfDoc.index)); /* ===== (3) Create buffer and copy objects into it ===== */ var bufferSize = tableOffset + table.bytesSize() + trailer.bytesSize(); var buffer = new Uint8Array(bufferSize); var remaining = pdfDoc.header.copyBytesInto(buffer); remaining = merged.reduce(function (remBytes, indirectObj) { return indirectObj.copyBytesInto(remBytes); }, remaining); remaining = table.copyBytesInto(remaining); remaining = trailer.copyBytesInto(remaining); return buffer; }; PDFDocumentWriter.saveToBytesWithObjectStreams = function (pdfDoc) { /* ===== (1) Split objects into streams and nonstreams ===== */ var _a = createIndirectObjectsFromIndex(pdfDoc.index), catalogRef = _a.catalogRef, streamObjects = _a.streamObjects, nonStreamObjects = _a.nonStreamObjects; if (!catalogRef) error('Missing PDFCatalog'); streamObjects.forEach(function (streamObj) { if (isFunction(streamObj.pdfObject.encode)) streamObj.pdfObject.encode(); }); /* ===== (2) Create ObjectStream ===== */ var objectStream = PDFObjectStream.create(pdfDoc.index, nonStreamObjects); objectStream.encode(); var objectStreamIndObj = PDFIndirectObject.of(objectStream).setReferenceNumbers(pdfDoc.index.highestObjectNumber + 1, 0); streamObjects.push(objectStreamIndObj); /* ===== (3) Compute indirect object offsets ===== */ var offsets = computeOffsets(pdfDoc.header.bytesSize(), streamObjects); var trailerOffset = last(offsets).endOffset; /* ===== (4) Create XRefStream and Trailer ===== */ var xrefStream = PDFXRefStreamFactory.forOffsetsAndObjectStream(offsets, objectStreamIndObj, catalogRef, pdfDoc.index); streamObjects.push(xrefStream); var trailer = PDFTrailer.from(trailerOffset); /* ===== (5) Create buffer and copy objects into it ===== */ var bufferSize = trailerOffset + xrefStream.bytesSize() + trailer.bytesSize(); var buffer = new Uint8Array(bufferSize); var remaining = pdfDoc.header.copyBytesInto(buffer); remaining = streamObjects.reduce(function (remBytes, obj) { return obj.copyBytesInto(remBytes); }, remaining); remaining = trailer.copyBytesInto(remaining); return buffer; }; return PDFDocumentWriter; }()); export default PDFDocumentWriter;