Initial kitchen scaffold — Phase 1 kitchen port (build-verified 2026-07-11)
FastAPI backend (Python 3.11, MSSQL ODBC for SambaPOS, Azure DI OCR),
kitchen_db on central PG. React/TS/Vite frontend with navy sidebar layout.
Backend: auth.py (APP_SLUG=kitchen, SimpleNamespace — archive routes use
.kitchen_id/.is_admin without modification), main.py (51 migrations, scheduler,
internal router for KDS bookings feed), api/internal.py, full archive API
(31 routers: invoices, recipes, menus, sambapos, resos, newbook, disputes,
purchase_orders, etc.), models, migrations, OCR pipeline.
kitchen_id pinned to 1 (B1 — single hotel).
Frontend: AuthGate (app=kitchen, token shim for archive compat — B5b pending),
Layout (navy sidebar, 6 sections, Lucide icons, teal --app-primary),
App.tsx (Outlet pattern, UploadApp outside Layout), index.css (full :root block).
strict: false — archive components have type issues; build clean.
Note: 45 archive components call fetch('/api/...') without /kitchen/ prefix
(B5b). Runtime 404s; deferred until after initial testing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
8d688b459d
10003 changed files with 1928395 additions and 0 deletions
299
frontend/node_modules/pdf-lib/src/core/PDFContext.ts
generated
vendored
Normal file
299
frontend/node_modules/pdf-lib/src/core/PDFContext.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
import pako from 'pako';
|
||||
|
||||
import PDFHeader from 'src/core/document/PDFHeader';
|
||||
import { UnexpectedObjectTypeError } from 'src/core/errors';
|
||||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFBool from 'src/core/objects/PDFBool';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNull from 'src/core/objects/PDFNull';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFRawStream from 'src/core/objects/PDFRawStream';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFStream from 'src/core/objects/PDFStream';
|
||||
import PDFString from 'src/core/objects/PDFString';
|
||||
import PDFOperator from 'src/core/operators/PDFOperator';
|
||||
import Ops from 'src/core/operators/PDFOperatorNames';
|
||||
import PDFContentStream from 'src/core/structures/PDFContentStream';
|
||||
import { typedArrayFor } from 'src/utils';
|
||||
import { SimpleRNG } from 'src/utils/rng';
|
||||
|
||||
type LookupKey = PDFRef | PDFObject | undefined;
|
||||
|
||||
interface LiteralObject {
|
||||
[name: string]: Literal | PDFObject;
|
||||
}
|
||||
|
||||
interface LiteralArray {
|
||||
[index: number]: Literal | PDFObject;
|
||||
}
|
||||
|
||||
type Literal =
|
||||
| LiteralObject
|
||||
| LiteralArray
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| undefined;
|
||||
|
||||
const byAscendingObjectNumber = (
|
||||
[a]: [PDFRef, PDFObject],
|
||||
[b]: [PDFRef, PDFObject],
|
||||
) => a.objectNumber - b.objectNumber;
|
||||
|
||||
class PDFContext {
|
||||
static create = () => new PDFContext();
|
||||
|
||||
largestObjectNumber: number;
|
||||
header: PDFHeader;
|
||||
trailerInfo: {
|
||||
Root?: PDFObject;
|
||||
Encrypt?: PDFObject;
|
||||
Info?: PDFObject;
|
||||
ID?: PDFObject;
|
||||
};
|
||||
rng: SimpleRNG;
|
||||
|
||||
private readonly indirectObjects: Map<PDFRef, PDFObject>;
|
||||
|
||||
private pushGraphicsStateContentStreamRef?: PDFRef;
|
||||
private popGraphicsStateContentStreamRef?: PDFRef;
|
||||
|
||||
private constructor() {
|
||||
this.largestObjectNumber = 0;
|
||||
this.header = PDFHeader.forVersion(1, 7);
|
||||
this.trailerInfo = {};
|
||||
|
||||
this.indirectObjects = new Map();
|
||||
this.rng = SimpleRNG.withSeed(1);
|
||||
}
|
||||
|
||||
assign(ref: PDFRef, object: PDFObject): void {
|
||||
this.indirectObjects.set(ref, object);
|
||||
if (ref.objectNumber > this.largestObjectNumber) {
|
||||
this.largestObjectNumber = ref.objectNumber;
|
||||
}
|
||||
}
|
||||
|
||||
nextRef(): PDFRef {
|
||||
this.largestObjectNumber += 1;
|
||||
return PDFRef.of(this.largestObjectNumber);
|
||||
}
|
||||
|
||||
register(object: PDFObject): PDFRef {
|
||||
const ref = this.nextRef();
|
||||
this.assign(ref, object);
|
||||
return ref;
|
||||
}
|
||||
|
||||
delete(ref: PDFRef): boolean {
|
||||
return this.indirectObjects.delete(ref);
|
||||
}
|
||||
|
||||
lookupMaybe(ref: LookupKey, type: typeof PDFArray): PDFArray | undefined;
|
||||
lookupMaybe(ref: LookupKey, type: typeof PDFBool): PDFBool | undefined;
|
||||
lookupMaybe(ref: LookupKey, type: typeof PDFDict): PDFDict | undefined;
|
||||
lookupMaybe(
|
||||
ref: LookupKey,
|
||||
type: typeof PDFHexString,
|
||||
): PDFHexString | undefined;
|
||||
lookupMaybe(ref: LookupKey, type: typeof PDFName): PDFName | undefined;
|
||||
lookupMaybe(ref: LookupKey, type: typeof PDFNull): typeof PDFNull | undefined;
|
||||
lookupMaybe(ref: LookupKey, type: typeof PDFNumber): PDFNumber | undefined;
|
||||
lookupMaybe(ref: LookupKey, type: typeof PDFStream): PDFStream | undefined;
|
||||
lookupMaybe(ref: LookupKey, type: typeof PDFRef): PDFRef | undefined;
|
||||
lookupMaybe(ref: LookupKey, type: typeof PDFString): PDFString | undefined;
|
||||
lookupMaybe(
|
||||
ref: LookupKey,
|
||||
type1: typeof PDFString,
|
||||
type2: typeof PDFHexString,
|
||||
): PDFString | PDFHexString | undefined;
|
||||
|
||||
lookupMaybe(ref: LookupKey, ...types: any[]) {
|
||||
// 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: LookupKey): PDFObject | undefined;
|
||||
lookup(ref: LookupKey, type: typeof PDFArray): PDFArray;
|
||||
lookup(ref: LookupKey, type: typeof PDFBool): PDFBool;
|
||||
lookup(ref: LookupKey, type: typeof PDFDict): PDFDict;
|
||||
lookup(ref: LookupKey, type: typeof PDFHexString): PDFHexString;
|
||||
lookup(ref: LookupKey, type: typeof PDFName): PDFName;
|
||||
lookup(ref: LookupKey, type: typeof PDFNull): typeof PDFNull;
|
||||
lookup(ref: LookupKey, type: typeof PDFNumber): PDFNumber;
|
||||
lookup(ref: LookupKey, type: typeof PDFStream): PDFStream;
|
||||
lookup(ref: LookupKey, type: typeof PDFRef): PDFRef;
|
||||
lookup(ref: LookupKey, type: typeof PDFString): PDFString;
|
||||
lookup(
|
||||
ref: LookupKey,
|
||||
type1: typeof PDFString,
|
||||
type2: typeof PDFHexString,
|
||||
): PDFString | PDFHexString;
|
||||
|
||||
lookup(ref: LookupKey, ...types: any[]) {
|
||||
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: PDFObject): PDFRef | undefined {
|
||||
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(): [PDFRef, PDFObject][] {
|
||||
return Array.from(this.indirectObjects.entries()).sort(
|
||||
byAscendingObjectNumber,
|
||||
);
|
||||
}
|
||||
|
||||
obj(literal: null | undefined): typeof PDFNull;
|
||||
obj(literal: string): PDFName;
|
||||
obj(literal: number): PDFNumber;
|
||||
obj(literal: boolean): PDFBool;
|
||||
obj(literal: LiteralObject): PDFDict;
|
||||
obj(literal: LiteralArray): PDFArray;
|
||||
|
||||
obj(literal: 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 (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 as LiteralObject)[key] as any;
|
||||
if (value !== undefined) dict.set(PDFName.of(key), this.obj(value));
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
|
||||
stream(
|
||||
contents: string | Uint8Array,
|
||||
dict: LiteralObject = {},
|
||||
): PDFRawStream {
|
||||
return PDFRawStream.of(this.obj(dict), typedArrayFor(contents));
|
||||
}
|
||||
|
||||
flateStream(
|
||||
contents: string | Uint8Array,
|
||||
dict: LiteralObject = {},
|
||||
): PDFRawStream {
|
||||
return this.stream(pako.deflate(typedArrayFor(contents)), {
|
||||
...dict,
|
||||
Filter: 'FlateDecode',
|
||||
});
|
||||
}
|
||||
|
||||
contentStream(
|
||||
operators: PDFOperator[],
|
||||
dict: LiteralObject = {},
|
||||
): PDFContentStream {
|
||||
return PDFContentStream.of(this.obj(dict), operators);
|
||||
}
|
||||
|
||||
formXObject(
|
||||
operators: PDFOperator[],
|
||||
dict: LiteralObject = {},
|
||||
): PDFContentStream {
|
||||
return this.contentStream(operators, {
|
||||
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(): PDFRef {
|
||||
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(): PDFRef {
|
||||
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: string, suffixLength = 4): string {
|
||||
return `${prefix}-${Math.floor(this.rng.nextInt() * 10 ** suffixLength)}`;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFContext;
|
||||
143
frontend/node_modules/pdf-lib/src/core/PDFObjectCopier.ts
generated
vendored
Normal file
143
frontend/node_modules/pdf-lib/src/core/PDFObjectCopier.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFStream from 'src/core/objects/PDFStream';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFPageLeaf from 'src/core/structures/PDFPageLeaf';
|
||||
|
||||
/**
|
||||
* PDFObjectCopier copies PDFObjects from a src context to a dest context.
|
||||
* The primary use case for this is to copy pages between PDFs.
|
||||
*
|
||||
* _Copying_ an object with a PDFObjectCopier is different from _cloning_ an
|
||||
* object with its [[PDFObject.clone]] method:
|
||||
*
|
||||
* ```
|
||||
* const src: PDFContext = ...
|
||||
* const dest: PDFContext = ...
|
||||
* const originalObject: PDFObject = ...
|
||||
* const copiedObject = PDFObjectCopier.for(src, dest).copy(originalObject);
|
||||
* const clonedObject = originalObject.clone();
|
||||
* ```
|
||||
*
|
||||
* Copying an object is equivalent to cloning it and then copying over any other
|
||||
* objects that it references. Note that only dictionaries, arrays, and streams
|
||||
* (or structures build from them) can contain indirect references to other
|
||||
* objects. Copying a PDFObject that is not a dictionary, array, or stream is
|
||||
* supported, but is equivalent to cloning it.
|
||||
*/
|
||||
class PDFObjectCopier {
|
||||
static for = (src: PDFContext, dest: PDFContext) =>
|
||||
new PDFObjectCopier(src, dest);
|
||||
|
||||
private readonly src: PDFContext;
|
||||
private readonly dest: PDFContext;
|
||||
private readonly traversedObjects = new Map<PDFObject, PDFObject>();
|
||||
|
||||
private constructor(src: PDFContext, dest: PDFContext) {
|
||||
this.src = src;
|
||||
this.dest = dest;
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
copy = <T extends PDFObject>(object: T): T => (
|
||||
object instanceof PDFPageLeaf ? this.copyPDFPage(object)
|
||||
: object instanceof PDFDict ? this.copyPDFDict(object)
|
||||
: object instanceof PDFArray ? this.copyPDFArray(object)
|
||||
: object instanceof PDFStream ? this.copyPDFStream(object)
|
||||
: object instanceof PDFRef ? this.copyPDFIndirectObject(object)
|
||||
: object.clone()
|
||||
) as T;
|
||||
|
||||
private copyPDFPage = (originalPage: PDFPageLeaf): PDFPageLeaf => {
|
||||
const clonedPage = originalPage.clone();
|
||||
|
||||
// Move any entries that the originalPage is inheriting from its parent
|
||||
// tree nodes directly into originalPage so they are preserved during
|
||||
// the copy.
|
||||
const { InheritableEntries } = PDFPageLeaf;
|
||||
for (let idx = 0, len = InheritableEntries.length; idx < len; idx++) {
|
||||
const key = PDFName.of(InheritableEntries[idx]);
|
||||
const value = clonedPage.getInheritableAttribute(key)!;
|
||||
if (!clonedPage.get(key) && value) clonedPage.set(key, value);
|
||||
}
|
||||
|
||||
// Remove the parent reference to prevent the whole donor document's page
|
||||
// tree from being copied when we only need a single page.
|
||||
clonedPage.delete(PDFName.of('Parent'));
|
||||
|
||||
return this.copyPDFDict(clonedPage) as PDFPageLeaf;
|
||||
};
|
||||
|
||||
private copyPDFDict = (originalDict: PDFDict): PDFDict => {
|
||||
if (this.traversedObjects.has(originalDict)) {
|
||||
return this.traversedObjects.get(originalDict) as PDFDict;
|
||||
}
|
||||
|
||||
const clonedDict = originalDict.clone(this.dest);
|
||||
this.traversedObjects.set(originalDict, clonedDict);
|
||||
|
||||
const entries = originalDict.entries();
|
||||
|
||||
for (let idx = 0, len = entries.length; idx < len; idx++) {
|
||||
const [key, value] = entries[idx];
|
||||
clonedDict.set(key, this.copy(value));
|
||||
}
|
||||
|
||||
return clonedDict;
|
||||
};
|
||||
|
||||
private copyPDFArray = (originalArray: PDFArray): PDFArray => {
|
||||
if (this.traversedObjects.has(originalArray)) {
|
||||
return this.traversedObjects.get(originalArray) as PDFArray;
|
||||
}
|
||||
|
||||
const clonedArray = originalArray.clone(this.dest);
|
||||
this.traversedObjects.set(originalArray, clonedArray);
|
||||
|
||||
for (let idx = 0, len = originalArray.size(); idx < len; idx++) {
|
||||
const value = originalArray.get(idx);
|
||||
clonedArray.set(idx, this.copy(value));
|
||||
}
|
||||
|
||||
return clonedArray;
|
||||
};
|
||||
|
||||
private copyPDFStream = (originalStream: PDFStream): PDFStream => {
|
||||
if (this.traversedObjects.has(originalStream)) {
|
||||
return this.traversedObjects.get(originalStream) as PDFStream;
|
||||
}
|
||||
|
||||
const clonedStream = originalStream.clone(this.dest);
|
||||
this.traversedObjects.set(originalStream, clonedStream);
|
||||
|
||||
const entries = originalStream.dict.entries();
|
||||
for (let idx = 0, len = entries.length; idx < len; idx++) {
|
||||
const [key, value] = entries[idx];
|
||||
clonedStream.dict.set(key, this.copy(value));
|
||||
}
|
||||
|
||||
return clonedStream;
|
||||
};
|
||||
|
||||
private copyPDFIndirectObject = (ref: PDFRef): PDFRef => {
|
||||
const alreadyMapped = this.traversedObjects.has(ref);
|
||||
|
||||
if (!alreadyMapped) {
|
||||
const newRef = this.dest.nextRef();
|
||||
this.traversedObjects.set(ref, newRef);
|
||||
|
||||
const dereferencedValue = this.src.lookup(ref);
|
||||
if (dereferencedValue) {
|
||||
const cloned = this.copy(dereferencedValue);
|
||||
this.dest.assign(newRef, cloned);
|
||||
}
|
||||
}
|
||||
|
||||
return this.traversedObjects.get(ref) as PDFRef;
|
||||
};
|
||||
}
|
||||
|
||||
export default PDFObjectCopier;
|
||||
114
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroButton.ts
generated
vendored
Normal file
114
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroButton.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFString from 'src/core/objects/PDFString';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFAcroTerminal from 'src/core/acroform/PDFAcroTerminal';
|
||||
import { IndexOutOfBoundsError } from 'src/core/errors';
|
||||
|
||||
class PDFAcroButton extends PDFAcroTerminal {
|
||||
Opt(): PDFString | PDFHexString | PDFArray | undefined {
|
||||
return this.dict.lookupMaybe(
|
||||
PDFName.of('Opt'),
|
||||
PDFString,
|
||||
PDFHexString,
|
||||
PDFArray,
|
||||
);
|
||||
}
|
||||
|
||||
setOpt(opt: PDFObject[]) {
|
||||
this.dict.set(PDFName.of('Opt'), this.dict.context.obj(opt));
|
||||
}
|
||||
|
||||
getExportValues(): (PDFString | PDFHexString)[] | undefined {
|
||||
const opt = this.Opt();
|
||||
|
||||
if (!opt) return undefined;
|
||||
|
||||
if (opt instanceof PDFString || opt instanceof PDFHexString) {
|
||||
return [opt];
|
||||
}
|
||||
|
||||
const values: (PDFString | PDFHexString)[] = [];
|
||||
for (let idx = 0, len = opt.size(); idx < len; idx++) {
|
||||
const value = opt.lookup(idx);
|
||||
if (value instanceof PDFString || value instanceof PDFHexString) {
|
||||
values.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
removeExportValue(idx: number) {
|
||||
const opt = this.Opt();
|
||||
|
||||
if (!opt) return;
|
||||
|
||||
if (opt instanceof PDFString || opt instanceof PDFHexString) {
|
||||
if (idx !== 0) throw new IndexOutOfBoundsError(idx, 0, 0);
|
||||
this.setOpt([]);
|
||||
} else {
|
||||
if (idx < 0 || idx > opt.size()) {
|
||||
throw new IndexOutOfBoundsError(idx, 0, opt.size());
|
||||
}
|
||||
opt.remove(idx);
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce use use of /Opt even if it isn't strictly necessary
|
||||
normalizeExportValues() {
|
||||
const exportValues = this.getExportValues() ?? [];
|
||||
|
||||
const Opt: (PDFString | PDFHexString)[] = [];
|
||||
|
||||
const widgets = this.getWidgets();
|
||||
for (let idx = 0, len = widgets.length; idx < len; idx++) {
|
||||
const widget = widgets[idx];
|
||||
const exportVal =
|
||||
exportValues[idx] ??
|
||||
PDFHexString.fromText(widget.getOnValue()?.decodeText() ?? '');
|
||||
Opt.push(exportVal);
|
||||
}
|
||||
|
||||
this.setOpt(Opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reuses existing opt if one exists with the same value (assuming
|
||||
* `useExistingIdx` is `true`). Returns index of existing (or new) opt.
|
||||
*/
|
||||
addOpt(opt: PDFHexString | PDFString, useExistingOptIdx: boolean): number {
|
||||
this.normalizeExportValues();
|
||||
|
||||
const optText = opt.decodeText();
|
||||
|
||||
let existingIdx: number | undefined;
|
||||
if (useExistingOptIdx) {
|
||||
const exportValues = this.getExportValues() ?? [];
|
||||
for (let idx = 0, len = exportValues.length; idx < len; idx++) {
|
||||
const exportVal = exportValues[idx];
|
||||
if (exportVal.decodeText() === optText) existingIdx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
const Opt = this.Opt() as PDFArray;
|
||||
Opt.push(opt);
|
||||
|
||||
return existingIdx ?? Opt.size() - 1;
|
||||
}
|
||||
|
||||
addWidgetWithOpt(
|
||||
widget: PDFRef,
|
||||
opt: PDFHexString | PDFString,
|
||||
useExistingOptIdx: boolean,
|
||||
) {
|
||||
const optIdx = this.addOpt(opt, useExistingOptIdx);
|
||||
const apStateValue = PDFName.of(String(optIdx));
|
||||
this.addWidget(widget);
|
||||
return apStateValue;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFAcroButton;
|
||||
49
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroCheckBox.ts
generated
vendored
Normal file
49
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroCheckBox.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFAcroButton from 'src/core/acroform/PDFAcroButton';
|
||||
import { InvalidAcroFieldValueError } from 'src/core/errors';
|
||||
|
||||
class PDFAcroCheckBox extends PDFAcroButton {
|
||||
static fromDict = (dict: PDFDict, ref: PDFRef) =>
|
||||
new PDFAcroCheckBox(dict, ref);
|
||||
|
||||
static create = (context: PDFContext) => {
|
||||
const dict = context.obj({
|
||||
FT: 'Btn',
|
||||
Kids: [],
|
||||
});
|
||||
const ref = context.register(dict);
|
||||
return new PDFAcroCheckBox(dict, ref);
|
||||
};
|
||||
|
||||
setValue(value: PDFName) {
|
||||
const onValue = this.getOnValue() ?? PDFName.of('Yes');
|
||||
if (value !== onValue && value !== PDFName.of('Off')) {
|
||||
throw new InvalidAcroFieldValueError();
|
||||
}
|
||||
|
||||
this.dict.set(PDFName.of('V'), value);
|
||||
|
||||
const widgets = this.getWidgets();
|
||||
for (let idx = 0, len = widgets.length; idx < len; idx++) {
|
||||
const widget = widgets[idx];
|
||||
const state = widget.getOnValue() === value ? value : PDFName.of('Off');
|
||||
widget.setAppearanceState(state);
|
||||
}
|
||||
}
|
||||
|
||||
getValue(): PDFName {
|
||||
const v = this.V();
|
||||
if (v instanceof PDFName) return v;
|
||||
return PDFName.of('Off');
|
||||
}
|
||||
|
||||
getOnValue(): PDFName | undefined {
|
||||
const [widget] = this.getWidgets();
|
||||
return widget?.getOnValue();
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFAcroCheckBox;
|
||||
153
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroChoice.ts
generated
vendored
Normal file
153
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroChoice.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
import PDFAcroTerminal from 'src/core/acroform/PDFAcroTerminal';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFString from 'src/core/objects/PDFString';
|
||||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import { AcroChoiceFlags } from 'src/core/acroform/flags';
|
||||
import {
|
||||
InvalidAcroFieldValueError,
|
||||
MultiSelectValueError,
|
||||
} from 'src/core/errors';
|
||||
|
||||
class PDFAcroChoice extends PDFAcroTerminal {
|
||||
setValues(values: (PDFString | PDFHexString)[]) {
|
||||
if (
|
||||
this.hasFlag(AcroChoiceFlags.Combo) &&
|
||||
!this.hasFlag(AcroChoiceFlags.Edit) &&
|
||||
!this.valuesAreValid(values)
|
||||
) {
|
||||
throw new InvalidAcroFieldValueError();
|
||||
}
|
||||
|
||||
if (values.length === 0) {
|
||||
this.dict.delete(PDFName.of('V'));
|
||||
}
|
||||
if (values.length === 1) {
|
||||
this.dict.set(PDFName.of('V'), values[0]);
|
||||
}
|
||||
if (values.length > 1) {
|
||||
if (!this.hasFlag(AcroChoiceFlags.MultiSelect)) {
|
||||
throw new MultiSelectValueError();
|
||||
}
|
||||
this.dict.set(PDFName.of('V'), this.dict.context.obj(values));
|
||||
}
|
||||
|
||||
this.updateSelectedIndices(values);
|
||||
}
|
||||
|
||||
valuesAreValid(values: (PDFString | PDFHexString)[]): boolean {
|
||||
const options = this.getOptions();
|
||||
for (let idx = 0, len = values.length; idx < len; idx++) {
|
||||
const val = values[idx].decodeText();
|
||||
if (!options.find((o) => val === (o.display || o.value).decodeText())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
updateSelectedIndices(values: (PDFString | PDFHexString)[]) {
|
||||
if (values.length > 1) {
|
||||
const indices = new Array<number>(values.length);
|
||||
const options = this.getOptions();
|
||||
for (let idx = 0, len = values.length; idx < len; idx++) {
|
||||
const val = values[idx].decodeText();
|
||||
indices[idx] = options.findIndex(
|
||||
(o) => val === (o.display || o.value).decodeText(),
|
||||
);
|
||||
}
|
||||
this.dict.set(PDFName.of('I'), this.dict.context.obj(indices.sort()));
|
||||
} else {
|
||||
this.dict.delete(PDFName.of('I'));
|
||||
}
|
||||
}
|
||||
|
||||
getValues(): (PDFString | PDFHexString)[] {
|
||||
const v = this.V();
|
||||
|
||||
if (v instanceof PDFString || v instanceof PDFHexString) return [v];
|
||||
|
||||
if (v instanceof PDFArray) {
|
||||
const values: (PDFString | PDFHexString)[] = [];
|
||||
|
||||
for (let idx = 0, len = v.size(); idx < len; idx++) {
|
||||
const value = v.lookup(idx);
|
||||
if (value instanceof PDFString || value instanceof PDFHexString) {
|
||||
values.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
Opt(): PDFArray | PDFString | PDFHexString | undefined {
|
||||
return this.dict.lookupMaybe(
|
||||
PDFName.of('Opt'),
|
||||
PDFString,
|
||||
PDFHexString,
|
||||
PDFArray,
|
||||
);
|
||||
}
|
||||
|
||||
setOptions(
|
||||
options: {
|
||||
value: PDFString | PDFHexString;
|
||||
display?: PDFString | PDFHexString;
|
||||
}[],
|
||||
) {
|
||||
const newOpt = new Array<PDFArray>(options.length);
|
||||
for (let idx = 0, len = options.length; idx < len; idx++) {
|
||||
const { value, display } = options[idx];
|
||||
newOpt[idx] = this.dict.context.obj([value, display || value]);
|
||||
}
|
||||
this.dict.set(PDFName.of('Opt'), this.dict.context.obj(newOpt));
|
||||
}
|
||||
|
||||
getOptions(): {
|
||||
value: PDFString | PDFHexString;
|
||||
display: PDFString | PDFHexString;
|
||||
}[] {
|
||||
const Opt = this.Opt();
|
||||
|
||||
// Not supposed to happen - Opt _should_ always be `PDFArray | undefined`
|
||||
if (Opt instanceof PDFString || Opt instanceof PDFHexString) {
|
||||
return [{ value: Opt, display: Opt }];
|
||||
}
|
||||
|
||||
if (Opt instanceof PDFArray) {
|
||||
const res: {
|
||||
value: PDFString | PDFHexString;
|
||||
display: PDFString | PDFHexString;
|
||||
}[] = [];
|
||||
|
||||
for (let idx = 0, len = Opt.size(); idx < len; idx++) {
|
||||
const item = Opt.lookup(idx);
|
||||
|
||||
// If `item` is a string, use that as both the export and text value
|
||||
if (item instanceof PDFString || item instanceof PDFHexString) {
|
||||
res.push({ value: item, display: item });
|
||||
}
|
||||
|
||||
// If `item` is an array of one, treat it the same as just a string,
|
||||
// if it's an array of two then `item[0]` is the export value and
|
||||
// `item[1]` is the text value
|
||||
if (item instanceof PDFArray) {
|
||||
if (item.size() > 0) {
|
||||
const first = item.lookup(0, PDFString, PDFHexString);
|
||||
const second = item.lookupMaybe(1, PDFString, PDFHexString);
|
||||
res.push({ value: first, display: second || first });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFAcroChoice;
|
||||
22
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroComboBox.ts
generated
vendored
Normal file
22
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroComboBox.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFAcroChoice from 'src/core/acroform/PDFAcroChoice';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import { AcroChoiceFlags } from 'src/core/acroform/flags';
|
||||
|
||||
class PDFAcroComboBox extends PDFAcroChoice {
|
||||
static fromDict = (dict: PDFDict, ref: PDFRef) =>
|
||||
new PDFAcroComboBox(dict, ref);
|
||||
|
||||
static create = (context: PDFContext) => {
|
||||
const dict = context.obj({
|
||||
FT: 'Ch',
|
||||
Ff: AcroChoiceFlags.Combo,
|
||||
Kids: [],
|
||||
});
|
||||
const ref = context.register(dict);
|
||||
return new PDFAcroComboBox(dict, ref);
|
||||
};
|
||||
}
|
||||
|
||||
export default PDFAcroComboBox;
|
||||
167
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroField.ts
generated
vendored
Normal file
167
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroField.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFString from 'src/core/objects/PDFString';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import { findLastMatch } from 'src/utils';
|
||||
import { MissingDAEntryError, MissingTfOperatorError } from 'src/core/errors';
|
||||
|
||||
// Examples:
|
||||
// `/Helv 12 Tf` -> ['Helv', '12']
|
||||
// `/HeBo 8.00 Tf` -> ['HeBo', '8.00']
|
||||
// `/HeBo Tf` -> ['HeBo', undefined]
|
||||
const tfRegex = /\/([^\0\t\n\f\r\ ]+)[\0\t\n\f\r\ ]*(\d*\.\d+|\d+)?[\0\t\n\f\r\ ]+Tf/;
|
||||
|
||||
class PDFAcroField {
|
||||
readonly dict: PDFDict;
|
||||
readonly ref: PDFRef;
|
||||
|
||||
protected constructor(dict: PDFDict, ref: PDFRef) {
|
||||
this.dict = dict;
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
T(): PDFString | PDFHexString | undefined {
|
||||
return this.dict.lookupMaybe(PDFName.of('T'), PDFString, PDFHexString);
|
||||
}
|
||||
|
||||
Ff(): PDFNumber | undefined {
|
||||
const numberOrRef = this.getInheritableAttribute(PDFName.of('Ff'));
|
||||
return this.dict.context.lookupMaybe(numberOrRef, PDFNumber);
|
||||
}
|
||||
|
||||
V(): PDFObject | undefined {
|
||||
const valueOrRef = this.getInheritableAttribute(PDFName.of('V'));
|
||||
return this.dict.context.lookup(valueOrRef);
|
||||
}
|
||||
|
||||
Kids(): PDFArray | undefined {
|
||||
return this.dict.lookupMaybe(PDFName.of('Kids'), PDFArray);
|
||||
}
|
||||
|
||||
// Parent(): PDFDict | undefined {
|
||||
// return this.dict.lookupMaybe(PDFName.of('Parent'), PDFDict);
|
||||
// }
|
||||
|
||||
DA(): PDFString | PDFHexString | undefined {
|
||||
const da = this.dict.lookup(PDFName.of('DA'));
|
||||
if (da instanceof PDFString || da instanceof PDFHexString) return da;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
setKids(kids: PDFObject[]) {
|
||||
this.dict.set(PDFName.of('Kids'), this.dict.context.obj(kids));
|
||||
}
|
||||
|
||||
getParent(): PDFAcroField | undefined {
|
||||
// const parent = this.Parent();
|
||||
// if (!parent) return undefined;
|
||||
// return new PDFAcroField(parent);
|
||||
|
||||
const parentRef = this.dict.get(PDFName.of('Parent'));
|
||||
if (parentRef instanceof PDFRef) {
|
||||
const parent = this.dict.lookup(PDFName.of('Parent'), PDFDict);
|
||||
return new PDFAcroField(parent, parentRef);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
setParent(parent: PDFRef | undefined) {
|
||||
if (!parent) this.dict.delete(PDFName.of('Parent'));
|
||||
else this.dict.set(PDFName.of('Parent'), parent);
|
||||
}
|
||||
|
||||
getFullyQualifiedName(): string | undefined {
|
||||
const parent = this.getParent();
|
||||
if (!parent) return this.getPartialName();
|
||||
return `${parent.getFullyQualifiedName()}.${this.getPartialName()}`;
|
||||
}
|
||||
|
||||
getPartialName(): string | undefined {
|
||||
return this.T()?.decodeText();
|
||||
}
|
||||
|
||||
setPartialName(partialName: string | undefined) {
|
||||
if (!partialName) this.dict.delete(PDFName.of('T'));
|
||||
else this.dict.set(PDFName.of('T'), PDFHexString.fromText(partialName));
|
||||
}
|
||||
|
||||
setDefaultAppearance(appearance: string) {
|
||||
this.dict.set(PDFName.of('DA'), PDFString.of(appearance));
|
||||
}
|
||||
|
||||
getDefaultAppearance(): string | undefined {
|
||||
const DA = this.DA();
|
||||
|
||||
if (DA instanceof PDFHexString) {
|
||||
return DA.decodeText();
|
||||
}
|
||||
|
||||
return DA?.asString();
|
||||
}
|
||||
|
||||
setFontSize(fontSize: number) {
|
||||
const name = this.getFullyQualifiedName() ?? '';
|
||||
|
||||
const da = this.getDefaultAppearance();
|
||||
if (!da) throw new MissingDAEntryError(name);
|
||||
|
||||
const daMatch = findLastMatch(da, tfRegex);
|
||||
if (!daMatch.match) throw new MissingTfOperatorError(name);
|
||||
|
||||
const daStart = da.slice(0, daMatch.pos - daMatch.match[0].length);
|
||||
const daEnd = daMatch.pos <= da.length ? da.slice(daMatch.pos) : '';
|
||||
const fontName = daMatch.match[1];
|
||||
const modifiedDa = `${daStart} /${fontName} ${fontSize} Tf ${daEnd}`;
|
||||
|
||||
this.setDefaultAppearance(modifiedDa);
|
||||
}
|
||||
|
||||
getFlags(): number {
|
||||
return this.Ff()?.asNumber() ?? 0;
|
||||
}
|
||||
|
||||
setFlags(flags: number) {
|
||||
this.dict.set(PDFName.of('Ff'), PDFNumber.of(flags));
|
||||
}
|
||||
|
||||
hasFlag(flag: number): boolean {
|
||||
const flags = this.getFlags();
|
||||
return (flags & flag) !== 0;
|
||||
}
|
||||
|
||||
setFlag(flag: number) {
|
||||
const flags = this.getFlags();
|
||||
this.setFlags(flags | flag);
|
||||
}
|
||||
|
||||
clearFlag(flag: number) {
|
||||
const flags = this.getFlags();
|
||||
this.setFlags(flags & ~flag);
|
||||
}
|
||||
|
||||
setFlagTo(flag: number, enable: boolean) {
|
||||
if (enable) this.setFlag(flag);
|
||||
else this.clearFlag(flag);
|
||||
}
|
||||
|
||||
getInheritableAttribute(name: PDFName): PDFObject | undefined {
|
||||
let attribute: PDFObject | undefined;
|
||||
this.ascend((node) => {
|
||||
if (!attribute) attribute = node.dict.get(name);
|
||||
});
|
||||
return attribute;
|
||||
}
|
||||
|
||||
ascend(visitor: (node: PDFAcroField) => any): void {
|
||||
visitor(this);
|
||||
const parent = this.getParent();
|
||||
if (parent) parent.ascend(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFAcroField;
|
||||
102
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroForm.ts
generated
vendored
Normal file
102
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroForm.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFAcroField from 'src/core/acroform/PDFAcroField';
|
||||
import PDFAcroNonTerminal from 'src/core/acroform/PDFAcroNonTerminal';
|
||||
import {
|
||||
createPDFAcroField,
|
||||
createPDFAcroFields,
|
||||
} from 'src/core/acroform/utils';
|
||||
|
||||
class PDFAcroForm {
|
||||
readonly dict: PDFDict;
|
||||
|
||||
static fromDict = (dict: PDFDict) => new PDFAcroForm(dict);
|
||||
|
||||
static create = (context: PDFContext) => {
|
||||
const dict = context.obj({ Fields: [] });
|
||||
return new PDFAcroForm(dict);
|
||||
};
|
||||
|
||||
private constructor(dict: PDFDict) {
|
||||
this.dict = dict;
|
||||
}
|
||||
|
||||
Fields(): PDFArray | undefined {
|
||||
const fields = this.dict.lookup(PDFName.of('Fields'));
|
||||
if (fields instanceof PDFArray) return fields;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getFields(): [PDFAcroField, PDFRef][] {
|
||||
const { Fields } = this.normalizedEntries();
|
||||
|
||||
const fields = new Array(Fields.size());
|
||||
for (let idx = 0, len = Fields.size(); idx < len; idx++) {
|
||||
const ref = Fields.get(idx) as PDFRef;
|
||||
const dict = Fields.lookup(idx, PDFDict);
|
||||
fields[idx] = [createPDFAcroField(dict, ref), ref];
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
getAllFields(): [PDFAcroField, PDFRef][] {
|
||||
const allFields: [PDFAcroField, PDFRef][] = [];
|
||||
|
||||
const pushFields = (fields?: [PDFAcroField, PDFRef][]) => {
|
||||
if (!fields) return;
|
||||
for (let idx = 0, len = fields.length; idx < len; idx++) {
|
||||
const field = fields[idx];
|
||||
allFields.push(field);
|
||||
const [fieldModel] = field;
|
||||
if (fieldModel instanceof PDFAcroNonTerminal) {
|
||||
pushFields(createPDFAcroFields(fieldModel.Kids()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pushFields(this.getFields());
|
||||
|
||||
return allFields;
|
||||
}
|
||||
|
||||
addField(field: PDFRef) {
|
||||
const { Fields } = this.normalizedEntries();
|
||||
Fields?.push(field);
|
||||
}
|
||||
|
||||
removeField(field: PDFAcroField): void {
|
||||
const parent = field.getParent();
|
||||
const fields =
|
||||
parent === undefined ? this.normalizedEntries().Fields : parent.Kids();
|
||||
|
||||
const index = fields?.indexOf(field.ref);
|
||||
if (fields === undefined || index === undefined) {
|
||||
throw new Error(
|
||||
`Tried to remove inexistent field ${field.getFullyQualifiedName()}`,
|
||||
);
|
||||
}
|
||||
|
||||
fields.remove(index);
|
||||
|
||||
if (parent !== undefined && fields.size() === 0) {
|
||||
this.removeField(parent);
|
||||
}
|
||||
}
|
||||
|
||||
normalizedEntries() {
|
||||
let Fields = this.Fields();
|
||||
|
||||
if (!Fields) {
|
||||
Fields = this.dict.context.obj([]);
|
||||
this.dict.set(PDFName.of('Fields'), Fields);
|
||||
}
|
||||
|
||||
return { Fields };
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFAcroForm;
|
||||
20
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroListBox.ts
generated
vendored
Normal file
20
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroListBox.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFAcroChoice from 'src/core/acroform/PDFAcroChoice';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
|
||||
class PDFAcroListBox extends PDFAcroChoice {
|
||||
static fromDict = (dict: PDFDict, ref: PDFRef) =>
|
||||
new PDFAcroListBox(dict, ref);
|
||||
|
||||
static create = (context: PDFContext) => {
|
||||
const dict = context.obj({
|
||||
FT: 'Ch',
|
||||
Kids: [],
|
||||
});
|
||||
const ref = context.register(dict);
|
||||
return new PDFAcroListBox(dict, ref);
|
||||
};
|
||||
}
|
||||
|
||||
export default PDFAcroListBox;
|
||||
34
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroNonTerminal.ts
generated
vendored
Normal file
34
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroNonTerminal.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFAcroField from 'src/core/acroform/PDFAcroField';
|
||||
|
||||
class PDFAcroNonTerminal extends PDFAcroField {
|
||||
static fromDict = (dict: PDFDict, ref: PDFRef) =>
|
||||
new PDFAcroNonTerminal(dict, ref);
|
||||
|
||||
static create = (context: PDFContext) => {
|
||||
const dict = context.obj({});
|
||||
const ref = context.register(dict);
|
||||
return new PDFAcroNonTerminal(dict, ref);
|
||||
};
|
||||
|
||||
addField(field: PDFRef) {
|
||||
const { Kids } = this.normalizedEntries();
|
||||
Kids?.push(field);
|
||||
}
|
||||
|
||||
normalizedEntries() {
|
||||
let Kids = this.Kids();
|
||||
|
||||
if (!Kids) {
|
||||
Kids = this.dict.context.obj([]);
|
||||
this.dict.set(PDFName.of('Kids'), Kids);
|
||||
}
|
||||
|
||||
return { Kids };
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFAcroNonTerminal;
|
||||
22
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroPushButton.ts
generated
vendored
Normal file
22
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroPushButton.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFAcroButton from 'src/core/acroform/PDFAcroButton';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import { AcroButtonFlags } from 'src/core/acroform/flags';
|
||||
|
||||
class PDFAcroPushButton extends PDFAcroButton {
|
||||
static fromDict = (dict: PDFDict, ref: PDFRef) =>
|
||||
new PDFAcroPushButton(dict, ref);
|
||||
|
||||
static create = (context: PDFContext) => {
|
||||
const dict = context.obj({
|
||||
FT: 'Btn',
|
||||
Ff: AcroButtonFlags.PushButton,
|
||||
Kids: [],
|
||||
});
|
||||
const ref = context.register(dict);
|
||||
return new PDFAcroPushButton(dict, ref);
|
||||
};
|
||||
}
|
||||
|
||||
export default PDFAcroPushButton;
|
||||
58
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroRadioButton.ts
generated
vendored
Normal file
58
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroRadioButton.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFAcroButton from 'src/core/acroform/PDFAcroButton';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import { AcroButtonFlags } from 'src/core/acroform/flags';
|
||||
import { InvalidAcroFieldValueError } from 'src/core/errors';
|
||||
|
||||
class PDFAcroRadioButton extends PDFAcroButton {
|
||||
static fromDict = (dict: PDFDict, ref: PDFRef) =>
|
||||
new PDFAcroRadioButton(dict, ref);
|
||||
|
||||
static create = (context: PDFContext) => {
|
||||
const dict = context.obj({
|
||||
FT: 'Btn',
|
||||
Ff: AcroButtonFlags.Radio,
|
||||
Kids: [],
|
||||
});
|
||||
const ref = context.register(dict);
|
||||
return new PDFAcroRadioButton(dict, ref);
|
||||
};
|
||||
|
||||
setValue(value: PDFName) {
|
||||
const onValues = this.getOnValues();
|
||||
if (!onValues.includes(value) && value !== PDFName.of('Off')) {
|
||||
throw new InvalidAcroFieldValueError();
|
||||
}
|
||||
|
||||
this.dict.set(PDFName.of('V'), value);
|
||||
|
||||
const widgets = this.getWidgets();
|
||||
for (let idx = 0, len = widgets.length; idx < len; idx++) {
|
||||
const widget = widgets[idx];
|
||||
const state = widget.getOnValue() === value ? value : PDFName.of('Off');
|
||||
widget.setAppearanceState(state);
|
||||
}
|
||||
}
|
||||
|
||||
getValue(): PDFName {
|
||||
const v = this.V();
|
||||
if (v instanceof PDFName) return v;
|
||||
return PDFName.of('Off');
|
||||
}
|
||||
|
||||
getOnValues(): PDFName[] {
|
||||
const widgets = this.getWidgets();
|
||||
|
||||
const onValues: PDFName[] = [];
|
||||
for (let idx = 0, len = widgets.length; idx < len; idx++) {
|
||||
const onValue = widgets[idx].getOnValue();
|
||||
if (onValue) onValues.push(onValue);
|
||||
}
|
||||
|
||||
return onValues;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFAcroRadioButton;
|
||||
10
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroSignature.ts
generated
vendored
Normal file
10
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroSignature.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFAcroTerminal from 'src/core/acroform/PDFAcroTerminal';
|
||||
|
||||
class PDFAcroSignature extends PDFAcroTerminal {
|
||||
static fromDict = (dict: PDFDict, ref: PDFRef) =>
|
||||
new PDFAcroSignature(dict, ref);
|
||||
}
|
||||
|
||||
export default PDFAcroSignature;
|
||||
71
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroTerminal.ts
generated
vendored
Normal file
71
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroTerminal.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFAcroField from 'src/core/acroform/PDFAcroField';
|
||||
import PDFWidgetAnnotation from 'src/core/annotation/PDFWidgetAnnotation';
|
||||
import { IndexOutOfBoundsError } from 'src/core/errors';
|
||||
|
||||
class PDFAcroTerminal extends PDFAcroField {
|
||||
static fromDict = (dict: PDFDict, ref: PDFRef) =>
|
||||
new PDFAcroTerminal(dict, ref);
|
||||
|
||||
FT(): PDFName {
|
||||
const nameOrRef = this.getInheritableAttribute(PDFName.of('FT'));
|
||||
return this.dict.context.lookup(nameOrRef, PDFName);
|
||||
}
|
||||
|
||||
getWidgets(): PDFWidgetAnnotation[] {
|
||||
const kidDicts = this.Kids();
|
||||
|
||||
// This field is itself a widget
|
||||
if (!kidDicts) return [PDFWidgetAnnotation.fromDict(this.dict)];
|
||||
|
||||
// This field's kids are its widgets
|
||||
const widgets = new Array<PDFWidgetAnnotation>(kidDicts.size());
|
||||
for (let idx = 0, len = kidDicts.size(); idx < len; idx++) {
|
||||
const dict = kidDicts.lookup(idx, PDFDict);
|
||||
widgets[idx] = PDFWidgetAnnotation.fromDict(dict);
|
||||
}
|
||||
|
||||
return widgets;
|
||||
}
|
||||
|
||||
addWidget(ref: PDFRef) {
|
||||
const { Kids } = this.normalizedEntries();
|
||||
Kids.push(ref);
|
||||
}
|
||||
|
||||
removeWidget(idx: number) {
|
||||
const kidDicts = this.Kids();
|
||||
|
||||
if (!kidDicts) {
|
||||
// This field is itself a widget
|
||||
if (idx !== 0) throw new IndexOutOfBoundsError(idx, 0, 0);
|
||||
this.setKids([]);
|
||||
} else {
|
||||
// This field's kids are its widgets
|
||||
if (idx < 0 || idx > kidDicts.size()) {
|
||||
throw new IndexOutOfBoundsError(idx, 0, kidDicts.size());
|
||||
}
|
||||
kidDicts.remove(idx);
|
||||
}
|
||||
}
|
||||
|
||||
normalizedEntries() {
|
||||
let Kids = this.Kids();
|
||||
|
||||
// If this field is itself a widget (because it was only rendered once in
|
||||
// the document, so the field and widget properties were merged) then we
|
||||
// add itself to the `Kids` array. The alternative would be to try
|
||||
// splitting apart the widget properties and creating a separate object
|
||||
// for them.
|
||||
if (!Kids) {
|
||||
Kids = this.dict.context.obj([this.ref]);
|
||||
this.dict.set(PDFName.of('Kids'), Kids);
|
||||
}
|
||||
|
||||
return { Kids };
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFAcroTerminal;
|
||||
76
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroText.ts
generated
vendored
Normal file
76
frontend/node_modules/pdf-lib/src/core/acroform/PDFAcroText.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFString from 'src/core/objects/PDFString';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFAcroTerminal from 'src/core/acroform/PDFAcroTerminal';
|
||||
|
||||
class PDFAcroText extends PDFAcroTerminal {
|
||||
static fromDict = (dict: PDFDict, ref: PDFRef) => new PDFAcroText(dict, ref);
|
||||
|
||||
static create = (context: PDFContext) => {
|
||||
const dict = context.obj({
|
||||
FT: 'Tx',
|
||||
Kids: [],
|
||||
});
|
||||
const ref = context.register(dict);
|
||||
return new PDFAcroText(dict, ref);
|
||||
};
|
||||
|
||||
MaxLen(): PDFNumber | undefined {
|
||||
const maxLen = this.dict.lookup(PDFName.of('MaxLen'));
|
||||
if (maxLen instanceof PDFNumber) return maxLen;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Q(): PDFNumber | undefined {
|
||||
const q = this.dict.lookup(PDFName.of('Q'));
|
||||
if (q instanceof PDFNumber) return q;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
setMaxLength(maxLength: number) {
|
||||
this.dict.set(PDFName.of('MaxLen'), PDFNumber.of(maxLength));
|
||||
}
|
||||
|
||||
removeMaxLength() {
|
||||
this.dict.delete(PDFName.of('MaxLen'));
|
||||
}
|
||||
|
||||
getMaxLength(): number | undefined {
|
||||
return this.MaxLen()?.asNumber();
|
||||
}
|
||||
|
||||
setQuadding(quadding: 0 | 1 | 2) {
|
||||
this.dict.set(PDFName.of('Q'), PDFNumber.of(quadding));
|
||||
}
|
||||
|
||||
getQuadding(): number | undefined {
|
||||
return this.Q()?.asNumber();
|
||||
}
|
||||
|
||||
setValue(value: PDFHexString | PDFString) {
|
||||
this.dict.set(PDFName.of('V'), value);
|
||||
|
||||
// const widgets = this.getWidgets();
|
||||
// for (let idx = 0, len = widgets.length; idx < len; idx++) {
|
||||
// const widget = widgets[idx];
|
||||
// const state = widget.getOnValue() === value ? value : PDFName.of('Off');
|
||||
// widget.setAppearanceState(state);
|
||||
// }
|
||||
}
|
||||
|
||||
removeValue() {
|
||||
this.dict.delete(PDFName.of('V'));
|
||||
}
|
||||
|
||||
getValue(): PDFString | PDFHexString | undefined {
|
||||
const v = this.V();
|
||||
if (v instanceof PDFString || v instanceof PDFHexString) return v;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFAcroText;
|
||||
162
frontend/node_modules/pdf-lib/src/core/acroform/flags.ts
generated
vendored
Normal file
162
frontend/node_modules/pdf-lib/src/core/acroform/flags.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
const flag = (bitIndex: number) => 1 << bitIndex;
|
||||
|
||||
/** From PDF spec table 221 */
|
||||
export enum AcroFieldFlags {
|
||||
/**
|
||||
* If set, the user may not change the value of the field. Any associated
|
||||
* widget annotations will not interact with the user; that is, they will not
|
||||
* respond to mouse clicks or change their appearance in response to mouse
|
||||
* motions. This flag is useful for fields whose values are computed or
|
||||
* imported from a database.
|
||||
*/
|
||||
ReadOnly = flag(1 - 1),
|
||||
|
||||
/**
|
||||
* If set, the field shall have a value at the time it is exported by a
|
||||
* submit-form action (see 12.7.5.2, "Submit-Form Action").
|
||||
*/
|
||||
Required = flag(2 - 1),
|
||||
|
||||
/**
|
||||
* If set, the field shall not be exported by a submit-form action
|
||||
* (see 12.7.5.2, "Submit-Form Action").
|
||||
*/
|
||||
NoExport = flag(3 - 1),
|
||||
}
|
||||
|
||||
/** From PDF spec table 226 */
|
||||
export enum AcroButtonFlags {
|
||||
/**
|
||||
* (Radio buttons only) If set, exactly one radio button shall be selected at
|
||||
* all times; selecting the currently selected button has no effect. If clear,
|
||||
* clicking the selected button deselects it, leaving no button selected.
|
||||
*/
|
||||
NoToggleToOff = flag(15 - 1),
|
||||
|
||||
/**
|
||||
* If set, the field is a set of radio buttons; if clear, the field is a check
|
||||
* box. This flag may be set only if the Pushbutton flag is clear.
|
||||
*/
|
||||
Radio = flag(16 - 1),
|
||||
|
||||
/**
|
||||
* If set, the field is a pushbutton that does not retain a permanent value.
|
||||
*/
|
||||
PushButton = flag(17 - 1),
|
||||
|
||||
/**
|
||||
* If set, a group of radio buttons within a radio button field that use the
|
||||
* same value for the on state will turn on and off in unison; that is if one
|
||||
* is checked, they are all checked. If clear, the buttons are mutually
|
||||
* exclusive (the same behavior as HTML radio buttons).
|
||||
*/
|
||||
RadiosInUnison = flag(26 - 1),
|
||||
}
|
||||
|
||||
/** From PDF spec table 228 */
|
||||
export enum AcroTextFlags {
|
||||
/**
|
||||
* If set, the field may contain multiple lines of text; if clear, the field's
|
||||
* text shall be restricted to a single line.
|
||||
*/
|
||||
Multiline = flag(13 - 1),
|
||||
|
||||
/**
|
||||
* If set, the field is intended for entering a secure password that should
|
||||
* not be echoed visibly to the screen. Characters typed from the keyboard
|
||||
* shall instead be echoed in some unreadable form, such as asterisks or
|
||||
* bullet characters.
|
||||
* > NOTE To protect password confidentiality, readers should never store
|
||||
* > the value of the text field in the PDF file if this flag is set.
|
||||
*/
|
||||
Password = flag(14 - 1),
|
||||
|
||||
/**
|
||||
* If set, the text entered in the field represents the pathname of a file
|
||||
* whose contents shall be submitted as the value of the field.
|
||||
*/
|
||||
FileSelect = flag(21 - 1),
|
||||
|
||||
/**
|
||||
* If set, text entered in the field shall not be spell-checked.
|
||||
*/
|
||||
DoNotSpellCheck = flag(23 - 1),
|
||||
|
||||
/**
|
||||
* If set, the field shall not scroll (horizontally for single-line fields,
|
||||
* vertically for multiple-line fields) to accommodate more text than fits
|
||||
* within its annotation rectangle. Once the field is full, no further text
|
||||
* shall be accepted for interactive form filling; for non-interactive form
|
||||
* filling, the filler should take care not to add more character than will
|
||||
* visibly fit in the defined area.
|
||||
*/
|
||||
DoNotScroll = flag(24 - 1),
|
||||
|
||||
/**
|
||||
* May be set only if the MaxLen entry is present in the text field dictionary
|
||||
* (see Table 229) and if the Multiline, Password, and FileSelect flags are
|
||||
* clear. If set, the field shall be automatically divided into as many
|
||||
* equally spaced positions, or combs, as the value of MaxLen, and the text
|
||||
* is laid out into those combs.
|
||||
*/
|
||||
Comb = flag(25 - 1),
|
||||
|
||||
/**
|
||||
* If set, the value of this field shall be a rich text string
|
||||
* (see 12.7.3.4, "Rich Text Strings"). If the field has a value, the RV
|
||||
* entry of the field dictionary (Table 222) shall specify the rich text
|
||||
* string.
|
||||
*/
|
||||
RichText = flag(26 - 1),
|
||||
}
|
||||
|
||||
/** From PDF spec table 230 */
|
||||
export enum AcroChoiceFlags {
|
||||
/**
|
||||
* If set, the field is a combo box; if clear, the field is a list box.
|
||||
*/
|
||||
Combo = flag(18 - 1),
|
||||
|
||||
/**
|
||||
* If set, the combo box shall include an editable text box as well as a
|
||||
* drop-down list; if clear, it shall include only a drop-down list. This
|
||||
* flag shall be used only if the Combo flag is set.
|
||||
*/
|
||||
Edit = flag(19 - 1),
|
||||
|
||||
/**
|
||||
* If set, the field's option items shall be sorted alphabetically. This flag
|
||||
* is intended for use by writers, not by readers. Conforming readers shall
|
||||
* display the options in the order in which they occur in the Opt array
|
||||
* (see Table 231).
|
||||
*/
|
||||
Sort = flag(20 - 1),
|
||||
|
||||
/**
|
||||
* If set, more than one of the field's option items may be selected
|
||||
* simultaneously; if clear, at most one item shall be selected.
|
||||
*/
|
||||
MultiSelect = flag(22 - 1),
|
||||
|
||||
/**
|
||||
* If set, text entered in the field shall not be spell-checked. This flag
|
||||
* shall not be used unless the Combo and Edit flags are both set.
|
||||
*/
|
||||
DoNotSpellCheck = flag(23 - 1),
|
||||
|
||||
/**
|
||||
* If set, the new value shall be committed as soon as a selection is made
|
||||
* (commonly with the pointing device). In this case, supplying a value for
|
||||
* a field involves three actions: selecting the field for fill-in,
|
||||
* selecting a choice for the fill-in value, and leaving that field, which
|
||||
* finalizes or "commits" the data choice and triggers any actions associated
|
||||
* with the entry or changing of this data. If this flag is on, then
|
||||
* processing does not wait for leaving the field action to occur, but
|
||||
* immediately proceeds to the third step.
|
||||
*
|
||||
* This option enables applications to perform an action once a selection is
|
||||
* made, without requiring the user to exit the field. If clear, the new
|
||||
* value is not committed until the user exits the field.
|
||||
*/
|
||||
CommitOnSelChange = flag(27 - 1),
|
||||
}
|
||||
15
frontend/node_modules/pdf-lib/src/core/acroform/index.ts
generated
vendored
Normal file
15
frontend/node_modules/pdf-lib/src/core/acroform/index.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
export { default as PDFAcroButton } from 'src/core/acroform/PDFAcroButton';
|
||||
export { default as PDFAcroCheckBox } from 'src/core/acroform/PDFAcroCheckBox';
|
||||
export { default as PDFAcroChoice } from 'src/core/acroform/PDFAcroChoice';
|
||||
export { default as PDFAcroComboBox } from 'src/core/acroform/PDFAcroComboBox';
|
||||
export { default as PDFAcroField } from 'src/core/acroform/PDFAcroField';
|
||||
export { default as PDFAcroForm } from 'src/core/acroform/PDFAcroForm';
|
||||
export { default as PDFAcroListBox } from 'src/core/acroform/PDFAcroListBox';
|
||||
export { default as PDFAcroNonTerminal } from 'src/core/acroform/PDFAcroNonTerminal';
|
||||
export { default as PDFAcroPushButton } from 'src/core/acroform/PDFAcroPushButton';
|
||||
export { default as PDFAcroRadioButton } from 'src/core/acroform/PDFAcroRadioButton';
|
||||
export { default as PDFAcroSignature } from 'src/core/acroform/PDFAcroSignature';
|
||||
export { default as PDFAcroTerminal } from 'src/core/acroform/PDFAcroTerminal';
|
||||
export { default as PDFAcroText } from 'src/core/acroform/PDFAcroText';
|
||||
export * from 'src/core/acroform/flags';
|
||||
export * from 'src/core/acroform/utils';
|
||||
135
frontend/node_modules/pdf-lib/src/core/acroform/utils.ts
generated
vendored
Normal file
135
frontend/node_modules/pdf-lib/src/core/acroform/utils.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
|
||||
import PDFAcroField from 'src/core/acroform/PDFAcroField';
|
||||
import PDFAcroTerminal from 'src/core/acroform/PDFAcroTerminal';
|
||||
import PDFAcroNonTerminal from 'src/core/acroform/PDFAcroNonTerminal';
|
||||
import PDFAcroButton from 'src/core/acroform/PDFAcroButton';
|
||||
import PDFAcroSignature from 'src/core/acroform/PDFAcroSignature';
|
||||
import PDFAcroChoice from 'src/core/acroform/PDFAcroChoice';
|
||||
import PDFAcroText from 'src/core/acroform/PDFAcroText';
|
||||
import PDFAcroPushButton from 'src/core/acroform/PDFAcroPushButton';
|
||||
import PDFAcroRadioButton from 'src/core/acroform/PDFAcroRadioButton';
|
||||
import PDFAcroCheckBox from 'src/core/acroform/PDFAcroCheckBox';
|
||||
import PDFAcroComboBox from 'src/core/acroform/PDFAcroComboBox';
|
||||
import PDFAcroListBox from 'src/core/acroform/PDFAcroListBox';
|
||||
import { AcroButtonFlags, AcroChoiceFlags } from 'src/core/acroform/flags';
|
||||
|
||||
export const createPDFAcroFields = (
|
||||
kidDicts?: PDFArray,
|
||||
): [PDFAcroField, PDFRef][] => {
|
||||
if (!kidDicts) return [];
|
||||
|
||||
const kids: [PDFAcroField, PDFRef][] = [];
|
||||
for (let idx = 0, len = kidDicts.size(); idx < len; idx++) {
|
||||
const ref = kidDicts.get(idx);
|
||||
const dict = kidDicts.lookup(idx);
|
||||
// if (dict instanceof PDFDict) kids.push(PDFAcroField.fromDict(dict));
|
||||
if (ref instanceof PDFRef && dict instanceof PDFDict) {
|
||||
kids.push([createPDFAcroField(dict, ref), ref]);
|
||||
}
|
||||
}
|
||||
|
||||
return kids;
|
||||
};
|
||||
|
||||
export const createPDFAcroField = (
|
||||
dict: PDFDict,
|
||||
ref: PDFRef,
|
||||
): PDFAcroField => {
|
||||
const isNonTerminal = isNonTerminalAcroField(dict);
|
||||
if (isNonTerminal) return PDFAcroNonTerminal.fromDict(dict, ref);
|
||||
return createPDFAcroTerminal(dict, ref);
|
||||
};
|
||||
|
||||
// TODO: Maybe just check if the dict is *not* a widget? That might be better.
|
||||
|
||||
// According to the PDF spec:
|
||||
//
|
||||
// > A field's children in the hierarchy may also include widget annotations
|
||||
// > that define its appearance on the page. A field that has children that
|
||||
// > are fields is called a non-terminal field. A field that does not have
|
||||
// > children that are fields is called a terminal field.
|
||||
//
|
||||
// The spec is not entirely clear about how to determine whether a given
|
||||
// dictionary represents an acrofield or a widget annotation. So we will assume
|
||||
// that a dictionary is an acrofield if it is a member of the `/Kids` array
|
||||
// and it contains a `/T` entry (widgets do not have `/T` entries). This isn't
|
||||
// a bullet proof solution, because the `/T` entry is technically defined as
|
||||
// optional for acrofields by the PDF spec. But in practice all acrofields seem
|
||||
// to have a `/T` entry defined.
|
||||
const isNonTerminalAcroField = (dict: PDFDict): boolean => {
|
||||
const kids = dict.lookup(PDFName.of('Kids'));
|
||||
|
||||
if (kids instanceof PDFArray) {
|
||||
for (let idx = 0, len = kids.size(); idx < len; idx++) {
|
||||
const kid = kids.lookup(idx);
|
||||
const kidIsField = kid instanceof PDFDict && kid.has(PDFName.of('T'));
|
||||
if (kidIsField) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const createPDFAcroTerminal = (dict: PDFDict, ref: PDFRef): PDFAcroTerminal => {
|
||||
const ftNameOrRef = getInheritableAttribute(dict, PDFName.of('FT'));
|
||||
const type = dict.context.lookup(ftNameOrRef, PDFName);
|
||||
|
||||
if (type === PDFName.of('Btn')) return createPDFAcroButton(dict, ref);
|
||||
if (type === PDFName.of('Ch')) return createPDFAcroChoice(dict, ref);
|
||||
if (type === PDFName.of('Tx')) return PDFAcroText.fromDict(dict, ref);
|
||||
if (type === PDFName.of('Sig')) return PDFAcroSignature.fromDict(dict, ref);
|
||||
|
||||
// We should never reach this line. But there are a lot of weird PDFs out
|
||||
// there. So, just to be safe, we'll try to handle things gracefully instead
|
||||
// of throwing an error.
|
||||
return PDFAcroTerminal.fromDict(dict, ref);
|
||||
};
|
||||
|
||||
const createPDFAcroButton = (dict: PDFDict, ref: PDFRef): PDFAcroButton => {
|
||||
const ffNumberOrRef = getInheritableAttribute(dict, PDFName.of('Ff'));
|
||||
const ffNumber = dict.context.lookupMaybe(ffNumberOrRef, PDFNumber);
|
||||
const flags = ffNumber?.asNumber() ?? 0;
|
||||
|
||||
if (flagIsSet(flags, AcroButtonFlags.PushButton)) {
|
||||
return PDFAcroPushButton.fromDict(dict, ref);
|
||||
} else if (flagIsSet(flags, AcroButtonFlags.Radio)) {
|
||||
return PDFAcroRadioButton.fromDict(dict, ref);
|
||||
} else {
|
||||
return PDFAcroCheckBox.fromDict(dict, ref);
|
||||
}
|
||||
};
|
||||
|
||||
const createPDFAcroChoice = (dict: PDFDict, ref: PDFRef): PDFAcroChoice => {
|
||||
const ffNumberOrRef = getInheritableAttribute(dict, PDFName.of('Ff'));
|
||||
const ffNumber = dict.context.lookupMaybe(ffNumberOrRef, PDFNumber);
|
||||
const flags = ffNumber?.asNumber() ?? 0;
|
||||
|
||||
if (flagIsSet(flags, AcroChoiceFlags.Combo)) {
|
||||
return PDFAcroComboBox.fromDict(dict, ref);
|
||||
} else {
|
||||
return PDFAcroListBox.fromDict(dict, ref);
|
||||
}
|
||||
};
|
||||
|
||||
const flagIsSet = (flags: number, flag: number): boolean =>
|
||||
(flags & flag) !== 0;
|
||||
|
||||
const getInheritableAttribute = (startNode: PDFDict, name: PDFName) => {
|
||||
let attribute: PDFObject | undefined;
|
||||
ascend(startNode, (node) => {
|
||||
if (!attribute) attribute = node.get(name);
|
||||
});
|
||||
return attribute;
|
||||
};
|
||||
|
||||
const ascend = (startNode: PDFDict, visitor: (node: PDFDict) => any) => {
|
||||
visitor(startNode);
|
||||
const Parent = startNode.lookupMaybe(PDFName.of('Parent'), PDFDict);
|
||||
if (Parent) ascend(Parent, visitor);
|
||||
};
|
||||
133
frontend/node_modules/pdf-lib/src/core/annotation/AppearanceCharacteristics.ts
generated
vendored
Normal file
133
frontend/node_modules/pdf-lib/src/core/annotation/AppearanceCharacteristics.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFString from 'src/core/objects/PDFString';
|
||||
|
||||
class AppearanceCharacteristics {
|
||||
readonly dict: PDFDict;
|
||||
|
||||
static fromDict = (dict: PDFDict): AppearanceCharacteristics =>
|
||||
new AppearanceCharacteristics(dict);
|
||||
|
||||
protected constructor(dict: PDFDict) {
|
||||
this.dict = dict;
|
||||
}
|
||||
|
||||
R(): PDFNumber | undefined {
|
||||
const R = this.dict.lookup(PDFName.of('R'));
|
||||
if (R instanceof PDFNumber) return R;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
BC(): PDFArray | undefined {
|
||||
const BC = this.dict.lookup(PDFName.of('BC'));
|
||||
if (BC instanceof PDFArray) return BC;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
BG(): PDFArray | undefined {
|
||||
const BG = this.dict.lookup(PDFName.of('BG'));
|
||||
if (BG instanceof PDFArray) return BG;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
CA(): PDFHexString | PDFString | undefined {
|
||||
const CA = this.dict.lookup(PDFName.of('CA'));
|
||||
if (CA instanceof PDFHexString || CA instanceof PDFString) return CA;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
RC(): PDFHexString | PDFString | undefined {
|
||||
const RC = this.dict.lookup(PDFName.of('RC'));
|
||||
if (RC instanceof PDFHexString || RC instanceof PDFString) return RC;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
AC(): PDFHexString | PDFString | undefined {
|
||||
const AC = this.dict.lookup(PDFName.of('AC'));
|
||||
if (AC instanceof PDFHexString || AC instanceof PDFString) return AC;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getRotation(): number | undefined {
|
||||
return this.R()?.asNumber();
|
||||
}
|
||||
|
||||
getBorderColor(): number[] | undefined {
|
||||
const BC = this.BC();
|
||||
|
||||
if (!BC) return undefined;
|
||||
|
||||
const components: number[] = [];
|
||||
for (let idx = 0, len = BC?.size(); idx < len; idx++) {
|
||||
const component = BC.get(idx);
|
||||
if (component instanceof PDFNumber) components.push(component.asNumber());
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
getBackgroundColor(): number[] | undefined {
|
||||
const BG = this.BG();
|
||||
|
||||
if (!BG) return undefined;
|
||||
|
||||
const components: number[] = [];
|
||||
for (let idx = 0, len = BG?.size(); idx < len; idx++) {
|
||||
const component = BG.get(idx);
|
||||
if (component instanceof PDFNumber) components.push(component.asNumber());
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
getCaptions(): { normal?: string; rollover?: string; down?: string } {
|
||||
const CA = this.CA();
|
||||
const RC = this.RC();
|
||||
const AC = this.AC();
|
||||
|
||||
return {
|
||||
normal: CA?.decodeText(),
|
||||
rollover: RC?.decodeText(),
|
||||
down: AC?.decodeText(),
|
||||
};
|
||||
}
|
||||
|
||||
setRotation(rotation: number) {
|
||||
const R = this.dict.context.obj(rotation);
|
||||
this.dict.set(PDFName.of('R'), R);
|
||||
}
|
||||
|
||||
setBorderColor(color: number[]) {
|
||||
const BC = this.dict.context.obj(color);
|
||||
this.dict.set(PDFName.of('BC'), BC);
|
||||
}
|
||||
|
||||
setBackgroundColor(color: number[]) {
|
||||
const BG = this.dict.context.obj(color);
|
||||
this.dict.set(PDFName.of('BG'), BG);
|
||||
}
|
||||
|
||||
setCaptions(captions: { normal: string; rollover?: string; down?: string }) {
|
||||
const CA = PDFHexString.fromText(captions.normal);
|
||||
this.dict.set(PDFName.of('CA'), CA);
|
||||
|
||||
if (captions.rollover) {
|
||||
const RC = PDFHexString.fromText(captions.rollover);
|
||||
this.dict.set(PDFName.of('RC'), RC);
|
||||
} else {
|
||||
this.dict.delete(PDFName.of('RC'));
|
||||
}
|
||||
|
||||
if (captions.down) {
|
||||
const AC = PDFHexString.fromText(captions.down);
|
||||
this.dict.set(PDFName.of('AC'), AC);
|
||||
} else {
|
||||
this.dict.delete(PDFName.of('AC'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AppearanceCharacteristics;
|
||||
31
frontend/node_modules/pdf-lib/src/core/annotation/BorderStyle.ts
generated
vendored
Normal file
31
frontend/node_modules/pdf-lib/src/core/annotation/BorderStyle.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
|
||||
// TODO: Also handle the `/S` and `/D` entries
|
||||
class BorderStyle {
|
||||
readonly dict: PDFDict;
|
||||
|
||||
static fromDict = (dict: PDFDict): BorderStyle => new BorderStyle(dict);
|
||||
|
||||
protected constructor(dict: PDFDict) {
|
||||
this.dict = dict;
|
||||
}
|
||||
|
||||
W(): PDFNumber | undefined {
|
||||
const W = this.dict.lookup(PDFName.of('W'));
|
||||
if (W instanceof PDFNumber) return W;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getWidth(): number | undefined {
|
||||
return this.W()?.asNumber() ?? 1;
|
||||
}
|
||||
|
||||
setWidth(width: number) {
|
||||
const W = this.dict.context.obj(width);
|
||||
this.dict.set(PDFName.of('W'), W);
|
||||
}
|
||||
}
|
||||
|
||||
export default BorderStyle;
|
||||
148
frontend/node_modules/pdf-lib/src/core/annotation/PDFAnnotation.ts
generated
vendored
Normal file
148
frontend/node_modules/pdf-lib/src/core/annotation/PDFAnnotation.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFStream from 'src/core/objects/PDFStream';
|
||||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
|
||||
class PDFAnnotation {
|
||||
readonly dict: PDFDict;
|
||||
|
||||
static fromDict = (dict: PDFDict): PDFAnnotation => new PDFAnnotation(dict);
|
||||
|
||||
protected constructor(dict: PDFDict) {
|
||||
this.dict = dict;
|
||||
}
|
||||
|
||||
// This is technically required by the PDF spec
|
||||
Rect(): PDFArray | undefined {
|
||||
return this.dict.lookup(PDFName.of('Rect'), PDFArray);
|
||||
}
|
||||
|
||||
AP(): PDFDict | undefined {
|
||||
return this.dict.lookupMaybe(PDFName.of('AP'), PDFDict);
|
||||
}
|
||||
|
||||
F(): PDFNumber | undefined {
|
||||
const numberOrRef = this.dict.lookup(PDFName.of('F'));
|
||||
return this.dict.context.lookupMaybe(numberOrRef, PDFNumber);
|
||||
}
|
||||
|
||||
getRectangle(): { x: number; y: number; width: number; height: number } {
|
||||
const Rect = this.Rect();
|
||||
return Rect?.asRectangle() ?? { x: 0, y: 0, width: 0, height: 0 };
|
||||
}
|
||||
|
||||
setRectangle(rect: { x: number; y: number; width: number; height: number }) {
|
||||
const { x, y, width, height } = rect;
|
||||
const Rect = this.dict.context.obj([x, y, x + width, y + height]);
|
||||
this.dict.set(PDFName.of('Rect'), Rect);
|
||||
}
|
||||
|
||||
getAppearanceState(): PDFName | undefined {
|
||||
const AS = this.dict.lookup(PDFName.of('AS'));
|
||||
if (AS instanceof PDFName) return AS;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
setAppearanceState(state: PDFName) {
|
||||
this.dict.set(PDFName.of('AS'), state);
|
||||
}
|
||||
|
||||
setAppearances(appearances: PDFDict) {
|
||||
this.dict.set(PDFName.of('AP'), appearances);
|
||||
}
|
||||
|
||||
ensureAP(): PDFDict {
|
||||
let AP = this.AP();
|
||||
if (!AP) {
|
||||
AP = this.dict.context.obj({});
|
||||
this.dict.set(PDFName.of('AP'), AP);
|
||||
}
|
||||
return AP;
|
||||
}
|
||||
|
||||
getNormalAppearance(): PDFRef | PDFDict {
|
||||
const AP = this.ensureAP();
|
||||
const N = AP.get(PDFName.of('N'));
|
||||
if (N instanceof PDFRef || N instanceof PDFDict) return N;
|
||||
|
||||
throw new Error(`Unexpected N type: ${N?.constructor.name}`);
|
||||
}
|
||||
|
||||
/** @param appearance A PDFDict or PDFStream (direct or ref) */
|
||||
setNormalAppearance(appearance: PDFRef | PDFDict) {
|
||||
const AP = this.ensureAP();
|
||||
AP.set(PDFName.of('N'), appearance);
|
||||
}
|
||||
|
||||
/** @param appearance A PDFDict or PDFStream (direct or ref) */
|
||||
setRolloverAppearance(appearance: PDFRef | PDFDict) {
|
||||
const AP = this.ensureAP();
|
||||
AP.set(PDFName.of('R'), appearance);
|
||||
}
|
||||
|
||||
/** @param appearance A PDFDict or PDFStream (direct or ref) */
|
||||
setDownAppearance(appearance: PDFRef | PDFDict) {
|
||||
const AP = this.ensureAP();
|
||||
AP.set(PDFName.of('D'), appearance);
|
||||
}
|
||||
|
||||
removeRolloverAppearance() {
|
||||
const AP = this.AP();
|
||||
AP?.delete(PDFName.of('R'));
|
||||
}
|
||||
|
||||
removeDownAppearance() {
|
||||
const AP = this.AP();
|
||||
AP?.delete(PDFName.of('D'));
|
||||
}
|
||||
|
||||
getAppearances():
|
||||
| {
|
||||
normal: PDFStream | PDFDict;
|
||||
rollover?: PDFStream | PDFDict;
|
||||
down?: PDFStream | PDFDict;
|
||||
}
|
||||
| undefined {
|
||||
const AP = this.AP();
|
||||
|
||||
if (!AP) return undefined;
|
||||
|
||||
const N = AP.lookup(PDFName.of('N'), PDFDict, PDFStream);
|
||||
const R = AP.lookupMaybe(PDFName.of('R'), PDFDict, PDFStream);
|
||||
const D = AP.lookupMaybe(PDFName.of('D'), PDFDict, PDFStream);
|
||||
|
||||
return { normal: N, rollover: R, down: D };
|
||||
}
|
||||
|
||||
getFlags(): number {
|
||||
return this.F()?.asNumber() ?? 0;
|
||||
}
|
||||
|
||||
setFlags(flags: number) {
|
||||
this.dict.set(PDFName.of('F'), PDFNumber.of(flags));
|
||||
}
|
||||
|
||||
hasFlag(flag: number): boolean {
|
||||
const flags = this.getFlags();
|
||||
return (flags & flag) !== 0;
|
||||
}
|
||||
|
||||
setFlag(flag: number) {
|
||||
const flags = this.getFlags();
|
||||
this.setFlags(flags | flag);
|
||||
}
|
||||
|
||||
clearFlag(flag: number) {
|
||||
const flags = this.getFlags();
|
||||
this.setFlags(flags & ~flag);
|
||||
}
|
||||
|
||||
setFlagTo(flag: number, enable: boolean) {
|
||||
if (enable) this.setFlag(flag);
|
||||
else this.clearFlag(flag);
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFAnnotation;
|
||||
112
frontend/node_modules/pdf-lib/src/core/annotation/PDFWidgetAnnotation.ts
generated
vendored
Normal file
112
frontend/node_modules/pdf-lib/src/core/annotation/PDFWidgetAnnotation.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFString from 'src/core/objects/PDFString';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import BorderStyle from 'src/core/annotation/BorderStyle';
|
||||
import PDFAnnotation from 'src/core/annotation/PDFAnnotation';
|
||||
import AppearanceCharacteristics from 'src/core/annotation/AppearanceCharacteristics';
|
||||
|
||||
class PDFWidgetAnnotation extends PDFAnnotation {
|
||||
static fromDict = (dict: PDFDict): PDFWidgetAnnotation =>
|
||||
new PDFWidgetAnnotation(dict);
|
||||
|
||||
static create = (context: PDFContext, parent: PDFRef) => {
|
||||
const dict = context.obj({
|
||||
Type: 'Annot',
|
||||
Subtype: 'Widget',
|
||||
Rect: [0, 0, 0, 0],
|
||||
Parent: parent,
|
||||
});
|
||||
return new PDFWidgetAnnotation(dict);
|
||||
};
|
||||
|
||||
MK(): PDFDict | undefined {
|
||||
const MK = this.dict.lookup(PDFName.of('MK'));
|
||||
if (MK instanceof PDFDict) return MK;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
BS(): PDFDict | undefined {
|
||||
const BS = this.dict.lookup(PDFName.of('BS'));
|
||||
if (BS instanceof PDFDict) return BS;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
DA(): PDFString | PDFHexString | undefined {
|
||||
const da = this.dict.lookup(PDFName.of('DA'));
|
||||
if (da instanceof PDFString || da instanceof PDFHexString) return da;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
P(): PDFRef | undefined {
|
||||
const P = this.dict.get(PDFName.of('P'));
|
||||
if (P instanceof PDFRef) return P;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
setP(page: PDFRef) {
|
||||
this.dict.set(PDFName.of('P'), page);
|
||||
}
|
||||
|
||||
setDefaultAppearance(appearance: string) {
|
||||
this.dict.set(PDFName.of('DA'), PDFString.of(appearance));
|
||||
}
|
||||
|
||||
getDefaultAppearance(): string | undefined {
|
||||
const DA = this.DA();
|
||||
|
||||
if (DA instanceof PDFHexString) {
|
||||
return DA.decodeText();
|
||||
}
|
||||
|
||||
return DA?.asString();
|
||||
}
|
||||
|
||||
getAppearanceCharacteristics(): AppearanceCharacteristics | undefined {
|
||||
const MK = this.MK();
|
||||
if (MK) return AppearanceCharacteristics.fromDict(MK);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getOrCreateAppearanceCharacteristics(): AppearanceCharacteristics {
|
||||
const MK = this.MK();
|
||||
if (MK) return AppearanceCharacteristics.fromDict(MK);
|
||||
|
||||
const ac = AppearanceCharacteristics.fromDict(this.dict.context.obj({}));
|
||||
this.dict.set(PDFName.of('MK'), ac.dict);
|
||||
return ac;
|
||||
}
|
||||
|
||||
getBorderStyle(): BorderStyle | undefined {
|
||||
const BS = this.BS();
|
||||
if (BS) return BorderStyle.fromDict(BS);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getOrCreateBorderStyle(): BorderStyle {
|
||||
const BS = this.BS();
|
||||
if (BS) return BorderStyle.fromDict(BS);
|
||||
|
||||
const bs = BorderStyle.fromDict(this.dict.context.obj({}));
|
||||
this.dict.set(PDFName.of('BS'), bs.dict);
|
||||
return bs;
|
||||
}
|
||||
|
||||
getOnValue(): PDFName | undefined {
|
||||
const normal = this.getAppearances()?.normal;
|
||||
|
||||
if (normal instanceof PDFDict) {
|
||||
const keys = normal.keys();
|
||||
for (let idx = 0, len = keys.length; idx < len; idx++) {
|
||||
const key = keys[idx];
|
||||
if (key !== PDFName.of('Off')) return key;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFWidgetAnnotation;
|
||||
90
frontend/node_modules/pdf-lib/src/core/annotation/flags.ts
generated
vendored
Normal file
90
frontend/node_modules/pdf-lib/src/core/annotation/flags.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
const flag = (bitIndex: number) => 1 << bitIndex;
|
||||
|
||||
/** From PDF spec table 165 */
|
||||
export enum AnnotationFlags {
|
||||
/**
|
||||
* If set, do not display the annotation if it does not belong to one of the
|
||||
* standard annotation types and no annotation handler is available. If clear,
|
||||
* display such an unknown annotation using an appearance stream specified by
|
||||
* its appearance dictionary, if any.
|
||||
*/
|
||||
Invisible = flag(1 - 1),
|
||||
|
||||
/**
|
||||
* If set, do not display or print the annotation or allow it to interact with
|
||||
* the user, regardless of its annotation type or whether an annotation
|
||||
* handler is available.
|
||||
*
|
||||
* In cases where screen space is limited, the ability to hide and show
|
||||
* annotations selectively can be used in combination with appearance streams
|
||||
* to display auxiliary pop-up information similar in function to online help
|
||||
* systems.
|
||||
*/
|
||||
Hidden = flag(2 - 1),
|
||||
|
||||
/**
|
||||
* If set, print the annotation when the page is printed. If clear, never
|
||||
* print the annotation, regardless of whether it is displayed on the screen.
|
||||
*
|
||||
* This can be useful for annotations representing interactive pushbuttons,
|
||||
* which would serve no meaningful purpose on the printed page.
|
||||
*/
|
||||
Print = flag(3 - 1),
|
||||
|
||||
/**
|
||||
* If set, do not scale the annotation’s appearance to match the magnification
|
||||
* of the page. The location of the annotation on the page (defined by the
|
||||
* upper-left corner of its annotation rectangle) shall remain fixed,
|
||||
* regardless of the page magnification.
|
||||
*/
|
||||
NoZoom = flag(4 - 1),
|
||||
|
||||
/**
|
||||
* If set, do not rotate the annotation’s appearance to match the rotation of
|
||||
* the page. The upper-left corner of the annotation rectangle shall remain in
|
||||
* a fixed location on the page, regardless of the page rotation.
|
||||
*/
|
||||
NoRotate = flag(5 - 1),
|
||||
|
||||
/**
|
||||
* If set, do not display the annotation on the screen or allow it to interact
|
||||
* with the user. The annotation may be printed (depending on the setting of
|
||||
* the Print flag) but should be considered hidden for purposes of on-screen
|
||||
* display and user interaction.
|
||||
*/
|
||||
NoView = flag(6 - 1),
|
||||
|
||||
/**
|
||||
* If set, do not allow the annotation to interact with the user. The
|
||||
* annotation may be displayed or printed (depending on the settings of the
|
||||
* NoView and Print flags) but should not respond to mouse clicks or change
|
||||
* its appearance in response to mouse motions.
|
||||
*
|
||||
* This flag shall be ignored for widget annotations; its function is
|
||||
* subsumed by the ReadOnly flag of the associated form field.
|
||||
*/
|
||||
ReadOnly = flag(7 - 1),
|
||||
|
||||
/**
|
||||
* If set, do not allow the annotation to be deleted or its properties
|
||||
* (including position and size) to be modified by the user. However, this
|
||||
* flag does not restrict changes to the annotation’s contents, such as the
|
||||
* value of a form field.
|
||||
*/
|
||||
Locked = flag(8 - 1),
|
||||
|
||||
/**
|
||||
* If set, invert the interpretation of the NoView flag for certain events.
|
||||
*
|
||||
* A typical use is to have an annotation that appears only when a mouse
|
||||
* cursor is held over it.
|
||||
*/
|
||||
ToggleNoView = flag(9 - 1),
|
||||
|
||||
/**
|
||||
* If set, do not allow the contents of the annotation to be modified by the
|
||||
* user. This flag does not restrict deletion of the annotation or changes to
|
||||
* other annotation properties, such as position and size.
|
||||
*/
|
||||
LockedContents = flag(10 - 1),
|
||||
}
|
||||
4
frontend/node_modules/pdf-lib/src/core/annotation/index.ts
generated
vendored
Normal file
4
frontend/node_modules/pdf-lib/src/core/annotation/index.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export { default as PDFAnnotation } from 'src/core/annotation/PDFAnnotation';
|
||||
export { default as PDFWidgetAnnotation } from 'src/core/annotation/PDFWidgetAnnotation';
|
||||
export { default as AppearanceCharacteristics } from 'src/core/annotation/AppearanceCharacteristics';
|
||||
export * from 'src/core/annotation/flags';
|
||||
173
frontend/node_modules/pdf-lib/src/core/document/PDFCrossRefSection.ts
generated
vendored
Normal file
173
frontend/node_modules/pdf-lib/src/core/document/PDFCrossRefSection.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import { copyStringIntoBuffer, padStart } from 'src/utils';
|
||||
|
||||
export interface Entry {
|
||||
ref: PDFRef;
|
||||
offset: number;
|
||||
deleted: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entries should be added using the [[addEntry]] and [[addDeletedEntry]]
|
||||
* methods **in order of ascending object number**.
|
||||
*/
|
||||
class PDFCrossRefSection {
|
||||
static create = () =>
|
||||
new PDFCrossRefSection({
|
||||
ref: PDFRef.of(0, 65535),
|
||||
offset: 0,
|
||||
deleted: true,
|
||||
});
|
||||
|
||||
static createEmpty = () => new PDFCrossRefSection();
|
||||
|
||||
private subsections: Entry[][];
|
||||
private chunkIdx: number;
|
||||
private chunkLength: number;
|
||||
|
||||
private constructor(firstEntry: Entry | void) {
|
||||
this.subsections = firstEntry ? [[firstEntry]] : [];
|
||||
this.chunkIdx = 0;
|
||||
this.chunkLength = firstEntry ? 1 : 0;
|
||||
}
|
||||
|
||||
addEntry(ref: PDFRef, offset: number): void {
|
||||
this.append({ ref, offset, deleted: false });
|
||||
}
|
||||
|
||||
addDeletedEntry(ref: PDFRef, nextFreeObjectNumber: number): void {
|
||||
this.append({ ref, offset: nextFreeObjectNumber, deleted: true });
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
let section = `xref\n`;
|
||||
|
||||
for (
|
||||
let rangeIdx = 0, rangeLen = this.subsections.length;
|
||||
rangeIdx < rangeLen;
|
||||
rangeIdx++
|
||||
) {
|
||||
const range = this.subsections[rangeIdx];
|
||||
section += `${range[0].ref.objectNumber} ${range.length}\n`;
|
||||
for (
|
||||
let entryIdx = 0, entryLen = range.length;
|
||||
entryIdx < entryLen;
|
||||
entryIdx++
|
||||
) {
|
||||
const entry = range[entryIdx];
|
||||
section += padStart(String(entry.offset), 10, '0');
|
||||
section += ' ';
|
||||
section += padStart(String(entry.ref.generationNumber), 5, '0');
|
||||
section += ' ';
|
||||
section += entry.deleted ? 'f' : 'n';
|
||||
section += ' \n';
|
||||
}
|
||||
}
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
let size = 5;
|
||||
for (let idx = 0, len = this.subsections.length; idx < len; idx++) {
|
||||
const subsection = this.subsections[idx];
|
||||
const subsectionLength = subsection.length;
|
||||
const [firstEntry] = subsection;
|
||||
size += 2;
|
||||
size += String(firstEntry.ref.objectNumber).length;
|
||||
size += String(subsectionLength).length;
|
||||
size += 20 * subsectionLength;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
const initialOffset = offset;
|
||||
|
||||
buffer[offset++] = CharCodes.x;
|
||||
buffer[offset++] = CharCodes.r;
|
||||
buffer[offset++] = CharCodes.e;
|
||||
buffer[offset++] = CharCodes.f;
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
|
||||
offset += this.copySubsectionsIntoBuffer(this.subsections, buffer, offset);
|
||||
|
||||
return offset - initialOffset;
|
||||
}
|
||||
|
||||
private copySubsectionsIntoBuffer(
|
||||
subsections: Entry[][],
|
||||
buffer: Uint8Array,
|
||||
offset: number,
|
||||
): number {
|
||||
const initialOffset = offset;
|
||||
const length = subsections.length;
|
||||
|
||||
for (let idx = 0; idx < length; idx++) {
|
||||
const subsection = this.subsections[idx];
|
||||
|
||||
const firstObjectNumber = String(subsection[0].ref.objectNumber);
|
||||
offset += copyStringIntoBuffer(firstObjectNumber, buffer, offset);
|
||||
buffer[offset++] = CharCodes.Space;
|
||||
|
||||
const rangeLength = String(subsection.length);
|
||||
offset += copyStringIntoBuffer(rangeLength, buffer, offset);
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
|
||||
offset += this.copyEntriesIntoBuffer(subsection, buffer, offset);
|
||||
}
|
||||
|
||||
return offset - initialOffset;
|
||||
}
|
||||
|
||||
private copyEntriesIntoBuffer(
|
||||
entries: Entry[],
|
||||
buffer: Uint8Array,
|
||||
offset: number,
|
||||
): number {
|
||||
const length = entries.length;
|
||||
|
||||
for (let idx = 0; idx < length; idx++) {
|
||||
const entry = entries[idx];
|
||||
|
||||
const entryOffset = padStart(String(entry.offset), 10, '0');
|
||||
offset += copyStringIntoBuffer(entryOffset, buffer, offset);
|
||||
buffer[offset++] = CharCodes.Space;
|
||||
|
||||
const entryGen = padStart(String(entry.ref.generationNumber), 5, '0');
|
||||
offset += copyStringIntoBuffer(entryGen, buffer, offset);
|
||||
buffer[offset++] = CharCodes.Space;
|
||||
|
||||
buffer[offset++] = entry.deleted ? CharCodes.f : CharCodes.n;
|
||||
|
||||
buffer[offset++] = CharCodes.Space;
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
}
|
||||
|
||||
return 20 * length;
|
||||
}
|
||||
|
||||
private append(currEntry: Entry): void {
|
||||
if (this.chunkLength === 0) {
|
||||
this.subsections.push([currEntry]);
|
||||
this.chunkIdx = 0;
|
||||
this.chunkLength = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
const chunk = this.subsections[this.chunkIdx];
|
||||
const prevEntry = chunk[this.chunkLength - 1];
|
||||
|
||||
if (currEntry.ref.objectNumber - prevEntry.ref.objectNumber > 1) {
|
||||
this.subsections.push([currEntry]);
|
||||
this.chunkIdx += 1;
|
||||
this.chunkLength = 1;
|
||||
} else {
|
||||
chunk.push(currEntry);
|
||||
this.chunkLength += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFCrossRefSection;
|
||||
49
frontend/node_modules/pdf-lib/src/core/document/PDFHeader.ts
generated
vendored
Normal file
49
frontend/node_modules/pdf-lib/src/core/document/PDFHeader.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import { charFromCode, copyStringIntoBuffer } from 'src/utils';
|
||||
|
||||
class PDFHeader {
|
||||
static forVersion = (major: number, minor: number) =>
|
||||
new PDFHeader(major, minor);
|
||||
|
||||
private readonly major: string;
|
||||
private readonly minor: string;
|
||||
|
||||
private constructor(major: number, minor: number) {
|
||||
this.major = String(major);
|
||||
this.minor = String(minor);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
const bc = charFromCode(129);
|
||||
return `%PDF-${this.major}.${this.minor}\n%${bc}${bc}${bc}${bc}`;
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
return 12 + this.major.length + this.minor.length;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
const initialOffset = offset;
|
||||
|
||||
buffer[offset++] = CharCodes.Percent;
|
||||
buffer[offset++] = CharCodes.P;
|
||||
buffer[offset++] = CharCodes.D;
|
||||
buffer[offset++] = CharCodes.F;
|
||||
buffer[offset++] = CharCodes.Dash;
|
||||
|
||||
offset += copyStringIntoBuffer(this.major, buffer, offset);
|
||||
buffer[offset++] = CharCodes.Period;
|
||||
offset += copyStringIntoBuffer(this.minor, buffer, offset);
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
|
||||
buffer[offset++] = CharCodes.Percent;
|
||||
buffer[offset++] = 129;
|
||||
buffer[offset++] = 129;
|
||||
buffer[offset++] = 129;
|
||||
buffer[offset++] = 129;
|
||||
|
||||
return offset - initialOffset;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFHeader;
|
||||
49
frontend/node_modules/pdf-lib/src/core/document/PDFTrailer.ts
generated
vendored
Normal file
49
frontend/node_modules/pdf-lib/src/core/document/PDFTrailer.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import { copyStringIntoBuffer } from 'src/utils';
|
||||
|
||||
class PDFTrailer {
|
||||
static forLastCrossRefSectionOffset = (offset: number) =>
|
||||
new PDFTrailer(offset);
|
||||
|
||||
private readonly lastXRefOffset: string;
|
||||
|
||||
private constructor(lastXRefOffset: number) {
|
||||
this.lastXRefOffset = String(lastXRefOffset);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `startxref\n${this.lastXRefOffset}\n%%EOF`;
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
return 16 + this.lastXRefOffset.length;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
const initialOffset = offset;
|
||||
|
||||
buffer[offset++] = CharCodes.s;
|
||||
buffer[offset++] = CharCodes.t;
|
||||
buffer[offset++] = CharCodes.a;
|
||||
buffer[offset++] = CharCodes.r;
|
||||
buffer[offset++] = CharCodes.t;
|
||||
buffer[offset++] = CharCodes.x;
|
||||
buffer[offset++] = CharCodes.r;
|
||||
buffer[offset++] = CharCodes.e;
|
||||
buffer[offset++] = CharCodes.f;
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
|
||||
offset += copyStringIntoBuffer(this.lastXRefOffset, buffer, offset);
|
||||
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
buffer[offset++] = CharCodes.Percent;
|
||||
buffer[offset++] = CharCodes.Percent;
|
||||
buffer[offset++] = CharCodes.E;
|
||||
buffer[offset++] = CharCodes.O;
|
||||
buffer[offset++] = CharCodes.F;
|
||||
|
||||
return offset - initialOffset;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFTrailer;
|
||||
39
frontend/node_modules/pdf-lib/src/core/document/PDFTrailerDict.ts
generated
vendored
Normal file
39
frontend/node_modules/pdf-lib/src/core/document/PDFTrailerDict.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
|
||||
class PDFTrailerDict {
|
||||
static of = (dict: PDFDict) => new PDFTrailerDict(dict);
|
||||
|
||||
readonly dict: PDFDict;
|
||||
|
||||
private constructor(dict: PDFDict) {
|
||||
this.dict = dict;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `trailer\n${this.dict.toString()}`;
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
return 8 + this.dict.sizeInBytes();
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
const initialOffset = offset;
|
||||
|
||||
buffer[offset++] = CharCodes.t;
|
||||
buffer[offset++] = CharCodes.r;
|
||||
buffer[offset++] = CharCodes.a;
|
||||
buffer[offset++] = CharCodes.i;
|
||||
buffer[offset++] = CharCodes.l;
|
||||
buffer[offset++] = CharCodes.e;
|
||||
buffer[offset++] = CharCodes.r;
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
|
||||
offset += this.dict.copyBytesInto(buffer, offset);
|
||||
|
||||
return offset - initialOffset;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFTrailerDict;
|
||||
70
frontend/node_modules/pdf-lib/src/core/embedders/CMap.ts
generated
vendored
Normal file
70
frontend/node_modules/pdf-lib/src/core/embedders/CMap.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import { Glyph } from 'src/types/fontkit';
|
||||
|
||||
import { toHexString, toHexStringOfMinLength } from 'src/utils';
|
||||
import {
|
||||
hasSurrogates,
|
||||
highSurrogate,
|
||||
isWithinBMP,
|
||||
lowSurrogate,
|
||||
} from 'src/utils/unicode';
|
||||
|
||||
/** [fontId, codePoint] */
|
||||
type BfChar = [string, string];
|
||||
|
||||
/** `glyphs` should be an array of unique glyphs */
|
||||
export const createCmap = (glyphs: Glyph[], glyphId: (g?: Glyph) => number) => {
|
||||
const bfChars: BfChar[] = new Array(glyphs.length);
|
||||
for (let idx = 0, len = glyphs.length; idx < len; idx++) {
|
||||
const glyph = glyphs[idx];
|
||||
const id = cmapHexFormat(cmapHexString(glyphId(glyph)));
|
||||
const unicode = cmapHexFormat(...glyph.codePoints.map(cmapCodePointFormat));
|
||||
bfChars[idx] = [id, unicode];
|
||||
}
|
||||
|
||||
return fillCmapTemplate(bfChars);
|
||||
};
|
||||
|
||||
/* =============================== Templates ================================ */
|
||||
|
||||
const fillCmapTemplate = (bfChars: BfChar[]) => `\
|
||||
/CIDInit /ProcSet findresource begin
|
||||
12 dict begin
|
||||
begincmap
|
||||
/CIDSystemInfo <<
|
||||
/Registry (Adobe)
|
||||
/Ordering (UCS)
|
||||
/Supplement 0
|
||||
>> def
|
||||
/CMapName /Adobe-Identity-UCS def
|
||||
/CMapType 2 def
|
||||
1 begincodespacerange
|
||||
<0000><ffff>
|
||||
endcodespacerange
|
||||
${bfChars.length} beginbfchar
|
||||
${bfChars.map(([glyphId, codePoint]) => `${glyphId} ${codePoint}`).join('\n')}
|
||||
endbfchar
|
||||
endcmap
|
||||
CMapName currentdict /CMap defineresource pop
|
||||
end
|
||||
end\
|
||||
`;
|
||||
|
||||
/* =============================== Utilities ================================ */
|
||||
|
||||
const cmapHexFormat = (...values: string[]) => `<${values.join('')}>`;
|
||||
|
||||
const cmapHexString = (value: number) => toHexStringOfMinLength(value, 4);
|
||||
|
||||
const cmapCodePointFormat = (codePoint: number) => {
|
||||
if (isWithinBMP(codePoint)) return cmapHexString(codePoint);
|
||||
|
||||
if (hasSurrogates(codePoint)) {
|
||||
const hs = highSurrogate(codePoint);
|
||||
const ls = lowSurrogate(codePoint);
|
||||
return `${cmapHexString(hs)}${cmapHexString(ls)}`;
|
||||
}
|
||||
|
||||
const hex = toHexString(codePoint);
|
||||
const msg = `0x${hex} is not a valid UTF-8 or UTF-16 codepoint.`;
|
||||
throw new Error(msg);
|
||||
};
|
||||
249
frontend/node_modules/pdf-lib/src/core/embedders/CustomFontEmbedder.ts
generated
vendored
Normal file
249
frontend/node_modules/pdf-lib/src/core/embedders/CustomFontEmbedder.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
import { Font, Fontkit, Glyph, TypeFeatures } from 'src/types/fontkit';
|
||||
|
||||
import { createCmap } from 'src/core/embedders/CMap';
|
||||
import { deriveFontFlags } from 'src/core/embedders/FontFlags';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFString from 'src/core/objects/PDFString';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import {
|
||||
byAscendingId,
|
||||
Cache,
|
||||
sortedUniq,
|
||||
toHexStringOfMinLength,
|
||||
} from 'src/utils';
|
||||
|
||||
/**
|
||||
* A note of thanks to the developers of https://github.com/foliojs/pdfkit, as
|
||||
* this class borrows from:
|
||||
* https://github.com/devongovett/pdfkit/blob/e71edab0dd4657b5a767804ba86c94c58d01fbca/lib/image/jpeg.coffee
|
||||
*/
|
||||
class CustomFontEmbedder {
|
||||
static async for(
|
||||
fontkit: Fontkit,
|
||||
fontData: Uint8Array,
|
||||
customName?: string,
|
||||
fontFeatures?: TypeFeatures,
|
||||
) {
|
||||
const font = await fontkit.create(fontData);
|
||||
return new CustomFontEmbedder(font, fontData, customName, fontFeatures);
|
||||
}
|
||||
|
||||
readonly font: Font;
|
||||
readonly scale: number;
|
||||
readonly fontData: Uint8Array;
|
||||
readonly fontName: string;
|
||||
readonly customName: string | undefined;
|
||||
readonly fontFeatures: TypeFeatures | undefined;
|
||||
|
||||
protected baseFontName: string;
|
||||
protected glyphCache: Cache<Glyph[]>;
|
||||
|
||||
protected constructor(
|
||||
font: Font,
|
||||
fontData: Uint8Array,
|
||||
customName?: string,
|
||||
fontFeatures?: TypeFeatures,
|
||||
) {
|
||||
this.font = font;
|
||||
this.scale = 1000 / this.font.unitsPerEm;
|
||||
this.fontData = fontData;
|
||||
this.fontName = this.font.postscriptName || 'Font';
|
||||
this.customName = customName;
|
||||
this.fontFeatures = fontFeatures;
|
||||
|
||||
this.baseFontName = '';
|
||||
this.glyphCache = Cache.populatedBy(this.allGlyphsInFontSortedById);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the JavaScript string into this font. (JavaScript encodes strings in
|
||||
* Unicode, but embedded fonts use their own custom encodings)
|
||||
*/
|
||||
encodeText(text: string): PDFHexString {
|
||||
const { glyphs } = this.font.layout(text, this.fontFeatures);
|
||||
const hexCodes = new Array(glyphs.length);
|
||||
for (let idx = 0, len = glyphs.length; idx < len; idx++) {
|
||||
hexCodes[idx] = toHexStringOfMinLength(glyphs[idx].id, 4);
|
||||
}
|
||||
return PDFHexString.of(hexCodes.join(''));
|
||||
}
|
||||
|
||||
// The advanceWidth takes into account kerning automatically, so we don't
|
||||
// have to do that manually like we do for the standard fonts.
|
||||
widthOfTextAtSize(text: string, size: number): number {
|
||||
const { glyphs } = this.font.layout(text, this.fontFeatures);
|
||||
let totalWidth = 0;
|
||||
for (let idx = 0, len = glyphs.length; idx < len; idx++) {
|
||||
totalWidth += glyphs[idx].advanceWidth * this.scale;
|
||||
}
|
||||
const scale = size / 1000;
|
||||
return totalWidth * scale;
|
||||
}
|
||||
|
||||
heightOfFontAtSize(
|
||||
size: number,
|
||||
options: { descender?: boolean } = {},
|
||||
): number {
|
||||
const { descender = true } = options;
|
||||
|
||||
const { ascent, descent, bbox } = this.font;
|
||||
const yTop = (ascent || bbox.maxY) * this.scale;
|
||||
const yBottom = (descent || bbox.minY) * this.scale;
|
||||
|
||||
let height = yTop - yBottom;
|
||||
if (!descender) height -= Math.abs(descent) || 0;
|
||||
|
||||
return (height / 1000) * size;
|
||||
}
|
||||
|
||||
sizeOfFontAtHeight(height: number): number {
|
||||
const { ascent, descent, bbox } = this.font;
|
||||
const yTop = (ascent || bbox.maxY) * this.scale;
|
||||
const yBottom = (descent || bbox.minY) * this.scale;
|
||||
return (1000 * height) / (yTop - yBottom);
|
||||
}
|
||||
|
||||
embedIntoContext(context: PDFContext, ref?: PDFRef): Promise<PDFRef> {
|
||||
this.baseFontName =
|
||||
this.customName || context.addRandomSuffix(this.fontName);
|
||||
return this.embedFontDict(context, ref);
|
||||
}
|
||||
|
||||
protected async embedFontDict(
|
||||
context: PDFContext,
|
||||
ref?: PDFRef,
|
||||
): Promise<PDFRef> {
|
||||
const cidFontDictRef = await this.embedCIDFontDict(context);
|
||||
const unicodeCMapRef = this.embedUnicodeCmap(context);
|
||||
|
||||
const fontDict = context.obj({
|
||||
Type: 'Font',
|
||||
Subtype: 'Type0',
|
||||
BaseFont: this.baseFontName,
|
||||
Encoding: 'Identity-H',
|
||||
DescendantFonts: [cidFontDictRef],
|
||||
ToUnicode: unicodeCMapRef,
|
||||
});
|
||||
|
||||
if (ref) {
|
||||
context.assign(ref, fontDict);
|
||||
return ref;
|
||||
} else {
|
||||
return context.register(fontDict);
|
||||
}
|
||||
}
|
||||
|
||||
protected isCFF(): boolean {
|
||||
return this.font.cff;
|
||||
}
|
||||
|
||||
protected async embedCIDFontDict(context: PDFContext): Promise<PDFRef> {
|
||||
const fontDescriptorRef = await this.embedFontDescriptor(context);
|
||||
|
||||
const cidFontDict = context.obj({
|
||||
Type: 'Font',
|
||||
Subtype: this.isCFF() ? 'CIDFontType0' : 'CIDFontType2',
|
||||
CIDToGIDMap: 'Identity',
|
||||
BaseFont: this.baseFontName,
|
||||
CIDSystemInfo: {
|
||||
Registry: PDFString.of('Adobe'),
|
||||
Ordering: PDFString.of('Identity'),
|
||||
Supplement: 0,
|
||||
},
|
||||
FontDescriptor: fontDescriptorRef,
|
||||
W: this.computeWidths(),
|
||||
});
|
||||
|
||||
return context.register(cidFontDict);
|
||||
}
|
||||
|
||||
protected async embedFontDescriptor(context: PDFContext): Promise<PDFRef> {
|
||||
const fontStreamRef = await this.embedFontStream(context);
|
||||
|
||||
const { scale } = this;
|
||||
const { italicAngle, ascent, descent, capHeight, xHeight } = this.font;
|
||||
const { minX, minY, maxX, maxY } = this.font.bbox;
|
||||
|
||||
const fontDescriptor = context.obj({
|
||||
Type: 'FontDescriptor',
|
||||
FontName: this.baseFontName,
|
||||
Flags: deriveFontFlags(this.font),
|
||||
FontBBox: [minX * scale, minY * scale, maxX * scale, maxY * scale],
|
||||
ItalicAngle: italicAngle,
|
||||
Ascent: ascent * scale,
|
||||
Descent: descent * scale,
|
||||
CapHeight: (capHeight || ascent) * scale,
|
||||
XHeight: (xHeight || 0) * scale,
|
||||
|
||||
// Not sure how to compute/find this, nor is anybody else really:
|
||||
// https://stackoverflow.com/questions/35485179/stemv-value-of-the-truetype-font
|
||||
StemV: 0,
|
||||
|
||||
[this.isCFF() ? 'FontFile3' : 'FontFile2']: fontStreamRef,
|
||||
});
|
||||
|
||||
return context.register(fontDescriptor);
|
||||
}
|
||||
|
||||
protected async serializeFont(): Promise<Uint8Array> {
|
||||
return this.fontData;
|
||||
}
|
||||
|
||||
protected async embedFontStream(context: PDFContext): Promise<PDFRef> {
|
||||
const fontStream = context.flateStream(await this.serializeFont(), {
|
||||
Subtype: this.isCFF() ? 'CIDFontType0C' : undefined,
|
||||
});
|
||||
return context.register(fontStream);
|
||||
}
|
||||
|
||||
protected embedUnicodeCmap(context: PDFContext): PDFRef {
|
||||
const cmap = createCmap(this.glyphCache.access(), this.glyphId.bind(this));
|
||||
const cmapStream = context.flateStream(cmap);
|
||||
return context.register(cmapStream);
|
||||
}
|
||||
|
||||
protected glyphId(glyph?: Glyph): number {
|
||||
return glyph ? glyph.id : -1;
|
||||
}
|
||||
|
||||
protected computeWidths(): (number | number[])[] {
|
||||
const glyphs = this.glyphCache.access();
|
||||
|
||||
const widths: (number | number[])[] = [];
|
||||
let currSection: number[] = [];
|
||||
|
||||
for (let idx = 0, len = glyphs.length; idx < len; idx++) {
|
||||
const currGlyph = glyphs[idx];
|
||||
const prevGlyph = glyphs[idx - 1];
|
||||
|
||||
const currGlyphId = this.glyphId(currGlyph);
|
||||
const prevGlyphId = this.glyphId(prevGlyph);
|
||||
|
||||
if (idx === 0) {
|
||||
widths.push(currGlyphId);
|
||||
} else if (currGlyphId - prevGlyphId !== 1) {
|
||||
widths.push(currSection);
|
||||
widths.push(currGlyphId);
|
||||
currSection = [];
|
||||
}
|
||||
|
||||
currSection.push(currGlyph.advanceWidth * this.scale);
|
||||
}
|
||||
|
||||
widths.push(currSection);
|
||||
|
||||
return widths;
|
||||
}
|
||||
|
||||
private allGlyphsInFontSortedById = (): Glyph[] => {
|
||||
const glyphs: Glyph[] = new Array(this.font.characterSet.length);
|
||||
for (let idx = 0, len = glyphs.length; idx < len; idx++) {
|
||||
const codePoint = this.font.characterSet[idx];
|
||||
glyphs[idx] = this.font.glyphForCodePoint(codePoint);
|
||||
}
|
||||
return sortedUniq(glyphs.sort(byAscendingId), (g) => g.id);
|
||||
};
|
||||
}
|
||||
|
||||
export default CustomFontEmbedder;
|
||||
84
frontend/node_modules/pdf-lib/src/core/embedders/CustomFontSubsetEmbedder.ts
generated
vendored
Normal file
84
frontend/node_modules/pdf-lib/src/core/embedders/CustomFontSubsetEmbedder.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import { Font, Fontkit, Glyph, Subset, TypeFeatures } from 'src/types/fontkit';
|
||||
|
||||
import CustomFontEmbedder from 'src/core/embedders/CustomFontEmbedder';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import { Cache, mergeUint8Arrays, toHexStringOfMinLength } from 'src/utils';
|
||||
|
||||
/**
|
||||
* A note of thanks to the developers of https://github.com/foliojs/pdfkit, as
|
||||
* this class borrows from:
|
||||
* https://github.com/devongovett/pdfkit/blob/e71edab0dd4657b5a767804ba86c94c58d01fbca/lib/image/jpeg.coffee
|
||||
*/
|
||||
class CustomFontSubsetEmbedder extends CustomFontEmbedder {
|
||||
static async for(
|
||||
fontkit: Fontkit,
|
||||
fontData: Uint8Array,
|
||||
customFontName?: string,
|
||||
fontFeatures?: TypeFeatures,
|
||||
) {
|
||||
const font = await fontkit.create(fontData);
|
||||
return new CustomFontSubsetEmbedder(
|
||||
font,
|
||||
fontData,
|
||||
customFontName,
|
||||
fontFeatures,
|
||||
);
|
||||
}
|
||||
|
||||
private readonly subset: Subset;
|
||||
private readonly glyphs: Glyph[];
|
||||
private readonly glyphIdMap: Map<number, number>;
|
||||
|
||||
private constructor(
|
||||
font: Font,
|
||||
fontData: Uint8Array,
|
||||
customFontName?: string,
|
||||
fontFeatures?: TypeFeatures,
|
||||
) {
|
||||
super(font, fontData, customFontName, fontFeatures);
|
||||
|
||||
this.subset = this.font.createSubset();
|
||||
this.glyphs = [];
|
||||
this.glyphCache = Cache.populatedBy(() => this.glyphs);
|
||||
this.glyphIdMap = new Map();
|
||||
}
|
||||
|
||||
encodeText(text: string): PDFHexString {
|
||||
const { glyphs } = this.font.layout(text, this.fontFeatures);
|
||||
const hexCodes = new Array(glyphs.length);
|
||||
|
||||
for (let idx = 0, len = glyphs.length; idx < len; idx++) {
|
||||
const glyph = glyphs[idx];
|
||||
const subsetGlyphId = this.subset.includeGlyph(glyph);
|
||||
|
||||
this.glyphs[subsetGlyphId - 1] = glyph;
|
||||
this.glyphIdMap.set(glyph.id, subsetGlyphId);
|
||||
|
||||
hexCodes[idx] = toHexStringOfMinLength(subsetGlyphId, 4);
|
||||
}
|
||||
|
||||
this.glyphCache.invalidate();
|
||||
return PDFHexString.of(hexCodes.join(''));
|
||||
}
|
||||
|
||||
protected isCFF(): boolean {
|
||||
return (this.subset as any).cff;
|
||||
}
|
||||
|
||||
protected glyphId(glyph?: Glyph): number {
|
||||
return glyph ? this.glyphIdMap.get(glyph.id)! : -1;
|
||||
}
|
||||
|
||||
protected serializeFont(): Promise<Uint8Array> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const parts: Uint8Array[] = [];
|
||||
this.subset
|
||||
.encodeStream()
|
||||
.on('data', (bytes) => parts.push(bytes))
|
||||
.on('end', () => resolve(mergeUint8Arrays(parts)))
|
||||
.on('error' as any, (err) => reject(err));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default CustomFontSubsetEmbedder;
|
||||
95
frontend/node_modules/pdf-lib/src/core/embedders/FileEmbedder.ts
generated
vendored
Normal file
95
frontend/node_modules/pdf-lib/src/core/embedders/FileEmbedder.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import PDFString from 'src/core/objects/PDFString';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
|
||||
/**
|
||||
* From the PDF-A3 specification, section **3.1. Requirements - General**.
|
||||
* See:
|
||||
* * https://www.pdfa.org/wp-content/uploads/2018/10/PDF20_AN002-AF.pdf
|
||||
*/
|
||||
export enum AFRelationship {
|
||||
Source = 'Source',
|
||||
Data = 'Data',
|
||||
Alternative = 'Alternative',
|
||||
Supplement = 'Supplement',
|
||||
EncryptedPayload = 'EncryptedPayload',
|
||||
FormData = 'EncryptedPayload',
|
||||
Schema = 'Schema',
|
||||
Unspecified = 'Unspecified',
|
||||
}
|
||||
|
||||
export interface EmbeddedFileOptions {
|
||||
mimeType?: string;
|
||||
description?: string;
|
||||
creationDate?: Date;
|
||||
modificationDate?: Date;
|
||||
afRelationship?: AFRelationship;
|
||||
}
|
||||
|
||||
class FileEmbedder {
|
||||
static for(
|
||||
bytes: Uint8Array,
|
||||
fileName: string,
|
||||
options: EmbeddedFileOptions = {},
|
||||
) {
|
||||
return new FileEmbedder(bytes, fileName, options);
|
||||
}
|
||||
|
||||
private readonly fileData: Uint8Array;
|
||||
readonly fileName: string;
|
||||
readonly options: EmbeddedFileOptions;
|
||||
|
||||
private constructor(
|
||||
fileData: Uint8Array,
|
||||
fileName: string,
|
||||
options: EmbeddedFileOptions = {},
|
||||
) {
|
||||
this.fileData = fileData;
|
||||
this.fileName = fileName;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
async embedIntoContext(context: PDFContext, ref?: PDFRef): Promise<PDFRef> {
|
||||
const {
|
||||
mimeType,
|
||||
description,
|
||||
creationDate,
|
||||
modificationDate,
|
||||
afRelationship,
|
||||
} = this.options;
|
||||
|
||||
const embeddedFileStream = context.flateStream(this.fileData, {
|
||||
Type: 'EmbeddedFile',
|
||||
Subtype: mimeType ?? undefined,
|
||||
Params: {
|
||||
Size: this.fileData.length,
|
||||
CreationDate: creationDate
|
||||
? PDFString.fromDate(creationDate)
|
||||
: undefined,
|
||||
ModDate: modificationDate
|
||||
? PDFString.fromDate(modificationDate)
|
||||
: undefined,
|
||||
},
|
||||
});
|
||||
const embeddedFileStreamRef = context.register(embeddedFileStream);
|
||||
|
||||
const fileSpecDict = context.obj({
|
||||
Type: 'Filespec',
|
||||
F: PDFString.of(this.fileName), // TODO: Assert that this is plain ASCII
|
||||
UF: PDFHexString.fromText(this.fileName),
|
||||
EF: { F: embeddedFileStreamRef },
|
||||
Desc: description ? PDFHexString.fromText(description) : undefined,
|
||||
AFRelationship: afRelationship ?? undefined,
|
||||
});
|
||||
|
||||
if (ref) {
|
||||
context.assign(ref, fileSpecDict);
|
||||
return ref;
|
||||
} else {
|
||||
return context.register(fileSpecDict);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default FileEmbedder;
|
||||
45
frontend/node_modules/pdf-lib/src/core/embedders/FontFlags.ts
generated
vendored
Normal file
45
frontend/node_modules/pdf-lib/src/core/embedders/FontFlags.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { Font } from 'src/types/fontkit';
|
||||
|
||||
export interface FontFlagOptions {
|
||||
fixedPitch?: boolean;
|
||||
serif?: boolean;
|
||||
symbolic?: boolean;
|
||||
script?: boolean;
|
||||
nonsymbolic?: boolean;
|
||||
italic?: boolean;
|
||||
allCap?: boolean;
|
||||
smallCap?: boolean;
|
||||
forceBold?: boolean;
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
const makeFontFlags = (options: FontFlagOptions) => {
|
||||
let flags = 0;
|
||||
|
||||
const flipBit = (bit: number) => { flags |= (1 << (bit - 1)); };
|
||||
|
||||
if (options.fixedPitch) flipBit(1);
|
||||
if (options.serif) flipBit(2);
|
||||
if (options.symbolic) flipBit(3);
|
||||
if (options.script) flipBit(4);
|
||||
if (options.nonsymbolic) flipBit(6);
|
||||
if (options.italic) flipBit(7);
|
||||
if (options.allCap) flipBit(17);
|
||||
if (options.smallCap) flipBit(18);
|
||||
if (options.forceBold) flipBit(19);
|
||||
|
||||
return flags;
|
||||
};
|
||||
|
||||
// From: https://github.com/foliojs/pdfkit/blob/83f5f7243172a017adcf6a7faa5547c55982c57b/lib/font/embedded.js#L123-L129
|
||||
export const deriveFontFlags = (font: Font): number => {
|
||||
const familyClass = font['OS/2'] ? font['OS/2'].sFamilyClass : 0;
|
||||
const flags = makeFontFlags({
|
||||
fixedPitch: font.post.isFixedPitch,
|
||||
serif: 1 <= familyClass && familyClass <= 7,
|
||||
symbolic: true, // Assume the font uses non-latin characters
|
||||
script: familyClass === 10,
|
||||
italic: font.head.macStyle.italic,
|
||||
});
|
||||
return flags;
|
||||
};
|
||||
34
frontend/node_modules/pdf-lib/src/core/embedders/JavaScriptEmbedder.ts
generated
vendored
Normal file
34
frontend/node_modules/pdf-lib/src/core/embedders/JavaScriptEmbedder.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
|
||||
class JavaScriptEmbedder {
|
||||
static for(script: string, scriptName: string) {
|
||||
return new JavaScriptEmbedder(script, scriptName);
|
||||
}
|
||||
|
||||
private readonly script: string;
|
||||
readonly scriptName: string;
|
||||
|
||||
private constructor(script: string, scriptName: string) {
|
||||
this.script = script;
|
||||
this.scriptName = scriptName;
|
||||
}
|
||||
|
||||
async embedIntoContext(context: PDFContext, ref?: PDFRef): Promise<PDFRef> {
|
||||
const jsActionDict = context.obj({
|
||||
Type: 'Action',
|
||||
S: 'JavaScript',
|
||||
JS: PDFHexString.fromText(this.script),
|
||||
});
|
||||
|
||||
if (ref) {
|
||||
context.assign(ref, jsActionDict);
|
||||
return ref;
|
||||
} else {
|
||||
return context.register(jsActionDict);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default JavaScriptEmbedder;
|
||||
127
frontend/node_modules/pdf-lib/src/core/embedders/JpegEmbedder.ts
generated
vendored
Normal file
127
frontend/node_modules/pdf-lib/src/core/embedders/JpegEmbedder.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
|
||||
// prettier-ignore
|
||||
const MARKERS = [
|
||||
0xffc0, 0xffc1, 0xffc2,
|
||||
0xffc3, 0xffc5, 0xffc6,
|
||||
0xffc7, 0xffc8, 0xffc9,
|
||||
0xffca, 0xffcb, 0xffcc,
|
||||
0xffcd, 0xffce, 0xffcf,
|
||||
];
|
||||
|
||||
enum ColorSpace {
|
||||
DeviceGray = 'DeviceGray',
|
||||
DeviceRGB = 'DeviceRGB',
|
||||
DeviceCMYK = 'DeviceCMYK',
|
||||
}
|
||||
|
||||
const ChannelToColorSpace: { [idx: number]: ColorSpace | undefined } = {
|
||||
1: ColorSpace.DeviceGray,
|
||||
3: ColorSpace.DeviceRGB,
|
||||
4: ColorSpace.DeviceCMYK,
|
||||
};
|
||||
|
||||
/**
|
||||
* A note of thanks to the developers of https://github.com/foliojs/pdfkit, as
|
||||
* this class borrows from:
|
||||
* https://github.com/foliojs/pdfkit/blob/a6af76467ce06bd6a2af4aa7271ccac9ff152a7d/lib/image/jpeg.js
|
||||
*/
|
||||
class JpegEmbedder {
|
||||
static async for(imageData: Uint8Array) {
|
||||
const dataView = new DataView(imageData.buffer);
|
||||
|
||||
const soi = dataView.getUint16(0);
|
||||
if (soi !== 0xffd8) throw new Error('SOI not found in JPEG');
|
||||
|
||||
let pos = 2;
|
||||
let marker: number;
|
||||
|
||||
while (pos < dataView.byteLength) {
|
||||
marker = dataView.getUint16(pos);
|
||||
pos += 2;
|
||||
if (MARKERS.includes(marker)) break;
|
||||
pos += dataView.getUint16(pos);
|
||||
}
|
||||
|
||||
if (!MARKERS.includes(marker!)) throw new Error('Invalid JPEG');
|
||||
pos += 2;
|
||||
|
||||
const bitsPerComponent = dataView.getUint8(pos++);
|
||||
const height = dataView.getUint16(pos);
|
||||
pos += 2;
|
||||
|
||||
const width = dataView.getUint16(pos);
|
||||
pos += 2;
|
||||
|
||||
const channelByte = dataView.getUint8(pos++);
|
||||
const channelName = ChannelToColorSpace[channelByte];
|
||||
|
||||
if (!channelName) throw new Error('Unknown JPEG channel.');
|
||||
|
||||
const colorSpace = channelName;
|
||||
|
||||
return new JpegEmbedder(
|
||||
imageData,
|
||||
bitsPerComponent,
|
||||
width,
|
||||
height,
|
||||
colorSpace,
|
||||
);
|
||||
}
|
||||
|
||||
readonly bitsPerComponent: number;
|
||||
readonly height: number;
|
||||
readonly width: number;
|
||||
readonly colorSpace: ColorSpace;
|
||||
|
||||
private readonly imageData: Uint8Array;
|
||||
|
||||
private constructor(
|
||||
imageData: Uint8Array,
|
||||
bitsPerComponent: number,
|
||||
width: number,
|
||||
height: number,
|
||||
colorSpace: ColorSpace,
|
||||
) {
|
||||
this.imageData = imageData;
|
||||
this.bitsPerComponent = bitsPerComponent;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.colorSpace = colorSpace;
|
||||
}
|
||||
|
||||
async embedIntoContext(context: PDFContext, ref?: PDFRef): Promise<PDFRef> {
|
||||
const xObject = context.stream(this.imageData, {
|
||||
Type: 'XObject',
|
||||
Subtype: 'Image',
|
||||
BitsPerComponent: this.bitsPerComponent,
|
||||
Width: this.width,
|
||||
Height: this.height,
|
||||
ColorSpace: this.colorSpace,
|
||||
Filter: 'DCTDecode',
|
||||
|
||||
// CMYK JPEG streams in PDF are typically stored complemented,
|
||||
// with 1 as 'off' and 0 as 'on' (PDF 32000-1:2008, 8.6.4.4).
|
||||
//
|
||||
// Standalone CMYK JPEG (usually exported by Photoshop) are
|
||||
// stored inverse, with 0 as 'off' and 1 as 'on', like RGB.
|
||||
//
|
||||
// Applying a swap here as a hedge that most bytes passing
|
||||
// through this method will benefit from it.
|
||||
Decode:
|
||||
this.colorSpace === ColorSpace.DeviceCMYK
|
||||
? [1, 0, 1, 0, 1, 0, 1, 0]
|
||||
: undefined,
|
||||
});
|
||||
|
||||
if (ref) {
|
||||
context.assign(ref, xObject);
|
||||
return ref;
|
||||
} else {
|
||||
return context.register(xObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default JpegEmbedder;
|
||||
141
frontend/node_modules/pdf-lib/src/core/embedders/PDFPageEmbedder.ts
generated
vendored
Normal file
141
frontend/node_modules/pdf-lib/src/core/embedders/PDFPageEmbedder.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
import {
|
||||
MissingPageContentsEmbeddingError,
|
||||
UnrecognizedStreamTypeError,
|
||||
} from 'src/core/errors';
|
||||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFRawStream from 'src/core/objects/PDFRawStream';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFStream from 'src/core/objects/PDFStream';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import { decodePDFRawStream } from 'src/core/streams/decode';
|
||||
import PDFContentStream from 'src/core/structures/PDFContentStream';
|
||||
import PDFPageLeaf from 'src/core/structures/PDFPageLeaf';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import { TransformationMatrix } from 'src/types/matrix';
|
||||
import { mergeIntoTypedArray } from 'src/utils';
|
||||
|
||||
/**
|
||||
* Represents a page bounding box.
|
||||
* Usually `left` and `bottom` are 0 and right, top are equal
|
||||
* to width, height if you want to clip to the whole page.
|
||||
*
|
||||
* y
|
||||
* ^
|
||||
* | +--------+ (width,height)
|
||||
* | | |
|
||||
* | | Page |
|
||||
* | | |
|
||||
* | | |
|
||||
* (0,0) | +--------+
|
||||
* +----------> x
|
||||
*/
|
||||
export interface PageBoundingBox {
|
||||
left: number /** The left of the bounding box */;
|
||||
bottom: number /** The bottom of the bounding box */;
|
||||
right: number /** The right of the bounding box */;
|
||||
top: number /** The top of the bounding box */;
|
||||
}
|
||||
|
||||
const fullPageBoundingBox = (page: PDFPageLeaf) => {
|
||||
const mediaBox = page.MediaBox();
|
||||
|
||||
const width =
|
||||
mediaBox.lookup(2, PDFNumber).asNumber() -
|
||||
mediaBox.lookup(0, PDFNumber).asNumber();
|
||||
|
||||
const height =
|
||||
mediaBox.lookup(3, PDFNumber).asNumber() -
|
||||
mediaBox.lookup(1, PDFNumber).asNumber();
|
||||
|
||||
return { left: 0, bottom: 0, right: width, top: height };
|
||||
};
|
||||
|
||||
// Returns the identity matrix, modified to position the content of the given
|
||||
// bounding box at (0, 0).
|
||||
const boundingBoxAdjustedMatrix = (
|
||||
bb: PageBoundingBox,
|
||||
): TransformationMatrix => [1, 0, 0, 1, -bb.left, -bb.bottom];
|
||||
|
||||
class PDFPageEmbedder {
|
||||
static async for(
|
||||
page: PDFPageLeaf,
|
||||
boundingBox?: PageBoundingBox,
|
||||
transformationMatrix?: TransformationMatrix,
|
||||
) {
|
||||
return new PDFPageEmbedder(page, boundingBox, transformationMatrix);
|
||||
}
|
||||
|
||||
readonly width: number;
|
||||
readonly height: number;
|
||||
readonly boundingBox: PageBoundingBox;
|
||||
readonly transformationMatrix: TransformationMatrix;
|
||||
|
||||
private readonly page: PDFPageLeaf;
|
||||
|
||||
private constructor(
|
||||
page: PDFPageLeaf,
|
||||
boundingBox?: PageBoundingBox,
|
||||
transformationMatrix?: TransformationMatrix,
|
||||
) {
|
||||
this.page = page;
|
||||
|
||||
const bb = boundingBox ?? fullPageBoundingBox(page);
|
||||
|
||||
this.width = bb.right - bb.left;
|
||||
this.height = bb.top - bb.bottom;
|
||||
this.boundingBox = bb;
|
||||
this.transformationMatrix =
|
||||
transformationMatrix ?? boundingBoxAdjustedMatrix(bb);
|
||||
}
|
||||
|
||||
async embedIntoContext(context: PDFContext, ref?: PDFRef): Promise<PDFRef> {
|
||||
const { Contents, Resources } = this.page.normalizedEntries();
|
||||
|
||||
if (!Contents) throw new MissingPageContentsEmbeddingError();
|
||||
const decodedContents = this.decodeContents(Contents);
|
||||
|
||||
const { left, bottom, right, top } = this.boundingBox;
|
||||
const xObject = context.flateStream(decodedContents, {
|
||||
Type: 'XObject',
|
||||
Subtype: 'Form',
|
||||
FormType: 1,
|
||||
BBox: [left, bottom, right, top],
|
||||
Matrix: this.transformationMatrix,
|
||||
Resources,
|
||||
});
|
||||
|
||||
if (ref) {
|
||||
context.assign(ref, xObject);
|
||||
return ref;
|
||||
} else {
|
||||
return context.register(xObject);
|
||||
}
|
||||
}
|
||||
|
||||
// `contents` is an array of streams which are merged to include them in the XObject.
|
||||
// This methods extracts each stream and joins them with a newline character.
|
||||
private decodeContents(contents: PDFArray) {
|
||||
const newline = Uint8Array.of(CharCodes.Newline);
|
||||
const decodedContents: Uint8Array[] = [];
|
||||
|
||||
for (let idx = 0, len = contents.size(); idx < len; idx++) {
|
||||
const stream = contents.lookup(idx, PDFStream);
|
||||
|
||||
let content: Uint8Array;
|
||||
if (stream instanceof PDFRawStream) {
|
||||
content = decodePDFRawStream(stream).decode();
|
||||
} else if (stream instanceof PDFContentStream) {
|
||||
content = stream.getUnencodedContents();
|
||||
} else {
|
||||
throw new UnrecognizedStreamTypeError(stream);
|
||||
}
|
||||
|
||||
decodedContents.push(content, newline);
|
||||
}
|
||||
|
||||
return mergeIntoTypedArray(...decodedContents);
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFPageEmbedder;
|
||||
69
frontend/node_modules/pdf-lib/src/core/embedders/PngEmbedder.ts
generated
vendored
Normal file
69
frontend/node_modules/pdf-lib/src/core/embedders/PngEmbedder.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import { PNG } from 'src/utils/png';
|
||||
|
||||
/**
|
||||
* A note of thanks to the developers of https://github.com/foliojs/pdfkit, as
|
||||
* this class borrows from:
|
||||
* https://github.com/devongovett/pdfkit/blob/e71edab0dd4657b5a767804ba86c94c58d01fbca/lib/image/png.coffee
|
||||
*/
|
||||
class PngEmbedder {
|
||||
static async for(imageData: Uint8Array) {
|
||||
const png = PNG.load(imageData);
|
||||
return new PngEmbedder(png);
|
||||
}
|
||||
|
||||
readonly bitsPerComponent: number;
|
||||
readonly height: number;
|
||||
readonly width: number;
|
||||
readonly colorSpace: 'DeviceRGB';
|
||||
|
||||
private readonly image: PNG;
|
||||
|
||||
private constructor(png: PNG) {
|
||||
this.image = png;
|
||||
this.bitsPerComponent = png.bitsPerComponent;
|
||||
this.width = png.width;
|
||||
this.height = png.height;
|
||||
this.colorSpace = 'DeviceRGB';
|
||||
}
|
||||
|
||||
async embedIntoContext(context: PDFContext, ref?: PDFRef): Promise<PDFRef> {
|
||||
const SMask = this.embedAlphaChannel(context);
|
||||
|
||||
const xObject = context.flateStream(this.image.rgbChannel, {
|
||||
Type: 'XObject',
|
||||
Subtype: 'Image',
|
||||
BitsPerComponent: this.image.bitsPerComponent,
|
||||
Width: this.image.width,
|
||||
Height: this.image.height,
|
||||
ColorSpace: this.colorSpace,
|
||||
SMask,
|
||||
});
|
||||
|
||||
if (ref) {
|
||||
context.assign(ref, xObject);
|
||||
return ref;
|
||||
} else {
|
||||
return context.register(xObject);
|
||||
}
|
||||
}
|
||||
|
||||
private embedAlphaChannel(context: PDFContext): PDFRef | undefined {
|
||||
if (!this.image.alphaChannel) return undefined;
|
||||
|
||||
const xObject = context.flateStream(this.image.alphaChannel, {
|
||||
Type: 'XObject',
|
||||
Subtype: 'Image',
|
||||
Height: this.image.height,
|
||||
Width: this.image.width,
|
||||
BitsPerComponent: this.image.bitsPerComponent,
|
||||
ColorSpace: 'DeviceGray',
|
||||
Decode: [0, 1],
|
||||
});
|
||||
|
||||
return context.register(xObject);
|
||||
}
|
||||
}
|
||||
|
||||
export default PngEmbedder;
|
||||
130
frontend/node_modules/pdf-lib/src/core/embedders/StandardFontEmbedder.ts
generated
vendored
Normal file
130
frontend/node_modules/pdf-lib/src/core/embedders/StandardFontEmbedder.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import {
|
||||
Encodings,
|
||||
Font,
|
||||
FontNames,
|
||||
EncodingType,
|
||||
} from '@pdf-lib/standard-fonts';
|
||||
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import { toCodePoint, toHexString } from 'src/utils';
|
||||
|
||||
export interface Glyph {
|
||||
code: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A note of thanks to the developers of https://github.com/foliojs/pdfkit, as
|
||||
* this class borrows from:
|
||||
* https://github.com/foliojs/pdfkit/blob/f91bdd61c164a72ea06be1a43dc0a412afc3925f/lib/font/afm.coffee
|
||||
*/
|
||||
class StandardFontEmbedder {
|
||||
static for = (fontName: FontNames, customName?: string) =>
|
||||
new StandardFontEmbedder(fontName, customName);
|
||||
|
||||
readonly font: Font;
|
||||
readonly encoding: EncodingType;
|
||||
readonly fontName: string;
|
||||
readonly customName: string | undefined;
|
||||
|
||||
private constructor(fontName: FontNames, customName?: string) {
|
||||
// prettier-ignore
|
||||
this.encoding = (
|
||||
fontName === FontNames.ZapfDingbats ? Encodings.ZapfDingbats
|
||||
: fontName === FontNames.Symbol ? Encodings.Symbol
|
||||
: Encodings.WinAnsi
|
||||
);
|
||||
this.font = Font.load(fontName);
|
||||
this.fontName = this.font.FontName;
|
||||
this.customName = customName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the JavaScript string into this font. (JavaScript encodes strings in
|
||||
* Unicode, but standard fonts use either WinAnsi, ZapfDingbats, or Symbol
|
||||
* encodings)
|
||||
*/
|
||||
encodeText(text: string): PDFHexString {
|
||||
const glyphs = this.encodeTextAsGlyphs(text);
|
||||
const hexCodes = new Array(glyphs.length);
|
||||
for (let idx = 0, len = glyphs.length; idx < len; idx++) {
|
||||
hexCodes[idx] = toHexString(glyphs[idx].code);
|
||||
}
|
||||
return PDFHexString.of(hexCodes.join(''));
|
||||
}
|
||||
|
||||
widthOfTextAtSize(text: string, size: number): number {
|
||||
const glyphs = this.encodeTextAsGlyphs(text);
|
||||
let totalWidth = 0;
|
||||
|
||||
for (let idx = 0, len = glyphs.length; idx < len; idx++) {
|
||||
const left = glyphs[idx].name;
|
||||
const right = (glyphs[idx + 1] || {}).name;
|
||||
const kernAmount = this.font.getXAxisKerningForPair(left, right) || 0;
|
||||
totalWidth += this.widthOfGlyph(left) + kernAmount;
|
||||
}
|
||||
|
||||
const scale = size / 1000;
|
||||
return totalWidth * scale;
|
||||
}
|
||||
|
||||
heightOfFontAtSize(
|
||||
size: number,
|
||||
options: { descender?: boolean } = {},
|
||||
): number {
|
||||
const { descender = true } = options;
|
||||
|
||||
const { Ascender, Descender, FontBBox } = this.font;
|
||||
const yTop = Ascender || FontBBox[3];
|
||||
const yBottom = Descender || FontBBox[1];
|
||||
|
||||
let height = yTop - yBottom;
|
||||
if (!descender) height += Descender || 0;
|
||||
|
||||
return (height / 1000) * size;
|
||||
}
|
||||
|
||||
sizeOfFontAtHeight(height: number): number {
|
||||
const { Ascender, Descender, FontBBox } = this.font;
|
||||
const yTop = Ascender || FontBBox[3];
|
||||
const yBottom = Descender || FontBBox[1];
|
||||
return (1000 * height) / (yTop - yBottom);
|
||||
}
|
||||
|
||||
embedIntoContext(context: PDFContext, ref?: PDFRef): PDFRef {
|
||||
const fontDict = context.obj({
|
||||
Type: 'Font',
|
||||
Subtype: 'Type1',
|
||||
BaseFont: this.customName || this.fontName,
|
||||
|
||||
Encoding:
|
||||
this.encoding === Encodings.WinAnsi ? 'WinAnsiEncoding' : undefined,
|
||||
});
|
||||
|
||||
if (ref) {
|
||||
context.assign(ref, fontDict);
|
||||
return ref;
|
||||
} else {
|
||||
return context.register(fontDict);
|
||||
}
|
||||
}
|
||||
|
||||
private widthOfGlyph(glyphName: string): number {
|
||||
// Default to 250 if font doesn't specify a width
|
||||
return this.font.getWidthOfGlyph(glyphName) || 250;
|
||||
}
|
||||
|
||||
private encodeTextAsGlyphs(text: string): Glyph[] {
|
||||
const codePoints = Array.from(text);
|
||||
const glyphs: Glyph[] = new Array(codePoints.length);
|
||||
for (let idx = 0, len = codePoints.length; idx < len; idx++) {
|
||||
const codePoint = toCodePoint(codePoints[idx])!;
|
||||
glyphs[idx] = this.encoding.encodeUnicodeCodePoint(codePoint);
|
||||
}
|
||||
return glyphs;
|
||||
}
|
||||
}
|
||||
|
||||
export default StandardFontEmbedder;
|
||||
221
frontend/node_modules/pdf-lib/src/core/errors.ts
generated
vendored
Normal file
221
frontend/node_modules/pdf-lib/src/core/errors.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
// tslint:disable: max-classes-per-file
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import { arrayAsString } from 'src/utils';
|
||||
|
||||
export class MethodNotImplementedError extends Error {
|
||||
constructor(className: string, methodName: string) {
|
||||
const msg = `Method ${className}.${methodName}() not implemented`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class PrivateConstructorError extends Error {
|
||||
constructor(className: string) {
|
||||
const msg = `Cannot construct ${className} - it has a private constructor`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class UnexpectedObjectTypeError extends Error {
|
||||
constructor(expected: any | any[], actual: any) {
|
||||
const name = (t: any) => t?.name ?? t?.constructor?.name;
|
||||
|
||||
const expectedTypes = Array.isArray(expected)
|
||||
? expected.map(name)
|
||||
: [name(expected)];
|
||||
|
||||
const msg =
|
||||
`Expected instance of ${expectedTypes.join(' or ')}, ` +
|
||||
`but got instance of ${actual ? name(actual) : actual}`;
|
||||
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class UnsupportedEncodingError extends Error {
|
||||
constructor(encoding: string) {
|
||||
const msg = `${encoding} stream encoding not supported`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class ReparseError extends Error {
|
||||
constructor(className: string, methodName: string) {
|
||||
const msg = `Cannot call ${className}.${methodName}() more than once`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class MissingCatalogError extends Error {
|
||||
constructor(ref?: PDFObject) {
|
||||
const msg = `Missing catalog (ref=${ref})`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class MissingPageContentsEmbeddingError extends Error {
|
||||
constructor() {
|
||||
const msg = `Can't embed page with missing Contents`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class UnrecognizedStreamTypeError extends Error {
|
||||
constructor(stream: any) {
|
||||
const streamType = stream?.contructor?.name ?? stream?.name ?? stream;
|
||||
const msg = `Unrecognized stream type: ${streamType}`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class PageEmbeddingMismatchedContextError extends Error {
|
||||
constructor() {
|
||||
const msg = `Found mismatched contexts while embedding pages. All pages in the array passed to \`PDFDocument.embedPages()\` must be from the same document.`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class PDFArrayIsNotRectangleError extends Error {
|
||||
constructor(size: number) {
|
||||
const msg = `Attempted to convert PDFArray with ${size} elements to rectangle, but must have exactly 4 elements.`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class InvalidPDFDateStringError extends Error {
|
||||
constructor(value: string) {
|
||||
const msg = `Attempted to convert "${value}" to a date, but it does not match the PDF date string format.`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class InvalidTargetIndexError extends Error {
|
||||
constructor(targetIndex: number, Count: number) {
|
||||
const msg = `Invalid targetIndex specified: targetIndex=${targetIndex} must be less than Count=${Count}`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class CorruptPageTreeError extends Error {
|
||||
constructor(targetIndex: number, operation: string) {
|
||||
const msg = `Failed to ${operation} at targetIndex=${targetIndex} due to corrupt page tree: It is likely that one or more 'Count' entries are invalid`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class IndexOutOfBoundsError extends Error {
|
||||
constructor(index: number, min: number, max: number) {
|
||||
const msg = `index should be at least ${min} and at most ${max}, but was actually ${index}`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class InvalidAcroFieldValueError extends Error {
|
||||
constructor() {
|
||||
const msg = `Attempted to set invalid field value`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class MultiSelectValueError extends Error {
|
||||
constructor() {
|
||||
const msg = `Attempted to select multiple values for single-select field`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class MissingDAEntryError extends Error {
|
||||
constructor(fieldName: string) {
|
||||
const msg = `No /DA (default appearance) entry found for field: ${fieldName}`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class MissingTfOperatorError extends Error {
|
||||
constructor(fieldName: string) {
|
||||
const msg = `No Tf operator found for DA of field: ${fieldName}`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/***** Parser Errors ******/
|
||||
|
||||
export interface Position {
|
||||
line: number;
|
||||
column: number;
|
||||
offset: number;
|
||||
}
|
||||
|
||||
export class NumberParsingError extends Error {
|
||||
constructor(pos: Position, value: string) {
|
||||
const msg =
|
||||
`Failed to parse number ` +
|
||||
`(line:${pos.line} col:${pos.column} offset=${pos.offset}): "${value}"`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class PDFParsingError extends Error {
|
||||
constructor(pos: Position, details: string) {
|
||||
const msg =
|
||||
`Failed to parse PDF document ` +
|
||||
`(line:${pos.line} col:${pos.column} offset=${pos.offset}): ${details}`;
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class NextByteAssertionError extends PDFParsingError {
|
||||
constructor(pos: Position, expectedByte: number, actualByte: number) {
|
||||
const msg = `Expected next byte to be ${expectedByte} but it was actually ${actualByte}`;
|
||||
super(pos, msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class PDFObjectParsingError extends PDFParsingError {
|
||||
constructor(pos: Position, byte: number) {
|
||||
const msg = `Failed to parse PDF object starting with the following byte: ${byte}`;
|
||||
super(pos, msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class PDFInvalidObjectParsingError extends PDFParsingError {
|
||||
constructor(pos: Position) {
|
||||
const msg = `Failed to parse invalid PDF object`;
|
||||
super(pos, msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class PDFStreamParsingError extends PDFParsingError {
|
||||
constructor(pos: Position) {
|
||||
const msg = `Failed to parse PDF stream`;
|
||||
super(pos, msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class UnbalancedParenthesisError extends PDFParsingError {
|
||||
constructor(pos: Position) {
|
||||
const msg = `Failed to parse PDF literal string due to unbalanced parenthesis`;
|
||||
super(pos, msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class StalledParserError extends PDFParsingError {
|
||||
constructor(pos: Position) {
|
||||
const msg = `Parser stalled`;
|
||||
super(pos, msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class MissingPDFHeaderError extends PDFParsingError {
|
||||
constructor(pos: Position) {
|
||||
const msg = `No PDF header found`;
|
||||
super(pos, msg);
|
||||
}
|
||||
}
|
||||
|
||||
export class MissingKeywordError extends PDFParsingError {
|
||||
constructor(pos: Position, keyword: number[]) {
|
||||
const msg = `Did not find expected keyword '${arrayAsString(keyword)}'`;
|
||||
super(pos, msg);
|
||||
}
|
||||
}
|
||||
69
frontend/node_modules/pdf-lib/src/core/index.ts
generated
vendored
Normal file
69
frontend/node_modules/pdf-lib/src/core/index.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
export * from 'src/core/errors';
|
||||
export { default as CharCodes } from 'src/core/syntax/CharCodes';
|
||||
|
||||
export { default as PDFContext } from 'src/core/PDFContext';
|
||||
export { default as PDFObjectCopier } from 'src/core/PDFObjectCopier';
|
||||
export { default as PDFWriter } from 'src/core/writers/PDFWriter';
|
||||
export { default as PDFStreamWriter } from 'src/core/writers/PDFStreamWriter';
|
||||
|
||||
export { default as PDFHeader } from 'src/core/document/PDFHeader';
|
||||
export { default as PDFTrailer } from 'src/core/document/PDFTrailer';
|
||||
export { default as PDFTrailerDict } from 'src/core/document/PDFTrailerDict';
|
||||
export { default as PDFCrossRefSection } from 'src/core/document/PDFCrossRefSection';
|
||||
|
||||
export { default as StandardFontEmbedder } from 'src/core/embedders/StandardFontEmbedder';
|
||||
export { default as CustomFontEmbedder } from 'src/core/embedders/CustomFontEmbedder';
|
||||
export { default as CustomFontSubsetEmbedder } from 'src/core/embedders/CustomFontSubsetEmbedder';
|
||||
export {
|
||||
default as FileEmbedder,
|
||||
AFRelationship,
|
||||
} from 'src/core/embedders/FileEmbedder';
|
||||
export { default as JpegEmbedder } from 'src/core/embedders/JpegEmbedder';
|
||||
export { default as PngEmbedder } from 'src/core/embedders/PngEmbedder';
|
||||
export {
|
||||
default as PDFPageEmbedder,
|
||||
PageBoundingBox,
|
||||
} from 'src/core/embedders/PDFPageEmbedder';
|
||||
|
||||
export {
|
||||
default as ViewerPreferences,
|
||||
NonFullScreenPageMode,
|
||||
ReadingDirection,
|
||||
PrintScaling,
|
||||
Duplex,
|
||||
} from 'src/core/interactive/ViewerPreferences';
|
||||
|
||||
export { default as PDFObject } from 'src/core/objects/PDFObject';
|
||||
export { default as PDFBool } from 'src/core/objects/PDFBool';
|
||||
export { default as PDFNumber } from 'src/core/objects/PDFNumber';
|
||||
export { default as PDFString } from 'src/core/objects/PDFString';
|
||||
export { default as PDFHexString } from 'src/core/objects/PDFHexString';
|
||||
export { default as PDFName } from 'src/core/objects/PDFName';
|
||||
export { default as PDFNull } from 'src/core/objects/PDFNull';
|
||||
export { default as PDFArray } from 'src/core/objects/PDFArray';
|
||||
export { default as PDFDict } from 'src/core/objects/PDFDict';
|
||||
export { default as PDFRef } from 'src/core/objects/PDFRef';
|
||||
export { default as PDFInvalidObject } from 'src/core/objects/PDFInvalidObject';
|
||||
export { default as PDFStream } from 'src/core/objects/PDFStream';
|
||||
export { default as PDFRawStream } from 'src/core/objects/PDFRawStream';
|
||||
|
||||
export { default as PDFCatalog } from 'src/core/structures/PDFCatalog';
|
||||
export { default as PDFContentStream } from 'src/core/structures/PDFContentStream';
|
||||
export { default as PDFCrossRefStream } from 'src/core/structures/PDFCrossRefStream';
|
||||
export { default as PDFObjectStream } from 'src/core/structures/PDFObjectStream';
|
||||
export { default as PDFPageTree } from 'src/core/structures/PDFPageTree';
|
||||
export { default as PDFPageLeaf } from 'src/core/structures/PDFPageLeaf';
|
||||
export { default as PDFFlateStream } from 'src/core/structures/PDFFlateStream';
|
||||
|
||||
export { default as PDFOperator } from 'src/core/operators/PDFOperator';
|
||||
export { default as PDFOperatorNames } from 'src/core/operators/PDFOperatorNames';
|
||||
|
||||
export { default as PDFObjectParser } from 'src/core/parser/PDFObjectParser';
|
||||
export { default as PDFObjectStreamParser } from 'src/core/parser/PDFObjectStreamParser';
|
||||
export { default as PDFParser } from 'src/core/parser/PDFParser';
|
||||
export { default as PDFXRefStreamParser } from 'src/core/parser/PDFXRefStreamParser';
|
||||
|
||||
export { decodePDFRawStream } from 'src/core/streams/decode';
|
||||
|
||||
export * from 'src/core/annotation';
|
||||
export * from 'src/core/acroform';
|
||||
579
frontend/node_modules/pdf-lib/src/core/interactive/ViewerPreferences.ts
generated
vendored
Normal file
579
frontend/node_modules/pdf-lib/src/core/interactive/ViewerPreferences.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,579 @@
|
|||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFBool from 'src/core/objects/PDFBool';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import {
|
||||
assertEachIs,
|
||||
assertInteger,
|
||||
assertIsOneOf,
|
||||
assertRange,
|
||||
} from 'src/utils';
|
||||
|
||||
const asEnum = <T extends string | number, U extends { [key: string]: T }>(
|
||||
rawValue: T | undefined,
|
||||
enumType: U,
|
||||
): U[keyof U] | undefined => {
|
||||
if (rawValue === undefined) return undefined;
|
||||
return enumType[rawValue];
|
||||
};
|
||||
|
||||
export enum NonFullScreenPageMode {
|
||||
/**
|
||||
* After exiting FullScreen mode, neither the document outline nor thumbnail
|
||||
* images should be visible.
|
||||
*/
|
||||
UseNone = 'UseNone',
|
||||
|
||||
/** After exiting FullScreen mode, the document outline should be visible. */
|
||||
UseOutlines = 'UseOutlines',
|
||||
|
||||
/** After exiting FullScreen mode, thumbnail images should be visible. */
|
||||
UseThumbs = 'UseThumbs',
|
||||
|
||||
/**
|
||||
* After exiting FullScreen mode, the optional content group panel should be
|
||||
* visible.
|
||||
*/
|
||||
UseOC = 'UseOC',
|
||||
}
|
||||
|
||||
export enum ReadingDirection {
|
||||
/** The predominant reading order is Left to Right. */
|
||||
L2R = 'L2R',
|
||||
|
||||
/**
|
||||
* The predominant reading order is Right to left (including vertical writing
|
||||
* systems, such as Chinese, Japanese and Korean).
|
||||
*/
|
||||
R2L = 'R2L',
|
||||
}
|
||||
|
||||
export enum PrintScaling {
|
||||
/** No page scaling. */
|
||||
None = 'None',
|
||||
|
||||
/* Use the PDF reader's default print scaling. */
|
||||
AppDefault = 'AppDefault',
|
||||
}
|
||||
|
||||
export enum Duplex {
|
||||
/** The PDF reader should print single-sided. */
|
||||
Simplex = 'Simplex',
|
||||
|
||||
/**
|
||||
* The PDF reader should print double sided and flip on the short edge of the
|
||||
* sheet.
|
||||
*/
|
||||
DuplexFlipShortEdge = 'DuplexFlipShortEdge',
|
||||
|
||||
/**
|
||||
* The PDF reader should print double sided and flip on the long edge of the
|
||||
* sheet.
|
||||
*/
|
||||
DuplexFlipLongEdge = 'DuplexFlipLongEdge',
|
||||
}
|
||||
|
||||
type BoolViewerPrefKey =
|
||||
| 'HideToolbar'
|
||||
| 'HideMenubar'
|
||||
| 'HideWindowUI'
|
||||
| 'FitWindow'
|
||||
| 'CenterWindow'
|
||||
| 'DisplayDocTitle'
|
||||
| 'PickTrayByPDFSize';
|
||||
type NameViewerPrefKey =
|
||||
| 'NonFullScreenPageMode'
|
||||
| 'Direction'
|
||||
| 'PrintScaling'
|
||||
| 'Duplex';
|
||||
|
||||
interface PageRange {
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
class ViewerPreferences {
|
||||
/** @ignore */
|
||||
readonly dict: PDFDict;
|
||||
|
||||
/** @ignore */
|
||||
static fromDict = (dict: PDFDict): ViewerPreferences =>
|
||||
new ViewerPreferences(dict);
|
||||
|
||||
/** @ignore */
|
||||
static create = (context: PDFContext) => {
|
||||
const dict = context.obj({});
|
||||
return new ViewerPreferences(dict);
|
||||
};
|
||||
|
||||
/** @ignore */
|
||||
protected constructor(dict: PDFDict) {
|
||||
this.dict = dict;
|
||||
}
|
||||
|
||||
protected lookupBool(key: BoolViewerPrefKey): PDFBool | undefined {
|
||||
const returnObj = this.dict.lookup(PDFName.of(key));
|
||||
if (returnObj instanceof PDFBool) return returnObj;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected lookupName(key: NameViewerPrefKey): PDFName | undefined {
|
||||
const returnObj = this.dict.lookup(PDFName.of(key));
|
||||
if (returnObj instanceof PDFName) return returnObj;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
HideToolbar(): PDFBool | undefined {
|
||||
return this.lookupBool('HideToolbar');
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
HideMenubar(): PDFBool | undefined {
|
||||
return this.lookupBool('HideMenubar');
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
HideWindowUI(): PDFBool | undefined {
|
||||
return this.lookupBool('HideWindowUI');
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
FitWindow(): PDFBool | undefined {
|
||||
return this.lookupBool('FitWindow');
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
CenterWindow(): PDFBool | undefined {
|
||||
return this.lookupBool('CenterWindow');
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
DisplayDocTitle(): PDFBool | undefined {
|
||||
return this.lookupBool('DisplayDocTitle');
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
NonFullScreenPageMode(): PDFName | undefined {
|
||||
return this.lookupName('NonFullScreenPageMode');
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
Direction(): PDFName | undefined {
|
||||
return this.lookupName('Direction');
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
PrintScaling(): PDFName | undefined {
|
||||
return this.lookupName('PrintScaling');
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
Duplex(): PDFName | undefined {
|
||||
return this.lookupName('Duplex');
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
PickTrayByPDFSize(): PDFBool | undefined {
|
||||
return this.lookupBool('PickTrayByPDFSize');
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
PrintPageRange(): PDFArray | undefined {
|
||||
const PrintPageRange = this.dict.lookup(PDFName.of('PrintPageRange'));
|
||||
if (PrintPageRange instanceof PDFArray) return PrintPageRange;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
NumCopies(): PDFNumber | undefined {
|
||||
const NumCopies = this.dict.lookup(PDFName.of('NumCopies'));
|
||||
if (NumCopies instanceof PDFNumber) return NumCopies;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if PDF readers should hide the toolbar menus when displaying
|
||||
* this document.
|
||||
* @returns Whether or not toolbars should be hidden.
|
||||
*/
|
||||
getHideToolbar(): boolean {
|
||||
return this.HideToolbar()?.asBoolean() ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if PDF readers should hide the menu bar when displaying this
|
||||
* document.
|
||||
* @returns Whether or not the menu bar should be hidden.
|
||||
*/
|
||||
getHideMenubar(): boolean {
|
||||
return this.HideMenubar()?.asBoolean() ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if PDF readers should hide the user interface elements in
|
||||
* the document's window (such as scroll bars and navigation controls),
|
||||
* leaving only the document's contents displayed.
|
||||
* @returns Whether or not user interface elements should be hidden.
|
||||
*/
|
||||
getHideWindowUI(): boolean {
|
||||
return this.HideWindowUI()?.asBoolean() ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if PDF readers should resize the document's window to fit
|
||||
* the size of the first displayed page.
|
||||
* @returns Whether or not the window should be resized to fit.
|
||||
*/
|
||||
getFitWindow(): boolean {
|
||||
return this.FitWindow()?.asBoolean() ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if PDF readers should position the document's window in the
|
||||
* center of the screen.
|
||||
* @returns Whether or not to center the document window.
|
||||
*/
|
||||
getCenterWindow(): boolean {
|
||||
return this.CenterWindow()?.asBoolean() ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the window's title bar should display the document
|
||||
* `Title`, taken from the document metadata (see [[PDFDocument.getTitle]]).
|
||||
* Returns `false` if the title bar should instead display the filename of the
|
||||
* PDF file.
|
||||
* @returns Whether to display the document title.
|
||||
*/
|
||||
getDisplayDocTitle(): boolean {
|
||||
return this.DisplayDocTitle()?.asBoolean() ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page mode, which tells the PDF reader how to display the
|
||||
* document after exiting full-screen mode.
|
||||
* @returns The page mode after exiting full-screen mode.
|
||||
*/
|
||||
getNonFullScreenPageMode(): NonFullScreenPageMode {
|
||||
const mode = this.NonFullScreenPageMode()?.decodeText();
|
||||
return asEnum(mode, NonFullScreenPageMode) ?? NonFullScreenPageMode.UseNone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the predominant reading order for text.
|
||||
* @returns The text reading order.
|
||||
*/
|
||||
getReadingDirection(): ReadingDirection {
|
||||
const direction = this.Direction()?.decodeText();
|
||||
return asEnum(direction, ReadingDirection) ?? ReadingDirection.L2R;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page scaling option that the PDF reader should select when the
|
||||
* print dialog is displayed.
|
||||
* @returns The page scaling option.
|
||||
*/
|
||||
getPrintScaling(): PrintScaling {
|
||||
const scaling = this.PrintScaling()?.decodeText();
|
||||
return asEnum(scaling, PrintScaling) ?? PrintScaling.AppDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paper handling option that should be used when printing the
|
||||
* file from the print dialog.
|
||||
* @returns The paper handling option.
|
||||
*/
|
||||
getDuplex(): Duplex | undefined {
|
||||
const duplex = this.Duplex()?.decodeText();
|
||||
return asEnum(duplex, Duplex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the PDF page size should be used to select the input
|
||||
* paper tray.
|
||||
* @returns Whether or not the PDF page size should be used to select the
|
||||
* input paper tray.
|
||||
*/
|
||||
getPickTrayByPDFSize(): boolean | undefined {
|
||||
return this.PickTrayByPDFSize()?.asBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of page number ranges, which are the values used to
|
||||
* initialize the print dialog box when the file is printed. Each range
|
||||
* specifies the first (`start`) and last (`end`) pages in a sub-range of
|
||||
* pages to be printed. The first page of the PDF file is denoted by 0.
|
||||
* For example:
|
||||
* ```js
|
||||
* const viewerPrefs = pdfDoc.catalog.getOrCreateViewerPreferences()
|
||||
* const includesPage3 = viewerPrefs
|
||||
* .getPrintRanges()
|
||||
* .some(pr => pr.start =< 2 && pr.end >= 2)
|
||||
* if (includesPage3) console.log('printRange includes page 3')
|
||||
* ```
|
||||
* @returns An array of objects, each with the properties `start` and `end`,
|
||||
* denoting page indices. If not, specified an empty array is
|
||||
* returned.
|
||||
*/
|
||||
getPrintPageRange(): PageRange[] {
|
||||
const rng = this.PrintPageRange();
|
||||
if (!rng) return [];
|
||||
|
||||
const pageRanges: PageRange[] = [];
|
||||
for (let i = 0; i < rng.size(); i += 2) {
|
||||
// Despite the spec clearly stating that "The first page of the PDF file
|
||||
// shall be donoted by 1", several test PDFs (spec 1.7) created in
|
||||
// Acrobat XI 11.0 and also read with Reader DC 2020.013 indicate this is
|
||||
// actually a 0 based index.
|
||||
const start = rng.lookup(i, PDFNumber).asNumber();
|
||||
const end = rng.lookup(i + 1, PDFNumber).asNumber();
|
||||
pageRanges.push({ start, end });
|
||||
}
|
||||
|
||||
return pageRanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of copies to be printed when the print dialog is opened
|
||||
* for this document.
|
||||
* @returns The default number of copies to be printed.
|
||||
*/
|
||||
getNumCopies(): number {
|
||||
return this.NumCopies()?.asNumber() ?? 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose whether the PDF reader's toolbars should be hidden while the
|
||||
* document is active.
|
||||
* @param hideToolbar `true` if the toolbar should be hidden.
|
||||
*/
|
||||
setHideToolbar(hideToolbar: boolean) {
|
||||
const HideToolbar = this.dict.context.obj(hideToolbar);
|
||||
this.dict.set(PDFName.of('HideToolbar'), HideToolbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose whether the PDF reader's menu bar should be hidden while the
|
||||
* document is active.
|
||||
* @param hideMenubar `true` if the menu bar should be hidden.
|
||||
*/
|
||||
setHideMenubar(hideMenubar: boolean) {
|
||||
const HideMenubar = this.dict.context.obj(hideMenubar);
|
||||
this.dict.set(PDFName.of('HideMenubar'), HideMenubar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose whether the PDF reader should hide user interface elements in the
|
||||
* document's window (such as scroll bars and navigation controls), leaving
|
||||
* only the document's contents displayed.
|
||||
* @param hideWindowUI `true` if the user interface elements should be hidden.
|
||||
*/
|
||||
setHideWindowUI(hideWindowUI: boolean) {
|
||||
const HideWindowUI = this.dict.context.obj(hideWindowUI);
|
||||
this.dict.set(PDFName.of('HideWindowUI'), HideWindowUI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose whether the PDF reader should resize the document's window to fit
|
||||
* the size of the first displayed page.
|
||||
* @param fitWindow `true` if the window should be resized.
|
||||
*/
|
||||
setFitWindow(fitWindow: boolean) {
|
||||
const FitWindow = this.dict.context.obj(fitWindow);
|
||||
this.dict.set(PDFName.of('FitWindow'), FitWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose whether the PDF reader should position the document's window in the
|
||||
* center of the screen.
|
||||
* @param centerWindow `true` if the window should be centered.
|
||||
*/
|
||||
setCenterWindow(centerWindow: boolean) {
|
||||
const CenterWindow = this.dict.context.obj(centerWindow);
|
||||
this.dict.set(PDFName.of('CenterWindow'), CenterWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose whether the window's title bar should display the document `Title`
|
||||
* taken from the document metadata (see [[PDFDocument.setTitle]]). If
|
||||
* `false`, the title bar should instead display the PDF filename.
|
||||
* @param displayTitle `true` if the document title should be displayed.
|
||||
*/
|
||||
setDisplayDocTitle(displayTitle: boolean) {
|
||||
const DisplayDocTitle = this.dict.context.obj(displayTitle);
|
||||
this.dict.set(PDFName.of('DisplayDocTitle'), DisplayDocTitle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose how the PDF reader should display the document upon exiting
|
||||
* full-screen mode. This entry is meaningful only if the value of the
|
||||
* `PageMode` entry in the document's [[PDFCatalog]] is `FullScreen`.
|
||||
*
|
||||
* For example:
|
||||
* ```js
|
||||
* import { PDFDocument, NonFullScreenPageMode, PDFName } from 'pdf-lib'
|
||||
*
|
||||
* const pdfDoc = await PDFDocument.create()
|
||||
*
|
||||
* // Set the PageMode
|
||||
* pdfDoc.catalog.set(PDFName.of('PageMode'),PDFName.of('FullScreen'))
|
||||
*
|
||||
* // Set what happens when full-screen is closed
|
||||
* const viewerPrefs = pdfDoc.catalog.getOrCreateViewerPreferences()
|
||||
* viewerPrefs.setNonFullScreenPageMode(NonFullScreenPageMode.UseOutlines)
|
||||
* ```
|
||||
*
|
||||
* @param nonFullScreenPageMode How the document should be displayed upon
|
||||
* exiting full screen mode.
|
||||
*/
|
||||
setNonFullScreenPageMode(nonFullScreenPageMode: NonFullScreenPageMode) {
|
||||
assertIsOneOf(
|
||||
nonFullScreenPageMode,
|
||||
'nonFullScreenPageMode',
|
||||
NonFullScreenPageMode,
|
||||
);
|
||||
const mode = PDFName.of(nonFullScreenPageMode);
|
||||
this.dict.set(PDFName.of('NonFullScreenPageMode'), mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the predominant reading order for text.
|
||||
*
|
||||
* This entry has no direct effect on the document's contents or page
|
||||
* numbering, but may be used to determine the relative positioning of pages
|
||||
* when displayed side by side or printed n-up.
|
||||
*
|
||||
* For example:
|
||||
* ```js
|
||||
* import { PDFDocument, ReadingDirection } from 'pdf-lib'
|
||||
*
|
||||
* const pdfDoc = await PDFDocument.create()
|
||||
* const viewerPrefs = pdfDoc.catalog.getOrCreateViewerPreferences()
|
||||
* viewerPrefs.setReadingDirection(ReadingDirection.R2L)
|
||||
* ```
|
||||
*
|
||||
* @param readingDirection The reading order for text.
|
||||
*/
|
||||
setReadingDirection(readingDirection: ReadingDirection) {
|
||||
assertIsOneOf(readingDirection, 'readingDirection', ReadingDirection);
|
||||
const direction = PDFName.of(readingDirection);
|
||||
this.dict.set(PDFName.of('Direction'), direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the page scaling option that should be selected when a print dialog
|
||||
* is displayed for this document.
|
||||
*
|
||||
* For example:
|
||||
* ```js
|
||||
* import { PDFDocument, PrintScaling } from 'pdf-lib'
|
||||
*
|
||||
* const pdfDoc = await PDFDocument.create()
|
||||
* const viewerPrefs = pdfDoc.catalog.getOrCreateViewerPreferences()
|
||||
* viewerPrefs.setPrintScaling(PrintScaling.None)
|
||||
* ```
|
||||
*
|
||||
* @param printScaling The print scaling option.
|
||||
*/
|
||||
setPrintScaling(printScaling: PrintScaling) {
|
||||
assertIsOneOf(printScaling, 'printScaling', PrintScaling);
|
||||
const scaling = PDFName.of(printScaling);
|
||||
this.dict.set(PDFName.of('PrintScaling'), scaling);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the paper handling option that should be selected by default in the
|
||||
* print dialog.
|
||||
*
|
||||
* For example:
|
||||
* ```js
|
||||
* import { PDFDocument, Duplex } from 'pdf-lib'
|
||||
*
|
||||
* const pdfDoc = await PDFDocument.create()
|
||||
* const viewerPrefs = pdfDoc.catalog.getOrCreateViewerPreferences()
|
||||
* viewerPrefs.setDuplex(Duplex.DuplexFlipShortEdge)
|
||||
* ```
|
||||
*
|
||||
* @param duplex The double or single sided printing option.
|
||||
*/
|
||||
setDuplex(duplex: Duplex) {
|
||||
assertIsOneOf(duplex, 'duplex', Duplex);
|
||||
const dup = PDFName.of(duplex);
|
||||
this.dict.set(PDFName.of('Duplex'), dup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose whether the PDF document's page size should be used to select the
|
||||
* input paper tray when printing. This setting influences only the preset
|
||||
* values used to populate the print dialog presented by a PDF reader.
|
||||
*
|
||||
* If PickTrayByPDFSize is true, the check box in the print dialog associated
|
||||
* with input paper tray should be checked. This setting has no effect on
|
||||
* operating systems that do not provide the ability to pick the input tray
|
||||
* by size.
|
||||
*
|
||||
* @param pickTrayByPDFSize `true` if the document's page size should be used
|
||||
* to select the input paper tray.
|
||||
*/
|
||||
setPickTrayByPDFSize(pickTrayByPDFSize: boolean) {
|
||||
const PickTrayByPDFSize = this.dict.context.obj(pickTrayByPDFSize);
|
||||
this.dict.set(PDFName.of('PickTrayByPDFSize'), PickTrayByPDFSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the page numbers used to initialize the print dialog box when the
|
||||
* file is printed. The first page of the PDF file is denoted by 0.
|
||||
*
|
||||
* For example:
|
||||
* ```js
|
||||
* import { PDFDocument } from 'pdf-lib'
|
||||
*
|
||||
* const pdfDoc = await PDFDocument.create()
|
||||
* const viewerPrefs = pdfDoc.catalog.getOrCreateViewerPreferences()
|
||||
*
|
||||
* // We can set the default print range to only the first page
|
||||
* viewerPrefs.setPrintPageRange({ start: 0, end: 0 })
|
||||
*
|
||||
* // Or we can supply noncontiguous ranges (e.g. pages 1, 3, and 5-7)
|
||||
* viewerPrefs.setPrintPageRange([
|
||||
* { start: 0, end: 0 },
|
||||
* { start: 2, end: 2 },
|
||||
* { start: 4, end: 6 },
|
||||
* ])
|
||||
* ```
|
||||
*
|
||||
* @param printPageRange An object or array of objects, each with the
|
||||
* properties `start` and `end`, denoting a range of
|
||||
* page indices.
|
||||
*/
|
||||
setPrintPageRange(printPageRange: PageRange[] | PageRange) {
|
||||
if (!Array.isArray(printPageRange)) printPageRange = [printPageRange];
|
||||
|
||||
const flatRange: number[] = [];
|
||||
for (let idx = 0, len = printPageRange.length; idx < len; idx++) {
|
||||
flatRange.push(printPageRange[idx].start);
|
||||
flatRange.push(printPageRange[idx].end);
|
||||
}
|
||||
|
||||
assertEachIs(flatRange, 'printPageRange', ['number']);
|
||||
|
||||
const pageRanges = this.dict.context.obj(flatRange);
|
||||
this.dict.set(PDFName.of('PrintPageRange'), pageRanges);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the default number of copies to be printed when the print dialog is
|
||||
* opened for this file.
|
||||
* @param numCopies The default number of copies.
|
||||
*/
|
||||
setNumCopies(numCopies: number) {
|
||||
assertRange(numCopies, 'numCopies', 1, Number.MAX_VALUE);
|
||||
assertInteger(numCopies, 'numCopies');
|
||||
const NumCopies = this.dict.context.obj(numCopies);
|
||||
this.dict.set(PDFName.of('NumCopies'), NumCopies);
|
||||
}
|
||||
}
|
||||
|
||||
export default ViewerPreferences;
|
||||
185
frontend/node_modules/pdf-lib/src/core/objects/PDFArray.ts
generated
vendored
Normal file
185
frontend/node_modules/pdf-lib/src/core/objects/PDFArray.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
import PDFBool from 'src/core/objects/PDFBool';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNull from 'src/core/objects/PDFNull';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFStream from 'src/core/objects/PDFStream';
|
||||
import PDFString from 'src/core/objects/PDFString';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import { PDFArrayIsNotRectangleError } from 'src/core/errors';
|
||||
import PDFRawStream from 'src/core/objects/PDFRawStream';
|
||||
|
||||
class PDFArray extends PDFObject {
|
||||
static withContext = (context: PDFContext) => new PDFArray(context);
|
||||
|
||||
private readonly array: PDFObject[];
|
||||
private readonly context: PDFContext;
|
||||
|
||||
private constructor(context: PDFContext) {
|
||||
super();
|
||||
this.array = [];
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
size(): number {
|
||||
return this.array.length;
|
||||
}
|
||||
|
||||
push(object: PDFObject): void {
|
||||
this.array.push(object);
|
||||
}
|
||||
|
||||
insert(index: number, object: PDFObject): void {
|
||||
this.array.splice(index, 0, object);
|
||||
}
|
||||
|
||||
indexOf(object: PDFObject): number | undefined {
|
||||
const index = this.array.indexOf(object);
|
||||
return index === -1 ? undefined : index;
|
||||
}
|
||||
|
||||
remove(index: number): void {
|
||||
this.array.splice(index, 1);
|
||||
}
|
||||
|
||||
set(idx: number, object: PDFObject): void {
|
||||
this.array[idx] = object;
|
||||
}
|
||||
|
||||
get(index: number): PDFObject {
|
||||
return this.array[index];
|
||||
}
|
||||
|
||||
lookupMaybe(index: number, type: typeof PDFArray): PDFArray | undefined;
|
||||
lookupMaybe(index: number, type: typeof PDFBool): PDFBool | undefined;
|
||||
lookupMaybe(index: number, type: typeof PDFDict): PDFDict | undefined;
|
||||
lookupMaybe(
|
||||
index: number,
|
||||
type: typeof PDFHexString,
|
||||
): PDFHexString | undefined;
|
||||
lookupMaybe(index: number, type: typeof PDFName): PDFName | undefined;
|
||||
lookupMaybe(index: number, type: typeof PDFNull): typeof PDFNull | undefined;
|
||||
lookupMaybe(index: number, type: typeof PDFNumber): PDFNumber | undefined;
|
||||
lookupMaybe(index: number, type: typeof PDFStream): PDFStream | undefined;
|
||||
lookupMaybe(
|
||||
index: number,
|
||||
type: typeof PDFRawStream,
|
||||
): PDFRawStream | undefined;
|
||||
lookupMaybe(index: number, type: typeof PDFRef): PDFRef | undefined;
|
||||
lookupMaybe(index: number, type: typeof PDFString): PDFString | undefined;
|
||||
lookupMaybe(
|
||||
index: number,
|
||||
type1: typeof PDFString,
|
||||
type2: typeof PDFHexString,
|
||||
): PDFString | PDFHexString | undefined;
|
||||
|
||||
lookupMaybe(index: number, ...types: any[]) {
|
||||
return this.context.lookupMaybe(
|
||||
this.get(index),
|
||||
// @ts-ignore
|
||||
...types,
|
||||
) as any;
|
||||
}
|
||||
|
||||
lookup(index: number): PDFObject | undefined;
|
||||
lookup(index: number, type: typeof PDFArray): PDFArray;
|
||||
lookup(index: number, type: typeof PDFBool): PDFBool;
|
||||
lookup(index: number, type: typeof PDFDict): PDFDict;
|
||||
lookup(index: number, type: typeof PDFHexString): PDFHexString;
|
||||
lookup(index: number, type: typeof PDFName): PDFName;
|
||||
lookup(index: number, type: typeof PDFNull): typeof PDFNull;
|
||||
lookup(index: number, type: typeof PDFNumber): PDFNumber;
|
||||
lookup(index: number, type: typeof PDFStream): PDFStream;
|
||||
lookup(index: number, type: typeof PDFRawStream): PDFRawStream;
|
||||
lookup(index: number, type: typeof PDFRef): PDFRef;
|
||||
lookup(index: number, type: typeof PDFString): PDFString;
|
||||
lookup(
|
||||
index: number,
|
||||
type1: typeof PDFString,
|
||||
type2: typeof PDFHexString,
|
||||
): PDFString | PDFHexString;
|
||||
|
||||
lookup(index: number, ...types: any[]) {
|
||||
return this.context.lookup(
|
||||
this.get(index),
|
||||
// @ts-ignore
|
||||
...types,
|
||||
) as any;
|
||||
}
|
||||
|
||||
asRectangle(): { x: number; y: number; width: number; height: number } {
|
||||
if (this.size() !== 4) throw new PDFArrayIsNotRectangleError(this.size());
|
||||
|
||||
const lowerLeftX = this.lookup(0, PDFNumber).asNumber();
|
||||
const lowerLeftY = this.lookup(1, PDFNumber).asNumber();
|
||||
const upperRightX = this.lookup(2, PDFNumber).asNumber();
|
||||
const upperRightY = this.lookup(3, PDFNumber).asNumber();
|
||||
|
||||
const x = lowerLeftX;
|
||||
const y = lowerLeftY;
|
||||
const width = upperRightX - lowerLeftX;
|
||||
const height = upperRightY - lowerLeftY;
|
||||
|
||||
return { x, y, width, height };
|
||||
}
|
||||
|
||||
asArray(): PDFObject[] {
|
||||
return this.array.slice();
|
||||
}
|
||||
|
||||
clone(context?: PDFContext): PDFArray {
|
||||
const clone = PDFArray.withContext(context || this.context);
|
||||
for (let idx = 0, len = this.size(); idx < len; idx++) {
|
||||
clone.push(this.array[idx]);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
let arrayString = '[ ';
|
||||
for (let idx = 0, len = this.size(); idx < len; idx++) {
|
||||
arrayString += this.get(idx).toString();
|
||||
arrayString += ' ';
|
||||
}
|
||||
arrayString += ']';
|
||||
return arrayString;
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
let size = 3;
|
||||
for (let idx = 0, len = this.size(); idx < len; idx++) {
|
||||
size += this.get(idx).sizeInBytes() + 1;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
const initialOffset = offset;
|
||||
|
||||
buffer[offset++] = CharCodes.LeftSquareBracket;
|
||||
buffer[offset++] = CharCodes.Space;
|
||||
for (let idx = 0, len = this.size(); idx < len; idx++) {
|
||||
offset += this.get(idx).copyBytesInto(buffer, offset);
|
||||
buffer[offset++] = CharCodes.Space;
|
||||
}
|
||||
buffer[offset++] = CharCodes.RightSquareBracket;
|
||||
|
||||
return offset - initialOffset;
|
||||
}
|
||||
|
||||
scalePDFNumbers(x: number, y: number): void {
|
||||
for (let idx = 0, len = this.size(); idx < len; idx++) {
|
||||
const el = this.lookup(idx);
|
||||
if (el instanceof PDFNumber) {
|
||||
const factor = idx % 2 === 0 ? x : y;
|
||||
this.set(idx, PDFNumber.of(el.asNumber() * factor));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFArray;
|
||||
53
frontend/node_modules/pdf-lib/src/core/objects/PDFBool.ts
generated
vendored
Normal file
53
frontend/node_modules/pdf-lib/src/core/objects/PDFBool.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { PrivateConstructorError } from 'src/core/errors';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
|
||||
const ENFORCER = {};
|
||||
|
||||
class PDFBool extends PDFObject {
|
||||
static readonly True = new PDFBool(ENFORCER, true);
|
||||
static readonly False = new PDFBool(ENFORCER, false);
|
||||
|
||||
private readonly value: boolean;
|
||||
|
||||
private constructor(enforcer: any, value: boolean) {
|
||||
if (enforcer !== ENFORCER) throw new PrivateConstructorError('PDFBool');
|
||||
super();
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
asBoolean(): boolean {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
clone(): PDFBool {
|
||||
return this;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return String(this.value);
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
return this.value ? 4 : 5;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
if (this.value) {
|
||||
buffer[offset++] = CharCodes.t;
|
||||
buffer[offset++] = CharCodes.r;
|
||||
buffer[offset++] = CharCodes.u;
|
||||
buffer[offset++] = CharCodes.e;
|
||||
return 4;
|
||||
} else {
|
||||
buffer[offset++] = CharCodes.f;
|
||||
buffer[offset++] = CharCodes.a;
|
||||
buffer[offset++] = CharCodes.l;
|
||||
buffer[offset++] = CharCodes.s;
|
||||
buffer[offset++] = CharCodes.e;
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFBool;
|
||||
226
frontend/node_modules/pdf-lib/src/core/objects/PDFDict.ts
generated
vendored
Normal file
226
frontend/node_modules/pdf-lib/src/core/objects/PDFDict.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFBool from 'src/core/objects/PDFBool';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNull from 'src/core/objects/PDFNull';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFStream from 'src/core/objects/PDFStream';
|
||||
import PDFString from 'src/core/objects/PDFString';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
|
||||
export type DictMap = Map<PDFName, PDFObject>;
|
||||
|
||||
class PDFDict extends PDFObject {
|
||||
static withContext = (context: PDFContext) => new PDFDict(new Map(), context);
|
||||
|
||||
static fromMapWithContext = (map: DictMap, context: PDFContext) =>
|
||||
new PDFDict(map, context);
|
||||
|
||||
readonly context: PDFContext;
|
||||
|
||||
private readonly dict: DictMap;
|
||||
|
||||
protected constructor(map: DictMap, context: PDFContext) {
|
||||
super();
|
||||
this.dict = map;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
keys(): PDFName[] {
|
||||
return Array.from(this.dict.keys());
|
||||
}
|
||||
|
||||
values(): PDFObject[] {
|
||||
return Array.from(this.dict.values());
|
||||
}
|
||||
|
||||
entries(): [PDFName, PDFObject][] {
|
||||
return Array.from(this.dict.entries());
|
||||
}
|
||||
|
||||
set(key: PDFName, value: PDFObject): void {
|
||||
this.dict.set(key, value);
|
||||
}
|
||||
|
||||
get(
|
||||
key: PDFName,
|
||||
// TODO: `preservePDFNull` is for backwards compatibility. Should be
|
||||
// removed in next breaking API change.
|
||||
preservePDFNull = false,
|
||||
): PDFObject | undefined {
|
||||
const value = this.dict.get(key);
|
||||
if (value === PDFNull && !preservePDFNull) return undefined;
|
||||
return value;
|
||||
}
|
||||
|
||||
has(key: PDFName): boolean {
|
||||
const value = this.dict.get(key);
|
||||
return value !== undefined && value !== PDFNull;
|
||||
}
|
||||
|
||||
lookupMaybe(key: PDFName, type: typeof PDFArray): PDFArray | undefined;
|
||||
lookupMaybe(key: PDFName, type: typeof PDFBool): PDFBool | undefined;
|
||||
lookupMaybe(key: PDFName, type: typeof PDFDict): PDFDict | undefined;
|
||||
lookupMaybe(
|
||||
key: PDFName,
|
||||
type: typeof PDFHexString,
|
||||
): PDFHexString | undefined;
|
||||
lookupMaybe(key: PDFName, type: typeof PDFName): PDFName | undefined;
|
||||
lookupMaybe(key: PDFName, type: typeof PDFNull): typeof PDFNull | undefined;
|
||||
lookupMaybe(key: PDFName, type: typeof PDFNumber): PDFNumber | undefined;
|
||||
lookupMaybe(key: PDFName, type: typeof PDFStream): PDFStream | undefined;
|
||||
lookupMaybe(key: PDFName, type: typeof PDFRef): PDFRef | undefined;
|
||||
lookupMaybe(key: PDFName, type: typeof PDFString): PDFString | undefined;
|
||||
lookupMaybe(
|
||||
ref: PDFName,
|
||||
type1: typeof PDFString,
|
||||
type2: typeof PDFHexString,
|
||||
): PDFString | PDFHexString | undefined;
|
||||
lookupMaybe(
|
||||
ref: PDFName,
|
||||
type1: typeof PDFDict,
|
||||
type2: typeof PDFStream,
|
||||
): PDFDict | PDFStream | undefined;
|
||||
lookupMaybe(
|
||||
ref: PDFName,
|
||||
type1: typeof PDFString,
|
||||
type2: typeof PDFHexString,
|
||||
type3: typeof PDFArray,
|
||||
): PDFString | PDFHexString | PDFArray | undefined;
|
||||
|
||||
lookupMaybe(key: PDFName, ...types: any[]) {
|
||||
// TODO: `preservePDFNull` is for backwards compatibility. Should be
|
||||
// removed in next breaking API change.
|
||||
const preservePDFNull = types.includes(PDFNull);
|
||||
|
||||
const value = this.context.lookupMaybe(
|
||||
this.get(key, preservePDFNull),
|
||||
// @ts-ignore
|
||||
...types,
|
||||
) as any;
|
||||
|
||||
if (value === PDFNull && !preservePDFNull) return undefined;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
lookup(key: PDFName): PDFObject | undefined;
|
||||
lookup(key: PDFName, type: typeof PDFArray): PDFArray;
|
||||
lookup(key: PDFName, type: typeof PDFBool): PDFBool;
|
||||
lookup(key: PDFName, type: typeof PDFDict): PDFDict;
|
||||
lookup(key: PDFName, type: typeof PDFHexString): PDFHexString;
|
||||
lookup(key: PDFName, type: typeof PDFName): PDFName;
|
||||
lookup(key: PDFName, type: typeof PDFNull): typeof PDFNull;
|
||||
lookup(key: PDFName, type: typeof PDFNumber): PDFNumber;
|
||||
lookup(key: PDFName, type: typeof PDFStream): PDFStream;
|
||||
lookup(key: PDFName, type: typeof PDFRef): PDFRef;
|
||||
lookup(key: PDFName, type: typeof PDFString): PDFString;
|
||||
lookup(
|
||||
ref: PDFName,
|
||||
type1: typeof PDFString,
|
||||
type2: typeof PDFHexString,
|
||||
): PDFString | PDFHexString;
|
||||
lookup(
|
||||
ref: PDFName,
|
||||
type1: typeof PDFDict,
|
||||
type2: typeof PDFStream,
|
||||
): PDFDict | PDFStream;
|
||||
lookup(
|
||||
ref: PDFName,
|
||||
type1: typeof PDFString,
|
||||
type2: typeof PDFHexString,
|
||||
type3: typeof PDFArray,
|
||||
): PDFString | PDFHexString | PDFArray;
|
||||
|
||||
lookup(key: PDFName, ...types: any[]) {
|
||||
// TODO: `preservePDFNull` is for backwards compatibility. Should be
|
||||
// removed in next breaking API change.
|
||||
const preservePDFNull = types.includes(PDFNull);
|
||||
|
||||
const value = this.context.lookup(
|
||||
this.get(key, preservePDFNull),
|
||||
// @ts-ignore
|
||||
...types,
|
||||
) as any;
|
||||
|
||||
if (value === PDFNull && !preservePDFNull) return undefined;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
delete(key: PDFName): boolean {
|
||||
return this.dict.delete(key);
|
||||
}
|
||||
|
||||
asMap(): Map<PDFName, PDFObject> {
|
||||
return new Map(this.dict);
|
||||
}
|
||||
|
||||
/** Generate a random key that doesn't exist in current key set */
|
||||
uniqueKey(tag = ''): PDFName {
|
||||
const existingKeys = this.keys();
|
||||
let key = PDFName.of(this.context.addRandomSuffix(tag, 10));
|
||||
while (existingKeys.includes(key)) {
|
||||
key = PDFName.of(this.context.addRandomSuffix(tag, 10));
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
clone(context?: PDFContext): PDFDict {
|
||||
const clone = PDFDict.withContext(context || this.context);
|
||||
const entries = this.entries();
|
||||
for (let idx = 0, len = entries.length; idx < len; idx++) {
|
||||
const [key, value] = entries[idx];
|
||||
clone.set(key, value);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
let dictString = '<<\n';
|
||||
const entries = this.entries();
|
||||
for (let idx = 0, len = entries.length; idx < len; idx++) {
|
||||
const [key, value] = entries[idx];
|
||||
dictString += key.toString() + ' ' + value.toString() + '\n';
|
||||
}
|
||||
dictString += '>>';
|
||||
return dictString;
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
let size = 5;
|
||||
const entries = this.entries();
|
||||
for (let idx = 0, len = entries.length; idx < len; idx++) {
|
||||
const [key, value] = entries[idx];
|
||||
size += key.sizeInBytes() + value.sizeInBytes() + 2;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
const initialOffset = offset;
|
||||
|
||||
buffer[offset++] = CharCodes.LessThan;
|
||||
buffer[offset++] = CharCodes.LessThan;
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
|
||||
const entries = this.entries();
|
||||
for (let idx = 0, len = entries.length; idx < len; idx++) {
|
||||
const [key, value] = entries[idx];
|
||||
offset += key.copyBytesInto(buffer, offset);
|
||||
buffer[offset++] = CharCodes.Space;
|
||||
offset += value.copyBytesInto(buffer, offset);
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
}
|
||||
|
||||
buffer[offset++] = CharCodes.GreaterThan;
|
||||
buffer[offset++] = CharCodes.GreaterThan;
|
||||
|
||||
return offset - initialOffset;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFDict;
|
||||
94
frontend/node_modules/pdf-lib/src/core/objects/PDFHexString.ts
generated
vendored
Normal file
94
frontend/node_modules/pdf-lib/src/core/objects/PDFHexString.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import {
|
||||
copyStringIntoBuffer,
|
||||
toHexStringOfMinLength,
|
||||
utf16Decode,
|
||||
utf16Encode,
|
||||
pdfDocEncodingDecode,
|
||||
parseDate,
|
||||
hasUtf16BOM,
|
||||
} from 'src/utils';
|
||||
import { InvalidPDFDateStringError } from 'src/core/errors';
|
||||
|
||||
class PDFHexString extends PDFObject {
|
||||
static of = (value: string) => new PDFHexString(value);
|
||||
|
||||
static fromText = (value: string) => {
|
||||
const encoded = utf16Encode(value);
|
||||
|
||||
let hex = '';
|
||||
for (let idx = 0, len = encoded.length; idx < len; idx++) {
|
||||
hex += toHexStringOfMinLength(encoded[idx], 4);
|
||||
}
|
||||
|
||||
return new PDFHexString(hex);
|
||||
};
|
||||
|
||||
private readonly value: string;
|
||||
|
||||
constructor(value: string) {
|
||||
super();
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
asBytes(): Uint8Array {
|
||||
// Append a zero if the number of digits is odd. See PDF spec 7.3.4.3
|
||||
const hex = this.value + (this.value.length % 2 === 1 ? '0' : '');
|
||||
const hexLength = hex.length;
|
||||
|
||||
const bytes = new Uint8Array(hex.length / 2);
|
||||
|
||||
let hexOffset = 0;
|
||||
let bytesOffset = 0;
|
||||
|
||||
// Interpret each pair of hex digits as a single byte
|
||||
while (hexOffset < hexLength) {
|
||||
const byte = parseInt(hex.substring(hexOffset, hexOffset + 2), 16);
|
||||
bytes[bytesOffset] = byte;
|
||||
|
||||
hexOffset += 2;
|
||||
bytesOffset += 1;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
decodeText(): string {
|
||||
const bytes = this.asBytes();
|
||||
if (hasUtf16BOM(bytes)) return utf16Decode(bytes);
|
||||
return pdfDocEncodingDecode(bytes);
|
||||
}
|
||||
|
||||
decodeDate(): Date {
|
||||
const text = this.decodeText();
|
||||
const date = parseDate(text);
|
||||
if (!date) throw new InvalidPDFDateStringError(text);
|
||||
return date;
|
||||
}
|
||||
|
||||
asString(): string {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
clone(): PDFHexString {
|
||||
return PDFHexString.of(this.value);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `<${this.value}>`;
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
return this.value.length + 2;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
buffer[offset++] = CharCodes.LessThan;
|
||||
offset += copyStringIntoBuffer(this.value, buffer, offset);
|
||||
buffer[offset++] = CharCodes.GreaterThan;
|
||||
return this.value.length + 2;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFHexString;
|
||||
34
frontend/node_modules/pdf-lib/src/core/objects/PDFInvalidObject.ts
generated
vendored
Normal file
34
frontend/node_modules/pdf-lib/src/core/objects/PDFInvalidObject.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
|
||||
class PDFInvalidObject extends PDFObject {
|
||||
static of = (data: Uint8Array) => new PDFInvalidObject(data);
|
||||
|
||||
private readonly data: Uint8Array;
|
||||
|
||||
private constructor(data: Uint8Array) {
|
||||
super();
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
clone(): PDFInvalidObject {
|
||||
return PDFInvalidObject.of(this.data.slice());
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `PDFInvalidObject(${this.data.length} bytes)`;
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
return this.data.length;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
const length = this.data.length;
|
||||
for (let idx = 0; idx < length; idx++) {
|
||||
buffer[offset++] = this.data[idx];
|
||||
}
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFInvalidObject;
|
||||
159
frontend/node_modules/pdf-lib/src/core/objects/PDFName.ts
generated
vendored
Normal file
159
frontend/node_modules/pdf-lib/src/core/objects/PDFName.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
import { PrivateConstructorError } from 'src/core/errors';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import { IsIrregular } from 'src/core/syntax/Irregular';
|
||||
import {
|
||||
charFromHexCode,
|
||||
copyStringIntoBuffer,
|
||||
toCharCode,
|
||||
toHexString,
|
||||
} from 'src/utils';
|
||||
|
||||
const decodeName = (name: string) =>
|
||||
name.replace(/#([\dABCDEF]{2})/g, (_, hex) => charFromHexCode(hex));
|
||||
|
||||
const isRegularChar = (charCode: number) =>
|
||||
charCode >= CharCodes.ExclamationPoint &&
|
||||
charCode <= CharCodes.Tilde &&
|
||||
!IsIrregular[charCode];
|
||||
|
||||
const ENFORCER = {};
|
||||
const pool = new Map<string, PDFName>();
|
||||
|
||||
class PDFName extends PDFObject {
|
||||
static of = (name: string): PDFName => {
|
||||
const decodedValue = decodeName(name);
|
||||
|
||||
let instance = pool.get(decodedValue);
|
||||
if (!instance) {
|
||||
instance = new PDFName(ENFORCER, decodedValue);
|
||||
pool.set(decodedValue, instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
/* tslint:disable member-ordering */
|
||||
static readonly Length = PDFName.of('Length');
|
||||
static readonly FlateDecode = PDFName.of('FlateDecode');
|
||||
static readonly Resources = PDFName.of('Resources');
|
||||
static readonly Font = PDFName.of('Font');
|
||||
static readonly XObject = PDFName.of('XObject');
|
||||
static readonly ExtGState = PDFName.of('ExtGState');
|
||||
static readonly Contents = PDFName.of('Contents');
|
||||
static readonly Type = PDFName.of('Type');
|
||||
static readonly Parent = PDFName.of('Parent');
|
||||
static readonly MediaBox = PDFName.of('MediaBox');
|
||||
static readonly Page = PDFName.of('Page');
|
||||
static readonly Annots = PDFName.of('Annots');
|
||||
static readonly TrimBox = PDFName.of('TrimBox');
|
||||
static readonly ArtBox = PDFName.of('ArtBox');
|
||||
static readonly BleedBox = PDFName.of('BleedBox');
|
||||
static readonly CropBox = PDFName.of('CropBox');
|
||||
static readonly Rotate = PDFName.of('Rotate');
|
||||
static readonly Title = PDFName.of('Title');
|
||||
static readonly Author = PDFName.of('Author');
|
||||
static readonly Subject = PDFName.of('Subject');
|
||||
static readonly Creator = PDFName.of('Creator');
|
||||
static readonly Keywords = PDFName.of('Keywords');
|
||||
static readonly Producer = PDFName.of('Producer');
|
||||
static readonly CreationDate = PDFName.of('CreationDate');
|
||||
static readonly ModDate = PDFName.of('ModDate');
|
||||
/* tslint:enable member-ordering */
|
||||
|
||||
private readonly encodedName: string;
|
||||
|
||||
private constructor(enforcer: any, name: string) {
|
||||
if (enforcer !== ENFORCER) throw new PrivateConstructorError('PDFName');
|
||||
super();
|
||||
|
||||
let encodedName = '/';
|
||||
for (let idx = 0, len = name.length; idx < len; idx++) {
|
||||
const character = name[idx];
|
||||
const code = toCharCode(character);
|
||||
encodedName += isRegularChar(code) ? character : `#${toHexString(code)}`;
|
||||
}
|
||||
|
||||
this.encodedName = encodedName;
|
||||
}
|
||||
|
||||
asBytes(): Uint8Array {
|
||||
const bytes: number[] = [];
|
||||
|
||||
let hex = '';
|
||||
let escaped = false;
|
||||
|
||||
const pushByte = (byte?: number) => {
|
||||
if (byte !== undefined) bytes.push(byte);
|
||||
escaped = false;
|
||||
};
|
||||
|
||||
for (let idx = 1, len = this.encodedName.length; idx < len; idx++) {
|
||||
const char = this.encodedName[idx];
|
||||
const byte = toCharCode(char);
|
||||
const nextChar = this.encodedName[idx + 1];
|
||||
if (!escaped) {
|
||||
if (byte === CharCodes.Hash) escaped = true;
|
||||
else pushByte(byte);
|
||||
} else {
|
||||
if (
|
||||
(byte >= CharCodes.Zero && byte <= CharCodes.Nine) ||
|
||||
(byte >= CharCodes.a && byte <= CharCodes.f) ||
|
||||
(byte >= CharCodes.A && byte <= CharCodes.F)
|
||||
) {
|
||||
hex += char;
|
||||
if (
|
||||
hex.length === 2 ||
|
||||
!(
|
||||
(nextChar >= '0' && nextChar <= '9') ||
|
||||
(nextChar >= 'a' && nextChar <= 'f') ||
|
||||
(nextChar >= 'A' && nextChar <= 'F')
|
||||
)
|
||||
) {
|
||||
pushByte(parseInt(hex, 16));
|
||||
hex = '';
|
||||
}
|
||||
} else {
|
||||
pushByte(byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Uint8Array(bytes);
|
||||
}
|
||||
|
||||
// TODO: This should probably use `utf8Decode()`
|
||||
// TODO: Polyfill Array.from?
|
||||
decodeText(): string {
|
||||
const bytes = this.asBytes();
|
||||
return String.fromCharCode(...Array.from(bytes));
|
||||
}
|
||||
|
||||
asString(): string {
|
||||
return this.encodedName;
|
||||
}
|
||||
|
||||
/** @deprecated in favor of [[PDFName.asString]] */
|
||||
value(): string {
|
||||
return this.encodedName;
|
||||
}
|
||||
|
||||
clone(): PDFName {
|
||||
return this;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.encodedName;
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
return this.encodedName.length;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
offset += copyStringIntoBuffer(this.encodedName, buffer, offset);
|
||||
return this.encodedName.length;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFName;
|
||||
30
frontend/node_modules/pdf-lib/src/core/objects/PDFNull.ts
generated
vendored
Normal file
30
frontend/node_modules/pdf-lib/src/core/objects/PDFNull.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
|
||||
class PDFNull extends PDFObject {
|
||||
asNull(): null {
|
||||
return null;
|
||||
}
|
||||
|
||||
clone(): PDFNull {
|
||||
return this;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
return 4;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
buffer[offset++] = CharCodes.n;
|
||||
buffer[offset++] = CharCodes.u;
|
||||
buffer[offset++] = CharCodes.l;
|
||||
buffer[offset++] = CharCodes.l;
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
export default new PDFNull();
|
||||
44
frontend/node_modules/pdf-lib/src/core/objects/PDFNumber.ts
generated
vendored
Normal file
44
frontend/node_modules/pdf-lib/src/core/objects/PDFNumber.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { copyStringIntoBuffer, numberToString } from 'src/utils/index';
|
||||
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
|
||||
class PDFNumber extends PDFObject {
|
||||
static of = (value: number) => new PDFNumber(value);
|
||||
|
||||
private readonly numberValue: number;
|
||||
private readonly stringValue: string;
|
||||
|
||||
private constructor(value: number) {
|
||||
super();
|
||||
this.numberValue = value;
|
||||
this.stringValue = numberToString(value);
|
||||
}
|
||||
|
||||
asNumber(): number {
|
||||
return this.numberValue;
|
||||
}
|
||||
|
||||
/** @deprecated in favor of [[PDFNumber.asNumber]] */
|
||||
value(): number {
|
||||
return this.numberValue;
|
||||
}
|
||||
|
||||
clone(): PDFNumber {
|
||||
return PDFNumber.of(this.numberValue);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.stringValue;
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
return this.stringValue.length;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
offset += copyStringIntoBuffer(this.stringValue, buffer, offset);
|
||||
return this.stringValue.length;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFNumber;
|
||||
22
frontend/node_modules/pdf-lib/src/core/objects/PDFObject.ts
generated
vendored
Normal file
22
frontend/node_modules/pdf-lib/src/core/objects/PDFObject.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { MethodNotImplementedError } from 'src/core/errors';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
|
||||
class PDFObject {
|
||||
clone(_context?: PDFContext): PDFObject {
|
||||
throw new MethodNotImplementedError(this.constructor.name, 'clone');
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
throw new MethodNotImplementedError(this.constructor.name, 'toString');
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
throw new MethodNotImplementedError(this.constructor.name, 'sizeInBytes');
|
||||
}
|
||||
|
||||
copyBytesInto(_buffer: Uint8Array, _offset: number): number {
|
||||
throw new MethodNotImplementedError(this.constructor.name, 'copyBytesInto');
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFObject;
|
||||
38
frontend/node_modules/pdf-lib/src/core/objects/PDFRawStream.ts
generated
vendored
Normal file
38
frontend/node_modules/pdf-lib/src/core/objects/PDFRawStream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFStream from 'src/core/objects/PDFStream';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import { arrayAsString } from 'src/utils';
|
||||
|
||||
class PDFRawStream extends PDFStream {
|
||||
static of = (dict: PDFDict, contents: Uint8Array) =>
|
||||
new PDFRawStream(dict, contents);
|
||||
|
||||
readonly contents: Uint8Array;
|
||||
|
||||
private constructor(dict: PDFDict, contents: Uint8Array) {
|
||||
super(dict);
|
||||
this.contents = contents;
|
||||
}
|
||||
|
||||
asUint8Array(): Uint8Array {
|
||||
return this.contents.slice();
|
||||
}
|
||||
|
||||
clone(context?: PDFContext): PDFRawStream {
|
||||
return PDFRawStream.of(this.dict.clone(context), this.contents.slice());
|
||||
}
|
||||
|
||||
getContentsString(): string {
|
||||
return arrayAsString(this.contents);
|
||||
}
|
||||
|
||||
getContents(): Uint8Array {
|
||||
return this.contents;
|
||||
}
|
||||
|
||||
getContentsSize(): number {
|
||||
return this.contents.length;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFRawStream;
|
||||
55
frontend/node_modules/pdf-lib/src/core/objects/PDFRef.ts
generated
vendored
Normal file
55
frontend/node_modules/pdf-lib/src/core/objects/PDFRef.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { PrivateConstructorError } from 'src/core/errors';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import { copyStringIntoBuffer } from 'src/utils';
|
||||
|
||||
const ENFORCER = {};
|
||||
const pool = new Map<string, PDFRef>();
|
||||
|
||||
class PDFRef extends PDFObject {
|
||||
static of = (objectNumber: number, generationNumber = 0) => {
|
||||
const tag = `${objectNumber} ${generationNumber} R`;
|
||||
|
||||
let instance = pool.get(tag);
|
||||
if (!instance) {
|
||||
instance = new PDFRef(ENFORCER, objectNumber, generationNumber);
|
||||
pool.set(tag, instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
readonly objectNumber: number;
|
||||
readonly generationNumber: number;
|
||||
readonly tag: string;
|
||||
|
||||
private constructor(
|
||||
enforcer: any,
|
||||
objectNumber: number,
|
||||
generationNumber: number,
|
||||
) {
|
||||
if (enforcer !== ENFORCER) throw new PrivateConstructorError('PDFRef');
|
||||
super();
|
||||
this.objectNumber = objectNumber;
|
||||
this.generationNumber = generationNumber;
|
||||
this.tag = `${objectNumber} ${generationNumber} R`;
|
||||
}
|
||||
|
||||
clone(): PDFRef {
|
||||
return this;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.tag;
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
return this.tag.length;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
offset += copyStringIntoBuffer(this.tag, buffer, offset);
|
||||
return this.tag.length;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFRef;
|
||||
93
frontend/node_modules/pdf-lib/src/core/objects/PDFStream.ts
generated
vendored
Normal file
93
frontend/node_modules/pdf-lib/src/core/objects/PDFStream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import { MethodNotImplementedError } from 'src/core/errors';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
|
||||
class PDFStream extends PDFObject {
|
||||
readonly dict: PDFDict;
|
||||
|
||||
constructor(dict: PDFDict) {
|
||||
super();
|
||||
this.dict = dict;
|
||||
}
|
||||
|
||||
clone(_context?: PDFContext): PDFStream {
|
||||
throw new MethodNotImplementedError(this.constructor.name, 'clone');
|
||||
}
|
||||
|
||||
getContentsString(): string {
|
||||
throw new MethodNotImplementedError(
|
||||
this.constructor.name,
|
||||
'getContentsString',
|
||||
);
|
||||
}
|
||||
|
||||
getContents(): Uint8Array {
|
||||
throw new MethodNotImplementedError(this.constructor.name, 'getContents');
|
||||
}
|
||||
|
||||
getContentsSize(): number {
|
||||
throw new MethodNotImplementedError(
|
||||
this.constructor.name,
|
||||
'getContentsSize',
|
||||
);
|
||||
}
|
||||
|
||||
updateDict(): void {
|
||||
const contentsSize = this.getContentsSize();
|
||||
this.dict.set(PDFName.Length, PDFNumber.of(contentsSize));
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
this.updateDict();
|
||||
return this.dict.sizeInBytes() + this.getContentsSize() + 18;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
this.updateDict();
|
||||
let streamString = this.dict.toString();
|
||||
streamString += '\nstream\n';
|
||||
streamString += this.getContentsString();
|
||||
streamString += '\nendstream';
|
||||
return streamString;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
this.updateDict();
|
||||
const initialOffset = offset;
|
||||
|
||||
offset += this.dict.copyBytesInto(buffer, offset);
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
|
||||
buffer[offset++] = CharCodes.s;
|
||||
buffer[offset++] = CharCodes.t;
|
||||
buffer[offset++] = CharCodes.r;
|
||||
buffer[offset++] = CharCodes.e;
|
||||
buffer[offset++] = CharCodes.a;
|
||||
buffer[offset++] = CharCodes.m;
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
|
||||
const contents = this.getContents();
|
||||
for (let idx = 0, len = contents.length; idx < len; idx++) {
|
||||
buffer[offset++] = contents[idx];
|
||||
}
|
||||
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
buffer[offset++] = CharCodes.e;
|
||||
buffer[offset++] = CharCodes.n;
|
||||
buffer[offset++] = CharCodes.d;
|
||||
buffer[offset++] = CharCodes.s;
|
||||
buffer[offset++] = CharCodes.t;
|
||||
buffer[offset++] = CharCodes.r;
|
||||
buffer[offset++] = CharCodes.e;
|
||||
buffer[offset++] = CharCodes.a;
|
||||
buffer[offset++] = CharCodes.m;
|
||||
|
||||
return offset - initialOffset;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFStream;
|
||||
118
frontend/node_modules/pdf-lib/src/core/objects/PDFString.ts
generated
vendored
Normal file
118
frontend/node_modules/pdf-lib/src/core/objects/PDFString.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import {
|
||||
copyStringIntoBuffer,
|
||||
padStart,
|
||||
utf16Decode,
|
||||
pdfDocEncodingDecode,
|
||||
toCharCode,
|
||||
parseDate,
|
||||
hasUtf16BOM,
|
||||
} from 'src/utils';
|
||||
import { InvalidPDFDateStringError } from 'src/core/errors';
|
||||
|
||||
class PDFString extends PDFObject {
|
||||
// The PDF spec allows newlines and parens to appear directly within a literal
|
||||
// string. These character _may_ be escaped. But they do not _have_ to be. So
|
||||
// for simplicity, we will not bother escaping them.
|
||||
static of = (value: string) => new PDFString(value);
|
||||
|
||||
static fromDate = (date: Date) => {
|
||||
const year = padStart(String(date.getUTCFullYear()), 4, '0');
|
||||
const month = padStart(String(date.getUTCMonth() + 1), 2, '0');
|
||||
const day = padStart(String(date.getUTCDate()), 2, '0');
|
||||
const hours = padStart(String(date.getUTCHours()), 2, '0');
|
||||
const mins = padStart(String(date.getUTCMinutes()), 2, '0');
|
||||
const secs = padStart(String(date.getUTCSeconds()), 2, '0');
|
||||
return new PDFString(`D:${year}${month}${day}${hours}${mins}${secs}Z`);
|
||||
};
|
||||
|
||||
private readonly value: string;
|
||||
|
||||
private constructor(value: string) {
|
||||
super();
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
asBytes(): Uint8Array {
|
||||
const bytes: number[] = [];
|
||||
|
||||
let octal = '';
|
||||
let escaped = false;
|
||||
|
||||
const pushByte = (byte?: number) => {
|
||||
if (byte !== undefined) bytes.push(byte);
|
||||
escaped = false;
|
||||
};
|
||||
|
||||
for (let idx = 0, len = this.value.length; idx < len; idx++) {
|
||||
const char = this.value[idx];
|
||||
const byte = toCharCode(char);
|
||||
const nextChar = this.value[idx + 1];
|
||||
if (!escaped) {
|
||||
if (byte === CharCodes.BackSlash) escaped = true;
|
||||
else pushByte(byte);
|
||||
} else {
|
||||
if (byte === CharCodes.Newline) pushByte();
|
||||
else if (byte === CharCodes.CarriageReturn) pushByte();
|
||||
else if (byte === CharCodes.n) pushByte(CharCodes.Newline);
|
||||
else if (byte === CharCodes.r) pushByte(CharCodes.CarriageReturn);
|
||||
else if (byte === CharCodes.t) pushByte(CharCodes.Tab);
|
||||
else if (byte === CharCodes.b) pushByte(CharCodes.Backspace);
|
||||
else if (byte === CharCodes.f) pushByte(CharCodes.FormFeed);
|
||||
else if (byte === CharCodes.LeftParen) pushByte(CharCodes.LeftParen);
|
||||
else if (byte === CharCodes.RightParen) pushByte(CharCodes.RightParen);
|
||||
else if (byte === CharCodes.Backspace) pushByte(CharCodes.BackSlash);
|
||||
else if (byte >= CharCodes.Zero && byte <= CharCodes.Seven) {
|
||||
octal += char;
|
||||
if (octal.length === 3 || !(nextChar >= '0' && nextChar <= '7')) {
|
||||
pushByte(parseInt(octal, 8));
|
||||
octal = '';
|
||||
}
|
||||
} else {
|
||||
pushByte(byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Uint8Array(bytes);
|
||||
}
|
||||
|
||||
decodeText(): string {
|
||||
const bytes = this.asBytes();
|
||||
if (hasUtf16BOM(bytes)) return utf16Decode(bytes);
|
||||
return pdfDocEncodingDecode(bytes);
|
||||
}
|
||||
|
||||
decodeDate(): Date {
|
||||
const text = this.decodeText();
|
||||
const date = parseDate(text);
|
||||
if (!date) throw new InvalidPDFDateStringError(text);
|
||||
return date;
|
||||
}
|
||||
|
||||
asString(): string {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
clone(): PDFString {
|
||||
return PDFString.of(this.value);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `(${this.value})`;
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
return this.value.length + 2;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
buffer[offset++] = CharCodes.LeftParen;
|
||||
offset += copyStringIntoBuffer(this.value, buffer, offset);
|
||||
buffer[offset++] = CharCodes.RightParen;
|
||||
return this.value.length + 2;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFString;
|
||||
79
frontend/node_modules/pdf-lib/src/core/operators/PDFOperator.ts
generated
vendored
Normal file
79
frontend/node_modules/pdf-lib/src/core/operators/PDFOperator.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFString from 'src/core/objects/PDFString';
|
||||
import PDFOperatorNames from 'src/core/operators/PDFOperatorNames';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import { copyStringIntoBuffer } from 'src/utils';
|
||||
|
||||
export type PDFOperatorArg =
|
||||
| string
|
||||
| PDFName
|
||||
| PDFArray
|
||||
| PDFNumber
|
||||
| PDFString
|
||||
| PDFHexString;
|
||||
|
||||
class PDFOperator {
|
||||
static of = (name: PDFOperatorNames, args?: PDFOperatorArg[]) =>
|
||||
new PDFOperator(name, args);
|
||||
|
||||
private readonly name: PDFOperatorNames;
|
||||
private readonly args: PDFOperatorArg[];
|
||||
|
||||
private constructor(name: PDFOperatorNames, args?: PDFOperatorArg[]) {
|
||||
this.name = name;
|
||||
this.args = args || [];
|
||||
}
|
||||
|
||||
clone(context?: PDFContext): PDFOperator {
|
||||
const args = new Array(this.args.length);
|
||||
for (let idx = 0, len = args.length; idx < len; idx++) {
|
||||
const arg = this.args[idx];
|
||||
args[idx] = arg instanceof PDFObject ? arg.clone(context) : arg;
|
||||
}
|
||||
return PDFOperator.of(this.name, args);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
let value = '';
|
||||
for (let idx = 0, len = this.args.length; idx < len; idx++) {
|
||||
value += String(this.args[idx]) + ' ';
|
||||
}
|
||||
value += this.name;
|
||||
return value;
|
||||
}
|
||||
|
||||
sizeInBytes(): number {
|
||||
let size = 0;
|
||||
for (let idx = 0, len = this.args.length; idx < len; idx++) {
|
||||
const arg = this.args[idx];
|
||||
size += (arg instanceof PDFObject ? arg.sizeInBytes() : arg.length) + 1;
|
||||
}
|
||||
size += this.name.length;
|
||||
return size;
|
||||
}
|
||||
|
||||
copyBytesInto(buffer: Uint8Array, offset: number): number {
|
||||
const initialOffset = offset;
|
||||
|
||||
for (let idx = 0, len = this.args.length; idx < len; idx++) {
|
||||
const arg = this.args[idx];
|
||||
if (arg instanceof PDFObject) {
|
||||
offset += arg.copyBytesInto(buffer, offset);
|
||||
} else {
|
||||
offset += copyStringIntoBuffer(arg, buffer, offset);
|
||||
}
|
||||
buffer[offset++] = CharCodes.Space;
|
||||
}
|
||||
|
||||
offset += copyStringIntoBuffer(this.name, buffer, offset);
|
||||
|
||||
return offset - initialOffset;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFOperator;
|
||||
92
frontend/node_modules/pdf-lib/src/core/operators/PDFOperatorNames.ts
generated
vendored
Normal file
92
frontend/node_modules/pdf-lib/src/core/operators/PDFOperatorNames.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
enum PDFOperatorNames {
|
||||
// Non Stroking Color Operators
|
||||
NonStrokingColor = 'sc',
|
||||
NonStrokingColorN = 'scn',
|
||||
NonStrokingColorRgb = 'rg',
|
||||
NonStrokingColorGray = 'g',
|
||||
NonStrokingColorCmyk = 'k',
|
||||
NonStrokingColorspace = 'cs',
|
||||
|
||||
// Stroking Color Operators
|
||||
StrokingColor = 'SC',
|
||||
StrokingColorN = 'SCN',
|
||||
StrokingColorRgb = 'RG',
|
||||
StrokingColorGray = 'G',
|
||||
StrokingColorCmyk = 'K',
|
||||
StrokingColorspace = 'CS',
|
||||
|
||||
// Marked Content Operators
|
||||
BeginMarkedContentSequence = 'BDC',
|
||||
BeginMarkedContent = 'BMC',
|
||||
EndMarkedContent = 'EMC',
|
||||
MarkedContentPointWithProps = 'DP',
|
||||
MarkedContentPoint = 'MP',
|
||||
DrawObject = 'Do',
|
||||
|
||||
// Graphics State Operators
|
||||
ConcatTransformationMatrix = 'cm',
|
||||
PopGraphicsState = 'Q',
|
||||
PushGraphicsState = 'q',
|
||||
SetFlatness = 'i',
|
||||
SetGraphicsStateParams = 'gs',
|
||||
SetLineCapStyle = 'J',
|
||||
SetLineDashPattern = 'd',
|
||||
SetLineJoinStyle = 'j',
|
||||
SetLineMiterLimit = 'M',
|
||||
SetLineWidth = 'w',
|
||||
SetTextMatrix = 'Tm',
|
||||
SetRenderingIntent = 'ri',
|
||||
|
||||
// Graphics Operators
|
||||
AppendRectangle = 're',
|
||||
BeginInlineImage = 'BI',
|
||||
BeginInlineImageData = 'ID',
|
||||
EndInlineImage = 'EI',
|
||||
ClipEvenOdd = 'W*',
|
||||
ClipNonZero = 'W',
|
||||
CloseAndStroke = 's',
|
||||
CloseFillEvenOddAndStroke = 'b*',
|
||||
CloseFillNonZeroAndStroke = 'b',
|
||||
ClosePath = 'h',
|
||||
AppendBezierCurve = 'c',
|
||||
CurveToReplicateFinalPoint = 'y',
|
||||
CurveToReplicateInitialPoint = 'v',
|
||||
EndPath = 'n',
|
||||
FillEvenOddAndStroke = 'B*',
|
||||
FillEvenOdd = 'f*',
|
||||
FillNonZeroAndStroke = 'B',
|
||||
FillNonZero = 'f',
|
||||
LegacyFillNonZero = 'F',
|
||||
LineTo = 'l',
|
||||
MoveTo = 'm',
|
||||
ShadingFill = 'sh',
|
||||
StrokePath = 'S',
|
||||
|
||||
// Text Operators
|
||||
BeginText = 'BT',
|
||||
EndText = 'ET',
|
||||
MoveText = 'Td',
|
||||
MoveTextSetLeading = 'TD',
|
||||
NextLine = 'T*',
|
||||
SetCharacterSpacing = 'Tc',
|
||||
SetFontAndSize = 'Tf',
|
||||
SetTextHorizontalScaling = 'Tz',
|
||||
SetTextLineHeight = 'TL',
|
||||
SetTextRenderingMode = 'Tr',
|
||||
SetTextRise = 'Ts',
|
||||
SetWordSpacing = 'Tw',
|
||||
ShowText = 'Tj',
|
||||
ShowTextAdjusted = 'TJ',
|
||||
ShowTextLine = "'", // tslint:disable-line quotemark
|
||||
ShowTextLineAndSpace = '"',
|
||||
|
||||
// Type3 Font Operators
|
||||
Type3D0 = 'd0',
|
||||
Type3D1 = 'd1',
|
||||
|
||||
// Compatibility Section Operators
|
||||
BeginCompatibilitySection = 'BX',
|
||||
EndCompatibilitySection = 'EX',
|
||||
}
|
||||
|
||||
export default PDFOperatorNames;
|
||||
119
frontend/node_modules/pdf-lib/src/core/parser/BaseParser.ts
generated
vendored
Normal file
119
frontend/node_modules/pdf-lib/src/core/parser/BaseParser.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
import { NumberParsingError } from 'src/core/errors';
|
||||
import ByteStream from 'src/core/parser/ByteStream';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import { IsDigit, IsNumeric } from 'src/core/syntax/Numeric';
|
||||
import { IsWhitespace } from 'src/core/syntax/Whitespace';
|
||||
import { charFromCode } from 'src/utils';
|
||||
|
||||
const { Newline, CarriageReturn } = CharCodes;
|
||||
|
||||
// TODO: Throw error if eof is reached before finishing object parse...
|
||||
class BaseParser {
|
||||
protected readonly bytes: ByteStream;
|
||||
protected readonly capNumbers: boolean;
|
||||
|
||||
constructor(bytes: ByteStream, capNumbers = false) {
|
||||
this.bytes = bytes;
|
||||
this.capNumbers = capNumbers;
|
||||
}
|
||||
|
||||
protected parseRawInt(): number {
|
||||
let value = '';
|
||||
|
||||
while (!this.bytes.done()) {
|
||||
const byte = this.bytes.peek();
|
||||
if (!IsDigit[byte]) break;
|
||||
value += charFromCode(this.bytes.next());
|
||||
}
|
||||
|
||||
const numberValue = Number(value);
|
||||
|
||||
if (!value || !isFinite(numberValue)) {
|
||||
throw new NumberParsingError(this.bytes.position(), value);
|
||||
}
|
||||
|
||||
return numberValue;
|
||||
}
|
||||
|
||||
// TODO: Maybe handle exponential format?
|
||||
// TODO: Compare performance of string concatenation to charFromCode(...bytes)
|
||||
protected parseRawNumber(): number {
|
||||
let value = '';
|
||||
|
||||
// Parse integer-part, the leading (+ | - | . | 0-9)
|
||||
while (!this.bytes.done()) {
|
||||
const byte = this.bytes.peek();
|
||||
if (!IsNumeric[byte]) break;
|
||||
value += charFromCode(this.bytes.next());
|
||||
if (byte === CharCodes.Period) break;
|
||||
}
|
||||
|
||||
// Parse decimal-part, the trailing (0-9)
|
||||
while (!this.bytes.done()) {
|
||||
const byte = this.bytes.peek();
|
||||
if (!IsDigit[byte]) break;
|
||||
value += charFromCode(this.bytes.next());
|
||||
}
|
||||
|
||||
const numberValue = Number(value);
|
||||
|
||||
if (!value || !isFinite(numberValue)) {
|
||||
throw new NumberParsingError(this.bytes.position(), value);
|
||||
}
|
||||
|
||||
if (numberValue > Number.MAX_SAFE_INTEGER) {
|
||||
if (this.capNumbers) {
|
||||
const msg = `Parsed number that is too large for some PDF readers: ${value}, using Number.MAX_SAFE_INTEGER instead.`;
|
||||
console.warn(msg);
|
||||
return Number.MAX_SAFE_INTEGER;
|
||||
} else {
|
||||
const msg = `Parsed number that is too large for some PDF readers: ${value}, not capping.`;
|
||||
console.warn(msg);
|
||||
}
|
||||
}
|
||||
|
||||
return numberValue;
|
||||
}
|
||||
|
||||
protected skipWhitespace(): void {
|
||||
while (!this.bytes.done() && IsWhitespace[this.bytes.peek()]) {
|
||||
this.bytes.next();
|
||||
}
|
||||
}
|
||||
|
||||
protected skipLine(): void {
|
||||
while (!this.bytes.done()) {
|
||||
const byte = this.bytes.peek();
|
||||
if (byte === Newline || byte === CarriageReturn) return;
|
||||
this.bytes.next();
|
||||
}
|
||||
}
|
||||
|
||||
protected skipComment(): boolean {
|
||||
if (this.bytes.peek() !== CharCodes.Percent) return false;
|
||||
while (!this.bytes.done()) {
|
||||
const byte = this.bytes.peek();
|
||||
if (byte === Newline || byte === CarriageReturn) return true;
|
||||
this.bytes.next();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected skipWhitespaceAndComments(): void {
|
||||
this.skipWhitespace();
|
||||
while (this.skipComment()) this.skipWhitespace();
|
||||
}
|
||||
|
||||
protected matchKeyword(keyword: number[]): boolean {
|
||||
const initialOffset = this.bytes.offset();
|
||||
for (let idx = 0, len = keyword.length; idx < len; idx++) {
|
||||
if (this.bytes.done() || this.bytes.next() !== keyword[idx]) {
|
||||
this.bytes.moveTo(initialOffset);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseParser;
|
||||
76
frontend/node_modules/pdf-lib/src/core/parser/ByteStream.ts
generated
vendored
Normal file
76
frontend/node_modules/pdf-lib/src/core/parser/ByteStream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import { NextByteAssertionError } from 'src/core/errors';
|
||||
import PDFRawStream from 'src/core/objects/PDFRawStream';
|
||||
import { decodePDFRawStream } from 'src/core/streams/decode';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
|
||||
// TODO: See how line/col tracking affects performance
|
||||
class ByteStream {
|
||||
static of = (bytes: Uint8Array) => new ByteStream(bytes);
|
||||
|
||||
static fromPDFRawStream = (rawStream: PDFRawStream) =>
|
||||
ByteStream.of(decodePDFRawStream(rawStream).decode());
|
||||
|
||||
private readonly bytes: Uint8Array;
|
||||
private readonly length: number;
|
||||
|
||||
private idx = 0;
|
||||
private line = 0;
|
||||
private column = 0;
|
||||
|
||||
constructor(bytes: Uint8Array) {
|
||||
this.bytes = bytes;
|
||||
this.length = this.bytes.length;
|
||||
}
|
||||
|
||||
moveTo(offset: number): void {
|
||||
this.idx = offset;
|
||||
}
|
||||
|
||||
next(): number {
|
||||
const byte = this.bytes[this.idx++];
|
||||
if (byte === CharCodes.Newline) {
|
||||
this.line += 1;
|
||||
this.column = 0;
|
||||
} else {
|
||||
this.column += 1;
|
||||
}
|
||||
return byte;
|
||||
}
|
||||
|
||||
assertNext(expected: number): number {
|
||||
if (this.peek() !== expected) {
|
||||
throw new NextByteAssertionError(this.position(), expected, this.peek());
|
||||
}
|
||||
return this.next();
|
||||
}
|
||||
|
||||
peek(): number {
|
||||
return this.bytes[this.idx];
|
||||
}
|
||||
|
||||
peekAhead(steps: number) {
|
||||
return this.bytes[this.idx + steps];
|
||||
}
|
||||
|
||||
peekAt(offset: number) {
|
||||
return this.bytes[offset];
|
||||
}
|
||||
|
||||
done(): boolean {
|
||||
return this.idx >= this.length;
|
||||
}
|
||||
|
||||
offset(): number {
|
||||
return this.idx;
|
||||
}
|
||||
|
||||
slice(start: number, end: number): Uint8Array {
|
||||
return this.bytes.slice(start, end);
|
||||
}
|
||||
|
||||
position(): { line: number; column: number; offset: number } {
|
||||
return { line: this.line, column: this.column, offset: this.idx };
|
||||
}
|
||||
}
|
||||
|
||||
export default ByteStream;
|
||||
274
frontend/node_modules/pdf-lib/src/core/parser/PDFObjectParser.ts
generated
vendored
Normal file
274
frontend/node_modules/pdf-lib/src/core/parser/PDFObjectParser.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
import {
|
||||
PDFObjectParsingError,
|
||||
PDFStreamParsingError,
|
||||
Position,
|
||||
UnbalancedParenthesisError,
|
||||
} from 'src/core/errors';
|
||||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFBool from 'src/core/objects/PDFBool';
|
||||
import PDFDict, { DictMap } from 'src/core/objects/PDFDict';
|
||||
import PDFHexString from 'src/core/objects/PDFHexString';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNull from 'src/core/objects/PDFNull';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFRawStream from 'src/core/objects/PDFRawStream';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFStream from 'src/core/objects/PDFStream';
|
||||
import PDFString from 'src/core/objects/PDFString';
|
||||
import BaseParser from 'src/core/parser/BaseParser';
|
||||
import ByteStream from 'src/core/parser/ByteStream';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFCatalog from 'src/core/structures/PDFCatalog';
|
||||
import PDFPageLeaf from 'src/core/structures/PDFPageLeaf';
|
||||
import PDFPageTree from 'src/core/structures/PDFPageTree';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import { IsDelimiter } from 'src/core/syntax/Delimiters';
|
||||
import { Keywords } from 'src/core/syntax/Keywords';
|
||||
import { IsDigit, IsNumeric } from 'src/core/syntax/Numeric';
|
||||
import { IsWhitespace } from 'src/core/syntax/Whitespace';
|
||||
import { charFromCode } from 'src/utils';
|
||||
|
||||
// TODO: Throw error if eof is reached before finishing object parse...
|
||||
class PDFObjectParser extends BaseParser {
|
||||
static forBytes = (
|
||||
bytes: Uint8Array,
|
||||
context: PDFContext,
|
||||
capNumbers?: boolean,
|
||||
) => new PDFObjectParser(ByteStream.of(bytes), context, capNumbers);
|
||||
|
||||
static forByteStream = (
|
||||
byteStream: ByteStream,
|
||||
context: PDFContext,
|
||||
capNumbers = false,
|
||||
) => new PDFObjectParser(byteStream, context, capNumbers);
|
||||
|
||||
protected readonly context: PDFContext;
|
||||
|
||||
constructor(byteStream: ByteStream, context: PDFContext, capNumbers = false) {
|
||||
super(byteStream, capNumbers);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
// TODO: Is it possible to reduce duplicate parsing for ref lookaheads?
|
||||
parseObject(): PDFObject {
|
||||
this.skipWhitespaceAndComments();
|
||||
|
||||
if (this.matchKeyword(Keywords.true)) return PDFBool.True;
|
||||
if (this.matchKeyword(Keywords.false)) return PDFBool.False;
|
||||
if (this.matchKeyword(Keywords.null)) return PDFNull;
|
||||
|
||||
const byte = this.bytes.peek();
|
||||
|
||||
if (
|
||||
byte === CharCodes.LessThan &&
|
||||
this.bytes.peekAhead(1) === CharCodes.LessThan
|
||||
) {
|
||||
return this.parseDictOrStream();
|
||||
}
|
||||
if (byte === CharCodes.LessThan) return this.parseHexString();
|
||||
if (byte === CharCodes.LeftParen) return this.parseString();
|
||||
if (byte === CharCodes.ForwardSlash) return this.parseName();
|
||||
if (byte === CharCodes.LeftSquareBracket) return this.parseArray();
|
||||
if (IsNumeric[byte]) return this.parseNumberOrRef();
|
||||
|
||||
throw new PDFObjectParsingError(this.bytes.position(), byte);
|
||||
}
|
||||
|
||||
protected parseNumberOrRef(): PDFNumber | PDFRef {
|
||||
const firstNum = this.parseRawNumber();
|
||||
this.skipWhitespaceAndComments();
|
||||
|
||||
const lookaheadStart = this.bytes.offset();
|
||||
if (IsDigit[this.bytes.peek()]) {
|
||||
const secondNum = this.parseRawNumber();
|
||||
this.skipWhitespaceAndComments();
|
||||
if (this.bytes.peek() === CharCodes.R) {
|
||||
this.bytes.assertNext(CharCodes.R);
|
||||
return PDFRef.of(firstNum, secondNum);
|
||||
}
|
||||
}
|
||||
|
||||
this.bytes.moveTo(lookaheadStart);
|
||||
return PDFNumber.of(firstNum);
|
||||
}
|
||||
|
||||
// TODO: Maybe update PDFHexString.of() logic to remove whitespace and validate input?
|
||||
protected parseHexString(): PDFHexString {
|
||||
let value = '';
|
||||
|
||||
this.bytes.assertNext(CharCodes.LessThan);
|
||||
while (!this.bytes.done() && this.bytes.peek() !== CharCodes.GreaterThan) {
|
||||
value += charFromCode(this.bytes.next());
|
||||
}
|
||||
this.bytes.assertNext(CharCodes.GreaterThan);
|
||||
|
||||
return PDFHexString.of(value);
|
||||
}
|
||||
|
||||
protected parseString(): PDFString {
|
||||
let nestingLvl = 0;
|
||||
let isEscaped = false;
|
||||
let value = '';
|
||||
|
||||
while (!this.bytes.done()) {
|
||||
const byte = this.bytes.next();
|
||||
value += charFromCode(byte);
|
||||
|
||||
// Check for unescaped parenthesis
|
||||
if (!isEscaped) {
|
||||
if (byte === CharCodes.LeftParen) nestingLvl += 1;
|
||||
if (byte === CharCodes.RightParen) nestingLvl -= 1;
|
||||
}
|
||||
|
||||
// Track whether current character is being escaped or not
|
||||
if (byte === CharCodes.BackSlash) {
|
||||
isEscaped = !isEscaped;
|
||||
} else if (isEscaped) {
|
||||
isEscaped = false;
|
||||
}
|
||||
|
||||
// Once (if) the unescaped parenthesis balance out, return their contents
|
||||
if (nestingLvl === 0) {
|
||||
// Remove the outer parens so they aren't part of the contents
|
||||
return PDFString.of(value.substring(1, value.length - 1));
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnbalancedParenthesisError(this.bytes.position());
|
||||
}
|
||||
|
||||
// TODO: Compare performance of string concatenation to charFromCode(...bytes)
|
||||
// TODO: Maybe preallocate small Uint8Array if can use charFromCode?
|
||||
protected parseName(): PDFName {
|
||||
this.bytes.assertNext(CharCodes.ForwardSlash);
|
||||
|
||||
let name = '';
|
||||
while (!this.bytes.done()) {
|
||||
const byte = this.bytes.peek();
|
||||
if (IsWhitespace[byte] || IsDelimiter[byte]) break;
|
||||
name += charFromCode(byte);
|
||||
this.bytes.next();
|
||||
}
|
||||
|
||||
return PDFName.of(name);
|
||||
}
|
||||
|
||||
protected parseArray(): PDFArray {
|
||||
this.bytes.assertNext(CharCodes.LeftSquareBracket);
|
||||
this.skipWhitespaceAndComments();
|
||||
|
||||
const pdfArray = PDFArray.withContext(this.context);
|
||||
while (this.bytes.peek() !== CharCodes.RightSquareBracket) {
|
||||
const element = this.parseObject();
|
||||
pdfArray.push(element);
|
||||
this.skipWhitespaceAndComments();
|
||||
}
|
||||
this.bytes.assertNext(CharCodes.RightSquareBracket);
|
||||
return pdfArray;
|
||||
}
|
||||
|
||||
protected parseDict(): PDFDict {
|
||||
this.bytes.assertNext(CharCodes.LessThan);
|
||||
this.bytes.assertNext(CharCodes.LessThan);
|
||||
this.skipWhitespaceAndComments();
|
||||
|
||||
const dict: DictMap = new Map();
|
||||
|
||||
while (
|
||||
!this.bytes.done() &&
|
||||
this.bytes.peek() !== CharCodes.GreaterThan &&
|
||||
this.bytes.peekAhead(1) !== CharCodes.GreaterThan
|
||||
) {
|
||||
const key = this.parseName();
|
||||
const value = this.parseObject();
|
||||
dict.set(key, value);
|
||||
this.skipWhitespaceAndComments();
|
||||
}
|
||||
|
||||
this.skipWhitespaceAndComments();
|
||||
this.bytes.assertNext(CharCodes.GreaterThan);
|
||||
this.bytes.assertNext(CharCodes.GreaterThan);
|
||||
|
||||
const Type = dict.get(PDFName.of('Type'));
|
||||
|
||||
if (Type === PDFName.of('Catalog')) {
|
||||
return PDFCatalog.fromMapWithContext(dict, this.context);
|
||||
} else if (Type === PDFName.of('Pages')) {
|
||||
return PDFPageTree.fromMapWithContext(dict, this.context);
|
||||
} else if (Type === PDFName.of('Page')) {
|
||||
return PDFPageLeaf.fromMapWithContext(dict, this.context);
|
||||
} else {
|
||||
return PDFDict.fromMapWithContext(dict, this.context);
|
||||
}
|
||||
}
|
||||
|
||||
protected parseDictOrStream(): PDFDict | PDFStream {
|
||||
const startPos = this.bytes.position();
|
||||
|
||||
const dict = this.parseDict();
|
||||
|
||||
this.skipWhitespaceAndComments();
|
||||
|
||||
if (
|
||||
!this.matchKeyword(Keywords.streamEOF1) &&
|
||||
!this.matchKeyword(Keywords.streamEOF2) &&
|
||||
!this.matchKeyword(Keywords.streamEOF3) &&
|
||||
!this.matchKeyword(Keywords.streamEOF4) &&
|
||||
!this.matchKeyword(Keywords.stream)
|
||||
) {
|
||||
return dict;
|
||||
}
|
||||
|
||||
const start = this.bytes.offset();
|
||||
let end: number;
|
||||
|
||||
const Length = dict.get(PDFName.of('Length'));
|
||||
if (Length instanceof PDFNumber) {
|
||||
end = start + Length.asNumber();
|
||||
this.bytes.moveTo(end);
|
||||
this.skipWhitespaceAndComments();
|
||||
if (!this.matchKeyword(Keywords.endstream)) {
|
||||
this.bytes.moveTo(start);
|
||||
end = this.findEndOfStreamFallback(startPos);
|
||||
}
|
||||
} else {
|
||||
end = this.findEndOfStreamFallback(startPos);
|
||||
}
|
||||
|
||||
const contents = this.bytes.slice(start, end);
|
||||
|
||||
return PDFRawStream.of(dict, contents);
|
||||
}
|
||||
|
||||
protected findEndOfStreamFallback(startPos: Position) {
|
||||
// Move to end of stream, while handling nested streams
|
||||
let nestingLvl = 1;
|
||||
let end = this.bytes.offset();
|
||||
|
||||
while (!this.bytes.done()) {
|
||||
end = this.bytes.offset();
|
||||
|
||||
if (this.matchKeyword(Keywords.stream)) {
|
||||
nestingLvl += 1;
|
||||
} else if (
|
||||
this.matchKeyword(Keywords.EOF1endstream) ||
|
||||
this.matchKeyword(Keywords.EOF2endstream) ||
|
||||
this.matchKeyword(Keywords.EOF3endstream) ||
|
||||
this.matchKeyword(Keywords.endstream)
|
||||
) {
|
||||
nestingLvl -= 1;
|
||||
} else {
|
||||
this.bytes.next();
|
||||
}
|
||||
|
||||
if (nestingLvl === 0) break;
|
||||
}
|
||||
|
||||
if (nestingLvl !== 0) throw new PDFStreamParsingError(startPos);
|
||||
|
||||
return end;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFObjectParser;
|
||||
67
frontend/node_modules/pdf-lib/src/core/parser/PDFObjectStreamParser.ts
generated
vendored
Normal file
67
frontend/node_modules/pdf-lib/src/core/parser/PDFObjectStreamParser.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { ReparseError } from 'src/core/errors';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFRawStream from 'src/core/objects/PDFRawStream';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import ByteStream from 'src/core/parser/ByteStream';
|
||||
import PDFObjectParser from 'src/core/parser/PDFObjectParser';
|
||||
import { waitForTick } from 'src/utils';
|
||||
|
||||
class PDFObjectStreamParser extends PDFObjectParser {
|
||||
static forStream = (
|
||||
rawStream: PDFRawStream,
|
||||
shouldWaitForTick?: () => boolean,
|
||||
) => new PDFObjectStreamParser(rawStream, shouldWaitForTick);
|
||||
|
||||
private alreadyParsed: boolean;
|
||||
private readonly shouldWaitForTick: () => boolean;
|
||||
private readonly firstOffset: number;
|
||||
private readonly objectCount: number;
|
||||
|
||||
constructor(rawStream: PDFRawStream, shouldWaitForTick?: () => boolean) {
|
||||
super(ByteStream.fromPDFRawStream(rawStream), rawStream.dict.context);
|
||||
|
||||
const { dict } = rawStream;
|
||||
|
||||
this.alreadyParsed = false;
|
||||
this.shouldWaitForTick = shouldWaitForTick || (() => false);
|
||||
this.firstOffset = dict.lookup(PDFName.of('First'), PDFNumber).asNumber();
|
||||
this.objectCount = dict.lookup(PDFName.of('N'), PDFNumber).asNumber();
|
||||
}
|
||||
|
||||
async parseIntoContext(): Promise<void> {
|
||||
if (this.alreadyParsed) {
|
||||
throw new ReparseError('PDFObjectStreamParser', 'parseIntoContext');
|
||||
}
|
||||
this.alreadyParsed = true;
|
||||
|
||||
const offsetsAndObjectNumbers = this.parseOffsetsAndObjectNumbers();
|
||||
for (let idx = 0, len = offsetsAndObjectNumbers.length; idx < len; idx++) {
|
||||
const { objectNumber, offset } = offsetsAndObjectNumbers[idx];
|
||||
this.bytes.moveTo(this.firstOffset + offset);
|
||||
const object = this.parseObject();
|
||||
const ref = PDFRef.of(objectNumber, 0);
|
||||
this.context.assign(ref, object);
|
||||
if (this.shouldWaitForTick()) await waitForTick();
|
||||
}
|
||||
}
|
||||
|
||||
private parseOffsetsAndObjectNumbers(): {
|
||||
objectNumber: number;
|
||||
offset: number;
|
||||
}[] {
|
||||
const offsetsAndObjectNumbers = [];
|
||||
for (let idx = 0, len = this.objectCount; idx < len; idx++) {
|
||||
this.skipWhitespaceAndComments();
|
||||
const objectNumber = this.parseRawInt();
|
||||
|
||||
this.skipWhitespaceAndComments();
|
||||
const offset = this.parseRawInt();
|
||||
|
||||
offsetsAndObjectNumbers.push({ objectNumber, offset });
|
||||
}
|
||||
return offsetsAndObjectNumbers;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFObjectStreamParser;
|
||||
364
frontend/node_modules/pdf-lib/src/core/parser/PDFParser.ts
generated
vendored
Normal file
364
frontend/node_modules/pdf-lib/src/core/parser/PDFParser.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
import PDFCrossRefSection from 'src/core/document/PDFCrossRefSection';
|
||||
import PDFHeader from 'src/core/document/PDFHeader';
|
||||
import PDFTrailer from 'src/core/document/PDFTrailer';
|
||||
import {
|
||||
MissingKeywordError,
|
||||
MissingPDFHeaderError,
|
||||
PDFInvalidObjectParsingError,
|
||||
ReparseError,
|
||||
StalledParserError,
|
||||
} from 'src/core/errors';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFInvalidObject from 'src/core/objects/PDFInvalidObject';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFRawStream from 'src/core/objects/PDFRawStream';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import ByteStream from 'src/core/parser/ByteStream';
|
||||
import PDFObjectParser from 'src/core/parser/PDFObjectParser';
|
||||
import PDFObjectStreamParser from 'src/core/parser/PDFObjectStreamParser';
|
||||
import PDFXRefStreamParser from 'src/core/parser/PDFXRefStreamParser';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import { Keywords } from 'src/core/syntax/Keywords';
|
||||
import { IsDigit } from 'src/core/syntax/Numeric';
|
||||
import { waitForTick } from 'src/utils';
|
||||
|
||||
class PDFParser extends PDFObjectParser {
|
||||
static forBytesWithOptions = (
|
||||
pdfBytes: Uint8Array,
|
||||
objectsPerTick?: number,
|
||||
throwOnInvalidObject?: boolean,
|
||||
capNumbers?: boolean,
|
||||
) =>
|
||||
new PDFParser(pdfBytes, objectsPerTick, throwOnInvalidObject, capNumbers);
|
||||
|
||||
private readonly objectsPerTick: number;
|
||||
private readonly throwOnInvalidObject: boolean;
|
||||
private alreadyParsed = false;
|
||||
private parsedObjects = 0;
|
||||
|
||||
constructor(
|
||||
pdfBytes: Uint8Array,
|
||||
objectsPerTick = Infinity,
|
||||
throwOnInvalidObject = false,
|
||||
capNumbers = false,
|
||||
) {
|
||||
super(ByteStream.of(pdfBytes), PDFContext.create(), capNumbers);
|
||||
this.objectsPerTick = objectsPerTick;
|
||||
this.throwOnInvalidObject = throwOnInvalidObject;
|
||||
}
|
||||
|
||||
async parseDocument(): Promise<PDFContext> {
|
||||
if (this.alreadyParsed) {
|
||||
throw new ReparseError('PDFParser', 'parseDocument');
|
||||
}
|
||||
this.alreadyParsed = true;
|
||||
|
||||
this.context.header = this.parseHeader();
|
||||
|
||||
let prevOffset;
|
||||
while (!this.bytes.done()) {
|
||||
await this.parseDocumentSection();
|
||||
const offset = this.bytes.offset();
|
||||
if (offset === prevOffset) {
|
||||
throw new StalledParserError(this.bytes.position());
|
||||
}
|
||||
prevOffset = offset;
|
||||
}
|
||||
|
||||
this.maybeRecoverRoot();
|
||||
|
||||
if (this.context.lookup(PDFRef.of(0))) {
|
||||
console.warn('Removing parsed object: 0 0 R');
|
||||
this.context.delete(PDFRef.of(0));
|
||||
}
|
||||
|
||||
return this.context;
|
||||
}
|
||||
|
||||
private maybeRecoverRoot(): void {
|
||||
const isValidCatalog = (obj?: PDFObject) =>
|
||||
obj instanceof PDFDict &&
|
||||
obj.lookup(PDFName.of('Type')) === PDFName.of('Catalog');
|
||||
|
||||
const catalog = this.context.lookup(this.context.trailerInfo.Root);
|
||||
|
||||
if (!isValidCatalog(catalog)) {
|
||||
const indirectObjects = this.context.enumerateIndirectObjects();
|
||||
for (let idx = 0, len = indirectObjects.length; idx < len; idx++) {
|
||||
const [ref, object] = indirectObjects[idx];
|
||||
if (isValidCatalog(object)) {
|
||||
this.context.trailerInfo.Root = ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private parseHeader(): PDFHeader {
|
||||
while (!this.bytes.done()) {
|
||||
if (this.matchKeyword(Keywords.header)) {
|
||||
const major = this.parseRawInt();
|
||||
this.bytes.assertNext(CharCodes.Period);
|
||||
const minor = this.parseRawInt();
|
||||
const header = PDFHeader.forVersion(major, minor);
|
||||
this.skipBinaryHeaderComment();
|
||||
return header;
|
||||
}
|
||||
this.bytes.next();
|
||||
}
|
||||
|
||||
throw new MissingPDFHeaderError(this.bytes.position());
|
||||
}
|
||||
|
||||
private parseIndirectObjectHeader(): PDFRef {
|
||||
this.skipWhitespaceAndComments();
|
||||
const objectNumber = this.parseRawInt();
|
||||
|
||||
this.skipWhitespaceAndComments();
|
||||
const generationNumber = this.parseRawInt();
|
||||
|
||||
this.skipWhitespaceAndComments();
|
||||
if (!this.matchKeyword(Keywords.obj)) {
|
||||
throw new MissingKeywordError(this.bytes.position(), Keywords.obj);
|
||||
}
|
||||
|
||||
return PDFRef.of(objectNumber, generationNumber);
|
||||
}
|
||||
|
||||
private matchIndirectObjectHeader(): boolean {
|
||||
const initialOffset = this.bytes.offset();
|
||||
try {
|
||||
this.parseIndirectObjectHeader();
|
||||
return true;
|
||||
} catch (e) {
|
||||
this.bytes.moveTo(initialOffset);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private shouldWaitForTick = () => {
|
||||
this.parsedObjects += 1;
|
||||
return this.parsedObjects % this.objectsPerTick === 0;
|
||||
};
|
||||
|
||||
private async parseIndirectObject(): Promise<PDFRef> {
|
||||
const ref = this.parseIndirectObjectHeader();
|
||||
|
||||
this.skipWhitespaceAndComments();
|
||||
const object = this.parseObject();
|
||||
|
||||
this.skipWhitespaceAndComments();
|
||||
// if (!this.matchKeyword(Keywords.endobj)) {
|
||||
// throw new MissingKeywordError(this.bytes.position(), Keywords.endobj);
|
||||
// }
|
||||
|
||||
// TODO: Log a warning if this fails...
|
||||
this.matchKeyword(Keywords.endobj);
|
||||
|
||||
if (
|
||||
object instanceof PDFRawStream &&
|
||||
object.dict.lookup(PDFName.of('Type')) === PDFName.of('ObjStm')
|
||||
) {
|
||||
await PDFObjectStreamParser.forStream(
|
||||
object,
|
||||
this.shouldWaitForTick,
|
||||
).parseIntoContext();
|
||||
} else if (
|
||||
object instanceof PDFRawStream &&
|
||||
object.dict.lookup(PDFName.of('Type')) === PDFName.of('XRef')
|
||||
) {
|
||||
PDFXRefStreamParser.forStream(object).parseIntoContext();
|
||||
} else {
|
||||
this.context.assign(ref, object);
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
// TODO: Improve and clean this up
|
||||
private tryToParseInvalidIndirectObject() {
|
||||
const startPos = this.bytes.position();
|
||||
|
||||
const msg = `Trying to parse invalid object: ${JSON.stringify(startPos)})`;
|
||||
if (this.throwOnInvalidObject) throw new Error(msg);
|
||||
console.warn(msg);
|
||||
|
||||
const ref = this.parseIndirectObjectHeader();
|
||||
|
||||
console.warn(`Invalid object ref: ${ref}`);
|
||||
|
||||
this.skipWhitespaceAndComments();
|
||||
const start = this.bytes.offset();
|
||||
|
||||
let failed = true;
|
||||
while (!this.bytes.done()) {
|
||||
if (this.matchKeyword(Keywords.endobj)) {
|
||||
failed = false;
|
||||
}
|
||||
if (!failed) break;
|
||||
this.bytes.next();
|
||||
}
|
||||
|
||||
if (failed) throw new PDFInvalidObjectParsingError(startPos);
|
||||
|
||||
const end = this.bytes.offset() - Keywords.endobj.length;
|
||||
|
||||
const object = PDFInvalidObject.of(this.bytes.slice(start, end));
|
||||
this.context.assign(ref, object);
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
private async parseIndirectObjects(): Promise<void> {
|
||||
this.skipWhitespaceAndComments();
|
||||
|
||||
while (!this.bytes.done() && IsDigit[this.bytes.peek()]) {
|
||||
const initialOffset = this.bytes.offset();
|
||||
|
||||
try {
|
||||
await this.parseIndirectObject();
|
||||
} catch (e) {
|
||||
// TODO: Add tracing/logging mechanism to track when this happens!
|
||||
this.bytes.moveTo(initialOffset);
|
||||
this.tryToParseInvalidIndirectObject();
|
||||
}
|
||||
this.skipWhitespaceAndComments();
|
||||
|
||||
// TODO: Can this be done only when needed, to avoid harming performance?
|
||||
this.skipJibberish();
|
||||
|
||||
if (this.shouldWaitForTick()) await waitForTick();
|
||||
}
|
||||
}
|
||||
|
||||
private maybeParseCrossRefSection(): PDFCrossRefSection | void {
|
||||
this.skipWhitespaceAndComments();
|
||||
if (!this.matchKeyword(Keywords.xref)) return;
|
||||
this.skipWhitespaceAndComments();
|
||||
|
||||
let objectNumber = -1;
|
||||
const xref = PDFCrossRefSection.createEmpty();
|
||||
|
||||
while (!this.bytes.done() && IsDigit[this.bytes.peek()]) {
|
||||
const firstInt = this.parseRawInt();
|
||||
this.skipWhitespaceAndComments();
|
||||
|
||||
const secondInt = this.parseRawInt();
|
||||
this.skipWhitespaceAndComments();
|
||||
|
||||
const byte = this.bytes.peek();
|
||||
if (byte === CharCodes.n || byte === CharCodes.f) {
|
||||
const ref = PDFRef.of(objectNumber, secondInt);
|
||||
if (this.bytes.next() === CharCodes.n) {
|
||||
xref.addEntry(ref, firstInt);
|
||||
} else {
|
||||
// this.context.delete(ref);
|
||||
xref.addDeletedEntry(ref, firstInt);
|
||||
}
|
||||
objectNumber += 1;
|
||||
} else {
|
||||
objectNumber = firstInt;
|
||||
}
|
||||
this.skipWhitespaceAndComments();
|
||||
}
|
||||
|
||||
return xref;
|
||||
}
|
||||
|
||||
private maybeParseTrailerDict(): void {
|
||||
this.skipWhitespaceAndComments();
|
||||
if (!this.matchKeyword(Keywords.trailer)) return;
|
||||
this.skipWhitespaceAndComments();
|
||||
|
||||
const dict = this.parseDict();
|
||||
|
||||
const { context } = this;
|
||||
context.trailerInfo = {
|
||||
Root: dict.get(PDFName.of('Root')) || context.trailerInfo.Root,
|
||||
Encrypt: dict.get(PDFName.of('Encrypt')) || context.trailerInfo.Encrypt,
|
||||
Info: dict.get(PDFName.of('Info')) || context.trailerInfo.Info,
|
||||
ID: dict.get(PDFName.of('ID')) || context.trailerInfo.ID,
|
||||
};
|
||||
}
|
||||
|
||||
private maybeParseTrailer(): PDFTrailer | void {
|
||||
this.skipWhitespaceAndComments();
|
||||
if (!this.matchKeyword(Keywords.startxref)) return;
|
||||
this.skipWhitespaceAndComments();
|
||||
|
||||
const offset = this.parseRawInt();
|
||||
|
||||
this.skipWhitespace();
|
||||
this.matchKeyword(Keywords.eof);
|
||||
this.skipWhitespaceAndComments();
|
||||
this.matchKeyword(Keywords.eof);
|
||||
this.skipWhitespaceAndComments();
|
||||
|
||||
return PDFTrailer.forLastCrossRefSectionOffset(offset);
|
||||
}
|
||||
|
||||
private async parseDocumentSection(): Promise<void> {
|
||||
await this.parseIndirectObjects();
|
||||
this.maybeParseCrossRefSection();
|
||||
this.maybeParseTrailerDict();
|
||||
this.maybeParseTrailer();
|
||||
|
||||
// TODO: Can this be done only when needed, to avoid harming performance?
|
||||
this.skipJibberish();
|
||||
}
|
||||
|
||||
/**
|
||||
* This operation is not necessary for valid PDF files. But some invalid PDFs
|
||||
* contain jibberish in between indirect objects. This method is designed to
|
||||
* skip past that jibberish, should it exist, until it reaches the next
|
||||
* indirect object header, an xref table section, or the file trailer.
|
||||
*/
|
||||
private skipJibberish(): void {
|
||||
this.skipWhitespaceAndComments();
|
||||
while (!this.bytes.done()) {
|
||||
const initialOffset = this.bytes.offset();
|
||||
const byte = this.bytes.peek();
|
||||
const isAlphaNumeric = byte >= CharCodes.Space && byte <= CharCodes.Tilde;
|
||||
if (isAlphaNumeric) {
|
||||
if (
|
||||
this.matchKeyword(Keywords.xref) ||
|
||||
this.matchKeyword(Keywords.trailer) ||
|
||||
this.matchKeyword(Keywords.startxref) ||
|
||||
this.matchIndirectObjectHeader()
|
||||
) {
|
||||
this.bytes.moveTo(initialOffset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.bytes.next();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips the binary comment following a PDF header. The specification
|
||||
* defines this binary comment (section 7.5.2 File Header) as a sequence of 4
|
||||
* or more bytes that are 128 or greater, and which are preceded by a "%".
|
||||
*
|
||||
* This would imply that to strip out this binary comment, we could check for
|
||||
* a sequence of bytes starting with "%", and remove all subsequent bytes that
|
||||
* are 128 or greater. This works for many documents that properly comply with
|
||||
* the spec. But in the wild, there are PDFs that omit the leading "%", and
|
||||
* include bytes that are less than 128 (e.g. 0 or 1). So in order to parse
|
||||
* these headers correctly, we just throw out all bytes leading up to the
|
||||
* first indirect object header.
|
||||
*/
|
||||
private skipBinaryHeaderComment(): void {
|
||||
this.skipWhitespaceAndComments();
|
||||
try {
|
||||
const initialOffset = this.bytes.offset();
|
||||
this.parseIndirectObjectHeader();
|
||||
this.bytes.moveTo(initialOffset);
|
||||
} catch (e) {
|
||||
this.bytes.next();
|
||||
this.skipWhitespaceAndComments();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFParser;
|
||||
130
frontend/node_modules/pdf-lib/src/core/parser/PDFXRefStreamParser.ts
generated
vendored
Normal file
130
frontend/node_modules/pdf-lib/src/core/parser/PDFXRefStreamParser.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import { ReparseError } from 'src/core/errors';
|
||||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFRawStream from 'src/core/objects/PDFRawStream';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import ByteStream from 'src/core/parser/ByteStream';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
|
||||
export interface Entry {
|
||||
ref: PDFRef;
|
||||
offset: number;
|
||||
deleted: boolean;
|
||||
inObjectStream: boolean;
|
||||
}
|
||||
|
||||
class PDFXRefStreamParser {
|
||||
static forStream = (rawStream: PDFRawStream) =>
|
||||
new PDFXRefStreamParser(rawStream);
|
||||
|
||||
private alreadyParsed: boolean;
|
||||
|
||||
private readonly dict: PDFDict;
|
||||
private readonly context: PDFContext;
|
||||
private readonly bytes: ByteStream;
|
||||
private readonly subsections: {
|
||||
firstObjectNumber: number;
|
||||
length: number;
|
||||
}[];
|
||||
private readonly byteWidths: [number, number, number];
|
||||
|
||||
constructor(rawStream: PDFRawStream) {
|
||||
this.alreadyParsed = false;
|
||||
|
||||
this.dict = rawStream.dict;
|
||||
this.bytes = ByteStream.fromPDFRawStream(rawStream);
|
||||
this.context = this.dict.context;
|
||||
|
||||
const Size = this.dict.lookup(PDFName.of('Size'), PDFNumber);
|
||||
|
||||
const Index = this.dict.lookup(PDFName.of('Index'));
|
||||
if (Index instanceof PDFArray) {
|
||||
this.subsections = [];
|
||||
for (let idx = 0, len = Index.size(); idx < len; idx += 2) {
|
||||
const firstObjectNumber = Index.lookup(idx + 0, PDFNumber).asNumber();
|
||||
const length = Index.lookup(idx + 1, PDFNumber).asNumber();
|
||||
this.subsections.push({ firstObjectNumber, length });
|
||||
}
|
||||
} else {
|
||||
this.subsections = [{ firstObjectNumber: 0, length: Size.asNumber() }];
|
||||
}
|
||||
|
||||
const W = this.dict.lookup(PDFName.of('W'), PDFArray);
|
||||
this.byteWidths = [-1, -1, -1];
|
||||
for (let idx = 0, len = W.size(); idx < len; idx++) {
|
||||
this.byteWidths[idx] = W.lookup(idx, PDFNumber).asNumber();
|
||||
}
|
||||
}
|
||||
|
||||
parseIntoContext(): Entry[] {
|
||||
if (this.alreadyParsed) {
|
||||
throw new ReparseError('PDFXRefStreamParser', 'parseIntoContext');
|
||||
}
|
||||
this.alreadyParsed = true;
|
||||
|
||||
this.context.trailerInfo = {
|
||||
Root: this.dict.get(PDFName.of('Root')),
|
||||
Encrypt: this.dict.get(PDFName.of('Encrypt')),
|
||||
Info: this.dict.get(PDFName.of('Info')),
|
||||
ID: this.dict.get(PDFName.of('ID')),
|
||||
};
|
||||
|
||||
const entries = this.parseEntries();
|
||||
|
||||
// for (let idx = 0, len = entries.length; idx < len; idx++) {
|
||||
// const entry = entries[idx];
|
||||
// if (entry.deleted) this.context.delete(entry.ref);
|
||||
// }
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
private parseEntries(): Entry[] {
|
||||
const entries = [];
|
||||
const [typeFieldWidth, offsetFieldWidth, genFieldWidth] = this.byteWidths;
|
||||
|
||||
for (
|
||||
let subsectionIdx = 0, subsectionLen = this.subsections.length;
|
||||
subsectionIdx < subsectionLen;
|
||||
subsectionIdx++
|
||||
) {
|
||||
const { firstObjectNumber, length } = this.subsections[subsectionIdx];
|
||||
|
||||
for (let objIdx = 0; objIdx < length; objIdx++) {
|
||||
let type = 0;
|
||||
for (let idx = 0, len = typeFieldWidth; idx < len; idx++) {
|
||||
type = (type << 8) | this.bytes.next();
|
||||
}
|
||||
|
||||
let offset = 0;
|
||||
for (let idx = 0, len = offsetFieldWidth; idx < len; idx++) {
|
||||
offset = (offset << 8) | this.bytes.next();
|
||||
}
|
||||
|
||||
let generationNumber = 0;
|
||||
for (let idx = 0, len = genFieldWidth; idx < len; idx++) {
|
||||
generationNumber = (generationNumber << 8) | this.bytes.next();
|
||||
}
|
||||
|
||||
// When the `type` field is absent, it defaults to 1
|
||||
if (typeFieldWidth === 0) type = 1;
|
||||
|
||||
const objectNumber = firstObjectNumber + objIdx;
|
||||
const entry = {
|
||||
ref: PDFRef.of(objectNumber, generationNumber),
|
||||
offset,
|
||||
deleted: type === 0,
|
||||
inObjectStream: type === 2,
|
||||
};
|
||||
|
||||
entries.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFXRefStreamParser;
|
||||
98
frontend/node_modules/pdf-lib/src/core/streams/Ascii85Stream.ts
generated
vendored
Normal file
98
frontend/node_modules/pdf-lib/src/core/streams/Ascii85Stream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* The Ascii85Stream class contained in this file is a TypeScript port of the
|
||||
* JavaScript Ascii85Stream class in Mozilla's pdf.js project, made available
|
||||
* under the Apache 2.0 open source license.
|
||||
*/
|
||||
|
||||
import DecodeStream from 'src/core/streams/DecodeStream';
|
||||
import { StreamType } from 'src/core/streams/Stream';
|
||||
|
||||
const isSpace = (ch: number) =>
|
||||
ch === 0x20 || ch === 0x09 || ch === 0x0d || ch === 0x0a;
|
||||
|
||||
class Ascii85Stream extends DecodeStream {
|
||||
private stream: StreamType;
|
||||
private input: Uint8Array;
|
||||
|
||||
constructor(stream: StreamType, maybeLength?: number) {
|
||||
super(maybeLength);
|
||||
|
||||
this.stream = stream;
|
||||
this.input = new Uint8Array(5);
|
||||
|
||||
// Most streams increase in size when decoded, but Ascii85 streams
|
||||
// typically shrink by ~20%.
|
||||
if (maybeLength) {
|
||||
maybeLength = 0.8 * maybeLength;
|
||||
}
|
||||
}
|
||||
|
||||
protected readBlock() {
|
||||
const TILDA_CHAR = 0x7e; // '~'
|
||||
const Z_LOWER_CHAR = 0x7a; // 'z'
|
||||
const EOF = -1;
|
||||
|
||||
const stream = this.stream;
|
||||
|
||||
let c = stream.getByte();
|
||||
while (isSpace(c)) {
|
||||
c = stream.getByte();
|
||||
}
|
||||
|
||||
if (c === EOF || c === TILDA_CHAR) {
|
||||
this.eof = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const bufferLength = this.bufferLength;
|
||||
let buffer;
|
||||
let i;
|
||||
|
||||
// special code for z
|
||||
if (c === Z_LOWER_CHAR) {
|
||||
buffer = this.ensureBuffer(bufferLength + 4);
|
||||
for (i = 0; i < 4; ++i) {
|
||||
buffer[bufferLength + i] = 0;
|
||||
}
|
||||
this.bufferLength += 4;
|
||||
} else {
|
||||
const input = this.input;
|
||||
input[0] = c;
|
||||
for (i = 1; i < 5; ++i) {
|
||||
c = stream.getByte();
|
||||
while (isSpace(c)) {
|
||||
c = stream.getByte();
|
||||
}
|
||||
|
||||
input[i] = c;
|
||||
|
||||
if (c === EOF || c === TILDA_CHAR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
buffer = this.ensureBuffer(bufferLength + i - 1);
|
||||
this.bufferLength += i - 1;
|
||||
|
||||
// partial ending;
|
||||
if (i < 5) {
|
||||
for (; i < 5; ++i) {
|
||||
input[i] = 0x21 + 84;
|
||||
}
|
||||
this.eof = true;
|
||||
}
|
||||
let t = 0;
|
||||
for (i = 0; i < 5; ++i) {
|
||||
t = t * 85 + (input[i] - 0x21);
|
||||
}
|
||||
|
||||
for (i = 3; i >= 0; --i) {
|
||||
buffer[bufferLength + i] = t & 0xff;
|
||||
t >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Ascii85Stream;
|
||||
77
frontend/node_modules/pdf-lib/src/core/streams/AsciiHexStream.ts
generated
vendored
Normal file
77
frontend/node_modules/pdf-lib/src/core/streams/AsciiHexStream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* The AsciiHexStream class contained in this file is a TypeScript port of the
|
||||
* JavaScript AsciiHexStream class in Mozilla's pdf.js project, made available
|
||||
* under the Apache 2.0 open source license.
|
||||
*/
|
||||
|
||||
import DecodeStream from 'src/core/streams/DecodeStream';
|
||||
import { StreamType } from 'src/core/streams/Stream';
|
||||
|
||||
class AsciiHexStream extends DecodeStream {
|
||||
private stream: StreamType;
|
||||
private firstDigit: number;
|
||||
|
||||
constructor(stream: StreamType, maybeLength?: number) {
|
||||
super(maybeLength);
|
||||
|
||||
this.stream = stream;
|
||||
|
||||
this.firstDigit = -1;
|
||||
|
||||
// Most streams increase in size when decoded, but AsciiHex streams shrink
|
||||
// by 50%.
|
||||
if (maybeLength) {
|
||||
maybeLength = 0.5 * maybeLength;
|
||||
}
|
||||
}
|
||||
|
||||
protected readBlock() {
|
||||
const UPSTREAM_BLOCK_SIZE = 8000;
|
||||
const bytes = this.stream.getBytes(UPSTREAM_BLOCK_SIZE);
|
||||
if (!bytes.length) {
|
||||
this.eof = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const maxDecodeLength = (bytes.length + 1) >> 1;
|
||||
const buffer = this.ensureBuffer(this.bufferLength + maxDecodeLength);
|
||||
let bufferLength = this.bufferLength;
|
||||
|
||||
let firstDigit = this.firstDigit;
|
||||
for (let i = 0, ii = bytes.length; i < ii; i++) {
|
||||
const ch = bytes[i];
|
||||
let digit;
|
||||
if (ch >= 0x30 && ch <= 0x39) {
|
||||
// '0'-'9'
|
||||
digit = ch & 0x0f;
|
||||
} else if ((ch >= 0x41 && ch <= 0x46) || (ch >= 0x61 && ch <= 0x66)) {
|
||||
// 'A'-'Z', 'a'-'z'
|
||||
digit = (ch & 0x0f) + 9;
|
||||
} else if (ch === 0x3e) {
|
||||
// '>'
|
||||
this.eof = true;
|
||||
break;
|
||||
} else {
|
||||
// probably whitespace
|
||||
continue; // ignoring
|
||||
}
|
||||
if (firstDigit < 0) {
|
||||
firstDigit = digit;
|
||||
} else {
|
||||
buffer[bufferLength++] = (firstDigit << 4) | digit;
|
||||
firstDigit = -1;
|
||||
}
|
||||
}
|
||||
if (firstDigit >= 0 && this.eof) {
|
||||
// incomplete byte
|
||||
buffer[bufferLength++] = firstDigit << 4;
|
||||
firstDigit = -1;
|
||||
}
|
||||
this.firstDigit = firstDigit;
|
||||
this.bufferLength = bufferLength;
|
||||
}
|
||||
}
|
||||
|
||||
export default AsciiHexStream;
|
||||
170
frontend/node_modules/pdf-lib/src/core/streams/DecodeStream.ts
generated
vendored
Normal file
170
frontend/node_modules/pdf-lib/src/core/streams/DecodeStream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
import { MethodNotImplementedError } from 'src/core/errors';
|
||||
import Stream, { StreamType } from 'src/core/streams/Stream';
|
||||
|
||||
/*
|
||||
* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* The DecodeStream class contained in this file is a TypeScript port of the
|
||||
* JavaScript DecodeStream class in Mozilla's pdf.js project, made available
|
||||
* under the Apache 2.0 open source license.
|
||||
*/
|
||||
|
||||
// Lots of DecodeStreams are created whose buffers are never used. For these
|
||||
// we share a single empty buffer. This is (a) space-efficient and (b) avoids
|
||||
// having special cases that would be required if we used |null| for an empty
|
||||
// buffer.
|
||||
const emptyBuffer = new Uint8Array(0);
|
||||
|
||||
/**
|
||||
* Super class for the decoding streams
|
||||
*/
|
||||
class DecodeStream implements StreamType {
|
||||
protected bufferLength: number;
|
||||
protected buffer: Uint8Array;
|
||||
protected eof: boolean;
|
||||
|
||||
private pos: number;
|
||||
private minBufferLength: number;
|
||||
|
||||
constructor(maybeMinBufferLength?: number) {
|
||||
this.pos = 0;
|
||||
this.bufferLength = 0;
|
||||
this.eof = false;
|
||||
this.buffer = emptyBuffer;
|
||||
this.minBufferLength = 512;
|
||||
if (maybeMinBufferLength) {
|
||||
// Compute the first power of two that is as big as maybeMinBufferLength.
|
||||
while (this.minBufferLength < maybeMinBufferLength) {
|
||||
this.minBufferLength *= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get isEmpty() {
|
||||
while (!this.eof && this.bufferLength === 0) {
|
||||
this.readBlock();
|
||||
}
|
||||
return this.bufferLength === 0;
|
||||
}
|
||||
|
||||
getByte() {
|
||||
const pos = this.pos;
|
||||
while (this.bufferLength <= pos) {
|
||||
if (this.eof) {
|
||||
return -1;
|
||||
}
|
||||
this.readBlock();
|
||||
}
|
||||
return this.buffer[this.pos++];
|
||||
}
|
||||
|
||||
getUint16() {
|
||||
const b0 = this.getByte();
|
||||
const b1 = this.getByte();
|
||||
if (b0 === -1 || b1 === -1) {
|
||||
return -1;
|
||||
}
|
||||
return (b0 << 8) + b1;
|
||||
}
|
||||
|
||||
getInt32() {
|
||||
const b0 = this.getByte();
|
||||
const b1 = this.getByte();
|
||||
const b2 = this.getByte();
|
||||
const b3 = this.getByte();
|
||||
return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
|
||||
}
|
||||
|
||||
getBytes(length: number, forceClamped = false) {
|
||||
let end;
|
||||
const pos = this.pos;
|
||||
|
||||
if (length) {
|
||||
this.ensureBuffer(pos + length);
|
||||
end = pos + length;
|
||||
|
||||
while (!this.eof && this.bufferLength < end) {
|
||||
this.readBlock();
|
||||
}
|
||||
const bufEnd = this.bufferLength;
|
||||
if (end > bufEnd) {
|
||||
end = bufEnd;
|
||||
}
|
||||
} else {
|
||||
while (!this.eof) {
|
||||
this.readBlock();
|
||||
}
|
||||
end = this.bufferLength;
|
||||
}
|
||||
|
||||
this.pos = end;
|
||||
const subarray = this.buffer.subarray(pos, end);
|
||||
// `this.buffer` is either a `Uint8Array` or `Uint8ClampedArray` here.
|
||||
return forceClamped && !(subarray instanceof Uint8ClampedArray)
|
||||
? new Uint8ClampedArray(subarray)
|
||||
: subarray;
|
||||
}
|
||||
|
||||
peekByte() {
|
||||
const peekedByte = this.getByte();
|
||||
this.pos--;
|
||||
return peekedByte;
|
||||
}
|
||||
|
||||
peekBytes(length: number, forceClamped = false) {
|
||||
const bytes = this.getBytes(length, forceClamped);
|
||||
this.pos -= bytes.length;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
skip(n: number) {
|
||||
if (!n) {
|
||||
n = 1;
|
||||
}
|
||||
this.pos += n;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.pos = 0;
|
||||
}
|
||||
|
||||
makeSubStream(start: number, length: number /* dict */) {
|
||||
const end = start + length;
|
||||
while (this.bufferLength <= end && !this.eof) {
|
||||
this.readBlock();
|
||||
}
|
||||
return new Stream(this.buffer, start, length /* dict */);
|
||||
}
|
||||
|
||||
decode(): Uint8Array {
|
||||
while (!this.eof) this.readBlock();
|
||||
return this.buffer.subarray(0, this.bufferLength);
|
||||
}
|
||||
|
||||
protected readBlock(): void {
|
||||
throw new MethodNotImplementedError(this.constructor.name, 'readBlock');
|
||||
}
|
||||
|
||||
protected ensureBuffer(requested: number) {
|
||||
const buffer = this.buffer;
|
||||
if (requested <= buffer.byteLength) {
|
||||
return buffer;
|
||||
}
|
||||
let size = this.minBufferLength;
|
||||
while (size < requested) {
|
||||
size *= 2;
|
||||
}
|
||||
const buffer2 = new Uint8Array(size);
|
||||
buffer2.set(buffer);
|
||||
return (this.buffer = buffer2);
|
||||
}
|
||||
|
||||
// getBaseStreams() {
|
||||
// if (this.str && this.str.getBaseStreams) {
|
||||
// return this.str.getBaseStreams();
|
||||
// }
|
||||
// return [];
|
||||
// }
|
||||
}
|
||||
|
||||
export default DecodeStream;
|
||||
407
frontend/node_modules/pdf-lib/src/core/streams/FlateStream.ts
generated
vendored
Normal file
407
frontend/node_modules/pdf-lib/src/core/streams/FlateStream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
/*
|
||||
* Copyright 1996-2003 Glyph & Cog, LLC
|
||||
*
|
||||
* The flate stream implementation contained in this file is a JavaScript port
|
||||
* of XPDF's implementation, made available under the Apache 2.0 open source
|
||||
* license.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* The FlateStream class contained in this file is a TypeScript port of the
|
||||
* JavaScript FlateStream class in Mozilla's pdf.js project, made available
|
||||
* under the Apache 2.0 open source license.
|
||||
*/
|
||||
|
||||
/* tslint:disable no-conditional-assignment */
|
||||
import DecodeStream from 'src/core/streams/DecodeStream';
|
||||
import { StreamType } from 'src/core/streams/Stream';
|
||||
|
||||
// prettier-ignore
|
||||
const codeLenCodeMap = new Int32Array([
|
||||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
|
||||
]);
|
||||
|
||||
// prettier-ignore
|
||||
const lengthDecode = new Int32Array([
|
||||
0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009, 0x0000a,
|
||||
0x1000b, 0x1000d, 0x1000f, 0x10011, 0x20013, 0x20017, 0x2001b, 0x2001f,
|
||||
0x30023, 0x3002b, 0x30033, 0x3003b, 0x40043, 0x40053, 0x40063, 0x40073,
|
||||
0x50083, 0x500a3, 0x500c3, 0x500e3, 0x00102, 0x00102, 0x00102
|
||||
]);
|
||||
|
||||
// prettier-ignore
|
||||
const distDecode = new Int32Array([
|
||||
0x00001, 0x00002, 0x00003, 0x00004, 0x10005, 0x10007, 0x20009, 0x2000d,
|
||||
0x30011, 0x30019, 0x40021, 0x40031, 0x50041, 0x50061, 0x60081, 0x600c1,
|
||||
0x70101, 0x70181, 0x80201, 0x80301, 0x90401, 0x90601, 0xa0801, 0xa0c01,
|
||||
0xb1001, 0xb1801, 0xc2001, 0xc3001, 0xd4001, 0xd6001
|
||||
]);
|
||||
|
||||
// prettier-ignore
|
||||
const fixedLitCodeTab = [new Int32Array([
|
||||
0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c0,
|
||||
0x70108, 0x80060, 0x80020, 0x900a0, 0x80000, 0x80080, 0x80040, 0x900e0,
|
||||
0x70104, 0x80058, 0x80018, 0x90090, 0x70114, 0x80078, 0x80038, 0x900d0,
|
||||
0x7010c, 0x80068, 0x80028, 0x900b0, 0x80008, 0x80088, 0x80048, 0x900f0,
|
||||
0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c8,
|
||||
0x7010a, 0x80064, 0x80024, 0x900a8, 0x80004, 0x80084, 0x80044, 0x900e8,
|
||||
0x70106, 0x8005c, 0x8001c, 0x90098, 0x70116, 0x8007c, 0x8003c, 0x900d8,
|
||||
0x7010e, 0x8006c, 0x8002c, 0x900b8, 0x8000c, 0x8008c, 0x8004c, 0x900f8,
|
||||
0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c4,
|
||||
0x70109, 0x80062, 0x80022, 0x900a4, 0x80002, 0x80082, 0x80042, 0x900e4,
|
||||
0x70105, 0x8005a, 0x8001a, 0x90094, 0x70115, 0x8007a, 0x8003a, 0x900d4,
|
||||
0x7010d, 0x8006a, 0x8002a, 0x900b4, 0x8000a, 0x8008a, 0x8004a, 0x900f4,
|
||||
0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cc,
|
||||
0x7010b, 0x80066, 0x80026, 0x900ac, 0x80006, 0x80086, 0x80046, 0x900ec,
|
||||
0x70107, 0x8005e, 0x8001e, 0x9009c, 0x70117, 0x8007e, 0x8003e, 0x900dc,
|
||||
0x7010f, 0x8006e, 0x8002e, 0x900bc, 0x8000e, 0x8008e, 0x8004e, 0x900fc,
|
||||
0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c2,
|
||||
0x70108, 0x80061, 0x80021, 0x900a2, 0x80001, 0x80081, 0x80041, 0x900e2,
|
||||
0x70104, 0x80059, 0x80019, 0x90092, 0x70114, 0x80079, 0x80039, 0x900d2,
|
||||
0x7010c, 0x80069, 0x80029, 0x900b2, 0x80009, 0x80089, 0x80049, 0x900f2,
|
||||
0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900ca,
|
||||
0x7010a, 0x80065, 0x80025, 0x900aa, 0x80005, 0x80085, 0x80045, 0x900ea,
|
||||
0x70106, 0x8005d, 0x8001d, 0x9009a, 0x70116, 0x8007d, 0x8003d, 0x900da,
|
||||
0x7010e, 0x8006d, 0x8002d, 0x900ba, 0x8000d, 0x8008d, 0x8004d, 0x900fa,
|
||||
0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c6,
|
||||
0x70109, 0x80063, 0x80023, 0x900a6, 0x80003, 0x80083, 0x80043, 0x900e6,
|
||||
0x70105, 0x8005b, 0x8001b, 0x90096, 0x70115, 0x8007b, 0x8003b, 0x900d6,
|
||||
0x7010d, 0x8006b, 0x8002b, 0x900b6, 0x8000b, 0x8008b, 0x8004b, 0x900f6,
|
||||
0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900ce,
|
||||
0x7010b, 0x80067, 0x80027, 0x900ae, 0x80007, 0x80087, 0x80047, 0x900ee,
|
||||
0x70107, 0x8005f, 0x8001f, 0x9009e, 0x70117, 0x8007f, 0x8003f, 0x900de,
|
||||
0x7010f, 0x8006f, 0x8002f, 0x900be, 0x8000f, 0x8008f, 0x8004f, 0x900fe,
|
||||
0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c1,
|
||||
0x70108, 0x80060, 0x80020, 0x900a1, 0x80000, 0x80080, 0x80040, 0x900e1,
|
||||
0x70104, 0x80058, 0x80018, 0x90091, 0x70114, 0x80078, 0x80038, 0x900d1,
|
||||
0x7010c, 0x80068, 0x80028, 0x900b1, 0x80008, 0x80088, 0x80048, 0x900f1,
|
||||
0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c9,
|
||||
0x7010a, 0x80064, 0x80024, 0x900a9, 0x80004, 0x80084, 0x80044, 0x900e9,
|
||||
0x70106, 0x8005c, 0x8001c, 0x90099, 0x70116, 0x8007c, 0x8003c, 0x900d9,
|
||||
0x7010e, 0x8006c, 0x8002c, 0x900b9, 0x8000c, 0x8008c, 0x8004c, 0x900f9,
|
||||
0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c5,
|
||||
0x70109, 0x80062, 0x80022, 0x900a5, 0x80002, 0x80082, 0x80042, 0x900e5,
|
||||
0x70105, 0x8005a, 0x8001a, 0x90095, 0x70115, 0x8007a, 0x8003a, 0x900d5,
|
||||
0x7010d, 0x8006a, 0x8002a, 0x900b5, 0x8000a, 0x8008a, 0x8004a, 0x900f5,
|
||||
0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cd,
|
||||
0x7010b, 0x80066, 0x80026, 0x900ad, 0x80006, 0x80086, 0x80046, 0x900ed,
|
||||
0x70107, 0x8005e, 0x8001e, 0x9009d, 0x70117, 0x8007e, 0x8003e, 0x900dd,
|
||||
0x7010f, 0x8006e, 0x8002e, 0x900bd, 0x8000e, 0x8008e, 0x8004e, 0x900fd,
|
||||
0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c3,
|
||||
0x70108, 0x80061, 0x80021, 0x900a3, 0x80001, 0x80081, 0x80041, 0x900e3,
|
||||
0x70104, 0x80059, 0x80019, 0x90093, 0x70114, 0x80079, 0x80039, 0x900d3,
|
||||
0x7010c, 0x80069, 0x80029, 0x900b3, 0x80009, 0x80089, 0x80049, 0x900f3,
|
||||
0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900cb,
|
||||
0x7010a, 0x80065, 0x80025, 0x900ab, 0x80005, 0x80085, 0x80045, 0x900eb,
|
||||
0x70106, 0x8005d, 0x8001d, 0x9009b, 0x70116, 0x8007d, 0x8003d, 0x900db,
|
||||
0x7010e, 0x8006d, 0x8002d, 0x900bb, 0x8000d, 0x8008d, 0x8004d, 0x900fb,
|
||||
0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c7,
|
||||
0x70109, 0x80063, 0x80023, 0x900a7, 0x80003, 0x80083, 0x80043, 0x900e7,
|
||||
0x70105, 0x8005b, 0x8001b, 0x90097, 0x70115, 0x8007b, 0x8003b, 0x900d7,
|
||||
0x7010d, 0x8006b, 0x8002b, 0x900b7, 0x8000b, 0x8008b, 0x8004b, 0x900f7,
|
||||
0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900cf,
|
||||
0x7010b, 0x80067, 0x80027, 0x900af, 0x80007, 0x80087, 0x80047, 0x900ef,
|
||||
0x70107, 0x8005f, 0x8001f, 0x9009f, 0x70117, 0x8007f, 0x8003f, 0x900df,
|
||||
0x7010f, 0x8006f, 0x8002f, 0x900bf, 0x8000f, 0x8008f, 0x8004f, 0x900ff
|
||||
]), 9] as [Int32Array, number];
|
||||
|
||||
// prettier-ignore
|
||||
const fixedDistCodeTab = [new Int32Array([
|
||||
0x50000, 0x50010, 0x50008, 0x50018, 0x50004, 0x50014, 0x5000c, 0x5001c,
|
||||
0x50002, 0x50012, 0x5000a, 0x5001a, 0x50006, 0x50016, 0x5000e, 0x00000,
|
||||
0x50001, 0x50011, 0x50009, 0x50019, 0x50005, 0x50015, 0x5000d, 0x5001d,
|
||||
0x50003, 0x50013, 0x5000b, 0x5001b, 0x50007, 0x50017, 0x5000f, 0x00000
|
||||
]), 5] as [Int32Array, number];
|
||||
|
||||
class FlateStream extends DecodeStream {
|
||||
private stream: StreamType;
|
||||
private codeSize: number;
|
||||
private codeBuf: number;
|
||||
|
||||
constructor(stream: StreamType, maybeLength?: number) {
|
||||
super(maybeLength);
|
||||
|
||||
this.stream = stream;
|
||||
|
||||
const cmf = stream.getByte();
|
||||
const flg = stream.getByte();
|
||||
if (cmf === -1 || flg === -1) {
|
||||
throw new Error(`Invalid header in flate stream: ${cmf}, ${flg}`);
|
||||
}
|
||||
if ((cmf & 0x0f) !== 0x08) {
|
||||
throw new Error(
|
||||
`Unknown compression method in flate stream: ${cmf}, ${flg}`,
|
||||
);
|
||||
}
|
||||
if (((cmf << 8) + flg) % 31 !== 0) {
|
||||
throw new Error(`Bad FCHECK in flate stream: ${cmf}, ${flg}`);
|
||||
}
|
||||
if (flg & 0x20) {
|
||||
throw new Error(`FDICT bit set in flate stream: ${cmf}, ${flg}`);
|
||||
}
|
||||
|
||||
this.codeSize = 0;
|
||||
this.codeBuf = 0;
|
||||
}
|
||||
|
||||
protected readBlock() {
|
||||
let buffer;
|
||||
let len;
|
||||
const str = this.stream;
|
||||
// read block header
|
||||
let hdr = this.getBits(3);
|
||||
if (hdr & 1) {
|
||||
this.eof = true;
|
||||
}
|
||||
hdr >>= 1;
|
||||
|
||||
if (hdr === 0) {
|
||||
// uncompressed block
|
||||
let b;
|
||||
|
||||
if ((b = str.getByte()) === -1) {
|
||||
throw new Error('Bad block header in flate stream');
|
||||
}
|
||||
let blockLen = b;
|
||||
if ((b = str.getByte()) === -1) {
|
||||
throw new Error('Bad block header in flate stream');
|
||||
}
|
||||
blockLen |= b << 8;
|
||||
if ((b = str.getByte()) === -1) {
|
||||
throw new Error('Bad block header in flate stream');
|
||||
}
|
||||
let check = b;
|
||||
if ((b = str.getByte()) === -1) {
|
||||
throw new Error('Bad block header in flate stream');
|
||||
}
|
||||
check |= b << 8;
|
||||
if (check !== (~blockLen & 0xffff) && (blockLen !== 0 || check !== 0)) {
|
||||
// Ignoring error for bad "empty" block (see issue 1277)
|
||||
throw new Error('Bad uncompressed block length in flate stream');
|
||||
}
|
||||
|
||||
this.codeBuf = 0;
|
||||
this.codeSize = 0;
|
||||
|
||||
const bufferLength = this.bufferLength;
|
||||
buffer = this.ensureBuffer(bufferLength + blockLen);
|
||||
const end = bufferLength + blockLen;
|
||||
this.bufferLength = end;
|
||||
if (blockLen === 0) {
|
||||
if (str.peekByte() === -1) {
|
||||
this.eof = true;
|
||||
}
|
||||
} else {
|
||||
for (let n = bufferLength; n < end; ++n) {
|
||||
if ((b = str.getByte()) === -1) {
|
||||
this.eof = true;
|
||||
break;
|
||||
}
|
||||
buffer[n] = b;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let litCodeTable;
|
||||
let distCodeTable;
|
||||
if (hdr === 1) {
|
||||
// compressed block, fixed codes
|
||||
litCodeTable = fixedLitCodeTab;
|
||||
distCodeTable = fixedDistCodeTab;
|
||||
} else if (hdr === 2) {
|
||||
// compressed block, dynamic codes
|
||||
const numLitCodes = this.getBits(5) + 257;
|
||||
const numDistCodes = this.getBits(5) + 1;
|
||||
const numCodeLenCodes = this.getBits(4) + 4;
|
||||
|
||||
// build the code lengths code table
|
||||
const codeLenCodeLengths = new Uint8Array(codeLenCodeMap.length);
|
||||
|
||||
let i;
|
||||
for (i = 0; i < numCodeLenCodes; ++i) {
|
||||
codeLenCodeLengths[codeLenCodeMap[i]] = this.getBits(3);
|
||||
}
|
||||
const codeLenCodeTab = this.generateHuffmanTable(codeLenCodeLengths);
|
||||
|
||||
// build the literal and distance code tables
|
||||
len = 0;
|
||||
i = 0;
|
||||
const codes = numLitCodes + numDistCodes;
|
||||
const codeLengths = new Uint8Array(codes);
|
||||
let bitsLength;
|
||||
let bitsOffset;
|
||||
let what;
|
||||
while (i < codes) {
|
||||
const code = this.getCode(codeLenCodeTab);
|
||||
if (code === 16) {
|
||||
bitsLength = 2;
|
||||
bitsOffset = 3;
|
||||
what = len;
|
||||
} else if (code === 17) {
|
||||
bitsLength = 3;
|
||||
bitsOffset = 3;
|
||||
what = len = 0;
|
||||
} else if (code === 18) {
|
||||
bitsLength = 7;
|
||||
bitsOffset = 11;
|
||||
what = len = 0;
|
||||
} else {
|
||||
codeLengths[i++] = len = code;
|
||||
continue;
|
||||
}
|
||||
|
||||
let repeatLength = this.getBits(bitsLength) + bitsOffset;
|
||||
while (repeatLength-- > 0) {
|
||||
codeLengths[i++] = what;
|
||||
}
|
||||
}
|
||||
|
||||
litCodeTable = this.generateHuffmanTable(
|
||||
codeLengths.subarray(0, numLitCodes),
|
||||
);
|
||||
distCodeTable = this.generateHuffmanTable(
|
||||
codeLengths.subarray(numLitCodes, codes),
|
||||
);
|
||||
} else {
|
||||
throw new Error('Unknown block type in flate stream');
|
||||
}
|
||||
|
||||
buffer = this.buffer;
|
||||
let limit = buffer ? buffer.length : 0;
|
||||
let pos = this.bufferLength;
|
||||
while (true) {
|
||||
let code1 = this.getCode(litCodeTable);
|
||||
if (code1 < 256) {
|
||||
if (pos + 1 >= limit) {
|
||||
buffer = this.ensureBuffer(pos + 1);
|
||||
limit = buffer.length;
|
||||
}
|
||||
buffer[pos++] = code1;
|
||||
continue;
|
||||
}
|
||||
if (code1 === 256) {
|
||||
this.bufferLength = pos;
|
||||
return;
|
||||
}
|
||||
code1 -= 257;
|
||||
code1 = lengthDecode[code1];
|
||||
let code2 = code1 >> 16;
|
||||
if (code2 > 0) {
|
||||
code2 = this.getBits(code2);
|
||||
}
|
||||
len = (code1 & 0xffff) + code2;
|
||||
code1 = this.getCode(distCodeTable);
|
||||
code1 = distDecode[code1];
|
||||
code2 = code1 >> 16;
|
||||
if (code2 > 0) {
|
||||
code2 = this.getBits(code2);
|
||||
}
|
||||
const dist = (code1 & 0xffff) + code2;
|
||||
if (pos + len >= limit) {
|
||||
buffer = this.ensureBuffer(pos + len);
|
||||
limit = buffer.length;
|
||||
}
|
||||
for (let k = 0; k < len; ++k, ++pos) {
|
||||
buffer[pos] = buffer[pos - dist];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getBits(bits: number) {
|
||||
const str = this.stream;
|
||||
let codeSize = this.codeSize;
|
||||
let codeBuf = this.codeBuf;
|
||||
|
||||
let b;
|
||||
while (codeSize < bits) {
|
||||
if ((b = str.getByte()) === -1) {
|
||||
throw new Error('Bad encoding in flate stream');
|
||||
}
|
||||
codeBuf |= b << codeSize;
|
||||
codeSize += 8;
|
||||
}
|
||||
b = codeBuf & ((1 << bits) - 1);
|
||||
this.codeBuf = codeBuf >> bits;
|
||||
this.codeSize = codeSize -= bits;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
private getCode(table: [Int32Array, number]) {
|
||||
const str = this.stream;
|
||||
const codes = table[0];
|
||||
const maxLen = table[1];
|
||||
let codeSize = this.codeSize;
|
||||
let codeBuf = this.codeBuf;
|
||||
|
||||
let b;
|
||||
while (codeSize < maxLen) {
|
||||
if ((b = str.getByte()) === -1) {
|
||||
// premature end of stream. code might however still be valid.
|
||||
// codeSize < codeLen check below guards against incomplete codeVal.
|
||||
break;
|
||||
}
|
||||
codeBuf |= b << codeSize;
|
||||
codeSize += 8;
|
||||
}
|
||||
const code = codes[codeBuf & ((1 << maxLen) - 1)];
|
||||
if (typeof codes === 'number') {
|
||||
console.log('FLATE:', code);
|
||||
}
|
||||
const codeLen = code >> 16;
|
||||
const codeVal = code & 0xffff;
|
||||
if (codeLen < 1 || codeSize < codeLen) {
|
||||
throw new Error('Bad encoding in flate stream');
|
||||
}
|
||||
this.codeBuf = codeBuf >> codeLen;
|
||||
this.codeSize = codeSize - codeLen;
|
||||
return codeVal;
|
||||
}
|
||||
|
||||
private generateHuffmanTable(lengths: Uint8Array): [Int32Array, number] {
|
||||
const n = lengths.length;
|
||||
|
||||
// find max code length
|
||||
let maxLen = 0;
|
||||
let i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (lengths[i] > maxLen) {
|
||||
maxLen = lengths[i];
|
||||
}
|
||||
}
|
||||
|
||||
// build the table
|
||||
const size = 1 << maxLen;
|
||||
const codes = new Int32Array(size);
|
||||
for (
|
||||
let len = 1, code = 0, skip = 2;
|
||||
len <= maxLen;
|
||||
++len, code <<= 1, skip <<= 1
|
||||
) {
|
||||
for (let val = 0; val < n; ++val) {
|
||||
if (lengths[val] === len) {
|
||||
// bit-reverse the code
|
||||
let code2 = 0;
|
||||
let t = code;
|
||||
for (i = 0; i < len; ++i) {
|
||||
code2 = (code2 << 1) | (t & 1);
|
||||
t >>= 1;
|
||||
}
|
||||
|
||||
// fill the table entries
|
||||
for (i = code2; i < size; i += skip) {
|
||||
codes[i] = (len << 16) | val;
|
||||
}
|
||||
++code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [codes, maxLen];
|
||||
}
|
||||
}
|
||||
|
||||
export default FlateStream;
|
||||
164
frontend/node_modules/pdf-lib/src/core/streams/LZWStream.ts
generated
vendored
Normal file
164
frontend/node_modules/pdf-lib/src/core/streams/LZWStream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* The LZWStream class contained in this file is a TypeScript port of the
|
||||
* JavaScript LZWStream class in Mozilla's pdf.js project, made available
|
||||
* under the Apache 2.0 open source license.
|
||||
*/
|
||||
|
||||
import DecodeStream from 'src/core/streams/DecodeStream';
|
||||
import { StreamType } from 'src/core/streams/Stream';
|
||||
|
||||
class LZWStream extends DecodeStream {
|
||||
private stream: StreamType;
|
||||
private cachedData: number;
|
||||
private bitsCached: number;
|
||||
private lzwState: {
|
||||
earlyChange: 0 | 1;
|
||||
codeLength: number;
|
||||
nextCode: number;
|
||||
dictionaryValues: Uint8Array;
|
||||
dictionaryLengths: Uint16Array;
|
||||
dictionaryPrevCodes: Uint16Array;
|
||||
currentSequence: Uint8Array;
|
||||
currentSequenceLength: number;
|
||||
prevCode?: number | null;
|
||||
};
|
||||
|
||||
constructor(
|
||||
stream: StreamType,
|
||||
maybeLength: number | undefined,
|
||||
earlyChange: 0 | 1,
|
||||
) {
|
||||
super(maybeLength);
|
||||
|
||||
this.stream = stream;
|
||||
this.cachedData = 0;
|
||||
this.bitsCached = 0;
|
||||
|
||||
const maxLzwDictionarySize = 4096;
|
||||
const lzwState = {
|
||||
earlyChange,
|
||||
codeLength: 9,
|
||||
nextCode: 258,
|
||||
dictionaryValues: new Uint8Array(maxLzwDictionarySize),
|
||||
dictionaryLengths: new Uint16Array(maxLzwDictionarySize),
|
||||
dictionaryPrevCodes: new Uint16Array(maxLzwDictionarySize),
|
||||
currentSequence: new Uint8Array(maxLzwDictionarySize),
|
||||
currentSequenceLength: 0,
|
||||
};
|
||||
for (let i = 0; i < 256; ++i) {
|
||||
lzwState.dictionaryValues[i] = i;
|
||||
lzwState.dictionaryLengths[i] = 1;
|
||||
}
|
||||
this.lzwState = lzwState;
|
||||
}
|
||||
|
||||
protected readBlock() {
|
||||
const blockSize = 512;
|
||||
|
||||
let estimatedDecodedSize = blockSize * 2;
|
||||
const decodedSizeDelta = blockSize;
|
||||
let i;
|
||||
let j;
|
||||
let q;
|
||||
|
||||
const lzwState = this.lzwState;
|
||||
if (!lzwState) {
|
||||
return; // eof was found
|
||||
}
|
||||
|
||||
const earlyChange = lzwState.earlyChange;
|
||||
let nextCode = lzwState.nextCode;
|
||||
const dictionaryValues = lzwState.dictionaryValues;
|
||||
const dictionaryLengths = lzwState.dictionaryLengths;
|
||||
const dictionaryPrevCodes = lzwState.dictionaryPrevCodes;
|
||||
let codeLength = lzwState.codeLength;
|
||||
let prevCode = lzwState.prevCode;
|
||||
const currentSequence = lzwState.currentSequence;
|
||||
let currentSequenceLength = lzwState.currentSequenceLength;
|
||||
|
||||
let decodedLength = 0;
|
||||
let currentBufferLength = this.bufferLength;
|
||||
let buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize);
|
||||
|
||||
for (i = 0; i < blockSize; i++) {
|
||||
const code = this.readBits(codeLength);
|
||||
const hasPrev = currentSequenceLength > 0;
|
||||
if (!code || code < 256) {
|
||||
currentSequence[0] = code as number;
|
||||
currentSequenceLength = 1;
|
||||
} else if (code >= 258) {
|
||||
if (code < nextCode) {
|
||||
currentSequenceLength = dictionaryLengths[code];
|
||||
for (j = currentSequenceLength - 1, q = code; j >= 0; j--) {
|
||||
currentSequence[j] = dictionaryValues[q];
|
||||
q = dictionaryPrevCodes[q];
|
||||
}
|
||||
} else {
|
||||
currentSequence[currentSequenceLength++] = currentSequence[0];
|
||||
}
|
||||
} else if (code === 256) {
|
||||
codeLength = 9;
|
||||
nextCode = 258;
|
||||
currentSequenceLength = 0;
|
||||
continue;
|
||||
} else {
|
||||
this.eof = true;
|
||||
delete this.lzwState;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasPrev) {
|
||||
dictionaryPrevCodes[nextCode] = prevCode as number;
|
||||
dictionaryLengths[nextCode] = dictionaryLengths[prevCode as number] + 1;
|
||||
dictionaryValues[nextCode] = currentSequence[0];
|
||||
nextCode++;
|
||||
codeLength =
|
||||
(nextCode + earlyChange) & (nextCode + earlyChange - 1)
|
||||
? codeLength
|
||||
: Math.min(
|
||||
Math.log(nextCode + earlyChange) / 0.6931471805599453 + 1,
|
||||
12,
|
||||
) | 0;
|
||||
}
|
||||
prevCode = code;
|
||||
|
||||
decodedLength += currentSequenceLength;
|
||||
if (estimatedDecodedSize < decodedLength) {
|
||||
do {
|
||||
estimatedDecodedSize += decodedSizeDelta;
|
||||
} while (estimatedDecodedSize < decodedLength);
|
||||
buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize);
|
||||
}
|
||||
for (j = 0; j < currentSequenceLength; j++) {
|
||||
buffer[currentBufferLength++] = currentSequence[j];
|
||||
}
|
||||
}
|
||||
lzwState.nextCode = nextCode;
|
||||
lzwState.codeLength = codeLength;
|
||||
lzwState.prevCode = prevCode;
|
||||
lzwState.currentSequenceLength = currentSequenceLength;
|
||||
|
||||
this.bufferLength = currentBufferLength;
|
||||
}
|
||||
|
||||
private readBits(n: number) {
|
||||
let bitsCached = this.bitsCached;
|
||||
let cachedData = this.cachedData;
|
||||
while (bitsCached < n) {
|
||||
const c = this.stream.getByte();
|
||||
if (c === -1) {
|
||||
this.eof = true;
|
||||
return null;
|
||||
}
|
||||
cachedData = (cachedData << 8) | c;
|
||||
bitsCached += 8;
|
||||
}
|
||||
this.bitsCached = bitsCached -= n;
|
||||
this.cachedData = cachedData;
|
||||
return (cachedData >>> bitsCached) & ((1 << n) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
export default LZWStream;
|
||||
55
frontend/node_modules/pdf-lib/src/core/streams/RunLengthStream.ts
generated
vendored
Normal file
55
frontend/node_modules/pdf-lib/src/core/streams/RunLengthStream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* The RunLengthStream class contained in this file is a TypeScript port of the
|
||||
* JavaScript RunLengthStream class in Mozilla's pdf.js project, made available
|
||||
* under the Apache 2.0 open source license.
|
||||
*/
|
||||
|
||||
import DecodeStream from 'src/core/streams/DecodeStream';
|
||||
import { StreamType } from 'src/core/streams/Stream';
|
||||
|
||||
class RunLengthStream extends DecodeStream {
|
||||
private stream: StreamType;
|
||||
|
||||
constructor(stream: StreamType, maybeLength?: number) {
|
||||
super(maybeLength);
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
protected readBlock() {
|
||||
// The repeatHeader has following format. The first byte defines type of run
|
||||
// and amount of bytes to repeat/copy: n = 0 through 127 - copy next n bytes
|
||||
// (in addition to the second byte from the header), n = 129 through 255 -
|
||||
// duplicate the second byte from the header (257 - n) times, n = 128 - end.
|
||||
const repeatHeader = this.stream.getBytes(2);
|
||||
if (!repeatHeader || repeatHeader.length < 2 || repeatHeader[0] === 128) {
|
||||
this.eof = true;
|
||||
return;
|
||||
}
|
||||
|
||||
let buffer;
|
||||
let bufferLength = this.bufferLength;
|
||||
let n = repeatHeader[0];
|
||||
if (n < 128) {
|
||||
// copy n bytes
|
||||
buffer = this.ensureBuffer(bufferLength + n + 1);
|
||||
buffer[bufferLength++] = repeatHeader[1];
|
||||
if (n > 0) {
|
||||
const source = this.stream.getBytes(n);
|
||||
buffer.set(source, bufferLength);
|
||||
bufferLength += n;
|
||||
}
|
||||
} else {
|
||||
n = 257 - n;
|
||||
const b = repeatHeader[1];
|
||||
buffer = this.ensureBuffer(bufferLength + n + 1);
|
||||
for (let i = 0; i < n; i++) {
|
||||
buffer[bufferLength++] = b;
|
||||
}
|
||||
}
|
||||
this.bufferLength = bufferLength;
|
||||
}
|
||||
}
|
||||
|
||||
export default RunLengthStream;
|
||||
132
frontend/node_modules/pdf-lib/src/core/streams/Stream.ts
generated
vendored
Normal file
132
frontend/node_modules/pdf-lib/src/core/streams/Stream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* The Stream class contained in this file is a TypeScript port of the
|
||||
* JavaScript Stream class in Mozilla's pdf.js project, made available
|
||||
* under the Apache 2.0 open source license.
|
||||
*/
|
||||
|
||||
export interface StreamType {
|
||||
isEmpty: boolean;
|
||||
getByte(): number;
|
||||
getUint16(): number;
|
||||
getInt32(): number;
|
||||
getBytes(
|
||||
length: number,
|
||||
forceClamped?: boolean,
|
||||
): Uint8Array | Uint8ClampedArray;
|
||||
peekByte(): number;
|
||||
peekBytes(
|
||||
length: number,
|
||||
forceClamped?: boolean,
|
||||
): Uint8Array | Uint8ClampedArray;
|
||||
skip(n: number): void;
|
||||
reset(): void;
|
||||
makeSubStream(start: number, length: number): StreamType;
|
||||
decode(): Uint8Array;
|
||||
}
|
||||
|
||||
class Stream implements StreamType {
|
||||
private bytes: Uint8Array;
|
||||
private start: number;
|
||||
private pos: number;
|
||||
private end: number;
|
||||
|
||||
constructor(buffer: Uint8Array, start?: number, length?: number) {
|
||||
this.bytes = buffer;
|
||||
this.start = start || 0;
|
||||
this.pos = this.start;
|
||||
this.end = !!start && !!length ? start + length : this.bytes.length;
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.end - this.start;
|
||||
}
|
||||
|
||||
get isEmpty() {
|
||||
return this.length === 0;
|
||||
}
|
||||
|
||||
getByte() {
|
||||
if (this.pos >= this.end) {
|
||||
return -1;
|
||||
}
|
||||
return this.bytes[this.pos++];
|
||||
}
|
||||
|
||||
getUint16() {
|
||||
const b0 = this.getByte();
|
||||
const b1 = this.getByte();
|
||||
if (b0 === -1 || b1 === -1) {
|
||||
return -1;
|
||||
}
|
||||
return (b0 << 8) + b1;
|
||||
}
|
||||
|
||||
getInt32() {
|
||||
const b0 = this.getByte();
|
||||
const b1 = this.getByte();
|
||||
const b2 = this.getByte();
|
||||
const b3 = this.getByte();
|
||||
return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
|
||||
}
|
||||
|
||||
// Returns subarray of original buffer, should only be read.
|
||||
getBytes(length: number, forceClamped = false) {
|
||||
const bytes = this.bytes;
|
||||
const pos = this.pos;
|
||||
const strEnd = this.end;
|
||||
|
||||
if (!length) {
|
||||
const subarray = bytes.subarray(pos, strEnd);
|
||||
// `this.bytes` is always a `Uint8Array` here.
|
||||
return forceClamped ? new Uint8ClampedArray(subarray) : subarray;
|
||||
} else {
|
||||
let end = pos + length;
|
||||
if (end > strEnd) {
|
||||
end = strEnd;
|
||||
}
|
||||
this.pos = end;
|
||||
const subarray = bytes.subarray(pos, end);
|
||||
// `this.bytes` is always a `Uint8Array` here.
|
||||
return forceClamped ? new Uint8ClampedArray(subarray) : subarray;
|
||||
}
|
||||
}
|
||||
|
||||
peekByte() {
|
||||
const peekedByte = this.getByte();
|
||||
this.pos--;
|
||||
return peekedByte;
|
||||
}
|
||||
|
||||
peekBytes(length: number, forceClamped = false) {
|
||||
const bytes = this.getBytes(length, forceClamped);
|
||||
this.pos -= bytes.length;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
skip(n: number) {
|
||||
if (!n) {
|
||||
n = 1;
|
||||
}
|
||||
this.pos += n;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.pos = this.start;
|
||||
}
|
||||
|
||||
moveStart() {
|
||||
this.start = this.pos;
|
||||
}
|
||||
|
||||
makeSubStream(start: number, length: number) {
|
||||
return new Stream(this.bytes, start, length);
|
||||
}
|
||||
|
||||
decode(): Uint8Array {
|
||||
return this.bytes;
|
||||
}
|
||||
}
|
||||
|
||||
export default Stream;
|
||||
73
frontend/node_modules/pdf-lib/src/core/streams/decode.ts
generated
vendored
Normal file
73
frontend/node_modules/pdf-lib/src/core/streams/decode.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import {
|
||||
UnexpectedObjectTypeError,
|
||||
UnsupportedEncodingError,
|
||||
} from 'src/core/errors';
|
||||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNull from 'src/core/objects/PDFNull';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFRawStream from 'src/core/objects/PDFRawStream';
|
||||
import Ascii85Stream from 'src/core/streams/Ascii85Stream';
|
||||
import AsciiHexStream from 'src/core/streams/AsciiHexStream';
|
||||
import FlateStream from 'src/core/streams/FlateStream';
|
||||
import LZWStream from 'src/core/streams/LZWStream';
|
||||
import RunLengthStream from 'src/core/streams/RunLengthStream';
|
||||
import Stream, { StreamType } from 'src/core/streams/Stream';
|
||||
|
||||
const decodeStream = (
|
||||
stream: StreamType,
|
||||
encoding: PDFName,
|
||||
params: undefined | typeof PDFNull | PDFDict,
|
||||
) => {
|
||||
if (encoding === PDFName.of('FlateDecode')) {
|
||||
return new FlateStream(stream);
|
||||
}
|
||||
if (encoding === PDFName.of('LZWDecode')) {
|
||||
let earlyChange = 1;
|
||||
if (params instanceof PDFDict) {
|
||||
const EarlyChange = params.lookup(PDFName.of('EarlyChange'));
|
||||
if (EarlyChange instanceof PDFNumber) {
|
||||
earlyChange = EarlyChange.asNumber();
|
||||
}
|
||||
}
|
||||
return new LZWStream(stream, undefined, earlyChange as 0 | 1);
|
||||
}
|
||||
if (encoding === PDFName.of('ASCII85Decode')) {
|
||||
return new Ascii85Stream(stream);
|
||||
}
|
||||
if (encoding === PDFName.of('ASCIIHexDecode')) {
|
||||
return new AsciiHexStream(stream);
|
||||
}
|
||||
if (encoding === PDFName.of('RunLengthDecode')) {
|
||||
return new RunLengthStream(stream);
|
||||
}
|
||||
throw new UnsupportedEncodingError(encoding.asString());
|
||||
};
|
||||
|
||||
export const decodePDFRawStream = ({ dict, contents }: PDFRawStream) => {
|
||||
let stream: StreamType = new Stream(contents);
|
||||
|
||||
const Filter = dict.lookup(PDFName.of('Filter'));
|
||||
const DecodeParms = dict.lookup(PDFName.of('DecodeParms'));
|
||||
|
||||
if (Filter instanceof PDFName) {
|
||||
stream = decodeStream(
|
||||
stream,
|
||||
Filter,
|
||||
DecodeParms as PDFDict | typeof PDFNull | undefined,
|
||||
);
|
||||
} else if (Filter instanceof PDFArray) {
|
||||
for (let idx = 0, len = Filter.size(); idx < len; idx++) {
|
||||
stream = decodeStream(
|
||||
stream,
|
||||
Filter.lookup(idx, PDFName),
|
||||
DecodeParms && (DecodeParms as PDFArray).lookupMaybe(idx, PDFDict),
|
||||
);
|
||||
}
|
||||
} else if (!!Filter) {
|
||||
throw new UnexpectedObjectTypeError([PDFName, PDFArray], Filter);
|
||||
}
|
||||
|
||||
return stream;
|
||||
};
|
||||
85
frontend/node_modules/pdf-lib/src/core/structures/PDFCatalog.ts
generated
vendored
Normal file
85
frontend/node_modules/pdf-lib/src/core/structures/PDFCatalog.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import PDFDict, { DictMap } from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFPageTree from 'src/core/structures/PDFPageTree';
|
||||
import { PDFAcroForm } from 'src/core/acroform';
|
||||
import ViewerPreferences from '../interactive/ViewerPreferences';
|
||||
|
||||
class PDFCatalog extends PDFDict {
|
||||
static withContextAndPages = (
|
||||
context: PDFContext,
|
||||
pages: PDFPageTree | PDFRef,
|
||||
) => {
|
||||
const dict = new Map();
|
||||
dict.set(PDFName.of('Type'), PDFName.of('Catalog'));
|
||||
dict.set(PDFName.of('Pages'), pages);
|
||||
return new PDFCatalog(dict, context);
|
||||
};
|
||||
|
||||
static fromMapWithContext = (map: DictMap, context: PDFContext) =>
|
||||
new PDFCatalog(map, context);
|
||||
|
||||
Pages(): PDFPageTree {
|
||||
return this.lookup(PDFName.of('Pages'), PDFDict) as PDFPageTree;
|
||||
}
|
||||
|
||||
AcroForm(): PDFDict | undefined {
|
||||
return this.lookupMaybe(PDFName.of('AcroForm'), PDFDict);
|
||||
}
|
||||
|
||||
getAcroForm(): PDFAcroForm | undefined {
|
||||
const dict = this.AcroForm();
|
||||
if (!dict) return undefined;
|
||||
return PDFAcroForm.fromDict(dict);
|
||||
}
|
||||
|
||||
getOrCreateAcroForm(): PDFAcroForm {
|
||||
let acroForm = this.getAcroForm();
|
||||
if (!acroForm) {
|
||||
acroForm = PDFAcroForm.create(this.context);
|
||||
const acroFormRef = this.context.register(acroForm.dict);
|
||||
this.set(PDFName.of('AcroForm'), acroFormRef);
|
||||
}
|
||||
return acroForm;
|
||||
}
|
||||
|
||||
ViewerPreferences(): PDFDict | undefined {
|
||||
return this.lookupMaybe(PDFName.of('ViewerPreferences'), PDFDict);
|
||||
}
|
||||
|
||||
getViewerPreferences(): ViewerPreferences | undefined {
|
||||
const dict = this.ViewerPreferences();
|
||||
if (!dict) return undefined;
|
||||
return ViewerPreferences.fromDict(dict);
|
||||
}
|
||||
|
||||
getOrCreateViewerPreferences(): ViewerPreferences {
|
||||
let viewerPrefs = this.getViewerPreferences();
|
||||
if (!viewerPrefs) {
|
||||
viewerPrefs = ViewerPreferences.create(this.context);
|
||||
const viewerPrefsRef = this.context.register(viewerPrefs.dict);
|
||||
this.set(PDFName.of('ViewerPreferences'), viewerPrefsRef);
|
||||
}
|
||||
return viewerPrefs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the given ref as a leaf node of this catalog's page tree at the
|
||||
* specified index (zero-based). Also increments the `Count` of each node in
|
||||
* the page tree hierarchy to accomodate the new page.
|
||||
*
|
||||
* Returns the ref of the PDFPageTree node into which `leafRef` was inserted.
|
||||
*/
|
||||
insertLeafNode(leafRef: PDFRef, index: number): PDFRef {
|
||||
const pagesRef = this.get(PDFName.of('Pages')) as PDFRef;
|
||||
const maybeParentRef = this.Pages().insertLeafNode(leafRef, index);
|
||||
return maybeParentRef || pagesRef;
|
||||
}
|
||||
|
||||
removeLeafNode(index: number): void {
|
||||
this.Pages().removeLeafNode(index);
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFCatalog;
|
||||
58
frontend/node_modules/pdf-lib/src/core/structures/PDFContentStream.ts
generated
vendored
Normal file
58
frontend/node_modules/pdf-lib/src/core/structures/PDFContentStream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFOperator from 'src/core/operators/PDFOperator';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFFlateStream from 'src/core/structures/PDFFlateStream';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
|
||||
class PDFContentStream extends PDFFlateStream {
|
||||
static of = (dict: PDFDict, operators: PDFOperator[], encode = true) =>
|
||||
new PDFContentStream(dict, operators, encode);
|
||||
|
||||
private readonly operators: PDFOperator[];
|
||||
|
||||
private constructor(dict: PDFDict, operators: PDFOperator[], encode = true) {
|
||||
super(dict, encode);
|
||||
this.operators = operators;
|
||||
}
|
||||
|
||||
push(...operators: PDFOperator[]): void {
|
||||
this.operators.push(...operators);
|
||||
}
|
||||
|
||||
clone(context?: PDFContext): PDFContentStream {
|
||||
const operators = new Array(this.operators.length);
|
||||
for (let idx = 0, len = this.operators.length; idx < len; idx++) {
|
||||
operators[idx] = this.operators[idx].clone(context);
|
||||
}
|
||||
const { dict, encode } = this;
|
||||
return PDFContentStream.of(dict.clone(context), operators, encode);
|
||||
}
|
||||
|
||||
getContentsString(): string {
|
||||
let value = '';
|
||||
for (let idx = 0, len = this.operators.length; idx < len; idx++) {
|
||||
value += `${this.operators[idx]}\n`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
getUnencodedContents(): Uint8Array {
|
||||
const buffer = new Uint8Array(this.getUnencodedContentsSize());
|
||||
let offset = 0;
|
||||
for (let idx = 0, len = this.operators.length; idx < len; idx++) {
|
||||
offset += this.operators[idx].copyBytesInto(buffer, offset);
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
getUnencodedContentsSize(): number {
|
||||
let size = 0;
|
||||
for (let idx = 0, len = this.operators.length; idx < len; idx++) {
|
||||
size += this.operators[idx].sizeInBytes() + 1;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFContentStream;
|
||||
246
frontend/node_modules/pdf-lib/src/core/structures/PDFCrossRefStream.ts
generated
vendored
Normal file
246
frontend/node_modules/pdf-lib/src/core/structures/PDFCrossRefStream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFFlateStream from 'src/core/structures/PDFFlateStream';
|
||||
import { bytesFor, Cache, reverseArray, sizeInBytes, sum } from 'src/utils';
|
||||
|
||||
export enum EntryType {
|
||||
Deleted = 0,
|
||||
Uncompressed = 1,
|
||||
Compressed = 2,
|
||||
}
|
||||
|
||||
export interface DeletedEntry {
|
||||
type: EntryType.Deleted;
|
||||
ref: PDFRef;
|
||||
nextFreeObjectNumber: number;
|
||||
}
|
||||
|
||||
export interface UncompressedEntry {
|
||||
type: EntryType.Uncompressed;
|
||||
ref: PDFRef;
|
||||
offset: number;
|
||||
}
|
||||
|
||||
export interface CompressedEntry {
|
||||
type: EntryType.Compressed;
|
||||
ref: PDFRef;
|
||||
objectStreamRef: PDFRef;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export type Entry = DeletedEntry | UncompressedEntry | CompressedEntry;
|
||||
|
||||
export type EntryTuple = [number, number, number];
|
||||
|
||||
/**
|
||||
* Entries should be added using the [[addDeletedEntry]],
|
||||
* [[addUncompressedEntry]], and [[addCompressedEntry]] methods
|
||||
* **in order of ascending object number**.
|
||||
*/
|
||||
class PDFCrossRefStream extends PDFFlateStream {
|
||||
static create = (dict: PDFDict, encode = true) => {
|
||||
const stream = new PDFCrossRefStream(dict, [], encode);
|
||||
stream.addDeletedEntry(PDFRef.of(0, 65535), 0);
|
||||
return stream;
|
||||
};
|
||||
|
||||
static of = (dict: PDFDict, entries: Entry[], encode = true) =>
|
||||
new PDFCrossRefStream(dict, entries, encode);
|
||||
|
||||
private readonly entries: Entry[];
|
||||
private readonly entryTuplesCache: Cache<EntryTuple[]>;
|
||||
private readonly maxByteWidthsCache: Cache<[number, number, number]>;
|
||||
private readonly indexCache: Cache<number[]>;
|
||||
|
||||
private constructor(dict: PDFDict, entries?: Entry[], encode = true) {
|
||||
super(dict, encode);
|
||||
|
||||
this.entries = entries || [];
|
||||
this.entryTuplesCache = Cache.populatedBy(this.computeEntryTuples);
|
||||
this.maxByteWidthsCache = Cache.populatedBy(this.computeMaxEntryByteWidths);
|
||||
this.indexCache = Cache.populatedBy(this.computeIndex);
|
||||
|
||||
dict.set(PDFName.of('Type'), PDFName.of('XRef'));
|
||||
}
|
||||
|
||||
addDeletedEntry(ref: PDFRef, nextFreeObjectNumber: number) {
|
||||
const type = EntryType.Deleted;
|
||||
this.entries.push({ type, ref, nextFreeObjectNumber });
|
||||
this.entryTuplesCache.invalidate();
|
||||
this.maxByteWidthsCache.invalidate();
|
||||
this.indexCache.invalidate();
|
||||
this.contentsCache.invalidate();
|
||||
}
|
||||
|
||||
addUncompressedEntry(ref: PDFRef, offset: number) {
|
||||
const type = EntryType.Uncompressed;
|
||||
this.entries.push({ type, ref, offset });
|
||||
this.entryTuplesCache.invalidate();
|
||||
this.maxByteWidthsCache.invalidate();
|
||||
this.indexCache.invalidate();
|
||||
this.contentsCache.invalidate();
|
||||
}
|
||||
|
||||
addCompressedEntry(ref: PDFRef, objectStreamRef: PDFRef, index: number) {
|
||||
const type = EntryType.Compressed;
|
||||
this.entries.push({ type, ref, objectStreamRef, index });
|
||||
this.entryTuplesCache.invalidate();
|
||||
this.maxByteWidthsCache.invalidate();
|
||||
this.indexCache.invalidate();
|
||||
this.contentsCache.invalidate();
|
||||
}
|
||||
|
||||
clone(context?: PDFContext): PDFCrossRefStream {
|
||||
const { dict, entries, encode } = this;
|
||||
return PDFCrossRefStream.of(dict.clone(context), entries.slice(), encode);
|
||||
}
|
||||
|
||||
getContentsString(): string {
|
||||
const entryTuples = this.entryTuplesCache.access();
|
||||
const byteWidths = this.maxByteWidthsCache.access();
|
||||
let value = '';
|
||||
|
||||
for (
|
||||
let entryIdx = 0, entriesLen = entryTuples.length;
|
||||
entryIdx < entriesLen;
|
||||
entryIdx++
|
||||
) {
|
||||
const [first, second, third] = entryTuples[entryIdx];
|
||||
|
||||
const firstBytes = reverseArray(bytesFor(first));
|
||||
const secondBytes = reverseArray(bytesFor(second));
|
||||
const thirdBytes = reverseArray(bytesFor(third));
|
||||
|
||||
for (let idx = byteWidths[0] - 1; idx >= 0; idx--) {
|
||||
value += (firstBytes[idx] || 0).toString(2);
|
||||
}
|
||||
for (let idx = byteWidths[1] - 1; idx >= 0; idx--) {
|
||||
value += (secondBytes[idx] || 0).toString(2);
|
||||
}
|
||||
for (let idx = byteWidths[2] - 1; idx >= 0; idx--) {
|
||||
value += (thirdBytes[idx] || 0).toString(2);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
getUnencodedContents(): Uint8Array {
|
||||
const entryTuples = this.entryTuplesCache.access();
|
||||
const byteWidths = this.maxByteWidthsCache.access();
|
||||
const buffer = new Uint8Array(this.getUnencodedContentsSize());
|
||||
|
||||
let offset = 0;
|
||||
for (
|
||||
let entryIdx = 0, entriesLen = entryTuples.length;
|
||||
entryIdx < entriesLen;
|
||||
entryIdx++
|
||||
) {
|
||||
const [first, second, third] = entryTuples[entryIdx];
|
||||
|
||||
const firstBytes = reverseArray(bytesFor(first));
|
||||
const secondBytes = reverseArray(bytesFor(second));
|
||||
const thirdBytes = reverseArray(bytesFor(third));
|
||||
|
||||
for (let idx = byteWidths[0] - 1; idx >= 0; idx--) {
|
||||
buffer[offset++] = firstBytes[idx] || 0;
|
||||
}
|
||||
for (let idx = byteWidths[1] - 1; idx >= 0; idx--) {
|
||||
buffer[offset++] = secondBytes[idx] || 0;
|
||||
}
|
||||
for (let idx = byteWidths[2] - 1; idx >= 0; idx--) {
|
||||
buffer[offset++] = thirdBytes[idx] || 0;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
getUnencodedContentsSize(): number {
|
||||
const byteWidths = this.maxByteWidthsCache.access();
|
||||
const entryWidth = sum(byteWidths);
|
||||
return entryWidth * this.entries.length;
|
||||
}
|
||||
|
||||
updateDict(): void {
|
||||
super.updateDict();
|
||||
|
||||
const byteWidths = this.maxByteWidthsCache.access();
|
||||
const index = this.indexCache.access();
|
||||
|
||||
const { context } = this.dict;
|
||||
this.dict.set(PDFName.of('W'), context.obj(byteWidths));
|
||||
this.dict.set(PDFName.of('Index'), context.obj(index));
|
||||
}
|
||||
|
||||
// Returns an array of integer pairs for each subsection of the cross ref
|
||||
// section, where each integer pair represents:
|
||||
// firstObjectNumber(OfSection), length(OfSection)
|
||||
private computeIndex = (): number[] => {
|
||||
const subsections: number[] = [];
|
||||
|
||||
let subsectionLength = 0;
|
||||
for (let idx = 0, len = this.entries.length; idx < len; idx++) {
|
||||
const currEntry = this.entries[idx];
|
||||
const prevEntry = this.entries[idx - 1];
|
||||
|
||||
if (idx === 0) {
|
||||
subsections.push(currEntry.ref.objectNumber);
|
||||
} else if (currEntry.ref.objectNumber - prevEntry.ref.objectNumber > 1) {
|
||||
subsections.push(subsectionLength);
|
||||
subsections.push(currEntry.ref.objectNumber);
|
||||
subsectionLength = 0;
|
||||
}
|
||||
|
||||
subsectionLength += 1;
|
||||
}
|
||||
subsections.push(subsectionLength);
|
||||
|
||||
return subsections;
|
||||
};
|
||||
|
||||
private computeEntryTuples = (): EntryTuple[] => {
|
||||
const entryTuples: EntryTuple[] = new Array(this.entries.length);
|
||||
|
||||
for (let idx = 0, len = this.entries.length; idx < len; idx++) {
|
||||
const entry = this.entries[idx];
|
||||
if (entry.type === EntryType.Deleted) {
|
||||
const { type, nextFreeObjectNumber, ref } = entry;
|
||||
entryTuples[idx] = [type, nextFreeObjectNumber, ref.generationNumber];
|
||||
}
|
||||
if (entry.type === EntryType.Uncompressed) {
|
||||
const { type, offset, ref } = entry;
|
||||
entryTuples[idx] = [type, offset, ref.generationNumber];
|
||||
}
|
||||
if (entry.type === EntryType.Compressed) {
|
||||
const { type, objectStreamRef, index } = entry;
|
||||
entryTuples[idx] = [type, objectStreamRef.objectNumber, index];
|
||||
}
|
||||
}
|
||||
|
||||
return entryTuples;
|
||||
};
|
||||
|
||||
private computeMaxEntryByteWidths = (): [number, number, number] => {
|
||||
const entryTuples = this.entryTuplesCache.access();
|
||||
const widths: [number, number, number] = [0, 0, 0];
|
||||
|
||||
for (let idx = 0, len = entryTuples.length; idx < len; idx++) {
|
||||
const [first, second, third] = entryTuples[idx];
|
||||
|
||||
const firstSize = sizeInBytes(first);
|
||||
const secondSize = sizeInBytes(second);
|
||||
const thirdSize = sizeInBytes(third);
|
||||
|
||||
if (firstSize > widths[0]) widths[0] = firstSize;
|
||||
if (secondSize > widths[1]) widths[1] = secondSize;
|
||||
if (thirdSize > widths[2]) widths[2] = thirdSize;
|
||||
}
|
||||
|
||||
return widths;
|
||||
};
|
||||
}
|
||||
|
||||
export default PDFCrossRefStream;
|
||||
43
frontend/node_modules/pdf-lib/src/core/structures/PDFFlateStream.ts
generated
vendored
Normal file
43
frontend/node_modules/pdf-lib/src/core/structures/PDFFlateStream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import pako from 'pako';
|
||||
|
||||
import { MethodNotImplementedError } from 'src/core/errors';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFStream from 'src/core/objects/PDFStream';
|
||||
import { Cache } from 'src/utils';
|
||||
|
||||
class PDFFlateStream extends PDFStream {
|
||||
protected readonly contentsCache: Cache<Uint8Array>;
|
||||
protected readonly encode: boolean;
|
||||
|
||||
constructor(dict: PDFDict, encode: boolean) {
|
||||
super(dict);
|
||||
|
||||
this.encode = encode;
|
||||
|
||||
if (encode) dict.set(PDFName.of('Filter'), PDFName.of('FlateDecode'));
|
||||
this.contentsCache = Cache.populatedBy(this.computeContents);
|
||||
}
|
||||
|
||||
computeContents = (): Uint8Array => {
|
||||
const unencodedContents = this.getUnencodedContents();
|
||||
return this.encode ? pako.deflate(unencodedContents) : unencodedContents;
|
||||
};
|
||||
|
||||
getContents(): Uint8Array {
|
||||
return this.contentsCache.access();
|
||||
}
|
||||
|
||||
getContentsSize(): number {
|
||||
return this.contentsCache.access().length;
|
||||
}
|
||||
|
||||
getUnencodedContents(): Uint8Array {
|
||||
throw new MethodNotImplementedError(
|
||||
this.constructor.name,
|
||||
'getUnencodedContents',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFFlateStream;
|
||||
101
frontend/node_modules/pdf-lib/src/core/structures/PDFObjectStream.ts
generated
vendored
Normal file
101
frontend/node_modules/pdf-lib/src/core/structures/PDFObjectStream.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFFlateStream from 'src/core/structures/PDFFlateStream';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import { copyStringIntoBuffer, last } from 'src/utils';
|
||||
|
||||
export type IndirectObject = [PDFRef, PDFObject];
|
||||
|
||||
class PDFObjectStream extends PDFFlateStream {
|
||||
static withContextAndObjects = (
|
||||
context: PDFContext,
|
||||
objects: IndirectObject[],
|
||||
encode = true,
|
||||
) => new PDFObjectStream(context, objects, encode);
|
||||
|
||||
private readonly objects: IndirectObject[];
|
||||
private readonly offsets: [number, number][];
|
||||
private readonly offsetsString: string;
|
||||
|
||||
private constructor(
|
||||
context: PDFContext,
|
||||
objects: IndirectObject[],
|
||||
encode = true,
|
||||
) {
|
||||
super(context.obj({}), encode);
|
||||
|
||||
this.objects = objects;
|
||||
this.offsets = this.computeObjectOffsets();
|
||||
this.offsetsString = this.computeOffsetsString();
|
||||
|
||||
this.dict.set(PDFName.of('Type'), PDFName.of('ObjStm'));
|
||||
this.dict.set(PDFName.of('N'), PDFNumber.of(this.objects.length));
|
||||
this.dict.set(PDFName.of('First'), PDFNumber.of(this.offsetsString.length));
|
||||
}
|
||||
|
||||
getObjectsCount(): number {
|
||||
return this.objects.length;
|
||||
}
|
||||
|
||||
clone(context?: PDFContext): PDFObjectStream {
|
||||
return PDFObjectStream.withContextAndObjects(
|
||||
context || this.dict.context,
|
||||
this.objects.slice(),
|
||||
this.encode,
|
||||
);
|
||||
}
|
||||
|
||||
getContentsString(): string {
|
||||
let value = this.offsetsString;
|
||||
for (let idx = 0, len = this.objects.length; idx < len; idx++) {
|
||||
const [, object] = this.objects[idx];
|
||||
value += `${object}\n`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
getUnencodedContents(): Uint8Array {
|
||||
const buffer = new Uint8Array(this.getUnencodedContentsSize());
|
||||
let offset = copyStringIntoBuffer(this.offsetsString, buffer, 0);
|
||||
for (let idx = 0, len = this.objects.length; idx < len; idx++) {
|
||||
const [, object] = this.objects[idx];
|
||||
offset += object.copyBytesInto(buffer, offset);
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
getUnencodedContentsSize(): number {
|
||||
return (
|
||||
this.offsetsString.length +
|
||||
last(this.offsets)[1] +
|
||||
last(this.objects)[1].sizeInBytes() +
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
private computeOffsetsString(): string {
|
||||
let offsetsString = '';
|
||||
for (let idx = 0, len = this.offsets.length; idx < len; idx++) {
|
||||
const [objectNumber, offset] = this.offsets[idx];
|
||||
offsetsString += `${objectNumber} ${offset} `;
|
||||
}
|
||||
return offsetsString;
|
||||
}
|
||||
|
||||
private computeObjectOffsets(): [number, number][] {
|
||||
let offset = 0;
|
||||
const offsets = new Array(this.objects.length);
|
||||
for (let idx = 0, len = this.objects.length; idx < len; idx++) {
|
||||
const [ref, object] = this.objects[idx];
|
||||
offsets[idx] = [ref.objectNumber, offset];
|
||||
offset += object.sizeInBytes() + 1; // '\n'
|
||||
}
|
||||
return offsets;
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFObjectStream;
|
||||
263
frontend/node_modules/pdf-lib/src/core/structures/PDFPageLeaf.ts
generated
vendored
Normal file
263
frontend/node_modules/pdf-lib/src/core/structures/PDFPageLeaf.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFDict, { DictMap } from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFStream from 'src/core/objects/PDFStream';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFPageTree from 'src/core/structures/PDFPageTree';
|
||||
|
||||
class PDFPageLeaf extends PDFDict {
|
||||
static readonly InheritableEntries = [
|
||||
'Resources',
|
||||
'MediaBox',
|
||||
'CropBox',
|
||||
'Rotate',
|
||||
];
|
||||
|
||||
static withContextAndParent = (context: PDFContext, parent: PDFRef) => {
|
||||
const dict = new Map();
|
||||
dict.set(PDFName.Type, PDFName.Page);
|
||||
dict.set(PDFName.Parent, parent);
|
||||
dict.set(PDFName.Resources, context.obj({}));
|
||||
dict.set(PDFName.MediaBox, context.obj([0, 0, 612, 792]));
|
||||
return new PDFPageLeaf(dict, context, false);
|
||||
};
|
||||
|
||||
static fromMapWithContext = (
|
||||
map: DictMap,
|
||||
context: PDFContext,
|
||||
autoNormalizeCTM = true,
|
||||
) => new PDFPageLeaf(map, context, autoNormalizeCTM);
|
||||
|
||||
private normalized = false;
|
||||
private readonly autoNormalizeCTM: boolean;
|
||||
|
||||
private constructor(
|
||||
map: DictMap,
|
||||
context: PDFContext,
|
||||
autoNormalizeCTM = true,
|
||||
) {
|
||||
super(map, context);
|
||||
this.autoNormalizeCTM = autoNormalizeCTM;
|
||||
}
|
||||
|
||||
clone(context?: PDFContext): PDFPageLeaf {
|
||||
const clone = PDFPageLeaf.fromMapWithContext(
|
||||
new Map(),
|
||||
context || this.context,
|
||||
this.autoNormalizeCTM,
|
||||
);
|
||||
const entries = this.entries();
|
||||
for (let idx = 0, len = entries.length; idx < len; idx++) {
|
||||
const [key, value] = entries[idx];
|
||||
clone.set(key, value);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
Parent(): PDFPageTree | undefined {
|
||||
return this.lookupMaybe(PDFName.Parent, PDFDict) as PDFPageTree | undefined;
|
||||
}
|
||||
|
||||
Contents(): PDFStream | PDFArray | undefined {
|
||||
return this.lookup(PDFName.of('Contents')) as
|
||||
| PDFStream
|
||||
| PDFArray
|
||||
| undefined;
|
||||
}
|
||||
|
||||
Annots(): PDFArray | undefined {
|
||||
return this.lookupMaybe(PDFName.Annots, PDFArray);
|
||||
}
|
||||
|
||||
BleedBox(): PDFArray | undefined {
|
||||
return this.lookupMaybe(PDFName.BleedBox, PDFArray);
|
||||
}
|
||||
|
||||
TrimBox(): PDFArray | undefined {
|
||||
return this.lookupMaybe(PDFName.TrimBox, PDFArray);
|
||||
}
|
||||
|
||||
ArtBox(): PDFArray | undefined {
|
||||
return this.lookupMaybe(PDFName.ArtBox, PDFArray);
|
||||
}
|
||||
|
||||
Resources(): PDFDict | undefined {
|
||||
const dictOrRef = this.getInheritableAttribute(PDFName.Resources);
|
||||
return this.context.lookupMaybe(dictOrRef, PDFDict);
|
||||
}
|
||||
|
||||
MediaBox(): PDFArray {
|
||||
const arrayOrRef = this.getInheritableAttribute(PDFName.MediaBox);
|
||||
return this.context.lookup(arrayOrRef, PDFArray);
|
||||
}
|
||||
|
||||
CropBox(): PDFArray | undefined {
|
||||
const arrayOrRef = this.getInheritableAttribute(PDFName.CropBox);
|
||||
return this.context.lookupMaybe(arrayOrRef, PDFArray);
|
||||
}
|
||||
|
||||
Rotate(): PDFNumber | undefined {
|
||||
const numberOrRef = this.getInheritableAttribute(PDFName.Rotate);
|
||||
return this.context.lookupMaybe(numberOrRef, PDFNumber);
|
||||
}
|
||||
|
||||
getInheritableAttribute(name: PDFName): PDFObject | undefined {
|
||||
let attribute: PDFObject | undefined;
|
||||
this.ascend((node) => {
|
||||
if (!attribute) attribute = node.get(name);
|
||||
});
|
||||
return attribute;
|
||||
}
|
||||
|
||||
setParent(parentRef: PDFRef): void {
|
||||
this.set(PDFName.Parent, parentRef);
|
||||
}
|
||||
|
||||
addContentStream(contentStreamRef: PDFRef): void {
|
||||
const Contents = this.normalizedEntries().Contents || this.context.obj([]);
|
||||
this.set(PDFName.Contents, Contents);
|
||||
Contents.push(contentStreamRef);
|
||||
}
|
||||
|
||||
wrapContentStreams(startStream: PDFRef, endStream: PDFRef): boolean {
|
||||
const Contents = this.Contents();
|
||||
if (Contents instanceof PDFArray) {
|
||||
Contents.insert(0, startStream);
|
||||
Contents.push(endStream);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
addAnnot(annotRef: PDFRef): void {
|
||||
const { Annots } = this.normalizedEntries();
|
||||
Annots.push(annotRef);
|
||||
}
|
||||
|
||||
removeAnnot(annotRef: PDFRef) {
|
||||
const { Annots } = this.normalizedEntries();
|
||||
const index = Annots.indexOf(annotRef);
|
||||
if (index !== undefined) {
|
||||
Annots.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
setFontDictionary(name: PDFName, fontDictRef: PDFRef): void {
|
||||
const { Font } = this.normalizedEntries();
|
||||
Font.set(name, fontDictRef);
|
||||
}
|
||||
|
||||
newFontDictionaryKey(tag: string): PDFName {
|
||||
const { Font } = this.normalizedEntries();
|
||||
return Font.uniqueKey(tag);
|
||||
}
|
||||
|
||||
newFontDictionary(tag: string, fontDictRef: PDFRef): PDFName {
|
||||
const key = this.newFontDictionaryKey(tag);
|
||||
this.setFontDictionary(key, fontDictRef);
|
||||
return key;
|
||||
}
|
||||
|
||||
setXObject(name: PDFName, xObjectRef: PDFRef): void {
|
||||
const { XObject } = this.normalizedEntries();
|
||||
XObject.set(name, xObjectRef);
|
||||
}
|
||||
|
||||
newXObjectKey(tag: string): PDFName {
|
||||
const { XObject } = this.normalizedEntries();
|
||||
return XObject.uniqueKey(tag);
|
||||
}
|
||||
|
||||
newXObject(tag: string, xObjectRef: PDFRef): PDFName {
|
||||
const key = this.newXObjectKey(tag);
|
||||
this.setXObject(key, xObjectRef);
|
||||
return key;
|
||||
}
|
||||
|
||||
setExtGState(name: PDFName, extGStateRef: PDFRef | PDFDict): void {
|
||||
const { ExtGState } = this.normalizedEntries();
|
||||
ExtGState.set(name, extGStateRef);
|
||||
}
|
||||
|
||||
newExtGStateKey(tag: string): PDFName {
|
||||
const { ExtGState } = this.normalizedEntries();
|
||||
return ExtGState.uniqueKey(tag);
|
||||
}
|
||||
|
||||
newExtGState(tag: string, extGStateRef: PDFRef | PDFDict): PDFName {
|
||||
const key = this.newExtGStateKey(tag);
|
||||
this.setExtGState(key, extGStateRef);
|
||||
return key;
|
||||
}
|
||||
|
||||
ascend(visitor: (node: PDFPageTree | PDFPageLeaf) => any): void {
|
||||
visitor(this);
|
||||
const Parent = this.Parent();
|
||||
if (Parent) Parent.ascend(visitor);
|
||||
}
|
||||
|
||||
normalize() {
|
||||
if (this.normalized) return;
|
||||
|
||||
const { context } = this;
|
||||
|
||||
const contentsRef = this.get(PDFName.Contents);
|
||||
const contents = this.context.lookup(contentsRef);
|
||||
if (contents instanceof PDFStream) {
|
||||
this.set(PDFName.Contents, context.obj([contentsRef]));
|
||||
}
|
||||
|
||||
if (this.autoNormalizeCTM) {
|
||||
this.wrapContentStreams(
|
||||
this.context.getPushGraphicsStateContentStream(),
|
||||
this.context.getPopGraphicsStateContentStream(),
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Clone `Resources` if it is inherited
|
||||
const dictOrRef = this.getInheritableAttribute(PDFName.Resources);
|
||||
const Resources =
|
||||
context.lookupMaybe(dictOrRef, PDFDict) || context.obj({});
|
||||
this.set(PDFName.Resources, Resources);
|
||||
|
||||
// TODO: Clone `Font` if it is inherited
|
||||
const Font =
|
||||
Resources.lookupMaybe(PDFName.Font, PDFDict) || context.obj({});
|
||||
Resources.set(PDFName.Font, Font);
|
||||
|
||||
// TODO: Clone `XObject` if it is inherited
|
||||
const XObject =
|
||||
Resources.lookupMaybe(PDFName.XObject, PDFDict) || context.obj({});
|
||||
Resources.set(PDFName.XObject, XObject);
|
||||
|
||||
// TODO: Clone `ExtGState` if it is inherited
|
||||
const ExtGState =
|
||||
Resources.lookupMaybe(PDFName.ExtGState, PDFDict) || context.obj({});
|
||||
Resources.set(PDFName.ExtGState, ExtGState);
|
||||
|
||||
const Annots = this.Annots() || context.obj([]);
|
||||
this.set(PDFName.Annots, Annots);
|
||||
|
||||
this.normalized = true;
|
||||
}
|
||||
|
||||
normalizedEntries() {
|
||||
this.normalize();
|
||||
const Annots = this.Annots()!;
|
||||
const Resources = this.Resources()!;
|
||||
const Contents = this.Contents() as PDFArray | undefined;
|
||||
return {
|
||||
Annots,
|
||||
Resources,
|
||||
Contents,
|
||||
Font: Resources.lookup(PDFName.Font, PDFDict),
|
||||
XObject: Resources.lookup(PDFName.XObject, PDFDict),
|
||||
ExtGState: Resources.lookup(PDFName.ExtGState, PDFDict),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFPageLeaf;
|
||||
195
frontend/node_modules/pdf-lib/src/core/structures/PDFPageTree.ts
generated
vendored
Normal file
195
frontend/node_modules/pdf-lib/src/core/structures/PDFPageTree.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
import PDFArray from 'src/core/objects/PDFArray';
|
||||
import PDFDict, { DictMap } from 'src/core/objects/PDFDict';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFPageLeaf from 'src/core/structures/PDFPageLeaf';
|
||||
import { InvalidTargetIndexError, CorruptPageTreeError } from 'src/core/errors';
|
||||
|
||||
export type TreeNode = PDFPageTree | PDFPageLeaf;
|
||||
|
||||
class PDFPageTree extends PDFDict {
|
||||
static withContext = (context: PDFContext, parent?: PDFRef) => {
|
||||
const dict = new Map();
|
||||
dict.set(PDFName.of('Type'), PDFName.of('Pages'));
|
||||
dict.set(PDFName.of('Kids'), context.obj([]));
|
||||
dict.set(PDFName.of('Count'), context.obj(0));
|
||||
if (parent) dict.set(PDFName.of('Parent'), parent);
|
||||
return new PDFPageTree(dict, context);
|
||||
};
|
||||
|
||||
static fromMapWithContext = (map: DictMap, context: PDFContext) =>
|
||||
new PDFPageTree(map, context);
|
||||
|
||||
Parent(): PDFPageTree | undefined {
|
||||
return this.lookup(PDFName.of('Parent')) as PDFPageTree | undefined;
|
||||
}
|
||||
|
||||
Kids(): PDFArray {
|
||||
return this.lookup(PDFName.of('Kids'), PDFArray);
|
||||
}
|
||||
|
||||
Count(): PDFNumber {
|
||||
return this.lookup(PDFName.of('Count'), PDFNumber);
|
||||
}
|
||||
|
||||
pushTreeNode(treeRef: PDFRef): void {
|
||||
const Kids = this.Kids();
|
||||
Kids.push(treeRef);
|
||||
}
|
||||
|
||||
pushLeafNode(leafRef: PDFRef): void {
|
||||
const Kids = this.Kids();
|
||||
this.insertLeafKid(Kids.size(), leafRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the given ref as a leaf node of this page tree at the specified
|
||||
* index (zero-based). Also increments the `Count` of each page tree in the
|
||||
* hierarchy to accomodate the new page.
|
||||
*
|
||||
* Returns the ref of the PDFPageTree node into which `leafRef` was inserted,
|
||||
* or `undefined` if it was inserted into the root node (the PDFPageTree upon
|
||||
* which the method was first called).
|
||||
*/
|
||||
insertLeafNode(leafRef: PDFRef, targetIndex: number): PDFRef | undefined {
|
||||
const Kids = this.Kids();
|
||||
const Count = this.Count().asNumber();
|
||||
|
||||
if (targetIndex > Count) {
|
||||
throw new InvalidTargetIndexError(targetIndex, Count);
|
||||
}
|
||||
|
||||
let leafsRemainingUntilTarget = targetIndex;
|
||||
for (let idx = 0, len = Kids.size(); idx < len; idx++) {
|
||||
if (leafsRemainingUntilTarget === 0) {
|
||||
// Insert page and return
|
||||
this.insertLeafKid(idx, leafRef);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const kidRef = Kids.get(idx) as PDFRef;
|
||||
const kid = this.context.lookup(kidRef);
|
||||
|
||||
if (kid instanceof PDFPageTree) {
|
||||
if (kid.Count().asNumber() > leafsRemainingUntilTarget) {
|
||||
// Dig in
|
||||
return (
|
||||
kid.insertLeafNode(leafRef, leafsRemainingUntilTarget) || kidRef
|
||||
);
|
||||
} else {
|
||||
// Move on
|
||||
leafsRemainingUntilTarget -= kid.Count().asNumber();
|
||||
}
|
||||
}
|
||||
|
||||
if (kid instanceof PDFPageLeaf) {
|
||||
// Move on
|
||||
leafsRemainingUntilTarget -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (leafsRemainingUntilTarget === 0) {
|
||||
// Insert page at the end and return
|
||||
this.insertLeafKid(Kids.size(), leafRef);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Should never get here if `targetIndex` is valid
|
||||
throw new CorruptPageTreeError(targetIndex, 'insertLeafNode');
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the leaf node at the specified index (zero-based) from this page
|
||||
* tree. Also decrements the `Count` of each page tree in the hierarchy to
|
||||
* account for the removed page.
|
||||
*
|
||||
* If `prune` is true, then intermediate tree nodes will be removed from the
|
||||
* tree if they contain 0 children after the leaf node is removed.
|
||||
*/
|
||||
removeLeafNode(targetIndex: number, prune = true): void {
|
||||
const Kids = this.Kids();
|
||||
const Count = this.Count().asNumber();
|
||||
|
||||
if (targetIndex >= Count) {
|
||||
throw new InvalidTargetIndexError(targetIndex, Count);
|
||||
}
|
||||
|
||||
let leafsRemainingUntilTarget = targetIndex;
|
||||
for (let idx = 0, len = Kids.size(); idx < len; idx++) {
|
||||
const kidRef = Kids.get(idx) as PDFRef;
|
||||
const kid = this.context.lookup(kidRef);
|
||||
|
||||
if (kid instanceof PDFPageTree) {
|
||||
if (kid.Count().asNumber() > leafsRemainingUntilTarget) {
|
||||
// Dig in
|
||||
kid.removeLeafNode(leafsRemainingUntilTarget, prune);
|
||||
if (prune && kid.Kids().size() === 0) Kids.remove(idx);
|
||||
return;
|
||||
} else {
|
||||
// Move on
|
||||
leafsRemainingUntilTarget -= kid.Count().asNumber();
|
||||
}
|
||||
}
|
||||
|
||||
if (kid instanceof PDFPageLeaf) {
|
||||
if (leafsRemainingUntilTarget === 0) {
|
||||
// Remove page and return
|
||||
this.removeKid(idx);
|
||||
return;
|
||||
} else {
|
||||
// Move on
|
||||
leafsRemainingUntilTarget -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Should never get here if `targetIndex` is valid
|
||||
throw new CorruptPageTreeError(targetIndex, 'removeLeafNode');
|
||||
}
|
||||
|
||||
ascend(visitor: (node: PDFPageTree) => any): void {
|
||||
visitor(this);
|
||||
const Parent = this.Parent();
|
||||
if (Parent) Parent.ascend(visitor);
|
||||
}
|
||||
|
||||
/** Performs a Post-Order traversal of this page tree */
|
||||
traverse(visitor: (node: TreeNode, ref: PDFRef) => any): void {
|
||||
const Kids = this.Kids();
|
||||
for (let idx = 0, len = Kids.size(); idx < len; idx++) {
|
||||
const kidRef = Kids.get(idx) as PDFRef;
|
||||
const kid = this.context.lookup(kidRef) as TreeNode;
|
||||
if (kid instanceof PDFPageTree) kid.traverse(visitor);
|
||||
visitor(kid, kidRef);
|
||||
}
|
||||
}
|
||||
|
||||
private insertLeafKid(kidIdx: number, leafRef: PDFRef): void {
|
||||
const Kids = this.Kids();
|
||||
|
||||
this.ascend((node) => {
|
||||
const newCount = node.Count().asNumber() + 1;
|
||||
node.set(PDFName.of('Count'), PDFNumber.of(newCount));
|
||||
});
|
||||
|
||||
Kids.insert(kidIdx, leafRef);
|
||||
}
|
||||
|
||||
private removeKid(kidIdx: number): void {
|
||||
const Kids = this.Kids();
|
||||
|
||||
const kid = Kids.lookup(kidIdx);
|
||||
if (kid instanceof PDFPageLeaf) {
|
||||
this.ascend((node) => {
|
||||
const newCount = node.Count().asNumber() - 1;
|
||||
node.set(PDFName.of('Count'), PDFNumber.of(newCount));
|
||||
});
|
||||
}
|
||||
|
||||
Kids.remove(kidIdx);
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFPageTree;
|
||||
62
frontend/node_modules/pdf-lib/src/core/syntax/CharCodes.ts
generated
vendored
Normal file
62
frontend/node_modules/pdf-lib/src/core/syntax/CharCodes.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
enum CharCodes {
|
||||
Null = 0,
|
||||
Backspace = 8,
|
||||
Tab = 9,
|
||||
Newline = 10,
|
||||
FormFeed = 12,
|
||||
CarriageReturn = 13,
|
||||
Space = 32,
|
||||
ExclamationPoint = 33,
|
||||
Hash = 35,
|
||||
Percent = 37,
|
||||
LeftParen = 40,
|
||||
RightParen = 41,
|
||||
Plus = 43,
|
||||
Minus = 45,
|
||||
Dash = 45,
|
||||
Period = 46,
|
||||
ForwardSlash = 47,
|
||||
Zero = 48,
|
||||
One = 49,
|
||||
Two = 50,
|
||||
Three = 51,
|
||||
Four = 52,
|
||||
Five = 53,
|
||||
Six = 54,
|
||||
Seven = 55,
|
||||
Eight = 56,
|
||||
Nine = 57,
|
||||
LessThan = 60,
|
||||
GreaterThan = 62,
|
||||
A = 65,
|
||||
D = 68,
|
||||
E = 69,
|
||||
F = 70,
|
||||
O = 79,
|
||||
P = 80,
|
||||
R = 82,
|
||||
LeftSquareBracket = 91,
|
||||
BackSlash = 92,
|
||||
RightSquareBracket = 93,
|
||||
a = 97,
|
||||
b = 98,
|
||||
d = 100,
|
||||
e = 101,
|
||||
f = 102,
|
||||
i = 105,
|
||||
j = 106,
|
||||
l = 108,
|
||||
m = 109,
|
||||
n = 110,
|
||||
o = 111,
|
||||
r = 114,
|
||||
s = 115,
|
||||
t = 116,
|
||||
u = 117,
|
||||
x = 120,
|
||||
LeftCurly = 123,
|
||||
RightCurly = 125,
|
||||
Tilde = 126,
|
||||
}
|
||||
|
||||
export default CharCodes;
|
||||
14
frontend/node_modules/pdf-lib/src/core/syntax/Delimiters.ts
generated
vendored
Normal file
14
frontend/node_modules/pdf-lib/src/core/syntax/Delimiters.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
|
||||
export const IsDelimiter = new Uint8Array(256);
|
||||
|
||||
IsDelimiter[CharCodes.LeftParen] = 1;
|
||||
IsDelimiter[CharCodes.RightParen] = 1;
|
||||
IsDelimiter[CharCodes.LessThan] = 1;
|
||||
IsDelimiter[CharCodes.GreaterThan] = 1;
|
||||
IsDelimiter[CharCodes.LeftSquareBracket] = 1;
|
||||
IsDelimiter[CharCodes.RightSquareBracket] = 1;
|
||||
IsDelimiter[CharCodes.LeftCurly] = 1;
|
||||
IsDelimiter[CharCodes.RightCurly] = 1;
|
||||
IsDelimiter[CharCodes.ForwardSlash] = 1;
|
||||
IsDelimiter[CharCodes.Percent] = 1;
|
||||
10
frontend/node_modules/pdf-lib/src/core/syntax/Irregular.ts
generated
vendored
Normal file
10
frontend/node_modules/pdf-lib/src/core/syntax/Irregular.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import { IsDelimiter } from 'src/core/syntax/Delimiters';
|
||||
import { IsWhitespace } from 'src/core/syntax/Whitespace';
|
||||
|
||||
export const IsIrregular = new Uint8Array(256);
|
||||
|
||||
for (let idx = 0, len = 256; idx < len; idx++) {
|
||||
IsIrregular[idx] = IsWhitespace[idx] || IsDelimiter[idx] ? 1 : 0;
|
||||
}
|
||||
IsIrregular[CharCodes.Hash] = 1;
|
||||
83
frontend/node_modules/pdf-lib/src/core/syntax/Keywords.ts
generated
vendored
Normal file
83
frontend/node_modules/pdf-lib/src/core/syntax/Keywords.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
|
||||
const { Space, CarriageReturn, Newline } = CharCodes;
|
||||
|
||||
const stream = [
|
||||
CharCodes.s,
|
||||
CharCodes.t,
|
||||
CharCodes.r,
|
||||
CharCodes.e,
|
||||
CharCodes.a,
|
||||
CharCodes.m,
|
||||
];
|
||||
|
||||
const endstream = [
|
||||
CharCodes.e,
|
||||
CharCodes.n,
|
||||
CharCodes.d,
|
||||
CharCodes.s,
|
||||
CharCodes.t,
|
||||
CharCodes.r,
|
||||
CharCodes.e,
|
||||
CharCodes.a,
|
||||
CharCodes.m,
|
||||
];
|
||||
|
||||
export const Keywords = {
|
||||
header: [
|
||||
CharCodes.Percent,
|
||||
CharCodes.P,
|
||||
CharCodes.D,
|
||||
CharCodes.F,
|
||||
CharCodes.Dash,
|
||||
],
|
||||
eof: [
|
||||
CharCodes.Percent,
|
||||
CharCodes.Percent,
|
||||
CharCodes.E,
|
||||
CharCodes.O,
|
||||
CharCodes.F,
|
||||
],
|
||||
obj: [CharCodes.o, CharCodes.b, CharCodes.j],
|
||||
endobj: [
|
||||
CharCodes.e,
|
||||
CharCodes.n,
|
||||
CharCodes.d,
|
||||
CharCodes.o,
|
||||
CharCodes.b,
|
||||
CharCodes.j,
|
||||
],
|
||||
xref: [CharCodes.x, CharCodes.r, CharCodes.e, CharCodes.f],
|
||||
trailer: [
|
||||
CharCodes.t,
|
||||
CharCodes.r,
|
||||
CharCodes.a,
|
||||
CharCodes.i,
|
||||
CharCodes.l,
|
||||
CharCodes.e,
|
||||
CharCodes.r,
|
||||
],
|
||||
startxref: [
|
||||
CharCodes.s,
|
||||
CharCodes.t,
|
||||
CharCodes.a,
|
||||
CharCodes.r,
|
||||
CharCodes.t,
|
||||
CharCodes.x,
|
||||
CharCodes.r,
|
||||
CharCodes.e,
|
||||
CharCodes.f,
|
||||
],
|
||||
true: [CharCodes.t, CharCodes.r, CharCodes.u, CharCodes.e],
|
||||
false: [CharCodes.f, CharCodes.a, CharCodes.l, CharCodes.s, CharCodes.e],
|
||||
null: [CharCodes.n, CharCodes.u, CharCodes.l, CharCodes.l],
|
||||
stream,
|
||||
streamEOF1: [...stream, Space, CarriageReturn, Newline],
|
||||
streamEOF2: [...stream, CarriageReturn, Newline],
|
||||
streamEOF3: [...stream, CarriageReturn],
|
||||
streamEOF4: [...stream, Newline],
|
||||
endstream,
|
||||
EOF1endstream: [CarriageReturn, Newline, ...endstream],
|
||||
EOF2endstream: [CarriageReturn, ...endstream],
|
||||
EOF3endstream: [Newline, ...endstream],
|
||||
};
|
||||
26
frontend/node_modules/pdf-lib/src/core/syntax/Numeric.ts
generated
vendored
Normal file
26
frontend/node_modules/pdf-lib/src/core/syntax/Numeric.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
|
||||
export const IsDigit = new Uint8Array(256);
|
||||
|
||||
IsDigit[CharCodes.Zero] = 1;
|
||||
IsDigit[CharCodes.One] = 1;
|
||||
IsDigit[CharCodes.Two] = 1;
|
||||
IsDigit[CharCodes.Three] = 1;
|
||||
IsDigit[CharCodes.Four] = 1;
|
||||
IsDigit[CharCodes.Five] = 1;
|
||||
IsDigit[CharCodes.Six] = 1;
|
||||
IsDigit[CharCodes.Seven] = 1;
|
||||
IsDigit[CharCodes.Eight] = 1;
|
||||
IsDigit[CharCodes.Nine] = 1;
|
||||
|
||||
export const IsNumericPrefix = new Uint8Array(256);
|
||||
|
||||
IsNumericPrefix[CharCodes.Period] = 1;
|
||||
IsNumericPrefix[CharCodes.Plus] = 1;
|
||||
IsNumericPrefix[CharCodes.Minus] = 1;
|
||||
|
||||
export const IsNumeric = new Uint8Array(256);
|
||||
|
||||
for (let idx = 0, len = 256; idx < len; idx++) {
|
||||
IsNumeric[idx] = IsDigit[idx] || IsNumericPrefix[idx] ? 1 : 0;
|
||||
}
|
||||
10
frontend/node_modules/pdf-lib/src/core/syntax/Whitespace.ts
generated
vendored
Normal file
10
frontend/node_modules/pdf-lib/src/core/syntax/Whitespace.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
|
||||
export const IsWhitespace = new Uint8Array(256);
|
||||
|
||||
IsWhitespace[CharCodes.Null] = 1;
|
||||
IsWhitespace[CharCodes.Tab] = 1;
|
||||
IsWhitespace[CharCodes.Newline] = 1;
|
||||
IsWhitespace[CharCodes.FormFeed] = 1;
|
||||
IsWhitespace[CharCodes.CarriageReturn] = 1;
|
||||
IsWhitespace[CharCodes.Space] = 1;
|
||||
123
frontend/node_modules/pdf-lib/src/core/writers/PDFStreamWriter.ts
generated
vendored
Normal file
123
frontend/node_modules/pdf-lib/src/core/writers/PDFStreamWriter.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
import PDFHeader from 'src/core/document/PDFHeader';
|
||||
import PDFTrailer from 'src/core/document/PDFTrailer';
|
||||
import PDFInvalidObject from 'src/core/objects/PDFInvalidObject';
|
||||
import PDFName from 'src/core/objects/PDFName';
|
||||
import PDFNumber from 'src/core/objects/PDFNumber';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFStream from 'src/core/objects/PDFStream';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFCrossRefStream from 'src/core/structures/PDFCrossRefStream';
|
||||
import PDFObjectStream from 'src/core/structures/PDFObjectStream';
|
||||
import PDFWriter from 'src/core/writers/PDFWriter';
|
||||
import { last, waitForTick } from 'src/utils';
|
||||
|
||||
class PDFStreamWriter extends PDFWriter {
|
||||
static forContext = (
|
||||
context: PDFContext,
|
||||
objectsPerTick: number,
|
||||
encodeStreams = true,
|
||||
objectsPerStream = 50,
|
||||
) =>
|
||||
new PDFStreamWriter(
|
||||
context,
|
||||
objectsPerTick,
|
||||
encodeStreams,
|
||||
objectsPerStream,
|
||||
);
|
||||
|
||||
private readonly encodeStreams: boolean;
|
||||
private readonly objectsPerStream: number;
|
||||
|
||||
private constructor(
|
||||
context: PDFContext,
|
||||
objectsPerTick: number,
|
||||
encodeStreams: boolean,
|
||||
objectsPerStream: number,
|
||||
) {
|
||||
super(context, objectsPerTick);
|
||||
|
||||
this.encodeStreams = encodeStreams;
|
||||
this.objectsPerStream = objectsPerStream;
|
||||
}
|
||||
|
||||
protected async computeBufferSize() {
|
||||
let objectNumber = this.context.largestObjectNumber + 1;
|
||||
|
||||
const header = PDFHeader.forVersion(1, 7);
|
||||
|
||||
let size = header.sizeInBytes() + 2;
|
||||
|
||||
const xrefStream = PDFCrossRefStream.create(
|
||||
this.createTrailerDict(),
|
||||
this.encodeStreams,
|
||||
);
|
||||
|
||||
const uncompressedObjects: [PDFRef, PDFObject][] = [];
|
||||
const compressedObjects: [PDFRef, PDFObject][][] = [];
|
||||
const objectStreamRefs: PDFRef[] = [];
|
||||
|
||||
const indirectObjects = this.context.enumerateIndirectObjects();
|
||||
for (let idx = 0, len = indirectObjects.length; idx < len; idx++) {
|
||||
const indirectObject = indirectObjects[idx];
|
||||
const [ref, object] = indirectObject;
|
||||
|
||||
const shouldNotCompress =
|
||||
ref === this.context.trailerInfo.Encrypt ||
|
||||
object instanceof PDFStream ||
|
||||
object instanceof PDFInvalidObject ||
|
||||
ref.generationNumber !== 0;
|
||||
|
||||
if (shouldNotCompress) {
|
||||
uncompressedObjects.push(indirectObject);
|
||||
xrefStream.addUncompressedEntry(ref, size);
|
||||
size += this.computeIndirectObjectSize(indirectObject);
|
||||
if (this.shouldWaitForTick(1)) await waitForTick();
|
||||
} else {
|
||||
let chunk = last(compressedObjects);
|
||||
let objectStreamRef = last(objectStreamRefs);
|
||||
if (!chunk || chunk.length % this.objectsPerStream === 0) {
|
||||
chunk = [];
|
||||
compressedObjects.push(chunk);
|
||||
objectStreamRef = PDFRef.of(objectNumber++);
|
||||
objectStreamRefs.push(objectStreamRef);
|
||||
}
|
||||
xrefStream.addCompressedEntry(ref, objectStreamRef, chunk.length);
|
||||
chunk.push(indirectObject);
|
||||
}
|
||||
}
|
||||
|
||||
for (let idx = 0, len = compressedObjects.length; idx < len; idx++) {
|
||||
const chunk = compressedObjects[idx];
|
||||
const ref = objectStreamRefs[idx];
|
||||
|
||||
const objectStream = PDFObjectStream.withContextAndObjects(
|
||||
this.context,
|
||||
chunk,
|
||||
this.encodeStreams,
|
||||
);
|
||||
|
||||
xrefStream.addUncompressedEntry(ref, size);
|
||||
size += this.computeIndirectObjectSize([ref, objectStream]);
|
||||
|
||||
uncompressedObjects.push([ref, objectStream]);
|
||||
|
||||
if (this.shouldWaitForTick(chunk.length)) await waitForTick();
|
||||
}
|
||||
|
||||
const xrefStreamRef = PDFRef.of(objectNumber++);
|
||||
xrefStream.dict.set(PDFName.of('Size'), PDFNumber.of(objectNumber));
|
||||
xrefStream.addUncompressedEntry(xrefStreamRef, size);
|
||||
const xrefOffset = size;
|
||||
size += this.computeIndirectObjectSize([xrefStreamRef, xrefStream]);
|
||||
|
||||
uncompressedObjects.push([xrefStreamRef, xrefStream]);
|
||||
|
||||
const trailer = PDFTrailer.forLastCrossRefSectionOffset(xrefOffset);
|
||||
size += trailer.sizeInBytes();
|
||||
|
||||
return { size, header, indirectObjects: uncompressedObjects, trailer };
|
||||
}
|
||||
}
|
||||
|
||||
export default PDFStreamWriter;
|
||||
156
frontend/node_modules/pdf-lib/src/core/writers/PDFWriter.ts
generated
vendored
Normal file
156
frontend/node_modules/pdf-lib/src/core/writers/PDFWriter.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
import PDFCrossRefSection from 'src/core/document/PDFCrossRefSection';
|
||||
import PDFHeader from 'src/core/document/PDFHeader';
|
||||
import PDFTrailer from 'src/core/document/PDFTrailer';
|
||||
import PDFTrailerDict from 'src/core/document/PDFTrailerDict';
|
||||
import PDFDict from 'src/core/objects/PDFDict';
|
||||
import PDFObject from 'src/core/objects/PDFObject';
|
||||
import PDFRef from 'src/core/objects/PDFRef';
|
||||
import PDFContext from 'src/core/PDFContext';
|
||||
import PDFObjectStream from 'src/core/structures/PDFObjectStream';
|
||||
import CharCodes from 'src/core/syntax/CharCodes';
|
||||
import { copyStringIntoBuffer, waitForTick } from 'src/utils';
|
||||
|
||||
export interface SerializationInfo {
|
||||
size: number;
|
||||
header: PDFHeader;
|
||||
indirectObjects: [PDFRef, PDFObject][];
|
||||
xref?: PDFCrossRefSection;
|
||||
trailerDict?: PDFTrailerDict;
|
||||
trailer: PDFTrailer;
|
||||
}
|
||||
|
||||
class PDFWriter {
|
||||
static forContext = (context: PDFContext, objectsPerTick: number) =>
|
||||
new PDFWriter(context, objectsPerTick);
|
||||
|
||||
protected readonly context: PDFContext;
|
||||
|
||||
protected readonly objectsPerTick: number;
|
||||
private parsedObjects = 0;
|
||||
|
||||
protected constructor(context: PDFContext, objectsPerTick: number) {
|
||||
this.context = context;
|
||||
this.objectsPerTick = objectsPerTick;
|
||||
}
|
||||
|
||||
async serializeToBuffer(): Promise<Uint8Array> {
|
||||
const {
|
||||
size,
|
||||
header,
|
||||
indirectObjects,
|
||||
xref,
|
||||
trailerDict,
|
||||
trailer,
|
||||
} = await this.computeBufferSize();
|
||||
|
||||
let offset = 0;
|
||||
const buffer = new Uint8Array(size);
|
||||
|
||||
offset += header.copyBytesInto(buffer, offset);
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
|
||||
for (let idx = 0, len = indirectObjects.length; idx < len; idx++) {
|
||||
const [ref, object] = indirectObjects[idx];
|
||||
|
||||
const objectNumber = String(ref.objectNumber);
|
||||
offset += copyStringIntoBuffer(objectNumber, buffer, offset);
|
||||
buffer[offset++] = CharCodes.Space;
|
||||
|
||||
const generationNumber = String(ref.generationNumber);
|
||||
offset += copyStringIntoBuffer(generationNumber, buffer, offset);
|
||||
buffer[offset++] = CharCodes.Space;
|
||||
|
||||
buffer[offset++] = CharCodes.o;
|
||||
buffer[offset++] = CharCodes.b;
|
||||
buffer[offset++] = CharCodes.j;
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
|
||||
offset += object.copyBytesInto(buffer, offset);
|
||||
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
buffer[offset++] = CharCodes.e;
|
||||
buffer[offset++] = CharCodes.n;
|
||||
buffer[offset++] = CharCodes.d;
|
||||
buffer[offset++] = CharCodes.o;
|
||||
buffer[offset++] = CharCodes.b;
|
||||
buffer[offset++] = CharCodes.j;
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
|
||||
const n =
|
||||
object instanceof PDFObjectStream ? object.getObjectsCount() : 1;
|
||||
if (this.shouldWaitForTick(n)) await waitForTick();
|
||||
}
|
||||
|
||||
if (xref) {
|
||||
offset += xref.copyBytesInto(buffer, offset);
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
}
|
||||
|
||||
if (trailerDict) {
|
||||
offset += trailerDict.copyBytesInto(buffer, offset);
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
buffer[offset++] = CharCodes.Newline;
|
||||
}
|
||||
|
||||
offset += trailer.copyBytesInto(buffer, offset);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
protected computeIndirectObjectSize([ref, object]: [
|
||||
PDFRef,
|
||||
PDFObject,
|
||||
]): number {
|
||||
const refSize = ref.sizeInBytes() + 3; // 'R' -> 'obj\n'
|
||||
const objectSize = object.sizeInBytes() + 9; // '\nendobj\n\n'
|
||||
return refSize + objectSize;
|
||||
}
|
||||
|
||||
protected createTrailerDict(): PDFDict {
|
||||
return this.context.obj({
|
||||
Size: this.context.largestObjectNumber + 1,
|
||||
Root: this.context.trailerInfo.Root,
|
||||
Encrypt: this.context.trailerInfo.Encrypt,
|
||||
Info: this.context.trailerInfo.Info,
|
||||
ID: this.context.trailerInfo.ID,
|
||||
});
|
||||
}
|
||||
|
||||
protected async computeBufferSize(): Promise<SerializationInfo> {
|
||||
const header = PDFHeader.forVersion(1, 7);
|
||||
|
||||
let size = header.sizeInBytes() + 2;
|
||||
|
||||
const xref = PDFCrossRefSection.create();
|
||||
|
||||
const indirectObjects = this.context.enumerateIndirectObjects();
|
||||
|
||||
for (let idx = 0, len = indirectObjects.length; idx < len; idx++) {
|
||||
const indirectObject = indirectObjects[idx];
|
||||
const [ref] = indirectObject;
|
||||
xref.addEntry(ref, size);
|
||||
size += this.computeIndirectObjectSize(indirectObject);
|
||||
if (this.shouldWaitForTick(1)) await waitForTick();
|
||||
}
|
||||
|
||||
const xrefOffset = size;
|
||||
size += xref.sizeInBytes() + 1; // '\n'
|
||||
|
||||
const trailerDict = PDFTrailerDict.of(this.createTrailerDict());
|
||||
size += trailerDict.sizeInBytes() + 2; // '\n\n'
|
||||
|
||||
const trailer = PDFTrailer.forLastCrossRefSectionOffset(xrefOffset);
|
||||
size += trailer.sizeInBytes();
|
||||
|
||||
return { size, header, indirectObjects, xref, trailerDict, trailer };
|
||||
}
|
||||
|
||||
protected shouldWaitForTick = (n: number) => {
|
||||
this.parsedObjects += n;
|
||||
return this.parsedObjects % this.objectsPerTick === 0;
|
||||
};
|
||||
}
|
||||
|
||||
export default PDFWriter;
|
||||
Loading…
Add table
Add a link
Reference in a new issue