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.
195 lines
5.1 KiB
195 lines
5.1 KiB
'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;
|
|
|