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