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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"extends": ["airbnb-base", "plugin:node/recommended"],
"plugins": ["node"],
"extends": ["airbnb-base", "plugin:node/recommended", "plugin:unicorn/recommended"],
"plugins": ["node", "unicorn"],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
Expand All @@ -9,6 +9,7 @@
"no-underscore-dangle": 0,
"no-shadow": 0,
"no-use-before-define": 0,
"import/extensions": [2, { "js": "always" }]
"import/extensions": [2, { "js": "always" }],
"unicorn/prevent-abbreviations": 0
}
}
64 changes: 34 additions & 30 deletions lib/configure.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,42 @@
import { extname, basename, join } from 'node:path';
import { existsSync, readFileSync } from 'node:fs';

import { merge } from './helpers.js';
import { getName, getWeight, getStyle } from './fontforge.js';

export default (options) => {
const _ = {
source: options.source,
dest_dir: options.dest,
collate: options.collate || false,
};

_.extension = extname(_.source);
_.basename = basename(_.source, _.extension);
_.dest_dir = _.collate ? join(_.dest_dir, _.basename) : _.dest_dir;
_.target = join(_.dest_dir, _.basename);
_.config_file = `${_.source.replace(_.extension, '')}.json`;
_.ttf = [_.target, '.ttf'].join('');
_.eot = [_.target, '.eot'].join('');
_.svg = [_.target, '.svg'].join('');
_.woff = [_.target, '.woff'].join('');
_.woff2 = [_.target, '.woff2'].join('');
_.css = [_.target, '.css'].join('');
_.css_fontpath = '';
_.name = getName(_.source);
_.weight = getWeight(_.source);
_.style = getStyle(_.source);
_.embed = [];
export default ({
source,
dest,
collate = false,
...options
}) => {
const ext = extname(source);
const base = basename(source, ext);
const destDir = collate ? join(dest, base) : dest;
const target = join(destDir, base);

if (existsSync(_.config_file)) {
merge(_, JSON.parse(readFileSync(_.config_file)));
}
const configFile = source.replace(new RegExp(`${ext}$`), '.json');
const config = existsSync(configFile) ? JSON.parse(readFileSync(configFile)) : {};

merge(_, options);

return _;
return {
source,
dest_dir: options.dest_dir ?? config.dest_dir ?? destDir,
collate,
extension: options.extension ?? config.extension ?? ext,
basename: options.basename ?? config.basename ?? base,
target: options.target ?? config.target ?? target,
ttf: options.ttf ?? config.ttf ?? `${target}.ttf`,
eot: options.eot ?? config.eot ?? `${target}.eot`,
svg: options.svg ?? config.svg ?? `${target}.svg`,
woff: options.woff ?? config.woff ?? `${target}.woff`,
woff2: options.woff2 ?? config.woff2 ?? `${target}.woff2`,
css: options.css ?? config.css ?? `${target}.css`,
css_fontpath: options.css_fontpath ?? config.css_fontpath ?? '',
scss: options.scss ?? config.scss,
less: options.less ?? config.less,
name: options.name ?? config.name ?? getName(source),
weight: options.weight ?? config.weight ?? getWeight(source),
style: options.style ?? config.style ?? getStyle(source),
embed: options.embed ?? config.embed ?? [],
subset: options.subset ?? config.subset,
};
};
26 changes: 0 additions & 26 deletions lib/encode.js

This file was deleted.

2 changes: 1 addition & 1 deletion lib/fontfacegen.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default (options) => {
mkdirp(dirname(config.scss));
}

ttf(config.source, config.ttf, config.name, config); // TODO: better options handling
ttf(config.source, config.ttf, config.name, { subset: config.subset });
ttf2eot(config.ttf, config.eot);
ttf2svg(config.ttf, config.svg);
ttf2woff(config.ttf, config.woff);
Expand Down
76 changes: 28 additions & 48 deletions lib/fontforge.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { execSync } from 'node:child_process';
import which from 'which';

import { isLinux } from './helpers.js';
import { memoize, isLinux } from './helpers.js';
import FontFaceException from './exception.js';

const FONTFORGE = 'fontforge';
Expand All @@ -20,79 +20,59 @@ const weightTable = {
black: '900',
};

function FontForgeException(e, cmd) {
this.message = `FontForge command failed: ${e.toString()}\n`
+ `From command: ${cmd}`;
this.name = 'FontForgeException';
}

let ensured = false;
function ensureFontForge() {
if (!ensured) {
try {
which.sync(FONTFORGE);
} catch (e) {
const installCmd = isLinux() ? 'sudo apt-get install' : 'brew install';
class FontForgeException extends Error {
name = 'FontForgeException';

throw new FontFaceException(
'We are missing some required font packages.\n'
+ 'That can be installed with:\n'
+ `${installCmd} ${FONTFORGE}`,
);
}

ensured = true;
constructor(e, cmd) {
super(`FontForge command failed: ${e.toString()}\n`
+ `From command: ${cmd}`);
}
}

export default function fontforge(source, script, target, name) {
ensureFontForge();

let cmd = `${FONTFORGE} -lang=ff -c '${script}' '${source}'`;

if (target !== undefined) {
cmd += ` '${target}'`;
const ensureFontForge = memoize(() => {
try {
which.sync(FONTFORGE);
} catch {
const installCmd = isLinux() ? 'sudo apt-get install' : 'brew install';

throw new FontFaceException(
'We are missing some required font packages.\n'
+ 'That can be installed with:\n'
+ `${installCmd} ${FONTFORGE}`,
);
}
});

if (name !== undefined) {
cmd += ` '${name}'`;
}
export default function fontforge(source, script, target, name) {
ensureFontForge();

let result;
const cmd = `${FONTFORGE} -lang=ff -c '${script}' '${source}'${target !== undefined ? ` '${target}'` : ''}${name !== undefined ? ` '${name}'` : ''}`;

try {
result = execSync(cmd, { stdio: ['pipe', 'pipe', process.stderr] }).toString();
} catch (e) {
throw new FontForgeException(e, cmd);
return execSync(cmd, { stdio: ['pipe', 'pipe', process.stderr] }).toString();
} catch (error) {
throw new FontForgeException(error, cmd);
}

return result;
}

export function getName(source) {
const result = fontforge(source, 'Open($1);Print($fontname);');
if (result) {
return result.trim().replace(' ', '_');
}
return false;
return fontforge(source, 'Open($1);Print($fontname);')?.trim().replace(' ', '_') ?? false;
}

export function getWeight(source) {
const result = fontforge(source, 'Open($1);Print($weight);');
if (result) {
const weight = result.trim().replace(' ', '').toLowerCase();
if (weightTable[weight]) {
return weightTable[weight];
}
return weight;

return weightTable[weight] ?? weight;
}
return false;
}

export function getStyle(source) {
const result = fontforge(source, 'Open($1);Print($italicangle);');
if (result) {
return (parseInt(result.trim(), 10) === 0) ? 'normal' : 'italic';
return (Number.parseInt(result.trim(), 10) === 0) ? 'normal' : 'italic';
}
return false;
}
57 changes: 16 additions & 41 deletions lib/helpers.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,27 @@
import { type } from 'node:os';
import { mkdirSync } from 'node:fs';

export function has(haystack, needle) {
return haystack.indexOf(needle) !== -1;
}

export function quote(str) {
return `"${str}"`;
}

export function merge(destination, source) {
Object.keys(source).forEach((property) => {
if (Object.prototype.hasOwnProperty.call(source, property)) {
destination[property] = source[property]; // eslint-disable-line no-param-reassign
}
});
return destination;
}

export function trim(buffer) {
if (!buffer) {
return '';
}
export const memoize = (fn) => {
let res; let
called = false;

return buffer.toString().trim();
}

export function removeNewLines(buffer) {
if (!buffer) {
return '';
}
return (...args) => {
if (called) {
return res;
}

return buffer.toString().replace(/\r?\n|\r/g);
}
res = fn(...args);
called = true;
return res;
};
};

export function uniqueChars(subset) {
return (typeof subset === 'string' ? subset.split('') : subset)
.filter((ch, i, chars) => chars.indexOf(ch) === i);
}
export const quote = (str) => `"${str}"`;

export function charToHex(ch) {
return ch.charCodeAt(0).toString(16);
}
export const trim = (buffer) => buffer?.toString().trim() ?? '';

const _isLinux = type().toLowerCase() === 'linux';
export const removeNewLines = (buffer) => buffer?.toString().replace(/\r?\n|\r/g) ?? '';

export function isLinux() {
return _isLinux;
}
export const isLinux = memoize(() => type().toLowerCase() === 'linux');

export const mkdirp = (path) => mkdirSync(path, { recursive: true });
Loading