Implementation
parseCFFCharstring(Font font, glyph, code) {
var c1x;
var c1y;
var c2x;
var c2y;
var p = new Path();
List<num> stack = [];
var nStems = 0;
var haveWidth = false;
var open = false;
num x = 0;
num y = 0;
var subrs;
var subrsBias;
var defaultWidthX;
var nominalWidthX;
if (font.isCIDFont) {
var fdIndex = font.tables["cff"]["topDict"]["_fdSelect"][glyph.index];
var fdDict = font.tables["cff"]["topDict"]["_fdArray"][fdIndex];
subrs = fdDict["_subrs"];
subrsBias = fdDict["_subrsBias"];
defaultWidthX = fdDict["_defaultWidthX"];
nominalWidthX = fdDict["_nominalWidthX"];
} else {
subrs = font.tables["cff"]["topDict"]["_subrs"];
subrsBias = font.tables["cff"]["topDict"]["_subrsBias"];
defaultWidthX = font.tables["cff"]["topDict"]["_defaultWidthX"];
nominalWidthX = font.tables["cff"]["topDict"]["_nominalWidthX"];
}
var width = defaultWidthX;
Function newContour = (x, y) {
if (open) {
p.closePath();
}
p.moveTo(x, y);
open = true;
};
Function parseStems = () {
var hasWidthArg;
// The number of stem operators on the stack is always even.
// If the value is uneven, that means a width is specified.
hasWidthArg = stack.length % 2 != 0;
if (hasWidthArg && !haveWidth) {
width = stack.removeAt(0) + nominalWidthX;
}
nStems += stack.length >> 1;
stack.length = 0;
haveWidth = true;
};
__parse(code) {
var b1;
var b2;
var b3;
var b4;
var codeIndex;
var subrCode;
var jpx;
var jpy;
var c3x;
var c3y;
var c4x;
var c4y;
var i = 0;
while (i < code.length) {
var v = code[i];
i += 1;
switch (v) {
case 1: // hstem
parseStems();
break;
case 3: // vstem
parseStems();
break;
case 4: // vmoveto
if (stack.length > 1 && !haveWidth) {
width = stack.removeAt(0) + nominalWidthX;
haveWidth = true;
}
y += stack.removeLast();
newContour(x, y);
break;
case 5: // rlineto
while (stack.length > 0) {
x += stack.removeAt(0);
y += stack.removeAt(0);
p.lineTo(x, y);
}
break;
case 6: // hlineto
while (stack.length > 0) {
x += stack.removeAt(0);
p.lineTo(x, y);
if (stack.length == 0) {
break;
}
y += stack.removeAt(0);
p.lineTo(x, y);
}
break;
case 7: // vlineto
while (stack.length > 0) {
y += stack.removeAt(0);
p.lineTo(x, y);
if (stack.length == 0) {
break;
}
x += stack.removeAt(0);
p.lineTo(x, y);
}
break;
case 8: // rrcurveto
while (stack.length > 0) {
c1x = x + stack.removeAt(0);
c1y = y + stack.removeAt(0);
c2x = c1x + stack.removeAt(0);
c2y = c1y + stack.removeAt(0);
x = c2x + stack.removeAt(0);
y = c2y + stack.removeAt(0);
p.curveTo(c1x, c1y, c2x, c2y, x, y);
}
break;
case 10: // callsubr
codeIndex = stack.removeLast() + subrsBias;
subrCode = subrs[codeIndex];
if (subrCode) {
__parse(subrCode);
}
break;
case 11: // return
return;
case 12: // flex operators
v = code[i];
i += 1;
switch (v) {
case 35: // flex
// |- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 dx6 dy6 fd flex (12 35) |-
c1x = x + stack.removeAt(0); // dx1
c1y = y + stack.removeAt(0); // dy1
c2x = c1x + stack.removeAt(0); // dx2
c2y = c1y + stack.removeAt(0); // dy2
jpx = c2x + stack.removeAt(0); // dx3
jpy = c2y + stack.removeAt(0); // dy3
c3x = jpx + stack.removeAt(0); // dx4
c3y = jpy + stack.removeAt(0); // dy4
c4x = c3x + stack.removeAt(0); // dx5
c4y = c3y + stack.removeAt(0); // dy5
x = c4x + stack.removeAt(0); // dx6
y = c4y + stack.removeAt(0); // dy6
stack.removeAt(0); // flex depth
p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
p.curveTo(c3x, c3y, c4x, c4y, x, y);
break;
case 34: // hflex
// |- dx1 dx2 dy2 dx3 dx4 dx5 dx6 hflex (12 34) |-
c1x = x + stack.removeAt(0); // dx1
c1y = y; // dy1
c2x = c1x + stack.removeAt(0); // dx2
c2y = c1y + stack.removeAt(0); // dy2
jpx = c2x + stack.removeAt(0); // dx3
jpy = c2y; // dy3
c3x = jpx + stack.removeAt(0); // dx4
c3y = c2y; // dy4
c4x = c3x + stack.removeAt(0); // dx5
c4y = y; // dy5
x = c4x + stack.removeAt(0); // dx6
p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
p.curveTo(c3x, c3y, c4x, c4y, x, y);
break;
case 36: // hflex1
// |- dx1 dy1 dx2 dy2 dx3 dx4 dx5 dy5 dx6 hflex1 (12 36) |-
c1x = x + stack.removeAt(0); // dx1
c1y = y + stack.removeAt(0); // dy1
c2x = c1x + stack.removeAt(0); // dx2
c2y = c1y + stack.removeAt(0); // dy2
jpx = c2x + stack.removeAt(0); // dx3
jpy = c2y; // dy3
c3x = jpx + stack.removeAt(0); // dx4
c3y = c2y; // dy4
c4x = c3x + stack.removeAt(0); // dx5
c4y = c3y + stack.removeAt(0); // dy5
x = c4x + stack.removeAt(0); // dx6
p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
p.curveTo(c3x, c3y, c4x, c4y, x, y);
break;
case 37: // flex1
// |- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 d6 flex1 (12 37) |-
c1x = x + stack.removeAt(0); // dx1
c1y = y + stack.removeAt(0); // dy1
c2x = c1x + stack.removeAt(0); // dx2
c2y = c1y + stack.removeAt(0); // dy2
jpx = c2x + stack.removeAt(0); // dx3
jpy = c2y + stack.removeAt(0); // dy3
c3x = jpx + stack.removeAt(0); // dx4
c3y = jpy + stack.removeAt(0); // dy4
c4x = c3x + stack.removeAt(0); // dx5
c4y = c3y + stack.removeAt(0); // dy5
if (Math.abs(c4x - x) > Math.abs(c4y - y)) {
x = c4x + stack.removeAt(0);
} else {
y = c4y + stack.removeAt(0);
}
p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
p.curveTo(c3x, c3y, c4x, c4y, x, y);
break;
default:
print('Glyph ' + glyph.index + ': unknown operator ${1200}' + v);
stack.length = 0;
}
break;
case 14: // endchar
if (stack.length > 0 && !haveWidth) {
width = stack.removeAt(0) + nominalWidthX;
haveWidth = true;
}
if (open) {
p.closePath();
open = false;
}
break;
case 18: // hstemhm
parseStems();
break;
case 19: // hintmask
case 20: // cntrmask
parseStems();
i += (nStems + 7) >> 3;
break;
case 21: // rmoveto
if (stack.length > 2 && !haveWidth) {
width = stack.removeAt(0) + nominalWidthX;
haveWidth = true;
}
y += stack.removeLast();
x += stack.removeLast();
newContour(x, y);
break;
case 22: // hmoveto
if (stack.length > 1 && !haveWidth) {
width = stack.removeAt(0) + nominalWidthX;
haveWidth = true;
}
x += stack.removeLast();
newContour(x, y);
break;
case 23: // vstemhm
parseStems();
break;
case 24: // rcurveline
while (stack.length > 2) {
c1x = x + stack.removeAt(0);
c1y = y + stack.removeAt(0);
c2x = c1x + stack.removeAt(0);
c2y = c1y + stack.removeAt(0);
x = c2x + stack.removeAt(0);
y = c2y + stack.removeAt(0);
p.curveTo(c1x, c1y, c2x, c2y, x, y);
}
x += stack.removeAt(0);
y += stack.removeAt(0);
p.lineTo(x, y);
break;
case 25: // rlinecurve
while (stack.length > 6) {
x += stack.removeAt(0);
y += stack.removeAt(0);
p.lineTo(x, y);
}
c1x = x + stack.removeAt(0);
c1y = y + stack.removeAt(0);
c2x = c1x + stack.removeAt(0);
c2y = c1y + stack.removeAt(0);
x = c2x + stack.removeAt(0);
y = c2y + stack.removeAt(0);
p.curveTo(c1x, c1y, c2x, c2y, x, y);
break;
case 26: // vvcurveto
if (stack.length % 2 != 0) {
x += stack.removeAt(0);
}
while (stack.length > 0) {
c1x = x;
c1y = y + stack.removeAt(0);
c2x = c1x + stack.removeAt(0);
c2y = c1y + stack.removeAt(0);
x = c2x;
y = c2y + stack.removeAt(0);
p.curveTo(c1x, c1y, c2x, c2y, x, y);
}
break;
case 27: // hhcurveto
if (stack.length % 2 != 0) {
y += stack.removeAt(0);
}
while (stack.length > 0) {
c1x = x + stack.removeAt(0);
c1y = y;
c2x = c1x + stack.removeAt(0);
c2y = c1y + stack.removeAt(0);
x = c2x + stack.removeAt(0);
y = c2y;
p.curveTo(c1x, c1y, c2x, c2y, x, y);
}
break;
case 28: // shortint
b1 = code[i];
b2 = code[i + 1];
stack.add(((b1 << 24) | (b2 << 16)) >> 16);
i += 2;
break;
case 29: // callgsubr
codeIndex = stack.removeLast() + font.gsubrsBias;
subrCode = font.gsubrs[codeIndex];
if (subrCode) {
__parse(subrCode);
}
break;
case 30: // vhcurveto
while (stack.length > 0) {
c1x = x;
c1y = y + stack.removeAt(0);
c2x = c1x + stack.removeAt(0);
c2y = c1y + stack.removeAt(0);
x = c2x + stack.removeAt(0);
y = c2y + (stack.length == 1 ? stack.removeAt(0) : 0);
p.curveTo(c1x, c1y, c2x, c2y, x, y);
if (stack.length == 0) {
break;
}
c1x = x + stack.removeAt(0);
c1y = y;
c2x = c1x + stack.removeAt(0);
c2y = c1y + stack.removeAt(0);
y = c2y + stack.removeAt(0);
x = c2x + (stack.length == 1 ? stack.removeAt(0) : 0);
p.curveTo(c1x, c1y, c2x, c2y, x, y);
}
break;
case 31: // hvcurveto
while (stack.length > 0) {
c1x = x + stack.removeAt(0);
c1y = y;
c2x = c1x + stack.removeAt(0);
c2y = c1y + stack.removeAt(0);
y = c2y + stack.removeAt(0);
x = c2x + (stack.length == 1 ? stack.removeAt(0) : 0);
p.curveTo(c1x, c1y, c2x, c2y, x, y);
if (stack.length == 0) {
break;
}
c1x = x;
c1y = y + stack.removeAt(0);
c2x = c1x + stack.removeAt(0);
c2y = c1y + stack.removeAt(0);
x = c2x + stack.removeAt(0);
y = c2y + (stack.length == 1 ? stack.removeAt(0) : 0);
p.curveTo(c1x, c1y, c2x, c2y, x, y);
}
break;
default:
if (v < 32) {
print('Glyph ' + glyph.index + ': unknown operator ' + v);
} else if (v < 247) {
stack.add(v - 139);
} else if (v < 251) {
b1 = code[i];
i += 1;
stack.add((v - 247) * 256 + b1 + 108);
} else if (v < 255) {
b1 = code[i];
i += 1;
stack.add(-(v - 251) * 256 - b1 - 108);
} else {
b1 = code[i];
b2 = code[i + 1];
b3 = code[i + 2];
b4 = code[i + 3];
i += 4;
stack.add(((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) / 65536);
}
}
}
}
__parse(code);
glyph.advanceWidth = width;
return p;
}