new changes

This commit is contained in:
Niranjan
2026-04-07 05:05:28 +05:30
parent 7c070224bd
commit a18bba15f2
29975 changed files with 3247495 additions and 2761 deletions

View File

@@ -0,0 +1 @@
export declare function fileSorter(fileA: any, fileB: any): number;

View File

@@ -0,0 +1,16 @@
const testExpression = /(^|\/|\\)(?:((?:u[0-9a-f]{4,6},?)+)-)(.+)\.svg$/i;
export function fileSorter(fileA, fileB) {
const hasUnicodeA = testExpression.test(fileA);
const hasUnicodeB = testExpression.test(fileB);
if (hasUnicodeA == hasUnicodeB) {
// just compare alphabetically
const fileA_ = fileA.substr(0, fileA.lastIndexOf('.'));
const fileB_ = fileB.substr(0, fileB.lastIndexOf('.'));
return fileA_ < fileB_ ? -1 : 1;
}
else {
// map true to 0, because we want it to be first
return (hasUnicodeA ? 0 : 1) - (hasUnicodeB ? 0 : 1);
}
}
//# sourceMappingURL=filesorter.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"filesorter.js","sourceRoot":"","sources":["../src/filesorter.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc,GAAG,kDAAkD,CAAC;AAE1E,MAAM,UAAU,UAAU,CAAC,KAAK,EAAE,KAAK;IACrC,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAE/C,IAAI,WAAW,IAAI,WAAW,EAAE,CAAC;QAC/B,8BAA8B;QAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;QACvD,OAAO,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;SAAM,CAAC;QACN,gDAAgD;QAChD,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;AACH,CAAC"}

View File

@@ -0,0 +1,19 @@
import { Readable } from 'node:stream';
import { getMetadataService, FileMetadata, MetadataServiceOptions } from './metadata.js';
export type SVGIconsDirStreamOptions = {
metadataProvider: ReturnType<typeof getMetadataService>;
};
export type SVGIconStream = Readable & {
metadata: Pick<FileMetadata, 'name' | 'unicode'>;
};
declare class SVGIconsDirStream extends Readable {
private _options;
gotFilesInfos: boolean;
fileInfos: FileMetadata[];
dir: string;
constructor(dir: string[], options: Partial<SVGIconsDirStreamOptions & MetadataServiceOptions>);
_getFilesInfos(files: any): void;
_pushSVGIcons(): void;
_read(): void;
}
export { SVGIconsDirStream };

View File

@@ -0,0 +1,93 @@
import { Readable } from 'node:stream';
import { createReadStream, readdir } from 'node:fs';
import { fileSorter } from './filesorter.js';
import { getMetadataService, } from './metadata.js';
import debug from 'debug';
const warn = debug('svgicons2svgfont');
class SVGIconsDirStream extends Readable {
_options;
gotFilesInfos = false;
fileInfos = [];
dir;
constructor(dir, options) {
super({ objectMode: true });
this._options = {
metadataProvider: options.metadataProvider || getMetadataService(options),
};
if (dir instanceof Array) {
this.dir = '';
this._getFilesInfos(dir);
}
else {
this.dir = dir;
}
}
_getFilesInfos(files) {
let filesProcessed = 0;
this.fileInfos = [];
// Ensure prefixed files come first
files = files.slice(0).sort(fileSorter);
files.forEach((file) => {
this._options.metadataProvider((this.dir ? this.dir + '/' : '') + file, (err, metadata) => {
filesProcessed++;
if (err) {
this.emit('error', err);
}
if (metadata) {
if (metadata.renamed) {
warn(' - Saved codepoint: ' +
'u' +
metadata.unicode[0]
.codePointAt(0)
?.toString(16)
.toUpperCase() +
' for the glyph "' +
metadata.name +
'"');
}
this.fileInfos.push(metadata);
}
if (files.length === filesProcessed) {
// Reorder files
this.fileInfos.sort((infosA, infosB) => infosA.unicode[0] > infosB.unicode[0] ? 1 : -1);
// Mark directory as processed
this.gotFilesInfos = true;
// Start processing
this._pushSVGIcons();
}
});
});
}
_pushSVGIcons() {
let fileInfo;
let svgIconStream;
while (this.fileInfos.length) {
fileInfo = this.fileInfos.shift();
svgIconStream = createReadStream(fileInfo.path);
svgIconStream.metadata = {
name: fileInfo.name,
unicode: fileInfo.unicode,
};
if (!this.push(svgIconStream)) {
return;
}
}
this.push(null);
}
_read() {
if (this.dir) {
readdir(this.dir, (err, files) => {
if (err) {
this.emit('error', err);
}
this._getFilesInfos(files);
});
return;
}
if (this.gotFilesInfos) {
this._pushSVGIcons();
}
}
}
export { SVGIconsDirStream };
//# sourceMappingURL=iconsdir.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"iconsdir.js","sourceRoot":"","sources":["../src/iconsdir.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,39 @@
import { Transform } from 'stream';
import { SVGPathData } from 'svg-pathdata';
export { fileSorter } from './filesorter.js';
export * from './iconsdir.js';
export * from './metadata.js';
export type SVGIcons2SVGFontStreamOptions = {
fontName: string;
fontId: string;
fixedWidth: boolean;
descent: number;
ascent?: number;
round: number;
metadata: string;
usePathBounds: boolean;
normalize?: boolean;
preserveAspectRatio?: boolean;
centerHorizontally?: boolean;
centerVertically?: boolean;
fontWeight?: number;
fontHeight?: number;
fontStyle?: string;
callback?: (glyphs: Glyph[]) => void;
};
export type Glyph = {
name: string;
width: number;
height: number;
defaultHeight?: number;
defaultWidth?: number;
unicode: string[];
paths?: SVGPathData[];
};
export declare class SVGIcons2SVGFontStream extends Transform {
private _options;
glyphs: Glyph[];
constructor(options: Partial<SVGIcons2SVGFontStreamOptions>);
_transform(svgIconStream: any, _unused: any, svgIconStreamCallback: any): void;
_flush(svgFontFlushCallback: any): void;
}

View File

@@ -0,0 +1,379 @@
import { Transform } from 'stream';
import Sax from 'sax';
import { SVGPathData } from 'svg-pathdata';
import svgShapesToPath from './svgshapes2svgpath.js';
import { scale, translate, compose, fromDefinition, fromTransformAttribute, } from 'transformation-matrix';
import { YError } from 'yerror';
import debug from 'debug';
const warn = debug('svgicons2svgfont');
export { fileSorter } from './filesorter.js';
export * from './iconsdir.js';
export * from './metadata.js';
function matrixFromTransformAttribute(transformAttributeString) {
return compose(fromDefinition(fromTransformAttribute(transformAttributeString)));
}
// Rendering
function tagShouldRender(curTag, parents) {
let values;
return !parents.some((tag) => {
if ('undefined' !== typeof tag.attributes.display &&
'none' === tag.attributes.display.toLowerCase()) {
return true;
}
if ('undefined' !== typeof tag.attributes.width &&
0 === parseFloat(tag.attributes.width)) {
return true;
}
if ('undefined' !== typeof tag.attributes.height &&
0 === parseFloat(tag.attributes.height)) {
return true;
}
if ('undefined' !== typeof tag.attributes.viewBox) {
values = tag.attributes.viewBox.split(/\s*,*\s|\s,*\s*|,/);
if (0 === parseFloat(values[2]) || 0 === parseFloat(values[3])) {
return true;
}
}
return false;
});
}
// According to the document (http://www.w3.org/TR/SVG/painting.html#FillProperties)
// fill <paint> none|currentColor|inherit|<color>
// [<icccolor>]|<funciri> (not support yet)
function getTagColor(currTag, parents) {
const defaultColor = 'black';
const fillVal = currTag.attributes.fill;
let color;
const parentsLength = parents.length;
if ('none' === fillVal) {
return color;
}
if ('currentColor' === fillVal) {
return defaultColor;
}
if ('inherit' === fillVal) {
if (0 === parentsLength) {
return defaultColor;
}
return getTagColor(parents[parentsLength - 1], parents.slice(0, parentsLength - 1));
// this might be null.
// For example: <svg ><path fill="inherit" /> </svg>
// in this case getTagColor should return null
// recursive call, the bottom element should be svg,
// and svg didn't fill color, so just return null
}
return fillVal;
}
export class SVGIcons2SVGFontStream extends Transform {
_options;
glyphs;
constructor(options) {
super({ objectMode: true });
this.glyphs = [];
this._options = {
...options,
fontName: options.fontName || 'iconfont',
fontId: options.fontId || options.fontName || 'iconfont',
fixedWidth: options.fixedWidth || false,
descent: options.descent || 0,
round: options.round || 10e12,
metadata: options.metadata || '',
usePathBounds: options.usePathBounds || false,
};
}
_transform(svgIconStream, _unused, svgIconStreamCallback) {
// Parsing each icons asynchronously
const saxStream = Sax.createStream(true);
const parents = [];
const transformStack = [];
function applyTransform(d) {
const last = transformStack[transformStack.length - 1];
if (!last)
return new SVGPathData(d);
return new SVGPathData(d).matrix(last.a, last.b, last.c, last.d, last.e, last.f);
}
const glyph = svgIconStream.metadata || {};
// init width and height os they aren't undefined if <svg> isn't renderable
glyph.width = 0;
glyph.height = 1;
glyph.paths = [];
this.glyphs.push(glyph);
if ('string' !== typeof glyph.name) {
this.emit('error', new Error(`Please provide a name for the glyph at index ${this.glyphs.length - 1}`));
}
if (this.glyphs.some((anotherGlyph) => anotherGlyph !== glyph && anotherGlyph.name === glyph.name)) {
this.emit('error', new Error(`The glyph name "${glyph.name}" must be unique.`));
}
if (glyph.unicode &&
glyph.unicode instanceof Array &&
glyph.unicode.length) {
if (glyph.unicode.some((unicodeA, i) => glyph.unicode.some((unicodeB, j) => i !== j && unicodeA === unicodeB))) {
this.emit('error', new Error(`Given codepoints for the glyph "${glyph.name}" contain duplicates.`));
}
}
else if ('string' !== typeof glyph.unicode) {
this.emit('error', new Error(`Please provide a codepoint for the glyph "${glyph.name}"`));
}
if (this.glyphs.some((anotherGlyph) => anotherGlyph !== glyph && anotherGlyph.unicode === glyph.unicode)) {
this.emit('error', new Error(`The glyph "${glyph.name}" codepoint seems to be used already elsewhere.`));
}
saxStream.on('opentag', (tag) => {
let values;
let color;
parents.push(tag);
try {
const currentTransform = transformStack[transformStack.length - 1];
if ('undefined' !== typeof tag.attributes.transform) {
const transform = matrixFromTransformAttribute(tag.attributes.transform);
transformStack.push(compose([currentTransform, transform].filter(Boolean)));
}
else {
transformStack.push(currentTransform);
}
// Checking if any parent rendering is disabled and exit if so
if (!tagShouldRender(tag, parents)) {
return;
}
// Save the view size
if ('svg' === tag.name) {
if ('viewBox' in tag.attributes) {
values = tag.attributes.viewBox.split(/\s*,*\s|\s,*\s*|,/);
const dX = parseFloat(values[0]);
const dY = parseFloat(values[1]);
const width = parseFloat(values[2]);
const height = parseFloat(values[3]);
// use the viewBox width/height if not specified explictly
glyph.width =
'width' in tag.attributes
? parseFloat(tag.attributes.width)
: width;
glyph.height =
'height' in tag.attributes
? parseFloat(tag.attributes.height)
: height;
transformStack[transformStack.length - 1] = compose([
transformStack[transformStack.length - 1],
translate(-dX, -dY),
scale(glyph.width / width, glyph.height / height),
].filter(Boolean));
}
else {
if ('width' in tag.attributes) {
glyph.width = parseFloat(tag.attributes.width);
}
else {
warn(`⚠️ - Glyph "${glyph.name}" has no width attribute, using current glyph horizontal bounds.`);
glyph.defaultWidth = true;
}
if ('height' in tag.attributes) {
glyph.height = parseFloat(tag.attributes.height);
}
else {
warn(`⚠️ - Glyph "${glyph.name}" has no height attribute, using current glyph vertical bounds.`);
glyph.defaultHeight = true;
}
}
}
else if ('clipPath' === tag.name) {
// Clipping path unsupported
warn(`🤷 - Found a clipPath element in the icon "${glyph.name}" the result may be different than expected.`);
}
else if ('rect' === tag.name && 'none' !== tag.attributes.fill) {
glyph.paths.push(applyTransform(svgShapesToPath.rectToPath(tag.attributes)));
}
else if ('line' === tag.name && 'none' !== tag.attributes.fill) {
warn(`🤷 - Found a line element in the icon "${glyph.name}" the result could be different than expected.`);
glyph.paths.push(applyTransform(svgShapesToPath.lineToPath(tag.attributes)));
}
else if ('polyline' === tag.name && 'none' !== tag.attributes.fill) {
warn(`🤷 - Found a polyline element in the icon "${glyph.name}" the result could be different than expected.`);
glyph.paths.push(applyTransform(svgShapesToPath.polylineToPath(tag.attributes)));
}
else if ('polygon' === tag.name && 'none' !== tag.attributes.fill) {
glyph.paths.push(applyTransform(svgShapesToPath.polygonToPath(tag.attributes)));
}
else if (['circle', 'ellipse'].includes(tag.name) &&
'none' !== tag.attributes.fill) {
glyph.paths.push(applyTransform(svgShapesToPath.circleToPath(tag.attributes)));
}
else if ('path' === tag.name &&
tag.attributes.d &&
'none' !== tag.attributes.fill) {
glyph.paths.push(applyTransform(tag.attributes.d));
}
// According to http://www.w3.org/TR/SVG/painting.html#SpecifyingPaint
// Map attribute fill to color property
if ('none' !== tag.attributes.fill) {
color = getTagColor(tag, parents);
if ('undefined' !== typeof color) {
glyph.color = color;
}
}
}
catch (err) {
this.emit('error', new Error(`Got an error parsing the glyph "${glyph.name}": ${err?.message}.`));
}
});
saxStream.on('error', (err) => {
this.emit('error', err);
});
saxStream.on('closetag', () => {
transformStack.pop();
parents.pop();
});
saxStream.on('end', () => {
svgIconStreamCallback();
});
svgIconStream.pipe(saxStream);
}
_flush(svgFontFlushCallback) {
this.glyphs.forEach((glyph) => {
if (glyph.defaultHeight ||
glyph.defaultWidth ||
this._options.usePathBounds) {
const glyphPath = new SVGPathData('');
(glyph.paths || []).forEach((path) => {
glyphPath.commands.push(...path.commands);
});
const bounds = glyphPath.getBounds();
if (glyph.defaultHeight || this._options.usePathBounds) {
glyph.height = bounds.maxY - bounds.minY;
}
if (glyph.defaultWidth || this._options.usePathBounds) {
glyph.width = bounds.maxX - bounds.minX;
}
}
});
const maxGlyphHeight = this.glyphs.reduce((curMax, glyph) => Math.max(curMax, glyph.height), 0);
const maxGlyphWidth = this.glyphs.reduce((curMax, glyph) => Math.max(curMax, glyph.width), 0);
const fontHeight = this._options.fontHeight || maxGlyphHeight;
let fontWidth = maxGlyphWidth;
if (this._options.normalize) {
fontWidth = this.glyphs.reduce((curMax, glyph) => Math.max(curMax, (fontHeight / glyph.height) * glyph.width), 0);
}
else if (this._options.fontHeight) {
// even if normalize is off, we need to scale the fontWidth if we have a custom fontHeight
fontWidth *= fontHeight / maxGlyphHeight;
}
this._options.ascent =
'undefined' !== typeof this._options.ascent
? this._options.ascent
: fontHeight - this._options.descent;
if (!this._options.normalize &&
fontHeight >
(1 < this.glyphs.length
? this.glyphs.reduce((curMin, glyph) => Math.min(curMin, glyph.height), Infinity)
: this.glyphs[0].height)) {
warn('🤷 - The provided icons do not have the same heights. This could lead' +
' to unexpected results. Using the normalize option may help.');
}
if (1000 > fontHeight) {
warn('🤷 - A fontHeight of at least than 1000 is recommended, otherwise ' +
'further steps (rounding in svg2ttf) could lead to ugly results.' +
' Use the fontHeight option to scale icons.');
}
// Output the SVG file
// (find a SAX parser that allows modifying SVG on the fly)
this.push('<?xml version="1.0" standalone="no"?>\n' +
'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >\n' +
'<svg xmlns="http://www.w3.org/2000/svg">\n' +
(this._options.metadata
? '<metadata>' + this._options.metadata + '</metadata>\n'
: '') +
'<defs>\n' +
' <font id="' +
this._options.fontId +
'" horiz-adv-x="' +
fontWidth +
'">\n' +
' <font-face font-family="' +
this._options.fontName +
'"\n' +
' units-per-em="' +
fontHeight +
'" ascent="' +
this._options.ascent +
'"\n' +
' descent="' +
this._options.descent +
'"' +
(this._options.fontWeight
? '\n font-weight="' + this._options.fontWeight + '"'
: '') +
(this._options.fontStyle
? '\n font-style="' + this._options.fontStyle + '"'
: '') +
' />\n' +
' <missing-glyph horiz-adv-x="0" />\n');
this.glyphs.forEach((glyph) => {
const ratio = this._options.normalize
? fontHeight /
(this._options.preserveAspectRatio && glyph.width > glyph.height
? glyph.width
: glyph.height)
: fontHeight / maxGlyphHeight;
if (!isFinite(ratio)) {
throw new YError('E_BAD_COMPUTED_RATIO', ratio);
}
glyph.width *= ratio;
glyph.height *= ratio;
const glyphPath = new SVGPathData('');
if (this._options.fixedWidth) {
glyph.width = fontWidth;
}
const yOffset = glyph.height - this._options.descent;
let glyphPathTransform = {
a: 1,
b: 0,
c: 0,
d: -1,
e: 0,
f: yOffset,
}; // ySymmetry
if (1 !== ratio) {
glyphPathTransform = compose(glyphPathTransform, scale(ratio, ratio));
}
(glyph.paths || []).forEach((path) => {
glyphPath.commands.push(...path
.toAbs()
.matrix(glyphPathTransform.a, glyphPathTransform.b, glyphPathTransform.c, glyphPathTransform.d, glyphPathTransform.e, glyphPathTransform.f).commands);
});
const bounds = (this._options.centerHorizontally || this._options.centerVertically) &&
glyphPath.getBounds();
if (this._options.centerHorizontally && bounds && 'maxX' in bounds) {
glyphPath.translate((glyph.width - (bounds.maxX - bounds.minX)) / 2 - bounds.minX);
}
if (this._options.centerVertically && bounds && 'maxX' in bounds) {
glyphPath.translate(0, (fontHeight - (bounds.maxY - bounds.minY)) / 2 -
bounds.minY -
this._options.descent);
}
delete glyph.paths;
const d = glyphPath.round(this._options.round).encode();
glyph.unicode.forEach((unicode, i) => {
const unicodeStr = [...unicode]
.map((char) => '&#x' + char.codePointAt(0).toString(16).toUpperCase() + ';')
.join('');
this.push(' <glyph glyph-name="' +
glyph.name +
(0 === i ? '' : '-' + i) +
'"\n' +
' unicode="' +
unicodeStr +
'"\n' +
' horiz-adv-x="' +
glyph.width +
'" d="' +
d +
'" />\n');
});
});
this.push(' </font>\n' + '</defs>\n' + '</svg>\n');
warn('✅ - Font created');
if ('function' === typeof this._options.callback) {
this._options.callback(this.glyphs);
}
svgFontFlushCallback();
}
}
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,12 @@
export type MetadataServiceOptions = {
prependUnicode: boolean;
startUnicode: number;
};
export type FileMetadata = {
path: string;
name: string;
unicode: string[] | string;
renamed: boolean;
};
declare function getMetadataService(options?: Partial<MetadataServiceOptions>): (file: string, cb: (error: Error | null, metadata?: FileMetadata) => void) => void;
export { getMetadataService };

View File

@@ -0,0 +1,70 @@
import { join, dirname, basename } from 'node:path';
import { rename } from 'node:fs';
function getMetadataService(options = {}) {
const usedUnicodes = [];
// Default options
const _options = {
prependUnicode: !!options.prependUnicode,
startUnicode: 'number' === typeof options.startUnicode ? options.startUnicode : 0xea01,
};
return function getMetadataFromFile(file, cb) {
const fileBasename = basename(file);
const metadata = {
path: file,
name: '',
unicode: [],
renamed: false,
};
const matches = fileBasename.match(/^(?:((?:u[0-9a-f]{4,6},?)+)-)?(.+)\.svg$/i);
metadata.name =
matches && matches[2] ? matches[2] : 'icon' + _options.startUnicode;
if (matches && matches[1]) {
metadata.unicode = matches[1].split(',').map((match) => {
match = match.substring(1);
return match
.split('u')
.map((code) => String.fromCodePoint(parseInt(code, 16)))
.join('');
});
if (-1 !== usedUnicodes.indexOf(metadata.unicode[0])) {
cb(new Error('The unicode codepoint of the glyph ' +
metadata.name +
' seems to be already used by another glyph.'));
return;
}
usedUnicodes.push(...metadata.unicode);
}
else {
do {
metadata.unicode[0] = String.fromCodePoint(_options.startUnicode++);
} while (usedUnicodes.includes(metadata.unicode[0]));
usedUnicodes.push(metadata.unicode[0]);
if (_options.prependUnicode) {
metadata.renamed = true;
metadata.path = join(dirname(file), 'u' +
metadata.unicode[0].codePointAt(0)?.toString(16).toUpperCase() +
'-' +
fileBasename);
rename(file, metadata.path, (err) => {
if (err) {
cb(new Error('Could not save codepoint: ' +
'u' +
metadata.unicode[0]
.codePointAt(0)
?.toString(16)
.toUpperCase() +
' for ' +
fileBasename));
return;
}
cb(null, metadata);
});
}
}
if (!metadata.renamed) {
setImmediate(() => cb(null, metadata));
}
};
}
export { getMetadataService };
//# sourceMappingURL=metadata.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,13 @@
declare const svgShapesToPath: {
rectToPath: typeof svgShapesToPathRectToPath;
polylineToPath: typeof svgShapesToPathPolylineToPath;
lineToPath: typeof svgShapesToPathLineToPath;
circleToPath: typeof svgShapesToPathCircleToPath;
polygonToPath: typeof svgShapesToPathPolygonToPath;
};
export default svgShapesToPath;
declare function svgShapesToPathRectToPath(attributes: any): string;
declare function svgShapesToPathPolylineToPath(attributes: any): string;
declare function svgShapesToPathLineToPath(attributes: any): string;
declare function svgShapesToPathCircleToPath(attributes: any): string;
declare function svgShapesToPathPolygonToPath(attributes: any): string;

View File

@@ -0,0 +1,113 @@
const svgShapesToPath = {
rectToPath: svgShapesToPathRectToPath,
polylineToPath: svgShapesToPathPolylineToPath,
lineToPath: svgShapesToPathLineToPath,
circleToPath: svgShapesToPathCircleToPath,
polygonToPath: svgShapesToPathPolygonToPath,
};
export default svgShapesToPath;
// Shapes helpers (should also move elsewhere)
function svgShapesToPathRectToPath(attributes) {
const x = 'undefined' !== typeof attributes.x ? parseFloat(attributes.x) : 0;
const y = 'undefined' !== typeof attributes.y ? parseFloat(attributes.y) : 0;
const width = 'undefined' !== typeof attributes.width ? parseFloat(attributes.width) : 0;
const height = 'undefined' !== typeof attributes.height
? parseFloat(attributes.height)
: 0;
const rx = 'undefined' !== typeof attributes.rx
? parseFloat(attributes.rx)
: 'undefined' !== typeof attributes.ry
? parseFloat(attributes.ry)
: 0;
const ry = 'undefined' !== typeof attributes.ry ? parseFloat(attributes.ry) : rx;
return ('' +
// start at the left corner
'M' +
(x + rx) +
' ' +
y +
// top line
'h' +
(width - rx * 2) +
// upper right corner
(rx || ry ? 'a ' + rx + ' ' + ry + ' 0 0 1 ' + rx + ' ' + ry : '') +
// Draw right side
'v' +
(height - ry * 2) +
// Draw bottom right corner
(rx || ry ? 'a ' + rx + ' ' + ry + ' 0 0 1 ' + rx * -1 + ' ' + ry : '') +
// Down the down side
'h' +
(width - rx * 2) * -1 +
// Draw bottom right corner
(rx || ry
? 'a ' + rx + ' ' + ry + ' 0 0 1 ' + rx * -1 + ' ' + ry * -1
: '') +
// Down the left side
'v' +
(height - ry * 2) * -1 +
// Draw bottom right corner
(rx || ry ? 'a ' + rx + ' ' + ry + ' 0 0 1 ' + rx + ' ' + ry * -1 : '') +
// Close path
'z');
}
function svgShapesToPathPolylineToPath(attributes) {
return 'M' + attributes.points;
}
function svgShapesToPathLineToPath(attributes) {
// Move to the line start
return ('' +
'M' +
(parseFloat(attributes.x1) || 0).toString(10) +
' ' +
(parseFloat(attributes.y1) || 0).toString(10) +
' ' +
((parseFloat(attributes.x1) || 0) + 1).toString(10) +
' ' +
((parseFloat(attributes.y1) || 0) + 1).toString(10) +
' ' +
((parseFloat(attributes.x2) || 0) + 1).toString(10) +
' ' +
((parseFloat(attributes.y2) || 0) + 1).toString(10) +
' ' +
(parseFloat(attributes.x2) || 0).toString(10) +
' ' +
(parseFloat(attributes.y2) || 0).toString(10) +
'Z');
}
function svgShapesToPathCircleToPath(attributes) {
const cx = parseFloat(attributes.cx || 0);
const cy = parseFloat(attributes.cy || 0);
const rx = 'undefined' !== typeof attributes.rx
? parseFloat(attributes.rx)
: parseFloat(attributes.r);
const ry = 'undefined' !== typeof attributes.ry
? parseFloat(attributes.ry)
: parseFloat(attributes.r);
// use two A commands because one command which returns to origin is invalid
return ('' +
'M' +
(cx - rx) +
',' +
cy +
'A' +
rx +
',' +
ry +
' 0,0,0 ' +
(cx + rx) +
',' +
cy +
'A' +
rx +
',' +
ry +
' 0,0,0 ' +
(cx - rx) +
',' +
cy);
}
function svgShapesToPathPolygonToPath(attributes) {
return 'M' + attributes.points + 'Z';
}
//# sourceMappingURL=svgshapes2svgpath.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"svgshapes2svgpath.js","sourceRoot":"","sources":["../src/svgshapes2svgpath.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,65 @@
import { describe, test, expect } from '@jest/globals';
import { readFile, mkdir } from 'node:fs/promises';
import { join } from 'node:path';
import { exec } from 'node:child_process';
import { promisify } from 'node:util';
try {
await mkdir(join('fixtures', 'results'));
}
catch (err) {
// empty
}
describe('Testing CLI', () => {
test('should work for simple SVG', async () => {
const command = `${'node' + ' '}${join('bin', 'svgicons2svgfont.js')} -o ${join('fixtures', 'results', 'originalicons-cli.svg')} -s 0xE001` + ` ${join('fixtures', 'icons', 'originalicons', '*.svg')}`;
await promisify(exec)(command);
expect(await readFile(join('fixtures', 'results', 'originalicons-cli.svg'), {
encoding: 'utf8',
})).toEqual(await readFile(join('fixtures', 'expected', 'originalicons-cli.svg'), {
encoding: 'utf8',
}));
});
test('should work for more than 32 SVG icons', async () => {
const command = 'node' +
' ' +
join('bin', 'svgicons2svgfont.js') +
' -o ' +
join('fixtures', 'results', 'lotoficons-cli.svg') +
' -s 0xE001' +
' -r 1e4' +
' ' +
join('fixtures', 'icons', 'cleanicons', '*.svg') +
' ' +
join('fixtures', 'icons', 'hiddenpathesicons', '*.svg') +
' ' +
join('fixtures', 'icons', 'multipathicons', 'kikoolol.svg') +
' ' +
join('fixtures', 'icons', 'originalicons', '*.svg') +
' ' +
join('fixtures', 'icons', 'realicons', '*.svg') +
' ' +
join('fixtures', 'icons', 'roundedcorners', '*.svg') +
' ' +
join('fixtures', 'icons', 'shapeicons', '*.svg') +
' ' +
join('fixtures', 'icons', 'tocentericons', '*.svg');
await promisify(exec)(command);
expect(await readFile(join('fixtures', 'results', 'lotoficons-cli.svg'), {
encoding: 'utf8',
})).toEqual(await readFile(join('fixtures', 'expected', 'lotoficons-cli.svg'), {
encoding: 'utf8',
}));
});
describe('with nested icons', () => {
test('should work', async () => {
const command = `${'node' + ' '}${join('bin', 'svgicons2svgfont.js')} -o ${join('fixtures', 'results', 'nestedicons-cli.svg')} ${join('fixtures', 'icons', 'nestedicons', '*.svg')}`;
await promisify(exec)(command);
expect(await readFile(join('fixtures', 'results', 'nestedicons-cli.svg'), {
encoding: 'utf8',
})).toEqual(await readFile(join('fixtures', 'expected', 'nestedicons-cli.svg'), {
encoding: 'utf8',
}));
});
});
});
//# sourceMappingURL=cli.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"cli.test.js","sourceRoot":"","sources":["../../src/tests/cli.test.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,79 @@
import { describe, test, expect } from '@jest/globals';
import { fileSorter } from '../filesorter.js';
describe('fileSorter', () => {
test('should sort files per filename', () => {
expect([
'/var/plop/c.svg',
'/var/plop/a.svg',
'/var/plop/A.svg',
'/var/plop/C.svg',
'/var/plop/B.svg',
'/var/plop/b.svg',
].sort(fileSorter)).toEqual([
'/var/plop/A.svg',
'/var/plop/B.svg',
'/var/plop/C.svg',
'/var/plop/a.svg',
'/var/plop/b.svg',
'/var/plop/c.svg',
]);
});
test('should sort files per codepoints', () => {
expect([
'/var/plop/uAE01-c.svg',
'/var/plop/uAE03-a.svg',
'/var/plop/uAE02-A.svg',
'/var/plop/uAE06-C.svg',
'/var/plop/uAE04-B.svg',
'/var/plop/uAE05-b.svg',
].sort(fileSorter)).toEqual([
'/var/plop/uAE01-c.svg',
'/var/plop/uAE02-A.svg',
'/var/plop/uAE03-a.svg',
'/var/plop/uAE04-B.svg',
'/var/plop/uAE05-b.svg',
'/var/plop/uAE06-C.svg',
]);
});
test('should put codepoints first', () => {
expect([
'/var/plop/uAE01-c.svg',
'/var/plop/uAE03-a.svg',
'/var/plop/uAE02-A.svg',
'/var/plop/C.svg',
'/var/plop/B.svg',
'/var/plop/b.svg',
].sort(fileSorter)).toEqual([
'/var/plop/uAE01-c.svg',
'/var/plop/uAE02-A.svg',
'/var/plop/uAE03-a.svg',
'/var/plop/B.svg',
'/var/plop/C.svg',
'/var/plop/b.svg',
]);
});
test('should work with the @pinin files', () => {
expect([
'bell-disabled.svg',
'bell-disabled-o.svg',
'bell-o.svg',
'UEA01-calendar-agenda.svg',
'UEA02-calendar-alert.svg',
'UEA03-calendar.svg',
'uEA04-bookmark-favorite.svg',
'uEA05-bookmark-o.svg',
'uEA06-bookmark.svg',
].sort(fileSorter)).toEqual([
'UEA01-calendar-agenda.svg',
'UEA02-calendar-alert.svg',
'UEA03-calendar.svg',
'uEA04-bookmark-favorite.svg',
'uEA05-bookmark-o.svg',
'uEA06-bookmark.svg',
'bell-disabled.svg',
'bell-disabled-o.svg',
'bell-o.svg',
]);
});
});
//# sourceMappingURL=filesorter.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"filesorter.test.js","sourceRoot":"","sources":["../../src/tests/filesorter.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;QAC1C,MAAM,CACJ;YACE,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;SAClB,CAAC,IAAI,CAAC,UAAU,CAAC,CACnB,CAAC,OAAO,CAAC;YACR,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;SAClB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC5C,MAAM,CACJ;YACE,uBAAuB;YACvB,uBAAuB;YACvB,uBAAuB;YACvB,uBAAuB;YACvB,uBAAuB;YACvB,uBAAuB;SACxB,CAAC,IAAI,CAAC,UAAU,CAAC,CACnB,CAAC,OAAO,CAAC;YACR,uBAAuB;YACvB,uBAAuB;YACvB,uBAAuB;YACvB,uBAAuB;YACvB,uBAAuB;YACvB,uBAAuB;SACxB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACvC,MAAM,CACJ;YACE,uBAAuB;YACvB,uBAAuB;YACvB,uBAAuB;YACvB,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;SAClB,CAAC,IAAI,CAAC,UAAU,CAAC,CACnB,CAAC,OAAO,CAAC;YACR,uBAAuB;YACvB,uBAAuB;YACvB,uBAAuB;YACvB,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;SAClB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC7C,MAAM,CACJ;YACE,mBAAmB;YACnB,qBAAqB;YACrB,YAAY;YACZ,2BAA2B;YAC3B,0BAA0B;YAC1B,oBAAoB;YACpB,6BAA6B;YAC7B,sBAAsB;YACtB,oBAAoB;SACrB,CAAC,IAAI,CAAC,UAAU,CAAC,CACnB,CAAC,OAAO,CAAC;YACR,2BAA2B;YAC3B,0BAA0B;YAC1B,oBAAoB;YACpB,6BAA6B;YAC7B,sBAAsB;YACtB,oBAAoB;YACpB,mBAAmB;YACnB,qBAAqB;YACrB,YAAY;SACb,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,553 @@
import { describe, test, expect } from '@jest/globals';
import assert from 'assert';
import fs from 'node:fs';
import { mkdir } from 'node:fs/promises';
import { join } from 'node:path';
import { SVGIcons2SVGFontStream } from '../index.js';
import { SVGIconsDirStream } from '../iconsdir.js';
import streamtest from 'streamtest';
import { BufferStream } from 'bufferstreams';
try {
await mkdir(join('fixtures', 'results'));
}
catch (err) {
// empty
}
const codepoint = JSON.parse(fs.readFileSync('./fixtures/expected/test-codepoint.json').toString());
// Helpers
async function generateFontToFile(options, fileSuffix, startUnicode, files) {
const dest = join('fixtures', 'results', `${options.fontName + (fileSuffix || '')}.svg`);
let resolve;
let reject;
const promise = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
options.log = () => { };
options.round = options.round || 1e3;
const svgFontStream = new SVGIcons2SVGFontStream(options);
svgFontStream.pipe(fs.createWriteStream(dest)).on('finish', () => {
try {
expect(fs.readFileSync(dest, { encoding: 'utf8' })).toEqual(fs.readFileSync(join('fixtures', 'expected', `${options.fontName + (fileSuffix || '')}.svg`), { encoding: 'utf8' }));
resolve();
}
catch (err) {
reject(err);
}
});
new SVGIconsDirStream(files || join('fixtures', 'icons', options.fontName), {
startUnicode: startUnicode || 0xe001,
}).pipe(svgFontStream);
return await promise;
}
async function generateFontToMemory(options, files, startUnicode) {
options.log = () => { };
options.round = options.round || 1e3;
options.callback = (glyphs) => {
const fontName = options.fontName;
expect(glyphs).toEqual(codepoint[fontName]);
};
const svgFontStream = new SVGIcons2SVGFontStream(options);
const promise = bufferStream(svgFontStream);
new SVGIconsDirStream(files || join('fixtures', 'icons', options.fontName), {
startUnicode: startUnicode || 0xe001,
}).pipe(svgFontStream);
expect((await promise).toString()).toEqual(fs.readFileSync(join('fixtures', 'expected', `${options.fontName}.svg`), {
encoding: 'utf8',
}));
}
// Tests
describe('Generating fonts to files', () => {
test('should work for simple SVG', async () => {
await generateFontToFile({
fontName: 'originalicons',
});
});
test('should work for simple fixedWidth and normalize option', async () => {
await generateFontToFile({
fontName: 'originalicons',
fixedWidth: true,
normalize: true,
}, 'n');
});
test('should work for simple SVG', async () => {
await generateFontToFile({
fontName: 'cleanicons',
});
});
test('should work for simple SVG and custom ascent', async () => {
await generateFontToFile({
fontName: 'cleanicons',
ascent: 100,
}, '-ascent');
});
test('should work for simple SVG and custom properties', async () => {
await generateFontToFile({
fontName: 'cleanicons',
fontStyle: 'italic',
fontWeight: 'bold',
}, '-stw');
});
test('should work for codepoint mapped SVG icons', async () => {
await generateFontToFile({
fontName: 'prefixedicons',
callback: () => { },
});
});
test('should work with multipath SVG icons', async () => {
await generateFontToFile({
fontName: 'multipathicons',
});
});
test('should work with simple shapes SVG icons', async () => {
await generateFontToFile({
fontName: 'shapeicons',
});
});
test('should work with variable height icons', async () => {
await generateFontToFile({
fontName: 'variableheighticons',
});
});
test('should work with variable height icons and the normalize option', async () => {
await generateFontToFile({
fontName: 'variableheighticons',
normalize: true,
}, 'n');
});
test('should work with variable height icons, the normalize option and the preserveAspectRatio option', async () => {
await generateFontToFile({
fontName: 'variableheighticons',
normalize: true,
preserveAspectRatio: true,
}, 'np');
});
test('should work with variable width icons', async () => {
await generateFontToFile({
fontName: 'variablewidthicons',
});
});
test('should work with centered variable width icons and the fixed width option', async () => {
await generateFontToFile({
fontName: 'variablewidthicons',
fixedWidth: true,
centerHorizontally: true,
}, 'n');
});
test('should calculate bounds when not specified in the svg file', async () => {
await generateFontToFile({
fontName: 'calcbounds',
});
});
test('should work with a font id', async () => {
await generateFontToFile({
fontName: 'variablewidthicons',
fixedWidth: true,
centerHorizontally: true,
fontId: 'plop',
}, 'id');
});
test('should work with scaled icons', async () => {
await generateFontToFile({
fontName: 'scaledicons',
fixedWidth: true,
centerHorizontally: true,
fontId: 'plop',
});
});
test('should not display hidden paths', async () => {
await generateFontToFile({
fontName: 'hiddenpathesicons',
});
});
test('should work with real world icons', async () => {
await generateFontToFile({
fontName: 'realicons',
});
});
test('should work with rendering test SVG icons', async () => {
await generateFontToFile({
fontName: 'rendricons',
});
});
test('should work with a single SVG icon', async () => {
await generateFontToFile({
fontName: 'singleicon',
});
});
test('should work with transformed SVG icons', async () => {
await generateFontToFile({
fontName: 'transformedicons',
});
});
test('should work when horizontally centering SVG icons', async () => {
await generateFontToFile({
fontName: 'tocentericons',
centerHorizontally: true,
});
});
test('should work when vertically centering SVG icons', async () => {
await generateFontToFile({
fontName: 'toverticalcentericons',
centerVertically: true,
});
});
test('should work with a icons with path with fill none', async () => {
await generateFontToFile({
fontName: 'pathfillnone',
});
});
test('should work with shapes with rounded corners', async () => {
await generateFontToFile({
fontName: 'roundedcorners',
});
});
test('should work with realworld icons', async () => {
await generateFontToFile({
fontName: 'realworld',
});
});
test('should work with a lot of icons', async () => {
await generateFontToFile({
fontName: 'lotoficons',
}, '', 0, [
'fixtures/icons/cleanicons/account.svg',
'fixtures/icons/cleanicons/arrow-down.svg',
'fixtures/icons/cleanicons/arrow-left.svg',
'fixtures/icons/cleanicons/arrow-right.svg',
'fixtures/icons/cleanicons/arrow-up.svg',
'fixtures/icons/cleanicons/basket.svg',
'fixtures/icons/cleanicons/close.svg',
'fixtures/icons/cleanicons/minus.svg',
'fixtures/icons/cleanicons/plus.svg',
'fixtures/icons/cleanicons/search.svg',
'fixtures/icons/hiddenpathesicons/sound--off.svg',
'fixtures/icons/hiddenpathesicons/sound--on.svg',
'fixtures/icons/multipathicons/kikoolol.svg',
'fixtures/icons/originalicons/mute.svg',
'fixtures/icons/originalicons/sound.svg',
'fixtures/icons/originalicons/speaker.svg',
'fixtures/icons/realicons/diegoliv.svg',
'fixtures/icons/realicons/hannesjohansson.svg',
'fixtures/icons/realicons/roelvanhitum.svg',
'fixtures/icons/realicons/safety-icon.svg',
'fixtures/icons/realicons/sb-icon.svg',
'fixtures/icons/realicons/settings-icon.svg',
'fixtures/icons/realicons/track-icon.svg',
'fixtures/icons/realicons/web-icon.svg',
'fixtures/icons/roundedcorners/roundedrect.svg',
'fixtures/icons/shapeicons/circle.svg',
'fixtures/icons/shapeicons/ellipse.svg',
'fixtures/icons/shapeicons/lines.svg',
'fixtures/icons/shapeicons/polygon.svg',
'fixtures/icons/shapeicons/polyline.svg',
'fixtures/icons/shapeicons/rect.svg',
'fixtures/icons/tocentericons/bottomleft.svg',
'fixtures/icons/tocentericons/center.svg',
'fixtures/icons/tocentericons/topright.svg',
]);
});
test('should work with rotated rectangle icon', async () => {
await generateFontToFile({
fontName: 'rotatedrectangle',
});
});
/**
* Issue #6
* icon by @paesku
* https://github.com/nfroidure/svgicons2svgfont/issues/6#issuecomment-125545925
*/
test('should work with complicated nested transforms', async () => {
await generateFontToFile({
fontName: 'paesku',
round: 1e3,
});
});
/**
* Issue #76
* https://github.com/nfroidure/svgicons2svgfont/issues/76#issue-259831969
*/
test('should work with transform=translate(x) without y', async () => {
await generateFontToFile({
fontName: 'translatex',
round: 1e3,
});
});
test('should work with skew', async () => {
await generateFontToFile({
fontName: 'skew',
});
});
test('should work when only rx is present', async () => {
await generateFontToFile({
fontName: 'onlywithrx',
});
});
test('should work when only ry is present', async () => {
await generateFontToFile({
fontName: 'onlywithry',
});
});
});
describe('Generating fonts to memory', () => {
test('should work for simple SVG', async () => {
await generateFontToMemory({
fontName: 'originalicons',
});
});
test('should work for simple SVG', async () => {
await generateFontToMemory({
fontName: 'cleanicons',
});
});
test('should work for codepoint mapped SVG icons', async () => {
await generateFontToMemory({
fontName: 'prefixedicons',
});
});
test('should work with multipath SVG icons', async () => {
await generateFontToMemory({
fontName: 'multipathicons',
});
});
test('should work with simple shapes SVG icons', async () => {
await generateFontToMemory({
fontName: 'shapeicons',
});
});
});
describe('Using options', () => {
test('should work with fixedWidth option set to true', async () => {
await generateFontToFile({
fontName: 'originalicons',
fixedWidth: true,
}, '2');
});
test('should work with custom fontHeight option', async () => {
await generateFontToFile({
fontName: 'originalicons',
fontHeight: 800,
}, '3');
});
test('should work with custom descent option', async () => {
await generateFontToFile({
fontName: 'originalicons',
descent: 200,
}, '4');
});
test('should work with fixedWidth set to true and with custom fontHeight option', async () => {
await generateFontToFile({
fontName: 'originalicons',
fontHeight: 800,
fixedWidth: true,
}, '5');
});
test('should work with fixedWidth and centerHorizontally set to true and with' +
' custom fontHeight option', async () => {
await generateFontToFile({
fontName: 'originalicons',
fontHeight: 800,
fixedWidth: true,
centerHorizontally: true,
round: 1e5,
}, '6');
});
test('should work with fixedWidth, normalize and centerHorizontally set to' +
' true and with custom fontHeight option', async () => {
await generateFontToFile({
fontName: 'originalicons',
fontHeight: 800,
normalize: true,
fixedWidth: true,
centerHorizontally: true,
round: 1e5,
}, '7');
});
test('should work with fixedWidth, normalize and centerHorizontally set to' +
' true and with a large custom fontHeight option', async () => {
await generateFontToFile({
fontName: 'originalicons',
fontHeight: 5000,
normalize: true,
fixedWidth: true,
centerHorizontally: true,
round: 1e5,
}, '8');
});
test('should work with nested icons', async () => {
await generateFontToFile({
fontName: 'nestedicons',
}, '', 0xea01);
});
});
describe('Passing code points', () => {
test('should work with multiple unicode values for a single icon', async () => {
const svgFontStream = new SVGIcons2SVGFontStream({ round: 1e3 });
const svgIconStream = fs.createReadStream(join('fixtures', 'icons', 'cleanicons', 'account.svg'));
svgIconStream.metadata = {
name: 'account',
unicode: ['\uE001', '\uE002'],
};
const promise = bufferStream(svgFontStream);
svgFontStream.write(svgIconStream);
svgFontStream.end();
assert.equal(await promise, fs.readFileSync(join('fixtures', 'expected', 'cleanicons-multi.svg'), {
encoding: 'utf8',
}));
});
test('should work with ligatures', async () => {
const svgFontStream = new SVGIcons2SVGFontStream({ round: 1e3 });
const svgIconStream = fs.createReadStream(join('fixtures', 'icons', 'cleanicons', 'account.svg'));
svgIconStream.metadata = {
name: 'account',
unicode: ['\uE001\uE002'],
};
const promise = bufferStream(svgFontStream);
svgFontStream.write(svgIconStream);
svgFontStream.end();
assert.equal(await promise, fs.readFileSync(join('fixtures', 'expected', 'cleanicons-lig.svg'), {
encoding: 'utf8',
}));
});
test('should work with high code points', async () => {
const svgFontStream = new SVGIcons2SVGFontStream({ round: 1e3 });
const svgIconStream = fs.createReadStream(join('fixtures', 'icons', 'cleanicons', 'account.svg'));
svgIconStream.metadata = {
name: 'account',
unicode: ['\u{1f63a}'],
};
const promise = bufferStream(svgFontStream);
svgFontStream.write(svgIconStream);
svgFontStream.end();
assert.equal((await promise).toString(), fs.readFileSync(join('fixtures', 'expected', 'cleanicons-high.svg'), {
encoding: 'utf8',
}));
});
});
describe('Providing bad glyphs', () => {
test('should fail when not providing glyph name', async () => {
const svgIconStream = fs.createReadStream(join('fixtures', 'icons', 'cleanicons', 'account.svg'));
svgIconStream.metadata = {
name: undefined,
unicode: '\uE001',
};
new SVGIcons2SVGFontStream({ round: 1e3 })
.on('error', (err) => {
assert.equal(err instanceof Error, true);
assert.equal(err.message, 'Please provide a name for the glyph at index 0');
})
.write(svgIconStream);
});
test('should fail when not providing codepoints', async () => {
const svgIconStream = fs.createReadStream(join('fixtures', 'icons', 'cleanicons', 'account.svg'));
svgIconStream.metadata = {
name: 'test',
unicode: undefined,
};
new SVGIcons2SVGFontStream({ round: 1e3 })
.on('error', (err) => {
assert.equal(err instanceof Error, true);
assert.equal(err.message, 'Please provide a codepoint for the glyph "test"');
})
.write(svgIconStream);
});
test('should fail when providing unicode value with duplicates', async () => {
const svgIconStream = fs.createReadStream(join('fixtures', 'icons', 'cleanicons', 'account.svg'));
svgIconStream.metadata = {
name: 'test',
unicode: ['\uE002', '\uE002'],
};
new SVGIcons2SVGFontStream({ round: 1e3 })
.on('error', (err) => {
assert.equal(err instanceof Error, true);
assert.equal(err.message, 'Given codepoints for the glyph "test" contain duplicates.');
})
.write(svgIconStream);
});
test('should fail when providing the same codepoint twice', async () => {
const svgIconStream = fs.createReadStream(join('fixtures', 'icons', 'cleanicons', 'account.svg'));
const svgIconStream2 = fs.createReadStream(join('fixtures', 'icons', 'cleanicons', 'account.svg'));
const svgFontStream = new SVGIcons2SVGFontStream({
round: 1e3,
});
svgIconStream.metadata = {
name: 'test',
unicode: '\uE002',
};
svgIconStream2.metadata = {
name: 'test2',
unicode: '\uE002',
};
svgFontStream.on('error', (err) => {
assert.equal(err instanceof Error, true);
assert.equal(err.message, 'The glyph "test2" codepoint seems to be used already elsewhere.');
});
svgFontStream.write(svgIconStream);
svgFontStream.write(svgIconStream2);
});
test('should fail when providing the same name twice', async () => {
const svgIconStream = fs.createReadStream(join('fixtures', 'icons', 'cleanicons', 'account.svg'));
const svgIconStream2 = fs.createReadStream(join('fixtures', 'icons', 'cleanicons', 'account.svg'));
const svgFontStream = new SVGIcons2SVGFontStream({ round: 1e3 });
svgIconStream.metadata = {
name: 'test',
unicode: '\uE001',
};
svgIconStream2.metadata = {
name: 'test',
unicode: '\uE002',
};
svgFontStream.on('error', (err) => {
assert.equal(err instanceof Error, true);
assert.equal(err.message, 'The glyph name "test" must be unique.');
});
svgFontStream.write(svgIconStream);
svgFontStream.write(svgIconStream2);
});
test('should fail when providing bad pathdata', async () => {
const svgIconStream = fs.createReadStream(join('fixtures', 'icons', 'badicons', 'pathdata.svg'));
svgIconStream.metadata = {
name: 'test',
unicode: ['\uE002'],
};
new SVGIcons2SVGFontStream({ round: 1e3 })
.on('error', (err) => {
assert.equal(err instanceof Error, true);
assert.equal(err.message, 'Got an error parsing the glyph "test":' +
' Expected a flag, got "20" at index "23".');
})
.on('end', () => { })
.write(svgIconStream);
});
test('should fail when providing bad XML', async () => {
const svgIconStream = streamtest.fromChunks([
Buffer.from('bad'),
Buffer.from('xml'),
]);
svgIconStream.metadata = {
name: 'test',
unicode: ['\uE002'],
};
let firstError = true;
new SVGIcons2SVGFontStream({ round: 1e3 })
.on('error', (err) => {
assert.equal(err instanceof Error, true);
if (firstError) {
firstError = false;
assert.equal(err.message, 'Non-whitespace before first tag.\nLine: 0\nColumn: 1\nChar: b');
}
})
.write(svgIconStream);
});
});
async function bufferStream(readableStream) {
return await new Promise((resolve, reject) => {
readableStream.pipe(new BufferStream((err, buf) => {
if (err) {
return reject(err);
}
resolve(buf);
}));
});
}
//# sourceMappingURL=index.test.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,167 @@
import { describe, test, expect } from '@jest/globals';
import { writeFile, readFile, unlink } from 'node:fs/promises';
import { promisify } from 'node:util';
import { getMetadataService } from '../metadata.js';
import { YError } from 'yerror';
import { mkdir } from 'node:fs/promises';
import { join } from 'node:path';
try {
await mkdir(join('fixtures', 'results'));
}
catch (err) {
// empty
}
describe('Metadata service', () => {
describe('for code generation', () => {
test('should extract right unicodes from files', async () => {
const metadataService = getMetadataService();
const infos = await promisify(metadataService)('/var/plop/hello.svg');
expect(infos).toEqual({
path: '/var/plop/hello.svg',
name: 'hello',
unicode: [String.fromCharCode(0xea01)],
renamed: false,
});
});
test('should append unicodes to files when the option is set', async () => {
const metadataService = getMetadataService({
prependUnicode: true,
});
await writeFile(join('fixtures', 'results', 'plop.svg'), 'plop', 'utf-8');
const infos = await promisify(metadataService)(join('fixtures', 'results', 'plop.svg'));
expect(infos).toEqual({
path: join('fixtures', 'results', 'uEA01-plop.svg'),
name: 'plop',
unicode: [String.fromCharCode(0xea01)],
renamed: true,
});
expect(await readFile(join('fixtures', 'results', 'uEA01-plop.svg'))).toBeTruthy();
unlink(join('fixtures', 'results', 'uEA01-plop.svg'));
try {
await readFile(join('fixtures', 'results', 'plop.svg'));
throw new YError('E_UNEXPECTED_SUCCESS');
}
catch (err) {
expect(err.code === 'E_UNEXPECTED_SUCCESS').toBeFalsy();
}
});
test('should log file rename errors', async () => {
const metadataService = getMetadataService({
prependUnicode: true,
startUnicode: 0xea02,
});
try {
await promisify(metadataService)(join('fixtures', 'results', 'plop.svg'));
throw new YError('E_UNEXPECTED_SUCCESS');
}
catch (err) {
expect(err).toBeTruthy();
expect(err.code === 'E_UNEXPECTED_SUCCESS').toBeFalsy();
}
try {
await readFile(join('fixtures', 'results', 'uEA02-plop.svg'));
throw new YError('E_UNEXPECTED_SUCCESS');
}
catch (err) {
expect(err.code === 'E_UNEXPECTED_SUCCESS').toBeFalsy();
}
});
});
describe('for code extraction', () => {
test('should work for simple codes', async () => {
const metadataService = getMetadataService();
const infos = await promisify(metadataService)('/var/plop/u0001-hello.svg');
expect(infos).toEqual({
path: '/var/plop/u0001-hello.svg',
name: 'hello',
unicode: [String.fromCharCode(0x0001)],
renamed: false,
});
});
test('should work for several codes', async () => {
const metadataService = getMetadataService();
const infos = await promisify(metadataService)('/var/plop/u0001,u0002-hello.svg');
expect(infos).toEqual({
path: '/var/plop/u0001,u0002-hello.svg',
name: 'hello',
unicode: [String.fromCharCode(0x0001), String.fromCharCode(0x0002)],
renamed: false,
});
});
test('should work for higher codepoint codes', async () => {
const metadataService = getMetadataService();
const infos = await promisify(metadataService)('/var/plop/u1F63A-hello.svg');
expect(infos).toEqual({
path: '/var/plop/u1F63A-hello.svg',
name: 'hello',
unicode: [String.fromCodePoint(0x1f63a)],
renamed: false,
});
});
test('should work for ligature codes', async () => {
const metadataService = getMetadataService();
const infos = await promisify(metadataService)('/var/plop/u0001u0002-hello.svg');
expect(infos).toEqual({
path: '/var/plop/u0001u0002-hello.svg',
name: 'hello',
unicode: [String.fromCharCode(0x0001) + String.fromCharCode(0x0002)],
renamed: false,
});
});
test('should work for nested codes', async () => {
const metadataService = getMetadataService();
const infos = await promisify(metadataService)('/var/plop/u0001u0002,u0001-hello.svg');
expect(infos).toEqual({
path: '/var/plop/u0001u0002,u0001-hello.svg',
name: 'hello',
unicode: [
String.fromCharCode(0x0001) + String.fromCharCode(0x0002),
String.fromCharCode(0x0001),
],
renamed: false,
});
});
test('should not set the same codepoint twice', async () => {
const metadataService = getMetadataService();
const infos = await promisify(metadataService)('/var/plop/uEA01-hello.svg');
expect(infos).toEqual({
path: '/var/plop/uEA01-hello.svg',
name: 'hello',
unicode: [String.fromCharCode(0xea01)],
renamed: false,
});
const infos2 = await promisify(metadataService)('/var/plop/plop.svg');
expect(infos2).toEqual({
path: '/var/plop/plop.svg',
name: 'plop',
unicode: [String.fromCharCode(0xea02)],
renamed: false,
});
});
test('should not set the same codepoint twice with different cases', async () => {
const metadataService = getMetadataService();
const infos = await promisify(metadataService)('/var/plop/UEA01-hello.svg');
expect(infos).toEqual({
path: '/var/plop/UEA01-hello.svg',
name: 'hello',
unicode: [String.fromCharCode(0xea01)],
renamed: false,
});
const infos2 = await promisify(metadataService)('/var/plop/uEA02-hello.svg');
expect(infos2).toEqual({
path: '/var/plop/uEA02-hello.svg',
name: 'hello',
unicode: [String.fromCharCode(0xea02)],
renamed: false,
});
const infos3 = await promisify(metadataService)('/var/plop/bell-o.svg');
expect(infos3).toEqual({
path: '/var/plop/bell-o.svg',
name: 'bell-o',
unicode: [String.fromCharCode(0xea03)],
renamed: false,
});
});
});
});
//# sourceMappingURL=metadata.test.js.map

File diff suppressed because one or more lines are too long