Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
extends: ['semistandard']
}
3 changes: 0 additions & 3 deletions .jshintignore

This file was deleted.

72 changes: 0 additions & 72 deletions .jshintrc

This file was deleted.

7 changes: 1 addition & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@ GITHUB_PROJ := fontello/${NPM_PACKAGE}


lint:
if test ! `which jshint` ; then \
echo "You need 'jshint' installed in order to run lint." >&2 ; \
echo " $ make dev-deps" >&2 ; \
exit 128 ; \
fi
jshint . --show-non-errors
npm run lint


publish:
Expand Down
42 changes: 40 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,50 @@ SVG font dumper

This tool is used to dump SVG font into separate images.

Installation:
Installation
-------
```bash
# cli (normal usage)
npm install -g svg-font-dump

# programmatically (advanced usage)
npm install -S svg-font-dump
```
npm install svg-font-dump

Usage
-------
```bash
svg-font-dump -i ./my-svg-font.svg -o ./svg-font-dump -n
```
CLI
-------
- `-i`, `--src-font` - Source font path **required**
- `-o`, `--glyphs-dir` - Glyphs output folder **required**, create folder if doesn't exist
- `-c`, `--config` - Font config file
- `-d`, `--diff-config` - Difference config output file
- `-f`, `--force` - Force override glyphs from config
- `-n`, `--names` - Try to guess new glyphs names

MODULE
-------
It's also possible to call the module programmatically, in case you want it ran in a build script.

`svgFontDump(options)`

Example:

```javascript
const path = require('path');
const svgFontDump = require('svg-font-dump');

svgFontDump({
 font: path.resolve('./my-svg-font.svg'),
 outputDir: path.resolve('./svg-font-dump'),
name: true
})
.then(() => { console.log('Icons seperated'); })
.catch((e) => { console.error(e); });
```

Authors
-------
Expand Down
54 changes: 54 additions & 0 deletions bin/svg-font-dump.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env node
'use strict';

const ArgumentParser = require('argparse').ArgumentParser;

const pkg = require('../package.json');
const svgFontDump = require('../lib/svgFontDump');

const parser = new ArgumentParser({
version: pkg.version,
addHelp: true,
description: 'Dump SVG font to separate glyphs'
});

parser.addArgument([ '-c', '--config' ], {
help: 'Font config file'
});

parser.addArgument([ '-i', '--src-font' ], {
help: 'Source font path',
required: true,
dest: 'font'
});

parser.addArgument([ '-o', '--glyphs-dir' ], {
help: 'Glyphs output folder',
required: true,
dest: 'outputDir'
});

parser.addArgument([ '-d', '--diff-config' ], {
help: 'Difference config output file',
dest: 'diffConfig'
});

parser.addArgument([ '-f', '--force' ], {
help: 'Force override glyphs from config',
action: 'storeTrue'
});

parser.addArgument([ '-n', '--names' ], {
help: 'Try to guess new glyphs names',
action: 'storeTrue'
});

const args = parser.parseArgs();

svgFontDump(args)
.then(() => {
process.exit(0);
}, (err) => {
console.error(err.message);
process.exit(1);
});
156 changes: 156 additions & 0 deletions lib/svgFontDump.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
'use strict';

const fs = require('mz/fs');
const path = require('path');
const crypto = require('crypto');
const yaml = require('js-yaml');
const co = require('co');
const mkdirp = require('mkdirp-then');

const fixedCharCodeAt = require('./utils/fixedCharCodeAt');
const loadSvgData = require('./utils/loadSvgData');
const loadFontelloData = require('./utils/loadFontelloData');

const svgTemplate = (width, height, d) => `
<svg height="${height}" width="${width}" xmlns="http://www.w3.org/2000/svg">
<path d="${d}" />
</svg>
`;

const DEF_OPTIONS = {
config: null,
font: null,
outputDir: null,
diffConfig: null,
force: false,
names: false
};

function loadSvgFont (fontPath) {
return fs.readFile(fontPath, 'utf-8')
.catch(() => {
throw new Error(`Can\'t read font file ${fontPath}`);
});
}

function loadConfig (configPath) {
if (!configPath) {
return {glyphs: []};
}

return fs.readFile(configPath, 'utf-8')
.then(yaml.load)
.catch(() => {
throw new Error(`Can\'t read config file ${configPath}`);
});
}

function createConfigTemplate (diffConfigPath, diffGlyphs = []) {
if (!diffConfigPath) {
throw new Error('No diff config path passed');
}

if (!diffGlyphs.length) {
// TODO: message
// return new Error('No new glyphs, skip writing diff');
return;
}

const options = {
flowLevel: 3,
styles: {
'!!int': 'hexadecimal'
}
};

const contents = yaml.dump({glyphs: diffGlyphs}, options);

return fs.writeFile(diffConfigPath, contents)
.catch(() => {
throw new Error(`Can\'t save new config template`);
});
}

module.exports = co.wrap(function* svgFontDump (options) {
// Merge options with default options
options = Object.assign({}, DEF_OPTIONS, options);

const data = yield loadSvgFont(options.font);
const config = yield loadConfig(options.config);

const diff = [];

// Create output directory if doesn't exist
yield mkdirp(options.outputDir)
.catch(() => {
throw new Error(`Can\'t create glyph output directory ${options.outputDir}`);
});

const glyphs = path.extname(options.font) === '.json'
? loadFontelloData(JSON.parse(data))
: loadSvgData(data);

yield glyphs.map(function (glyph) {
let exists = null;

// Convert multibyte unicode char to number
glyph.unicode = fixedCharCodeAt(glyph.unicode);

// if got config from existing font, then write only missed files
if (config) {
exists = config.glyphs.find((element) => {
// console.log('---' + element.from + '---' + glyph.unicode)
return (element.from || element.code) === glyph.unicode;
});

if (exists && !options.force) {
console.log((glyph.unicode.toString(16)) + ' exists, skipping');
return;
}
}

// Fix for FontForge: need space between old and new polyline
glyph.d = glyph.d.replace(/zm/g, 'z m');

glyph.svg = svgTemplate(glyph.width, glyph.height, glyph.d);

// If glyph exists in config, but we forced dump
if (exists) {
const glyphName = path.join(options.outputDir, (exists.file || exists.css) + '.svg');
console.log((glyph.unicode.toString(16)) + ' - Found, but override forced');
return fs.writeFile(glyphName, glyph.svg);
}

// Completely new glyph
const glyphOut = {
css: glyph.name,
code: glyph.unicode,
uid: glyph.uid || crypto.randomBytes(16).toString('hex'),
search: glyph.search || []
};

console.log((glyph.unicode.toString(16)) + ' - NEW glyph, writing...');

let filename;

if (options.names) {
filename = glyph.name + '.svg';
} else {
if (glyph.unicode === +glyph.unicode) {
filename = 'glyph__' + glyph.unicode.toString(16) + '.svg';
} else {
filename = 'glyph__' + glyph.unicode + '.svg';
}
}

return fs.writeFile(path.join(options.outputDir, filename), glyph.svg)
.then(() => {
diff.push(glyphOut);
});
});

// Create config template for new glyphs, if option set
if (options.diffConfig) {
yield createConfigTemplate(options.diffConfig, diff);
}
});
18 changes: 18 additions & 0 deletions lib/utils/fixedCharCodeAt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict';

/**
* Char to Int, with fix for big numbers
*/
module.exports = function fixedCharCodeAt (chr) {
/* jshint bitwise: false */
const char1 = chr.charCodeAt(0);
const char2 = chr.charCodeAt(1);

if ((chr.length >= 2) &&
((char1 & 0xfc00) === 0xd800) &&
((char2 & 0xfc00) === 0xdc00)) {
return 0x10000 + ((char1 - 0xd800) << 10) + (char2 - 0xdc00);
} else {
return char1;
}
};
20 changes: 20 additions & 0 deletions lib/utils/fixedFromCharCode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict';

/**
* Int to char, with fix for big numbers
* @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/fromCharCode
* @param {[type]} code [description]
* @return {String}
*/
module.exports = function fixedFromCharCode (code) {
if (code > 0xffff) {
code -= 0x10000;

const surrogate1 = 0xd800 + (code >> 10);
const surrogate2 = 0xdc00 + (code & 0x3ff);

return String.fromCharCode(surrogate1, surrogate2);
} else {
return String.fromCharCode(code);
}
};
Loading