printmaker
Version:
Generate PDF documents and from JavaScript objects
100 lines (89 loc) • 2.88 kB
text/typescript
import fontkit from '@pdf-lib/fontkit';
import { PDFDict, PDFDocument, PDFHexString, PDFName } from 'pdf-lib';
import { embedFonts, Font, parseFonts } from './fonts.js';
import { embedImages, Image, parseImages } from './images.js';
import {
asArray,
asDate,
asObject,
asString,
check,
getFrom,
Obj,
optional,
pickDefined,
} from './types.js';
export type Document = {
fonts: Font[];
images: Image[];
pdfDoc: PDFDocument;
};
export async function createDocument(def: Obj): Promise<Document> {
const pdfDoc = await PDFDocument.create();
pdfDoc.registerFontkit(fontkit);
const fonts = await embedFonts(getFrom(def, 'fonts', parseFonts), pdfDoc);
const images = await embedImages(getFrom(def, 'images', parseImages), pdfDoc);
setMetadata(getFrom(def, 'info', optional(parseInfo)), pdfDoc);
return { fonts, images, pdfDoc };
}
type Metadata = {
title?: string;
subject?: string;
keywords?: string[];
author?: string;
creationDate?: Date;
creator?: string;
producer?: string;
custom?: Record<string, string>;
};
export function parseInfo(input: unknown): Metadata {
const obj = asObject(input);
const { title, subject, keywords, author, creationDate, creator, producer, ...custom } = obj;
return pickDefined({
title: check(title, 'title', optional(asString)),
subject: check(subject, 'subject', optional(asString)),
keywords: check(keywords, 'keywords', optional(asStringArray)) as string[],
author: check(author, 'author', optional(asString)),
creationDate: check(creationDate, 'creationDate', optional(asDate)),
creator: check(creator, 'creator', optional(asString)),
producer: check(producer, 'producer', optional(asString)),
custom: parseCustomAttrs(custom),
});
}
function parseCustomAttrs(custom: Obj): Record<string, string> {
if (custom == null || !Object.keys(custom).length) return undefined;
Object.entries(asObject(custom)).forEach(([key, value]) => check(value, key, asString));
return custom as Record<string, string>;
}
function asStringArray(input: unknown): string[] {
return asArray(input).map((el, idx) => check(el, `[${idx}]`, asString));
}
function setMetadata(info: Metadata, doc: PDFDocument) {
if (info?.title) {
doc.setTitle(info.title);
}
if (info?.subject) {
doc.setSubject(info.subject);
}
if (info?.keywords) {
doc.setKeywords(info.keywords);
}
if (info?.author) {
doc.setAuthor(info.author);
}
if (info?.creationDate) {
doc.setCreationDate(info.creationDate);
}
if (info?.creator) {
doc.setCreator(info.creator);
}
if (info?.producer) {
doc.setProducer(info.producer);
}
if (info?.custom) {
const dict = (doc as any).getInfoDict() as PDFDict;
for (const [key, value] of Object.entries(info.custom)) {
dict.set(PDFName.of(key), PDFHexString.fromText(value));
}
}
}