UNPKG

pdf-lib

Version:

Create and modify PDF files with JavaScript

238 lines 11.4 kB
import { __awaiter, __generator } from "tslib"; import { createCmap } from "./CMap"; import { deriveFontFlags } from "./FontFlags"; import PDFHexString from "../objects/PDFHexString"; import PDFString from "../objects/PDFString"; import { byAscendingId, Cache, sortedUniq, toHexStringOfMinLength, } from "../../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 */ var CustomFontEmbedder = /** @class */ (function () { function CustomFontEmbedder(font, fontData, customName, fontFeatures) { var _this = this; this.allGlyphsInFontSortedById = function () { var glyphs = new Array(_this.font.characterSet.length); for (var idx = 0, len = glyphs.length; idx < len; idx++) { var codePoint = _this.font.characterSet[idx]; glyphs[idx] = _this.font.glyphForCodePoint(codePoint); } return sortedUniq(glyphs.sort(byAscendingId), function (g) { return g.id; }); }; 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); } CustomFontEmbedder.for = function (fontkit, fontData, customName, fontFeatures) { return __awaiter(this, void 0, void 0, function () { var font; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, fontkit.create(fontData)]; case 1: font = _a.sent(); return [2 /*return*/, new CustomFontEmbedder(font, fontData, customName, fontFeatures)]; } }); }); }; /** * Encode the JavaScript string into this font. (JavaScript encodes strings in * Unicode, but embedded fonts use their own custom encodings) */ CustomFontEmbedder.prototype.encodeText = function (text) { var glyphs = this.font.layout(text, this.fontFeatures).glyphs; var hexCodes = new Array(glyphs.length); for (var 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. CustomFontEmbedder.prototype.widthOfTextAtSize = function (text, size) { var glyphs = this.font.layout(text, this.fontFeatures).glyphs; var totalWidth = 0; for (var idx = 0, len = glyphs.length; idx < len; idx++) { totalWidth += glyphs[idx].advanceWidth * this.scale; } var scale = size / 1000; return totalWidth * scale; }; CustomFontEmbedder.prototype.heightOfFontAtSize = function (size, options) { if (options === void 0) { options = {}; } var _a = options.descender, descender = _a === void 0 ? true : _a; var _b = this.font, ascent = _b.ascent, descent = _b.descent, bbox = _b.bbox; var yTop = (ascent || bbox.maxY) * this.scale; var yBottom = (descent || bbox.minY) * this.scale; var height = yTop - yBottom; if (!descender) height -= Math.abs(descent) || 0; return (height / 1000) * size; }; CustomFontEmbedder.prototype.sizeOfFontAtHeight = function (height) { var _a = this.font, ascent = _a.ascent, descent = _a.descent, bbox = _a.bbox; var yTop = (ascent || bbox.maxY) * this.scale; var yBottom = (descent || bbox.minY) * this.scale; return (1000 * height) / (yTop - yBottom); }; CustomFontEmbedder.prototype.embedIntoContext = function (context, ref) { this.baseFontName = this.customName || context.addRandomSuffix(this.fontName); return this.embedFontDict(context, ref); }; CustomFontEmbedder.prototype.embedFontDict = function (context, ref) { return __awaiter(this, void 0, void 0, function () { var cidFontDictRef, unicodeCMapRef, fontDict; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.embedCIDFontDict(context)]; case 1: cidFontDictRef = _a.sent(); unicodeCMapRef = this.embedUnicodeCmap(context); fontDict = context.obj({ Type: 'Font', Subtype: 'Type0', BaseFont: this.baseFontName, Encoding: 'Identity-H', DescendantFonts: [cidFontDictRef], ToUnicode: unicodeCMapRef, }); if (ref) { context.assign(ref, fontDict); return [2 /*return*/, ref]; } else { return [2 /*return*/, context.register(fontDict)]; } return [2 /*return*/]; } }); }); }; CustomFontEmbedder.prototype.isCFF = function () { return this.font.cff; }; CustomFontEmbedder.prototype.embedCIDFontDict = function (context) { return __awaiter(this, void 0, void 0, function () { var fontDescriptorRef, cidFontDict; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.embedFontDescriptor(context)]; case 1: fontDescriptorRef = _a.sent(); 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 [2 /*return*/, context.register(cidFontDict)]; } }); }); }; CustomFontEmbedder.prototype.embedFontDescriptor = function (context) { return __awaiter(this, void 0, void 0, function () { var fontStreamRef, scale, _a, italicAngle, ascent, descent, capHeight, xHeight, _b, minX, minY, maxX, maxY, fontDescriptor; var _c; return __generator(this, function (_d) { switch (_d.label) { case 0: return [4 /*yield*/, this.embedFontStream(context)]; case 1: fontStreamRef = _d.sent(); scale = this.scale; _a = this.font, italicAngle = _a.italicAngle, ascent = _a.ascent, descent = _a.descent, capHeight = _a.capHeight, xHeight = _a.xHeight; _b = this.font.bbox, minX = _b.minX, minY = _b.minY, maxX = _b.maxX, maxY = _b.maxY; fontDescriptor = context.obj((_c = { 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 }, _c[this.isCFF() ? 'FontFile3' : 'FontFile2'] = fontStreamRef, _c)); return [2 /*return*/, context.register(fontDescriptor)]; } }); }); }; CustomFontEmbedder.prototype.serializeFont = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.fontData]; }); }); }; CustomFontEmbedder.prototype.embedFontStream = function (context) { return __awaiter(this, void 0, void 0, function () { var fontStream, _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: _b = (_a = context).flateStream; return [4 /*yield*/, this.serializeFont()]; case 1: fontStream = _b.apply(_a, [_c.sent(), { Subtype: this.isCFF() ? 'CIDFontType0C' : undefined, }]); return [2 /*return*/, context.register(fontStream)]; } }); }); }; CustomFontEmbedder.prototype.embedUnicodeCmap = function (context) { var cmap = createCmap(this.glyphCache.access(), this.glyphId.bind(this)); var cmapStream = context.flateStream(cmap); return context.register(cmapStream); }; CustomFontEmbedder.prototype.glyphId = function (glyph) { return glyph ? glyph.id : -1; }; CustomFontEmbedder.prototype.computeWidths = function () { var glyphs = this.glyphCache.access(); var widths = []; var currSection = []; for (var idx = 0, len = glyphs.length; idx < len; idx++) { var currGlyph = glyphs[idx]; var prevGlyph = glyphs[idx - 1]; var currGlyphId = this.glyphId(currGlyph); var 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; }; return CustomFontEmbedder; }()); export default CustomFontEmbedder; //# sourceMappingURL=CustomFontEmbedder.js.map