add ttf writer
This commit is contained in:
parent
00f87c74f8
commit
2258bba42a
@ -167,6 +167,10 @@ define(
|
||||
tcmap.tables = subTables;
|
||||
|
||||
return tcmap;
|
||||
},
|
||||
|
||||
write: function(writer, ttf) {
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -45,6 +45,9 @@ define(
|
||||
}
|
||||
|
||||
return writer;
|
||||
},
|
||||
size: function(ttf) {
|
||||
return ttf.numTables * 16;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -4,14 +4,131 @@
|
||||
* @date
|
||||
* @description
|
||||
* glyf表
|
||||
*
|
||||
* modified from svg2ttf
|
||||
* https://github.com/fontello/svg2ttf
|
||||
*/
|
||||
|
||||
define(
|
||||
function(require) {
|
||||
|
||||
var glyFlag = require('../enum/glyFlag');
|
||||
var componentFlag = require('../enum/componentFlag');
|
||||
var table = require('./table');
|
||||
var TTFglyf = require('./ttfglyf');
|
||||
|
||||
/**
|
||||
* 获取glyf的大小
|
||||
*
|
||||
* @param {Object} glyf glyf对象
|
||||
* @return {number} 大小
|
||||
*/
|
||||
function sizeof(glyf, flags) {
|
||||
if (!glyf.contours.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//fixed header + instructions + endPtsOfContours
|
||||
var result = 10
|
||||
+ 2
|
||||
+ (glyf.instructions ? glyf.instructions.length : 0)
|
||||
+ glyf.contours.length * 2;
|
||||
|
||||
flags.forEach(function(flag) {
|
||||
result += flag & glyFlag.XSHORT ? 1 : 2;
|
||||
result += flag & glyFlag.YSHORT ? 1 : 2;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 复合图元size
|
||||
*
|
||||
* @param {Object} glyf glyf对象
|
||||
* @return {number} 大小
|
||||
*/
|
||||
function sizeofCompound(glyf) {
|
||||
var size = 10;
|
||||
glyf.glyfs.forEach(function(g) {
|
||||
// flags + glyfIndex
|
||||
size += 4;
|
||||
// a, b, c, d, e
|
||||
// xy values or points
|
||||
if(g.e >= -0xFF && g.e <= 0xFF && g.f >= 0xFF && g.f <= 0xFF) {
|
||||
size += 2;
|
||||
}
|
||||
else {
|
||||
size += 4;
|
||||
}
|
||||
|
||||
// scale
|
||||
if (g.a != 1 || g.d != 1) {
|
||||
size += g.a == g.d ? 2 : 4;
|
||||
}
|
||||
|
||||
// 01 , 10
|
||||
if (g.b || g.c) {
|
||||
size += 4;
|
||||
}
|
||||
});
|
||||
|
||||
if (glyf.instructions) {
|
||||
size += 2 + glyf.instructions.length;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取flats
|
||||
*
|
||||
* @param {Object} glyf glyf对象
|
||||
* @return {Array}
|
||||
*/
|
||||
function getFlags(glyf) {
|
||||
var flags = [];
|
||||
var prev = {};
|
||||
glyf.contours.forEach(function(contour) {
|
||||
contour.forEach(function(p) {
|
||||
var flag = p.onCurve ? glyFlag.ONCURVE : 0;
|
||||
|
||||
if (-0xFF <= p.x && p.x <= 0xFF) {
|
||||
flag += glyFlag.XSHORT;
|
||||
}
|
||||
|
||||
if (p.x == prev.x) {
|
||||
flag += glyFlag.XSAME;
|
||||
}
|
||||
|
||||
if (-0xFF <= p.y && p.y <= 0xFF) {
|
||||
flag += glyFlag.YSHORT;
|
||||
}
|
||||
|
||||
if (p.y == prev.y) {
|
||||
flag += glyFlag.YSAME;
|
||||
}
|
||||
|
||||
flags.push(flag);
|
||||
});
|
||||
});
|
||||
|
||||
// remove repeating flags
|
||||
var result = [], prev = -1;
|
||||
flags.forEach(function(flag) {
|
||||
if (prev == flag) {
|
||||
result[result.length - 1] |= glyFlag.REPEAT;
|
||||
}
|
||||
else {
|
||||
prev = flag;
|
||||
result.push(flag);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
var glyf = table.create(
|
||||
'glyf',
|
||||
[],
|
||||
@ -42,6 +159,188 @@ define(
|
||||
}
|
||||
}
|
||||
return glyf;
|
||||
},
|
||||
write: function(writer, ttf) {
|
||||
|
||||
ttf.glyf.forEach(function(glyf, index) {
|
||||
|
||||
// header
|
||||
writer.writeUint16(glyf.compound ? -1 : glyf.contours.length);
|
||||
writer.writeInt16(glyf.xMin);
|
||||
writer.writeInt16(glyf.yMin);
|
||||
writer.writeInt16(glyf.xMax);
|
||||
writer.writeInt16(glyf.yMax);
|
||||
|
||||
// 复合图元
|
||||
if (glyf.compound) {
|
||||
|
||||
for (var i = 0, l = glyf.glyfs; i < l; i++) {
|
||||
|
||||
var flags = componentFlag.ARGS_ARE_XY_VALUES; // xy values
|
||||
|
||||
// more components
|
||||
if (i < l - 1) {
|
||||
flags += componentFlag.MORE_COMPONENTS;
|
||||
}
|
||||
|
||||
var g = glyf.glyfs[i];
|
||||
|
||||
// instructions
|
||||
flags += glyf.instructions ? componentFlag.WE_HAVE_INSTRUCTIONS : 0;
|
||||
// use my metrics
|
||||
flags += g.useMyMetrics ? componentFlag.USE_MY_METRICS : 0;
|
||||
// overlap compound
|
||||
flags += g.overlapCompound ? componentFlag.OVERLAP_COMPOUND : 0;
|
||||
|
||||
var a = g.a;
|
||||
var b = g.b;
|
||||
var c = g.c;
|
||||
var d = g.d;
|
||||
var e = g.e;
|
||||
var f = g.f;
|
||||
|
||||
// xy values or points
|
||||
if(e < -0xFF || e > 0xFF || f < 0xFF || f > 0xFF) {
|
||||
flags += componentFlag.ARG_1_AND_2_ARE_WORDS;
|
||||
}
|
||||
|
||||
if (b || c) {
|
||||
flags += componentFlag.WE_HAVE_A_TWO_BY_TWO;
|
||||
}
|
||||
else {
|
||||
if (a != 1 || d != 1 && a == d) {
|
||||
flags += componentFlag.WE_HAVE_A_SCALE;
|
||||
}
|
||||
else if (a != 1 || d != 1) {
|
||||
flags += componentFlag.WE_HAVE_AN_X_AND_Y_SCALE;
|
||||
}
|
||||
}
|
||||
|
||||
writer.writeUint16(e);
|
||||
|
||||
if (componentFlag.ARG_1_AND_2_ARE_WORDS & flags) {
|
||||
writer.writeInt16(e);
|
||||
writer.writeInt16(f);
|
||||
|
||||
}
|
||||
else {
|
||||
writer.writeInt8(e);
|
||||
writer.writeInt8(f);
|
||||
}
|
||||
|
||||
if (componentFlag.WE_HAVE_A_SCALE & flags) {
|
||||
writer.writeInt16(Math.round(a * 16384));
|
||||
}
|
||||
else if (componentFlag.WE_HAVE_AN_X_AND_Y_SCALE & flags) {
|
||||
writer.writeInt16(Math.round(a * 16384));
|
||||
writer.writeInt16(Math.round(d * 16384));
|
||||
}
|
||||
else if (componentFlag.WE_HAVE_A_TWO_BY_TWO & flags) {
|
||||
writer.writeInt16(Math.round(a * 16384));
|
||||
writer.writeInt16(Math.round(b * 16384));
|
||||
writer.writeInt16(Math.round(c * 16384));
|
||||
writer.writeInt16(Math.round(d * 16384));
|
||||
}
|
||||
}
|
||||
|
||||
if (glyf.instructions) {
|
||||
var instructions = glyf.instructions;
|
||||
writer.writeUint16(instructions.length);
|
||||
for (var i = 0, l = instructions.length; i < l; i++) {
|
||||
writer.writeUint8(instructions[i] & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
var endPtsOfContours = -1;
|
||||
glyf.contours.forEach(function(contour) {
|
||||
endPtsOfContours += contour.length;
|
||||
writer.writeUint16(endPtsOfContours);
|
||||
});
|
||||
|
||||
// not support instruction
|
||||
if (glyf.instructions) {
|
||||
var instructions = glyf.instructions;
|
||||
writer.writeUint16(instructions.length);
|
||||
for (var i = 0, l = instructions.length; i < l; i++) {
|
||||
writer.writeUint8(instructions[i] & 0xFF);
|
||||
}
|
||||
}
|
||||
else {
|
||||
writer.writeUint16(0);
|
||||
}
|
||||
|
||||
// 获取暂存中的flags
|
||||
var flags = ttf.support.glyf[index].flags;
|
||||
for (var i = 0, l = flags.length; i < l; i++) {
|
||||
writer.writeUint8(flags[i]);
|
||||
}
|
||||
|
||||
// //write x coord
|
||||
// glyf.contours.forEach(function(contour) {
|
||||
// contour.forEach(function(p) {
|
||||
// if (-0xFF <= p.x && p.x <= 0xFF) {
|
||||
// writer.writeUint8(Math.abs(p.x));
|
||||
// }
|
||||
// else {
|
||||
// writer.writeInt16(p.x);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
// //write y coord
|
||||
// glyf.contours.forEach(function(contour) {
|
||||
// contour.forEach(function(p) {
|
||||
// if (-0xFF <= p.y && p.y <= 0xFF) {
|
||||
// writer.writeUint8(Math.abs(p.y));
|
||||
// }
|
||||
// else {
|
||||
// writer.writeInt16(p.y);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
}
|
||||
|
||||
// 4字节对齐
|
||||
var glyfSize = ttf.support.glyf[index].glyfSize;
|
||||
if (glyfSize % 4) {
|
||||
for (var i = 0, l = 4 - glyfSize % 4; i < l; i++) {
|
||||
writer.writeUint8(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return writer;
|
||||
},
|
||||
size: function(ttf) {
|
||||
|
||||
ttf.support.glyf = [];
|
||||
var tableSize = 0;
|
||||
ttf.glyf.forEach(function(glyf) {
|
||||
|
||||
var flags = glyf.compound ? [] : getFlags(glyf);
|
||||
var contours = glyf.compound ? sizeofCompound(glyf) : sizeof(glyf, flags);
|
||||
var size = contours + flags.length;
|
||||
|
||||
// 记录实际size, 用于4字节对齐
|
||||
var glyfSize = size;
|
||||
|
||||
// glyph size must be divisible by 4.
|
||||
if (size % 4) {
|
||||
size += 4 - size % 4;
|
||||
}
|
||||
|
||||
ttf.support.glyf.push({
|
||||
flags: flags,
|
||||
size: size,
|
||||
glyfSize: glyfSize
|
||||
});
|
||||
|
||||
tableSize += size;
|
||||
});
|
||||
|
||||
return tableSize;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -49,6 +49,10 @@ define(
|
||||
|
||||
return hMetrics;
|
||||
|
||||
},
|
||||
|
||||
write: function(writer, ttf) {
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -35,6 +35,10 @@ define(
|
||||
}
|
||||
|
||||
return wordOffset;
|
||||
},
|
||||
|
||||
write: function(writer, ttf) {
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -16,6 +16,7 @@ define(
|
||||
['version', struct.Fixed],
|
||||
['numGlyphs', struct.Uint16],
|
||||
['maxPoints', struct.Uint16],
|
||||
['maxContours', struct.Uint16],
|
||||
['maxCompositePoints', struct.Uint16],
|
||||
['maxCompositeContours', struct.Uint16],
|
||||
['maxZones', struct.Uint16],
|
||||
@ -25,10 +26,54 @@ define(
|
||||
['maxInstructionDefs', struct.Uint16],
|
||||
['maxStackElements', struct.Uint16],
|
||||
['maxSizeOfInstructions', struct.Uint16],
|
||||
['macStyle', struct.Uint16],
|
||||
['maxComponentElements', struct.Uint16],
|
||||
['maxComponentDepth', struct.Int16]
|
||||
]
|
||||
],
|
||||
{
|
||||
write: function(writer, ttf) {
|
||||
var maxPoints = 0, maxContours = 0, maxCompositePoints = 0,
|
||||
maxCompositeContours = 0;
|
||||
ttf.glyf.forEach(function(glyf) {
|
||||
// 复合图元
|
||||
if (glyf.compound) {
|
||||
maxCompositePoints++;
|
||||
maxCompositeContours += glyf.glyfs.length;
|
||||
}
|
||||
// 简单图元
|
||||
else {
|
||||
maxContours += glyf.contours.length;
|
||||
glyf.contours.forEach(function(contour) {
|
||||
maxPoints += contour.length;
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var mock = {
|
||||
maxp: {
|
||||
version: 1.0,
|
||||
numGlyphs: ttf.glyf.length,
|
||||
maxPoints: maxPoints,
|
||||
maxContours: maxContours,
|
||||
maxCompositePoints: maxCompositePoints,
|
||||
maxCompositeContours: maxCompositeContours,
|
||||
maxZones: 2,
|
||||
maxTwilightPoints: 0,
|
||||
// It is unclear how to calculate maxStorage, maxFunctionDefs and maxInstructionDefs.
|
||||
// These are magic constants now, with values exceeding values from FontForge
|
||||
// see svg2ttf on github
|
||||
maxStorage: 10,
|
||||
maxFunctionDefs: 10,
|
||||
maxStackElements: 255,
|
||||
maxSizeOfInstructions: 0,
|
||||
maxComponentElements: 0,
|
||||
maxComponentDepth: 0
|
||||
}
|
||||
};
|
||||
table.write.call(this, writer, mock);
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return maxp;
|
||||
|
@ -84,7 +84,7 @@ define(
|
||||
var table = ttf[this.name];
|
||||
|
||||
if (!table) {
|
||||
throw 'can not find ttf table' + this.name;
|
||||
throw 'can not find table:' + this.name;
|
||||
}
|
||||
|
||||
this.struct.forEach(function(item){
|
||||
@ -130,6 +130,63 @@ define(
|
||||
return writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取ttf表的size大小
|
||||
*
|
||||
* @param {string} name 表名
|
||||
* @param {Object} ttf ttf数据结构
|
||||
* @return {number} 表大小
|
||||
*/
|
||||
function size(ttf) {
|
||||
|
||||
if (!ttf[this.name]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var sz = 0;
|
||||
this.struct.forEach(function(item) {
|
||||
var type = item[1];
|
||||
switch (type) {
|
||||
case struct.Int8:
|
||||
case struct.Uint8:
|
||||
sz += 1;
|
||||
break;
|
||||
|
||||
case struct.Int16:
|
||||
case struct.Uint16:
|
||||
sz += 2;
|
||||
break;
|
||||
|
||||
case struct.Int32:
|
||||
case struct.Uint32:
|
||||
case struct.Fixed:
|
||||
sz += 4;
|
||||
break;
|
||||
|
||||
case struct.LongDateTime:
|
||||
sz += 8;
|
||||
break;
|
||||
|
||||
case struct.Byte:
|
||||
sz += item[2] || 0;
|
||||
break;
|
||||
|
||||
case struct.Char:
|
||||
sz += 1;
|
||||
break;
|
||||
|
||||
case struct.String:
|
||||
sz += item[2] || 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw 'unknown type:' + name + ':' + type;
|
||||
}
|
||||
});
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象的值
|
||||
*
|
||||
@ -146,6 +203,10 @@ define(
|
||||
}
|
||||
|
||||
var exports = {
|
||||
read: read,
|
||||
write: write,
|
||||
size: size,
|
||||
valueOf: valueOf,
|
||||
|
||||
/**
|
||||
* 创建一个表结构
|
||||
@ -157,13 +218,14 @@ define(
|
||||
*/
|
||||
create: function(name, struct, prototype) {
|
||||
function Table(offset) {
|
||||
this._name = name;
|
||||
this.name = name;
|
||||
this.struct = struct;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
Table.prototype.read = read;
|
||||
Table.prototype.write = write;
|
||||
Table.prototype.size = size;
|
||||
Table.prototype.valueOf = valueOf;
|
||||
|
||||
extend(Table.prototype, prototype);
|
||||
|
@ -205,10 +205,10 @@ define(
|
||||
arg2 = reader.readInt8();
|
||||
}
|
||||
|
||||
if (componentFlag.ROUND_XY_TO_GRID & flags) {
|
||||
arg1 = Math.round(arg1);
|
||||
arg2 = Math.round(arg2);
|
||||
}
|
||||
// if (componentFlag.ROUND_XY_TO_GRID & flags) {
|
||||
// arg1 = Math.round(arg1);
|
||||
// arg2 = Math.round(arg2);
|
||||
// }
|
||||
|
||||
if (componentFlag.WE_HAVE_A_SCALE & flags) {
|
||||
scaleX = reader.readInt16();
|
||||
@ -226,6 +226,8 @@ define(
|
||||
}
|
||||
|
||||
if (componentFlag.ARGS_ARE_XY_VALUES & flags) {
|
||||
glyf.useMyMetrics = !!flags & componentFlag.USE_MY_METRICS;
|
||||
glyf.overlapCompound = !!flags & componentFlag.OVERLAP_COMPOUND;
|
||||
|
||||
glyf.transform = {
|
||||
a: Math.round(10000 * scaleX / 16384) / 10000,
|
||||
|
@ -195,7 +195,7 @@ define(
|
||||
ttf = ttf || this.ttf;
|
||||
ttf.codes = readWindowsAllCodes(ttf);
|
||||
resolveGlyf.call(this, ttf);
|
||||
cleanTables.call(this, ttf);
|
||||
//cleanTables.call(this, ttf);
|
||||
return ttf;
|
||||
};
|
||||
|
||||
|
@ -12,9 +12,10 @@ define(
|
||||
|
||||
var Writer = require('./writer');
|
||||
var Directory = require('./table/directory');
|
||||
var supportTables = require('./table/support');
|
||||
|
||||
// 支持写的表, 注意表顺序
|
||||
var supportTables = [
|
||||
var tableList = [
|
||||
'OS/2',
|
||||
'cmap',
|
||||
'glyf',
|
||||
@ -33,18 +34,10 @@ define(
|
||||
*/
|
||||
function resolve() {
|
||||
var ttf = this.ttf;
|
||||
ttf.numTables = supportTables.length;
|
||||
ttf.entrySelector = Math.floor(Math.log(supportTables.length)/Math.LN2);
|
||||
ttf.numTables = tableList.length;
|
||||
ttf.entrySelector = Math.floor(Math.log(tableList.length)/Math.LN2);
|
||||
ttf.searchRange = Math.pow(2, ttf.entrySelector) * 16;
|
||||
ttf.rangeShift = supportTables.length * 16 - ttf.searchRange;
|
||||
ttf.tables = supportTables.map(function(table) {
|
||||
return {
|
||||
name: table,
|
||||
checkSum: 0,
|
||||
offset: 0,
|
||||
length: 10
|
||||
};
|
||||
});
|
||||
ttf.rangeShift = tableList.length * 16 - ttf.searchRange;
|
||||
}
|
||||
|
||||
|
||||
@ -53,22 +46,50 @@ define(
|
||||
*/
|
||||
function write() {
|
||||
var ttf = this.ttf;
|
||||
var buffer = {};
|
||||
|
||||
// 写头部
|
||||
var headSize = 20 + ttf.numTables * 16;
|
||||
var writer = new Writer(new ArrayBuffer(headSize));
|
||||
writer.writeFixed(ttf.version);
|
||||
writer.writeUint16(ttf.numTables);
|
||||
writer.writeUint16(ttf.searchRange);
|
||||
writer.writeUint16(ttf.entrySelector);
|
||||
writer.writeUint16(ttf.rangeShift);
|
||||
// 用来做写入缓存的对象,用完后删掉
|
||||
ttf.support = {};
|
||||
|
||||
!new Directory(writer.offset).write(writer, ttf);
|
||||
// 写入glyf
|
||||
var glyfTbl = new supportTables['glyf']();
|
||||
var glyfWriter = new Writer(new ArrayBuffer(glyfTbl.size(ttf)));
|
||||
glyfTbl.write(glyfWriter, ttf);
|
||||
|
||||
buffer.tables = writer.getBuffer();
|
||||
// 写入loca
|
||||
var locaTbl = new supportTables['glyf']();
|
||||
var locaWriter = new Writer(new ArrayBuffer(locaTbl.size(ttf)));
|
||||
locaTbl.write(locaWriter, ttf);
|
||||
|
||||
return buffer.tables;
|
||||
|
||||
|
||||
|
||||
// var ttfSize = 20 + tableList.length * 16;
|
||||
// ttf.tables = [];
|
||||
// tableList.forEach(function(tableName) {
|
||||
// var size = new supportTables[tableName]().size(ttf);
|
||||
// ttfSize += size;
|
||||
// ttf.tables.push({
|
||||
// name: tableName,
|
||||
// checkSum: 0,
|
||||
// offset: 0,
|
||||
// length: size
|
||||
// });
|
||||
// });
|
||||
|
||||
// var writer = new Writer(new ArrayBuffer(ttfSize));
|
||||
// writer.writeFixed(ttf.version);
|
||||
// writer.writeUint16(ttf.numTables);
|
||||
// writer.writeUint16(ttf.searchRange);
|
||||
// writer.writeUint16(ttf.entrySelector);
|
||||
// writer.writeUint16(ttf.rangeShift);
|
||||
// new Directory().write(writer, ttf);
|
||||
|
||||
// // 读取支持的表数据
|
||||
// tableList.forEach(function(tableName) {
|
||||
// new supportTables[tableName]().write(writer, ttf);
|
||||
// });
|
||||
|
||||
return new ArrayBuffer(10);
|
||||
}
|
||||
|
||||
|
||||
|
@ -246,8 +246,14 @@ define(
|
||||
offset = this.offset;
|
||||
}
|
||||
|
||||
if (typeof value.getTime === 'function') {
|
||||
value = value.getTime();
|
||||
}
|
||||
else {
|
||||
value = Date.parse(value);
|
||||
}
|
||||
var delta = -2077545600000; // new Date(1970, 1, 1).getTime() - new Date(1904, 1, 1).getTime();
|
||||
var time = Math.round((value.getTime() - delta) / 1000);
|
||||
var time = Math.round((value - delta) / 1000);
|
||||
this.writeUint32(0, offset);
|
||||
this.writeUint32(time, offset + 4);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user