'
+ + '
'
+ '
'
+ '
${unicode}
${name}
'
+ '
';
@@ -35,7 +39,7 @@ define(
$('#font-import').click();
},
export: function() {
- $(this).attr('href', '#');
+ //$(this).attr('href', '#');
}
};
@@ -43,18 +47,23 @@ define(
function showGLYF(ttf) {
var unitsPerEm = ttf.head.unitsPerEm;
var descent = unitsPerEm + ttf.hhea.descent;
- var glyfStr = '';
- ttf.glyf.forEach(function(glyf) {
+ var glyfStr = '', d = '';
+ ttf.glyf.forEach(function(glyf, index) {
var g = {
+ index: index,
modify: glyf.modify,
unitsPerEm: unitsPerEm,
descent: descent,
unicode: (glyf.unicode || []).map(function(u) {
return '$' + u.toString(16).toUpperCase();
}).join(','),
- name: glyf.name,
- d: glyf.contours && glyf.contours.length ? 'd="'+ contours2svg(glyf.contours) +'"': ''
+ name: glyf.name
};
+
+ if (d = glyf2svg(glyf, ttf)) {
+ g.d = 'd="'+ d +'"';
+ }
+
glyfStr += string.format(GLYF_ITEM_TPL, g);
});
@@ -62,11 +71,16 @@ define(
}
// 加载ttf
- function loadTTF(file) {
+ function loadSFNT(file, type) {
program.loading.show();
var fileReader = new FileReader();
fileReader.onload = function(e) {
var buffer = e.target.result;
+
+ if (type == 'woff') {
+ buffer = woff2ttf(buffer, woffOptions);
+ }
+
var ttfReader = new TTFReader();
program.data.ttf = ttfReader.read(buffer);
showGLYF(program.data.ttf);
@@ -127,9 +141,9 @@ define(
function onUpFile(e) {
var file = e.target.files[0];
- if (program.action == 'open' && file.name.match(/\.ttf$/)) {
+ if (program.action == 'open' && file.name.match(/(\.ttf|\.woff)$/)) {
program.data.file = file.name;
- loadTTF(file);
+ loadSFNT(file, file.name.slice(file.name.lastIndexOf('.') + 1));
}
else if (program.action == 'svg' && file.name.match(/\.svg$/)) {
if (program.data.ttf) {
@@ -149,8 +163,9 @@ define(
var ttf = program.data.ttf;
if (ttf) {
var buffer = new TTFWriter().write(ttf);
- e.target.download = (ttf.name.fontFamily || 'export') + '.ttf';
- e.target.href = ttf2base64(buffer);
+ var target = $(e.target);
+ target.attr('download', (ttf.name.fontFamily || 'export') + '.ttf');
+ target.attr('href', ttf2base64(buffer));
}
}
diff --git a/src/ttf/svg2ttfobject.js b/src/ttf/svg2ttfobject.js
index fd4ad18..be2fa4a 100644
--- a/src/ttf/svg2ttfobject.js
+++ b/src/ttf/svg2ttfobject.js
@@ -76,7 +76,7 @@ define(
ttf.head.xMax = Math.round(ttf.head.xMax);
ttf.head.yMin = Math.round(ttf.head.yMin);
ttf.head.yMax = Math.round(ttf.head.yMax);
- ttf.head.unitsPerEm = Math.round(ttf.head.unitsPerEm || 1024);
+ ttf.head.unitsPerEm = ttf.head.unitsPerEm ? Math.round(ttf.head.unitsPerEm) : 0;
return ttf;
}
@@ -148,6 +148,14 @@ define(
}
}
+ // 如果没有定义unitsPerEm,可以用viewBox代替
+ if (!ttf.head.unitsPerEm && svgNode.getAttribute('viewBox')) {
+ var bound = svgNode.getAttribute('viewBox').split(' ');
+ if (bound.length == 4) {
+ ttf.head.unitsPerEm = +bound[2];
+ }
+ }
+
// 解析glyf
var d, unicode;
if (missingNode) {
diff --git a/src/ttf/ttf2woff.js b/src/ttf/ttf2woff.js
index e7ea048..2cdc775 100644
--- a/src/ttf/ttf2woff.js
+++ b/src/ttf/ttf2woff.js
@@ -19,7 +19,6 @@ define(
function(require) {
var Reader = require('./reader');
var Writer = require('./writer');
- var checkSum = require('./util/checkSum');
var string = require('common/string');
/**
@@ -134,6 +133,7 @@ define(
* @param {ArrayBuffer} ttfBuffer ttf缓冲数组
* @param {Object} options 选项
* @param {Object} options.metadata 字体相关的信息
+ * @param {Object} options.deflate 压缩相关函数
*
* @return {ArrayBuffer} woff格式byte流
*/
diff --git a/src/ttf/util/glyf2svg.js b/src/ttf/util/glyf2svg.js
index 38c6690..aacf7fd 100644
--- a/src/ttf/util/glyf2svg.js
+++ b/src/ttf/util/glyf2svg.js
@@ -14,8 +14,10 @@
define(
function(require) {
var contour2svg = require('./contour2svg');
+ var contours2svg = require('./contours2svg');
var pathAdjust = require('graphics/pathAdjust');
var matrixTransform = require('graphics/transform');
+ var lang = require('common/lang');
/**
* glyf转换svg
@@ -23,29 +25,25 @@ define(
* @param {Object} glyf 解析后的glyf结构
* @return {string} svg文本
*/
- function glyf2svg(glyf, options) {
- if(!glyf) {
- return null;
- }
- var pathArray = [];
- var contours = glyf.contours;
- var height = glyf.yMax;
- var x = options.x || 0;
- var y = height + (options.y || 0);
- var scale = options.scale || 1;
+ function glyf2svg(glyf, ttf) {
+ if(!glyf) {
+ return '';
+ }
+
+ var pathArray = [];
if (!glyf.compound) {
- for ( var i = 0, l = contours.length; i < l; i++) {
- pathAdjust(contours[i], 1, -1);
- pathAdjust(contours[i], scale, scale, x, y);
- pathArray.push(contour2svg(contours[i]));
+ if (glyf.contours && glyf.contours.length) {
+ pathArray.push(contours2svg(glyf.contours));
}
+
}
else {
var glyfs = glyf.glyfs;
glyfs.forEach(function(g) {
- var contours = g.glyf.contours;
+ var compound = ttf.glyf[g.glyphIndex];
+ var contours = lang.clone(compound.contours); // 这里需要进行matrix变换,需要复制一份
var transform = g.transform;
for ( var i = 0, l = contours.length; i < l; i++) {
matrixTransform(contours[i],
@@ -56,8 +54,6 @@ define(
transform.e,
transform.f
);
- pathAdjust(contours[i], 1, -1);
- pathAdjust(contours[i], scale, scale, x, y);
pathArray.push(contour2svg(contours[i]));
}
});
diff --git a/src/ttf/util/svg2contours.js b/src/ttf/util/svg2contours.js
index 5fdd8f7..844a5dc 100644
--- a/src/ttf/util/svg2contours.js
+++ b/src/ttf/util/svg2contours.js
@@ -71,9 +71,10 @@ define(
// 获取segments
var segments = [];
- var cmd, lastIndex, args, segReg = /\-?\d+(?:\.\d+)?\b/g;
+ var cmd, relative = false, c, r, lastIndex, args, segReg = /\-?\d+(?:\.\d+)?\b/g;
for (var i = 0, l = path.length;i < l; i++) {
- var c = path.charAt(i).toUpperCase();
+ c = path[i].toUpperCase();
+ r = c != path[i];
switch (c) {
case 'M':
if (i == 0) {
@@ -97,6 +98,7 @@ define(
args = path.slice(lastIndex, i);
segments.push({
cmd: cmd,
+ relative: relative,
args: args.match(segReg).map(function(d) {
return +d.trim();
})
@@ -104,6 +106,7 @@ define(
}
cmd = c;
+ relative = r;
lastIndex = i + 1;
break;
}
@@ -115,39 +118,86 @@ define(
var contours = [], contour = [], prevX = 0, prevY = 0, prevc1;
for (var i = 0, l = segments.length;i < l; i++) {
segment = segments[i];
+ cmd = segment.cmd;
+ relative = segment.relative;
- if (segment.cmd === 'Z') {
+ if (cmd === 'Z') {
contours.push(contour);
contour = [];
}
- else if (segment.cmd === 'M') {
- prevX = segment.args[0];
- prevY = segment.args[1];
+ else if (cmd === 'M') {
+ if (relative) {
+ prevX += segment.args[0];
+ prevY += segment.args[1];
+ }
+ else {
+ prevX = segment.args[0];
+ prevY = segment.args[1];
+ }
+
contour.push({
x: prevX,
y: prevY,
onCurve: true
});
}
- else if (segment.cmd === 'H') {
- prevX += segment.args[0];
+ else if (cmd === 'H') {
+
+ if (relative) {
+ prevX += segment.args[0];
+ }
+ else {
+ prevX = segment.args[0];
+ }
+
contour.push({
x: prevX,
y: prevY,
onCurve: true
});
}
- else if (segment.cmd === 'V') {
- prevY += segment.args[0];
+ else if (cmd === 'V') {
+
+ if (relative) {
+ prevY += segment.args[0];
+ }
+ else {
+ prevY = segment.args[0];
+ }
+
contour.push({
x: prevX,
y: prevY,
onCurve: true
});
}
- else if (segment.cmd === 'L') {
- prevX += segment.args[0];
- prevY += segment.args[1];
+ else if (cmd === 'L') {
+
+ // 这里可能会连续绘制,最后一个是终点
+ var q = 0, ql = segment.args.length - 2, px = 0, py = 0;
+
+ if (relative) {
+ px = prevX;
+ py = prevY;
+ }
+
+ for (; q < ql ; q += 2) {
+ contour.push({
+ x: px + segment.args[q],
+ y: py + segment.args[q + 1],
+ onCurve: true
+ });
+ }
+
+ if (relative) {
+ prevX += segment.args[ql];
+ prevY += segment.args[ql + 1];
+ }
+ else {
+ prevX = segment.args[ql];
+ prevY = segment.args[ql + 1];
+ }
+
contour.push({
x: prevX,
y: prevY,
@@ -155,40 +205,78 @@ define(
});
}
// 二次贝塞尔
- else if (segment.cmd === 'Q') {
+ else if (cmd === 'Q') {
// 这里可能会连续绘制,最后一个是终点
- var q = 0, ql = segment.args.length - 2;
- for (; q < ql ; q += 2) {
+ var q = 0, ql = segment.args.length, px = 0, py = 0;
+
+ if (relative) {
+ px = prevX;
+ py = prevY;
+ }
+
+ for (; q < ql ; q += 4) {
contour.push({
- x: prevX + segment.args[q],
- y: prevY + segment.args[q + 1]
+ x: px + segment.args[q],
+ y: py + segment.args[q + 1]
+ });
+ contour.push({
+ x: px + segment.args[q + 2],
+ y: py + segment.args[q + 3],
+ onCurve: true
});
}
- prevX += segment.args[ql];
- prevY += segment.args[ql + 1];
-
- contour.push({
- x: prevX,
- y: prevY,
- onCurve: true
- });
+ ql = segment.args.length - 2;
+ if (relative) {
+ prevX += segment.args[ql];
+ prevY += segment.args[ql + 1];
+ }
+ else {
+ prevX = segment.args[ql];
+ prevY = segment.args[ql + 1];
+ }
}
// 二次贝塞尔平滑
- else if (segment.cmd === 'T') {
+ else if (cmd === 'T') {
- var pc = contour[contour.length - 2];
-
// 这里需要移除上一个曲线的终点
- contour.pop();
+ var last = contour.pop();
+
+ var pc = contour[contour.length - 1];
contour.push({
- x: 2 * prevX - pc.x,
- y: 2 * prevY - pc.y
+ x: 2 * last.x - pc.x,
+ y: 2 * last.y - pc.y
});
- prevX += segment.args[0];
- prevY += segment.args[1];
+ var q = 0, ql = segment.args.length - 2, px = 0, py = 0;
+
+ if (relative) {
+ px = prevX;
+ py = prevY;
+ }
+
+ for (; q < ql ; q += 2) {
+ pc = contour[contour.length - 1];
+ last = {
+ x: px + segment.args[q],
+ y: py + segment.args[q + 1]
+ };
+ contour.push({
+ x: 2 * last.x - pc.x,
+ y: 2 * last.y - pc.y
+ });
+ }
+
+ if (relative) {
+ prevX += segment.args[ql];
+ prevY += segment.args[ql + 1];
+ }
+ else {
+ prevX = segment.args[ql];
+ prevY = segment.args[ql + 1];
+ }
+
contour.push({
x: prevX,
y: prevY,
@@ -197,21 +285,26 @@ define(
}
// 三次贝塞尔
- else if (segment.cmd === 'C') {
+ else if (cmd === 'C') {
// 这里可能会连续绘制,最后一个是终点
- var q = 0, ql = segment.args.length - 2;
+ var q = 0, ql = segment.args.length - 2, px = 0, py = 0;
var cubicList = [];
+ if (relative) {
+ px = prevX;
+ py = prevY;
+ }
+
for (; q < ql ; q += 4) {
var cubic = [];
var c1 = {
- x: prevX + segment.args[q],
- y: prevY + segment.args[q + 1]
+ x: px + segment.args[q],
+ y: py + segment.args[q + 1]
};
var c2 = {
- x: prevX + segment.args[q + 2],
- y: prevY + segment.args[q + 3]
+ x: px + segment.args[q + 2],
+ y: py + segment.args[q + 3]
};
// 计算中间点
@@ -236,29 +329,53 @@ define(
cubicList.push(cubic);
}
- prevX += segment.args[ql];
- prevY += segment.args[ql + 1];
+ if (relative) {
+ prevX += segment.args[ql];
+ prevY += segment.args[ql + 1];
+ }
+ else {
+ prevX = segment.args[ql];
+ prevY = segment.args[ql + 1];
+ }
cubicList[cubicList.length - 1].push({x: prevX, y: prevY});
cubic2Points(cubicList, contour);
prevc1 = cubicList[cubicList.length - 1][2];
}
// 三次贝塞尔平滑
- else if (segment.cmd === 'S') {
+ else if (cmd === 'S') {
+ // TODO 这里没有支持连续的情况,有时间再搞
+ if (segment.args.length > 4) {
+ throw 'not support svg "S" command continuous!'
+ }
+
// 这里需要移除上一个曲线的终点
var p1 = contour.pop();
var c1 = {
x: 2 * p1.x - prevc1.x,
y: 2 * p1.y - prevc1.y
};
+
+ var px = 0, py = 0;
+
+ if (relative) {
+ px = prevX;
+ py = prevY;
+ }
var c2 = {
- x: prevX + segment.args[0],
- y: prevX + segment.args[1]
+ x: px + segment.args[0],
+ y: py + segment.args[1]
};
- prevX += segment.args[2];
- prevY += segment.args[3];
+ if (relative) {
+ prevX += segment.args[2];
+ prevY += segment.args[3];
+ }
+ else {
+ prevX = segment.args[2];
+ prevY = segment.args[3];
+ }
var p2 = {
x: prevX,
@@ -269,7 +386,7 @@ define(
prevc1 = c2;
}
// 圆弧
- else if (segment.cmd === 'A') {
+ else if (cmd === 'A') {
throw 'not support arc';
}
}
diff --git a/src/ttf/woff2ttf.js b/src/ttf/woff2ttf.js
new file mode 100644
index 0000000..ab43d72
--- /dev/null
+++ b/src/ttf/woff2ttf.js
@@ -0,0 +1,122 @@
+/**
+ * @file woff2ttf.js
+ * @author mengke01
+ * @date
+ * @description
+ * woff转换ttf
+ */
+
+
+define(
+ function(require) {
+
+ var Reader = require('./reader');
+ var Writer = require('./writer');
+
+ /**
+ * 写空数据
+ *
+ * @param {Writer} writer writer对象
+ * @param {number} length 大小
+ * @return {Writer}
+ */
+ function writeEmpty(writer, length) {
+ while (length-- > 0) {
+ writer.writeUint8(0);
+ }
+ return writer;
+ }
+
+ /**
+ * woff格式转换成ttf字体格式
+ *
+ * @param {ArrayBuffer} woffBuffer ttf缓冲数组
+ * @param {Object} options 选项
+ * @param {Object} options.inflate 解压相关函数
+ *
+ * @return {ArrayBuffer} woff格式byte流
+ */
+ function woff2ttf(woffBuffer, options) {
+ options = options || {};
+
+ var reader = new Reader(woffBuffer);
+ var signature = reader.readUint32(0);
+ var flavor = reader.readUint32(4);
+
+ if (signature !== 0x774F4646 || flavor !== 0x10000) {
+ reader.dispose();
+ throw 'not a ttf sfnt format!';
+ }
+
+ var numTables = reader.readUint16(12);
+ var ttfSize = reader.readUint32(16);
+ var tableEntries = [];
+
+ // 读取woff表索引信息
+ for (var i = 0; i < numTables; ++i) {
+ reader.seek(44 + i * 20);
+ var tableEntry = {
+ tag: reader.readString(reader.offset, 4),
+ offset: reader.readUint32(),
+ compLength: reader.readUint32(),
+ length: reader.readUint32(),
+ checkSum: reader.readUint32()
+ };
+
+ // ttf 表数据
+ var deflateData = reader.readBytes(tableEntry.offset, tableEntry.compLength);
+ // 需要解压
+ if (deflateData.length < tableEntry.length) {
+ if (!options.inflate) {
+ reader.dispose();
+ throw 'no inflate function!';
+ }
+ tableEntry.data = options.inflate(deflateData);
+ }
+ else {
+ tableEntry.data = deflateData;
+ }
+ tableEntry.length = tableEntry.data.length;
+
+ tableEntries.push(tableEntry);
+ }
+
+
+ var writer = new Writer(new ArrayBuffer(ttfSize));
+ // 写头部
+ var entrySelector = Math.floor(Math.log(numTables)/Math.LN2);
+ var searchRange = Math.pow(2, entrySelector) * 16;
+ var rangeShift = numTables * 16 - searchRange;
+ writer.writeFixed(1);
+ writer.writeUint16(numTables);
+ writer.writeUint16(searchRange);
+ writer.writeUint16(entrySelector);
+ writer.writeUint16(rangeShift);
+
+ // 写ttf表索引
+ var tblOffset = 12 + 16 * tableEntries.length;
+ for (var i = 0, l = tableEntries.length; i < l; ++i) {
+ var tableEntry = tableEntries[i];
+ writer.writeString(tableEntry.tag);
+ writer.writeUint32(tableEntry.checkSum);
+ writer.writeUint32(tblOffset);
+ writer.writeUint32(tableEntry.length);
+ tblOffset += tableEntry.length + (tableEntry.length % 4 ? 4 - tableEntry.length % 4 : 0)
+ }
+
+ // 写ttf表数据
+ for (var i = 0, l = tableEntries.length; i < l; ++i) {
+ var tableEntry = tableEntries[i];
+ writer.writeBytes(tableEntry.data);
+ if (tableEntry.length % 4) {
+ writeEmpty(writer, 4 - tableEntry.length % 4);
+ }
+ }
+
+ return writer.getBuffer();
+ }
+
+
+ return woff2ttf;
+ }
+);
diff --git a/ttf.html b/ttf.html
index 80942d3..aae9d79 100644
--- a/ttf.html
+++ b/ttf.html
@@ -36,7 +36,11 @@