UNPKG

pdf-lib

Version:

Library for creating and modifying PDF files in JavaScript

263 lines (262 loc) 12.5 kB
import isNumber from 'lodash/isNumber'; import values from 'lodash/values'; import { FontNames as StandardFontNames, } from '@pdf-lib/standard-fonts'; import PDFObjectCopier from '../pdf-document/PDFObjectCopier'; import PDFObjectIndex from '../pdf-document/PDFObjectIndex'; import { PDFDictionary, PDFObject, } from '../pdf-objects'; import { PDFCatalog, PDFContentStream, PDFHeader, PDFPage, PDFPageTree, } from '../pdf-structures'; import JPEGXObjectFactory from '../pdf-structures/factories/JPEGXObjectFactory'; import PDFEmbeddedFontFactory from '../pdf-structures/factories/PDFEmbeddedFontFactory'; import PDFFontFactory from '../pdf-structures/factories/PDFFontFactory'; import PDFStandardFontFactory from '../pdf-structures/factories/PDFStandardFontFactory'; import PNGXObjectFactory from '../pdf-structures/factories/PNGXObjectFactory'; import { isInstance, oneOf, validate } from '../../utils/validate'; var PDFDocument = /** @class */ (function () { function PDFDocument(catalog, index) { var _this = this; /** @hidden */ this.header = PDFHeader.forVersion(1, 7); /** * Registers a [[PDFObject]] to the [[PDFDocument]]'s `index`. Returns a * [[PDFIndirectReference]] that can be used to reference the given `object` * in other `pdf-lib` methods. * * @param object The [[PDFObject]] to be registered. * * @returns The [[PDFIndirectReference]] under which the `object` has been * registered. */ this.register = function (object) { validate(object, isInstance(PDFObject), 'object must be a PDFObject'); return _this.index.assignNextObjectNumberTo(object); }; /** * @returns An array of [[PDFPage]] objects representing the pages of the * [[PDFDocument]]. The order of the [[PDFPage]] documents in the * array mirrors the order in which they will be rendered in the * [[PDFDocument]]. */ this.getPages = function () { var pages = []; _this.catalog.Pages.traverse(function (kid) { if (kid instanceof PDFPage) pages.push(kid); }); return pages; }; /** * Creates a new [[PDFPage]] of the given `size`. And optionally, with the * given `resources` dictionary. * * Note that the [[PDFPage]] returned by this method is **not** automatically * added to the [[PDFDocument]]. You must call the [[addPage]] or [[insertPage]] * methods for it to be rendered in the document. * * @param size A tuple containing the width and height of the page, * respectively. * @param resources A resources dictionary for the page. * * @returns The newly created [[PDFPage]]. */ this.createPage = function (size, resources) { return PDFPage.create(_this.index, size, resources); }; /** * Creates a new [[PDFContentStream]] with the given operators. * * Note that the [[PDFContentStream]] returned by this method is **not** * automatically registered to the document or added to any of its pages. * You must first call the [[register]] method for it to be registered to the * [[PDFDocument]]. Then, you must call [[PDFPage.addContentStreams]] to add * the registered [[PDFContentStream]] to the desired page(s). * * @param operators One or more [[PDFOperator]]s to be added to the * [[PDFContentStream]]. * * @returns The newly created [[PDFContentStream]]. */ this.createContentStream = function () { var operators = []; for (var _i = 0; _i < arguments.length; _i++) { operators[_i] = arguments[_i]; } return PDFContentStream.of.apply(PDFContentStream, [PDFDictionary.from({}, _this.index)].concat(operators)); }; /** * Adds a page to the end of the [[PDFDocument]]. * * @param page The page to be added. */ this.addPage = function (page) { validate(page, isInstance(PDFPage), 'page must be a PDFPage'); // If page comes from another document, copy it into this one if (page.index !== _this.index) { page = PDFObjectCopier.for(page.index, _this.index).copy(page); } var Pages = _this.catalog.Pages; var lastPageTree = Pages; var lastPageTreeRef = _this.catalog.get('Pages'); Pages.traverseRight(function (kid, ref) { if (kid instanceof PDFPageTree) { lastPageTree = kid; lastPageTreeRef = ref; } }); page.set('Parent', lastPageTreeRef); lastPageTree.addPage(_this.register(page)); return _this; }; // TODO: Clean up unused objects when possible after removing page from tree // TODO: Make sure "idx" is within required range /** * Removes a page from the document. * * @param index The index of the page to be removed. The index is zero-based, * e.g. the first page in the document is index `0`. */ this.removePage = function (index) { validate(index, isNumber, 'idx must be a number'); var pageTreeRef = _this.catalog.get('Pages'); // TODO: Use a "stop" callback to avoid unneccesarily traversing whole page tree... var treeRef = pageTreeRef; var pageCount = 0; var kidNum = 0; _this.catalog.Pages.traverse(function (kid, ref) { if (pageCount !== index) { if (kid instanceof PDFPageTree) kidNum = 0; if (kid instanceof PDFPage) { pageCount += 1; kidNum += 1; if (pageCount === index) treeRef = kid.get('Parent'); } } }); var tree = _this.index.lookup(treeRef); tree.removePage(kidNum); return _this; }; // TODO: Make sure "idx" is within required range /** * Inserts a page into the document at the specified index. The page that is * displaced by the insertion will be become the page immediately following * the inserted page. * * @param index The index of the page to be removed. The index is zero-based, * e.g. the first page in the document is index `0`. * @param page The page to be inserted. */ this.insertPage = function (index, page) { validate(index, isNumber, 'idx must be a number'); validate(page, isInstance(PDFPage), 'page must be a PDFPage'); // If page comes from another document, copy it into this one if (page.index !== _this.index) { page = PDFObjectCopier.for(page.index, _this.index).copy(page); } var pageTreeRef = _this.catalog.get('Pages'); // TODO: Use a "stop" callback to avoid unneccesarily traversing whole page tree... var treeRef = pageTreeRef; var pageCount = 0; var kidNum = 0; _this.catalog.Pages.traverse(function (kid, ref) { if (pageCount !== index) { if (kid instanceof PDFPageTree) kidNum = 0; if (kid instanceof PDFPage) { pageCount += 1; kidNum += 1; if (pageCount === index) treeRef = kid.get('Parent'); } } }); page.set('Parent', treeRef); var tree = _this.index.lookup(treeRef); tree.insertPage(kidNum, _this.register(page)); return _this; }; /** * Embeds one of the Standard 14 Fonts fonts in the document. This method * does **not** require a `Uint8Array` containing a font to be passed, because * the Standard 14 Fonts are automatically available to all PDF documents. * * @param fontName Name of the font to be embedded. * * @returns A tuple containing the [[PDFIndirectReference]] under which the * specified font is registered. */ this.embedStandardFont = function (fontName) { validate(fontName, oneOf.apply(void 0, values(StandardFontNames)), 'PDFDocument.embedStandardFont: "fontName" must be one of the Standard 14 Fonts: ' + values(StandardFontNames).join(', ')); var standardFontFactory = PDFStandardFontFactory.for(fontName); return [standardFontFactory.embedFontIn(_this), standardFontFactory]; }; /** * **Deprecated** - please use [[PDFDocument.embedNonStandardFont]] instead. * * Embeds the font contained in the specified `Uint8Array` in the document. * * @param fontData A `Uint8Array` containing an OpenType (`.otf`) or TrueType * (`.ttf`) font. * * @returns A tuple containing (1) the [[PDFIndirectReference]] under which the * specified font is registered, and (2) a [[PDFFontFactory]] object * containing font metadata properties and methods. */ this.embedFont = function (fontData, fontFlags) { if (fontFlags === void 0) { fontFlags = { Nonsymbolic: true }; } var fontFactory = PDFFontFactory.for(fontData, fontFlags); return [fontFactory.embedFontIn(_this), fontFactory]; }; /** * Embeds the font contained in the specified `Uint8Array` in the document. * * @param fontData A `Uint8Array` containing an OpenType (`.otf`) or TrueType * (`.ttf`) font. * * @returns A tuple containing (1) the [[PDFIndirectReference]] under which the * specified font is registered, and (2) a [[PDFEmbeddedFontFactory]] * object containing font metadata properties and methods. */ this.embedNonstandardFont = function (fontData) { var fontFactory = PDFEmbeddedFontFactory.for(fontData); return [fontFactory.embedFontIn(_this), fontFactory]; }; /** * Embeds the PNG image contained in the specified `Uint8Array` in the document. * * @param pngData A `Uint8Array` containing a PNG (`.png`) image. * * @returns A tuple containing (1) the [[PDFIndirectReference]] under which the * specified image is registered, and (2) a [[PNGXObjectFactory]] * object containing the image's width and height. */ this.embedPNG = function (pngData) { var pngFactory = PNGXObjectFactory.for(pngData); return [pngFactory.embedImageIn(_this), pngFactory]; }; /** * Embeds the JPG image contained in the specified `Uint8Array` in the document. * * @param jpgData A `Uint8Array` containing a JPG (`.jpg`) image. * * @returns A tuple containing (1) the [[PDFIndirectReference]] under which the * specified image is registered, and (2) a [[JPEGXObjectFactory]] * object containing the image's width and height. */ this.embedJPG = function (jpgData) { var jpgFactory = JPEGXObjectFactory.for(jpgData); return [jpgFactory.embedImageIn(_this), jpgFactory]; }; validate(catalog, isInstance(PDFCatalog), '"catalog" must be a PDFCatalog object'); validate(index, isInstance(PDFObjectIndex), '"index" must be a PDFObjectIndex object'); this.catalog = catalog; this.index = index; } PDFDocument.from = function (catalog, index) { return new PDFDocument(catalog, index); }; return PDFDocument; }()); export default PDFDocument;