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
91 changes: 91 additions & 0 deletions src/createFontFaceSrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const path = require('path');
const stringTemplate = require('string-template');

// Templates src property by font type
const srcPropertyTemplates = {

eotIE: 'url(\'{fontPath}.eot{fontHash}\')',
eot: 'url(\'{fontPath}.eot{fontHash}#iefix\') format(\'embedded-opentype\')',
woff2: 'url(\'{fontPath}.woff2{fontHash}\') format(\'woff2\')',
woff: 'url(\'{fontPath}.woff{fontHash}\') format(\'woff\')',
ttf: 'url(\'{fontPath}.ttf{fontHash}\') format(\'truetype\')',
svg: 'url(\'{fontPath}.svg{fontHash}#${fontName}\') format(\'svg\')',

};

/**
* Get template font hash string.
*
* @param {string} fontHash use fontHash
*/
const getTemplateFontHash = (fontHash) => {

return fontHash ? `?${fontHash}` : '';

};



/**
* Creates font-face src property.
*
* @param {object} iconFont icon font properties.
* @param {object} options options of generating fonts.
* @returns {string} font-face src property.
*/
exports.createFontFaceSrcProperty = (iconFont, options) => {

// Creates font path
const fontPath = path.relative(
path.resolve(options.publishPath, options.stylesheetPath),
path.join(options.outputPath, iconFont.fontName)
).replace(/\\/g, path.posix.sep);

const srcFormats = [];

// for each formats
options.formats.forEach((format) => {

const template = srcPropertyTemplates[format];

// Is not empty template?
if (template) {

srcFormats.push(stringTemplate(template, {
fontPath,
fontHash: getTemplateFontHash(iconFont.fontHash),
fontName: iconFont.fontName
}));

}

});

// returns src property
return srcFormats.join(', ');

};

/**
* Creates font-face src property with EOT.
*
* @param {object} iconFont icon font properties.
* @param {object} options options of generating fonts.
* @returns {string} font-face src property.
*/
exports.createFontFaceSrcPropertyWithEOT = (iconFont, options) => {

// Creates font path
const fontPath = path.relative(
path.resolve(options.publishPath, options.stylesheetPath),
path.join(options.outputPath, iconFont.fontName)
);

// returns src property
return stringTemplate(srcPropertyTemplates.eotIE, {
fontPath,
fontHash: getTemplateFontHash(iconFont.fontHash),
fontName: iconFont.fontName
});

};
64 changes: 64 additions & 0 deletions src/createFonts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const path = require('path');
const glob = require('glob');
const fontGenerator = require('./font_generator');

/**
* Creates icon fonts.
*
* @param {object} iconFont icon font options.
* @param {object} rulesets Rulesets of PostCSS object.
* @param {object} options web font options.
* @returns {Promise} returns glyphs when successed.
*/
module.exports = (iconFont, rulesets, options) => {

// Empty font src?
if (!iconFont.src) {

// noop
return Promise.resolve();

}

const files = [].concat(glob.sync(iconFont.src));

// Empty files?
if (files.length === 0) {

// noop
return Promise.resolve();

}

return new Promise((resolve, reject) => {

fontGenerator({
files,
dest: path.resolve(options.outputPath),
cachePath: options.cachePath,
fontOptions: {
formats: options.formats,
fontName: iconFont.fontName,
fontHeight: options.fontHeight,
ascent: options.ascent,
descent: options.descent,
normalize: options.normalize,
centerHorizontally: options.centerHorizontally,
fixedWidth: options.fixedWidth,
fixedHash: options.fixedHash,
startUnicode: options.startUnicode,
prependUnicode: options.prependUnicode,
}
}).then((glyphs) => {

resolve(glyphs);

}).catch((error) => {

reject(error);

});

});

};
134 changes: 134 additions & 0 deletions src/createWebFontRuleSets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
const postcss = require('postcss');
const {
createFontFaceSrcProperty,
createFontFaceSrcPropertyWithEOT
} = require('./createFontFaceSrc');


/**
* Creates rulesets of web font.
*
* @param {string} iconFont target icon font.
* @param {object} rulesets Rulesets of PostCSS object.
* @param {object} glyphs glyphs of web font.
* @param {object} options generating font options.
*/
module.exports = (iconFont, rulesets, glyphs, options) => {

// inserts new src property
rulesets.fontFaceRule.insertAfter(iconFont.srcDecl, postcss.decl({
prop: 'src',
value: createFontFaceSrcProperty(iconFont, options)
}));

// No contains eot format?
if (options.formats.indexOf('eot') === -1) {

// Remove src property
iconFont.srcDecl.remove();

} else {

// Replace src property
iconFont.srcDecl.replaceWith({
prop: 'src',
value: createFontFaceSrcPropertyWithEOT(iconFont, options)
});

}

// creates class prefix names
const useClassNamePrefix = options.classNamePrefix ? `${options.classNamePrefix}-` : '';
const useClassNamePrefixBefore = options.classNamePrefixBefore ? `${options.classNamePrefixBefore}-` : '';
const useClassNamePrefixAfter = options.classNamePrefixAfter ? `${options.classNamePrefixAfter}-` : '';

// append base ruleset
const iconRule = postcss.rule({
selectors: [
`[class^='${useClassNamePrefix}${iconFont.fontName}-']::before`,
`[class*=' ${useClassNamePrefix}${iconFont.fontName}-']::before`,
`[class^='${useClassNamePrefix}${useClassNamePrefixBefore}${iconFont.fontName}-']::before`,
`[class*=' ${useClassNamePrefix}${useClassNamePrefixBefore}${iconFont.fontName}-']::before`,
`[class^='${useClassNamePrefix}${useClassNamePrefixAfter}${iconFont.fontName}-']::after`,
`[class*=' ${useClassNamePrefix}${useClassNamePrefixAfter}${iconFont.fontName}-']::after`,
]
});
iconRule.append({
prop: 'font-family',
value: `'${iconFont.fontName}', sans-serif`
},
{
prop: 'font-style',
value: 'normal'
},
{
prop: 'font-weight',
value: 'normal'
},
{
prop: 'font-variant',
value: 'normal'
},
{
prop: 'text-transform',
value: 'none'
},
{
prop: 'line-height',
value: '1'
},
{
prop: 'vertical-align',
value: `${options.verticalAlign}`
},
{
prop: '-webkit-font-smoothing',
value: 'antialiased'
},
{
prop: '-moz-osx-font-smoothing',
value: 'grayscale'
}
);
rulesets.root.insertAfter(rulesets.fontFaceRule, iconRule);


let baseRule = iconRule;

// append glyphs
glyphs.forEach((glyph) => {

[
{
prefix: useClassNamePrefix,
pseudo: 'before',
},
{
prefix: `${useClassNamePrefix}${useClassNamePrefixBefore}`,
pseudo: 'before',
},
{
prefix: `${useClassNamePrefix}${useClassNamePrefixAfter}`,
pseudo: 'after',
},
].forEach((classNamingConvention) => {

const fontRule = postcss.rule({
selector: `.${classNamingConvention.prefix}${iconFont.fontName}-${glyph.name}::${classNamingConvention.pseudo}`,
});
fontRule.append({
prop: 'content',
value: glyph.content
});

// insert ruleset
rulesets.root.insertAfter(baseRule, fontRule);

// replace base ruleset
baseRule = fontRule;

});

});

};
58 changes: 58 additions & 0 deletions src/css_font_generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const path = require('path');
const fontGenerator = require('./font_generator');


/**
* Process generating web fonts of rulesets.
*
* @param {object} root root of PostCSS.
* @param {object} options options of generating fonts.
* @returns {Promise} promise object.
*/
module.exports = async(root, options) => {
const contents = [];

root.walkDecls(
'content',
decl => {
if (!options.contentIconMatch.exec(decl.value)) return;

contents.push({
decl,
file: decl.value.replace(/^\s*url\(['"]?([^'")]+)['"]?\)/, '$1')
});

}
);

if (!contents.length) return;
const files = Array.from(new Set(contents.map(({ file }) => file)));

const fontResult = await fontGenerator({
files,
dest: path.resolve(options.outputPath),
cachePath: options.cachePath,
fontOptions: {
formats: options.formats,
fontName: options.fontName,
fontHeight: options.fontHeight,
ascent: options.ascent,
descent: options.descent,
normalize: options.normalize,
centerHorizontally: options.centerHorizontally,
fixedWidth: options.fixedWidth,
fixedHash: options.fixedHash,
startUnicode: options.startUnicode,
prependUnicode: options.prependUnicode,
}
});

if (!fontResult) return;

fontResult.glyphs.forEach(glyph => contents
.filter(({ file }) => file === glyph.file)
.forEach(({ decl }) => {
decl.value = `'\\${glyph.codepoint.toString(16).toUpperCase()}'`;
})
);
};
Loading