UNPKG

modern-font

Version:
2,402 lines (2,386 loc) 145 kB
import { zlibSync, unzlibSync } from 'fflate'; const PKG_NAME = "modern-font"; function assert(condition, msg) { if (!condition) { throw new Error(`[${PKG_NAME}] ${msg}`); } } function toBuffer(source) { if ("buffer" in source) { const buffer = source.buffer; if (source.byteOffset > 0 || source.byteLength < source.buffer.byteLength) { return buffer.slice(source.byteOffset, source.byteOffset + source.byteLength); } return buffer; } else { return source; } } function toDataView(source) { if ("buffer" in source) { return new DataView(source.buffer, source.byteOffset, source.byteLength); } else { return new DataView(source); } } function stringify(str) { let newStr = ""; for (let i = 0, l = str.length, ch; i < l; i++) { ch = str.charCodeAt(i); if (ch === 0) { continue; } newStr += String.fromCharCode(ch); } return newStr; } function toUCS2Bytes(str) { str = stringify(str); const byteArray = []; for (let i = 0, l = str.length, ch; i < l; i++) { ch = str.charCodeAt(i); byteArray.push(ch >> 8); byteArray.push(ch & 255); } return byteArray; } function getUTF8String(bytes) { let str = ""; for (let i = 0, l = bytes.length; i < l; i++) { if (bytes[i] < 127) { str += String.fromCharCode(bytes[i]); } else { str += `%${(256 + bytes[i]).toString(16).slice(1)}`; } } return unescape(str); } function getUCS2String(bytes) { let str = ""; for (let i = 0, l = bytes.length; i < l; i += 2) { str += String.fromCharCode((bytes[i] << 8) + bytes[i + 1]); } return str; } var __defProp$o = Object.defineProperty; var __decorateClass$u = (decorators, target, key, kind) => { var result = void 0 ; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (decorator(target, key, result) ) || result; if (result) __defProp$o(target, key, result); return result; }; const dataTypeToByteLength = { int8: 1, int16: 2, int32: 4, uint8: 1, uint16: 2, uint32: 4, float32: 4, float64: 8, fixed: 4, longDateTime: 8, char: 1 }; function defineMethod() { return function(target, name) { Object.defineProperty(target.constructor.prototype, name, { get() { if (typeof name === "string") { if (name.startsWith("read")) { return (...args) => this.read(name.substring("read".length).toLowerCase(), ...args); } else if (name.startsWith("write")) { return (...args) => this.write(name.substring("write".length).toLowerCase(), ...args); } } return void 0; }, configurable: true, enumerable: true }); }; } class FontDataView extends DataView { constructor(source, byteOffset, byteLength, littleEndian) { super(toBuffer(source), byteOffset, byteLength); this.littleEndian = littleEndian; } cursor = 0; readColumn(column) { if (column.size) { const array = Array.from({ length: column.size }, (_, i) => { return this.read(column.type, column.offset + i); }); switch (column.type) { case "char": return array.join(""); default: return array; } } else { return this.read(column.type, column.offset); } } writeColumn(column, value) { if (column.size) { Array.from({ length: column.size }, (_, i) => { this.write(column.type, value[i], column.offset + i); }); } else { this.write(column.type, value, column.offset); } } read(type, byteOffset = this.cursor, littleEndian = this.littleEndian) { switch (type) { case "char": return this.readChar(byteOffset); case "fixed": return this.readFixed(byteOffset, littleEndian); case "longDateTime": return this.readLongDateTime(byteOffset, littleEndian); } const key = `get${type.replace(/^\S/, (s) => s.toUpperCase())}`; const self = this; const method = self[key]?.bind(self); const result = method?.(byteOffset, littleEndian); this.cursor += dataTypeToByteLength[type]; return result; } readUint24(byteOffset = this.cursor) { const [i, j, k] = this.readBytes(byteOffset, 3); return (i << 16) + (j << 8) + k; } readBytes(byteOffset, length) { if (length == null) { length = byteOffset; byteOffset = this.cursor; } const array = []; for (let i = 0; i < length; ++i) { array.push(this.getUint8(byteOffset + i)); } this.cursor = byteOffset + length; return array; } readString(byteOffset, length) { const bytes = this.readBytes(byteOffset, length); let str = ""; for (let i = 0, len = bytes.length; i < len; i++) { str += String.fromCharCode(bytes[i]); } return str; } readFixed(byteOffset, littleEndian) { const val = this.readInt32(byteOffset, littleEndian) / 65536; return Math.ceil(val * 1e5) / 1e5; } readLongDateTime(byteOffset = this.cursor, littleEndian) { const time = this.readUint32(byteOffset + 4, littleEndian); const date = /* @__PURE__ */ new Date(); date.setTime(time * 1e3 + -20775456e5); return date; } readChar(byteOffset) { return this.readString(byteOffset, 1); } write(type, value, byteOffset = this.cursor, littleEndian = this.littleEndian) { switch (type) { case "char": return this.writeChar(value, byteOffset); case "fixed": return this.writeFixed(value, byteOffset); case "longDateTime": return this.writeLongDateTime(value, byteOffset); } const key = `set${type.replace(/^\S/, (s) => s.toUpperCase())}`; const self = this; const method = self[key]?.bind(self); const result = method?.(byteOffset, value, littleEndian); this.cursor += dataTypeToByteLength[type.toLowerCase()]; return result; } writeString(str = "", byteOffset = this.cursor) { const length = str.replace(/[^\x00-\xFF]/g, "11").length; this.seek(byteOffset); for (let i = 0, l = str.length, charCode; i < l; ++i) { charCode = str.charCodeAt(i) || 0; if (charCode > 127) { this.writeUint16(charCode); } else { this.writeUint8(charCode); } } this.cursor += length; return this; } writeChar(value, byteOffset) { return this.writeString(value, byteOffset); } writeFixed(value, byteOffset) { this.writeInt32(Math.round(value * 65536), byteOffset); return this; } writeLongDateTime(value, byteOffset = this.cursor) { const delta = -20775456e5; if (typeof value === "undefined") { value = delta; } else if (typeof value.getTime === "function") { value = value.getTime(); } else if (/^\d+$/.test(value)) { value = +value; } else { value = Date.parse(value); } const time = Math.round((value - delta) / 1e3); this.writeUint32(0, byteOffset); this.writeUint32(time, byteOffset + 4); return this; } writeBytes(value, byteOffset = this.cursor) { let len; if (Array.isArray(value)) { len = value.length; for (let i = 0; i < len; ++i) { this.setUint8(byteOffset + i, value[i]); } } else { const view = toDataView(value); len = view.byteLength; for (let i = 0; i < len; ++i) { this.setUint8(byteOffset + i, view.getUint8(i)); } } this.cursor = byteOffset + len; return this; } seek(byteOffset) { this.cursor = byteOffset; return this; } } __decorateClass$u([ defineMethod() ], FontDataView.prototype, "readInt8"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "readInt16"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "readInt32"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "readUint8"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "readUint16"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "readUint32"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "readFloat32"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "readFloat64"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "writeInt8"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "writeInt16"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "writeInt32"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "writeUint8"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "writeUint16"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "writeUint32"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "writeFloat32"); __decorateClass$u([ defineMethod() ], FontDataView.prototype, "writeFloat64"); const definitions = /* @__PURE__ */ new WeakMap(); function defineColumn(options) { const config = typeof options === "object" ? options : { type: options }; const { size = 1, type } = config; return (target, name) => { if (typeof name !== "string") return; let definition = definitions.get(target); if (!definition) { definition = { columns: [], byteLength: 0 }; definitions.set(target, definition); } const column = { ...config, name, byteLength: size * dataTypeToByteLength[type], offset: config.offset ?? definition.columns.reduce((offset, column2) => offset + column2.byteLength, 0) }; definition.columns.push(column); definition.byteLength = definition.columns.reduce((byteLength, column2) => { return byteLength + dataTypeToByteLength[column2.type] * (column2.size ?? 1); }, 0); Object.defineProperty(target.constructor.prototype, name, { get() { return this.view.readColumn(column); }, set(value) { this.view.writeColumn(column, value); }, configurable: true, enumerable: true }); }; } class FontDataObject { view; constructor(source, byteOffset, byteLength, littleEndian) { this.view = new FontDataView(source, byteOffset, byteLength, littleEndian); } } class BaseFont extends FontDataObject { get buffer() { return this.view.buffer; } toBuffer() { return this.view.buffer.slice(this.view.byteOffset, this.view.byteOffset + this.view.byteLength); } toBlob() { return new Blob( [new Uint8Array(this.view.buffer, this.view.byteOffset, this.view.byteLength)], { type: this.mimeType } ); } toFontFace(family) { return new FontFace(family, this.view.buffer); } } var __defProp$n = Object.defineProperty; var __decorateClass$t = (decorators, target, key, kind) => { var result = void 0 ; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (decorator(target, key, result) ) || result; if (result) __defProp$n(target, key, result); return result; }; const _EOT = class _EOT extends BaseFont { format = "EmbeddedOpenType"; mimeType = "application/vnd.ms-fontobject"; // FamilyNameSize // FamilyName // Padding2 // StyleNameSize // StyleName // Padding3 // VersionNameSize // VersionName // Padding4 // FullNameSize // FullName // FontData static from(ttf) { const sfnt = ttf.sfnt; const name = sfnt.name; const names = name.names; const FamilyName = toUCS2Bytes(names.fontFamily || ""); const FamilyNameSize = FamilyName.length; const StyleName = toUCS2Bytes(names.fontStyle || ""); const StyleNameSize = StyleName.length; const VersionName = toUCS2Bytes(names.version || ""); const VersionNameSize = VersionName.length; const FullName = toUCS2Bytes(names.fullName || ""); const FullNameSize = FullName.length; const size = 82 + 4 + FamilyNameSize + 4 + StyleNameSize + 4 + VersionNameSize + 4 + FullNameSize + 2 + ttf.view.byteLength; const eot = new _EOT(new ArrayBuffer(size), 0, size, true); eot.EOTSize = eot.view.byteLength; eot.FontDataSize = ttf.view.byteLength; eot.Version = 131073; eot.Flags = 0; eot.Charset = 1; eot.MagicNumber = 20556; eot.Padding1 = 0; eot.CheckSumAdjustment = sfnt.head.checkSumAdjustment; const os2 = sfnt.os2; if (os2) { eot.FontPANOSE = os2.fontPANOSE; eot.Italic = os2.fsSelection; eot.Weight = os2.usWeightClass; eot.fsType = os2.fsType; eot.UnicodeRange = os2.ulUnicodeRange; eot.CodePageRange = os2.ulCodePageRange; } eot.view.writeUint16(FamilyNameSize); eot.view.writeBytes(FamilyName); eot.view.writeUint16(0); eot.view.writeUint16(StyleNameSize); eot.view.writeBytes(StyleName); eot.view.writeUint16(0); eot.view.writeUint16(VersionNameSize); eot.view.writeBytes(VersionName); eot.view.writeUint16(0); eot.view.writeUint16(FullNameSize); eot.view.writeBytes(FullName); eot.view.writeUint16(0); eot.view.writeUint16(0); eot.view.writeBytes(ttf.view); return eot; } }; __decorateClass$t([ defineColumn("uint32") ], _EOT.prototype, "EOTSize"); __decorateClass$t([ defineColumn("uint32") ], _EOT.prototype, "FontDataSize"); __decorateClass$t([ defineColumn("uint32") ], _EOT.prototype, "Version"); __decorateClass$t([ defineColumn("uint32") ], _EOT.prototype, "Flags"); __decorateClass$t([ defineColumn({ type: "uint8", size: 10 }) ], _EOT.prototype, "FontPANOSE"); __decorateClass$t([ defineColumn("uint8") ], _EOT.prototype, "Charset"); __decorateClass$t([ defineColumn("uint8") ], _EOT.prototype, "Italic"); __decorateClass$t([ defineColumn("uint32") ], _EOT.prototype, "Weight"); __decorateClass$t([ defineColumn("uint16") ], _EOT.prototype, "fsType"); __decorateClass$t([ defineColumn("uint16") ], _EOT.prototype, "MagicNumber"); __decorateClass$t([ defineColumn({ type: "uint8", size: 16 }) ], _EOT.prototype, "UnicodeRange"); __decorateClass$t([ defineColumn({ type: "uint8", size: 8 }) ], _EOT.prototype, "CodePageRange"); __decorateClass$t([ defineColumn("uint32") ], _EOT.prototype, "CheckSumAdjustment"); __decorateClass$t([ defineColumn({ type: "uint8", size: 16 }) ], _EOT.prototype, "Reserved"); __decorateClass$t([ defineColumn("uint16") ], _EOT.prototype, "Padding1"); let EOT = _EOT; var __defProp$m = Object.defineProperty; var __decorateClass$s = (decorators, target, key, kind) => { var result = void 0 ; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (decorator(target, key, result) ) || result; if (result) __defProp$m(target, key, result); return result; }; class TableDirectory extends FontDataObject { constructor(buffer, byteOffset) { super(buffer, byteOffset, 16); } } __decorateClass$s([ defineColumn({ type: "char", size: 4 }) ], TableDirectory.prototype, "tag"); __decorateClass$s([ defineColumn("uint32") ], TableDirectory.prototype, "checkSum"); __decorateClass$s([ defineColumn("uint32") ], TableDirectory.prototype, "offset"); __decorateClass$s([ defineColumn("uint32") ], TableDirectory.prototype, "length"); const cffStandardEncoding = [ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", "", "endash", "dagger", "daggerdbl", "periodcentered", "", "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis", "perthousand", "", "questiondown", "", "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", "dieresis", "", "ring", "cedilla", "", "hungarumlaut", "ogonek", "caron", "emdash", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "AE", "", "ordfeminine", "", "", "", "", "Lslash", "Oslash", "OE", "ordmasculine", "", "", "", "", "", "ae", "", "", "", "dotlessi", "", "", "lslash", "oslash", "oe", "germandbls" ]; const cffExpertEncoding = [ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "space", "exclamsmall", "Hungarumlautsmall", "", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon", "commasuperior", "threequartersemdash", "periodsuperior", "questionsmall", "", "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "", "", "isuperior", "", "", "lsuperior", "msuperior", "nsuperior", "osuperior", "", "", "rsuperior", "ssuperior", "tsuperior", "", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior", "", "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "exclamdownsmall", "centoldstyle", "Lslashsmall", "", "", "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", "", "Dotaccentsmall", "", "", "Macronsmall", "", "", "figuredash", "hypheninferior", "", "", "Ogoneksmall", "Ringsmall", "Cedillasmall", "", "", "", "onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", "", "", "zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall" ]; class Glyph { index; name; isComposite = false; components = []; pathCommands = []; constructor(options) { const config = { ...options }; this.index = config.index ?? 0; if (config.name === ".notdef") { config.unicode = void 0; } else if (config.name === ".null") { config.unicode = 0; } if (config.unicode === 0 && config.name !== ".null") { throw new Error('The unicode value "0" is reserved for the glyph name ".null" and cannot be used by any other glyph.'); } this.name = config.name ?? null; if (config.unicode) { this.unicode = config.unicode; } if (config.unicodes) { this.unicodes = config.unicodes; } else if (config.unicode) { this.unicodes = [config.unicode]; } } getPathCommands(x = 0, y = 0, fontSize = 72, options = {}, sfnt) { const scale = 1 / (sfnt?.unitsPerEm ?? 1e3) * fontSize; const { xScale = scale, yScale = scale } = options; const pathCommands = this.pathCommands; const commands = []; for (let i = 0, len = pathCommands.length; i < len; i += 1) { const cmd = pathCommands[i]; if (cmd.type === "M") { commands.push({ type: "M", x: x + cmd.x * xScale, y: y + -cmd.y * yScale }); } else if (cmd.type === "L") { commands.push({ type: "L", x: x + cmd.x * xScale, y: y + -cmd.y * yScale }); } else if (cmd.type === "Q") { commands.push({ type: "Q", x1: x + cmd.x1 * xScale, y1: y + -cmd.y1 * yScale, x: x + cmd.x * xScale, y: y + -cmd.y * yScale }); } else if (cmd.type === "C") { commands.push({ type: "C", x1: x + cmd.x1 * xScale, y1: y + -cmd.y1 * yScale, x2: x + cmd.x2 * xScale, y2: y + -cmd.y2 * yScale, x: x + cmd.x * xScale, y: y + -cmd.y * yScale }); } else if (cmd.type === "Z") { commands.push({ type: "Z" }); } } return commands; } } class CffGlyph extends Glyph { parse(cff, code, glyphs) { const self = this; const { nominalWidthX, defaultWidthX, gsubrsBias, subrsBias } = cff; const paintType = cff.topDict.paintType; const index = this.index; let c1x; let c1y; let c2x; let c2y; const pathCommands = []; const stack = []; let nStems = 0; let haveWidth = false; let open = false; let width = defaultWidthX; let x = 0; let y = 0; function lineTo(x2, y2) { pathCommands.push({ type: "L", x: x2, y: y2 }); } function curveTo(c1x2, c1y2, c2x2, c2y2, x2, y2) { pathCommands.push({ type: "C", x1: c1x2, y1: c1y2, x2: c2x2, y2: c2y2, x: x2, y: y2 }); } function moveTo(x2, y2) { if (open && paintType !== 2) { closePath(); } open = true; pathCommands.push({ type: "M", x: x2, y: y2 }); } function closePath() { pathCommands.push({ type: "Z" }); } function extend(commands) { pathCommands.push(...commands); } function parseStems() { const hasWidthArg = stack.length % 2 !== 0; if (hasWidthArg && !haveWidth) { width = stack.shift() + nominalWidthX; } nStems += stack.length >> 1; stack.length = 0; haveWidth = true; } function parse(code2) { let b1; let b2; let b3; let b4; let codeIndex; let subrCode; let jpx; let jpy; let c3x; let c3y; let c4x; let c4y; let i = 0; while (i < code2.length) { let v = code2[i++]; switch (v) { case 1: parseStems(); break; case 3: parseStems(); break; case 4: if (stack.length > 1 && !haveWidth) { width = stack.shift() + nominalWidthX; haveWidth = true; } y += stack.pop(); moveTo(x, y); break; case 5: while (stack.length > 0) { x += stack.shift(); y += stack.shift(); lineTo(x, y); } break; case 6: while (stack.length > 0) { x += stack.shift(); lineTo(x, y); if (stack.length === 0) { break; } y += stack.shift(); lineTo(x, y); } break; case 7: while (stack.length > 0) { y += stack.shift(); lineTo(x, y); if (stack.length === 0) { break; } x += stack.shift(); lineTo(x, y); } break; case 8: while (stack.length > 0) { c1x = x + stack.shift(); c1y = y + stack.shift(); c2x = c1x + stack.shift(); c2y = c1y + stack.shift(); x = c2x + stack.shift(); y = c2y + stack.shift(); curveTo(c1x, c1y, c2x, c2y, x, y); } break; case 10: codeIndex = stack.pop() + subrsBias; subrCode = cff.subrs[codeIndex]; if (subrCode) { parse(subrCode); } break; case 11: return; case 12: v = code2[i]; i += 1; switch (v) { case 35: c1x = x + stack.shift(); c1y = y + stack.shift(); c2x = c1x + stack.shift(); c2y = c1y + stack.shift(); jpx = c2x + stack.shift(); jpy = c2y + stack.shift(); c3x = jpx + stack.shift(); c3y = jpy + stack.shift(); c4x = c3x + stack.shift(); c4y = c3y + stack.shift(); x = c4x + stack.shift(); y = c4y + stack.shift(); stack.shift(); curveTo(c1x, c1y, c2x, c2y, jpx, jpy); curveTo(c3x, c3y, c4x, c4y, x, y); break; case 34: c1x = x + stack.shift(); c1y = y; c2x = c1x + stack.shift(); c2y = c1y + stack.shift(); jpx = c2x + stack.shift(); jpy = c2y; c3x = jpx + stack.shift(); c3y = c2y; c4x = c3x + stack.shift(); c4y = y; x = c4x + stack.shift(); curveTo(c1x, c1y, c2x, c2y, jpx, jpy); curveTo(c3x, c3y, c4x, c4y, x, y); break; case 36: c1x = x + stack.shift(); c1y = y + stack.shift(); c2x = c1x + stack.shift(); c2y = c1y + stack.shift(); jpx = c2x + stack.shift(); jpy = c2y; c3x = jpx + stack.shift(); c3y = c2y; c4x = c3x + stack.shift(); c4y = c3y + stack.shift(); x = c4x + stack.shift(); curveTo(c1x, c1y, c2x, c2y, jpx, jpy); curveTo(c3x, c3y, c4x, c4y, x, y); break; case 37: c1x = x + stack.shift(); c1y = y + stack.shift(); c2x = c1x + stack.shift(); c2y = c1y + stack.shift(); jpx = c2x + stack.shift(); jpy = c2y + stack.shift(); c3x = jpx + stack.shift(); c3y = jpy + stack.shift(); c4x = c3x + stack.shift(); c4y = c3y + stack.shift(); if (Math.abs(c4x - x) > Math.abs(c4y - y)) { x = c4x + stack.shift(); } else { y = c4y + stack.shift(); } curveTo(c1x, c1y, c2x, c2y, jpx, jpy); curveTo(c3x, c3y, c4x, c4y, x, y); break; default: console.warn(`Glyph ${index}: unknown operator ${1200 + v}`); stack.length = 0; } break; case 14: if (stack.length >= 4) { const acharName = cffStandardEncoding[stack.pop()]; const bcharName = cffStandardEncoding[stack.pop()]; const ady = stack.pop(); const adx = stack.pop(); if (acharName && bcharName) { self.isComposite = true; self.components = []; const acharGlyphIndex = cff.charset.indexOf(acharName); const bcharGlyphIndex = cff.charset.indexOf(bcharName); self.components.push({ glyphIndex: bcharGlyphIndex, dx: 0, dy: 0 }); self.components.push({ glyphIndex: acharGlyphIndex, dx: adx, dy: ady }); extend(glyphs.get(bcharGlyphIndex).pathCommands); const shiftedCommands = JSON.parse(JSON.stringify(glyphs.get(acharGlyphIndex).pathCommands)); for (let i2 = 0; i2 < shiftedCommands.length; i2 += 1) { const cmd = shiftedCommands[i2]; if (cmd.type !== "Z") { cmd.x += adx; cmd.y += ady; } if (cmd.type === "Q" || cmd.type === "C") { cmd.x1 += adx; cmd.y1 += ady; } if (cmd.type === "C") { cmd.x2 += adx; cmd.y2 += ady; } } extend(shiftedCommands); } } else if (stack.length > 0 && !haveWidth) { width = stack.shift() + nominalWidthX; haveWidth = true; } if (open && paintType !== 2) { closePath(); open = false; } break; case 18: parseStems(); break; case 19: // hintmask case 20: parseStems(); i += nStems + 7 >> 3; break; case 21: if (stack.length > 2 && !haveWidth) { width = stack.shift() + nominalWidthX; haveWidth = true; } y += stack.pop(); x += stack.pop(); moveTo(x, y); break; case 22: if (stack.length > 1 && !haveWidth) { width = stack.shift() + nominalWidthX; haveWidth = true; } x += stack.pop(); moveTo(x, y); break; case 23: parseStems(); break; case 24: while (stack.length > 2) { c1x = x + stack.shift(); c1y = y + stack.shift(); c2x = c1x + stack.shift(); c2y = c1y + stack.shift(); x = c2x + stack.shift(); y = c2y + stack.shift(); curveTo(c1x, c1y, c2x, c2y, x, y); } x += stack.shift(); y += stack.shift(); lineTo(x, y); break; case 25: while (stack.length > 6) { x += stack.shift(); y += stack.shift(); lineTo(x, y); } c1x = x + stack.shift(); c1y = y + stack.shift(); c2x = c1x + stack.shift(); c2y = c1y + stack.shift(); x = c2x + stack.shift(); y = c2y + stack.shift(); curveTo(c1x, c1y, c2x, c2y, x, y); break; case 26: if (stack.length % 2) { x += stack.shift(); } while (stack.length > 0) { c1x = x; c1y = y + stack.shift(); c2x = c1x + stack.shift(); c2y = c1y + stack.shift(); x = c2x; y = c2y + stack.shift(); curveTo(c1x, c1y, c2x, c2y, x, y); } break; case 27: if (stack.length % 2) { y += stack.shift(); } while (stack.length > 0) { c1x = x + stack.shift(); c1y = y; c2x = c1x + stack.shift(); c2y = c1y + stack.shift(); x = c2x + stack.shift(); y = c2y; curveTo(c1x, c1y, c2x, c2y, x, y); } break; case 28: b1 = code2[i]; b2 = code2[i + 1]; stack.push((b1 << 24 | b2 << 16) >> 16); i += 2; break; case 29: codeIndex = stack.pop() + gsubrsBias; subrCode = cff.gsubrs[codeIndex]; if (subrCode) { parse(subrCode); } break; case 30: while (stack.length > 0) { c1x = x; c1y = y + stack.shift(); c2x = c1x + stack.shift(); c2y = c1y + stack.shift(); x = c2x + stack.shift(); y = c2y + (stack.length === 1 ? stack.shift() : 0); curveTo(c1x, c1y, c2x, c2y, x, y); if (stack.length === 0) { break; } c1x = x + stack.shift(); c1y = y; c2x = c1x + stack.shift(); c2y = c1y + stack.shift(); y = c2y + stack.shift(); x = c2x + (stack.length === 1 ? stack.shift() : 0); curveTo(c1x, c1y, c2x, c2y, x, y); } break; case 31: while (stack.length > 0) { c1x = x + stack.shift(); c1y = y; c2x = c1x + stack.shift(); c2y = c1y + stack.shift(); y = c2y + stack.shift(); x = c2x + (stack.length === 1 ? stack.shift() : 0); curveTo(c1x, c1y, c2x, c2y, x, y); if (stack.length === 0) { break; } c1x = x; c1y = y + stack.shift(); c2x = c1x + stack.shift(); c2y = c1y + stack.shift(); x = c2x + stack.shift(); y = c2y + (stack.length === 1 ? stack.shift() : 0); curveTo(c1x, c1y, c2x, c2y, x, y); } break; default: if (v < 32) { console.warn(`Glyph ${index}: unknown operator ${v}`); } else if (v < 247) { stack.push(v - 139); } else if (v < 251) { b1 = code2[i]; i += 1; stack.push((v - 247) * 256 + b1 + 108); } else if (v < 255) { b1 = code2[i]; i += 1; stack.push(-(v - 251) * 256 - b1 - 108); } else { b1 = code2[i]; b2 = code2[i + 1]; b3 = code2[i + 2]; b4 = code2[i + 3]; i += 4; stack.push((b1 << 24 | b2 << 16 | b3 << 8 | b4) / 65536); } } } } parse(code); this.pathCommands = pathCommands; if (haveWidth) { this.advanceWidth = width; } } } class GlyphSet { constructor(_sfnt) { this._sfnt = _sfnt; } _items = []; get(index) { const _glyph = this._items[index]; let glyph; if (_glyph) { glyph = _glyph; } else { glyph = this._get(index); const metric = this._sfnt.hmtx.metrics[index]; if (metric) { glyph.advanceWidth = glyph.advanceWidth || metric.advanceWidth; glyph.leftSideBearing = glyph.leftSideBearing || metric.leftSideBearing; } const unicodes = this._sfnt.cmap.glyphIndexToUnicodesMap.get(index); if (unicodes) { glyph.unicode ??= unicodes[0]; glyph.unicodes ??= unicodes; } this._items[index] = glyph; } return glyph; } } class CffGlyphSet extends GlyphSet { get length() { return this._sfnt.cff.charStringsIndex.offsets.length - 1; } _get(index) { const cff = this._sfnt.cff; const glyph = new CffGlyph({ index }); glyph.parse(cff, cff.charStringsIndex.get(index), this); glyph.name = cff.charset[index]; return glyph; } } var __defProp$l = Object.defineProperty; var __decorateClass$r = (decorators, target, key, kind) => { var result = void 0 ; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (decorator(target, key, result) ) || result; if (result) __defProp$l(target, key, result); return result; }; class CffIndex extends FontDataObject { _offsets; get offsets() { return this._offsets ??= this.readOffsets(); } _objects; get objects() { return this._objects ??= this.readObjects(); } constructor(source, byteOffset, byteLength, littleEndian) { super(source, byteOffset, byteLength, littleEndian); this._init(); } _init() { const reader = this.view; const count = this.count; const offsetSize = this.offsetSize; this.objectOffset = (count + 1) * offsetSize + 2; this.endOffset = reader.byteOffset + this.objectOffset + this.offsets[count]; } readOffsets() { const reader = this.view; const count = this.count; const offsetSize = this.offsetSize; reader.seek(3); const offsets = []; for (let i = 0, l = count + 1; i < l; i++) { const reader2 = this.view; let v = 0; for (let i2 = 0; i2 < offsetSize; i2++) { v <<= 8; v += reader2.readUint8(); } offsets.push(v); } return offsets; } readObjects() { const objects = []; for (let i = 0, len = this.count; i < len; i++) { objects.push(this.get(i)); } return objects; } get(index) { const offsets = this.offsets; const objectOffset = this.objectOffset; const start = objectOffset + offsets[index]; const end = objectOffset + offsets[index + 1]; const len = end - start; if (this._isString) { return this.view.readString(start, len); } else { return this.view.readBytes(start, len); } } } __decorateClass$r([ defineColumn("uint16") ], CffIndex.prototype, "count"); __decorateClass$r([ defineColumn("uint8") ], CffIndex.prototype, "offsetSize"); class CffNumberIndex extends CffIndex { _isString = false; } class CffStringIndex extends CffIndex { _isString = true; } const cffStandardStrings = [ ".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", "endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis", "perthousand", "questiondown", "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE", "ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls", "onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus", "Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn", "threequarters", "twosuperior", "registered", "minus", "eth", "multiply", "threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave", "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute", "acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis", "igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde", "scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall", "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "266 ff", "onedotenleader", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "commasuperior", "threequartersemdash", "periodsuperior", "questionsmall", "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior", "tsuperior", "ff", "ffi", "ffl", "parenleftinferior", "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall", "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", "zerosuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall", "001.000", "001.001", "001.002", "001.003", "Black", "Bold", "Book", "Light", "Medium", "Regular", "Roman", "Semibold" ]; const cffISOAdobeStrings = [ ".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", "endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis", "perthousand", "questiondown", "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE", "ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls", "onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus", "Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn", "threequarters", "twosuperior", "registered", "minus", "eth", "multiply", "threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave", "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute", "acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis", "igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde", "scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis", "zcaron" ]; const cffIExpertStrings = [ ".notdef", "space", "exclamsmall", "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon", "commasuperior", "threequartersemdash", "periodsuperior", "questionsmall", "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior", "tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior", "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall", "onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", "Icircumfle