8147 lines
321 KiB
JavaScript
8147 lines
321 KiB
JavaScript
|
|
import inflate from "tiny-inflate";
|
|||
|
|
|
|||
|
|
//#region ../@fontkitten/restructure/dist/index.js
|
|||
|
|
const Latin1Decoder = new TextDecoder("latin1");
|
|||
|
|
var DecodeStream = class DecodeStream {
|
|||
|
|
#view;
|
|||
|
|
pos;
|
|||
|
|
length;
|
|||
|
|
constructor(buffer) {
|
|||
|
|
this.buffer = buffer;
|
|||
|
|
this.#view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|||
|
|
this.pos = 0;
|
|||
|
|
this.length = buffer.length;
|
|||
|
|
}
|
|||
|
|
readString(length, encoding = "ascii") {
|
|||
|
|
const buf = this.readBuffer(length);
|
|||
|
|
try {
|
|||
|
|
return new TextDecoder(encoding).decode(buf);
|
|||
|
|
} catch {
|
|||
|
|
return Latin1Decoder.decode(buf);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
readBuffer(length) {
|
|||
|
|
return this.buffer.slice(this.pos, this.pos += length);
|
|||
|
|
}
|
|||
|
|
readUInt8() {
|
|||
|
|
const ret = this.#view.getUint8(this.pos);
|
|||
|
|
this.pos += DecodeStream.TYPES.UInt8;
|
|||
|
|
return ret;
|
|||
|
|
}
|
|||
|
|
readUInt16BE() {
|
|||
|
|
const ret = this.#view.getUint16(this.pos);
|
|||
|
|
this.pos += DecodeStream.TYPES.UInt16;
|
|||
|
|
return ret;
|
|||
|
|
}
|
|||
|
|
readUInt32BE() {
|
|||
|
|
const ret = this.#view.getUint32(this.pos);
|
|||
|
|
this.pos += DecodeStream.TYPES.UInt32;
|
|||
|
|
return ret;
|
|||
|
|
}
|
|||
|
|
readInt8() {
|
|||
|
|
const ret = this.#view.getInt8(this.pos);
|
|||
|
|
this.pos += DecodeStream.TYPES.Int8;
|
|||
|
|
return ret;
|
|||
|
|
}
|
|||
|
|
readInt16BE() {
|
|||
|
|
const ret = this.#view.getInt16(this.pos);
|
|||
|
|
this.pos += DecodeStream.TYPES.Int16;
|
|||
|
|
return ret;
|
|||
|
|
}
|
|||
|
|
readUInt24BE() {
|
|||
|
|
return (this.readUInt16BE() << 8) + this.readUInt8();
|
|||
|
|
}
|
|||
|
|
readInt32BE() {
|
|||
|
|
const ret = this.#view.getInt32(this.pos);
|
|||
|
|
this.pos += DecodeStream.TYPES.Int32;
|
|||
|
|
return ret;
|
|||
|
|
}
|
|||
|
|
static TYPES = {
|
|||
|
|
UInt8: 1,
|
|||
|
|
UInt16: 2,
|
|||
|
|
UInt24: 3,
|
|||
|
|
UInt32: 4,
|
|||
|
|
Int8: 1,
|
|||
|
|
Int16: 2,
|
|||
|
|
Int32: 4
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
var NumberT = class {
|
|||
|
|
#size;
|
|||
|
|
#readFnName;
|
|||
|
|
constructor(type) {
|
|||
|
|
this.#readFnName = type === "Int8" || type === "UInt8" ? `read${type}` : `read${type}BE`;
|
|||
|
|
this.#size = DecodeStream.TYPES[type];
|
|||
|
|
}
|
|||
|
|
size() {
|
|||
|
|
return this.#size;
|
|||
|
|
}
|
|||
|
|
decode(stream) {
|
|||
|
|
return stream[this.#readFnName]();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const uint8 = new NumberT("UInt8");
|
|||
|
|
const uint16 = new NumberT("UInt16");
|
|||
|
|
const uint24 = new NumberT("UInt24");
|
|||
|
|
const uint32 = new NumberT("UInt32");
|
|||
|
|
const int8 = new NumberT("Int8");
|
|||
|
|
const int16 = new NumberT("Int16");
|
|||
|
|
const int32 = new NumberT("Int32");
|
|||
|
|
var Fixed = class extends NumberT {
|
|||
|
|
#point;
|
|||
|
|
constructor(size, fracBits = size >> 1) {
|
|||
|
|
super(`Int${size}`);
|
|||
|
|
this.#point = 1 << fracBits;
|
|||
|
|
}
|
|||
|
|
decode(stream) {
|
|||
|
|
return super.decode(stream) / this.#point;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const fixed16 = new Fixed(16);
|
|||
|
|
const fixed32 = new Fixed(32);
|
|||
|
|
function resolveLength(length, stream, parent) {
|
|||
|
|
let res;
|
|||
|
|
if (typeof length === "number") res = length;
|
|||
|
|
else if (typeof length === "function") res = length.call(parent, parent);
|
|||
|
|
else if (parent && typeof length === "string") res = parent[length];
|
|||
|
|
else if (stream && length instanceof NumberT) res = length.decode(stream);
|
|||
|
|
if (isNaN(res)) throw new Error("Not a fixed size");
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
var PropertyDescriptor = class {
|
|||
|
|
enumerable = true;
|
|||
|
|
configurable = true;
|
|||
|
|
constructor(opts = {}) {
|
|||
|
|
for (const key in opts) this[key] = opts[key];
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var ArrayT = class {
|
|||
|
|
#lengthType;
|
|||
|
|
constructor(type, length, lengthType = "count") {
|
|||
|
|
this.type = type;
|
|||
|
|
this.length = length;
|
|||
|
|
this.#lengthType = lengthType;
|
|||
|
|
}
|
|||
|
|
decode(stream, parent) {
|
|||
|
|
let length;
|
|||
|
|
const { pos } = stream;
|
|||
|
|
const res = [];
|
|||
|
|
let ctx = parent;
|
|||
|
|
if (this.length != null) length = resolveLength(this.length, stream, parent);
|
|||
|
|
if (this.length instanceof NumberT) {
|
|||
|
|
Object.defineProperties(res, {
|
|||
|
|
parent: { value: parent },
|
|||
|
|
_startOffset: { value: pos },
|
|||
|
|
_currentOffset: {
|
|||
|
|
value: 0,
|
|||
|
|
writable: true
|
|||
|
|
},
|
|||
|
|
_length: { value: length }
|
|||
|
|
});
|
|||
|
|
ctx = res;
|
|||
|
|
}
|
|||
|
|
if (length == null || this.#lengthType === "bytes") {
|
|||
|
|
const target = length != null ? stream.pos + length : (parent != null ? parent._length : void 0) ? parent._startOffset + parent._length : stream.length;
|
|||
|
|
while (stream.pos < target) res.push(this.type.decode(stream, ctx));
|
|||
|
|
} else for (let i = 0, end = length; i < end; i++) res.push(this.type.decode(stream, ctx));
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var LazyArray = class extends ArrayT {
|
|||
|
|
decode(stream, parent) {
|
|||
|
|
const { pos } = stream;
|
|||
|
|
const length = resolveLength(this.length, stream, parent);
|
|||
|
|
if (this.length instanceof NumberT) parent = {
|
|||
|
|
parent,
|
|||
|
|
_startOffset: pos,
|
|||
|
|
_currentOffset: 0,
|
|||
|
|
_length: length
|
|||
|
|
};
|
|||
|
|
const res = new LazyArrayValue(this.type, length, stream, parent);
|
|||
|
|
stream.pos += length * this.type.size(null, parent);
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var LazyArrayValue = class {
|
|||
|
|
#type;
|
|||
|
|
#stream;
|
|||
|
|
#ctx;
|
|||
|
|
#base;
|
|||
|
|
#items;
|
|||
|
|
length;
|
|||
|
|
constructor(type, length, stream, ctx) {
|
|||
|
|
this.#type = type;
|
|||
|
|
this.length = length;
|
|||
|
|
this.#stream = stream;
|
|||
|
|
this.#ctx = ctx;
|
|||
|
|
this.#base = this.#stream.pos;
|
|||
|
|
this.#items = [];
|
|||
|
|
}
|
|||
|
|
get(index) {
|
|||
|
|
if (index < 0 || index >= this.length) return;
|
|||
|
|
if (this.#items[index] == null) {
|
|||
|
|
const { pos } = this.#stream;
|
|||
|
|
this.#stream.pos = this.#base + this.#type.size(null, this.#ctx) * index;
|
|||
|
|
this.#items[index] = this.#type.decode(this.#stream, this.#ctx);
|
|||
|
|
this.#stream.pos = pos;
|
|||
|
|
}
|
|||
|
|
return this.#items[index];
|
|||
|
|
}
|
|||
|
|
toArray() {
|
|||
|
|
const result = [];
|
|||
|
|
for (let i = 0, end = this.length; i < end; i++) result.push(this.get(i));
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var Bitfield = class {
|
|||
|
|
#type;
|
|||
|
|
#flags;
|
|||
|
|
constructor(type, flags = []) {
|
|||
|
|
this.#type = type;
|
|||
|
|
this.#flags = flags;
|
|||
|
|
}
|
|||
|
|
decode(stream) {
|
|||
|
|
const val = this.#type.decode(stream);
|
|||
|
|
const res = {};
|
|||
|
|
for (let i = 0; i < this.#flags.length; i++) {
|
|||
|
|
const flag = this.#flags[i];
|
|||
|
|
if (flag != null) res[flag] = !!(val & 1 << i);
|
|||
|
|
}
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var BufferT = class {
|
|||
|
|
#length;
|
|||
|
|
constructor(length) {
|
|||
|
|
this.#length = length;
|
|||
|
|
}
|
|||
|
|
decode(stream, parent) {
|
|||
|
|
const length = resolveLength(this.#length, stream, parent);
|
|||
|
|
return stream.readBuffer(length);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var Optional = class {
|
|||
|
|
#type;
|
|||
|
|
#condition;
|
|||
|
|
constructor(type, condition = true) {
|
|||
|
|
this.#type = type;
|
|||
|
|
this.#condition = condition;
|
|||
|
|
}
|
|||
|
|
decode(stream, parent) {
|
|||
|
|
if (typeof this.#condition === "function" ? this.#condition.call(parent, parent) : this.#condition) return this.#type.decode(stream, parent);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var Reserved = class {
|
|||
|
|
#type;
|
|||
|
|
#count;
|
|||
|
|
constructor(type, count = 1) {
|
|||
|
|
this.#type = type;
|
|||
|
|
this.#count = count;
|
|||
|
|
}
|
|||
|
|
decode(stream, parent) {
|
|||
|
|
stream.pos += this.size(null, parent);
|
|||
|
|
}
|
|||
|
|
size(data, parent) {
|
|||
|
|
const count = resolveLength(this.#count, null, parent);
|
|||
|
|
return this.#type.size() * count;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var StringT = class {
|
|||
|
|
#length;
|
|||
|
|
#encoding;
|
|||
|
|
constructor(length, encoding = "ascii") {
|
|||
|
|
this.#length = length;
|
|||
|
|
this.#encoding = encoding;
|
|||
|
|
}
|
|||
|
|
decode(stream, parent) {
|
|||
|
|
const encoding = typeof this.#encoding === "function" ? this.#encoding.call(parent, parent) || "ascii" : this.#encoding;
|
|||
|
|
const width = encodingWidth(encoding);
|
|||
|
|
const length = resolveLength(this.#length, stream, parent);
|
|||
|
|
const string = stream.readString(length, encoding);
|
|||
|
|
if (this.#length == null && stream.pos < stream.length) stream.pos += width;
|
|||
|
|
return string;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
function encodingWidth(encoding) {
|
|||
|
|
switch (encoding) {
|
|||
|
|
case "ascii":
|
|||
|
|
case "utf8": return 1;
|
|||
|
|
case "utf-16be":
|
|||
|
|
case "utf-16le":
|
|||
|
|
case "utf16be":
|
|||
|
|
case "utf16-be":
|
|||
|
|
case "ucs2": return 2;
|
|||
|
|
default: return 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
var Struct = class {
|
|||
|
|
#fields;
|
|||
|
|
process;
|
|||
|
|
constructor(fields = {}) {
|
|||
|
|
this.#fields = fields;
|
|||
|
|
}
|
|||
|
|
decode(stream, parent, length = 0) {
|
|||
|
|
const res = this._setup(stream, parent, length);
|
|||
|
|
this._parseFields(stream, res, this.#fields);
|
|||
|
|
if (this.process != null) this.process.call(res, stream);
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
_setup(stream, parent, length) {
|
|||
|
|
const res = {};
|
|||
|
|
Object.defineProperties(res, {
|
|||
|
|
parent: { value: parent },
|
|||
|
|
_startOffset: { value: stream.pos },
|
|||
|
|
_currentOffset: {
|
|||
|
|
value: 0,
|
|||
|
|
writable: true
|
|||
|
|
},
|
|||
|
|
_length: { value: length }
|
|||
|
|
});
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
_parseFields(stream, res, fields) {
|
|||
|
|
for (const key in fields) {
|
|||
|
|
let val;
|
|||
|
|
const type = fields[key];
|
|||
|
|
if (typeof type === "function") val = type.call(res, res);
|
|||
|
|
else val = type.decode(stream, res);
|
|||
|
|
if (val !== void 0) if (val instanceof PropertyDescriptor) Object.defineProperty(res, key, val);
|
|||
|
|
else res[key] = val;
|
|||
|
|
res._currentOffset = stream.pos - res._startOffset;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
size(val, parent, includePointers = true) {
|
|||
|
|
if (val == null) val = {};
|
|||
|
|
const ctx = {
|
|||
|
|
parent,
|
|||
|
|
val,
|
|||
|
|
pointerSize: 0
|
|||
|
|
};
|
|||
|
|
let size = 0;
|
|||
|
|
for (let key in this.#fields) {
|
|||
|
|
const type = this.#fields[key];
|
|||
|
|
if (typeof type !== "function" && "size" in type && type.size != null) size += type.size(val[key], ctx);
|
|||
|
|
}
|
|||
|
|
if (includePointers) size += ctx.pointerSize;
|
|||
|
|
return size;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const getPath = (object, pathArray) => {
|
|||
|
|
return pathArray.reduce((prevObj, key) => prevObj && prevObj[key], object);
|
|||
|
|
};
|
|||
|
|
var VersionedStruct = class VersionedStruct extends Struct {
|
|||
|
|
#type;
|
|||
|
|
#versionPath;
|
|||
|
|
constructor(type, versions) {
|
|||
|
|
super();
|
|||
|
|
this.versions = versions;
|
|||
|
|
this.#type = type;
|
|||
|
|
if (typeof type === "string") this.#versionPath = type.split(".");
|
|||
|
|
}
|
|||
|
|
decode(stream, parent, length = 0) {
|
|||
|
|
const res = this._setup(stream, parent, length);
|
|||
|
|
if (typeof this.#type === "string") res.version = getPath(parent, this.#versionPath);
|
|||
|
|
else res.version = this.#type.decode(stream);
|
|||
|
|
if (this.versions.header) this._parseFields(stream, res, this.versions.header);
|
|||
|
|
const fields = this.versions[res.version];
|
|||
|
|
if (fields == null) throw new Error(`Unknown version ${res.version}`);
|
|||
|
|
if (fields instanceof VersionedStruct) return fields.decode(stream, parent);
|
|||
|
|
this._parseFields(stream, res, fields);
|
|||
|
|
if (this.process != null) this.process.call(res, stream);
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var Pointer = class {
|
|||
|
|
#type;
|
|||
|
|
#options;
|
|||
|
|
constructor(offsetType, type, options = {}) {
|
|||
|
|
this.offsetType = offsetType;
|
|||
|
|
this.#type = type === "void" ? null : type;
|
|||
|
|
this.#options = {
|
|||
|
|
type: "local",
|
|||
|
|
allowNull: true,
|
|||
|
|
nullValue: 0,
|
|||
|
|
lazy: false,
|
|||
|
|
...options
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
decode(stream, ctx) {
|
|||
|
|
const offset = this.offsetType.decode(stream, ctx);
|
|||
|
|
if (offset === this.#options.nullValue && this.#options.allowNull) return null;
|
|||
|
|
let relative;
|
|||
|
|
switch (this.#options.type) {
|
|||
|
|
case "local":
|
|||
|
|
relative = ctx._startOffset;
|
|||
|
|
break;
|
|||
|
|
case "parent":
|
|||
|
|
relative = ctx.parent._startOffset;
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
var c = ctx;
|
|||
|
|
while (c.parent) c = c.parent;
|
|||
|
|
relative = c._startOffset || 0;
|
|||
|
|
}
|
|||
|
|
if (this.#options.relativeTo) relative += this.#options.relativeTo(ctx);
|
|||
|
|
const ptr = offset + relative;
|
|||
|
|
if (this.#type != null) {
|
|||
|
|
let val = null;
|
|||
|
|
const decodeValue = () => {
|
|||
|
|
if (val != null) return val;
|
|||
|
|
const { pos } = stream;
|
|||
|
|
stream.pos = ptr;
|
|||
|
|
val = this.#type.decode(stream, ctx);
|
|||
|
|
stream.pos = pos;
|
|||
|
|
return val;
|
|||
|
|
};
|
|||
|
|
if (this.#options.lazy) return new PropertyDescriptor({ get: decodeValue });
|
|||
|
|
return decodeValue();
|
|||
|
|
} else return ptr;
|
|||
|
|
}
|
|||
|
|
size() {
|
|||
|
|
return this.offsetType.size();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/decorators.ts
|
|||
|
|
/**
|
|||
|
|
* This decorator caches the results of a getter or method such that
|
|||
|
|
* the results are lazily computed once, and then cached.
|
|||
|
|
*/
|
|||
|
|
function cache(_target, key, descriptor) {
|
|||
|
|
if (descriptor.get) {
|
|||
|
|
const get = descriptor.get;
|
|||
|
|
descriptor.get = function() {
|
|||
|
|
const value = get.call(this);
|
|||
|
|
Object.defineProperty(this, key, { value });
|
|||
|
|
return value;
|
|||
|
|
};
|
|||
|
|
} else if (typeof descriptor.value === "function") {
|
|||
|
|
const fn = descriptor.value;
|
|||
|
|
return { get() {
|
|||
|
|
const cache = /* @__PURE__ */ new Map();
|
|||
|
|
const memoized = ((...args) => {
|
|||
|
|
const key = args.length > 0 ? args[0] : "value";
|
|||
|
|
if (cache.has(key)) return cache.get(key);
|
|||
|
|
const result = fn.apply(this, args);
|
|||
|
|
cache.set(key, result);
|
|||
|
|
return result;
|
|||
|
|
});
|
|||
|
|
Object.defineProperty(this, key, { value: memoized });
|
|||
|
|
return memoized;
|
|||
|
|
} };
|
|||
|
|
}
|
|||
|
|
return descriptor;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/directory.ts
|
|||
|
|
const TableEntry = new Struct({
|
|||
|
|
tag: new StringT(4),
|
|||
|
|
checkSum: uint32,
|
|||
|
|
offset: new Pointer(uint32, "void", { type: "global" }),
|
|||
|
|
length: uint32
|
|||
|
|
});
|
|||
|
|
const Directory = new Struct({
|
|||
|
|
tag: new StringT(4),
|
|||
|
|
numTables: uint16,
|
|||
|
|
searchRange: uint16,
|
|||
|
|
entrySelector: uint16,
|
|||
|
|
rangeShift: uint16,
|
|||
|
|
tables: new ArrayT(TableEntry, "numTables")
|
|||
|
|
});
|
|||
|
|
Directory.process = function() {
|
|||
|
|
this.tables = Object.fromEntries(this.tables.map((table) => [table.tag, table]));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/cmap.ts
|
|||
|
|
const SubHeader = new Struct({
|
|||
|
|
firstCode: uint16,
|
|||
|
|
entryCount: uint16,
|
|||
|
|
idDelta: int16,
|
|||
|
|
idRangeOffset: uint16
|
|||
|
|
});
|
|||
|
|
const CmapGroup = new Struct({
|
|||
|
|
startCharCode: uint32,
|
|||
|
|
endCharCode: uint32,
|
|||
|
|
glyphID: uint32
|
|||
|
|
});
|
|||
|
|
const UnicodeValueRange = new Struct({
|
|||
|
|
startUnicodeValue: uint24,
|
|||
|
|
additionalCount: uint8
|
|||
|
|
});
|
|||
|
|
const UVSMapping = new Struct({
|
|||
|
|
unicodeValue: uint24,
|
|||
|
|
glyphID: uint16
|
|||
|
|
});
|
|||
|
|
const DefaultUVS = new ArrayT(UnicodeValueRange, uint32);
|
|||
|
|
const NonDefaultUVS = new ArrayT(UVSMapping, uint32);
|
|||
|
|
const VarSelectorRecord = new Struct({
|
|||
|
|
varSelector: uint24,
|
|||
|
|
defaultUVS: new Pointer(uint32, DefaultUVS, { type: "parent" }),
|
|||
|
|
nonDefaultUVS: new Pointer(uint32, NonDefaultUVS, { type: "parent" })
|
|||
|
|
});
|
|||
|
|
const CmapSubtable = new VersionedStruct(uint16, {
|
|||
|
|
0: {
|
|||
|
|
length: uint16,
|
|||
|
|
language: uint16,
|
|||
|
|
codeMap: new LazyArray(uint8, 256)
|
|||
|
|
},
|
|||
|
|
2: {
|
|||
|
|
length: uint16,
|
|||
|
|
language: uint16,
|
|||
|
|
subHeaderKeys: new ArrayT(uint16, 256),
|
|||
|
|
subHeaderCount: (t) => Math.max.apply(Math, t.subHeaderKeys),
|
|||
|
|
subHeaders: new LazyArray(SubHeader, "subHeaderCount"),
|
|||
|
|
glyphIndexArray: new LazyArray(uint16, "subHeaderCount")
|
|||
|
|
},
|
|||
|
|
4: {
|
|||
|
|
length: uint16,
|
|||
|
|
language: uint16,
|
|||
|
|
segCountX2: uint16,
|
|||
|
|
segCount: (t) => t.segCountX2 >> 1,
|
|||
|
|
searchRange: uint16,
|
|||
|
|
entrySelector: uint16,
|
|||
|
|
rangeShift: uint16,
|
|||
|
|
endCode: new LazyArray(uint16, "segCount"),
|
|||
|
|
reservedPad: new Reserved(uint16),
|
|||
|
|
startCode: new LazyArray(uint16, "segCount"),
|
|||
|
|
idDelta: new LazyArray(int16, "segCount"),
|
|||
|
|
idRangeOffset: new LazyArray(uint16, "segCount"),
|
|||
|
|
glyphIndexArray: new LazyArray(uint16, (t) => (t.length - t._currentOffset) / 2)
|
|||
|
|
},
|
|||
|
|
6: {
|
|||
|
|
length: uint16,
|
|||
|
|
language: uint16,
|
|||
|
|
firstCode: uint16,
|
|||
|
|
entryCount: uint16,
|
|||
|
|
glyphIndices: new LazyArray(uint16, "entryCount")
|
|||
|
|
},
|
|||
|
|
8: {
|
|||
|
|
reserved: new Reserved(uint16),
|
|||
|
|
length: uint32,
|
|||
|
|
language: uint16,
|
|||
|
|
is32: new LazyArray(uint8, 8192),
|
|||
|
|
nGroups: uint32,
|
|||
|
|
groups: new LazyArray(CmapGroup, "nGroups")
|
|||
|
|
},
|
|||
|
|
10: {
|
|||
|
|
reserved: new Reserved(uint16),
|
|||
|
|
length: uint32,
|
|||
|
|
language: uint32,
|
|||
|
|
firstCode: uint32,
|
|||
|
|
entryCount: uint32,
|
|||
|
|
glyphIndices: new LazyArray(uint16, "numChars")
|
|||
|
|
},
|
|||
|
|
12: {
|
|||
|
|
reserved: new Reserved(uint16),
|
|||
|
|
length: uint32,
|
|||
|
|
language: uint32,
|
|||
|
|
nGroups: uint32,
|
|||
|
|
groups: new LazyArray(CmapGroup, "nGroups")
|
|||
|
|
},
|
|||
|
|
13: {
|
|||
|
|
reserved: new Reserved(uint16),
|
|||
|
|
length: uint32,
|
|||
|
|
language: uint32,
|
|||
|
|
nGroups: uint32,
|
|||
|
|
groups: new LazyArray(CmapGroup, "nGroups")
|
|||
|
|
},
|
|||
|
|
14: {
|
|||
|
|
length: uint32,
|
|||
|
|
numRecords: uint32,
|
|||
|
|
varSelectors: new LazyArray(VarSelectorRecord, "numRecords")
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
const CmapEntry = new Struct({
|
|||
|
|
platformID: uint16,
|
|||
|
|
encodingID: uint16,
|
|||
|
|
table: new Pointer(uint32, CmapSubtable, {
|
|||
|
|
type: "parent",
|
|||
|
|
lazy: true
|
|||
|
|
})
|
|||
|
|
});
|
|||
|
|
var cmap_default = new Struct({
|
|||
|
|
version: uint16,
|
|||
|
|
numSubtables: uint16,
|
|||
|
|
tables: new ArrayT(CmapEntry, "numSubtables")
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/head.ts
|
|||
|
|
var head_default = new Struct({
|
|||
|
|
version: int32,
|
|||
|
|
revision: int32,
|
|||
|
|
checkSumAdjustment: uint32,
|
|||
|
|
magicNumber: uint32,
|
|||
|
|
flags: uint16,
|
|||
|
|
unitsPerEm: uint16,
|
|||
|
|
created: new ArrayT(int32, 2),
|
|||
|
|
modified: new ArrayT(int32, 2),
|
|||
|
|
xMin: int16,
|
|||
|
|
yMin: int16,
|
|||
|
|
xMax: int16,
|
|||
|
|
yMax: int16,
|
|||
|
|
macStyle: new Bitfield(uint16, [
|
|||
|
|
"bold",
|
|||
|
|
"italic",
|
|||
|
|
"underline",
|
|||
|
|
"outline",
|
|||
|
|
"shadow",
|
|||
|
|
"condensed",
|
|||
|
|
"extended"
|
|||
|
|
]),
|
|||
|
|
lowestRecPPEM: uint16,
|
|||
|
|
fontDirectionHint: int16,
|
|||
|
|
indexToLocFormat: int16,
|
|||
|
|
glyphDataFormat: int16
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/hhea.ts
|
|||
|
|
var hhea_default = new Struct({
|
|||
|
|
version: int32,
|
|||
|
|
ascent: int16,
|
|||
|
|
descent: int16,
|
|||
|
|
lineGap: int16,
|
|||
|
|
advanceWidthMax: uint16,
|
|||
|
|
minLeftSideBearing: int16,
|
|||
|
|
minRightSideBearing: int16,
|
|||
|
|
xMaxExtent: int16,
|
|||
|
|
caretSlopeRise: int16,
|
|||
|
|
caretSlopeRun: int16,
|
|||
|
|
caretOffset: int16,
|
|||
|
|
reserved: new Reserved(int16, 4),
|
|||
|
|
metricDataFormat: int16,
|
|||
|
|
numberOfMetrics: uint16
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/hmtx.ts
|
|||
|
|
const HmtxEntry = new Struct({
|
|||
|
|
advance: uint16,
|
|||
|
|
bearing: int16
|
|||
|
|
});
|
|||
|
|
var hmtx_default = new Struct({
|
|||
|
|
metrics: new LazyArray(HmtxEntry, (t) => t.parent.hhea.numberOfMetrics),
|
|||
|
|
bearings: new LazyArray(int16, (t) => t.parent.maxp.numGlyphs - t.parent.hhea.numberOfMetrics)
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/maxp.ts
|
|||
|
|
var maxp_default = new Struct({
|
|||
|
|
version: int32,
|
|||
|
|
numGlyphs: uint16,
|
|||
|
|
maxPoints: uint16,
|
|||
|
|
maxContours: uint16,
|
|||
|
|
maxComponentPoints: uint16,
|
|||
|
|
maxComponentContours: uint16,
|
|||
|
|
maxZones: uint16,
|
|||
|
|
maxTwilightPoints: uint16,
|
|||
|
|
maxStorage: uint16,
|
|||
|
|
maxFunctionDefs: uint16,
|
|||
|
|
maxInstructionDefs: uint16,
|
|||
|
|
maxStackElements: uint16,
|
|||
|
|
maxSizeOfInstructions: uint16,
|
|||
|
|
maxComponentElements: uint16,
|
|||
|
|
maxComponentDepth: uint16
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/encodings.ts
|
|||
|
|
/**
|
|||
|
|
* Gets an encoding name from platform, encoding, and language ids.
|
|||
|
|
* Returned encoding names can be used in iconv-lite to decode text.
|
|||
|
|
*/
|
|||
|
|
function getEncoding(platformID, encodingID, languageID = 0) {
|
|||
|
|
return platformID === 1 && MAC_LANGUAGE_ENCODINGS[languageID] ? MAC_LANGUAGE_ENCODINGS[languageID] : ENCODINGS[platformID][encodingID];
|
|||
|
|
}
|
|||
|
|
const SINGLE_BYTE_ENCODINGS = new Set([
|
|||
|
|
"x-mac-roman",
|
|||
|
|
"x-mac-cyrillic",
|
|||
|
|
"iso-8859-6",
|
|||
|
|
"iso-8859-8"
|
|||
|
|
]);
|
|||
|
|
const MAC_ENCODINGS = {
|
|||
|
|
"x-mac-croatian": "ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®Š™´¨≠ŽØ∞±≤≥∆µ∂∑∏š∫ªºΩžø¿¡¬√ƒ≈ƫȅ ÀÃÕŒœĐ—“”‘’÷◊©⁄€‹›Æ»–·‚„‰ÂćÁčÈÍÎÏÌÓÔđÒÚÛÙıˆ˜¯πË˚¸Êæˇ",
|
|||
|
|
"x-mac-gaelic": "ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØḂ±≤≥ḃĊċḊḋḞḟĠġṀæøṁṖṗɼƒſṠ«»… ÀÃÕŒœ–—“”‘’ṡẛÿŸṪ€‹›Ŷŷṫ·Ỳỳ⁊ÂÊÁËÈÍÎÏÌÓÔ♣ÒÚÛÙıÝýŴŵẄẅẀẁẂẃ",
|
|||
|
|
"x-mac-greek": "Ĺ²É³ÖÜ΅àâä΄¨çéèê룙î‰ôö¦€ùûü†ΓΔΘΛΞΠß®©ΣΪ§≠°·Α±≤≥¥ΒΕΖΗΙΚΜΦΫΨΩάΝ¬ΟΡ≈Τ«»… ΥΧΆΈœ–―“”‘’÷ΉΊΌΎέήίόΏύαβψδεφγηιξκλμνοπώρστθωςχυζϊϋΐΰ",
|
|||
|
|
"x-mac-icelandic": "ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûüݰ¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€ÐðÞþý·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ",
|
|||
|
|
"x-mac-inuit": "ᐃᐄᐅᐆᐊᐋᐱᐲᐳᐴᐸᐹᑉᑎᑏᑐᑑᑕᑖᑦᑭᑮᑯᑰᑲᑳᒃᒋᒌᒍᒎᒐᒑ°ᒡᒥᒦ•¶ᒧ®©™ᒨᒪᒫᒻᓂᓃᓄᓅᓇᓈᓐᓯᓰᓱᓲᓴᓵᔅᓕᓖᓗᓘᓚᓛᓪᔨᔩᔪᔫᔭ… ᔮᔾᕕᕖᕗ–—“”‘’ᕘᕙᕚᕝᕆᕇᕈᕉᕋᕌᕐᕿᖀᖁᖂᖃᖄᖅᖏᖐᖑᖒᖓᖔᖕᙱᙲᙳᙴᙵᙶᖖᖠᖡᖢᖣᖤᖥᖦᕼŁł",
|
|||
|
|
"x-mac-ce": "ÄĀāÉĄÖÜáąČäčĆć鏟ĎíďĒēĖóėôöõúĚěü†°Ę£§•¶ß®©™ę¨≠ģĮįĪ≤≥īĶ∂∑łĻļĽľĹĺŅņѬ√ńŇ∆«»… ňŐÕőŌ–—“”‘’÷◊ōŔŕŘ‹›řŖŗŠ‚„šŚśÁŤťÍŽžŪÓÔūŮÚůŰűŲųÝýķŻŁżĢˇ",
|
|||
|
|
"x-mac-romanian": "ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ĂȘ∞±≤≥¥µ∂∑∏π∫ªºΩăș¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€‹›Țț‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ",
|
|||
|
|
"x-mac-turkish": "ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸĞğİıŞş‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙˆ˜¯˘˙˚¸˝˛ˇ"
|
|||
|
|
};
|
|||
|
|
const encodingCache = /* @__PURE__ */ new Map();
|
|||
|
|
function getEncodingMapping(encoding) {
|
|||
|
|
const cached = encodingCache.get(encoding);
|
|||
|
|
if (cached) return cached;
|
|||
|
|
const mapping = MAC_ENCODINGS[encoding];
|
|||
|
|
if (mapping) {
|
|||
|
|
const res = /* @__PURE__ */ new Map();
|
|||
|
|
for (let i = 0; i < mapping.length; i++) res.set(mapping.charCodeAt(i), 128 + i);
|
|||
|
|
encodingCache.set(encoding, res);
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
if (SINGLE_BYTE_ENCODINGS.has(encoding)) {
|
|||
|
|
const decoder = new TextDecoder(encoding);
|
|||
|
|
const mapping = new Uint8Array(128);
|
|||
|
|
for (let i = 0; i < 128; i++) mapping[i] = 128 + i;
|
|||
|
|
const res = /* @__PURE__ */ new Map();
|
|||
|
|
const s = decoder.decode(mapping);
|
|||
|
|
for (let i = 0; i < 128; i++) res.set(s.charCodeAt(i), 128 + i);
|
|||
|
|
encodingCache.set(encoding, res);
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
const ENCODINGS = [
|
|||
|
|
[
|
|||
|
|
"utf-16be",
|
|||
|
|
"utf-16be",
|
|||
|
|
"utf-16be",
|
|||
|
|
"utf-16be",
|
|||
|
|
"utf-16be",
|
|||
|
|
"utf-16be",
|
|||
|
|
"utf-16be"
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
"x-mac-roman",
|
|||
|
|
"shift-jis",
|
|||
|
|
"big5",
|
|||
|
|
"euc-kr",
|
|||
|
|
"iso-8859-6",
|
|||
|
|
"iso-8859-8",
|
|||
|
|
"x-mac-greek",
|
|||
|
|
"x-mac-cyrillic",
|
|||
|
|
"x-mac-symbol",
|
|||
|
|
"x-mac-devanagari",
|
|||
|
|
"x-mac-gurmukhi",
|
|||
|
|
"x-mac-gujarati",
|
|||
|
|
"Oriya",
|
|||
|
|
"Bengali",
|
|||
|
|
"Tamil",
|
|||
|
|
"Telugu",
|
|||
|
|
"Kannada",
|
|||
|
|
"Malayalam",
|
|||
|
|
"Sinhalese",
|
|||
|
|
"Burmese",
|
|||
|
|
"Khmer",
|
|||
|
|
"iso-8859-11",
|
|||
|
|
"Laotian",
|
|||
|
|
"Georgian",
|
|||
|
|
"Armenian",
|
|||
|
|
"gbk",
|
|||
|
|
"Tibetan",
|
|||
|
|
"Mongolian",
|
|||
|
|
"Geez",
|
|||
|
|
"x-mac-ce",
|
|||
|
|
"Vietnamese",
|
|||
|
|
"Sindhi"
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
"ascii",
|
|||
|
|
null,
|
|||
|
|
"iso-8859-1"
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
"symbol",
|
|||
|
|
"utf-16be",
|
|||
|
|
"shift-jis",
|
|||
|
|
"gb18030",
|
|||
|
|
"big5",
|
|||
|
|
"euc-kr",
|
|||
|
|
"johab",
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
"utf-16be"
|
|||
|
|
]
|
|||
|
|
];
|
|||
|
|
const MAC_LANGUAGE_ENCODINGS = {
|
|||
|
|
15: "x-mac-icelandic",
|
|||
|
|
17: "x-mac-turkish",
|
|||
|
|
18: "x-mac-croatian",
|
|||
|
|
24: "x-mac-ce",
|
|||
|
|
25: "x-mac-ce",
|
|||
|
|
26: "x-mac-ce",
|
|||
|
|
27: "x-mac-ce",
|
|||
|
|
28: "x-mac-ce",
|
|||
|
|
30: "x-mac-icelandic",
|
|||
|
|
37: "x-mac-romanian",
|
|||
|
|
38: "x-mac-ce",
|
|||
|
|
39: "x-mac-ce",
|
|||
|
|
40: "x-mac-ce",
|
|||
|
|
143: "x-mac-inuit",
|
|||
|
|
146: "x-mac-gaelic"
|
|||
|
|
};
|
|||
|
|
const LANGUAGES = [
|
|||
|
|
[],
|
|||
|
|
{
|
|||
|
|
0: "en",
|
|||
|
|
30: "fo",
|
|||
|
|
60: "ks",
|
|||
|
|
90: "rw",
|
|||
|
|
1: "fr",
|
|||
|
|
31: "fa",
|
|||
|
|
61: "ku",
|
|||
|
|
91: "rn",
|
|||
|
|
2: "de",
|
|||
|
|
32: "ru",
|
|||
|
|
62: "sd",
|
|||
|
|
92: "ny",
|
|||
|
|
3: "it",
|
|||
|
|
33: "zh",
|
|||
|
|
63: "bo",
|
|||
|
|
93: "mg",
|
|||
|
|
4: "nl",
|
|||
|
|
34: "nl-BE",
|
|||
|
|
64: "ne",
|
|||
|
|
94: "eo",
|
|||
|
|
5: "sv",
|
|||
|
|
35: "ga",
|
|||
|
|
65: "sa",
|
|||
|
|
128: "cy",
|
|||
|
|
6: "es",
|
|||
|
|
36: "sq",
|
|||
|
|
66: "mr",
|
|||
|
|
129: "eu",
|
|||
|
|
7: "da",
|
|||
|
|
37: "ro",
|
|||
|
|
67: "bn",
|
|||
|
|
130: "ca",
|
|||
|
|
8: "pt",
|
|||
|
|
38: "cz",
|
|||
|
|
68: "as",
|
|||
|
|
131: "la",
|
|||
|
|
9: "no",
|
|||
|
|
39: "sk",
|
|||
|
|
69: "gu",
|
|||
|
|
132: "qu",
|
|||
|
|
10: "he",
|
|||
|
|
40: "si",
|
|||
|
|
70: "pa",
|
|||
|
|
133: "gn",
|
|||
|
|
11: "ja",
|
|||
|
|
41: "yi",
|
|||
|
|
71: "or",
|
|||
|
|
134: "ay",
|
|||
|
|
12: "ar",
|
|||
|
|
42: "sr",
|
|||
|
|
72: "ml",
|
|||
|
|
135: "tt",
|
|||
|
|
13: "fi",
|
|||
|
|
43: "mk",
|
|||
|
|
73: "kn",
|
|||
|
|
136: "ug",
|
|||
|
|
14: "el",
|
|||
|
|
44: "bg",
|
|||
|
|
74: "ta",
|
|||
|
|
137: "dz",
|
|||
|
|
15: "is",
|
|||
|
|
45: "uk",
|
|||
|
|
75: "te",
|
|||
|
|
138: "jv",
|
|||
|
|
16: "mt",
|
|||
|
|
46: "be",
|
|||
|
|
76: "si",
|
|||
|
|
139: "su",
|
|||
|
|
17: "tr",
|
|||
|
|
47: "uz",
|
|||
|
|
77: "my",
|
|||
|
|
140: "gl",
|
|||
|
|
18: "hr",
|
|||
|
|
48: "kk",
|
|||
|
|
78: "km",
|
|||
|
|
141: "af",
|
|||
|
|
19: "zh-Hant",
|
|||
|
|
49: "az-Cyrl",
|
|||
|
|
79: "lo",
|
|||
|
|
142: "br",
|
|||
|
|
20: "ur",
|
|||
|
|
50: "az-Arab",
|
|||
|
|
80: "vi",
|
|||
|
|
143: "iu",
|
|||
|
|
21: "hi",
|
|||
|
|
51: "hy",
|
|||
|
|
81: "id",
|
|||
|
|
144: "gd",
|
|||
|
|
22: "th",
|
|||
|
|
52: "ka",
|
|||
|
|
82: "tl",
|
|||
|
|
145: "gv",
|
|||
|
|
23: "ko",
|
|||
|
|
53: "mo",
|
|||
|
|
83: "ms",
|
|||
|
|
146: "ga",
|
|||
|
|
24: "lt",
|
|||
|
|
54: "ky",
|
|||
|
|
84: "ms-Arab",
|
|||
|
|
147: "to",
|
|||
|
|
25: "pl",
|
|||
|
|
55: "tg",
|
|||
|
|
85: "am",
|
|||
|
|
148: "el-polyton",
|
|||
|
|
26: "hu",
|
|||
|
|
56: "tk",
|
|||
|
|
86: "ti",
|
|||
|
|
149: "kl",
|
|||
|
|
27: "es",
|
|||
|
|
57: "mn-CN",
|
|||
|
|
87: "om",
|
|||
|
|
150: "az",
|
|||
|
|
28: "lv",
|
|||
|
|
58: "mn",
|
|||
|
|
88: "so",
|
|||
|
|
151: "nn",
|
|||
|
|
29: "se",
|
|||
|
|
59: "ps",
|
|||
|
|
89: "sw"
|
|||
|
|
},
|
|||
|
|
[],
|
|||
|
|
{
|
|||
|
|
1078: "af",
|
|||
|
|
16393: "en-IN",
|
|||
|
|
1159: "rw",
|
|||
|
|
1074: "tn",
|
|||
|
|
1052: "sq",
|
|||
|
|
6153: "en-IE",
|
|||
|
|
1089: "sw",
|
|||
|
|
1115: "si",
|
|||
|
|
1156: "gsw",
|
|||
|
|
8201: "en-JM",
|
|||
|
|
1111: "kok",
|
|||
|
|
1051: "sk",
|
|||
|
|
1118: "am",
|
|||
|
|
17417: "en-MY",
|
|||
|
|
1042: "ko",
|
|||
|
|
1060: "sl",
|
|||
|
|
5121: "ar-DZ",
|
|||
|
|
5129: "en-NZ",
|
|||
|
|
1088: "ky",
|
|||
|
|
11274: "es-AR",
|
|||
|
|
15361: "ar-BH",
|
|||
|
|
13321: "en-PH",
|
|||
|
|
1108: "lo",
|
|||
|
|
16394: "es-BO",
|
|||
|
|
3073: "ar",
|
|||
|
|
18441: "en-SG",
|
|||
|
|
1062: "lv",
|
|||
|
|
13322: "es-CL",
|
|||
|
|
2049: "ar-IQ",
|
|||
|
|
7177: "en-ZA",
|
|||
|
|
1063: "lt",
|
|||
|
|
9226: "es-CO",
|
|||
|
|
11265: "ar-JO",
|
|||
|
|
11273: "en-TT",
|
|||
|
|
2094: "dsb",
|
|||
|
|
5130: "es-CR",
|
|||
|
|
13313: "ar-KW",
|
|||
|
|
2057: "en-GB",
|
|||
|
|
1134: "lb",
|
|||
|
|
7178: "es-DO",
|
|||
|
|
12289: "ar-LB",
|
|||
|
|
1033: "en",
|
|||
|
|
1071: "mk",
|
|||
|
|
12298: "es-EC",
|
|||
|
|
4097: "ar-LY",
|
|||
|
|
12297: "en-ZW",
|
|||
|
|
2110: "ms-BN",
|
|||
|
|
17418: "es-SV",
|
|||
|
|
6145: "ary",
|
|||
|
|
1061: "et",
|
|||
|
|
1086: "ms",
|
|||
|
|
4106: "es-GT",
|
|||
|
|
8193: "ar-OM",
|
|||
|
|
1080: "fo",
|
|||
|
|
1100: "ml",
|
|||
|
|
18442: "es-HN",
|
|||
|
|
16385: "ar-QA",
|
|||
|
|
1124: "fil",
|
|||
|
|
1082: "mt",
|
|||
|
|
2058: "es-MX",
|
|||
|
|
1025: "ar-SA",
|
|||
|
|
1035: "fi",
|
|||
|
|
1153: "mi",
|
|||
|
|
19466: "es-NI",
|
|||
|
|
10241: "ar-SY",
|
|||
|
|
2060: "fr-BE",
|
|||
|
|
1146: "arn",
|
|||
|
|
6154: "es-PA",
|
|||
|
|
7169: "aeb",
|
|||
|
|
3084: "fr-CA",
|
|||
|
|
1102: "mr",
|
|||
|
|
15370: "es-PY",
|
|||
|
|
14337: "ar-AE",
|
|||
|
|
1036: "fr",
|
|||
|
|
1148: "moh",
|
|||
|
|
10250: "es-PE",
|
|||
|
|
9217: "ar-YE",
|
|||
|
|
5132: "fr-LU",
|
|||
|
|
1104: "mn",
|
|||
|
|
20490: "es-PR",
|
|||
|
|
1067: "hy",
|
|||
|
|
6156: "fr-MC",
|
|||
|
|
2128: "mn-CN",
|
|||
|
|
3082: "es",
|
|||
|
|
1101: "as",
|
|||
|
|
4108: "fr-CH",
|
|||
|
|
1121: "ne",
|
|||
|
|
1034: "es",
|
|||
|
|
2092: "az-Cyrl",
|
|||
|
|
1122: "fy",
|
|||
|
|
1044: "nb",
|
|||
|
|
21514: "es-US",
|
|||
|
|
1068: "az",
|
|||
|
|
1110: "gl",
|
|||
|
|
2068: "nn",
|
|||
|
|
14346: "es-UY",
|
|||
|
|
1133: "ba",
|
|||
|
|
1079: "ka",
|
|||
|
|
1154: "oc",
|
|||
|
|
8202: "es-VE",
|
|||
|
|
1069: "eu",
|
|||
|
|
3079: "de-AT",
|
|||
|
|
1096: "or",
|
|||
|
|
2077: "sv-FI",
|
|||
|
|
1059: "be",
|
|||
|
|
1031: "de",
|
|||
|
|
1123: "ps",
|
|||
|
|
1053: "sv",
|
|||
|
|
2117: "bn",
|
|||
|
|
5127: "de-LI",
|
|||
|
|
1045: "pl",
|
|||
|
|
1114: "syr",
|
|||
|
|
1093: "bn-IN",
|
|||
|
|
4103: "de-LU",
|
|||
|
|
1046: "pt",
|
|||
|
|
1064: "tg",
|
|||
|
|
8218: "bs-Cyrl",
|
|||
|
|
2055: "de-CH",
|
|||
|
|
2070: "pt-PT",
|
|||
|
|
2143: "tzm",
|
|||
|
|
5146: "bs",
|
|||
|
|
1032: "el",
|
|||
|
|
1094: "pa",
|
|||
|
|
1097: "ta",
|
|||
|
|
1150: "br",
|
|||
|
|
1135: "kl",
|
|||
|
|
1131: "qu-BO",
|
|||
|
|
1092: "tt",
|
|||
|
|
1026: "bg",
|
|||
|
|
1095: "gu",
|
|||
|
|
2155: "qu-EC",
|
|||
|
|
1098: "te",
|
|||
|
|
1027: "ca",
|
|||
|
|
1128: "ha",
|
|||
|
|
3179: "qu",
|
|||
|
|
1054: "th",
|
|||
|
|
3076: "zh-HK",
|
|||
|
|
1037: "he",
|
|||
|
|
1048: "ro",
|
|||
|
|
1105: "bo",
|
|||
|
|
5124: "zh-MO",
|
|||
|
|
1081: "hi",
|
|||
|
|
1047: "rm",
|
|||
|
|
1055: "tr",
|
|||
|
|
2052: "zh",
|
|||
|
|
1038: "hu",
|
|||
|
|
1049: "ru",
|
|||
|
|
1090: "tk",
|
|||
|
|
4100: "zh-SG",
|
|||
|
|
1039: "is",
|
|||
|
|
9275: "smn",
|
|||
|
|
1152: "ug",
|
|||
|
|
1028: "zh-TW",
|
|||
|
|
1136: "ig",
|
|||
|
|
4155: "smj-NO",
|
|||
|
|
1058: "uk",
|
|||
|
|
1155: "co",
|
|||
|
|
1057: "id",
|
|||
|
|
5179: "smj",
|
|||
|
|
1070: "hsb",
|
|||
|
|
1050: "hr",
|
|||
|
|
1117: "iu",
|
|||
|
|
3131: "se-FI",
|
|||
|
|
1056: "ur",
|
|||
|
|
4122: "hr-BA",
|
|||
|
|
2141: "iu-Latn",
|
|||
|
|
1083: "se",
|
|||
|
|
2115: "uz-Cyrl",
|
|||
|
|
1029: "cs",
|
|||
|
|
2108: "ga",
|
|||
|
|
2107: "se-SE",
|
|||
|
|
1091: "uz",
|
|||
|
|
1030: "da",
|
|||
|
|
1076: "xh",
|
|||
|
|
8251: "sms",
|
|||
|
|
1066: "vi",
|
|||
|
|
1164: "prs",
|
|||
|
|
1077: "zu",
|
|||
|
|
6203: "sma-NO",
|
|||
|
|
1106: "cy",
|
|||
|
|
1125: "dv",
|
|||
|
|
1040: "it",
|
|||
|
|
7227: "sms",
|
|||
|
|
1160: "wo",
|
|||
|
|
2067: "nl-BE",
|
|||
|
|
2064: "it-CH",
|
|||
|
|
1103: "sa",
|
|||
|
|
1157: "sah",
|
|||
|
|
1043: "nl",
|
|||
|
|
1041: "ja",
|
|||
|
|
7194: "sr-Cyrl-BA",
|
|||
|
|
1144: "ii",
|
|||
|
|
3081: "en-AU",
|
|||
|
|
1099: "kn",
|
|||
|
|
3098: "sr",
|
|||
|
|
1130: "yo",
|
|||
|
|
10249: "en-BZ",
|
|||
|
|
1087: "kk",
|
|||
|
|
6170: "sr-Latn-BA",
|
|||
|
|
4105: "en-CA",
|
|||
|
|
1107: "km",
|
|||
|
|
2074: "sr-Latn",
|
|||
|
|
9225: "en-029",
|
|||
|
|
1158: "quc",
|
|||
|
|
1132: "nso"
|
|||
|
|
}
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/name.ts
|
|||
|
|
const NameRecord = new Struct({
|
|||
|
|
platformID: uint16,
|
|||
|
|
encodingID: uint16,
|
|||
|
|
languageID: uint16,
|
|||
|
|
nameID: uint16,
|
|||
|
|
length: uint16,
|
|||
|
|
string: new Pointer(uint16, new StringT("length", (t) => getEncoding(t.platformID, t.encodingID, t.languageID)), {
|
|||
|
|
type: "parent",
|
|||
|
|
relativeTo: (ctx) => ctx.parent.stringOffset,
|
|||
|
|
allowNull: false
|
|||
|
|
})
|
|||
|
|
});
|
|||
|
|
const LangTagRecord = new Struct({
|
|||
|
|
length: uint16,
|
|||
|
|
tag: new Pointer(uint16, new StringT("length", "utf16-be"), {
|
|||
|
|
type: "parent",
|
|||
|
|
relativeTo: (ctx) => ctx.stringOffset
|
|||
|
|
})
|
|||
|
|
});
|
|||
|
|
const NameTable = new VersionedStruct(uint16, {
|
|||
|
|
0: {
|
|||
|
|
count: uint16,
|
|||
|
|
stringOffset: uint16,
|
|||
|
|
records: new ArrayT(NameRecord, "count")
|
|||
|
|
},
|
|||
|
|
1: {
|
|||
|
|
count: uint16,
|
|||
|
|
stringOffset: uint16,
|
|||
|
|
records: new ArrayT(NameRecord, "count"),
|
|||
|
|
langTagCount: uint16,
|
|||
|
|
langTags: new ArrayT(LangTagRecord, "langTagCount")
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
const NAMES = [
|
|||
|
|
"copyright",
|
|||
|
|
"fontFamily",
|
|||
|
|
"fontSubfamily",
|
|||
|
|
"uniqueSubfamily",
|
|||
|
|
"fullName",
|
|||
|
|
"version",
|
|||
|
|
"postscriptName",
|
|||
|
|
"trademark",
|
|||
|
|
"manufacturer",
|
|||
|
|
"designer",
|
|||
|
|
"description",
|
|||
|
|
"vendorURL",
|
|||
|
|
"designerURL",
|
|||
|
|
"license",
|
|||
|
|
"licenseURL",
|
|||
|
|
null,
|
|||
|
|
"preferredFamily",
|
|||
|
|
"preferredSubfamily",
|
|||
|
|
"compatibleFull",
|
|||
|
|
"sampleText",
|
|||
|
|
"postscriptCIDFontName",
|
|||
|
|
"wwsFamilyName",
|
|||
|
|
"wwsSubfamilyName"
|
|||
|
|
];
|
|||
|
|
NameTable.process = function(stream) {
|
|||
|
|
const records = {};
|
|||
|
|
for (const record of this.records) {
|
|||
|
|
let language = LANGUAGES[record.platformID][record.languageID];
|
|||
|
|
if (language == null && this.langTags != null && record.languageID >= 32768) language = this.langTags[record.languageID - 32768].tag;
|
|||
|
|
if (language == null) language = record.platformID + "-" + record.languageID;
|
|||
|
|
const key = record.nameID >= 256 ? "fontFeatures" : NAMES[record.nameID] || record.nameID;
|
|||
|
|
if (records[key] == null) records[key] = {};
|
|||
|
|
let obj = records[key];
|
|||
|
|
if (record.nameID >= 256) obj = obj[record.nameID] || (obj[record.nameID] = {});
|
|||
|
|
if (typeof record.string === "string" || typeof obj[language] !== "string") obj[language] = record.string;
|
|||
|
|
}
|
|||
|
|
this.records = records;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/OS2.ts
|
|||
|
|
const version1 = {
|
|||
|
|
typoAscender: int16,
|
|||
|
|
typoDescender: int16,
|
|||
|
|
typoLineGap: int16,
|
|||
|
|
winAscent: uint16,
|
|||
|
|
winDescent: uint16,
|
|||
|
|
codePageRange: new ArrayT(uint32, 2)
|
|||
|
|
};
|
|||
|
|
const version2 = {
|
|||
|
|
...version1,
|
|||
|
|
xHeight: int16,
|
|||
|
|
capHeight: int16,
|
|||
|
|
defaultChar: uint16,
|
|||
|
|
breakChar: uint16,
|
|||
|
|
maxContent: uint16
|
|||
|
|
};
|
|||
|
|
const OS2 = new VersionedStruct(uint16, {
|
|||
|
|
header: {
|
|||
|
|
xAvgCharWidth: int16,
|
|||
|
|
usWeightClass: uint16,
|
|||
|
|
usWidthClass: uint16,
|
|||
|
|
fsType: new Bitfield(uint16, [
|
|||
|
|
null,
|
|||
|
|
"noEmbedding",
|
|||
|
|
"viewOnly",
|
|||
|
|
"editable",
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
"noSubsetting",
|
|||
|
|
"bitmapOnly"
|
|||
|
|
]),
|
|||
|
|
ySubscriptXSize: int16,
|
|||
|
|
ySubscriptYSize: int16,
|
|||
|
|
ySubscriptXOffset: int16,
|
|||
|
|
ySubscriptYOffset: int16,
|
|||
|
|
ySuperscriptXSize: int16,
|
|||
|
|
ySuperscriptYSize: int16,
|
|||
|
|
ySuperscriptXOffset: int16,
|
|||
|
|
ySuperscriptYOffset: int16,
|
|||
|
|
yStrikeoutSize: int16,
|
|||
|
|
yStrikeoutPosition: int16,
|
|||
|
|
sFamilyClass: int16,
|
|||
|
|
panose: new ArrayT(uint8, 10),
|
|||
|
|
ulCharRange: new ArrayT(uint32, 4),
|
|||
|
|
vendorID: new StringT(4),
|
|||
|
|
fsSelection: new Bitfield(uint16, [
|
|||
|
|
"italic",
|
|||
|
|
"underscore",
|
|||
|
|
"negative",
|
|||
|
|
"outlined",
|
|||
|
|
"strikeout",
|
|||
|
|
"bold",
|
|||
|
|
"regular",
|
|||
|
|
"useTypoMetrics",
|
|||
|
|
"wws",
|
|||
|
|
"oblique"
|
|||
|
|
]),
|
|||
|
|
usFirstCharIndex: uint16,
|
|||
|
|
usLastCharIndex: uint16
|
|||
|
|
},
|
|||
|
|
0: {},
|
|||
|
|
1: version1,
|
|||
|
|
2: version2,
|
|||
|
|
3: version2,
|
|||
|
|
4: version2,
|
|||
|
|
5: {
|
|||
|
|
...version2,
|
|||
|
|
usLowerOpticalPointSize: uint16,
|
|||
|
|
usUpperOpticalPointSize: uint16
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/post.ts
|
|||
|
|
var post_default = new VersionedStruct(fixed32, {
|
|||
|
|
header: {
|
|||
|
|
italicAngle: fixed32,
|
|||
|
|
underlinePosition: int16,
|
|||
|
|
underlineThickness: int16,
|
|||
|
|
isFixedPitch: uint32,
|
|||
|
|
minMemType42: uint32,
|
|||
|
|
maxMemType42: uint32,
|
|||
|
|
minMemType1: uint32,
|
|||
|
|
maxMemType1: uint32
|
|||
|
|
},
|
|||
|
|
1: {},
|
|||
|
|
2: {
|
|||
|
|
numberOfGlyphs: uint16,
|
|||
|
|
glyphNameIndex: new ArrayT(uint16, "numberOfGlyphs"),
|
|||
|
|
names: new ArrayT(new StringT(uint8))
|
|||
|
|
},
|
|||
|
|
2.5: {
|
|||
|
|
numberOfGlyphs: uint16,
|
|||
|
|
offsets: new ArrayT(uint8, "numberOfGlyphs")
|
|||
|
|
},
|
|||
|
|
3: {},
|
|||
|
|
4: { map: new ArrayT(uint32, (t) => t.parent.maxp.numGlyphs) }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/loca.ts
|
|||
|
|
const loca = new VersionedStruct("head.indexToLocFormat", {
|
|||
|
|
0: { offsets: new ArrayT(uint16) },
|
|||
|
|
1: { offsets: new ArrayT(uint32) }
|
|||
|
|
});
|
|||
|
|
loca.process = function() {
|
|||
|
|
if (this.version === 0 && !this._processed) {
|
|||
|
|
for (let i = 0; i < this.offsets.length; i++) this.offsets[i] <<= 1;
|
|||
|
|
this._processed = true;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/glyf.ts
|
|||
|
|
var glyf_default = new ArrayT(new BufferT());
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/cff/CFFOperand.ts
|
|||
|
|
const FLOAT_EOF = 15;
|
|||
|
|
const FLOAT_LOOKUP = [
|
|||
|
|
"0",
|
|||
|
|
"1",
|
|||
|
|
"2",
|
|||
|
|
"3",
|
|||
|
|
"4",
|
|||
|
|
"5",
|
|||
|
|
"6",
|
|||
|
|
"7",
|
|||
|
|
"8",
|
|||
|
|
"9",
|
|||
|
|
".",
|
|||
|
|
"E",
|
|||
|
|
"E-",
|
|||
|
|
null,
|
|||
|
|
"-"
|
|||
|
|
];
|
|||
|
|
var CFFOperand = class {
|
|||
|
|
static decode(stream, value) {
|
|||
|
|
if (32 <= value && value <= 246) return value - 139;
|
|||
|
|
if (247 <= value && value <= 250) return (value - 247) * 256 + stream.readUInt8() + 108;
|
|||
|
|
if (251 <= value && value <= 254) return -(value - 251) * 256 - stream.readUInt8() - 108;
|
|||
|
|
if (value === 28) return stream.readInt16BE();
|
|||
|
|
if (value === 29) return stream.readInt32BE();
|
|||
|
|
if (value === 30) {
|
|||
|
|
let str = "";
|
|||
|
|
while (true) {
|
|||
|
|
const b = stream.readUInt8();
|
|||
|
|
const n1 = b >> 4;
|
|||
|
|
if (n1 === FLOAT_EOF) break;
|
|||
|
|
str += FLOAT_LOOKUP[n1];
|
|||
|
|
const n2 = b & 15;
|
|||
|
|
if (n2 === FLOAT_EOF) break;
|
|||
|
|
str += FLOAT_LOOKUP[n2];
|
|||
|
|
}
|
|||
|
|
return parseFloat(str);
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/cff/CFFDict.ts
|
|||
|
|
var CFFDict = class {
|
|||
|
|
constructor(ops = []) {
|
|||
|
|
this.ops = ops;
|
|||
|
|
this.fields = Object.fromEntries(ops.map((field) => {
|
|||
|
|
return [Array.isArray(field[0]) ? field[0][0] << 8 | field[0][1] : field[0], field];
|
|||
|
|
}));
|
|||
|
|
}
|
|||
|
|
decodeOperands(type, stream, ret, operands) {
|
|||
|
|
if (Array.isArray(type)) return operands.map((op, i) => this.decodeOperands(type[i], stream, ret, [op]));
|
|||
|
|
else if (type.decode != null) return type.decode(stream, ret, operands);
|
|||
|
|
else switch (type) {
|
|||
|
|
case "number":
|
|||
|
|
case "offset":
|
|||
|
|
case "sid": return operands[0];
|
|||
|
|
case "boolean": return !!operands[0];
|
|||
|
|
default: return operands;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
decode(stream, parent) {
|
|||
|
|
const end = stream.pos + parent.length;
|
|||
|
|
const ret = {};
|
|||
|
|
let operands = [];
|
|||
|
|
Object.defineProperties(ret, {
|
|||
|
|
parent: { value: parent },
|
|||
|
|
_startOffset: { value: stream.pos }
|
|||
|
|
});
|
|||
|
|
for (const key in this.fields) {
|
|||
|
|
const field = this.fields[key];
|
|||
|
|
ret[field[1]] = field[3];
|
|||
|
|
}
|
|||
|
|
while (stream.pos < end) {
|
|||
|
|
let b = stream.readUInt8();
|
|||
|
|
if (b < 28) {
|
|||
|
|
if (b === 12) b = b << 8 | stream.readUInt8();
|
|||
|
|
const field = this.fields[b];
|
|||
|
|
if (!field) throw new Error(`Unknown operator ${b}`);
|
|||
|
|
const val = this.decodeOperands(field[2], stream, ret, operands);
|
|||
|
|
if (val != null) if (val instanceof PropertyDescriptor) Object.defineProperty(ret, field[1], val);
|
|||
|
|
else ret[field[1]] = val;
|
|||
|
|
operands = [];
|
|||
|
|
} else operands.push(CFFOperand.decode(stream, b));
|
|||
|
|
}
|
|||
|
|
return ret;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/cff/CFFIndex.ts
|
|||
|
|
var CFFIndex = class {
|
|||
|
|
constructor(type) {
|
|||
|
|
this.type = type;
|
|||
|
|
}
|
|||
|
|
getCFFVersion(ctx) {
|
|||
|
|
while (ctx && !ctx.hdrSize) ctx = ctx.parent;
|
|||
|
|
return ctx?.version ?? -1;
|
|||
|
|
}
|
|||
|
|
decode(stream, parent) {
|
|||
|
|
const count = this.getCFFVersion(parent) >= 2 ? stream.readUInt32BE() : stream.readUInt16BE();
|
|||
|
|
if (count === 0) return [];
|
|||
|
|
const offSize = stream.readUInt8();
|
|||
|
|
let offsetType;
|
|||
|
|
if (offSize === 1) offsetType = uint8;
|
|||
|
|
else if (offSize === 2) offsetType = uint16;
|
|||
|
|
else if (offSize === 3) offsetType = uint24;
|
|||
|
|
else if (offSize === 4) offsetType = uint32;
|
|||
|
|
else throw new Error(`Bad offset size in CFFIndex: ${offSize} ${stream.pos}`);
|
|||
|
|
const ret = [];
|
|||
|
|
const startPos = stream.pos + (count + 1) * offSize - 1;
|
|||
|
|
let start = offsetType.decode(stream);
|
|||
|
|
for (let i = 0; i < count; i++) {
|
|||
|
|
const end = offsetType.decode(stream);
|
|||
|
|
if (this.type != null) {
|
|||
|
|
const { pos } = stream;
|
|||
|
|
stream.pos = startPos + start;
|
|||
|
|
parent.length = end - start;
|
|||
|
|
ret.push(this.type.decode(stream, parent));
|
|||
|
|
stream.pos = pos;
|
|||
|
|
} else ret.push({
|
|||
|
|
offset: startPos + start,
|
|||
|
|
length: end - start
|
|||
|
|
});
|
|||
|
|
start = end;
|
|||
|
|
}
|
|||
|
|
stream.pos = startPos + start;
|
|||
|
|
return ret;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/cff/CFFPointer.ts
|
|||
|
|
var CFFPointer = class extends Pointer {
|
|||
|
|
constructor(type, options = {}) {
|
|||
|
|
if (options.type == null) options.type = "global";
|
|||
|
|
super(null, type, options);
|
|||
|
|
}
|
|||
|
|
decode(stream, parent, operands) {
|
|||
|
|
this.offsetType = { decode: () => operands[0] };
|
|||
|
|
return super.decode(stream, parent);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/cff/CFFPrivateDict.ts
|
|||
|
|
var CFFBlendOp = class {
|
|||
|
|
static decode(_stream, _parent, operands) {
|
|||
|
|
let numBlends = operands.pop();
|
|||
|
|
while (operands.length > numBlends) operands.pop();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var CFFPrivateDict_default = new CFFDict([
|
|||
|
|
[
|
|||
|
|
6,
|
|||
|
|
"BlueValues",
|
|||
|
|
"delta",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
7,
|
|||
|
|
"OtherBlues",
|
|||
|
|
"delta",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
8,
|
|||
|
|
"FamilyBlues",
|
|||
|
|
"delta",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
9,
|
|||
|
|
"FamilyOtherBlues",
|
|||
|
|
"delta",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 9],
|
|||
|
|
"BlueScale",
|
|||
|
|
"number",
|
|||
|
|
.039625
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 10],
|
|||
|
|
"BlueShift",
|
|||
|
|
"number",
|
|||
|
|
7
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 11],
|
|||
|
|
"BlueFuzz",
|
|||
|
|
"number",
|
|||
|
|
1
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
10,
|
|||
|
|
"StdHW",
|
|||
|
|
"number",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
11,
|
|||
|
|
"StdVW",
|
|||
|
|
"number",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 12],
|
|||
|
|
"StemSnapH",
|
|||
|
|
"delta",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 13],
|
|||
|
|
"StemSnapV",
|
|||
|
|
"delta",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 14],
|
|||
|
|
"ForceBold",
|
|||
|
|
"boolean",
|
|||
|
|
false
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 17],
|
|||
|
|
"LanguageGroup",
|
|||
|
|
"number",
|
|||
|
|
0
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 18],
|
|||
|
|
"ExpansionFactor",
|
|||
|
|
"number",
|
|||
|
|
.06
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 19],
|
|||
|
|
"initialRandomSeed",
|
|||
|
|
"number",
|
|||
|
|
0
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
20,
|
|||
|
|
"defaultWidthX",
|
|||
|
|
"number",
|
|||
|
|
0
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
21,
|
|||
|
|
"nominalWidthX",
|
|||
|
|
"number",
|
|||
|
|
0
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
22,
|
|||
|
|
"vsindex",
|
|||
|
|
"number",
|
|||
|
|
0
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
23,
|
|||
|
|
"blend",
|
|||
|
|
CFFBlendOp,
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
19,
|
|||
|
|
"Subrs",
|
|||
|
|
new CFFPointer(new CFFIndex(), { type: "local" }),
|
|||
|
|
null
|
|||
|
|
]
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/cff/CFFEncodings.ts
|
|||
|
|
const StandardEncoding = [
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"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 ExpertEncoding = [
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"",
|
|||
|
|
"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"
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/cff/CFFCharsets.ts
|
|||
|
|
const ISOAdobeCharset = [
|
|||
|
|
".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 ExpertCharset = [
|
|||
|
|
".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",
|
|||
|
|
"Icircumflexsmall",
|
|||
|
|
"Idieresissmall",
|
|||
|
|
"Ethsmall",
|
|||
|
|
"Ntildesmall",
|
|||
|
|
"Ogravesmall",
|
|||
|
|
"Oacutesmall",
|
|||
|
|
"Ocircumflexsmall",
|
|||
|
|
"Otildesmall",
|
|||
|
|
"Odieresissmall",
|
|||
|
|
"OEsmall",
|
|||
|
|
"Oslashsmall",
|
|||
|
|
"Ugravesmall",
|
|||
|
|
"Uacutesmall",
|
|||
|
|
"Ucircumflexsmall",
|
|||
|
|
"Udieresissmall",
|
|||
|
|
"Yacutesmall",
|
|||
|
|
"Thornsmall",
|
|||
|
|
"Ydieresissmall"
|
|||
|
|
];
|
|||
|
|
const ExpertSubsetCharset = [
|
|||
|
|
".notdef",
|
|||
|
|
"space",
|
|||
|
|
"dollaroldstyle",
|
|||
|
|
"dollarsuperior",
|
|||
|
|
"parenleftsuperior",
|
|||
|
|
"parenrightsuperior",
|
|||
|
|
"twodotenleader",
|
|||
|
|
"onedotenleader",
|
|||
|
|
"comma",
|
|||
|
|
"hyphen",
|
|||
|
|
"period",
|
|||
|
|
"fraction",
|
|||
|
|
"zerooldstyle",
|
|||
|
|
"oneoldstyle",
|
|||
|
|
"twooldstyle",
|
|||
|
|
"threeoldstyle",
|
|||
|
|
"fouroldstyle",
|
|||
|
|
"fiveoldstyle",
|
|||
|
|
"sixoldstyle",
|
|||
|
|
"sevenoldstyle",
|
|||
|
|
"eightoldstyle",
|
|||
|
|
"nineoldstyle",
|
|||
|
|
"colon",
|
|||
|
|
"semicolon",
|
|||
|
|
"commasuperior",
|
|||
|
|
"threequartersemdash",
|
|||
|
|
"periodsuperior",
|
|||
|
|
"asuperior",
|
|||
|
|
"bsuperior",
|
|||
|
|
"centsuperior",
|
|||
|
|
"dsuperior",
|
|||
|
|
"esuperior",
|
|||
|
|
"isuperior",
|
|||
|
|
"lsuperior",
|
|||
|
|
"msuperior",
|
|||
|
|
"nsuperior",
|
|||
|
|
"osuperior",
|
|||
|
|
"rsuperior",
|
|||
|
|
"ssuperior",
|
|||
|
|
"tsuperior",
|
|||
|
|
"ff",
|
|||
|
|
"fi",
|
|||
|
|
"fl",
|
|||
|
|
"ffi",
|
|||
|
|
"ffl",
|
|||
|
|
"parenleftinferior",
|
|||
|
|
"parenrightinferior",
|
|||
|
|
"hyphensuperior",
|
|||
|
|
"colonmonetary",
|
|||
|
|
"onefitted",
|
|||
|
|
"rupiah",
|
|||
|
|
"centoldstyle",
|
|||
|
|
"figuredash",
|
|||
|
|
"hypheninferior",
|
|||
|
|
"onequarter",
|
|||
|
|
"onehalf",
|
|||
|
|
"threequarters",
|
|||
|
|
"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"
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/opentype.ts
|
|||
|
|
const LangSysTable = new Struct({
|
|||
|
|
reserved: new Reserved(uint16),
|
|||
|
|
reqFeatureIndex: uint16,
|
|||
|
|
featureCount: uint16,
|
|||
|
|
featureIndexes: new ArrayT(uint16, "featureCount")
|
|||
|
|
});
|
|||
|
|
const LangSysRecord = new Struct({
|
|||
|
|
tag: new StringT(4),
|
|||
|
|
langSys: new Pointer(uint16, LangSysTable, { type: "parent" })
|
|||
|
|
});
|
|||
|
|
const Script = new Struct({
|
|||
|
|
defaultLangSys: new Pointer(uint16, LangSysTable),
|
|||
|
|
count: uint16,
|
|||
|
|
langSysRecords: new ArrayT(LangSysRecord, "count")
|
|||
|
|
});
|
|||
|
|
const ScriptRecord = new Struct({
|
|||
|
|
tag: new StringT(4),
|
|||
|
|
script: new Pointer(uint16, Script, { type: "parent" })
|
|||
|
|
});
|
|||
|
|
const ScriptList = new ArrayT(ScriptRecord, uint16);
|
|||
|
|
const FeatureParams = new Struct({
|
|||
|
|
version: uint16,
|
|||
|
|
nameID: uint16
|
|||
|
|
});
|
|||
|
|
const Feature = new Struct({
|
|||
|
|
featureParams: new Pointer(uint16, FeatureParams),
|
|||
|
|
lookupCount: uint16,
|
|||
|
|
lookupListIndexes: new ArrayT(uint16, "lookupCount")
|
|||
|
|
});
|
|||
|
|
const FeatureRecord = new Struct({
|
|||
|
|
tag: new StringT(4),
|
|||
|
|
feature: new Pointer(uint16, Feature, { type: "parent" })
|
|||
|
|
});
|
|||
|
|
const FeatureList = new ArrayT(FeatureRecord, uint16);
|
|||
|
|
const LookupFlags = new Struct({
|
|||
|
|
markAttachmentType: uint8,
|
|||
|
|
flags: new Bitfield(uint8, [
|
|||
|
|
"rightToLeft",
|
|||
|
|
"ignoreBaseGlyphs",
|
|||
|
|
"ignoreLigatures",
|
|||
|
|
"ignoreMarks",
|
|||
|
|
"useMarkFilteringSet"
|
|||
|
|
])
|
|||
|
|
});
|
|||
|
|
function LookupList(SubTable) {
|
|||
|
|
const Lookup = new Struct({
|
|||
|
|
lookupType: uint16,
|
|||
|
|
flags: LookupFlags,
|
|||
|
|
subTableCount: uint16,
|
|||
|
|
subTables: new ArrayT(new Pointer(uint16, SubTable), "subTableCount"),
|
|||
|
|
markFilteringSet: new Optional(uint16, (t) => t.flags.flags.useMarkFilteringSet)
|
|||
|
|
});
|
|||
|
|
return new LazyArray(new Pointer(uint16, Lookup), uint16);
|
|||
|
|
}
|
|||
|
|
const RangeRecord = new Struct({
|
|||
|
|
start: uint16,
|
|||
|
|
end: uint16,
|
|||
|
|
startCoverageIndex: uint16
|
|||
|
|
});
|
|||
|
|
const Coverage = new VersionedStruct(uint16, {
|
|||
|
|
1: {
|
|||
|
|
glyphCount: uint16,
|
|||
|
|
glyphs: new ArrayT(uint16, "glyphCount")
|
|||
|
|
},
|
|||
|
|
2: {
|
|||
|
|
rangeCount: uint16,
|
|||
|
|
rangeRecords: new ArrayT(RangeRecord, "rangeCount")
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
const ClassRangeRecord = new Struct({
|
|||
|
|
start: uint16,
|
|||
|
|
end: uint16,
|
|||
|
|
class: uint16
|
|||
|
|
});
|
|||
|
|
const ClassDef = new VersionedStruct(uint16, {
|
|||
|
|
1: {
|
|||
|
|
startGlyph: uint16,
|
|||
|
|
glyphCount: uint16,
|
|||
|
|
classValueArray: new ArrayT(uint16, "glyphCount")
|
|||
|
|
},
|
|||
|
|
2: {
|
|||
|
|
classRangeCount: uint16,
|
|||
|
|
classRangeRecord: new ArrayT(ClassRangeRecord, "classRangeCount")
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
const LookupRecord = new Struct({
|
|||
|
|
sequenceIndex: uint16,
|
|||
|
|
lookupListIndex: uint16
|
|||
|
|
});
|
|||
|
|
const Rule = new Struct({
|
|||
|
|
glyphCount: uint16,
|
|||
|
|
lookupCount: uint16,
|
|||
|
|
input: new ArrayT(uint16, (t) => t.glyphCount - 1),
|
|||
|
|
lookupRecords: new ArrayT(LookupRecord, "lookupCount")
|
|||
|
|
});
|
|||
|
|
const RuleSet = new ArrayT(new Pointer(uint16, Rule), uint16);
|
|||
|
|
const ClassRule = new Struct({
|
|||
|
|
glyphCount: uint16,
|
|||
|
|
lookupCount: uint16,
|
|||
|
|
classes: new ArrayT(uint16, (t) => t.glyphCount - 1),
|
|||
|
|
lookupRecords: new ArrayT(LookupRecord, "lookupCount")
|
|||
|
|
});
|
|||
|
|
const ClassSet = new ArrayT(new Pointer(uint16, ClassRule), uint16);
|
|||
|
|
const Context = new VersionedStruct(uint16, {
|
|||
|
|
1: {
|
|||
|
|
coverage: new Pointer(uint16, Coverage),
|
|||
|
|
ruleSetCount: uint16,
|
|||
|
|
ruleSets: new ArrayT(new Pointer(uint16, RuleSet), "ruleSetCount")
|
|||
|
|
},
|
|||
|
|
2: {
|
|||
|
|
coverage: new Pointer(uint16, Coverage),
|
|||
|
|
classDef: new Pointer(uint16, ClassDef),
|
|||
|
|
classSetCnt: uint16,
|
|||
|
|
classSet: new ArrayT(new Pointer(uint16, ClassSet), "classSetCnt")
|
|||
|
|
},
|
|||
|
|
3: {
|
|||
|
|
glyphCount: uint16,
|
|||
|
|
lookupCount: uint16,
|
|||
|
|
coverages: new ArrayT(new Pointer(uint16, Coverage), "glyphCount"),
|
|||
|
|
lookupRecords: new ArrayT(LookupRecord, "lookupCount")
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
const ChainRule = new Struct({
|
|||
|
|
backtrackGlyphCount: uint16,
|
|||
|
|
backtrack: new ArrayT(uint16, "backtrackGlyphCount"),
|
|||
|
|
inputGlyphCount: uint16,
|
|||
|
|
input: new ArrayT(uint16, (t) => t.inputGlyphCount - 1),
|
|||
|
|
lookaheadGlyphCount: uint16,
|
|||
|
|
lookahead: new ArrayT(uint16, "lookaheadGlyphCount"),
|
|||
|
|
lookupCount: uint16,
|
|||
|
|
lookupRecords: new ArrayT(LookupRecord, "lookupCount")
|
|||
|
|
});
|
|||
|
|
const ChainRuleSet = new ArrayT(new Pointer(uint16, ChainRule), uint16);
|
|||
|
|
const ChainingContext = new VersionedStruct(uint16, {
|
|||
|
|
1: {
|
|||
|
|
coverage: new Pointer(uint16, Coverage),
|
|||
|
|
chainCount: uint16,
|
|||
|
|
chainRuleSets: new ArrayT(new Pointer(uint16, ChainRuleSet), "chainCount")
|
|||
|
|
},
|
|||
|
|
2: {
|
|||
|
|
coverage: new Pointer(uint16, Coverage),
|
|||
|
|
backtrackClassDef: new Pointer(uint16, ClassDef),
|
|||
|
|
inputClassDef: new Pointer(uint16, ClassDef),
|
|||
|
|
lookaheadClassDef: new Pointer(uint16, ClassDef),
|
|||
|
|
chainCount: uint16,
|
|||
|
|
chainClassSet: new ArrayT(new Pointer(uint16, ChainRuleSet), "chainCount")
|
|||
|
|
},
|
|||
|
|
3: {
|
|||
|
|
backtrackGlyphCount: uint16,
|
|||
|
|
backtrackCoverage: new ArrayT(new Pointer(uint16, Coverage), "backtrackGlyphCount"),
|
|||
|
|
inputGlyphCount: uint16,
|
|||
|
|
inputCoverage: new ArrayT(new Pointer(uint16, Coverage), "inputGlyphCount"),
|
|||
|
|
lookaheadGlyphCount: uint16,
|
|||
|
|
lookaheadCoverage: new ArrayT(new Pointer(uint16, Coverage), "lookaheadGlyphCount"),
|
|||
|
|
lookupCount: uint16,
|
|||
|
|
lookupRecords: new ArrayT(LookupRecord, "lookupCount")
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/variations.ts
|
|||
|
|
/*******************
|
|||
|
|
* Variation Store *
|
|||
|
|
*******************/
|
|||
|
|
const F2DOT14 = new Fixed(16, 14);
|
|||
|
|
const RegionAxisCoordinates = new Struct({
|
|||
|
|
startCoord: F2DOT14,
|
|||
|
|
peakCoord: F2DOT14,
|
|||
|
|
endCoord: F2DOT14
|
|||
|
|
});
|
|||
|
|
const VariationRegionList = new Struct({
|
|||
|
|
axisCount: uint16,
|
|||
|
|
regionCount: uint16,
|
|||
|
|
variationRegions: new ArrayT(new ArrayT(RegionAxisCoordinates, "axisCount"), "regionCount")
|
|||
|
|
});
|
|||
|
|
const DeltaSet = new Struct({
|
|||
|
|
shortDeltas: new ArrayT(int16, (t) => t.parent.shortDeltaCount),
|
|||
|
|
regionDeltas: new ArrayT(int8, (t) => t.parent.regionIndexCount - t.parent.shortDeltaCount),
|
|||
|
|
deltas: (t) => t.shortDeltas.concat(t.regionDeltas)
|
|||
|
|
});
|
|||
|
|
const ItemVariationData = new Struct({
|
|||
|
|
itemCount: uint16,
|
|||
|
|
shortDeltaCount: uint16,
|
|||
|
|
regionIndexCount: uint16,
|
|||
|
|
regionIndexes: new ArrayT(uint16, "regionIndexCount"),
|
|||
|
|
deltaSets: new ArrayT(DeltaSet, "itemCount")
|
|||
|
|
});
|
|||
|
|
const ItemVariationStore = new Struct({
|
|||
|
|
format: uint16,
|
|||
|
|
variationRegionList: new Pointer(uint32, VariationRegionList),
|
|||
|
|
variationDataCount: uint16,
|
|||
|
|
itemVariationData: new ArrayT(new Pointer(uint32, ItemVariationData), "variationDataCount")
|
|||
|
|
});
|
|||
|
|
/**********************
|
|||
|
|
* Feature Variations *
|
|||
|
|
**********************/
|
|||
|
|
const ConditionTable = new VersionedStruct(uint16, { 1: {
|
|||
|
|
axisIndex: uint16,
|
|||
|
|
axisIndex: uint16,
|
|||
|
|
filterRangeMinValue: F2DOT14,
|
|||
|
|
filterRangeMaxValue: F2DOT14
|
|||
|
|
} });
|
|||
|
|
const ConditionSet = new Struct({
|
|||
|
|
conditionCount: uint16,
|
|||
|
|
conditionTable: new ArrayT(new Pointer(uint32, ConditionTable), "conditionCount")
|
|||
|
|
});
|
|||
|
|
const FeatureTableSubstitutionRecord = new Struct({
|
|||
|
|
featureIndex: uint16,
|
|||
|
|
alternateFeatureTable: new Pointer(uint32, Feature, { type: "parent" })
|
|||
|
|
});
|
|||
|
|
const FeatureTableSubstitution = new Struct({
|
|||
|
|
version: fixed32,
|
|||
|
|
substitutionCount: uint16,
|
|||
|
|
substitutions: new ArrayT(FeatureTableSubstitutionRecord, "substitutionCount")
|
|||
|
|
});
|
|||
|
|
const FeatureVariationRecord = new Struct({
|
|||
|
|
conditionSet: new Pointer(uint32, ConditionSet, { type: "parent" }),
|
|||
|
|
featureTableSubstitution: new Pointer(uint32, FeatureTableSubstitution, { type: "parent" })
|
|||
|
|
});
|
|||
|
|
const FeatureVariations = new Struct({
|
|||
|
|
majorVersion: uint16,
|
|||
|
|
minorVersion: uint16,
|
|||
|
|
featureVariationRecordCount: uint32,
|
|||
|
|
featureVariationRecords: new ArrayT(FeatureVariationRecord, "featureVariationRecordCount")
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/cff/CFFTop.ts
|
|||
|
|
var PredefinedOp = class {
|
|||
|
|
constructor(predefinedOps, type) {
|
|||
|
|
this.predefinedOps = predefinedOps;
|
|||
|
|
this.type = type;
|
|||
|
|
}
|
|||
|
|
decode(stream, parent, operands) {
|
|||
|
|
if (this.predefinedOps[operands[0]]) return this.predefinedOps[operands[0]];
|
|||
|
|
return this.type.decode(stream, parent, operands);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var CFFEncodingVersion = class extends NumberT {
|
|||
|
|
constructor() {
|
|||
|
|
super("UInt8");
|
|||
|
|
}
|
|||
|
|
decode(stream) {
|
|||
|
|
return uint8.decode(stream) & 127;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const Range1 = new Struct({
|
|||
|
|
first: uint16,
|
|||
|
|
nLeft: uint8
|
|||
|
|
});
|
|||
|
|
const Range2 = new Struct({
|
|||
|
|
first: uint16,
|
|||
|
|
nLeft: uint16
|
|||
|
|
});
|
|||
|
|
const CFFCustomEncoding = new VersionedStruct(new CFFEncodingVersion(), {
|
|||
|
|
0: {
|
|||
|
|
nCodes: uint8,
|
|||
|
|
codes: new ArrayT(uint8, "nCodes")
|
|||
|
|
},
|
|||
|
|
1: {
|
|||
|
|
nRanges: uint8,
|
|||
|
|
ranges: new ArrayT(Range1, "nRanges")
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
const CFFEncoding = new PredefinedOp([StandardEncoding, ExpertEncoding], new CFFPointer(CFFCustomEncoding, { lazy: true }));
|
|||
|
|
var RangeArray = class extends ArrayT {
|
|||
|
|
decode(stream, parent) {
|
|||
|
|
const length = resolveLength(this.length, stream, parent);
|
|||
|
|
let count = 0;
|
|||
|
|
const res = [];
|
|||
|
|
while (count < length) {
|
|||
|
|
const range = this.type.decode(stream, parent);
|
|||
|
|
range.offset = count;
|
|||
|
|
count += range.nLeft + 1;
|
|||
|
|
res.push(range);
|
|||
|
|
}
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const getCharsetLength = (t) => t.parent.CharStrings.length - 1;
|
|||
|
|
const CFFCustomCharset = new VersionedStruct(uint8, {
|
|||
|
|
0: { glyphs: new ArrayT(uint16, getCharsetLength) },
|
|||
|
|
1: { ranges: new RangeArray(Range1, getCharsetLength) },
|
|||
|
|
2: { ranges: new RangeArray(Range2, getCharsetLength) }
|
|||
|
|
});
|
|||
|
|
const CFFCharset = new PredefinedOp([
|
|||
|
|
ISOAdobeCharset,
|
|||
|
|
ExpertCharset,
|
|||
|
|
ExpertSubsetCharset
|
|||
|
|
], new CFFPointer(CFFCustomCharset, { lazy: true }));
|
|||
|
|
const FDRange3 = new Struct({
|
|||
|
|
first: uint16,
|
|||
|
|
fd: uint8
|
|||
|
|
});
|
|||
|
|
const FDRange4 = new Struct({
|
|||
|
|
first: uint32,
|
|||
|
|
fd: uint16
|
|||
|
|
});
|
|||
|
|
const FDSelect = new VersionedStruct(uint8, {
|
|||
|
|
0: { fds: new ArrayT(uint8, (t) => t.parent.CharStrings.length) },
|
|||
|
|
3: {
|
|||
|
|
nRanges: uint16,
|
|||
|
|
ranges: new ArrayT(FDRange3, "nRanges"),
|
|||
|
|
sentinel: uint16
|
|||
|
|
},
|
|||
|
|
4: {
|
|||
|
|
nRanges: uint32,
|
|||
|
|
ranges: new ArrayT(FDRange4, "nRanges"),
|
|||
|
|
sentinel: uint32
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
const ptr = new CFFPointer(CFFPrivateDict_default);
|
|||
|
|
var CFFPrivateOp = class {
|
|||
|
|
decode(stream, parent, operands) {
|
|||
|
|
parent.length = operands[0];
|
|||
|
|
return ptr.decode(stream, parent, [operands[1]]);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const Private = [
|
|||
|
|
18,
|
|||
|
|
"Private",
|
|||
|
|
new CFFPrivateOp(),
|
|||
|
|
null
|
|||
|
|
];
|
|||
|
|
const FontName = [
|
|||
|
|
[12, 38],
|
|||
|
|
"FontName",
|
|||
|
|
"sid",
|
|||
|
|
null
|
|||
|
|
];
|
|||
|
|
const FontMatrix = [
|
|||
|
|
[12, 7],
|
|||
|
|
"FontMatrix",
|
|||
|
|
"array",
|
|||
|
|
[
|
|||
|
|
.001,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
.001,
|
|||
|
|
0,
|
|||
|
|
0
|
|||
|
|
]
|
|||
|
|
];
|
|||
|
|
const PaintType = [
|
|||
|
|
[12, 5],
|
|||
|
|
"PaintType",
|
|||
|
|
"number",
|
|||
|
|
0
|
|||
|
|
];
|
|||
|
|
const CharStrings = [
|
|||
|
|
17,
|
|||
|
|
"CharStrings",
|
|||
|
|
new CFFPointer(new CFFIndex()),
|
|||
|
|
null
|
|||
|
|
];
|
|||
|
|
const FDSelectEntry = [
|
|||
|
|
[12, 37],
|
|||
|
|
"FDSelect",
|
|||
|
|
new CFFPointer(FDSelect),
|
|||
|
|
null
|
|||
|
|
];
|
|||
|
|
const FDArray = [
|
|||
|
|
[12, 36],
|
|||
|
|
"FDArray",
|
|||
|
|
new CFFPointer(new CFFIndex(new CFFDict([
|
|||
|
|
Private,
|
|||
|
|
FontName,
|
|||
|
|
FontMatrix,
|
|||
|
|
PaintType
|
|||
|
|
]))),
|
|||
|
|
null
|
|||
|
|
];
|
|||
|
|
const CFFTopDict = new CFFDict([
|
|||
|
|
[
|
|||
|
|
[12, 30],
|
|||
|
|
"ROS",
|
|||
|
|
[
|
|||
|
|
"sid",
|
|||
|
|
"sid",
|
|||
|
|
"number"
|
|||
|
|
],
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
0,
|
|||
|
|
"version",
|
|||
|
|
"sid",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
1,
|
|||
|
|
"Notice",
|
|||
|
|
"sid",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 0],
|
|||
|
|
"Copyright",
|
|||
|
|
"sid",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
2,
|
|||
|
|
"FullName",
|
|||
|
|
"sid",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
3,
|
|||
|
|
"FamilyName",
|
|||
|
|
"sid",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
4,
|
|||
|
|
"Weight",
|
|||
|
|
"sid",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 1],
|
|||
|
|
"isFixedPitch",
|
|||
|
|
"boolean",
|
|||
|
|
false
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 2],
|
|||
|
|
"ItalicAngle",
|
|||
|
|
"number",
|
|||
|
|
0
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 3],
|
|||
|
|
"UnderlinePosition",
|
|||
|
|
"number",
|
|||
|
|
-100
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 4],
|
|||
|
|
"UnderlineThickness",
|
|||
|
|
"number",
|
|||
|
|
50
|
|||
|
|
],
|
|||
|
|
PaintType,
|
|||
|
|
[
|
|||
|
|
[12, 6],
|
|||
|
|
"CharstringType",
|
|||
|
|
"number",
|
|||
|
|
2
|
|||
|
|
],
|
|||
|
|
FontMatrix,
|
|||
|
|
[
|
|||
|
|
13,
|
|||
|
|
"UniqueID",
|
|||
|
|
"number",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
5,
|
|||
|
|
"FontBBox",
|
|||
|
|
"array",
|
|||
|
|
[
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 8],
|
|||
|
|
"StrokeWidth",
|
|||
|
|
"number",
|
|||
|
|
0
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
14,
|
|||
|
|
"XUID",
|
|||
|
|
"array",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
15,
|
|||
|
|
"charset",
|
|||
|
|
CFFCharset,
|
|||
|
|
ISOAdobeCharset
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
16,
|
|||
|
|
"Encoding",
|
|||
|
|
CFFEncoding,
|
|||
|
|
StandardEncoding
|
|||
|
|
],
|
|||
|
|
CharStrings,
|
|||
|
|
Private,
|
|||
|
|
[
|
|||
|
|
[12, 20],
|
|||
|
|
"SyntheticBase",
|
|||
|
|
"number",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 21],
|
|||
|
|
"PostScript",
|
|||
|
|
"sid",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 22],
|
|||
|
|
"BaseFontName",
|
|||
|
|
"sid",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 23],
|
|||
|
|
"BaseFontBlend",
|
|||
|
|
"delta",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 31],
|
|||
|
|
"CIDFontVersion",
|
|||
|
|
"number",
|
|||
|
|
0
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 32],
|
|||
|
|
"CIDFontRevision",
|
|||
|
|
"number",
|
|||
|
|
0
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 33],
|
|||
|
|
"CIDFontType",
|
|||
|
|
"number",
|
|||
|
|
0
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 34],
|
|||
|
|
"CIDCount",
|
|||
|
|
"number",
|
|||
|
|
8720
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
[12, 35],
|
|||
|
|
"UIDBase",
|
|||
|
|
"number",
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
FDSelectEntry,
|
|||
|
|
FDArray,
|
|||
|
|
FontName
|
|||
|
|
]);
|
|||
|
|
const CFF2TopDict = new CFFDict([
|
|||
|
|
FontMatrix,
|
|||
|
|
CharStrings,
|
|||
|
|
FDSelectEntry,
|
|||
|
|
FDArray,
|
|||
|
|
[
|
|||
|
|
24,
|
|||
|
|
"vstore",
|
|||
|
|
new CFFPointer(new Struct({
|
|||
|
|
length: uint16,
|
|||
|
|
itemVariationStore: ItemVariationStore
|
|||
|
|
})),
|
|||
|
|
null
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
25,
|
|||
|
|
"maxstack",
|
|||
|
|
"number",
|
|||
|
|
193
|
|||
|
|
]
|
|||
|
|
]);
|
|||
|
|
const CFFTop = new VersionedStruct(fixed16, {
|
|||
|
|
1: {
|
|||
|
|
hdrSize: uint8,
|
|||
|
|
offSize: uint8,
|
|||
|
|
nameIndex: new CFFIndex(new StringT("length")),
|
|||
|
|
topDictIndex: new CFFIndex(CFFTopDict),
|
|||
|
|
stringIndex: new CFFIndex(new StringT("length")),
|
|||
|
|
globalSubrIndex: new CFFIndex()
|
|||
|
|
},
|
|||
|
|
2: {
|
|||
|
|
hdrSize: uint8,
|
|||
|
|
length: uint16,
|
|||
|
|
topDict: CFF2TopDict,
|
|||
|
|
globalSubrIndex: new CFFIndex()
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/cff/CFFStandardStrings.ts
|
|||
|
|
var CFFStandardStrings_default = [
|
|||
|
|
".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",
|
|||
|
|
"twodotenleader",
|
|||
|
|
"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"
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/cff/CFFFont.ts
|
|||
|
|
var CFFFont = class CFFFont {
|
|||
|
|
constructor(stream) {
|
|||
|
|
this.stream = stream;
|
|||
|
|
this.decode();
|
|||
|
|
}
|
|||
|
|
static decode(stream) {
|
|||
|
|
return new CFFFont(stream);
|
|||
|
|
}
|
|||
|
|
decode() {
|
|||
|
|
const top = CFFTop.decode(this.stream);
|
|||
|
|
for (const key in top) this[key] = top[key];
|
|||
|
|
if (this.version < 2) {
|
|||
|
|
if (this.topDictIndex.length !== 1) throw new Error("Only a single font is allowed in CFF");
|
|||
|
|
this.topDict = this.topDictIndex[0];
|
|||
|
|
}
|
|||
|
|
this.isCIDFont = this.topDict.ROS != null;
|
|||
|
|
return this;
|
|||
|
|
}
|
|||
|
|
string(sid) {
|
|||
|
|
if (this.version >= 2) return null;
|
|||
|
|
if (sid < CFFStandardStrings_default.length) return CFFStandardStrings_default[sid];
|
|||
|
|
return this.stringIndex[sid - CFFStandardStrings_default.length];
|
|||
|
|
}
|
|||
|
|
get postscriptName() {
|
|||
|
|
return this.version < 2 ? this.nameIndex[0] : null;
|
|||
|
|
}
|
|||
|
|
get fullName() {
|
|||
|
|
return this.string(this.topDict.FullName);
|
|||
|
|
}
|
|||
|
|
get familyName() {
|
|||
|
|
return this.string(this.topDict.FamilyName);
|
|||
|
|
}
|
|||
|
|
getCharString(glyph) {
|
|||
|
|
this.stream.pos = this.topDict.CharStrings[glyph].offset;
|
|||
|
|
return this.stream.readBuffer(this.topDict.CharStrings[glyph].length);
|
|||
|
|
}
|
|||
|
|
getGlyphName(gid) {
|
|||
|
|
if (this.version >= 2 || this.isCIDFont) return null;
|
|||
|
|
const { charset } = this.topDict;
|
|||
|
|
if (Array.isArray(charset)) return charset[gid];
|
|||
|
|
if (gid === 0) return ".notdef";
|
|||
|
|
gid -= 1;
|
|||
|
|
switch (charset.version) {
|
|||
|
|
case 0: return this.string(charset.glyphs[gid]);
|
|||
|
|
case 1:
|
|||
|
|
case 2:
|
|||
|
|
for (let i = 0; i < charset.ranges.length; i++) {
|
|||
|
|
let range = charset.ranges[i];
|
|||
|
|
if (range.offset <= gid && gid <= range.offset + range.nLeft) return this.string(range.first + (gid - range.offset));
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
fdForGlyph(gid) {
|
|||
|
|
if (!this.topDict.FDSelect) return null;
|
|||
|
|
switch (this.topDict.FDSelect.version) {
|
|||
|
|
case 0: return this.topDict.FDSelect.fds[gid];
|
|||
|
|
case 3:
|
|||
|
|
case 4:
|
|||
|
|
const { ranges } = this.topDict.FDSelect;
|
|||
|
|
let low = 0;
|
|||
|
|
let high = ranges.length - 1;
|
|||
|
|
while (low <= high) {
|
|||
|
|
const mid = low + high >> 1;
|
|||
|
|
if (gid < ranges[mid].first) high = mid - 1;
|
|||
|
|
else if (mid < high && gid >= ranges[mid + 1].first) low = mid + 1;
|
|||
|
|
else return ranges[mid].fd;
|
|||
|
|
}
|
|||
|
|
default: throw new Error(`Unknown FDSelect version: ${this.topDict.FDSelect.version}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
privateDictForGlyph(gid) {
|
|||
|
|
if (this.topDict.FDSelect) {
|
|||
|
|
const fd = this.fdForGlyph(gid);
|
|||
|
|
if (this.topDict.FDArray[fd]) return this.topDict.FDArray[fd].Private;
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
if (this.version < 2) return this.topDict.Private;
|
|||
|
|
return this.topDict.FDArray[0].Private;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/sbix.ts
|
|||
|
|
const ImageTable = new Struct({
|
|||
|
|
ppem: uint16,
|
|||
|
|
resolution: uint16,
|
|||
|
|
imageOffsets: new ArrayT(new Pointer(uint32, "void"), (t) => t.parent.parent.maxp.numGlyphs + 1)
|
|||
|
|
});
|
|||
|
|
var sbix_default = new Struct({
|
|||
|
|
version: uint16,
|
|||
|
|
flags: new Bitfield(uint16, ["renderOutlines"]),
|
|||
|
|
numImgTables: uint32,
|
|||
|
|
imageTables: new ArrayT(new Pointer(uint32, ImageTable), "numImgTables")
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/COLR.ts
|
|||
|
|
let LayerRecord = new Struct({
|
|||
|
|
gid: uint16,
|
|||
|
|
paletteIndex: uint16
|
|||
|
|
});
|
|||
|
|
let BaseGlyphRecord = new Struct({
|
|||
|
|
gid: uint16,
|
|||
|
|
firstLayerIndex: uint16,
|
|||
|
|
numLayers: uint16
|
|||
|
|
});
|
|||
|
|
var COLR_default = new Struct({
|
|||
|
|
version: uint16,
|
|||
|
|
numBaseGlyphRecords: uint16,
|
|||
|
|
baseGlyphRecord: new Pointer(uint32, new ArrayT(BaseGlyphRecord, "numBaseGlyphRecords")),
|
|||
|
|
layerRecords: new Pointer(uint32, new ArrayT(LayerRecord, "numLayerRecords"), { lazy: true }),
|
|||
|
|
numLayerRecords: uint16
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/CPAL.ts
|
|||
|
|
const ColorRecord = new Struct({
|
|||
|
|
blue: uint8,
|
|||
|
|
green: uint8,
|
|||
|
|
red: uint8,
|
|||
|
|
alpha: uint8
|
|||
|
|
});
|
|||
|
|
var CPAL_default = new VersionedStruct(uint16, {
|
|||
|
|
header: {
|
|||
|
|
numPaletteEntries: uint16,
|
|||
|
|
numPalettes: uint16,
|
|||
|
|
numColorRecords: uint16,
|
|||
|
|
colorRecords: new Pointer(uint32, new ArrayT(ColorRecord, "numColorRecords")),
|
|||
|
|
colorRecordIndices: new ArrayT(uint16, "numPalettes")
|
|||
|
|
},
|
|||
|
|
0: {},
|
|||
|
|
1: {
|
|||
|
|
offsetPaletteTypeArray: new Pointer(uint32, new ArrayT(uint32, "numPalettes")),
|
|||
|
|
offsetPaletteLabelArray: new Pointer(uint32, new ArrayT(uint16, "numPalettes")),
|
|||
|
|
offsetPaletteEntryLabelArray: new Pointer(uint32, new ArrayT(uint16, "numPaletteEntries"))
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/GSUB.ts
|
|||
|
|
const Sequence = new ArrayT(uint16, uint16);
|
|||
|
|
const AlternateSet = Sequence;
|
|||
|
|
const Ligature = new Struct({
|
|||
|
|
glyph: uint16,
|
|||
|
|
compCount: uint16,
|
|||
|
|
components: new ArrayT(uint16, (t) => t.compCount - 1)
|
|||
|
|
});
|
|||
|
|
const LigatureSet = new ArrayT(new Pointer(uint16, Ligature), uint16);
|
|||
|
|
const GSUBLookup = new VersionedStruct("lookupType", {
|
|||
|
|
1: new VersionedStruct(uint16, {
|
|||
|
|
1: {
|
|||
|
|
coverage: new Pointer(uint16, Coverage),
|
|||
|
|
deltaGlyphID: int16
|
|||
|
|
},
|
|||
|
|
2: {
|
|||
|
|
coverage: new Pointer(uint16, Coverage),
|
|||
|
|
glyphCount: uint16,
|
|||
|
|
substitute: new LazyArray(uint16, "glyphCount")
|
|||
|
|
}
|
|||
|
|
}),
|
|||
|
|
2: {
|
|||
|
|
substFormat: uint16,
|
|||
|
|
coverage: new Pointer(uint16, Coverage),
|
|||
|
|
count: uint16,
|
|||
|
|
sequences: new LazyArray(new Pointer(uint16, Sequence), "count")
|
|||
|
|
},
|
|||
|
|
3: {
|
|||
|
|
substFormat: uint16,
|
|||
|
|
coverage: new Pointer(uint16, Coverage),
|
|||
|
|
count: uint16,
|
|||
|
|
alternateSet: new LazyArray(new Pointer(uint16, AlternateSet), "count")
|
|||
|
|
},
|
|||
|
|
4: {
|
|||
|
|
substFormat: uint16,
|
|||
|
|
coverage: new Pointer(uint16, Coverage),
|
|||
|
|
count: uint16,
|
|||
|
|
ligatureSets: new LazyArray(new Pointer(uint16, LigatureSet), "count")
|
|||
|
|
},
|
|||
|
|
5: Context,
|
|||
|
|
6: ChainingContext,
|
|||
|
|
7: {
|
|||
|
|
substFormat: uint16,
|
|||
|
|
lookupType: uint16,
|
|||
|
|
extension: new Pointer(uint32, null)
|
|||
|
|
},
|
|||
|
|
8: {
|
|||
|
|
substFormat: uint16,
|
|||
|
|
coverage: new Pointer(uint16, Coverage),
|
|||
|
|
backtrackCoverage: new ArrayT(new Pointer(uint16, Coverage), "backtrackGlyphCount"),
|
|||
|
|
lookaheadGlyphCount: uint16,
|
|||
|
|
lookaheadCoverage: new ArrayT(new Pointer(uint16, Coverage), "lookaheadGlyphCount"),
|
|||
|
|
glyphCount: uint16,
|
|||
|
|
substitutes: new ArrayT(uint16, "glyphCount")
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
GSUBLookup.versions[7].extension.type = GSUBLookup;
|
|||
|
|
var GSUB_default = new VersionedStruct(uint32, {
|
|||
|
|
header: {
|
|||
|
|
scriptList: new Pointer(uint16, ScriptList),
|
|||
|
|
featureList: new Pointer(uint16, FeatureList),
|
|||
|
|
lookupList: new Pointer(uint16, new LookupList(GSUBLookup))
|
|||
|
|
},
|
|||
|
|
65536: {},
|
|||
|
|
65537: { featureVariations: new Pointer(uint32, FeatureVariations) }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/HVAR.ts
|
|||
|
|
var VariableSizeNumber = class {
|
|||
|
|
#size;
|
|||
|
|
constructor(size) {
|
|||
|
|
this.#size = size;
|
|||
|
|
}
|
|||
|
|
decode(stream, parent) {
|
|||
|
|
switch (this.size(0, parent)) {
|
|||
|
|
case 1: return stream.readUInt8();
|
|||
|
|
case 2: return stream.readUInt16BE();
|
|||
|
|
case 3: return stream.readUInt24BE();
|
|||
|
|
case 4: return stream.readUInt32BE();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
size(_val, parent) {
|
|||
|
|
return resolveLength(this.#size, null, parent);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const MapDataEntry = new Struct({
|
|||
|
|
entry: new VariableSizeNumber((t) => ((t.parent.entryFormat & 48) >> 4) + 1),
|
|||
|
|
outerIndex: (t) => t.entry >> (t.parent.entryFormat & 15) + 1,
|
|||
|
|
innerIndex: (t) => t.entry & (1 << (t.parent.entryFormat & 15) + 1) - 1
|
|||
|
|
});
|
|||
|
|
const DeltaSetIndexMap = new Struct({
|
|||
|
|
entryFormat: uint16,
|
|||
|
|
mapCount: uint16,
|
|||
|
|
mapData: new ArrayT(MapDataEntry, "mapCount")
|
|||
|
|
});
|
|||
|
|
var HVAR_default = new Struct({
|
|||
|
|
majorVersion: uint16,
|
|||
|
|
minorVersion: uint16,
|
|||
|
|
itemVariationStore: new Pointer(uint32, ItemVariationStore),
|
|||
|
|
advanceWidthMapping: new Pointer(uint32, DeltaSetIndexMap),
|
|||
|
|
LSBMapping: new Pointer(uint32, DeltaSetIndexMap),
|
|||
|
|
RSBMapping: new Pointer(uint32, DeltaSetIndexMap)
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/vhea.ts
|
|||
|
|
var vhea_default = new Struct({
|
|||
|
|
version: uint16,
|
|||
|
|
ascent: int16,
|
|||
|
|
descent: int16,
|
|||
|
|
lineGap: int16,
|
|||
|
|
advanceHeightMax: int16,
|
|||
|
|
minTopSideBearing: int16,
|
|||
|
|
minBottomSideBearing: int16,
|
|||
|
|
yMaxExtent: int16,
|
|||
|
|
caretSlopeRise: int16,
|
|||
|
|
caretSlopeRun: int16,
|
|||
|
|
caretOffset: int16,
|
|||
|
|
reserved: new Reserved(int16, 4),
|
|||
|
|
metricDataFormat: int16,
|
|||
|
|
numberOfMetrics: uint16
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/vmtx.ts
|
|||
|
|
const VmtxEntry = new Struct({
|
|||
|
|
advance: uint16,
|
|||
|
|
bearing: int16
|
|||
|
|
});
|
|||
|
|
var vmtx_default = new Struct({
|
|||
|
|
metrics: new LazyArray(VmtxEntry, (t) => t.parent.vhea.numberOfMetrics),
|
|||
|
|
bearings: new LazyArray(int16, (t) => t.parent.maxp.numGlyphs - t.parent.vhea.numberOfMetrics)
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/avar.ts
|
|||
|
|
const shortFrac$1 = new Fixed(16, 14);
|
|||
|
|
const Correspondence = new Struct({
|
|||
|
|
fromCoord: shortFrac$1,
|
|||
|
|
toCoord: shortFrac$1
|
|||
|
|
});
|
|||
|
|
const Segment = new Struct({
|
|||
|
|
pairCount: uint16,
|
|||
|
|
correspondence: new ArrayT(Correspondence, "pairCount")
|
|||
|
|
});
|
|||
|
|
var avar_default = new Struct({
|
|||
|
|
version: fixed32,
|
|||
|
|
axisCount: uint32,
|
|||
|
|
segment: new ArrayT(Segment, "axisCount")
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/fvar.ts
|
|||
|
|
const Axis = new Struct({
|
|||
|
|
axisTag: new StringT(4),
|
|||
|
|
minValue: fixed32,
|
|||
|
|
defaultValue: fixed32,
|
|||
|
|
maxValue: fixed32,
|
|||
|
|
flags: uint16,
|
|||
|
|
nameID: uint16,
|
|||
|
|
name: (t) => t.parent.parent.name.records.fontFeatures[t.nameID]
|
|||
|
|
});
|
|||
|
|
const Instance = new Struct({
|
|||
|
|
nameID: uint16,
|
|||
|
|
name: (t) => t.parent.parent.name.records.fontFeatures[t.nameID],
|
|||
|
|
flags: uint16,
|
|||
|
|
coord: new ArrayT(fixed32, (t) => t.parent.axisCount),
|
|||
|
|
postscriptNameID: new Optional(uint16, (t) => t.parent.instanceSize - t._currentOffset > 0)
|
|||
|
|
});
|
|||
|
|
var fvar_default = new Struct({
|
|||
|
|
version: fixed32,
|
|||
|
|
offsetToData: uint16,
|
|||
|
|
countSizePairs: uint16,
|
|||
|
|
axisCount: uint16,
|
|||
|
|
axisSize: uint16,
|
|||
|
|
instanceCount: uint16,
|
|||
|
|
instanceSize: uint16,
|
|||
|
|
axis: new ArrayT(Axis, "axisCount"),
|
|||
|
|
instance: new ArrayT(Instance, "instanceCount")
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/gvar.ts
|
|||
|
|
const shortFrac = new Fixed(16, 14);
|
|||
|
|
var Offset = class {
|
|||
|
|
static decode(stream, parent) {
|
|||
|
|
return parent.flags ? stream.readUInt32BE() : stream.readUInt16BE() * 2;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const gvar = new Struct({
|
|||
|
|
version: uint16,
|
|||
|
|
reserved: new Reserved(uint16),
|
|||
|
|
axisCount: uint16,
|
|||
|
|
globalCoordCount: uint16,
|
|||
|
|
globalCoords: new Pointer(uint32, new ArrayT(new ArrayT(shortFrac, "axisCount"), "globalCoordCount")),
|
|||
|
|
glyphCount: uint16,
|
|||
|
|
flags: uint16,
|
|||
|
|
offsetToData: uint32,
|
|||
|
|
offsets: new ArrayT(new Pointer(Offset, "void", {
|
|||
|
|
relativeTo: (ctx) => ctx.offsetToData,
|
|||
|
|
allowNull: false
|
|||
|
|
}), (t) => t.glyphCount + 1)
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/index.ts
|
|||
|
|
const tables = {
|
|||
|
|
cmap: cmap_default,
|
|||
|
|
head: head_default,
|
|||
|
|
hhea: hhea_default,
|
|||
|
|
hmtx: hmtx_default,
|
|||
|
|
maxp: maxp_default,
|
|||
|
|
name: NameTable,
|
|||
|
|
"OS/2": OS2,
|
|||
|
|
post: post_default,
|
|||
|
|
loca,
|
|||
|
|
glyf: glyf_default,
|
|||
|
|
"CFF ": CFFFont,
|
|||
|
|
CFF2: CFFFont,
|
|||
|
|
sbix: sbix_default,
|
|||
|
|
COLR: COLR_default,
|
|||
|
|
CPAL: CPAL_default,
|
|||
|
|
GSUB: GSUB_default,
|
|||
|
|
HVAR: HVAR_default,
|
|||
|
|
vhea: vhea_default,
|
|||
|
|
vmtx: vmtx_default,
|
|||
|
|
avar: avar_default,
|
|||
|
|
fvar: fvar_default,
|
|||
|
|
gvar
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/utils.ts
|
|||
|
|
function binarySearch(arr, cmp) {
|
|||
|
|
let min = 0;
|
|||
|
|
let max = arr.length - 1;
|
|||
|
|
while (min <= max) {
|
|||
|
|
const mid = min + max >> 1;
|
|||
|
|
const res = cmp(arr[mid]);
|
|||
|
|
if (res < 0) max = mid - 1;
|
|||
|
|
else if (res > 0) min = mid + 1;
|
|||
|
|
else return mid;
|
|||
|
|
}
|
|||
|
|
return -1;
|
|||
|
|
}
|
|||
|
|
function range(index, end) {
|
|||
|
|
const range = [];
|
|||
|
|
while (index < end) range.push(index++);
|
|||
|
|
return range;
|
|||
|
|
}
|
|||
|
|
const asciiDecoder = new TextDecoder("ascii");
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region \0@oxc-project+runtime@0.112.0/helpers/decorate.js
|
|||
|
|
function __decorate(decorators, target, key, desc) {
|
|||
|
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|||
|
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|||
|
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|||
|
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/CmapProcessor.ts
|
|||
|
|
var CmapProcessor = class {
|
|||
|
|
#cmap;
|
|||
|
|
#uvs;
|
|||
|
|
#encoding;
|
|||
|
|
constructor(cmapTable) {
|
|||
|
|
this.#encoding = null;
|
|||
|
|
this.#cmap = this.#findSubtable(cmapTable, [
|
|||
|
|
[3, 10],
|
|||
|
|
[0, 6],
|
|||
|
|
[0, 4],
|
|||
|
|
[3, 1],
|
|||
|
|
[0, 3],
|
|||
|
|
[0, 2],
|
|||
|
|
[0, 1],
|
|||
|
|
[0, 0]
|
|||
|
|
]);
|
|||
|
|
if (!this.#cmap) for (let cmap of cmapTable.tables) {
|
|||
|
|
const mapping = getEncodingMapping(getEncoding(cmap.platformID, cmap.encodingID, cmap.table.language - 1));
|
|||
|
|
if (mapping) {
|
|||
|
|
this.#cmap = cmap.table;
|
|||
|
|
this.#encoding = mapping;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (!this.#cmap) throw new Error("Could not find a supported cmap table");
|
|||
|
|
this.#uvs = this.#findSubtable(cmapTable, [[0, 5]]);
|
|||
|
|
if (this.#uvs && this.#uvs.version !== 14) this.#uvs = null;
|
|||
|
|
}
|
|||
|
|
#findSubtable(cmapTable, pairs) {
|
|||
|
|
for (let [platformID, encodingID] of pairs) for (let cmap of cmapTable.tables) if (cmap.platformID === platformID && cmap.encodingID === encodingID) return cmap.table;
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
lookup(codepoint, variationSelector) {
|
|||
|
|
if (this.#encoding) codepoint = this.#encoding.get(codepoint) || codepoint;
|
|||
|
|
else if (variationSelector) {
|
|||
|
|
let gid = this.#getVariationSelector(codepoint, variationSelector);
|
|||
|
|
if (gid) return gid;
|
|||
|
|
}
|
|||
|
|
let cmap = this.#cmap;
|
|||
|
|
switch (cmap.version) {
|
|||
|
|
case 0: return cmap.codeMap.get(codepoint) || 0;
|
|||
|
|
case 4: {
|
|||
|
|
let min = 0;
|
|||
|
|
let max = cmap.segCount - 1;
|
|||
|
|
while (min <= max) {
|
|||
|
|
let mid = min + max >> 1;
|
|||
|
|
if (codepoint < cmap.startCode.get(mid)) max = mid - 1;
|
|||
|
|
else if (codepoint > cmap.endCode.get(mid)) min = mid + 1;
|
|||
|
|
else {
|
|||
|
|
let rangeOffset = cmap.idRangeOffset.get(mid);
|
|||
|
|
let gid;
|
|||
|
|
if (rangeOffset === 0) gid = codepoint + cmap.idDelta.get(mid);
|
|||
|
|
else {
|
|||
|
|
let index = rangeOffset / 2 + (codepoint - cmap.startCode.get(mid)) - (cmap.segCount - mid);
|
|||
|
|
gid = cmap.glyphIndexArray.get(index) || 0;
|
|||
|
|
if (gid !== 0) gid += cmap.idDelta.get(mid);
|
|||
|
|
}
|
|||
|
|
return gid & 65535;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
case 8: throw new Error("TODO: cmap format 8");
|
|||
|
|
case 6:
|
|||
|
|
case 10: return cmap.glyphIndices.get(codepoint - cmap.firstCode) || 0;
|
|||
|
|
case 12:
|
|||
|
|
case 13: {
|
|||
|
|
let min = 0;
|
|||
|
|
let max = cmap.nGroups - 1;
|
|||
|
|
while (min <= max) {
|
|||
|
|
let mid = min + max >> 1;
|
|||
|
|
let group = cmap.groups.get(mid);
|
|||
|
|
if (codepoint < group.startCharCode) max = mid - 1;
|
|||
|
|
else if (codepoint > group.endCharCode) min = mid + 1;
|
|||
|
|
else if (cmap.version === 12) return group.glyphID + (codepoint - group.startCharCode);
|
|||
|
|
else return group.glyphID;
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
case 14: throw new Error("TODO: cmap format 14");
|
|||
|
|
default: throw new Error(`Unknown cmap format ${cmap.version}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
#getVariationSelector(codepoint, variationSelector) {
|
|||
|
|
if (!this.#uvs) return 0;
|
|||
|
|
const selectors = this.#uvs.varSelectors.toArray();
|
|||
|
|
let i = binarySearch(selectors, (x) => variationSelector - x.varSelector);
|
|||
|
|
const sel = selectors[i];
|
|||
|
|
if (i !== -1 && sel.defaultUVS) i = binarySearch(sel.defaultUVS, (x) => codepoint < x.startUnicodeValue ? -1 : codepoint > x.startUnicodeValue + x.additionalCount ? 1 : 0);
|
|||
|
|
if (i !== -1 && sel.nonDefaultUVS) {
|
|||
|
|
i = binarySearch(sel.nonDefaultUVS, (x) => codepoint - x.unicodeValue);
|
|||
|
|
if (i !== -1) return sel.nonDefaultUVS[i].glyphID;
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
getCharacterSet() {
|
|||
|
|
const cmap = this.#cmap;
|
|||
|
|
switch (cmap.version) {
|
|||
|
|
case 0: return range(0, cmap.codeMap.length);
|
|||
|
|
case 4: return cmap.endCode.toArray().flatMap((endCode, i) => range(cmap.startCode.get(i), endCode + 1));
|
|||
|
|
case 8: throw new Error("TODO: cmap format 8");
|
|||
|
|
case 6:
|
|||
|
|
case 10: return range(cmap.firstCode, cmap.firstCode + cmap.glyphIndices.length);
|
|||
|
|
case 12:
|
|||
|
|
case 13: return cmap.groups.toArray().flatMap((group) => range(group.startCharCode, group.endCharCode + 1));
|
|||
|
|
case 14: throw new Error("TODO: cmap format 14");
|
|||
|
|
default: throw new Error(`Unknown cmap format ${cmap.version}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
__decorate([cache], CmapProcessor.prototype, "getCharacterSet", null);
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/glyph/BBox.ts
|
|||
|
|
/**
|
|||
|
|
* Represents a glyph bounding box
|
|||
|
|
*/
|
|||
|
|
var BBox = class {
|
|||
|
|
constructor(minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity) {
|
|||
|
|
this.minX = minX;
|
|||
|
|
this.minY = minY;
|
|||
|
|
this.maxX = maxX;
|
|||
|
|
this.maxY = maxY;
|
|||
|
|
}
|
|||
|
|
get width() {
|
|||
|
|
return this.maxX - this.minX;
|
|||
|
|
}
|
|||
|
|
get height() {
|
|||
|
|
return this.maxY - this.minY;
|
|||
|
|
}
|
|||
|
|
addPoint(x, y) {
|
|||
|
|
if (Math.abs(x) !== Infinity) {
|
|||
|
|
if (x < this.minX) this.minX = x;
|
|||
|
|
if (x > this.maxX) this.maxX = x;
|
|||
|
|
}
|
|||
|
|
if (Math.abs(y) !== Infinity) {
|
|||
|
|
if (y < this.minY) this.minY = y;
|
|||
|
|
if (y > this.maxY) this.maxY = y;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/glyph/Path.ts
|
|||
|
|
const SVG_COMMANDS = {
|
|||
|
|
moveTo: "M",
|
|||
|
|
lineTo: "L",
|
|||
|
|
quadraticCurveTo: "Q",
|
|||
|
|
bezierCurveTo: "C",
|
|||
|
|
closePath: "Z"
|
|||
|
|
};
|
|||
|
|
/**
|
|||
|
|
* Path objects are returned by glyphs and represent the actual
|
|||
|
|
* vector outlines for each glyph in the font.
|
|||
|
|
*/
|
|||
|
|
var Path = class Path {
|
|||
|
|
commands = [];
|
|||
|
|
_bbox = null;
|
|||
|
|
_cbox = null;
|
|||
|
|
/**
|
|||
|
|
* Converts the path to an SVG path data string
|
|||
|
|
*/
|
|||
|
|
toSVG() {
|
|||
|
|
return this.commands.map((c) => {
|
|||
|
|
const args = c.args.map((arg) => Math.round(arg * 100) / 100);
|
|||
|
|
return `${SVG_COMMANDS[c.command]}${args.join(" ")}`;
|
|||
|
|
}).join("");
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Gets the "control box" of a path.
|
|||
|
|
* This is like the bounding box, but it includes all points including
|
|||
|
|
* control points of bezier segments and is much faster to compute than
|
|||
|
|
* the real bounding box.
|
|||
|
|
*/
|
|||
|
|
get cbox() {
|
|||
|
|
if (!this._cbox) {
|
|||
|
|
const cbox = new BBox();
|
|||
|
|
for (const command of this.commands) for (let i = 0; i < command.args.length; i += 2) cbox.addPoint(command.args[i], command.args[i + 1]);
|
|||
|
|
this._cbox = Object.freeze(cbox);
|
|||
|
|
}
|
|||
|
|
return this._cbox;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Gets the exact bounding box of the path by evaluating curve segments.
|
|||
|
|
* Slower to compute than the control box, but more accurate.
|
|||
|
|
*/
|
|||
|
|
get bbox() {
|
|||
|
|
if (this._bbox) return this._bbox;
|
|||
|
|
const bbox = new BBox();
|
|||
|
|
let cx = 0, cy = 0;
|
|||
|
|
const f = (t) => Math.pow(1 - t, 3) * p0[i] + 3 * Math.pow(1 - t, 2) * t * p1[i] + 3 * (1 - t) * Math.pow(t, 2) * p2[i] + Math.pow(t, 3) * p3[i];
|
|||
|
|
for (const c of this.commands) switch (c.command) {
|
|||
|
|
case "moveTo":
|
|||
|
|
case "lineTo":
|
|||
|
|
const [x, y] = c.args;
|
|||
|
|
bbox.addPoint(x, y);
|
|||
|
|
cx = x;
|
|||
|
|
cy = y;
|
|||
|
|
break;
|
|||
|
|
case "quadraticCurveTo":
|
|||
|
|
case "bezierCurveTo":
|
|||
|
|
if (c.command === "quadraticCurveTo") {
|
|||
|
|
var [qp1x, qp1y, p3x, p3y] = c.args;
|
|||
|
|
var cp1x = cx + 2 / 3 * (qp1x - cx);
|
|||
|
|
var cp1y = cy + 2 / 3 * (qp1y - cy);
|
|||
|
|
var cp2x = p3x + 2 / 3 * (qp1x - p3x);
|
|||
|
|
var cp2y = p3y + 2 / 3 * (qp1y - p3y);
|
|||
|
|
} else var [cp1x, cp1y, cp2x, cp2y, p3x, p3y] = c.args;
|
|||
|
|
bbox.addPoint(p3x, p3y);
|
|||
|
|
var p0 = [cx, cy];
|
|||
|
|
var p1 = [cp1x, cp1y];
|
|||
|
|
var p2 = [cp2x, cp2y];
|
|||
|
|
var p3 = [p3x, p3y];
|
|||
|
|
for (var i = 0; i <= 1; i++) {
|
|||
|
|
const b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i];
|
|||
|
|
const a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i];
|
|||
|
|
const c = 3 * p1[i] - 3 * p0[i];
|
|||
|
|
if (a === 0) {
|
|||
|
|
if (b === 0) continue;
|
|||
|
|
const t = -c / b;
|
|||
|
|
if (0 < t && t < 1) {
|
|||
|
|
if (i === 0) bbox.addPoint(f(t), bbox.maxY);
|
|||
|
|
else if (i === 1) bbox.addPoint(bbox.maxX, f(t));
|
|||
|
|
}
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
const b2ac = Math.pow(b, 2) - 4 * c * a;
|
|||
|
|
if (b2ac < 0) continue;
|
|||
|
|
const t1 = (-b + Math.sqrt(b2ac)) / (2 * a);
|
|||
|
|
if (0 < t1 && t1 < 1) {
|
|||
|
|
if (i === 0) bbox.addPoint(f(t1), bbox.maxY);
|
|||
|
|
else if (i === 1) bbox.addPoint(bbox.maxX, f(t1));
|
|||
|
|
}
|
|||
|
|
const t2 = (-b - Math.sqrt(b2ac)) / (2 * a);
|
|||
|
|
if (0 < t2 && t2 < 1) {
|
|||
|
|
if (i === 0) bbox.addPoint(f(t2), bbox.maxY);
|
|||
|
|
else if (i === 1) bbox.addPoint(bbox.maxX, f(t2));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
cx = p3x;
|
|||
|
|
cy = p3y;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
return this._bbox = Object.freeze(bbox);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Applies a mapping function to each point in the path.
|
|||
|
|
*/
|
|||
|
|
mapPoints(fn) {
|
|||
|
|
const path = new Path();
|
|||
|
|
for (const c of this.commands) {
|
|||
|
|
const args = [];
|
|||
|
|
for (let i = 0; i < c.args.length; i += 2) {
|
|||
|
|
const [x, y] = fn(c.args[i], c.args[i + 1]);
|
|||
|
|
args.push(x, y);
|
|||
|
|
}
|
|||
|
|
path[c.command](...args);
|
|||
|
|
}
|
|||
|
|
return path;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Transforms the path by the given matrix.
|
|||
|
|
*/
|
|||
|
|
transform(m0, m1, m2, m3, m4, m5) {
|
|||
|
|
return this.mapPoints((x, y) => {
|
|||
|
|
return [m0 * x + m2 * y + m4, m1 * x + m3 * y + m5];
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Translates the path by the given offset.
|
|||
|
|
*/
|
|||
|
|
translate(x, y) {
|
|||
|
|
return this.transform(1, 0, 0, 1, x, y);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Rotates the path by the given angle (in radians).
|
|||
|
|
*/
|
|||
|
|
rotate(angle) {
|
|||
|
|
const cos = Math.cos(angle);
|
|||
|
|
const sin = Math.sin(angle);
|
|||
|
|
return this.transform(cos, sin, -sin, cos, 0, 0);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Scales the path.
|
|||
|
|
*/
|
|||
|
|
scale(scaleX, scaleY = scaleX) {
|
|||
|
|
return this.transform(scaleX, 0, 0, scaleY, 0, 0);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
for (let command of [
|
|||
|
|
"moveTo",
|
|||
|
|
"lineTo",
|
|||
|
|
"quadraticCurveTo",
|
|||
|
|
"bezierCurveTo",
|
|||
|
|
"closePath"
|
|||
|
|
]) Path.prototype[command] = function(...args) {
|
|||
|
|
this._bbox = this._cbox = null;
|
|||
|
|
this.commands.push({
|
|||
|
|
command,
|
|||
|
|
args
|
|||
|
|
});
|
|||
|
|
return this;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/glyph/isMark.ts
|
|||
|
|
const markRanges = JSON.parse("[[768,879],[1155,1161],[1425,1469],1471,[1473,1474],[1476,1477],1479,[1552,1562],[1611,1631],1648,[1750,1756],[1759,1764],[1767,1768],[1770,1773],1809,[1840,1866],[1958,1968],[2027,2035],2045,[2070,2073],[2075,2083],[2085,2087],[2089,2093],[2137,2139],[2259,2273],[2275,2307],[2362,2364],[2366,2383],[2385,2391],[2402,2403],[2433,2435],2492,[2494,2500],[2503,2504],[2507,2509],2519,[2530,2531],2558,[2561,2563],2620,[2622,2626],[2631,2632],[2635,2637],2641,[2672,2673],2677,[2689,2691],2748,[2750,2757],[2759,2761],[2763,2765],[2786,2787],[2810,2815],[2817,2819],2876,[2878,2884],[2887,2888],[2891,2893],[2902,2903],[2914,2915],2946,[3006,3010],[3014,3016],[3018,3021],3031,[3072,3076],[3134,3140],[3142,3144],[3146,3149],[3157,3158],[3170,3171],[3201,3203],3260,[3262,3268],[3270,3272],[3274,3277],[3285,3286],[3298,3299],[3328,3331],[3387,3388],[3390,3396],[3398,3400],[3402,3405],3415,[3426,3427],[3458,3459],3530,[3535,3540],3542,[3544,3551],[3570,3571],3633,[3636,3642],[3655,3662],3761,[3764,3772],[3784,3789],[3864,3865],3893,3895,3897,[3902,3903],[3953,3972],[3974,3975],[3981,3991],[3993,4028],4038,[4139,4158],[4182,4185],[4190,4192],[4194,4196],[4199,4205],[4209,4212],[4226,4237],4239,[4250,4253],[4957,4959],[5906,5908],[5938,5940],[5970,5971],[6002,6003],[6068,6099],6109,[6155,6157],[6277,6278],6313,[6432,6443],[6448,6459],[6679,6683],[6741,6750],[6752,6780],6783,[6832,6846],[6912,6916],[6964,6980],[7019,7027],[7040,7042],[7073,7085],[7142,7155],[7204,7223],[7376,7378],[7380,7400],7405,7412,[7415,7417],[7616,7673],[7675,7679],[8400,8432],[11503,11505],11647,[11744,11775],[12330,12335],[12441,12442],[42607,42610],[42612,42621],[42654,42655],[42736,42737],43010,43014,43019,[43043,43047],[43136,43137],[43188,43205],[43232,43249],43263,[43302,43309],[43335,43347],[43392,43395],[43443,43456],43493,[43561,43574],43587,[43596,43597],[43643,43645],43696,[43698,43700],[43703,43704],[43710,43711],43713,[43755,43759],[43765,43766],[44003,44010],[44012,44013],64286,[65024,65039],[65056,65071],66045,66272,[66422,66426],[68097,68099],[68101,68102],[68108,68111],[68152,68154],68159,[68325,68326],[68900,68903],[69446,69456],[69632,69634],[69688,69702],[69759,69762],[69808,69818],[69888,69890],[69927,69940],[69957,69958],70003,[70016,70018],[70067,70080],[70089,70092],[70188,70199],70206,[70367,70378],[70400,70403],[70459,70460],[70462,70468],[70471,70472],[70475,70477],70487,[70498,70499],[70502,70508],[70512,70516],[70709,70726],70750,[70832,70851],[71087,71093],[71096,71104],[71132,71133],[71216,71232],[71339,71351],[71453,71467],[71724,71738],[72145,72151],[72154,72160],72164,[72193,72202],[72243,72249],[72251,72254],72263,[72273,72283],[72330,72345],[72751,72758],[72760,72767],[72850,72871],[72873,72886],[73009,73014],73018,[73020,73021],[73023,73029],73031,[73098,73102],[73104,73105],[73107,73111],[73459,73462],[92912,92916],[92976,92982],94031,[94033,94087],[94095,94098],[113821,113822],[119141,119145],[119149,119154],[119163,119170],[119173,119179],[119210,119213],[119362,119364],[121344,121398],[121403,121452],121461,121476,[121499,121503],[121505,121519],[122880,122886],[122888,122904],[122907,122913],[122915,122916],[122918,122922],[123184,123190],[123628,123631],[125136,125142],[125252,125258],[917760,917999]]").map((item) => typeof item === "number" ? [item, item] : item);
|
|||
|
|
function isMark(codePoint) {
|
|||
|
|
return binarySearch(markRanges, ([start, end]) => codePoint < start ? -1 : codePoint > end ? 1 : 0) > -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/glyph/StandardNames.ts
|
|||
|
|
var StandardNames_default = [
|
|||
|
|
".notdef",
|
|||
|
|
".null",
|
|||
|
|
"nonmarkingreturn",
|
|||
|
|
"space",
|
|||
|
|
"exclam",
|
|||
|
|
"quotedbl",
|
|||
|
|
"numbersign",
|
|||
|
|
"dollar",
|
|||
|
|
"percent",
|
|||
|
|
"ampersand",
|
|||
|
|
"quotesingle",
|
|||
|
|
"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",
|
|||
|
|
"grave",
|
|||
|
|
"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",
|
|||
|
|
"Adieresis",
|
|||
|
|
"Aring",
|
|||
|
|
"Ccedilla",
|
|||
|
|
"Eacute",
|
|||
|
|
"Ntilde",
|
|||
|
|
"Odieresis",
|
|||
|
|
"Udieresis",
|
|||
|
|
"aacute",
|
|||
|
|
"agrave",
|
|||
|
|
"acircumflex",
|
|||
|
|
"adieresis",
|
|||
|
|
"atilde",
|
|||
|
|
"aring",
|
|||
|
|
"ccedilla",
|
|||
|
|
"eacute",
|
|||
|
|
"egrave",
|
|||
|
|
"ecircumflex",
|
|||
|
|
"edieresis",
|
|||
|
|
"iacute",
|
|||
|
|
"igrave",
|
|||
|
|
"icircumflex",
|
|||
|
|
"idieresis",
|
|||
|
|
"ntilde",
|
|||
|
|
"oacute",
|
|||
|
|
"ograve",
|
|||
|
|
"ocircumflex",
|
|||
|
|
"odieresis",
|
|||
|
|
"otilde",
|
|||
|
|
"uacute",
|
|||
|
|
"ugrave",
|
|||
|
|
"ucircumflex",
|
|||
|
|
"udieresis",
|
|||
|
|
"dagger",
|
|||
|
|
"degree",
|
|||
|
|
"cent",
|
|||
|
|
"sterling",
|
|||
|
|
"section",
|
|||
|
|
"bullet",
|
|||
|
|
"paragraph",
|
|||
|
|
"germandbls",
|
|||
|
|
"registered",
|
|||
|
|
"copyright",
|
|||
|
|
"trademark",
|
|||
|
|
"acute",
|
|||
|
|
"dieresis",
|
|||
|
|
"notequal",
|
|||
|
|
"AE",
|
|||
|
|
"Oslash",
|
|||
|
|
"infinity",
|
|||
|
|
"plusminus",
|
|||
|
|
"lessequal",
|
|||
|
|
"greaterequal",
|
|||
|
|
"yen",
|
|||
|
|
"mu",
|
|||
|
|
"partialdiff",
|
|||
|
|
"summation",
|
|||
|
|
"product",
|
|||
|
|
"pi",
|
|||
|
|
"integral",
|
|||
|
|
"ordfeminine",
|
|||
|
|
"ordmasculine",
|
|||
|
|
"Omega",
|
|||
|
|
"ae",
|
|||
|
|
"oslash",
|
|||
|
|
"questiondown",
|
|||
|
|
"exclamdown",
|
|||
|
|
"logicalnot",
|
|||
|
|
"radical",
|
|||
|
|
"florin",
|
|||
|
|
"approxequal",
|
|||
|
|
"Delta",
|
|||
|
|
"guillemotleft",
|
|||
|
|
"guillemotright",
|
|||
|
|
"ellipsis",
|
|||
|
|
"nonbreakingspace",
|
|||
|
|
"Agrave",
|
|||
|
|
"Atilde",
|
|||
|
|
"Otilde",
|
|||
|
|
"OE",
|
|||
|
|
"oe",
|
|||
|
|
"endash",
|
|||
|
|
"emdash",
|
|||
|
|
"quotedblleft",
|
|||
|
|
"quotedblright",
|
|||
|
|
"quoteleft",
|
|||
|
|
"quoteright",
|
|||
|
|
"divide",
|
|||
|
|
"lozenge",
|
|||
|
|
"ydieresis",
|
|||
|
|
"Ydieresis",
|
|||
|
|
"fraction",
|
|||
|
|
"currency",
|
|||
|
|
"guilsinglleft",
|
|||
|
|
"guilsinglright",
|
|||
|
|
"fi",
|
|||
|
|
"fl",
|
|||
|
|
"daggerdbl",
|
|||
|
|
"periodcentered",
|
|||
|
|
"quotesinglbase",
|
|||
|
|
"quotedblbase",
|
|||
|
|
"perthousand",
|
|||
|
|
"Acircumflex",
|
|||
|
|
"Ecircumflex",
|
|||
|
|
"Aacute",
|
|||
|
|
"Edieresis",
|
|||
|
|
"Egrave",
|
|||
|
|
"Iacute",
|
|||
|
|
"Icircumflex",
|
|||
|
|
"Idieresis",
|
|||
|
|
"Igrave",
|
|||
|
|
"Oacute",
|
|||
|
|
"Ocircumflex",
|
|||
|
|
"apple",
|
|||
|
|
"Ograve",
|
|||
|
|
"Uacute",
|
|||
|
|
"Ucircumflex",
|
|||
|
|
"Ugrave",
|
|||
|
|
"dotlessi",
|
|||
|
|
"circumflex",
|
|||
|
|
"tilde",
|
|||
|
|
"macron",
|
|||
|
|
"breve",
|
|||
|
|
"dotaccent",
|
|||
|
|
"ring",
|
|||
|
|
"cedilla",
|
|||
|
|
"hungarumlaut",
|
|||
|
|
"ogonek",
|
|||
|
|
"caron",
|
|||
|
|
"Lslash",
|
|||
|
|
"lslash",
|
|||
|
|
"Scaron",
|
|||
|
|
"scaron",
|
|||
|
|
"Zcaron",
|
|||
|
|
"zcaron",
|
|||
|
|
"brokenbar",
|
|||
|
|
"Eth",
|
|||
|
|
"eth",
|
|||
|
|
"Yacute",
|
|||
|
|
"yacute",
|
|||
|
|
"Thorn",
|
|||
|
|
"thorn",
|
|||
|
|
"minus",
|
|||
|
|
"multiply",
|
|||
|
|
"onesuperior",
|
|||
|
|
"twosuperior",
|
|||
|
|
"threesuperior",
|
|||
|
|
"onehalf",
|
|||
|
|
"onequarter",
|
|||
|
|
"threequarters",
|
|||
|
|
"franc",
|
|||
|
|
"Gbreve",
|
|||
|
|
"gbreve",
|
|||
|
|
"Idotaccent",
|
|||
|
|
"Scedilla",
|
|||
|
|
"scedilla",
|
|||
|
|
"Cacute",
|
|||
|
|
"cacute",
|
|||
|
|
"Ccaron",
|
|||
|
|
"ccaron",
|
|||
|
|
"dcroat"
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/glyph/Glyph.ts
|
|||
|
|
/**
|
|||
|
|
* Glyph objects represent a glyph in the font. They have various properties for accessing metrics and
|
|||
|
|
* the actual vector path the glyph represents.
|
|||
|
|
*
|
|||
|
|
* You do not create glyph objects directly. They are created by various methods on the font object.
|
|||
|
|
* There are several subclasses of the base Glyph class internally that may be returned depending
|
|||
|
|
* on the font format, but they all inherit from this class.
|
|||
|
|
*/
|
|||
|
|
var Glyph = class {
|
|||
|
|
isMark;
|
|||
|
|
isLigature;
|
|||
|
|
_metrics;
|
|||
|
|
constructor(id, codePoints, _font) {
|
|||
|
|
this.id = id;
|
|||
|
|
this.codePoints = codePoints;
|
|||
|
|
this._font = _font;
|
|||
|
|
this.isMark = this.codePoints.length > 0 && this.codePoints.every(isMark);
|
|||
|
|
this.isLigature = this.codePoints.length > 1;
|
|||
|
|
}
|
|||
|
|
_getPath() {
|
|||
|
|
return new Path();
|
|||
|
|
}
|
|||
|
|
_getCBox() {
|
|||
|
|
return this.path.cbox;
|
|||
|
|
}
|
|||
|
|
_getBBox() {
|
|||
|
|
return this.path.bbox;
|
|||
|
|
}
|
|||
|
|
#getTableMetrics(table) {
|
|||
|
|
if (this.id < table.metrics.length) return table.metrics.get(this.id);
|
|||
|
|
const metric = table.metrics.get(table.metrics.length - 1);
|
|||
|
|
return {
|
|||
|
|
advance: metric ? metric.advance : 0,
|
|||
|
|
bearing: table.bearings.get(this.id - table.metrics.length) || 0
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
_getMetrics(cbox) {
|
|||
|
|
if (this._metrics) return this._metrics;
|
|||
|
|
let { advance: advanceWidth, bearing: leftBearing } = this.#getTableMetrics(this._font.hmtx);
|
|||
|
|
let advanceHeight, topBearing;
|
|||
|
|
if (this._font.vmtx) ({advance: advanceHeight, bearing: topBearing} = this.#getTableMetrics(this._font.vmtx));
|
|||
|
|
else {
|
|||
|
|
const os2 = this._font["OS/2"];
|
|||
|
|
if (!cbox) ({cbox} = this);
|
|||
|
|
if (os2.version) {
|
|||
|
|
advanceHeight = Math.abs(os2.typoAscender - os2.typoDescender);
|
|||
|
|
topBearing = os2.typoAscender - cbox.maxY;
|
|||
|
|
} else {
|
|||
|
|
const { hhea } = this._font;
|
|||
|
|
advanceHeight = Math.abs(hhea.ascent - hhea.descent);
|
|||
|
|
topBearing = hhea.ascent - cbox.maxY;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (this._font._variationProcessor && this._font.HVAR) advanceWidth += this._font._variationProcessor.getAdvanceAdjustment(this.id, this._font.HVAR);
|
|||
|
|
return this._metrics = {
|
|||
|
|
advanceWidth,
|
|||
|
|
advanceHeight,
|
|||
|
|
leftBearing,
|
|||
|
|
topBearing
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The glyph’s control box.
|
|||
|
|
* This is often the same as the bounding box, but is faster to compute.
|
|||
|
|
* Because of the way bezier curves are defined, some of the control points
|
|||
|
|
* can be outside of the bounding box. Where `bbox` takes this into account,
|
|||
|
|
* `cbox` does not. Thus, cbox is less accurate, but faster to compute.
|
|||
|
|
* See [here](http://www.freetype.org/freetype2/docs/glyphs/glyphs-6.html#section-2)
|
|||
|
|
* for a more detailed description.
|
|||
|
|
*/
|
|||
|
|
get cbox() {
|
|||
|
|
return this._getCBox();
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The glyph’s bounding box, i.e. the rectangle that encloses the
|
|||
|
|
* glyph outline as tightly as possible.
|
|||
|
|
*/
|
|||
|
|
get bbox() {
|
|||
|
|
return this._getBBox();
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* A vector Path object representing the glyph outline.
|
|||
|
|
*/
|
|||
|
|
get path() {
|
|||
|
|
return this._getPath();
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Returns a path scaled to the given font size.
|
|||
|
|
*/
|
|||
|
|
getScaledPath(size) {
|
|||
|
|
let scale = 1 / this._font.unitsPerEm * size;
|
|||
|
|
return this.path.scale(scale);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The glyph's advance width.
|
|||
|
|
*/
|
|||
|
|
get advanceWidth() {
|
|||
|
|
return this._getMetrics().advanceWidth;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The glyph's advance height.
|
|||
|
|
*/
|
|||
|
|
get advanceHeight() {
|
|||
|
|
return this._getMetrics().advanceHeight;
|
|||
|
|
}
|
|||
|
|
_getName() {
|
|||
|
|
const { post } = this._font;
|
|||
|
|
if (!post) return null;
|
|||
|
|
switch (post.version) {
|
|||
|
|
case 1: return StandardNames_default[this.id];
|
|||
|
|
case 2:
|
|||
|
|
const id = post.glyphNameIndex[this.id];
|
|||
|
|
return id < StandardNames_default.length ? StandardNames_default[id] : post.names[id - StandardNames_default.length];
|
|||
|
|
case 2.5: return StandardNames_default[this.id + post.offsets[this.id]];
|
|||
|
|
case 4: return String.fromCharCode(post.map[this.id]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The glyph's name
|
|||
|
|
*/
|
|||
|
|
get name() {
|
|||
|
|
return this._getName();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
__decorate([cache], Glyph.prototype, "cbox", null);
|
|||
|
|
__decorate([cache], Glyph.prototype, "bbox", null);
|
|||
|
|
__decorate([cache], Glyph.prototype, "path", null);
|
|||
|
|
__decorate([cache], Glyph.prototype, "advanceWidth", null);
|
|||
|
|
__decorate([cache], Glyph.prototype, "advanceHeight", null);
|
|||
|
|
__decorate([cache], Glyph.prototype, "name", null);
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/glyph/TTFGlyph.ts
|
|||
|
|
const GlyfHeader = new Struct({
|
|||
|
|
numberOfContours: int16,
|
|||
|
|
xMin: int16,
|
|||
|
|
yMin: int16,
|
|||
|
|
xMax: int16,
|
|||
|
|
yMax: int16
|
|||
|
|
});
|
|||
|
|
const ON_CURVE = 1;
|
|||
|
|
const X_SHORT_VECTOR = 2;
|
|||
|
|
const Y_SHORT_VECTOR = 4;
|
|||
|
|
const REPEAT = 8;
|
|||
|
|
const SAME_X = 16;
|
|||
|
|
const SAME_Y = 32;
|
|||
|
|
const ARG_1_AND_2_ARE_WORDS = 1;
|
|||
|
|
const WE_HAVE_A_SCALE = 8;
|
|||
|
|
const MORE_COMPONENTS = 32;
|
|||
|
|
const WE_HAVE_AN_X_AND_Y_SCALE = 64;
|
|||
|
|
const WE_HAVE_A_TWO_BY_TWO = 128;
|
|||
|
|
const WE_HAVE_INSTRUCTIONS = 256;
|
|||
|
|
var Point = class Point {
|
|||
|
|
constructor(onCurve, endContour, x = 0, y = 0) {
|
|||
|
|
this.onCurve = onCurve;
|
|||
|
|
this.endContour = endContour;
|
|||
|
|
this.x = x;
|
|||
|
|
this.y = y;
|
|||
|
|
}
|
|||
|
|
copy() {
|
|||
|
|
return new Point(this.onCurve, this.endContour, this.x, this.y);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var Component = class {
|
|||
|
|
pos = 0;
|
|||
|
|
scaleX = 1;
|
|||
|
|
scaleY = 1;
|
|||
|
|
scale01 = 0;
|
|||
|
|
scale10 = 0;
|
|||
|
|
constructor(glyphID, dx, dy) {
|
|||
|
|
this.glyphID = glyphID;
|
|||
|
|
this.dx = dx;
|
|||
|
|
this.dy = dy;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
/**
|
|||
|
|
* Represents a TrueType glyph.
|
|||
|
|
*/
|
|||
|
|
var TTFGlyph = class extends Glyph {
|
|||
|
|
type = "TTF";
|
|||
|
|
_getCBox(internal) {
|
|||
|
|
if (this._font._variationProcessor && !internal) return this.path.cbox;
|
|||
|
|
const stream = this._font._getTableStream("glyf");
|
|||
|
|
stream.pos += this._font.loca.offsets[this.id];
|
|||
|
|
const glyph = GlyfHeader.decode(stream);
|
|||
|
|
const cbox = new BBox(glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax);
|
|||
|
|
return Object.freeze(cbox);
|
|||
|
|
}
|
|||
|
|
_parseGlyphCoord(stream, prev, short, same) {
|
|||
|
|
let val;
|
|||
|
|
if (short) {
|
|||
|
|
val = stream.readUInt8();
|
|||
|
|
if (!same) val = -val;
|
|||
|
|
val += prev;
|
|||
|
|
} else val = same ? prev : prev + stream.readInt16BE();
|
|||
|
|
return val;
|
|||
|
|
}
|
|||
|
|
_decode() {
|
|||
|
|
let glyfPos = this._font.loca.offsets[this.id];
|
|||
|
|
if (glyfPos === this._font.loca.offsets[this.id + 1]) return null;
|
|||
|
|
const stream = this._font._getTableStream("glyf");
|
|||
|
|
stream.pos += glyfPos;
|
|||
|
|
const startPos = stream.pos;
|
|||
|
|
const glyph = GlyfHeader.decode(stream);
|
|||
|
|
if (glyph.numberOfContours > 0) this._decodeSimple(glyph, stream);
|
|||
|
|
else if (glyph.numberOfContours < 0) this._decodeComposite(glyph, stream, startPos);
|
|||
|
|
return glyph;
|
|||
|
|
}
|
|||
|
|
_decodeSimple(glyph, stream) {
|
|||
|
|
glyph.points = [];
|
|||
|
|
const endPtsOfContours = new ArrayT(uint16, glyph.numberOfContours).decode(stream);
|
|||
|
|
glyph.instructions = new ArrayT(uint8, uint16).decode(stream);
|
|||
|
|
const flags = [];
|
|||
|
|
const numCoords = endPtsOfContours[endPtsOfContours.length - 1] + 1;
|
|||
|
|
while (flags.length < numCoords) {
|
|||
|
|
const flag = stream.readUInt8();
|
|||
|
|
flags.push(flag);
|
|||
|
|
if (flag & REPEAT) {
|
|||
|
|
const count = stream.readUInt8();
|
|||
|
|
for (let j = 0; j < count; j++) flags.push(flag);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
for (let i = 0; i < flags.length; i++) {
|
|||
|
|
const flag = flags[i];
|
|||
|
|
const point = new Point(!!(flag & ON_CURVE), endPtsOfContours.indexOf(i) >= 0, 0, 0);
|
|||
|
|
glyph.points.push(point);
|
|||
|
|
}
|
|||
|
|
let px = 0;
|
|||
|
|
for (let i = 0; i < flags.length; i++) {
|
|||
|
|
const flag = flags[i];
|
|||
|
|
glyph.points[i].x = px = this._parseGlyphCoord(stream, px, flag & X_SHORT_VECTOR, flag & SAME_X);
|
|||
|
|
}
|
|||
|
|
let py = 0;
|
|||
|
|
for (let i = 0; i < flags.length; i++) {
|
|||
|
|
const flag = flags[i];
|
|||
|
|
glyph.points[i].y = py = this._parseGlyphCoord(stream, py, flag & Y_SHORT_VECTOR, flag & SAME_Y);
|
|||
|
|
}
|
|||
|
|
if (this._font._variationProcessor) {
|
|||
|
|
let points = glyph.points.slice();
|
|||
|
|
points.push(...this.#getPhantomPoints(glyph));
|
|||
|
|
this._font._variationProcessor.transformPoints(this.id, points);
|
|||
|
|
glyph.phantomPoints = points.slice(-4);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_decodeComposite(glyph, stream, offset = 0) {
|
|||
|
|
glyph.components = [];
|
|||
|
|
let haveInstructions = false;
|
|||
|
|
let flags = MORE_COMPONENTS;
|
|||
|
|
while (flags & MORE_COMPONENTS) {
|
|||
|
|
flags = stream.readUInt16BE();
|
|||
|
|
const gPos = stream.pos - offset;
|
|||
|
|
const glyphID = stream.readUInt16BE();
|
|||
|
|
if (!haveInstructions) haveInstructions = (flags & WE_HAVE_INSTRUCTIONS) !== 0;
|
|||
|
|
let dx, dy;
|
|||
|
|
if (flags & ARG_1_AND_2_ARE_WORDS) {
|
|||
|
|
dx = stream.readInt16BE();
|
|||
|
|
dy = stream.readInt16BE();
|
|||
|
|
} else {
|
|||
|
|
dx = stream.readInt8();
|
|||
|
|
dy = stream.readInt8();
|
|||
|
|
}
|
|||
|
|
const component = new Component(glyphID, dx, dy);
|
|||
|
|
component.pos = gPos;
|
|||
|
|
const two_30 = 1 << 30;
|
|||
|
|
if (flags & WE_HAVE_A_SCALE) component.scaleX = component.scaleY = (stream.readUInt8() << 24 | stream.readUInt8() << 16) / two_30;
|
|||
|
|
else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
|
|||
|
|
component.scaleX = (stream.readUInt8() << 24 | stream.readUInt8() << 16) / two_30;
|
|||
|
|
component.scaleY = (stream.readUInt8() << 24 | stream.readUInt8() << 16) / two_30;
|
|||
|
|
} else if (flags & WE_HAVE_A_TWO_BY_TWO) {
|
|||
|
|
component.scaleX = (stream.readUInt8() << 24 | stream.readUInt8() << 16) / two_30;
|
|||
|
|
component.scale01 = (stream.readUInt8() << 24 | stream.readUInt8() << 16) / two_30;
|
|||
|
|
component.scale10 = (stream.readUInt8() << 24 | stream.readUInt8() << 16) / two_30;
|
|||
|
|
component.scaleY = (stream.readUInt8() << 24 | stream.readUInt8() << 16) / two_30;
|
|||
|
|
}
|
|||
|
|
glyph.components.push(component);
|
|||
|
|
}
|
|||
|
|
if (this._font._variationProcessor) {
|
|||
|
|
const points = glyph.components.map((c) => new Point(true, true, c.dx, c.dy));
|
|||
|
|
points.push(...this.#getPhantomPoints(glyph));
|
|||
|
|
this._font._variationProcessor.transformPoints(this.id, points);
|
|||
|
|
glyph.phantomPoints = points.splice(-4, 4);
|
|||
|
|
for (let i = 0; i < points.length; i++) {
|
|||
|
|
const point = points[i];
|
|||
|
|
glyph.components[i].dx = point.x;
|
|||
|
|
glyph.components[i].dy = point.y;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return haveInstructions;
|
|||
|
|
}
|
|||
|
|
#getPhantomPoints(glyph) {
|
|||
|
|
const cbox = this._getCBox(true);
|
|||
|
|
this._metrics ??= Glyph.prototype._getMetrics.call(this, cbox);
|
|||
|
|
const { advanceWidth, advanceHeight, leftBearing, topBearing } = this._metrics;
|
|||
|
|
return [
|
|||
|
|
new Point(false, true, glyph.xMin - leftBearing, 0),
|
|||
|
|
new Point(false, true, glyph.xMin - leftBearing + advanceWidth, 0),
|
|||
|
|
new Point(false, true, 0, glyph.yMax + topBearing),
|
|||
|
|
new Point(false, true, 0, glyph.yMax + topBearing + advanceHeight)
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
_getContours() {
|
|||
|
|
const glyph = this._decode();
|
|||
|
|
if (!glyph) return [];
|
|||
|
|
let points = [];
|
|||
|
|
if (glyph.numberOfContours < 0) for (let component of glyph.components) {
|
|||
|
|
const contours = this._font.getGlyph(component.glyphID)._getContours();
|
|||
|
|
for (let i = 0; i < contours.length; i++) {
|
|||
|
|
const contour = contours[i];
|
|||
|
|
for (let j = 0; j < contour.length; j++) {
|
|||
|
|
const point = contour[j];
|
|||
|
|
const x = point.x * component.scaleX + point.y * component.scale01 + component.dx;
|
|||
|
|
const y = point.y * component.scaleY + point.x * component.scale10 + component.dy;
|
|||
|
|
points.push(new Point(point.onCurve, point.endContour, x, y));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else points = glyph.points || [];
|
|||
|
|
if (glyph.phantomPoints && !this._font.directory.tables.HVAR) {
|
|||
|
|
this._metrics.advanceWidth = glyph.phantomPoints[1].x - glyph.phantomPoints[0].x;
|
|||
|
|
this._metrics.advanceHeight = glyph.phantomPoints[3].y - glyph.phantomPoints[2].y;
|
|||
|
|
this._metrics.leftBearing = glyph.xMin - glyph.phantomPoints[0].x;
|
|||
|
|
this._metrics.topBearing = glyph.phantomPoints[2].y - glyph.yMax;
|
|||
|
|
}
|
|||
|
|
const contours = [];
|
|||
|
|
let cur = [];
|
|||
|
|
for (let k = 0; k < points.length; k++) {
|
|||
|
|
const point = points[k];
|
|||
|
|
cur.push(point);
|
|||
|
|
if (point.endContour) {
|
|||
|
|
contours.push(cur);
|
|||
|
|
cur = [];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return contours;
|
|||
|
|
}
|
|||
|
|
_getMetrics() {
|
|||
|
|
if (this._metrics) return this._metrics;
|
|||
|
|
const cbox = this._getCBox(true);
|
|||
|
|
super._getMetrics(cbox);
|
|||
|
|
if (this._font._variationProcessor && !this._font.HVAR) this.path;
|
|||
|
|
return this._metrics;
|
|||
|
|
}
|
|||
|
|
_getPath() {
|
|||
|
|
const contours = this._getContours();
|
|||
|
|
const path = new Path();
|
|||
|
|
let curvePt;
|
|||
|
|
for (const contour of contours) {
|
|||
|
|
let firstPt = contour[0];
|
|||
|
|
const lastPt = contour[contour.length - 1];
|
|||
|
|
let start = 0;
|
|||
|
|
if (firstPt.onCurve) {
|
|||
|
|
curvePt = null;
|
|||
|
|
start = 1;
|
|||
|
|
} else {
|
|||
|
|
if (lastPt.onCurve) firstPt = lastPt;
|
|||
|
|
else firstPt = new Point(false, false, (firstPt.x + lastPt.x) / 2, (firstPt.y + lastPt.y) / 2);
|
|||
|
|
curvePt = firstPt;
|
|||
|
|
}
|
|||
|
|
path.moveTo(firstPt.x, firstPt.y);
|
|||
|
|
for (let j = start; j < contour.length; j++) {
|
|||
|
|
const pt = contour[j];
|
|||
|
|
const prevPt = j === 0 ? firstPt : contour[j - 1];
|
|||
|
|
if (prevPt.onCurve && pt.onCurve) path.lineTo(pt.x, pt.y);
|
|||
|
|
else if (prevPt.onCurve && !pt.onCurve) curvePt = pt;
|
|||
|
|
else if (!prevPt.onCurve && !pt.onCurve) {
|
|||
|
|
const midX = (prevPt.x + pt.x) / 2;
|
|||
|
|
const midY = (prevPt.y + pt.y) / 2;
|
|||
|
|
path.quadraticCurveTo(prevPt.x, prevPt.y, midX, midY);
|
|||
|
|
curvePt = pt;
|
|||
|
|
} else if (!prevPt.onCurve && pt.onCurve) {
|
|||
|
|
path.quadraticCurveTo(curvePt.x, curvePt.y, pt.x, pt.y);
|
|||
|
|
curvePt = null;
|
|||
|
|
} else throw new Error("Unknown TTF path state");
|
|||
|
|
}
|
|||
|
|
if (curvePt) path.quadraticCurveTo(curvePt.x, curvePt.y, firstPt.x, firstPt.y);
|
|||
|
|
path.closePath();
|
|||
|
|
}
|
|||
|
|
return path;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/glyph/CFFGlyph.ts
|
|||
|
|
/**
|
|||
|
|
* Represents an OpenType PostScript glyph, in the Compact Font Format.
|
|||
|
|
*/
|
|||
|
|
var CFFGlyph = class extends Glyph {
|
|||
|
|
type = "CFF";
|
|||
|
|
_getName() {
|
|||
|
|
if (this._font.CFF2) return super._getName();
|
|||
|
|
return this._font["CFF "].getGlyphName(this.id);
|
|||
|
|
}
|
|||
|
|
bias(s) {
|
|||
|
|
return s.length < 1240 ? 107 : s.length < 33900 ? 1131 : 32768;
|
|||
|
|
}
|
|||
|
|
_getPath() {
|
|||
|
|
const cff = this._font.CFF2 || this._font["CFF "];
|
|||
|
|
const { stream } = cff;
|
|||
|
|
const str = cff.topDict.CharStrings[this.id];
|
|||
|
|
let end = str.offset + str.length;
|
|||
|
|
stream.pos = str.offset;
|
|||
|
|
const path = new Path();
|
|||
|
|
const stack = [];
|
|||
|
|
const trans = [];
|
|||
|
|
let width = null;
|
|||
|
|
let nStems = 0;
|
|||
|
|
let x = 0, y = 0;
|
|||
|
|
let usedGsubrs;
|
|||
|
|
let usedSubrs;
|
|||
|
|
let open = false;
|
|||
|
|
this._usedGsubrs = usedGsubrs = {};
|
|||
|
|
this._usedSubrs = usedSubrs = {};
|
|||
|
|
const gsubrs = cff.globalSubrIndex || [];
|
|||
|
|
const gsubrsBias = this.bias(gsubrs);
|
|||
|
|
const privateDict = cff.privateDictForGlyph(this.id) || {};
|
|||
|
|
const subrs = privateDict.Subrs || [];
|
|||
|
|
const subrsBias = this.bias(subrs);
|
|||
|
|
const vstore = cff.topDict.vstore?.itemVariationStore;
|
|||
|
|
let { vsindex } = privateDict;
|
|||
|
|
const variationProcessor = this._font._variationProcessor;
|
|||
|
|
const checkWidth = () => {
|
|||
|
|
width ??= stack.shift() + privateDict.nominalWidthX;
|
|||
|
|
};
|
|||
|
|
const parseStems = () => {
|
|||
|
|
if (stack.length % 2 !== 0) checkWidth();
|
|||
|
|
nStems += stack.length >> 1;
|
|||
|
|
return stack.length = 0;
|
|||
|
|
};
|
|||
|
|
const moveTo = (x, y) => {
|
|||
|
|
if (open) path.closePath();
|
|||
|
|
path.moveTo(x, y);
|
|||
|
|
open = true;
|
|||
|
|
};
|
|||
|
|
const parse = () => {
|
|||
|
|
while (stream.pos < end) {
|
|||
|
|
let op = stream.readUInt8();
|
|||
|
|
if (op < 32) {
|
|||
|
|
let index, subr, phase;
|
|||
|
|
let c1x, c1y, c2x, c2y, c3x, c3y;
|
|||
|
|
let c4x, c4y, c5x, c5y, c6x, c6y;
|
|||
|
|
let pts;
|
|||
|
|
switch (op) {
|
|||
|
|
case 1:
|
|||
|
|
case 3:
|
|||
|
|
case 18:
|
|||
|
|
case 23:
|
|||
|
|
parseStems();
|
|||
|
|
break;
|
|||
|
|
case 4:
|
|||
|
|
if (stack.length > 1) checkWidth();
|
|||
|
|
y += stack.shift();
|
|||
|
|
moveTo(x, y);
|
|||
|
|
break;
|
|||
|
|
case 5:
|
|||
|
|
while (stack.length >= 2) {
|
|||
|
|
x += stack.shift();
|
|||
|
|
y += stack.shift();
|
|||
|
|
path.lineTo(x, y);
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case 6:
|
|||
|
|
case 7:
|
|||
|
|
phase = op === 6;
|
|||
|
|
while (stack.length >= 1) {
|
|||
|
|
if (phase) x += stack.shift();
|
|||
|
|
else y += stack.shift();
|
|||
|
|
path.lineTo(x, y);
|
|||
|
|
phase = !phase;
|
|||
|
|
}
|
|||
|
|
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();
|
|||
|
|
path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case 10:
|
|||
|
|
index = stack.pop() + subrsBias;
|
|||
|
|
subr = subrs[index];
|
|||
|
|
if (subr) {
|
|||
|
|
usedSubrs[index] = true;
|
|||
|
|
const p = stream.pos;
|
|||
|
|
const e = end;
|
|||
|
|
stream.pos = subr.offset;
|
|||
|
|
end = subr.offset + subr.length;
|
|||
|
|
parse();
|
|||
|
|
stream.pos = p;
|
|||
|
|
end = e;
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case 11:
|
|||
|
|
if (cff.version >= 2) break;
|
|||
|
|
return;
|
|||
|
|
case 14:
|
|||
|
|
if (cff.version >= 2) break;
|
|||
|
|
if (stack.length > 0) checkWidth();
|
|||
|
|
if (open) {
|
|||
|
|
path.closePath();
|
|||
|
|
open = false;
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case 15:
|
|||
|
|
if (cff.version < 2) throw new Error("vsindex operator not supported in CFF v1");
|
|||
|
|
vsindex = stack.pop();
|
|||
|
|
break;
|
|||
|
|
case 16: {
|
|||
|
|
if (cff.version < 2) throw new Error("blend operator not supported in CFF v1");
|
|||
|
|
if (!variationProcessor) throw new Error("blend operator in non-variation font");
|
|||
|
|
const blendVector = variationProcessor.getBlendVector(vstore, vsindex);
|
|||
|
|
const numBlends = stack.pop();
|
|||
|
|
let numOperands = numBlends * blendVector.length;
|
|||
|
|
let delta = stack.length - numOperands;
|
|||
|
|
const base = delta - numBlends;
|
|||
|
|
for (let i = 0; i < numBlends; i++) {
|
|||
|
|
let sum = stack[base + i];
|
|||
|
|
for (let j = 0; j < blendVector.length; j++) sum += blendVector[j] * stack[delta++];
|
|||
|
|
stack[base + i] = sum;
|
|||
|
|
}
|
|||
|
|
while (numOperands--) stack.pop();
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case 19:
|
|||
|
|
case 20:
|
|||
|
|
parseStems();
|
|||
|
|
stream.pos += nStems + 7 >> 3;
|
|||
|
|
break;
|
|||
|
|
case 21:
|
|||
|
|
if (stack.length > 2) checkWidth();
|
|||
|
|
x += stack.shift();
|
|||
|
|
y += stack.shift();
|
|||
|
|
moveTo(x, y);
|
|||
|
|
break;
|
|||
|
|
case 22:
|
|||
|
|
if (stack.length > 1) checkWidth();
|
|||
|
|
x += stack.shift();
|
|||
|
|
moveTo(x, y);
|
|||
|
|
break;
|
|||
|
|
case 24:
|
|||
|
|
while (stack.length >= 8) {
|
|||
|
|
c1x = x + stack.shift();
|
|||
|
|
c1y = y + stack.shift();
|
|||
|
|
c2x = c1x + stack.shift();
|
|||
|
|
c2y = c1y + stack.shift();
|
|||
|
|
x = c2x + stack.shift();
|
|||
|
|
y = c2y + stack.shift();
|
|||
|
|
path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
|
|||
|
|
}
|
|||
|
|
x += stack.shift();
|
|||
|
|
y += stack.shift();
|
|||
|
|
path.lineTo(x, y);
|
|||
|
|
break;
|
|||
|
|
case 25:
|
|||
|
|
while (stack.length >= 8) {
|
|||
|
|
x += stack.shift();
|
|||
|
|
y += stack.shift();
|
|||
|
|
path.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();
|
|||
|
|
path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
|
|||
|
|
break;
|
|||
|
|
case 26:
|
|||
|
|
if (stack.length % 2) x += stack.shift();
|
|||
|
|
while (stack.length >= 4) {
|
|||
|
|
c1x = x;
|
|||
|
|
c1y = y + stack.shift();
|
|||
|
|
c2x = c1x + stack.shift();
|
|||
|
|
c2y = c1y + stack.shift();
|
|||
|
|
x = c2x;
|
|||
|
|
y = c2y + stack.shift();
|
|||
|
|
path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case 27:
|
|||
|
|
if (stack.length % 2) y += stack.shift();
|
|||
|
|
while (stack.length >= 4) {
|
|||
|
|
c1x = x + stack.shift();
|
|||
|
|
c1y = y;
|
|||
|
|
c2x = c1x + stack.shift();
|
|||
|
|
c2y = c1y + stack.shift();
|
|||
|
|
x = c2x + stack.shift();
|
|||
|
|
y = c2y;
|
|||
|
|
path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case 28:
|
|||
|
|
stack.push(stream.readInt16BE());
|
|||
|
|
break;
|
|||
|
|
case 29:
|
|||
|
|
index = stack.pop() + gsubrsBias;
|
|||
|
|
subr = gsubrs[index];
|
|||
|
|
if (subr) {
|
|||
|
|
usedGsubrs[index] = true;
|
|||
|
|
const p = stream.pos;
|
|||
|
|
const e = end;
|
|||
|
|
stream.pos = subr.offset;
|
|||
|
|
end = subr.offset + subr.length;
|
|||
|
|
parse();
|
|||
|
|
stream.pos = p;
|
|||
|
|
end = e;
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case 30:
|
|||
|
|
case 31:
|
|||
|
|
phase = op === 31;
|
|||
|
|
while (stack.length >= 4) {
|
|||
|
|
if (phase) {
|
|||
|
|
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);
|
|||
|
|
} else {
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
|
|||
|
|
phase = !phase;
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case 12:
|
|||
|
|
op = stream.readUInt8();
|
|||
|
|
switch (op) {
|
|||
|
|
case 3:
|
|||
|
|
let a = stack.pop();
|
|||
|
|
let b = stack.pop();
|
|||
|
|
stack.push(a && b ? 1 : 0);
|
|||
|
|
break;
|
|||
|
|
case 4:
|
|||
|
|
a = stack.pop();
|
|||
|
|
b = stack.pop();
|
|||
|
|
stack.push(a || b ? 1 : 0);
|
|||
|
|
break;
|
|||
|
|
case 5:
|
|||
|
|
a = stack.pop();
|
|||
|
|
stack.push(a ? 0 : 1);
|
|||
|
|
break;
|
|||
|
|
case 9:
|
|||
|
|
a = stack.pop();
|
|||
|
|
stack.push(Math.abs(a));
|
|||
|
|
break;
|
|||
|
|
case 10:
|
|||
|
|
a = stack.pop();
|
|||
|
|
b = stack.pop();
|
|||
|
|
stack.push(a + b);
|
|||
|
|
break;
|
|||
|
|
case 11:
|
|||
|
|
a = stack.pop();
|
|||
|
|
b = stack.pop();
|
|||
|
|
stack.push(a - b);
|
|||
|
|
break;
|
|||
|
|
case 12:
|
|||
|
|
a = stack.pop();
|
|||
|
|
b = stack.pop();
|
|||
|
|
stack.push(a / b);
|
|||
|
|
break;
|
|||
|
|
case 14:
|
|||
|
|
a = stack.pop();
|
|||
|
|
stack.push(-a);
|
|||
|
|
break;
|
|||
|
|
case 15:
|
|||
|
|
a = stack.pop();
|
|||
|
|
b = stack.pop();
|
|||
|
|
stack.push(a === b ? 1 : 0);
|
|||
|
|
break;
|
|||
|
|
case 18:
|
|||
|
|
stack.pop();
|
|||
|
|
break;
|
|||
|
|
case 20:
|
|||
|
|
let val = stack.pop();
|
|||
|
|
let idx = stack.pop();
|
|||
|
|
trans[idx] = val;
|
|||
|
|
break;
|
|||
|
|
case 21:
|
|||
|
|
idx = stack.pop();
|
|||
|
|
stack.push(trans[idx] || 0);
|
|||
|
|
break;
|
|||
|
|
case 22:
|
|||
|
|
const s1 = stack.pop();
|
|||
|
|
const s2 = stack.pop();
|
|||
|
|
const v1 = stack.pop();
|
|||
|
|
const v2 = stack.pop();
|
|||
|
|
stack.push(v1 <= v2 ? s1 : s2);
|
|||
|
|
break;
|
|||
|
|
case 23:
|
|||
|
|
stack.push(Math.random());
|
|||
|
|
break;
|
|||
|
|
case 24:
|
|||
|
|
a = stack.pop();
|
|||
|
|
b = stack.pop();
|
|||
|
|
stack.push(a * b);
|
|||
|
|
break;
|
|||
|
|
case 26:
|
|||
|
|
a = stack.pop();
|
|||
|
|
stack.push(Math.sqrt(a));
|
|||
|
|
break;
|
|||
|
|
case 27:
|
|||
|
|
a = stack.pop();
|
|||
|
|
stack.push(a, a);
|
|||
|
|
break;
|
|||
|
|
case 28:
|
|||
|
|
a = stack.pop();
|
|||
|
|
b = stack.pop();
|
|||
|
|
stack.push(b, a);
|
|||
|
|
break;
|
|||
|
|
case 29:
|
|||
|
|
idx = stack.pop();
|
|||
|
|
if (idx < 0) idx = 0;
|
|||
|
|
else if (idx > stack.length - 1) idx = stack.length - 1;
|
|||
|
|
stack.push(stack[idx]);
|
|||
|
|
break;
|
|||
|
|
case 30:
|
|||
|
|
const n = stack.pop();
|
|||
|
|
let j = stack.pop();
|
|||
|
|
if (j >= 0) while (j > 0) {
|
|||
|
|
var t = stack[n - 1];
|
|||
|
|
for (let i = n - 2; i >= 0; i--) stack[i + 1] = stack[i];
|
|||
|
|
stack[0] = t;
|
|||
|
|
j--;
|
|||
|
|
}
|
|||
|
|
else while (j < 0) {
|
|||
|
|
var t = stack[0];
|
|||
|
|
for (let i = 0; i <= n; i++) stack[i] = stack[i + 1];
|
|||
|
|
stack[n - 1] = t;
|
|||
|
|
j++;
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case 34:
|
|||
|
|
c1x = x + stack.shift();
|
|||
|
|
c1y = y;
|
|||
|
|
c2x = c1x + stack.shift();
|
|||
|
|
c2y = c1y + stack.shift();
|
|||
|
|
c3x = c2x + stack.shift();
|
|||
|
|
c3y = c2y;
|
|||
|
|
c4x = c3x + stack.shift();
|
|||
|
|
c4y = c3y;
|
|||
|
|
c5x = c4x + stack.shift();
|
|||
|
|
c5y = c4y;
|
|||
|
|
c6x = c5x + stack.shift();
|
|||
|
|
c6y = c5y;
|
|||
|
|
x = c6x;
|
|||
|
|
y = c6y;
|
|||
|
|
path.bezierCurveTo(c1x, c1y, c2x, c2y, c3x, c3y);
|
|||
|
|
path.bezierCurveTo(c4x, c4y, c5x, c5y, c6x, c6y);
|
|||
|
|
break;
|
|||
|
|
case 35:
|
|||
|
|
pts = [];
|
|||
|
|
for (let i = 0; i <= 5; i++) {
|
|||
|
|
x += stack.shift();
|
|||
|
|
y += stack.shift();
|
|||
|
|
pts.push(x, y);
|
|||
|
|
}
|
|||
|
|
path.bezierCurveTo(...pts.slice(0, 6));
|
|||
|
|
path.bezierCurveTo(...pts.slice(6));
|
|||
|
|
stack.shift();
|
|||
|
|
break;
|
|||
|
|
case 36:
|
|||
|
|
c1x = x + stack.shift();
|
|||
|
|
c1y = y + stack.shift();
|
|||
|
|
c2x = c1x + stack.shift();
|
|||
|
|
c2y = c1y + stack.shift();
|
|||
|
|
c3x = c2x + stack.shift();
|
|||
|
|
c3y = c2y;
|
|||
|
|
c4x = c3x + stack.shift();
|
|||
|
|
c4y = c3y;
|
|||
|
|
c5x = c4x + stack.shift();
|
|||
|
|
c5y = c4y + stack.shift();
|
|||
|
|
c6x = c5x + stack.shift();
|
|||
|
|
c6y = c5y;
|
|||
|
|
x = c6x;
|
|||
|
|
y = c6y;
|
|||
|
|
path.bezierCurveTo(c1x, c1y, c2x, c2y, c3x, c3y);
|
|||
|
|
path.bezierCurveTo(c4x, c4y, c5x, c5y, c6x, c6y);
|
|||
|
|
break;
|
|||
|
|
case 37:
|
|||
|
|
const startx = x;
|
|||
|
|
const starty = y;
|
|||
|
|
pts = [];
|
|||
|
|
for (let i = 0; i <= 4; i++) {
|
|||
|
|
x += stack.shift();
|
|||
|
|
y += stack.shift();
|
|||
|
|
pts.push(x, y);
|
|||
|
|
}
|
|||
|
|
if (Math.abs(x - startx) > Math.abs(y - starty)) {
|
|||
|
|
x += stack.shift();
|
|||
|
|
y = starty;
|
|||
|
|
} else {
|
|||
|
|
x = startx;
|
|||
|
|
y += stack.shift();
|
|||
|
|
}
|
|||
|
|
pts.push(x, y);
|
|||
|
|
path.bezierCurveTo(...pts.slice(0, 6));
|
|||
|
|
path.bezierCurveTo(...pts.slice(6));
|
|||
|
|
break;
|
|||
|
|
default: throw new Error(`Unknown op: 12 ${op}`);
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
default: throw new Error(`Unknown op: ${op}`);
|
|||
|
|
}
|
|||
|
|
} else if (op < 247) stack.push(op - 139);
|
|||
|
|
else if (op < 251) {
|
|||
|
|
var b1 = stream.readUInt8();
|
|||
|
|
stack.push((op - 247) * 256 + b1 + 108);
|
|||
|
|
} else if (op < 255) {
|
|||
|
|
var b1 = stream.readUInt8();
|
|||
|
|
stack.push(-(op - 251) * 256 - b1 - 108);
|
|||
|
|
} else stack.push(stream.readInt32BE() / 65536);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
parse();
|
|||
|
|
if (open) path.closePath();
|
|||
|
|
return path;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/glyph/SBIXGlyph.ts
|
|||
|
|
const SBIXImage = new Struct({
|
|||
|
|
originX: uint16,
|
|||
|
|
originY: uint16,
|
|||
|
|
type: new StringT(4),
|
|||
|
|
data: new BufferT((t) => t.parent.buflen - t._currentOffset)
|
|||
|
|
});
|
|||
|
|
/**
|
|||
|
|
* Represents a color (e.g. emoji) glyph in Apple's SBIX format.
|
|||
|
|
*/
|
|||
|
|
var SBIXGlyph = class extends TTFGlyph {
|
|||
|
|
type = "SBIX";
|
|||
|
|
/**
|
|||
|
|
* Returns an object representing a glyph image at the given point size.
|
|||
|
|
* The object has a data property with a Buffer containing the actual image data,
|
|||
|
|
* along with the image type, and origin.
|
|||
|
|
*/
|
|||
|
|
getImageForSize(size) {
|
|||
|
|
for (let i = 0; i < this._font.sbix.imageTables.length; i++) {
|
|||
|
|
var table = this._font.sbix.imageTables[i];
|
|||
|
|
if (table.ppem >= size) break;
|
|||
|
|
}
|
|||
|
|
const offsets = table.imageOffsets;
|
|||
|
|
const start = offsets[this.id];
|
|||
|
|
const end = offsets[this.id + 1];
|
|||
|
|
if (start === end) return null;
|
|||
|
|
this._font.stream.pos = start;
|
|||
|
|
return SBIXImage.decode(this._font.stream, { buflen: end - start });
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/glyph/COLRGlyph.ts
|
|||
|
|
const COLRLayer = (glyph, color) => ({
|
|||
|
|
glyph,
|
|||
|
|
color
|
|||
|
|
});
|
|||
|
|
const Black = {
|
|||
|
|
red: 0,
|
|||
|
|
green: 0,
|
|||
|
|
blue: 0,
|
|||
|
|
alpha: 255
|
|||
|
|
};
|
|||
|
|
/**
|
|||
|
|
* Represents a color (e.g. emoji) glyph in Microsoft's COLR format.
|
|||
|
|
* Each glyph in this format contain a list of colored layers, each
|
|||
|
|
* of which is another vector glyph.
|
|||
|
|
*/
|
|||
|
|
var COLRGlyph = class extends Glyph {
|
|||
|
|
type = "COLR";
|
|||
|
|
_getBBox() {
|
|||
|
|
const bbox = new BBox();
|
|||
|
|
for (const layer of this.layers) {
|
|||
|
|
const b = layer.glyph.bbox;
|
|||
|
|
bbox.addPoint(b.minX, b.minY);
|
|||
|
|
bbox.addPoint(b.maxX, b.maxY);
|
|||
|
|
}
|
|||
|
|
return bbox;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Returns an array of objects containing the glyph and color for
|
|||
|
|
* each layer in the composite color glyph.
|
|||
|
|
* @type {object[]}
|
|||
|
|
*/
|
|||
|
|
get layers() {
|
|||
|
|
const cpal = this._font.CPAL;
|
|||
|
|
const colr = this._font.COLR;
|
|||
|
|
let low = 0;
|
|||
|
|
let high = colr.baseGlyphRecord.length - 1;
|
|||
|
|
while (low <= high) {
|
|||
|
|
const mid = low + high >> 1;
|
|||
|
|
const rec = colr.baseGlyphRecord[mid];
|
|||
|
|
if (this.id < rec.gid) high = mid - 1;
|
|||
|
|
else if (this.id > rec.gid) low = mid + 1;
|
|||
|
|
else {
|
|||
|
|
var baseLayer = rec;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (baseLayer == null) {
|
|||
|
|
const g = this._font._getBaseGlyph(this.id);
|
|||
|
|
return g === this ? [] : [COLRLayer(g, Black)];
|
|||
|
|
}
|
|||
|
|
const layers = [];
|
|||
|
|
for (let i = baseLayer.firstLayerIndex; i < baseLayer.firstLayerIndex + baseLayer.numLayers; i++) {
|
|||
|
|
const rec = colr.layerRecords[i];
|
|||
|
|
const color = cpal.colorRecords[rec.paletteIndex];
|
|||
|
|
const g = this._font._getBaseGlyph(rec.gid);
|
|||
|
|
layers.push(COLRLayer(g, color));
|
|||
|
|
}
|
|||
|
|
return layers;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/glyph/GlyphVariationProcessor.ts
|
|||
|
|
const TUPLES_SHARE_POINT_NUMBERS = 32768;
|
|||
|
|
const TUPLE_COUNT_MASK = 4095;
|
|||
|
|
const EMBEDDED_TUPLE_COORD = 32768;
|
|||
|
|
const INTERMEDIATE_TUPLE = 16384;
|
|||
|
|
const PRIVATE_POINT_NUMBERS = 8192;
|
|||
|
|
const TUPLE_INDEX_MASK = 4095;
|
|||
|
|
const POINTS_ARE_WORDS = 128;
|
|||
|
|
const POINT_RUN_COUNT_MASK = 127;
|
|||
|
|
const DELTAS_ARE_ZERO = 128;
|
|||
|
|
const DELTAS_ARE_WORDS = 64;
|
|||
|
|
const DELTA_RUN_COUNT_MASK = 63;
|
|||
|
|
/**
|
|||
|
|
* This class is transforms TrueType glyphs according to the data from
|
|||
|
|
* the Apple Advanced Typography variation tables (fvar, gvar, and avar).
|
|||
|
|
* These tables allow infinite adjustments to glyph weight, width, slant,
|
|||
|
|
* and optical size without the designer needing to specify every exact style.
|
|||
|
|
*
|
|||
|
|
* Apple's documentation for these tables is not great, so thanks to the
|
|||
|
|
* Freetype project for figuring much of this out.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
var GlyphVariationProcessor = class {
|
|||
|
|
#normalizedCoords;
|
|||
|
|
#blendVectors = /* @__PURE__ */ new Map();
|
|||
|
|
constructor(font, coords) {
|
|||
|
|
this.font = font;
|
|||
|
|
this.#normalizedCoords = this.normalizeCoords(coords);
|
|||
|
|
}
|
|||
|
|
normalizeCoords(coords) {
|
|||
|
|
const normalized = this.font.fvar.axis.map((axis, i) => (coords[i] - axis.defaultValue + Number.EPSILON) / ((coords[i] < axis.defaultValue ? axis.defaultValue - axis.minValue : axis.maxValue - axis.defaultValue) + Number.EPSILON));
|
|||
|
|
for (let i = 0; i < this.font.avar?.segment.length || 0; i++) {
|
|||
|
|
const segment = this.font.avar.segment[i];
|
|||
|
|
for (let j = 0; j < segment.correspondence.length; j++) {
|
|||
|
|
const pair = segment.correspondence[j];
|
|||
|
|
if (j >= 1 && normalized[i] < pair.fromCoord) {
|
|||
|
|
const prev = segment.correspondence[j - 1];
|
|||
|
|
normalized[i] = ((normalized[i] - prev.fromCoord) * (pair.toCoord - prev.toCoord) + Number.EPSILON) / (pair.fromCoord - prev.fromCoord + Number.EPSILON) + prev.toCoord;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return normalized;
|
|||
|
|
}
|
|||
|
|
transformPoints(gid, glyphPoints) {
|
|||
|
|
if (!this.font.fvar || !this.font.gvar) return;
|
|||
|
|
const { gvar } = this.font;
|
|||
|
|
if (gid >= gvar.glyphCount) return;
|
|||
|
|
const offset = gvar.offsets[gid];
|
|||
|
|
if (offset === gvar.offsets[gid + 1]) return;
|
|||
|
|
const { stream } = this.font;
|
|||
|
|
stream.pos = offset;
|
|||
|
|
if (stream.pos >= stream.length) return;
|
|||
|
|
let tupleCount = stream.readUInt16BE();
|
|||
|
|
let offsetToData = offset + stream.readUInt16BE();
|
|||
|
|
let sharedPoints;
|
|||
|
|
if (tupleCount & TUPLES_SHARE_POINT_NUMBERS) {
|
|||
|
|
var here = stream.pos;
|
|||
|
|
stream.pos = offsetToData;
|
|||
|
|
sharedPoints = this.decodePoints();
|
|||
|
|
offsetToData = stream.pos;
|
|||
|
|
stream.pos = here;
|
|||
|
|
}
|
|||
|
|
const origPoints = glyphPoints.map((pt) => pt.copy());
|
|||
|
|
tupleCount &= TUPLE_COUNT_MASK;
|
|||
|
|
for (let i = 0; i < tupleCount; i++) {
|
|||
|
|
const tupleDataSize = stream.readUInt16BE();
|
|||
|
|
const tupleIndex = stream.readUInt16BE();
|
|||
|
|
let tupleCoords, startCoords, endCoords;
|
|||
|
|
if (tupleIndex & EMBEDDED_TUPLE_COORD) {
|
|||
|
|
tupleCoords = [];
|
|||
|
|
for (let a = 0; a < gvar.axisCount; a++) tupleCoords.push(stream.readInt16BE() / 16384);
|
|||
|
|
} else {
|
|||
|
|
if ((tupleIndex & TUPLE_INDEX_MASK) >= gvar.globalCoordCount) throw new Error("Invalid gvar table");
|
|||
|
|
tupleCoords = gvar.globalCoords[tupleIndex & TUPLE_INDEX_MASK];
|
|||
|
|
}
|
|||
|
|
if (tupleIndex & INTERMEDIATE_TUPLE) {
|
|||
|
|
startCoords = [];
|
|||
|
|
for (let a = 0; a < gvar.axisCount; a++) startCoords.push(stream.readInt16BE() / 16384);
|
|||
|
|
endCoords = [];
|
|||
|
|
for (let a = 0; a < gvar.axisCount; a++) endCoords.push(stream.readInt16BE() / 16384);
|
|||
|
|
}
|
|||
|
|
let factor = this.tupleFactor(tupleIndex, tupleCoords, startCoords, endCoords);
|
|||
|
|
if (factor === 0) {
|
|||
|
|
offsetToData += tupleDataSize;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
var here = stream.pos;
|
|||
|
|
stream.pos = offsetToData;
|
|||
|
|
let points;
|
|||
|
|
if (tupleIndex & PRIVATE_POINT_NUMBERS) points = this.decodePoints();
|
|||
|
|
else points = sharedPoints;
|
|||
|
|
const nPoints = points.length === 0 ? glyphPoints.length : points.length;
|
|||
|
|
const xDeltas = this.decodeDeltas(nPoints);
|
|||
|
|
const yDeltas = this.decodeDeltas(nPoints);
|
|||
|
|
if (points.length === 0) for (let i = 0; i < glyphPoints.length; i++) {
|
|||
|
|
const point = glyphPoints[i];
|
|||
|
|
point.x += Math.round(xDeltas[i] * factor);
|
|||
|
|
point.y += Math.round(yDeltas[i] * factor);
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
let outPoints = origPoints.map((pt) => pt.copy());
|
|||
|
|
let hasDelta = glyphPoints.map(() => false);
|
|||
|
|
for (let i = 0; i < points.length; i++) {
|
|||
|
|
const idx = points[i];
|
|||
|
|
if (idx < glyphPoints.length) {
|
|||
|
|
const point = outPoints[idx];
|
|||
|
|
hasDelta[idx] = true;
|
|||
|
|
point.x += xDeltas[i] * factor;
|
|||
|
|
point.y += yDeltas[i] * factor;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
this.interpolateMissingDeltas(outPoints, origPoints, hasDelta);
|
|||
|
|
for (let i = 0; i < glyphPoints.length; i++) {
|
|||
|
|
const deltaX = outPoints[i].x - origPoints[i].x;
|
|||
|
|
const deltaY = outPoints[i].y - origPoints[i].y;
|
|||
|
|
glyphPoints[i].x = Math.round(glyphPoints[i].x + deltaX);
|
|||
|
|
glyphPoints[i].y = Math.round(glyphPoints[i].y + deltaY);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
offsetToData += tupleDataSize;
|
|||
|
|
stream.pos = here;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
decodePoints() {
|
|||
|
|
const { stream } = this.font;
|
|||
|
|
let count = stream.readUInt8();
|
|||
|
|
if (count & POINTS_ARE_WORDS) count = (count & POINT_RUN_COUNT_MASK) << 8 | stream.readUInt8();
|
|||
|
|
const points = new Uint16Array(count);
|
|||
|
|
let i = 0;
|
|||
|
|
let point = 0;
|
|||
|
|
while (i < count) {
|
|||
|
|
const run = stream.readUInt8();
|
|||
|
|
const runCount = (run & POINT_RUN_COUNT_MASK) + 1;
|
|||
|
|
const fn = run & POINTS_ARE_WORDS ? stream.readUInt16BE : stream.readUInt8;
|
|||
|
|
for (let j = 0; j < runCount && i < count; j++) {
|
|||
|
|
point += fn.call(stream);
|
|||
|
|
points[i++] = point;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return points;
|
|||
|
|
}
|
|||
|
|
decodeDeltas(count) {
|
|||
|
|
const { stream } = this.font;
|
|||
|
|
let i = 0;
|
|||
|
|
const deltas = new Int16Array(count);
|
|||
|
|
while (i < count) {
|
|||
|
|
const run = stream.readUInt8();
|
|||
|
|
const runCount = (run & DELTA_RUN_COUNT_MASK) + 1;
|
|||
|
|
if (run & DELTAS_ARE_ZERO) i += runCount;
|
|||
|
|
else {
|
|||
|
|
const fn = run & DELTAS_ARE_WORDS ? stream.readInt16BE : stream.readInt8;
|
|||
|
|
for (let j = 0; j < runCount && i < count; j++) deltas[i++] = fn.call(stream);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return deltas;
|
|||
|
|
}
|
|||
|
|
tupleFactor(tupleIndex, tupleCoords, startCoords, endCoords) {
|
|||
|
|
const normalized = this.#normalizedCoords;
|
|||
|
|
const { gvar } = this.font;
|
|||
|
|
let factor = 1;
|
|||
|
|
for (let i = 0; i < gvar.axisCount; i++) {
|
|||
|
|
if (tupleCoords[i] === 0) continue;
|
|||
|
|
if (normalized[i] === 0) return 0;
|
|||
|
|
if ((tupleIndex & INTERMEDIATE_TUPLE) === 0) {
|
|||
|
|
if (normalized[i] < Math.min(0, tupleCoords[i]) || normalized[i] > Math.max(0, tupleCoords[i])) return 0;
|
|||
|
|
factor = (factor * normalized[i] + Number.EPSILON) / (tupleCoords[i] + Number.EPSILON);
|
|||
|
|
} else if (normalized[i] < startCoords[i] || normalized[i] > endCoords[i]) return 0;
|
|||
|
|
else if (normalized[i] < tupleCoords[i]) factor = factor * (normalized[i] - startCoords[i] + Number.EPSILON) / (tupleCoords[i] - startCoords[i] + Number.EPSILON);
|
|||
|
|
else factor = factor * (endCoords[i] - normalized[i] + Number.EPSILON) / (endCoords[i] - tupleCoords[i] + Number.EPSILON);
|
|||
|
|
}
|
|||
|
|
return factor;
|
|||
|
|
}
|
|||
|
|
interpolateMissingDeltas(points, inPoints, hasDelta) {
|
|||
|
|
if (points.length === 0) return;
|
|||
|
|
let point = 0;
|
|||
|
|
while (point < points.length) {
|
|||
|
|
const firstPoint = point;
|
|||
|
|
let endPoint = point;
|
|||
|
|
let pt = points[endPoint];
|
|||
|
|
while (!pt.endContour) pt = points[++endPoint];
|
|||
|
|
while (point <= endPoint && !hasDelta[point]) point++;
|
|||
|
|
if (point > endPoint) continue;
|
|||
|
|
const firstDelta = point;
|
|||
|
|
let curDelta = point;
|
|||
|
|
point++;
|
|||
|
|
while (point <= endPoint) {
|
|||
|
|
if (hasDelta[point]) {
|
|||
|
|
this.deltaInterpolate(curDelta + 1, point - 1, curDelta, point, inPoints, points);
|
|||
|
|
curDelta = point;
|
|||
|
|
}
|
|||
|
|
point++;
|
|||
|
|
}
|
|||
|
|
if (curDelta === firstDelta) this.deltaShift(firstPoint, endPoint, curDelta, inPoints, points);
|
|||
|
|
else {
|
|||
|
|
this.deltaInterpolate(curDelta + 1, endPoint, curDelta, firstDelta, inPoints, points);
|
|||
|
|
if (firstDelta > 0) this.deltaInterpolate(firstPoint, firstDelta - 1, curDelta, firstDelta, inPoints, points);
|
|||
|
|
}
|
|||
|
|
point = endPoint + 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
deltaInterpolate(p1, p2, ref1, ref2, inPoints, outPoints) {
|
|||
|
|
if (p1 > p2) return;
|
|||
|
|
const iterable = ["x", "y"];
|
|||
|
|
for (let i = 0; i < iterable.length; i++) {
|
|||
|
|
const k = iterable[i];
|
|||
|
|
if (inPoints[ref1][k] > inPoints[ref2][k]) {
|
|||
|
|
const p = ref1;
|
|||
|
|
ref1 = ref2;
|
|||
|
|
ref2 = p;
|
|||
|
|
}
|
|||
|
|
const in1 = inPoints[ref1][k];
|
|||
|
|
const in2 = inPoints[ref2][k];
|
|||
|
|
const out1 = outPoints[ref1][k];
|
|||
|
|
const out2 = outPoints[ref2][k];
|
|||
|
|
if (in1 !== in2 || out1 === out2) {
|
|||
|
|
const scale = in1 === in2 ? 0 : (out2 - out1) / (in2 - in1);
|
|||
|
|
for (let p = p1; p <= p2; p++) {
|
|||
|
|
let out = inPoints[p][k];
|
|||
|
|
if (out <= in1) out += out1 - in1;
|
|||
|
|
else if (out >= in2) out += out2 - in2;
|
|||
|
|
else out = out1 + (out - in1) * scale;
|
|||
|
|
outPoints[p][k] = out;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
deltaShift(p1, p2, ref, inPoints, outPoints) {
|
|||
|
|
const deltaX = outPoints[ref].x - inPoints[ref].x;
|
|||
|
|
const deltaY = outPoints[ref].y - inPoints[ref].y;
|
|||
|
|
if (deltaX === 0 && deltaY === 0) return;
|
|||
|
|
for (let p = p1; p <= p2; p++) if (p !== ref) {
|
|||
|
|
outPoints[p].x += deltaX;
|
|||
|
|
outPoints[p].y += deltaY;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
getAdvanceAdjustment(gid, table) {
|
|||
|
|
let outerIndex, innerIndex;
|
|||
|
|
if (table.advanceWidthMapping) {
|
|||
|
|
const { mapCount, mapData } = table.advanceWidthMapping;
|
|||
|
|
const idx = gid >= mapCount ? mapCount - 1 : gid;
|
|||
|
|
({outerIndex, innerIndex} = mapData[idx]);
|
|||
|
|
} else {
|
|||
|
|
outerIndex = 0;
|
|||
|
|
innerIndex = gid;
|
|||
|
|
}
|
|||
|
|
return this.getDelta(table.itemVariationStore, outerIndex, innerIndex);
|
|||
|
|
}
|
|||
|
|
getDelta(itemStore, outerIndex, innerIndex) {
|
|||
|
|
const varData = itemStore.itemVariationData[outerIndex];
|
|||
|
|
if (outerIndex >= itemStore.itemVariationData.length || innerIndex >= varData.deltaSets.length) return 0;
|
|||
|
|
const deltaSet = varData.deltaSets[innerIndex];
|
|||
|
|
const blendVector = this.getBlendVector(itemStore, outerIndex);
|
|||
|
|
let netAdjustment = 0;
|
|||
|
|
for (let master = 0; master < varData.regionIndexCount; master++) netAdjustment += deltaSet.deltas[master] * blendVector[master];
|
|||
|
|
return netAdjustment;
|
|||
|
|
}
|
|||
|
|
getBlendVector(itemStore, outerIndex) {
|
|||
|
|
const varData = itemStore.itemVariationData[outerIndex];
|
|||
|
|
if (this.#blendVectors.has(varData)) return this.#blendVectors.get(varData);
|
|||
|
|
const normalizedCoords = this.#normalizedCoords;
|
|||
|
|
const blendVector = [];
|
|||
|
|
for (let master = 0; master < varData.regionIndexCount; master++) {
|
|||
|
|
let scalar = 1;
|
|||
|
|
const regionIndex = varData.regionIndexes[master];
|
|||
|
|
const axes = itemStore.variationRegionList.variationRegions[regionIndex];
|
|||
|
|
for (let j = 0; j < axes.length; j++) {
|
|||
|
|
const axis = axes[j];
|
|||
|
|
let axisScalar;
|
|||
|
|
if (axis.startCoord > axis.peakCoord || axis.peakCoord > axis.endCoord) axisScalar = 1;
|
|||
|
|
else if (axis.startCoord < 0 && axis.endCoord > 0 && axis.peakCoord !== 0) axisScalar = 1;
|
|||
|
|
else if (axis.peakCoord === 0) axisScalar = 1;
|
|||
|
|
else if (normalizedCoords[j] < axis.startCoord || normalizedCoords[j] > axis.endCoord) axisScalar = 0;
|
|||
|
|
else if (normalizedCoords[j] === axis.peakCoord) axisScalar = 1;
|
|||
|
|
else if (normalizedCoords[j] < axis.peakCoord) axisScalar = (normalizedCoords[j] - axis.startCoord + Number.EPSILON) / (axis.peakCoord - axis.startCoord + Number.EPSILON);
|
|||
|
|
else axisScalar = (axis.endCoord - normalizedCoords[j] + Number.EPSILON) / (axis.endCoord - axis.peakCoord + Number.EPSILON);
|
|||
|
|
scalar *= axisScalar;
|
|||
|
|
}
|
|||
|
|
blendVector[master] = scalar;
|
|||
|
|
}
|
|||
|
|
this.#blendVectors.set(varData, blendVector);
|
|||
|
|
return blendVector;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/TTFFont.ts
|
|||
|
|
/**
|
|||
|
|
* This is the base class for all SFNT-based font formats in fontkitten.
|
|||
|
|
* It supports TrueType, and PostScript glyphs, and several color glyph formats.
|
|||
|
|
*/
|
|||
|
|
var TTFFont = class TTFFont {
|
|||
|
|
type = "TTF";
|
|||
|
|
isCollection = false;
|
|||
|
|
stream;
|
|||
|
|
#variationCoords;
|
|||
|
|
#directoryPos;
|
|||
|
|
#tables = {};
|
|||
|
|
_glyphs = {};
|
|||
|
|
directory;
|
|||
|
|
"OS/2";
|
|||
|
|
"hhea";
|
|||
|
|
static probe(buffer) {
|
|||
|
|
const format = asciiDecoder.decode(buffer.slice(0, 4));
|
|||
|
|
return format === "true" || format === "OTTO" || format === String.fromCharCode(0, 1, 0, 0);
|
|||
|
|
}
|
|||
|
|
constructor(stream, variationCoords = null) {
|
|||
|
|
this.stream = stream;
|
|||
|
|
this.#variationCoords = variationCoords;
|
|||
|
|
this.#directoryPos = this.stream.pos;
|
|||
|
|
this.directory = this._decodeDirectory();
|
|||
|
|
for (const tag in this.directory.tables) {
|
|||
|
|
const table = this.directory.tables[tag];
|
|||
|
|
if (tables[tag] && table.length > 0) Object.defineProperty(this, tag, { get: this.#getTable.bind(this, table) });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
#getTable(table) {
|
|||
|
|
try {
|
|||
|
|
this.#tables[table.tag] ??= this._decodeTable(table);
|
|||
|
|
} catch {}
|
|||
|
|
return this.#tables[table.tag];
|
|||
|
|
}
|
|||
|
|
_getTableStream(tag) {
|
|||
|
|
const table = this.directory.tables[tag];
|
|||
|
|
if (table) {
|
|||
|
|
this.stream.pos = table.offset;
|
|||
|
|
return this.stream;
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
_decodeDirectory() {
|
|||
|
|
return Directory.decode(this.stream, { _startOffset: 0 });
|
|||
|
|
}
|
|||
|
|
_decodeTable(table) {
|
|||
|
|
const pos = this.stream.pos;
|
|||
|
|
const stream = this._getTableStream(table.tag);
|
|||
|
|
const result = tables[table.tag].decode(stream, this, table.length);
|
|||
|
|
this.stream.pos = pos;
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Gets a string from the font's `name` table
|
|||
|
|
*/
|
|||
|
|
getName(key) {
|
|||
|
|
const record = this.name?.records[key];
|
|||
|
|
return record?.["en"] || record?.[Object.keys(record)[0]] || null;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The unique PostScript name for this font, e.g. "Helvetica-Bold"
|
|||
|
|
*/
|
|||
|
|
get postscriptName() {
|
|||
|
|
return this.getName("postscriptName");
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The font's full name, e.g. "Helvetica Bold"
|
|||
|
|
*/
|
|||
|
|
get fullName() {
|
|||
|
|
return this.getName("fullName");
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The font's family name, e.g. "Helvetica"
|
|||
|
|
*/
|
|||
|
|
get familyName() {
|
|||
|
|
return this.getName("fontFamily");
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The font's sub-family, e.g. "Bold".
|
|||
|
|
*/
|
|||
|
|
get subfamilyName() {
|
|||
|
|
return this.getName("fontSubfamily");
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The font's copyright information
|
|||
|
|
*/
|
|||
|
|
get copyright() {
|
|||
|
|
return this.getName("copyright");
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The font's version number
|
|||
|
|
*/
|
|||
|
|
get version() {
|
|||
|
|
return this.getName("version");
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The font’s [ascender](https://en.wikipedia.org/wiki/Ascender_(typography))
|
|||
|
|
*/
|
|||
|
|
get ascent() {
|
|||
|
|
return this.hhea.ascent;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The font’s [descender](https://en.wikipedia.org/wiki/Descender)
|
|||
|
|
*/
|
|||
|
|
get descent() {
|
|||
|
|
return this.hhea.descent;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The amount of space that should be included between lines
|
|||
|
|
*/
|
|||
|
|
get lineGap() {
|
|||
|
|
return this.hhea.lineGap;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The offset from the normal underline position that should be used
|
|||
|
|
*/
|
|||
|
|
get underlinePosition() {
|
|||
|
|
return this.post.underlinePosition;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The weight of the underline that should be used
|
|||
|
|
*/
|
|||
|
|
get underlineThickness() {
|
|||
|
|
return this.post.underlineThickness;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* If this is an italic font, the angle the cursor should be drawn at to match the font design
|
|||
|
|
*/
|
|||
|
|
get italicAngle() {
|
|||
|
|
return this.post.italicAngle;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The height of capital letters above the baseline.
|
|||
|
|
* See [here](https://en.wikipedia.org/wiki/Cap_height) for more details.
|
|||
|
|
*/
|
|||
|
|
get capHeight() {
|
|||
|
|
let os2 = this["OS/2"];
|
|||
|
|
return os2 ? os2.capHeight : this.ascent;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The height of lower case letters in the font.
|
|||
|
|
* See [here](https://en.wikipedia.org/wiki/X-height) for more details.
|
|||
|
|
*/
|
|||
|
|
get xHeight() {
|
|||
|
|
return this["OS/2"]?.xHeight;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The number of glyphs in the font.
|
|||
|
|
*/
|
|||
|
|
get numGlyphs() {
|
|||
|
|
return this.maxp.numGlyphs;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The size of the font’s internal coordinate grid
|
|||
|
|
*/
|
|||
|
|
get unitsPerEm() {
|
|||
|
|
return this.head.unitsPerEm;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* The font’s bounding box, i.e. the box that encloses all glyphs in the font.
|
|||
|
|
*/
|
|||
|
|
get bbox() {
|
|||
|
|
return Object.freeze(new BBox(this.head.xMin, this.head.yMin, this.head.xMax, this.head.yMax));
|
|||
|
|
}
|
|||
|
|
get _cmapProcessor() {
|
|||
|
|
return new CmapProcessor(this.cmap);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* An array of all of the unicode code points supported by the font.
|
|||
|
|
*/
|
|||
|
|
get characterSet() {
|
|||
|
|
return this._cmapProcessor.getCharacterSet();
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Returns whether there is glyph in the font for the given unicode code point.
|
|||
|
|
*
|
|||
|
|
* @param {number} codePoint
|
|||
|
|
* @return {boolean}
|
|||
|
|
*/
|
|||
|
|
hasGlyphForCodePoint(codePoint) {
|
|||
|
|
return !!this._cmapProcessor.lookup(codePoint);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Maps a single unicode code point to a Glyph object.
|
|||
|
|
* Does not perform any advanced substitutions (there is no context to do so).
|
|||
|
|
*/
|
|||
|
|
glyphForCodePoint(codePoint) {
|
|||
|
|
return this.getGlyph(this._cmapProcessor.lookup(codePoint), [codePoint]);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Returns an array of Glyph objects for the given string.
|
|||
|
|
* This is only a one-to-one mapping from characters to glyphs.
|
|||
|
|
*/
|
|||
|
|
glyphsForString(string) {
|
|||
|
|
const glyphs = [];
|
|||
|
|
const len = string.length;
|
|||
|
|
let idx = 0;
|
|||
|
|
let last = -1;
|
|||
|
|
let state = -1;
|
|||
|
|
while (idx <= len) {
|
|||
|
|
let code = 0;
|
|||
|
|
let nextState = 0;
|
|||
|
|
if (idx < len) {
|
|||
|
|
code = string.charCodeAt(idx++);
|
|||
|
|
if (55296 <= code && code <= 56319 && idx < len) {
|
|||
|
|
const next = string.charCodeAt(idx);
|
|||
|
|
if (56320 <= next && next <= 57343) {
|
|||
|
|
idx++;
|
|||
|
|
code = ((code & 1023) << 10) + (next & 1023) + 65536;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
nextState = 65024 <= code && code <= 65039 || 917760 <= code && code <= 917999 ? 1 : 0;
|
|||
|
|
} else idx++;
|
|||
|
|
if (state === 0 && nextState === 1) glyphs.push(this.getGlyph(this._cmapProcessor.lookup(last, code), [last, code]));
|
|||
|
|
else if (state === 0 && nextState === 0) glyphs.push(this.glyphForCodePoint(last));
|
|||
|
|
last = code;
|
|||
|
|
state = nextState;
|
|||
|
|
}
|
|||
|
|
return glyphs;
|
|||
|
|
}
|
|||
|
|
_getBaseGlyph(glyph, characters = []) {
|
|||
|
|
if (!this._glyphs[glyph]) {
|
|||
|
|
if (this.directory.tables.glyf) this._glyphs[glyph] = new TTFGlyph(glyph, characters, this);
|
|||
|
|
else if (this.directory.tables["CFF "] || this.directory.tables.CFF2) this._glyphs[glyph] = new CFFGlyph(glyph, characters, this);
|
|||
|
|
}
|
|||
|
|
return this._glyphs[glyph] || null;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Returns a glyph object for the given glyph id.
|
|||
|
|
* You can pass the array of code points this glyph represents for
|
|||
|
|
* your use later, and it will be stored in the glyph object.
|
|||
|
|
*/
|
|||
|
|
getGlyph(glyph, characters = []) {
|
|||
|
|
if (!this._glyphs[glyph]) if (this.directory.tables.sbix) this._glyphs[glyph] = new SBIXGlyph(glyph, characters, this);
|
|||
|
|
else if (this.directory.tables.COLR && this.directory.tables.CPAL) this._glyphs[glyph] = new COLRGlyph(glyph, characters, this);
|
|||
|
|
else this._getBaseGlyph(glyph, characters);
|
|||
|
|
return this._glyphs[glyph] || null;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Returns an object describing the available variation axes
|
|||
|
|
* that this font supports. Keys are setting tags, and values
|
|||
|
|
* contain the axis name, range, and default value.
|
|||
|
|
*/
|
|||
|
|
get variationAxes() {
|
|||
|
|
return Object.fromEntries(this.fvar?.axis.map((axis) => [axis.axisTag.trim(), {
|
|||
|
|
name: axis.name.en,
|
|||
|
|
min: axis.minValue,
|
|||
|
|
default: axis.defaultValue,
|
|||
|
|
max: axis.maxValue
|
|||
|
|
}]) || []);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Returns an object describing the named variation instances
|
|||
|
|
* that the font designer has specified. Keys are variation names
|
|||
|
|
* and values are the variation settings for this instance.
|
|||
|
|
*/
|
|||
|
|
get namedVariations() {
|
|||
|
|
return Object.fromEntries(this.fvar?.instance.map((instance) => {
|
|||
|
|
const settings = {};
|
|||
|
|
for (let i = 0; i < this.fvar.axis.length; i++) {
|
|||
|
|
const axis = this.fvar.axis[i];
|
|||
|
|
settings[axis.axisTag.trim()] = instance.coord[i];
|
|||
|
|
}
|
|||
|
|
return [instance.name.en, settings];
|
|||
|
|
}) || []);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Returns a new font with the given variation settings applied.
|
|||
|
|
* Settings can either be an instance name, or an object containing
|
|||
|
|
* variation tags as specified by the `variationAxes` property.
|
|||
|
|
*
|
|||
|
|
* @param {object} settings
|
|||
|
|
* @return {TTFFont}
|
|||
|
|
*/
|
|||
|
|
getVariation(settings) {
|
|||
|
|
if (!(this.directory.tables.fvar && (this.directory.tables.gvar && this.directory.tables.glyf || this.directory.tables.CFF2))) throw new Error("Variations require a font with the fvar, gvar and glyf, or CFF2 tables.");
|
|||
|
|
if (typeof settings === "string") settings = this.namedVariations[settings];
|
|||
|
|
if (typeof settings !== "object") throw new Error("Variation settings must be either a variation name or settings object.");
|
|||
|
|
const coords = this.fvar.axis.map((axis) => {
|
|||
|
|
const axisTag = axis.axisTag.trim();
|
|||
|
|
if (axisTag in settings) return Math.max(axis.minValue, Math.min(axis.maxValue, settings[axisTag]));
|
|||
|
|
else return axis.defaultValue;
|
|||
|
|
});
|
|||
|
|
const stream = new DecodeStream(this.stream.buffer);
|
|||
|
|
stream.pos = this.#directoryPos;
|
|||
|
|
const font = new TTFFont(stream, coords);
|
|||
|
|
font.#tables = this.#tables;
|
|||
|
|
return font;
|
|||
|
|
}
|
|||
|
|
get _variationProcessor() {
|
|||
|
|
if (!this.fvar) return null;
|
|||
|
|
let variationCoords = this.#variationCoords;
|
|||
|
|
if (!variationCoords) {
|
|||
|
|
if (!this.CFF2) return null;
|
|||
|
|
variationCoords = this.fvar.axis.map((axis) => axis.defaultValue);
|
|||
|
|
}
|
|||
|
|
return new GlyphVariationProcessor(this, variationCoords);
|
|||
|
|
}
|
|||
|
|
getFont(name) {
|
|||
|
|
return this.getVariation(name);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
__decorate([cache], TTFFont.prototype, "bbox", null);
|
|||
|
|
__decorate([cache], TTFFont.prototype, "_cmapProcessor", null);
|
|||
|
|
__decorate([cache], TTFFont.prototype, "characterSet", null);
|
|||
|
|
__decorate([cache], TTFFont.prototype, "variationAxes", null);
|
|||
|
|
__decorate([cache], TTFFont.prototype, "namedVariations", null);
|
|||
|
|
__decorate([cache], TTFFont.prototype, "_variationProcessor", null);
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/WOFFDirectory.ts
|
|||
|
|
let WOFFDirectoryEntry = new Struct({
|
|||
|
|
tag: new StringT(4),
|
|||
|
|
offset: new Pointer(uint32, "void", { type: "global" }),
|
|||
|
|
compLength: uint32,
|
|||
|
|
length: uint32,
|
|||
|
|
origChecksum: uint32
|
|||
|
|
});
|
|||
|
|
let WOFFDirectory = new Struct({
|
|||
|
|
tag: new StringT(4),
|
|||
|
|
flavor: uint32,
|
|||
|
|
length: uint32,
|
|||
|
|
numTables: uint16,
|
|||
|
|
reserved: new Reserved(uint16),
|
|||
|
|
totalSfntSize: uint32,
|
|||
|
|
majorVersion: uint16,
|
|||
|
|
minorVersion: uint16,
|
|||
|
|
metaOffset: uint32,
|
|||
|
|
metaLength: uint32,
|
|||
|
|
metaOrigLength: uint32,
|
|||
|
|
privOffset: uint32,
|
|||
|
|
privLength: uint32,
|
|||
|
|
tables: new ArrayT(WOFFDirectoryEntry, "numTables")
|
|||
|
|
});
|
|||
|
|
WOFFDirectory.process = function() {
|
|||
|
|
this.tables = Object.fromEntries(this.tables.map((table) => [table.tag, table]));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/WOFFFont.ts
|
|||
|
|
var WOFFFont = class extends TTFFont {
|
|||
|
|
type = "WOFF";
|
|||
|
|
static probe(buffer) {
|
|||
|
|
return asciiDecoder.decode(buffer.slice(0, 4)) === "wOFF";
|
|||
|
|
}
|
|||
|
|
_decodeDirectory() {
|
|||
|
|
return WOFFDirectory.decode(this.stream, { _startOffset: 0 });
|
|||
|
|
}
|
|||
|
|
_getTableStream(tag) {
|
|||
|
|
const table = this.directory.tables[tag];
|
|||
|
|
if (table) {
|
|||
|
|
this.stream.pos = table.offset;
|
|||
|
|
if (table.compLength < table.length) {
|
|||
|
|
this.stream.pos += 2;
|
|||
|
|
const outBuffer = new Uint8Array(table.length);
|
|||
|
|
const buf = inflate(this.stream.readBuffer(table.compLength - 2), outBuffer);
|
|||
|
|
return new DecodeStream(buf);
|
|||
|
|
} else return this.stream;
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/vendor/brotliDecode.ts
|
|||
|
|
const MAX_HUFFMAN_TABLE_SIZE = Int32Array.from([
|
|||
|
|
256,
|
|||
|
|
402,
|
|||
|
|
436,
|
|||
|
|
468,
|
|||
|
|
500,
|
|||
|
|
534,
|
|||
|
|
566,
|
|||
|
|
598,
|
|||
|
|
630,
|
|||
|
|
662,
|
|||
|
|
694,
|
|||
|
|
726,
|
|||
|
|
758,
|
|||
|
|
790,
|
|||
|
|
822,
|
|||
|
|
854,
|
|||
|
|
886,
|
|||
|
|
920,
|
|||
|
|
952,
|
|||
|
|
984,
|
|||
|
|
1016,
|
|||
|
|
1048,
|
|||
|
|
1080
|
|||
|
|
]);
|
|||
|
|
const CODE_LENGTH_CODE_ORDER = Int32Array.from([
|
|||
|
|
1,
|
|||
|
|
2,
|
|||
|
|
3,
|
|||
|
|
4,
|
|||
|
|
0,
|
|||
|
|
5,
|
|||
|
|
17,
|
|||
|
|
6,
|
|||
|
|
16,
|
|||
|
|
7,
|
|||
|
|
8,
|
|||
|
|
9,
|
|||
|
|
10,
|
|||
|
|
11,
|
|||
|
|
12,
|
|||
|
|
13,
|
|||
|
|
14,
|
|||
|
|
15
|
|||
|
|
]);
|
|||
|
|
const DISTANCE_SHORT_CODE_INDEX_OFFSET = Int32Array.from([
|
|||
|
|
0,
|
|||
|
|
3,
|
|||
|
|
2,
|
|||
|
|
1,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
3,
|
|||
|
|
3,
|
|||
|
|
3,
|
|||
|
|
3,
|
|||
|
|
3,
|
|||
|
|
3
|
|||
|
|
]);
|
|||
|
|
const DISTANCE_SHORT_CODE_VALUE_OFFSET = Int32Array.from([
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
-1,
|
|||
|
|
1,
|
|||
|
|
-2,
|
|||
|
|
2,
|
|||
|
|
-3,
|
|||
|
|
3,
|
|||
|
|
-1,
|
|||
|
|
1,
|
|||
|
|
-2,
|
|||
|
|
2,
|
|||
|
|
-3,
|
|||
|
|
3
|
|||
|
|
]);
|
|||
|
|
const FIXED_TABLE = Int32Array.from([
|
|||
|
|
131072,
|
|||
|
|
131076,
|
|||
|
|
131075,
|
|||
|
|
196610,
|
|||
|
|
131072,
|
|||
|
|
131076,
|
|||
|
|
131075,
|
|||
|
|
262145,
|
|||
|
|
131072,
|
|||
|
|
131076,
|
|||
|
|
131075,
|
|||
|
|
196610,
|
|||
|
|
131072,
|
|||
|
|
131076,
|
|||
|
|
131075,
|
|||
|
|
262149
|
|||
|
|
]);
|
|||
|
|
const BLOCK_LENGTH_OFFSET = Int32Array.from([
|
|||
|
|
1,
|
|||
|
|
5,
|
|||
|
|
9,
|
|||
|
|
13,
|
|||
|
|
17,
|
|||
|
|
25,
|
|||
|
|
33,
|
|||
|
|
41,
|
|||
|
|
49,
|
|||
|
|
65,
|
|||
|
|
81,
|
|||
|
|
97,
|
|||
|
|
113,
|
|||
|
|
145,
|
|||
|
|
177,
|
|||
|
|
209,
|
|||
|
|
241,
|
|||
|
|
305,
|
|||
|
|
369,
|
|||
|
|
497,
|
|||
|
|
753,
|
|||
|
|
1265,
|
|||
|
|
2289,
|
|||
|
|
4337,
|
|||
|
|
8433,
|
|||
|
|
16625
|
|||
|
|
]);
|
|||
|
|
const BLOCK_LENGTH_N_BITS = Int32Array.from([
|
|||
|
|
2,
|
|||
|
|
2,
|
|||
|
|
2,
|
|||
|
|
2,
|
|||
|
|
3,
|
|||
|
|
3,
|
|||
|
|
3,
|
|||
|
|
3,
|
|||
|
|
4,
|
|||
|
|
4,
|
|||
|
|
4,
|
|||
|
|
4,
|
|||
|
|
5,
|
|||
|
|
5,
|
|||
|
|
5,
|
|||
|
|
5,
|
|||
|
|
6,
|
|||
|
|
6,
|
|||
|
|
7,
|
|||
|
|
8,
|
|||
|
|
9,
|
|||
|
|
10,
|
|||
|
|
11,
|
|||
|
|
12,
|
|||
|
|
13,
|
|||
|
|
24
|
|||
|
|
]);
|
|||
|
|
const INSERT_LENGTH_N_BITS = Int16Array.from([
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
1,
|
|||
|
|
1,
|
|||
|
|
2,
|
|||
|
|
2,
|
|||
|
|
3,
|
|||
|
|
3,
|
|||
|
|
4,
|
|||
|
|
4,
|
|||
|
|
5,
|
|||
|
|
5,
|
|||
|
|
6,
|
|||
|
|
7,
|
|||
|
|
8,
|
|||
|
|
9,
|
|||
|
|
10,
|
|||
|
|
12,
|
|||
|
|
14,
|
|||
|
|
24
|
|||
|
|
]);
|
|||
|
|
const COPY_LENGTH_N_BITS = Int16Array.from([
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
0,
|
|||
|
|
1,
|
|||
|
|
1,
|
|||
|
|
2,
|
|||
|
|
2,
|
|||
|
|
3,
|
|||
|
|
3,
|
|||
|
|
4,
|
|||
|
|
4,
|
|||
|
|
5,
|
|||
|
|
5,
|
|||
|
|
6,
|
|||
|
|
7,
|
|||
|
|
8,
|
|||
|
|
9,
|
|||
|
|
10,
|
|||
|
|
24
|
|||
|
|
]);
|
|||
|
|
const CMD_LOOKUP = new Int16Array(2816);
|
|||
|
|
unpackCommandLookupTable(CMD_LOOKUP);
|
|||
|
|
function log2floor(i) {
|
|||
|
|
let result = -1;
|
|||
|
|
let step = 16;
|
|||
|
|
let v = i;
|
|||
|
|
while (step > 0) {
|
|||
|
|
let next = v >> step;
|
|||
|
|
if (next !== 0) {
|
|||
|
|
result += step;
|
|||
|
|
v = next;
|
|||
|
|
}
|
|||
|
|
step = step >> 1;
|
|||
|
|
}
|
|||
|
|
return result + v;
|
|||
|
|
}
|
|||
|
|
function calculateDistanceAlphabetSize(npostfix, ndirect, maxndistbits) {
|
|||
|
|
return 16 + ndirect + 2 * (maxndistbits << npostfix);
|
|||
|
|
}
|
|||
|
|
function calculateDistanceAlphabetLimit(s, maxDistance, npostfix, ndirect) {
|
|||
|
|
if (maxDistance < ndirect + (2 << npostfix)) return makeError(s, -23);
|
|||
|
|
const offset = (maxDistance - ndirect >> npostfix) + 4;
|
|||
|
|
const ndistbits = log2floor(offset) - 1;
|
|||
|
|
return ((ndistbits - 1 << 1 | offset >> ndistbits & 1) - 1 << npostfix) + (1 << npostfix) + ndirect + 16;
|
|||
|
|
}
|
|||
|
|
function unpackCommandLookupTable(cmdLookup) {
|
|||
|
|
const insertLengthOffsets = new Int32Array(24);
|
|||
|
|
const copyLengthOffsets = new Int32Array(24);
|
|||
|
|
copyLengthOffsets[0] = 2;
|
|||
|
|
for (let i = 0; i < 23; ++i) {
|
|||
|
|
insertLengthOffsets[i + 1] = insertLengthOffsets[i] + (1 << INSERT_LENGTH_N_BITS[i]);
|
|||
|
|
copyLengthOffsets[i + 1] = copyLengthOffsets[i] + (1 << COPY_LENGTH_N_BITS[i]);
|
|||
|
|
}
|
|||
|
|
for (let cmdCode = 0; cmdCode < 704; ++cmdCode) {
|
|||
|
|
let rangeIdx = cmdCode >> 6;
|
|||
|
|
let distanceContextOffset = -4;
|
|||
|
|
if (rangeIdx >= 2) {
|
|||
|
|
rangeIdx -= 2;
|
|||
|
|
distanceContextOffset = 0;
|
|||
|
|
}
|
|||
|
|
const insertCode = (170064 >> rangeIdx * 2 & 3) << 3 | cmdCode >> 3 & 7;
|
|||
|
|
const copyCode = (156228 >> rangeIdx * 2 & 3) << 3 | cmdCode & 7;
|
|||
|
|
const copyLengthOffset = copyLengthOffsets[copyCode];
|
|||
|
|
const distanceContext = distanceContextOffset + Math.min(copyLengthOffset, 5) - 2;
|
|||
|
|
const index = cmdCode * 4;
|
|||
|
|
cmdLookup[index] = INSERT_LENGTH_N_BITS[insertCode] | COPY_LENGTH_N_BITS[copyCode] << 8;
|
|||
|
|
cmdLookup[index + 1] = insertLengthOffsets[insertCode];
|
|||
|
|
cmdLookup[index + 2] = copyLengthOffsets[copyCode];
|
|||
|
|
cmdLookup[index + 3] = distanceContext;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function decodeWindowBits(s) {
|
|||
|
|
const largeWindowEnabled = s.isLargeWindow;
|
|||
|
|
s.isLargeWindow = 0;
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
if (readFewBits(s, 1) === 0) return 16;
|
|||
|
|
let n = readFewBits(s, 3);
|
|||
|
|
if (n !== 0) return 17 + n;
|
|||
|
|
n = readFewBits(s, 3);
|
|||
|
|
if (n !== 0) {
|
|||
|
|
if (n === 1) {
|
|||
|
|
if (largeWindowEnabled === 0) return -1;
|
|||
|
|
s.isLargeWindow = 1;
|
|||
|
|
if (readFewBits(s, 1) === 1) return -1;
|
|||
|
|
n = readFewBits(s, 6);
|
|||
|
|
if (n < 10 || n > 30) return -1;
|
|||
|
|
return n;
|
|||
|
|
}
|
|||
|
|
return 8 + n;
|
|||
|
|
}
|
|||
|
|
return 17;
|
|||
|
|
}
|
|||
|
|
function initState(s) {
|
|||
|
|
if (s.runningState !== 0) return makeError(s, -26);
|
|||
|
|
s.blockTrees = new Int32Array(3091);
|
|||
|
|
s.blockTrees[0] = 7;
|
|||
|
|
s.distRbIdx = 3;
|
|||
|
|
let result = calculateDistanceAlphabetLimit(s, 2147483644, 3, 120);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
const maxDistanceAlphabetLimit = result;
|
|||
|
|
s.distExtraBits = new Int8Array(maxDistanceAlphabetLimit);
|
|||
|
|
s.distOffset = new Int32Array(maxDistanceAlphabetLimit);
|
|||
|
|
result = initBitReader(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
s.runningState = 1;
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function close(s) {
|
|||
|
|
if (s.runningState === 0) return makeError(s, -25);
|
|||
|
|
if (s.runningState > 0) s.runningState = 11;
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function decodeVarLenUnsignedByte(s) {
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
if (readFewBits(s, 1) !== 0) {
|
|||
|
|
const n = readFewBits(s, 3);
|
|||
|
|
if (n === 0) return 1;
|
|||
|
|
return readFewBits(s, n) + (1 << n);
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function decodeMetaBlockLength(s) {
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
s.inputEnd = readFewBits(s, 1);
|
|||
|
|
s.metaBlockLength = 0;
|
|||
|
|
s.isUncompressed = 0;
|
|||
|
|
s.isMetadata = 0;
|
|||
|
|
if (s.inputEnd !== 0 && readFewBits(s, 1) !== 0) return 0;
|
|||
|
|
const sizeNibbles = readFewBits(s, 2) + 4;
|
|||
|
|
if (sizeNibbles === 7) {
|
|||
|
|
s.isMetadata = 1;
|
|||
|
|
if (readFewBits(s, 1) !== 0) return makeError(s, -6);
|
|||
|
|
const sizeBytes = readFewBits(s, 2);
|
|||
|
|
if (sizeBytes === 0) return 0;
|
|||
|
|
for (let i = 0; i < sizeBytes; ++i) {
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
const bits = readFewBits(s, 8);
|
|||
|
|
if (bits === 0 && i + 1 === sizeBytes && sizeBytes > 1) return makeError(s, -8);
|
|||
|
|
s.metaBlockLength += bits << i * 8;
|
|||
|
|
}
|
|||
|
|
} else for (let i = 0; i < sizeNibbles; ++i) {
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
const bits = readFewBits(s, 4);
|
|||
|
|
if (bits === 0 && i + 1 === sizeNibbles && sizeNibbles > 4) return makeError(s, -8);
|
|||
|
|
s.metaBlockLength += bits << i * 4;
|
|||
|
|
}
|
|||
|
|
s.metaBlockLength++;
|
|||
|
|
if (s.inputEnd === 0) s.isUncompressed = readFewBits(s, 1);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function readSymbol(tableGroup, tableIdx, s) {
|
|||
|
|
let offset = tableGroup[tableIdx];
|
|||
|
|
const v = s.accumulator32 >>> s.bitOffset;
|
|||
|
|
offset += v & 255;
|
|||
|
|
const bits = tableGroup[offset] >> 16;
|
|||
|
|
const sym = tableGroup[offset] & 65535;
|
|||
|
|
if (bits <= 8) {
|
|||
|
|
s.bitOffset += bits;
|
|||
|
|
return sym;
|
|||
|
|
}
|
|||
|
|
offset += sym;
|
|||
|
|
const mask = (1 << bits) - 1;
|
|||
|
|
offset += (v & mask) >>> 8;
|
|||
|
|
s.bitOffset += (tableGroup[offset] >> 16) + 8;
|
|||
|
|
return tableGroup[offset] & 65535;
|
|||
|
|
}
|
|||
|
|
function readBlockLength(tableGroup, tableIdx, s) {
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
const code = readSymbol(tableGroup, tableIdx, s);
|
|||
|
|
const n = BLOCK_LENGTH_N_BITS[code];
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
return BLOCK_LENGTH_OFFSET[code] + (n <= 16 ? readFewBits(s, n) : readManyBits(s, n));
|
|||
|
|
}
|
|||
|
|
function moveToFront(v, index) {
|
|||
|
|
let i = index;
|
|||
|
|
const value = v[i];
|
|||
|
|
while (i > 0) {
|
|||
|
|
v[i] = v[i - 1];
|
|||
|
|
i--;
|
|||
|
|
}
|
|||
|
|
v[0] = value;
|
|||
|
|
}
|
|||
|
|
function inverseMoveToFrontTransform(v, vLen) {
|
|||
|
|
const mtf = new Int32Array(256);
|
|||
|
|
for (let i = 0; i < 256; ++i) mtf[i] = i;
|
|||
|
|
for (let i = 0; i < vLen; ++i) {
|
|||
|
|
const index = v[i] & 255;
|
|||
|
|
v[i] = mtf[index];
|
|||
|
|
if (index !== 0) moveToFront(mtf, index);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function readHuffmanCodeLengths(codeLengthCodeLengths, numSymbols, codeLengths, s) {
|
|||
|
|
let symbol = 0;
|
|||
|
|
let prevCodeLen = 8;
|
|||
|
|
let repeat = 0;
|
|||
|
|
let repeatCodeLen = 0;
|
|||
|
|
let space = 32768;
|
|||
|
|
const table = new Int32Array(33);
|
|||
|
|
buildHuffmanTable(table, table.length - 1, 5, codeLengthCodeLengths, 18);
|
|||
|
|
while (symbol < numSymbols && space > 0) {
|
|||
|
|
if (s.halfOffset > 2030) {
|
|||
|
|
const result = readMoreInput(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
}
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
const p = s.accumulator32 >>> s.bitOffset & 31;
|
|||
|
|
s.bitOffset += table[p] >> 16;
|
|||
|
|
const codeLen = table[p] & 65535;
|
|||
|
|
if (codeLen < 16) {
|
|||
|
|
repeat = 0;
|
|||
|
|
codeLengths[symbol++] = codeLen;
|
|||
|
|
if (codeLen !== 0) {
|
|||
|
|
prevCodeLen = codeLen;
|
|||
|
|
space -= 32768 >> codeLen;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
const extraBits = codeLen - 14;
|
|||
|
|
let newLen = 0;
|
|||
|
|
if (codeLen === 16) newLen = prevCodeLen;
|
|||
|
|
if (repeatCodeLen !== newLen) {
|
|||
|
|
repeat = 0;
|
|||
|
|
repeatCodeLen = newLen;
|
|||
|
|
}
|
|||
|
|
const oldRepeat = repeat;
|
|||
|
|
if (repeat > 0) {
|
|||
|
|
repeat -= 2;
|
|||
|
|
repeat = repeat << extraBits;
|
|||
|
|
}
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
repeat += readFewBits(s, extraBits) + 3;
|
|||
|
|
const repeatDelta = repeat - oldRepeat;
|
|||
|
|
if (symbol + repeatDelta > numSymbols) return makeError(s, -2);
|
|||
|
|
for (let i = 0; i < repeatDelta; ++i) codeLengths[symbol++] = repeatCodeLen;
|
|||
|
|
if (repeatCodeLen !== 0) space -= repeatDelta << 15 - repeatCodeLen;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (space !== 0) return makeError(s, -18);
|
|||
|
|
codeLengths.fill(0, symbol, numSymbols);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function checkDupes(s, symbols, length) {
|
|||
|
|
for (let i = 0; i < length - 1; ++i) for (let j = i + 1; j < length; ++j) if (symbols[i] === symbols[j]) return makeError(s, -7);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function readSimpleHuffmanCode(alphabetSizeMax, alphabetSizeLimit, tableGroup, tableIdx, s) {
|
|||
|
|
const codeLengths = new Int32Array(alphabetSizeLimit);
|
|||
|
|
const symbols = new Int32Array(4);
|
|||
|
|
const maxBits = 1 + log2floor(alphabetSizeMax - 1);
|
|||
|
|
const numSymbols = readFewBits(s, 2) + 1;
|
|||
|
|
for (let i = 0; i < numSymbols; ++i) {
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
const symbol = readFewBits(s, maxBits);
|
|||
|
|
if (symbol >= alphabetSizeLimit) return makeError(s, -15);
|
|||
|
|
symbols[i] = symbol;
|
|||
|
|
}
|
|||
|
|
const result = checkDupes(s, symbols, numSymbols);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
let histogramId = numSymbols;
|
|||
|
|
if (numSymbols === 4) histogramId += readFewBits(s, 1);
|
|||
|
|
switch (histogramId) {
|
|||
|
|
case 1:
|
|||
|
|
codeLengths[symbols[0]] = 1;
|
|||
|
|
break;
|
|||
|
|
case 2:
|
|||
|
|
codeLengths[symbols[0]] = 1;
|
|||
|
|
codeLengths[symbols[1]] = 1;
|
|||
|
|
break;
|
|||
|
|
case 3:
|
|||
|
|
codeLengths[symbols[0]] = 1;
|
|||
|
|
codeLengths[symbols[1]] = 2;
|
|||
|
|
codeLengths[symbols[2]] = 2;
|
|||
|
|
break;
|
|||
|
|
case 4:
|
|||
|
|
codeLengths[symbols[0]] = 2;
|
|||
|
|
codeLengths[symbols[1]] = 2;
|
|||
|
|
codeLengths[symbols[2]] = 2;
|
|||
|
|
codeLengths[symbols[3]] = 2;
|
|||
|
|
break;
|
|||
|
|
case 5:
|
|||
|
|
codeLengths[symbols[0]] = 1;
|
|||
|
|
codeLengths[symbols[1]] = 2;
|
|||
|
|
codeLengths[symbols[2]] = 3;
|
|||
|
|
codeLengths[symbols[3]] = 3;
|
|||
|
|
break;
|
|||
|
|
default: break;
|
|||
|
|
}
|
|||
|
|
return buildHuffmanTable(tableGroup, tableIdx, 8, codeLengths, alphabetSizeLimit);
|
|||
|
|
}
|
|||
|
|
function readComplexHuffmanCode(alphabetSizeLimit, skip, tableGroup, tableIdx, s) {
|
|||
|
|
const codeLengths = new Int32Array(alphabetSizeLimit);
|
|||
|
|
const codeLengthCodeLengths = new Int32Array(18);
|
|||
|
|
let space = 32;
|
|||
|
|
let numCodes = 0;
|
|||
|
|
for (let i = skip; i < 18; ++i) {
|
|||
|
|
const codeLenIdx = CODE_LENGTH_CODE_ORDER[i];
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
const p = s.accumulator32 >>> s.bitOffset & 15;
|
|||
|
|
s.bitOffset += FIXED_TABLE[p] >> 16;
|
|||
|
|
const v = FIXED_TABLE[p] & 65535;
|
|||
|
|
codeLengthCodeLengths[codeLenIdx] = v;
|
|||
|
|
if (v !== 0) {
|
|||
|
|
space -= 32 >> v;
|
|||
|
|
numCodes++;
|
|||
|
|
if (space <= 0) break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (space !== 0 && numCodes !== 1) return makeError(s, -4);
|
|||
|
|
const result = readHuffmanCodeLengths(codeLengthCodeLengths, alphabetSizeLimit, codeLengths, s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
return buildHuffmanTable(tableGroup, tableIdx, 8, codeLengths, alphabetSizeLimit);
|
|||
|
|
}
|
|||
|
|
function readHuffmanCode(alphabetSizeMax, alphabetSizeLimit, tableGroup, tableIdx, s) {
|
|||
|
|
if (s.halfOffset > 2030) {
|
|||
|
|
const result = readMoreInput(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
}
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
const simpleCodeOrSkip = readFewBits(s, 2);
|
|||
|
|
if (simpleCodeOrSkip === 1) return readSimpleHuffmanCode(alphabetSizeMax, alphabetSizeLimit, tableGroup, tableIdx, s);
|
|||
|
|
return readComplexHuffmanCode(alphabetSizeLimit, simpleCodeOrSkip, tableGroup, tableIdx, s);
|
|||
|
|
}
|
|||
|
|
function decodeContextMap(contextMapSize, contextMap, s) {
|
|||
|
|
let result;
|
|||
|
|
if (s.halfOffset > 2030) {
|
|||
|
|
result = readMoreInput(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
}
|
|||
|
|
const numTrees = decodeVarLenUnsignedByte(s) + 1;
|
|||
|
|
if (numTrees === 1) {
|
|||
|
|
contextMap.fill(0, 0, contextMapSize);
|
|||
|
|
return numTrees;
|
|||
|
|
}
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
const useRleForZeros = readFewBits(s, 1);
|
|||
|
|
let maxRunLengthPrefix = 0;
|
|||
|
|
if (useRleForZeros !== 0) maxRunLengthPrefix = readFewBits(s, 4) + 1;
|
|||
|
|
const alphabetSize = numTrees + maxRunLengthPrefix;
|
|||
|
|
const tableSize = MAX_HUFFMAN_TABLE_SIZE[alphabetSize + 31 >> 5];
|
|||
|
|
const table = new Int32Array(tableSize + 1);
|
|||
|
|
const tableIdx = table.length - 1;
|
|||
|
|
result = readHuffmanCode(alphabetSize, alphabetSize, table, tableIdx, s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
let i = 0;
|
|||
|
|
while (i < contextMapSize) {
|
|||
|
|
if (s.halfOffset > 2030) {
|
|||
|
|
result = readMoreInput(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
}
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
const code = readSymbol(table, tableIdx, s);
|
|||
|
|
if (code === 0) {
|
|||
|
|
contextMap[i] = 0;
|
|||
|
|
i++;
|
|||
|
|
} else if (code <= maxRunLengthPrefix) {
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
let reps = (1 << code) + readFewBits(s, code);
|
|||
|
|
while (reps !== 0) {
|
|||
|
|
if (i >= contextMapSize) return makeError(s, -3);
|
|||
|
|
contextMap[i] = 0;
|
|||
|
|
i++;
|
|||
|
|
reps--;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
contextMap[i] = code - maxRunLengthPrefix;
|
|||
|
|
i++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
if (readFewBits(s, 1) === 1) inverseMoveToFrontTransform(contextMap, contextMapSize);
|
|||
|
|
return numTrees;
|
|||
|
|
}
|
|||
|
|
function decodeBlockTypeAndLength(s, treeType, numBlockTypes) {
|
|||
|
|
const ringBuffers = s.rings;
|
|||
|
|
const offset = 4 + treeType * 2;
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
let blockType = readSymbol(s.blockTrees, 2 * treeType, s);
|
|||
|
|
const result = readBlockLength(s.blockTrees, 2 * treeType + 1, s);
|
|||
|
|
if (blockType === 1) blockType = ringBuffers[offset + 1] + 1;
|
|||
|
|
else if (blockType === 0) blockType = ringBuffers[offset];
|
|||
|
|
else blockType -= 2;
|
|||
|
|
if (blockType >= numBlockTypes) blockType -= numBlockTypes;
|
|||
|
|
ringBuffers[offset] = ringBuffers[offset + 1];
|
|||
|
|
ringBuffers[offset + 1] = blockType;
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
function decodeLiteralBlockSwitch(s) {
|
|||
|
|
s.literalBlockLength = decodeBlockTypeAndLength(s, 0, s.numLiteralBlockTypes);
|
|||
|
|
const literalBlockType = s.rings[5];
|
|||
|
|
s.contextMapSlice = literalBlockType << 6;
|
|||
|
|
s.literalTreeIdx = s.contextMap[s.contextMapSlice] & 255;
|
|||
|
|
s.contextLookupOffset1 = s.contextModes[literalBlockType] << 9;
|
|||
|
|
s.contextLookupOffset2 = s.contextLookupOffset1 + 256;
|
|||
|
|
}
|
|||
|
|
function decodeCommandBlockSwitch(s) {
|
|||
|
|
s.commandBlockLength = decodeBlockTypeAndLength(s, 1, s.numCommandBlockTypes);
|
|||
|
|
s.commandTreeIdx = s.rings[7];
|
|||
|
|
}
|
|||
|
|
function decodeDistanceBlockSwitch(s) {
|
|||
|
|
s.distanceBlockLength = decodeBlockTypeAndLength(s, 2, s.numDistanceBlockTypes);
|
|||
|
|
s.distContextMapSlice = s.rings[9] << 2;
|
|||
|
|
}
|
|||
|
|
function maybeReallocateRingBuffer(s) {
|
|||
|
|
let newSize = s.maxRingBufferSize;
|
|||
|
|
if (newSize > s.expectedTotalSize) {
|
|||
|
|
const minimalNewSize = s.expectedTotalSize;
|
|||
|
|
while (newSize >> 1 > minimalNewSize) newSize = newSize >> 1;
|
|||
|
|
if (s.inputEnd === 0 && newSize < 16384 && s.maxRingBufferSize >= 16384) newSize = 16384;
|
|||
|
|
}
|
|||
|
|
if (newSize <= s.ringBufferSize) return;
|
|||
|
|
const ringBufferSizeWithSlack = newSize + 37;
|
|||
|
|
const newBuffer = new Int8Array(ringBufferSizeWithSlack);
|
|||
|
|
const oldBuffer = s.ringBuffer;
|
|||
|
|
if (oldBuffer.length !== 0) newBuffer.set(oldBuffer.subarray(0, s.ringBufferSize), 0);
|
|||
|
|
s.ringBuffer = newBuffer;
|
|||
|
|
s.ringBufferSize = newSize;
|
|||
|
|
}
|
|||
|
|
function readNextMetablockHeader(s) {
|
|||
|
|
if (s.inputEnd !== 0) {
|
|||
|
|
s.nextRunningState = 10;
|
|||
|
|
s.runningState = 12;
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
s.literalTreeGroup = new Int32Array(0);
|
|||
|
|
s.commandTreeGroup = new Int32Array(0);
|
|||
|
|
s.distanceTreeGroup = new Int32Array(0);
|
|||
|
|
let result;
|
|||
|
|
if (s.halfOffset > 2030) {
|
|||
|
|
result = readMoreInput(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
}
|
|||
|
|
result = decodeMetaBlockLength(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
if (s.metaBlockLength === 0 && s.isMetadata === 0) return 0;
|
|||
|
|
if (s.isUncompressed !== 0 || s.isMetadata !== 0) {
|
|||
|
|
result = jumpToByteBoundary(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
if (s.isMetadata === 0) s.runningState = 6;
|
|||
|
|
else s.runningState = 5;
|
|||
|
|
} else s.runningState = 3;
|
|||
|
|
if (s.isMetadata !== 0) return 0;
|
|||
|
|
s.expectedTotalSize += s.metaBlockLength;
|
|||
|
|
if (s.expectedTotalSize > 1 << 30) s.expectedTotalSize = 1 << 30;
|
|||
|
|
if (s.ringBufferSize < s.maxRingBufferSize) maybeReallocateRingBuffer(s);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function readMetablockPartition(s, treeType, numBlockTypes) {
|
|||
|
|
let offset = s.blockTrees[2 * treeType];
|
|||
|
|
if (numBlockTypes <= 1) {
|
|||
|
|
s.blockTrees[2 * treeType + 1] = offset;
|
|||
|
|
s.blockTrees[2 * treeType + 2] = offset;
|
|||
|
|
return 1 << 28;
|
|||
|
|
}
|
|||
|
|
const blockTypeAlphabetSize = numBlockTypes + 2;
|
|||
|
|
let result = readHuffmanCode(blockTypeAlphabetSize, blockTypeAlphabetSize, s.blockTrees, 2 * treeType, s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
offset += result;
|
|||
|
|
s.blockTrees[2 * treeType + 1] = offset;
|
|||
|
|
const blockLengthAlphabetSize = 26;
|
|||
|
|
result = readHuffmanCode(blockLengthAlphabetSize, blockLengthAlphabetSize, s.blockTrees, 2 * treeType + 1, s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
offset += result;
|
|||
|
|
s.blockTrees[2 * treeType + 2] = offset;
|
|||
|
|
return readBlockLength(s.blockTrees, 2 * treeType + 1, s);
|
|||
|
|
}
|
|||
|
|
function calculateDistanceLut(s, alphabetSizeLimit) {
|
|||
|
|
const distExtraBits = s.distExtraBits;
|
|||
|
|
const distOffset = s.distOffset;
|
|||
|
|
const npostfix = s.distancePostfixBits;
|
|||
|
|
const ndirect = s.numDirectDistanceCodes;
|
|||
|
|
const postfix = 1 << npostfix;
|
|||
|
|
let bits = 1;
|
|||
|
|
let half = 0;
|
|||
|
|
let i = 16;
|
|||
|
|
for (let j = 0; j < ndirect; ++j) {
|
|||
|
|
distExtraBits[i] = 0;
|
|||
|
|
distOffset[i] = j + 1;
|
|||
|
|
++i;
|
|||
|
|
}
|
|||
|
|
while (i < alphabetSizeLimit) {
|
|||
|
|
const base = ndirect + ((2 + half << bits) - 4 << npostfix) + 1;
|
|||
|
|
for (let j = 0; j < postfix; ++j) {
|
|||
|
|
distExtraBits[i] = bits;
|
|||
|
|
distOffset[i] = base + j;
|
|||
|
|
++i;
|
|||
|
|
}
|
|||
|
|
bits = bits + half;
|
|||
|
|
half = half ^ 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function readMetablockHuffmanCodesAndContextMaps(s) {
|
|||
|
|
s.numLiteralBlockTypes = decodeVarLenUnsignedByte(s) + 1;
|
|||
|
|
let result = readMetablockPartition(s, 0, s.numLiteralBlockTypes);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
s.literalBlockLength = result;
|
|||
|
|
s.numCommandBlockTypes = decodeVarLenUnsignedByte(s) + 1;
|
|||
|
|
result = readMetablockPartition(s, 1, s.numCommandBlockTypes);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
s.commandBlockLength = result;
|
|||
|
|
s.numDistanceBlockTypes = decodeVarLenUnsignedByte(s) + 1;
|
|||
|
|
result = readMetablockPartition(s, 2, s.numDistanceBlockTypes);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
s.distanceBlockLength = result;
|
|||
|
|
if (s.halfOffset > 2030) {
|
|||
|
|
result = readMoreInput(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
}
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
s.distancePostfixBits = readFewBits(s, 2);
|
|||
|
|
s.numDirectDistanceCodes = readFewBits(s, 4) << s.distancePostfixBits;
|
|||
|
|
s.contextModes = new Int8Array(s.numLiteralBlockTypes);
|
|||
|
|
let i = 0;
|
|||
|
|
while (i < s.numLiteralBlockTypes) {
|
|||
|
|
const limit = Math.min(i + 96, s.numLiteralBlockTypes);
|
|||
|
|
while (i < limit) {
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
s.contextModes[i] = readFewBits(s, 2);
|
|||
|
|
i++;
|
|||
|
|
}
|
|||
|
|
if (s.halfOffset > 2030) {
|
|||
|
|
result = readMoreInput(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
const contextMapLength = s.numLiteralBlockTypes << 6;
|
|||
|
|
s.contextMap = new Int8Array(contextMapLength);
|
|||
|
|
result = decodeContextMap(contextMapLength, s.contextMap, s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
const numLiteralTrees = result;
|
|||
|
|
s.trivialLiteralContext = 1;
|
|||
|
|
for (let j = 0; j < contextMapLength; ++j) if (s.contextMap[j] !== j >> 6) {
|
|||
|
|
s.trivialLiteralContext = 0;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
s.distContextMap = new Int8Array(s.numDistanceBlockTypes << 2);
|
|||
|
|
result = decodeContextMap(s.numDistanceBlockTypes << 2, s.distContextMap, s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
const numDistTrees = result;
|
|||
|
|
s.literalTreeGroup = new Int32Array(huffmanTreeGroupAllocSize(256, numLiteralTrees));
|
|||
|
|
result = decodeHuffmanTreeGroup(256, 256, numLiteralTrees, s, s.literalTreeGroup);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
s.commandTreeGroup = new Int32Array(huffmanTreeGroupAllocSize(704, s.numCommandBlockTypes));
|
|||
|
|
result = decodeHuffmanTreeGroup(704, 704, s.numCommandBlockTypes, s, s.commandTreeGroup);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
let distanceAlphabetSizeMax = calculateDistanceAlphabetSize(s.distancePostfixBits, s.numDirectDistanceCodes, 24);
|
|||
|
|
let distanceAlphabetSizeLimit = distanceAlphabetSizeMax;
|
|||
|
|
if (s.isLargeWindow === 1) {
|
|||
|
|
distanceAlphabetSizeMax = calculateDistanceAlphabetSize(s.distancePostfixBits, s.numDirectDistanceCodes, 62);
|
|||
|
|
result = calculateDistanceAlphabetLimit(s, 2147483644, s.distancePostfixBits, s.numDirectDistanceCodes);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
distanceAlphabetSizeLimit = result;
|
|||
|
|
}
|
|||
|
|
s.distanceTreeGroup = new Int32Array(huffmanTreeGroupAllocSize(distanceAlphabetSizeLimit, numDistTrees));
|
|||
|
|
result = decodeHuffmanTreeGroup(distanceAlphabetSizeMax, distanceAlphabetSizeLimit, numDistTrees, s, s.distanceTreeGroup);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
calculateDistanceLut(s, distanceAlphabetSizeLimit);
|
|||
|
|
s.contextMapSlice = 0;
|
|||
|
|
s.distContextMapSlice = 0;
|
|||
|
|
s.contextLookupOffset1 = s.contextModes[0] * 512;
|
|||
|
|
s.contextLookupOffset2 = s.contextLookupOffset1 + 256;
|
|||
|
|
s.literalTreeIdx = 0;
|
|||
|
|
s.commandTreeIdx = 0;
|
|||
|
|
s.rings[4] = 1;
|
|||
|
|
s.rings[5] = 0;
|
|||
|
|
s.rings[6] = 1;
|
|||
|
|
s.rings[7] = 0;
|
|||
|
|
s.rings[8] = 1;
|
|||
|
|
s.rings[9] = 0;
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function copyUncompressedData(s) {
|
|||
|
|
throw new Error("copyUncompressedData not implemented");
|
|||
|
|
}
|
|||
|
|
function writeRingBuffer(s) {
|
|||
|
|
const toWrite = Math.min(s.outputLength - s.outputUsed, s.ringBufferBytesReady - s.ringBufferBytesWritten);
|
|||
|
|
if (toWrite !== 0) {
|
|||
|
|
s.output.set(s.ringBuffer.subarray(s.ringBufferBytesWritten, s.ringBufferBytesWritten + toWrite), s.outputOffset + s.outputUsed);
|
|||
|
|
s.outputUsed += toWrite;
|
|||
|
|
s.ringBufferBytesWritten += toWrite;
|
|||
|
|
}
|
|||
|
|
if (s.outputUsed < s.outputLength) return 0;
|
|||
|
|
return 2;
|
|||
|
|
}
|
|||
|
|
function huffmanTreeGroupAllocSize(alphabetSizeLimit, n) {
|
|||
|
|
return n + n * MAX_HUFFMAN_TABLE_SIZE[alphabetSizeLimit + 31 >> 5];
|
|||
|
|
}
|
|||
|
|
function decodeHuffmanTreeGroup(alphabetSizeMax, alphabetSizeLimit, n, s, group) {
|
|||
|
|
let next = n;
|
|||
|
|
for (let i = 0; i < n; ++i) {
|
|||
|
|
group[i] = next;
|
|||
|
|
const result = readHuffmanCode(alphabetSizeMax, alphabetSizeLimit, group, i, s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
next += result;
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function calculateFence(s) {
|
|||
|
|
let result = s.ringBufferSize;
|
|||
|
|
if (s.isEager !== 0) result = Math.min(result, s.ringBufferBytesWritten + s.outputLength - s.outputUsed);
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
function doUseDictionary(s, fence) {
|
|||
|
|
if (s.distance > 2147483644) return makeError(s, -9);
|
|||
|
|
const address = s.distance - s.maxDistance - 1 - s.cdTotalSize;
|
|||
|
|
if (address < 0) {
|
|||
|
|
const result = initializeCompoundDictionaryCopy(s, -address - 1, s.copyLength);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
s.runningState = 14;
|
|||
|
|
} else {
|
|||
|
|
const dictionaryData = data;
|
|||
|
|
const wordLength = s.copyLength;
|
|||
|
|
if (wordLength > 31) return makeError(s, -9);
|
|||
|
|
const shift = sizeBits[wordLength];
|
|||
|
|
if (shift === 0) return makeError(s, -9);
|
|||
|
|
let offset = offsets[wordLength];
|
|||
|
|
const wordIdx = address & (1 << shift) - 1;
|
|||
|
|
const transformIdx = address >> shift;
|
|||
|
|
offset += wordIdx * wordLength;
|
|||
|
|
const transforms = RFC_TRANSFORMS;
|
|||
|
|
if (transformIdx >= transforms.numTransforms) return makeError(s, -9);
|
|||
|
|
const len = transformDictionaryWord(s.ringBuffer, s.pos, dictionaryData, offset, wordLength, transforms, transformIdx);
|
|||
|
|
s.pos += len;
|
|||
|
|
s.metaBlockLength -= len;
|
|||
|
|
if (s.pos >= fence) {
|
|||
|
|
s.nextRunningState = 4;
|
|||
|
|
s.runningState = 12;
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
s.runningState = 4;
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function initializeCompoundDictionaryCopy(s, address, length) {
|
|||
|
|
throw new Error("initializeCompoundDictionaryCopy not implemented");
|
|||
|
|
}
|
|||
|
|
function copyFromCompoundDictionary(s, fence) {
|
|||
|
|
throw new Error("copyFromCompoundDictionary not implemented");
|
|||
|
|
}
|
|||
|
|
function decompress(s) {
|
|||
|
|
let result;
|
|||
|
|
if (s.runningState === 0) return makeError(s, -25);
|
|||
|
|
if (s.runningState < 0) return makeError(s, -28);
|
|||
|
|
if (s.runningState === 11) return makeError(s, -22);
|
|||
|
|
if (s.runningState === 1) {
|
|||
|
|
const windowBits = decodeWindowBits(s);
|
|||
|
|
if (windowBits === -1) return makeError(s, -11);
|
|||
|
|
s.maxRingBufferSize = 1 << windowBits;
|
|||
|
|
s.maxBackwardDistance = s.maxRingBufferSize - 16;
|
|||
|
|
s.runningState = 2;
|
|||
|
|
}
|
|||
|
|
let fence = calculateFence(s);
|
|||
|
|
let ringBufferMask = s.ringBufferSize - 1;
|
|||
|
|
let ringBuffer = s.ringBuffer;
|
|||
|
|
while (s.runningState !== 10) switch (s.runningState) {
|
|||
|
|
case 2:
|
|||
|
|
if (s.metaBlockLength < 0) return makeError(s, -10);
|
|||
|
|
result = readNextMetablockHeader(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
fence = calculateFence(s);
|
|||
|
|
ringBufferMask = s.ringBufferSize - 1;
|
|||
|
|
ringBuffer = s.ringBuffer;
|
|||
|
|
continue;
|
|||
|
|
case 3:
|
|||
|
|
result = readMetablockHuffmanCodesAndContextMaps(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
s.runningState = 4;
|
|||
|
|
continue;
|
|||
|
|
case 4:
|
|||
|
|
if (s.metaBlockLength <= 0) {
|
|||
|
|
s.runningState = 2;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
if (s.halfOffset > 2030) {
|
|||
|
|
result = readMoreInput(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
}
|
|||
|
|
if (s.commandBlockLength === 0) decodeCommandBlockSwitch(s);
|
|||
|
|
s.commandBlockLength--;
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
const cmdCode = readSymbol(s.commandTreeGroup, s.commandTreeIdx, s) << 2;
|
|||
|
|
const insertAndCopyExtraBits = CMD_LOOKUP[cmdCode];
|
|||
|
|
const insertLengthOffset = CMD_LOOKUP[cmdCode + 1];
|
|||
|
|
const copyLengthOffset = CMD_LOOKUP[cmdCode + 2];
|
|||
|
|
s.distanceCode = CMD_LOOKUP[cmdCode + 3];
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
const insertLengthExtraBits = insertAndCopyExtraBits & 255;
|
|||
|
|
s.insertLength = insertLengthOffset + (insertLengthExtraBits <= 16 ? readFewBits(s, insertLengthExtraBits) : readManyBits(s, insertLengthExtraBits));
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
const copyLengthExtraBits = insertAndCopyExtraBits >> 8;
|
|||
|
|
s.copyLength = copyLengthOffset + (copyLengthExtraBits <= 16 ? readFewBits(s, copyLengthExtraBits) : readManyBits(s, copyLengthExtraBits));
|
|||
|
|
s.j = 0;
|
|||
|
|
s.runningState = 7;
|
|||
|
|
continue;
|
|||
|
|
case 7:
|
|||
|
|
if (s.trivialLiteralContext !== 0) while (s.j < s.insertLength) {
|
|||
|
|
if (s.halfOffset > 2030) {
|
|||
|
|
result = readMoreInput(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
}
|
|||
|
|
if (s.literalBlockLength === 0) decodeLiteralBlockSwitch(s);
|
|||
|
|
s.literalBlockLength--;
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
ringBuffer[s.pos] = readSymbol(s.literalTreeGroup, s.literalTreeIdx, s);
|
|||
|
|
s.pos++;
|
|||
|
|
s.j++;
|
|||
|
|
if (s.pos >= fence) {
|
|||
|
|
s.nextRunningState = 7;
|
|||
|
|
s.runningState = 12;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
let prevByte1 = ringBuffer[s.pos - 1 & ringBufferMask] & 255;
|
|||
|
|
let prevByte2 = ringBuffer[s.pos - 2 & ringBufferMask] & 255;
|
|||
|
|
while (s.j < s.insertLength) {
|
|||
|
|
if (s.halfOffset > 2030) {
|
|||
|
|
result = readMoreInput(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
}
|
|||
|
|
if (s.literalBlockLength === 0) decodeLiteralBlockSwitch(s);
|
|||
|
|
const literalContext = LOOKUP[s.contextLookupOffset1 + prevByte1] | LOOKUP[s.contextLookupOffset2 + prevByte2];
|
|||
|
|
const literalTreeIdx = s.contextMap[s.contextMapSlice + literalContext] & 255;
|
|||
|
|
s.literalBlockLength--;
|
|||
|
|
prevByte2 = prevByte1;
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
prevByte1 = readSymbol(s.literalTreeGroup, literalTreeIdx, s);
|
|||
|
|
ringBuffer[s.pos] = prevByte1;
|
|||
|
|
s.pos++;
|
|||
|
|
s.j++;
|
|||
|
|
if (s.pos >= fence) {
|
|||
|
|
s.nextRunningState = 7;
|
|||
|
|
s.runningState = 12;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (s.runningState !== 7) continue;
|
|||
|
|
s.metaBlockLength -= s.insertLength;
|
|||
|
|
if (s.metaBlockLength <= 0) {
|
|||
|
|
s.runningState = 4;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
let distanceCode = s.distanceCode;
|
|||
|
|
if (distanceCode < 0) s.distance = s.rings[s.distRbIdx];
|
|||
|
|
else {
|
|||
|
|
if (s.halfOffset > 2030) {
|
|||
|
|
result = readMoreInput(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
}
|
|||
|
|
if (s.distanceBlockLength === 0) decodeDistanceBlockSwitch(s);
|
|||
|
|
s.distanceBlockLength--;
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
const distTreeIdx = s.distContextMap[s.distContextMapSlice + distanceCode] & 255;
|
|||
|
|
distanceCode = readSymbol(s.distanceTreeGroup, distTreeIdx, s);
|
|||
|
|
if (distanceCode < 16) {
|
|||
|
|
const index = s.distRbIdx + DISTANCE_SHORT_CODE_INDEX_OFFSET[distanceCode] & 3;
|
|||
|
|
s.distance = s.rings[index] + DISTANCE_SHORT_CODE_VALUE_OFFSET[distanceCode];
|
|||
|
|
if (s.distance < 0) return makeError(s, -12);
|
|||
|
|
} else {
|
|||
|
|
const extraBits = s.distExtraBits[distanceCode];
|
|||
|
|
let bits;
|
|||
|
|
if (s.bitOffset + extraBits <= 32) bits = readFewBits(s, extraBits);
|
|||
|
|
else {
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
bits = extraBits <= 16 ? readFewBits(s, extraBits) : readManyBits(s, extraBits);
|
|||
|
|
}
|
|||
|
|
s.distance = s.distOffset[distanceCode] + (bits << s.distancePostfixBits);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (s.maxDistance !== s.maxBackwardDistance && s.pos < s.maxBackwardDistance) s.maxDistance = s.pos;
|
|||
|
|
else s.maxDistance = s.maxBackwardDistance;
|
|||
|
|
if (s.distance > s.maxDistance) {
|
|||
|
|
s.runningState = 9;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
if (distanceCode > 0) {
|
|||
|
|
s.distRbIdx = s.distRbIdx + 1 & 3;
|
|||
|
|
s.rings[s.distRbIdx] = s.distance;
|
|||
|
|
}
|
|||
|
|
if (s.copyLength > s.metaBlockLength) return makeError(s, -9);
|
|||
|
|
s.j = 0;
|
|||
|
|
s.runningState = 8;
|
|||
|
|
continue;
|
|||
|
|
case 8:
|
|||
|
|
let src = s.pos - s.distance & ringBufferMask;
|
|||
|
|
let dst = s.pos;
|
|||
|
|
const copyLength = s.copyLength - s.j;
|
|||
|
|
const srcEnd = src + copyLength;
|
|||
|
|
const dstEnd = dst + copyLength;
|
|||
|
|
if (srcEnd < ringBufferMask && dstEnd < ringBufferMask) {
|
|||
|
|
if (copyLength < 12 || srcEnd > dst && dstEnd > src) {
|
|||
|
|
const numQuads = copyLength + 3 >> 2;
|
|||
|
|
for (let k = 0; k < numQuads; ++k) {
|
|||
|
|
ringBuffer[dst++] = ringBuffer[src++];
|
|||
|
|
ringBuffer[dst++] = ringBuffer[src++];
|
|||
|
|
ringBuffer[dst++] = ringBuffer[src++];
|
|||
|
|
ringBuffer[dst++] = ringBuffer[src++];
|
|||
|
|
}
|
|||
|
|
} else ringBuffer.copyWithin(dst, src, srcEnd);
|
|||
|
|
s.j += copyLength;
|
|||
|
|
s.metaBlockLength -= copyLength;
|
|||
|
|
s.pos += copyLength;
|
|||
|
|
} else while (s.j < s.copyLength) {
|
|||
|
|
ringBuffer[s.pos] = ringBuffer[s.pos - s.distance & ringBufferMask];
|
|||
|
|
s.metaBlockLength--;
|
|||
|
|
s.pos++;
|
|||
|
|
s.j++;
|
|||
|
|
if (s.pos >= fence) {
|
|||
|
|
s.nextRunningState = 8;
|
|||
|
|
s.runningState = 12;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (s.runningState === 8) s.runningState = 4;
|
|||
|
|
continue;
|
|||
|
|
case 9:
|
|||
|
|
result = doUseDictionary(s, fence);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
continue;
|
|||
|
|
case 14:
|
|||
|
|
s.pos += copyFromCompoundDictionary(s, fence);
|
|||
|
|
if (s.pos >= fence) {
|
|||
|
|
s.nextRunningState = 14;
|
|||
|
|
s.runningState = 12;
|
|||
|
|
return 2;
|
|||
|
|
}
|
|||
|
|
s.runningState = 4;
|
|||
|
|
continue;
|
|||
|
|
case 5:
|
|||
|
|
while (s.metaBlockLength > 0) {
|
|||
|
|
if (s.halfOffset > 2030) {
|
|||
|
|
result = readMoreInput(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
}
|
|||
|
|
if (s.bitOffset >= 16) {
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
}
|
|||
|
|
readFewBits(s, 8);
|
|||
|
|
s.metaBlockLength--;
|
|||
|
|
}
|
|||
|
|
s.runningState = 2;
|
|||
|
|
continue;
|
|||
|
|
case 6:
|
|||
|
|
result = copyUncompressedData(s);
|
|||
|
|
if (result < 0) return result;
|
|||
|
|
continue;
|
|||
|
|
case 12:
|
|||
|
|
s.ringBufferBytesReady = Math.min(s.pos, s.ringBufferSize);
|
|||
|
|
s.runningState = 13;
|
|||
|
|
continue;
|
|||
|
|
case 13:
|
|||
|
|
result = writeRingBuffer(s);
|
|||
|
|
if (result !== 0) return result;
|
|||
|
|
if (s.pos >= s.maxBackwardDistance) s.maxDistance = s.maxBackwardDistance;
|
|||
|
|
if (s.pos >= s.ringBufferSize) {
|
|||
|
|
if (s.pos > s.ringBufferSize) ringBuffer.copyWithin(0, s.ringBufferSize, s.pos);
|
|||
|
|
s.pos = s.pos & ringBufferMask;
|
|||
|
|
s.ringBufferBytesWritten = 0;
|
|||
|
|
}
|
|||
|
|
s.runningState = s.nextRunningState;
|
|||
|
|
continue;
|
|||
|
|
default: return makeError(s, -28);
|
|||
|
|
}
|
|||
|
|
if (s.runningState !== 10) return makeError(s, -29);
|
|||
|
|
if (s.metaBlockLength < 0) return makeError(s, -10);
|
|||
|
|
result = jumpToByteBoundary(s);
|
|||
|
|
if (result !== 0) return result;
|
|||
|
|
result = checkHealth(s, 1);
|
|||
|
|
if (result !== 0) return result;
|
|||
|
|
return 1;
|
|||
|
|
}
|
|||
|
|
var Transforms = class {
|
|||
|
|
numTransforms = 0;
|
|||
|
|
triplets = new Int32Array(0);
|
|||
|
|
prefixSuffixStorage = new Int8Array(0);
|
|||
|
|
prefixSuffixHeads = new Int32Array(0);
|
|||
|
|
params = new Int16Array(0);
|
|||
|
|
constructor(numTransforms, prefixSuffixLen, prefixSuffixCount) {
|
|||
|
|
this.numTransforms = numTransforms;
|
|||
|
|
this.triplets = new Int32Array(numTransforms * 3);
|
|||
|
|
this.params = new Int16Array(numTransforms);
|
|||
|
|
this.prefixSuffixStorage = new Int8Array(prefixSuffixLen);
|
|||
|
|
this.prefixSuffixHeads = new Int32Array(prefixSuffixCount + 1);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const RFC_TRANSFORMS = new Transforms(121, 167, 50);
|
|||
|
|
function unpackTransforms(prefixSuffix, prefixSuffixHeads, transforms, prefixSuffixSrc, transformsSrc) {
|
|||
|
|
const prefixSuffixBytes = toUtf8Runes(prefixSuffixSrc);
|
|||
|
|
const n = prefixSuffixBytes.length;
|
|||
|
|
let index = 1;
|
|||
|
|
let j = 0;
|
|||
|
|
for (let i = 0; i < n; ++i) {
|
|||
|
|
const c = prefixSuffixBytes[i];
|
|||
|
|
if (c === 35) prefixSuffixHeads[index++] = j;
|
|||
|
|
else prefixSuffix[j++] = c;
|
|||
|
|
}
|
|||
|
|
for (let i = 0; i < 363; ++i) transforms[i] = transformsSrc.charCodeAt(i) - 32;
|
|||
|
|
}
|
|||
|
|
unpackTransforms(RFC_TRANSFORMS.prefixSuffixStorage, RFC_TRANSFORMS.prefixSuffixHeads, RFC_TRANSFORMS.triplets, "# #s #, #e #.# the #.com/#Â\xA0# of # and # in # to #\"#\">#\n#]# for # a # that #. # with #'# from # by #. The # on # as # is #ing #\n #:#ed #(# at #ly #=\"# of the #. This #,# not #er #al #='#ful #ive #less #est #ize #ous #", " !! ! , *! &! \" ! ) * * - ! # ! #!*! + ,$ ! - % . / # 0 1 . \" 2 3!* 4% ! # / 5 6 7 8 0 1 & $ 9 + : ; < ' != > ?! 4 @ 4 2 & A *# ( B C& ) % ) !*# *-% A +! *. D! %' & E *6 F G% ! *A *% H! D I!+! J!+ K +- *4! A L!*4 M N +6 O!*% +.! K *G P +%( ! G *D +D Q +# *K!*G!+D!+# +G +A +4!+% +K!+4!*D!+K!*K");
|
|||
|
|
function transformDictionaryWord(dst, dstOffset, src, srcOffset, wordLen, transforms, transformIndex) {
|
|||
|
|
let offset = dstOffset;
|
|||
|
|
const triplets = transforms.triplets;
|
|||
|
|
const prefixSuffixStorage = transforms.prefixSuffixStorage;
|
|||
|
|
const prefixSuffixHeads = transforms.prefixSuffixHeads;
|
|||
|
|
const transformOffset = 3 * transformIndex;
|
|||
|
|
const prefixIdx = triplets[transformOffset];
|
|||
|
|
const transformType = triplets[transformOffset + 1];
|
|||
|
|
const suffixIdx = triplets[transformOffset + 2];
|
|||
|
|
let prefix = prefixSuffixHeads[prefixIdx];
|
|||
|
|
const prefixEnd = prefixSuffixHeads[prefixIdx + 1];
|
|||
|
|
let suffix = prefixSuffixHeads[suffixIdx];
|
|||
|
|
const suffixEnd = prefixSuffixHeads[suffixIdx + 1];
|
|||
|
|
let omitFirst = transformType - 11;
|
|||
|
|
let omitLast = transformType;
|
|||
|
|
if (omitFirst < 1 || omitFirst > 9) omitFirst = 0;
|
|||
|
|
if (omitLast < 1 || omitLast > 9) omitLast = 0;
|
|||
|
|
while (prefix !== prefixEnd) dst[offset++] = prefixSuffixStorage[prefix++];
|
|||
|
|
let len = wordLen;
|
|||
|
|
if (omitFirst > len) omitFirst = len;
|
|||
|
|
let dictOffset = srcOffset + omitFirst;
|
|||
|
|
len -= omitFirst;
|
|||
|
|
len -= omitLast;
|
|||
|
|
let i = len;
|
|||
|
|
while (i > 0) {
|
|||
|
|
dst[offset++] = src[dictOffset++];
|
|||
|
|
i--;
|
|||
|
|
}
|
|||
|
|
if (transformType === 10 || transformType === 11) {
|
|||
|
|
let uppercaseOffset = offset - len;
|
|||
|
|
if (transformType === 10) len = 1;
|
|||
|
|
while (len > 0) {
|
|||
|
|
const c0 = dst[uppercaseOffset] & 255;
|
|||
|
|
if (c0 < 192) {
|
|||
|
|
if (c0 >= 97 && c0 <= 122) dst[uppercaseOffset] = dst[uppercaseOffset] ^ 32;
|
|||
|
|
uppercaseOffset += 1;
|
|||
|
|
len -= 1;
|
|||
|
|
} else if (c0 < 224) {
|
|||
|
|
dst[uppercaseOffset + 1] = dst[uppercaseOffset + 1] ^ 32;
|
|||
|
|
uppercaseOffset += 2;
|
|||
|
|
len -= 2;
|
|||
|
|
} else {
|
|||
|
|
dst[uppercaseOffset + 2] = dst[uppercaseOffset + 2] ^ 5;
|
|||
|
|
uppercaseOffset += 3;
|
|||
|
|
len -= 3;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else if (transformType === 21 || transformType === 22) throw new Error("transformDictionaryWord: transformType 21 and 22 not implemented");
|
|||
|
|
while (suffix !== suffixEnd) dst[offset++] = prefixSuffixStorage[suffix++];
|
|||
|
|
return offset - dstOffset;
|
|||
|
|
}
|
|||
|
|
function getNextKey(key, len) {
|
|||
|
|
let step = 1 << len - 1;
|
|||
|
|
while ((key & step) !== 0) step = step >> 1;
|
|||
|
|
return (key & step - 1) + step;
|
|||
|
|
}
|
|||
|
|
function replicateValue(table, offset, step, end, item) {
|
|||
|
|
let pos = end;
|
|||
|
|
while (pos > 0) {
|
|||
|
|
pos -= step;
|
|||
|
|
table[offset + pos] = item;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function nextTableBitSize(count, len, rootBits) {
|
|||
|
|
let bits = len;
|
|||
|
|
let left = 1 << bits - rootBits;
|
|||
|
|
while (bits < 15) {
|
|||
|
|
left -= count[bits];
|
|||
|
|
if (left <= 0) break;
|
|||
|
|
bits++;
|
|||
|
|
left = left << 1;
|
|||
|
|
}
|
|||
|
|
return bits - rootBits;
|
|||
|
|
}
|
|||
|
|
function buildHuffmanTable(tableGroup, tableIdx, rootBits, codeLengths, codeLengthsSize) {
|
|||
|
|
const tableOffset = tableGroup[tableIdx];
|
|||
|
|
const sorted = new Int32Array(codeLengthsSize);
|
|||
|
|
const count = new Int32Array(16);
|
|||
|
|
const offset = new Int32Array(16);
|
|||
|
|
for (let sym = 0; sym < codeLengthsSize; ++sym) count[codeLengths[sym]]++;
|
|||
|
|
offset[1] = 0;
|
|||
|
|
for (let len = 1; len < 15; ++len) offset[len + 1] = offset[len] + count[len];
|
|||
|
|
for (let sym = 0; sym < codeLengthsSize; ++sym) if (codeLengths[sym] !== 0) sorted[offset[codeLengths[sym]]++] = sym;
|
|||
|
|
let tableBits = rootBits;
|
|||
|
|
let tableSize = 1 << tableBits;
|
|||
|
|
let totalSize = tableSize;
|
|||
|
|
if (offset[15] === 1) {
|
|||
|
|
for (let k = 0; k < totalSize; ++k) tableGroup[tableOffset + k] = sorted[0];
|
|||
|
|
return totalSize;
|
|||
|
|
}
|
|||
|
|
let key = 0;
|
|||
|
|
let symbol = 0;
|
|||
|
|
let step = 1;
|
|||
|
|
for (let len = 1; len <= rootBits; ++len) {
|
|||
|
|
step = step << 1;
|
|||
|
|
while (count[len] > 0) {
|
|||
|
|
replicateValue(tableGroup, tableOffset + key, step, tableSize, len << 16 | sorted[symbol++]);
|
|||
|
|
key = getNextKey(key, len);
|
|||
|
|
count[len]--;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
const mask = totalSize - 1;
|
|||
|
|
let low = -1;
|
|||
|
|
let currentOffset = tableOffset;
|
|||
|
|
step = 1;
|
|||
|
|
for (let len = rootBits + 1; len <= 15; ++len) {
|
|||
|
|
step = step << 1;
|
|||
|
|
while (count[len] > 0) {
|
|||
|
|
if ((key & mask) !== low) {
|
|||
|
|
currentOffset += tableSize;
|
|||
|
|
tableBits = nextTableBitSize(count, len, rootBits);
|
|||
|
|
tableSize = 1 << tableBits;
|
|||
|
|
totalSize += tableSize;
|
|||
|
|
low = key & mask;
|
|||
|
|
tableGroup[tableOffset + low] = tableBits + rootBits << 16 | currentOffset - tableOffset - low;
|
|||
|
|
}
|
|||
|
|
replicateValue(tableGroup, currentOffset + (key >> rootBits), step, tableSize, len - rootBits << 16 | sorted[symbol++]);
|
|||
|
|
key = getNextKey(key, len);
|
|||
|
|
count[len]--;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return totalSize;
|
|||
|
|
}
|
|||
|
|
function readMoreInput(s) {
|
|||
|
|
if (s.endOfStreamReached !== 0) {
|
|||
|
|
if (halfAvailable(s) >= -2) return 0;
|
|||
|
|
return makeError(s, -16);
|
|||
|
|
}
|
|||
|
|
const readOffset = s.halfOffset << 1;
|
|||
|
|
let bytesInBuffer = 4096 - readOffset;
|
|||
|
|
s.byteBuffer.copyWithin(0, readOffset, 4096);
|
|||
|
|
s.halfOffset = 0;
|
|||
|
|
while (bytesInBuffer < 4096) {
|
|||
|
|
const spaceLeft = 4096 - bytesInBuffer;
|
|||
|
|
const len = readInput(s, s.byteBuffer, bytesInBuffer, spaceLeft);
|
|||
|
|
if (len < -1) return len;
|
|||
|
|
if (len <= 0) {
|
|||
|
|
s.endOfStreamReached = 1;
|
|||
|
|
s.tailBytes = bytesInBuffer;
|
|||
|
|
bytesInBuffer += 1;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
bytesInBuffer += len;
|
|||
|
|
}
|
|||
|
|
bytesToNibbles(s, bytesInBuffer);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function checkHealth(s, endOfStream) {
|
|||
|
|
if (s.endOfStreamReached === 0) return 0;
|
|||
|
|
const byteOffset = (s.halfOffset << 1) + (s.bitOffset + 7 >> 3) - 4;
|
|||
|
|
if (byteOffset > s.tailBytes) return makeError(s, -13);
|
|||
|
|
if (endOfStream !== 0 && byteOffset !== s.tailBytes) return makeError(s, -17);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function readFewBits(s, n) {
|
|||
|
|
const v = s.accumulator32 >>> s.bitOffset & (1 << n) - 1;
|
|||
|
|
s.bitOffset += n;
|
|||
|
|
return v;
|
|||
|
|
}
|
|||
|
|
function readManyBits(s, n) {
|
|||
|
|
const low = readFewBits(s, 16);
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
return low | readFewBits(s, n - 16) << 16;
|
|||
|
|
}
|
|||
|
|
function initBitReader(s) {
|
|||
|
|
s.byteBuffer = new Int8Array(4160);
|
|||
|
|
s.accumulator32 = 0;
|
|||
|
|
s.shortBuffer = new Int16Array(2080);
|
|||
|
|
s.bitOffset = 32;
|
|||
|
|
s.halfOffset = 2048;
|
|||
|
|
s.endOfStreamReached = 0;
|
|||
|
|
return prepare(s);
|
|||
|
|
}
|
|||
|
|
function prepare(s) {
|
|||
|
|
if (s.halfOffset > 2030) {
|
|||
|
|
const result = readMoreInput(s);
|
|||
|
|
if (result !== 0) return result;
|
|||
|
|
}
|
|||
|
|
let health = checkHealth(s, 0);
|
|||
|
|
if (health !== 0) return health;
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
s.accumulator32 = s.shortBuffer[s.halfOffset++] << 16 | s.accumulator32 >>> 16;
|
|||
|
|
s.bitOffset -= 16;
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function jumpToByteBoundary(s) {
|
|||
|
|
const padding = 32 - s.bitOffset & 7;
|
|||
|
|
if (padding !== 0) {
|
|||
|
|
if (readFewBits(s, padding) !== 0) return makeError(s, -5);
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
function halfAvailable(s) {
|
|||
|
|
let limit = 2048;
|
|||
|
|
if (s.endOfStreamReached !== 0) limit = s.tailBytes + 1 >> 1;
|
|||
|
|
return limit - s.halfOffset;
|
|||
|
|
}
|
|||
|
|
function bytesToNibbles(s, byteLen) {
|
|||
|
|
const byteBuffer = s.byteBuffer;
|
|||
|
|
const halfLen = byteLen >> 1;
|
|||
|
|
const shortBuffer = s.shortBuffer;
|
|||
|
|
for (let i = 0; i < halfLen; ++i) shortBuffer[i] = byteBuffer[i * 2] & 255 | (byteBuffer[i * 2 + 1] & 255) << 8;
|
|||
|
|
}
|
|||
|
|
const LOOKUP = new Int32Array(2048);
|
|||
|
|
function unpackLookupTable(lookup, utfMap, utfRle) {
|
|||
|
|
for (let i = 0; i < 256; ++i) {
|
|||
|
|
lookup[i] = i & 63;
|
|||
|
|
lookup[512 + i] = i >> 2;
|
|||
|
|
lookup[1792 + i] = 2 + (i >> 6);
|
|||
|
|
}
|
|||
|
|
for (let i = 0; i < 128; ++i) lookup[1024 + i] = 4 * (utfMap.charCodeAt(i) - 32);
|
|||
|
|
for (let i = 0; i < 64; ++i) {
|
|||
|
|
lookup[1152 + i] = i & 1;
|
|||
|
|
lookup[1216 + i] = 2 + (i & 1);
|
|||
|
|
}
|
|||
|
|
let offset = 1280;
|
|||
|
|
for (let k = 0; k < 19; ++k) {
|
|||
|
|
const value = k & 3;
|
|||
|
|
const rep = utfRle.charCodeAt(k) - 32;
|
|||
|
|
for (let i = 0; i < rep; ++i) lookup[offset++] = value;
|
|||
|
|
}
|
|||
|
|
for (let i = 0; i < 16; ++i) {
|
|||
|
|
lookup[1792 + i] = 1;
|
|||
|
|
lookup[2032 + i] = 6;
|
|||
|
|
}
|
|||
|
|
lookup[1792] = 0;
|
|||
|
|
lookup[2047] = 7;
|
|||
|
|
for (let i = 0; i < 256; ++i) lookup[1536 + i] = lookup[1792 + i] << 3;
|
|||
|
|
}
|
|||
|
|
unpackLookupTable(LOOKUP, " !! ! \"#$##%#$&'##(#)#++++++++++((&*'##,---,---,-----,-----,-----&#'###.///.///./////./////./////&#'# ", "A/* ': & : $ @");
|
|||
|
|
var State = class {
|
|||
|
|
ringBuffer = new Int8Array(0);
|
|||
|
|
contextModes = new Int8Array(0);
|
|||
|
|
contextMap = new Int8Array(0);
|
|||
|
|
distContextMap = new Int8Array(0);
|
|||
|
|
distExtraBits = new Int8Array(0);
|
|||
|
|
output = new Int8Array(0);
|
|||
|
|
byteBuffer = new Int8Array(0);
|
|||
|
|
shortBuffer = new Int16Array(0);
|
|||
|
|
intBuffer = new Int32Array(0);
|
|||
|
|
rings = new Int32Array(0);
|
|||
|
|
blockTrees = new Int32Array(0);
|
|||
|
|
literalTreeGroup = new Int32Array(0);
|
|||
|
|
commandTreeGroup = new Int32Array(0);
|
|||
|
|
distanceTreeGroup = new Int32Array(0);
|
|||
|
|
distOffset = new Int32Array(0);
|
|||
|
|
accumulator64 = 0;
|
|||
|
|
runningState = 0;
|
|||
|
|
nextRunningState = 0;
|
|||
|
|
accumulator32 = 0;
|
|||
|
|
bitOffset = 0;
|
|||
|
|
halfOffset = 0;
|
|||
|
|
tailBytes = 0;
|
|||
|
|
endOfStreamReached = 0;
|
|||
|
|
metaBlockLength = 0;
|
|||
|
|
inputEnd = 0;
|
|||
|
|
isUncompressed = 0;
|
|||
|
|
isMetadata = 0;
|
|||
|
|
literalBlockLength = 0;
|
|||
|
|
numLiteralBlockTypes = 0;
|
|||
|
|
commandBlockLength = 0;
|
|||
|
|
numCommandBlockTypes = 0;
|
|||
|
|
distanceBlockLength = 0;
|
|||
|
|
numDistanceBlockTypes = 0;
|
|||
|
|
pos = 0;
|
|||
|
|
maxDistance = 0;
|
|||
|
|
distRbIdx = 0;
|
|||
|
|
trivialLiteralContext = 0;
|
|||
|
|
literalTreeIdx = 0;
|
|||
|
|
commandTreeIdx = 0;
|
|||
|
|
j = 0;
|
|||
|
|
insertLength = 0;
|
|||
|
|
contextMapSlice = 0;
|
|||
|
|
distContextMapSlice = 0;
|
|||
|
|
contextLookupOffset1 = 0;
|
|||
|
|
contextLookupOffset2 = 0;
|
|||
|
|
distanceCode = 0;
|
|||
|
|
numDirectDistanceCodes = 0;
|
|||
|
|
distancePostfixBits = 0;
|
|||
|
|
distance = 0;
|
|||
|
|
copyLength = 0;
|
|||
|
|
maxBackwardDistance = 0;
|
|||
|
|
maxRingBufferSize = 0;
|
|||
|
|
ringBufferSize = 0;
|
|||
|
|
expectedTotalSize = 0;
|
|||
|
|
outputOffset = 0;
|
|||
|
|
outputLength = 0;
|
|||
|
|
outputUsed = 0;
|
|||
|
|
ringBufferBytesWritten = 0;
|
|||
|
|
ringBufferBytesReady = 0;
|
|||
|
|
isEager = 0;
|
|||
|
|
isLargeWindow = 0;
|
|||
|
|
cdNumChunks = 0;
|
|||
|
|
cdTotalSize = 0;
|
|||
|
|
cdBrIndex = 0;
|
|||
|
|
cdBrOffset = 0;
|
|||
|
|
cdBrLength = 0;
|
|||
|
|
cdBrCopied = 0;
|
|||
|
|
cdChunks = new Array(0);
|
|||
|
|
cdChunkOffsets = new Int32Array(0);
|
|||
|
|
cdBlockBits = 0;
|
|||
|
|
cdBlockMap = new Int8Array(0);
|
|||
|
|
input = new InputStream(new Int8Array(0));
|
|||
|
|
constructor() {
|
|||
|
|
this.ringBuffer = new Int8Array(0);
|
|||
|
|
this.rings = new Int32Array(10);
|
|||
|
|
this.rings[0] = 16;
|
|||
|
|
this.rings[1] = 15;
|
|||
|
|
this.rings[2] = 11;
|
|||
|
|
this.rings[3] = 4;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
let data = new Int8Array(0);
|
|||
|
|
const offsets = new Int32Array(32);
|
|||
|
|
const sizeBits = new Int32Array(32);
|
|||
|
|
function setData(newData, newSizeBits) {
|
|||
|
|
const dictionaryOffsets = offsets;
|
|||
|
|
const dictionarySizeBits = sizeBits;
|
|||
|
|
for (let i = 0; i < newSizeBits.length; ++i) dictionarySizeBits[i] = newSizeBits[i];
|
|||
|
|
let pos = 0;
|
|||
|
|
for (let i = 0; i < newSizeBits.length; ++i) {
|
|||
|
|
dictionaryOffsets[i] = pos;
|
|||
|
|
const bits = dictionarySizeBits[i];
|
|||
|
|
if (bits !== 0) pos += i << (bits & 31);
|
|||
|
|
}
|
|||
|
|
for (let i = newSizeBits.length; i < 32; ++i) dictionaryOffsets[i] = pos;
|
|||
|
|
data = newData;
|
|||
|
|
}
|
|||
|
|
function unpackDictionaryData(dictionary, data0, data1, skipFlip, sizeBits, sizeBitsData) {
|
|||
|
|
const dict = toUsAsciiBytes(data0 + data1);
|
|||
|
|
const skipFlipRunes = toUtf8Runes(skipFlip);
|
|||
|
|
let offset = 0;
|
|||
|
|
const n = skipFlipRunes.length >> 1;
|
|||
|
|
for (let i = 0; i < n; ++i) {
|
|||
|
|
const skip = skipFlipRunes[2 * i] - 36;
|
|||
|
|
const flip = skipFlipRunes[2 * i + 1] - 36;
|
|||
|
|
for (let j = 0; j < skip; ++j) {
|
|||
|
|
dict[offset] = dict[offset] ^ 3;
|
|||
|
|
offset++;
|
|||
|
|
}
|
|||
|
|
for (let j = 0; j < flip; ++j) {
|
|||
|
|
dict[offset] = dict[offset] ^ 236;
|
|||
|
|
offset++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
for (let i = 0; i < sizeBitsData.length; ++i) sizeBits[i] = sizeBitsData.charCodeAt(i) - 65;
|
|||
|
|
dictionary.set(dict);
|
|||
|
|
}
|
|||
|
|
{
|
|||
|
|
const dictionaryData = new Int8Array(122784);
|
|||
|
|
const dictionarySizeBits = new Int32Array(25);
|
|||
|
|
unpackDictionaryData(dictionaryData, "wjnfgltmojefofewab`h`lgfgbwbpkltlmozpjwf`jwzlsfmivpwojhfeqfftlqhwf{wzfbqlufqalgzolufelqnallhsobzojufojmfkfosklnfpjgfnlqftlqgolmdwkfnujftejmgsbdfgbzpevookfbgwfqnfb`kbqfbeqlnwqvfnbqhbaofvslmkjdkgbwfobmgmftpfufmmf{w`bpfalwkslpwvpfgnbgfkbmgkfqftkbwmbnfOjmhaoldpjyfabpfkfognbhfnbjmvpfq$*#(klogfmgptjwkMftpqfbgtfqfpjdmwbhfkbufdbnfpffm`boosbwktfoosovpnfmvejonsbqwiljmwkjpojpwdllgmffgtbzptfpwilapnjmgboploldlqj`kvpfpobpwwfbnbqnzellghjmdtjoofbpwtbqgafpwejqfSbdfhmltbtbz-smdnlufwkbmolbgdjufpfoemlwfnv`keffgnbmzql`hj`lmlm`follhkjgfgjfgKlnfqvofklpwbib{jmel`ovaobtpofppkboeplnfpv`kylmf233&lmfp`bqfWjnfqb`faovfelvqtffheb`fklsfdbufkbqgolpwtkfmsbqhhfswsbpppkjsqllnKWNOsobmWzsfglmfpbufhffseobdojmhplogejufwllhqbwfwltmivnswkvpgbqh`bqgejofefbqpwbzhjoowkbweboobvwlfufq-`lnwbohpklsulwfgffsnlgfqfpwwvqmalqmabmgefooqlpfvqo+phjmqlof`lnfb`wpbdfpnffwdlog-isdjwfnubqzefowwkfmpfmggqlsUjft`lsz2-3!?,b=pwlsfopfojfpwlvqsb`h-djesbpw`pp<dqbznfbm%dw8qjgfpklwobwfpbjgqlbgubq#effoilkmqj`hslqwebpw$VB.gfbg?,a=sllqajoowzsfV-P-tllgnvpw1s{8JmelqbmhtjgftbmwtbooofbgX3^8sbvotbufpvqf'+$ tbjwnbppbqnpdlfpdbjmobmdsbjg\"..#ol`hvmjwqllwtbohejqntjef{no!plmdwfpw13s{hjmgqltpwlloelmwnbjopbefpwbqnbsp`lqfqbjmeoltabazpsbmpbzp7s{85s{8bqwpellwqfbotjhjkfbwpwfswqjslqd,obhftfbhwlogElqn`bpwebmpabmhufqzqvmpivozwbph2s{8dlbodqftpoltfgdfjg>!pfwp6s{8-ip<73s{je#+pllmpfbwmlmfwvafyfqlpfmwqffgeb`wjmwldjewkbqn2;s{`bnfkjooalogyllnuljgfbpzqjmdejoosfbhjmjw`lpw0s{8ib`hwbdpajwpqloofgjwhmftmfbq?\"..dqltIPLMgvwzMbnfpbofzlv#olwpsbjmibyy`logfzfpejpkttt-qjphwbapsqfu23s{qjpf16s{Aovfgjmd033/abooelqgfbqmtjogal{-ebjqob`hufqpsbjqivmfwf`kje+\"sj`hfujo'+! tbqnolqgglfpsvoo/333jgfbgqbtkvdfpslwevmgavqmkqfe`foohfzpwj`hklvqolppevfo21s{pvjwgfboQPP!bdfgdqfzDFW!fbpfbjnpdjqobjgp;s{8mbuzdqjgwjsp :::tbqpobgz`bqp*8#~sks<kfoowbootklnyk9 ), #233kboo- B4s{8svpk`kbw3s{8`qft),?,kbpk46s{eobwqbqf#%%#wfoo`bnslmwlobjgnjppphjswfmwejmfnbofdfwpsolw733/ `lloeffw-sks?aq=fqj`nlpwdvjgafoogfp`kbjqnbwkbwln,jnd% ;1ov`h`fmw3338wjmzdlmfkwnopfoogqvdEQFFmlgfmj`h<jg>olpfmvooubpwtjmgQPP#tfbqqfozaffmpbnfgvhfmbpb`bsftjpkdvoeW109kjwppolwdbwfhj`haovqwkfz26s{$$*8*8!=npjftjmpajqgplqwafwbpffhW2;9lqgpwqffnboo53s{ebqnlupalzpX3^-$*8!SLPWafbqhjgp*8~~nbqzwfmg+VH*rvbgyk9\n.pjy....sqls$*8ojewW2:9uj`fbmgzgfaw=QPPsllomf`haoltW259gllqfuboW249ofwpebjolqbosloomlub`lopdfmf#lxplewqlnfwjooqlpp?k0=slvqebgfsjmh?wq=njmj*\"+njmfyk9abqpkfbq33*8njoh#..=jqlmeqfggjphtfmwpljosvwp,ip,klozW119JPAMW139bgbnpffp?k1=iplm$/#$`lmwW129#QPPollsbpjbnllm?,s=plvoOJMFelqw`bqwW279?k2=;3s{\"..?:s{8W379njhf975Ymj`fjm`kZlqhqj`fyk9\b$**8svqfnbdfsbqbwlmfalmg904Y\\le\\$^*8333/yk9\vwbmhzbqgaltoavpk965YIbub03s{ ~ &@0&907YifeeF[SJ`bpkujpbdloepmltyk9rvfq-`pppj`hnfbwnjm-ajmggfookjqfsj`pqfmw905YKWWS.132elwltloeFMG#{al{967YALGZgj`h8 ~ f{jw906Yubqpafbw$~*8gjfw:::8bmmf~~?,Xj^-Obmdhn.^tjqfwlzpbggppfbobof{8 \n~f`klmjmf-lqd336*wlmziftppbmgofdpqlle333*#133tjmfdfbqgldpallwdbqz`vwpwzofwfnswjlm-{no`l`hdbmd'+$-63s{Sk-Gnjp`bobmolbmgfphnjofqzbmvmj{gjp`*8~ gvpw`ojs*- 43s{.133GUGp4^=?wbsfgfnlj((*tbdffvqlskjolswpklofEBRpbpjm.15WobapsfwpVQO#avoh`llh8~ KFBGX3^*baaqivbm+2:;ofpkwtjm?,j=plmzdvzpev`hsjsf. \"331*mgltX2^8X^8 Old#pbow \n\nabmdwqjnabwk*x 33s{ ~*8hl9\0effpbg=p9,,#X^8wloosovd+*x x #-ip$133sgvboalbw-ISD*8 ~rvlw*8 $*8 ~1327132613251324132;132:13131312131113101317131613151314131;131:130313021301130013071306130513041320132113221323133:133;133413351336133713301331133213332:::2::;2::42::52::62::72::02::12::22::32:;:2:;;2:;42:;52:;62:;72:;02:;12:;22:;32:4:2:4;2:442:452:462:472:402:412:422:432:5:2:5;2:542:552:562:572:502:512:522:532:6:2:6;2:642:652:662:672:602:612:622:632333231720:73333::::`lnln/Mpfpwffpwbsfqlwlglkb`f`bgbb/]lajfmg/Abbp/Aujgb`bpllwqlelqlplollwqb`vbogjilpjgldqbmwjslwfnbgfafbodlrv/Efpwlmbgbwqfpsl`l`bpbabilwlgbpjmlbdvbsvfpvmlpbmwfgj`fovjpfoobnbzlylmbbnlqsjpllaqb`oj`foolgjlpklqb`bpj<[<\\<Q<\\<R<P=l<\\=l=o=n<\\<Q<Y<S<R<R=n<T<[<Q<R<X<R=n<R<Z<Y<R<Q<T=i<q<\\<Y<Y<]=g<P=g<~=g=m<R<^=g<^<R<q<R<R<]<s<R<W<T<Q<T<L<H<q<Y<p=g=n=g<r<Q<T<P<X<\\<{<\\<x<\\<q=o<r<]=n<Y<t<[<Y<U<Q=o<P<P<N=g=o<Z5m5f4O5j5i4K5i4U5o5h4O5d4]4C5f4K5m5e5k5d5h5i5h5o4K5d5h5k4D4_4K5h4I5j5k5f4O5f5n4C5k5h4G5i4
|
|||
|
|
setData(dictionaryData, dictionarySizeBits);
|
|||
|
|
}
|
|||
|
|
var InputStream = class {
|
|||
|
|
data = new Int8Array(0);
|
|||
|
|
offset = 0;
|
|||
|
|
constructor(data) {
|
|||
|
|
this.data = data;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
function readInput(s, dst, offset, length) {
|
|||
|
|
if (s.input === null) return -1;
|
|||
|
|
const src = s.input;
|
|||
|
|
const end = Math.min(src.offset + length, src.data.length);
|
|||
|
|
const bytesRead = end - src.offset;
|
|||
|
|
dst.set(src.data.subarray(src.offset, end), offset);
|
|||
|
|
src.offset += bytesRead;
|
|||
|
|
return bytesRead;
|
|||
|
|
}
|
|||
|
|
function closeInput(s) {
|
|||
|
|
s.input = new InputStream(new Int8Array(0));
|
|||
|
|
}
|
|||
|
|
function toUsAsciiBytes(src) {
|
|||
|
|
const n = src.length;
|
|||
|
|
const result = new Int8Array(n);
|
|||
|
|
for (let i = 0; i < n; ++i) result[i] = src.charCodeAt(i);
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
function toUtf8Runes(src) {
|
|||
|
|
const n = src.length;
|
|||
|
|
const result = new Int32Array(n);
|
|||
|
|
for (let i = 0; i < n; ++i) result[i] = src.charCodeAt(i);
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
function makeError(s, code) {
|
|||
|
|
if (code >= 0) return code;
|
|||
|
|
if (s.runningState >= 0) s.runningState = code;
|
|||
|
|
throw new Error("Brotli error code: " + code);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Decodes brotli stream.
|
|||
|
|
*/
|
|||
|
|
function brotliDecode(bytes) {
|
|||
|
|
const s = new State();
|
|||
|
|
s.input = new InputStream(bytes);
|
|||
|
|
initState(s);
|
|||
|
|
let totalOutput = 0;
|
|||
|
|
const chunks = [];
|
|||
|
|
while (true) {
|
|||
|
|
const chunk = new Int8Array(16384);
|
|||
|
|
chunks.push(chunk);
|
|||
|
|
s.output = chunk;
|
|||
|
|
s.outputOffset = 0;
|
|||
|
|
s.outputLength = 16384;
|
|||
|
|
s.outputUsed = 0;
|
|||
|
|
decompress(s);
|
|||
|
|
totalOutput += s.outputUsed;
|
|||
|
|
if (s.outputUsed < 16384) break;
|
|||
|
|
}
|
|||
|
|
close(s);
|
|||
|
|
closeInput(s);
|
|||
|
|
const result = new Int8Array(totalOutput);
|
|||
|
|
let offset = 0;
|
|||
|
|
for (let i = 0; i < chunks.length; ++i) {
|
|||
|
|
const chunk = chunks[i];
|
|||
|
|
const len = Math.min(totalOutput, offset + 16384) - offset;
|
|||
|
|
if (len < 16384) result.set(chunk.subarray(0, len), offset);
|
|||
|
|
else result.set(chunk, offset);
|
|||
|
|
offset += len;
|
|||
|
|
}
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/glyph/WOFF2Glyph.ts
|
|||
|
|
/**
|
|||
|
|
* Represents a TrueType glyph in the WOFF2 format, which compresses glyphs differently.
|
|||
|
|
*/
|
|||
|
|
var WOFF2Glyph = class extends TTFGlyph {
|
|||
|
|
type = "WOFF2";
|
|||
|
|
_decode() {
|
|||
|
|
return this._font._transformedGlyphs[this.id];
|
|||
|
|
}
|
|||
|
|
_getCBox() {
|
|||
|
|
return this.path.bbox;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/tables/WOFF2Directory.ts
|
|||
|
|
const Base128 = { decode(stream) {
|
|||
|
|
let result = 0;
|
|||
|
|
for (let i = 0; i < 5; i++) {
|
|||
|
|
const code = stream.readUInt8();
|
|||
|
|
if (result & 3758096384) throw new Error("Overflow");
|
|||
|
|
result = result << 7 | code & 127;
|
|||
|
|
if ((code & 128) === 0) return result;
|
|||
|
|
}
|
|||
|
|
throw new Error("Bad base 128 number");
|
|||
|
|
} };
|
|||
|
|
const knownTags = [
|
|||
|
|
"cmap",
|
|||
|
|
"head",
|
|||
|
|
"hhea",
|
|||
|
|
"hmtx",
|
|||
|
|
"maxp",
|
|||
|
|
"name",
|
|||
|
|
"OS/2",
|
|||
|
|
"post",
|
|||
|
|
"cvt ",
|
|||
|
|
"fpgm",
|
|||
|
|
"glyf",
|
|||
|
|
"loca",
|
|||
|
|
"prep",
|
|||
|
|
"CFF ",
|
|||
|
|
"VORG",
|
|||
|
|
"EBDT",
|
|||
|
|
"EBLC",
|
|||
|
|
"gasp",
|
|||
|
|
"hdmx",
|
|||
|
|
"kern",
|
|||
|
|
"LTSH",
|
|||
|
|
"PCLT",
|
|||
|
|
"VDMX",
|
|||
|
|
"vhea",
|
|||
|
|
"vmtx",
|
|||
|
|
"BASE",
|
|||
|
|
"GDEF",
|
|||
|
|
"GPOS",
|
|||
|
|
"GSUB",
|
|||
|
|
"EBSC",
|
|||
|
|
"JSTF",
|
|||
|
|
"MATH",
|
|||
|
|
"CBDT",
|
|||
|
|
"CBLC",
|
|||
|
|
"COLR",
|
|||
|
|
"CPAL",
|
|||
|
|
"SVG ",
|
|||
|
|
"sbix",
|
|||
|
|
"acnt",
|
|||
|
|
"avar",
|
|||
|
|
"bdat",
|
|||
|
|
"bloc",
|
|||
|
|
"bsln",
|
|||
|
|
"cvar",
|
|||
|
|
"fdsc",
|
|||
|
|
"feat",
|
|||
|
|
"fmtx",
|
|||
|
|
"fvar",
|
|||
|
|
"gvar",
|
|||
|
|
"hsty",
|
|||
|
|
"just",
|
|||
|
|
"lcar",
|
|||
|
|
"mort",
|
|||
|
|
"morx",
|
|||
|
|
"opbd",
|
|||
|
|
"prop",
|
|||
|
|
"trak",
|
|||
|
|
"Zapf",
|
|||
|
|
"Silf",
|
|||
|
|
"Glat",
|
|||
|
|
"Gloc",
|
|||
|
|
"Feat",
|
|||
|
|
"Sill"
|
|||
|
|
];
|
|||
|
|
const WOFF2DirectoryEntry = new Struct({
|
|||
|
|
flags: uint8,
|
|||
|
|
customTag: new Optional(new StringT(4), (t) => (t.flags & 63) === 63),
|
|||
|
|
tag: (t) => t.customTag || knownTags[t.flags & 63],
|
|||
|
|
length: Base128,
|
|||
|
|
transformVersion: (t) => t.flags >>> 6 & 3,
|
|||
|
|
transformed: (t) => t.tag === "glyf" || t.tag === "loca" ? t.transformVersion === 0 : t.transformVersion !== 0,
|
|||
|
|
transformLength: new Optional(Base128, (t) => t.transformed)
|
|||
|
|
});
|
|||
|
|
const WOFF2Directory = new Struct({
|
|||
|
|
tag: new StringT(4),
|
|||
|
|
flavor: uint32,
|
|||
|
|
length: uint32,
|
|||
|
|
numTables: uint16,
|
|||
|
|
reserved: new Reserved(uint16),
|
|||
|
|
totalSfntSize: uint32,
|
|||
|
|
totalCompressedSize: uint32,
|
|||
|
|
majorVersion: uint16,
|
|||
|
|
minorVersion: uint16,
|
|||
|
|
metaOffset: uint32,
|
|||
|
|
metaLength: uint32,
|
|||
|
|
metaOrigLength: uint32,
|
|||
|
|
privOffset: uint32,
|
|||
|
|
privLength: uint32,
|
|||
|
|
tables: new ArrayT(WOFF2DirectoryEntry, "numTables")
|
|||
|
|
});
|
|||
|
|
WOFF2Directory.process = function() {
|
|||
|
|
this.tables = Object.fromEntries(this.tables.map((table) => [table.tag, table]));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/WOFF2Font.ts
|
|||
|
|
/**
|
|||
|
|
* Subclass of TTFFont that represents a TTF/OTF font compressed by WOFF2
|
|||
|
|
* See spec here: http://www.w3.org/TR/WOFF2/
|
|||
|
|
*/
|
|||
|
|
var WOFF2Font = class extends TTFFont {
|
|||
|
|
type = "WOFF2";
|
|||
|
|
_decompressed = false;
|
|||
|
|
static probe(buffer) {
|
|||
|
|
return asciiDecoder.decode(buffer.slice(0, 4)) === "wOF2";
|
|||
|
|
}
|
|||
|
|
_decodeDirectory() {
|
|||
|
|
const directory = WOFF2Directory.decode(this.stream);
|
|||
|
|
this._dataPos = this.stream.pos;
|
|||
|
|
return directory;
|
|||
|
|
}
|
|||
|
|
#decompress() {
|
|||
|
|
if (!this._decompressed) {
|
|||
|
|
this.stream.pos = this._dataPos;
|
|||
|
|
const buffer = this.stream.readBuffer(this.directory.totalCompressedSize);
|
|||
|
|
let decompressedSize = 0;
|
|||
|
|
for (const tag in this.directory.tables) {
|
|||
|
|
const entry = this.directory.tables[tag];
|
|||
|
|
entry.offset = decompressedSize;
|
|||
|
|
decompressedSize += entry.transformLength != null ? entry.transformLength : entry.length;
|
|||
|
|
}
|
|||
|
|
const decompressed = brotliDecode(new Int8Array(buffer));
|
|||
|
|
if (!decompressed) throw new Error("Error decoding compressed data in WOFF2");
|
|||
|
|
this.stream = new DecodeStream(decompressed);
|
|||
|
|
this._decompressed = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_decodeTable(table) {
|
|||
|
|
this.#decompress();
|
|||
|
|
return super._decodeTable(table);
|
|||
|
|
}
|
|||
|
|
_getBaseGlyph(glyph, characters = []) {
|
|||
|
|
if (!this._glyphs[glyph]) if (this.directory.tables.glyf?.transformed) {
|
|||
|
|
if (!this._transformedGlyphs) this._transformGlyfTable();
|
|||
|
|
return this._glyphs[glyph] = new WOFF2Glyph(glyph, characters, this);
|
|||
|
|
} else return super._getBaseGlyph(glyph, characters);
|
|||
|
|
else return this._glyphs[glyph];
|
|||
|
|
}
|
|||
|
|
_transformGlyfTable() {
|
|||
|
|
this.#decompress();
|
|||
|
|
this.stream.pos = this.directory.tables.glyf.offset;
|
|||
|
|
const table = GlyfTable.decode(this.stream);
|
|||
|
|
const glyphs = [];
|
|||
|
|
for (let index = 0; index < table.numGlyphs; index++) {
|
|||
|
|
const nContours = table.nContours.readInt16BE();
|
|||
|
|
const glyph = { numberOfContours: nContours };
|
|||
|
|
if (nContours > 0) {
|
|||
|
|
const nPoints = [];
|
|||
|
|
let totalPoints = 0;
|
|||
|
|
for (let i = 0; i < nContours; i++) {
|
|||
|
|
totalPoints += read255UInt16(table.nPoints);
|
|||
|
|
nPoints.push(totalPoints);
|
|||
|
|
}
|
|||
|
|
glyph.points = decodeTriplet(table.flags, table.glyphs, totalPoints);
|
|||
|
|
for (let i = 0; i < nContours; i++) glyph.points[nPoints[i] - 1].endContour = true;
|
|||
|
|
read255UInt16(table.glyphs);
|
|||
|
|
} else if (nContours < 0) {
|
|||
|
|
if (TTFGlyph.prototype._decodeComposite.call({ _font: this }, glyph, table.composites)) read255UInt16(table.glyphs);
|
|||
|
|
}
|
|||
|
|
glyphs.push(glyph);
|
|||
|
|
}
|
|||
|
|
this._transformedGlyphs = glyphs;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var Substream = class {
|
|||
|
|
#buf;
|
|||
|
|
constructor(length) {
|
|||
|
|
this.length = length;
|
|||
|
|
this.#buf = new BufferT(length);
|
|||
|
|
}
|
|||
|
|
decode(stream, parent) {
|
|||
|
|
return new DecodeStream(this.#buf.decode(stream, parent));
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const GlyfTable = new Struct({
|
|||
|
|
version: uint32,
|
|||
|
|
numGlyphs: uint16,
|
|||
|
|
indexFormat: uint16,
|
|||
|
|
nContourStreamSize: uint32,
|
|||
|
|
nPointsStreamSize: uint32,
|
|||
|
|
flagStreamSize: uint32,
|
|||
|
|
glyphStreamSize: uint32,
|
|||
|
|
compositeStreamSize: uint32,
|
|||
|
|
bboxStreamSize: uint32,
|
|||
|
|
instructionStreamSize: uint32,
|
|||
|
|
nContours: new Substream("nContourStreamSize"),
|
|||
|
|
nPoints: new Substream("nPointsStreamSize"),
|
|||
|
|
flags: new Substream("flagStreamSize"),
|
|||
|
|
glyphs: new Substream("glyphStreamSize"),
|
|||
|
|
composites: new Substream("compositeStreamSize"),
|
|||
|
|
bboxes: new Substream("bboxStreamSize"),
|
|||
|
|
instructions: new Substream("instructionStreamSize")
|
|||
|
|
});
|
|||
|
|
const WORD_CODE = 253;
|
|||
|
|
const ONE_MORE_BYTE_CODE2 = 254;
|
|||
|
|
const ONE_MORE_BYTE_CODE1 = 255;
|
|||
|
|
const LOWEST_U_CODE = 253;
|
|||
|
|
function read255UInt16(stream) {
|
|||
|
|
const code = stream.readUInt8();
|
|||
|
|
switch (code) {
|
|||
|
|
case WORD_CODE: return stream.readUInt16BE();
|
|||
|
|
case ONE_MORE_BYTE_CODE1: return stream.readUInt8() + LOWEST_U_CODE;
|
|||
|
|
case ONE_MORE_BYTE_CODE2: return stream.readUInt8() + LOWEST_U_CODE * 2;
|
|||
|
|
default: return code;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function withSign(flag, baseval) {
|
|||
|
|
return flag & 1 ? baseval : -baseval;
|
|||
|
|
}
|
|||
|
|
function decodeTriplet(flags, glyphs, nPoints) {
|
|||
|
|
let y;
|
|||
|
|
let x = y = 0;
|
|||
|
|
const res = [];
|
|||
|
|
for (let i = 0; i < nPoints; i++) {
|
|||
|
|
let dx = 0, dy = 0;
|
|||
|
|
let flag = flags.readUInt8();
|
|||
|
|
const onCurve = !(flag >> 7);
|
|||
|
|
flag &= 127;
|
|||
|
|
if (flag < 10) {
|
|||
|
|
dx = 0;
|
|||
|
|
dy = withSign(flag, ((flag & 14) << 7) + glyphs.readUInt8());
|
|||
|
|
} else if (flag < 20) {
|
|||
|
|
dx = withSign(flag, ((flag - 10 & 14) << 7) + glyphs.readUInt8());
|
|||
|
|
dy = 0;
|
|||
|
|
} else if (flag < 84) {
|
|||
|
|
var b0 = flag - 20;
|
|||
|
|
var b1 = glyphs.readUInt8();
|
|||
|
|
dx = withSign(flag, 1 + (b0 & 48) + (b1 >> 4));
|
|||
|
|
dy = withSign(flag >> 1, 1 + ((b0 & 12) << 2) + (b1 & 15));
|
|||
|
|
} else if (flag < 120) {
|
|||
|
|
var b0 = flag - 84;
|
|||
|
|
dx = withSign(flag, 1 + (b0 / 12 << 8) + glyphs.readUInt8());
|
|||
|
|
dy = withSign(flag >> 1, 1 + (b0 % 12 >> 2 << 8) + glyphs.readUInt8());
|
|||
|
|
} else if (flag < 124) {
|
|||
|
|
var b1 = glyphs.readUInt8();
|
|||
|
|
let b2 = glyphs.readUInt8();
|
|||
|
|
dx = withSign(flag, (b1 << 4) + (b2 >> 4));
|
|||
|
|
dy = withSign(flag >> 1, ((b2 & 15) << 8) + glyphs.readUInt8());
|
|||
|
|
} else {
|
|||
|
|
dx = withSign(flag, glyphs.readUInt16BE());
|
|||
|
|
dy = withSign(flag >> 1, glyphs.readUInt16BE());
|
|||
|
|
}
|
|||
|
|
x += dx;
|
|||
|
|
y += dy;
|
|||
|
|
res.push(new Point(onCurve, false, x, y));
|
|||
|
|
}
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/TrueTypeCollection.ts
|
|||
|
|
const TTCHeader = new VersionedStruct(uint32, {
|
|||
|
|
65536: {
|
|||
|
|
numFonts: uint32,
|
|||
|
|
offsets: new ArrayT(uint32, "numFonts")
|
|||
|
|
},
|
|||
|
|
131072: {
|
|||
|
|
numFonts: uint32,
|
|||
|
|
offsets: new ArrayT(uint32, "numFonts"),
|
|||
|
|
dsigTag: uint32,
|
|||
|
|
dsigLength: uint32,
|
|||
|
|
dsigOffset: uint32
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
var TrueTypeCollection = class {
|
|||
|
|
type = "TTC";
|
|||
|
|
isCollection = true;
|
|||
|
|
static probe(buffer) {
|
|||
|
|
return asciiDecoder.decode(buffer.slice(0, 4)) === "ttcf";
|
|||
|
|
}
|
|||
|
|
constructor(stream) {
|
|||
|
|
this.stream = stream;
|
|||
|
|
if (stream.readString(4) !== "ttcf") throw new Error("Not a TrueType collection");
|
|||
|
|
this.header = TTCHeader.decode(stream);
|
|||
|
|
}
|
|||
|
|
getFont(name) {
|
|||
|
|
for (const offset of this.header.offsets) {
|
|||
|
|
const stream = new DecodeStream(this.stream.buffer);
|
|||
|
|
stream.pos = offset;
|
|||
|
|
const font = new TTFFont(stream);
|
|||
|
|
if (font.postscriptName === name || font.postscriptName instanceof Uint8Array && name instanceof Uint8Array && font.postscriptName.every((v, i) => name[i] === v)) return font;
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
get fonts() {
|
|||
|
|
return this.header.offsets.map((offset) => {
|
|||
|
|
const stream = new DecodeStream(this.stream.buffer);
|
|||
|
|
stream.pos = offset;
|
|||
|
|
return new TTFFont(stream);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/DFont.ts
|
|||
|
|
const DFontName = new StringT(uint8);
|
|||
|
|
const Ref = new Struct({
|
|||
|
|
id: uint16,
|
|||
|
|
nameOffset: int16,
|
|||
|
|
attr: uint8,
|
|||
|
|
dataOffset: uint24,
|
|||
|
|
handle: uint32
|
|||
|
|
});
|
|||
|
|
const Type = new Struct({
|
|||
|
|
name: new StringT(4),
|
|||
|
|
maxTypeIndex: uint16,
|
|||
|
|
refList: new Pointer(uint16, new ArrayT(Ref, (t) => t.maxTypeIndex + 1), { type: "parent" })
|
|||
|
|
});
|
|||
|
|
const TypeList = new Struct({
|
|||
|
|
length: uint16,
|
|||
|
|
types: new ArrayT(Type, (t) => t.length + 1)
|
|||
|
|
});
|
|||
|
|
const DFontMap = new Struct({
|
|||
|
|
reserved: new Reserved(uint8, 24),
|
|||
|
|
typeList: new Pointer(uint16, TypeList),
|
|||
|
|
nameListOffset: new Pointer(uint16, "void")
|
|||
|
|
});
|
|||
|
|
const DFontHeader = new Struct({
|
|||
|
|
dataOffset: uint32,
|
|||
|
|
map: new Pointer(uint32, DFontMap),
|
|||
|
|
dataLength: uint32,
|
|||
|
|
mapLength: uint32
|
|||
|
|
});
|
|||
|
|
var DFont = class {
|
|||
|
|
type = "DFont";
|
|||
|
|
isCollection = true;
|
|||
|
|
static probe(buffer) {
|
|||
|
|
const stream = new DecodeStream(buffer);
|
|||
|
|
let header;
|
|||
|
|
try {
|
|||
|
|
header = DFontHeader.decode(stream);
|
|||
|
|
} catch {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
for (const type of header.map.typeList.types) if (type.name === "sfnt") return true;
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
constructor(stream) {
|
|||
|
|
this.stream = stream;
|
|||
|
|
this.header = DFontHeader.decode(this.stream);
|
|||
|
|
for (const type of this.header.map.typeList.types) {
|
|||
|
|
for (const ref of type.refList) if (ref.nameOffset >= 0) {
|
|||
|
|
this.stream.pos = ref.nameOffset + this.header.map.nameListOffset;
|
|||
|
|
ref.name = DFontName.decode(this.stream);
|
|||
|
|
} else ref.name = null;
|
|||
|
|
if (type.name === "sfnt") this.sfnt = type;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
getFont(name) {
|
|||
|
|
if (!this.sfnt) return null;
|
|||
|
|
for (const ref of this.sfnt.refList) {
|
|||
|
|
const pos = this.header.dataOffset + ref.dataOffset + 4;
|
|||
|
|
const font = new TTFFont(new DecodeStream(this.stream.buffer.slice(pos)));
|
|||
|
|
if (font.postscriptName === name || font.postscriptName instanceof Uint8Array && name instanceof Uint8Array && font.postscriptName.every((v, i) => name[i] === v)) return font;
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
get fonts() {
|
|||
|
|
return this.sfnt.refList.map((ref) => {
|
|||
|
|
const pos = this.header.dataOffset + ref.dataOffset + 4;
|
|||
|
|
return new TTFFont(new DecodeStream(this.stream.buffer.slice(pos)));
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#region src/index.ts
|
|||
|
|
const formats = [
|
|||
|
|
TTFFont,
|
|||
|
|
WOFFFont,
|
|||
|
|
WOFF2Font,
|
|||
|
|
TrueTypeCollection,
|
|||
|
|
DFont
|
|||
|
|
];
|
|||
|
|
/**
|
|||
|
|
* Returns a font object for the given buffer.
|
|||
|
|
* For collection fonts (such as TrueType collection files), you can pass a postscriptName to get
|
|||
|
|
* that font out of the collection instead of a collection object.
|
|||
|
|
* @param buffer `Buffer` containing font data
|
|||
|
|
* @param postscriptName Optional PostScript name of font to extract from collection file.
|
|||
|
|
*/
|
|||
|
|
function create(buffer, postscriptName) {
|
|||
|
|
for (const format of formats) if (format.probe(buffer)) {
|
|||
|
|
const font = new format(new DecodeStream(buffer));
|
|||
|
|
if (postscriptName) return font.getFont(postscriptName);
|
|||
|
|
return font;
|
|||
|
|
}
|
|||
|
|
throw new Error("Unknown font format");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
export { create };
|