pdf-lib
Version:
Library for creating and modifying PDF files in JavaScript
114 lines (113 loc) • 6.05 kB
JavaScript
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;