svgicons2svgfont
Advanced tools
Comparing version 1.1.0 to 2.0.0
#! /usr/bin/env node | ||
var svgicons2svgfont = require(__dirname + '/../src/index.js') | ||
, Fs = require('fs') | ||
, codepoint = 0xE001 | ||
; | ||
var program = require('commander'); | ||
var fs = require('fs'); | ||
svgicons2svgfont( | ||
Fs.readdirSync(process.argv[2]).map(function(file) { | ||
return { | ||
name: 'glyph' + codepoint, | ||
codepoint: codepoint++, | ||
stream: Fs.createReadStream( | ||
process.argv[2] + '/' + file | ||
) | ||
}; | ||
}), { | ||
fontName: process.argv[4] || '' | ||
} | ||
).pipe(Fs.createWriteStream(process.argv[3])); | ||
var SVGIcons2SVGFontStream = require(__dirname + '/../src/index.js'); | ||
var SVGIconsDirStream = require(__dirname + '/../src/iconsdir.js'); | ||
program | ||
.version('2.0.0') | ||
.usage('[options] <icons ...>') | ||
.option('-v, --verbose', 'tell me everything!') | ||
.option('-o, --output [/dev/stdout]', 'Output file.') | ||
.option('-f, --fontname [value]', 'the font family name you want [iconfont].') | ||
.option('-w, --fixedWidth', 'creates a monospace font of the width of the largest input icon.') | ||
.option('-c, --centerhorizontally', 'calculate the bounds of a glyph and center it horizontally.') | ||
.option('-n, --normalize', 'normalize icons by scaling them to the height of the highest icon.') | ||
.option('-h, --height [value]', 'the outputted font height [MAX(icons.height)].', parseInt) | ||
.option('-r, --round [value]', 'setup the SVG path rounding [10e12].', parseInt) | ||
.option('-d, --descent [value]', 'the font descent [0].', parseInt) | ||
.parse(process.argv); | ||
if(!program.args.length) { | ||
console.error('No icons specified!'); | ||
process.exit(1); | ||
} | ||
SVGIconsDirStream(program.args) | ||
.pipe(SVGIcons2SVGFontStream({ | ||
fontName: program.fontname, | ||
fixedwidth: program.fixedwidth, | ||
centerhorizontally: program.centerHorizontally, | ||
normalize: program.normalize, | ||
height: program.height, | ||
round: program.round, | ||
descent: program.descent, | ||
log: program.v ? console.log : function() {} | ||
})) | ||
.pipe(program.output ? fs.createWriteStream(program.output) : process.stdout); |
{ | ||
"name": "svgicons2svgfont", | ||
"version": "1.1.0", | ||
"version": "2.0.0", | ||
"description": "Read a set of SVG icons and ouput a SVG font", | ||
@@ -24,12 +24,13 @@ "homepage": "https://github.com/nfroidure/svgicons2svgfont", | ||
"dependencies": { | ||
"commander": "^2.8.1", | ||
"readable-stream": "^1.0.33", | ||
"sax": "0.6.x", | ||
"svg-pathdata": "1.0.0", | ||
"readable-stream": "^1.0.33" | ||
"svg-pathdata": "1.0.0" | ||
}, | ||
"devDependencies": { | ||
"mocha": "~2.0.1", | ||
"mocha-lcov-reporter": "0.0.1", | ||
"mocha": "~2.2.5", | ||
"mocha-lcov-reporter": "0.0.2", | ||
"coveralls": "~2.11.2", | ||
"istanbul": "~0.3.2", | ||
"sax": "~0.6.1" | ||
"istanbul": "~0.3.14", | ||
"sax": "~1.1.1" | ||
}, | ||
@@ -36,0 +37,0 @@ "author": { |
@@ -19,25 +19,57 @@ # svgicons2svgfont | ||
## Usage | ||
NodeJS module: | ||
### In your scripts | ||
```js | ||
var svgicons2svgfont = require('svgicons2svgfont') | ||
, fs = require('fs'); | ||
, fontStream = svgicons2svgfont([{ | ||
codepoint: 0xE001, | ||
stream: fs.createReadStream('icons/icon1.svg') | ||
}, { | ||
codepoint: 0xE002, | ||
stream: fs.createReadStream('icons/icon2.svg') | ||
}], options); | ||
var svgicons2svgfont = require('svgicons2svgfont'); | ||
var fs = require('fs'); | ||
var fontStream = svgicons2svgfont({ | ||
fontName: 'hello' | ||
}); | ||
// Saving in a file | ||
fontStream.pipe(fs.createWriteStream('font/destination/file.svg')) | ||
// Setting the font destination | ||
fontStream.pipe(fs.createWriteStream('fonts/hello.svg')) | ||
.on('finish',function() { | ||
console.log('Font written !') | ||
console.log('Font successfully created!') | ||
}) | ||
.on('error',function(err) { | ||
console.log(err); | ||
}); | ||
// Writing glyphs | ||
var glyph1 = fs.createReadStream('icons/icon1.svg'); | ||
glyph1.metadata = { | ||
unicode: '\uE001\uE002', | ||
name: 'icon1' | ||
}; | ||
fontStream.write(glyph); | ||
// Multiple unicode values are possible | ||
var glyph2 = fs.createReadStream('icons/icon1.svg'); | ||
glyph2.metadata = { | ||
unicode: ['\uE002', '\uEA02'], | ||
name: 'icon2' | ||
}; | ||
fontStream.write(glyph2); | ||
// Either ligatures are available | ||
var glyph3 = fs.createReadStream('icons/icon1.svg'); | ||
glyph3.metadata = { | ||
unicode: '\uE001\uE002', | ||
name: 'icon1-icon2' | ||
}; | ||
fontStream.write(glyph3); | ||
// Do not forget to end the stream | ||
fontStream.end(); | ||
``` | ||
CLI (install the module globally): | ||
# CLI interface | ||
All options are available except the `log` one by using this pattern: | ||
`--{LOWER_CASE(optionName)}={optionValue}`. | ||
```sh | ||
svgicons2svgfont icons/directory font/destination/file.svg | ||
svgicons2svgfont --fontname=hello -o font/destination/file.svg icons/directory/*.svg | ||
``` | ||
Note that you won't be able to customize icon names or icons unicodes by | ||
passing options but by using the following convention to name your icons files: | ||
`${icon.unicode}-${icon.name}.svg` where `icon.unicode` is a comma separated | ||
list of unicode strings (ex: 'uEA01,uE001,uEOO1uEOO2', note that the last | ||
string is in fact a ligature). | ||
@@ -68,3 +100,3 @@ ## API | ||
may not work for some icons. We need to create a svg-pathdata-draw module on | ||
top of svg-pathdata to get the real bounds of the icon. It's in on the bottom | ||
top of svg-pathdata to get the real bounds of the icon. It's on the bottom | ||
of my to do, but feel free to work on it. Discuss it in the | ||
@@ -106,2 +138,9 @@ [related issue](https://github.com/nfroidure/svgicons2svgfont/issues/18). | ||
#### options.error | ||
Type: `Function` | ||
Default value: `false` | ||
Allows you to provide your own error logging function. Set to `function(){}` to | ||
impeach logging. | ||
## Build systems | ||
@@ -108,0 +147,0 @@ |
369
src/index.js
@@ -5,3 +5,3 @@ /* | ||
* | ||
* Copyright (c) 2013 Nicolas Froidure, Cameron Hunter | ||
* Copyright (c) 2013-2015 Nicolas Froidure and contributors | ||
* Licensed under the MIT license. | ||
@@ -11,5 +11,2 @@ */ | ||
// http://www.whizkidtech.redprince.net/bezier/circle/ | ||
var KAPPA = ((Math.sqrt(2)-1)/3)*4; | ||
// Transform helpers (will move elsewhere later) | ||
@@ -41,5 +38,27 @@ function parseTransforms(value) { | ||
// Rendering | ||
function tagShouldRender(tag, parents) { | ||
return !parents.some(function(tag) { | ||
if('undefined' !== typeof tag.attributes.display && | ||
'none' == tag.attributes.display.toLowerCase()) { | ||
return true; | ||
} | ||
if('undefined' !== typeof tag.attributes.width && | ||
0 === parseFloat(tag.attributes.width, 0)) { | ||
return true; | ||
} | ||
if('undefined' !== typeof tag.attributes.height && | ||
0 === parseFloat(tag.attributes.height, 0)) { | ||
return true; | ||
} | ||
if('undefined' !== typeof tag.attributes.viewBox) { | ||
var values = tag.attributes.viewBox.split(/\s*,*\s|\s,*\s*|,/); | ||
if(0 === parseFloat(values[2]) || 0 === parseFloat(values[3])) { | ||
return true; | ||
} | ||
} | ||
}); | ||
} | ||
// Shapes helpers (should also move elsewhere) | ||
function rectToPath(attributes) { | ||
@@ -100,10 +119,70 @@ var x = 'undefined' !== typeof attributes.x ? | ||
function polylineToPath(attributes) { | ||
return 'M' + attributes.points; | ||
} | ||
function lineToPath(attributes) { | ||
// Move to the line start | ||
return '' + | ||
'M' + (parseFloat(attributes.x1,10)||0).toString(10) + | ||
' ' + (parseFloat(attributes.y1,10)||0).toString(10) + | ||
' ' + ((parseFloat(attributes.x1,10)||0)+1).toString(10) + | ||
' ' + ((parseFloat(attributes.y1,10)||0)+1).toString(10) + | ||
' ' + ((parseFloat(attributes.x2,10)||0)+1).toString(10) + | ||
' ' + ((parseFloat(attributes.y2,10)||0)+1).toString(10) + | ||
' ' + (parseFloat(attributes.x2,10)||0).toString(10) + | ||
' ' + (parseFloat(attributes.y2,10)||0).toString(10) + | ||
'Z'; | ||
} | ||
// http://www.whizkidtech.redprince.net/bezier/circle/ | ||
var KAPPA = ((Math.sqrt(2)-1)/3)*4; | ||
function circleToPath(attributes) { | ||
var cx = parseFloat(attributes.cx, 10); | ||
var cy = parseFloat(attributes.cy, 10); | ||
var rx = 'undefined' !== typeof attributes.rx ? | ||
parseFloat(attributes.rx, 10) : | ||
parseFloat(attributes.r, 10); | ||
var ry = 'undefined' !== typeof attributes.ry ? | ||
parseFloat(attributes.ry, 10) : | ||
parseFloat(attributes.r, 10); | ||
return '' + | ||
'M' + (cx - rx) + ',' + cy + | ||
'C' + (cx - rx) + ',' + (cy + ry*KAPPA) + | ||
' ' + (cx - rx*KAPPA) + ',' + (cy + ry) + | ||
' ' + cx + ',' + (cy + ry) + | ||
'C' + (cx + rx*KAPPA) + ',' + (cy+ry) + | ||
' ' + (cx + rx) + ',' + (cy + ry*KAPPA) + | ||
' ' + (cx + rx) + ',' + cy + | ||
'C' + (cx + rx) + ',' + (cy - ry*KAPPA) + | ||
' ' + (cx + rx*KAPPA) + ',' + (cy - ry) + | ||
' ' + cx + ',' + (cy - ry) + | ||
'C' + (cx - rx*KAPPA) + ',' + (cy - ry) + | ||
' ' + (cx - rx) + ',' + (cy - ry*KAPPA) + | ||
' ' + (cx - rx) + ',' + cy + | ||
'Z'; | ||
} | ||
function polygonToPath(attributes) { | ||
return 'M' + attributes.points + 'Z'; | ||
} | ||
// Required modules | ||
var Path = require("path") | ||
, Stream = require("readable-stream") | ||
, Sax = require("sax") | ||
, SVGPathData = require("svg-pathdata") | ||
; | ||
var util = require("util"); | ||
var Stream = require("readable-stream"); | ||
var Sax = require("sax"); | ||
var SVGPathData = require("svg-pathdata"); | ||
function svgicons2svgfont(glyphs, options) { | ||
// Inherit of duplex stream | ||
util.inherits(SVGIcons2SVGFontStream, Stream.Transform); | ||
// Constructor | ||
function SVGIcons2SVGFontStream(options) { | ||
var _this = this; | ||
var glyphs = []; | ||
var log; | ||
var error; | ||
options = options || {}; | ||
@@ -114,36 +193,66 @@ options.fontName = options.fontName || 'iconfont'; | ||
options.round = options.round || 10e12; | ||
var outputStream = new Stream.PassThrough() | ||
, log = (options.log || console.log.bind(console)) | ||
, error = options.error || console.error.bind(console); | ||
glyphs = glyphs.forEach(function (glyph, index, glyphs) { | ||
log = (options.log || console.log.bind(console)); | ||
error = options.error || console.error.bind(console); | ||
// Ensure new were used | ||
if(!(this instanceof SVGIcons2SVGFontStream)) { | ||
return new SVGIcons2SVGFontStream(options); | ||
} | ||
// Parent constructor | ||
Stream.Transform.call(this, { | ||
objectMode: true | ||
}); | ||
// Setting objectMode separately | ||
this._writableState.objectMode = true; | ||
this._readableState.objectMode = false; | ||
// Parse input | ||
this._transform = function _svgIcons2SVGFontStreamTransform( | ||
svgIconStream, unused, svgIconStreamCallback | ||
) { | ||
// Parsing each icons asynchronously | ||
var saxStream = Sax.createStream(true) | ||
, parents = [] | ||
; | ||
saxStream.on('closetag', function(tag) { | ||
parents.pop(); | ||
}); | ||
var saxStream = Sax.createStream(true); | ||
var parents = []; | ||
var glyph = svgIconStream.metadata || {}; | ||
glyph.d = []; | ||
glyphs.push(glyph); | ||
if('string' !== typeof glyph.name) { | ||
_this.emit('error', new Error('Please provide a name for the glyph at' + | ||
' index ' + (glyphs.length - 1))); | ||
} | ||
if(glyphs.some(function(anotherGlyph) { | ||
return (anotherGlyph !== glyph && anotherGlyph.name === glyph.name); | ||
})) { | ||
_this.emit('error', new Error('The glyph name "' + glyph.name + | ||
'" must be unique.')); | ||
} | ||
if(glyph.unicode && glyph.unicode instanceof Array && glyph.unicode.length) { | ||
if(glyph.unicode.some(function(unicodeA, i) { | ||
return glyph.unicode.some(function(unicodeB, j) { | ||
return i !== j && unicodeA === unicodeB; | ||
}); | ||
})) { | ||
_this.emit('error', new Error('Given codepoints for the glyph "' + | ||
glyph.name + '" contain duplicates.')); | ||
} | ||
} else if('string' !== typeof glyph.unicode) { | ||
_this.emit('error', new Error('Please provide a codepoint for the glyph "' + | ||
glyph.name + '"')); | ||
} | ||
if(glyphs.some(function(anotherGlyph) { | ||
return (anotherGlyph !== glyph && anotherGlyph.unicode === glyph.unicode); | ||
})) { | ||
_this.emit('error', new Error('The glyph "' + glyph.name + | ||
'" codepoint seems to be used already elsewhere.')); | ||
} | ||
saxStream.on('opentag', function(tag) { | ||
parents.push(tag); | ||
// Checking if any parent rendering is disabled and exit if so | ||
if(parents.some(function(tag) { | ||
if('undefined' != typeof tag.attributes.display | ||
&& 'none' == tag.attributes.display.toLowerCase()) { | ||
return true; | ||
} | ||
if('undefined' != typeof tag.attributes.width | ||
&& 0 === parseFloat(tag.attributes.width, 0)) { | ||
return true; | ||
} | ||
if('undefined' != typeof tag.attributes.height | ||
&& 0 === parseFloat(tag.attributes.height, 0)) { | ||
return true; | ||
} | ||
if('undefined' != typeof tag.attributes.viewBox) { | ||
var values = tag.attributes.viewBox.split(/\s*,*\s|\s,*\s*|,/); | ||
if(0 === parseFloat(values[2]) || 0 === parseFloat(values[3])) { | ||
return true; | ||
} | ||
} | ||
})) { | ||
if(!tagShouldRender(tag, parents)) { | ||
return; | ||
@@ -169,5 +278,5 @@ } | ||
if(!glyph.width || !glyph.height) { | ||
log('Glyph "' + glyph.name + '" has no size attribute on which to' | ||
+ ' get the gylph dimensions (heigh and width or viewBox' | ||
+ ' attributes)'); | ||
log('Glyph "' + glyph.name + '" has no size attribute on which to' + | ||
' get the gylph dimensions (heigh and width or viewBox' + | ||
' attributes)'); | ||
glyph.width = 150; | ||
@@ -178,4 +287,4 @@ glyph.height = 150; | ||
} else if('clipPath' === tag.name) { | ||
log('Found a clipPath element in the icon "' + glyph.name + '" the' | ||
+ 'result may be different than expected.'); | ||
log('Found a clipPath element in the icon "' + glyph.name + '" the' + | ||
'result may be different than expected.'); | ||
// Change rect elements to the corresponding path | ||
@@ -185,50 +294,14 @@ } else if('rect' === tag.name && 'none' !== tag.attributes.fill) { | ||
} else if('line' === tag.name && 'none' !== tag.attributes.fill) { | ||
log('Found a line element in the icon "' + glyph.name + '" the result' | ||
+' could be different than expected.'); | ||
glyph.d.push(applyTransforms( | ||
// Move to the line start | ||
'M' + (parseFloat(tag.attributes.x1,10)||0).toString(10) | ||
+ ' ' + (parseFloat(tag.attributes.y1,10)||0).toString(10) | ||
+ ' ' + ((parseFloat(tag.attributes.x1,10)||0)+1).toString(10) | ||
+ ' ' + ((parseFloat(tag.attributes.y1,10)||0)+1).toString(10) | ||
+ ' ' + ((parseFloat(tag.attributes.x2,10)||0)+1).toString(10) | ||
+ ' ' + ((parseFloat(tag.attributes.y2,10)||0)+1).toString(10) | ||
+ ' ' + (parseFloat(tag.attributes.x2,10)||0).toString(10) | ||
+ ' ' + (parseFloat(tag.attributes.y2,10)||0).toString(10) | ||
+ 'Z', parents | ||
)); | ||
log('Found a line element in the icon "' + glyph.name + '" the result' + | ||
' could be different than expected.'); | ||
glyph.d.push(applyTransforms(lineToPath(tag.attributes), parents)); | ||
} else if('polyline' === tag.name && 'none' !== tag.attributes.fill) { | ||
log('Found a polyline element in the icon "' + glyph.name + '" the' | ||
+' result could be different than expected.'); | ||
glyph.d.push(applyTransforms( | ||
'M' + tag.attributes.points, parents | ||
)); | ||
log('Found a polyline element in the icon "' + glyph.name + '" the' + | ||
' result could be different than expected.'); | ||
glyph.d.push(applyTransforms(polylineToPath(tag.attributes), parents)); | ||
} else if('polygon' === tag.name && 'none' !== tag.attributes.fill) { | ||
glyph.d.push(applyTransforms( | ||
'M' + tag.attributes.points + 'Z', parents | ||
)); | ||
glyph.d.push(applyTransforms(polygonToPath(tag.attributes), parents)); | ||
} else if('circle' === tag.name || 'ellipse' === tag.name && | ||
'none' !== tag.attributes.fill) { | ||
var cx = parseFloat(tag.attributes.cx,10) | ||
, cy = parseFloat(tag.attributes.cy,10) | ||
, rx = 'undefined' !== typeof tag.attributes.rx ? | ||
parseFloat(tag.attributes.rx,10) : parseFloat(tag.attributes.r,10) | ||
, ry = 'undefined' !== typeof tag.attributes.ry ? | ||
parseFloat(tag.attributes.ry,10) : parseFloat(tag.attributes.r,10); | ||
glyph.d.push(applyTransforms( | ||
'M' + (cx - rx) + ',' + cy | ||
+ 'C' + (cx - rx) + ',' + (cy + ry*KAPPA) | ||
+ ' ' + (cx - rx*KAPPA) + ',' + (cy + ry) | ||
+ ' ' + cx + ',' + (cy + ry) | ||
+ 'C' + (cx + rx*KAPPA) + ',' + (cy+ry) | ||
+ ' ' + (cx + rx) + ',' + (cy + ry*KAPPA) | ||
+ ' ' + (cx + rx) + ',' + cy | ||
+ 'C' + (cx + rx) + ',' + (cy - ry*KAPPA) | ||
+ ' ' + (cx + rx*KAPPA) + ',' + (cy - ry) | ||
+ ' ' + cx + ',' + (cy - ry) | ||
+ 'C' + (cx - rx*KAPPA) + ',' + (cy - ry) | ||
+ ' ' + (cx - rx) + ',' + (cy - ry*KAPPA) | ||
+ ' ' + (cx - rx) + ',' + cy | ||
+ 'Z', parents | ||
)); | ||
glyph.d.push(applyTransforms(circleToPath(tag.attributes), parents)); | ||
} else if('path' === tag.name && tag.attributes.d && | ||
@@ -239,25 +312,44 @@ 'none' !== tag.attributes.fill) { | ||
}); | ||
saxStream.on('closetag', function(tag) { | ||
parents.pop(); | ||
}); | ||
saxStream.on('end', function() { | ||
glyph.running = false; | ||
if(glyphs.every(function(glyph) { | ||
return !glyph.running; | ||
})) { | ||
var fontWidth = (glyphs.length > 1 ? glyphs.reduce(function (curMax, glyph) { | ||
return Math.max(curMax, glyph.width); | ||
}, 0) : glyphs[0].width) | ||
, fontHeight = options.fontHeight || | ||
(glyphs.length > 1 ? glyphs.reduce(function (curMax, glyph) { | ||
return Math.max(curMax, glyph.height); | ||
}, 0) : glyphs[0].height); | ||
if((!options.normalize) | ||
&& fontHeight>(glyphs.length > 1 ? glyphs.reduce(function (curMin, glyph) { | ||
svgIconStreamCallback(); | ||
}); | ||
svgIconStream.pipe(saxStream); | ||
}; | ||
// Output data | ||
this._flush = function _svgIcons2SVGFontStreamFlush(svgFontFlushCallback) { | ||
var fontWidth = ( | ||
glyphs.length > 1 ? | ||
glyphs.reduce(function (curMax, glyph) { | ||
return Math.max(curMax, glyph.width); | ||
}, 0) : | ||
glyphs[0].width); | ||
var fontHeight = options.fontHeight || ( | ||
glyphs.length > 1 ? glyphs.reduce(function (curMax, glyph) { | ||
return Math.max(curMax, glyph.height); | ||
}, 0) : | ||
glyphs[0].height); | ||
if( | ||
(!options.normalize) && | ||
fontHeight>(glyphs.length > 1 ? | ||
glyphs.reduce(function (curMin, glyph) { | ||
return Math.min(curMin, glyph.height); | ||
}, Infinity) : glyphs[0].height)) { | ||
log('The provided icons does not have the same height it could lead' | ||
+' to unexpected results. Using the normalize option could' | ||
+' solve the problem.'); | ||
} | ||
// Output the SVG file | ||
// (find a SAX parser that allows modifying SVG on the fly) | ||
outputStream.write('<?xml version="1.0" standalone="no"?> \n\ | ||
}, Infinity) : | ||
glyphs[0].height | ||
) | ||
) { | ||
log('The provided icons does not have the same height it could lead' + | ||
' to unexpected results. Using the normalize option could' + | ||
' solve the problem.'); | ||
} | ||
// Output the SVG file | ||
// (find a SAX parser that allows modifying SVG on the fly) | ||
_this.push('\ | ||
<?xml version="1.0" standalone="no"?> \n\ | ||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >\n\ | ||
@@ -316,42 +408,25 @@ <svg xmlns="http://www.w3.org/2000/svg">\n\ | ||
delete glyph.running; | ||
outputStream.write('\ | ||
<glyph glyph-name="' + glyph.name + '"\n\ | ||
unicode="&#x' + (glyph.codepoint.toString(16)).toUpperCase() + ';"\n\ | ||
(glyph.unicode instanceof Array ? | ||
glyph.unicode : | ||
[glyph.unicode] | ||
).forEach(function(unicode, i){ | ||
_this.push('\ | ||
<glyph glyph-name="' + glyph.name + (i == 0 ? '' : '-' + i) + '"\n\ | ||
unicode="' + unicode.split('').map(function(char) { | ||
return '&#x' + char.charCodeAt(0).toString(16).toUpperCase() + ';'; | ||
}).join('') + '"\n\ | ||
horiz-adv-x="' + glyph.width + '" d="' + d +'" />\n'); | ||
}); | ||
outputStream.write('\ | ||
}); | ||
}); | ||
_this.push('\ | ||
</font>\n\ | ||
</defs>\n\ | ||
</svg>\n'); | ||
outputStream.on('finish', function() { | ||
log("Font created"); | ||
'function' === (typeof options.callback) && (options.callback)(glyphs); | ||
}); | ||
outputStream.end(); | ||
} | ||
}); | ||
if('string' !== typeof glyph.name) { | ||
throw new Error('Please provide a name for the glyph at index ' + index); | ||
} | ||
if(glyphs.some(function(g) { | ||
return (g !== glyph && g.name === glyph.name); | ||
})) { | ||
throw new Error('The glyph name "' + glyph.name + '" must be unique.'); | ||
} | ||
if('number' !== typeof glyph.codepoint) { | ||
throw new Error('Please provide a codepoint for the glyph "' + glyph.name + '"'); | ||
} | ||
if(glyphs.some(function(g) { | ||
return (g !== glyph && g.codepoint === glyph.codepoint); | ||
})) { | ||
throw new Error('The glyph "' + glyph.name | ||
+ '" codepoint seems to be used already elsewhere.'); | ||
} | ||
glyph.running = true; | ||
glyph.d = []; | ||
glyph.stream.pipe(saxStream); | ||
}); | ||
return outputStream; | ||
log("Font created"); | ||
'function' === (typeof options.callback) && (options.callback)(glyphs); | ||
svgFontFlushCallback(); | ||
}; | ||
} | ||
module.exports = svgicons2svgfont; | ||
module.exports = SVGIcons2SVGFontStream; |
@@ -1,27 +0,20 @@ | ||
var assert = require('assert') | ||
, svgicons2svgfont = require(__dirname + '/../src/index.js') | ||
, Fs = require('fs') | ||
, StringDecoder = require('string_decoder').StringDecoder | ||
, Path = require("path"); | ||
var assert = require('assert'); | ||
var fs = require('fs'); | ||
var SVGIcons2SVGFontStream = require(__dirname + '/../src/index.js'); | ||
var StringDecoder = require('string_decoder').StringDecoder; | ||
var SVGIconsDirStream = require(__dirname + '/../src/iconsdir'); | ||
// Helpers | ||
function generateFontToFile(options, done, fileSuffix) { | ||
var codepoint = 0xE001 | ||
, dest = __dirname + '/results/' + options.fontName | ||
+ (fileSuffix || '') + '.svg' | ||
, stream = svgicons2svgfont(Fs.readdirSync(__dirname + '/fixtures/' + options.fontName) | ||
.map(function(file) { | ||
var matches = file.match(/^(?:u([0-9a-f]{4})\-)?(.*).svg$/i); | ||
return { | ||
codepoint: (matches[1] ? parseInt(matches[1], 16) : codepoint++), | ||
name: matches[2], | ||
stream: Fs.createReadStream(__dirname + '/fixtures/' + options.fontName + '/' + file) | ||
}; | ||
}), options); | ||
stream.pipe(Fs.createWriteStream(dest)).on('finish', function() { | ||
var dest = __dirname + '/results/' + options.fontName + | ||
(fileSuffix || '') + '.svg'; | ||
var svgFontStream = SVGIcons2SVGFontStream(options); | ||
svgFontStream.pipe(fs.createWriteStream(dest)).on('finish', function() { | ||
assert.equal( | ||
Fs.readFileSync(__dirname + '/expected/' + options.fontName | ||
+ (fileSuffix || '') + '.svg', | ||
fs.readFileSync(__dirname + '/expected/' + options.fontName + | ||
(fileSuffix || '') + '.svg', | ||
{encoding: 'utf8'}), | ||
Fs.readFileSync(dest, | ||
fs.readFileSync(dest, | ||
{encoding: 'utf8'}) | ||
@@ -31,23 +24,19 @@ ); | ||
}); | ||
SVGIconsDirStream(__dirname + '/fixtures/' + options.fontName) | ||
.pipe(svgFontStream); | ||
} | ||
function generateFontToMemory(options, done) { | ||
var content = '' | ||
, decoder = new StringDecoder('utf8') | ||
, codepoint = 0xE001 | ||
, stream = svgicons2svgfont(Fs.readdirSync(__dirname + '/fixtures/' + options.fontName) | ||
.map(function(file) { | ||
var matches = file.match(/^(?:u([0-9a-f]{4})\-)?(.*).svg$/i); | ||
return { | ||
codepoint: (matches[1] ? parseInt(matches[1], 16) : codepoint++), | ||
name: matches[2], | ||
stream: Fs.createReadStream(__dirname + '/fixtures/' + options.fontName + '/' + file) | ||
}; | ||
}), options); | ||
stream.on('data', function(chunk) { | ||
var content = ''; | ||
var decoder = new StringDecoder('utf8'); | ||
var svgFontStream = SVGIcons2SVGFontStream(options); | ||
svgFontStream.on('data', function(chunk) { | ||
content += decoder.write(chunk); | ||
}); | ||
stream.on('finish', function() { | ||
svgFontStream.on('finish', function() { | ||
assert.equal( | ||
Fs.readFileSync(__dirname + '/expected/' + options.fontName + '.svg', | ||
fs.readFileSync(__dirname + '/expected/' + options.fontName + '.svg', | ||
{encoding: 'utf8'}), | ||
@@ -58,2 +47,6 @@ content | ||
}); | ||
SVGIconsDirStream(__dirname + '/fixtures/' + options.fontName) | ||
.pipe(svgFontStream); | ||
} | ||
@@ -64,9 +57,9 @@ | ||
it("should work for simple SVG", function(done) { | ||
it("should work for simple SVG", function(done) { | ||
generateFontToFile({ | ||
fontName: 'originalicons' | ||
}, done); | ||
}); | ||
}); | ||
it("should work for simple fixedWidth and normalize option", function(done) { | ||
it("should work for simple fixedWidth and normalize option", function(done) { | ||
generateFontToFile({ | ||
@@ -77,11 +70,11 @@ fontName: 'originalicons', | ||
}, done, 'n'); | ||
}); | ||
}); | ||
it("should work for simple SVG", function(done) { | ||
it("should work for simple SVG", function(done) { | ||
generateFontToFile({ | ||
fontName: 'cleanicons' | ||
}, done); | ||
}); | ||
}); | ||
it("should work for codepoint mapped SVG icons", function(done) { | ||
it("should work for codepoint mapped SVG icons", function(done) { | ||
generateFontToFile({ | ||
@@ -91,23 +84,23 @@ fontName: 'prefixedicons', | ||
}, done); | ||
}); | ||
}); | ||
it("should work with multipath SVG icons", function(done) { | ||
it("should work with multipath SVG icons", function(done) { | ||
generateFontToFile({ | ||
fontName: 'multipathicons' | ||
}, done); | ||
}); | ||
}); | ||
it("should work with simple shapes SVG icons", function(done) { | ||
it("should work with simple shapes SVG icons", function(done) { | ||
generateFontToFile({ | ||
fontName: 'shapeicons' | ||
}, done); | ||
}); | ||
}); | ||
it("should work with variable height icons", function(done) { | ||
it("should work with variable height icons", function(done) { | ||
generateFontToFile({ | ||
fontName: 'variableheighticons' | ||
}, done); | ||
}); | ||
}); | ||
it("should work with variable height icons and the normalize option", function(done) { | ||
it("should work with variable height icons and the normalize option", function(done) { | ||
generateFontToFile({ | ||
@@ -117,11 +110,11 @@ fontName: 'variableheighticons', | ||
}, done, 'n'); | ||
}); | ||
}); | ||
it("should work with variable width icons", function(done) { | ||
it("should work with variable width icons", function(done) { | ||
generateFontToFile({ | ||
fontName: 'variablewidthicons' | ||
}, done); | ||
}); | ||
}); | ||
it("should work with centered variable width icons and the fixed width option", function(done) { | ||
it("should work with centered variable width icons and the fixed width option", function(done) { | ||
generateFontToFile({ | ||
@@ -132,15 +125,15 @@ fontName: 'variablewidthicons', | ||
}, done, 'n'); | ||
}); | ||
}); | ||
it("should not display hidden pathes", function(done) { | ||
it("should not display hidden pathes", function(done) { | ||
generateFontToFile({ | ||
fontName: 'hiddenpathesicons' | ||
}, done); | ||
}); | ||
}); | ||
it("should work with real world icons", function(done) { | ||
it("should work with real world icons", function(done) { | ||
generateFontToFile({ | ||
fontName: 'realicons' | ||
}, done); | ||
}); | ||
}); | ||
@@ -272,19 +265,29 @@ it("should work with rendering test SVG icons", function(done) { | ||
describe('Testing CLI', function() { | ||
describe('Using multiple unicode values for a single icon', function() { | ||
it("should work for simple SVG", function(done) { | ||
(require('child_process').exec)( | ||
'node '+__dirname+'../bin/svgicons2svgfont.js ' | ||
+ __dirname + '/expected/originalicons.svg ' | ||
+ __dirname + '/results/originalicons.svg', | ||
function() { | ||
assert.equal( | ||
Fs.readFileSync(__dirname + '/expected/originalicons.svg', | ||
{encoding: 'utf8'}), | ||
Fs.readFileSync(__dirname + '/results/originalicons.svg', | ||
{encoding: 'utf8'}) | ||
); | ||
done(); | ||
} | ||
); | ||
it("should work", function(done) { | ||
var svgIconStream = fs.createReadStream(__dirname + '/fixtures/cleanicons/account.svg'); | ||
svgIconStream.metadata = { | ||
name: 'account', | ||
unicode: ['\uE001', '\uE002'] | ||
}; | ||
var svgFontStream = SVGIcons2SVGFontStream(); | ||
var content = ''; | ||
var decoder = new StringDecoder('utf8'); | ||
svgFontStream.on('data', function(chunk) { | ||
content += decoder.write(chunk); | ||
}); | ||
svgFontStream.on('finish', function() { | ||
assert.equal( | ||
fs.readFileSync(__dirname + '/expected/cleanicons-multi.svg', | ||
{encoding: 'utf8'}), | ||
content | ||
); | ||
done(); | ||
}); | ||
svgFontStream.write(svgIconStream); | ||
svgFontStream.end(); | ||
}); | ||
@@ -294,74 +297,101 @@ | ||
describe('Using ligatures', function() { | ||
it("should work", function(done) { | ||
var svgIconStream = fs.createReadStream(__dirname + '/fixtures/cleanicons/account.svg'); | ||
svgIconStream.metadata = { | ||
name: 'account', | ||
unicode: ['\uE001\uE002'] | ||
}; | ||
var svgFontStream = SVGIcons2SVGFontStream(); | ||
var content = ''; | ||
var decoder = new StringDecoder('utf8'); | ||
svgFontStream.on('data', function(chunk) { | ||
content += decoder.write(chunk); | ||
}); | ||
svgFontStream.on('finish', function() { | ||
assert.equal( | ||
fs.readFileSync(__dirname + '/expected/cleanicons-lig.svg', | ||
{encoding: 'utf8'}), | ||
content | ||
); | ||
done(); | ||
}); | ||
svgFontStream.write(svgIconStream); | ||
svgFontStream.end(); | ||
}); | ||
}); | ||
describe('Providing bad glyphs', function() { | ||
it("should fail when not providing glyph name", function() { | ||
var hadError = false; | ||
try { | ||
svgicons2svgfont([{ | ||
stream: Fs.createReadStream('/dev/null'), | ||
codepoint: 0xE001 | ||
}]); | ||
} catch(err) { | ||
assert.equal(err instanceof Error, true); | ||
assert.equal(err.message, 'Please provide a name for the glyph at index 0'); | ||
hadError = true; | ||
} | ||
assert.equal(hadError, true); | ||
}); | ||
it("should fail when not providing glyph name", function(done) { | ||
var svgIconStream = fs.createReadStream(__dirname + '/fixtures/cleanicons/account.svg'); | ||
svgIconStream.metadata = { | ||
unicode: '\uE001' | ||
}; | ||
SVGIcons2SVGFontStream().on('error', function(err) { | ||
assert.equal(err instanceof Error, true); | ||
assert.equal(err.message, 'Please provide a name for the glyph at index 0'); | ||
done(); | ||
}).write(svgIconStream); | ||
}); | ||
it("should fail when not providing codepoints", function() { | ||
var hadError = false; | ||
try { | ||
svgicons2svgfont([{ | ||
stream: Fs.createReadStream('/dev/null'), | ||
name: 'test' | ||
}]); | ||
} catch(err) { | ||
assert.equal(err instanceof Error, true); | ||
assert.equal(err.message, 'Please provide a codepoint for the glyph "test"'); | ||
hadError = true; | ||
} | ||
assert.equal(hadError, true); | ||
}); | ||
it("should fail when not providing codepoints", function(done) { | ||
var svgIconStream = fs.createReadStream(__dirname + '/fixtures/cleanicons/account.svg'); | ||
svgIconStream.metadata = { | ||
name: 'test' | ||
}; | ||
SVGIcons2SVGFontStream().on('error', function(err) { | ||
assert.equal(err instanceof Error, true); | ||
assert.equal(err.message, 'Please provide a codepoint for the glyph "test"'); | ||
done(); | ||
}).write(svgIconStream); | ||
}); | ||
it("should fail when providing the same codepoint twice", function() { | ||
var hadError = false; | ||
try { | ||
svgicons2svgfont([{ | ||
stream: Fs.createReadStream('/dev/null'), | ||
name: 'test', | ||
codepoint: 0xE001 | ||
},{ | ||
stream: Fs.createReadStream('/dev/null'), | ||
name: 'test2', | ||
codepoint: 0xE001 | ||
}]); | ||
} catch(err) { | ||
assert.equal(err instanceof Error, true); | ||
assert.equal(err.message, 'The glyph "test" codepoint seems to be used already elsewhere.'); | ||
hadError = true; | ||
} | ||
assert.equal(hadError, true); | ||
}); | ||
it("should fail when providing the same codepoint twice", function(done) { | ||
var svgIconStream = fs.createReadStream(__dirname + '/fixtures/cleanicons/account.svg'); | ||
svgIconStream.metadata = { | ||
name: 'test', | ||
unicode: '\uE002' | ||
}; | ||
var svgIconStream2 = fs.createReadStream(__dirname + '/fixtures/cleanicons/account.svg'); | ||
svgIconStream2.metadata = { | ||
name: 'test2', | ||
unicode: '\uE002' | ||
}; | ||
var svgFontStream = SVGIcons2SVGFontStream(); | ||
svgFontStream.on('error', function(err) { | ||
assert.equal(err instanceof Error, true); | ||
assert.equal(err.message, 'The glyph "test2" codepoint seems to be used already elsewhere.'); | ||
done(); | ||
}); | ||
svgFontStream.write(svgIconStream); | ||
svgFontStream.write(svgIconStream2); | ||
}); | ||
it("should fail when providing the same name twice", function() { | ||
var hadError = false; | ||
try { | ||
svgicons2svgfont([{ | ||
stream: Fs.createReadStream('/dev/null'), | ||
name: 'test', | ||
codepoint: 0xE001 | ||
},{ | ||
stream: Fs.createReadStream('/dev/null'), | ||
name: 'test', | ||
codepoint: 0xE002 | ||
}]); | ||
} catch(err) { | ||
assert.equal(err instanceof Error, true); | ||
assert.equal(err.message, 'The glyph name "test" must be unique.'); | ||
hadError = true; | ||
} | ||
assert.equal(hadError, true); | ||
}); | ||
it("should fail when providing the same name twice", function(done) { | ||
var svgIconStream = fs.createReadStream(__dirname + '/fixtures/cleanicons/account.svg'); | ||
svgIconStream.metadata = { | ||
name: 'test', | ||
unicode: '\uE001' | ||
}; | ||
var svgIconStream2 = fs.createReadStream(__dirname + '/fixtures/cleanicons/account.svg'); | ||
svgIconStream2.metadata = { | ||
name: 'test', | ||
unicode: '\uE002' | ||
}; | ||
var svgFontStream = SVGIcons2SVGFontStream(); | ||
svgFontStream.on('error', function(err) { | ||
assert.equal(err instanceof Error, true); | ||
assert.equal(err.message, 'The glyph name "test" must be unique.'); | ||
done(); | ||
}); | ||
svgFontStream.write(svgIconStream); | ||
svgFontStream.write(svgIconStream2); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
142627
94
839
180
4
9
+ Addedcommander@^2.8.1
+ Addedcommander@2.20.3(transitive)