Show More
Commit Description:
merge
Commit Description:
merge
References:
File last commit:
Show/Diff file:
Action:
node_modules/fontkit/src/glyph/CFFGlyph.js
| 599 lines
| 16.6 KiB
| application/javascript
| JavascriptGenshiLexer
|
r789 | 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 { stream } = this._font; | ||||
let { pos } = stream; | ||||
let cff = this._font.CFF2 || this._font['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; | ||||
} | ||||
} | ||||