add ttf writer

This commit is contained in:
mkwiser 2014-09-28 00:28:02 +08:00
parent 00f87c74f8
commit 2258bba42a
11 changed files with 485 additions and 35 deletions

View File

@ -167,6 +167,10 @@ define(
tcmap.tables = subTables;
return tcmap;
},
write: function(writer, ttf) {
return writer;
}
}
);

View File

@ -45,6 +45,9 @@ define(
}
return writer;
},
size: function(ttf) {
return ttf.numTables * 16;
}
}
);

View File

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

View File

@ -49,6 +49,10 @@ define(
return hMetrics;
},
write: function(writer, ttf) {
return writer;
}
}
);

View File

@ -35,6 +35,10 @@ define(
}
return wordOffset;
},
write: function(writer, ttf) {
return writer;
}
}
);

View File

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

View File

@ -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);

View File

@ -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,

View File

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

View File

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

View File

@ -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);