UNPKG

pdf-lib

Version:

Library for creating and modifying PDF files in JavaScript

269 lines (268 loc) 11.3 kB
var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); import isArray from 'lodash/isArray'; import isNumber from 'lodash/isNumber'; import isString from 'lodash/isString'; import { PDFArray, PDFDictionary, PDFIndirectReference, PDFName, PDFNumber, } from '../pdf-objects'; import { isIdentity, isInstance, optional, validate, validateArr, } from '../../utils/validate'; /** @hidden */ var VALID_KEYS = Object.freeze([ 'Type', 'Parent', 'LastModified', 'Resources', 'MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox', 'BoxColorInfo', 'Contents', 'Rotate', 'Group', 'Thumb', 'B', 'Dur', 'Trans', 'Annots', 'AA', 'Metadata', 'PieceInfo', 'StructParents', 'ID', 'PZ', 'SeparationInfo', 'Tabs', 'TemplateInstantiated', 'PresSteps', 'UserUnit', 'VP', ]); var PDFPage = /** @class */ (function (_super) { __extends(PDFPage, _super); function PDFPage() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.autoNormalizeCTM = true; /* Convert "Contents" to array if it exists and is not already */ // TODO: See is this is inefficient... /** @hidden */ _this.normalizeContents = function () { var Contents = _this.getMaybe('Contents'); if (Contents) { var contents = _this.index.lookup(Contents); if (!(contents instanceof PDFArray)) { _this.set('Contents', PDFArray.fromArray([Contents], _this.index)); } } }; /** * Ensures that content streams added to the [[PDFPage]] after calling * [[normalizeCTM]] will be working in the default Content Transformation * Matrix. * * This can be useful in cases where PDFs are being modified that * have existing content streams which modify the CTM outside without * resetting their changes (with the Q and q operators). * * Works by wrapping any existing content streams for this page in two new * content streams that contain a single operator each: `q` and `Q`, * respectively. * * Note that the `Contents` entry in this [[PDFPage]] must be a PDFArray. * Calling [[normalizeContents]] first will ensure that this is the case. * * @param pdfDoc The document containing this PDFPage, to which the two new * [[PDFContentStream]]s will be added * * @returns Returns this [[PDFPage]] instance. */ _this.normalizeCTM = function () { var contents = _this.getMaybe('Contents'); if (!contents) return _this; var _a = _this.index, pushGraphicsStateContentStream = _a.pushGraphicsStateContentStream, popGraphicsStateContentStream = _a.popGraphicsStateContentStream; if (pushGraphicsStateContentStream && popGraphicsStateContentStream && contents.array[0] !== pushGraphicsStateContentStream) { contents.array.unshift(pushGraphicsStateContentStream); contents.array.push(popGraphicsStateContentStream); } return _this; }; /** @hidden */ _this.normalizeResources = function (_a) { var Font = _a.Font, XObject = _a.XObject; if (!_this.getMaybe('Resources')) { _this.set('Resources', PDFDictionary.from(new Map(), _this.index)); } if (Font && !_this.Resources.getMaybe('Font')) { _this.Resources.set('Font', PDFDictionary.from(new Map(), _this.index)); } if (XObject && !_this.Resources.getMaybe('XObject')) { _this.Resources.set('XObject', PDFDictionary.from(new Map(), _this.index)); } }; // TODO: Consider allowing *insertion* of content streams so order can be changed /** * Add one or more content streams to the page. * * Note that this method does * **not** directly accept [[PDFContentStream]](s) as its arguments. Instead, * it accepts references to the content streams in the form of * [[PDFIndirectReference]] objects. To obtain a reference for a * [[PDFContentStream]], you must call the [[PDFDocument.register]] method * with the [[PDFContentStream]]. * * @param contentStreams The content stream(s) to be added to the page. */ _this.addContentStreams = function () { var contentStreams = []; for (var _i = 0; _i < arguments.length; _i++) { contentStreams[_i] = arguments[_i]; } validateArr(contentStreams, isInstance(PDFIndirectReference), '"contentStream" must be of type PDFIndirectReference<PDFContentStream>'); _this.normalizeContents(); if (_this.autoNormalizeCTM) _this.normalizeCTM(); if (!_this.getMaybe('Contents')) { _this.set('Contents', PDFArray.fromArray(contentStreams, _this.index)); } else { (_a = _this.Contents).push.apply(_a, contentStreams); } return _this; var _a; }; /** * Adds a font dictionary to the page. * * Note that this method does **not** directly accept font * [[PDFDictionary]](s) as its arguments. Instead, it accepts references to * the font dictionaries in the form of [[PDFIndirectReference]] objects. * * The first element of the tuples returned by the * [[PDFDocument.embedStandardFont]] and [[PDFDocument.embedFont]] methods * is a [[PDFIndirectReference]] to a font dictionary that can be passed as * the `fontDict` parameter of this method. * * @param key The name by which the font dictionary will be referenced. * @param fontDict The font dictionary to be added to the page. */ _this.addFontDictionary = function (key, // TODO: Allow PDFName objects to be passed too fontDict) { validate(key, isString, '"key" must be a string'); validate(fontDict, isInstance(PDFIndirectReference), '"fontDict" must be an instance of PDFIndirectReference'); _this.normalizeResources({ Font: true }); var Font = _this.index.lookup(_this.Resources.get('Font')); Font.set(key, fontDict); return _this; }; /** * **Note:** This method is an alias for [[addXObject]]. It exists because its * name is more descriptive and familiar than `addXObject` is. * * Adds an image object to the page. * * Note that this method does **not** directly accept a [[PDFStream]] object * as its argument. Instead, it accepts a reference to the [[PDFStream]] in * the form of a [[PDFIndirectReference]] object. * * The first element of the tuples returned by the * [[PDFDocument.embedPNG]] and [[PDFDocument.embedJPG]] methods * is a [[PDFIndirectReference]] to a [[PDFStream]] that can be passed as * the `imageObject` parameter of this method. * * @param key The name by which the image object will be referenced. * @param imageObject The image object to be added to the page. */ _this.addImageObject = function (key, imageObject) { _this.addXObject(key, imageObject); return _this; }; /** * Adds an XObject to the page. * * Note that this method does **not** directly accept a [[PDFStream]] object * as its argument. Instead, it accepts a reference to the [[PDFStream]] in * the form of a [[PDFIndirectReference]] object. * * @param key The name by which the XObject will be referenced. * @param xObject The XObject to be added to the page. */ _this.addXObject = function (key, xObject) { validate(key, isString, '"key" must be a string'); validate(xObject, isInstance(PDFIndirectReference), '"xObject" must be an instance of PDFIndirectReference'); _this.normalizeResources({ XObject: true }); var XObject = _this.index.lookup(_this.Resources.get('XObject')); XObject.set(key, xObject); return _this; }; _this.clone = function () { return PDFPage.fromDict(PDFDictionary.from(new Map(_this.map), _this.index)); }; return _this; } Object.defineProperty(PDFPage.prototype, "Parent", { /** @hidden */ get: function () { return this.index.lookup(this.get('Parent')); }, enumerable: true, configurable: true }); Object.defineProperty(PDFPage.prototype, "Resources", { /** @hidden */ get: function () { return this.index.lookup(this.get('Resources')); }, enumerable: true, configurable: true }); Object.defineProperty(PDFPage.prototype, "Contents", { /** @hidden */ get: function () { return this.index.lookup(this.get('Contents')); }, enumerable: true, configurable: true }); /** @hidden */ PDFPage.validKeys = VALID_KEYS; /** @hidden */ PDFPage.INHERITABLE_ENTRIES = [ 'Resources', 'MediaBox', 'CropBox', 'Rotate', ]; PDFPage.create = function (index, size, resources) { validate(size, isArray, 'size must be an array of 2 numbers.'); validate(size.length, isIdentity(2), 'size tuple must have two elements.'); validate(size[0], isNumber, 'size tuple entries must be Numbers.'); validate(size[1], isNumber, 'size tuple entries must be Numbers.'); validate(resources, optional(isInstance(PDFDictionary)), 'resources must be a PDFDictionary'); var mediaBox = [0, 0, size[0], size[1]]; var page = new PDFPage({ Type: PDFName.from('Page'), // TODO: Convert this to use PDFRectangle MediaBox: PDFArray.fromArray(mediaBox.map(PDFNumber.fromNumber), index), }, index, VALID_KEYS); if (resources) page.set('Resources', resources); return page; }; PDFPage.fromDict = function (dict) { validate(dict, isInstance(PDFDictionary), '"dict" must be a PDFDictionary'); return new PDFPage(dict.map, dict.index, VALID_KEYS); }; return PDFPage; }(PDFDictionary)); export default PDFPage;