
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 r from '@foliojs-fork/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;
}
}