├── .gitignore ├── .npmignore ├── .travis.yml ├── HISTORY.md ├── README.md ├── README_CN.md ├── package.json ├── src ├── defer.js ├── fontGenerator.js ├── index.js ├── multiStream.js └── svgFontParser.js ├── template ├── html.handlebars └── svg.handlebars └── test ├── builder ├── _index.js ├── dest │ └── .hodor └── test.svg ├── mocha.opts └── parser ├── _index.js └── test.svg /.gitignore: -------------------------------------------------------------------------------- 1 | # idea ignore 2 | .idea/ 3 | 4 | # temp ignore 5 | *.log 6 | *.cache 7 | *.diff 8 | *.patch 9 | *.tmp 10 | 11 | # system ignore 12 | .DS_Store 13 | Thumbs.db 14 | 15 | # project ignore 16 | /node_modules/ 17 | 18 | # test ignore 19 | /test/builder/dest/ 20 | !.hodor 21 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # idea ignore 2 | .idea/ 3 | 4 | # temp ignore 5 | *.log 6 | *.cache 7 | *.diff 8 | *.patch 9 | *.tmp 10 | 11 | # system ignore 12 | .DS_Store 13 | Thumbs.db 14 | 15 | # project ignore 16 | /node_modules/ 17 | 18 | # test ignore 19 | /test/ 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "0.12" 5 | - "4.0" 6 | - "5" 7 | - "6" 8 | - "stable" 9 | 10 | sudo: false 11 | 12 | script: 13 | - npm test 14 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | 1.0.1 / 2015-07-01 2 | ------------------ 3 | 4 | - Added examples 5 | - Multiple fixes 6 | 7 | 8 | 1.0.0 / 2015-06-26 9 | ------------------ 10 | 11 | - First release 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iconfont-builder 2 | 3 | [![Build Status](https://travis-ci.org/q-iconfont/iconfont-builder.svg?branch=master)](https://travis-ci.org/q-iconfont/iconfont-builder) 4 | [![npm](https://img.shields.io/npm/dm/iconfont-builder.svg)](https://www.npmjs.com/package/iconfont-builder) 5 | 6 | [中文版](https://github.com/malcolmyu/iconfont-builder/blob/master/README_CN.md) 7 | 8 | ## Introduction 9 | 10 | Iconfont-builder is a node.js package for providing a middleware that create some font files. 11 | 12 | ## Installation (via [npm](https://npmjs.org/package/iconfont-builder)) 13 | 14 | ```bash 15 | $ npm i --save iconfont-builder 16 | ``` 17 | 18 | ## Usage 19 | 20 | ### Simple Usage 21 | 22 | ```js 23 | var builder = require('iconfont-builder'); 24 | var path = require('path'); 25 | 26 | var options = { 27 | icons: [ 28 | { 29 | name: 'www-font-o', 30 | file: 'abc.svg', 31 | codepoint: 61441 32 | } 33 | ], 34 | src: path.join(__dirname, 'src'), 35 | fontName: 'iconfont', 36 | descent: 0, 37 | dest: path.join(__dirname, 'dest') 38 | }; 39 | 40 | builder(options) 41 | .then().catch(); 42 | ``` 43 | 44 | ### List of options 45 | 46 | #### icons 47 | 48 | Type: `Array` 49 | 50 | Example: 51 | 52 | ```js 53 | { 54 | name: 'www-font-o', // className of icon 55 | file: 'abc.svg', // fileName of icon 56 | codepoint: 61441 // unicode of icon 57 | } 58 | ``` 59 | 60 | #### writeFiles 61 | 62 | Type: `Boolean` 63 | 64 | Default: `true` 65 | 66 | It is possible to not create font files but get the attribute **d** of each icon svg. The attribute d contains all paths' information of an icon, which can be use to draw a svg icon. 67 | 68 | #### readFiles 69 | 70 | Type: `Boolean` 71 | 72 | Default: `true` 73 | 74 | You can only use attribute **d** to create font files! If this param is `false`, the Object in param `icons` should have attribute `d`. 75 | 76 | #### fontName 77 | 78 | Type: `String` 79 | 80 | Default: `'iconfont'` 81 | 82 | Name of font and font files. 83 | 84 | #### startCodePoint 85 | 86 | Type: `Number` 87 | 88 | Default: `0xF000` 89 | 90 | Start of font's unicode in DEC(e.g. `61441`) or HEX(e.g. `0xF001`). When passing `options` without `icons`, builder will use `startCodePoint` as the first unicode of font icon, and the unicode of each remaining icons will increased by one in order. 91 | 92 | #### src 93 | 94 | Type: `String` 95 | 96 | Default: `'.'` 97 | 98 | Directory of source svg font files. 99 | 100 | #### dest 101 | 102 | Type: `String` 103 | 104 | Directory for generated font files. 105 | 106 | #### descent 107 | 108 | Type: `Number` 109 | 110 | Default: `0` 111 | 112 | The font descent. It's useful to fix the font baseline yourself. 113 | 114 | Warning: The descent is a positive value! 115 | 116 | ## Author 117 | 118 | [missmiss](http://www.weibo.com/ssherrylliu) 119 | 120 | [malcolmyu](https://twitter.com/minghaoyu) 121 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # iconfont-builder 2 | 3 | ## 简介 4 | 5 | 我是一个通过 svg 文件生成字体文件的包,对比其他同类型的包,我们的包存在以下优势: 6 | 7 | - ~~用了以后会变帅;~~ 8 | - 能不生成字体文件而仅产出每个 svg 压缩过后的 **d** 属性; 9 | - 能仅通过图标的 **d** 属性生成字体。 10 | 11 | ## 安装 (通过 [npm](https://npmjs.org/package/iconfont-builder)) 12 | 13 | ```bash 14 | $ npm i --save iconfont-builder 15 | ``` 16 | 17 | ## 使用 18 | 19 | ### 简单案例 20 | 21 | ```js 22 | var builder = require('iconfont-builder'); 23 | var path = require('path'); 24 | 25 | var options = { 26 | // 图标信息 27 | icons: [ 28 | { 29 | name: 'www-font-o', 30 | file: 'abc.svg', 31 | codepoint: 61441 32 | } 33 | ], 34 | // 图标文件夹 35 | src: path.join(__dirname, 'src'), 36 | // 生成字体名称 37 | fontName: 'iconfont', 38 | // 整体偏移量 39 | descent: 0, 40 | // 字体生成位置 41 | dest: path.join(__dirname, 'dest') 42 | }; 43 | 44 | builder(options) 45 | .then().catch(); 46 | ``` 47 | 48 | ### options 参数 49 | 50 | #### icons 51 | 52 | 类型: `Array` 53 | 54 | 例子: 55 | 56 | ```js 57 | { 58 | name: 'www-font-o', // 图标样式名 59 | file: 'abc.svg', // 图标文件名 60 | codepoint: 61441 // 图标 unicode,这里是十进制,也可以用16进制 61 | } 62 | ``` 63 | 64 | #### writeFiles 65 | 66 | 类型: `Boolean` 67 | 68 | 默认值: `true` 69 | 70 | 这个参数如果设定为 `true` 表示生成四个字体文件,如果设定为 `false` 则进行以下操作: 71 | 72 | - 对所有 svg 文件进行压缩(最终压缩为一个只有 d 属性的 path) 73 | - 返回每个 svg 对应 path 的 d 属性 74 | - 不生成字体文件 75 | 76 | #### readFiles 77 | 78 | 类型: `Boolean` 79 | 80 | 默认值: `true` 81 | 82 | 这个参数表示是否通过 svg 文件来生成字体,如果为 `false`,则 `icons` 数组里的每个图标对象都必须要有 d 属性。 83 | 84 | #### fontName 85 | 86 | 类型: `String` 87 | 88 | 默认值: `'iconfont'` 89 | 90 | 生成的字体文件名。 91 | 92 | #### startCodePoint 93 | 94 | 类型: `Number` 95 | 96 | 默认值: `0xF000` 97 | 98 | 图标开始编码,可以是10进制或16进制。当 `options` 参数里没有 `icons` 数组的时候,将会使用这一属性作为字体开始编码,之后的字体编码将依次加一。 99 | 100 | #### src 101 | 102 | 类型: `String` 103 | 104 | 默认值: `'.'` 105 | 106 | svg 文件所处的文件夹。 107 | 108 | #### dest 109 | 110 | 类型: `String` 111 | 112 | 生成字体的目标文件夹。 113 | 114 | #### descent 115 | 116 | 类型: `Number` 117 | 118 | 默认值: `0` 119 | 120 | 字体偏移量,可以用来修正整体字体偏移。 121 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iconfont-builder", 3 | "version": "2.3.2", 4 | "description": "a tool to build fonts via svg", 5 | "repository": "https://github.com/malcolmyu/iconfont-builder.git", 6 | "main": "src/index.js", 7 | "scripts": { 8 | "test": "./node_modules/.bin/mocha" 9 | }, 10 | "keywords": [ 11 | "iconfont svg ttf png eot woff" 12 | ], 13 | "author": [ 14 | "missmiss", 15 | "minghao.yu" 16 | ], 17 | "dependencies": { 18 | "handlebars": "^4.0.5", 19 | "mkdirp": "^0.5.1", 20 | "q": "^1.1.2", 21 | "sax": "^1.2.1", 22 | "svg2ttf": "^1.2.0", 23 | "svgicons2svgfont": "^3.2.1", 24 | "svgpath": "^2.1.6", 25 | "ttf2eot": "^1.3.0", 26 | "ttf2woff": "^1.3.0", 27 | "underscore": "^1.8.3" 28 | }, 29 | "devDependencies": { 30 | "mocha": "^2.3.4" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/defer.js: -------------------------------------------------------------------------------- 1 | // defer-polyfill 2 | module.exports = function() { 3 | var defer = {}; 4 | var promise = new Promise(function(resolve, reject) { 5 | defer.resolve = resolve; 6 | defer.reject = reject; 7 | }); 8 | defer.promise = promise; 9 | return defer; 10 | }; -------------------------------------------------------------------------------- /src/fontGenerator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 生成eot, svg, ttf, woff 四种格式的文件 3 | * 支持只返回各个字体的 path-d 而不生成字体文件 4 | * 支持通过仅传入 d 来生成字体 5 | */ 6 | 7 | var fs = require('fs'); 8 | var path = require('path'); 9 | var Q = require('q'); 10 | var _ = require('underscore'); 11 | var s2s = require('svgicons2svgfont'); 12 | var svg2ttf = require('svg2ttf'); 13 | var ttf2eot = require('ttf2eot'); 14 | var ttf2woff = require('ttf2woff'); 15 | var handlebars = require('handlebars'); 16 | 17 | var defer = require('./defer'); 18 | var parser = require('./svgFontParser'); 19 | var multiStream = require('./multiStream'); 20 | 21 | /** 22 | * 生成 svg 字体 23 | * 24 | * @param {Array} icons 图标数组,包含图标类名与 code 25 | * @param {Object} svgOpts s2s 插件的参数对象 26 | * @param {Object} options iconfont-builder 的参数对象 27 | * @returns {Promise} 28 | */ 29 | function generateSvg(icons, svgOpts, options) { 30 | var stream; 31 | var font = new Buffer(0); 32 | var def = defer(); 33 | 34 | // 进行内部自定义设定 35 | svgOpts.normalize = true; // 大小统一 36 | svgOpts.fontHeight = 1024; // 高度统一为1024 37 | svgOpts.round = 1000; // path值保留三位小数 38 | svgOpts.log = function(){}; // 沉默控制台输出 39 | 40 | stream = s2s(svgOpts) 41 | .on('data', function(data) { 42 | font = Buffer.concat([font, data]); 43 | }) 44 | .on('error', function(err) { 45 | def.reject(err); 46 | }) 47 | .on('finish', function() { 48 | def.resolve(font.toString()); 49 | }); 50 | 51 | _.each(icons, function(icon) { 52 | try { 53 | var glyph; 54 | // 优先使用 buffer 55 | if (icon.buffer && icon.buffer instanceof Buffer) { 56 | glyph = multiStream.createReadStream(icon.buffer); 57 | } else { 58 | var iconFile = path.join(options.src, icon.file); 59 | glyph = fs.createReadStream(iconFile); 60 | } 61 | 62 | glyph.metadata = { 63 | name: icon.name, 64 | unicode: [String.fromCharCode(icon.codepoint)] 65 | }; 66 | stream.write(glyph); 67 | } catch(e) { 68 | def.reject(e); 69 | return false; 70 | } 71 | }); 72 | 73 | stream.end(); 74 | return def.promise; 75 | } 76 | 77 | /** 78 | * 生成 ttf 字体,依赖于 svg 字体的生成 79 | * 80 | * @param {String} svgFont 81 | */ 82 | function generateTtf(svgFont) { 83 | var font = svg2ttf(svgFont); 84 | return new Buffer(font.buffer); 85 | } 86 | 87 | /** 88 | * 生成 woff 字体,依赖于 ttf 字体的生成 89 | * 90 | * @param {String} ttfFont 91 | */ 92 | function generateWoff(ttfFont) { 93 | var font = ttf2woff(new Uint8Array(ttfFont)); 94 | return new Buffer(font.buffer); 95 | } 96 | 97 | /** 98 | * 生成 eot 字体,依赖于 ttf 字体的生成 99 | * 100 | * @param {String} ttfFont 101 | */ 102 | function generateEot(ttfFont) { 103 | var font = ttf2eot(new Uint8Array(ttfFont)); 104 | return new Buffer(font.buffer); 105 | } 106 | 107 | /** 108 | * 生成方便用户查看字体的 html 109 | * 110 | * @param {Object} options 111 | */ 112 | 113 | function generateHtml(options) { 114 | var tmpPath = path.join(__dirname, '../template/html.handlebars'); 115 | options.timestamp = +new Date; 116 | return Q.nfcall(fs.readFile, tmpPath, 'utf-8') 117 | .then(function(source) { 118 | var template = handlebars.compile(source); 119 | return template(options); 120 | }); 121 | } 122 | 123 | /** 124 | * 按照依赖关系生成字体 125 | * 126 | * @param {Object} options 生成字体参数对象 127 | * @returns {Promise} 128 | */ 129 | function generateFonts(options) { 130 | // 使用 ascent 和 descent 进行字体的基线调整 131 | var svgOpts = _.pick(options, 132 | 'fontName', 'ascent', 'descent' 133 | ); 134 | var svg; 135 | 136 | // 首先进行 svg 的生成 137 | // 当 readFiles 为 false 时,使用 d 生成 138 | // 否则使用文件生成 139 | if (!options.readFiles) { 140 | svg = parser.getSvgIcon(options); 141 | } else { 142 | svg = generateSvg(options.icons, svgOpts, options); 143 | } 144 | 145 | // 当不需要 writeFiles 时不需要生成 ttf/eot/woff 146 | if (!options.writeFiles) { 147 | return svg; 148 | } 149 | 150 | // ttf 依赖 svg 的生成 151 | var ttf = svg.then(generateTtf); 152 | // eot 和 woff 依赖 tff 的生成 153 | var eot = ttf.then(generateEot); 154 | var woff = ttf.then(generateWoff); 155 | 156 | // 最后生成 html,这东西其实无所谓 157 | var html = generateHtml(options); 158 | 159 | return Promise.all([svg, ttf, eot, woff, html]); 160 | } 161 | 162 | module.exports = generateFonts; 163 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var Q = require('q'); 4 | var _ = require('underscore'); 5 | var mkdirp = require('mkdirp'); 6 | 7 | var defer = require('./defer'); 8 | var generator = require('./fontGenerator'); 9 | var parser = require('./svgFontParser'); 10 | 11 | var DEFAULT_OPTIONS = { 12 | readFiles: true, 13 | writeFiles: true, 14 | fontName: 'iconfont', 15 | startCodePoint: 0xE000, 16 | src: '.', 17 | dest: '.', 18 | descent: 0 19 | }; 20 | 21 | /** 22 | * 入口函数,根据参数生成字体或 icon 对象 23 | * 24 | * @param {Object} options 传递参数,详见 readme 25 | * @returns {Promise} 26 | */ 27 | function builder(options) { 28 | options = _.extend({}, DEFAULT_OPTIONS, options); 29 | options.ascent = 1024 - options.descent; 30 | 31 | // 填充 icons 数据 32 | return fillIcons(options) 33 | .then(function(icons) { 34 | options.icons = icons; 35 | return generator(options); 36 | }) 37 | .then(function(data) { 38 | if (options.writeFiles) { 39 | return writeFonts(data, options); 40 | } else { 41 | // 直接返回包含 d 的 icon 数据 42 | // 注意这里的 data 不是数组,是 svg 文件内容 43 | return parser.getPathData(data, options); 44 | } 45 | }); 46 | } 47 | 48 | /** 49 | * 字体写入方法,生成四种字体 50 | * 51 | * @param {Array} fonts 字体内容数组 52 | * @param {Object} options 参数对象 53 | * @returns {Promise} 54 | */ 55 | function writeFonts(fonts, options) { 56 | var type = ['svg', 'ttf', 'eot', 'woff', 'html']; 57 | 58 | var fontsQ = _.map(fonts, function(font, i) { 59 | var filePath = path.join(options.dest, options.fontName + '.' + type[i]); 60 | 61 | var mkdirQ = new Promise(function(resolve, reject) { 62 | mkdirp(path.dirname(filePath), function(err) { 63 | err ? reject(err) : resolve() 64 | }) 65 | }) 66 | var writeFileQ = Q.nfcall(fs.writeFile, filePath, font); 67 | 68 | return mkdirQ.then(writeFileQ); 69 | }); 70 | 71 | return Promise.all(fontsQ); 72 | } 73 | 74 | /** 75 | * 判断是否传入 icons 对象,选择排查或补充 76 | * 77 | * @param {Object} options 参数对象 78 | * @returns {Promise} 79 | */ 80 | function fillIcons(options) { 81 | // 如果有 icons 数据,确保数据不为空 82 | if (options.icons) { 83 | var def = defer(); 84 | var baseCode = options.startCodePoint; 85 | var codeSet = options.icons.map(function(icon) { 86 | return icon.codepoint; 87 | }); 88 | 89 | _.each(options.icons, function(icon) { 90 | // name 是必备的 91 | if (!icon.name) { 92 | def.reject(new Error('icon ' + icon.file + ' has no name')); 93 | return false; 94 | } 95 | 96 | // 如果没有编码,则进行自动生成 97 | if (!icon.codepoint) { 98 | while(codeSet.indexOf(baseCode) > -1) { 99 | baseCode++; 100 | } 101 | icon.codepoint = baseCode++; 102 | } 103 | icon.xmlCode = '&#x' + icon.codepoint.toString(16) + ';'; 104 | 105 | // 有 d 的前提下可以不写 file 106 | if (!options.readFiles && !icon.d) { 107 | def.reject(new Error('icon ' + icon.name + ' has no path data(d)')); 108 | return false; 109 | } 110 | }); 111 | 112 | def.resolve(options.icons); 113 | return def.promise; 114 | } else { 115 | // 如果没有 icons 数据,从 src 里自动生成 116 | var base = options.startCodePoint; 117 | 118 | return Q.nfcall(fs.readdir, options.src) 119 | .then(function(files) { 120 | var svgFiles = _.filter(files, function(file) { 121 | return /\.svg$/i.test(file); 122 | }); 123 | 124 | return _.map(svgFiles, function(file) { 125 | return { 126 | name: 'glyph-' + base, 127 | codepoint: base++, 128 | file: file 129 | }; 130 | }); 131 | }); 132 | } 133 | } 134 | 135 | module.exports = builder; 136 | -------------------------------------------------------------------------------- /src/multiStream.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var stream = require('stream'); 3 | 4 | module.exports.createReadStream = function (object, options) { 5 | return new MultiStream (object, options); 6 | }; 7 | 8 | var MultiStream = function (object, options) { 9 | if (object instanceof Buffer || typeof object === 'string') { 10 | options = options || {}; 11 | stream.Readable.call(this, { 12 | highWaterMark: options.highWaterMark, 13 | encoding: options.encoding 14 | }); 15 | } else { 16 | stream.Readable.call(this, { objectMode: true }); 17 | } 18 | this._object = object; 19 | }; 20 | 21 | util.inherits(MultiStream, stream.Readable); 22 | 23 | MultiStream.prototype._read = function () { 24 | this.push(this._object); 25 | this._object = null; 26 | }; 27 | -------------------------------------------------------------------------------- /src/svgFontParser.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var Q = require('q'); 5 | var handlebars = require('handlebars'); 6 | var svgp = require('svgpath'); 7 | 8 | var sax = require('sax'); 9 | var parser = require('sax').parser(); 10 | 11 | function getPathData(svgContent, options) { 12 | var icons = options.icons; 13 | parser.onopentag = function(node) { 14 | if (node.name === 'GLYPH') { 15 | var attributes = node.attributes; 16 | var name = attributes['GLYPH-NAME']; 17 | var d = attributes['D']; 18 | _.each(icons, function(icon) { 19 | if (icon.name === name) { 20 | icon.d = d; 21 | return false; 22 | } 23 | }); 24 | } 25 | }; 26 | 27 | parser.write(svgContent).close(); 28 | return icons; 29 | } 30 | 31 | function getSvgIcon(options) { 32 | var tmpPath = path.join(__dirname, '../template/svg.handlebars'); 33 | return Q.nfcall(fs.readFile, tmpPath, 'utf-8') 34 | .then(function(source) { 35 | var template = handlebars.compile(source); 36 | // 在使用纯 path 写入 svg 时,支持进行字体偏移量调整 37 | // 通过 buffer 和文件读入的就先不管了…… 38 | if (options.translate) { 39 | options.icons.map(function(icon) { 40 | icon.d = svgp(icon.d).translate(0, options.translate); 41 | }); 42 | } 43 | return template(options); 44 | }); 45 | } 46 | 47 | exports.getPathData = getPathData; 48 | exports.getSvgIcon = getSvgIcon; -------------------------------------------------------------------------------- /template/html.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | IconFont 7 | 85 | 86 | 87 |
88 |

IconFont 图标

89 |
    90 | {{#each icons}} 91 |
  • 92 | {{{xmlCode}}} 93 |
    {{name}}
    94 |
    {{xmlCode}}
    95 |
  • 96 | {{/each}} 97 |
98 |
99 | 第一步:使用font-face声明字体 100 |
101 |   @font-face {font-family: '{{fontName}}';
102 |     src: url('{{fontName}}.eot'); /* IE9*/
103 |     src: url('{{fontName}}.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
104 |     url('{{fontName}}.woff') format('woff'), /* chrome、firefox */
105 |     url('{{fontName}}.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
106 |     url('{{fontName}}.svg#iconfont') format('svg'); /* iOS 4.1- */
107 |   }
108 |       
109 | 第二步:定义使用iconfont的样式 110 |
111 |   .iconfont {
112 |     font-family:"{{fontName}}" !important;
113 |     font-size:16px;font-style:normal;
114 |     -webkit-font-smoothing: antialiased;
115 |     -webkit-text-stroke-width: 0.2px;
116 |     -moz-osx-font-smoothing: grayscale;}
117 |       
118 | 第三步:挑选相应图标并获取字体编码,应用于页面 119 |
120 |   <i class="iconfont">&#x33;</i>
121 |       
122 |
123 |
124 | 125 | 126 | -------------------------------------------------------------------------------- /template/svg.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | {{#each icons}} 14 | 18 | {{/each}} 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/builder/_index.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var Q = require('q'); 5 | var sax = require('sax'); 6 | var parser = require('sax').parser(); 7 | 8 | var _ = require('underscore'); 9 | var builder = require('../../src'); 10 | 11 | var dest = path.join(__dirname, 'dest'); 12 | 13 | function getOptions() { 14 | return { 15 | icons: [ 16 | { 17 | name: 'www-font-o', 18 | codepoint: 0xF000, 19 | file: 'test.svg' 20 | } 21 | ], 22 | src: __dirname, 23 | dest: dest, 24 | fontName: 'myfont', 25 | descent: 128 26 | }; 27 | } 28 | 29 | function generateFonts(options) { 30 | var hasDirQ = Q.nfcall(fs.access, dest); 31 | var rmDirQ = Q.nfcall(fs.rmdir, dest); 32 | 33 | return hasDirQ 34 | .then(rmDirQ) 35 | .finally(function() { 36 | return builder(options); 37 | }) 38 | .then(function() { 39 | var fonts = ['svg', 'ttf', 'eot', 'woff']; 40 | var fontsQ = _.map(fonts, function(type) { 41 | var file = path.join(dest, 'myfont.' + type); 42 | return Q.nfcall(fs.access, file); 43 | }); 44 | 45 | return Promise.all(fontsQ) 46 | }); 47 | } 48 | 49 | describe('生成正确的字体文件', function() { 50 | it('使用 buffer 生产字体', function(done) { 51 | var options = getOptions(); 52 | var iconFile = path.join(options.src, options.icons[0].file); 53 | Q.nfcall(fs.readFile, iconFile) 54 | .then(function(buffer) { 55 | options.icons[0].buffer = buffer; 56 | delete options.icons[0].codepoint; 57 | delete options.icons[0].file; 58 | return generateFonts(options); 59 | }) 60 | .then(function() { 61 | done(); 62 | }) 63 | .catch(function(err) { 64 | done(err); 65 | }); 66 | }); 67 | 68 | it('传递完整的icon信息', function(done) { 69 | var options = getOptions(); 70 | 71 | generateFonts(options) 72 | .then(function() { 73 | done(); 74 | }) 75 | .catch(function(err) { 76 | done(err); 77 | }); 78 | }); 79 | 80 | it('缺失部分icon信息', function(done) { 81 | var options = getOptions(); 82 | delete options.icons[0].name; 83 | 84 | generateFonts(options) 85 | .then(function() { 86 | done(new Error('It cannot be resolved')) 87 | }) 88 | .catch(function() { 89 | done(); 90 | }); 91 | }); 92 | 93 | it('不传递icon信息', function(done) { 94 | var options = getOptions(); 95 | delete options.icons; 96 | 97 | generateFonts(options) 98 | .then(function() { 99 | done(); 100 | }) 101 | .catch(function(err) { 102 | done(err); 103 | }); 104 | }); 105 | 106 | it('使用d数据来生成字体', function(done) { 107 | var options = getOptions(); 108 | delete options.icons[0].file; 109 | options.icons[0].d = ' M896,960 L832,960 L832,128 L192,128 L192,960 L128,960 Q101,959 83,941 Q65,923 64,896 L64,64 Q65,37 83,19 Q101,1 128,0 L896,0 Q923,1 941,19 Q959,37 960,64 L960,896 Q959,923 941,941 Q923,959 896,960 M320,768 Q347,769 365,787 Q383,805 384,832 L384,960 Q383,987 365,1005 Q347,1023 320,1023 Q293,1023 275,1005 Q257,987 256,960 L256,832 Q257,805 275,787 Q293,769 320,768 M704,768 Q731,769 749,787 Q767,805 768,832 L768,960 Q767,987 749,1005 Q731,1023 704,1023 Q677,1023 659,1005 Q641,987 640,960 L640,832 Q641,805 659,787 Q677,769 704,768 M256,704 L768,704 L768,576 L256,576 M256,512 L768,512 L768,384 L256,384 M448,960 L576,960 L576,832 L448,832'; 110 | options.readFiles = false; 111 | options.translate = -128; 112 | 113 | generateFonts(options) 114 | .then(function() { 115 | done(); 116 | }) 117 | .catch(function(err) { 118 | done(err); 119 | }); 120 | }); 121 | 122 | it('检测自动填充 codepoint 生成是否正确', function(done) { 123 | var options = getOptions(); 124 | delete options.icons[0].codepoint; 125 | options.icons.push({ 126 | name: 'www-font-x', 127 | file: 'test.svg' 128 | }); 129 | 130 | generateFonts(options) 131 | .then(function() { 132 | parser.onopentag = function(node) { 133 | if (node.name === 'GLYPH') { 134 | var attributes = node.attributes; 135 | var name = attributes['GLYPH-NAME']; 136 | var code = attributes['UNICODE'].codePointAt(0); 137 | switch (name) { 138 | case 'www-font-o': 139 | if (code !== 0xE000) { 140 | done(new Error('第一个字体的编码错误,应为 0xe000,输出 ' + code.toString(16))); 141 | } 142 | break; 143 | case 'www-font-x': 144 | if (code !== 0xE001) { 145 | done(new Error('第二个字体的编码错误,应为 0xe001,输出 ' + code.toString(16))); 146 | } 147 | break; 148 | } 149 | } 150 | }; 151 | parser.onend = function() { 152 | done(); 153 | }; 154 | var svgPath = path.join(dest, 'myfont.svg'); 155 | Q.nfcall(fs.readFile, svgPath).then(function(data) { 156 | parser.write(data).close(); 157 | }); 158 | }) 159 | .catch(function(err) { 160 | done(err); 161 | }); 162 | }); 163 | 164 | it('codepoint 去重', function(done) { 165 | var options = getOptions(); 166 | delete options.icons[0].codepoint; 167 | options.icons.push({ 168 | name: 'www-font-x', 169 | codepoint: 0xE000, 170 | file: 'test.svg' 171 | }); 172 | 173 | generateFonts(options) 174 | .then(function() { 175 | parser.onopentag = function(node) { 176 | if (node.name === 'GLYPH') { 177 | var attributes = node.attributes; 178 | var name = attributes['GLYPH-NAME']; 179 | var code = attributes['UNICODE'].codePointAt(0); 180 | switch (name) { 181 | case 'www-font-o': 182 | if (code !== 0xE001) { 183 | done(new Error('第一个字体的编码错误,应为 0xE001,输出 0x' + code.toString(16))); 184 | } 185 | break; 186 | case 'www-font-x': 187 | if (code !== 0xE000) { 188 | done(new Error('第二个字体的编码错误,应为 0xE000,输出 0x' + code.toString(16))); 189 | } 190 | break; 191 | } 192 | } 193 | }; 194 | parser.onend = function() { 195 | done(); 196 | }; 197 | var svgPath = path.join(dest, 'myfont.svg'); 198 | Q.nfcall(fs.readFile, svgPath).then(function(data) { 199 | parser.write(data).close(); 200 | }); 201 | }) 202 | .catch(function(err) { 203 | done(err); 204 | }); 205 | }); 206 | }); 207 | -------------------------------------------------------------------------------- /test/builder/dest/.hodor: -------------------------------------------------------------------------------- 1 | Just hold the place. -------------------------------------------------------------------------------- /test/builder/test.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | test/builder 2 | test/parser -------------------------------------------------------------------------------- /test/parser/_index.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | 3 | var builder = require('../../src'); 4 | 5 | var basePath = 'test/parser'; 6 | 7 | function getOptions() { 8 | return { 9 | icons: [ 10 | { 11 | name: 'www-font-o', 12 | codepoint: 0xF001, 13 | file: 'test.svg' 14 | } 15 | ], 16 | src: basePath, 17 | fontName: 'myfont' 18 | }; 19 | } 20 | 21 | describe('能正确解析字体文件', function() { 22 | it('不生成字体,获取path信息', function(done) { 23 | var options = getOptions(); 24 | options.writeFiles = false; 25 | 26 | builder(options) 27 | .then(function(icons) { 28 | if (!icons) done(new Error('no icons')); 29 | _.each(icons, function(icon) { 30 | if (!icon.d) { 31 | done(new Error('icon ' + icon.name + 'has no path')); 32 | } 33 | }); 34 | done(); 35 | }) 36 | }); 37 | }); -------------------------------------------------------------------------------- /test/parser/test.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 11 | 13 | 14 | 16 | 17 | 18 | --------------------------------------------------------------------------------