├── .gitignore ├── LICENSE ├── lib ├── .templates │ ├── licenses │ │ ├── mpl2.0 │ │ ├── ofl1.1 │ │ ├── ufl1.0 │ │ └── apache2.0 │ ├── LICENSE.mustache │ ├── package.json.mustache │ ├── index.js.mustache │ └── README.md.mustache ├── utils.js ├── font_metadata.js ├── name_ids.js ├── generate_license.js ├── generate_readme.js ├── generate_package_json.js ├── licenses.js ├── normalize_filenames.js ├── generate_fonts_from_ttf.js ├── configurator.js ├── subset.js ├── generic_config.js ├── directory_metadata.js ├── generate_index_js.js ├── directory.js └── ext │ └── subset.py ├── .jshintrc ├── scripts ├── setup ├── display_file_metadata ├── display_directory_metadata ├── create_webfonts ├── create_license ├── normalize_filenames ├── create_index ├── create_readme ├── create_package_json ├── subset └── create_fontpack ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This Source Code Form is subject to the terms of the Mozilla Public 2 | License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 | You can obtain one at http://mozilla.org/MPL/2.0/. 4 | -------------------------------------------------------------------------------- /lib/.templates/licenses/mpl2.0: -------------------------------------------------------------------------------- 1 | This Source Code Form is subject to the terms of the Mozilla Public 2 | License, v. 2.0. If a copy of the MPL was not distributed with this 3 | file, You can obtain one at http://mozilla.org/MPL/2.0/ 4 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "passfail": false, 3 | "maxerr": 100, 4 | "node": true, 5 | "forin": false, 6 | "boss": true, 7 | "noarg": true, 8 | "undef": true, 9 | "unused": true, 10 | "browser": true, 11 | "laxbreak": true, 12 | "laxcomma": true, 13 | "eqeqeq": true, 14 | "eqnull": true, 15 | "expr": true, 16 | "indent": 2, 17 | "white": false, 18 | "predef": [ 19 | "exports", 20 | "require", 21 | "process" 22 | ], 23 | "es5": true, 24 | "esnext": true, 25 | "shadow": false, 26 | "supernew": false, 27 | "strict": false 28 | } 29 | -------------------------------------------------------------------------------- /scripts/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | const program = require('commodore') 8 | .description('Set up generic configuration for connect-fonts') 9 | .version('0.0.1') 10 | .parse(process.argv), 11 | config_json = require('../lib/generic_config'); 12 | 13 | config_json.ask(function(err, config) { 14 | if (err) return process.exit(1); 15 | process.exit(0); 16 | }); 17 | 18 | -------------------------------------------------------------------------------- /lib/.templates/LICENSE.mustache: -------------------------------------------------------------------------------- 1 | Software: Licenced under version 2.0 of the MPL 2 | 3 | https://www.mozilla.org/MPL/ 4 | 5 | {{#license.custom}} 6 | Fonts: {{&license.custom}} 7 | {{/license.custom}} 8 | {{#license.version}} 9 | Fonts: Licensed under version {{license.version}} of the {{license.name}} 10 | {{/license.version}} 11 | 12 | {{&license.url}} 13 | 14 | ======================= 15 | 16 | This Source Code Form is subject to the terms of the Mozilla Public 17 | License, v. 2.0. If a copy of the MPL was not distributed with this file, 18 | You can obtain one at http://mozilla.org/MPL/2.0/. 19 | 20 | ====================== 21 | 22 | {{&font.copyright}} 23 | 24 | {{&license.text}} 25 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | exports.splitField = function(toSplit, field) { 6 | if (toSplit && toSplit[field]) { 7 | // only have "all_" if there is more than one font. 8 | if (toSplit[field].indexOf(',') > -1) { 9 | toSplit["all_" + field] = toSplit[field]; 10 | } 11 | toSplit[field] = toSplit[field].split(','); 12 | toSplit["first_" + field] = toSplit[field][0]; 13 | } 14 | } 15 | 16 | exports.deepCopy = function(toCopy) { 17 | return JSON.parse(JSON.stringify(toCopy)); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /lib/font_metadata.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const path = require('path'), 6 | ttfinfo = require('ttfinfo'); 7 | 8 | module.exports = function(fontPath, done) { 9 | try { 10 | ttfinfo(fontPath, function(err, info) { 11 | if (err) return done(err); 12 | 13 | info.tables.file = { 14 | path: fontPath, 15 | basename: path.basename(fontPath, '.ttf') 16 | }; 17 | 18 | done(null, info); 19 | }); 20 | } catch(e) { 21 | done(e); 22 | } 23 | }; 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/name_ids.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * A map of name id's => name table indexes 7 | */ 8 | 9 | // Info fetched from http://www.microsoft.com/typography/otspec/name.htm 10 | const NAME_IDS = { 11 | copyright: 0, 12 | font_family: 1, 13 | font_subfamily: 2, 14 | font_id: 3, 15 | font_full_name: 4, 16 | version: 5, 17 | postscript_name: 6, 18 | trademark: 7, 19 | manufacturer: 8, 20 | designer: 9, 21 | description: 10, 22 | url_vendor: 11, 23 | url_designer: 12, 24 | license_desc: 13, 25 | url_license: 14 26 | }; 27 | 28 | module.exports = NAME_IDS; 29 | 30 | -------------------------------------------------------------------------------- /scripts/display_file_metadata: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | const path = require('path'), 8 | fs = require('fs') 9 | font_metadata = require('../lib/font_metadata'), 10 | optimist = require('optimist') 11 | .usage('usage: ' + path.basename(__filename) + 12 | ' ') 13 | argv = optimist.argv; 14 | 15 | var fileName = path.resolve(process.cwd(), argv._[0]); 16 | 17 | if (!fileName) { 18 | optimist.showHelp(); 19 | process.exit(1); 20 | } 21 | 22 | font_metadata(fileName, function(err, info) { 23 | console.log(String(err)); 24 | for (var key in info) { 25 | console.log(key + ": " + JSON.stringify(info[key], null, 2)); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /scripts/display_directory_metadata: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | const path = require('path'), 8 | fs = require('fs') 9 | directory_metadata = require('../lib/directory_metadata'), 10 | optimist = require('optimist') 11 | .usage('usage: ' + path.basename(__filename) + 12 | ' '), 13 | argv = optimist.argv; 14 | 15 | var directoryName = path.resolve(process.cwd(), argv._[0]); 16 | 17 | if (!directoryName) { 18 | optimist.showHelp(); 19 | process.exit(1); 20 | } 21 | 22 | directory_metadata(directoryName, function(err, metaInfo) { 23 | if (err) { 24 | console.error(String(err)); 25 | process.exit(1); 26 | } 27 | 28 | console.log(metaInfo); 29 | }); 30 | -------------------------------------------------------------------------------- /lib/.templates/package.json.mustache: -------------------------------------------------------------------------------- 1 | { 2 | "author": "{{author.name}} <{{author.first_emails}}> ({{&author.first_urls}})", 3 | "name": "{{package.name}}", 4 | "description": "{{font.family}} font pack for connect-fonts", 5 | "keywords": ["font", "font-face", "CSS", "connect-fonts"{{#font.names}}, "{{.}}"{{/font.names}}], 6 | {{#package.homepage}} 7 | "homepage": "{{&package.homepage}}", 8 | {{/package.homepage}} 9 | "licenses": [ 10 | { 11 | "covers": "software", 12 | "type": "MPL-2.0", 13 | "url": "http://mozilla.org/MPL/2.0/" 14 | }, 15 | { 16 | "covers": "fonts", 17 | "type": "{{&license.abbreviation}}", 18 | "url": "{{&license.url}}" 19 | } 20 | ], 21 | {{#package.repourl}} 22 | "repository": { 23 | "type": "git", 24 | "url": "{{&package.repourl}}" 25 | }, 26 | {{/package.repourl}} 27 | {{#package.bugsurl}} 28 | "bugs": { 29 | "url": "{{&package.bugsurl}}" 30 | }, 31 | {{/package.bugsurl}} 32 | "version": "0.0.1", 33 | "engines": { 34 | "node": ">= 0.4.7" 35 | }, 36 | "main": "index" 37 | } 38 | 39 | -------------------------------------------------------------------------------- /scripts/create_webfonts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | const path = require('path'), 8 | generate_fonts_from_ttf 9 | = require('../lib/generate_fonts_from_ttf') 10 | optimist = require('optimist') 11 | .usage('\nCreate web fonts from a .ttf\n\n' + 12 | 'usage: ' + path.basename(__filename) + 13 | ' ') 14 | argv = optimist.argv; 15 | 16 | var fileName = argv._[0]; 17 | var targetName = argv._[1]; 18 | 19 | if (!(fileName && targetName)) { 20 | optimist.showHelp(); 21 | process.exit(1); 22 | } 23 | 24 | generate_fonts_from_ttf.write(fileName, targetName, function(err) { 25 | if (err) { 26 | var msg = String(err); 27 | if (msg === "not a ttf") { 28 | console.error(path.basename(__filename) + " only works with ttf files"); 29 | } 30 | else { 31 | console.error(msg); 32 | } 33 | process.exit(1); 34 | } 35 | }); 36 | 37 | 38 | -------------------------------------------------------------------------------- /scripts/create_license: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | const path = require('path'), 8 | generic_config 9 | = require('../lib/generic_config') 10 | gen_license = require('../lib/generate_license'), 11 | licenses = require('../lib/licenses').licenses, 12 | program = require('commodore') 13 | .description('Create a LICENSE file') 14 | .option('-l, --license ', 'font license') 15 | .oneof('license', Object.keys(licenses)) 16 | .demand('l') 17 | .option('--tp ', 'target path') 18 | .demand('tp') 19 | .parse(process.argv); 20 | 21 | gen_license.write({ 22 | target_dir: generic_config.resolve(process.cwd(), program.tp), 23 | license: licenses[program.license] 24 | }, function(err) { 25 | if (err) { 26 | console.error(String(err)); 27 | process.exit(1); 28 | } 29 | }); 30 | -------------------------------------------------------------------------------- /lib/generate_license.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const path = require('path'), 6 | fs = require('fs'), 7 | mkdirp = require('mkdirp'), 8 | mustache = require('mustache'); 9 | 10 | const TEMPLATES_PATH = path.join(__dirname, '.templates'); 11 | const LICENSE_TEMPLATE_PATH = path.join(TEMPLATES_PATH, 'LICENSE.mustache'); 12 | 13 | /** 14 | * Write LICENSE to a directory 15 | * 16 | * config: 17 | * target_dir 18 | * license.version 19 | * license.name 20 | * license.url 21 | * license.text 22 | * license.custom 23 | * font.copyright 24 | */ 25 | exports.write = function(config, done) { 26 | var err; 27 | try { 28 | var template = fs.readFileSync(LICENSE_TEMPLATE_PATH, 'utf8'); 29 | var output = mustache.render(template, config); 30 | 31 | var targetPath = config.target_dir; 32 | mkdirp.sync(targetPath); 33 | 34 | var packagePath = path.join(targetPath,"LICENSE"); 35 | fs.writeFileSync(packagePath, output, 'utf8'); 36 | } catch(e) { 37 | err = e; 38 | } 39 | 40 | done(err); 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /lib/.templates/index.js.mustache: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | "root": path.join(__dirname, "fonts"), 5 | 6 | {{#package}} 7 | // Package info 8 | "package": {{{package}}}, 9 | {{/package}} 10 | 11 | {{#author}} 12 | // Package author info 13 | "author": {{{author}}}, 14 | {{/author}} 15 | 16 | {{#license}} 17 | // package license info 18 | "license": {{{license}}}, 19 | {{/license}} 20 | 21 | {{#font_common}} 22 | // Common font information 23 | "font_common": {{{font_common}}}, 24 | {{/font_common}} 25 | 26 | 27 | // where to find a locale's fonts in the fonts directory 28 | "locale-to-subdirs": { 29 | {{#aliases}} 30 | {{from}}: "{{to}}"{{separator}} 31 | {{/aliases}} 32 | }, 33 | 34 | // what font types are enabled and what are the extensions of 35 | // the font files. 36 | // 37 | // valid types are embedded-opentype, woff, truetype, svg 38 | "enabled-types": [ "eot", "woff", "ttf", "svg" ], 39 | 40 | // The fonts. The name of the font must be the same as the font 41 | // in the fonts directory. 42 | "fonts": { 43 | {{#fonts}} 44 | "{{name}}": { 45 | "fontFamily": "{{family}}", 46 | "fontStyle": "{{style}}", 47 | "fontWeight": "{{weight}}", 48 | "local": [ {{&local}} ] 49 | }{{separator}} 50 | {{/fonts}} 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /scripts/normalize_filenames: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | const path = require('path'), 8 | normalize_filenames 9 | = require('../lib/normalize_filenames'), 10 | optimist = require('optimist') 11 | .usage('\nNormalize the filenames of all .ttf files in a directory\n\n' + 12 | 'usage: ' + path.basename(__filename) + ' [-d]') 13 | .describe('d', 'Perform a dry run without changing files on disk') 14 | .default('d', false), 15 | argv = optimist.argv; 16 | 17 | /** 18 | * This is a helper script that will convert the names of the font files in 19 | * a directory to make it simpler to work with connect-fonts 20 | * 21 | * All filenames will be: 22 | * => lowercased 23 | * => -webfont will be removed. 24 | * => it. will be changed to italics. 25 | */ 26 | 27 | 28 | 29 | var subdirName = process.argv[2]; 30 | if (!subdirName) { 31 | optimist.showHelp(); 32 | process.exit(1); 33 | } 34 | 35 | normalize_filenames(subdirName, argv.d, function(err, numFilesProcessed) { 36 | console.log(numFilesProcessed + " files processed"); 37 | }); 38 | 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "Author": "Shane Tomlinson (https://shanetomlinson.com)", 3 | "name": "connect-fonts-tools", 4 | "description": "Utility scripts for working with connect-fonts font packs", 5 | "keywords": [ 6 | "font", 7 | "font-face", 8 | "CSS", 9 | "connect-fonts-tools" 10 | ], 11 | "homepage": "https://github.com/shane-tomlinson/connect-fonts-tools", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/shane-tomlinson/connect-fonts-tools.git" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/shane-tomlinson/connect-fonts-tools/issues" 18 | }, 19 | "version": "0.0.5", 20 | "engines": { 21 | "node": ">= 0.6.2" 22 | }, 23 | "main": "index", 24 | "dependencies": { 25 | "webfonts": "0.1.1", 26 | "mustache": "0.7.2", 27 | "mkdirp": "0.3.5", 28 | "optimist": "0.3.5", 29 | "tmp": "0.0.16", 30 | "fs-extra": "0.5.0", 31 | "ncp": "0.4.2", 32 | "rimraf": "2.1.4", 33 | "ttfinfo": "git://github.com/shane-tomlinson/ttfinfo.git#907c9ff981db8b6a6ea2bdfdf793ab2e96105cf9", 34 | "async": "0.2.6", 35 | "easierobject": "0.0.1", 36 | "commodore": "0.0.1", 37 | "prompt": "0.2.9", 38 | "strformat": "0.0.3" 39 | }, 40 | "bin": { 41 | "create_fontpack": "./scripts/create_fontpack", 42 | "setup_connect_fonts": "./scripts/setup", 43 | "subset_font": "./scripts/subset" 44 | }, 45 | "scripts": {} 46 | } 47 | -------------------------------------------------------------------------------- /lib/generate_readme.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const path = require('path'), 6 | fs = require('fs'), 7 | mkdirp = require('mkdirp'), 8 | mustache = require('mustache'), 9 | splitField = require('./utils').splitField, 10 | deepCopy = require('./utils').deepCopy; 11 | 12 | const TEMPLATES_PATH = path.join(__dirname, '.templates'); 13 | const README_TEMPLATE_PATH = path.join(TEMPLATES_PATH, 'README.md.mustache'); 14 | 15 | /** 16 | * Write README.md to a directory 17 | * 18 | * config: 19 | * target_dir 20 | * credits 21 | * author.name 22 | * author.email 23 | * author.url 24 | * author.github 25 | * author.twitter 26 | * font.name 27 | * font.family 28 | * license.version 29 | * license.name 30 | * license.url 31 | * meta_info.subsets 32 | */ 33 | exports.write = function(iConfig, done) { 34 | // create a copy because the config is modified by splitField. 35 | var config = deepCopy(iConfig); 36 | var err = null; 37 | try { 38 | splitField(config.author, "emails"); 39 | splitField(config.author, "urls"); 40 | splitField(config.author, "githubs"); 41 | splitField(config.font, "names"); 42 | 43 | var template = fs.readFileSync(README_TEMPLATE_PATH, 'utf8'); 44 | var output = mustache.render(template, config); 45 | 46 | var targetPath = config.target_dir; 47 | mkdirp.sync(targetPath); 48 | 49 | var packagePath = path.join(targetPath,"README.md"); 50 | fs.writeFileSync(packagePath, output, 'utf8'); 51 | } catch(e) { 52 | err = e; 53 | } 54 | 55 | done && done(err); 56 | }; 57 | 58 | -------------------------------------------------------------------------------- /lib/generate_package_json.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const path = require('path'), 6 | fs = require('fs'), 7 | mkdirp = require('mkdirp'), 8 | mustache = require('mustache'); 9 | 10 | const TEMPLATES_PATH = path.join(__dirname, '.templates'); 11 | const PACKAGE_JSON_PATH = path.join(TEMPLATES_PATH, 'package.json.mustache'); 12 | 13 | /** 14 | * Write package.json to a directory 15 | * 16 | * config: 17 | * target_dir 18 | * author.name 19 | * author.email 20 | * author.url 21 | * package.name 22 | * package.description 23 | * package.homepage 24 | * package.repourl 25 | * package.bugsurl 26 | * license.abbreviation 27 | * license.url 28 | * font.family 29 | * font.names 30 | */ 31 | exports.write = function(iConfig, done) { 32 | // create a copy because the config is modified by split. 33 | var config = JSON.parse(JSON.stringify(iConfig)); 34 | var err; 35 | try { 36 | var template = fs.readFileSync(PACKAGE_JSON_PATH, 'utf8'); 37 | split(config.font, "names"); 38 | split(config.author, "emails"); 39 | split(config.author, "urls"); 40 | var output = mustache.render(template, config); 41 | 42 | var package = JSON.parse(output); 43 | var targetPath = config.target_dir; 44 | mkdirp.sync(targetPath); 45 | 46 | var packagePath = path.join(targetPath,"package.json"); 47 | fs.writeFileSync(packagePath, output, 'utf8'); 48 | } catch(e) { 49 | err = e; 50 | } 51 | done && done(err); 52 | }; 53 | 54 | function split(toSplit, field) { 55 | console.log("splitting", field, typeof toSplit[field], toSplit[field]); 56 | 57 | if (toSplit[field]) { 58 | toSplit[field] = toSplit[field].split(','); 59 | toSplit["first_" + field] = toSplit[field][0]; 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /scripts/create_index: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | 8 | const path = require('path'), 9 | gen_index = require('../lib/generate_index_js'), 10 | optimist = require('optimist') 11 | .describe('fn', 'font name(s) - comma separated list if more than one') 12 | .demand('fn') 13 | .describe('fl', 'font local name(s) - comma separated list if more than one. Must be the same in numer as --fn') 14 | .demand('fl') 15 | .describe('ff', 'font family') 16 | .demand('ff') 17 | .describe('a', 'aliases - comma separated list of aliased lcoales to their subdirectories - eg: de:german') 18 | .usage('\nCreate an font pack index.js file\n\n' + 19 | 'usage: ' + path.basename(__filename) + 20 | ' [options]'), 21 | argv = optimist.argv; 22 | 23 | var targetName = argv._[0]; 24 | if (!targetName) { 25 | optimist.showHelp(); 26 | process.exit(1); 27 | } 28 | 29 | var aliases = {}; 30 | if (argv.a) { 31 | var aliasInput = argv.a.split(','); 32 | aliasInput.forEach(function(alias) { 33 | var parts = alias.split(':'); 34 | var from = parts[0].trim(); 35 | var to = parts[1].trim(); 36 | aliases[from] = to; 37 | }); 38 | } 39 | 40 | try { 41 | gen_index.write({ 42 | target_dir: targetName, 43 | font: { 44 | names: argv.fn.split(','), 45 | family: argv.ff.trim(), 46 | local: argv.fl.split(','), 47 | }, 48 | aliases: aliases 49 | }); 50 | } catch(e) { 51 | optimist.showHelp(); 52 | console.error(String(e)); 53 | process.exit(1); 54 | } 55 | 56 | 57 | -------------------------------------------------------------------------------- /lib/licenses.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const path = require('path'), 6 | fs = require('fs'); 7 | 8 | const TEMPLATES_PATH = path.join(__dirname, '.templates', 'licenses'); 9 | 10 | exports.licenses = { 11 | "apache2.0": { 12 | name: "Apache", 13 | version: "2.0", 14 | abbreviation: "Apache-2.0", 15 | url: "http://www.apache.org/licenses/LICENSE-2.0" 16 | }, 17 | "mpl2.0": { 18 | name: "Mozilla Public License", 19 | version: "2.0", 20 | abbreviation: 'MPL-2.0', 21 | url: "http://mozilla.org/MPL/2.0/" 22 | }, 23 | "ofl1.1": { 24 | name: "SIL Open Font License", 25 | version: "1.1", 26 | abbreviation: 'OFL-1.1', 27 | url: "http://scripts.sil.org/OFL" 28 | }, 29 | "ufl1.0": { 30 | name: "Ubuntu Font License", 31 | version: "1.0", 32 | abbreviation: "UFL-1.0", 33 | url: "http://font.ubuntu.com/ufl/ubuntu-font-licence-1.0.txt" 34 | } 35 | }; 36 | 37 | for (var key in exports.licenses) { 38 | var templatePath = path.join(TEMPLATES_PATH, key); 39 | exports.licenses[key].text = fs.readFileSync(templatePath, 'utf8'); 40 | } 41 | 42 | exports.toIdentifier = function(description, copyright, url) { 43 | var license = getLicense(description) 44 | || getLicense(copyright) 45 | || getLicense(url); 46 | 47 | return license; 48 | }; 49 | 50 | function getLicense(text) { 51 | var license; 52 | if (/SIL/g.test(text) 53 | || /sil.org/gi.test(text) 54 | || /Open Font License/gi.test(text) 55 | || /OFL/g.test(text)) { 56 | license = exports.licenses["ofl1.1"]; 57 | } else if(/apache/gi.test(text)) { 58 | license = exports.licenses["apache2.0"]; 59 | } else if(/mozilla/gi.test(text)) { 60 | license = exports.licenses["mpl2.0"]; 61 | } else if(/ubuntu/gi.test(text)) { 62 | license = exports.licenses["ufl1.0"]; 63 | } 64 | return license; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /lib/normalize_filenames.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * This is a helper lib that will convert the names of the font files in 7 | * a directory to make it simpler to work with connect-fonts 8 | * 9 | * All filenames will be: 10 | * => lowercased 11 | * => -webfont will be removed. 12 | * => it. will be changed to italics. 13 | */ 14 | 15 | const fs = require('fs'), 16 | path = require('path'); 17 | 18 | const extensionsToRename = [ 19 | 'eot', 20 | 'svg', 21 | 'ttf', 22 | 'woff' 23 | ]; 24 | 25 | function nonExistentDirectory(directory) { 26 | console.error(directory + " does not exist"); 27 | process.exit(1); 28 | } 29 | 30 | function invalidDirectory(directory) { 31 | console.error(directory + " is not a directory"); 32 | process.exit(1); 33 | } 34 | 35 | 36 | module.exports = function(subdirName, dryRun, done) { 37 | var subdir = /^\//.test(subdirName) ? subdirName 38 | : path.join(process.cwd(), subdirName); 39 | 40 | if (!fs.existsSync(subdir)) { 41 | nonExistentDirectory(subdirName); 42 | } 43 | 44 | var stats = fs.statSync(subdir); 45 | if (!stats.isDirectory()) { 46 | invalidDirectory(subdirName); 47 | } 48 | 49 | var numFilesProcessed = 0; 50 | var files = fs.readdirSync(subdir); 51 | files.forEach(function(file) { 52 | if (extensionsToRename.indexOf(path.extname(file).replace('.', '')) > -1) { 53 | var newname = file.toLowerCase(); 54 | newname = newname.replace('-webfont', ''); 55 | newname = newname.replace('it.', 'italics.'); 56 | 57 | var oldpath = path.join(subdir, file); 58 | var newpath = path.join(subdir, newname); 59 | 60 | if (oldpath !== newpath) { 61 | console.log("* " + file + " => " + newname); 62 | if (!dryRun) { 63 | fs.renameSync(oldpath, newpath); 64 | } 65 | numFilesProcessed++; 66 | } 67 | } 68 | }); 69 | 70 | done(null, numFilesProcessed); 71 | } 72 | 73 | -------------------------------------------------------------------------------- /lib/generate_fonts_from_ttf.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const fs = require('fs-extra'), 6 | path = require('path'), 7 | tmp = require('tmp'), 8 | mkdirp = require('mkdirp'), 9 | child_process = require('child_process'); 10 | 11 | tmp.setGracefulCleanup(); 12 | 13 | const WEB_FONTS_PROCESSOR_PATH = path.join(__dirname, '..', 'node_modules', '.bin', 'webfonts'); 14 | 15 | exports.write = function(sourcePath, targetPath, done) { 16 | fs.stat(sourcePath, function(err, stats) { 17 | if (err) return done(err); 18 | 19 | // If source is a directory, generate fonts for the entire directory. 20 | if (stats.isDirectory()) { 21 | generate(sourcePath); 22 | } 23 | else if (path.extname(sourcePath).toLowerCase() === '.ttf') { 24 | tmp.dir(function(err, tmpPath) { 25 | var tmpFile = path.join(tmpPath, path.basename(sourcePath)); 26 | fs.copy(sourcePath, tmpFile, function(err) { 27 | if (err) return done(err); 28 | generate(tmpPath); 29 | }); 30 | }); 31 | } 32 | else { 33 | return done(new Error("sourcePath must be a .ttf or directory")); 34 | } 35 | }); 36 | 37 | /* 38 | * Create a temporary directory to place the source font into or 39 | * else the webfonts tool tries to create fonts for all files in the 40 | * directory. 41 | */ 42 | 43 | function generate(sourcePath) { 44 | mkdirp(targetPath, function(err) { 45 | if (err) return done(err); 46 | 47 | spawn(WEB_FONTS_PROCESSOR_PATH, 48 | [sourcePath, '-o', targetPath], null, done); 49 | }); 50 | } 51 | 52 | 53 | function spawn(cmd, args, opts, done) { 54 | var child = child_process.spawn(cmd, args, opts); 55 | child.stdout.pipe(process.stdout); 56 | child.stderr.pipe(process.stderr); 57 | child.on('exit', function(code) { 58 | if (code) return done(new Error("exited spawn with code: " + code)); 59 | done(null); 60 | }); 61 | } 62 | }; 63 | 64 | 65 | -------------------------------------------------------------------------------- /lib/configurator.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const EasierObject = require('easierobject').easierObject, 6 | licenses = require('./licenses'); 7 | 8 | module.exports = function(iArgv, iMetaInfo, done) { 9 | var config, 10 | err; 11 | 12 | try { 13 | var argv = new EasierObject(iArgv); 14 | var metaInfo = new EasierObject(iMetaInfo); 15 | iMetaInfo.subsets = argv.getItem('ss') || ["latin"]; 16 | 17 | var fontNames = metaInfo.getItem("fonts"); 18 | if (fontNames) fontNames = Object.keys(fontNames).join(','); 19 | 20 | config = { 21 | meta_info: iMetaInfo, 22 | target_dir: argv.getItem('target_dir'), 23 | author: { 24 | name: argv.getItem('an') || metaInfo.getItem("common", "designer"), 25 | emails: argv.getItem('ae'), 26 | urls: argv.getItem('au') || metaInfo.getItem("common", "url_designer"), 27 | githubs: argv.getItem('ag'), 28 | twitter: argv.getItem('at') 29 | }, 30 | package: { 31 | name: argv.getItem('pn'), 32 | homepage: argv.getItem('ph') || false, 33 | repourl: argv.getItem('pr') || false, 34 | bugsurl: argv.getItem('pb') || false, 35 | description: argv.getItem('description') || false 36 | }, 37 | font: { 38 | names: argv.getItem('fn') || fontNames, 39 | description: metaInfo.getItem("common", "description"), 40 | family: argv.getItem('ff') || metaInfo.getItem("common", "font_family"), 41 | copyright: metaInfo.getItem("common", "copyright"), 42 | trademark: metaInfo.getItem("common", "trademark"), 43 | manufacturer: metaInfo.getItem("common", "manufacturer"), 44 | url_vendor: metaInfo.getItem("common", "url_vendor"), 45 | designer: metaInfo.getItem("common", "designer"), 46 | url_designer: metaInfo.getItem("common", "url_designer") 47 | }, 48 | credits: argv.getItem('c'), 49 | license: argv.getItem('license') ? licenses.licenses[argv.getItem('license')] : metaInfo.getItem("common", "license") 50 | }; 51 | } catch(e) { 52 | err = e; 53 | } 54 | 55 | done(err, !err && config); 56 | }; 57 | 58 | -------------------------------------------------------------------------------- /lib/subset.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const path = require('path'), 6 | mkdirp = require('mkdirp'), 7 | child_process = require('child_process'); 8 | 9 | 10 | exports.SUPPORTED_SUBSETS = [ 11 | 'ascii', 12 | 'cyrillic', 13 | 'cyrillic-ext', 14 | 'de', 15 | 'en', 16 | 'es', 17 | 'fr', 18 | 'greek', 19 | 'greek-ext', 20 | 'latin', 21 | 'latin-ext', 22 | 'pt', 23 | 'vietnamese' 24 | ]; 25 | 26 | exports.subset = function(subsets, sourcePath, targetPath, createDirForSubset, done) { 27 | var targetPaths = []; 28 | subsets = [].concat(subsets); 29 | processNext(); 30 | 31 | 32 | function processNext() { 33 | var subsetToCreate = subsets.shift(); 34 | if (!subsetToCreate) return done(null, targetPaths); 35 | 36 | var subsetTargetPath = getSubsetTargetPath(sourcePath, targetPath, 37 | subsetToCreate, createDirForSubset); 38 | targetPaths.push(subsetTargetPath); 39 | 40 | subset(subsetToCreate, sourcePath, subsetTargetPath, function(err) { 41 | if (err) return done(err); 42 | processNext(); 43 | }); 44 | } 45 | }; 46 | 47 | 48 | function getSubsetTargetPath(sourcePath, targetPath, 49 | subset, createDirForSubset) { 50 | if (createDirForSubset) { 51 | return path.join(targetPath, subset, path.basename(sourcePath)); 52 | } 53 | else { 54 | return path.join(targetPath, path.basename(sourcePath, '.ttf') + '-' + subset + '.ttf'); 55 | } 56 | } 57 | 58 | function subset(subset, sourcePath, targetPath, done) { 59 | mkdirp(path.dirname(targetPath), function(err) { 60 | if (err) return done(err); 61 | 62 | args = [ 63 | path.join(__dirname, 'ext', 'subset.py'), 64 | '--null', 65 | '--nmr', 66 | '--roundtrip', 67 | '--script', 68 | '--subset=' + subset, 69 | sourcePath, 70 | targetPath 71 | ]; 72 | spawn('python', args, {}, done); 73 | 74 | }); 75 | 76 | } 77 | 78 | function spawn(cmd, args, opts, done) { 79 | var child = child_process.spawn(cmd, args, opts); 80 | child.stdout.pipe(process.stdout); 81 | child.stderr.pipe(process.stderr); 82 | child.on('exit', function(code) { 83 | if (code) { 84 | var msg = cmd + " exited with code " + code; 85 | console.error(msg); 86 | done(new Error(msg)); 87 | } 88 | done(null); 89 | }); 90 | } 91 | 92 | 93 | -------------------------------------------------------------------------------- /scripts/create_readme: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | 8 | const path = require('path'), 9 | gen_readme = require('../lib/generate_readme'), 10 | licenses = require('../lib/licenses').licenses, 11 | generic_config 12 | = require('../lib/generic_config') 13 | configurator = require('../lib/configurator') 14 | program = require('commodore') 15 | .description('Create a README.md file') 16 | .option('-l, --license ', 'Font License: ' + Object.keys(licenses).join(', ')) 17 | .demand('l') 18 | .option('--fn ', 'font name(s) - comma separated list if more than one') 19 | .demand('fn') 20 | .option('--ff ', 'font family') 21 | .demand('ff') 22 | .option('--an ', 'Author name') 23 | .loadable('an') 24 | .demand('an') 25 | .option('--ae ', 26 | 'Author email(s) - comma separated list if more than one') 27 | .loadable('ae') 28 | .demand('ae') 29 | .option('--au ', 30 | 'Author url(s) - comma separated list if more than one') 31 | .loadable('au') 32 | .option('--at ', 'Author twitter') 33 | .loadable('at') 34 | .option('--ag ', 'Author github') 35 | .loadable('ag') 36 | .option('--pn ', 'Package name') 37 | .demand('pn') 38 | .option('--ph ', 'Project homepage URL') 39 | .option('--pr ', 'Project repo URL') 40 | .option('--pb ', 'Project bug tracker URL') 41 | .option('-c ', 'Credits') 42 | .option('--tp ', 'Target Path') 43 | .demand('tp') 44 | .load(generic_config.config_path) 45 | .parse(process.argv); 46 | 47 | program.target_dir = generic_config.resolve(process.cwd(), program.tp); 48 | 49 | if (!licenses[program.license]) { 50 | program.outputHelp(); 51 | process.exit(1); 52 | } 53 | 54 | 55 | configurator(program, undefined, function(err, config) { 56 | gen_readme.write(config); 57 | }); 58 | 59 | 60 | -------------------------------------------------------------------------------- /scripts/create_package_json: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | 8 | const path = require('path'), 9 | packageJSON = require('../lib/generate_package_json'), 10 | licenses = require('../lib/licenses').licenses, 11 | generic_config 12 | = require('../lib/generic_config') 13 | configurator = require('../lib/configurator') 14 | program = require('commodore') 15 | .description('Create a package.json file') 16 | .option('-l, --license ', 'Font License: ' + Object.keys(licenses).join(', ')) 17 | .demand('l') 18 | .option('--fn ', 'font name(s) - comma separated list if more than one') 19 | .demand('fn') 20 | .option('--ff ', 'font family') 21 | .demand('ff') 22 | .option('--an ', 'Author name') 23 | .loadable('an') 24 | .demand('an') 25 | .option('--ae ', 26 | 'Author email(s) - comma separated list if more than one') 27 | .loadable('ae') 28 | .demand('ae') 29 | .option('--au ', 30 | 'Author url(s) - comma separated list if more than one') 31 | .loadable('au') 32 | .option('--at ', 'Author twitter') 33 | .loadable('at') 34 | .option('--ag ', 'Author github') 35 | .loadable('ag') 36 | .option('--pn ', 'Package name') 37 | .demand('pn') 38 | .option('--ph ', 'Project homepage URL') 39 | .option('--pr ', 'Project repo URL') 40 | .option('--pb ', 'Project bug tracker URL') 41 | .option('-c ', 'Credits') 42 | .option('--tp ', 'Target Path') 43 | .demand('tp') 44 | .load(generic_config.config_path) 45 | .parse(process.argv); 46 | 47 | 48 | program.target_dir = generic_config.resolve(process.cwd(), program.tp); 49 | 50 | if (!licenses[program.license]) { 51 | program.outputHelp(); 52 | process.exit(1); 53 | } 54 | 55 | 56 | configurator(program, undefined, function(err, config) { 57 | packageJSON.write(config); 58 | }); 59 | 60 | 61 | -------------------------------------------------------------------------------- /lib/generic_config.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Takes care of configuration management for generic connect-fonts config like 7 | * author name, author urls, etc. 8 | */ 9 | 10 | const path = require('path'), 11 | fs = require('fs'), 12 | mkdirp = require('mkdirp'), 13 | prompt = require('prompt'); 14 | 15 | const CONFIG_PATH = path.join(getUserHome(), ".connect-fonts"), 16 | CONFIG_JSON_PATH 17 | = path.join(CONFIG_PATH, "config.json"); 18 | 19 | const VALID_FIELDS = [ 20 | 'an', // author name 21 | 'ae', // author emails 22 | 'au', // author urls 23 | 'at', // author twitter 24 | 'ag', // author github 25 | ]; 26 | 27 | prompt.message = ">".red; 28 | 29 | exports.config_path = CONFIG_JSON_PATH; 30 | 31 | exports.user_home = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME']; 32 | 33 | exports.resolve = function(from, to) { 34 | if (to === ".") to = ""; 35 | else if (/^~/.test(to)) to = to.replace(/^~/, getUserHome()); 36 | 37 | return path.resolve(from, to); 38 | }; 39 | 40 | 41 | /** 42 | * Write ~home/.connect-fonts/config.json for use in scripts like 43 | * create_repo_from_directory. 44 | */ 45 | exports.write = function(config, done) { 46 | mkdirp(CONFIG_PATH, function(err) { 47 | if (err) return done(err); 48 | 49 | var objToSave = filter(config, VALID_FIELDS); 50 | fs.writeFile(CONFIG_JSON_PATH, JSON.stringify(objToSave, null, 2) + "\n", 'utf8', done); 51 | }); 52 | }; 53 | 54 | exports.read = function(done) { 55 | fs.readFile(CONFIG_JSON_PATH, function(err, data) { 56 | if (err && err.code === "ENOENT") { 57 | // file does not exist 58 | data = "{}"; 59 | } 60 | else if (err) return done(err); 61 | 62 | var err = null; 63 | try { 64 | data = JSON.parse(data); 65 | } catch(e) { 66 | err = e; 67 | } 68 | 69 | done(err, data); 70 | }); 71 | }; 72 | 73 | exports.ask = function(done) { 74 | exports.read(function(err, config) { 75 | if (err) return done(err); 76 | 77 | prompt.get([ 78 | toPromptConfig("Author Name", "an", config), 79 | toPromptConfig("Author Emails (comma spearated list)", "ae", config), 80 | toPromptConfig("Author URLs (comma spearated list)", "au", config), 81 | toPromptConfig("Author Twitter", "at", config), 82 | toPromptConfig("Author GitHubs (comma separated list)", "ag", config), 83 | ], function(err, result) { 84 | if (err) return done(err); 85 | 86 | exports.write(result, done); 87 | }); 88 | }); 89 | } 90 | 91 | function getUserHome() { 92 | return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME']; 93 | } 94 | 95 | function filter(config, fields) { 96 | var filtered = {}; 97 | fields.forEach(function(key) { 98 | filtered[key] = config[key]; 99 | }); 100 | return filtered; 101 | } 102 | 103 | function toPromptConfig(msg, key, config) { 104 | return { 105 | name: key, 106 | required: true, 107 | description: msg.green, 108 | default: config[key] 109 | }; 110 | } 111 | 112 | 113 | -------------------------------------------------------------------------------- /lib/.templates/README.md.mustache: -------------------------------------------------------------------------------- 1 | # {{package.name}} 2 | 3 | {{font.family}} fontpack for [connect-fonts](https://github.com/shane-tomlinson/connect-fonts). 4 | 5 | ## Usage 6 | 7 | 1. Include [connect-fonts](https://github.com/shane-tomlinson/connect-fonts) in a node module. 8 | ```js 9 | const font_middleware = require("connect-fonts"); 10 | ``` 11 | 12 | 2. Include the font packs that you want to serve. 13 | ```js 14 | const font_pack = require("{{package.name}}"); 15 | ``` 16 | 17 | 3. Add a middleware by calling the `setup` function. 18 | ```js 19 | app.use(font_middleware.setup({ 20 | fonts: [ font_pack ], 21 | allow_origin: "https://exampledomain.com" 22 | })); 23 | ``` 24 | 25 | 4. Add a link tag to include the font CSS. 26 | ```html 27 | 28 | ``` 29 | 30 | {{#font.all_names}} 31 | Multiple fonts from the family can be included by using a comma separated list of fonts: 32 | ```html 33 | 34 | ``` 35 | {{/font.all_names}} 36 | 37 | Available fonts: 38 | {{#font.names}} 39 | * {{.}} 40 | {{/font.names}} 41 | 42 | Locale-optimised font sets can be served by specifying the locale in the fonts.css URL. 43 | ```html 44 | 45 | ``` 46 | 47 | Available subsets: 48 | {{#meta_info.subsets}} 49 | * {{.}} 50 | {{/meta_info.subsets}} 51 | 52 | 5. Set your CSS up to use the new font by using the "{{font.family}}" font-family. 53 | ``` 54 | body { 55 | font-family: '{{font.family}}', 'sans-serif', 'serif'; 56 | } 57 | ``` 58 | 59 | ## Font Info 60 | {{font.family}} 61 | 62 | {{#font.description}} 63 | * Description: {{&font.description}} 64 | {{/font.description}} 65 | {{#font.copyright}} 66 | * Copyright: {{&font.copyright}} 67 | {{/font.copyright}} 68 | {{#font.trademark}} 69 | * Trademark: {{&font.trademark}} 70 | {{/font.trademark}} 71 | {{#font.designer}} 72 | * Designer: {{&font.designer}} 73 | {{/font.designer}} 74 | {{#font.url_designer}} 75 | * Designer URL: {{&font.url_designer}} 76 | {{/font.url_designer}} 77 | {{#font.manufacturer}} 78 | * Vendor: {{&font.manufacturer}} 79 | {{/font.manufacturer}} 80 | {{#font.url_vendor}} 81 | * Vendor URL: {{&font.url_vendor}} 82 | {{/font.url_vendor}} 83 | 84 | ## Development Info 85 | {{#package.homepage}} 86 | * Homepage: {{&package.homepage}} 87 | {{/package.homepage}} 88 | {{#package.repourl}} 89 | * Repo: {{&package.repourl}} 90 | {{/package.repourl}} 91 | {{#package.bugsurl}} 92 | * Bugs: {{&package.bugsurl}} 93 | {{/package.bugsurl}} 94 | 95 | ## Font pack author 96 | * {{author.name}} 97 | {{#author.emails}} 98 | * {{&.}} 99 | {{/author.emails}} 100 | {{#author.urls}} 101 | * {{&.}} 102 | {{/author.urls}} 103 | {{#author.githubs}} 104 | * {{&.}} 105 | {{/author.githubs}} 106 | * {{author.twitter}} 107 | 108 | {{#credits}} 109 | ## Credits 110 | {{credits}} 111 | {{/credits}} 112 | 113 | ## License 114 | 115 | {{#license.custom}} 116 | Fonts: {{&license.custom}} 117 | {{/license.custom}} 118 | {{#license.version}} 119 | Fonts: Licensed under version {{license.version}} of the {{license.name}} 120 | {{/license.version}} 121 | 122 | {{&license.url}} 123 | 124 | Software: Licenced under version 2.0 of the MPL 125 | 126 | https://www.mozilla.org/MPL/ 127 | 128 | -------------------------------------------------------------------------------- /scripts/subset: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | const path = require('path'), 8 | fs = require('fs'), 9 | subset = require('../lib/subset'), 10 | generate_fonts_from_ttf 11 | = require('../lib/generate_fonts_from_ttf') 12 | generic_config 13 | = require('../lib/generic_config'), 14 | SUPPORTED_SUBSETS 15 | = subset.SUPPORTED_SUBSETS, 16 | program = require('commodore') 17 | .description('Create a locale-optimized font set') 18 | .option('-s, --ss ', 'subset to create.') 19 | .demand('ss') 20 | .comboof('ss', SUPPORTED_SUBSETS) 21 | .option('--sp ', 'Source Path') 22 | .demand('sp') 23 | .option('--tp ', 'Target Path') 24 | .demand('tp') 25 | .option('-o', 'place subsets into subdirectories of target') 26 | .option('--wf', 'create web fonts (svg, eot, woff) of each subset') 27 | .parse(process.argv); 28 | 29 | var sourcePath = generic_config.resolve(process.cwd(), program.sp); 30 | var targetPath = generic_config.resolve(process.cwd(), program.tp); 31 | 32 | fs.stat(sourcePath, function(err, stats) { 33 | if (err) showErrorAndExit(err); 34 | 35 | if (stats.isDirectory()) { 36 | ls(sourcePath, /\.ttf$/i, true, function(err, files) { 37 | if (err) showErrorAndExit(err); 38 | 39 | var ttfPaths = files.map(function(fileName) { 40 | return path.join(sourcePath, fileName); 41 | }); 42 | 43 | subsetNext(); 44 | 45 | function subsetNext() { 46 | var ttfPath = ttfPaths.shift(); 47 | if (!ttfPath) return; 48 | 49 | subsetFont(ttfPath, function(err) { 50 | if (err) showErrorAndExit(err); 51 | subsetNext(); 52 | }); 53 | } 54 | }); 55 | } else { 56 | subsetFont(sourcePath, function(err) { 57 | if (err) showErrorAndExit(err); 58 | }); 59 | } 60 | }); 61 | 62 | function subsetFont(sourcePath, done) { 63 | subset.subset(program.ss, sourcePath, targetPath, program.O, function(err, subsettedFonts) { 64 | if (err) showErrorAndExit(err); 65 | 66 | if (program.wf) { 67 | createNextWebfont(); 68 | } 69 | 70 | function createNextWebfont() { 71 | var subsettedFont = subsettedFonts.shift(); 72 | if (!subsettedFont) return done(null); 73 | 74 | generate_fonts_from_ttf.write(subsettedFont, path.dirname(subsettedFont), function(err) { 75 | if (err) return done(err); 76 | 77 | createNextWebfont(); 78 | }); 79 | } 80 | }); 81 | } 82 | 83 | function showErrorAndExit(err) { 84 | console.error(String(err)); 85 | process.exit(1); 86 | } 87 | 88 | function ls(target, matchRegExp, warn, done) { 89 | fs.readdir(target, function(err, files) { 90 | if (err) return done(err); 91 | 92 | var matches = []; 93 | files.forEach(function(fileName) { 94 | fileName = fileName.trim(); 95 | if (matchRegExp.test(fileName)) { 96 | matches.push(fileName); 97 | } else if (warn) { 98 | console.warn(fileName + " does not match: " + matchRegExp); 99 | } 100 | }); 101 | 102 | done(null, matches); 103 | }); 104 | }; 105 | 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # connect-fonts-tools 2 | 3 | A collection of tools to create [connect-fonts](https://github.com/shane-tomlinson/connect-fonts) and npm compatible packages. 4 | 5 | ## Usage 6 | 7 | ### Set up generic author/package maintainer information 8 | 9 | Run `scripts/setup` to set up generic author information that can be used to create multiple font packs. 10 | 11 | ### Create a Font Pack 12 | 13 | `scripts/create_fontpack` creates a connect-fonts compatible font pack from the .ttf files in a source directory. If you have already run `scripts/setup`, creating a font pack is easy: 14 | 15 | ```bash 16 | /scripts/create_fontpack --pn --sp --tp 17 | ``` 18 | 19 | 20 | If the font pack is for public use, additional parameters can be specified that will be placed inside the font pack's package.json and README.md files. 21 | 22 | ```bash 23 | /scripts/create_fontpack --pn --ph --pr --pb --sp --tp 24 | ``` 25 | 26 | Once the pack is created, it can be published to npm: 27 | 28 | ```bash 29 | cd 30 | npm publish 31 | ``` 32 | 33 | The font pack can then be installed from npm: 34 | ```bash 35 | npm install 36 | ``` 37 | 38 | If the font pack is not to be published to the npm repository, it can be installed to another local project directory: 39 | 40 | ```bash 41 | cd 42 | npm install 43 | ``` 44 | 45 | ### Subset an already installed font pack 46 | 47 | `scripts/subset` can be used to subset an already installed font pack into fonts that are locale-optimised. 48 | 49 | ```bash 50 | cd node_modules//fonts/default 51 | /script/subset --ss= --sp --tp ../ -o --wf 52 | ``` 53 | 54 | ### Other tools 55 | Tools exist to create individual portions of a font pack or npm module. 56 | 57 | * `create_index` - create an index.js for use by connect-fonts 58 | * `create_license` - create LICENSE 59 | * `create_package_json` - create package.json for use by npm 60 | * `create_readme` - create README.md file that contains font, author, license and repo information. 61 | * `create_webfonts` - create .woff, .svg, and .eot fonts from a .ttf file 62 | * `display_directory_metadata` - read and display the common metadata embedded in the .ttf files in a directory 63 | * `display_file_metadata` - read and display the metadata embedded in a single .ttf file 64 | * `normalize_filenames` - normalize all the filenames. Lowercases all filenames, expand -it to -italics, remove -webfont 65 | * `subset` - subset a .ttf font into smaller, locale specific fonts 66 | 67 | ## Requirements 68 | 69 | `create_fontpack` and `subset` make use of [FontForge](http://fontforge.org/). 70 | 71 | FontForge can be installed in Mac OSX with Homebrew by typing `brew install fontforge`. 72 | In Linux or Windows, see the directions provided by the [Open Font Library](http://openfontlibrary.org/en/guidebook/how_to_install_fontforge). 73 | 74 | 75 | ## Author: 76 | * Shane Tomlinson 77 | * shane@shanetomlinson.com 78 | * stomlinson@mozilla.com 79 | * set117@yahoo.com 80 | * https://shanetomlinson.com 81 | * http://github.com/shane-tomlinson 82 | * http://github.com/stomlinson 83 | * @shane_tomlinson 84 | 85 | ## Credits: 86 | subset.py comes from [Google Font Directory](http://code.google.com/p/googlefontdirectory/) and is licensed under the Apache 2.0 license. Its authors are Raph Levien and Dave Crossland. 87 | 88 | ## Getting involved: 89 | MOAR font packs! 90 | 91 | Any updates to connect-fonts-tools are appreciated. All submissions will be reviewed and considered for merge. 92 | 93 | ## License: 94 | This software is available under version 2.0 of the MPL: 95 | 96 | https://www.mozilla.org/MPL/ 97 | 98 | subset.py, from [Google Font Directory](http://code.google.com/p/googlefontdirectory/), is licensed under the Apache 2.0 license. 99 | 100 | http://www.apache.org/licenses/LICENSE-2.0 101 | 102 | -------------------------------------------------------------------------------- /lib/.templates/licenses/ofl1.1: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | 5 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /lib/directory_metadata.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const path = require('path'), 6 | fs = require('fs') 7 | font_metadata = require('./font_metadata'), 8 | name_ids = require('./name_ids'), 9 | licenses = require('./licenses'); 10 | 11 | const NAME_TABLE_FIELDS = [ 12 | 'copyright', 13 | 'font_family', 14 | 'trademark', 15 | 'manufacturer', 16 | 'designer', 17 | 'description', 18 | 'url_vendor', 19 | 'url_designer', 20 | 'license_desc', 21 | 'url_license' 22 | ]; 23 | 24 | module.exports = function(directoryName, done) { 25 | ls(directoryName, /\.ttf$/i, true, function(err, files) { 26 | if (err) return done(err); 27 | 28 | // convert file names to full paths 29 | files = files.map(function(fileName) { 30 | return path.join(directoryName, fileName); 31 | }); 32 | 33 | getTables(files, function(err, tables) { 34 | if (err) return done(err); 35 | 36 | getCommonInfo(tables.name, function(err, commonInfo) { 37 | if (err) return done(err); 38 | 39 | getFontInfo(tables, function(err, fontInfo) { 40 | if (err) return done(err); 41 | 42 | var license = licenses.toIdentifier(commonInfo.license_desc, commonInfo.copyright, commonInfo.url_license); 43 | if (license) { 44 | commonInfo.license = license; 45 | commonInfo.license_desc = license.name + " " + license.version; 46 | commonInfo.url_license = license.url; 47 | commonInfo.license_abbreviation = license.abbreviation; 48 | } 49 | done(null, { common: commonInfo, fonts: fontInfo }); 50 | }); 51 | }); 52 | }); 53 | }); 54 | }; 55 | 56 | /** 57 | * Get all files in a directory that match. 58 | */ 59 | function ls(target, matchRegExp, warn, done) { 60 | fs.readdir(target, function(err, files) { 61 | if (err) return done(err); 62 | 63 | var matches = []; 64 | files.forEach(function(fileName) { 65 | fileName = fileName.trim(); 66 | if (matchRegExp.test(fileName)) { 67 | matches.push(fileName); 68 | } else if (warn) { 69 | console.warn(fileName + " does not match: " + matchRegExp); 70 | } 71 | }); 72 | 73 | done(null, matches); 74 | }); 75 | }; 76 | 77 | 78 | 79 | function convertNameTable(nameTable) { 80 | var convertedTable = {}; 81 | for (var key in nameTable) { 82 | convertedTable[keyOf(name_ids, parseInt(key, 10))] = nameTable[key]; 83 | } 84 | 85 | return convertedTable; 86 | } 87 | 88 | function getTables(files, done) { 89 | var tables = { 90 | name: [], 91 | os2: [], 92 | post: [], 93 | file: [] 94 | }; 95 | 96 | function processNextFile() { 97 | var filePath = files.shift(); 98 | if (!filePath) return done(null, tables); 99 | 100 | font_metadata(filePath, function(err, info) { 101 | if (err) return done(err); 102 | 103 | tables.name.push(convertNameTable(info.tables.name)); 104 | tables.os2.push(info.tables['OS/2']); 105 | tables.post.push(info.tables.post); 106 | tables.file.push(info.tables.file); 107 | processNextFile(); 108 | }); 109 | } 110 | 111 | processNextFile(); 112 | } 113 | 114 | function getCommonInfo(nameTables, done) { 115 | var commonInfo = nameTables.reduce(function(prevValue, currValue) { 116 | for (var key in currValue) { 117 | if (NAME_TABLE_FIELDS.indexOf(key) > -1) { 118 | if (!(key in prevValue)) { 119 | prevValue[key] = currValue[key]; 120 | } 121 | else if (prevValue[key] !== currValue[key]) { 122 | console.warn("conflict in " + key + " '" + prevValue[key] + '" !== "' + currValue[key] + '"'); 123 | } 124 | } 125 | } 126 | 127 | return prevValue; 128 | }, {}); 129 | 130 | done(null, commonInfo); 131 | } 132 | 133 | function keyOf(obj, value) { 134 | for (var key in obj) { 135 | if (obj[key] === value) return key; 136 | } 137 | } 138 | 139 | function getFontInfo(tables, done) { 140 | var fontInfo = {}; 141 | tables.file.forEach(function(fileInfo, index) { 142 | var nameTable = tables.name[index]; 143 | var os2Table = tables.os2[index]; 144 | var postTable = tables.post[index]; 145 | var fileTable = tables.file[index]; 146 | 147 | fontInfo[fileInfo.basename] = { 148 | weight: os2Table.weightClass, 149 | style: postTable.italicAngle !== 0 ? "italic" : "normal", 150 | local: [ nameTable.font_full_name, nameTable.postscript_name ], 151 | path: fileTable.path 152 | }; 153 | }); 154 | 155 | done(null, fontInfo); 156 | } 157 | 158 | -------------------------------------------------------------------------------- /lib/.templates/licenses/ufl1.0: -------------------------------------------------------------------------------- 1 | ------------------------------- 2 | UBUNTU FONT LICENCE Version 1.0 3 | ------------------------------- 4 | 5 | PREAMBLE 6 | This licence allows the licensed fonts to be used, studied, modified and 7 | redistributed freely. The fonts, including any derivative works, can be 8 | bundled, embedded, and redistributed provided the terms of this licence 9 | are met. The fonts and derivatives, however, cannot be released under 10 | any other licence. The requirement for fonts to remain under this 11 | licence does not require any document created using the fonts or their 12 | derivatives to be published under this licence, as long as the primary 13 | purpose of the document is not to be a vehicle for the distribution of 14 | the fonts. 15 | 16 | DEFINITIONS 17 | "Font Software" refers to the set of files released by the Copyright 18 | Holder(s) under this licence and clearly marked as such. This may 19 | include source files, build scripts and documentation. 20 | 21 | "Original Version" refers to the collection of Font Software components 22 | as received under this licence. 23 | 24 | "Modified Version" refers to any derivative made by adding to, deleting, 25 | or substituting -- in part or in whole -- any of the components of the 26 | Original Version, by changing formats or by porting the Font Software to 27 | a new environment. 28 | 29 | "Copyright Holder(s)" refers to all individuals and companies who have a 30 | copyright ownership of the Font Software. 31 | 32 | "Substantially Changed" refers to Modified Versions which can be easily 33 | identified as dissimilar to the Font Software by users of the Font 34 | Software comparing the Original Version with the Modified Version. 35 | 36 | To "Propagate" a work means to do anything with it that, without 37 | permission, would make you directly or secondarily liable for 38 | infringement under applicable copyright law, except executing it on a 39 | computer or modifying a private copy. Propagation includes copying, 40 | distribution (with or without modification and with or without charging 41 | a redistribution fee), making available to the public, and in some 42 | countries other activities as well. 43 | 44 | PERMISSION & CONDITIONS 45 | This licence does not grant any rights under trademark law and all such 46 | rights are reserved. 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a 49 | copy of the Font Software, to propagate the Font Software, subject to 50 | the below conditions: 51 | 52 | 1) Each copy of the Font Software must contain the above copyright 53 | notice and this licence. These can be included either as stand-alone 54 | text files, human-readable headers or in the appropriate machine- 55 | readable metadata fields within text or binary files as long as those 56 | fields can be easily viewed by the user. 57 | 58 | 2) The font name complies with the following: 59 | (a) The Original Version must retain its name, unmodified. 60 | (b) Modified Versions which are Substantially Changed must be renamed to 61 | avoid use of the name of the Original Version or similar names entirely. 62 | (c) Modified Versions which are not Substantially Changed must be 63 | renamed to both (i) retain the name of the Original Version and (ii) add 64 | additional naming elements to distinguish the Modified Version from the 65 | Original Version. The name of such Modified Versions must be the name of 66 | the Original Version, with "derivative X" where X represents the name of 67 | the new work, appended to that name. 68 | 69 | 3) The name(s) of the Copyright Holder(s) and any contributor to the 70 | Font Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except (i) as required by this licence, (ii) to 72 | acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with 73 | their explicit written permission. 74 | 75 | 4) The Font Software, modified or unmodified, in part or in whole, must 76 | be distributed entirely under this licence, and must not be distributed 77 | under any other licence. The requirement for fonts to remain under this 78 | licence does not affect any document created using the Font Software, 79 | except any version of the Font Software extracted from a document 80 | created using the Font Software may only be distributed under this 81 | licence. 82 | 83 | TERMINATION 84 | This licence becomes null and void if any of the above conditions are 85 | not met. 86 | 87 | DISCLAIMER 88 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 89 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 90 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF 91 | COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 92 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 93 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 94 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 95 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER 96 | DEALINGS IN THE FONT SOFTWARE. 97 | -------------------------------------------------------------------------------- /lib/generate_index_js.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const path = require('path'), 6 | fs = require('fs'), 7 | mkdirp = require('mkdirp'), 8 | mustache = require('mustache'), 9 | splitField = require('./utils').splitField, 10 | deepCopy = require('./utils').deepCopy; 11 | 12 | const TEMPLATES_PATH = path.join(__dirname, '.templates'); 13 | const LICENSE_TEMPLATE_PATH = path.join(TEMPLATES_PATH, 'index.js.mustache'); 14 | 15 | 16 | /** 17 | * Write index.js to a directory 18 | * 19 | * config: 20 | * target_dir 21 | * meta_info.font.names 22 | * meta_info.font.family 23 | * meta_info.font.local 24 | */ 25 | exports.write = function(iConfig, done) { 26 | var outputConfig = getOutputConfig(deepCopy(iConfig)); 27 | saveIndex(iConfig.target_dir, outputConfig, done); 28 | }; 29 | 30 | function saveIndex(targetPath, outputConfig, done) { 31 | var err; 32 | try { 33 | var template = fs.readFileSync(LICENSE_TEMPLATE_PATH, 'utf8'); 34 | var output = mustache.render(template, outputConfig); 35 | 36 | mkdirp.sync(targetPath); 37 | 38 | var outputPath = path.join(targetPath, "index.js"); 39 | fs.writeFileSync(outputPath, output, 'utf8'); 40 | 41 | // check to make sure the index file loads correctly. 42 | var config = require(outputPath); 43 | } catch(e) { 44 | err = e; 45 | } 46 | 47 | done(err); 48 | } 49 | 50 | /* 51 | * Info fetched from http://www.webtype.com/info/articles/fonts-weights/ 52 | */ 53 | const NAME_TO_WEIGHT = { 54 | ultralight: 100, 55 | extralight: 100, 56 | light: 200, 57 | thin: 200, 58 | book: 300, 59 | demi: 300, 60 | normal: 400, 61 | regular: 400, 62 | medium: 500, 63 | semibold: 600, 64 | demibold: 600, 65 | bold: 700, 66 | black: 800, 67 | extrabold: 800, 68 | heavy: 800, 69 | extrablack: 900, 70 | fat: 900, 71 | poster: 900, 72 | ultrablack: 900, 73 | }; 74 | 75 | function getFontWeight(fontName) { 76 | for (var name in NAME_TO_WEIGHT) { 77 | var reg = new RegExp(name, 'gi'); 78 | if (reg.test(fontName)) return NAME_TO_WEIGHT[name]; 79 | } 80 | return 400; 81 | } 82 | 83 | function getFontStyle(fontName) { 84 | if (/italic/gi.test(fontName)) return "italic"; 85 | return "normal"; 86 | } 87 | 88 | function getAliases(config) { 89 | var aliases = config.aliases; 90 | var outputAliases = []; 91 | var lastAliasIndex = Object.keys(aliases).length - 1; 92 | var index = 0; 93 | for(var from in aliases) { 94 | outputAliases.push({ from: from, to: aliases[from], separator: index !== lastAliasIndex ? "," : ""}); 95 | index++; 96 | } 97 | 98 | return outputAliases; 99 | } 100 | 101 | function getFontConfigFromTtfInfo(config) { 102 | var outputFonts = []; 103 | 104 | var lastFontIndex = Object.keys(config.fonts).length - 1; 105 | var index = 0; 106 | for (var key in config.fonts) { 107 | var font = config.fonts[key]; 108 | var fontConfig = { 109 | name: key, 110 | family: config.common.font_family, 111 | weight: font.weight, 112 | style: font.style, 113 | local: '"' + font.local.join('", "') + '"', 114 | separator: index !== lastFontIndex ? "," : "" 115 | }; 116 | outputFonts.push(fontConfig); 117 | index++; 118 | } 119 | 120 | return outputFonts; 121 | } 122 | 123 | function getFontConfigFromFontname(config) { 124 | var fontNames = config.font.names; 125 | var lastFontIndex = fontNames.length - 1; 126 | var localNames = config.font.local; 127 | var fontFamily = config.font.family; 128 | 129 | if (fontNames.length !== localNames.length) { 130 | throw new Error("Number of local names must match number of font names"); 131 | } 132 | 133 | var outputFonts = []; 134 | fontNames.forEach(function(fontName, index) { 135 | fontName = fontName.trim(); 136 | var local = localNames[index].trim(); 137 | 138 | var fontConfig = { 139 | name: fontName, 140 | family: fontFamily, 141 | weight: getFontWeight(fontName), 142 | style: getFontStyle(fontName), 143 | local: [ '"' + local+ '"', '"' + local.replace(/\s+/g, '') + '"' ].join(', '), 144 | separator: index !== lastFontIndex ? "," : "" 145 | }; 146 | 147 | outputFonts.push(fontConfig); 148 | }); 149 | 150 | return outputFonts; 151 | } 152 | 153 | function getOutputConfig(config) { 154 | var metaInfo = config.meta_info; 155 | var outputConfig = { 156 | package: toString(config.package), 157 | author: toString(config.author), 158 | license: toString(config.license), 159 | font_common: toString(config.font), 160 | fonts: metaInfo.common ? getFontConfigFromTtfInfo(metaInfo) 161 | : getFontConfigFromFontname(config), 162 | aliases: getAliases(metaInfo) 163 | }; 164 | 165 | return outputConfig; 166 | } 167 | 168 | function toString(config) { 169 | return typeof config !== "undefined" ? JSON.stringify(config, null, 4).replace(/^}/gm, ' }') : ""; 170 | } 171 | 172 | 173 | -------------------------------------------------------------------------------- /scripts/create_fontpack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | const path = require('path'), 8 | strformat = require('strformat'), 9 | directory = require('../lib/directory'), 10 | generic_config 11 | = require('../lib/generic_config'), 12 | SUPPORTED_SUBSETS 13 | = require('../lib/subset').SUPPORTED_SUBSETS, 14 | program = require('commodore') 15 | .description('create a connect-fonts font pack from the .ttf fonts located in a directory') 16 | .version('0.0.1') 17 | .option('--an ', 'Author name') 18 | .loadable('an') 19 | .demand('an') 20 | .option('--ae ', 21 | 'Author email(s) - comma separated list if more than one') 22 | .loadable('ae') 23 | .demand('ae') 24 | .option('--au ', 25 | 'Author url(s) - comma separated list if more than one') 26 | .loadable('au') 27 | .option('--at ', 'Author twitter') 28 | .loadable('at') 29 | .option('--ag ', 'Author github') 30 | .loadable('ag') 31 | .option('--pn ', 'Package name') 32 | .option('--ph ', 'Project homepage URL') 33 | .option('--pr ', 'Project repo URL') 34 | .option('--pb ', 'Project bug tracker URL') 35 | .option('-c ', 'Credits') 36 | .option('--sp ', 'Source Path') 37 | .demand('sp') 38 | .option('--tp ', 'Target Path') 39 | .option('-s, --ss ', 'subset(s) to create.') 40 | .comboof('ss', SUPPORTED_SUBSETS) 41 | .load(generic_config.config_path) 42 | .parse(process.argv); 43 | 44 | 45 | series( 46 | getProjectName, 47 | getHomepage, 48 | getRepo, 49 | getBugTracker, 50 | getTargetPath, 51 | function() { 52 | var sourcePath = generic_config.resolve(process.cwd(), program.sp); 53 | var targetPath = generic_config.resolve(process.cwd(), program.tp); 54 | 55 | directory.process(sourcePath, targetPath, program, function(err) { 56 | console.log("all done!"); 57 | if (err) { 58 | console.error(String(err)); 59 | process.exit(1); 60 | } 61 | process.exit(0); 62 | }); 63 | } 64 | ); 65 | 66 | function series() { 67 | var funcs = [].slice.call(arguments, 0); 68 | 69 | next(); 70 | 71 | function next() { 72 | var func = funcs.shift(); 73 | if (!func) return; 74 | 75 | func(next); 76 | } 77 | } 78 | 79 | 80 | function getProjectName(done) { 81 | if (program.pn) return done(); 82 | 83 | program.prompt('Project name: ', function(val) { 84 | if ( ! val) return getProjectName(done); 85 | 86 | program.pn = val; 87 | done(); 88 | }); 89 | 90 | } 91 | 92 | function getHomepage(done) { 93 | if (program.ph) return done(); 94 | 95 | var homepage = program.ag ? program.ag + '/' + program.pn : ''; 96 | var promptText = strformat('Project homepage [{0}]: ', homepage); 97 | program.prompt(promptText, function(val) { 98 | program.ph = val || homepage; 99 | console.log(strformat('Homepage => {0}', program.ph)); 100 | done(); 101 | }); 102 | } 103 | 104 | function getRepo(done) { 105 | if (program.pr) return done(); 106 | 107 | var repo = program.ph ? program.ph + '.git' : ''; 108 | var promptText = strformat('Project repo [{0}]: ', repo); 109 | program.prompt(promptText, function(val) { 110 | program.pr = val || repo; 111 | console.log(strformat('Repo => {0}', program.pr)); 112 | done(); 113 | }); 114 | } 115 | 116 | function getBugTracker(done) { 117 | if (program.pb) return done(); 118 | 119 | var repo = program.ph ? program.ph + '/issues' : ''; 120 | var promptText = strformat('Project bug tracker [{0}]: ', repo); 121 | program.prompt(promptText, function(val) { 122 | program.pb = val || repo; 123 | console.log(strformat('Bug tracker => {0}', program.pb)); 124 | done(); 125 | }); 126 | } 127 | 128 | function getTargetPath(done) { 129 | if (program.tp) return done(); 130 | 131 | var targetPath = program.pn ? path.join(process.cwd(), program.pn) : __dirname; 132 | var promptText = strformat('Target directory [{0}]: ', targetPath); 133 | program.prompt(promptText, function(val) { 134 | program.tp = val || targetPath; 135 | console.log(strformat('Target directory => {0}', program.tp)); 136 | done(); 137 | }); 138 | } 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /lib/directory.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const path = require('path'), 6 | fs = require('fs'), 7 | rimraf = require('rimraf'), 8 | mkdirp = require('mkdirp'), 9 | ncp = require('ncp').ncp, 10 | url = require('url'), 11 | async = require('async'), 12 | configurator = require('./configurator'), 13 | gen_readme = require('./generate_readme'), 14 | gen_index = require('./generate_index_js'), 15 | gen_license = require('./generate_license'), 16 | gen_package_json 17 | = require('./generate_package_json'), 18 | gen_fonts_from_ttf 19 | = require('./generate_fonts_from_ttf'), 20 | gen_subsets = require('./subset'), 21 | licenses = require('./licenses'), 22 | directory_metadata 23 | = require('./directory_metadata'), 24 | normalize_filenames 25 | = require('./normalize_filenames'); 26 | 27 | /* 28 | * Process a directory. 29 | */ 30 | exports.process = function(sourcePath, targetPath, argv, done) { 31 | var fontTargetPath = path.join(getFontTargetPath(targetPath), 'default'); 32 | async.waterfall([ 33 | // prepareTarget takes sourcePath and fontTargetPath 34 | async.apply(prepareTarget, sourcePath, fontTargetPath), 35 | directory_metadata, 36 | // the rest take targetPath, argv, and metaInfo. metaInfo is passed in as 37 | // the result from directory_metadata. 38 | async.apply(getConfig, targetPath, argv), 39 | ensureRegularFontIfNeeded, 40 | generateSubsets, 41 | generateReadme, 42 | generateIndex, 43 | generateLicense, 44 | generatePackageJson, 45 | generateWebFontsFromTtf 46 | ], done); 47 | }; 48 | 49 | /* 50 | * Copy .ttf files from the source path to target path. 51 | * Create target if needed. Normalize all filenames in target. 52 | * Leaves original directory untouched. 53 | */ 54 | function prepareTarget(sourcePath, targetPath, done) { 55 | console.log("preparing target"); 56 | rimraf(targetPath, function(err) { 57 | if (err) return done(err); 58 | 59 | mkdirp(targetPath, function(err) { 60 | if (err) return done(err); 61 | 62 | function filter(name) { 63 | var copy = (name === sourcePath || /\.ttf$/.test(name)); 64 | if (!copy) { console.warn(path.basename(name) + ' is not a .ttf, skipping'); } 65 | return copy; 66 | } 67 | 68 | ncp(sourcePath, targetPath, {filter: filter}, function(err) { 69 | if (err) return done(err); 70 | 71 | normalize_filenames(targetPath, false, function(err) { 72 | if (err) return done(err); 73 | done(null, targetPath); 74 | }); 75 | }); 76 | }); 77 | }); 78 | } 79 | 80 | function getConfig(targetPath, argv, metaInfo, done) { 81 | argv.target_dir = targetPath; 82 | 83 | configurator(argv, metaInfo, function(err, config) { 84 | if (err) return done(err); 85 | console.log(JSON.stringify(config)); 86 | done(null, config); 87 | }); 88 | } 89 | 90 | 91 | function getFontTargetPath(targetPath) { 92 | return path.join(targetPath, 'fonts'); 93 | } 94 | 95 | 96 | /** 97 | * If there is more than one .ttf file in the targetPath, find the most likely 98 | * candidate to be the "regular" font. Append -regular onto the filename. 99 | */ 100 | function ensureRegularFontIfNeeded(config, done) { 101 | var metaInfo = config.meta_info; 102 | 103 | var fonts = Object.keys(metaInfo.fonts); 104 | 105 | // If there is only one font, nobody cares. 106 | if (fonts.length > 1) { 107 | var regularFont = fonts.reduce(function(foundRegularFont, fontName) { 108 | if (/-regular/.test(fontName)) return fontName; 109 | return foundRegularFont; 110 | }, null); 111 | 112 | if (!regularFont) { 113 | // no font passes the regular test, go find the most likely candidate 114 | // from the list of configs. 115 | regularFont = fonts.reduce(function(foundRegularFontConfig, fontName) { 116 | if (foundRegularFontConfig) return foundRegularFontConfig; 117 | 118 | var fontConfig = metaInfo.fonts[fontName]; 119 | if (fontConfig.style === 'normal' && 120 | [400, 500].indexOf(fontConfig.weight) > -1) { 121 | return fontName; 122 | } 123 | }, null); 124 | 125 | if (regularFont) { 126 | // found a candidate font, now rename the file and update the 127 | // configuration object. 128 | var fontConfig = metaInfo.fonts[regularFont]; 129 | var newFontName = regularFont + '-regular'; 130 | var newPath = path.join(path.dirname(fontConfig.path), newFontName + '.ttf'); 131 | 132 | console.log("renaming", path.basename(fontConfig.path), "to", path.basename(newPath)); 133 | 134 | // return now or else done is called twice. 135 | return fs.rename(fontConfig.path, newPath, function(err) { 136 | if (err) return done(err); 137 | 138 | fontConfig.path = newPath; 139 | 140 | delete metaInfo.fonts[regularFont]; 141 | metaInfo.fonts[newFontName] = fontConfig; 142 | 143 | done(null, config); 144 | }); 145 | } else { 146 | console.error("could not find regular font, continuing anyways"); 147 | } 148 | } 149 | } 150 | done(null, config); 151 | } 152 | 153 | /** 154 | * Generate subsets from the .ttf fonts located in the 'default' directory. 155 | * subsets will be in their own sibling directories. 156 | */ 157 | function generateSubsets(config, done) { 158 | console.log("generating locale specific subset"); 159 | var targetPath = config.target_dir; 160 | var metaInfo = config.meta_info; 161 | 162 | var fontTargetPath = getFontTargetPath(targetPath); 163 | var allFonts = Object.keys(metaInfo.fonts); 164 | 165 | processNext(); 166 | function processNext() { 167 | var fontName = allFonts.shift(); 168 | if (!fontName) return done(null, config); 169 | 170 | var fontInfo = metaInfo.fonts[fontName]; 171 | gen_subsets.subset(metaInfo.subsets, fontInfo.path, fontTargetPath, true, function(err, subsetPaths) { 172 | if (err) return done(err + " for " + fontInfo.path); 173 | 174 | // paths for every subset are saved to create .svg, .woff, and .eot 175 | // files. 176 | fontInfo.paths = [fontInfo.path].concat(subsetPaths); 177 | processNext(); 178 | }); 179 | } 180 | } 181 | 182 | /** 183 | * Generate the README 184 | */ 185 | function generateReadme(config, done) { 186 | console.log("generating README.md"); 187 | gen_readme.write(config, function(err) { 188 | if (err) return done(err); 189 | done(null, config); 190 | }); 191 | } 192 | 193 | /** 194 | * Generate index.js 195 | */ 196 | function generateIndex(config, done) { 197 | console.log("generating index.js"); 198 | config.meta_info.aliases = {}; 199 | gen_index.write(config, function(err) { 200 | if (err) return done(err); 201 | done(null, config); 202 | }); 203 | } 204 | 205 | /** 206 | * Generate the LICENSE 207 | */ 208 | function generateLicense(config, done) { 209 | console.log("generating LICENSE"); 210 | gen_license.write(config, function(err) { 211 | if (err) return done(err); 212 | done(null, config); 213 | }); 214 | } 215 | 216 | /** 217 | * Generate package.json 218 | */ 219 | function generatePackageJson(config, done) { 220 | console.log("generating package.json"); 221 | gen_package_json.write(config, function(err) { 222 | if (err) return done(err); 223 | done(null, config); 224 | }); 225 | } 226 | 227 | /** 228 | * Generate .woff, .eot, and .svg fonts from the .ttf in the same directory. 229 | */ 230 | function generateWebFontsFromTtf(config, done) { 231 | console.log("generating webfonts"); 232 | // First, build a list of all subdirectories, one for each created locale. 233 | // Then generate fonts for each subdirectory. 234 | var metaInfo = config.meta_info; 235 | 236 | var fontPaths = Object.keys(metaInfo.fonts).map(function(fontName) { 237 | return metaInfo.fonts[fontName].paths; 238 | }).reduce(function(allFontPaths, fontPaths) { 239 | fontPaths.forEach(function(fontPath) { 240 | allFontPaths[path.dirname(fontPath)] = true; 241 | }); 242 | 243 | return allFontPaths; 244 | }, []); 245 | 246 | fontPaths = Object.keys(fontPaths); 247 | 248 | processNext(); 249 | 250 | function processNext() { 251 | var fontPath = fontPaths.shift(); 252 | if (!fontPath) return done(null, config); 253 | 254 | gen_fonts_from_ttf.write(fontPath, fontPath, function(err) { 255 | if (err) return done(err); 256 | processNext(); 257 | }); 258 | } 259 | } 260 | 261 | -------------------------------------------------------------------------------- /lib/.templates/licenses/apache2.0: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /lib/ext/subset.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright 2010-2012, Google Inc. 4 | # Author: Raph Levien (@gmail.com) 5 | # Author: Dave Crossland (dave@understandinglimited.com) 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | # Version 1.01 Released 2012-03-27 20 | # 21 | # A script for subsetting a font, using FontForge. See README for details. 22 | 23 | import fontforge 24 | import sys 25 | import getopt 26 | import os 27 | import struct 28 | 29 | def log_namelist(nam, unicode): 30 | if nam and isinstance(unicode, int): 31 | print >> nam, "0x%0.4X" % unicode, fontforge.nameFromUnicode(unicode) 32 | 33 | def select_with_refs(font, unicode, newfont, pe = None, nam = None): 34 | newfont.selection.select(('more', 'unicode'), unicode) 35 | log_namelist(nam, unicode) 36 | if pe: 37 | print >> pe, "SelectMore(%d)" % unicode 38 | try: 39 | for ref in font[unicode].references: 40 | newfont.selection.select(('more',), ref[0]) 41 | log_namelist(nam, ref[0]) 42 | if pe: 43 | print >> pe, 'SelectMore("%s")' % ref[0] 44 | except: 45 | print 'Resolving references on u+%04x failed' % unicode 46 | 47 | def subset_font_raw(font_in, font_out, unicodes, opts): 48 | if '--namelist' in opts: 49 | # 2010-12-06 DC To allow setting namelist filenames, 50 | # change getopt.gnu_getopt from namelist to namelist= 51 | # and invert comments on following 2 lines 52 | # nam_fn = opts['--namelist'] 53 | nam_fn = font_out + '.nam' 54 | nam = file(nam_fn, 'w') 55 | else: 56 | nam = None 57 | if '--script' in opts: 58 | pe_fn = "/tmp/script.pe" 59 | pe = file(pe_fn, 'w') 60 | else: 61 | pe = None 62 | font = fontforge.open(font_in) 63 | if pe: 64 | print >> pe, 'Open("' + font_in + '")' 65 | extract_vert_to_script(font_in, pe) 66 | for i in unicodes: 67 | select_with_refs(font, i, font, pe, nam) 68 | 69 | addl_glyphs = [] 70 | if '--nmr' in opts: addl_glyphs.append('nonmarkingreturn') 71 | if '--null' in opts: addl_glyphs.append('.null') 72 | for glyph in addl_glyphs: 73 | font.selection.select(('more',), glyph) 74 | if nam: 75 | print >> nam, "0x%0.4X" % fontforge.unicodeFromName(glyph), glyph 76 | if pe: 77 | print >> pe, 'SelectMore("%s")' % glyph 78 | 79 | flags = () 80 | 81 | if '--simplify' in opts: 82 | font.simplify() 83 | font.round() 84 | flags = ('omit-instructions',) 85 | 86 | if '--strip_names' in opts: 87 | font.sfnt_names = () 88 | 89 | if '--new' in opts: 90 | font.copy() 91 | new = fontforge.font() 92 | new.encoding = font.encoding 93 | new.em = font.em 94 | new.layers['Fore'].is_quadratic = font.layers['Fore'].is_quadratic 95 | for i in unicodes: 96 | select_with_refs(font, i, new, pe, nam) 97 | new.paste() 98 | # This is a hack - it should have been taken care of above. 99 | font.selection.select('space') 100 | font.copy() 101 | new.selection.select('space') 102 | new.paste() 103 | new.sfnt_names = font.sfnt_names 104 | font = new 105 | else: 106 | font.selection.invert() 107 | print >> pe, "SelectInvert()" 108 | font.cut() 109 | print >> pe, "Clear()" 110 | 111 | if nam: 112 | print "Writing NameList", 113 | nam.close() 114 | 115 | if pe: 116 | print >> pe, 'Generate("' + font_out + '")' 117 | pe.close() 118 | os.system("fontforge -script " + pe_fn) 119 | else: 120 | font.generate(font_out, flags = flags) 121 | font.close() 122 | 123 | if '--roundtrip' in opts: 124 | # FontForge apparently contains a bug where it incorrectly calculates 125 | # the advanceWidthMax in the hhea table, and a workaround is to open 126 | # and re-generate 127 | font2 = fontforge.open(font_out) 128 | font2.generate(font_out, flags = flags) 129 | 130 | def subset_font(font_in, font_out, unicodes, opts): 131 | font_out_raw = font_out 132 | if not font_out_raw.endswith('.ttf'): 133 | font_out_raw += '.ttf'; 134 | subset_font_raw(font_in, font_out_raw, unicodes, opts) 135 | if font_out != font_out_raw: 136 | os.rename(font_out_raw, font_out) 137 | # 2011-02-14 DC this needs to only happen with --namelist is used 138 | # os.rename(font_out_raw + '.nam', font_out + '.nam') 139 | 140 | def getsubset(subset): 141 | subsets = subset.split('+') 142 | 143 | punc = [0x00a2] # cent 144 | punc += [0x00a3] # sterling 145 | punc += [0x00a5] # yen 146 | punc += [0x00a9] # copyright 147 | punc += [0x00ad] # softhyphen 148 | punc += [0x00ae] # registered 149 | punc += [0x00b4] # accuteaccent 150 | 151 | punc += [0x2010] # hyphen 152 | punc += [0x2011] # nonbreakinghyphen 153 | punc += [0x2012] # figuredash 154 | punc += [0x2013] # endash 155 | punc += [0x2014] # emdash 156 | punc += [0x2015] # horizontalbar 157 | punc += [0x2026] # horizontalellipsis 158 | punc += [0x20ac] # euro 159 | punc += [0x2122] # trademark 160 | punc += [0x25a0] # blacksquare 161 | 162 | quotes = [0x2013] # endash 163 | quotes += [0x2014] # emdash 164 | quotes += [0x2018] # quoteleft 165 | quotes += [0x2019] # quoteright 166 | quotes += [0x201A] # quotesinglbase 167 | quotes += [0x201C] # quotedblleft 168 | quotes += [0x201D] # quotedblright 169 | quotes += [0x201E] # quotedblbase 170 | quotes += [0x2022] # bullet 171 | quotes += [0x2039] # guilsinglleft 172 | quotes += [0x203A] # guilsinglright 173 | 174 | basic = range(0x20, 0x7f) # Basic Latin (A-Z, a-z, numbers) 175 | 176 | latin = range(0xa0, 0x100) # Western European symbols and diacritics 177 | latin += [0x20ac] # Euro 178 | latin += [0x0152] # OE 179 | latin += [0x0153] # oe 180 | latin += [0x003b] # semicolon 181 | latin += [0x00b7] # periodcentered 182 | latin += [0x0131] # dotlessi 183 | latin += [0x02c6] # circumflex 184 | latin += [0x02da] # ring 185 | latin += [0x02dc] # tilde 186 | latin += [0x2074] # foursuperior 187 | latin += [0x2215] # divison slash 188 | latin += [0x2044] # fraction slash 189 | latin += [0xe0ff] # PUA: Font logo 190 | latin += [0xeffd] # PUA: Font version number 191 | latin += [0xf000] # PUA: font ppem size indicator: run `ftview -f 1255 10 Ubuntu-Regular.ttf` to see it in action! 192 | 193 | dieresis = [0x00a8] # dieresis * 194 | dieresis += [0x00c4] # ADieresis * 195 | dieresis += [0x00cb] # Edieresis * 196 | dieresis += [0x00cf] # Idieresis * 197 | dieresis += [0x00d6] # ODieresis * 198 | dieresis += [0x00dc] # Udieresis * 199 | dieresis += [0x00e4] # adieresis * 200 | dieresis += [0x00eb] # edieresis * 201 | dieresis += [0x00ef] # idieresis * 202 | dieresis += [0x00f6] # odieresis * 203 | dieresis += [0x00fc] # udieresis * 204 | dieresis += [0x00ff] # ydieresis * 205 | 206 | circumflex = [0x00c2] # Acircumflex * 207 | circumflex += [0x00ca] # Ecircumflex * 208 | circumflex += [0x00ce] # Icircumflex * 209 | circumflex += [0x00d4] # Ocircumflex * 210 | circumflex += [0x00db] # Ucircumflex * 211 | circumflex += [0x00e2] # acircumflex * 212 | circumflex += [0x00ea] # ecircumflex * 213 | circumflex += [0x00ee] # icircumflex * 214 | circumflex += [0x00f4] # ocircumflex * 215 | circumflex += [0x00fb] # ucircumflex * 216 | circumflex += [0x02c6] # circumflex * 217 | 218 | acute = [0x00c1] # Aacute * 219 | acute += [0x00c9] # Eacute * 220 | acute += [0x00cd] # Iacute * 221 | acute += [0x00d3] # Oacute * 222 | acute += [0x00da] # Uacute * 223 | acute += [0x00e1] # aacute * 224 | acute += [0x00e9] # eacute * 225 | acute += [0x00ed] # iacute * 226 | acute += [0x00f3] # oacute * 227 | acute += [0x00fa] # uacute * 228 | 229 | result = quotes 230 | if 'ascii' in subset: 231 | result += basic 232 | if 'en' in subset: 233 | result += basic 234 | result += punc 235 | if 'es' in subset: 236 | result += basic 237 | result += punc 238 | result += acute 239 | result += [0x00a1] # exclamdown * 240 | result += [0x00a8] # dieresis * 241 | result += [0x00ab] # guillemotleft * 242 | 243 | result += [0x00b8] # cedilla * 244 | result += [0x00bb] # guillemotright * 245 | result += [0x00bf] # questiondown * 246 | result += [0x00d1] # Ntilde * 247 | result += [0x00dc] # UDieresis * 248 | result += [0x00f1] # ntilde * 249 | result += [0x00fc] # udieresis * 250 | result += [0x0131] # dotlessi * 251 | result += [0x02c6] # circumflex * 252 | result += [0x02da] # ring * 253 | result += [0x02dc] # tilde * 254 | 255 | # Taken from: 256 | # http://en.wikipedia.org/wiki/Portuguese_alphabet 257 | # http://symbolcodes.tlt.psu.edu/bylanguage/portuguese.html 258 | if 'pt' in subset: 259 | result += basic 260 | result += punc 261 | result += acute 262 | 263 | result += [0x00ab] # guillemotleft * 264 | result += [0x00bb] # guillemotright * 265 | 266 | result += [0x00aa] # ordfeminine * 267 | result += [0x00ba] # ordmasculine * 268 | 269 | result += [0x00c2] # Acircumflex * 270 | result += [0x00ca] # Ecircumflex * 271 | result += [0x00d4] # Ocircumflex * 272 | result += [0x00e2] # acircumflex * 273 | result += [0x00ea] # ecircumflex * 274 | result += [0x00f4] # ocircumflex * 275 | 276 | result += [0x00c3] # Atilde * 277 | result += [0x00e3] # atilde * 278 | result += [0x00d5] # Otilde * 279 | result += [0x00f5] # otilde * 280 | 281 | result += [0x00dc] # Udiaeresis * 282 | result += [0x00fc] # udiaeresis * 283 | 284 | result += [0x00c0] # Agrave * 285 | result += [0x00e0] # agrave * 286 | result += [0x00d2] # Ograve * 287 | result += [0x00f2] # ograve * 288 | 289 | result += [0x00b8] # cedilla * 290 | result += [0x00c7] # Ccedilla * 291 | result += [0x00e7] # ccedilla * 292 | 293 | if 'de' in subset: 294 | result += basic 295 | result += punc 296 | result += dieresis 297 | result += [0x00ab] # guillemotleft * 298 | 299 | result += [0x00b8] # cedilla * 300 | result += [0x00bb] # guillemotright * 301 | result += [0x00df] # germandbls * 302 | result += [0x02c6] # circumflex * 303 | result += [0x02da] # ring * 304 | result += [0x02dc] # tilde * 305 | 306 | if 'fr' in subset: 307 | result += basic 308 | result += punc 309 | result += dieresis 310 | result += circumflex 311 | result += [0x00a0] # uni00A0 * 312 | result += [0x00ab] # guillemotleft * 313 | result += [0x00b8] # cedilla * 314 | result += [0x00bb] # guillemotright * 315 | result += [0x00c0] # Agrave * 316 | result += [0x00c6] # AE 317 | result += [0x00c7] # Ccedillia * 318 | result += [0x00c8] # Egrave * 319 | result += [0x00c9] # Eacute * 320 | result += [0x00d9] # Ugrave * 321 | result += [0x00e0] # agrave * 322 | result += [0x00e6] # AE 323 | result += [0x00e7] # ccedillia * 324 | result += [0x00e8] # egrave * 325 | result += [0x00e9] # eacute * 326 | result += [0x00f9] # ugrave * 327 | 328 | result += [0x0131] # dotlessi * 329 | result += [0x0152] # OE * 330 | result += [0x0153] # oe * 331 | 332 | result += [0x02da] # ring * 333 | result += [0x02dc] # tilde * 334 | 335 | 336 | if 'latin' in subset: 337 | result += basic 338 | result += latin 339 | if 'latin-ext' in subset: 340 | # These ranges include Extended A, B, C, D, and Additional with the 341 | # exception of Vietnamese, which is a separate range 342 | result += (range(0x100, 0x370) + 343 | range(0x1d00, 0x1ea0) + 344 | range(0x1ef2, 0x1f00) + 345 | range(0x2070, 0x20d0) + 346 | range(0x2c60, 0x2c80) + 347 | range(0xa700, 0xa800)) 348 | if 'vietnamese' in subset: 349 | # 2011-07-16 DC: Charset from http://vietunicode.sourceforge.net/charset/ + U+1ef9 from Fontaine 350 | result += [0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00C8, 0x00C9, 351 | 0x00CA, 0x00CC, 0x00CD, 0x00D2, 0x00D3, 0x00D4, 352 | 0x00D5, 0x00D9, 0x00DA, 0x00DD, 0x00E0, 0x00E1, 353 | 0x00E2, 0x00E3, 0x00E8, 0x00E9, 0x00EA, 0x00EC, 354 | 0x00ED, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F9, 355 | 0x00FA, 0x00FD, 0x0102, 0x0103, 0x0110, 0x0111, 356 | 0x0128, 0x0129, 0x0168, 0x0169, 0x01A0, 0x01A1, 357 | 0x01AF, 0x01B0, 0x20AB] + range(0x1EA0, 0x1EFA) 358 | if 'greek' in subset: 359 | # Could probably be more aggressive here and exclude archaic characters, 360 | # but lack data 361 | result += range(0x370, 0x400) 362 | if 'greek-ext' in subset: 363 | result += range(0x370, 0x400) + range(0x1f00, 0x2000) 364 | if 'cyrillic' in subset: 365 | # Based on character frequency analysis 366 | result += range(0x400, 0x460) + [0x490, 0x491, 0x4b0, 0x4b1] 367 | if 'cyrillic-ext' in subset: 368 | result += (range(0x400, 0x530) + 369 | [0x20b4] + 370 | range(0x2de0, 0x2e00) + 371 | range(0xa640, 0xa6a0)) 372 | if 'arabic' in subset: 373 | # Based on Droid Arabic Kufi 1.0 374 | result += [0x000D, 0x0020, 0x0621, 0x0627, 0x062D, 375 | 0x062F, 0x0631, 0x0633, 0x0635, 0x0637, 0x0639, 376 | 0x0643, 0x0644, 0x0645, 0x0647, 0x0648, 0x0649, 377 | 0x0640, 0x066E, 0x066F, 0x0660, 0x0661, 0x0662, 378 | 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 379 | 0x0669, 0x06F4, 0x06F5, 0x06F6, 0x06BE, 0x06D2, 380 | 0x06A9, 0x06AF, 0x06BA, 0x066A, 0x061F, 0x060C, 381 | 0x061B, 0x066B, 0x066C, 0x066D, 0x064B, 0x064D, 382 | 0x064E, 0x064F, 0x064C, 0x0650, 0x0651, 0x0652, 383 | 0x0653, 0x0654, 0x0655, 0x0670, 0x0656, 0x0615, 384 | 0x0686, 0x0623, 0x0625, 0x0622, 0x0671, 0x0628, 385 | 0x067E, 0x062A, 0x062B, 0x0679, 0x0629, 0x062C, 386 | 0x062E, 0x0630, 0x0688, 0x0632, 0x0691, 0x0698, 387 | 0x0634, 0x0636, 0x0638, 0x063A, 0x0641, 0x0642, 388 | 0x0646, 0x06D5, 0x06C0, 0x0624, 0x064A, 0x06CC, 389 | 0x06D3, 0x0626, 0x06C2, 0x06C1, 0x06C3, 0x06F0, 390 | 0x06F1, 0x06F2, 0x06F3, 0x06F9, 0x06F7, 0x06F8, 391 | 0xFC63, 0x0672, 0x0673, 0x0675, 0x0676, 0x0677, 392 | 0x0678, 0x067A, 0x067B, 0x067C, 0x067D, 0x067F, 393 | 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 394 | 0x0687, 0x0689, 0x068A, 0x068B, 0x068C, 0x068D, 395 | 0x068E, 0x068F, 0x0690, 0x0692, 0x0693, 0x0694, 396 | 0x0695, 0x0696, 0x0697, 0x0699, 0x069A, 0x069B, 397 | 0x069C, 0x069D, 0x069E, 0x069F, 0x06A0, 0x06A1, 398 | 0x06A2, 0x06A3, 0x06A5, 0x06A6, 0x06A7, 0x06A8, 399 | 0x06AA, 0x06AB, 0x06AC, 0x06AD, 0x06AE, 0x06B0, 400 | 0x06B1, 0x06B2, 0x06B3, 0x06B4, 0x06B5, 0x06B6, 401 | 0x06B7, 0x06B8, 0x06B9, 0x06BB, 0x06BC, 0x06BD, 402 | 0x06BF, 0x06C4, 0x06C5, 0x06CD, 0x06D6, 0x06D7, 403 | 0x06D8, 0x06D9, 0x06DA, 0x06DB, 0x06DC, 0x06DF, 404 | 0x06E1, 0x06E2, 0x06E3, 0x06E4, 0x06E5, 0x06E6, 405 | 0x06E7, 0x06E8, 0x06EA, 0x06EB, 0x06ED, 0x06FB, 406 | 0x06FC, 0x06FD, 0x06FE, 0x0600, 0x0601, 0x0602, 407 | 0x0603, 0x060E, 0x060F, 0x0610, 0x0611, 0x0612, 408 | 0x0613, 0x0614, 0x0657, 0x0658, 0x06EE, 0x06EF, 409 | 0x06FF, 0x060B, 0x061E, 0x0659, 0x065A, 0x065B, 410 | 0x065C, 0x065D, 0x065E, 0x0750, 0x0751, 0x0752, 411 | 0x0753, 0x0754, 0x0755, 0x0756, 0x0757, 0x0758, 412 | 0x0759, 0x075A, 0x075B, 0x075C, 0x075D, 0x075E, 413 | 0x075F, 0x0760, 0x0761, 0x0762, 0x0763, 0x0764, 414 | 0x0765, 0x0766, 0x0767, 0x0768, 0x0769, 0x076A, 415 | 0x076B, 0x076C, 0x076D, 0x06A4, 0x06C6, 0x06C7, 416 | 0x06C8, 0x06C9, 0x06CA, 0x06CB, 0x06CF, 0x06CE, 417 | 0x06D0, 0x06D1, 0x06D4, 0x06FA, 0x06DD, 0x06DE, 418 | 0x06E0, 0x06E9, 0x060D, 0xFD3E, 0xFD3F, 0x25CC, 419 | # Added from https://groups.google.com/d/topic/googlefontdirectory-discuss/MwlMWMPNCXs/discussion 420 | 0x063b, 0x063c, 0x063d, 0x063e, 0x063f, 0x0620, 421 | 0x0674, 0x0674, 0x06EC] 422 | return result 423 | 424 | # code for extracting vertical metrics from a TrueType font 425 | 426 | class Sfnt: 427 | def __init__(self, data): 428 | version, numTables, _, _, _ = struct.unpack('>IHHHH', data[:12]) 429 | self.tables = {} 430 | for i in range(numTables): 431 | tag, checkSum, offset, length = struct.unpack('>4sIII', data[12 + 16 * i: 28 + 16 * i]) 432 | self.tables[tag] = data[offset: offset + length] 433 | 434 | def hhea(self): 435 | r = {} 436 | d = self.tables['hhea'] 437 | r['Ascender'], r['Descender'], r['LineGap'] = struct.unpack('>hhh', d[4:10]) 438 | return r 439 | 440 | def os2(self): 441 | r = {} 442 | d = self.tables['OS/2'] 443 | r['fsSelection'], = struct.unpack('>H', d[62:64]) 444 | r['sTypoAscender'], r['sTypoDescender'], r['sTypoLineGap'] = struct.unpack('>hhh', d[68:74]) 445 | r['usWinAscender'], r['usWinDescender'] = struct.unpack('>HH', d[74:78]) 446 | return r 447 | 448 | def set_os2(pe, name, val): 449 | print >> pe, 'SetOS2Value("' + name + '", %d)' % val 450 | 451 | def set_os2_vert(pe, name, val): 452 | set_os2(pe, name + 'IsOffset', 0) 453 | set_os2(pe, name, val) 454 | 455 | # Extract vertical metrics data directly out of font file, and emit 456 | # script code to set the values in the generated font. This is a (rather 457 | # ugly) workaround for the issue described in: 458 | # http://sourceforge.net/mailarchive/forum.php?thread_name=20100906085718.GB1907%40khaled-laptop&forum_name=fontforge-users 459 | 460 | def extract_vert_to_script(font_in, pe): 461 | data = file(font_in, 'rb').read() 462 | sfnt = Sfnt(data) 463 | hhea = sfnt.hhea() 464 | os2 = sfnt.os2() 465 | set_os2_vert(pe, "WinAscent", os2['usWinAscender']) 466 | set_os2_vert(pe, "WinDescent", os2['usWinDescender']) 467 | set_os2_vert(pe, "TypoAscent", os2['sTypoAscender']) 468 | set_os2_vert(pe, "TypoDescent", os2['sTypoDescender']) 469 | set_os2_vert(pe, "HHeadAscent", hhea['Ascender']) 470 | set_os2_vert(pe, "HHeadDescent", hhea['Descender']) 471 | 472 | def main(argv): 473 | optlist, args = getopt.gnu_getopt(argv, '', ['string=', 'strip_names', 474 | 'simplify', 'new', 'script', 475 | 'nmr', 'roundtrip', 'subset=', 476 | 'namelist', 'null']) 477 | 478 | font_in, font_out = args 479 | opts = dict(optlist) 480 | if '--string' in opts: 481 | subset = map(ord, opts['--string']) 482 | else: 483 | subset = getsubset(opts.get('--subset', 'latin')) 484 | subset_font(font_in, font_out, subset, opts) 485 | 486 | if __name__ == '__main__': 487 | main(sys.argv[1:]) 488 | --------------------------------------------------------------------------------