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