@pdfme/pdf-lib
Version:
Create and modify PDF files with JavaScript
356 lines (276 loc) • 12.7 kB
text/typescript
import {
PDFArray,
PDFContext,
PDFDict,
PDFName,
PDFPageLeaf,
PDFPageTree,
PDFRef,
} from '../../../src/index';
describe(`PDFPageLeaf`, () => {
it(`can be constructed directly from a Map and PDFContext`, () => {
const context = PDFContext.create();
const dict = new Map();
const pageTree = PDFPageLeaf.fromMapWithContext(dict, context);
expect(pageTree).toBeInstanceOf(PDFPageLeaf);
expect(pageTree.get(PDFName.of('Type'))).toBeUndefined();
expect(pageTree.get(PDFName.of('Kids'))).toBeUndefined();
expect(pageTree.get(PDFName.of('Count'))).toBeUndefined();
expect(pageTree.get(PDFName.of('Parent'))).toBeUndefined();
});
it(`is constructed with the correct Type and entries`, () => {
const context = PDFContext.create();
const parentRef = PDFRef.of(1);
const pageTree = PDFPageLeaf.withContextAndParent(context, parentRef);
expect(pageTree).toBeInstanceOf(PDFPageLeaf);
expect(pageTree.get(PDFName.of('Type'))).toBe(PDFName.of('Page'));
expect(pageTree.get(PDFName.of('Parent'))).toBe(parentRef);
expect(pageTree.get(PDFName.of('Resources'))).toBeInstanceOf(PDFDict);
expect(pageTree.get(PDFName.of('MediaBox'))).toBeInstanceOf(PDFArray);
});
it(`returns its Parent, Contents, Annots, BleedBox, TrimBox, Resources, MediaBox, CropBox, and Rotate entry values when they are references`, () => {
const context = PDFContext.create();
const parent = PDFPageTree.withContext(context);
const parentRef = context.register(parent);
const contents = context.obj([]);
const contentsRef = context.register(contents);
const annots = context.obj([]);
const annotsRef = context.register(annots);
const bleedBox = context.obj([]);
const bleedBoxRef = context.register(bleedBox);
const trimBox = context.obj([]);
const trimBoxRef = context.register(trimBox);
const resources = context.obj({});
const resourcesRef = context.register(resources);
const mediaBox = context.obj([]);
const mediaBoxRef = context.register(mediaBox);
const cropBox = context.obj([]);
const cropBoxRef = context.register(cropBox);
const rotate = context.obj(270);
const rotateRef = context.register(rotate);
const pageLeaf = PDFPageLeaf.withContextAndParent(context, parentRef);
const pageLeafRef = context.register(pageLeaf);
parent.pushLeafNode(pageLeafRef);
pageLeaf.set(PDFName.of('Contents'), contentsRef);
pageLeaf.set(PDFName.of('Annots'), annotsRef);
pageLeaf.set(PDFName.of('BleedBox'), bleedBoxRef);
pageLeaf.set(PDFName.of('TrimBox'), trimBoxRef);
pageLeaf.set(PDFName.of('Resources'), resourcesRef);
pageLeaf.set(PDFName.of('MediaBox'), mediaBoxRef);
pageLeaf.set(PDFName.of('CropBox'), cropBoxRef);
pageLeaf.set(PDFName.of('Rotate'), rotateRef);
expect(pageLeaf.Parent()).toBe(parent);
expect(pageLeaf.Contents()).toBe(contents);
expect(pageLeaf.Annots()).toBe(annots);
expect(pageLeaf.BleedBox()).toBe(bleedBox);
expect(pageLeaf.TrimBox()).toBe(trimBox);
expect(pageLeaf.Resources()).toBe(resources);
expect(pageLeaf.MediaBox()).toBe(mediaBox);
expect(pageLeaf.CropBox()).toBe(cropBox);
expect(pageLeaf.Rotate()).toBe(rotate);
});
it(`returns its Parent, Contents, Annots, BleedBox, TrimBox, Resources, MediaBox, CropBox, and Rotate entry values when they are direct objects`, () => {
const context = PDFContext.create();
const parent = PDFPageTree.withContext(context);
const parentRef = context.register(parent);
const contents = context.obj([]);
const annots = context.obj([]);
const bleedBox = context.obj([]);
const trimBox = context.obj([]);
const resources = context.obj({});
const mediaBox = context.obj([]);
const cropBox = context.obj([]);
const rotate = context.obj(270);
const pageLeaf = PDFPageLeaf.withContextAndParent(context, parentRef);
const pageLeafRef = context.register(pageLeaf);
parent.pushLeafNode(pageLeafRef);
pageLeaf.set(PDFName.of('Contents'), contents);
pageLeaf.set(PDFName.of('Annots'), annots);
pageLeaf.set(PDFName.of('BleedBox'), bleedBox);
pageLeaf.set(PDFName.of('TrimBox'), trimBox);
pageLeaf.set(PDFName.of('Resources'), resources);
pageLeaf.set(PDFName.of('MediaBox'), mediaBox);
pageLeaf.set(PDFName.of('CropBox'), cropBox);
pageLeaf.set(PDFName.of('Rotate'), rotate);
expect(pageLeaf.Parent()).toBe(parent);
expect(pageLeaf.Contents()).toBe(contents);
expect(pageLeaf.Annots()).toBe(annots);
expect(pageLeaf.BleedBox()).toBe(bleedBox);
expect(pageLeaf.TrimBox()).toBe(trimBox);
expect(pageLeaf.Resources()).toBe(resources);
expect(pageLeaf.MediaBox()).toBe(mediaBox);
expect(pageLeaf.CropBox()).toBe(cropBox);
expect(pageLeaf.Rotate()).toBe(rotate);
});
it(`returns its Resources, MediaBox, CropBox, and Rotate entry values when they are inherited`, () => {
const context = PDFContext.create();
const resources = context.obj({});
const resourcesRef = context.register(resources);
const mediaBox = context.obj([]);
const mediaBoxRef = context.register(mediaBox);
const cropBox = context.obj([]);
const cropBoxRef = context.register(cropBox);
const rotate = context.obj(270);
const rotateRef = context.register(rotate);
const parent = PDFPageTree.withContext(context);
const parentRef = context.register(parent);
parent.set(PDFName.of('Resources'), resourcesRef);
parent.set(PDFName.of('MediaBox'), mediaBoxRef);
parent.set(PDFName.of('CropBox'), cropBoxRef);
parent.set(PDFName.of('Rotate'), rotateRef);
const pageLeaf = PDFPageLeaf.withContextAndParent(context, parentRef);
const pageLeafRef = context.register(pageLeaf);
parent.pushLeafNode(pageLeafRef);
pageLeaf.delete(PDFName.of('Resources'));
pageLeaf.delete(PDFName.of('MediaBox'));
expect(pageLeaf.Parent()).toBe(parent);
expect(pageLeaf.Resources()).toBe(resources);
expect(pageLeaf.MediaBox()).toBe(mediaBox);
expect(pageLeaf.CropBox()).toBe(cropBox);
expect(pageLeaf.Rotate()).toBe(rotate);
});
it(`returns its Resources, MediaBox, CropBox, and Rotate entry values after being normalized, when they are inherited`, () => {
const context = PDFContext.create();
const resources = context.obj({
Font: { Foo: PDFRef.of(2100) },
XObject: { Foo: PDFRef.of(9000) },
});
const resourcesRef = context.register(resources);
const mediaBox = context.obj([]);
const mediaBoxRef = context.register(mediaBox);
const cropBox = context.obj([]);
const cropBoxRef = context.register(cropBox);
const rotate = context.obj(270);
const rotateRef = context.register(rotate);
const parent = PDFPageTree.withContext(context);
const parentRef = context.register(parent);
parent.set(PDFName.of('Resources'), resourcesRef);
parent.set(PDFName.of('MediaBox'), mediaBoxRef);
parent.set(PDFName.of('CropBox'), cropBoxRef);
parent.set(PDFName.of('Rotate'), rotateRef);
const pageLeaf = PDFPageLeaf.withContextAndParent(context, parentRef);
const pageLeafRef = context.register(pageLeaf);
parent.pushLeafNode(pageLeafRef);
pageLeaf.delete(PDFName.of('Resources'));
pageLeaf.delete(PDFName.of('MediaBox'));
const { Resources, Font, XObject } = pageLeaf.normalizedEntries();
expect(pageLeaf.Parent()).toBe(parent);
expect(pageLeaf.Resources()).toBe(resources);
expect(pageLeaf.MediaBox()).toBe(mediaBox);
expect(pageLeaf.CropBox()).toBe(cropBox);
expect(pageLeaf.Rotate()).toBe(rotate);
expect(Resources).toBe(resources);
expect(Font).toBe(resources.get(PDFName.Font));
expect(XObject).toBe(resources.get(PDFName.XObject));
});
it(`can set its Parent`, () => {
const context = PDFContext.create();
const parentRef = PDFRef.of(1);
const pageTree = PDFPageLeaf.withContextAndParent(context, parentRef);
pageTree.setParent(PDFRef.of(21));
expect(pageTree.get(PDFName.of('Parent'))).toBe(PDFRef.of(21));
});
it(`can add content stream refs`, () => {
const context = PDFContext.create();
const parentRef = PDFRef.of(1);
const pageTree = PDFPageLeaf.withContextAndParent(context, parentRef);
pageTree.addContentStream(PDFRef.of(21));
expect(pageTree.Contents()!.toString()).toBe('[ 21 0 R ]');
pageTree.addContentStream(PDFRef.of(99));
expect(pageTree.Contents()!.toString()).toBe('[ 21 0 R 99 0 R ]');
});
it(`can set font dictionary refs`, () => {
const context = PDFContext.create();
const parentRef = PDFRef.of(1);
const pageTree = PDFPageLeaf.withContextAndParent(context, parentRef);
const Font = PDFName.of('Font');
pageTree.setFontDictionary(PDFName.of('Foo'), PDFRef.of(21));
expect(pageTree.Resources()!.get(Font)!.toString()).toBe(
'<<\n/Foo 21 0 R\n>>',
);
pageTree.setFontDictionary(PDFName.of('Bar'), PDFRef.of(99));
expect(pageTree.Resources()!.get(Font)!.toString()).toBe(
'<<\n/Foo 21 0 R\n/Bar 99 0 R\n>>',
);
});
it(`can set XObject refs`, () => {
const context = PDFContext.create();
const parentRef = PDFRef.of(1);
const pageTree = PDFPageLeaf.withContextAndParent(context, parentRef);
const XObject = PDFName.of('XObject');
pageTree.setXObject(PDFName.of('Foo'), PDFRef.of(21));
expect(pageTree.Resources()!.get(XObject)!.toString()).toBe(
'<<\n/Foo 21 0 R\n>>',
);
pageTree.setXObject(PDFName.of('Bar'), PDFRef.of(99));
expect(pageTree.Resources()!.get(XObject)!.toString()).toBe(
'<<\n/Foo 21 0 R\n/Bar 99 0 R\n>>',
);
});
it(`can set ExtGState refs and dicts`, () => {
const context = PDFContext.create();
const parentRef = PDFRef.of(1);
const pageTree = PDFPageLeaf.withContextAndParent(context, parentRef);
const ExtGState = PDFName.of('ExtGState');
pageTree.setExtGState(PDFName.of('Foo'), PDFRef.of(21));
expect(pageTree.Resources()!.get(ExtGState)!.toString()).toBe(
'<<\n/Foo 21 0 R\n>>',
);
pageTree.setExtGState(PDFName.of('Bar'), context.obj({ CA: 0.1 }));
expect(pageTree.Resources()!.get(ExtGState)!.toString()).toBe(
'<<\n/Foo 21 0 R\n/Bar <<\n/CA 0.1\n>>\n>>',
);
});
it(`can be ascended`, () => {
const context = PDFContext.create();
const pageTree1 = PDFPageTree.withContext(context);
const pageTree1Ref = context.register(pageTree1);
const pageTree2 = PDFPageTree.withContext(context, pageTree1Ref);
const pageTree2Ref = context.register(pageTree2);
pageTree1.pushTreeNode(pageTree2Ref);
const pageLeaf = PDFPageLeaf.withContextAndParent(context, pageTree2Ref);
const pageLeafRef = context.register(pageLeaf);
pageTree2.pushLeafNode(pageLeafRef);
const visitations: (PDFPageLeaf | PDFPageTree)[] = [];
pageLeaf.ascend((node) => {
visitations.push(node);
});
expect(visitations).toEqual([pageLeaf, pageTree2, pageTree1]);
});
it(`can be normalized with autoNormalizeCTM=false`, () => {
const context = PDFContext.create();
const parentRef = PDFRef.of(1);
const pageTree = PDFPageLeaf.withContextAndParent(context, parentRef);
const stream = context.stream('foo');
const streamRef = PDFRef.of(21);
context.assign(streamRef, stream);
pageTree.set(PDFName.of('Contents'), streamRef);
expect(pageTree.Contents()).toBe(stream);
expect(pageTree.Resources()!.toString()).toBe('<<\n>>');
pageTree.normalize();
expect(pageTree.Contents()!.toString()).toBe('[ 21 0 R ]');
expect(pageTree.Resources()!.toString()).toBe(
'<<\n/Font <<\n>>\n/XObject <<\n>>\n/ExtGState <<\n>>\n>>',
);
});
it(`can be normalized with autoNormalizeCTM=true`, () => {
const context = PDFContext.create();
const map = new Map();
const pageTree = PDFPageLeaf.fromMapWithContext(map, context);
const stream = context.stream('foo');
const streamRef = PDFRef.of(21);
context.assign(streamRef, stream);
pageTree.set(PDFName.of('Contents'), streamRef);
expect(pageTree.Contents()).toBe(stream);
expect(pageTree.get(PDFName.of('Resources'))).toBeUndefined();
pageTree.normalize();
const pushRef = context.getPushGraphicsStateContentStream();
const popRef = context.getPopGraphicsStateContentStream();
expect(pageTree.Contents()!.toString()).toBe(
`[ ${pushRef} 21 0 R ${popRef} ]`,
);
expect(pageTree.Resources()!.toString()).toBe(
'<<\n/Font <<\n>>\n/XObject <<\n>>\n/ExtGState <<\n>>\n>>',
);
});
});