From f253efa46407326e4b0154e5e28a7abe07c24dc2 Mon Sep 17 00:00:00 2001 From: kekee000 Date: Thu, 8 Jan 2015 03:18:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0svg=20circle,=20ellipse,=20po?= =?UTF-8?q?lygon,=20path=20=E5=AF=BC=E5=85=A5,=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8F=98=E6=8D=A2=E5=AF=BC=E5=85=A5=E8=BD=AE=E5=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- demo/js/svg2contours.js | 8 +- demo/js/svgnode2contours.js | 43 +++++++ demo/svgnode2contours.html | 35 ++++++ src/editor/group/rotateTransform.js | 4 +- src/graphics/getArc.js | 70 +---------- src/graphics/path/circle.js | 79 +++++++++++++ src/graphics/{pathLean.js => pathSkew.js} | 6 +- .../{transform.js => pathTransform.js} | 0 src/ttf/svg/contoursTransform.js | 60 ++++++++++ src/ttf/svg/oval2contour.js | 44 +++++++ src/ttf/svg/parseParams.js | 27 +++++ src/ttf/svg/parseTransform.js | 41 +++++++ .../svg2contours.js => svg/path2contours.js} | 17 ++- src/ttf/svg/polygon2contour.js | 41 +++++++ src/ttf/svg/rect2contour.js | 53 +++++++++ src/ttf/svg/svgnode2contours.js | 111 ++++++++++++++++++ src/ttf/svg2ttfobject.js | 80 +++++++++++-- src/ttf/util/glyf2svg.js | 4 +- test/icomoon.svg | 16 +++ test/svgnodes.svg | 47 ++++++++ 20 files changed, 687 insertions(+), 99 deletions(-) create mode 100644 demo/js/svgnode2contours.js create mode 100644 demo/svgnode2contours.html create mode 100644 src/graphics/path/circle.js rename src/graphics/{pathLean.js => pathSkew.js} (90%) rename src/graphics/{transform.js => pathTransform.js} (100%) create mode 100644 src/ttf/svg/contoursTransform.js create mode 100644 src/ttf/svg/oval2contour.js create mode 100644 src/ttf/svg/parseParams.js create mode 100644 src/ttf/svg/parseTransform.js rename src/ttf/{util/svg2contours.js => svg/path2contours.js} (98%) create mode 100644 src/ttf/svg/polygon2contour.js create mode 100644 src/ttf/svg/rect2contour.js create mode 100644 src/ttf/svg/svgnode2contours.js create mode 100644 test/icomoon.svg create mode 100644 test/svgnodes.svg diff --git a/demo/js/svg2contours.js b/demo/js/svg2contours.js index 352b6c5..3f52854 100644 --- a/demo/js/svg2contours.js +++ b/demo/js/svg2contours.js @@ -9,7 +9,7 @@ define( function(require) { - var svg2contours = require('ttf/util/svg2contours'); + var path2contours = require('ttf/svg/path2contours'); var contours2svg = require('ttf/util/contours2svg'); //var text = 'M890.88 748.512q28.672 0 52.736-10.752t41.984-29.184 28.16-43.52 10.24-53.76l0-620.544q0-28.672-10.24-53.76t-28.16-43.52-41.984-29.184-52.736-10.752l-689.152 0q-28.672 0-53.76 10.752t-43.52 29.184-29.184 43.52-10.752 53.76l0 620.544q0 28.672 10.752 53.76t29.184 43.52 43.52 29.184 53.76 10.752l689.152 0zM256 557.024q9.216 0 27.648-0.512t43.008-0.512l52.224 0 55.296 0q64.512 0 141.312 1.024l0 63.488-126.976 0 0 64.512-64.512 0 0-64.512-128 0 0-63.488zM377.856 451.552q-8.192 15.36-15.36 29.696-6.144 12.288-12.288 25.088t-9.216 20.992l-58.368-21.504 32.768-76.8zM254.976 55.264l101.376 143.36-66.56 26.624-98.304-145.408zM448.512 237.536l126.976 0 0 62.464-45.056 0-81.92 0 0 64.512q23.552 0 46.08-0.512t40.96-0.512l40.96 0 0 63.488-66.56 0q-19.456 0-40.96 1.024l68.608 65.536-68.608 32.768-20.48-98.304-257.024 0 0-63.488 35.84 0 47.104 0 53.248 0 56.32 0 0-64.512-128 0 0-62.464 40.96 0q18.432 0 40.96-0.512t46.08 0.512l0-129.024q0-16.384-2.048-27.136t-10.24-15.36-25.088-4.608-47.616 5.12q25.6-31.744 27.648-71.68 13.312 2.048 34.304 1.024t40.448 1.536 33.28 12.8 13.824 34.816l0 192.512zM568.32 126.944q-16.384 19.456-29.696 37.888-11.264 15.36-23.552 30.208t-18.432 23.04l-46.08-30.72 64.512-98.304zM896 432.096q-20.48 1.024-53.248 0.512t-63.488-1.536q-36.864-1.024-75.776-3.072l0 129.024q49.152 0 85.504 4.608t59.904 9.728q27.648 6.144 46.08 14.336-18.432 27.648-29.696 46.08-5.12 8.192-9.728 15.36t-7.68 11.264-3.584 4.608 1.536-4.608q-21.504-16.384-57.344-24.064t-69.632-10.752-57.344-3.072-21.504 0q1.024-16.384 1.536-44.032t0-77.824-1.024-129.024-0.512-196.608q0-13.312-3.072-33.28t-10.24-39.936-19.968-35.328-31.232-19.456q12.288-3.072 21.504-11.776t16.896-18.432 13.824-19.456 12.288-14.848q12.288 18.432 23.552 42.496t20.48 50.176 14.336 52.224 5.12 47.616l0 40.96 0 43.008 0 47.104 0 60.416 64.512 0 0-384 63.488 0 0 384q23.552-1.024 35.328-0.512t29.184-3.584l0 71.68z'; @@ -17,8 +17,8 @@ define( //var text = 'M890.88 748.512q28.672 0 52.736-10.752t41.984-29.184 28.16-43.52 10.24-53.76l0-620.544q0-28.672-10.24-53.76t-28.16-43.52-41.984-29.184-52.736-10.752l-689.152 0q-28.672 0-53.76 10.752t-43.52 29.184-29.184 43.52-10.752 53.76l0 620.544q0 28.672 10.752 53.76t29.184 43.52 43.52 29.184 53.76 10.752l689.152 0zM256 557.024q9.216 0 27.648-0.512t43.008-0.512l52.224 0 55.296 0q64.512 0 141.312 1.024l0 63.488-126.976 0 0 64.512-64.512 0 0-64.512-128 0 0-63.488zM377.856 451.552q-8.192 15.36-15.36 29.696-6.144 12.288-12.288 25.088t-9.216 20.992l-58.368-21.504 32.768-76.8zM254.976 55.264l101.376 143.36-66.56 26.624-98.304-145.408zM448.512 237.536l126.976 0 0 62.464-45.056 0-81.92 0 0 64.512q23.552 0 46.08-0.512t40.96-0.512l40.96 0 0 63.488-66.56 0q-19.456 0-40.96 1.024l68.608 65.536-68.608 32.768-20.48-98.304-257.024 0 0-63.488 35.84 0 47.104 0 53.248 0 56.32 0 0-64.512-128 0 0-62.464 40.96 0q18.432 0 40.96-0.512t46.08 0.512l0-129.024q0-16.384-2.048-27.136t-10.24-15.36-25.088-4.608-47.616 5.12q25.6-31.744 27.648-71.68 13.312 2.048 34.304 1.024t40.448 1.536 33.28 12.8 13.824 34.816l0 192.512zM568.32 126.944q-16.384 19.456-29.696 37.888-11.264 15.36-23.552 30.208t-18.432 23.04l-46.08-30.72 64.512-98.304zM896 432.096q-20.48 1.024-53.248 0.512t-63.488-1.536q-36.864-1.024-75.776-3.072l0 129.024q49.152 0 85.504 4.608t59.904 9.728q27.648 6.144 46.08 14.336-18.432 27.648-29.696 46.08-5.12 8.192-9.728 15.36t-7.68 11.264-3.584 4.608 1.536-4.608q-21.504-16.384-57.344-24.064t-69.632-10.752-57.344-3.072-21.504 0q1.024-16.384 1.536-44.032t0-77.824-1.024-129.024-0.512-196.608q0-13.312-3.072-33.28t-10.24-39.936-19.968-35.328-31.232-19.456q12.288-3.072 21.504-11.776t16.896-18.432 13.824-19.456 12.288-14.848q12.288 18.432 23.552 42.496t20.48 50.176 14.336 52.224 5.12 47.616l0 40.96 0 43.008 0 47.104 0 60.416 64.512 0 0-384 63.488 0 0 384q23.552-1.024 35.328-0.512t29.184-3.584l0 71.68z' //var text = 'M117 419 L335 419 Q337 419 337 417 L337 383 Q337 381 335 381 L300 381 Q373 237 373 235 L375 235 L448 382 Q446 382 412 418 L414 419 L557 419 Q559 419 559 417 L559 383 Q559 381 557 381 L521 381 L338 13 L337 13 Q335 14 152 381 L117 381 Q115 381 115 383 L115 417 Q115 419 117 419 Z' //var text = 'M269.5,102V36.3c0-8,10-15.2,18-15.2s16.6,7.2,16.6,15.2v45.9h22.5V36.3C326.5,17.4,309.799,0,291,0 c-30.1,0-42.6,17.4-42.6,36.3v67.2c-29.701,5.7-54.602,26.1-67.701,52.3 -11.4-23-32-41.4-56.8-49.5v-70C123.8,17.4,111.3,0,81.2,0 C62.4,0,45.6,17.4,45.6,36.3v45.9h22.5V36.3c0-8,8.601-15.2,16.601-15.2s18,7.2,18,15.2v65.9c-2.2-0.2-4.5-0.3-6.8-0.3 C43.8,101.9,0,145.7,0,197.8c0,52.1,43.8,94.5,95.9,94.5h169.399c52.102,0,95.9-42.4,95.9-94.5 C361.299,147.1,319.799,104.2,269.5,102z M265.299,269.8H95.9c-41.301,0-73.4-30.801-73.4-72c0-41.3,32.2-74.1,73.4-74.1 c41.3,0,74.8,32.899,74.8,74.1h19.701c0-41.3,33.6-74.1,74.799-74.1c41.301,0,73.4,32.899,73.4,74.1 C338.7,239.099,306.6,269.8,265.299,269.8z M90.6,326.3h180.5v-11.6H90.6V326.3z' - //var text = 'M216.501,199.847c-11.442,0-21.956,3.891-30.38,10.367l-86.92-52.15c0.436-2.668,0.722-5.389,0.722-8.178c0-2.793-0.286-5.509-0.722-8.177l86.92-52.153c8.424,6.478,18.938,10.368,30.38,10.368c27.589,0,49.962-22.372,49.962-49.962 C266.463,22.372,244.09,0,216.501,0c-27.59,0-49.961,22.372-49.961,49.961c0,2.792,0.286,5.511,0.728,8.178L80.349,110.29 c-8.425-6.476-18.941-10.367-30.387-10.367C22.372,99.923,0,122.295,0,149.886c0,27.59,22.372,49.961,49.962,49.961 c11.446,0,21.962-3.891,30.387-10.365l86.918,52.15c-0.441,2.666-0.728,5.383-0.728,8.178c0,27.588,22.371,49.961,49.961,49.961 c27.589,0,49.962-22.373,49.962-49.961C266.463,222.22,244.09,199.847,216.501,199.847Z' - var text = 'M300,200 h-150 a150,150 0 1,0 150,-150 z'; + var text = 'M216.501,199.847c-11.442,0-21.956,3.891-30.38,10.367l-86.92-52.15c0.436-2.668,0.722-5.389,0.722-8.178c0-2.793-0.286-5.509-0.722-8.177l86.92-52.153c8.424,6.478,18.938,10.368,30.38,10.368c27.589,0,49.962-22.372,49.962-49.962 C266.463,22.372,244.09,0,216.501,0c-27.59,0-49.961,22.372-49.961,49.961c0,2.792,0.286,5.511,0.728,8.178L80.349,110.29 c-8.425-6.476-18.941-10.367-30.387-10.367C22.372,99.923,0,122.295,0,149.886c0,27.59,22.372,49.961,49.962,49.961 c11.446,0,21.962-3.891,30.387-10.365l86.918,52.15c-0.441,2.666-0.728,5.383-0.728,8.178c0,27.588,22.371,49.961,49.961,49.961 c27.589,0,49.962-22.373,49.962-49.961C266.463,222.22,244.09,199.847,216.501,199.847Z' + //var text = 'M300,200 h-150 a150,150 0 1,0 150,-150 z'; var entry = { /** @@ -26,7 +26,7 @@ define( */ init: function () { - var contours = svg2contours(text); + var contours = path2contours(text); var path = contours2svg(contours); $('#path').attr('d', path); diff --git a/demo/js/svgnode2contours.js b/demo/js/svgnode2contours.js new file mode 100644 index 0000000..4929137 --- /dev/null +++ b/demo/js/svgnode2contours.js @@ -0,0 +1,43 @@ +/** + * @file svgnode2contours.js + * @author mengke01 + * @date + * @description + * svg转ttfobject + */ + +define( + function(require) { + var ajaxFile = require('common/ajaxFile'); + var svgnode2contours = require('ttf/svg/svgnode2contours'); + var contours2svg = require('ttf/util/contours2svg'); + + var entry = { + + /** + * 初始化 + */ + init: function () { + + ajaxFile({ + type: 'xml', + url: '../test/svgnodes.svg', + onSuccess: function(xml) { + + var contours = svgnode2contours(xml.getElementsByTagName('*')); + var path = contours2svg(contours); + + $('#path').attr('d', path); + }, + onError: function() { + console.error('error read file'); + } + }); + } + }; + + entry.init(); + + return entry; + } +); \ No newline at end of file diff --git a/demo/svgnode2contours.html b/demo/svgnode2contours.html new file mode 100644 index 0000000..54f4795 --- /dev/null +++ b/demo/svgnode2contours.html @@ -0,0 +1,35 @@ + + + + + svg2contours test + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/editor/group/rotateTransform.js b/src/editor/group/rotateTransform.js index ee850dd..52e4ffb 100644 --- a/src/editor/group/rotateTransform.js +++ b/src/editor/group/rotateTransform.js @@ -12,7 +12,7 @@ define( var getRotateMatrix = require('./getRotateMatrix'); var pathRotate = require('graphics/pathRotate'); - var pathLean = require('graphics/pathLean'); + var pathSkew = require('graphics/pathSkew'); var lang = require('common/lang'); /** @@ -25,7 +25,7 @@ define( var matrix = getRotateMatrix(point.pos, this.bound, camera); - var transformer = point.pos <= 4 ? pathRotate : pathLean; + var transformer = point.pos <= 4 ? pathRotate : pathSkew; // 更新shape var shapes = this.shapes; diff --git a/src/graphics/getArc.js b/src/graphics/getArc.js index acf91b5..dabcd90 100644 --- a/src/graphics/getArc.js +++ b/src/graphics/getArc.js @@ -22,74 +22,8 @@ define( var deInterpolate = pathUtil.deInterpolate; var getSegmentPathJoint = require('./join/getSegmentPathJoint'); var lang = require('common/lang'); + var circlePath = require('./path/circle'); - // 圆路径,逆时针 - var PATH_CIRCLE = [ - { - 'x': 582, - 'y': 0 - }, - { - 'x': 758, - 'y': 75 - }, - { - 'x': 890, - 'y': 208 - }, - { - 'x': 965, - 'y': 384 - }, - { - 'x': 965, - 'y': 583 - }, - { - 'x': 890, - 'y': 760 - }, - { - 'x': 758, - 'y': 891 - }, - { - 'x': 582, - 'y': 966 - }, - { - 'x': 383, - 'y': 966 - }, - { - 'x': 207, - 'y': 891 - }, - { - 'x': 75, - 'y': 760 - }, - { - 'x': 0, - 'y': 583 - }, - { - 'x': 0, - 'y': 384 - }, - { - 'x': 75, - 'y': 208 - }, - { - 'x': 207, - 'y': 75 - }, - { - 'x': 383, - 'y': 0 - } - ]; /** * 获取椭圆形状 @@ -101,7 +35,7 @@ define( * @return {Array} 椭圆形状 */ function getOval(rx, ry, angle, center) { - var path = lang.clone(PATH_CIRCLE); + var path = lang.clone(circlePath); var bound = computeBoundingBox.computePath(path); var scaleX = rx / bound.width; diff --git a/src/graphics/path/circle.js b/src/graphics/path/circle.js new file mode 100644 index 0000000..e0361fc --- /dev/null +++ b/src/graphics/path/circle.js @@ -0,0 +1,79 @@ +/** + * @file circle.js + * @author mengke01 + * @description + * 圆路径集合,逆时针 + */ + + +define( + function (require) { + + return [ + { + 'x': 582, + 'y': 0 + }, + { + 'x': 758, + 'y': 75 + }, + { + 'x': 890, + 'y': 208 + }, + { + 'x': 965, + 'y': 384 + }, + { + 'x': 965, + 'y': 583 + }, + { + 'x': 890, + 'y': 760 + }, + { + 'x': 758, + 'y': 891 + }, + { + 'x': 582, + 'y': 966 + }, + { + 'x': 383, + 'y': 966 + }, + { + 'x': 207, + 'y': 891 + }, + { + 'x': 75, + 'y': 760 + }, + { + 'x': 0, + 'y': 583 + }, + { + 'x': 0, + 'y': 384 + }, + { + 'x': 75, + 'y': 208 + }, + { + 'x': 207, + 'y': 75 + }, + { + 'x': 383, + 'y': 0 + } + ]; + } +); diff --git a/src/graphics/pathLean.js b/src/graphics/pathSkew.js similarity index 90% rename from src/graphics/pathLean.js rename to src/graphics/pathSkew.js index 6782e92..3cdd555 100644 --- a/src/graphics/pathLean.js +++ b/src/graphics/pathSkew.js @@ -1,5 +1,5 @@ /** - * @file pathLean.js + * @file pathSkew.js * @author mengke01 * @date * @description @@ -20,7 +20,7 @@ define( * * @return {Object} contour 坐标点 */ - function pathLean(contour, angle, offsetX, offsetY) { + function pathSkew(contour, angle, offsetX, offsetY) { angle = angle === undefined ? 0 : angle; var x = offsetX || 0; var tan = Math.tan(angle); @@ -46,6 +46,6 @@ define( return contour; } - return pathLean; + return pathSkew; } ); diff --git a/src/graphics/transform.js b/src/graphics/pathTransform.js similarity index 100% rename from src/graphics/transform.js rename to src/graphics/pathTransform.js diff --git a/src/ttf/svg/contoursTransform.js b/src/ttf/svg/contoursTransform.js new file mode 100644 index 0000000..30a425d --- /dev/null +++ b/src/ttf/svg/contoursTransform.js @@ -0,0 +1,60 @@ +/** + * @file contoursTransform.js + * @author mengke01 + * @description + * 变换轮廓 + */ + + +define( + function (require) { + var pathAdjust = require('graphics/pathAdjust'); + var pathRotate = require('graphics/pathRotate'); + var pathTransform = require('graphics/pathTransform'); + + /** + * 轮廓变换 + * + * @param {Array} contours 轮廓集合 + * @param {Array} transforms 变换指令集合 + * transforms = [{ + * name: 'scale' + * params: [3,4] + * }] + */ + function contoursTransform(contours, transforms) { + if (!contours || !contours.length || !transforms || !transforms.length) { + return contours; + } + + contours.forEach(function (p) { + for (var i = 0, l = transforms.length; i < l; i++) { + var transform = transforms[i]; + var params = transform.params; + switch (transform.name) { + case 'translate': + pathAdjust(p, 1, 1, params[0], params[1]); + break; + case 'scale': + pathAdjust(p, params[0], params[1]); + break; + case 'matrix': + pathTransform(p, params[0], params[1], params[2], params[3], params[4], params[5]); + break; + } + } + }); + + // var bound = computeBoundingBox.computePath.apply(null, contours); + // var x = bound.x; + // var y = bound.y; + // var w = bound.width; + // var h = bound.height; + + return contours; + } + + + return contoursTransform; + } +); diff --git a/src/ttf/svg/oval2contour.js b/src/ttf/svg/oval2contour.js new file mode 100644 index 0000000..b34beea --- /dev/null +++ b/src/ttf/svg/oval2contour.js @@ -0,0 +1,44 @@ +/** + * @file oval2contour.js + * @author mengke01 + * @description + * 椭圆转换成轮廓 + */ + + +define( + function (require) { + var computeBoundingBox = require('graphics/computeBoundingBox'); + var pathAdjust = require('graphics/pathAdjust'); + var circlePath = require('graphics/path/circle'); + var lang = require('common/lang'); + + /** + * 椭圆转换成轮廓 + * + * @param {number} cx cx + * @param {number} cy cy + * @param {number} rx rx + * @param {number} ry ry + * @return {Array} contours + */ + function oval2contour(cx, cy, rx, ry) { + + if (undefined === ry) { + ry = rx; + } + + var bound = computeBoundingBox.computePath(circlePath); + var scaleX = (+rx) * 2 / bound.width; + var scaleY = (+ry) * 2 / bound.height; + var centerX = bound.width * scaleX / 2; + var centerY = bound.height * scaleY / 2; + var path = lang.clone(circlePath); + pathAdjust(path, scaleX, scaleY); + pathAdjust(path, 1, 1, +cx - centerX, +cy - centerY); + return path; + } + + return oval2contour; + } +); diff --git a/src/ttf/svg/parseParams.js b/src/ttf/svg/parseParams.js new file mode 100644 index 0000000..b25a9fa --- /dev/null +++ b/src/ttf/svg/parseParams.js @@ -0,0 +1,27 @@ +/** + * @file parseParams.js + * @author mengke01 + * @description + * 解析参数集合 + */ + + +define( + function (require) { + + var SEGMENT_REGEX = /\-?\d+(?:\.\d+)?\b/g; + + function getSegment(d) { + return +d.trim(); + } + + return function (str) { + + if (!str) { + return []; + } + var matchs = str.match(SEGMENT_REGEX); + return matchs ? matchs.map(getSegment) : []; + }; + } +); diff --git a/src/ttf/svg/parseTransform.js b/src/ttf/svg/parseTransform.js new file mode 100644 index 0000000..bb6cd7e --- /dev/null +++ b/src/ttf/svg/parseTransform.js @@ -0,0 +1,41 @@ +/** + * @file parseTransform.js + * @author mengke01 + * @description + * 解析transform参数 + */ + + +define( + function (require) { + var parseParams = require('./parseParams'); + + var TRANSFORM_REGEX = /(\w+)\s*\(([\d-.,\s]*)\)/g; + + /** + * 解析transform参数 + * + * @param {string} str 参数字符串 + * @return {[type]} [return description] + */ + function parseTransform(str) { + + if (!str) { + return false; + } + + TRANSFORM_REGEX.lastIndex = 0; + var transforms = []; + var match; + while (match = TRANSFORM_REGEX.exec(str)) { + transforms.push({ + name: match[1], + params: parseParams(match[2]) + }); + } + return transforms; + } + + return parseTransform; + } +); diff --git a/src/ttf/util/svg2contours.js b/src/ttf/svg/path2contours.js similarity index 98% rename from src/ttf/util/svg2contours.js rename to src/ttf/svg/path2contours.js index d58faef..c8fa9f9 100644 --- a/src/ttf/util/svg2contours.js +++ b/src/ttf/svg/path2contours.js @@ -1,5 +1,5 @@ /** - * @file svg2contours.js + * @file path2contours.js * @author mengke01 * @date * @description @@ -12,6 +12,7 @@ define( var bezierCubic2Q2 = require('math/bezierCubic2Q2'); var getArc = require('graphics/getArc'); + var parseParams = require('./parseParams'); /** * 三次贝塞尔曲线列表,转二次贝塞尔曲线列表 @@ -445,15 +446,16 @@ define( return contours; } - /* eslint-enable fecs-max-statements, max-depth */ + + /* eslint-enable fecs-max-statements, max-depth */ /** * svg path 转 contours * * @param {string} path path对象 * @return {Array} contours */ - function svg2contours(path) { + function path2contours(path) { if (!path || !path.length) { return null; @@ -470,17 +472,12 @@ define( path += 'Z'; } - var argsMap = function (d) { - return +d.trim(); - }; - // 获取segments var segments = []; var cmd; var relative = false; var lastIndex; var args; - var segReg = /\-?\d+(?:\.\d+)?\b/g; for (var i = 0, l = path.length; i < l; i++) { var c = path[i].toUpperCase(); @@ -511,7 +508,7 @@ define( segments.push({ cmd: cmd, relative: relative, - args: args.match(segReg).map(argsMap) + args: parseParams(args) }); } @@ -530,6 +527,6 @@ define( - return svg2contours; + return path2contours; } ); diff --git a/src/ttf/svg/polygon2contour.js b/src/ttf/svg/polygon2contour.js new file mode 100644 index 0000000..d822703 --- /dev/null +++ b/src/ttf/svg/polygon2contour.js @@ -0,0 +1,41 @@ +/** + * @file polygon2contour.js + * @author mengke01 + * @description + * 多边形转换成轮廓 + */ + + +define( + function (require) { + + var parseParams = require('./parseParams'); + + /** + * 多边形点转换成轮廓 + * + * @param {Array} points 点集合 + * @return {Array} contours + */ + function polygon2contour(points) { + + if (!points || !points.length) { + return null; + } + + var contours = []; + var segments = parseParams(points); + for (var i = 0, l = segments.length; i < l; i += 2) { + contours.push({ + x: segments[i], + y: segments[i + 1], + onCurve: true + }); + } + + return contours; + } + + return polygon2contour; + } +); diff --git a/src/ttf/svg/rect2contour.js b/src/ttf/svg/rect2contour.js new file mode 100644 index 0000000..f9277bd --- /dev/null +++ b/src/ttf/svg/rect2contour.js @@ -0,0 +1,53 @@ +/** + * @file rect2contour.js + * @author mengke01 + * @description + * 矩形转换成轮廓 + */ + + +define( + function (require) { + + /** + * 矩形转换成轮廓 + * + * @param {number} x x + * @param {number} y y + * @param {number} width width + * @param {number} height height + * @return {Array} contours + */ + function rect2contour(x, y, width, height) { + x = +x; + y = +y; + width = +width; + height = +height; + + return [ + { + x: x, + y: y, + onCurve: true + }, + { + x: x + width, + y: y, + onCurve: true + }, + { + x: x + width, + y: y + height, + onCurve: true + }, + { + x: x, + y: y + height, + onCurve: true + } + ]; + } + + return rect2contour; + } +); diff --git a/src/ttf/svg/svgnode2contours.js b/src/ttf/svg/svgnode2contours.js new file mode 100644 index 0000000..ff65079 --- /dev/null +++ b/src/ttf/svg/svgnode2contours.js @@ -0,0 +1,111 @@ +/** + * @file svgnode2contours.js + * @author mengke01 + * @description + * svg节点转字形轮廓 + */ + + +define( + function (require) { + var path2contours = require('./path2contours'); + var oval2contour = require('./oval2contour'); + var polygon2contour = require('./polygon2contour'); + var rect2contour = require('./rect2contour'); + var computeBoundingBox = require('graphics/computeBoundingBox'); + var pathAdjust = require('graphics/pathAdjust'); + var parseTransform = require('./parseTransform'); + var contoursTransform = require('./contoursTransform'); + + // 支持的解析器集合 + var support = { + + path: { + parse: path2contours, // 解析器 + params: ['d'], // 参数列表 + contours: true // 是否是多个轮廓 + }, + + circle: { + parse: oval2contour, + params: ['cx', 'cy', 'r'] + }, + + ellipse: { + parse: oval2contour, + params: ['cx', 'cy', 'rx', 'ry'] + }, + + rect: { + parse: rect2contour, + params: ['x', 'y', 'width', 'height'] + }, + + polygon: { + parse: polygon2contour, + params: ['points'] + } + }; + + /** + * svg节点转字形轮廓 + * + * @param {Array} xmlNodes xml节点集合 + * @return {Array|false} contours + */ + function svgnode2contours(xmlNodes) { + + var i; + var length; + + // 解析支持的指令集合 + var parsedSegments = []; + if (xmlNodes.length) { + for (i = 0, length = xmlNodes.length; i < length; i++) { + var node = xmlNodes[i]; + var name = node.tagName; + if (support[name]) { + var supportParams = support[name].params; + var params = []; + for (var j = 0, attr; attr = supportParams[j++];) { + params.push(node.getAttribute(attr)); + } + + var segment = { + name: name, + params: params, + transform: parseTransform(node.getAttribute('transform')) + }; + parsedSegments.push(segment); + } + } + } + + if (parsedSegments.length) { + var result = []; + for (i = 0, length = parsedSegments.length; i < length; i++) { + var segment = parsedSegments[i]; + var parser = support[segment.name]; + var contour = parser.parse.apply(null, segment.params); + if (contour && contour.length) { + var contours = parser.contours ? contour : [contour]; + + // 如果有变换则应用变换规则 + if (segment.transform) { + contours = contoursTransform(contours, segment.transform); + } + + contours.forEach(function(p) { + result.push(p); + }); + } + } + return result; + } + + return false; + } + + return svgnode2contours; + } +); diff --git a/src/ttf/svg2ttfobject.js b/src/ttf/svg2ttfobject.js index 1cf976a..0723222 100644 --- a/src/ttf/svg2ttfobject.js +++ b/src/ttf/svg2ttfobject.js @@ -10,10 +10,12 @@ define( function (require) { var string = require('common/string'); - var svg2contours = require('./util/svg2contours'); + var path2contours = require('./svg/path2contours'); + var svgnode2contours = require('./svg/svgnode2contours'); + var contoursTransform = require('./svg/contoursTransform'); var computeBoundingBox = require('graphics/computeBoundingBox'); - var error = require('./error'); var glyfAdjust = require('./util/glyfAdjust'); + var error = require('./error'); /** * 加载xml字符串 @@ -220,7 +222,7 @@ define( } if ((d = missingNode.getAttribute('d'))) { - missing.contours = svg2contours(d); + missing.contours = path2contours(d); } ttf.glyf.push(missing); @@ -228,11 +230,6 @@ define( var glyfNodes = xmlDoc.getElementsByTagName('glyph'); - // path 对象也支持 - if (!glyfNodes.length) { - glyfNodes = xmlDoc.getElementsByTagName('path'); - } - if (glyfNodes.length) { // map unicode @@ -256,7 +253,7 @@ define( } if ((d = node.getAttribute('d'))) { - glyf.contours = svg2contours(d); + glyf.contours = path2contours(d); } ttf.glyf.push(glyf); } @@ -265,6 +262,62 @@ define( return ttf; } + function mirrorContours(contours) { + // 这里为了使ai等工具里面的字形方便导入,对svg做了反向处理 + var bound = computeBoundingBox.computePathBox.apply(null, contours); + contours = contoursTransform(contours, [ + { + name: 'scale', + params: [1, -1] + }, + { + name: 'translate', + params: [1, bound.height] + } + ]); + return contours; + } + + /** + * 解析字体信息相关节点 + * + * @param {XMLDocument} xmlDoc XML文档对象 + * @param {Object} ttf ttf对象 + * @return {Object} ttf对象 + */ + function parsePath(xmlDoc, ttf) { + + // 单个path组成一个glfy字形 + var pathNodes = xmlDoc.getElementsByTagName('path'); + if (pathNodes.length) { + for (var i = 0, length = pathNodes.length; i < length; i++) { + var node = pathNodes[i]; + var glyf = { + name: node.getAttribute('name') || '' + }; + var contours = svgnode2contours([node]); + glyf.contours = mirrorContours(contours); + ttf.glyf.push(glyf); + } + } + + // 其他svg指令组成一个glyf字形 + var contours = svgnode2contours( + Array.prototype.slice.call(xmlDoc.getElementsByTagName('*')).filter(function (node) { + return node.tagName !== 'path'; + }) + ); + if (contours) { + var glyf = { + name: '' + }; + + glyf.contours = mirrorContours(contours); + ttf.glyf.push(glyf); + } + } + + /** * 解析xml文档 * @@ -280,7 +333,14 @@ define( } parseFont(xmlDoc, ttf); - parseGlyf(xmlDoc, ttf); + + // 如果是svg字体格式,则解析glyf,否则解析path + if (ttf.from === 'svgfont') { + parseGlyf(xmlDoc, ttf); + } + else { + parsePath(xmlDoc, ttf); + } if (!ttf.glyf.length) { error.raise(10201); diff --git a/src/ttf/util/glyf2svg.js b/src/ttf/util/glyf2svg.js index 7560e0d..9e263b9 100644 --- a/src/ttf/util/glyf2svg.js +++ b/src/ttf/util/glyf2svg.js @@ -16,7 +16,7 @@ define( var contour2svg = require('./contour2svg'); var contours2svg = require('./contours2svg'); - var matrixTransform = require('graphics/transform'); + var pathTransform = require('graphics/pathTransform'); var lang = require('common/lang'); /** @@ -52,7 +52,7 @@ define( var contours = lang.clone(glyph.contours); // 这里需要进行matrix变换,需要复制一份 var transform = g.transform; for (var i = 0, l = contours.length; i < l; i++) { - matrixTransform( + pathTransform( contours[i], transform.a, transform.b, diff --git a/test/icomoon.svg b/test/icomoon.svg new file mode 100644 index 0000000..f155d0d --- /dev/null +++ b/test/icomoon.svg @@ -0,0 +1,16 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + \ No newline at end of file diff --git a/test/svgnodes.svg b/test/svgnodes.svg new file mode 100644 index 0000000..6777e4b --- /dev/null +++ b/test/svgnodes.svg @@ -0,0 +1,47 @@ + + + +]> + + + + + + + + + + +