feat(flight-finder): implement milestone M1 - domain model and skill contract
This commit is contained in:
72
skills/flight-finder/node_modules/fontkit/src/glyph/BBox.js
generated
vendored
Normal file
72
skills/flight-finder/node_modules/fontkit/src/glyph/BBox.js
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Represents a glyph bounding box
|
||||
*/
|
||||
export default class BBox {
|
||||
constructor(minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity) {
|
||||
/**
|
||||
* The minimum X position in the bounding box
|
||||
* @type {number}
|
||||
*/
|
||||
this.minX = minX;
|
||||
|
||||
/**
|
||||
* The minimum Y position in the bounding box
|
||||
* @type {number}
|
||||
*/
|
||||
this.minY = minY;
|
||||
|
||||
/**
|
||||
* The maxmimum X position in the bounding box
|
||||
* @type {number}
|
||||
*/
|
||||
this.maxX = maxX;
|
||||
|
||||
/**
|
||||
* The maxmimum Y position in the bounding box
|
||||
* @type {number}
|
||||
*/
|
||||
this.maxY = maxY;
|
||||
}
|
||||
|
||||
/**
|
||||
* The width of the bounding box
|
||||
* @type {number}
|
||||
*/
|
||||
get width() {
|
||||
return this.maxX - this.minX;
|
||||
}
|
||||
|
||||
/**
|
||||
* The height of the bounding box
|
||||
* @type {number}
|
||||
*/
|
||||
get height() {
|
||||
return this.maxY - this.minY;
|
||||
}
|
||||
|
||||
addPoint(x, y) {
|
||||
if (Math.abs(x) !== Infinity) {
|
||||
if (x < this.minX) {
|
||||
this.minX = x;
|
||||
}
|
||||
|
||||
if (x > this.maxX) {
|
||||
this.maxX = x;
|
||||
}
|
||||
}
|
||||
|
||||
if (Math.abs(y) !== Infinity) {
|
||||
if (y < this.minY) {
|
||||
this.minY = y;
|
||||
}
|
||||
|
||||
if (y > this.maxY) {
|
||||
this.maxY = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
copy() {
|
||||
return new BBox(this.minX, this.minY, this.maxX, this.maxY);
|
||||
}
|
||||
}
|
||||
604
skills/flight-finder/node_modules/fontkit/src/glyph/CFFGlyph.js
generated
vendored
Normal file
604
skills/flight-finder/node_modules/fontkit/src/glyph/CFFGlyph.js
generated
vendored
Normal file
@@ -0,0 +1,604 @@
|
||||
import Glyph from './Glyph';
|
||||
import Path from './Path';
|
||||
|
||||
/**
|
||||
* Represents an OpenType PostScript glyph, in the Compact Font Format.
|
||||
*/
|
||||
export default class CFFGlyph extends Glyph {
|
||||
type = 'CFF';
|
||||
|
||||
_getName() {
|
||||
if (this._font.CFF2) {
|
||||
return super._getName();
|
||||
}
|
||||
|
||||
return this._font['CFF '].getGlyphName(this.id);
|
||||
}
|
||||
|
||||
bias(s) {
|
||||
if (s.length < 1240) {
|
||||
return 107;
|
||||
} else if (s.length < 33900) {
|
||||
return 1131;
|
||||
} else {
|
||||
return 32768;
|
||||
}
|
||||
}
|
||||
|
||||
_getPath() {
|
||||
let cff = this._font.CFF2 || this._font['CFF '];
|
||||
let { stream } = cff;
|
||||
let str = cff.topDict.CharStrings[this.id];
|
||||
let end = str.offset + str.length;
|
||||
stream.pos = str.offset;
|
||||
|
||||
let path = new Path;
|
||||
let stack = [];
|
||||
let trans = [];
|
||||
|
||||
let width = null;
|
||||
let nStems = 0;
|
||||
let x = 0, y = 0;
|
||||
let usedGsubrs;
|
||||
let usedSubrs;
|
||||
let open = false;
|
||||
|
||||
this._usedGsubrs = usedGsubrs = {};
|
||||
this._usedSubrs = usedSubrs = {};
|
||||
|
||||
let gsubrs = cff.globalSubrIndex || [];
|
||||
let gsubrsBias = this.bias(gsubrs);
|
||||
|
||||
let privateDict = cff.privateDictForGlyph(this.id) || {};
|
||||
let subrs = privateDict.Subrs || [];
|
||||
let subrsBias = this.bias(subrs);
|
||||
|
||||
let vstore = cff.topDict.vstore && cff.topDict.vstore.itemVariationStore;
|
||||
let vsindex = privateDict.vsindex;
|
||||
let variationProcessor = this._font._variationProcessor;
|
||||
|
||||
function checkWidth() {
|
||||
if (width == null) {
|
||||
width = stack.shift() + privateDict.nominalWidthX;
|
||||
}
|
||||
}
|
||||
|
||||
function parseStems() {
|
||||
if (stack.length % 2 !== 0) {
|
||||
checkWidth();
|
||||
}
|
||||
|
||||
nStems += stack.length >> 1;
|
||||
return stack.length = 0;
|
||||
}
|
||||
|
||||
function moveTo(x, y) {
|
||||
if (open) {
|
||||
path.closePath();
|
||||
}
|
||||
|
||||
path.moveTo(x, y);
|
||||
open = true;
|
||||
}
|
||||
|
||||
let parse = function () {
|
||||
while (stream.pos < end) {
|
||||
let op = stream.readUInt8();
|
||||
if (op < 32) {
|
||||
let index, subr, phase;
|
||||
let c1x, c1y, c2x, c2y, c3x, c3y;
|
||||
let c4x, c4y, c5x, c5y, c6x, c6y;
|
||||
let pts;
|
||||
|
||||
switch (op) {
|
||||
case 1: // hstem
|
||||
case 3: // vstem
|
||||
case 18: // hstemhm
|
||||
case 23: // vstemhm
|
||||
parseStems();
|
||||
break;
|
||||
|
||||
case 4: // vmoveto
|
||||
if (stack.length > 1) {
|
||||
checkWidth();
|
||||
}
|
||||
|
||||
y += stack.shift();
|
||||
moveTo(x, y);
|
||||
break;
|
||||
|
||||
case 5: // rlineto
|
||||
while (stack.length >= 2) {
|
||||
x += stack.shift();
|
||||
y += stack.shift();
|
||||
path.lineTo(x, y);
|
||||
}
|
||||
break;
|
||||
|
||||
case 6: // hlineto
|
||||
case 7: // vlineto
|
||||
phase = op === 6;
|
||||
while (stack.length >= 1) {
|
||||
if (phase) {
|
||||
x += stack.shift();
|
||||
} else {
|
||||
y += stack.shift();
|
||||
}
|
||||
|
||||
path.lineTo(x, y);
|
||||
phase = !phase;
|
||||
}
|
||||
break;
|
||||
|
||||
case 8: // rrcurveto
|
||||
while (stack.length > 0) {
|
||||
c1x = x + stack.shift();
|
||||
c1y = y + stack.shift();
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
x = c2x + stack.shift();
|
||||
y = c2y + stack.shift();
|
||||
path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
}
|
||||
break;
|
||||
|
||||
case 10: // callsubr
|
||||
index = stack.pop() + subrsBias;
|
||||
subr = subrs[index];
|
||||
if (subr) {
|
||||
usedSubrs[index] = true;
|
||||
let p = stream.pos;
|
||||
let e = end;
|
||||
stream.pos = subr.offset;
|
||||
end = subr.offset + subr.length;
|
||||
parse();
|
||||
stream.pos = p;
|
||||
end = e;
|
||||
}
|
||||
break;
|
||||
|
||||
case 11: // return
|
||||
if (cff.version >= 2) {
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
case 14: // endchar
|
||||
if (cff.version >= 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (stack.length > 0) {
|
||||
checkWidth();
|
||||
}
|
||||
|
||||
if (open) {
|
||||
path.closePath();
|
||||
open = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 15: { // vsindex
|
||||
if (cff.version < 2) {
|
||||
throw new Error('vsindex operator not supported in CFF v1');
|
||||
}
|
||||
|
||||
vsindex = stack.pop();
|
||||
break;
|
||||
}
|
||||
|
||||
case 16: { // blend
|
||||
if (cff.version < 2) {
|
||||
throw new Error('blend operator not supported in CFF v1');
|
||||
}
|
||||
|
||||
if (!variationProcessor) {
|
||||
throw new Error('blend operator in non-variation font');
|
||||
}
|
||||
|
||||
let blendVector = variationProcessor.getBlendVector(vstore, vsindex);
|
||||
let numBlends = stack.pop();
|
||||
let numOperands = numBlends * blendVector.length;
|
||||
let delta = stack.length - numOperands;
|
||||
let base = delta - numBlends;
|
||||
|
||||
for (let i = 0; i < numBlends; i++) {
|
||||
let sum = stack[base + i];
|
||||
for (let j = 0; j < blendVector.length; j++) {
|
||||
sum += blendVector[j] * stack[delta++];
|
||||
}
|
||||
|
||||
stack[base + i] = sum;
|
||||
}
|
||||
|
||||
while (numOperands--) {
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 19: // hintmask
|
||||
case 20: // cntrmask
|
||||
parseStems();
|
||||
stream.pos += (nStems + 7) >> 3;
|
||||
break;
|
||||
|
||||
case 21: // rmoveto
|
||||
if (stack.length > 2) {
|
||||
checkWidth();
|
||||
}
|
||||
|
||||
x += stack.shift();
|
||||
y += stack.shift();
|
||||
moveTo(x, y);
|
||||
break;
|
||||
|
||||
case 22: // hmoveto
|
||||
if (stack.length > 1) {
|
||||
checkWidth();
|
||||
}
|
||||
|
||||
x += stack.shift();
|
||||
moveTo(x, y);
|
||||
break;
|
||||
|
||||
case 24: // rcurveline
|
||||
while (stack.length >= 8) {
|
||||
c1x = x + stack.shift();
|
||||
c1y = y + stack.shift();
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
x = c2x + stack.shift();
|
||||
y = c2y + stack.shift();
|
||||
path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
}
|
||||
|
||||
x += stack.shift();
|
||||
y += stack.shift();
|
||||
path.lineTo(x, y);
|
||||
break;
|
||||
|
||||
case 25: // rlinecurve
|
||||
while (stack.length >= 8) {
|
||||
x += stack.shift();
|
||||
y += stack.shift();
|
||||
path.lineTo(x, y);
|
||||
}
|
||||
|
||||
c1x = x + stack.shift();
|
||||
c1y = y + stack.shift();
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
x = c2x + stack.shift();
|
||||
y = c2y + stack.shift();
|
||||
path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
break;
|
||||
|
||||
case 26: // vvcurveto
|
||||
if (stack.length % 2) {
|
||||
x += stack.shift();
|
||||
}
|
||||
|
||||
while (stack.length >= 4) {
|
||||
c1x = x;
|
||||
c1y = y + stack.shift();
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
x = c2x;
|
||||
y = c2y + stack.shift();
|
||||
path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
}
|
||||
break;
|
||||
|
||||
case 27: // hhcurveto
|
||||
if (stack.length % 2) {
|
||||
y += stack.shift();
|
||||
}
|
||||
|
||||
while (stack.length >= 4) {
|
||||
c1x = x + stack.shift();
|
||||
c1y = y;
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
x = c2x + stack.shift();
|
||||
y = c2y;
|
||||
path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
}
|
||||
break;
|
||||
|
||||
case 28: // shortint
|
||||
stack.push(stream.readInt16BE());
|
||||
break;
|
||||
|
||||
case 29: // callgsubr
|
||||
index = stack.pop() + gsubrsBias;
|
||||
subr = gsubrs[index];
|
||||
if (subr) {
|
||||
usedGsubrs[index] = true;
|
||||
let p = stream.pos;
|
||||
let e = end;
|
||||
stream.pos = subr.offset;
|
||||
end = subr.offset + subr.length;
|
||||
parse();
|
||||
stream.pos = p;
|
||||
end = e;
|
||||
}
|
||||
break;
|
||||
|
||||
case 30: // vhcurveto
|
||||
case 31: // hvcurveto
|
||||
phase = op === 31;
|
||||
while (stack.length >= 4) {
|
||||
if (phase) {
|
||||
c1x = x + stack.shift();
|
||||
c1y = y;
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
y = c2y + stack.shift();
|
||||
x = c2x + (stack.length === 1 ? stack.shift() : 0);
|
||||
} else {
|
||||
c1x = x;
|
||||
c1y = y + stack.shift();
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
x = c2x + stack.shift();
|
||||
y = c2y + (stack.length === 1 ? stack.shift() : 0);
|
||||
}
|
||||
|
||||
path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
phase = !phase;
|
||||
}
|
||||
break;
|
||||
|
||||
case 12:
|
||||
op = stream.readUInt8();
|
||||
switch (op) {
|
||||
case 3: // and
|
||||
let a = stack.pop();
|
||||
let b = stack.pop();
|
||||
stack.push(a && b ? 1 : 0);
|
||||
break;
|
||||
|
||||
case 4: // or
|
||||
a = stack.pop();
|
||||
b = stack.pop();
|
||||
stack.push(a || b ? 1 : 0);
|
||||
break;
|
||||
|
||||
case 5: // not
|
||||
a = stack.pop();
|
||||
stack.push(a ? 0 : 1);
|
||||
break;
|
||||
|
||||
case 9: // abs
|
||||
a = stack.pop();
|
||||
stack.push(Math.abs(a));
|
||||
break;
|
||||
|
||||
case 10: // add
|
||||
a = stack.pop();
|
||||
b = stack.pop();
|
||||
stack.push(a + b);
|
||||
break;
|
||||
|
||||
case 11: // sub
|
||||
a = stack.pop();
|
||||
b = stack.pop();
|
||||
stack.push(a - b);
|
||||
break;
|
||||
|
||||
case 12: // div
|
||||
a = stack.pop();
|
||||
b = stack.pop();
|
||||
stack.push(a / b);
|
||||
break;
|
||||
|
||||
case 14: // neg
|
||||
a = stack.pop();
|
||||
stack.push(-a);
|
||||
break;
|
||||
|
||||
case 15: // eq
|
||||
a = stack.pop();
|
||||
b = stack.pop();
|
||||
stack.push(a === b ? 1 : 0);
|
||||
break;
|
||||
|
||||
case 18: // drop
|
||||
stack.pop();
|
||||
break;
|
||||
|
||||
case 20: // put
|
||||
let val = stack.pop();
|
||||
let idx = stack.pop();
|
||||
trans[idx] = val;
|
||||
break;
|
||||
|
||||
case 21: // get
|
||||
idx = stack.pop();
|
||||
stack.push(trans[idx] || 0);
|
||||
break;
|
||||
|
||||
case 22: // ifelse
|
||||
let s1 = stack.pop();
|
||||
let s2 = stack.pop();
|
||||
let v1 = stack.pop();
|
||||
let v2 = stack.pop();
|
||||
stack.push(v1 <= v2 ? s1 : s2);
|
||||
break;
|
||||
|
||||
case 23: // random
|
||||
stack.push(Math.random());
|
||||
break;
|
||||
|
||||
case 24: // mul
|
||||
a = stack.pop();
|
||||
b = stack.pop();
|
||||
stack.push(a * b);
|
||||
break;
|
||||
|
||||
case 26: // sqrt
|
||||
a = stack.pop();
|
||||
stack.push(Math.sqrt(a));
|
||||
break;
|
||||
|
||||
case 27: // dup
|
||||
a = stack.pop();
|
||||
stack.push(a, a);
|
||||
break;
|
||||
|
||||
case 28: // exch
|
||||
a = stack.pop();
|
||||
b = stack.pop();
|
||||
stack.push(b, a);
|
||||
break;
|
||||
|
||||
case 29: // index
|
||||
idx = stack.pop();
|
||||
if (idx < 0) {
|
||||
idx = 0;
|
||||
} else if (idx > stack.length - 1) {
|
||||
idx = stack.length - 1;
|
||||
}
|
||||
|
||||
stack.push(stack[idx]);
|
||||
break;
|
||||
|
||||
case 30: // roll
|
||||
let n = stack.pop();
|
||||
let j = stack.pop();
|
||||
|
||||
if (j >= 0) {
|
||||
while (j > 0) {
|
||||
var t = stack[n - 1];
|
||||
for (let i = n - 2; i >= 0; i--) {
|
||||
stack[i + 1] = stack[i];
|
||||
}
|
||||
|
||||
stack[0] = t;
|
||||
j--;
|
||||
}
|
||||
} else {
|
||||
while (j < 0) {
|
||||
var t = stack[0];
|
||||
for (let i = 0; i <= n; i++) {
|
||||
stack[i] = stack[i + 1];
|
||||
}
|
||||
|
||||
stack[n - 1] = t;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 34: // hflex
|
||||
c1x = x + stack.shift();
|
||||
c1y = y;
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
c3x = c2x + stack.shift();
|
||||
c3y = c2y;
|
||||
c4x = c3x + stack.shift();
|
||||
c4y = c3y;
|
||||
c5x = c4x + stack.shift();
|
||||
c5y = c4y;
|
||||
c6x = c5x + stack.shift();
|
||||
c6y = c5y;
|
||||
x = c6x;
|
||||
y = c6y;
|
||||
|
||||
path.bezierCurveTo(c1x, c1y, c2x, c2y, c3x, c3y);
|
||||
path.bezierCurveTo(c4x, c4y, c5x, c5y, c6x, c6y);
|
||||
break;
|
||||
|
||||
case 35: // flex
|
||||
pts = [];
|
||||
|
||||
for (let i = 0; i <= 5; i++) {
|
||||
x += stack.shift();
|
||||
y += stack.shift();
|
||||
pts.push(x, y);
|
||||
}
|
||||
|
||||
path.bezierCurveTo(...pts.slice(0, 6));
|
||||
path.bezierCurveTo(...pts.slice(6));
|
||||
stack.shift(); // fd
|
||||
break;
|
||||
|
||||
case 36: // hflex1
|
||||
c1x = x + stack.shift();
|
||||
c1y = y + stack.shift();
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
c3x = c2x + stack.shift();
|
||||
c3y = c2y;
|
||||
c4x = c3x + stack.shift();
|
||||
c4y = c3y;
|
||||
c5x = c4x + stack.shift();
|
||||
c5y = c4y + stack.shift();
|
||||
c6x = c5x + stack.shift();
|
||||
c6y = c5y;
|
||||
x = c6x;
|
||||
y = c6y;
|
||||
|
||||
path.bezierCurveTo(c1x, c1y, c2x, c2y, c3x, c3y);
|
||||
path.bezierCurveTo(c4x, c4y, c5x, c5y, c6x, c6y);
|
||||
break;
|
||||
|
||||
case 37: // flex1
|
||||
let startx = x;
|
||||
let starty = y;
|
||||
|
||||
pts = [];
|
||||
for (let i = 0; i <= 4; i++) {
|
||||
x += stack.shift();
|
||||
y += stack.shift();
|
||||
pts.push(x, y);
|
||||
}
|
||||
|
||||
if (Math.abs(x - startx) > Math.abs(y - starty)) { // horizontal
|
||||
x += stack.shift();
|
||||
y = starty;
|
||||
} else {
|
||||
x = startx;
|
||||
y += stack.shift();
|
||||
}
|
||||
|
||||
pts.push(x, y);
|
||||
path.bezierCurveTo(...pts.slice(0, 6));
|
||||
path.bezierCurveTo(...pts.slice(6));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown op: 12 ${op}`);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown op: ${op}`);
|
||||
}
|
||||
|
||||
} else if (op < 247) {
|
||||
stack.push(op - 139);
|
||||
} else if (op < 251) {
|
||||
var b1 = stream.readUInt8();
|
||||
stack.push((op - 247) * 256 + b1 + 108);
|
||||
} else if (op < 255) {
|
||||
var b1 = stream.readUInt8();
|
||||
stack.push(-(op - 251) * 256 - b1 - 108);
|
||||
} else {
|
||||
stack.push(stream.readInt32BE() / 65536);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
parse();
|
||||
|
||||
if (open) {
|
||||
path.closePath();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
90
skills/flight-finder/node_modules/fontkit/src/glyph/COLRGlyph.js
generated
vendored
Normal file
90
skills/flight-finder/node_modules/fontkit/src/glyph/COLRGlyph.js
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
import Glyph from './Glyph';
|
||||
import BBox from './BBox';
|
||||
|
||||
class COLRLayer {
|
||||
constructor(glyph, color) {
|
||||
this.glyph = glyph;
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a color (e.g. emoji) glyph in Microsoft's COLR format.
|
||||
* Each glyph in this format contain a list of colored layers, each
|
||||
* of which is another vector glyph.
|
||||
*/
|
||||
export default class COLRGlyph extends Glyph {
|
||||
type = 'COLR';
|
||||
|
||||
_getBBox() {
|
||||
let bbox = new BBox;
|
||||
for (let i = 0; i < this.layers.length; i++) {
|
||||
let layer = this.layers[i];
|
||||
let b = layer.glyph.bbox;
|
||||
bbox.addPoint(b.minX, b.minY);
|
||||
bbox.addPoint(b.maxX, b.maxY);
|
||||
}
|
||||
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of objects containing the glyph and color for
|
||||
* each layer in the composite color glyph.
|
||||
* @type {object[]}
|
||||
*/
|
||||
get layers() {
|
||||
let cpal = this._font.CPAL;
|
||||
let colr = this._font.COLR;
|
||||
let low = 0;
|
||||
let high = colr.baseGlyphRecord.length - 1;
|
||||
|
||||
while (low <= high) {
|
||||
let mid = (low + high) >> 1;
|
||||
var rec = colr.baseGlyphRecord[mid];
|
||||
|
||||
if (this.id < rec.gid) {
|
||||
high = mid - 1;
|
||||
} else if (this.id > rec.gid) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
var baseLayer = rec;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if base glyph not found in COLR table,
|
||||
// default to normal glyph from glyf or CFF
|
||||
if (baseLayer == null) {
|
||||
var g = this._font._getBaseGlyph(this.id);
|
||||
var color = {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0,
|
||||
alpha: 255
|
||||
};
|
||||
|
||||
return [new COLRLayer(g, color)];
|
||||
}
|
||||
|
||||
// otherwise, return an array of all the layers
|
||||
let layers = [];
|
||||
for (let i = baseLayer.firstLayerIndex; i < baseLayer.firstLayerIndex + baseLayer.numLayers; i++) {
|
||||
var rec = colr.layerRecords[i];
|
||||
var color = cpal.colorRecords[rec.paletteIndex];
|
||||
var g = this._font._getBaseGlyph(rec.gid);
|
||||
layers.push(new COLRLayer(g, color));
|
||||
}
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
render(ctx, size) {
|
||||
for (let {glyph, color} of this.layers) {
|
||||
ctx.fillColor([color.red, color.green, color.blue], color.alpha / 255 * 100);
|
||||
glyph.render(ctx, size);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
212
skills/flight-finder/node_modules/fontkit/src/glyph/Glyph.js
generated
vendored
Normal file
212
skills/flight-finder/node_modules/fontkit/src/glyph/Glyph.js
generated
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
import { cache } from '../decorators';
|
||||
import Path from './Path';
|
||||
import {isMark} from 'unicode-properties';
|
||||
import StandardNames from './StandardNames';
|
||||
|
||||
/**
|
||||
* Glyph objects represent a glyph in the font. They have various properties for accessing metrics and
|
||||
* the actual vector path the glyph represents, and methods for rendering the glyph to a graphics context.
|
||||
*
|
||||
* You do not create glyph objects directly. They are created by various methods on the font object.
|
||||
* There are several subclasses of the base Glyph class internally that may be returned depending
|
||||
* on the font format, but they all inherit from this class.
|
||||
*/
|
||||
export default class Glyph {
|
||||
constructor(id, codePoints, font) {
|
||||
/**
|
||||
* The glyph id in the font
|
||||
* @type {number}
|
||||
*/
|
||||
this.id = id;
|
||||
|
||||
/**
|
||||
* An array of unicode code points that are represented by this glyph.
|
||||
* There can be multiple code points in the case of ligatures and other glyphs
|
||||
* that represent multiple visual characters.
|
||||
* @type {number[]}
|
||||
*/
|
||||
this.codePoints = codePoints;
|
||||
this._font = font;
|
||||
|
||||
// TODO: get this info from GDEF if available
|
||||
this.isMark = this.codePoints.length > 0 && this.codePoints.every(isMark);
|
||||
this.isLigature = this.codePoints.length > 1;
|
||||
}
|
||||
|
||||
_getPath() {
|
||||
return new Path();
|
||||
}
|
||||
|
||||
_getCBox() {
|
||||
return this.path.cbox;
|
||||
}
|
||||
|
||||
_getBBox() {
|
||||
return this.path.bbox;
|
||||
}
|
||||
|
||||
_getTableMetrics(table) {
|
||||
if (this.id < table.metrics.length) {
|
||||
return table.metrics.get(this.id);
|
||||
}
|
||||
|
||||
let metric = table.metrics.get(table.metrics.length - 1);
|
||||
let res = {
|
||||
advance: metric ? metric.advance : 0,
|
||||
bearing: table.bearings.get(this.id - table.metrics.length) || 0
|
||||
};
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
_getMetrics(cbox) {
|
||||
if (this._metrics) { return this._metrics; }
|
||||
|
||||
let {advance:advanceWidth, bearing:leftBearing} = this._getTableMetrics(this._font.hmtx);
|
||||
|
||||
// For vertical metrics, use vmtx if available, or fall back to global data from OS/2 or hhea
|
||||
if (this._font.vmtx) {
|
||||
var {advance:advanceHeight, bearing:topBearing} = this._getTableMetrics(this._font.vmtx);
|
||||
|
||||
} else {
|
||||
let os2;
|
||||
if (typeof cbox === 'undefined' || cbox === null) { ({ cbox } = this); }
|
||||
|
||||
if ((os2 = this._font['OS/2']) && os2.version > 0) {
|
||||
var advanceHeight = Math.abs(os2.typoAscender - os2.typoDescender);
|
||||
var topBearing = os2.typoAscender - cbox.maxY;
|
||||
|
||||
} else {
|
||||
let { hhea } = this._font;
|
||||
var advanceHeight = Math.abs(hhea.ascent - hhea.descent);
|
||||
var topBearing = hhea.ascent - cbox.maxY;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._font._variationProcessor && this._font.HVAR) {
|
||||
advanceWidth += this._font._variationProcessor.getAdvanceAdjustment(this.id, this._font.HVAR);
|
||||
}
|
||||
|
||||
return this._metrics = { advanceWidth, advanceHeight, leftBearing, topBearing };
|
||||
}
|
||||
|
||||
/**
|
||||
* The glyph’s control box.
|
||||
* This is often the same as the bounding box, but is faster to compute.
|
||||
* Because of the way bezier curves are defined, some of the control points
|
||||
* can be outside of the bounding box. Where `bbox` takes this into account,
|
||||
* `cbox` does not. Thus, cbox is less accurate, but faster to compute.
|
||||
* See [here](http://www.freetype.org/freetype2/docs/glyphs/glyphs-6.html#section-2)
|
||||
* for a more detailed description.
|
||||
*
|
||||
* @type {BBox}
|
||||
*/
|
||||
@cache
|
||||
get cbox() {
|
||||
return this._getCBox();
|
||||
}
|
||||
|
||||
/**
|
||||
* The glyph’s bounding box, i.e. the rectangle that encloses the
|
||||
* glyph outline as tightly as possible.
|
||||
* @type {BBox}
|
||||
*/
|
||||
@cache
|
||||
get bbox() {
|
||||
return this._getBBox();
|
||||
}
|
||||
|
||||
/**
|
||||
* A vector Path object representing the glyph outline.
|
||||
* @type {Path}
|
||||
*/
|
||||
@cache
|
||||
get path() {
|
||||
// Cache the path so we only decode it once
|
||||
// Decoding is actually performed by subclasses
|
||||
return this._getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a path scaled to the given font size.
|
||||
* @param {number} size
|
||||
* @return {Path}
|
||||
*/
|
||||
getScaledPath(size) {
|
||||
let scale = 1 / this._font.unitsPerEm * size;
|
||||
return this.path.scale(scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* The glyph's advance width.
|
||||
* @type {number}
|
||||
*/
|
||||
@cache
|
||||
get advanceWidth() {
|
||||
return this._getMetrics().advanceWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* The glyph's advance height.
|
||||
* @type {number}
|
||||
*/
|
||||
@cache
|
||||
get advanceHeight() {
|
||||
return this._getMetrics().advanceHeight;
|
||||
}
|
||||
|
||||
get ligatureCaretPositions() {}
|
||||
|
||||
_getName() {
|
||||
let { post } = this._font;
|
||||
if (!post) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (post.version) {
|
||||
case 1:
|
||||
return StandardNames[this.id];
|
||||
|
||||
case 2:
|
||||
let id = post.glyphNameIndex[this.id];
|
||||
if (id < StandardNames.length) {
|
||||
return StandardNames[id];
|
||||
}
|
||||
|
||||
return post.names[id - StandardNames.length];
|
||||
|
||||
case 2.5:
|
||||
return StandardNames[this.id + post.offsets[this.id]];
|
||||
|
||||
case 4:
|
||||
return String.fromCharCode(post.map[this.id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The glyph's name
|
||||
* @type {string}
|
||||
*/
|
||||
@cache
|
||||
get name() {
|
||||
return this._getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the glyph to the given graphics context, at the specified font size.
|
||||
* @param {CanvasRenderingContext2d} ctx
|
||||
* @param {number} size
|
||||
*/
|
||||
render(ctx, size) {
|
||||
ctx.save();
|
||||
|
||||
let scale = 1 / this._font.head.unitsPerEm * size;
|
||||
ctx.scale(scale, scale);
|
||||
|
||||
let fn = this.path.toFunction();
|
||||
fn(ctx);
|
||||
ctx.fill();
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
486
skills/flight-finder/node_modules/fontkit/src/glyph/GlyphVariationProcessor.js
generated
vendored
Normal file
486
skills/flight-finder/node_modules/fontkit/src/glyph/GlyphVariationProcessor.js
generated
vendored
Normal file
@@ -0,0 +1,486 @@
|
||||
const TUPLES_SHARE_POINT_NUMBERS = 0x8000;
|
||||
const TUPLE_COUNT_MASK = 0x0fff;
|
||||
const EMBEDDED_TUPLE_COORD = 0x8000;
|
||||
const INTERMEDIATE_TUPLE = 0x4000;
|
||||
const PRIVATE_POINT_NUMBERS = 0x2000;
|
||||
const TUPLE_INDEX_MASK = 0x0fff;
|
||||
const POINTS_ARE_WORDS = 0x80;
|
||||
const POINT_RUN_COUNT_MASK = 0x7f;
|
||||
const DELTAS_ARE_ZERO = 0x80;
|
||||
const DELTAS_ARE_WORDS = 0x40;
|
||||
const DELTA_RUN_COUNT_MASK = 0x3f;
|
||||
|
||||
/**
|
||||
* This class is transforms TrueType glyphs according to the data from
|
||||
* the Apple Advanced Typography variation tables (fvar, gvar, and avar).
|
||||
* These tables allow infinite adjustments to glyph weight, width, slant,
|
||||
* and optical size without the designer needing to specify every exact style.
|
||||
*
|
||||
* Apple's documentation for these tables is not great, so thanks to the
|
||||
* Freetype project for figuring much of this out.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export default class GlyphVariationProcessor {
|
||||
constructor(font, coords) {
|
||||
this.font = font;
|
||||
this.normalizedCoords = this.normalizeCoords(coords);
|
||||
this.blendVectors = new Map;
|
||||
}
|
||||
|
||||
normalizeCoords(coords) {
|
||||
// the default mapping is linear along each axis, in two segments:
|
||||
// from the minValue to defaultValue, and from defaultValue to maxValue.
|
||||
let normalized = [];
|
||||
for (var i = 0; i < this.font.fvar.axis.length; i++) {
|
||||
let axis = this.font.fvar.axis[i];
|
||||
if (coords[i] < axis.defaultValue) {
|
||||
normalized.push((coords[i] - axis.defaultValue + Number.EPSILON) / (axis.defaultValue - axis.minValue + Number.EPSILON));
|
||||
} else {
|
||||
normalized.push((coords[i] - axis.defaultValue + Number.EPSILON) / (axis.maxValue - axis.defaultValue + Number.EPSILON));
|
||||
}
|
||||
}
|
||||
|
||||
// if there is an avar table, the normalized value is calculated
|
||||
// by interpolating between the two nearest mapped values.
|
||||
if (this.font.avar) {
|
||||
for (var i = 0; i < this.font.avar.segment.length; i++) {
|
||||
let segment = this.font.avar.segment[i];
|
||||
for (let j = 0; j < segment.correspondence.length; j++) {
|
||||
let pair = segment.correspondence[j];
|
||||
if (j >= 1 && normalized[i] < pair.fromCoord) {
|
||||
let prev = segment.correspondence[j - 1];
|
||||
normalized[i] = ((normalized[i] - prev.fromCoord) * (pair.toCoord - prev.toCoord) + Number.EPSILON) /
|
||||
(pair.fromCoord - prev.fromCoord + Number.EPSILON) +
|
||||
prev.toCoord;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
transformPoints(gid, glyphPoints) {
|
||||
if (!this.font.fvar || !this.font.gvar) { return; }
|
||||
|
||||
let { gvar } = this.font;
|
||||
if (gid >= gvar.glyphCount) { return; }
|
||||
|
||||
let offset = gvar.offsets[gid];
|
||||
if (offset === gvar.offsets[gid + 1]) { return; }
|
||||
|
||||
// Read the gvar data for this glyph
|
||||
let { stream } = this.font;
|
||||
stream.pos = offset;
|
||||
if (stream.pos >= stream.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tupleCount = stream.readUInt16BE();
|
||||
let offsetToData = offset + stream.readUInt16BE();
|
||||
|
||||
if (tupleCount & TUPLES_SHARE_POINT_NUMBERS) {
|
||||
var here = stream.pos;
|
||||
stream.pos = offsetToData;
|
||||
var sharedPoints = this.decodePoints();
|
||||
offsetToData = stream.pos;
|
||||
stream.pos = here;
|
||||
}
|
||||
|
||||
let origPoints = glyphPoints.map(pt => pt.copy());
|
||||
|
||||
tupleCount &= TUPLE_COUNT_MASK;
|
||||
for (let i = 0; i < tupleCount; i++) {
|
||||
let tupleDataSize = stream.readUInt16BE();
|
||||
let tupleIndex = stream.readUInt16BE();
|
||||
|
||||
if (tupleIndex & EMBEDDED_TUPLE_COORD) {
|
||||
var tupleCoords = [];
|
||||
for (let a = 0; a < gvar.axisCount; a++) {
|
||||
tupleCoords.push(stream.readInt16BE() / 16384);
|
||||
}
|
||||
|
||||
} else {
|
||||
if ((tupleIndex & TUPLE_INDEX_MASK) >= gvar.globalCoordCount) {
|
||||
throw new Error('Invalid gvar table');
|
||||
}
|
||||
|
||||
var tupleCoords = gvar.globalCoords[tupleIndex & TUPLE_INDEX_MASK];
|
||||
}
|
||||
|
||||
if (tupleIndex & INTERMEDIATE_TUPLE) {
|
||||
var startCoords = [];
|
||||
for (let a = 0; a < gvar.axisCount; a++) {
|
||||
startCoords.push(stream.readInt16BE() / 16384);
|
||||
}
|
||||
|
||||
var endCoords = [];
|
||||
for (let a = 0; a < gvar.axisCount; a++) {
|
||||
endCoords.push(stream.readInt16BE() / 16384);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the factor at which to apply this tuple
|
||||
let factor = this.tupleFactor(tupleIndex, tupleCoords, startCoords, endCoords);
|
||||
if (factor === 0) {
|
||||
offsetToData += tupleDataSize;
|
||||
continue;
|
||||
}
|
||||
|
||||
var here = stream.pos;
|
||||
stream.pos = offsetToData;
|
||||
|
||||
if (tupleIndex & PRIVATE_POINT_NUMBERS) {
|
||||
var points = this.decodePoints();
|
||||
} else {
|
||||
var points = sharedPoints;
|
||||
}
|
||||
|
||||
// points.length = 0 means there are deltas for all points
|
||||
let nPoints = points.length === 0 ? glyphPoints.length : points.length;
|
||||
let xDeltas = this.decodeDeltas(nPoints);
|
||||
let yDeltas = this.decodeDeltas(nPoints);
|
||||
|
||||
if (points.length === 0) { // all points
|
||||
for (let i = 0; i < glyphPoints.length; i++) {
|
||||
var point = glyphPoints[i];
|
||||
point.x += Math.round(xDeltas[i] * factor);
|
||||
point.y += Math.round(yDeltas[i] * factor);
|
||||
}
|
||||
} else {
|
||||
let outPoints = origPoints.map(pt => pt.copy());
|
||||
let hasDelta = glyphPoints.map(() => false);
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
let idx = points[i];
|
||||
if (idx < glyphPoints.length) {
|
||||
let point = outPoints[idx];
|
||||
hasDelta[idx] = true;
|
||||
|
||||
point.x += xDeltas[i] * factor;
|
||||
point.y += yDeltas[i] * factor;
|
||||
}
|
||||
}
|
||||
|
||||
this.interpolateMissingDeltas(outPoints, origPoints, hasDelta);
|
||||
|
||||
for (let i = 0; i < glyphPoints.length; i++) {
|
||||
let deltaX = outPoints[i].x - origPoints[i].x;
|
||||
let deltaY = outPoints[i].y - origPoints[i].y;
|
||||
|
||||
glyphPoints[i].x = Math.round(glyphPoints[i].x + deltaX);
|
||||
glyphPoints[i].y = Math.round(glyphPoints[i].y + deltaY);
|
||||
}
|
||||
}
|
||||
|
||||
offsetToData += tupleDataSize;
|
||||
stream.pos = here;
|
||||
}
|
||||
}
|
||||
|
||||
decodePoints() {
|
||||
let stream = this.font.stream;
|
||||
let count = stream.readUInt8();
|
||||
|
||||
if (count & POINTS_ARE_WORDS) {
|
||||
count = (count & POINT_RUN_COUNT_MASK) << 8 | stream.readUInt8();
|
||||
}
|
||||
|
||||
let points = new Uint16Array(count);
|
||||
let i = 0;
|
||||
let point = 0;
|
||||
while (i < count) {
|
||||
let run = stream.readUInt8();
|
||||
let runCount = (run & POINT_RUN_COUNT_MASK) + 1;
|
||||
let fn = run & POINTS_ARE_WORDS ? stream.readUInt16 : stream.readUInt8;
|
||||
|
||||
for (let j = 0; j < runCount && i < count; j++) {
|
||||
point += fn.call(stream);
|
||||
points[i++] = point;
|
||||
}
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
decodeDeltas(count) {
|
||||
let stream = this.font.stream;
|
||||
let i = 0;
|
||||
let deltas = new Int16Array(count);
|
||||
|
||||
while (i < count) {
|
||||
let run = stream.readUInt8();
|
||||
let runCount = (run & DELTA_RUN_COUNT_MASK) + 1;
|
||||
|
||||
if (run & DELTAS_ARE_ZERO) {
|
||||
i += runCount;
|
||||
|
||||
} else {
|
||||
let fn = run & DELTAS_ARE_WORDS ? stream.readInt16BE : stream.readInt8;
|
||||
for (let j = 0; j < runCount && i < count; j++) {
|
||||
deltas[i++] = fn.call(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
tupleFactor(tupleIndex, tupleCoords, startCoords, endCoords) {
|
||||
let normalized = this.normalizedCoords;
|
||||
let { gvar } = this.font;
|
||||
let factor = 1;
|
||||
|
||||
for (let i = 0; i < gvar.axisCount; i++) {
|
||||
if (tupleCoords[i] === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (normalized[i] === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((tupleIndex & INTERMEDIATE_TUPLE) === 0) {
|
||||
if ((normalized[i] < Math.min(0, tupleCoords[i])) ||
|
||||
(normalized[i] > Math.max(0, tupleCoords[i]))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
factor = (factor * normalized[i] + Number.EPSILON) / (tupleCoords[i] + Number.EPSILON);
|
||||
} else {
|
||||
if ((normalized[i] < startCoords[i]) ||
|
||||
(normalized[i] > endCoords[i])) {
|
||||
return 0;
|
||||
|
||||
} else if (normalized[i] < tupleCoords[i]) {
|
||||
factor = factor * (normalized[i] - startCoords[i] + Number.EPSILON) / (tupleCoords[i] - startCoords[i] + Number.EPSILON);
|
||||
|
||||
} else {
|
||||
factor = factor * (endCoords[i] - normalized[i] + Number.EPSILON) / (endCoords[i] - tupleCoords[i] + Number.EPSILON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
// Interpolates points without delta values.
|
||||
// Needed for the Ø and Q glyphs in Skia.
|
||||
// Algorithm from Freetype.
|
||||
interpolateMissingDeltas(points, inPoints, hasDelta) {
|
||||
if (points.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let point = 0;
|
||||
while (point < points.length) {
|
||||
let firstPoint = point;
|
||||
|
||||
// find the end point of the contour
|
||||
let endPoint = point;
|
||||
let pt = points[endPoint];
|
||||
while (!pt.endContour) {
|
||||
pt = points[++endPoint];
|
||||
}
|
||||
|
||||
// find the first point that has a delta
|
||||
while (point <= endPoint && !hasDelta[point]) {
|
||||
point++;
|
||||
}
|
||||
|
||||
if (point > endPoint) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let firstDelta = point;
|
||||
let curDelta = point;
|
||||
point++;
|
||||
|
||||
while (point <= endPoint) {
|
||||
// find the next point with a delta, and interpolate intermediate points
|
||||
if (hasDelta[point]) {
|
||||
this.deltaInterpolate(curDelta + 1, point - 1, curDelta, point, inPoints, points);
|
||||
curDelta = point;
|
||||
}
|
||||
|
||||
point++;
|
||||
}
|
||||
|
||||
// shift contour if we only have a single delta
|
||||
if (curDelta === firstDelta) {
|
||||
this.deltaShift(firstPoint, endPoint, curDelta, inPoints, points);
|
||||
} else {
|
||||
// otherwise, handle the remaining points at the end and beginning of the contour
|
||||
this.deltaInterpolate(curDelta + 1, endPoint, curDelta, firstDelta, inPoints, points);
|
||||
|
||||
if (firstDelta > 0) {
|
||||
this.deltaInterpolate(firstPoint, firstDelta - 1, curDelta, firstDelta, inPoints, points);
|
||||
}
|
||||
}
|
||||
|
||||
point = endPoint + 1;
|
||||
}
|
||||
}
|
||||
|
||||
deltaInterpolate(p1, p2, ref1, ref2, inPoints, outPoints) {
|
||||
if (p1 > p2) {
|
||||
return;
|
||||
}
|
||||
|
||||
let iterable = ['x', 'y'];
|
||||
for (let i = 0; i < iterable.length; i++) {
|
||||
let k = iterable[i];
|
||||
if (inPoints[ref1][k] > inPoints[ref2][k]) {
|
||||
var p = ref1;
|
||||
ref1 = ref2;
|
||||
ref2 = p;
|
||||
}
|
||||
|
||||
let in1 = inPoints[ref1][k];
|
||||
let in2 = inPoints[ref2][k];
|
||||
let out1 = outPoints[ref1][k];
|
||||
let out2 = outPoints[ref2][k];
|
||||
|
||||
// If the reference points have the same coordinate but different
|
||||
// delta, inferred delta is zero. Otherwise interpolate.
|
||||
if (in1 !== in2 || out1 === out2) {
|
||||
let scale = in1 === in2 ? 0 : (out2 - out1) / (in2 - in1);
|
||||
|
||||
for (let p = p1; p <= p2; p++) {
|
||||
let out = inPoints[p][k];
|
||||
|
||||
if (out <= in1) {
|
||||
out += out1 - in1;
|
||||
} else if (out >= in2) {
|
||||
out += out2 - in2;
|
||||
} else {
|
||||
out = out1 + (out - in1) * scale;
|
||||
}
|
||||
|
||||
outPoints[p][k] = out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deltaShift(p1, p2, ref, inPoints, outPoints) {
|
||||
let deltaX = outPoints[ref].x - inPoints[ref].x;
|
||||
let deltaY = outPoints[ref].y - inPoints[ref].y;
|
||||
|
||||
if (deltaX === 0 && deltaY === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let p = p1; p <= p2; p++) {
|
||||
if (p !== ref) {
|
||||
outPoints[p].x += deltaX;
|
||||
outPoints[p].y += deltaY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAdvanceAdjustment(gid, table) {
|
||||
let outerIndex, innerIndex;
|
||||
|
||||
if (table.advanceWidthMapping) {
|
||||
let idx = gid;
|
||||
if (idx >= table.advanceWidthMapping.mapCount) {
|
||||
idx = table.advanceWidthMapping.mapCount - 1;
|
||||
}
|
||||
|
||||
let entryFormat = table.advanceWidthMapping.entryFormat;
|
||||
({outerIndex, innerIndex} = table.advanceWidthMapping.mapData[idx]);
|
||||
} else {
|
||||
outerIndex = 0;
|
||||
innerIndex = gid;
|
||||
}
|
||||
|
||||
return this.getDelta(table.itemVariationStore, outerIndex, innerIndex);
|
||||
}
|
||||
|
||||
// See pseudo code from `Font Variations Overview'
|
||||
// in the OpenType specification.
|
||||
getDelta(itemStore, outerIndex, innerIndex) {
|
||||
if (outerIndex >= itemStore.itemVariationData.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let varData = itemStore.itemVariationData[outerIndex];
|
||||
if (innerIndex >= varData.deltaSets.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let deltaSet = varData.deltaSets[innerIndex];
|
||||
let blendVector = this.getBlendVector(itemStore, outerIndex);
|
||||
let netAdjustment = 0;
|
||||
|
||||
for (let master = 0; master < varData.regionIndexCount; master++) {
|
||||
netAdjustment += deltaSet.deltas[master] * blendVector[master];
|
||||
}
|
||||
|
||||
return netAdjustment;
|
||||
}
|
||||
|
||||
getBlendVector(itemStore, outerIndex) {
|
||||
let varData = itemStore.itemVariationData[outerIndex];
|
||||
if (this.blendVectors.has(varData)) {
|
||||
return this.blendVectors.get(varData);
|
||||
}
|
||||
|
||||
let normalizedCoords = this.normalizedCoords;
|
||||
let blendVector = [];
|
||||
|
||||
// outer loop steps through master designs to be blended
|
||||
for (let master = 0; master < varData.regionIndexCount; master++) {
|
||||
let scalar = 1;
|
||||
let regionIndex = varData.regionIndexes[master];
|
||||
let axes = itemStore.variationRegionList.variationRegions[regionIndex];
|
||||
|
||||
// inner loop steps through axes in this region
|
||||
for (let j = 0; j < axes.length; j++) {
|
||||
let axis = axes[j];
|
||||
let axisScalar;
|
||||
|
||||
// compute the scalar contribution of this axis
|
||||
// ignore invalid ranges
|
||||
if (axis.startCoord > axis.peakCoord || axis.peakCoord > axis.endCoord) {
|
||||
axisScalar = 1;
|
||||
|
||||
} else if (axis.startCoord < 0 && axis.endCoord > 0 && axis.peakCoord !== 0) {
|
||||
axisScalar = 1;
|
||||
|
||||
// peak of 0 means ignore this axis
|
||||
} else if (axis.peakCoord === 0) {
|
||||
axisScalar = 1;
|
||||
|
||||
// ignore this region if coords are out of range
|
||||
} else if (normalizedCoords[j] < axis.startCoord || normalizedCoords[j] > axis.endCoord) {
|
||||
axisScalar = 0;
|
||||
|
||||
// calculate a proportional factor
|
||||
} else {
|
||||
if (normalizedCoords[j] === axis.peakCoord) {
|
||||
axisScalar = 1;
|
||||
} else if (normalizedCoords[j] < axis.peakCoord) {
|
||||
axisScalar = (normalizedCoords[j] - axis.startCoord + Number.EPSILON) /
|
||||
(axis.peakCoord - axis.startCoord + Number.EPSILON);
|
||||
} else {
|
||||
axisScalar = (axis.endCoord - normalizedCoords[j] + Number.EPSILON) /
|
||||
(axis.endCoord - axis.peakCoord + Number.EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
// take product of all the axis scalars
|
||||
scalar *= axisScalar;
|
||||
}
|
||||
|
||||
blendVector[master] = scalar;
|
||||
}
|
||||
|
||||
this.blendVectors.set(varData, blendVector);
|
||||
return blendVector;
|
||||
}
|
||||
}
|
||||
244
skills/flight-finder/node_modules/fontkit/src/glyph/Path.js
generated
vendored
Normal file
244
skills/flight-finder/node_modules/fontkit/src/glyph/Path.js
generated
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
import BBox from './BBox';
|
||||
|
||||
const SVG_COMMANDS = {
|
||||
moveTo: 'M',
|
||||
lineTo: 'L',
|
||||
quadraticCurveTo: 'Q',
|
||||
bezierCurveTo: 'C',
|
||||
closePath: 'Z'
|
||||
};
|
||||
|
||||
/**
|
||||
* Path objects are returned by glyphs and represent the actual
|
||||
* vector outlines for each glyph in the font. Paths can be converted
|
||||
* to SVG path data strings, or to functions that can be applied to
|
||||
* render the path to a graphics context.
|
||||
*/
|
||||
export default class Path {
|
||||
constructor() {
|
||||
this.commands = [];
|
||||
this._bbox = null;
|
||||
this._cbox = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the path to a JavaScript function that can be applied with
|
||||
* a graphics context in order to render the path.
|
||||
* @return {string}
|
||||
*/
|
||||
toFunction() {
|
||||
return ctx => {
|
||||
this.commands.forEach(c => {
|
||||
return ctx[c.command].apply(ctx, c.args)
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the path to an SVG path data string
|
||||
* @return {string}
|
||||
*/
|
||||
toSVG() {
|
||||
let cmds = this.commands.map(c => {
|
||||
let args = c.args.map(arg => Math.round(arg * 100) / 100);
|
||||
return `${SVG_COMMANDS[c.command]}${args.join(' ')}`;
|
||||
});
|
||||
|
||||
return cmds.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "control box" of a path.
|
||||
* This is like the bounding box, but it includes all points including
|
||||
* control points of bezier segments and is much faster to compute than
|
||||
* the real bounding box.
|
||||
* @type {BBox}
|
||||
*/
|
||||
get cbox() {
|
||||
if (!this._cbox) {
|
||||
let cbox = new BBox;
|
||||
for (let command of this.commands) {
|
||||
for (let i = 0; i < command.args.length; i += 2) {
|
||||
cbox.addPoint(command.args[i], command.args[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
this._cbox = Object.freeze(cbox);
|
||||
}
|
||||
|
||||
return this._cbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exact bounding box of the path by evaluating curve segments.
|
||||
* Slower to compute than the control box, but more accurate.
|
||||
* @type {BBox}
|
||||
*/
|
||||
get bbox() {
|
||||
if (this._bbox) {
|
||||
return this._bbox;
|
||||
}
|
||||
|
||||
let bbox = new BBox;
|
||||
let cx = 0, cy = 0;
|
||||
|
||||
let f = t => (
|
||||
Math.pow(1 - t, 3) * p0[i]
|
||||
+ 3 * Math.pow(1 - t, 2) * t * p1[i]
|
||||
+ 3 * (1 - t) * Math.pow(t, 2) * p2[i]
|
||||
+ Math.pow(t, 3) * p3[i]
|
||||
);
|
||||
|
||||
for (let c of this.commands) {
|
||||
switch (c.command) {
|
||||
case 'moveTo':
|
||||
case 'lineTo':
|
||||
let [x, y] = c.args;
|
||||
bbox.addPoint(x, y);
|
||||
cx = x;
|
||||
cy = y;
|
||||
break;
|
||||
|
||||
case 'quadraticCurveTo':
|
||||
case 'bezierCurveTo':
|
||||
if (c.command === 'quadraticCurveTo') {
|
||||
// http://fontforge.org/bezier.html
|
||||
var [qp1x, qp1y, p3x, p3y] = c.args;
|
||||
var cp1x = cx + 2 / 3 * (qp1x - cx); // CP1 = QP0 + 2/3 * (QP1-QP0)
|
||||
var cp1y = cy + 2 / 3 * (qp1y - cy);
|
||||
var cp2x = p3x + 2 / 3 * (qp1x - p3x); // CP2 = QP2 + 2/3 * (QP1-QP2)
|
||||
var cp2y = p3y + 2 / 3 * (qp1y - p3y);
|
||||
} else {
|
||||
var [cp1x, cp1y, cp2x, cp2y, p3x, p3y] = c.args;
|
||||
}
|
||||
|
||||
// http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
|
||||
bbox.addPoint(p3x, p3y);
|
||||
|
||||
var p0 = [cx, cy];
|
||||
var p1 = [cp1x, cp1y];
|
||||
var p2 = [cp2x, cp2y];
|
||||
var p3 = [p3x, p3y];
|
||||
|
||||
for (var i = 0; i <= 1; i++) {
|
||||
let b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i];
|
||||
let a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i];
|
||||
c = 3 * p1[i] - 3 * p0[i];
|
||||
|
||||
if (a === 0) {
|
||||
if (b === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let t = -c / b;
|
||||
if (0 < t && t < 1) {
|
||||
if (i === 0) {
|
||||
bbox.addPoint(f(t), bbox.maxY);
|
||||
} else if (i === 1) {
|
||||
bbox.addPoint(bbox.maxX, f(t));
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
let b2ac = Math.pow(b, 2) - 4 * c * a;
|
||||
if (b2ac < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let t1 = (-b + Math.sqrt(b2ac)) / (2 * a);
|
||||
if (0 < t1 && t1 < 1) {
|
||||
if (i === 0) {
|
||||
bbox.addPoint(f(t1), bbox.maxY);
|
||||
} else if (i === 1) {
|
||||
bbox.addPoint(bbox.maxX, f(t1));
|
||||
}
|
||||
}
|
||||
|
||||
let t2 = (-b - Math.sqrt(b2ac)) / (2 * a);
|
||||
if (0 < t2 && t2 < 1) {
|
||||
if (i === 0) {
|
||||
bbox.addPoint(f(t2), bbox.maxY);
|
||||
} else if (i === 1) {
|
||||
bbox.addPoint(bbox.maxX, f(t2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cx = p3x;
|
||||
cy = p3y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return this._bbox = Object.freeze(bbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a mapping function to each point in the path.
|
||||
* @param {function} fn
|
||||
* @return {Path}
|
||||
*/
|
||||
mapPoints(fn) {
|
||||
let path = new Path;
|
||||
|
||||
for (let c of this.commands) {
|
||||
let args = [];
|
||||
for (let i = 0; i < c.args.length; i += 2) {
|
||||
let [x, y] = fn(c.args[i], c.args[i + 1]);
|
||||
args.push(x, y);
|
||||
}
|
||||
|
||||
path[c.command](...args);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the path by the given matrix.
|
||||
*/
|
||||
transform(m0, m1, m2, m3, m4, m5) {
|
||||
return this.mapPoints((x, y) => {
|
||||
const tx = m0 * x + m2 * y + m4;
|
||||
const ty = m1 * x + m3 * y + m5;
|
||||
return [tx, ty];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the path by the given offset.
|
||||
*/
|
||||
translate(x, y) {
|
||||
return this.transform(1, 0, 0, 1, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the path by the given angle (in radians).
|
||||
*/
|
||||
rotate(angle) {
|
||||
let cos = Math.cos(angle);
|
||||
let sin = Math.sin(angle);
|
||||
return this.transform(cos, sin, -sin, cos, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales the path.
|
||||
*/
|
||||
scale(scaleX, scaleY = scaleX) {
|
||||
return this.transform(scaleX, 0, 0, scaleY, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
for (let command of ['moveTo', 'lineTo', 'quadraticCurveTo', 'bezierCurveTo', 'closePath']) {
|
||||
Path.prototype[command] = function(...args) {
|
||||
this._bbox = this._cbox = null;
|
||||
this.commands.push({
|
||||
command,
|
||||
args
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
}
|
||||
54
skills/flight-finder/node_modules/fontkit/src/glyph/SBIXGlyph.js
generated
vendored
Normal file
54
skills/flight-finder/node_modules/fontkit/src/glyph/SBIXGlyph.js
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
import TTFGlyph from './TTFGlyph';
|
||||
import * as r from 'restructure';
|
||||
|
||||
let SBIXImage = new r.Struct({
|
||||
originX: r.uint16,
|
||||
originY: r.uint16,
|
||||
type: new r.String(4),
|
||||
data: new r.Buffer(t => t.parent.buflen - t._currentOffset)
|
||||
});
|
||||
|
||||
/**
|
||||
* Represents a color (e.g. emoji) glyph in Apple's SBIX format.
|
||||
*/
|
||||
export default class SBIXGlyph extends TTFGlyph {
|
||||
type = 'SBIX';
|
||||
|
||||
/**
|
||||
* Returns an object representing a glyph image at the given point size.
|
||||
* The object has a data property with a Buffer containing the actual image data,
|
||||
* along with the image type, and origin.
|
||||
*
|
||||
* @param {number} size
|
||||
* @return {object}
|
||||
*/
|
||||
getImageForSize(size) {
|
||||
for (let i = 0; i < this._font.sbix.imageTables.length; i++) {
|
||||
var table = this._font.sbix.imageTables[i];
|
||||
if (table.ppem >= size) { break; }
|
||||
}
|
||||
|
||||
let offsets = table.imageOffsets;
|
||||
let start = offsets[this.id];
|
||||
let end = offsets[this.id + 1];
|
||||
|
||||
if (start === end) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this._font.stream.pos = start;
|
||||
return SBIXImage.decode(this._font.stream, {buflen: end - start});
|
||||
}
|
||||
|
||||
render(ctx, size) {
|
||||
let img = this.getImageForSize(size);
|
||||
if (img != null) {
|
||||
let scale = size / this._font.unitsPerEm;
|
||||
ctx.image(img.data, {height: size, x: img.originX, y: (this.bbox.minY - img.originY) * scale});
|
||||
}
|
||||
|
||||
if (this._font.sbix.flags.renderOutlines) {
|
||||
super.render(ctx, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
skills/flight-finder/node_modules/fontkit/src/glyph/StandardNames.js
generated
vendored
Normal file
27
skills/flight-finder/node_modules/fontkit/src/glyph/StandardNames.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
export default [
|
||||
'.notdef', '.null', 'nonmarkingreturn', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent',
|
||||
'ampersand', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash',
|
||||
'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less',
|
||||
'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
||||
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright',
|
||||
'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde',
|
||||
'Adieresis', 'Aring', 'Ccedilla', 'Eacute', 'Ntilde', 'Odieresis', 'Udieresis', 'aacute', 'agrave',
|
||||
'acircumflex', 'adieresis', 'atilde', 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex', 'edieresis',
|
||||
'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde', 'oacute', 'ograve', 'ocircumflex', 'odieresis',
|
||||
'otilde', 'uacute', 'ugrave', 'ucircumflex', 'udieresis', 'dagger', 'degree', 'cent', 'sterling', 'section',
|
||||
'bullet', 'paragraph', 'germandbls', 'registered', 'copyright', 'trademark', 'acute', 'dieresis', 'notequal',
|
||||
'AE', 'Oslash', 'infinity', 'plusminus', 'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff', 'summation',
|
||||
'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', 'Omega', 'ae', 'oslash', 'questiondown',
|
||||
'exclamdown', 'logicalnot', 'radical', 'florin', 'approxequal', 'Delta', 'guillemotleft', 'guillemotright',
|
||||
'ellipsis', 'nonbreakingspace', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash', 'emdash', 'quotedblleft',
|
||||
'quotedblright', 'quoteleft', 'quoteright', 'divide', 'lozenge', 'ydieresis', 'Ydieresis', 'fraction',
|
||||
'currency', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'daggerdbl', 'periodcentered', 'quotesinglbase',
|
||||
'quotedblbase', 'perthousand', 'Acircumflex', 'Ecircumflex', 'Aacute', 'Edieresis', 'Egrave', 'Iacute',
|
||||
'Icircumflex', 'Idieresis', 'Igrave', 'Oacute', 'Ocircumflex', 'apple', 'Ograve', 'Uacute', 'Ucircumflex',
|
||||
'Ugrave', 'dotlessi', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla', 'hungarumlaut',
|
||||
'ogonek', 'caron', 'Lslash', 'lslash', 'Scaron', 'scaron', 'Zcaron', 'zcaron', 'brokenbar', 'Eth', 'eth',
|
||||
'Yacute', 'yacute', 'Thorn', 'thorn', 'minus', 'multiply', 'onesuperior', 'twosuperior', 'threesuperior',
|
||||
'onehalf', 'onequarter', 'threequarters', 'franc', 'Gbreve', 'gbreve', 'Idotaccent', 'Scedilla', 'scedilla',
|
||||
'Cacute', 'cacute', 'Ccaron', 'ccaron', 'dcroat'
|
||||
];
|
||||
393
skills/flight-finder/node_modules/fontkit/src/glyph/TTFGlyph.js
generated
vendored
Normal file
393
skills/flight-finder/node_modules/fontkit/src/glyph/TTFGlyph.js
generated
vendored
Normal file
@@ -0,0 +1,393 @@
|
||||
import Glyph from './Glyph';
|
||||
import Path from './Path';
|
||||
import BBox from './BBox';
|
||||
import * as r from 'restructure';
|
||||
|
||||
// The header for both simple and composite glyphs
|
||||
let GlyfHeader = new r.Struct({
|
||||
numberOfContours: r.int16, // if negative, this is a composite glyph
|
||||
xMin: r.int16,
|
||||
yMin: r.int16,
|
||||
xMax: r.int16,
|
||||
yMax: r.int16
|
||||
});
|
||||
|
||||
// Flags for simple glyphs
|
||||
const ON_CURVE = 1 << 0;
|
||||
const X_SHORT_VECTOR = 1 << 1;
|
||||
const Y_SHORT_VECTOR = 1 << 2;
|
||||
const REPEAT = 1 << 3;
|
||||
const SAME_X = 1 << 4;
|
||||
const SAME_Y = 1 << 5;
|
||||
|
||||
// Flags for composite glyphs
|
||||
const ARG_1_AND_2_ARE_WORDS = 1 << 0;
|
||||
const ARGS_ARE_XY_VALUES = 1 << 1;
|
||||
const ROUND_XY_TO_GRID = 1 << 2;
|
||||
const WE_HAVE_A_SCALE = 1 << 3;
|
||||
const MORE_COMPONENTS = 1 << 5;
|
||||
const WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
|
||||
const WE_HAVE_A_TWO_BY_TWO = 1 << 7;
|
||||
const WE_HAVE_INSTRUCTIONS = 1 << 8;
|
||||
const USE_MY_METRICS = 1 << 9;
|
||||
const OVERLAP_COMPOUND = 1 << 10;
|
||||
const SCALED_COMPONENT_OFFSET = 1 << 11;
|
||||
const UNSCALED_COMPONENT_OFFSET = 1 << 12;
|
||||
|
||||
// Represents a point in a simple glyph
|
||||
export class Point {
|
||||
constructor(onCurve, endContour, x = 0, y = 0) {
|
||||
this.onCurve = onCurve;
|
||||
this.endContour = endContour;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
copy() {
|
||||
return new Point(this.onCurve, this.endContour, this.x, this.y);
|
||||
}
|
||||
}
|
||||
|
||||
// Represents a component in a composite glyph
|
||||
class Component {
|
||||
constructor(glyphID, dx, dy) {
|
||||
this.glyphID = glyphID;
|
||||
this.dx = dx;
|
||||
this.dy = dy;
|
||||
this.pos = 0;
|
||||
this.scaleX = this.scaleY = 1;
|
||||
this.scale01 = this.scale10 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a TrueType glyph.
|
||||
*/
|
||||
export default class TTFGlyph extends Glyph {
|
||||
type = 'TTF';
|
||||
|
||||
// Parses just the glyph header and returns the bounding box
|
||||
_getCBox(internal) {
|
||||
// We need to decode the glyph if variation processing is requested,
|
||||
// so it's easier just to recompute the path's cbox after decoding.
|
||||
if (this._font._variationProcessor && !internal) {
|
||||
return this.path.cbox;
|
||||
}
|
||||
|
||||
let stream = this._font._getTableStream('glyf');
|
||||
stream.pos += this._font.loca.offsets[this.id];
|
||||
let glyph = GlyfHeader.decode(stream);
|
||||
|
||||
let cbox = new BBox(glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax);
|
||||
return Object.freeze(cbox);
|
||||
}
|
||||
|
||||
// Parses a single glyph coordinate
|
||||
_parseGlyphCoord(stream, prev, short, same) {
|
||||
if (short) {
|
||||
var val = stream.readUInt8();
|
||||
if (!same) {
|
||||
val = -val;
|
||||
}
|
||||
|
||||
val += prev;
|
||||
} else {
|
||||
if (same) {
|
||||
var val = prev;
|
||||
} else {
|
||||
var val = prev + stream.readInt16BE();
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
// Decodes the glyph data into points for simple glyphs,
|
||||
// or components for composite glyphs
|
||||
_decode() {
|
||||
let glyfPos = this._font.loca.offsets[this.id];
|
||||
let nextPos = this._font.loca.offsets[this.id + 1];
|
||||
|
||||
// Nothing to do if there is no data for this glyph
|
||||
if (glyfPos === nextPos) { return null; }
|
||||
|
||||
let stream = this._font._getTableStream('glyf');
|
||||
stream.pos += glyfPos;
|
||||
let startPos = stream.pos;
|
||||
|
||||
let glyph = GlyfHeader.decode(stream);
|
||||
|
||||
if (glyph.numberOfContours > 0) {
|
||||
this._decodeSimple(glyph, stream);
|
||||
|
||||
} else if (glyph.numberOfContours < 0) {
|
||||
this._decodeComposite(glyph, stream, startPos);
|
||||
}
|
||||
|
||||
return glyph;
|
||||
}
|
||||
|
||||
_decodeSimple(glyph, stream) {
|
||||
// this is a simple glyph
|
||||
glyph.points = [];
|
||||
|
||||
let endPtsOfContours = new r.Array(r.uint16, glyph.numberOfContours).decode(stream);
|
||||
glyph.instructions = new r.Array(r.uint8, r.uint16).decode(stream);
|
||||
|
||||
let flags = [];
|
||||
let numCoords = endPtsOfContours[endPtsOfContours.length - 1] + 1;
|
||||
|
||||
while (flags.length < numCoords) {
|
||||
var flag = stream.readUInt8();
|
||||
flags.push(flag);
|
||||
|
||||
// check for repeat flag
|
||||
if (flag & REPEAT) {
|
||||
let count = stream.readUInt8();
|
||||
for (let j = 0; j < count; j++) {
|
||||
flags.push(flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < flags.length; i++) {
|
||||
var flag = flags[i];
|
||||
let point = new Point(!!(flag & ON_CURVE), endPtsOfContours.indexOf(i) >= 0, 0, 0);
|
||||
glyph.points.push(point);
|
||||
}
|
||||
|
||||
let px = 0;
|
||||
for (var i = 0; i < flags.length; i++) {
|
||||
var flag = flags[i];
|
||||
glyph.points[i].x = px = this._parseGlyphCoord(stream, px, flag & X_SHORT_VECTOR, flag & SAME_X);
|
||||
}
|
||||
|
||||
let py = 0;
|
||||
for (var i = 0; i < flags.length; i++) {
|
||||
var flag = flags[i];
|
||||
glyph.points[i].y = py = this._parseGlyphCoord(stream, py, flag & Y_SHORT_VECTOR, flag & SAME_Y);
|
||||
}
|
||||
|
||||
if (this._font._variationProcessor) {
|
||||
let points = glyph.points.slice();
|
||||
points.push(...this._getPhantomPoints(glyph));
|
||||
|
||||
this._font._variationProcessor.transformPoints(this.id, points);
|
||||
glyph.phantomPoints = points.slice(-4);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_decodeComposite(glyph, stream, offset = 0) {
|
||||
// this is a composite glyph
|
||||
glyph.components = [];
|
||||
let haveInstructions = false;
|
||||
let flags = MORE_COMPONENTS;
|
||||
|
||||
while (flags & MORE_COMPONENTS) {
|
||||
flags = stream.readUInt16BE();
|
||||
let gPos = stream.pos - offset;
|
||||
let glyphID = stream.readUInt16BE();
|
||||
if (!haveInstructions) {
|
||||
haveInstructions = (flags & WE_HAVE_INSTRUCTIONS) !== 0;
|
||||
}
|
||||
|
||||
if (flags & ARG_1_AND_2_ARE_WORDS) {
|
||||
var dx = stream.readInt16BE();
|
||||
var dy = stream.readInt16BE();
|
||||
} else {
|
||||
var dx = stream.readInt8();
|
||||
var dy = stream.readInt8();
|
||||
}
|
||||
|
||||
var component = new Component(glyphID, dx, dy);
|
||||
component.pos = gPos;
|
||||
|
||||
if (flags & WE_HAVE_A_SCALE) {
|
||||
// fixed number with 14 bits of fraction
|
||||
component.scaleX =
|
||||
component.scaleY = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
||||
|
||||
} else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
|
||||
component.scaleX = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
||||
component.scaleY = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
||||
|
||||
} else if (flags & WE_HAVE_A_TWO_BY_TWO) {
|
||||
component.scaleX = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
||||
component.scale01 = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
||||
component.scale10 = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
||||
component.scaleY = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
||||
}
|
||||
|
||||
glyph.components.push(component);
|
||||
}
|
||||
|
||||
if (this._font._variationProcessor) {
|
||||
let points = [];
|
||||
for (let j = 0; j < glyph.components.length; j++) {
|
||||
var component = glyph.components[j];
|
||||
points.push(new Point(true, true, component.dx, component.dy));
|
||||
}
|
||||
|
||||
points.push(...this._getPhantomPoints(glyph));
|
||||
|
||||
this._font._variationProcessor.transformPoints(this.id, points);
|
||||
glyph.phantomPoints = points.splice(-4, 4);
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
let point = points[i];
|
||||
glyph.components[i].dx = point.x;
|
||||
glyph.components[i].dy = point.y;
|
||||
}
|
||||
}
|
||||
|
||||
return haveInstructions;
|
||||
}
|
||||
|
||||
_getPhantomPoints(glyph) {
|
||||
let cbox = this._getCBox(true);
|
||||
if (this._metrics == null) {
|
||||
this._metrics = Glyph.prototype._getMetrics.call(this, cbox);
|
||||
}
|
||||
|
||||
let { advanceWidth, advanceHeight, leftBearing, topBearing } = this._metrics;
|
||||
|
||||
return [
|
||||
new Point(false, true, glyph.xMin - leftBearing, 0),
|
||||
new Point(false, true, glyph.xMin - leftBearing + advanceWidth, 0),
|
||||
new Point(false, true, 0, glyph.yMax + topBearing),
|
||||
new Point(false, true, 0, glyph.yMax + topBearing + advanceHeight)
|
||||
];
|
||||
}
|
||||
|
||||
// Decodes font data, resolves composite glyphs, and returns an array of contours
|
||||
_getContours() {
|
||||
let glyph = this._decode();
|
||||
if (!glyph) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let points = [];
|
||||
|
||||
if (glyph.numberOfContours < 0) {
|
||||
// resolve composite glyphs
|
||||
for (let component of glyph.components) {
|
||||
let contours = this._font.getGlyph(component.glyphID)._getContours();
|
||||
for (let i = 0; i < contours.length; i++) {
|
||||
let contour = contours[i];
|
||||
for (let j = 0; j < contour.length; j++) {
|
||||
let point = contour[j];
|
||||
let x = point.x * component.scaleX + point.y * component.scale01 + component.dx;
|
||||
let y = point.y * component.scaleY + point.x * component.scale10 + component.dy;
|
||||
points.push(new Point(point.onCurve, point.endContour, x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
points = glyph.points || [];
|
||||
}
|
||||
|
||||
// Recompute and cache metrics if we performed variation processing, and don't have an HVAR table
|
||||
if (glyph.phantomPoints && !this._font.directory.tables.HVAR) {
|
||||
this._metrics.advanceWidth = glyph.phantomPoints[1].x - glyph.phantomPoints[0].x;
|
||||
this._metrics.advanceHeight = glyph.phantomPoints[3].y - glyph.phantomPoints[2].y;
|
||||
this._metrics.leftBearing = glyph.xMin - glyph.phantomPoints[0].x;
|
||||
this._metrics.topBearing = glyph.phantomPoints[2].y - glyph.yMax;
|
||||
}
|
||||
|
||||
let contours = [];
|
||||
let cur = [];
|
||||
for (let k = 0; k < points.length; k++) {
|
||||
var point = points[k];
|
||||
cur.push(point);
|
||||
if (point.endContour) {
|
||||
contours.push(cur);
|
||||
cur = [];
|
||||
}
|
||||
}
|
||||
|
||||
return contours;
|
||||
}
|
||||
|
||||
_getMetrics() {
|
||||
if (this._metrics) {
|
||||
return this._metrics;
|
||||
}
|
||||
|
||||
let cbox = this._getCBox(true);
|
||||
super._getMetrics(cbox);
|
||||
|
||||
if (this._font._variationProcessor && !this._font.HVAR) {
|
||||
// No HVAR table, decode the glyph. This triggers recomputation of metrics.
|
||||
this.path;
|
||||
}
|
||||
|
||||
return this._metrics;
|
||||
}
|
||||
|
||||
// Converts contours to a Path object that can be rendered
|
||||
_getPath() {
|
||||
let contours = this._getContours();
|
||||
let path = new Path;
|
||||
|
||||
for (let i = 0; i < contours.length; i++) {
|
||||
let contour = contours[i];
|
||||
let firstPt = contour[0];
|
||||
let lastPt = contour[contour.length - 1];
|
||||
let start = 0;
|
||||
|
||||
if (firstPt.onCurve) {
|
||||
// The first point will be consumed by the moveTo command, so skip in the loop
|
||||
var curvePt = null;
|
||||
start = 1;
|
||||
} else {
|
||||
if (lastPt.onCurve) {
|
||||
// Start at the last point if the first point is off curve and the last point is on curve
|
||||
firstPt = lastPt;
|
||||
} else {
|
||||
// Start at the middle if both the first and last points are off curve
|
||||
firstPt = new Point(false, false, (firstPt.x + lastPt.x) / 2, (firstPt.y + lastPt.y) / 2);
|
||||
}
|
||||
|
||||
var curvePt = firstPt;
|
||||
}
|
||||
|
||||
path.moveTo(firstPt.x, firstPt.y);
|
||||
|
||||
for (let j = start; j < contour.length; j++) {
|
||||
let pt = contour[j];
|
||||
let prevPt = j === 0 ? firstPt : contour[j - 1];
|
||||
|
||||
if (prevPt.onCurve && pt.onCurve) {
|
||||
path.lineTo(pt.x, pt.y);
|
||||
|
||||
} else if (prevPt.onCurve && !pt.onCurve) {
|
||||
var curvePt = pt;
|
||||
|
||||
} else if (!prevPt.onCurve && !pt.onCurve) {
|
||||
let midX = (prevPt.x + pt.x) / 2;
|
||||
let midY = (prevPt.y + pt.y) / 2;
|
||||
path.quadraticCurveTo(prevPt.x, prevPt.y, midX, midY);
|
||||
var curvePt = pt;
|
||||
|
||||
} else if (!prevPt.onCurve && pt.onCurve) {
|
||||
path.quadraticCurveTo(curvePt.x, curvePt.y, pt.x, pt.y);
|
||||
var curvePt = null;
|
||||
|
||||
} else {
|
||||
throw new Error("Unknown TTF path state");
|
||||
}
|
||||
}
|
||||
|
||||
// Connect the first and last points
|
||||
if (curvePt) {
|
||||
path.quadraticCurveTo(curvePt.x, curvePt.y, firstPt.x, firstPt.y);
|
||||
}
|
||||
|
||||
path.closePath();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
158
skills/flight-finder/node_modules/fontkit/src/glyph/TTFGlyphEncoder.js
generated
vendored
Normal file
158
skills/flight-finder/node_modules/fontkit/src/glyph/TTFGlyphEncoder.js
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
import * as r from 'restructure';
|
||||
|
||||
// Flags for simple glyphs
|
||||
const ON_CURVE = 1 << 0;
|
||||
const X_SHORT_VECTOR = 1 << 1;
|
||||
const Y_SHORT_VECTOR = 1 << 2;
|
||||
const REPEAT = 1 << 3;
|
||||
const SAME_X = 1 << 4;
|
||||
const SAME_Y = 1 << 5;
|
||||
|
||||
class Point {
|
||||
static size(val) {
|
||||
return val >= 0 && val <= 255 ? 1 : 2;
|
||||
}
|
||||
|
||||
static encode(stream, value) {
|
||||
if (value >= 0 && value <= 255) {
|
||||
stream.writeUInt8(value);
|
||||
} else {
|
||||
stream.writeInt16BE(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let Glyf = new r.Struct({
|
||||
numberOfContours: r.int16, // if negative, this is a composite glyph
|
||||
xMin: r.int16,
|
||||
yMin: r.int16,
|
||||
xMax: r.int16,
|
||||
yMax: r.int16,
|
||||
endPtsOfContours: new r.Array(r.uint16, 'numberOfContours'),
|
||||
instructions: new r.Array(r.uint8, r.uint16),
|
||||
flags: new r.Array(r.uint8, 0),
|
||||
xPoints: new r.Array(Point, 0),
|
||||
yPoints: new r.Array(Point, 0)
|
||||
});
|
||||
|
||||
/**
|
||||
* Encodes TrueType glyph outlines
|
||||
*/
|
||||
export default class TTFGlyphEncoder {
|
||||
encodeSimple(path, instructions = []) {
|
||||
let endPtsOfContours = [];
|
||||
let xPoints = [];
|
||||
let yPoints = [];
|
||||
let flags = [];
|
||||
let same = 0;
|
||||
let lastX = 0, lastY = 0, lastFlag = 0;
|
||||
let pointCount = 0;
|
||||
|
||||
for (let i = 0; i < path.commands.length; i++) {
|
||||
let c = path.commands[i];
|
||||
|
||||
for (let j = 0; j < c.args.length; j += 2) {
|
||||
let x = c.args[j];
|
||||
let y = c.args[j + 1];
|
||||
let flag = 0;
|
||||
|
||||
// If the ending point of a quadratic curve is the midpoint
|
||||
// between the control point and the control point of the next
|
||||
// quadratic curve, we can omit the ending point.
|
||||
if (c.command === 'quadraticCurveTo' && j === 2) {
|
||||
let next = path.commands[i + 1];
|
||||
if (next && next.command === 'quadraticCurveTo') {
|
||||
let midX = (lastX + next.args[0]) / 2;
|
||||
let midY = (lastY + next.args[1]) / 2;
|
||||
|
||||
if (x === midX && y === midY) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All points except control points are on curve.
|
||||
if (!(c.command === 'quadraticCurveTo' && j === 0)) {
|
||||
flag |= ON_CURVE;
|
||||
}
|
||||
|
||||
flag = this._encodePoint(x, lastX, xPoints, flag, X_SHORT_VECTOR, SAME_X);
|
||||
flag = this._encodePoint(y, lastY, yPoints, flag, Y_SHORT_VECTOR, SAME_Y);
|
||||
|
||||
if (flag === lastFlag && same < 255) {
|
||||
flags[flags.length - 1] |= REPEAT;
|
||||
same++;
|
||||
} else {
|
||||
if (same > 0) {
|
||||
flags.push(same);
|
||||
same = 0;
|
||||
}
|
||||
|
||||
flags.push(flag);
|
||||
lastFlag = flag;
|
||||
}
|
||||
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
pointCount++;
|
||||
}
|
||||
|
||||
if (c.command === 'closePath') {
|
||||
endPtsOfContours.push(pointCount - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Close the path if the last command didn't already
|
||||
if (path.commands.length > 1 && path.commands[path.commands.length - 1].command !== 'closePath') {
|
||||
endPtsOfContours.push(pointCount - 1);
|
||||
}
|
||||
|
||||
let bbox = path.bbox;
|
||||
let glyf = {
|
||||
numberOfContours: endPtsOfContours.length,
|
||||
xMin: bbox.minX,
|
||||
yMin: bbox.minY,
|
||||
xMax: bbox.maxX,
|
||||
yMax: bbox.maxY,
|
||||
endPtsOfContours: endPtsOfContours,
|
||||
instructions: instructions,
|
||||
flags: flags,
|
||||
xPoints: xPoints,
|
||||
yPoints: yPoints
|
||||
};
|
||||
|
||||
let size = Glyf.size(glyf);
|
||||
let tail = 4 - (size % 4);
|
||||
|
||||
let stream = new r.EncodeStream(size + tail);
|
||||
Glyf.encode(stream, glyf);
|
||||
|
||||
// Align to 4-byte length
|
||||
if (tail !== 0) {
|
||||
stream.fill(0, tail);
|
||||
}
|
||||
|
||||
return stream.buffer;
|
||||
}
|
||||
|
||||
_encodePoint(value, last, points, flag, shortFlag, sameFlag) {
|
||||
let diff = value - last;
|
||||
|
||||
if (value === last) {
|
||||
flag |= sameFlag;
|
||||
} else {
|
||||
if (-255 <= diff && diff <= 255) {
|
||||
flag |= shortFlag;
|
||||
if (diff < 0) {
|
||||
diff = -diff;
|
||||
} else {
|
||||
flag |= sameFlag;
|
||||
}
|
||||
}
|
||||
|
||||
points.push(diff);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
17
skills/flight-finder/node_modules/fontkit/src/glyph/WOFF2Glyph.js
generated
vendored
Normal file
17
skills/flight-finder/node_modules/fontkit/src/glyph/WOFF2Glyph.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import TTFGlyph from './TTFGlyph';
|
||||
|
||||
/**
|
||||
* Represents a TrueType glyph in the WOFF2 format, which compresses glyphs differently.
|
||||
*/
|
||||
export default class WOFF2Glyph extends TTFGlyph {
|
||||
type = 'WOFF2';
|
||||
|
||||
_decode() {
|
||||
// We have to decode in advance (in WOFF2Font), so just return the pre-decoded data.
|
||||
return this._font._transformedGlyphs[this.id];
|
||||
}
|
||||
|
||||
_getCBox() {
|
||||
return this.path.bbox;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user