123 lines
4.0 KiB
JavaScript
123 lines
4.0 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const mime = require('mime'); //需要安装
|
|
const { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom'); //需要安装
|
|
const xmlFormatter = require('xml-formatter'); //需要安装
|
|
const sharp = require('sharp'); //需要安装
|
|
|
|
const svgNS = 'http://www.w3.org/2000/svg';
|
|
|
|
class Icon {
|
|
constructor(file, dir) {
|
|
this.fileName = file;
|
|
this.directory = dir;
|
|
this.buffer = fs.readFileSync(this.path());
|
|
this.sharp = sharp(this.buffer);
|
|
this.init();
|
|
}
|
|
async init() {
|
|
this.buffer = await this.sharp.metadata();
|
|
const {data, info} = await this.sharp.webp({ nearLossless : true}) //近似无损,而非绝对无损
|
|
.toBuffer({ resolveWithObject: true });
|
|
this.webpBuffer = data;
|
|
this.webpInfo = info;
|
|
}
|
|
path() {
|
|
return path.join(this.directory, this.fileName);
|
|
}
|
|
base64() {
|
|
return `data:${mime.getType(this.fileName)};base64,${this.buffer.toString('base64')}`;
|
|
}
|
|
webpBase64() {
|
|
return `data:${mime.getType('webp')};base64,${this.webpBuffer.toString('base64')}`;
|
|
}
|
|
}
|
|
|
|
|
|
//const dt = new DOMImplementation().createDocumentType('svg:svg', '-//W3C//DTD SVG 1.1//EN', 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd');
|
|
//const svg = new DOMImplementation().createDocument(svgNS, 'svg', dt);
|
|
|
|
async function main({directory, idPre, svgFilename, rectFunc}) {
|
|
const files = fs.readdirSync(directory);
|
|
|
|
const iconArr = files.map(file=>new Icon(file, directory));
|
|
|
|
for (const icon of iconArr) {
|
|
await icon.init();
|
|
}
|
|
|
|
iconArr.sort((a,b)=>{
|
|
function nameNum(fileName){
|
|
let fileNameWithOutExtName = path.parse(fileName).name;
|
|
let reg = /^(\D*)(\d+)/.exec(fileNameWithOutExtName);
|
|
let parse = {str: "", num: 0};
|
|
if (reg) {
|
|
parse.str = reg[1];
|
|
parse.num = parseInt(reg[2], 10);
|
|
} else {
|
|
parse.str = fileNameWithOutExtName;
|
|
}
|
|
return parse;
|
|
}
|
|
let pa = nameNum(a.fileName), pb = nameNum(b.fileName);
|
|
return pa.str.localeCompare(pb.str) || (pa.num - pb.num);
|
|
});
|
|
|
|
const svgDoc = new DOMImplementation().createDocument(svgNS, 'svg');
|
|
|
|
let heightSum = 0;
|
|
for (let i = 0; i < iconArr.length; i++)
|
|
{
|
|
const icon = iconArr[i];
|
|
console.log('正在处理 %s %s', directory, icon.fileName);
|
|
const parseName = path.parse(icon.fileName);
|
|
const regRes = /^(\d+)(.*)$/ig.exec(parseName.name);
|
|
const iconId = regRes ? `${parseInt(regRes[1])}${regRes[2]}` : parseName.name;
|
|
const symbolId = `${idPre}-${iconId}`;
|
|
|
|
const imgWidth = icon.webpInfo.width, imgHeight = icon.webpInfo.height;
|
|
const {x=0, y=0, width=imgWidth, height=imgHeight} = rectFunc ? rectFunc(imgWidth, imgHeight, parseName.name) : {};
|
|
const image = svgDoc.createElement('image');
|
|
image.setAttribute('width', imgWidth);
|
|
image.setAttribute('height', imgHeight);
|
|
image.setAttribute('href', icon.webpBase64());
|
|
|
|
const symbol = svgDoc.createElement('symbol');
|
|
symbol.setAttribute('id', symbolId);
|
|
symbol.setAttribute('viewBox', `${x} ${y} ${width} ${height}`);
|
|
symbol.appendChild(image);
|
|
svgDoc.documentElement.appendChild(symbol);
|
|
|
|
const use = svgDoc.createElement('use');
|
|
use.setAttribute('href',`#${symbolId}`);
|
|
use.setAttribute('width', width);
|
|
use.setAttribute('height', height);
|
|
use.setAttribute('y', heightSum);
|
|
svgDoc.documentElement.appendChild(use);
|
|
|
|
const text = svgDoc.createElement('text');
|
|
text.textContent = symbolId;
|
|
text.setAttribute('dominant-baseline', "hanging");
|
|
text.setAttribute('x', width);
|
|
text.setAttribute('y', heightSum);
|
|
svgDoc.documentElement.appendChild(text);
|
|
|
|
heightSum += height;
|
|
}
|
|
svgDoc.documentElement.setAttribute('height', heightSum);
|
|
|
|
const serialized = new XMLSerializer().serializeToString(svgDoc);
|
|
const formattedXml = xmlFormatter(serialized, {
|
|
indentation: '\t',
|
|
filter: (node) => node.type !== 'Comment',
|
|
collapseContent: true,
|
|
lineSeparator: '\n'
|
|
});
|
|
fs.writeFileSync(svgFilename, formattedXml);
|
|
}
|
|
|
|
const tasks = [
|
|
{directory: './icons', idPre: 'i', svgFilename: './icons-symbol.svg'},
|
|
];
|
|
|
|
tasks.forEach(main); |