///
import path from 'path';
import fs from 'fs-extra';
import image2uri from 'image2uri';
import { SvgIcons2FontOptions } from 'svgicons2svgfont';
import color from 'colors-cli';
import { Config } from 'svgo';
import * as YAML from 'yaml';
import { log } from './log';
import { generateIconsSource, generateReactIcons } from './generate';
import { createSVG, createTTF, createEOT, createWOFF, createWOFF2, createSvgSymbol, copyTemplate, CSSOptions, createHTML, createTypescript, TypescriptOptions } from './utils';
export type SvgToFontOptions = {
/** A value of `false` disables logging */
log?: boolean;
/** log callback function */
logger?: (message: string) => void;
/**
* The output directory.
* @default fonts
* @example
* ```
* path.join(process.cwd(), 'fonts')
* ```
*/
dist?: string;
/**
* svg path
* @default svg
* @example
* ```
* path.join(process.cwd(), 'svg')
* ```
*/
src?: string;
/**
* The font family name you want.
* @default iconfont
*/
fontName?: string;
/**
* Create CSS/LESS/Scss/Styl files, default `true`.
*/
css?: boolean | CSSOptions;
/**
* Output `./dist/react/`, SVG generates `react` components.
*/
outSVGReact?: boolean;
/**
* Output `./dist/svgtofont.json`, The content is as follows:
* @example
* ```js
* {
* "adobe": ["M14.868 3H23v19L14.868 3zM1 3h8.8.447z...."],
* "git": ["M2.6 10.59L8.38 4.8l1.69 1.7c-.24c-.6.34-1 .99-1..."],
* "stylelint": ["M129.74 243.648c28-100.5.816c2.65..."]
* }
* ```
*/
outSVGPath?: boolean;
/**
* Output `./dist/info.json`, The content is as follows:
* @example
* ```js
* {
* "adobe": {
* "encodedCode": "\\ea01",
* "prefix": "svgtofont",
* "className": "svgtofont-adobe",
* "unicode": ""
* },
* .....
* }
* ```
*/
generateInfoData?: boolean;
/**
* This is the setting for [svgicons2svgfont](https://github.com/nfroidure/svgicons2svgfont/tree/dd713bea4f97afa59f7dba6a21ff7f22db565bcf#api)
*/
svgicons2svgfont?: SvgIcons2FontOptions;
/** Some options can be configured with svgoOptions though it. [svgo](https://github.com/svg/svgo#configuration) */
svgoOptions?: Config;
/**
* Create font class name prefix, default value font name.
* @default fontName
*/
classNamePrefix?: SvgToFontOptions['fontName'];
/**
* Symbol Name Delimiter, @default `-`
*/
symbolNameDelimiter?: string;
/**
* Directory of custom templates.
*/
styleTemplates?: string;
/**
* unicode start number
* @default 10000
*/
startUnicode?: number;
/**
* should the name(file name) be used as unicode? this switch allows for the support of ligatures.
* @default false
*/
useNameAsUnicode?: boolean;
/**
* Custom unicode map (key: filename, value: unicode or name)
*/
unicodeMap?: Record
/**
* Clear output directory contents
* @default false
*/
emptyDist?: boolean;
/**
* This is the setting for [svg2ttf](https://github.com/fontello/svg2ttf/tree/c33a126920f46b030e8ce960cc7a0e38a6946bbc#svg2ttfsvgfontstring-options---buf)
*/
svg2ttf?: unknown;
website?: {
/**
* Add a Github corner to your website
* @like https://github.com/uiwjs/react-github-corners
*/
corners?: {
/**
* @example `https://github.com/jaywcjlove/svgtofont`
*/
url?: string,
/**
* @default 60
*/
width?: number,
/**
* @default 60
*/
height?: number,
/**
* @default #151513
*/
bgColor?: '#dc3545'
},
/**
* @default unicode
*/
index?: 'font-class' | 'unicode' | 'symbol';
/**
* website title
*/
title?: string;
/**
* @example
* ```js
* path.resolve(rootPath, "favicon.png")
* ```
*/
favicon?: string;
/**
* Must be a .svg format image.
* @example
* ```js
* path.resolve(rootPath, "svg", "git.svg")
* ```
*/
logo?: string,
version?: string,
meta?: {
description?: string;
keywords?: string;
},
description?: string;
template?: string;
footerInfo?: string;
links: Array<{
title: string;
url: string;
}>;
};
/**
* Create typescript file with declarations for icon classnames
* @default false
*/
typescript?: boolean | TypescriptOptions
}
export type IconInfo = {
prefix: string;
symbol: string;
unicode: string;
className: string;
encodedCode: string | number;
}
export type InfoData = Record>
export default async (options: SvgToFontOptions = {}) => {
const confPath = path.join(process.cwd(), '.svgtofontrc');
if (fs.pathExistsSync(confPath)) {
const conf = await fs.readJson(confPath);
options = { ...options, ...conf };
} else {
// load yaml config
const confPath = path.join(process.cwd(), '.svgtofontrc.yaml');
if (fs.pathExistsSync(confPath)) {
const conf = await YAML.parse(fs.readFileSync(confPath, 'utf-8'));
options = { ...options, ...conf };
}
}
const pkgPath = path.join(process.cwd(), 'package.json');
if (fs.pathExistsSync(pkgPath)) {
const pkg = require(pkgPath);
if (pkg.svgtofont) {
options = { ...options, ...pkg.svgtofont }
}
if (options.website && pkg.version) {
options.website.version = pkg.version;
}
}
if (options.log === undefined) options.log = true;
log.disabled = !options.log;
if (options.logger && typeof options.logger === 'function') log.logger = options.logger;
options.dist = options.dist || path.resolve(process.cwd(), 'fonts');
options.src = options.src || path.resolve(process.cwd(), 'svg');
options.startUnicode = options.startUnicode || 0xea01;
options.svg2ttf = options.svg2ttf || {};
options.emptyDist = options.emptyDist;
options.fontName = options.fontName || 'iconfont';
options.svgicons2svgfont = options.svgicons2svgfont || {};
options.svgicons2svgfont.fontName = options.fontName;
options.symbolNameDelimiter = options.symbolNameDelimiter || '-';
options.classNamePrefix = options.classNamePrefix || options.fontName;
const fontSize = options.css && typeof options.css !== 'boolean' && options.css.fontSize ? options.css.fontSize : '16px';
// If you generate a font you need to generate a style.
if (options.website && !options.css) options.css = true;
const infoDataPath = path.resolve(options.dist, 'info.json');
try {
if (options.emptyDist) {
await fs.emptyDir(options.dist);
}
// Ensures that the directory exists.
await fs.ensureDir(options.dist);
const unicodeObject = await createSVG(options);
let cssString: string[] = [];
let cssToVars: string[] = [];
let cssIconHtml: string[] = [];
let unicodeHtml: string[] = [];
let symbolHtml: string[] = [];
const prefix = options.classNamePrefix || options.fontName;
const infoData: InfoData = {}
Object.keys(unicodeObject).forEach(name => {
if (!infoData[name]) infoData[name] = {};
const _code = unicodeObject[name];
let symbolName = options.classNamePrefix + options.symbolNameDelimiter + name
let iconPart = symbolName + '">';
let encodedCodes: string | number = _code.charCodeAt(0);
if (options.useNameAsUnicode) {
symbolName = name;
iconPart = prefix + '">' + name;
encodedCodes = _code.split('').map(x => x.charCodeAt(0)).join(';&#');
} else {
cssString.push(`.${symbolName}:before { content: "\\${encodedCodes.toString(16)}"; }\n`);
cssToVars.push(`$${symbolName}: "\\${encodedCodes.toString(16)}";\n`);
}
infoData[name].encodedCode = `\\${encodedCodes.toString(16)}`;
infoData[name].prefix = prefix;
infoData[name].className = symbolName;
infoData[name].unicode = `${encodedCodes};`;
cssIconHtml.push(`${name}
`);
unicodeHtml.push(`${_code}${name}
&#${encodedCodes};`);
symbolHtml.push(`
${symbolName}
`);
});
if (options.generateInfoData) {
await fs.writeJSON(infoDataPath, infoData, { spaces: 2 });
log.log(`${color.green('SUCCESS')} Created ${infoDataPath} `);
}
const ttf = await createTTF(options);
await createEOT(options, ttf);
await createWOFF(options, ttf);
await createWOFF2(options, ttf);
await createSvgSymbol(options);
if (options.css) {
const styleTemplatePath = options.styleTemplates || (!options.useNameAsUnicode ? path.resolve(__dirname, 'styles') : path.resolve(__dirname, 'ligature-styles'));
await copyTemplate(styleTemplatePath, options.dist, {
fontname: options.fontName,
cssString: cssString.join(''),
cssToVars: cssToVars.join(''),
fontSize: fontSize,
timestamp: new Date().getTime(),
prefix,
_opts: typeof options.css === 'boolean' ? {} : { ...options.css }
});
}
if (options.typescript) {
await createTypescript({ ...options, typescript: options.typescript })
}
if (options.website) {
const pageName = ['font-class', 'unicode', 'symbol'];
let fontClassPath = path.join(options.dist, 'index.html');
let unicodePath = path.join(options.dist, 'unicode.html');
let symbolPath = path.join(options.dist, 'symbol.html');
// setting default home page.
const indexName = pageName.includes(options.website.index) ? pageName.indexOf(options.website.index) : 0;
pageName.forEach((name, index) => {
const _path = path.join(options.dist, indexName === index ? 'index.html' : `${name}.html`);
if (name === 'font-class') fontClassPath = _path;
if (name === 'unicode') unicodePath = _path;
if (name === 'symbol') symbolPath = _path;
});
// default template
options.website.template = options.website.template || path.join(__dirname, 'website', 'index.ejs');
// template data
const tempData: SvgToFontOptions['website'] & {
fontname: string;
classNamePrefix: string;
_type: string;
_link: string;
_IconHtml: string;
_title: string;
} = {
meta: null,
links: null,
corners: null,
description: null,
footerInfo: null,
...options.website,
fontname: options.fontName,
classNamePrefix: options.classNamePrefix,
_type: 'font-class',
_link: `${(options.css && typeof options.css !== 'boolean' && options.css.fileName) || options.fontName}.css`,
_IconHtml: cssIconHtml.join(''),
_title: options.website.title || options.fontName
};
// website logo
if (options.website.logo && fs.pathExistsSync(options.website.logo) && path.extname(options.website.logo) === '.svg') {
tempData.logo = fs.readFileSync(options.website.logo).toString();
}
// website favicon
if (options.website.favicon && fs.pathExistsSync(options.website.favicon)) {
tempData.favicon = image2uri(options.website.favicon);
} else {
tempData.favicon = '';
}
const classHtmlStr = await createHTML(options.website.template, tempData);
fs.outputFileSync(fontClassPath, classHtmlStr);
log.log(`${color.green('SUCCESS')} Created ${fontClassPath} `);
tempData._IconHtml = unicodeHtml.join('');
tempData._type = 'unicode';
const unicodeHtmlStr = await createHTML(options.website.template, tempData);
fs.outputFileSync(unicodePath, unicodeHtmlStr);
log.log(`${color.green('SUCCESS')} Created ${unicodePath} `);
tempData._IconHtml = symbolHtml.join('');
tempData._type = 'symbol';
const symbolHtmlStr = await createHTML(options.website.template, tempData);
fs.outputFileSync(symbolPath, symbolHtmlStr);
log.log(`${color.green('SUCCESS')} Created ${unicodePath} `);
}
if (options.outSVGPath) {
const outPath = await generateIconsSource(options);
log.log(`${color.green('SUCCESS')} Created ${outPath} `);
}
if (options.outSVGReact) {
const outPath = await generateReactIcons(options);
log.log(`${color.green('SUCCESS')} Created React Components. `);
}
} catch (error) {
log.log('SvgToFont:CLI:ERR:', error);
}
}
/**
* https://github.com/Microsoft/TypeScript/issues/5565#issuecomment-155226290
*/
module.exports = exports["default"];