pdf-lib
Version:
Library for creating and modifying PDF files in JavaScript
126 lines (125 loc) • 6.04 kB
JavaScript
import fontkit from '@pdf-lib/fontkit';
import isNil from 'lodash/isNil';
import isObject from 'lodash/isObject';
import isString from 'lodash/isString';
import range from 'lodash/range';
import pako from 'pako';
import PDFDocument from '../../pdf-document/PDFDocument';
import { PDFArray, PDFDictionary, PDFName, PDFNumber, PDFRawStream, } from '../../pdf-objects';
import { or, setCharAt } from '../../../utils';
import { isInstance, validate } from '../../../utils/validate';
/** @hidden */
var unsigned32Bit = '00000000000000000000000000000000';
// TODO: Make sure this works correctly. Setting any flag besides
// Nonsymbolic to true seems to screw up the font...
/*
* Doing this by bit-twiddling a string, and then parsing it, gets around
* JavaScript converting the results of bit-shifting ops back into 64-bit integers.
*/
// prettier-ignore
/** @hidden */
var fontFlags = function (options) {
var flags = unsigned32Bit;
if (options.FixedPitch)
flags = setCharAt(flags, 32 - 1, '1');
if (options.Serif)
flags = setCharAt(flags, 32 - 2, '1');
if (options.Symbolic)
flags = setCharAt(flags, 32 - 3, '1');
if (options.Script)
flags = setCharAt(flags, 32 - 4, '1');
if (options.Nonsymbolic)
flags = setCharAt(flags, 32 - 6, '1');
if (options.Italic)
flags = setCharAt(flags, 32 - 7, '1');
if (options.AllCap)
flags = setCharAt(flags, 32 - 17, '1');
if (options.SmallCap)
flags = setCharAt(flags, 32 - 18, '1');
if (options.ForceBold)
flags = setCharAt(flags, 32 - 19, '1');
return parseInt(flags, 2);
};
/**
* This Factory supports TrueType and OpenType fonts. Note that the apparent
* hardcoding of values for OpenType fonts does not actually affect TrueType
* fonts.
*
* A note of thanks to the developers of https://github.com/devongovett/pdfkit,
* as this class borrows heavily from:
* https://github.com/devongovett/pdfkit/blob/e71edab0dd4657b5a767804ba86c94c58d01fbca/lib/font/embedded.coffee
*/
var PDFFontFactory = /** @class */ (function () {
function PDFFontFactory(fontData, flagOptions) {
var _this = this;
/*
TODO: This is hardcoded for "Simple Fonts" with non-modified encodings, need
to broaden support to other fonts.
*/
this.embedFontIn = function (pdfDoc, name) {
validate(pdfDoc, isInstance(PDFDocument), 'PDFFontFactory.embedFontIn: "pdfDoc" must be an instance of PDFDocument');
validate(name, or(isString, isNil), '"name" must be a string or undefined');
var randSuffix = "-rand_" + Math.floor(Math.random() * 10000);
var fontName = name || _this.font.postscriptName + randSuffix || 'Font' + randSuffix;
var deflatedFontData = pako.deflate(_this.fontData);
var fontStreamDict = PDFDictionary.from({
Subtype: PDFName.from('OpenType'),
Filter: PDFName.from('FlateDecode'),
Length: PDFNumber.fromNumber(deflatedFontData.length),
}, pdfDoc.index);
var fontStream = pdfDoc.register(PDFRawStream.from(fontStreamDict, deflatedFontData));
var _a = _this.font, italicAngle = _a.italicAngle, ascent = _a.ascent, descent = _a.descent, capHeight = _a.capHeight, xHeight = _a.xHeight, bbox = _a.bbox;
var fontDescriptor = PDFDictionary.from({
Type: PDFName.from('FontDescriptor'),
FontName: PDFName.from(fontName),
Flags: PDFNumber.fromNumber(fontFlags(_this.flagOptions)),
FontBBox: PDFArray.fromArray([
PDFNumber.fromNumber(bbox.minX * _this.scale),
PDFNumber.fromNumber(bbox.minY * _this.scale),
PDFNumber.fromNumber(bbox.maxX * _this.scale),
PDFNumber.fromNumber(bbox.maxY * _this.scale),
], pdfDoc.index),
ItalicAngle: PDFNumber.fromNumber(italicAngle),
Ascent: PDFNumber.fromNumber(ascent * _this.scale),
Descent: PDFNumber.fromNumber(descent * _this.scale),
CapHeight: PDFNumber.fromNumber((capHeight || ascent) * _this.scale),
XHeight: PDFNumber.fromNumber((xHeight || 0) * _this.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: PDFNumber.fromNumber(0),
FontFile3: fontStream,
}, pdfDoc.index);
return pdfDoc.register(PDFDictionary.from({
Type: PDFName.from('Font'),
Subtype: PDFName.from('OpenType'),
BaseFont: PDFName.from(fontName),
FirstChar: PDFNumber.fromNumber(0),
LastChar: PDFNumber.fromNumber(255),
Widths: _this.getWidths(pdfDoc.index),
FontDescriptor: pdfDoc.register(fontDescriptor),
}, pdfDoc.index));
};
/** @hidden */
this.getWidths = function (index) {
return PDFArray.fromArray(range(0, 256)
.map(_this.getCodePointWidth)
.map(PDFNumber.fromNumber), index);
};
this.getCodePointWidth = function (code) {
return _this.font.characterSet.includes(code)
? _this.font.glyphForCodePoint(code).advanceWidth * _this.scale
: 0;
};
validate(fontData, isInstance(Uint8Array), '"fontData" must be a Uint8Array');
validate(flagOptions, isObject, '"flagOptions" must be an Object');
this.fontData = fontData;
this.flagOptions = flagOptions;
this.font = fontkit.create(fontData);
this.scale = 1000 / this.font.unitsPerEm;
}
PDFFontFactory.for = function (fontData, flagOptions) {
return new PDFFontFactory(fontData, flagOptions);
};
return PDFFontFactory;
}());
export default PDFFontFactory;