pdf-lib
Version:
Create and modify PDF files with JavaScript
85 lines (70 loc) • 2.5 kB
text/typescript
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;