import { writeToFile } from './utils' import createUnicodes from './unicodes' const path = require('path') const fs = require('fs') const _ = require('lodash') const xmldom = require('xmldom') const DOMParser = xmldom.DOMParser // 解析为文档对象 const svgpath = require('svgpath') const svg2ttf = require('svg2ttf') const ttf2svg = require('ttf2svg') const ttf2eot = require('ttf2eot') const ttf2woff = require('ttf2woff') const ttf2woff2 = require('ttf2woff2') import { fontTemplate, fontCSSTemplate, svgSymbolTemplate, htmlTemplate, AndroidTemplate, iOSTemplate, RNTemplate } from "./template"; // font attribute doc // https://www.w3.org/TR/1999/WD-SVG-19991203/fonts.html#FontElementAscentAttribute const DEFAULT_CONFIG = { font: { id: 'svg2font', horizAdvX: 1024, vertAdvY: 1024, }, fontface: { fontFamily: 'svg2font', fontWeight: '500', fontStretch: 'normal', unitsPerEm: '1024', ascent: '896', descent: '-128' }, glyphs: [] } /** * * 字体对象 * @class Font * @support svg, ttf, eot, woff, woff2 */ export default class Font { public glyphs; public fontName; public fontFamily; public fontFamilyClass; public svgFont; public ascent; public descent; constructor ({ fontName = 'svg2font', fontFamily = 'svg2font', fontFamilyClass = 'font_family', glyphSvgs, ascent = 896, descent = -128, startCodePoint = 57344, customUnicodeList }) { this.fontName = fontName this.fontFamily = fontFamily this.fontFamilyClass = fontFamilyClass this.ascent = ascent this.descent = descent this.glyphs = this.createGlyphs(glyphSvgs, startCodePoint, customUnicodeList) const CONFIG = _.merge(DEFAULT_CONFIG, { font: { id: fontName }, fontface: { fontFamily: fontName, ascent, descent, }, glyphs: this.glyphs, }) this.svgFont = fontTemplate(DEFAULT_CONFIG) } createGlyphs (glyphSvgs, startCodePoint, customUnicodeList) { const { ascent } = this const glyphs = [] if(Array.isArray(glyphSvgs)){ let unicodes if(customUnicodeList && Array.isArray(customUnicodeList) && customUnicodeList.length >= glyphSvgs.length){ unicodes = customUnicodeList } else { unicodes = createUnicodes(glyphSvgs.length, startCodePoint) } glyphSvgs.map((data, idx) => { const d = this.getGlyphData(data, ascent) glyphs.push({ unicode: unicodes[idx], d: d[0], originDs: d[1], horizAdvX: 1024, vertAdvY: 1024, glyphName: `${this.fontName}_${idx}`}) }) return glyphs } if(Object.prototype.toString.call(glyphSvgs) === '[object Object]'){ const glyphArr = Object.keys(glyphSvgs) let unicodes if(customUnicodeList && Array.isArray(customUnicodeList) && customUnicodeList.length >= glyphArr.length){ unicodes = customUnicodeList } else { unicodes = createUnicodes(glyphArr.length, startCodePoint) } glyphArr.map((glyphName, idx) => { const d = this.getGlyphData(glyphSvgs[glyphName].path, ascent) glyphs.push({ unicode: unicodes[idx], d: d[0], originDs: d[1], horizAdvX: 1024, vertAdvY: 1024, glyphName, originName: glyphSvgs[glyphName].originName || glyphName}) }) return glyphs } if(typeof glyphSvgs === 'string'){ let unicodes if(customUnicodeList && Array.isArray(customUnicodeList) && customUnicodeList.length >= glyphSvgs.length){ unicodes = customUnicodeList } else { unicodes = createUnicodes(1, startCodePoint) } const d = this.getGlyphData(glyphSvgs, ascent) glyphs.push({ unicode: unicodes[0], d: d[0], originDs: d[1], horizAdvX: 1024, vertAdvY: 1024, glyphName: `${this.fontName}_0`}) return glyphs } } getGlyphData (data, ascent) { let d1 = '' // font fontcss let d2 = [] // symbol originD list const Node = new DOMParser().parseFromString(data, 'application/xml') Array.from(Node.documentElement.childNodes).map( (node:any) => { if (node.nodeName.toUpperCase() === 'PATH' && node.hasAttribute && node.hasAttribute('d')) { d2.push({ d: svgpath(node.getAttribute('d')).rel().round(2).toString(), fill: node.getAttribute('fill') || '' }) // SVG字体与标准图形坐标系不一致 https://www.w3.org/TR/SVG/text.html#DominantBaselineProperty const signalD = svgpath(node.getAttribute('d')).rel().round(2).scale(1, -1).translate(0, ascent).toString() if(!/z$/.test(signalD)){ d1 += signalD+'z' } else { d1 += signalD } } }) return [d1, d2] } getGlyph () { return this.svgFont } getTTF () { const ttfBuffer = Buffer.from(svg2ttf(this.svgFont, { copyright: 'Copyright (C) 2019 by original authors @ master Gao' }).buffer) return ttfBuffer } getEOT () { const ttfBuffer = this.getTTF() return ttf2eot(ttfBuffer) } getWOFF () { const ttfBuffer = this.getTTF() return Buffer.from(ttf2woff(ttfBuffer).buffer) } getWOFF2 () { const ttfBuffer = this.getTTF() return ttf2woff2(ttfBuffer) } convertFonts ({dist = './', fontTypes = ['eot', 'woff2', 'woff', 'ttf', 'svg'], css = true, symbol = true, html = true, fontCdnUrl = ''}) { const fontName = this.fontName const fontFamily = this.fontFamily const fontFamilyClass = this.fontFamilyClass const glyphs = this.glyphs fontTypes.map( format => { switch (format) { case 'svg': fs.writeFileSync(path.join(dist, `${fontName}.svg`), this.getGlyph()) break; case 'ttf': fs.writeFileSync(path.join(dist, `${fontName}.ttf`), this.getTTF()) break; case 'eot': fs.writeFileSync(path.join(dist, `${fontName}.eot`), this.getEOT()) break; case 'woff': fs.writeFileSync(path.join(dist, `${fontName}.woff`), this.getWOFF()) break; case 'woff2': fs.writeFileSync(path.join(dist, `${fontName}.woff2`), this.getWOFF2()) break; } }) if(css && fontTypes.length > 0){ const CSSTMPL = fontCSSTemplate(fontTypes, fontName, fontFamily, fontFamilyClass, glyphs, fontCdnUrl) fs.writeFileSync(path.join(dist, `${fontName}.css`), CSSTMPL) } if(symbol && fontTypes.length > 0){ const SYMBOLTMPL = svgSymbolTemplate(fontTypes, fontName, glyphs) fs.writeFileSync(path.join(dist, `${fontName}.js`), SYMBOLTMPL) } if(html && fontTypes.length > 0){ const HTMLTMPL = htmlTemplate(fontTypes, fontName, fontFamilyClass, glyphs) fs.writeFileSync(path.join(dist, `${fontName}.html`), HTMLTMPL) } const ANDROIDTMPL = AndroidTemplate(fontName, glyphs) fs.writeFileSync(path.join(dist, `icon_${fontFamily}.xml`), ANDROIDTMPL) const IOSTMPL = iOSTemplate(fontFamily, glyphs) fs.writeFileSync(path.join(dist, `JDIF_${fontFamily.replace(fontFamily[0], fontFamily[0].toUpperCase())}.h`), IOSTMPL) const RNTMPL = RNTemplate(fontFamily, glyphs) fs.writeFileSync(path.join(dist, `icon_${fontFamily}.js`), RNTMPL) } }