4063 lines
168 KiB
Plaintext
4063 lines
168 KiB
Plaintext
import {
|
|
SDFGenerator,
|
|
bidi_default,
|
|
defineWorkerModule,
|
|
terminateWorker
|
|
} from "/_nuxt/node_modules/.cache/vite/client/deps/chunk-ZMJJZNZH.js?v=e4f18c29";
|
|
import {
|
|
createDerivedMaterial,
|
|
voidMainRegExp
|
|
} from "/_nuxt/node_modules/.cache/vite/client/deps/chunk-REWIZMPU.js?v=e4f18c29";
|
|
import {
|
|
Box3,
|
|
Color,
|
|
DataTexture,
|
|
DoubleSide,
|
|
DynamicDrawUsage,
|
|
FloatType,
|
|
InstancedBufferAttribute,
|
|
InstancedBufferGeometry,
|
|
LinearFilter,
|
|
Matrix3,
|
|
Matrix4,
|
|
Mesh,
|
|
MeshBasicMaterial,
|
|
PlaneGeometry,
|
|
RGBAFormat,
|
|
Sphere,
|
|
Texture,
|
|
Vector2,
|
|
Vector3,
|
|
Vector4
|
|
} from "/_nuxt/node_modules/.cache/vite/client/deps/chunk-KHL3VXVA.js?v=e4f18c29";
|
|
import "/_nuxt/node_modules/.cache/vite/client/deps/chunk-V4OQ3NZ2.js?v=e4f18c29";
|
|
|
|
// ../../node_modules/troika-three-text/dist/troika-three-text.esm.js
|
|
function typrFactory() {
|
|
return "undefined" == typeof window && (self.window = self), function(r) {
|
|
var e = { parse: function(r2) {
|
|
var t2 = e._bin, a2 = new Uint8Array(r2);
|
|
if ("ttcf" == t2.readASCII(a2, 0, 4)) {
|
|
var n = 4;
|
|
t2.readUshort(a2, n), n += 2, t2.readUshort(a2, n), n += 2;
|
|
var o = t2.readUint(a2, n);
|
|
n += 4;
|
|
for (var s = [], i = 0; i < o; i++) {
|
|
var h = t2.readUint(a2, n);
|
|
n += 4, s.push(e._readFont(a2, h));
|
|
}
|
|
return s;
|
|
}
|
|
return [e._readFont(a2, 0)];
|
|
}, _readFont: function(r2, t2) {
|
|
var a2 = e._bin, n = t2;
|
|
a2.readFixed(r2, t2), t2 += 4;
|
|
var o = a2.readUshort(r2, t2);
|
|
t2 += 2, a2.readUshort(r2, t2), t2 += 2, a2.readUshort(r2, t2), t2 += 2, a2.readUshort(r2, t2), t2 += 2;
|
|
for (var s = ["cmap", "head", "hhea", "maxp", "hmtx", "name", "OS/2", "post", "loca", "glyf", "kern", "CFF ", "GDEF", "GPOS", "GSUB", "SVG "], i = { _data: r2, _offset: n }, h = {}, d = 0; d < o; d++) {
|
|
var f = a2.readASCII(r2, t2, 4);
|
|
t2 += 4, a2.readUint(r2, t2), t2 += 4;
|
|
var u = a2.readUint(r2, t2);
|
|
t2 += 4;
|
|
var l = a2.readUint(r2, t2);
|
|
t2 += 4, h[f] = { offset: u, length: l };
|
|
}
|
|
for (d = 0; d < s.length; d++) {
|
|
var v = s[d];
|
|
h[v] && (i[v.trim()] = e[v.trim()].parse(r2, h[v].offset, h[v].length, i));
|
|
}
|
|
return i;
|
|
}, _tabOffset: function(r2, t2, a2) {
|
|
for (var n = e._bin, o = n.readUshort(r2, a2 + 4), s = a2 + 12, i = 0; i < o; i++) {
|
|
var h = n.readASCII(r2, s, 4);
|
|
s += 4, n.readUint(r2, s), s += 4;
|
|
var d = n.readUint(r2, s);
|
|
if (s += 4, n.readUint(r2, s), s += 4, h == t2) return d;
|
|
}
|
|
return 0;
|
|
} };
|
|
e._bin = { readFixed: function(r2, e2) {
|
|
return (r2[e2] << 8 | r2[e2 + 1]) + (r2[e2 + 2] << 8 | r2[e2 + 3]) / 65540;
|
|
}, readF2dot14: function(r2, t2) {
|
|
return e._bin.readShort(r2, t2) / 16384;
|
|
}, readInt: function(r2, t2) {
|
|
return e._bin._view(r2).getInt32(t2);
|
|
}, readInt8: function(r2, t2) {
|
|
return e._bin._view(r2).getInt8(t2);
|
|
}, readShort: function(r2, t2) {
|
|
return e._bin._view(r2).getInt16(t2);
|
|
}, readUshort: function(r2, t2) {
|
|
return e._bin._view(r2).getUint16(t2);
|
|
}, readUshorts: function(r2, t2, a2) {
|
|
for (var n = [], o = 0; o < a2; o++) n.push(e._bin.readUshort(r2, t2 + 2 * o));
|
|
return n;
|
|
}, readUint: function(r2, t2) {
|
|
return e._bin._view(r2).getUint32(t2);
|
|
}, readUint64: function(r2, t2) {
|
|
return 4294967296 * e._bin.readUint(r2, t2) + e._bin.readUint(r2, t2 + 4);
|
|
}, readASCII: function(r2, e2, t2) {
|
|
for (var a2 = "", n = 0; n < t2; n++) a2 += String.fromCharCode(r2[e2 + n]);
|
|
return a2;
|
|
}, readUnicode: function(r2, e2, t2) {
|
|
for (var a2 = "", n = 0; n < t2; n++) {
|
|
var o = r2[e2++] << 8 | r2[e2++];
|
|
a2 += String.fromCharCode(o);
|
|
}
|
|
return a2;
|
|
}, _tdec: "undefined" != typeof window && window.TextDecoder ? new window.TextDecoder() : null, readUTF8: function(r2, t2, a2) {
|
|
var n = e._bin._tdec;
|
|
return n && 0 == t2 && a2 == r2.length ? n.decode(r2) : e._bin.readASCII(r2, t2, a2);
|
|
}, readBytes: function(r2, e2, t2) {
|
|
for (var a2 = [], n = 0; n < t2; n++) a2.push(r2[e2 + n]);
|
|
return a2;
|
|
}, readASCIIArray: function(r2, e2, t2) {
|
|
for (var a2 = [], n = 0; n < t2; n++) a2.push(String.fromCharCode(r2[e2 + n]));
|
|
return a2;
|
|
}, _view: function(r2) {
|
|
return r2._dataView || (r2._dataView = r2.buffer ? new DataView(r2.buffer, r2.byteOffset, r2.byteLength) : new DataView(new Uint8Array(r2).buffer));
|
|
} }, e._lctf = {}, e._lctf.parse = function(r2, t2, a2, n, o) {
|
|
var s = e._bin, i = {}, h = t2;
|
|
s.readFixed(r2, t2), t2 += 4;
|
|
var d = s.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var f = s.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var u = s.readUshort(r2, t2);
|
|
return t2 += 2, i.scriptList = e._lctf.readScriptList(r2, h + d), i.featureList = e._lctf.readFeatureList(r2, h + f), i.lookupList = e._lctf.readLookupList(r2, h + u, o), i;
|
|
}, e._lctf.readLookupList = function(r2, t2, a2) {
|
|
var n = e._bin, o = t2, s = [], i = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (var h = 0; h < i; h++) {
|
|
var d = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var f = e._lctf.readLookupTable(r2, o + d, a2);
|
|
s.push(f);
|
|
}
|
|
return s;
|
|
}, e._lctf.readLookupTable = function(r2, t2, a2) {
|
|
var n = e._bin, o = t2, s = { tabs: [] };
|
|
s.ltype = n.readUshort(r2, t2), t2 += 2, s.flag = n.readUshort(r2, t2), t2 += 2;
|
|
var i = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (var h = s.ltype, d = 0; d < i; d++) {
|
|
var f = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var u = a2(r2, h, o + f, s);
|
|
s.tabs.push(u);
|
|
}
|
|
return s;
|
|
}, e._lctf.numOfOnes = function(r2) {
|
|
for (var e2 = 0, t2 = 0; t2 < 32; t2++) 0 != (r2 >>> t2 & 1) && e2++;
|
|
return e2;
|
|
}, e._lctf.readClassDef = function(r2, t2) {
|
|
var a2 = e._bin, n = [], o = a2.readUshort(r2, t2);
|
|
if (t2 += 2, 1 == o) {
|
|
var s = a2.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var i = a2.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (var h = 0; h < i; h++) n.push(s + h), n.push(s + h), n.push(a2.readUshort(r2, t2)), t2 += 2;
|
|
}
|
|
if (2 == o) {
|
|
var d = a2.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (h = 0; h < d; h++) n.push(a2.readUshort(r2, t2)), t2 += 2, n.push(a2.readUshort(r2, t2)), t2 += 2, n.push(a2.readUshort(r2, t2)), t2 += 2;
|
|
}
|
|
return n;
|
|
}, e._lctf.getInterval = function(r2, e2) {
|
|
for (var t2 = 0; t2 < r2.length; t2 += 3) {
|
|
var a2 = r2[t2], n = r2[t2 + 1];
|
|
if (r2[t2 + 2], a2 <= e2 && e2 <= n) return t2;
|
|
}
|
|
return -1;
|
|
}, e._lctf.readCoverage = function(r2, t2) {
|
|
var a2 = e._bin, n = {};
|
|
n.fmt = a2.readUshort(r2, t2), t2 += 2;
|
|
var o = a2.readUshort(r2, t2);
|
|
return t2 += 2, 1 == n.fmt && (n.tab = a2.readUshorts(r2, t2, o)), 2 == n.fmt && (n.tab = a2.readUshorts(r2, t2, 3 * o)), n;
|
|
}, e._lctf.coverageIndex = function(r2, t2) {
|
|
var a2 = r2.tab;
|
|
if (1 == r2.fmt) return a2.indexOf(t2);
|
|
if (2 == r2.fmt) {
|
|
var n = e._lctf.getInterval(a2, t2);
|
|
if (-1 != n) return a2[n + 2] + (t2 - a2[n]);
|
|
}
|
|
return -1;
|
|
}, e._lctf.readFeatureList = function(r2, t2) {
|
|
var a2 = e._bin, n = t2, o = [], s = a2.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (var i = 0; i < s; i++) {
|
|
var h = a2.readASCII(r2, t2, 4);
|
|
t2 += 4;
|
|
var d = a2.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var f = e._lctf.readFeatureTable(r2, n + d);
|
|
f.tag = h.trim(), o.push(f);
|
|
}
|
|
return o;
|
|
}, e._lctf.readFeatureTable = function(r2, t2) {
|
|
var a2 = e._bin, n = t2, o = {}, s = a2.readUshort(r2, t2);
|
|
t2 += 2, s > 0 && (o.featureParams = n + s);
|
|
var i = a2.readUshort(r2, t2);
|
|
t2 += 2, o.tab = [];
|
|
for (var h = 0; h < i; h++) o.tab.push(a2.readUshort(r2, t2 + 2 * h));
|
|
return o;
|
|
}, e._lctf.readScriptList = function(r2, t2) {
|
|
var a2 = e._bin, n = t2, o = {}, s = a2.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (var i = 0; i < s; i++) {
|
|
var h = a2.readASCII(r2, t2, 4);
|
|
t2 += 4;
|
|
var d = a2.readUshort(r2, t2);
|
|
t2 += 2, o[h.trim()] = e._lctf.readScriptTable(r2, n + d);
|
|
}
|
|
return o;
|
|
}, e._lctf.readScriptTable = function(r2, t2) {
|
|
var a2 = e._bin, n = t2, o = {}, s = a2.readUshort(r2, t2);
|
|
t2 += 2, s > 0 && (o.default = e._lctf.readLangSysTable(r2, n + s));
|
|
var i = a2.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (var h = 0; h < i; h++) {
|
|
var d = a2.readASCII(r2, t2, 4);
|
|
t2 += 4;
|
|
var f = a2.readUshort(r2, t2);
|
|
t2 += 2, o[d.trim()] = e._lctf.readLangSysTable(r2, n + f);
|
|
}
|
|
return o;
|
|
}, e._lctf.readLangSysTable = function(r2, t2) {
|
|
var a2 = e._bin, n = {};
|
|
a2.readUshort(r2, t2), t2 += 2, n.reqFeature = a2.readUshort(r2, t2), t2 += 2;
|
|
var o = a2.readUshort(r2, t2);
|
|
return t2 += 2, n.features = a2.readUshorts(r2, t2, o), n;
|
|
}, e.CFF = {}, e.CFF.parse = function(r2, t2, a2) {
|
|
var n = e._bin;
|
|
(r2 = new Uint8Array(r2.buffer, t2, a2))[t2 = 0], r2[++t2], r2[++t2], r2[++t2], t2++;
|
|
var o = [];
|
|
t2 = e.CFF.readIndex(r2, t2, o);
|
|
for (var s = [], i = 0; i < o.length - 1; i++) s.push(n.readASCII(r2, t2 + o[i], o[i + 1] - o[i]));
|
|
t2 += o[o.length - 1];
|
|
var h = [];
|
|
t2 = e.CFF.readIndex(r2, t2, h);
|
|
var d = [];
|
|
for (i = 0; i < h.length - 1; i++) d.push(e.CFF.readDict(r2, t2 + h[i], t2 + h[i + 1]));
|
|
t2 += h[h.length - 1];
|
|
var f = d[0], u = [];
|
|
t2 = e.CFF.readIndex(r2, t2, u);
|
|
var l = [];
|
|
for (i = 0; i < u.length - 1; i++) l.push(n.readASCII(r2, t2 + u[i], u[i + 1] - u[i]));
|
|
if (t2 += u[u.length - 1], e.CFF.readSubrs(r2, t2, f), f.CharStrings) {
|
|
t2 = f.CharStrings;
|
|
u = [];
|
|
t2 = e.CFF.readIndex(r2, t2, u);
|
|
var v = [];
|
|
for (i = 0; i < u.length - 1; i++) v.push(n.readBytes(r2, t2 + u[i], u[i + 1] - u[i]));
|
|
f.CharStrings = v;
|
|
}
|
|
if (f.ROS) {
|
|
t2 = f.FDArray;
|
|
var c = [];
|
|
t2 = e.CFF.readIndex(r2, t2, c), f.FDArray = [];
|
|
for (i = 0; i < c.length - 1; i++) {
|
|
var p = e.CFF.readDict(r2, t2 + c[i], t2 + c[i + 1]);
|
|
e.CFF._readFDict(r2, p, l), f.FDArray.push(p);
|
|
}
|
|
t2 += c[c.length - 1], t2 = f.FDSelect, f.FDSelect = [];
|
|
var U = r2[t2];
|
|
if (t2++, 3 != U) throw U;
|
|
var g = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (i = 0; i < g + 1; i++) f.FDSelect.push(n.readUshort(r2, t2), r2[t2 + 2]), t2 += 3;
|
|
}
|
|
return f.Encoding && (f.Encoding = e.CFF.readEncoding(r2, f.Encoding, f.CharStrings.length)), f.charset && (f.charset = e.CFF.readCharset(r2, f.charset, f.CharStrings.length)), e.CFF._readFDict(r2, f, l), f;
|
|
}, e.CFF._readFDict = function(r2, t2, a2) {
|
|
var n;
|
|
for (var o in t2.Private && (n = t2.Private[1], t2.Private = e.CFF.readDict(r2, n, n + t2.Private[0]), t2.Private.Subrs && e.CFF.readSubrs(r2, n + t2.Private.Subrs, t2.Private)), t2) -1 != ["FamilyName", "FontName", "FullName", "Notice", "version", "Copyright"].indexOf(o) && (t2[o] = a2[t2[o] - 426 + 35]);
|
|
}, e.CFF.readSubrs = function(r2, t2, a2) {
|
|
var n = e._bin, o = [];
|
|
t2 = e.CFF.readIndex(r2, t2, o);
|
|
var s, i = o.length;
|
|
s = i < 1240 ? 107 : i < 33900 ? 1131 : 32768, a2.Bias = s, a2.Subrs = [];
|
|
for (var h = 0; h < o.length - 1; h++) a2.Subrs.push(n.readBytes(r2, t2 + o[h], o[h + 1] - o[h]));
|
|
}, e.CFF.tableSE = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 0, 111, 112, 113, 114, 0, 115, 116, 117, 118, 119, 120, 121, 122, 0, 123, 0, 124, 125, 126, 127, 128, 129, 130, 131, 0, 132, 133, 0, 134, 135, 136, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 0, 139, 0, 0, 0, 0, 140, 141, 142, 143, 0, 0, 0, 0, 0, 144, 0, 0, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 0, 0], e.CFF.glyphByUnicode = function(r2, e2) {
|
|
for (var t2 = 0; t2 < r2.charset.length; t2++) if (r2.charset[t2] == e2) return t2;
|
|
return -1;
|
|
}, e.CFF.glyphBySE = function(r2, t2) {
|
|
return t2 < 0 || t2 > 255 ? -1 : e.CFF.glyphByUnicode(r2, e.CFF.tableSE[t2]);
|
|
}, e.CFF.readEncoding = function(r2, t2, a2) {
|
|
e._bin;
|
|
var n = [".notdef"], o = r2[t2];
|
|
if (t2++, 0 != o) throw "error: unknown encoding format: " + o;
|
|
var s = r2[t2];
|
|
t2++;
|
|
for (var i = 0; i < s; i++) n.push(r2[t2 + i]);
|
|
return n;
|
|
}, e.CFF.readCharset = function(r2, t2, a2) {
|
|
var n = e._bin, o = [".notdef"], s = r2[t2];
|
|
if (t2++, 0 == s) for (var i = 0; i < a2; i++) {
|
|
var h = n.readUshort(r2, t2);
|
|
t2 += 2, o.push(h);
|
|
}
|
|
else {
|
|
if (1 != s && 2 != s) throw "error: format: " + s;
|
|
for (; o.length < a2; ) {
|
|
h = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var d = 0;
|
|
1 == s ? (d = r2[t2], t2++) : (d = n.readUshort(r2, t2), t2 += 2);
|
|
for (i = 0; i <= d; i++) o.push(h), h++;
|
|
}
|
|
}
|
|
return o;
|
|
}, e.CFF.readIndex = function(r2, t2, a2) {
|
|
var n = e._bin, o = n.readUshort(r2, t2) + 1, s = r2[t2 += 2];
|
|
if (t2++, 1 == s) for (var i = 0; i < o; i++) a2.push(r2[t2 + i]);
|
|
else if (2 == s) for (i = 0; i < o; i++) a2.push(n.readUshort(r2, t2 + 2 * i));
|
|
else if (3 == s) for (i = 0; i < o; i++) a2.push(16777215 & n.readUint(r2, t2 + 3 * i - 1));
|
|
else if (1 != o) throw "unsupported offset size: " + s + ", count: " + o;
|
|
return (t2 += o * s) - 1;
|
|
}, e.CFF.getCharString = function(r2, t2, a2) {
|
|
var n = e._bin, o = r2[t2], s = r2[t2 + 1];
|
|
r2[t2 + 2], r2[t2 + 3], r2[t2 + 4];
|
|
var i = 1, h = null, d = null;
|
|
o <= 20 && (h = o, i = 1), 12 == o && (h = 100 * o + s, i = 2), 21 <= o && o <= 27 && (h = o, i = 1), 28 == o && (d = n.readShort(r2, t2 + 1), i = 3), 29 <= o && o <= 31 && (h = o, i = 1), 32 <= o && o <= 246 && (d = o - 139, i = 1), 247 <= o && o <= 250 && (d = 256 * (o - 247) + s + 108, i = 2), 251 <= o && o <= 254 && (d = 256 * -(o - 251) - s - 108, i = 2), 255 == o && (d = n.readInt(r2, t2 + 1) / 65535, i = 5), a2.val = null != d ? d : "o" + h, a2.size = i;
|
|
}, e.CFF.readCharString = function(r2, t2, a2) {
|
|
for (var n = t2 + a2, o = e._bin, s = []; t2 < n; ) {
|
|
var i = r2[t2], h = r2[t2 + 1];
|
|
r2[t2 + 2], r2[t2 + 3], r2[t2 + 4];
|
|
var d = 1, f = null, u = null;
|
|
i <= 20 && (f = i, d = 1), 12 == i && (f = 100 * i + h, d = 2), 19 != i && 20 != i || (f = i, d = 2), 21 <= i && i <= 27 && (f = i, d = 1), 28 == i && (u = o.readShort(r2, t2 + 1), d = 3), 29 <= i && i <= 31 && (f = i, d = 1), 32 <= i && i <= 246 && (u = i - 139, d = 1), 247 <= i && i <= 250 && (u = 256 * (i - 247) + h + 108, d = 2), 251 <= i && i <= 254 && (u = 256 * -(i - 251) - h - 108, d = 2), 255 == i && (u = o.readInt(r2, t2 + 1) / 65535, d = 5), s.push(null != u ? u : "o" + f), t2 += d;
|
|
}
|
|
return s;
|
|
}, e.CFF.readDict = function(r2, t2, a2) {
|
|
for (var n = e._bin, o = {}, s = []; t2 < a2; ) {
|
|
var i = r2[t2], h = r2[t2 + 1];
|
|
r2[t2 + 2], r2[t2 + 3], r2[t2 + 4];
|
|
var d = 1, f = null, u = null;
|
|
if (28 == i && (u = n.readShort(r2, t2 + 1), d = 3), 29 == i && (u = n.readInt(r2, t2 + 1), d = 5), 32 <= i && i <= 246 && (u = i - 139, d = 1), 247 <= i && i <= 250 && (u = 256 * (i - 247) + h + 108, d = 2), 251 <= i && i <= 254 && (u = 256 * -(i - 251) - h - 108, d = 2), 255 == i) throw u = n.readInt(r2, t2 + 1) / 65535, d = 5, "unknown number";
|
|
if (30 == i) {
|
|
var l = [];
|
|
for (d = 1; ; ) {
|
|
var v = r2[t2 + d];
|
|
d++;
|
|
var c = v >> 4, p = 15 & v;
|
|
if (15 != c && l.push(c), 15 != p && l.push(p), 15 == p) break;
|
|
}
|
|
for (var U = "", g = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ".", "e", "e-", "reserved", "-", "endOfNumber"], S = 0; S < l.length; S++) U += g[l[S]];
|
|
u = parseFloat(U);
|
|
}
|
|
if (i <= 21) {
|
|
if (f = ["version", "Notice", "FullName", "FamilyName", "Weight", "FontBBox", "BlueValues", "OtherBlues", "FamilyBlues", "FamilyOtherBlues", "StdHW", "StdVW", "escape", "UniqueID", "XUID", "charset", "Encoding", "CharStrings", "Private", "Subrs", "defaultWidthX", "nominalWidthX"][i], d = 1, 12 == i) f = ["Copyright", "isFixedPitch", "ItalicAngle", "UnderlinePosition", "UnderlineThickness", "PaintType", "CharstringType", "FontMatrix", "StrokeWidth", "BlueScale", "BlueShift", "BlueFuzz", "StemSnapH", "StemSnapV", "ForceBold", 0, 0, "LanguageGroup", "ExpansionFactor", "initialRandomSeed", "SyntheticBase", "PostScript", "BaseFontName", "BaseFontBlend", 0, 0, 0, 0, 0, 0, "ROS", "CIDFontVersion", "CIDFontRevision", "CIDFontType", "CIDCount", "UIDBase", "FDArray", "FDSelect", "FontName"][h], d = 2;
|
|
}
|
|
null != f ? (o[f] = 1 == s.length ? s[0] : s, s = []) : s.push(u), t2 += d;
|
|
}
|
|
return o;
|
|
}, e.cmap = {}, e.cmap.parse = function(r2, t2, a2) {
|
|
r2 = new Uint8Array(r2.buffer, t2, a2), t2 = 0;
|
|
var n = e._bin, o = {};
|
|
n.readUshort(r2, t2), t2 += 2;
|
|
var s = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var i = [];
|
|
o.tables = [];
|
|
for (var h = 0; h < s; h++) {
|
|
var d = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var f = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var u = n.readUint(r2, t2);
|
|
t2 += 4;
|
|
var l = "p" + d + "e" + f, v = i.indexOf(u);
|
|
if (-1 == v) {
|
|
var c;
|
|
v = o.tables.length, i.push(u);
|
|
var p = n.readUshort(r2, u);
|
|
0 == p ? c = e.cmap.parse0(r2, u) : 4 == p ? c = e.cmap.parse4(r2, u) : 6 == p ? c = e.cmap.parse6(r2, u) : 12 == p ? c = e.cmap.parse12(r2, u) : console.debug("unknown format: " + p, d, f, u), o.tables.push(c);
|
|
}
|
|
if (null != o[l]) throw "multiple tables for one platform+encoding";
|
|
o[l] = v;
|
|
}
|
|
return o;
|
|
}, e.cmap.parse0 = function(r2, t2) {
|
|
var a2 = e._bin, n = {};
|
|
n.format = a2.readUshort(r2, t2), t2 += 2;
|
|
var o = a2.readUshort(r2, t2);
|
|
t2 += 2, a2.readUshort(r2, t2), t2 += 2, n.map = [];
|
|
for (var s = 0; s < o - 6; s++) n.map.push(r2[t2 + s]);
|
|
return n;
|
|
}, e.cmap.parse4 = function(r2, t2) {
|
|
var a2 = e._bin, n = t2, o = {};
|
|
o.format = a2.readUshort(r2, t2), t2 += 2;
|
|
var s = a2.readUshort(r2, t2);
|
|
t2 += 2, a2.readUshort(r2, t2), t2 += 2;
|
|
var i = a2.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var h = i / 2;
|
|
o.searchRange = a2.readUshort(r2, t2), t2 += 2, o.entrySelector = a2.readUshort(r2, t2), t2 += 2, o.rangeShift = a2.readUshort(r2, t2), t2 += 2, o.endCount = a2.readUshorts(r2, t2, h), t2 += 2 * h, t2 += 2, o.startCount = a2.readUshorts(r2, t2, h), t2 += 2 * h, o.idDelta = [];
|
|
for (var d = 0; d < h; d++) o.idDelta.push(a2.readShort(r2, t2)), t2 += 2;
|
|
for (o.idRangeOffset = a2.readUshorts(r2, t2, h), t2 += 2 * h, o.glyphIdArray = []; t2 < n + s; ) o.glyphIdArray.push(a2.readUshort(r2, t2)), t2 += 2;
|
|
return o;
|
|
}, e.cmap.parse6 = function(r2, t2) {
|
|
var a2 = e._bin, n = {};
|
|
n.format = a2.readUshort(r2, t2), t2 += 2, a2.readUshort(r2, t2), t2 += 2, a2.readUshort(r2, t2), t2 += 2, n.firstCode = a2.readUshort(r2, t2), t2 += 2;
|
|
var o = a2.readUshort(r2, t2);
|
|
t2 += 2, n.glyphIdArray = [];
|
|
for (var s = 0; s < o; s++) n.glyphIdArray.push(a2.readUshort(r2, t2)), t2 += 2;
|
|
return n;
|
|
}, e.cmap.parse12 = function(r2, t2) {
|
|
var a2 = e._bin, n = {};
|
|
n.format = a2.readUshort(r2, t2), t2 += 2, t2 += 2, a2.readUint(r2, t2), t2 += 4, a2.readUint(r2, t2), t2 += 4;
|
|
var o = a2.readUint(r2, t2);
|
|
t2 += 4, n.groups = [];
|
|
for (var s = 0; s < o; s++) {
|
|
var i = t2 + 12 * s, h = a2.readUint(r2, i + 0), d = a2.readUint(r2, i + 4), f = a2.readUint(r2, i + 8);
|
|
n.groups.push([h, d, f]);
|
|
}
|
|
return n;
|
|
}, e.glyf = {}, e.glyf.parse = function(r2, e2, t2, a2) {
|
|
for (var n = [], o = 0; o < a2.maxp.numGlyphs; o++) n.push(null);
|
|
return n;
|
|
}, e.glyf._parseGlyf = function(r2, t2) {
|
|
var a2 = e._bin, n = r2._data, o = e._tabOffset(n, "glyf", r2._offset) + r2.loca[t2];
|
|
if (r2.loca[t2] == r2.loca[t2 + 1]) return null;
|
|
var s = {};
|
|
if (s.noc = a2.readShort(n, o), o += 2, s.xMin = a2.readShort(n, o), o += 2, s.yMin = a2.readShort(n, o), o += 2, s.xMax = a2.readShort(n, o), o += 2, s.yMax = a2.readShort(n, o), o += 2, s.xMin >= s.xMax || s.yMin >= s.yMax) return null;
|
|
if (s.noc > 0) {
|
|
s.endPts = [];
|
|
for (var i = 0; i < s.noc; i++) s.endPts.push(a2.readUshort(n, o)), o += 2;
|
|
var h = a2.readUshort(n, o);
|
|
if (o += 2, n.length - o < h) return null;
|
|
s.instructions = a2.readBytes(n, o, h), o += h;
|
|
var d = s.endPts[s.noc - 1] + 1;
|
|
s.flags = [];
|
|
for (i = 0; i < d; i++) {
|
|
var f = n[o];
|
|
if (o++, s.flags.push(f), 0 != (8 & f)) {
|
|
var u = n[o];
|
|
o++;
|
|
for (var l = 0; l < u; l++) s.flags.push(f), i++;
|
|
}
|
|
}
|
|
s.xs = [];
|
|
for (i = 0; i < d; i++) {
|
|
var v = 0 != (2 & s.flags[i]), c = 0 != (16 & s.flags[i]);
|
|
v ? (s.xs.push(c ? n[o] : -n[o]), o++) : c ? s.xs.push(0) : (s.xs.push(a2.readShort(n, o)), o += 2);
|
|
}
|
|
s.ys = [];
|
|
for (i = 0; i < d; i++) {
|
|
v = 0 != (4 & s.flags[i]), c = 0 != (32 & s.flags[i]);
|
|
v ? (s.ys.push(c ? n[o] : -n[o]), o++) : c ? s.ys.push(0) : (s.ys.push(a2.readShort(n, o)), o += 2);
|
|
}
|
|
var p = 0, U = 0;
|
|
for (i = 0; i < d; i++) p += s.xs[i], U += s.ys[i], s.xs[i] = p, s.ys[i] = U;
|
|
} else {
|
|
var g;
|
|
s.parts = [];
|
|
do {
|
|
g = a2.readUshort(n, o), o += 2;
|
|
var S = { m: { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 }, p1: -1, p2: -1 };
|
|
if (s.parts.push(S), S.glyphIndex = a2.readUshort(n, o), o += 2, 1 & g) {
|
|
var m = a2.readShort(n, o);
|
|
o += 2;
|
|
var b = a2.readShort(n, o);
|
|
o += 2;
|
|
} else {
|
|
m = a2.readInt8(n, o);
|
|
o++;
|
|
b = a2.readInt8(n, o);
|
|
o++;
|
|
}
|
|
2 & g ? (S.m.tx = m, S.m.ty = b) : (S.p1 = m, S.p2 = b), 8 & g ? (S.m.a = S.m.d = a2.readF2dot14(n, o), o += 2) : 64 & g ? (S.m.a = a2.readF2dot14(n, o), o += 2, S.m.d = a2.readF2dot14(n, o), o += 2) : 128 & g && (S.m.a = a2.readF2dot14(n, o), o += 2, S.m.b = a2.readF2dot14(n, o), o += 2, S.m.c = a2.readF2dot14(n, o), o += 2, S.m.d = a2.readF2dot14(n, o), o += 2);
|
|
} while (32 & g);
|
|
if (256 & g) {
|
|
var y = a2.readUshort(n, o);
|
|
o += 2, s.instr = [];
|
|
for (i = 0; i < y; i++) s.instr.push(n[o]), o++;
|
|
}
|
|
}
|
|
return s;
|
|
}, e.GDEF = {}, e.GDEF.parse = function(r2, t2, a2, n) {
|
|
var o = t2;
|
|
t2 += 4;
|
|
var s = e._bin.readUshort(r2, t2);
|
|
return { glyphClassDef: 0 === s ? null : e._lctf.readClassDef(r2, o + s) };
|
|
}, e.GPOS = {}, e.GPOS.parse = function(r2, t2, a2, n) {
|
|
return e._lctf.parse(r2, t2, a2, n, e.GPOS.subt);
|
|
}, e.GPOS.subt = function(r2, t2, a2, n) {
|
|
var o = e._bin, s = a2, i = {};
|
|
if (i.fmt = o.readUshort(r2, a2), a2 += 2, 1 == t2 || 2 == t2 || 3 == t2 || 7 == t2 || 8 == t2 && i.fmt <= 2) {
|
|
var h = o.readUshort(r2, a2);
|
|
a2 += 2, i.coverage = e._lctf.readCoverage(r2, h + s);
|
|
}
|
|
if (1 == t2 && 1 == i.fmt) {
|
|
var d = o.readUshort(r2, a2);
|
|
a2 += 2, 0 != d && (i.pos = e.GPOS.readValueRecord(r2, a2, d));
|
|
} else if (2 == t2 && i.fmt >= 1 && i.fmt <= 2) {
|
|
d = o.readUshort(r2, a2);
|
|
a2 += 2;
|
|
var f = o.readUshort(r2, a2);
|
|
a2 += 2;
|
|
var u = e._lctf.numOfOnes(d), l = e._lctf.numOfOnes(f);
|
|
if (1 == i.fmt) {
|
|
i.pairsets = [];
|
|
var v = o.readUshort(r2, a2);
|
|
a2 += 2;
|
|
for (var c = 0; c < v; c++) {
|
|
var p = s + o.readUshort(r2, a2);
|
|
a2 += 2;
|
|
var U = o.readUshort(r2, p);
|
|
p += 2;
|
|
for (var g = [], S = 0; S < U; S++) {
|
|
var m = o.readUshort(r2, p);
|
|
p += 2, 0 != d && (P = e.GPOS.readValueRecord(r2, p, d), p += 2 * u), 0 != f && (x = e.GPOS.readValueRecord(r2, p, f), p += 2 * l), g.push({ gid2: m, val1: P, val2: x });
|
|
}
|
|
i.pairsets.push(g);
|
|
}
|
|
}
|
|
if (2 == i.fmt) {
|
|
var b = o.readUshort(r2, a2);
|
|
a2 += 2;
|
|
var y = o.readUshort(r2, a2);
|
|
a2 += 2;
|
|
var F = o.readUshort(r2, a2);
|
|
a2 += 2;
|
|
var C = o.readUshort(r2, a2);
|
|
a2 += 2, i.classDef1 = e._lctf.readClassDef(r2, s + b), i.classDef2 = e._lctf.readClassDef(r2, s + y), i.matrix = [];
|
|
for (c = 0; c < F; c++) {
|
|
var _ = [];
|
|
for (S = 0; S < C; S++) {
|
|
var P = null, x = null;
|
|
0 != d && (P = e.GPOS.readValueRecord(r2, a2, d), a2 += 2 * u), 0 != f && (x = e.GPOS.readValueRecord(r2, a2, f), a2 += 2 * l), _.push({ val1: P, val2: x });
|
|
}
|
|
i.matrix.push(_);
|
|
}
|
|
}
|
|
} else if (4 == t2 && 1 == i.fmt) i.markCoverage = e._lctf.readCoverage(r2, o.readUshort(r2, a2) + s), i.baseCoverage = e._lctf.readCoverage(r2, o.readUshort(r2, a2 + 2) + s), i.markClassCount = o.readUshort(r2, a2 + 4), i.markArray = e.GPOS.readMarkArray(r2, o.readUshort(r2, a2 + 6) + s), i.baseArray = e.GPOS.readBaseArray(r2, o.readUshort(r2, a2 + 8) + s, i.markClassCount);
|
|
else if (6 == t2 && 1 == i.fmt) i.mark1Coverage = e._lctf.readCoverage(r2, o.readUshort(r2, a2) + s), i.mark2Coverage = e._lctf.readCoverage(r2, o.readUshort(r2, a2 + 2) + s), i.markClassCount = o.readUshort(r2, a2 + 4), i.mark1Array = e.GPOS.readMarkArray(r2, o.readUshort(r2, a2 + 6) + s), i.mark2Array = e.GPOS.readBaseArray(r2, o.readUshort(r2, a2 + 8) + s, i.markClassCount);
|
|
else {
|
|
if (9 == t2 && 1 == i.fmt) {
|
|
var I = o.readUshort(r2, a2);
|
|
a2 += 2;
|
|
var w = o.readUint(r2, a2);
|
|
if (a2 += 4, 9 == n.ltype) n.ltype = I;
|
|
else if (n.ltype != I) throw "invalid extension substitution";
|
|
return e.GPOS.subt(r2, n.ltype, s + w);
|
|
}
|
|
console.debug("unsupported GPOS table LookupType", t2, "format", i.fmt);
|
|
}
|
|
return i;
|
|
}, e.GPOS.readValueRecord = function(r2, t2, a2) {
|
|
var n = e._bin, o = [];
|
|
return o.push(1 & a2 ? n.readShort(r2, t2) : 0), t2 += 1 & a2 ? 2 : 0, o.push(2 & a2 ? n.readShort(r2, t2) : 0), t2 += 2 & a2 ? 2 : 0, o.push(4 & a2 ? n.readShort(r2, t2) : 0), t2 += 4 & a2 ? 2 : 0, o.push(8 & a2 ? n.readShort(r2, t2) : 0), t2 += 8 & a2 ? 2 : 0, o;
|
|
}, e.GPOS.readBaseArray = function(r2, t2, a2) {
|
|
var n = e._bin, o = [], s = t2, i = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (var h = 0; h < i; h++) {
|
|
for (var d = [], f = 0; f < a2; f++) d.push(e.GPOS.readAnchorRecord(r2, s + n.readUshort(r2, t2))), t2 += 2;
|
|
o.push(d);
|
|
}
|
|
return o;
|
|
}, e.GPOS.readMarkArray = function(r2, t2) {
|
|
var a2 = e._bin, n = [], o = t2, s = a2.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (var i = 0; i < s; i++) {
|
|
var h = e.GPOS.readAnchorRecord(r2, a2.readUshort(r2, t2 + 2) + o);
|
|
h.markClass = a2.readUshort(r2, t2), n.push(h), t2 += 4;
|
|
}
|
|
return n;
|
|
}, e.GPOS.readAnchorRecord = function(r2, t2) {
|
|
var a2 = e._bin, n = {};
|
|
return n.fmt = a2.readUshort(r2, t2), n.x = a2.readShort(r2, t2 + 2), n.y = a2.readShort(r2, t2 + 4), n;
|
|
}, e.GSUB = {}, e.GSUB.parse = function(r2, t2, a2, n) {
|
|
return e._lctf.parse(r2, t2, a2, n, e.GSUB.subt);
|
|
}, e.GSUB.subt = function(r2, t2, a2, n) {
|
|
var o = e._bin, s = a2, i = {};
|
|
if (i.fmt = o.readUshort(r2, a2), a2 += 2, 1 != t2 && 2 != t2 && 4 != t2 && 5 != t2 && 6 != t2) return null;
|
|
if (1 == t2 || 2 == t2 || 4 == t2 || 5 == t2 && i.fmt <= 2 || 6 == t2 && i.fmt <= 2) {
|
|
var h = o.readUshort(r2, a2);
|
|
a2 += 2, i.coverage = e._lctf.readCoverage(r2, s + h);
|
|
}
|
|
if (1 == t2 && i.fmt >= 1 && i.fmt <= 2) {
|
|
if (1 == i.fmt) i.delta = o.readShort(r2, a2), a2 += 2;
|
|
else if (2 == i.fmt) {
|
|
var d = o.readUshort(r2, a2);
|
|
a2 += 2, i.newg = o.readUshorts(r2, a2, d), a2 += 2 * i.newg.length;
|
|
}
|
|
} else if (2 == t2 && 1 == i.fmt) {
|
|
d = o.readUshort(r2, a2);
|
|
a2 += 2, i.seqs = [];
|
|
for (var f = 0; f < d; f++) {
|
|
var u = o.readUshort(r2, a2) + s;
|
|
a2 += 2;
|
|
var l = o.readUshort(r2, u);
|
|
i.seqs.push(o.readUshorts(r2, u + 2, l));
|
|
}
|
|
} else if (4 == t2) {
|
|
i.vals = [];
|
|
d = o.readUshort(r2, a2);
|
|
a2 += 2;
|
|
for (f = 0; f < d; f++) {
|
|
var v = o.readUshort(r2, a2);
|
|
a2 += 2, i.vals.push(e.GSUB.readLigatureSet(r2, s + v));
|
|
}
|
|
} else if (5 == t2 && 2 == i.fmt) {
|
|
if (2 == i.fmt) {
|
|
var c = o.readUshort(r2, a2);
|
|
a2 += 2, i.cDef = e._lctf.readClassDef(r2, s + c), i.scset = [];
|
|
var p = o.readUshort(r2, a2);
|
|
a2 += 2;
|
|
for (f = 0; f < p; f++) {
|
|
var U = o.readUshort(r2, a2);
|
|
a2 += 2, i.scset.push(0 == U ? null : e.GSUB.readSubClassSet(r2, s + U));
|
|
}
|
|
}
|
|
} else if (6 == t2 && 3 == i.fmt) {
|
|
if (3 == i.fmt) {
|
|
for (f = 0; f < 3; f++) {
|
|
d = o.readUshort(r2, a2);
|
|
a2 += 2;
|
|
for (var g = [], S = 0; S < d; S++) g.push(e._lctf.readCoverage(r2, s + o.readUshort(r2, a2 + 2 * S)));
|
|
a2 += 2 * d, 0 == f && (i.backCvg = g), 1 == f && (i.inptCvg = g), 2 == f && (i.ahedCvg = g);
|
|
}
|
|
d = o.readUshort(r2, a2);
|
|
a2 += 2, i.lookupRec = e.GSUB.readSubstLookupRecords(r2, a2, d);
|
|
}
|
|
} else {
|
|
if (7 == t2 && 1 == i.fmt) {
|
|
var m = o.readUshort(r2, a2);
|
|
a2 += 2;
|
|
var b = o.readUint(r2, a2);
|
|
if (a2 += 4, 9 == n.ltype) n.ltype = m;
|
|
else if (n.ltype != m) throw "invalid extension substitution";
|
|
return e.GSUB.subt(r2, n.ltype, s + b);
|
|
}
|
|
console.debug("unsupported GSUB table LookupType", t2, "format", i.fmt);
|
|
}
|
|
return i;
|
|
}, e.GSUB.readSubClassSet = function(r2, t2) {
|
|
var a2 = e._bin.readUshort, n = t2, o = [], s = a2(r2, t2);
|
|
t2 += 2;
|
|
for (var i = 0; i < s; i++) {
|
|
var h = a2(r2, t2);
|
|
t2 += 2, o.push(e.GSUB.readSubClassRule(r2, n + h));
|
|
}
|
|
return o;
|
|
}, e.GSUB.readSubClassRule = function(r2, t2) {
|
|
var a2 = e._bin.readUshort, n = {}, o = a2(r2, t2), s = a2(r2, t2 += 2);
|
|
t2 += 2, n.input = [];
|
|
for (var i = 0; i < o - 1; i++) n.input.push(a2(r2, t2)), t2 += 2;
|
|
return n.substLookupRecords = e.GSUB.readSubstLookupRecords(r2, t2, s), n;
|
|
}, e.GSUB.readSubstLookupRecords = function(r2, t2, a2) {
|
|
for (var n = e._bin.readUshort, o = [], s = 0; s < a2; s++) o.push(n(r2, t2), n(r2, t2 + 2)), t2 += 4;
|
|
return o;
|
|
}, e.GSUB.readChainSubClassSet = function(r2, t2) {
|
|
var a2 = e._bin, n = t2, o = [], s = a2.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (var i = 0; i < s; i++) {
|
|
var h = a2.readUshort(r2, t2);
|
|
t2 += 2, o.push(e.GSUB.readChainSubClassRule(r2, n + h));
|
|
}
|
|
return o;
|
|
}, e.GSUB.readChainSubClassRule = function(r2, t2) {
|
|
for (var a2 = e._bin, n = {}, o = ["backtrack", "input", "lookahead"], s = 0; s < o.length; s++) {
|
|
var i = a2.readUshort(r2, t2);
|
|
t2 += 2, 1 == s && i--, n[o[s]] = a2.readUshorts(r2, t2, i), t2 += 2 * n[o[s]].length;
|
|
}
|
|
i = a2.readUshort(r2, t2);
|
|
return t2 += 2, n.subst = a2.readUshorts(r2, t2, 2 * i), t2 += 2 * n.subst.length, n;
|
|
}, e.GSUB.readLigatureSet = function(r2, t2) {
|
|
var a2 = e._bin, n = t2, o = [], s = a2.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (var i = 0; i < s; i++) {
|
|
var h = a2.readUshort(r2, t2);
|
|
t2 += 2, o.push(e.GSUB.readLigature(r2, n + h));
|
|
}
|
|
return o;
|
|
}, e.GSUB.readLigature = function(r2, t2) {
|
|
var a2 = e._bin, n = { chain: [] };
|
|
n.nglyph = a2.readUshort(r2, t2), t2 += 2;
|
|
var o = a2.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (var s = 0; s < o - 1; s++) n.chain.push(a2.readUshort(r2, t2)), t2 += 2;
|
|
return n;
|
|
}, e.head = {}, e.head.parse = function(r2, t2, a2) {
|
|
var n = e._bin, o = {};
|
|
return n.readFixed(r2, t2), t2 += 4, o.fontRevision = n.readFixed(r2, t2), t2 += 4, n.readUint(r2, t2), t2 += 4, n.readUint(r2, t2), t2 += 4, o.flags = n.readUshort(r2, t2), t2 += 2, o.unitsPerEm = n.readUshort(r2, t2), t2 += 2, o.created = n.readUint64(r2, t2), t2 += 8, o.modified = n.readUint64(r2, t2), t2 += 8, o.xMin = n.readShort(r2, t2), t2 += 2, o.yMin = n.readShort(r2, t2), t2 += 2, o.xMax = n.readShort(r2, t2), t2 += 2, o.yMax = n.readShort(r2, t2), t2 += 2, o.macStyle = n.readUshort(r2, t2), t2 += 2, o.lowestRecPPEM = n.readUshort(r2, t2), t2 += 2, o.fontDirectionHint = n.readShort(r2, t2), t2 += 2, o.indexToLocFormat = n.readShort(r2, t2), t2 += 2, o.glyphDataFormat = n.readShort(r2, t2), t2 += 2, o;
|
|
}, e.hhea = {}, e.hhea.parse = function(r2, t2, a2) {
|
|
var n = e._bin, o = {};
|
|
return n.readFixed(r2, t2), t2 += 4, o.ascender = n.readShort(r2, t2), t2 += 2, o.descender = n.readShort(r2, t2), t2 += 2, o.lineGap = n.readShort(r2, t2), t2 += 2, o.advanceWidthMax = n.readUshort(r2, t2), t2 += 2, o.minLeftSideBearing = n.readShort(r2, t2), t2 += 2, o.minRightSideBearing = n.readShort(r2, t2), t2 += 2, o.xMaxExtent = n.readShort(r2, t2), t2 += 2, o.caretSlopeRise = n.readShort(r2, t2), t2 += 2, o.caretSlopeRun = n.readShort(r2, t2), t2 += 2, o.caretOffset = n.readShort(r2, t2), t2 += 2, t2 += 8, o.metricDataFormat = n.readShort(r2, t2), t2 += 2, o.numberOfHMetrics = n.readUshort(r2, t2), t2 += 2, o;
|
|
}, e.hmtx = {}, e.hmtx.parse = function(r2, t2, a2, n) {
|
|
for (var o = e._bin, s = { aWidth: [], lsBearing: [] }, i = 0, h = 0, d = 0; d < n.maxp.numGlyphs; d++) d < n.hhea.numberOfHMetrics && (i = o.readUshort(r2, t2), t2 += 2, h = o.readShort(r2, t2), t2 += 2), s.aWidth.push(i), s.lsBearing.push(h);
|
|
return s;
|
|
}, e.kern = {}, e.kern.parse = function(r2, t2, a2, n) {
|
|
var o = e._bin, s = o.readUshort(r2, t2);
|
|
if (t2 += 2, 1 == s) return e.kern.parseV1(r2, t2 - 2, a2, n);
|
|
var i = o.readUshort(r2, t2);
|
|
t2 += 2;
|
|
for (var h = { glyph1: [], rval: [] }, d = 0; d < i; d++) {
|
|
t2 += 2;
|
|
a2 = o.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var f = o.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var u = f >>> 8;
|
|
if (0 != (u &= 15)) throw "unknown kern table format: " + u;
|
|
t2 = e.kern.readFormat0(r2, t2, h);
|
|
}
|
|
return h;
|
|
}, e.kern.parseV1 = function(r2, t2, a2, n) {
|
|
var o = e._bin;
|
|
o.readFixed(r2, t2), t2 += 4;
|
|
var s = o.readUint(r2, t2);
|
|
t2 += 4;
|
|
for (var i = { glyph1: [], rval: [] }, h = 0; h < s; h++) {
|
|
o.readUint(r2, t2), t2 += 4;
|
|
var d = o.readUshort(r2, t2);
|
|
t2 += 2, o.readUshort(r2, t2), t2 += 2;
|
|
var f = d >>> 8;
|
|
if (0 != (f &= 15)) throw "unknown kern table format: " + f;
|
|
t2 = e.kern.readFormat0(r2, t2, i);
|
|
}
|
|
return i;
|
|
}, e.kern.readFormat0 = function(r2, t2, a2) {
|
|
var n = e._bin, o = -1, s = n.readUshort(r2, t2);
|
|
t2 += 2, n.readUshort(r2, t2), t2 += 2, n.readUshort(r2, t2), t2 += 2, n.readUshort(r2, t2), t2 += 2;
|
|
for (var i = 0; i < s; i++) {
|
|
var h = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var d = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var f = n.readShort(r2, t2);
|
|
t2 += 2, h != o && (a2.glyph1.push(h), a2.rval.push({ glyph2: [], vals: [] }));
|
|
var u = a2.rval[a2.rval.length - 1];
|
|
u.glyph2.push(d), u.vals.push(f), o = h;
|
|
}
|
|
return t2;
|
|
}, e.loca = {}, e.loca.parse = function(r2, t2, a2, n) {
|
|
var o = e._bin, s = [], i = n.head.indexToLocFormat, h = n.maxp.numGlyphs + 1;
|
|
if (0 == i) for (var d = 0; d < h; d++) s.push(o.readUshort(r2, t2 + (d << 1)) << 1);
|
|
if (1 == i) for (d = 0; d < h; d++) s.push(o.readUint(r2, t2 + (d << 2)));
|
|
return s;
|
|
}, e.maxp = {}, e.maxp.parse = function(r2, t2, a2) {
|
|
var n = e._bin, o = {}, s = n.readUint(r2, t2);
|
|
return t2 += 4, o.numGlyphs = n.readUshort(r2, t2), t2 += 2, 65536 == s && (o.maxPoints = n.readUshort(r2, t2), t2 += 2, o.maxContours = n.readUshort(r2, t2), t2 += 2, o.maxCompositePoints = n.readUshort(r2, t2), t2 += 2, o.maxCompositeContours = n.readUshort(r2, t2), t2 += 2, o.maxZones = n.readUshort(r2, t2), t2 += 2, o.maxTwilightPoints = n.readUshort(r2, t2), t2 += 2, o.maxStorage = n.readUshort(r2, t2), t2 += 2, o.maxFunctionDefs = n.readUshort(r2, t2), t2 += 2, o.maxInstructionDefs = n.readUshort(r2, t2), t2 += 2, o.maxStackElements = n.readUshort(r2, t2), t2 += 2, o.maxSizeOfInstructions = n.readUshort(r2, t2), t2 += 2, o.maxComponentElements = n.readUshort(r2, t2), t2 += 2, o.maxComponentDepth = n.readUshort(r2, t2), t2 += 2), o;
|
|
}, e.name = {}, e.name.parse = function(r2, t2, a2) {
|
|
var n = e._bin, o = {};
|
|
n.readUshort(r2, t2), t2 += 2;
|
|
var s = n.readUshort(r2, t2);
|
|
t2 += 2, n.readUshort(r2, t2);
|
|
for (var i, h = ["copyright", "fontFamily", "fontSubfamily", "ID", "fullName", "version", "postScriptName", "trademark", "manufacturer", "designer", "description", "urlVendor", "urlDesigner", "licence", "licenceURL", "---", "typoFamilyName", "typoSubfamilyName", "compatibleFull", "sampleText", "postScriptCID", "wwsFamilyName", "wwsSubfamilyName", "lightPalette", "darkPalette"], d = t2 += 2, f = 0; f < s; f++) {
|
|
var u = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var l = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var v = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var c = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var p = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var U = n.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var g, S = h[c], m = d + 12 * s + U;
|
|
if (0 == u) g = n.readUnicode(r2, m, p / 2);
|
|
else if (3 == u && 0 == l) g = n.readUnicode(r2, m, p / 2);
|
|
else if (0 == l) g = n.readASCII(r2, m, p);
|
|
else if (1 == l) g = n.readUnicode(r2, m, p / 2);
|
|
else if (3 == l) g = n.readUnicode(r2, m, p / 2);
|
|
else {
|
|
if (1 != u) throw "unknown encoding " + l + ", platformID: " + u;
|
|
g = n.readASCII(r2, m, p), console.debug("reading unknown MAC encoding " + l + " as ASCII");
|
|
}
|
|
var b = "p" + u + "," + v.toString(16);
|
|
null == o[b] && (o[b] = {}), o[b][void 0 !== S ? S : c] = g, o[b]._lang = v;
|
|
}
|
|
for (var y in o) if (null != o[y].postScriptName && 1033 == o[y]._lang) return o[y];
|
|
for (var y in o) if (null != o[y].postScriptName && 0 == o[y]._lang) return o[y];
|
|
for (var y in o) if (null != o[y].postScriptName && 3084 == o[y]._lang) return o[y];
|
|
for (var y in o) if (null != o[y].postScriptName) return o[y];
|
|
for (var y in o) {
|
|
i = y;
|
|
break;
|
|
}
|
|
return console.debug("returning name table with languageID " + o[i]._lang), o[i];
|
|
}, e["OS/2"] = {}, e["OS/2"].parse = function(r2, t2, a2) {
|
|
var n = e._bin.readUshort(r2, t2);
|
|
t2 += 2;
|
|
var o = {};
|
|
if (0 == n) e["OS/2"].version0(r2, t2, o);
|
|
else if (1 == n) e["OS/2"].version1(r2, t2, o);
|
|
else if (2 == n || 3 == n || 4 == n) e["OS/2"].version2(r2, t2, o);
|
|
else {
|
|
if (5 != n) throw "unknown OS/2 table version: " + n;
|
|
e["OS/2"].version5(r2, t2, o);
|
|
}
|
|
return o;
|
|
}, e["OS/2"].version0 = function(r2, t2, a2) {
|
|
var n = e._bin;
|
|
return a2.xAvgCharWidth = n.readShort(r2, t2), t2 += 2, a2.usWeightClass = n.readUshort(r2, t2), t2 += 2, a2.usWidthClass = n.readUshort(r2, t2), t2 += 2, a2.fsType = n.readUshort(r2, t2), t2 += 2, a2.ySubscriptXSize = n.readShort(r2, t2), t2 += 2, a2.ySubscriptYSize = n.readShort(r2, t2), t2 += 2, a2.ySubscriptXOffset = n.readShort(r2, t2), t2 += 2, a2.ySubscriptYOffset = n.readShort(r2, t2), t2 += 2, a2.ySuperscriptXSize = n.readShort(r2, t2), t2 += 2, a2.ySuperscriptYSize = n.readShort(r2, t2), t2 += 2, a2.ySuperscriptXOffset = n.readShort(r2, t2), t2 += 2, a2.ySuperscriptYOffset = n.readShort(r2, t2), t2 += 2, a2.yStrikeoutSize = n.readShort(r2, t2), t2 += 2, a2.yStrikeoutPosition = n.readShort(r2, t2), t2 += 2, a2.sFamilyClass = n.readShort(r2, t2), t2 += 2, a2.panose = n.readBytes(r2, t2, 10), t2 += 10, a2.ulUnicodeRange1 = n.readUint(r2, t2), t2 += 4, a2.ulUnicodeRange2 = n.readUint(r2, t2), t2 += 4, a2.ulUnicodeRange3 = n.readUint(r2, t2), t2 += 4, a2.ulUnicodeRange4 = n.readUint(r2, t2), t2 += 4, a2.achVendID = [n.readInt8(r2, t2), n.readInt8(r2, t2 + 1), n.readInt8(r2, t2 + 2), n.readInt8(r2, t2 + 3)], t2 += 4, a2.fsSelection = n.readUshort(r2, t2), t2 += 2, a2.usFirstCharIndex = n.readUshort(r2, t2), t2 += 2, a2.usLastCharIndex = n.readUshort(r2, t2), t2 += 2, a2.sTypoAscender = n.readShort(r2, t2), t2 += 2, a2.sTypoDescender = n.readShort(r2, t2), t2 += 2, a2.sTypoLineGap = n.readShort(r2, t2), t2 += 2, a2.usWinAscent = n.readUshort(r2, t2), t2 += 2, a2.usWinDescent = n.readUshort(r2, t2), t2 += 2;
|
|
}, e["OS/2"].version1 = function(r2, t2, a2) {
|
|
var n = e._bin;
|
|
return t2 = e["OS/2"].version0(r2, t2, a2), a2.ulCodePageRange1 = n.readUint(r2, t2), t2 += 4, a2.ulCodePageRange2 = n.readUint(r2, t2), t2 += 4;
|
|
}, e["OS/2"].version2 = function(r2, t2, a2) {
|
|
var n = e._bin;
|
|
return t2 = e["OS/2"].version1(r2, t2, a2), a2.sxHeight = n.readShort(r2, t2), t2 += 2, a2.sCapHeight = n.readShort(r2, t2), t2 += 2, a2.usDefault = n.readUshort(r2, t2), t2 += 2, a2.usBreak = n.readUshort(r2, t2), t2 += 2, a2.usMaxContext = n.readUshort(r2, t2), t2 += 2;
|
|
}, e["OS/2"].version5 = function(r2, t2, a2) {
|
|
var n = e._bin;
|
|
return t2 = e["OS/2"].version2(r2, t2, a2), a2.usLowerOpticalPointSize = n.readUshort(r2, t2), t2 += 2, a2.usUpperOpticalPointSize = n.readUshort(r2, t2), t2 += 2;
|
|
}, e.post = {}, e.post.parse = function(r2, t2, a2) {
|
|
var n = e._bin, o = {};
|
|
return o.version = n.readFixed(r2, t2), t2 += 4, o.italicAngle = n.readFixed(r2, t2), t2 += 4, o.underlinePosition = n.readShort(r2, t2), t2 += 2, o.underlineThickness = n.readShort(r2, t2), t2 += 2, o;
|
|
}, null == e && (e = {}), null == e.U && (e.U = {}), e.U.codeToGlyph = function(r2, e2) {
|
|
var t2 = r2.cmap, a2 = -1;
|
|
if (null != t2.p0e4 ? a2 = t2.p0e4 : null != t2.p3e1 ? a2 = t2.p3e1 : null != t2.p1e0 ? a2 = t2.p1e0 : null != t2.p0e3 && (a2 = t2.p0e3), -1 == a2) throw "no familiar platform and encoding!";
|
|
var n = t2.tables[a2];
|
|
if (0 == n.format) return e2 >= n.map.length ? 0 : n.map[e2];
|
|
if (4 == n.format) {
|
|
for (var o = -1, s = 0; s < n.endCount.length; s++) if (e2 <= n.endCount[s]) {
|
|
o = s;
|
|
break;
|
|
}
|
|
if (-1 == o) return 0;
|
|
if (n.startCount[o] > e2) return 0;
|
|
return 65535 & (0 != n.idRangeOffset[o] ? n.glyphIdArray[e2 - n.startCount[o] + (n.idRangeOffset[o] >> 1) - (n.idRangeOffset.length - o)] : e2 + n.idDelta[o]);
|
|
}
|
|
if (12 == n.format) {
|
|
if (e2 > n.groups[n.groups.length - 1][1]) return 0;
|
|
for (s = 0; s < n.groups.length; s++) {
|
|
var i = n.groups[s];
|
|
if (i[0] <= e2 && e2 <= i[1]) return i[2] + (e2 - i[0]);
|
|
}
|
|
return 0;
|
|
}
|
|
throw "unknown cmap table format " + n.format;
|
|
}, e.U.glyphToPath = function(r2, t2) {
|
|
var a2 = { cmds: [], crds: [] };
|
|
if (r2.SVG && r2.SVG.entries[t2]) {
|
|
var n = r2.SVG.entries[t2];
|
|
return null == n ? a2 : ("string" == typeof n && (n = e.SVG.toPath(n), r2.SVG.entries[t2] = n), n);
|
|
}
|
|
if (r2.CFF) {
|
|
var o = { x: 0, y: 0, stack: [], nStems: 0, haveWidth: false, width: r2.CFF.Private ? r2.CFF.Private.defaultWidthX : 0, open: false }, s = r2.CFF, i = r2.CFF.Private;
|
|
if (s.ROS) {
|
|
for (var h = 0; s.FDSelect[h + 2] <= t2; ) h += 2;
|
|
i = s.FDArray[s.FDSelect[h + 1]].Private;
|
|
}
|
|
e.U._drawCFF(r2.CFF.CharStrings[t2], o, s, i, a2);
|
|
} else r2.glyf && e.U._drawGlyf(t2, r2, a2);
|
|
return a2;
|
|
}, e.U._drawGlyf = function(r2, t2, a2) {
|
|
var n = t2.glyf[r2];
|
|
null == n && (n = t2.glyf[r2] = e.glyf._parseGlyf(t2, r2)), null != n && (n.noc > -1 ? e.U._simpleGlyph(n, a2) : e.U._compoGlyph(n, t2, a2));
|
|
}, e.U._simpleGlyph = function(r2, t2) {
|
|
for (var a2 = 0; a2 < r2.noc; a2++) {
|
|
for (var n = 0 == a2 ? 0 : r2.endPts[a2 - 1] + 1, o = r2.endPts[a2], s = n; s <= o; s++) {
|
|
var i = s == n ? o : s - 1, h = s == o ? n : s + 1, d = 1 & r2.flags[s], f = 1 & r2.flags[i], u = 1 & r2.flags[h], l = r2.xs[s], v = r2.ys[s];
|
|
if (s == n) if (d) {
|
|
if (!f) {
|
|
e.U.P.moveTo(t2, l, v);
|
|
continue;
|
|
}
|
|
e.U.P.moveTo(t2, r2.xs[i], r2.ys[i]);
|
|
} else f ? e.U.P.moveTo(t2, r2.xs[i], r2.ys[i]) : e.U.P.moveTo(t2, (r2.xs[i] + l) / 2, (r2.ys[i] + v) / 2);
|
|
d ? f && e.U.P.lineTo(t2, l, v) : u ? e.U.P.qcurveTo(t2, l, v, r2.xs[h], r2.ys[h]) : e.U.P.qcurveTo(t2, l, v, (l + r2.xs[h]) / 2, (v + r2.ys[h]) / 2);
|
|
}
|
|
e.U.P.closePath(t2);
|
|
}
|
|
}, e.U._compoGlyph = function(r2, t2, a2) {
|
|
for (var n = 0; n < r2.parts.length; n++) {
|
|
var o = { cmds: [], crds: [] }, s = r2.parts[n];
|
|
e.U._drawGlyf(s.glyphIndex, t2, o);
|
|
for (var i = s.m, h = 0; h < o.crds.length; h += 2) {
|
|
var d = o.crds[h], f = o.crds[h + 1];
|
|
a2.crds.push(d * i.a + f * i.b + i.tx), a2.crds.push(d * i.c + f * i.d + i.ty);
|
|
}
|
|
for (h = 0; h < o.cmds.length; h++) a2.cmds.push(o.cmds[h]);
|
|
}
|
|
}, e.U._getGlyphClass = function(r2, t2) {
|
|
var a2 = e._lctf.getInterval(t2, r2);
|
|
return -1 == a2 ? 0 : t2[a2 + 2];
|
|
}, e.U._applySubs = function(r2, t2, a2, n) {
|
|
for (var o = r2.length - t2 - 1, s = 0; s < a2.tabs.length; s++) if (null != a2.tabs[s]) {
|
|
var i, h = a2.tabs[s];
|
|
if (!h.coverage || -1 != (i = e._lctf.coverageIndex(h.coverage, r2[t2]))) {
|
|
if (1 == a2.ltype) r2[t2], 1 == h.fmt ? r2[t2] = r2[t2] + h.delta : r2[t2] = h.newg[i];
|
|
else if (4 == a2.ltype) for (var d = h.vals[i], f = 0; f < d.length; f++) {
|
|
var u = d[f], l = u.chain.length;
|
|
if (!(l > o)) {
|
|
for (var v = true, c = 0, p = 0; p < l; p++) {
|
|
for (; -1 == r2[t2 + c + (1 + p)]; ) c++;
|
|
u.chain[p] != r2[t2 + c + (1 + p)] && (v = false);
|
|
}
|
|
if (v) {
|
|
r2[t2] = u.nglyph;
|
|
for (p = 0; p < l + c; p++) r2[t2 + p + 1] = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (5 == a2.ltype && 2 == h.fmt) for (var U = e._lctf.getInterval(h.cDef, r2[t2]), g = h.cDef[U + 2], S = h.scset[g], m = 0; m < S.length; m++) {
|
|
var b = S[m], y = b.input;
|
|
if (!(y.length > o)) {
|
|
for (v = true, p = 0; p < y.length; p++) {
|
|
var F = e._lctf.getInterval(h.cDef, r2[t2 + 1 + p]);
|
|
if (-1 == U && h.cDef[F + 2] != y[p]) {
|
|
v = false;
|
|
break;
|
|
}
|
|
}
|
|
if (v) {
|
|
var C = b.substLookupRecords;
|
|
for (f = 0; f < C.length; f += 2) C[f], C[f + 1];
|
|
}
|
|
}
|
|
}
|
|
else if (6 == a2.ltype && 3 == h.fmt) {
|
|
if (!e.U._glsCovered(r2, h.backCvg, t2 - h.backCvg.length)) continue;
|
|
if (!e.U._glsCovered(r2, h.inptCvg, t2)) continue;
|
|
if (!e.U._glsCovered(r2, h.ahedCvg, t2 + h.inptCvg.length)) continue;
|
|
var _ = h.lookupRec;
|
|
for (m = 0; m < _.length; m += 2) {
|
|
U = _[m];
|
|
var P = n[_[m + 1]];
|
|
e.U._applySubs(r2, t2 + U, P, n);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, e.U._glsCovered = function(r2, t2, a2) {
|
|
for (var n = 0; n < t2.length; n++) {
|
|
if (-1 == e._lctf.coverageIndex(t2[n], r2[a2 + n])) return false;
|
|
}
|
|
return true;
|
|
}, e.U.glyphsToPath = function(r2, t2, a2) {
|
|
for (var n = { cmds: [], crds: [] }, o = 0, s = 0; s < t2.length; s++) {
|
|
var i = t2[s];
|
|
if (-1 != i) {
|
|
for (var h = s < t2.length - 1 && -1 != t2[s + 1] ? t2[s + 1] : 0, d = e.U.glyphToPath(r2, i), f = 0; f < d.crds.length; f += 2) n.crds.push(d.crds[f] + o), n.crds.push(d.crds[f + 1]);
|
|
a2 && n.cmds.push(a2);
|
|
for (f = 0; f < d.cmds.length; f++) n.cmds.push(d.cmds[f]);
|
|
a2 && n.cmds.push("X"), o += r2.hmtx.aWidth[i], s < t2.length - 1 && (o += e.U.getPairAdjustment(r2, i, h));
|
|
}
|
|
}
|
|
return n;
|
|
}, e.U.P = {}, e.U.P.moveTo = function(r2, e2, t2) {
|
|
r2.cmds.push("M"), r2.crds.push(e2, t2);
|
|
}, e.U.P.lineTo = function(r2, e2, t2) {
|
|
r2.cmds.push("L"), r2.crds.push(e2, t2);
|
|
}, e.U.P.curveTo = function(r2, e2, t2, a2, n, o, s) {
|
|
r2.cmds.push("C"), r2.crds.push(e2, t2, a2, n, o, s);
|
|
}, e.U.P.qcurveTo = function(r2, e2, t2, a2, n) {
|
|
r2.cmds.push("Q"), r2.crds.push(e2, t2, a2, n);
|
|
}, e.U.P.closePath = function(r2) {
|
|
r2.cmds.push("Z");
|
|
}, e.U._drawCFF = function(r2, t2, a2, n, o) {
|
|
for (var s = t2.stack, i = t2.nStems, h = t2.haveWidth, d = t2.width, f = t2.open, u = 0, l = t2.x, v = t2.y, c = 0, p = 0, U = 0, g = 0, S = 0, m = 0, b = 0, y = 0, F = 0, C = 0, _ = { val: 0, size: 0 }; u < r2.length; ) {
|
|
e.CFF.getCharString(r2, u, _);
|
|
var P = _.val;
|
|
if (u += _.size, "o1" == P || "o18" == P) s.length % 2 != 0 && !h && (d = s.shift() + n.nominalWidthX), i += s.length >> 1, s.length = 0, h = true;
|
|
else if ("o3" == P || "o23" == P) {
|
|
s.length % 2 != 0 && !h && (d = s.shift() + n.nominalWidthX), i += s.length >> 1, s.length = 0, h = true;
|
|
} else if ("o4" == P) s.length > 1 && !h && (d = s.shift() + n.nominalWidthX, h = true), f && e.U.P.closePath(o), v += s.pop(), e.U.P.moveTo(o, l, v), f = true;
|
|
else if ("o5" == P) for (; s.length > 0; ) l += s.shift(), v += s.shift(), e.U.P.lineTo(o, l, v);
|
|
else if ("o6" == P || "o7" == P) for (var x = s.length, I = "o6" == P, w = 0; w < x; w++) {
|
|
var k = s.shift();
|
|
I ? l += k : v += k, I = !I, e.U.P.lineTo(o, l, v);
|
|
}
|
|
else if ("o8" == P || "o24" == P) {
|
|
x = s.length;
|
|
for (var G = 0; G + 6 <= x; ) c = l + s.shift(), p = v + s.shift(), U = c + s.shift(), g = p + s.shift(), l = U + s.shift(), v = g + s.shift(), e.U.P.curveTo(o, c, p, U, g, l, v), G += 6;
|
|
"o24" == P && (l += s.shift(), v += s.shift(), e.U.P.lineTo(o, l, v));
|
|
} else {
|
|
if ("o11" == P) break;
|
|
if ("o1234" == P || "o1235" == P || "o1236" == P || "o1237" == P) "o1234" == P && (p = v, U = (c = l + s.shift()) + s.shift(), C = g = p + s.shift(), m = g, y = v, l = (b = (S = (F = U + s.shift()) + s.shift()) + s.shift()) + s.shift(), e.U.P.curveTo(o, c, p, U, g, F, C), e.U.P.curveTo(o, S, m, b, y, l, v)), "o1235" == P && (c = l + s.shift(), p = v + s.shift(), U = c + s.shift(), g = p + s.shift(), F = U + s.shift(), C = g + s.shift(), S = F + s.shift(), m = C + s.shift(), b = S + s.shift(), y = m + s.shift(), l = b + s.shift(), v = y + s.shift(), s.shift(), e.U.P.curveTo(o, c, p, U, g, F, C), e.U.P.curveTo(o, S, m, b, y, l, v)), "o1236" == P && (c = l + s.shift(), p = v + s.shift(), U = c + s.shift(), C = g = p + s.shift(), m = g, b = (S = (F = U + s.shift()) + s.shift()) + s.shift(), y = m + s.shift(), l = b + s.shift(), e.U.P.curveTo(o, c, p, U, g, F, C), e.U.P.curveTo(o, S, m, b, y, l, v)), "o1237" == P && (c = l + s.shift(), p = v + s.shift(), U = c + s.shift(), g = p + s.shift(), F = U + s.shift(), C = g + s.shift(), S = F + s.shift(), m = C + s.shift(), b = S + s.shift(), y = m + s.shift(), Math.abs(b - l) > Math.abs(y - v) ? l = b + s.shift() : v = y + s.shift(), e.U.P.curveTo(o, c, p, U, g, F, C), e.U.P.curveTo(o, S, m, b, y, l, v));
|
|
else if ("o14" == P) {
|
|
if (s.length > 0 && !h && (d = s.shift() + a2.nominalWidthX, h = true), 4 == s.length) {
|
|
var O = s.shift(), T = s.shift(), D = s.shift(), B = s.shift(), A = e.CFF.glyphBySE(a2, D), R = e.CFF.glyphBySE(a2, B);
|
|
e.U._drawCFF(a2.CharStrings[A], t2, a2, n, o), t2.x = O, t2.y = T, e.U._drawCFF(a2.CharStrings[R], t2, a2, n, o);
|
|
}
|
|
f && (e.U.P.closePath(o), f = false);
|
|
} else if ("o19" == P || "o20" == P) {
|
|
s.length % 2 != 0 && !h && (d = s.shift() + n.nominalWidthX), i += s.length >> 1, s.length = 0, h = true, u += i + 7 >> 3;
|
|
} else if ("o21" == P) s.length > 2 && !h && (d = s.shift() + n.nominalWidthX, h = true), v += s.pop(), l += s.pop(), f && e.U.P.closePath(o), e.U.P.moveTo(o, l, v), f = true;
|
|
else if ("o22" == P) s.length > 1 && !h && (d = s.shift() + n.nominalWidthX, h = true), l += s.pop(), f && e.U.P.closePath(o), e.U.P.moveTo(o, l, v), f = true;
|
|
else if ("o25" == P) {
|
|
for (; s.length > 6; ) l += s.shift(), v += s.shift(), e.U.P.lineTo(o, l, v);
|
|
c = l + s.shift(), p = v + s.shift(), U = c + s.shift(), g = p + s.shift(), l = U + s.shift(), v = g + s.shift(), e.U.P.curveTo(o, c, p, U, g, l, v);
|
|
} else if ("o26" == P) for (s.length % 2 && (l += s.shift()); s.length > 0; ) c = l, p = v + s.shift(), l = U = c + s.shift(), v = (g = p + s.shift()) + s.shift(), e.U.P.curveTo(o, c, p, U, g, l, v);
|
|
else if ("o27" == P) for (s.length % 2 && (v += s.shift()); s.length > 0; ) p = v, U = (c = l + s.shift()) + s.shift(), g = p + s.shift(), l = U + s.shift(), v = g, e.U.P.curveTo(o, c, p, U, g, l, v);
|
|
else if ("o10" == P || "o29" == P) {
|
|
var L = "o10" == P ? n : a2;
|
|
if (0 == s.length) console.debug("error: empty stack");
|
|
else {
|
|
var W = s.pop(), M = L.Subrs[W + L.Bias];
|
|
t2.x = l, t2.y = v, t2.nStems = i, t2.haveWidth = h, t2.width = d, t2.open = f, e.U._drawCFF(M, t2, a2, n, o), l = t2.x, v = t2.y, i = t2.nStems, h = t2.haveWidth, d = t2.width, f = t2.open;
|
|
}
|
|
} else if ("o30" == P || "o31" == P) {
|
|
var V = s.length, E = (G = 0, "o31" == P);
|
|
for (G += V - (x = -3 & V); G < x; ) E ? (p = v, U = (c = l + s.shift()) + s.shift(), v = (g = p + s.shift()) + s.shift(), x - G == 5 ? (l = U + s.shift(), G++) : l = U, E = false) : (c = l, p = v + s.shift(), U = c + s.shift(), g = p + s.shift(), l = U + s.shift(), x - G == 5 ? (v = g + s.shift(), G++) : v = g, E = true), e.U.P.curveTo(o, c, p, U, g, l, v), G += 4;
|
|
} else {
|
|
if ("o" == (P + "").charAt(0)) throw console.debug("Unknown operation: " + P, r2), P;
|
|
s.push(P);
|
|
}
|
|
}
|
|
}
|
|
t2.x = l, t2.y = v, t2.nStems = i, t2.haveWidth = h, t2.width = d, t2.open = f;
|
|
};
|
|
var t = e, a = { Typr: t };
|
|
return r.Typr = t, r.default = a, Object.defineProperty(r, "__esModule", { value: true }), r;
|
|
}({}).Typr;
|
|
}
|
|
function woff2otfFactory() {
|
|
return function(r) {
|
|
var e = Uint8Array, n = Uint16Array, t = Uint32Array, a = new e([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0, 0]), i = new e([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0]), o = new e([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]), f = function(r2, e2) {
|
|
for (var a2 = new n(31), i2 = 0; i2 < 31; ++i2) a2[i2] = e2 += 1 << r2[i2 - 1];
|
|
var o2 = new t(a2[30]);
|
|
for (i2 = 1; i2 < 30; ++i2) for (var f2 = a2[i2]; f2 < a2[i2 + 1]; ++f2) o2[f2] = f2 - a2[i2] << 5 | i2;
|
|
return [a2, o2];
|
|
}, u = f(a, 2), v = u[0], s = u[1];
|
|
v[28] = 258, s[258] = 28;
|
|
for (var l = f(i, 0)[0], c = new n(32768), g = 0; g < 32768; ++g) {
|
|
var h = (43690 & g) >>> 1 | (21845 & g) << 1;
|
|
h = (61680 & (h = (52428 & h) >>> 2 | (13107 & h) << 2)) >>> 4 | (3855 & h) << 4, c[g] = ((65280 & h) >>> 8 | (255 & h) << 8) >>> 1;
|
|
}
|
|
var w = function(r2, e2, t2) {
|
|
for (var a2 = r2.length, i2 = 0, o2 = new n(e2); i2 < a2; ++i2) ++o2[r2[i2] - 1];
|
|
var f2, u2 = new n(e2);
|
|
for (i2 = 0; i2 < e2; ++i2) u2[i2] = u2[i2 - 1] + o2[i2 - 1] << 1;
|
|
if (t2) {
|
|
f2 = new n(1 << e2);
|
|
var v2 = 15 - e2;
|
|
for (i2 = 0; i2 < a2; ++i2) if (r2[i2]) for (var s2 = i2 << 4 | r2[i2], l2 = e2 - r2[i2], g2 = u2[r2[i2] - 1]++ << l2, h2 = g2 | (1 << l2) - 1; g2 <= h2; ++g2) f2[c[g2] >>> v2] = s2;
|
|
} else for (f2 = new n(a2), i2 = 0; i2 < a2; ++i2) r2[i2] && (f2[i2] = c[u2[r2[i2] - 1]++] >>> 15 - r2[i2]);
|
|
return f2;
|
|
}, d = new e(288);
|
|
for (g = 0; g < 144; ++g) d[g] = 8;
|
|
for (g = 144; g < 256; ++g) d[g] = 9;
|
|
for (g = 256; g < 280; ++g) d[g] = 7;
|
|
for (g = 280; g < 288; ++g) d[g] = 8;
|
|
var m = new e(32);
|
|
for (g = 0; g < 32; ++g) m[g] = 5;
|
|
var b = w(d, 9, 1), p = w(m, 5, 1), y = function(r2) {
|
|
for (var e2 = r2[0], n2 = 1; n2 < r2.length; ++n2) r2[n2] > e2 && (e2 = r2[n2]);
|
|
return e2;
|
|
}, L = function(r2, e2, n2) {
|
|
var t2 = e2 / 8 | 0;
|
|
return (r2[t2] | r2[t2 + 1] << 8) >> (7 & e2) & n2;
|
|
}, U = function(r2, e2) {
|
|
var n2 = e2 / 8 | 0;
|
|
return (r2[n2] | r2[n2 + 1] << 8 | r2[n2 + 2] << 16) >> (7 & e2);
|
|
}, k = ["unexpected EOF", "invalid block type", "invalid length/literal", "invalid distance", "stream finished", "no stream handler", , "no callback", "invalid UTF-8 data", "extra field too long", "date not in range 1980-2099", "filename too long", "stream finishing", "invalid zip data"], T = function(r2, e2, n2) {
|
|
var t2 = new Error(e2 || k[r2]);
|
|
if (t2.code = r2, Error.captureStackTrace && Error.captureStackTrace(t2, T), !n2) throw t2;
|
|
return t2;
|
|
}, O = function(r2, f2, u2) {
|
|
var s2 = r2.length;
|
|
if (!s2 || u2 && !u2.l && s2 < 5) return f2 || new e(0);
|
|
var c2 = !f2 || u2, g2 = !u2 || u2.i;
|
|
u2 || (u2 = {}), f2 || (f2 = new e(3 * s2));
|
|
var h2, d2 = function(r3) {
|
|
var n2 = f2.length;
|
|
if (r3 > n2) {
|
|
var t2 = new e(Math.max(2 * n2, r3));
|
|
t2.set(f2), f2 = t2;
|
|
}
|
|
}, m2 = u2.f || 0, k2 = u2.p || 0, O2 = u2.b || 0, A2 = u2.l, x2 = u2.d, E = u2.m, D = u2.n, M = 8 * s2;
|
|
do {
|
|
if (!A2) {
|
|
u2.f = m2 = L(r2, k2, 1);
|
|
var S = L(r2, k2 + 1, 3);
|
|
if (k2 += 3, !S) {
|
|
var V = r2[(I = ((h2 = k2) / 8 | 0) + (7 & h2 && 1) + 4) - 4] | r2[I - 3] << 8, _ = I + V;
|
|
if (_ > s2) {
|
|
g2 && T(0);
|
|
break;
|
|
}
|
|
c2 && d2(O2 + V), f2.set(r2.subarray(I, _), O2), u2.b = O2 += V, u2.p = k2 = 8 * _;
|
|
continue;
|
|
}
|
|
if (1 == S) A2 = b, x2 = p, E = 9, D = 5;
|
|
else if (2 == S) {
|
|
var j = L(r2, k2, 31) + 257, z = L(r2, k2 + 10, 15) + 4, C = j + L(r2, k2 + 5, 31) + 1;
|
|
k2 += 14;
|
|
for (var F = new e(C), P = new e(19), q = 0; q < z; ++q) P[o[q]] = L(r2, k2 + 3 * q, 7);
|
|
k2 += 3 * z;
|
|
var B = y(P), G = (1 << B) - 1, H = w(P, B, 1);
|
|
for (q = 0; q < C; ) {
|
|
var I, J = H[L(r2, k2, G)];
|
|
if (k2 += 15 & J, (I = J >>> 4) < 16) F[q++] = I;
|
|
else {
|
|
var K = 0, N = 0;
|
|
for (16 == I ? (N = 3 + L(r2, k2, 3), k2 += 2, K = F[q - 1]) : 17 == I ? (N = 3 + L(r2, k2, 7), k2 += 3) : 18 == I && (N = 11 + L(r2, k2, 127), k2 += 7); N--; ) F[q++] = K;
|
|
}
|
|
}
|
|
var Q = F.subarray(0, j), R = F.subarray(j);
|
|
E = y(Q), D = y(R), A2 = w(Q, E, 1), x2 = w(R, D, 1);
|
|
} else T(1);
|
|
if (k2 > M) {
|
|
g2 && T(0);
|
|
break;
|
|
}
|
|
}
|
|
c2 && d2(O2 + 131072);
|
|
for (var W = (1 << E) - 1, X = (1 << D) - 1, Y = k2; ; Y = k2) {
|
|
var Z = (K = A2[U(r2, k2) & W]) >>> 4;
|
|
if ((k2 += 15 & K) > M) {
|
|
g2 && T(0);
|
|
break;
|
|
}
|
|
if (K || T(2), Z < 256) f2[O2++] = Z;
|
|
else {
|
|
if (256 == Z) {
|
|
Y = k2, A2 = null;
|
|
break;
|
|
}
|
|
var $ = Z - 254;
|
|
if (Z > 264) {
|
|
var rr = a[q = Z - 257];
|
|
$ = L(r2, k2, (1 << rr) - 1) + v[q], k2 += rr;
|
|
}
|
|
var er = x2[U(r2, k2) & X], nr = er >>> 4;
|
|
er || T(3), k2 += 15 & er;
|
|
R = l[nr];
|
|
if (nr > 3) {
|
|
rr = i[nr];
|
|
R += U(r2, k2) & (1 << rr) - 1, k2 += rr;
|
|
}
|
|
if (k2 > M) {
|
|
g2 && T(0);
|
|
break;
|
|
}
|
|
c2 && d2(O2 + 131072);
|
|
for (var tr = O2 + $; O2 < tr; O2 += 4) f2[O2] = f2[O2 - R], f2[O2 + 1] = f2[O2 + 1 - R], f2[O2 + 2] = f2[O2 + 2 - R], f2[O2 + 3] = f2[O2 + 3 - R];
|
|
O2 = tr;
|
|
}
|
|
}
|
|
u2.l = A2, u2.p = Y, u2.b = O2, A2 && (m2 = 1, u2.m = E, u2.d = x2, u2.n = D);
|
|
} while (!m2);
|
|
return O2 == f2.length ? f2 : function(r3, a2, i2) {
|
|
(null == a2 || a2 < 0) && (a2 = 0), (null == i2 || i2 > r3.length) && (i2 = r3.length);
|
|
var o2 = new (r3 instanceof n ? n : r3 instanceof t ? t : e)(i2 - a2);
|
|
return o2.set(r3.subarray(a2, i2)), o2;
|
|
}(f2, 0, O2);
|
|
}, A = new e(0);
|
|
var x = "undefined" != typeof TextDecoder && new TextDecoder();
|
|
try {
|
|
x.decode(A, { stream: true }), 1;
|
|
} catch (r2) {
|
|
}
|
|
return r.convert_streams = function(r2) {
|
|
var e2 = new DataView(r2), n2 = 0;
|
|
function t2() {
|
|
var r3 = e2.getUint16(n2);
|
|
return n2 += 2, r3;
|
|
}
|
|
function a2() {
|
|
var r3 = e2.getUint32(n2);
|
|
return n2 += 4, r3;
|
|
}
|
|
function i2(r3) {
|
|
m2.setUint16(b2, r3), b2 += 2;
|
|
}
|
|
function o2(r3) {
|
|
m2.setUint32(b2, r3), b2 += 4;
|
|
}
|
|
for (var f2 = { signature: a2(), flavor: a2(), length: a2(), numTables: t2(), reserved: t2(), totalSfntSize: a2(), majorVersion: t2(), minorVersion: t2(), metaOffset: a2(), metaLength: a2(), metaOrigLength: a2(), privOffset: a2(), privLength: a2() }, u2 = 0; Math.pow(2, u2) <= f2.numTables; ) u2++;
|
|
u2--;
|
|
for (var v2 = 16 * Math.pow(2, u2), s2 = 16 * f2.numTables - v2, l2 = 12, c2 = [], g2 = 0; g2 < f2.numTables; g2++) c2.push({ tag: a2(), offset: a2(), compLength: a2(), origLength: a2(), origChecksum: a2() }), l2 += 16;
|
|
var h2, w2 = new Uint8Array(12 + 16 * c2.length + c2.reduce(function(r3, e3) {
|
|
return r3 + e3.origLength + 4;
|
|
}, 0)), d2 = w2.buffer, m2 = new DataView(d2), b2 = 0;
|
|
return o2(f2.flavor), i2(f2.numTables), i2(v2), i2(u2), i2(s2), c2.forEach(function(r3) {
|
|
o2(r3.tag), o2(r3.origChecksum), o2(l2), o2(r3.origLength), r3.outOffset = l2, (l2 += r3.origLength) % 4 != 0 && (l2 += 4 - l2 % 4);
|
|
}), c2.forEach(function(e3) {
|
|
var n3, t3 = r2.slice(e3.offset, e3.offset + e3.compLength);
|
|
if (e3.compLength != e3.origLength) {
|
|
var a3 = new Uint8Array(e3.origLength);
|
|
n3 = new Uint8Array(t3, 2), O(n3, a3);
|
|
} else a3 = new Uint8Array(t3);
|
|
w2.set(a3, e3.outOffset);
|
|
var i3 = 0;
|
|
(l2 = e3.outOffset + e3.origLength) % 4 != 0 && (i3 = 4 - l2 % 4), w2.set(new Uint8Array(i3).buffer, e3.outOffset + e3.origLength), h2 = l2 + i3;
|
|
}), d2.slice(0, h2);
|
|
}, Object.defineProperty(r, "__esModule", { value: true }), r;
|
|
}({}).convert_streams;
|
|
}
|
|
function parserFactory(Typr, woff2otf) {
|
|
const cmdArgLengths = {
|
|
M: 2,
|
|
L: 2,
|
|
Q: 4,
|
|
C: 6,
|
|
Z: 0
|
|
};
|
|
const joiningTypeRawData = { "C": "18g,ca,368,1kz", "D": "17k,6,2,2+4,5+c,2+6,2+1,10+1,9+f,j+11,2+1,a,2,2+1,15+2,3,j+2,6+3,2+8,2,2,2+1,w+a,4+e,3+3,2,3+2,3+5,23+w,2f+4,3,2+9,2,b,2+3,3,1k+9,6+1,3+1,2+2,2+d,30g,p+y,1,1+1g,f+x,2,sd2+1d,jf3+4,f+3,2+4,2+2,b+3,42,2,4+2,2+1,2,3,t+1,9f+w,2,el+2,2+g,d+2,2l,2+1,5,3+1,2+1,2,3,6,16wm+1v", "R": "17m+3,2,2,6+3,m,15+2,2+2,h+h,13,3+8,2,2,3+1,2,p+1,x,5+4,5,a,2,2,3,u,c+2,g+1,5,2+1,4+1,5j,6+1,2,b,2+2,f,2+1,1s+2,2,3+1,7,1ez0,2,2+1,4+4,b,4,3,b,42,2+2,4,3,2+1,2,o+3,ae,ep,x,2o+2,3+1,3,5+1,6", "L": "x9u,jff,a,fd,jv", "T": "4t,gj+33,7o+4,1+1,7c+18,2,2+1,2+1,2,21+a,2,1b+k,h,2u+6,3+5,3+1,2+3,y,2,v+q,2k+a,1n+8,a,p+3,2+8,2+2,2+4,18+2,3c+e,2+v,1k,2,5+7,5,4+6,b+1,u,1n,5+3,9,l+1,r,3+1,1m,5+1,5+1,3+2,4,v+1,4,c+1,1m,5+4,2+1,5,l+1,n+5,2,1n,3,2+3,9,8+1,c+1,v,1q,d,1f,4,1m+2,6+2,2+3,8+1,c+1,u,1n,3,7,6+1,l+1,t+1,1m+1,5+3,9,l+1,u,21,8+2,2,2j,3+6,d+7,2r,3+8,c+5,23+1,s,2,2,1k+d,2+4,2+1,6+a,2+z,a,2v+3,2+5,2+1,3+1,q+1,5+2,h+3,e,3+1,7,g,jk+2,qb+2,u+2,u+1,v+1,1t+1,2+6,9,3+a,a,1a+2,3c+1,z,3b+2,5+1,a,7+2,64+1,3,1n,2+6,2,2,3+7,7+9,3,1d+d,1,1+1,1s+3,1d,2+4,2,6,15+8,d+1,x+3,3+1,2+2,1l,2+1,4,2+2,1n+7,3+1,49+2,2+c,2+6,5,7,4+1,5j+1l,2+4,ek,3+1,r+4,1e+4,6+5,2p+c,1+3,1,1+2,1+b,2db+2,3y,2p+v,ff+3,30+1,n9x,1+2,2+9,x+1,29+1,7l,4,5,q+1,6,48+1,r+h,e,13+7,q+a,1b+2,1d,3+3,3+1,14,1w+5,3+1,3+1,d,9,1c,1g,2+2,3+1,6+1,2,17+1,9,6n,3,5,fn5,ki+f,h+f,5s,6y+2,ea,6b,46+4,1af+2,2+1,6+3,15+2,5,4m+1,fy+3,as+1,4a+a,4x,1j+e,1l+2,1e+3,3+1,1y+2,11+4,2+7,1r,d+1,1h+8,b+3,3,2o+2,3,2+1,7,4h,4+7,m+1,1m+1,4,12+6,4+4,5g+7,3+2,2,o,2d+5,2,5+1,2+1,6n+3,7+1,2+1,s+1,2e+7,3,2+1,2z,2,3+5,2,2u+2,3+3,2+4,78+8,2+1,75+1,2,5,41+3,3+1,5,x+9,15+5,3+3,9,a+5,3+2,1b+c,2+1,bb+6,2+5,2,2b+l,3+6,2+1,2+1,3f+5,4,2+1,2+6,2,21+1,4,2,9o+1,470+8,at4+4,1o+6,t5,1s+3,2a,f5l+1,2+3,43o+2,a+7,1+7,3+6,v+3,45+2,1j0+1i,5+1d,9,f,n+4,2+e,11t+6,2+g,3+6,2+1,2+4,7a+6,c6+3,15t+6,32+6,1,gzau,v+2n,3l+6n" };
|
|
const JT_LEFT = 1, JT_RIGHT = 2, JT_DUAL = 4, JT_TRANSPARENT = 8, JT_JOIN_CAUSING = 16, JT_NON_JOINING = 32;
|
|
let joiningTypeMap;
|
|
function getCharJoiningType(ch) {
|
|
if (!joiningTypeMap) {
|
|
const m = {
|
|
R: JT_RIGHT,
|
|
L: JT_LEFT,
|
|
D: JT_DUAL,
|
|
C: JT_JOIN_CAUSING,
|
|
U: JT_NON_JOINING,
|
|
T: JT_TRANSPARENT
|
|
};
|
|
joiningTypeMap = /* @__PURE__ */ new Map();
|
|
for (let type in joiningTypeRawData) {
|
|
let lastCode = 0;
|
|
joiningTypeRawData[type].split(",").forEach((range) => {
|
|
let [skip, step] = range.split("+");
|
|
skip = parseInt(skip, 36);
|
|
step = step ? parseInt(step, 36) : 0;
|
|
joiningTypeMap.set(lastCode += skip, m[type]);
|
|
for (let i = step; i--; ) {
|
|
joiningTypeMap.set(++lastCode, m[type]);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
return joiningTypeMap.get(ch) || JT_NON_JOINING;
|
|
}
|
|
const ISOL = 1, INIT = 2, FINA = 3, MEDI = 4;
|
|
const formsToFeatures = [null, "isol", "init", "fina", "medi"];
|
|
function detectJoiningForms(str) {
|
|
const joiningForms = new Uint8Array(str.length);
|
|
let prevJoiningType = JT_NON_JOINING;
|
|
let prevForm = ISOL;
|
|
let prevIndex = -1;
|
|
for (let i = 0; i < str.length; i++) {
|
|
const code = str.codePointAt(i);
|
|
let joiningType = getCharJoiningType(code) | 0;
|
|
let form = ISOL;
|
|
if (joiningType & JT_TRANSPARENT) {
|
|
continue;
|
|
}
|
|
if (prevJoiningType & (JT_LEFT | JT_DUAL | JT_JOIN_CAUSING)) {
|
|
if (joiningType & (JT_RIGHT | JT_DUAL | JT_JOIN_CAUSING)) {
|
|
form = FINA;
|
|
if (prevForm === ISOL || prevForm === FINA) {
|
|
joiningForms[prevIndex]++;
|
|
}
|
|
} else if (joiningType & (JT_LEFT | JT_NON_JOINING)) {
|
|
if (prevForm === INIT || prevForm === MEDI) {
|
|
joiningForms[prevIndex]--;
|
|
}
|
|
}
|
|
} else if (prevJoiningType & (JT_RIGHT | JT_NON_JOINING)) {
|
|
if (prevForm === INIT || prevForm === MEDI) {
|
|
joiningForms[prevIndex]--;
|
|
}
|
|
}
|
|
prevForm = joiningForms[i] = form;
|
|
prevJoiningType = joiningType;
|
|
prevIndex = i;
|
|
if (code > 65535) i++;
|
|
}
|
|
return joiningForms;
|
|
}
|
|
function stringToGlyphs(font, str) {
|
|
const glyphIds = [];
|
|
for (let i = 0; i < str.length; i++) {
|
|
const cc = str.codePointAt(i);
|
|
if (cc > 65535) i++;
|
|
glyphIds.push(Typr.U.codeToGlyph(font, cc));
|
|
}
|
|
const gsub = font["GSUB"];
|
|
if (gsub) {
|
|
const { lookupList, featureList } = gsub;
|
|
let joiningForms;
|
|
const supportedFeatures = /^(rlig|liga|mset|isol|init|fina|medi|half|pres|blws|ccmp)$/;
|
|
const usedLookups = [];
|
|
featureList.forEach((feature) => {
|
|
if (supportedFeatures.test(feature.tag)) {
|
|
for (let ti = 0; ti < feature.tab.length; ti++) {
|
|
if (usedLookups[feature.tab[ti]]) continue;
|
|
usedLookups[feature.tab[ti]] = true;
|
|
const tab = lookupList[feature.tab[ti]];
|
|
const isJoiningFeature = /^(isol|init|fina|medi)$/.test(feature.tag);
|
|
if (isJoiningFeature && !joiningForms) {
|
|
joiningForms = detectJoiningForms(str);
|
|
}
|
|
for (let ci = 0; ci < glyphIds.length; ci++) {
|
|
if (!joiningForms || !isJoiningFeature || formsToFeatures[joiningForms[ci]] === feature.tag) {
|
|
Typr.U._applySubs(glyphIds, ci, tab, lookupList);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return glyphIds;
|
|
}
|
|
function calcGlyphPositions(font, glyphIds) {
|
|
const positions = new Int16Array(glyphIds.length * 3);
|
|
let glyphIndex = 0;
|
|
for (; glyphIndex < glyphIds.length; glyphIndex++) {
|
|
const glyphId = glyphIds[glyphIndex];
|
|
if (glyphId === -1) continue;
|
|
positions[glyphIndex * 3 + 2] = font.hmtx.aWidth[glyphId];
|
|
const gpos = font.GPOS;
|
|
if (gpos) {
|
|
const llist = gpos.lookupList;
|
|
for (let i = 0; i < llist.length; i++) {
|
|
const lookup = llist[i];
|
|
for (let j = 0; j < lookup.tabs.length; j++) {
|
|
const tab = lookup.tabs[j];
|
|
if (lookup.ltype === 1) {
|
|
const ind = Typr._lctf.coverageIndex(tab.coverage, glyphId);
|
|
if (ind !== -1 && tab.pos) {
|
|
applyValueRecord(tab.pos, glyphIndex);
|
|
break;
|
|
}
|
|
} else if (lookup.ltype === 2) {
|
|
let adj = null;
|
|
let prevGlyphIndex = getPrevGlyphIndex();
|
|
if (prevGlyphIndex !== -1) {
|
|
const coverageIndex = Typr._lctf.coverageIndex(tab.coverage, glyphIds[prevGlyphIndex]);
|
|
if (coverageIndex !== -1) {
|
|
if (tab.fmt === 1) {
|
|
const right = tab.pairsets[coverageIndex];
|
|
for (let k = 0; k < right.length; k++) {
|
|
if (right[k].gid2 === glyphId) adj = right[k];
|
|
}
|
|
} else if (tab.fmt === 2) {
|
|
const c1 = Typr.U._getGlyphClass(glyphIds[prevGlyphIndex], tab.classDef1);
|
|
const c2 = Typr.U._getGlyphClass(glyphId, tab.classDef2);
|
|
adj = tab.matrix[c1][c2];
|
|
}
|
|
if (adj) {
|
|
if (adj.val1) applyValueRecord(adj.val1, prevGlyphIndex);
|
|
if (adj.val2) applyValueRecord(adj.val2, glyphIndex);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else if (lookup.ltype === 4) {
|
|
const markArrIndex = Typr._lctf.coverageIndex(tab.markCoverage, glyphId);
|
|
if (markArrIndex !== -1) {
|
|
const baseGlyphIndex = getPrevGlyphIndex(isBaseGlyph);
|
|
const baseArrIndex = baseGlyphIndex === -1 ? -1 : Typr._lctf.coverageIndex(tab.baseCoverage, glyphIds[baseGlyphIndex]);
|
|
if (baseArrIndex !== -1) {
|
|
const markRecord = tab.markArray[markArrIndex];
|
|
const baseAnchor = tab.baseArray[baseArrIndex][markRecord.markClass];
|
|
positions[glyphIndex * 3] = baseAnchor.x - markRecord.x + positions[baseGlyphIndex * 3] - positions[baseGlyphIndex * 3 + 2];
|
|
positions[glyphIndex * 3 + 1] = baseAnchor.y - markRecord.y + positions[baseGlyphIndex * 3 + 1];
|
|
break;
|
|
}
|
|
}
|
|
} else if (lookup.ltype === 6) {
|
|
const mark1ArrIndex = Typr._lctf.coverageIndex(tab.mark1Coverage, glyphId);
|
|
if (mark1ArrIndex !== -1) {
|
|
const prevGlyphIndex = getPrevGlyphIndex();
|
|
if (prevGlyphIndex !== -1) {
|
|
const prevGlyphId = glyphIds[prevGlyphIndex];
|
|
if (getGlyphClass(font, prevGlyphId) === 3) {
|
|
const mark2ArrIndex = Typr._lctf.coverageIndex(tab.mark2Coverage, prevGlyphId);
|
|
if (mark2ArrIndex !== -1) {
|
|
const mark1Record = tab.mark1Array[mark1ArrIndex];
|
|
const mark2Anchor = tab.mark2Array[mark2ArrIndex][mark1Record.markClass];
|
|
positions[glyphIndex * 3] = mark2Anchor.x - mark1Record.x + positions[prevGlyphIndex * 3] - positions[prevGlyphIndex * 3 + 2];
|
|
positions[glyphIndex * 3 + 1] = mark2Anchor.y - mark1Record.y + positions[prevGlyphIndex * 3 + 1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (font.kern && !font.cff) {
|
|
const prevGlyphIndex = getPrevGlyphIndex();
|
|
if (prevGlyphIndex !== -1) {
|
|
const ind1 = font.kern.glyph1.indexOf(glyphIds[prevGlyphIndex]);
|
|
if (ind1 !== -1) {
|
|
const ind2 = font.kern.rval[ind1].glyph2.indexOf(glyphId);
|
|
if (ind2 !== -1) {
|
|
positions[prevGlyphIndex * 3 + 2] += font.kern.rval[ind1].vals[ind2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return positions;
|
|
function getPrevGlyphIndex(filter) {
|
|
for (let i = glyphIndex - 1; i >= 0; i--) {
|
|
if (glyphIds[i] !== -1 && (!filter || filter(glyphIds[i]))) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
function isBaseGlyph(glyphId) {
|
|
return getGlyphClass(font, glyphId) === 1;
|
|
}
|
|
function applyValueRecord(source, gi) {
|
|
for (let i = 0; i < 3; i++) {
|
|
positions[gi * 3 + i] += source[i] || 0;
|
|
}
|
|
}
|
|
}
|
|
function getGlyphClass(font, glyphId) {
|
|
const classDef = font.GDEF && font.GDEF.glyphClassDef;
|
|
return classDef ? Typr.U._getGlyphClass(glyphId, classDef) : 0;
|
|
}
|
|
function firstNum(...args) {
|
|
for (let i = 0; i < args.length; i++) {
|
|
if (typeof args[i] === "number") {
|
|
return args[i];
|
|
}
|
|
}
|
|
}
|
|
function wrapFontObj(typrFont) {
|
|
const glyphMap = /* @__PURE__ */ Object.create(null);
|
|
const os2 = typrFont["OS/2"];
|
|
const hhea = typrFont.hhea;
|
|
const unitsPerEm = typrFont.head.unitsPerEm;
|
|
const ascender = firstNum(os2 && os2.sTypoAscender, hhea && hhea.ascender, unitsPerEm);
|
|
const fontObj = {
|
|
unitsPerEm,
|
|
ascender,
|
|
descender: firstNum(os2 && os2.sTypoDescender, hhea && hhea.descender, 0),
|
|
capHeight: firstNum(os2 && os2.sCapHeight, ascender),
|
|
xHeight: firstNum(os2 && os2.sxHeight, ascender),
|
|
lineGap: firstNum(os2 && os2.sTypoLineGap, hhea && hhea.lineGap),
|
|
supportsCodePoint(code) {
|
|
return Typr.U.codeToGlyph(typrFont, code) > 0;
|
|
},
|
|
forEachGlyph(text, fontSize, letterSpacing, callback) {
|
|
let penX = 0;
|
|
const fontScale = 1 / fontObj.unitsPerEm * fontSize;
|
|
const glyphIds = stringToGlyphs(typrFont, text);
|
|
let charIndex = 0;
|
|
const positions = calcGlyphPositions(typrFont, glyphIds);
|
|
glyphIds.forEach((glyphId, i) => {
|
|
if (glyphId !== -1) {
|
|
let glyphObj = glyphMap[glyphId];
|
|
if (!glyphObj) {
|
|
const { cmds, crds } = Typr.U.glyphToPath(typrFont, glyphId);
|
|
let path = "";
|
|
let crdsIdx = 0;
|
|
for (let i2 = 0, len = cmds.length; i2 < len; i2++) {
|
|
const numArgs = cmdArgLengths[cmds[i2]];
|
|
path += cmds[i2];
|
|
for (let j = 1; j <= numArgs; j++) {
|
|
path += (j > 1 ? "," : "") + crds[crdsIdx++];
|
|
}
|
|
}
|
|
let xMin, yMin, xMax, yMax;
|
|
if (crds.length) {
|
|
xMin = yMin = Infinity;
|
|
xMax = yMax = -Infinity;
|
|
for (let i2 = 0, len = crds.length; i2 < len; i2 += 2) {
|
|
let x = crds[i2];
|
|
let y = crds[i2 + 1];
|
|
if (x < xMin) xMin = x;
|
|
if (y < yMin) yMin = y;
|
|
if (x > xMax) xMax = x;
|
|
if (y > yMax) yMax = y;
|
|
}
|
|
} else {
|
|
xMin = xMax = yMin = yMax = 0;
|
|
}
|
|
glyphObj = glyphMap[glyphId] = {
|
|
index: glyphId,
|
|
advanceWidth: typrFont.hmtx.aWidth[glyphId],
|
|
xMin,
|
|
yMin,
|
|
xMax,
|
|
yMax,
|
|
path
|
|
};
|
|
}
|
|
callback.call(
|
|
null,
|
|
glyphObj,
|
|
penX + positions[i * 3] * fontScale,
|
|
positions[i * 3 + 1] * fontScale,
|
|
charIndex
|
|
);
|
|
penX += positions[i * 3 + 2] * fontScale;
|
|
if (letterSpacing) {
|
|
penX += letterSpacing * fontSize;
|
|
}
|
|
}
|
|
charIndex += text.codePointAt(charIndex) > 65535 ? 2 : 1;
|
|
});
|
|
return penX;
|
|
}
|
|
};
|
|
return fontObj;
|
|
}
|
|
return function parse(buffer) {
|
|
const peek = new Uint8Array(buffer, 0, 4);
|
|
const tag = Typr._bin.readASCII(peek, 0, 4);
|
|
if (tag === "wOFF") {
|
|
buffer = woff2otf(buffer);
|
|
} else if (tag === "wOF2") {
|
|
throw new Error("woff2 fonts not supported");
|
|
}
|
|
return wrapFontObj(Typr.parse(buffer)[0]);
|
|
};
|
|
}
|
|
var workerModule = defineWorkerModule({
|
|
name: "Typr Font Parser",
|
|
dependencies: [typrFactory, woff2otfFactory, parserFactory],
|
|
init(typrFactory2, woff2otfFactory2, parserFactory2) {
|
|
const Typr = typrFactory2();
|
|
const woff2otf = woff2otfFactory2();
|
|
return parserFactory2(Typr, woff2otf);
|
|
}
|
|
});
|
|
function unicodeFontResolverClientFactory() {
|
|
return function(t) {
|
|
var n = function() {
|
|
this.buckets = /* @__PURE__ */ new Map();
|
|
};
|
|
n.prototype.add = function(t2) {
|
|
var n2 = t2 >> 5;
|
|
this.buckets.set(n2, (this.buckets.get(n2) || 0) | 1 << (31 & t2));
|
|
}, n.prototype.has = function(t2) {
|
|
var n2 = this.buckets.get(t2 >> 5);
|
|
return void 0 !== n2 && 0 != (n2 & 1 << (31 & t2));
|
|
}, n.prototype.serialize = function() {
|
|
var t2 = [];
|
|
return this.buckets.forEach(function(n2, r2) {
|
|
t2.push((+r2).toString(36) + ":" + n2.toString(36));
|
|
}), t2.join(",");
|
|
}, n.prototype.deserialize = function(t2) {
|
|
var n2 = this;
|
|
this.buckets.clear(), t2.split(",").forEach(function(t3) {
|
|
var r2 = t3.split(":");
|
|
n2.buckets.set(parseInt(r2[0], 36), parseInt(r2[1], 36));
|
|
});
|
|
};
|
|
var r = Math.pow(2, 8), e = r - 1, o = ~e;
|
|
function a(t2) {
|
|
var n2 = function(t3) {
|
|
return t3 & o;
|
|
}(t2).toString(16), e2 = function(t3) {
|
|
return (t3 & o) + r - 1;
|
|
}(t2).toString(16);
|
|
return "codepoint-index/plane" + (t2 >> 16) + "/" + n2 + "-" + e2 + ".json";
|
|
}
|
|
function i(t2, n2) {
|
|
var r2 = t2 & e, o2 = n2.codePointAt(r2 / 6 | 0);
|
|
return 0 != ((o2 = (o2 || 48) - 48) & 1 << r2 % 6);
|
|
}
|
|
function u(t2, n2) {
|
|
var r2;
|
|
(r2 = t2, r2.replace(/U\+/gi, "").replace(/^,+|,+$/g, "").split(/,+/).map(function(t3) {
|
|
return t3.split("-").map(function(t4) {
|
|
return parseInt(t4.trim(), 16);
|
|
});
|
|
})).forEach(function(t3) {
|
|
var r3 = t3[0], e2 = t3[1];
|
|
void 0 === e2 && (e2 = r3), n2(r3, e2);
|
|
});
|
|
}
|
|
function c(t2, n2) {
|
|
u(t2, function(t3, r2) {
|
|
for (var e2 = t3; e2 <= r2; e2++) n2(e2);
|
|
});
|
|
}
|
|
var s = {}, f = {}, l = /* @__PURE__ */ new WeakMap(), v = "https://cdn.jsdelivr.net/gh/lojjic/unicode-font-resolver@v1.0.1/packages/data";
|
|
function d(t2) {
|
|
var r2 = l.get(t2);
|
|
return r2 || (r2 = new n(), c(t2.ranges, function(t3) {
|
|
return r2.add(t3);
|
|
}), l.set(t2, r2)), r2;
|
|
}
|
|
var h, p = /* @__PURE__ */ new Map();
|
|
function g(t2, n2, r2) {
|
|
return t2[n2] ? n2 : t2[r2] ? r2 : function(t3) {
|
|
for (var n3 in t3) return n3;
|
|
}(t2);
|
|
}
|
|
function w(t2, n2) {
|
|
var r2 = n2;
|
|
if (!t2.includes(r2)) {
|
|
r2 = 1 / 0;
|
|
for (var e2 = 0; e2 < t2.length; e2++) Math.abs(t2[e2] - n2) < Math.abs(r2 - n2) && (r2 = t2[e2]);
|
|
}
|
|
return r2;
|
|
}
|
|
function k(t2) {
|
|
return h || (h = /* @__PURE__ */ new Set(), c("9-D,20,85,A0,1680,2000-200A,2028-202F,205F,3000", function(t3) {
|
|
h.add(t3);
|
|
})), h.has(t2);
|
|
}
|
|
return t.CodePointSet = n, t.clearCache = function() {
|
|
s = {}, f = {};
|
|
}, t.getFontsForString = function(t2, n2) {
|
|
void 0 === n2 && (n2 = {});
|
|
var r2, e2 = n2.lang;
|
|
void 0 === e2 && (e2 = /\p{Script=Hangul}/u.test(r2 = t2) ? "ko" : /\p{Script=Hiragana}|\p{Script=Katakana}/u.test(r2) ? "ja" : "en");
|
|
var o2 = n2.category;
|
|
void 0 === o2 && (o2 = "sans-serif");
|
|
var u2 = n2.style;
|
|
void 0 === u2 && (u2 = "normal");
|
|
var c2 = n2.weight;
|
|
void 0 === c2 && (c2 = 400);
|
|
var l2 = (n2.dataUrl || v).replace(/\/$/g, ""), h2 = /* @__PURE__ */ new Map(), y = new Uint8Array(t2.length), b = {}, m = {}, A = new Array(t2.length), S = /* @__PURE__ */ new Map(), j = false;
|
|
function M(t3) {
|
|
var n3 = p.get(t3);
|
|
return n3 || (n3 = fetch(l2 + "/" + t3).then(function(t4) {
|
|
if (!t4.ok) throw new Error(t4.statusText);
|
|
return t4.json().then(function(t5) {
|
|
if (!Array.isArray(t5) || 1 !== t5[0]) throw new Error("Incorrect schema version; need 1, got " + t5[0]);
|
|
return t5[1];
|
|
});
|
|
}).catch(function(n4) {
|
|
if (l2 !== v) return j || (console.error('unicode-font-resolver: Failed loading from dataUrl "' + l2 + '", trying default CDN. ' + n4.message), j = true), l2 = v, p.delete(t3), M(t3);
|
|
throw n4;
|
|
}), p.set(t3, n3)), n3;
|
|
}
|
|
for (var P = function(n3) {
|
|
var r3 = t2.codePointAt(n3), e3 = a(r3);
|
|
A[n3] = e3, s[e3] || S.has(e3) || S.set(e3, M(e3).then(function(t3) {
|
|
s[e3] = t3;
|
|
})), r3 > 65535 && (n3++, E = n3);
|
|
}, E = 0; E < t2.length; E++) P(E);
|
|
return Promise.all(S.values()).then(function() {
|
|
S.clear();
|
|
for (var n3 = function(n4) {
|
|
var o3 = t2.codePointAt(n4), a2 = null, u3 = s[A[n4]], c3 = void 0;
|
|
for (var l3 in u3) {
|
|
var v2 = m[l3];
|
|
if (void 0 === v2 && (v2 = m[l3] = new RegExp(l3).test(e2 || "en")), v2) {
|
|
for (var d2 in c3 = l3, u3[l3]) if (i(o3, u3[l3][d2])) {
|
|
a2 = d2;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!a2) {
|
|
t: for (var h3 in u3) if (h3 !== c3) {
|
|
for (var p2 in u3[h3]) if (i(o3, u3[h3][p2])) {
|
|
a2 = p2;
|
|
break t;
|
|
}
|
|
}
|
|
}
|
|
a2 || (console.debug("No font coverage for U+" + o3.toString(16)), a2 = "latin"), A[n4] = a2, f[a2] || S.has(a2) || S.set(a2, M("font-meta/" + a2 + ".json").then(function(t3) {
|
|
f[a2] = t3;
|
|
})), o3 > 65535 && (n4++, r3 = n4);
|
|
}, r3 = 0; r3 < t2.length; r3++) n3(r3);
|
|
return Promise.all(S.values());
|
|
}).then(function() {
|
|
for (var n3, r3 = null, e3 = 0; e3 < t2.length; e3++) {
|
|
var a2 = t2.codePointAt(e3);
|
|
if (r3 && (k(a2) || d(r3).has(a2))) y[e3] = y[e3 - 1];
|
|
else {
|
|
r3 = f[A[e3]];
|
|
var i2 = b[r3.id];
|
|
if (!i2) {
|
|
var s2 = r3.typeforms, v2 = g(s2, o2, "sans-serif"), p2 = g(s2[v2], u2, "normal"), m2 = w(null === (n3 = s2[v2]) || void 0 === n3 ? void 0 : n3[p2], c2);
|
|
i2 = b[r3.id] = l2 + "/font-files/" + r3.id + "/" + v2 + "." + p2 + "." + m2 + ".woff";
|
|
}
|
|
var S2 = h2.get(i2);
|
|
null == S2 && (S2 = h2.size, h2.set(i2, S2)), y[e3] = S2;
|
|
}
|
|
a2 > 65535 && (e3++, y[e3] = y[e3 - 1]);
|
|
}
|
|
return { fontUrls: Array.from(h2.keys()), chars: y };
|
|
});
|
|
}, Object.defineProperty(t, "__esModule", { value: true }), t;
|
|
}({});
|
|
}
|
|
function createFontResolver(fontParser, unicodeFontResolverClient) {
|
|
const parsedFonts = /* @__PURE__ */ Object.create(null);
|
|
const loadingFonts = /* @__PURE__ */ Object.create(null);
|
|
function doLoadFont(url, callback) {
|
|
const onError = (err) => {
|
|
console.error(`Failure loading font ${url}`, err);
|
|
};
|
|
try {
|
|
const request = new XMLHttpRequest();
|
|
request.open("get", url, true);
|
|
request.responseType = "arraybuffer";
|
|
request.onload = function() {
|
|
if (request.status >= 400) {
|
|
onError(new Error(request.statusText));
|
|
} else if (request.status > 0) {
|
|
try {
|
|
const fontObj = fontParser(request.response);
|
|
fontObj.src = url;
|
|
callback(fontObj);
|
|
} catch (e) {
|
|
onError(e);
|
|
}
|
|
}
|
|
};
|
|
request.onerror = onError;
|
|
request.send();
|
|
} catch (err) {
|
|
onError(err);
|
|
}
|
|
}
|
|
function loadFont(fontUrl, callback) {
|
|
let font = parsedFonts[fontUrl];
|
|
if (font) {
|
|
callback(font);
|
|
} else if (loadingFonts[fontUrl]) {
|
|
loadingFonts[fontUrl].push(callback);
|
|
} else {
|
|
loadingFonts[fontUrl] = [callback];
|
|
doLoadFont(fontUrl, (fontObj) => {
|
|
fontObj.src = fontUrl;
|
|
parsedFonts[fontUrl] = fontObj;
|
|
loadingFonts[fontUrl].forEach((cb) => cb(fontObj));
|
|
delete loadingFonts[fontUrl];
|
|
});
|
|
}
|
|
}
|
|
return function(text, callback, {
|
|
lang,
|
|
fonts: userFonts = [],
|
|
style = "normal",
|
|
weight = "normal",
|
|
unicodeFontsURL
|
|
} = {}) {
|
|
const charResolutions = new Uint8Array(text.length);
|
|
const fontResolutions = [];
|
|
if (!text.length) {
|
|
allDone();
|
|
}
|
|
const fontIndices = /* @__PURE__ */ new Map();
|
|
const fallbackRanges = [];
|
|
if (style !== "italic") style = "normal";
|
|
if (typeof weight !== "number") {
|
|
weight = weight === "bold" ? 700 : 400;
|
|
}
|
|
if (userFonts && !Array.isArray(userFonts)) {
|
|
userFonts = [userFonts];
|
|
}
|
|
userFonts = userFonts.slice().filter((def) => !def.lang || def.lang.test(lang)).reverse();
|
|
if (userFonts.length) {
|
|
const UNKNOWN = 0;
|
|
const RESOLVED = 1;
|
|
const NEEDS_FALLBACK = 2;
|
|
let prevCharResult = UNKNOWN;
|
|
(function resolveUserFonts(startIndex = 0) {
|
|
for (let i = startIndex, iLen = text.length; i < iLen; i++) {
|
|
const codePoint = text.codePointAt(i);
|
|
if (prevCharResult === RESOLVED && fontResolutions[charResolutions[i - 1]].supportsCodePoint(codePoint) || i > 0 && /\s/.test(text[i])) {
|
|
charResolutions[i] = charResolutions[i - 1];
|
|
if (prevCharResult === NEEDS_FALLBACK) {
|
|
fallbackRanges[fallbackRanges.length - 1][1] = i;
|
|
}
|
|
} else {
|
|
for (let j = charResolutions[i], jLen = userFonts.length; j <= jLen; j++) {
|
|
if (j === jLen) {
|
|
const range = prevCharResult === NEEDS_FALLBACK ? fallbackRanges[fallbackRanges.length - 1] : fallbackRanges[fallbackRanges.length] = [i, i];
|
|
range[1] = i;
|
|
prevCharResult = NEEDS_FALLBACK;
|
|
} else {
|
|
charResolutions[i] = j;
|
|
const { src, unicodeRange } = userFonts[j];
|
|
if (!unicodeRange || isCodeInRanges(codePoint, unicodeRange)) {
|
|
const fontObj = parsedFonts[src];
|
|
if (!fontObj) {
|
|
loadFont(src, () => {
|
|
resolveUserFonts(i);
|
|
});
|
|
return;
|
|
}
|
|
if (fontObj.supportsCodePoint(codePoint)) {
|
|
let fontIndex = fontIndices.get(fontObj);
|
|
if (typeof fontIndex !== "number") {
|
|
fontIndex = fontResolutions.length;
|
|
fontResolutions.push(fontObj);
|
|
fontIndices.set(fontObj, fontIndex);
|
|
}
|
|
charResolutions[i] = fontIndex;
|
|
prevCharResult = RESOLVED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (codePoint > 65535 && i + 1 < iLen) {
|
|
charResolutions[i + 1] = charResolutions[i];
|
|
i++;
|
|
if (prevCharResult === NEEDS_FALLBACK) {
|
|
fallbackRanges[fallbackRanges.length - 1][1] = i;
|
|
}
|
|
}
|
|
}
|
|
resolveFallbacks();
|
|
})();
|
|
} else {
|
|
fallbackRanges.push([0, text.length - 1]);
|
|
resolveFallbacks();
|
|
}
|
|
function resolveFallbacks() {
|
|
if (fallbackRanges.length) {
|
|
const fallbackString = fallbackRanges.map((range) => text.substring(range[0], range[1] + 1)).join("\n");
|
|
unicodeFontResolverClient.getFontsForString(fallbackString, {
|
|
lang: lang || void 0,
|
|
style,
|
|
weight,
|
|
dataUrl: unicodeFontsURL
|
|
}).then(({ fontUrls, chars }) => {
|
|
const fontIndexOffset = fontResolutions.length;
|
|
let charIdx = 0;
|
|
fallbackRanges.forEach((range) => {
|
|
for (let i = 0, endIdx = range[1] - range[0]; i <= endIdx; i++) {
|
|
charResolutions[range[0] + i] = chars[charIdx++] + fontIndexOffset;
|
|
}
|
|
charIdx++;
|
|
});
|
|
let loadedCount = 0;
|
|
fontUrls.forEach((url, i) => {
|
|
loadFont(url, (fontObj) => {
|
|
fontResolutions[i + fontIndexOffset] = fontObj;
|
|
if (++loadedCount === fontUrls.length) {
|
|
allDone();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
} else {
|
|
allDone();
|
|
}
|
|
}
|
|
function allDone() {
|
|
callback({
|
|
chars: charResolutions,
|
|
fonts: fontResolutions
|
|
});
|
|
}
|
|
function isCodeInRanges(code, ranges) {
|
|
for (let k = 0; k < ranges.length; k++) {
|
|
const [start, end = start] = ranges[k];
|
|
if (start <= code && code <= end) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
}
|
|
var fontResolverWorkerModule = defineWorkerModule({
|
|
name: "FontResolver",
|
|
dependencies: [
|
|
createFontResolver,
|
|
workerModule,
|
|
unicodeFontResolverClientFactory
|
|
],
|
|
init(createFontResolver2, fontParser, unicodeFontResolverClientFactory2) {
|
|
return createFontResolver2(fontParser, unicodeFontResolverClientFactory2());
|
|
}
|
|
});
|
|
function createTypesetter(resolveFonts, bidi) {
|
|
const INF = Infinity;
|
|
const DEFAULT_IGNORABLE_CHARS = /[\u00AD\u034F\u061C\u115F-\u1160\u17B4-\u17B5\u180B-\u180E\u200B-\u200F\u202A-\u202E\u2060-\u206F\u3164\uFE00-\uFE0F\uFEFF\uFFA0\uFFF0-\uFFF8]/;
|
|
const lineBreakingWhiteSpace = `[^\\S\\u00A0]`;
|
|
const BREAK_AFTER_CHARS = new RegExp(`${lineBreakingWhiteSpace}|[\\-\\u007C\\u00AD\\u2010\\u2012-\\u2014\\u2027\\u2056\\u2E17\\u2E40]`);
|
|
function calculateFontRuns({ text, lang, fonts, style, weight, preResolvedFonts, unicodeFontsURL }, onDone) {
|
|
const onResolved = ({ chars, fonts: parsedFonts }) => {
|
|
let curRun, prevVal;
|
|
const runs = [];
|
|
for (let i = 0; i < chars.length; i++) {
|
|
if (chars[i] !== prevVal) {
|
|
prevVal = chars[i];
|
|
runs.push(curRun = { start: i, end: i, fontObj: parsedFonts[chars[i]] });
|
|
} else {
|
|
curRun.end = i;
|
|
}
|
|
}
|
|
onDone(runs);
|
|
};
|
|
if (preResolvedFonts) {
|
|
onResolved(preResolvedFonts);
|
|
} else {
|
|
resolveFonts(
|
|
text,
|
|
onResolved,
|
|
{ lang, fonts, style, weight, unicodeFontsURL }
|
|
);
|
|
}
|
|
}
|
|
function typeset({
|
|
text = "",
|
|
font,
|
|
lang,
|
|
sdfGlyphSize = 64,
|
|
fontSize = 400,
|
|
fontWeight = 1,
|
|
fontStyle = "normal",
|
|
letterSpacing = 0,
|
|
lineHeight = "normal",
|
|
maxWidth = INF,
|
|
direction,
|
|
textAlign = "left",
|
|
textIndent = 0,
|
|
whiteSpace = "normal",
|
|
overflowWrap = "normal",
|
|
anchorX = 0,
|
|
anchorY = 0,
|
|
metricsOnly = false,
|
|
unicodeFontsURL,
|
|
preResolvedFonts = null,
|
|
includeCaretPositions = false,
|
|
chunkedBoundsSize = 8192,
|
|
colorRanges = null
|
|
}, callback) {
|
|
const mainStart = now2();
|
|
const timings = { fontLoad: 0, typesetting: 0 };
|
|
if (text.indexOf("\r") > -1) {
|
|
console.info("Typesetter: got text with \\r chars; normalizing to \\n");
|
|
text = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
}
|
|
fontSize = +fontSize;
|
|
letterSpacing = +letterSpacing;
|
|
maxWidth = +maxWidth;
|
|
lineHeight = lineHeight || "normal";
|
|
textIndent = +textIndent;
|
|
calculateFontRuns({
|
|
text,
|
|
lang,
|
|
style: fontStyle,
|
|
weight: fontWeight,
|
|
fonts: typeof font === "string" ? [{ src: font }] : font,
|
|
unicodeFontsURL,
|
|
preResolvedFonts
|
|
}, (runs) => {
|
|
timings.fontLoad = now2() - mainStart;
|
|
const hasMaxWidth = isFinite(maxWidth);
|
|
let glyphIds = null;
|
|
let glyphFontIndices = null;
|
|
let glyphPositions = null;
|
|
let glyphData = null;
|
|
let glyphColors = null;
|
|
let caretPositions = null;
|
|
let visibleBounds = null;
|
|
let chunkedBounds = null;
|
|
let maxLineWidth = 0;
|
|
let renderableGlyphCount = 0;
|
|
let canWrap = whiteSpace !== "nowrap";
|
|
const metricsByFont = /* @__PURE__ */ new Map();
|
|
const typesetStart = now2();
|
|
let lineXOffset = textIndent;
|
|
let prevRunEndX = 0;
|
|
let currentLine = new TextLine();
|
|
const lines = [currentLine];
|
|
runs.forEach((run) => {
|
|
const { fontObj } = run;
|
|
const { ascender, descender, unitsPerEm, lineGap, capHeight, xHeight } = fontObj;
|
|
let fontData2 = metricsByFont.get(fontObj);
|
|
if (!fontData2) {
|
|
const fontSizeMult2 = fontSize / unitsPerEm;
|
|
const calcLineHeight = lineHeight === "normal" ? (ascender - descender + lineGap) * fontSizeMult2 : lineHeight * fontSize;
|
|
const halfLeading = (calcLineHeight - (ascender - descender) * fontSizeMult2) / 2;
|
|
const caretHeight = Math.min(calcLineHeight, (ascender - descender) * fontSizeMult2);
|
|
const caretTop = (ascender + descender) / 2 * fontSizeMult2 + caretHeight / 2;
|
|
fontData2 = {
|
|
index: metricsByFont.size,
|
|
src: fontObj.src,
|
|
fontObj,
|
|
fontSizeMult: fontSizeMult2,
|
|
unitsPerEm,
|
|
ascender: ascender * fontSizeMult2,
|
|
descender: descender * fontSizeMult2,
|
|
capHeight: capHeight * fontSizeMult2,
|
|
xHeight: xHeight * fontSizeMult2,
|
|
lineHeight: calcLineHeight,
|
|
baseline: -halfLeading - ascender * fontSizeMult2,
|
|
// baseline offset from top of line height
|
|
// cap: -halfLeading - capHeight * fontSizeMult, // cap from top of line height
|
|
// ex: -halfLeading - xHeight * fontSizeMult, // ex from top of line height
|
|
caretTop,
|
|
caretBottom: caretTop - caretHeight
|
|
};
|
|
metricsByFont.set(fontObj, fontData2);
|
|
}
|
|
const { fontSizeMult } = fontData2;
|
|
const runText = text.slice(run.start, run.end + 1);
|
|
let prevGlyphX, prevGlyphObj;
|
|
fontObj.forEachGlyph(runText, fontSize, letterSpacing, (glyphObj, glyphX, glyphY, charIndex) => {
|
|
glyphX += prevRunEndX;
|
|
charIndex += run.start;
|
|
prevGlyphX = glyphX;
|
|
prevGlyphObj = glyphObj;
|
|
const char = text.charAt(charIndex);
|
|
const glyphWidth = glyphObj.advanceWidth * fontSizeMult;
|
|
const curLineCount = currentLine.count;
|
|
let nextLine;
|
|
if (!("isEmpty" in glyphObj)) {
|
|
glyphObj.isWhitespace = !!char && new RegExp(lineBreakingWhiteSpace).test(char);
|
|
glyphObj.canBreakAfter = !!char && BREAK_AFTER_CHARS.test(char);
|
|
glyphObj.isEmpty = glyphObj.xMin === glyphObj.xMax || glyphObj.yMin === glyphObj.yMax || DEFAULT_IGNORABLE_CHARS.test(char);
|
|
}
|
|
if (!glyphObj.isWhitespace && !glyphObj.isEmpty) {
|
|
renderableGlyphCount++;
|
|
}
|
|
if (canWrap && hasMaxWidth && !glyphObj.isWhitespace && glyphX + glyphWidth + lineXOffset > maxWidth && curLineCount) {
|
|
if (currentLine.glyphAt(curLineCount - 1).glyphObj.canBreakAfter) {
|
|
nextLine = new TextLine();
|
|
lineXOffset = -glyphX;
|
|
} else {
|
|
for (let i = curLineCount; i--; ) {
|
|
if (i === 0 && overflowWrap === "break-word") {
|
|
nextLine = new TextLine();
|
|
lineXOffset = -glyphX;
|
|
break;
|
|
} else if (currentLine.glyphAt(i).glyphObj.canBreakAfter) {
|
|
nextLine = currentLine.splitAt(i + 1);
|
|
const adjustX = nextLine.glyphAt(0).x;
|
|
lineXOffset -= adjustX;
|
|
for (let j = nextLine.count; j--; ) {
|
|
nextLine.glyphAt(j).x -= adjustX;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (nextLine) {
|
|
currentLine.isSoftWrapped = true;
|
|
currentLine = nextLine;
|
|
lines.push(currentLine);
|
|
maxLineWidth = maxWidth;
|
|
}
|
|
}
|
|
let fly = currentLine.glyphAt(currentLine.count);
|
|
fly.glyphObj = glyphObj;
|
|
fly.x = glyphX + lineXOffset;
|
|
fly.y = glyphY;
|
|
fly.width = glyphWidth;
|
|
fly.charIndex = charIndex;
|
|
fly.fontData = fontData2;
|
|
if (char === "\n") {
|
|
currentLine = new TextLine();
|
|
lines.push(currentLine);
|
|
lineXOffset = -(glyphX + glyphWidth + letterSpacing * fontSize) + textIndent;
|
|
}
|
|
});
|
|
prevRunEndX = prevGlyphX + prevGlyphObj.advanceWidth * fontSizeMult + letterSpacing * fontSize;
|
|
});
|
|
let totalHeight = 0;
|
|
lines.forEach((line) => {
|
|
let isTrailingWhitespace = true;
|
|
for (let i = line.count; i--; ) {
|
|
const glyphInfo = line.glyphAt(i);
|
|
if (isTrailingWhitespace && !glyphInfo.glyphObj.isWhitespace) {
|
|
line.width = glyphInfo.x + glyphInfo.width;
|
|
if (line.width > maxLineWidth) {
|
|
maxLineWidth = line.width;
|
|
}
|
|
isTrailingWhitespace = false;
|
|
}
|
|
let { lineHeight: lineHeight2, capHeight, xHeight, baseline } = glyphInfo.fontData;
|
|
if (lineHeight2 > line.lineHeight) line.lineHeight = lineHeight2;
|
|
const baselineDiff = baseline - line.baseline;
|
|
if (baselineDiff < 0) {
|
|
line.baseline += baselineDiff;
|
|
line.cap += baselineDiff;
|
|
line.ex += baselineDiff;
|
|
}
|
|
line.cap = Math.max(line.cap, line.baseline + capHeight);
|
|
line.ex = Math.max(line.ex, line.baseline + xHeight);
|
|
}
|
|
line.baseline -= totalHeight;
|
|
line.cap -= totalHeight;
|
|
line.ex -= totalHeight;
|
|
totalHeight += line.lineHeight;
|
|
});
|
|
let anchorXOffset = 0;
|
|
let anchorYOffset = 0;
|
|
if (anchorX) {
|
|
if (typeof anchorX === "number") {
|
|
anchorXOffset = -anchorX;
|
|
} else if (typeof anchorX === "string") {
|
|
anchorXOffset = -maxLineWidth * (anchorX === "left" ? 0 : anchorX === "center" ? 0.5 : anchorX === "right" ? 1 : parsePercent(anchorX));
|
|
}
|
|
}
|
|
if (anchorY) {
|
|
if (typeof anchorY === "number") {
|
|
anchorYOffset = -anchorY;
|
|
} else if (typeof anchorY === "string") {
|
|
anchorYOffset = anchorY === "top" ? 0 : anchorY === "top-baseline" ? -lines[0].baseline : anchorY === "top-cap" ? -lines[0].cap : anchorY === "top-ex" ? -lines[0].ex : anchorY === "middle" ? totalHeight / 2 : anchorY === "bottom" ? totalHeight : anchorY === "bottom-baseline" ? -lines[lines.length - 1].baseline : parsePercent(anchorY) * totalHeight;
|
|
}
|
|
}
|
|
if (!metricsOnly) {
|
|
const bidiLevelsResult = bidi.getEmbeddingLevels(text, direction);
|
|
glyphIds = new Uint16Array(renderableGlyphCount);
|
|
glyphFontIndices = new Uint8Array(renderableGlyphCount);
|
|
glyphPositions = new Float32Array(renderableGlyphCount * 2);
|
|
glyphData = {};
|
|
visibleBounds = [INF, INF, -INF, -INF];
|
|
chunkedBounds = [];
|
|
if (includeCaretPositions) {
|
|
caretPositions = new Float32Array(text.length * 4);
|
|
}
|
|
if (colorRanges) {
|
|
glyphColors = new Uint8Array(renderableGlyphCount * 3);
|
|
}
|
|
let renderableGlyphIndex = 0;
|
|
let prevCharIndex = -1;
|
|
let colorCharIndex = -1;
|
|
let chunk;
|
|
let currentColor;
|
|
lines.forEach((line, lineIndex) => {
|
|
let { count: lineGlyphCount, width: lineWidth } = line;
|
|
if (lineGlyphCount > 0) {
|
|
let trailingWhitespaceCount = 0;
|
|
for (let i = lineGlyphCount; i-- && line.glyphAt(i).glyphObj.isWhitespace; ) {
|
|
trailingWhitespaceCount++;
|
|
}
|
|
let lineXOffset2 = 0;
|
|
let justifyAdjust = 0;
|
|
if (textAlign === "center") {
|
|
lineXOffset2 = (maxLineWidth - lineWidth) / 2;
|
|
} else if (textAlign === "right") {
|
|
lineXOffset2 = maxLineWidth - lineWidth;
|
|
} else if (textAlign === "justify" && line.isSoftWrapped) {
|
|
let whitespaceCount = 0;
|
|
for (let i = lineGlyphCount - trailingWhitespaceCount; i--; ) {
|
|
if (line.glyphAt(i).glyphObj.isWhitespace) {
|
|
whitespaceCount++;
|
|
}
|
|
}
|
|
justifyAdjust = (maxLineWidth - lineWidth) / whitespaceCount;
|
|
}
|
|
if (justifyAdjust || lineXOffset2) {
|
|
let justifyOffset = 0;
|
|
for (let i = 0; i < lineGlyphCount; i++) {
|
|
let glyphInfo = line.glyphAt(i);
|
|
const glyphObj2 = glyphInfo.glyphObj;
|
|
glyphInfo.x += lineXOffset2 + justifyOffset;
|
|
if (justifyAdjust !== 0 && glyphObj2.isWhitespace && i < lineGlyphCount - trailingWhitespaceCount) {
|
|
justifyOffset += justifyAdjust;
|
|
glyphInfo.width += justifyAdjust;
|
|
}
|
|
}
|
|
}
|
|
const flips = bidi.getReorderSegments(
|
|
text,
|
|
bidiLevelsResult,
|
|
line.glyphAt(0).charIndex,
|
|
line.glyphAt(line.count - 1).charIndex
|
|
);
|
|
for (let fi = 0; fi < flips.length; fi++) {
|
|
const [start, end] = flips[fi];
|
|
let left = Infinity, right = -Infinity;
|
|
for (let i = 0; i < lineGlyphCount; i++) {
|
|
if (line.glyphAt(i).charIndex >= start) {
|
|
let startInLine = i, endInLine = i;
|
|
for (; endInLine < lineGlyphCount; endInLine++) {
|
|
let info = line.glyphAt(endInLine);
|
|
if (info.charIndex > end) {
|
|
break;
|
|
}
|
|
if (endInLine < lineGlyphCount - trailingWhitespaceCount) {
|
|
left = Math.min(left, info.x);
|
|
right = Math.max(right, info.x + info.width);
|
|
}
|
|
}
|
|
for (let j = startInLine; j < endInLine; j++) {
|
|
const glyphInfo = line.glyphAt(j);
|
|
glyphInfo.x = right - (glyphInfo.x + glyphInfo.width - left);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
let glyphObj;
|
|
const setGlyphObj = (g) => glyphObj = g;
|
|
for (let i = 0; i < lineGlyphCount; i++) {
|
|
const glyphInfo = line.glyphAt(i);
|
|
glyphObj = glyphInfo.glyphObj;
|
|
const glyphId = glyphObj.index;
|
|
const rtl = bidiLevelsResult.levels[glyphInfo.charIndex] & 1;
|
|
if (rtl) {
|
|
const mirrored = bidi.getMirroredCharacter(text[glyphInfo.charIndex]);
|
|
if (mirrored) {
|
|
glyphInfo.fontData.fontObj.forEachGlyph(mirrored, 0, 0, setGlyphObj);
|
|
}
|
|
}
|
|
if (includeCaretPositions) {
|
|
const { charIndex, fontData: fontData2 } = glyphInfo;
|
|
const caretLeft = glyphInfo.x + anchorXOffset;
|
|
const caretRight = glyphInfo.x + glyphInfo.width + anchorXOffset;
|
|
caretPositions[charIndex * 4] = rtl ? caretRight : caretLeft;
|
|
caretPositions[charIndex * 4 + 1] = rtl ? caretLeft : caretRight;
|
|
caretPositions[charIndex * 4 + 2] = line.baseline + fontData2.caretBottom + anchorYOffset;
|
|
caretPositions[charIndex * 4 + 3] = line.baseline + fontData2.caretTop + anchorYOffset;
|
|
const ligCount = charIndex - prevCharIndex;
|
|
if (ligCount > 1) {
|
|
fillLigatureCaretPositions(caretPositions, prevCharIndex, ligCount);
|
|
}
|
|
prevCharIndex = charIndex;
|
|
}
|
|
if (colorRanges) {
|
|
const { charIndex } = glyphInfo;
|
|
while (charIndex > colorCharIndex) {
|
|
colorCharIndex++;
|
|
if (colorRanges.hasOwnProperty(colorCharIndex)) {
|
|
currentColor = colorRanges[colorCharIndex];
|
|
}
|
|
}
|
|
}
|
|
if (!glyphObj.isWhitespace && !glyphObj.isEmpty) {
|
|
const idx = renderableGlyphIndex++;
|
|
const { fontSizeMult, src: fontSrc, index: fontIndex } = glyphInfo.fontData;
|
|
const fontGlyphData = glyphData[fontSrc] || (glyphData[fontSrc] = {});
|
|
if (!fontGlyphData[glyphId]) {
|
|
fontGlyphData[glyphId] = {
|
|
path: glyphObj.path,
|
|
pathBounds: [glyphObj.xMin, glyphObj.yMin, glyphObj.xMax, glyphObj.yMax]
|
|
};
|
|
}
|
|
const glyphX = glyphInfo.x + anchorXOffset;
|
|
const glyphY = glyphInfo.y + line.baseline + anchorYOffset;
|
|
glyphPositions[idx * 2] = glyphX;
|
|
glyphPositions[idx * 2 + 1] = glyphY;
|
|
const visX0 = glyphX + glyphObj.xMin * fontSizeMult;
|
|
const visY0 = glyphY + glyphObj.yMin * fontSizeMult;
|
|
const visX1 = glyphX + glyphObj.xMax * fontSizeMult;
|
|
const visY1 = glyphY + glyphObj.yMax * fontSizeMult;
|
|
if (visX0 < visibleBounds[0]) visibleBounds[0] = visX0;
|
|
if (visY0 < visibleBounds[1]) visibleBounds[1] = visY0;
|
|
if (visX1 > visibleBounds[2]) visibleBounds[2] = visX1;
|
|
if (visY1 > visibleBounds[3]) visibleBounds[3] = visY1;
|
|
if (idx % chunkedBoundsSize === 0) {
|
|
chunk = { start: idx, end: idx, rect: [INF, INF, -INF, -INF] };
|
|
chunkedBounds.push(chunk);
|
|
}
|
|
chunk.end++;
|
|
const chunkRect = chunk.rect;
|
|
if (visX0 < chunkRect[0]) chunkRect[0] = visX0;
|
|
if (visY0 < chunkRect[1]) chunkRect[1] = visY0;
|
|
if (visX1 > chunkRect[2]) chunkRect[2] = visX1;
|
|
if (visY1 > chunkRect[3]) chunkRect[3] = visY1;
|
|
glyphIds[idx] = glyphId;
|
|
glyphFontIndices[idx] = fontIndex;
|
|
if (colorRanges) {
|
|
const start = idx * 3;
|
|
glyphColors[start] = currentColor >> 16 & 255;
|
|
glyphColors[start + 1] = currentColor >> 8 & 255;
|
|
glyphColors[start + 2] = currentColor & 255;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
if (caretPositions) {
|
|
const ligCount = text.length - prevCharIndex;
|
|
if (ligCount > 1) {
|
|
fillLigatureCaretPositions(caretPositions, prevCharIndex, ligCount);
|
|
}
|
|
}
|
|
}
|
|
const fontData = [];
|
|
metricsByFont.forEach(({ index, src, unitsPerEm, ascender, descender, lineHeight: lineHeight2, capHeight, xHeight }) => {
|
|
fontData[index] = { src, unitsPerEm, ascender, descender, lineHeight: lineHeight2, capHeight, xHeight };
|
|
});
|
|
timings.typesetting = now2() - typesetStart;
|
|
callback({
|
|
glyphIds,
|
|
//id for each glyph, specific to that glyph's font
|
|
glyphFontIndices,
|
|
//index into fontData for each glyph
|
|
glyphPositions,
|
|
//x,y of each glyph's origin in layout
|
|
glyphData,
|
|
//dict holding data about each glyph appearing in the text
|
|
fontData,
|
|
//data about each font used in the text
|
|
caretPositions,
|
|
//startX,endX,bottomY caret positions for each char
|
|
// caretHeight, //height of cursor from bottom to top - todo per glyph?
|
|
glyphColors,
|
|
//color for each glyph, if color ranges supplied
|
|
chunkedBounds,
|
|
//total rects per (n=chunkedBoundsSize) consecutive glyphs
|
|
fontSize,
|
|
//calculated em height
|
|
topBaseline: anchorYOffset + lines[0].baseline,
|
|
//y coordinate of the top line's baseline
|
|
blockBounds: [
|
|
//bounds for the whole block of text, including vertical padding for lineHeight
|
|
anchorXOffset,
|
|
anchorYOffset - totalHeight,
|
|
anchorXOffset + maxLineWidth,
|
|
anchorYOffset
|
|
],
|
|
visibleBounds,
|
|
//total bounds of visible text paths, may be larger or smaller than blockBounds
|
|
timings
|
|
});
|
|
});
|
|
}
|
|
function measure(args, callback) {
|
|
typeset({ ...args, metricsOnly: true }, (result) => {
|
|
const [x0, y0, x1, y1] = result.blockBounds;
|
|
callback({
|
|
width: x1 - x0,
|
|
height: y1 - y0
|
|
});
|
|
});
|
|
}
|
|
function parsePercent(str) {
|
|
let match = str.match(/^([\d.]+)%$/);
|
|
let pct = match ? parseFloat(match[1]) : NaN;
|
|
return isNaN(pct) ? 0 : pct / 100;
|
|
}
|
|
function fillLigatureCaretPositions(caretPositions, ligStartIndex, ligCount) {
|
|
const ligStartX = caretPositions[ligStartIndex * 4];
|
|
const ligEndX = caretPositions[ligStartIndex * 4 + 1];
|
|
const ligBottom = caretPositions[ligStartIndex * 4 + 2];
|
|
const ligTop = caretPositions[ligStartIndex * 4 + 3];
|
|
const guessedAdvanceX = (ligEndX - ligStartX) / ligCount;
|
|
for (let i = 0; i < ligCount; i++) {
|
|
const startIndex = (ligStartIndex + i) * 4;
|
|
caretPositions[startIndex] = ligStartX + guessedAdvanceX * i;
|
|
caretPositions[startIndex + 1] = ligStartX + guessedAdvanceX * (i + 1);
|
|
caretPositions[startIndex + 2] = ligBottom;
|
|
caretPositions[startIndex + 3] = ligTop;
|
|
}
|
|
}
|
|
function now2() {
|
|
return (self.performance || Date).now();
|
|
}
|
|
function TextLine() {
|
|
this.data = [];
|
|
}
|
|
const textLineProps = ["glyphObj", "x", "y", "width", "charIndex", "fontData"];
|
|
TextLine.prototype = {
|
|
width: 0,
|
|
lineHeight: 0,
|
|
baseline: 0,
|
|
cap: 0,
|
|
ex: 0,
|
|
isSoftWrapped: false,
|
|
get count() {
|
|
return Math.ceil(this.data.length / textLineProps.length);
|
|
},
|
|
glyphAt(i) {
|
|
let fly = TextLine.flyweight;
|
|
fly.data = this.data;
|
|
fly.index = i;
|
|
return fly;
|
|
},
|
|
splitAt(i) {
|
|
let newLine = new TextLine();
|
|
newLine.data = this.data.splice(i * textLineProps.length);
|
|
return newLine;
|
|
}
|
|
};
|
|
TextLine.flyweight = textLineProps.reduce((obj, prop, i, all) => {
|
|
Object.defineProperty(obj, prop, {
|
|
get() {
|
|
return this.data[this.index * textLineProps.length + i];
|
|
},
|
|
set(val) {
|
|
this.data[this.index * textLineProps.length + i] = val;
|
|
}
|
|
});
|
|
return obj;
|
|
}, { data: null, index: 0 });
|
|
return {
|
|
typeset,
|
|
measure
|
|
};
|
|
}
|
|
var now = () => (self.performance || Date).now();
|
|
var mainThreadGenerator = SDFGenerator();
|
|
var warned;
|
|
function generateSDF(width, height, path, viewBox, distance, exponent, canvas, x, y, channel, useWebGL = true) {
|
|
if (!useWebGL) {
|
|
return generateSDF_JS_Worker(width, height, path, viewBox, distance, exponent, canvas, x, y, channel);
|
|
}
|
|
return generateSDF_GL(width, height, path, viewBox, distance, exponent, canvas, x, y, channel).then(
|
|
null,
|
|
(err) => {
|
|
if (!warned) {
|
|
console.warn(`WebGL SDF generation failed, falling back to JS`, err);
|
|
warned = true;
|
|
}
|
|
return generateSDF_JS_Worker(width, height, path, viewBox, distance, exponent, canvas, x, y, channel);
|
|
}
|
|
);
|
|
}
|
|
var queue = [];
|
|
var chunkTimeBudget = 5;
|
|
var timer = 0;
|
|
function nextChunk() {
|
|
const start = now();
|
|
while (queue.length && now() - start < chunkTimeBudget) {
|
|
queue.shift()();
|
|
}
|
|
timer = queue.length ? setTimeout(nextChunk, 0) : 0;
|
|
}
|
|
var generateSDF_GL = (...args) => {
|
|
return new Promise((resolve, reject) => {
|
|
queue.push(() => {
|
|
const start = now();
|
|
try {
|
|
mainThreadGenerator.webgl.generateIntoCanvas(...args);
|
|
resolve({ timing: now() - start });
|
|
} catch (err) {
|
|
reject(err);
|
|
}
|
|
});
|
|
if (!timer) {
|
|
timer = setTimeout(nextChunk, 0);
|
|
}
|
|
});
|
|
};
|
|
var threadCount = 4;
|
|
var idleTimeout = 2e3;
|
|
var threads = {};
|
|
var callNum = 0;
|
|
function generateSDF_JS_Worker(width, height, path, viewBox, distance, exponent, canvas, x, y, channel) {
|
|
const workerId = "TroikaTextSDFGenerator_JS_" + callNum++ % threadCount;
|
|
let thread = threads[workerId];
|
|
if (!thread) {
|
|
thread = threads[workerId] = {
|
|
workerModule: defineWorkerModule({
|
|
name: workerId,
|
|
workerId,
|
|
dependencies: [
|
|
SDFGenerator,
|
|
now
|
|
],
|
|
init(_createSDFGenerator, now2) {
|
|
const generate = _createSDFGenerator().javascript.generate;
|
|
return function(...args) {
|
|
const start = now2();
|
|
const textureData = generate(...args);
|
|
return {
|
|
textureData,
|
|
timing: now2() - start
|
|
};
|
|
};
|
|
},
|
|
getTransferables(result) {
|
|
return [result.textureData.buffer];
|
|
}
|
|
}),
|
|
requests: 0,
|
|
idleTimer: null
|
|
};
|
|
}
|
|
thread.requests++;
|
|
clearTimeout(thread.idleTimer);
|
|
return thread.workerModule(width, height, path, viewBox, distance, exponent).then(({ textureData, timing }) => {
|
|
const start = now();
|
|
const imageData = new Uint8Array(textureData.length * 4);
|
|
for (let i = 0; i < textureData.length; i++) {
|
|
imageData[i * 4 + channel] = textureData[i];
|
|
}
|
|
mainThreadGenerator.webglUtils.renderImageData(canvas, imageData, x, y, width, height, 1 << 3 - channel);
|
|
timing += now() - start;
|
|
if (--thread.requests === 0) {
|
|
thread.idleTimer = setTimeout(() => {
|
|
terminateWorker(workerId);
|
|
}, idleTimeout);
|
|
}
|
|
return { timing };
|
|
});
|
|
}
|
|
function warmUpSDFCanvas(canvas) {
|
|
if (!canvas._warm) {
|
|
mainThreadGenerator.webgl.isSupported(canvas);
|
|
canvas._warm = true;
|
|
}
|
|
}
|
|
var resizeWebGLCanvasWithoutClearing = mainThreadGenerator.webglUtils.resizeWebGLCanvasWithoutClearing;
|
|
var CONFIG = {
|
|
defaultFontURL: null,
|
|
unicodeFontsURL: null,
|
|
sdfGlyphSize: 64,
|
|
sdfMargin: 1 / 16,
|
|
sdfExponent: 9,
|
|
textureWidth: 2048,
|
|
useWorker: true
|
|
};
|
|
var tempColor = new Color();
|
|
var hasRequested = false;
|
|
function now$1() {
|
|
return (self.performance || Date).now();
|
|
}
|
|
function configureTextBuilder(config) {
|
|
if (hasRequested) {
|
|
console.warn("configureTextBuilder called after first font request; will be ignored.");
|
|
} else {
|
|
assign(CONFIG, config);
|
|
}
|
|
}
|
|
var atlases = /* @__PURE__ */ Object.create(null);
|
|
function getTextRenderInfo(args, callback) {
|
|
hasRequested = true;
|
|
args = assign({}, args);
|
|
const totalStart = now$1();
|
|
const { defaultFontURL } = CONFIG;
|
|
const fonts = [];
|
|
if (defaultFontURL) {
|
|
fonts.push({ label: "default", src: toAbsoluteURL(defaultFontURL) });
|
|
}
|
|
if (args.font) {
|
|
fonts.push({ label: "user", src: toAbsoluteURL(args.font) });
|
|
}
|
|
args.font = fonts;
|
|
args.text = "" + args.text;
|
|
args.sdfGlyphSize = args.sdfGlyphSize || CONFIG.sdfGlyphSize;
|
|
args.unicodeFontsURL = args.unicodeFontsURL || CONFIG.unicodeFontsURL;
|
|
if (args.colorRanges != null) {
|
|
let colors = {};
|
|
for (let key in args.colorRanges) {
|
|
if (args.colorRanges.hasOwnProperty(key)) {
|
|
let val = args.colorRanges[key];
|
|
if (typeof val !== "number") {
|
|
val = tempColor.set(val).getHex();
|
|
}
|
|
colors[key] = val;
|
|
}
|
|
}
|
|
args.colorRanges = colors;
|
|
}
|
|
Object.freeze(args);
|
|
const { textureWidth, sdfExponent } = CONFIG;
|
|
const { sdfGlyphSize } = args;
|
|
const glyphsPerRow = textureWidth / sdfGlyphSize * 4;
|
|
let atlas = atlases[sdfGlyphSize];
|
|
if (!atlas) {
|
|
const canvas = document.createElement("canvas");
|
|
canvas.width = textureWidth;
|
|
canvas.height = sdfGlyphSize * 256 / glyphsPerRow;
|
|
atlas = atlases[sdfGlyphSize] = {
|
|
glyphCount: 0,
|
|
sdfGlyphSize,
|
|
sdfCanvas: canvas,
|
|
sdfTexture: new Texture(
|
|
canvas,
|
|
void 0,
|
|
void 0,
|
|
void 0,
|
|
LinearFilter,
|
|
LinearFilter
|
|
),
|
|
contextLost: false,
|
|
glyphsByFont: /* @__PURE__ */ new Map()
|
|
};
|
|
atlas.sdfTexture.generateMipmaps = false;
|
|
initContextLossHandling(atlas);
|
|
}
|
|
const { sdfTexture, sdfCanvas } = atlas;
|
|
const typeset = CONFIG.useWorker ? typesetInWorker : typesetOnMainThread;
|
|
typeset(args).then((result) => {
|
|
const { glyphIds, glyphFontIndices, fontData, glyphPositions, fontSize, timings } = result;
|
|
const neededSDFs = [];
|
|
const glyphBounds = new Float32Array(glyphIds.length * 4);
|
|
let boundsIdx = 0;
|
|
let positionsIdx = 0;
|
|
const quadsStart = now$1();
|
|
const fontGlyphMaps = fontData.map((font) => {
|
|
let map = atlas.glyphsByFont.get(font.src);
|
|
if (!map) {
|
|
atlas.glyphsByFont.set(font.src, map = /* @__PURE__ */ new Map());
|
|
}
|
|
return map;
|
|
});
|
|
glyphIds.forEach((glyphId, i) => {
|
|
const fontIndex = glyphFontIndices[i];
|
|
const { src: fontSrc, unitsPerEm } = fontData[fontIndex];
|
|
let glyphInfo = fontGlyphMaps[fontIndex].get(glyphId);
|
|
if (!glyphInfo) {
|
|
const { path, pathBounds } = result.glyphData[fontSrc][glyphId];
|
|
const fontUnitsMargin = Math.max(pathBounds[2] - pathBounds[0], pathBounds[3] - pathBounds[1]) / sdfGlyphSize * (CONFIG.sdfMargin * sdfGlyphSize + 0.5);
|
|
const atlasIndex = atlas.glyphCount++;
|
|
const sdfViewBox2 = [
|
|
pathBounds[0] - fontUnitsMargin,
|
|
pathBounds[1] - fontUnitsMargin,
|
|
pathBounds[2] + fontUnitsMargin,
|
|
pathBounds[3] + fontUnitsMargin
|
|
];
|
|
fontGlyphMaps[fontIndex].set(glyphId, glyphInfo = { path, atlasIndex, sdfViewBox: sdfViewBox2 });
|
|
neededSDFs.push(glyphInfo);
|
|
}
|
|
const { sdfViewBox } = glyphInfo;
|
|
const posX = glyphPositions[positionsIdx++];
|
|
const posY = glyphPositions[positionsIdx++];
|
|
const fontSizeMult = fontSize / unitsPerEm;
|
|
glyphBounds[boundsIdx++] = posX + sdfViewBox[0] * fontSizeMult;
|
|
glyphBounds[boundsIdx++] = posY + sdfViewBox[1] * fontSizeMult;
|
|
glyphBounds[boundsIdx++] = posX + sdfViewBox[2] * fontSizeMult;
|
|
glyphBounds[boundsIdx++] = posY + sdfViewBox[3] * fontSizeMult;
|
|
glyphIds[i] = glyphInfo.atlasIndex;
|
|
});
|
|
timings.quads = (timings.quads || 0) + (now$1() - quadsStart);
|
|
const sdfStart = now$1();
|
|
timings.sdf = {};
|
|
const currentHeight = sdfCanvas.height;
|
|
const neededRows = Math.ceil(atlas.glyphCount / glyphsPerRow);
|
|
const neededHeight = Math.pow(2, Math.ceil(Math.log2(neededRows * sdfGlyphSize)));
|
|
if (neededHeight > currentHeight) {
|
|
console.info(`Increasing SDF texture size ${currentHeight}->${neededHeight}`);
|
|
resizeWebGLCanvasWithoutClearing(sdfCanvas, textureWidth, neededHeight);
|
|
sdfTexture.dispose();
|
|
}
|
|
Promise.all(neededSDFs.map(
|
|
(glyphInfo) => generateGlyphSDF(glyphInfo, atlas, args.gpuAccelerateSDF).then(({ timing }) => {
|
|
timings.sdf[glyphInfo.atlasIndex] = timing;
|
|
})
|
|
)).then(() => {
|
|
if (neededSDFs.length && !atlas.contextLost) {
|
|
safariPre15Workaround(atlas);
|
|
sdfTexture.needsUpdate = true;
|
|
}
|
|
timings.sdfTotal = now$1() - sdfStart;
|
|
timings.total = now$1() - totalStart;
|
|
callback(Object.freeze({
|
|
parameters: args,
|
|
sdfTexture,
|
|
sdfGlyphSize,
|
|
sdfExponent,
|
|
glyphBounds,
|
|
glyphAtlasIndices: glyphIds,
|
|
glyphColors: result.glyphColors,
|
|
caretPositions: result.caretPositions,
|
|
chunkedBounds: result.chunkedBounds,
|
|
ascender: result.ascender,
|
|
descender: result.descender,
|
|
lineHeight: result.lineHeight,
|
|
capHeight: result.capHeight,
|
|
xHeight: result.xHeight,
|
|
topBaseline: result.topBaseline,
|
|
blockBounds: result.blockBounds,
|
|
visibleBounds: result.visibleBounds,
|
|
timings: result.timings
|
|
}));
|
|
});
|
|
});
|
|
Promise.resolve().then(() => {
|
|
if (!atlas.contextLost) {
|
|
warmUpSDFCanvas(sdfCanvas);
|
|
}
|
|
});
|
|
}
|
|
function generateGlyphSDF({ path, atlasIndex, sdfViewBox }, { sdfGlyphSize, sdfCanvas, contextLost }, useGPU) {
|
|
if (contextLost) {
|
|
return Promise.resolve({ timing: -1 });
|
|
}
|
|
const { textureWidth, sdfExponent } = CONFIG;
|
|
const maxDist = Math.max(sdfViewBox[2] - sdfViewBox[0], sdfViewBox[3] - sdfViewBox[1]);
|
|
const squareIndex = Math.floor(atlasIndex / 4);
|
|
const x = squareIndex % (textureWidth / sdfGlyphSize) * sdfGlyphSize;
|
|
const y = Math.floor(squareIndex / (textureWidth / sdfGlyphSize)) * sdfGlyphSize;
|
|
const channel = atlasIndex % 4;
|
|
return generateSDF(sdfGlyphSize, sdfGlyphSize, path, sdfViewBox, maxDist, sdfExponent, sdfCanvas, x, y, channel, useGPU);
|
|
}
|
|
function initContextLossHandling(atlas) {
|
|
const canvas = atlas.sdfCanvas;
|
|
canvas.addEventListener("webglcontextlost", (event) => {
|
|
console.log("Context Lost", event);
|
|
event.preventDefault();
|
|
atlas.contextLost = true;
|
|
});
|
|
canvas.addEventListener("webglcontextrestored", (event) => {
|
|
console.log("Context Restored", event);
|
|
atlas.contextLost = false;
|
|
const promises = [];
|
|
atlas.glyphsByFont.forEach((glyphMap) => {
|
|
glyphMap.forEach((glyph) => {
|
|
promises.push(generateGlyphSDF(glyph, atlas, true));
|
|
});
|
|
});
|
|
Promise.all(promises).then(() => {
|
|
safariPre15Workaround(atlas);
|
|
atlas.sdfTexture.needsUpdate = true;
|
|
});
|
|
});
|
|
}
|
|
function preloadFont({ font, characters, sdfGlyphSize }, callback) {
|
|
let text = Array.isArray(characters) ? characters.join("\n") : "" + characters;
|
|
getTextRenderInfo({ font, sdfGlyphSize, text }, callback);
|
|
}
|
|
function assign(toObj, fromObj) {
|
|
for (let key in fromObj) {
|
|
if (fromObj.hasOwnProperty(key)) {
|
|
toObj[key] = fromObj[key];
|
|
}
|
|
}
|
|
return toObj;
|
|
}
|
|
var linkEl;
|
|
function toAbsoluteURL(path) {
|
|
if (!linkEl) {
|
|
linkEl = typeof document === "undefined" ? {} : document.createElement("a");
|
|
}
|
|
linkEl.href = path;
|
|
return linkEl.href;
|
|
}
|
|
function safariPre15Workaround(atlas) {
|
|
if (typeof createImageBitmap !== "function") {
|
|
console.info("Safari<15: applying SDF canvas workaround");
|
|
const { sdfCanvas, sdfTexture } = atlas;
|
|
const { width, height } = sdfCanvas;
|
|
const gl = atlas.sdfCanvas.getContext("webgl");
|
|
let pixels = sdfTexture.image.data;
|
|
if (!pixels || pixels.length !== width * height * 4) {
|
|
pixels = new Uint8Array(width * height * 4);
|
|
sdfTexture.image = { width, height, data: pixels };
|
|
sdfTexture.flipY = false;
|
|
sdfTexture.isDataTexture = true;
|
|
}
|
|
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
|
|
}
|
|
}
|
|
var typesetterWorkerModule = defineWorkerModule({
|
|
name: "Typesetter",
|
|
dependencies: [
|
|
createTypesetter,
|
|
fontResolverWorkerModule,
|
|
bidi_default
|
|
],
|
|
init(createTypesetter2, fontResolver, bidiFactory) {
|
|
return createTypesetter2(fontResolver, bidiFactory());
|
|
}
|
|
});
|
|
var typesetInWorker = defineWorkerModule({
|
|
name: "Typesetter",
|
|
dependencies: [
|
|
typesetterWorkerModule
|
|
],
|
|
init(typesetter) {
|
|
return function(args) {
|
|
return new Promise((resolve) => {
|
|
typesetter.typeset(args, resolve);
|
|
});
|
|
};
|
|
},
|
|
getTransferables(result) {
|
|
const transferables = [];
|
|
for (let p in result) {
|
|
if (result[p] && result[p].buffer) {
|
|
transferables.push(result[p].buffer);
|
|
}
|
|
}
|
|
return transferables;
|
|
}
|
|
});
|
|
var typesetOnMainThread = typesetInWorker.onMainThread;
|
|
function dumpSDFTextures() {
|
|
Object.keys(atlases).forEach((size) => {
|
|
const canvas = atlases[size].sdfCanvas;
|
|
const { width, height } = canvas;
|
|
console.log("%c.", `
|
|
background: url(${canvas.toDataURL()});
|
|
background-size: ${width}px ${height}px;
|
|
color: transparent;
|
|
font-size: 0;
|
|
line-height: ${height}px;
|
|
padding-left: ${width}px;
|
|
`);
|
|
});
|
|
}
|
|
var templateGeometries = {};
|
|
function getTemplateGeometry(detail) {
|
|
let geom = templateGeometries[detail];
|
|
if (!geom) {
|
|
geom = templateGeometries[detail] = new PlaneGeometry(1, 1, detail, detail).translate(0.5, 0.5, 0);
|
|
}
|
|
return geom;
|
|
}
|
|
var glyphBoundsAttrName = "aTroikaGlyphBounds";
|
|
var glyphIndexAttrName = "aTroikaGlyphIndex";
|
|
var glyphColorAttrName = "aTroikaGlyphColor";
|
|
var GlyphsGeometry = class extends InstancedBufferGeometry {
|
|
constructor() {
|
|
super();
|
|
this.detail = 1;
|
|
this.curveRadius = 0;
|
|
this.groups = [
|
|
{ start: 0, count: Infinity, materialIndex: 0 },
|
|
{ start: 0, count: Infinity, materialIndex: 1 }
|
|
];
|
|
this.boundingSphere = new Sphere();
|
|
this.boundingBox = new Box3();
|
|
}
|
|
computeBoundingSphere() {
|
|
}
|
|
computeBoundingBox() {
|
|
}
|
|
set detail(detail) {
|
|
if (detail !== this._detail) {
|
|
this._detail = detail;
|
|
if (typeof detail !== "number" || detail < 1) {
|
|
detail = 1;
|
|
}
|
|
let tpl = getTemplateGeometry(detail);
|
|
["position", "normal", "uv"].forEach((attr) => {
|
|
this.attributes[attr] = tpl.attributes[attr].clone();
|
|
});
|
|
this.setIndex(tpl.getIndex().clone());
|
|
}
|
|
}
|
|
get detail() {
|
|
return this._detail;
|
|
}
|
|
set curveRadius(r) {
|
|
if (r !== this._curveRadius) {
|
|
this._curveRadius = r;
|
|
this._updateBounds();
|
|
}
|
|
}
|
|
get curveRadius() {
|
|
return this._curveRadius;
|
|
}
|
|
/**
|
|
* Update the geometry for a new set of glyphs.
|
|
* @param {Float32Array} glyphBounds - An array holding the planar bounds for all glyphs
|
|
* to be rendered, 4 entries for each glyph: x1,x2,y1,y1
|
|
* @param {Float32Array} glyphAtlasIndices - An array holding the index of each glyph within
|
|
* the SDF atlas texture.
|
|
* @param {Array} blockBounds - An array holding the [minX, minY, maxX, maxY] across all glyphs
|
|
* @param {Array} [chunkedBounds] - An array of objects describing bounds for each chunk of N
|
|
* consecutive glyphs: `{start:N, end:N, rect:[minX, minY, maxX, maxY]}`. This can be
|
|
* used with `applyClipRect` to choose an optimized `instanceCount`.
|
|
* @param {Uint8Array} [glyphColors] - An array holding r,g,b values for each glyph.
|
|
*/
|
|
updateGlyphs(glyphBounds, glyphAtlasIndices, blockBounds, chunkedBounds, glyphColors) {
|
|
this.updateAttributeData(glyphBoundsAttrName, glyphBounds, 4);
|
|
this.updateAttributeData(glyphIndexAttrName, glyphAtlasIndices, 1);
|
|
this.updateAttributeData(glyphColorAttrName, glyphColors, 3);
|
|
this._blockBounds = blockBounds;
|
|
this._chunkedBounds = chunkedBounds;
|
|
this.instanceCount = glyphAtlasIndices.length;
|
|
this._updateBounds();
|
|
}
|
|
_updateBounds() {
|
|
const bounds = this._blockBounds;
|
|
if (bounds) {
|
|
const { curveRadius, boundingBox: bbox } = this;
|
|
if (curveRadius) {
|
|
const { PI, floor, min, max, sin, cos } = Math;
|
|
const halfPi = PI / 2;
|
|
const twoPi = PI * 2;
|
|
const absR = Math.abs(curveRadius);
|
|
const leftAngle = bounds[0] / absR;
|
|
const rightAngle = bounds[2] / absR;
|
|
const minX = floor((leftAngle + halfPi) / twoPi) !== floor((rightAngle + halfPi) / twoPi) ? -absR : min(sin(leftAngle) * absR, sin(rightAngle) * absR);
|
|
const maxX = floor((leftAngle - halfPi) / twoPi) !== floor((rightAngle - halfPi) / twoPi) ? absR : max(sin(leftAngle) * absR, sin(rightAngle) * absR);
|
|
const maxZ = floor((leftAngle + PI) / twoPi) !== floor((rightAngle + PI) / twoPi) ? absR * 2 : max(absR - cos(leftAngle) * absR, absR - cos(rightAngle) * absR);
|
|
bbox.min.set(minX, bounds[1], curveRadius < 0 ? -maxZ : 0);
|
|
bbox.max.set(maxX, bounds[3], curveRadius < 0 ? 0 : maxZ);
|
|
} else {
|
|
bbox.min.set(bounds[0], bounds[1], 0);
|
|
bbox.max.set(bounds[2], bounds[3], 0);
|
|
}
|
|
bbox.getBoundingSphere(this.boundingSphere);
|
|
}
|
|
}
|
|
/**
|
|
* Given a clipping rect, and the chunkedBounds from the last updateGlyphs call, choose the lowest
|
|
* `instanceCount` that will show all glyphs within the clipped view. This is an optimization
|
|
* for long blocks of text that are clipped, to skip vertex shader evaluation for glyphs that would
|
|
* be clipped anyway.
|
|
*
|
|
* Note that since `drawElementsInstanced[ANGLE]` only accepts an instance count and not a starting
|
|
* offset, this optimization becomes less effective as the clipRect moves closer to the end of the
|
|
* text block. We could fix that by switching from instancing to a full geometry with a drawRange,
|
|
* but at the expense of much larger attribute buffers (see classdoc above.)
|
|
*
|
|
* @param {Vector4} clipRect
|
|
*/
|
|
applyClipRect(clipRect) {
|
|
let count = this.getAttribute(glyphIndexAttrName).count;
|
|
let chunks = this._chunkedBounds;
|
|
if (chunks) {
|
|
for (let i = chunks.length; i--; ) {
|
|
count = chunks[i].end;
|
|
let rect = chunks[i].rect;
|
|
if (rect[1] < clipRect.w && rect[3] > clipRect.y && rect[0] < clipRect.z && rect[2] > clipRect.x) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
this.instanceCount = count;
|
|
}
|
|
/**
|
|
* Utility for updating instance attributes with automatic resizing
|
|
*/
|
|
updateAttributeData(attrName, newArray, itemSize) {
|
|
const attr = this.getAttribute(attrName);
|
|
if (newArray) {
|
|
if (attr && attr.array.length === newArray.length) {
|
|
attr.array.set(newArray);
|
|
attr.needsUpdate = true;
|
|
} else {
|
|
this.setAttribute(attrName, new InstancedBufferAttribute(newArray, itemSize));
|
|
delete this._maxInstanceCount;
|
|
this.dispose();
|
|
}
|
|
} else if (attr) {
|
|
this.deleteAttribute(attrName);
|
|
}
|
|
}
|
|
};
|
|
var VERTEX_DEFS = `
|
|
uniform vec2 uTroikaSDFTextureSize;
|
|
uniform float uTroikaSDFGlyphSize;
|
|
uniform vec4 uTroikaTotalBounds;
|
|
uniform vec4 uTroikaClipRect;
|
|
uniform mat3 uTroikaOrient;
|
|
uniform bool uTroikaUseGlyphColors;
|
|
uniform float uTroikaEdgeOffset;
|
|
uniform float uTroikaBlurRadius;
|
|
uniform vec2 uTroikaPositionOffset;
|
|
uniform float uTroikaCurveRadius;
|
|
attribute vec4 aTroikaGlyphBounds;
|
|
attribute float aTroikaGlyphIndex;
|
|
attribute vec3 aTroikaGlyphColor;
|
|
varying vec2 vTroikaGlyphUV;
|
|
varying vec4 vTroikaTextureUVBounds;
|
|
varying float vTroikaTextureChannel;
|
|
varying vec3 vTroikaGlyphColor;
|
|
varying vec2 vTroikaGlyphDimensions;
|
|
`;
|
|
var VERTEX_TRANSFORM = `
|
|
vec4 bounds = aTroikaGlyphBounds;
|
|
bounds.xz += uTroikaPositionOffset.x;
|
|
bounds.yw -= uTroikaPositionOffset.y;
|
|
|
|
vec4 outlineBounds = vec4(
|
|
bounds.xy - uTroikaEdgeOffset - uTroikaBlurRadius,
|
|
bounds.zw + uTroikaEdgeOffset + uTroikaBlurRadius
|
|
);
|
|
vec4 clippedBounds = vec4(
|
|
clamp(outlineBounds.xy, uTroikaClipRect.xy, uTroikaClipRect.zw),
|
|
clamp(outlineBounds.zw, uTroikaClipRect.xy, uTroikaClipRect.zw)
|
|
);
|
|
|
|
vec2 clippedXY = (mix(clippedBounds.xy, clippedBounds.zw, position.xy) - bounds.xy) / (bounds.zw - bounds.xy);
|
|
|
|
position.xy = mix(bounds.xy, bounds.zw, clippedXY);
|
|
|
|
uv = (position.xy - uTroikaTotalBounds.xy) / (uTroikaTotalBounds.zw - uTroikaTotalBounds.xy);
|
|
|
|
float rad = uTroikaCurveRadius;
|
|
if (rad != 0.0) {
|
|
float angle = position.x / rad;
|
|
position.xz = vec2(sin(angle) * rad, rad - cos(angle) * rad);
|
|
normal.xz = vec2(sin(angle), cos(angle));
|
|
}
|
|
|
|
position = uTroikaOrient * position;
|
|
normal = uTroikaOrient * normal;
|
|
|
|
vTroikaGlyphUV = clippedXY.xy;
|
|
vTroikaGlyphDimensions = vec2(bounds[2] - bounds[0], bounds[3] - bounds[1]);
|
|
|
|
${""}
|
|
float txCols = uTroikaSDFTextureSize.x / uTroikaSDFGlyphSize;
|
|
vec2 txUvPerSquare = uTroikaSDFGlyphSize / uTroikaSDFTextureSize;
|
|
vec2 txStartUV = txUvPerSquare * vec2(
|
|
mod(floor(aTroikaGlyphIndex / 4.0), txCols),
|
|
floor(floor(aTroikaGlyphIndex / 4.0) / txCols)
|
|
);
|
|
vTroikaTextureUVBounds = vec4(txStartUV, vec2(txStartUV) + txUvPerSquare);
|
|
vTroikaTextureChannel = mod(aTroikaGlyphIndex, 4.0);
|
|
`;
|
|
var FRAGMENT_DEFS = `
|
|
uniform sampler2D uTroikaSDFTexture;
|
|
uniform vec2 uTroikaSDFTextureSize;
|
|
uniform float uTroikaSDFGlyphSize;
|
|
uniform float uTroikaSDFExponent;
|
|
uniform float uTroikaEdgeOffset;
|
|
uniform float uTroikaFillOpacity;
|
|
uniform float uTroikaBlurRadius;
|
|
uniform vec3 uTroikaStrokeColor;
|
|
uniform float uTroikaStrokeWidth;
|
|
uniform float uTroikaStrokeOpacity;
|
|
uniform bool uTroikaSDFDebug;
|
|
varying vec2 vTroikaGlyphUV;
|
|
varying vec4 vTroikaTextureUVBounds;
|
|
varying float vTroikaTextureChannel;
|
|
varying vec2 vTroikaGlyphDimensions;
|
|
|
|
float troikaSdfValueToSignedDistance(float alpha) {
|
|
// Inverse of exponential encoding in webgl-sdf-generator
|
|
${""}
|
|
float maxDimension = max(vTroikaGlyphDimensions.x, vTroikaGlyphDimensions.y);
|
|
float absDist = (1.0 - pow(2.0 * (alpha > 0.5 ? 1.0 - alpha : alpha), 1.0 / uTroikaSDFExponent)) * maxDimension;
|
|
float signedDist = absDist * (alpha > 0.5 ? -1.0 : 1.0);
|
|
return signedDist;
|
|
}
|
|
|
|
float troikaGlyphUvToSdfValue(vec2 glyphUV) {
|
|
vec2 textureUV = mix(vTroikaTextureUVBounds.xy, vTroikaTextureUVBounds.zw, glyphUV);
|
|
vec4 rgba = texture2D(uTroikaSDFTexture, textureUV);
|
|
float ch = floor(vTroikaTextureChannel + 0.5); //NOTE: can't use round() in WebGL1
|
|
return ch == 0.0 ? rgba.r : ch == 1.0 ? rgba.g : ch == 2.0 ? rgba.b : rgba.a;
|
|
}
|
|
|
|
float troikaGlyphUvToDistance(vec2 uv) {
|
|
return troikaSdfValueToSignedDistance(troikaGlyphUvToSdfValue(uv));
|
|
}
|
|
|
|
float troikaGetAADist() {
|
|
${""}
|
|
#if defined(GL_OES_standard_derivatives) || __VERSION__ >= 300
|
|
return length(fwidth(vTroikaGlyphUV * vTroikaGlyphDimensions)) * 0.5;
|
|
#else
|
|
return vTroikaGlyphDimensions.x / 64.0;
|
|
#endif
|
|
}
|
|
|
|
float troikaGetFragDistValue() {
|
|
vec2 clampedGlyphUV = clamp(vTroikaGlyphUV, 0.5 / uTroikaSDFGlyphSize, 1.0 - 0.5 / uTroikaSDFGlyphSize);
|
|
float distance = troikaGlyphUvToDistance(clampedGlyphUV);
|
|
|
|
// Extrapolate distance when outside bounds:
|
|
distance += clampedGlyphUV == vTroikaGlyphUV ? 0.0 :
|
|
length((vTroikaGlyphUV - clampedGlyphUV) * vTroikaGlyphDimensions);
|
|
|
|
${""}
|
|
|
|
return distance;
|
|
}
|
|
|
|
float troikaGetEdgeAlpha(float distance, float distanceOffset, float aaDist) {
|
|
#if defined(IS_DEPTH_MATERIAL) || defined(IS_DISTANCE_MATERIAL)
|
|
float alpha = step(-distanceOffset, -distance);
|
|
#else
|
|
|
|
float alpha = smoothstep(
|
|
distanceOffset + aaDist,
|
|
distanceOffset - aaDist,
|
|
distance
|
|
);
|
|
#endif
|
|
|
|
return alpha;
|
|
}
|
|
`;
|
|
var FRAGMENT_TRANSFORM = `
|
|
float aaDist = troikaGetAADist();
|
|
float fragDistance = troikaGetFragDistValue();
|
|
float edgeAlpha = uTroikaSDFDebug ?
|
|
troikaGlyphUvToSdfValue(vTroikaGlyphUV) :
|
|
troikaGetEdgeAlpha(fragDistance, uTroikaEdgeOffset, max(aaDist, uTroikaBlurRadius));
|
|
|
|
#if !defined(IS_DEPTH_MATERIAL) && !defined(IS_DISTANCE_MATERIAL)
|
|
vec4 fillRGBA = gl_FragColor;
|
|
fillRGBA.a *= uTroikaFillOpacity;
|
|
vec4 strokeRGBA = uTroikaStrokeWidth == 0.0 ? fillRGBA : vec4(uTroikaStrokeColor, uTroikaStrokeOpacity);
|
|
if (fillRGBA.a == 0.0) fillRGBA.rgb = strokeRGBA.rgb;
|
|
gl_FragColor = mix(fillRGBA, strokeRGBA, smoothstep(
|
|
-uTroikaStrokeWidth - aaDist,
|
|
-uTroikaStrokeWidth + aaDist,
|
|
fragDistance
|
|
));
|
|
gl_FragColor.a *= edgeAlpha;
|
|
#endif
|
|
|
|
if (edgeAlpha == 0.0) {
|
|
discard;
|
|
}
|
|
`;
|
|
function createTextDerivedMaterial(baseMaterial) {
|
|
const textMaterial = createDerivedMaterial(baseMaterial, {
|
|
chained: true,
|
|
extensions: {
|
|
derivatives: true
|
|
},
|
|
uniforms: {
|
|
uTroikaSDFTexture: { value: null },
|
|
uTroikaSDFTextureSize: { value: new Vector2() },
|
|
uTroikaSDFGlyphSize: { value: 0 },
|
|
uTroikaSDFExponent: { value: 0 },
|
|
uTroikaTotalBounds: { value: new Vector4(0, 0, 0, 0) },
|
|
uTroikaClipRect: { value: new Vector4(0, 0, 0, 0) },
|
|
uTroikaEdgeOffset: { value: 0 },
|
|
uTroikaFillOpacity: { value: 1 },
|
|
uTroikaPositionOffset: { value: new Vector2() },
|
|
uTroikaCurveRadius: { value: 0 },
|
|
uTroikaBlurRadius: { value: 0 },
|
|
uTroikaStrokeWidth: { value: 0 },
|
|
uTroikaStrokeColor: { value: new Color() },
|
|
uTroikaStrokeOpacity: { value: 1 },
|
|
uTroikaOrient: { value: new Matrix3() },
|
|
uTroikaUseGlyphColors: { value: true },
|
|
uTroikaSDFDebug: { value: false }
|
|
},
|
|
vertexDefs: VERTEX_DEFS,
|
|
vertexTransform: VERTEX_TRANSFORM,
|
|
fragmentDefs: FRAGMENT_DEFS,
|
|
fragmentColorTransform: FRAGMENT_TRANSFORM,
|
|
customRewriter({ vertexShader, fragmentShader }) {
|
|
let uDiffuseRE = /\buniform\s+vec3\s+diffuse\b/;
|
|
if (uDiffuseRE.test(fragmentShader)) {
|
|
fragmentShader = fragmentShader.replace(uDiffuseRE, "varying vec3 vTroikaGlyphColor").replace(/\bdiffuse\b/g, "vTroikaGlyphColor");
|
|
if (!uDiffuseRE.test(vertexShader)) {
|
|
vertexShader = vertexShader.replace(
|
|
voidMainRegExp,
|
|
"uniform vec3 diffuse;\n$&\nvTroikaGlyphColor = uTroikaUseGlyphColors ? aTroikaGlyphColor / 255.0 : diffuse;\n"
|
|
);
|
|
}
|
|
}
|
|
return { vertexShader, fragmentShader };
|
|
}
|
|
});
|
|
textMaterial.transparent = true;
|
|
textMaterial.forceSinglePass = true;
|
|
Object.defineProperties(textMaterial, {
|
|
isTroikaTextMaterial: { value: true },
|
|
// WebGLShadowMap reverses the side of the shadow material by default, which fails
|
|
// for planes, so here we force the `shadowSide` to always match the main side.
|
|
shadowSide: {
|
|
get() {
|
|
return this.side;
|
|
},
|
|
set() {
|
|
}
|
|
}
|
|
});
|
|
return textMaterial;
|
|
}
|
|
var defaultMaterial = new MeshBasicMaterial({
|
|
color: 16777215,
|
|
side: DoubleSide,
|
|
transparent: true
|
|
});
|
|
var defaultStrokeColor = 8421504;
|
|
var tempMat4 = new Matrix4();
|
|
var tempVec3a = new Vector3();
|
|
var tempVec3b = new Vector3();
|
|
var tempArray = [];
|
|
var origin = new Vector3();
|
|
var defaultOrient = "+x+y";
|
|
function first(o) {
|
|
return Array.isArray(o) ? o[0] : o;
|
|
}
|
|
var getFlatRaycastMesh = () => {
|
|
const mesh = new Mesh(
|
|
new PlaneGeometry(1, 1),
|
|
defaultMaterial
|
|
);
|
|
getFlatRaycastMesh = () => mesh;
|
|
return mesh;
|
|
};
|
|
var getCurvedRaycastMesh = () => {
|
|
const mesh = new Mesh(
|
|
new PlaneGeometry(1, 1, 32, 1),
|
|
defaultMaterial
|
|
);
|
|
getCurvedRaycastMesh = () => mesh;
|
|
return mesh;
|
|
};
|
|
var syncStartEvent = { type: "syncstart" };
|
|
var syncCompleteEvent = { type: "synccomplete" };
|
|
var SYNCABLE_PROPS = [
|
|
"font",
|
|
"fontSize",
|
|
"fontStyle",
|
|
"fontWeight",
|
|
"lang",
|
|
"letterSpacing",
|
|
"lineHeight",
|
|
"maxWidth",
|
|
"overflowWrap",
|
|
"text",
|
|
"direction",
|
|
"textAlign",
|
|
"textIndent",
|
|
"whiteSpace",
|
|
"anchorX",
|
|
"anchorY",
|
|
"colorRanges",
|
|
"sdfGlyphSize"
|
|
];
|
|
var COPYABLE_PROPS = SYNCABLE_PROPS.concat(
|
|
"material",
|
|
"color",
|
|
"depthOffset",
|
|
"clipRect",
|
|
"curveRadius",
|
|
"orientation",
|
|
"glyphGeometryDetail"
|
|
);
|
|
var Text = class extends Mesh {
|
|
constructor() {
|
|
const geometry = new GlyphsGeometry();
|
|
super(geometry, null);
|
|
this.text = "";
|
|
this.anchorX = 0;
|
|
this.anchorY = 0;
|
|
this.curveRadius = 0;
|
|
this.direction = "auto";
|
|
this.font = null;
|
|
this.unicodeFontsURL = null;
|
|
this.fontSize = 0.1;
|
|
this.fontWeight = "normal";
|
|
this.fontStyle = "normal";
|
|
this.lang = null;
|
|
this.letterSpacing = 0;
|
|
this.lineHeight = "normal";
|
|
this.maxWidth = Infinity;
|
|
this.overflowWrap = "normal";
|
|
this.textAlign = "left";
|
|
this.textIndent = 0;
|
|
this.whiteSpace = "normal";
|
|
this.material = null;
|
|
this.color = null;
|
|
this.colorRanges = null;
|
|
this.outlineWidth = 0;
|
|
this.outlineColor = 0;
|
|
this.outlineOpacity = 1;
|
|
this.outlineBlur = 0;
|
|
this.outlineOffsetX = 0;
|
|
this.outlineOffsetY = 0;
|
|
this.strokeWidth = 0;
|
|
this.strokeColor = defaultStrokeColor;
|
|
this.strokeOpacity = 1;
|
|
this.fillOpacity = 1;
|
|
this.depthOffset = 0;
|
|
this.clipRect = null;
|
|
this.orientation = defaultOrient;
|
|
this.glyphGeometryDetail = 1;
|
|
this.sdfGlyphSize = null;
|
|
this.gpuAccelerateSDF = true;
|
|
this.debugSDF = false;
|
|
}
|
|
/**
|
|
* Updates the text rendering according to the current text-related configuration properties.
|
|
* This is an async process, so you can pass in a callback function to be executed when it
|
|
* finishes.
|
|
* @param {function} [callback]
|
|
*/
|
|
sync(callback) {
|
|
if (this._needsSync) {
|
|
this._needsSync = false;
|
|
if (this._isSyncing) {
|
|
(this._queuedSyncs || (this._queuedSyncs = [])).push(callback);
|
|
} else {
|
|
this._isSyncing = true;
|
|
this.dispatchEvent(syncStartEvent);
|
|
getTextRenderInfo({
|
|
text: this.text,
|
|
font: this.font,
|
|
lang: this.lang,
|
|
fontSize: this.fontSize || 0.1,
|
|
fontWeight: this.fontWeight || "normal",
|
|
fontStyle: this.fontStyle || "normal",
|
|
letterSpacing: this.letterSpacing || 0,
|
|
lineHeight: this.lineHeight || "normal",
|
|
maxWidth: this.maxWidth,
|
|
direction: this.direction || "auto",
|
|
textAlign: this.textAlign,
|
|
textIndent: this.textIndent,
|
|
whiteSpace: this.whiteSpace,
|
|
overflowWrap: this.overflowWrap,
|
|
anchorX: this.anchorX,
|
|
anchorY: this.anchorY,
|
|
colorRanges: this.colorRanges,
|
|
includeCaretPositions: true,
|
|
//TODO parameterize
|
|
sdfGlyphSize: this.sdfGlyphSize,
|
|
gpuAccelerateSDF: this.gpuAccelerateSDF,
|
|
unicodeFontsURL: this.unicodeFontsURL
|
|
}, (textRenderInfo) => {
|
|
this._isSyncing = false;
|
|
this._textRenderInfo = textRenderInfo;
|
|
this.geometry.updateGlyphs(
|
|
textRenderInfo.glyphBounds,
|
|
textRenderInfo.glyphAtlasIndices,
|
|
textRenderInfo.blockBounds,
|
|
textRenderInfo.chunkedBounds,
|
|
textRenderInfo.glyphColors
|
|
);
|
|
const queued = this._queuedSyncs;
|
|
if (queued) {
|
|
this._queuedSyncs = null;
|
|
this._needsSync = true;
|
|
this.sync(() => {
|
|
queued.forEach((fn) => fn && fn());
|
|
});
|
|
}
|
|
this.dispatchEvent(syncCompleteEvent);
|
|
if (callback) {
|
|
callback();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Initiate a sync if needed - note it won't complete until next frame at the
|
|
* earliest so if possible it's a good idea to call sync() manually as soon as
|
|
* all the properties have been set.
|
|
* @override
|
|
*/
|
|
onBeforeRender(renderer, scene, camera, geometry, material, group) {
|
|
this.sync();
|
|
if (material.isTroikaTextMaterial) {
|
|
this._prepareForRender(material);
|
|
}
|
|
}
|
|
/**
|
|
* Shortcut to dispose the geometry specific to this instance.
|
|
* Note: we don't also dispose the derived material here because if anything else is
|
|
* sharing the same base material it will result in a pause next frame as the program
|
|
* is recompiled. Instead users can dispose the base material manually, like normal,
|
|
* and we'll also dispose the derived material at that time.
|
|
*/
|
|
dispose() {
|
|
this.geometry.dispose();
|
|
}
|
|
/**
|
|
* @property {TroikaTextRenderInfo|null} textRenderInfo
|
|
* @readonly
|
|
* The current processed rendering data for this TextMesh, returned by the TextBuilder after
|
|
* a `sync()` call. This will be `null` initially, and may be stale for a short period until
|
|
* the asynchrous `sync()` process completes.
|
|
*/
|
|
get textRenderInfo() {
|
|
return this._textRenderInfo || null;
|
|
}
|
|
/**
|
|
* Create the text derived material from the base material. Can be overridden to use a custom
|
|
* derived material.
|
|
*/
|
|
createDerivedMaterial(baseMaterial) {
|
|
return createTextDerivedMaterial(baseMaterial);
|
|
}
|
|
// Handler for automatically wrapping the base material with our upgrades. We do the wrapping
|
|
// lazily on _read_ rather than write to avoid unnecessary wrapping on transient values.
|
|
get material() {
|
|
let derivedMaterial = this._derivedMaterial;
|
|
const baseMaterial = this._baseMaterial || this._defaultMaterial || (this._defaultMaterial = defaultMaterial.clone());
|
|
if (!derivedMaterial || !derivedMaterial.isDerivedFrom(baseMaterial)) {
|
|
derivedMaterial = this._derivedMaterial = this.createDerivedMaterial(baseMaterial);
|
|
baseMaterial.addEventListener("dispose", function onDispose() {
|
|
baseMaterial.removeEventListener("dispose", onDispose);
|
|
derivedMaterial.dispose();
|
|
});
|
|
}
|
|
if (this.hasOutline()) {
|
|
let outlineMaterial = derivedMaterial._outlineMtl;
|
|
if (!outlineMaterial) {
|
|
outlineMaterial = derivedMaterial._outlineMtl = Object.create(derivedMaterial, {
|
|
id: { value: derivedMaterial.id + 0.1 }
|
|
});
|
|
outlineMaterial.isTextOutlineMaterial = true;
|
|
outlineMaterial.depthWrite = false;
|
|
outlineMaterial.map = null;
|
|
derivedMaterial.addEventListener("dispose", function onDispose() {
|
|
derivedMaterial.removeEventListener("dispose", onDispose);
|
|
outlineMaterial.dispose();
|
|
});
|
|
}
|
|
return [
|
|
outlineMaterial,
|
|
derivedMaterial
|
|
];
|
|
} else {
|
|
return derivedMaterial;
|
|
}
|
|
}
|
|
set material(baseMaterial) {
|
|
if (baseMaterial && baseMaterial.isTroikaTextMaterial) {
|
|
this._derivedMaterial = baseMaterial;
|
|
this._baseMaterial = baseMaterial.baseMaterial;
|
|
} else {
|
|
this._baseMaterial = baseMaterial;
|
|
}
|
|
}
|
|
hasOutline() {
|
|
return !!(this.outlineWidth || this.outlineBlur || this.outlineOffsetX || this.outlineOffsetY);
|
|
}
|
|
get glyphGeometryDetail() {
|
|
return this.geometry.detail;
|
|
}
|
|
set glyphGeometryDetail(detail) {
|
|
this.geometry.detail = detail;
|
|
}
|
|
get curveRadius() {
|
|
return this.geometry.curveRadius;
|
|
}
|
|
set curveRadius(r) {
|
|
this.geometry.curveRadius = r;
|
|
}
|
|
// Create and update material for shadows upon request:
|
|
get customDepthMaterial() {
|
|
return first(this.material).getDepthMaterial();
|
|
}
|
|
set customDepthMaterial(m) {
|
|
}
|
|
get customDistanceMaterial() {
|
|
return first(this.material).getDistanceMaterial();
|
|
}
|
|
set customDistanceMaterial(m) {
|
|
}
|
|
_prepareForRender(material) {
|
|
const isOutline = material.isTextOutlineMaterial;
|
|
const uniforms = material.uniforms;
|
|
const textInfo = this.textRenderInfo;
|
|
if (textInfo) {
|
|
const { sdfTexture, blockBounds } = textInfo;
|
|
uniforms.uTroikaSDFTexture.value = sdfTexture;
|
|
uniforms.uTroikaSDFTextureSize.value.set(sdfTexture.image.width, sdfTexture.image.height);
|
|
uniforms.uTroikaSDFGlyphSize.value = textInfo.sdfGlyphSize;
|
|
uniforms.uTroikaSDFExponent.value = textInfo.sdfExponent;
|
|
uniforms.uTroikaTotalBounds.value.fromArray(blockBounds);
|
|
uniforms.uTroikaUseGlyphColors.value = !isOutline && !!textInfo.glyphColors;
|
|
let distanceOffset = 0;
|
|
let blurRadius = 0;
|
|
let strokeWidth = 0;
|
|
let fillOpacity;
|
|
let strokeOpacity;
|
|
let strokeColor;
|
|
let offsetX = 0;
|
|
let offsetY = 0;
|
|
if (isOutline) {
|
|
let { outlineWidth, outlineOffsetX, outlineOffsetY, outlineBlur, outlineOpacity } = this;
|
|
distanceOffset = this._parsePercent(outlineWidth) || 0;
|
|
blurRadius = Math.max(0, this._parsePercent(outlineBlur) || 0);
|
|
fillOpacity = outlineOpacity;
|
|
offsetX = this._parsePercent(outlineOffsetX) || 0;
|
|
offsetY = this._parsePercent(outlineOffsetY) || 0;
|
|
} else {
|
|
strokeWidth = Math.max(0, this._parsePercent(this.strokeWidth) || 0);
|
|
if (strokeWidth) {
|
|
strokeColor = this.strokeColor;
|
|
uniforms.uTroikaStrokeColor.value.set(strokeColor == null ? defaultStrokeColor : strokeColor);
|
|
strokeOpacity = this.strokeOpacity;
|
|
if (strokeOpacity == null) strokeOpacity = 1;
|
|
}
|
|
fillOpacity = this.fillOpacity;
|
|
}
|
|
uniforms.uTroikaEdgeOffset.value = distanceOffset;
|
|
uniforms.uTroikaPositionOffset.value.set(offsetX, offsetY);
|
|
uniforms.uTroikaBlurRadius.value = blurRadius;
|
|
uniforms.uTroikaStrokeWidth.value = strokeWidth;
|
|
uniforms.uTroikaStrokeOpacity.value = strokeOpacity;
|
|
uniforms.uTroikaFillOpacity.value = fillOpacity == null ? 1 : fillOpacity;
|
|
uniforms.uTroikaCurveRadius.value = this.curveRadius || 0;
|
|
let clipRect = this.clipRect;
|
|
if (clipRect && Array.isArray(clipRect) && clipRect.length === 4) {
|
|
uniforms.uTroikaClipRect.value.fromArray(clipRect);
|
|
} else {
|
|
const pad = (this.fontSize || 0.1) * 100;
|
|
uniforms.uTroikaClipRect.value.set(
|
|
blockBounds[0] - pad,
|
|
blockBounds[1] - pad,
|
|
blockBounds[2] + pad,
|
|
blockBounds[3] + pad
|
|
);
|
|
}
|
|
this.geometry.applyClipRect(uniforms.uTroikaClipRect.value);
|
|
}
|
|
uniforms.uTroikaSDFDebug.value = !!this.debugSDF;
|
|
material.polygonOffset = !!this.depthOffset;
|
|
material.polygonOffsetFactor = material.polygonOffsetUnits = this.depthOffset || 0;
|
|
const color = isOutline ? this.outlineColor || 0 : this.color;
|
|
if (color == null) {
|
|
delete material.color;
|
|
} else {
|
|
const colorObj = material.hasOwnProperty("color") ? material.color : material.color = new Color();
|
|
if (color !== colorObj._input || typeof color === "object") {
|
|
colorObj.set(colorObj._input = color);
|
|
}
|
|
}
|
|
let orient = this.orientation || defaultOrient;
|
|
if (orient !== material._orientation) {
|
|
let rotMat = uniforms.uTroikaOrient.value;
|
|
orient = orient.replace(/[^-+xyz]/g, "");
|
|
let match = orient !== defaultOrient && orient.match(/^([-+])([xyz])([-+])([xyz])$/);
|
|
if (match) {
|
|
let [, hSign, hAxis, vSign, vAxis] = match;
|
|
tempVec3a.set(0, 0, 0)[hAxis] = hSign === "-" ? 1 : -1;
|
|
tempVec3b.set(0, 0, 0)[vAxis] = vSign === "-" ? -1 : 1;
|
|
tempMat4.lookAt(origin, tempVec3a.cross(tempVec3b), tempVec3b);
|
|
rotMat.setFromMatrix4(tempMat4);
|
|
} else {
|
|
rotMat.identity();
|
|
}
|
|
material._orientation = orient;
|
|
}
|
|
}
|
|
_parsePercent(value) {
|
|
if (typeof value === "string") {
|
|
let match = value.match(/^(-?[\d.]+)%$/);
|
|
let pct = match ? parseFloat(match[1]) : NaN;
|
|
value = (isNaN(pct) ? 0 : pct / 100) * this.fontSize;
|
|
}
|
|
return value;
|
|
}
|
|
/**
|
|
* Translate a point in local space to an x/y in the text plane.
|
|
*/
|
|
localPositionToTextCoords(position, target = new Vector2()) {
|
|
target.copy(position);
|
|
const r = this.curveRadius;
|
|
if (r) {
|
|
target.x = Math.atan2(position.x, Math.abs(r) - Math.abs(position.z)) * Math.abs(r);
|
|
}
|
|
return target;
|
|
}
|
|
/**
|
|
* Translate a point in world space to an x/y in the text plane.
|
|
*/
|
|
worldPositionToTextCoords(position, target = new Vector2()) {
|
|
tempVec3a.copy(position);
|
|
return this.localPositionToTextCoords(this.worldToLocal(tempVec3a), target);
|
|
}
|
|
/**
|
|
* @override Custom raycasting to test against the whole text block's max rectangular bounds
|
|
* TODO is there any reason to make this more granular, like within individual line or glyph rects?
|
|
*/
|
|
raycast(raycaster, intersects) {
|
|
const { textRenderInfo, curveRadius } = this;
|
|
if (textRenderInfo) {
|
|
const bounds = textRenderInfo.blockBounds;
|
|
const raycastMesh = curveRadius ? getCurvedRaycastMesh() : getFlatRaycastMesh();
|
|
const geom = raycastMesh.geometry;
|
|
const { position, uv } = geom.attributes;
|
|
for (let i = 0; i < uv.count; i++) {
|
|
let x = bounds[0] + uv.getX(i) * (bounds[2] - bounds[0]);
|
|
const y = bounds[1] + uv.getY(i) * (bounds[3] - bounds[1]);
|
|
let z = 0;
|
|
if (curveRadius) {
|
|
z = curveRadius - Math.cos(x / curveRadius) * curveRadius;
|
|
x = Math.sin(x / curveRadius) * curveRadius;
|
|
}
|
|
position.setXYZ(i, x, y, z);
|
|
}
|
|
geom.boundingSphere = this.geometry.boundingSphere;
|
|
geom.boundingBox = this.geometry.boundingBox;
|
|
raycastMesh.matrixWorld = this.matrixWorld;
|
|
raycastMesh.material.side = this.material.side;
|
|
tempArray.length = 0;
|
|
raycastMesh.raycast(raycaster, tempArray);
|
|
for (let i = 0; i < tempArray.length; i++) {
|
|
tempArray[i].object = this;
|
|
intersects.push(tempArray[i]);
|
|
}
|
|
}
|
|
}
|
|
copy(source) {
|
|
const geom = this.geometry;
|
|
super.copy(source);
|
|
this.geometry = geom;
|
|
COPYABLE_PROPS.forEach((prop) => {
|
|
this[prop] = source[prop];
|
|
});
|
|
return this;
|
|
}
|
|
clone() {
|
|
return new this.constructor().copy(this);
|
|
}
|
|
};
|
|
SYNCABLE_PROPS.forEach((prop) => {
|
|
const privateKey = "_private_" + prop;
|
|
Object.defineProperty(Text.prototype, prop, {
|
|
get() {
|
|
return this[privateKey];
|
|
},
|
|
set(value) {
|
|
if (value !== this[privateKey]) {
|
|
this[privateKey] = value;
|
|
this._needsSync = true;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
var syncStartEvent$1 = { type: "syncstart" };
|
|
var syncCompleteEvent$1 = { type: "synccomplete" };
|
|
var memberIndexAttrName = "aTroikaTextBatchMemberIndex";
|
|
var floatsPerMember = 32;
|
|
var tempBox3 = new Box3();
|
|
var tempColor$1 = new Color();
|
|
var BatchedText = class _BatchedText extends Text {
|
|
constructor() {
|
|
super();
|
|
this._members = /* @__PURE__ */ new Map();
|
|
this._dataTextures = {};
|
|
this._onMemberSynced = (e) => {
|
|
this._members.get(e.target).dirty = true;
|
|
};
|
|
}
|
|
/**
|
|
* @override
|
|
* Batch any Text objects added as children
|
|
*/
|
|
add(...objects) {
|
|
for (let i = 0; i < objects.length; i++) {
|
|
if (objects[i] instanceof Text) {
|
|
this.addText(objects[i]);
|
|
} else {
|
|
super.add(objects[i]);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
/**
|
|
* @override
|
|
*/
|
|
remove(...objects) {
|
|
for (let i = 0; i < objects.length; i++) {
|
|
if (objects[i] instanceof Text) {
|
|
this.removeText(objects[i]);
|
|
} else {
|
|
super.remove(objects[i]);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
/**
|
|
* @param {Text} text
|
|
*/
|
|
addText(text) {
|
|
if (!this._members.has(text)) {
|
|
this._members.set(text, {
|
|
index: -1,
|
|
glyphCount: -1,
|
|
dirty: true
|
|
});
|
|
text.addEventListener("synccomplete", this._onMemberSynced);
|
|
}
|
|
}
|
|
/**
|
|
* @param {Text} text
|
|
*/
|
|
removeText(text) {
|
|
this._needsRepack = true;
|
|
text.removeEventListener("synccomplete", this._onMemberSynced);
|
|
this._members.delete(text);
|
|
}
|
|
/**
|
|
* Use the custom derivation with extra batching logic
|
|
*/
|
|
createDerivedMaterial(baseMaterial) {
|
|
return createBatchedTextMaterial(baseMaterial);
|
|
}
|
|
updateMatrixWorld(force) {
|
|
super.updateMatrixWorld(force);
|
|
this.updateBounds();
|
|
}
|
|
/**
|
|
* Update the batched geometry bounds to hold all members
|
|
*/
|
|
updateBounds() {
|
|
const bbox = this.geometry.boundingBox.makeEmpty();
|
|
this._members.forEach((_, text) => {
|
|
if (text.matrixAutoUpdate) text.updateMatrix();
|
|
tempBox3.copy(text.geometry.boundingBox).applyMatrix4(text.matrix);
|
|
bbox.union(tempBox3);
|
|
});
|
|
bbox.getBoundingSphere(this.geometry.boundingSphere);
|
|
}
|
|
/** @override */
|
|
hasOutline() {
|
|
for (let member of this._members.keys()) {
|
|
if (member.hasOutline()) return true;
|
|
}
|
|
return false;
|
|
}
|
|
/**
|
|
* @override
|
|
* Copy member matrices and uniform values into the data texture
|
|
*/
|
|
_prepareForRender(material) {
|
|
const isOutline = material.isTextOutlineMaterial;
|
|
material.uniforms.uTroikaIsOutline.value = isOutline;
|
|
let texture = this._dataTextures[isOutline ? "outline" : "main"];
|
|
const dataLength = Math.pow(2, Math.ceil(Math.log2(this._members.size * floatsPerMember)));
|
|
if (!texture || dataLength !== texture.image.data.length) {
|
|
if (texture) texture.dispose();
|
|
const width = Math.min(dataLength / 4, 1024);
|
|
texture = this._dataTextures[isOutline ? "outline" : "main"] = new DataTexture(
|
|
new Float32Array(dataLength),
|
|
width,
|
|
dataLength / 4 / width,
|
|
RGBAFormat,
|
|
FloatType
|
|
);
|
|
}
|
|
const texData = texture.image.data;
|
|
const setTexData = (index, value) => {
|
|
if (value !== texData[index]) {
|
|
texData[index] = value;
|
|
texture.needsUpdate = true;
|
|
}
|
|
};
|
|
this._members.forEach(({ index, dirty }, text) => {
|
|
if (index > -1) {
|
|
const startIndex = index * floatsPerMember;
|
|
const matrix = text.matrix.elements;
|
|
for (let i = 0; i < 16; i++) {
|
|
setTexData(startIndex + i, matrix[i]);
|
|
}
|
|
text._prepareForRender(material);
|
|
const {
|
|
uTroikaTotalBounds,
|
|
uTroikaClipRect,
|
|
uTroikaPositionOffset,
|
|
uTroikaEdgeOffset,
|
|
uTroikaBlurRadius,
|
|
uTroikaStrokeWidth,
|
|
uTroikaStrokeColor,
|
|
uTroikaStrokeOpacity,
|
|
uTroikaFillOpacity,
|
|
uTroikaCurveRadius
|
|
} = material.uniforms;
|
|
for (let i = 0; i < 4; i++) {
|
|
setTexData(startIndex + 16 + i, uTroikaTotalBounds.value.getComponent(i));
|
|
}
|
|
for (let i = 0; i < 4; i++) {
|
|
setTexData(startIndex + 20 + i, uTroikaClipRect.value.getComponent(i));
|
|
}
|
|
let color = isOutline ? text.outlineColor || 0 : text.color;
|
|
if (color == null) color = this.color;
|
|
if (color == null) color = this.material.color;
|
|
if (color == null) color = 16777215;
|
|
setTexData(startIndex + 24, tempColor$1.set(color).getHex());
|
|
setTexData(startIndex + 25, uTroikaFillOpacity.value);
|
|
setTexData(startIndex + 26, uTroikaCurveRadius.value);
|
|
if (isOutline) {
|
|
setTexData(startIndex + 28, uTroikaPositionOffset.value.x);
|
|
setTexData(startIndex + 29, uTroikaPositionOffset.value.y);
|
|
setTexData(startIndex + 30, uTroikaEdgeOffset.value);
|
|
setTexData(startIndex + 31, uTroikaBlurRadius.value);
|
|
} else {
|
|
setTexData(startIndex + 28, uTroikaStrokeWidth.value);
|
|
setTexData(startIndex + 29, tempColor$1.set(uTroikaStrokeColor.value).getHex());
|
|
setTexData(startIndex + 30, uTroikaStrokeOpacity.value);
|
|
}
|
|
}
|
|
});
|
|
material.setMatrixTexture(texture);
|
|
super._prepareForRender(material);
|
|
}
|
|
sync(callback) {
|
|
let syncPromises = this._needsRepack ? [] : null;
|
|
this._needsRepack = false;
|
|
this._members.forEach((packingInfo, text) => {
|
|
if (packingInfo.dirty || text._needsSync) {
|
|
packingInfo.dirty = false;
|
|
(syncPromises || (syncPromises = [])).push(new Promise((resolve) => {
|
|
if (text._needsSync) {
|
|
text.sync(resolve);
|
|
} else {
|
|
resolve();
|
|
}
|
|
}));
|
|
}
|
|
});
|
|
if (syncPromises) {
|
|
this.dispatchEvent(syncStartEvent$1);
|
|
Promise.all(syncPromises).then(() => {
|
|
const { geometry } = this;
|
|
const batchedAttributes = geometry.attributes;
|
|
let memberIndexes = batchedAttributes[memberIndexAttrName] && batchedAttributes[memberIndexAttrName].array || new Uint16Array(0);
|
|
let batchedGlyphIndexes = batchedAttributes[glyphIndexAttrName] && batchedAttributes[glyphIndexAttrName].array || new Float32Array(0);
|
|
let batchedGlyphBounds = batchedAttributes[glyphBoundsAttrName] && batchedAttributes[glyphBoundsAttrName].array || new Float32Array(0);
|
|
let totalGlyphCount = 0;
|
|
this._members.forEach((packingInfo, { textRenderInfo }) => {
|
|
if (textRenderInfo) {
|
|
totalGlyphCount += textRenderInfo.glyphAtlasIndices.length;
|
|
this._textRenderInfo = textRenderInfo;
|
|
}
|
|
});
|
|
if (totalGlyphCount !== memberIndexes.length) {
|
|
memberIndexes = cloneAndResize(memberIndexes, totalGlyphCount);
|
|
batchedGlyphIndexes = cloneAndResize(batchedGlyphIndexes, totalGlyphCount);
|
|
batchedGlyphBounds = cloneAndResize(batchedGlyphBounds, totalGlyphCount * 4);
|
|
}
|
|
let memberIndex = 0;
|
|
let glyphIndex = 0;
|
|
this._members.forEach((packingInfo, { textRenderInfo }) => {
|
|
if (textRenderInfo) {
|
|
const glyphCount = textRenderInfo.glyphAtlasIndices.length;
|
|
memberIndexes.fill(memberIndex, glyphIndex, glyphIndex + glyphCount);
|
|
batchedGlyphIndexes.set(textRenderInfo.glyphAtlasIndices, glyphIndex, glyphIndex + glyphCount);
|
|
batchedGlyphBounds.set(textRenderInfo.glyphBounds, glyphIndex * 4, (glyphIndex + glyphCount) * 4);
|
|
glyphIndex += glyphCount;
|
|
packingInfo.index = memberIndex++;
|
|
}
|
|
});
|
|
geometry.updateAttributeData(memberIndexAttrName, memberIndexes, 1);
|
|
geometry.getAttribute(memberIndexAttrName).setUsage(DynamicDrawUsage);
|
|
geometry.updateAttributeData(glyphIndexAttrName, batchedGlyphIndexes, 1);
|
|
geometry.updateAttributeData(glyphBoundsAttrName, batchedGlyphBounds, 4);
|
|
this.updateBounds();
|
|
this.dispatchEvent(syncCompleteEvent$1);
|
|
if (callback) {
|
|
callback();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
copy(source) {
|
|
if (source instanceof _BatchedText) {
|
|
super.copy(source);
|
|
this._members.forEach((_, text) => this.removeText(text));
|
|
source._members.forEach((_, text) => this.addText(text));
|
|
}
|
|
return this;
|
|
}
|
|
dispose() {
|
|
super.dispose();
|
|
Object.values(this._dataTextures).forEach((tex) => tex.dispose());
|
|
}
|
|
};
|
|
function cloneAndResize(source, newLength) {
|
|
const copy = new source.constructor(newLength);
|
|
copy.set(source.subarray(0, newLength));
|
|
return copy;
|
|
}
|
|
function createBatchedTextMaterial(baseMaterial) {
|
|
const texUniformName = "uTroikaMatricesTexture";
|
|
const texSizeUniformName = "uTroikaMatricesTextureSize";
|
|
let batchMaterial = createDerivedMaterial(baseMaterial, {
|
|
chained: true,
|
|
uniforms: {
|
|
[texSizeUniformName]: { value: new Vector2() },
|
|
[texUniformName]: { value: null }
|
|
},
|
|
// language=GLSL
|
|
vertexDefs: `
|
|
uniform highp sampler2D ${texUniformName};
|
|
uniform vec2 ${texSizeUniformName};
|
|
attribute float ${memberIndexAttrName};
|
|
|
|
vec4 troikaBatchTexel(float offset) {
|
|
offset += ${memberIndexAttrName} * ${floatsPerMember.toFixed(1)} / 4.0;
|
|
float w = ${texSizeUniformName}.x;
|
|
vec2 uv = (vec2(mod(offset, w), floor(offset / w)) + 0.5) / ${texSizeUniformName};
|
|
return texture2D(${texUniformName}, uv);
|
|
}
|
|
`,
|
|
// language=GLSL prefix="void main() {" suffix="}"
|
|
vertexTransform: `
|
|
mat4 matrix = mat4(
|
|
troikaBatchTexel(0.0),
|
|
troikaBatchTexel(1.0),
|
|
troikaBatchTexel(2.0),
|
|
troikaBatchTexel(3.0)
|
|
);
|
|
position.xyz = (matrix * vec4(position, 1.0)).xyz;
|
|
`
|
|
});
|
|
batchMaterial = createTextDerivedMaterial(batchMaterial);
|
|
batchMaterial = createDerivedMaterial(batchMaterial, {
|
|
chained: true,
|
|
uniforms: {
|
|
uTroikaIsOutline: { value: false }
|
|
},
|
|
customRewriter(shaders) {
|
|
const varyingUniforms = [
|
|
"uTroikaTotalBounds",
|
|
"uTroikaClipRect",
|
|
"uTroikaPositionOffset",
|
|
"uTroikaEdgeOffset",
|
|
"uTroikaBlurRadius",
|
|
"uTroikaStrokeWidth",
|
|
"uTroikaStrokeColor",
|
|
"uTroikaStrokeOpacity",
|
|
"uTroikaFillOpacity",
|
|
"uTroikaCurveRadius",
|
|
"diffuse"
|
|
];
|
|
varyingUniforms.forEach((uniformName) => {
|
|
shaders = uniformToVarying(shaders, uniformName);
|
|
});
|
|
return shaders;
|
|
},
|
|
// language=GLSL
|
|
vertexDefs: `
|
|
uniform bool uTroikaIsOutline;
|
|
vec3 troikaFloatToColor(float v) {
|
|
return mod(floor(vec3(v / 65536.0, v / 256.0, v)), 256.0) / 256.0;
|
|
}
|
|
`,
|
|
// language=GLSL prefix="void main() {" suffix="}"
|
|
vertexTransform: `
|
|
uTroikaTotalBounds = troikaBatchTexel(4.0);
|
|
uTroikaClipRect = troikaBatchTexel(5.0);
|
|
|
|
vec4 data = troikaBatchTexel(6.0);
|
|
diffuse = troikaFloatToColor(data.x);
|
|
uTroikaFillOpacity = data.y;
|
|
uTroikaCurveRadius = data.z;
|
|
|
|
data = troikaBatchTexel(7.0);
|
|
if (uTroikaIsOutline) {
|
|
if (data == vec4(0.0)) { // degenerate if zero outline
|
|
position = vec3(0.0);
|
|
} else {
|
|
uTroikaPositionOffset = data.xy;
|
|
uTroikaEdgeOffset = data.z;
|
|
uTroikaBlurRadius = data.w;
|
|
}
|
|
} else {
|
|
uTroikaStrokeWidth = data.x;
|
|
uTroikaStrokeColor = troikaFloatToColor(data.y);
|
|
uTroikaStrokeOpacity = data.z;
|
|
}
|
|
`
|
|
});
|
|
batchMaterial.setMatrixTexture = (texture) => {
|
|
batchMaterial.uniforms[texUniformName].value = texture;
|
|
batchMaterial.uniforms[texSizeUniformName].value.set(texture.image.width, texture.image.height);
|
|
};
|
|
return batchMaterial;
|
|
}
|
|
function uniformToVarying({ vertexShader, fragmentShader }, uniformName, varyingName = uniformName) {
|
|
const uniformRE = new RegExp(`uniform\\s+(bool|float|vec[234]|mat[34])\\s+${uniformName}\\b`);
|
|
let type;
|
|
let hadFragmentUniform = false;
|
|
fragmentShader = fragmentShader.replace(uniformRE, ($0, $1) => {
|
|
hadFragmentUniform = true;
|
|
return `varying ${type = $1} ${varyingName}`;
|
|
});
|
|
let hadVertexUniform = false;
|
|
vertexShader = vertexShader.replace(uniformRE, (_, $1) => {
|
|
hadVertexUniform = true;
|
|
return `${hadFragmentUniform ? "varying" : ""} ${type = $1} ${varyingName}`;
|
|
});
|
|
if (!hadVertexUniform) {
|
|
vertexShader = `${hadFragmentUniform ? "varying" : ""} ${type} ${varyingName};
|
|
${vertexShader}`;
|
|
}
|
|
return { vertexShader, fragmentShader };
|
|
}
|
|
function getCaretAtPoint(textRenderInfo, x, y) {
|
|
let closestCaret = null;
|
|
const rows = groupCaretsByRow(textRenderInfo);
|
|
let closestRow = null;
|
|
rows.forEach((row) => {
|
|
if (!closestRow || Math.abs(y - (row.top + row.bottom) / 2) < Math.abs(y - (closestRow.top + closestRow.bottom) / 2)) {
|
|
closestRow = row;
|
|
}
|
|
});
|
|
closestRow.carets.forEach((caret) => {
|
|
if (!closestCaret || Math.abs(x - caret.x) < Math.abs(x - closestCaret.x)) {
|
|
closestCaret = caret;
|
|
}
|
|
});
|
|
return closestCaret;
|
|
}
|
|
var _rectsCache = /* @__PURE__ */ new WeakMap();
|
|
function getSelectionRects(textRenderInfo, start, end) {
|
|
let rects;
|
|
if (textRenderInfo) {
|
|
let prevResult = _rectsCache.get(textRenderInfo);
|
|
if (prevResult && prevResult.start === start && prevResult.end === end) {
|
|
return prevResult.rects;
|
|
}
|
|
const { caretPositions } = textRenderInfo;
|
|
if (end < start) {
|
|
const s = start;
|
|
start = end;
|
|
end = s;
|
|
}
|
|
start = Math.max(start, 0);
|
|
end = Math.min(end, caretPositions.length + 1);
|
|
rects = [];
|
|
let currentRect = null;
|
|
for (let i = start; i < end; i++) {
|
|
const x1 = caretPositions[i * 4];
|
|
const x2 = caretPositions[i * 4 + 1];
|
|
const left = Math.min(x1, x2);
|
|
const right = Math.max(x1, x2);
|
|
const bottom = caretPositions[i * 4 + 2];
|
|
const top = caretPositions[i * 4 + 3];
|
|
if (!currentRect || bottom !== currentRect.bottom || top !== currentRect.top || left > currentRect.right || right < currentRect.left) {
|
|
currentRect = {
|
|
left: Infinity,
|
|
right: -Infinity,
|
|
bottom,
|
|
top
|
|
};
|
|
rects.push(currentRect);
|
|
}
|
|
currentRect.left = Math.min(left, currentRect.left);
|
|
currentRect.right = Math.max(right, currentRect.right);
|
|
}
|
|
rects.sort((a, b) => b.bottom - a.bottom || a.left - b.left);
|
|
for (let i = rects.length - 1; i-- > 0; ) {
|
|
const rectA = rects[i];
|
|
const rectB = rects[i + 1];
|
|
if (rectA.bottom === rectB.bottom && rectA.top === rectB.top && rectA.left <= rectB.right && rectA.right >= rectB.left) {
|
|
rectB.left = Math.min(rectB.left, rectA.left);
|
|
rectB.right = Math.max(rectB.right, rectA.right);
|
|
rects.splice(i, 1);
|
|
}
|
|
}
|
|
_rectsCache.set(textRenderInfo, { start, end, rects });
|
|
}
|
|
return rects;
|
|
}
|
|
var _caretsByRowCache = /* @__PURE__ */ new WeakMap();
|
|
function groupCaretsByRow(textRenderInfo) {
|
|
let rows = _caretsByRowCache.get(textRenderInfo);
|
|
if (!rows) {
|
|
rows = [];
|
|
const { caretPositions } = textRenderInfo;
|
|
let curRow;
|
|
const visitCaret = (x, bottom, top, charIndex) => {
|
|
if (!curRow || top < (curRow.top + curRow.bottom) / 2) {
|
|
rows.push(curRow = { bottom, top, carets: [] });
|
|
}
|
|
if (top > curRow.top) curRow.top = top;
|
|
if (bottom < curRow.bottom) curRow.bottom = bottom;
|
|
curRow.carets.push({
|
|
x,
|
|
y: bottom,
|
|
height: top - bottom,
|
|
charIndex
|
|
});
|
|
};
|
|
let i = 0;
|
|
for (; i < caretPositions.length; i += 4) {
|
|
visitCaret(caretPositions[i], caretPositions[i + 2], caretPositions[i + 3], i / 4);
|
|
}
|
|
visitCaret(caretPositions[i - 3], caretPositions[i - 2], caretPositions[i - 1], i / 4);
|
|
}
|
|
_caretsByRowCache.set(textRenderInfo, rows);
|
|
return rows;
|
|
}
|
|
export {
|
|
BatchedText,
|
|
GlyphsGeometry,
|
|
Text,
|
|
configureTextBuilder,
|
|
createTextDerivedMaterial,
|
|
dumpSDFTextures,
|
|
fontResolverWorkerModule,
|
|
getCaretAtPoint,
|
|
getSelectionRects,
|
|
getTextRenderInfo,
|
|
preloadFont,
|
|
typesetterWorkerModule
|
|
};
|
|
/*! Bundled license information:
|
|
|
|
troika-three-text/dist/troika-three-text.esm.js:
|
|
(*!
|
|
Custom build of Typr.ts (https://github.com/fredli74/Typr.ts) for use in Troika text rendering.
|
|
Original MIT license applies: https://github.com/fredli74/Typr.ts/blob/master/LICENSE
|
|
*)
|
|
(*!
|
|
Custom bundle of woff2otf (https://github.com/arty-name/woff2otf) with fflate
|
|
(https://github.com/101arrowz/fflate) for use in Troika text rendering.
|
|
Original licenses apply:
|
|
- fflate: https://github.com/101arrowz/fflate/blob/master/LICENSE (MIT)
|
|
- woff2otf.js: https://github.com/arty-name/woff2otf/blob/master/woff2otf.js (Apache2)
|
|
*)
|
|
(*!
|
|
Custom bundle of @unicode-font-resolver/client v1.0.2 (https://github.com/lojjic/unicode-font-resolver)
|
|
for use in Troika text rendering.
|
|
Original MIT license applies
|
|
*)
|
|
*/
|
|
//# sourceMappingURL=troika-three-text.js.map
|