Current File : /home/tradevaly/www/node_modules/fontkit/src/glyph/TTFGlyphEncoder.js |
import 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;
}
}