
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
import Glyph from './Glyph';
import Path from './Path';
/**
* Represents an OpenType PostScript glyph, in the Compact Font Format.
*/
export default class CFFGlyph extends Glyph {
_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) {
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
let 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) {
var c1x = x + stack.shift();
var c1y = y + stack.shift();
var c2x = c1x + stack.shift();
var c2y = c1y + stack.shift();
x = c2x + stack.shift();
y = c2y + stack.shift();
path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
}
break;
case 10: // callsubr
let index = stack.pop() + subrsBias;
let subr = subrs[index];
if (subr) {
usedSubrs[index] = true;
var p = stream.pos;
var 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) {
var c1x = x + stack.shift();
var c1y = y + stack.shift();
var c2x = c1x + stack.shift();
var 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);
}
var c1x = x + stack.shift();
var c1y = y + stack.shift();
var c2x = c1x + stack.shift();
var 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;
var p = stream.pos;
var 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();
let c3x = c2x + stack.shift();
let c3y = c2y;
let c4x = c3x + stack.shift();
let c4y = c3y;
let c5x = c4x + stack.shift();
let c5y = c4y;
let c6x = c5x + stack.shift();
let 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
let 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;
}
}