You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
5.1 KiB
196 lines
5.1 KiB
2 years ago
|
'use strict';
|
||
|
|
||
|
// See documentation here: http://www.microsoft.com/typography/otspec/glyf.htm
|
||
|
|
||
|
var _ = require('lodash');
|
||
|
var ByteBuffer = require('microbuffer');
|
||
|
|
||
|
function getFlags(glyph) {
|
||
|
var result = [];
|
||
|
|
||
|
_.forEach(glyph.ttfContours, function (contour) {
|
||
|
_.forEach(contour, function (point) {
|
||
|
var flag = point.onCurve ? 1 : 0;
|
||
|
|
||
|
if (point.x === 0) {
|
||
|
flag += 16;
|
||
|
} else {
|
||
|
if (-0xFF <= point.x && point.x <= 0xFF) {
|
||
|
flag += 2; // the corresponding x-coordinate is 1 byte long
|
||
|
}
|
||
|
if (point.x > 0 && point.x <= 0xFF) {
|
||
|
flag += 16; // If x-Short Vector is set, this bit describes the sign of the value, with 1 equalling positive and 0 negative
|
||
|
}
|
||
|
}
|
||
|
if (point.y === 0) {
|
||
|
flag += 32;
|
||
|
} else {
|
||
|
if (-0xFF <= point.y && point.y <= 0xFF) {
|
||
|
flag += 4; // the corresponding y-coordinate is 1 byte long
|
||
|
}
|
||
|
if (point.y > 0 && point.y <= 0xFF) {
|
||
|
flag += 32; // If y-Short Vector is set, this bit describes the sign of the value, with 1 equalling positive and 0 negative.
|
||
|
}
|
||
|
}
|
||
|
result.push(flag);
|
||
|
});
|
||
|
});
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
//repeating flags can be packed
|
||
|
function compactFlags(flags) {
|
||
|
var result = [];
|
||
|
var prevFlag = -1;
|
||
|
var firstRepeat = false;
|
||
|
|
||
|
_.forEach(flags, function (flag) {
|
||
|
if (prevFlag === flag) {
|
||
|
if (firstRepeat) {
|
||
|
result[result.length - 1] += 8; //current flag repeats previous one, need to set 3rd bit of previous flag and set 1 to the current one
|
||
|
result.push(1);
|
||
|
firstRepeat = false;
|
||
|
} else {
|
||
|
result[result.length - 1]++; //when flag is repeating second or more times, we need to increase the last flag value
|
||
|
}
|
||
|
} else {
|
||
|
firstRepeat = true;
|
||
|
prevFlag = flag;
|
||
|
result.push(flag);
|
||
|
}
|
||
|
});
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
function getCoords(glyph, coordName) {
|
||
|
var result = [];
|
||
|
|
||
|
_.forEach(glyph.ttfContours, function (contour) {
|
||
|
result.push.apply(result, _.map(contour, coordName));
|
||
|
});
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
function compactCoords(coords) {
|
||
|
return _.filter(coords, function (coord) {
|
||
|
return coord !== 0;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
//calculates length of glyph data in GLYF table
|
||
|
function glyphDataSize(glyph) {
|
||
|
// Ignore glyphs without outlines. These will get a length of zero in the “loca” table
|
||
|
if (!glyph.contours.length) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
var result = 12; //glyph fixed properties
|
||
|
|
||
|
result += glyph.contours.length * 2; //add contours
|
||
|
|
||
|
_.forEach(glyph.ttf_x, function (x) {
|
||
|
//add 1 or 2 bytes for each coordinate depending of its size
|
||
|
result += ((-0xFF <= x && x <= 0xFF)) ? 1 : 2;
|
||
|
});
|
||
|
|
||
|
_.forEach(glyph.ttf_y, function (y) {
|
||
|
//add 1 or 2 bytes for each coordinate depending of its size
|
||
|
result += ((-0xFF <= y && y <= 0xFF)) ? 1 : 2;
|
||
|
});
|
||
|
|
||
|
// Add flags length to glyph size.
|
||
|
result += glyph.ttf_flags.length;
|
||
|
|
||
|
if (result % 4 !== 0) { // glyph size must be divisible by 4.
|
||
|
result += 4 - result % 4;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
function tableSize(font) {
|
||
|
var result = 0;
|
||
|
|
||
|
_.forEach(font.glyphs, function (glyph) {
|
||
|
glyph.ttf_size = glyphDataSize(glyph);
|
||
|
result += glyph.ttf_size;
|
||
|
});
|
||
|
font.ttf_glyph_size = result; //sum of all glyph lengths
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
function createGlyfTable(font) {
|
||
|
|
||
|
_.forEach(font.glyphs, function (glyph) {
|
||
|
glyph.ttf_flags = getFlags(glyph);
|
||
|
glyph.ttf_flags = compactFlags(glyph.ttf_flags);
|
||
|
glyph.ttf_x = getCoords(glyph, 'x');
|
||
|
glyph.ttf_x = compactCoords(glyph.ttf_x);
|
||
|
glyph.ttf_y = getCoords(glyph, 'y');
|
||
|
glyph.ttf_y = compactCoords(glyph.ttf_y);
|
||
|
});
|
||
|
|
||
|
var buf = new ByteBuffer(tableSize(font));
|
||
|
|
||
|
_.forEach(font.glyphs, function (glyph) {
|
||
|
|
||
|
// Ignore glyphs without outlines. These will get a length of zero in the “loca” table
|
||
|
if (!glyph.contours.length) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var offset = buf.tell();
|
||
|
|
||
|
buf.writeInt16(glyph.contours.length); // numberOfContours
|
||
|
buf.writeInt16(glyph.xMin); // xMin
|
||
|
buf.writeInt16(glyph.yMin); // yMin
|
||
|
buf.writeInt16(glyph.xMax); // xMax
|
||
|
buf.writeInt16(glyph.yMax); // yMax
|
||
|
|
||
|
// Array of end points
|
||
|
var endPtsOfContours = -1;
|
||
|
|
||
|
var ttfContours = glyph.ttfContours;
|
||
|
|
||
|
_.forEach(ttfContours, function (contour) {
|
||
|
endPtsOfContours += contour.length;
|
||
|
buf.writeInt16(endPtsOfContours);
|
||
|
});
|
||
|
|
||
|
buf.writeInt16(0); // instructionLength, is not used here
|
||
|
|
||
|
// Array of flags
|
||
|
_.forEach(glyph.ttf_flags, function (flag) {
|
||
|
buf.writeInt8(flag);
|
||
|
});
|
||
|
|
||
|
// Array of X relative coordinates
|
||
|
_.forEach(glyph.ttf_x, function (x) {
|
||
|
if (-0xFF <= x && x <= 0xFF) {
|
||
|
buf.writeUint8(Math.abs(x));
|
||
|
} else {
|
||
|
buf.writeInt16(x);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Array of Y relative coordinates
|
||
|
_.forEach(glyph.ttf_y, function (y) {
|
||
|
if (-0xFF <= y && y <= 0xFF) {
|
||
|
buf.writeUint8(Math.abs(y));
|
||
|
} else {
|
||
|
buf.writeInt16(y);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
var tail = (buf.tell() - offset) % 4;
|
||
|
|
||
|
if (tail !== 0) { // glyph size must be divisible by 4.
|
||
|
for (; tail < 4; tail++) {
|
||
|
buf.writeUint8(0);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
module.exports = createGlyfTable;
|