|
|
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;
|
|
|
}
|
|
|
}
|
|
|
|