diff --git a/src/fonteditor/controller/actions.js b/src/fonteditor/controller/actions.js index b5f73ab..87f22d4 100644 --- a/src/fonteditor/controller/actions.js +++ b/src/fonteditor/controller/actions.js @@ -32,7 +32,8 @@ define( program.loading.hide(); if (program.ttfManager.get()) { program.ttfManager.merge(imported, { - scale: true + scale: true, + adjustGlyf: imported.from === 'svg' ? true : false }); } else { diff --git a/src/fonteditor/data/online-font.js b/src/fonteditor/data/online-font.js index 5d3da43..ece1297 100644 --- a/src/fonteditor/data/online-font.js +++ b/src/fonteditor/data/online-font.js @@ -40,6 +40,11 @@ define( url: 'http://vs-static.baidu.com/m-health/new-composite/20141223/asset/common/css/font/baiduHealth.woff', from: '百度健康图标' }, + { + name: 'fonteditor.woff', + url: 'http://vs-static.baidu.com/m-health/med-detail/201412301413/asset/common/css/fonts/fonteditor.woff', + from: '百度健康医药馆图标' + }, { name: 'i-edu.woff', url: 'http://vs-static.baidu.com/edu/m/asset/common/css/font/i-edu.woff', diff --git a/src/fonteditor/main.js b/src/fonteditor/main.js index 138740e..7fab106 100644 --- a/src/fonteditor/main.js +++ b/src/fonteditor/main.js @@ -47,7 +47,10 @@ define( type: ext, success: function (imported) { if (imported.glyf.length) { - program.ttfManager.merge(imported, {scale: true}); + program.ttfManager.merge(imported, { + scale: true, + adjustGlyf: imported.from === 'svg' ? true : false + }); } } }); diff --git a/src/fonteditor/widget/ttfmanager.js b/src/fonteditor/widget/ttfmanager.js index 80a55ae..1bac440 100644 --- a/src/fonteditor/widget/ttfmanager.js +++ b/src/fonteditor/widget/ttfmanager.js @@ -162,6 +162,7 @@ define( * @param {Object} imported ttfObject * @param {Object} options 参数选项 * @param {boolean} options.scale 是否自动缩放 + * @param {boolean} options.adjustGlyf 是否调整字形以适应边界 * * @return {this} */ diff --git a/src/ttf/svg2ttfobject.js b/src/ttf/svg2ttfobject.js index f801777..1cf976a 100644 --- a/src/ttf/svg2ttfobject.js +++ b/src/ttf/svg2ttfobject.js @@ -13,6 +13,7 @@ define( var svg2contours = require('./util/svg2contours'); var computeBoundingBox = require('graphics/computeBoundingBox'); var error = require('./error'); + var glyfAdjust = require('./util/glyfAdjust'); /** * 加载xml字符串 @@ -51,6 +52,39 @@ define( }; } + /** + * 根据边界获取unitsPerEm + * + * @param {number} xMin x最小值 + * @param {number} xMax x最大值 + * @param {number} yMin y最小值 + * @param {number} yMax y最大值 + * @return {number} + */ + function getUnitsPerEm(xMin, xMax, yMin, yMax) { + var seed = Math.ceil(Math.min(yMax - yMin, xMax - xMin)); + + if (!seed) { + return 1024; + } + + if (seed <= 128) { + return seed; + } + + // 获取合适的unitsPerEm + var unitsPerEm = 128; + while (unitsPerEm < 16384) { + + if (seed <= 1.2 * unitsPerEm) { + return unitsPerEm; + } + + unitsPerEm = unitsPerEm << 1; + } + + return 1024; + } /** * 对ttfObject进行处理,去除小数 @@ -60,58 +94,41 @@ define( */ function resolve(ttf) { - var scale = 1; - // 对于小尺寸svg进行合理的缩放 - if (ttf.head.unitsPerEm && ttf.head.unitsPerEm < 256) { - scale = 1024 / ttf.head.unitsPerEm; + // 如果是svg格式字体,则去小数 + if (ttf.from === 'svgfont' && ttf.head.unitsPerEm > 128) { + ttf.glyf.forEach(function (g) { + glyfAdjust(g); + }); + } + // 否则重新计算字形大小,缩放到1024的em + else { + var xMin = 16384; + var xMax = -16384; + var yMin = 16384; + var yMax = -16384; + + ttf.glyf.forEach(function (g) { + if (g.contours) { + var bound = computeBoundingBox.computePathBox.apply(null, g.contours); + if (bound) { + xMin = Math.min(xMin, bound.x); + xMax = Math.max(xMax, bound.x + bound.width); + yMin = Math.min(yMin, bound.y); + yMax = Math.max(yMax, bound.y + bound.height); + } + } + }); + + var unitsPerEm = getUnitsPerEm(xMin, xMax, yMin, yMax); + var scale = 1024 / unitsPerEm; + + ttf.glyf.forEach(function (g) { + glyfAdjust(g, scale, scale); + }); ttf.head.unitsPerEm = 1024; } - ttf.glyf.forEach(function (glyf) { - if (glyf.contours && glyf.contours.length) { - - var pointIterator = function (p) { - p.x = Math.round(p.x * scale); - p.y = Math.round(p.y * scale); - }; - var contourIterator = function (contour) { - contour.forEach(pointIterator); - }; - - glyf.contours.forEach(contourIterator); - - var bound = computeBoundingBox.computePathBox.apply(null, glyf.contours); - glyf.xMin = bound.x; - glyf.xMax = bound.x + bound.width; - glyf.yMin = bound.y; - glyf.yMax = bound.y + bound.height; - - if (glyf.leftSideBearing) { - glyf.leftSideBearing = Math.round(glyf.leftSideBearing * scale); - } - else { - glyf.leftSideBearing = glyf.xMin; - } - - if (glyf.advanceWidth) { - glyf.advanceWidth = Math.round(glyf.advanceWidth * scale); - } - else { - glyf.advanceWidth = glyf.xMax + Math.abs(glyf.xMin); - } - } - }); - - if (undefined !== ttf.head.xMin && undefined !== ttf.head.yMax) { - ttf.head.xMin = Math.round(ttf.head.xMin * scale); - ttf.head.xMax = Math.round(ttf.head.xMax * scale); - ttf.head.yMin = Math.round(ttf.head.yMin * scale); - ttf.head.yMax = Math.round(ttf.head.yMax * scale); - } - - ttf.head.unitsPerEm = ttf.head.unitsPerEm ? Math.round(ttf.head.unitsPerEm) : 1024; - return ttf; } @@ -132,10 +149,11 @@ define( ttf.metadata = string.decodeHTML(metaNode.textContent.trim()); } - // 解析font + // 解析font,如果有font节点说明是svg格式字体文件 if (fontNode) { ttf.id = fontNode.getAttribute('id') || ''; ttf.hhea.advanceWidthMax = +(fontNode.getAttribute('horiz-adv-x') || 0); + ttf.from = 'svgfont'; } if (fontFaceNode) { @@ -174,15 +192,6 @@ define( } } - // 如果没有定义unitsPerEm,可以用viewBox代替 - var svgNode = xmlDoc.getElementsByTagName('svg')[0]; - if (!ttf.head.unitsPerEm && svgNode.getAttribute('viewBox')) { - var bound = svgNode.getAttribute('viewBox').split(' '); - if (bound.length === 4) { - ttf.head.unitsPerEm = +bound[2]; - } - } - return ttf; } diff --git a/src/ttf/ttf.js b/src/ttf/ttf.js index 126014c..2ffa41e 100644 --- a/src/ttf/ttf.js +++ b/src/ttf/ttf.js @@ -18,6 +18,54 @@ define( var computeBoundingBox = require('graphics/computeBoundingBox'); var glyfAdjust = require('./util/glyfAdjust'); + + /** + * 缩放到EM框 + * + * @param {Array} glyfList glyf列表 + * @param {number} ascent 上升 + * @param {number} descent 下降 + * @param {number} ajdustToEmPadding 顶部和底部留白 + * @return {Array} glyfList + */ + function adjustToEmBox(glyfList, ascent, descent, ajdustToEmPadding) { + + glyfList.forEach(function (g) { + + if (g.contours && g.contours.length) { + var rightSideBearing = g.advanceWidth - g.xMax; + var bound = computeBoundingBox.computePath.apply(null, g.contours); + var scale = (ascent - descent - ajdustToEmPadding) / bound.height; + var center = (ascent + descent) / 2; + var yOffset = center - (bound.y + bound.height / 2) * scale; + + g.contours.forEach(function (contour) { + if (scale !== 1) { + pathAdjust(contour, scale, scale); + } + + pathAdjust(contour, 1, 1, 0, yOffset); + pathCeil(contour); + }); + + var box = computeBoundingBox.computePathBox.apply(null, g.contours); + + g.xMin = box.x; + g.xMax = box.x + box.width; + g.yMin = box.y; + g.yMax = box.y + box.height; + + g.leftSideBearing = g.xMin; + g.advanceWidth = g.xMax + rightSideBearing; + + } + + }); + + return glyfList; + } + + /** * 合并两个ttfObject,此处仅合并简单字形 * @@ -25,18 +73,13 @@ define( * @param {Object} imported ttfObject * @param {Object} options 参数选项 * @param {boolean} options.scale 是否自动缩放 + * @param {boolean} options.adjustGlyf 是否调整字形以适应边界 * * @return {Object} 合并后的ttfObject */ function merge(ttf, imported, options) { options = options || {}; - var scale = 1; - // 调整glyf对导入的轮廓进行缩放处理 - if (options.scale && imported.head.unitsPerEm && imported.head.unitsPerEm !== ttf.head.unitsPerEm) { - scale = ttf.head.unitsPerEm / imported.head.unitsPerEm; - } - var list = imported.glyf.filter(function (g, index) { // 简单轮廓 return g.contours && g.contours.length @@ -44,14 +87,37 @@ define( && g.name !== '.notdef' && g.name !== '.null' && g.name !== 'nonmarkingreturn'; }); - list.forEach(function (g) { - glyfAdjust(g, scale, scale); - ttf.glyf.push(g); - }); + // 调整字形以适应边界 + if (options.adjustGlyf) { + var ascent = ttf.hhea.ascent; + var descent = ttf.hhea.descent; + var ajdustToEmPadding = 0; + adjustToEmBox(list, ascent, descent, ajdustToEmPadding); + + list.forEach(function (g) { + ttf.glyf.push(g); + }); + } + // 根据unitsPerEm 进行缩放 + else if (options.scale) { + + var scale = 1; + + // 调整glyf对导入的轮廓进行缩放处理 + if (imported.head.unitsPerEm && imported.head.unitsPerEm !== ttf.head.unitsPerEm) { + scale = ttf.head.unitsPerEm / imported.head.unitsPerEm; + } + + list.forEach(function (g) { + glyfAdjust(g, scale, scale); + ttf.glyf.push(g); + }); + } return list; } + /** * ttf读取函数 * @@ -342,7 +408,6 @@ define( // 基线高度 if (undefined !== setting.verticalAlign) { - changed = true; var verticalAlign = setting.verticalAlign || 0; @@ -404,39 +469,11 @@ define( else if (setting.ajdustToEmBox) { changed = true; - - var dencent = this.ttf.hhea.descent; - var unitsPerEm = this.ttf.head.unitsPerEm; + var ascent = this.ttf.hhea.ascent; + var descent = this.ttf.hhea.descent; var ajdustToEmPadding = 2 * (setting.ajdustToEmPadding || 0); - glyfList.forEach(function (g) { - if (g.contours && g.contours.length) { - - var rightSideBearing = g.advanceWidth - g.xMax; - var bound = computeBoundingBox.computePath.apply(null, g.contours); - var scale = (unitsPerEm - ajdustToEmPadding) / bound.height; - - if (scale !== 1) { - var yOffset = (unitsPerEm / 2 + dencent) - (bound.y + bound.height / 2) * scale; - g.contours.forEach(function (contour) { - pathAdjust(contour, scale, scale); - pathAdjust(contour, 1, 1, 0, yOffset); - pathCeil(contour); - }); - - var box = computeBoundingBox.computePathBox.apply(null, g.contours); - - g.xMin = box.x; - g.xMax = box.x + box.width; - g.yMin = box.y; - g.yMax = box.y + box.height; - - g.leftSideBearing = g.xMin; - g.advanceWidth = g.xMax + rightSideBearing; - } - } - - }); + adjustToEmBox(glyfList, ascent, descent, ajdustToEmPadding); } return changed ? glyfList : []; diff --git a/src/ttf/util/glyfAdjust.js b/src/ttf/util/glyfAdjust.js index 1b00c8a..6f38864 100644 --- a/src/ttf/util/glyfAdjust.js +++ b/src/ttf/util/glyfAdjust.js @@ -14,6 +14,7 @@ define( var pathCeil = require('graphics/pathCeil'); var computeBoundingBox = require('graphics/computeBoundingBox'); + /* eslint-disable max-params */ /** * 简单字形的缩放和平移调整 * @@ -22,9 +23,11 @@ define( * @param {number} scaleY y缩放比例 * @param {number} offsetX x偏移 * @param {number} offsetY y偏移 + * @param {boolan} ceil 是否对字形设置取整,默认取整 + * * @return {Object} 调整后的glyf对象 */ - function glyfAdjust(g, scaleX, scaleY, offsetX, offsetY) { + function glyfAdjust(g, scaleX, scaleY, offsetX, offsetY, ceil) { scaleX = scaleX || 1; scaleY = scaleY || 1; @@ -35,7 +38,6 @@ define( if (scaleX !== 1 || scaleY !== 1) { g.contours.forEach(function (contour) { pathAdjust(contour, scaleX, scaleY); - pathCeil(contour); }); } @@ -44,6 +46,12 @@ define( pathAdjust(contour, 1, 1, offsetX, offsetY); }); } + + if (false !== ceil) { + g.contours.forEach(function (contour) { + pathCeil(contour); + }); + } } // 重新计算xmin,xmax,ymin,ymax @@ -94,6 +102,7 @@ define( return g; } + /* eslint-enable max-params */ return glyfAdjust; }