@cantoo/pdf-lib
Version:
Create and modify PDF files with JavaScript
226 lines • 8.82 kB
JavaScript
import pako from 'pako';
import PDFHeader from './document/PDFHeader.js';
import { UnexpectedObjectTypeError } from './errors.js';
import PDFArray from './objects/PDFArray.js';
import PDFBool from './objects/PDFBool.js';
import PDFDict from './objects/PDFDict.js';
import PDFHexString from './objects/PDFHexString.js';
import PDFName from './objects/PDFName.js';
import PDFNull from './objects/PDFNull.js';
import PDFNumber from './objects/PDFNumber.js';
import PDFObject from './objects/PDFObject.js';
import PDFRawStream from './objects/PDFRawStream.js';
import PDFRef from './objects/PDFRef.js';
import PDFStream from './objects/PDFStream.js';
import PDFString from './objects/PDFString.js';
import PDFOperator from './operators/PDFOperator.js';
import Ops from './operators/PDFOperatorNames.js';
import PDFContentStream from './structures/PDFContentStream.js';
import { typedArrayFor } from '../utils/index.js';
import { SimpleRNG } from '../utils/rng.js';
const byAscendingObjectNumber = ([a], [b]) => a.objectNumber - b.objectNumber;
class PDFContext {
constructor() {
this.isDecrypted = true;
this.largestObjectNumber = 0;
this.header = PDFHeader.forVersion(1, 7);
this.trailerInfo = {};
this.indirectObjects = new Map();
this.rng = SimpleRNG.withSeed(1);
}
assign(ref, object) {
this.indirectObjects.set(ref, object);
if (ref.objectNumber > this.largestObjectNumber) {
this.largestObjectNumber = ref.objectNumber;
}
}
nextRef() {
this.largestObjectNumber += 1;
return PDFRef.of(this.largestObjectNumber);
}
register(object) {
const ref = this.nextRef();
this.assign(ref, object);
return ref;
}
delete(ref) {
return this.indirectObjects.delete(ref);
}
lookupMaybe(ref, ...types) {
// TODO: `preservePDFNull` is for backwards compatibility. Should be
// removed in next breaking API change.
const preservePDFNull = types.includes(PDFNull);
const result = ref instanceof PDFRef ? this.indirectObjects.get(ref) : ref;
if (!result || (result === PDFNull && !preservePDFNull))
return undefined;
for (let idx = 0, len = types.length; idx < len; idx++) {
const type = types[idx];
if (type === PDFNull) {
if (result === PDFNull)
return result;
}
else {
if (result instanceof type)
return result;
}
}
throw new UnexpectedObjectTypeError(types, result);
}
lookup(ref, ...types) {
const result = ref instanceof PDFRef ? this.indirectObjects.get(ref) : ref;
if (types.length === 0)
return result;
for (let idx = 0, len = types.length; idx < len; idx++) {
const type = types[idx];
if (type === PDFNull) {
if (result === PDFNull)
return result;
}
else {
if (result instanceof type)
return result;
}
}
throw new UnexpectedObjectTypeError(types, result);
}
getObjectRef(pdfObject) {
const entries = Array.from(this.indirectObjects.entries());
for (let idx = 0, len = entries.length; idx < len; idx++) {
const [ref, object] = entries[idx];
if (object === pdfObject) {
return ref;
}
}
return undefined;
}
enumerateIndirectObjects() {
return Array.from(this.indirectObjects.entries()).sort(byAscendingObjectNumber);
}
obj(literal) {
if (literal instanceof PDFObject) {
return literal;
}
else if (literal === null || literal === undefined) {
return PDFNull;
}
else if (typeof literal === 'string') {
return PDFName.of(literal);
}
else if (typeof literal === 'number') {
return PDFNumber.of(literal);
}
else if (typeof literal === 'boolean') {
return literal ? PDFBool.True : PDFBool.False;
}
else if (literal instanceof Uint8Array) {
return PDFHexString.fromBytes(literal);
}
else if (Array.isArray(literal)) {
const array = PDFArray.withContext(this);
for (let idx = 0, len = literal.length; idx < len; idx++) {
array.push(this.obj(literal[idx]));
}
return array;
}
else {
const dict = PDFDict.withContext(this);
const keys = Object.keys(literal);
for (let idx = 0, len = keys.length; idx < len; idx++) {
const key = keys[idx];
const value = literal[key];
if (value !== undefined)
dict.set(PDFName.of(key), this.obj(value));
}
return dict;
}
}
getLiteral(obj, { deep = true, literalRef = false, literalStreamDict = false, literalString = false, } = {}) {
const cfg = { deep, literalRef, literalStreamDict, literalString };
if (obj instanceof PDFArray) {
const lit = obj.asArray();
return deep ? lit.map((value) => this.getLiteral(value, cfg)) : lit;
}
else if (obj instanceof PDFBool) {
return obj.asBoolean();
}
else if (obj instanceof PDFDict) {
const lit = {};
const entries = obj.entries();
for (let idx = 0, len = entries.length; idx < len; idx++) {
const [name, value] = entries[idx];
lit[this.getLiteral(name)] = deep ? this.getLiteral(value, cfg) : value;
}
return lit;
}
else if (obj instanceof PDFName) {
return obj.decodeText();
}
else if (obj === PDFNull) {
return null;
}
else if (obj instanceof PDFNumber) {
return obj.asNumber();
}
else if (obj instanceof PDFRef && literalRef) {
return obj.objectNumber;
}
else if (obj instanceof PDFStream && literalStreamDict) {
return this.getLiteral(obj.dict, cfg);
}
else if ((obj instanceof PDFString || obj instanceof PDFHexString) &&
literalString) {
return obj.asString();
}
return obj;
}
stream(contents, dict = {}) {
return PDFRawStream.of(this.obj(dict), typedArrayFor(contents));
}
flateStream(contents, dict = {}) {
return this.stream(pako.deflate(typedArrayFor(contents)), Object.assign(Object.assign({}, dict), { Filter: 'FlateDecode' }));
}
contentStream(operators, dict = {}) {
return PDFContentStream.of(this.obj(dict), operators);
}
formXObject(operators, dict = {}) {
return this.contentStream(operators, Object.assign(Object.assign({ BBox: this.obj([0, 0, 0, 0]), Matrix: this.obj([1, 0, 0, 1, 0, 0]) }, dict), { Type: 'XObject', Subtype: 'Form' }));
}
/*
* Reference to PDFContentStream that contains a single PDFOperator: `q`.
* Used by [[PDFPageLeaf]] instances to ensure that when content streams are
* added to a modified PDF, they start in the default, unchanged graphics
* state.
*/
getPushGraphicsStateContentStream() {
if (this.pushGraphicsStateContentStreamRef) {
return this.pushGraphicsStateContentStreamRef;
}
const dict = this.obj({});
const op = PDFOperator.of(Ops.PushGraphicsState);
const stream = PDFContentStream.of(dict, [op]);
this.pushGraphicsStateContentStreamRef = this.register(stream);
return this.pushGraphicsStateContentStreamRef;
}
/*
* Reference to PDFContentStream that contains a single PDFOperator: `Q`.
* Used by [[PDFPageLeaf]] instances to ensure that when content streams are
* added to a modified PDF, they start in the default, unchanged graphics
* state.
*/
getPopGraphicsStateContentStream() {
if (this.popGraphicsStateContentStreamRef) {
return this.popGraphicsStateContentStreamRef;
}
const dict = this.obj({});
const op = PDFOperator.of(Ops.PopGraphicsState);
const stream = PDFContentStream.of(dict, [op]);
this.popGraphicsStateContentStreamRef = this.register(stream);
return this.popGraphicsStateContentStreamRef;
}
addRandomSuffix(prefix, suffixLength = 4) {
return `${prefix}-${Math.floor(this.rng.nextInt() * Math.pow(10, suffixLength))}`;
}
}
PDFContext.create = () => new PDFContext();
export default PDFContext;
//# sourceMappingURL=PDFContext.js.map