@sctg/fontminify
Version:
Minify font seamlessly, font subsetter, webfont (eot, woff, svg) converter.
206 lines (180 loc) • 4.64 kB
text/typescript
/**
* @file css
* @author junmer eltorio
*/
/* eslint-env node */
import _ from "lodash";
import * as fs from "fs";
import * as path from "path";
import isTtf from "is-ttf";
import through from "through2";
import replaceExt from "replace-ext";
import {b2a} from "b3b";
import {fileURLToPath} from 'url';
import type { CssOption, FontInfo } from "../index.js";
import { TTF } from "fonteditor-core/index.js";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
type Glyph = {
code: string,
codeName: string,
name: string,
}
/**
* listUnicode
*
* @param {Array} unicode unicode
* @return {string} unicode string
*/
function listUnicode(unicode) {
return unicode
.map(u => {
return "\\" + u.toString(16);
})
.join(",");
}
/**
* get glyf list from ttf obj
*
* @param {ttfObject} ttf ttfObject
* @return {Object} icon obj
*/
function getGlyfList(ttf:TTF.TTFObject) {
const glyfList = [] as Glyph[];
// exclude empty glyf
const filtered = ttf.glyf.filter(g => {
return (
g.name !== ".notdef" &&
g.name !== ".null" &&
g.name !== "nonmarkingreturn" &&
g.unicode &&
g.unicode.length
);
});
// format glyf info
filtered.forEach(g => {
glyfList.push({
code: "&#x" + g.unicode[0].toString(16) + ";",
codeName: listUnicode(g.unicode),
name: g.name || "uni" + g.unicode[0].toString(16),
});
});
return {
glyfList: glyfList,
};
}
/**
* get font family name
*
* @param {Object} fontInfo font info object
* @param {ttfObject} ttf ttfObject
* @param {Object} opts opts
* @return {string} font family name
*/
function getFontFamily(fontInfo, ttf, opts) {
let fontFamily = opts.fontFamily;
// Call transform function
if (typeof fontFamily === "function") {
fontFamily = fontFamily(_.cloneDeep(fontInfo), ttf);
}
return fontFamily || ttf.name.fontFamily || fontInfo.fontFile;
}
/**
* Transform font family name
* @callback FontFamilyTransform
* @param {Object} font info object
* @param {ttfObject} ttf ttfObject
* @return {string} font family name
*/
// function(fontInfo, ttfObject) { return "Font Name"; }
/**
* css fontmin plugin
*
* @param {(string|FontFamilyTransform)=} opts.fontFamily fontFamily
* @return {Object} stream.Transform instance
* @api public
*/
export default (_opts?: CssOption) => {
const opts = _opts || {};
return through.ctor(
{
objectMode: true,
},
function (file, enc, cb) {
// check null
if (file.isNull()) {
cb(null, file);
return;
}
// check stream
if (file.isStream()) {
cb(new Error("Streaming is not supported"));
return;
}
// check ttf
if (!isTtf(file.contents)) {
cb(null, file);
return;
}
// clone
this.push(file.clone(false));
file.path = replaceExt(file.path, ".css");
const fontFile = opts.filename || path.basename(file.path, ".css");
// font data
const fontInfo = {
fontFile: fontFile,
fontPath: "",
base64: false,
glyph: false,
iconPrefix: "icon",
local: false,
} as FontInfo;
// opts
_.extend(fontInfo, opts);
// ttf obj
const ttfObject = file.ttfObject || {
name: {},
};
// glyph
if (opts.glyph && ttfObject.glyf) {
_.extend(fontInfo, getGlyfList(ttfObject));
}
// font family
fontInfo.fontFamily = getFontFamily(fontInfo, ttfObject, opts);
// rewrite font family as filename
if (opts.asFileName) {
fontInfo.fontFamily = fontFile;
}
// base64
if (opts.base64) {
fontInfo.base64 =
"" +
"data:application/x-font-ttf;charset=utf-8;base64," +
b2a(file.contents);
}
// local
// if (fontInfo.local === true) {
// fontInfo.local = fontInfo.fontFamily as string;
// }
// render
const output = _.attempt(data => {
let tpl = "";
if (opts.tpl && opts.tpl.length && fs.existsSync(opts.tpl)) {
tpl = fs.readFileSync(opts.tpl).toString("utf-8");
} else {
tpl = fs
.readFileSync(path.resolve(__dirname, "../lib/font-face.tpl"))
.toString("utf-8");
}
const renderCss = _.template(tpl);
return Buffer.from(renderCss(data));
}, fontInfo);
if (_.isError(output)) {
cb(output, file);
} else {
file.contents = output;
cb(null, file);
}
}
);
};