mirror of
https://github.com/tabler/tabler-icons.git
synced 2025-07-31 11:20:31 +02:00
Enhance webfont build process to support additional stroke weights and corresponding styles (#1313)
This commit is contained in:
@@ -8,123 +8,131 @@ import { execSync } from 'child_process'
|
|||||||
|
|
||||||
const DIR = getPackageDir('icons-webfont')
|
const DIR = getPackageDir('icons-webfont')
|
||||||
|
|
||||||
|
const strokes = {
|
||||||
|
200: 1,
|
||||||
|
300: 1.5,
|
||||||
|
400: 2,
|
||||||
|
}
|
||||||
|
|
||||||
const buildOutline = async () => {
|
const buildOutline = async () => {
|
||||||
let filesList = {}
|
let filesList = {}
|
||||||
const icons = getAllIcons(true)
|
const icons = getAllIcons(true)
|
||||||
|
|
||||||
const compileOptions = getCompileOptions()
|
const compileOptions = getCompileOptions()
|
||||||
|
|
||||||
await asyncForEach(Object.entries(icons), async ([type, icons]) => {
|
for (const strokeName in strokes) {
|
||||||
fs.mkdirSync(resolve(DIR, `icons-outlined/${type}`), { recursive: true })
|
const stroke = strokes[strokeName]
|
||||||
filesList[type] = []
|
|
||||||
|
|
||||||
await asyncForEach(icons, async function ({ name, content, unicode }) {
|
await asyncForEach(Object.entries(icons), async ([type, icons]) => {
|
||||||
console.log(type, name);
|
fs.mkdirSync(resolve(DIR, `icons-outlined/${strokeName}/${type}`), { recursive: true })
|
||||||
|
filesList[type] = []
|
||||||
|
|
||||||
if (compileOptions.includeIcons.length === 0 || compileOptions.includeIcons.indexOf(name) >= 0) {
|
await asyncForEach(icons, async function ({ name, content, unicode }) {
|
||||||
|
console.log(type, name);
|
||||||
|
|
||||||
if (unicode) {
|
if (compileOptions.includeIcons.length === 0 || compileOptions.includeIcons.indexOf(name) >= 0) {
|
||||||
console.log('Stroke for:', name, unicode)
|
|
||||||
|
|
||||||
let filename = `${name}.svg`
|
|
||||||
if (unicode) {
|
if (unicode) {
|
||||||
filename = `u${unicode.toUpperCase()}-${name}.svg`
|
console.log(`Stroke ${strokeName} for:`, name, unicode)
|
||||||
}
|
|
||||||
|
|
||||||
filesList[type].push(filename)
|
let filename = `${name}.svg`
|
||||||
|
if (unicode) {
|
||||||
content = content
|
filename = `u${unicode.toUpperCase()}-${name}.svg`
|
||||||
.replace('width="24"', 'width="1000"')
|
|
||||||
.replace('height="24"', 'height="1000"')
|
|
||||||
|
|
||||||
if (compileOptions.strokeWidth) {
|
|
||||||
content = content
|
|
||||||
.replace('stroke-width="2"', `stroke-width="${compileOptions.strokeWidth}"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const cachedFilename = `u${unicode.toUpperCase()}-${name}.svg`;
|
|
||||||
|
|
||||||
if (unicode && fs.existsSync(resolve(DIR, `icons-outlined/${type}/${cachedFilename}`))) {
|
|
||||||
// Get content
|
|
||||||
let cachedContent = fs.readFileSync(resolve(DIR, `icons-outlined/${type}/${cachedFilename}`), 'utf-8')
|
|
||||||
|
|
||||||
// Get hash
|
|
||||||
let cachedHash = '';
|
|
||||||
cachedContent = cachedContent.replace(/<!--\!cache:([a-z0-9]+)-->/, function (m, hash) {
|
|
||||||
cachedHash = hash;
|
|
||||||
return '';
|
|
||||||
})
|
|
||||||
|
|
||||||
// Check hash
|
|
||||||
if (crypto.createHash('sha1').update(cachedContent).digest("hex") === cachedHash) {
|
|
||||||
console.log('Cached stroke for:', name, unicode)
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filesList[type].push(filename)
|
||||||
|
|
||||||
|
content = content
|
||||||
|
.replace('width="24"', 'width="1000"')
|
||||||
|
.replace('height="24"', 'height="1000"')
|
||||||
|
|
||||||
|
content = content
|
||||||
|
.replace('stroke-width="2"', `stroke-width="${stroke}"`)
|
||||||
|
|
||||||
|
const cachedFilename = `u${unicode.toUpperCase()}-${name}.svg`;
|
||||||
|
|
||||||
|
if (unicode && fs.existsSync(resolve(DIR, `icons-outlined/${strokeName}/${type}/${cachedFilename}`))) {
|
||||||
|
// Get content
|
||||||
|
let cachedContent = fs.readFileSync(resolve(DIR, `icons-outlined/${strokeName}/${type}/${cachedFilename}`), 'utf-8')
|
||||||
|
|
||||||
|
// Get hash
|
||||||
|
let cachedHash = '';
|
||||||
|
cachedContent = cachedContent.replace(/<!--\!cache:([a-z0-9]+)-->/, function (m, hash) {
|
||||||
|
cachedHash = hash;
|
||||||
|
return '';
|
||||||
|
})
|
||||||
|
|
||||||
|
// Check hash
|
||||||
|
if (crypto.createHash('sha1').update(cachedContent).digest("hex") === cachedHash) {
|
||||||
|
console.log('Cached stroke for:', name, unicode)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await outlineStroke(content, {
|
||||||
|
optCurve: true,
|
||||||
|
steps: 4,
|
||||||
|
round: 0,
|
||||||
|
centerHorizontally: true,
|
||||||
|
fixedWidth: false,
|
||||||
|
color: 'black'
|
||||||
|
}).then(outlined => {
|
||||||
|
// Save file
|
||||||
|
fs.writeFileSync(resolve(DIR, `icons-outlined/${strokeName}/${type}/${filename}`), outlined, 'utf-8')
|
||||||
|
|
||||||
|
// Fix outline
|
||||||
|
execSync(`fontforge -lang=py -script .build/fix-outline.py icons-outlined/${strokeName}/${type}/${filename}`).toString()
|
||||||
|
execSync(`svgo icons-outlined/${strokeName}/${type}/${filename}`).toString()
|
||||||
|
|
||||||
|
// Add hash
|
||||||
|
const fixedFileContent = fs
|
||||||
|
.readFileSync(resolve(DIR, `icons-outlined/${strokeName}/${type}/${filename}`), 'utf-8')
|
||||||
|
.replace(/\n/g, ' ')
|
||||||
|
.trim(),
|
||||||
|
hashString = `<!--!cache:${crypto.createHash('sha1').update(fixedFileContent).digest("hex")}-->`
|
||||||
|
|
||||||
|
// Save file
|
||||||
|
fs.writeFileSync(
|
||||||
|
resolve(DIR, `icons-outlined/${strokeName}/${type}/${filename}`),
|
||||||
|
fixedFileContent + hashString,
|
||||||
|
'utf-8'
|
||||||
|
)
|
||||||
|
}).catch(error => console.log(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
await outlineStroke(content, {
|
|
||||||
optCurve: true,
|
|
||||||
steps: 4,
|
|
||||||
round: 0,
|
|
||||||
centerHorizontally: true,
|
|
||||||
fixedWidth: false,
|
|
||||||
color: 'black'
|
|
||||||
}).then(outlined => {
|
|
||||||
// Save file
|
|
||||||
fs.writeFileSync(resolve(DIR, `icons-outlined/${type}/${filename}`), outlined, 'utf-8')
|
|
||||||
|
|
||||||
// Fix outline
|
|
||||||
execSync(`fontforge -lang=py -script .build/fix-outline.py icons-outlined/${type}/${filename}`).toString()
|
|
||||||
execSync(`svgo icons-outlined/${type}/${filename}`).toString()
|
|
||||||
|
|
||||||
// Add hash
|
|
||||||
const fixedFileContent = fs
|
|
||||||
.readFileSync(resolve(DIR, `icons-outlined/${type}/${filename}`), 'utf-8')
|
|
||||||
.replace(/\n/g, ' ')
|
|
||||||
.trim(),
|
|
||||||
hashString = `<!--!cache:${crypto.createHash('sha1').update(fixedFileContent).digest("hex")}-->`
|
|
||||||
|
|
||||||
// Save file
|
|
||||||
fs.writeFileSync(
|
|
||||||
resolve(DIR, `icons-outlined/${type}/${filename}`),
|
|
||||||
fixedFileContent + hashString,
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
}).catch(error => console.log(error))
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
// Remove old files
|
// Remove old files
|
||||||
await asyncForEach(Object.entries(icons), async ([type, icons]) => {
|
await asyncForEach(Object.entries(icons), async ([type, icons]) => {
|
||||||
const existedFiles = (await glob(resolve(DIR, `icons-outlined/${type}/*.svg`))).map(file => basename(file))
|
const existedFiles = (await glob(resolve(DIR, `icons-outlined/${strokeName}/${type}/*.svg`))).map(file => basename(file))
|
||||||
existedFiles.forEach(file => {
|
existedFiles.forEach(file => {
|
||||||
if (filesList[type].indexOf(file) === -1) {
|
if (filesList[type].indexOf(file) === -1) {
|
||||||
console.log('Remove:', file)
|
console.log('Remove:', file)
|
||||||
fs.unlinkSync(resolve(DIR, `icons-outlined/${type}/${file}`))
|
fs.unlinkSync(resolve(DIR, `icons-outlined/${strokeName}/${type}/${file}`))
|
||||||
}
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
// Copy icons from firs to all directory
|
// Copy icons from firs to all directory
|
||||||
await asyncForEach(Object.entries(icons), async ([type, icons]) => {
|
await asyncForEach(Object.entries(icons), async ([type, icons]) => {
|
||||||
fs.mkdirSync(resolve(DIR, `icons-outlined/all`), { recursive: true })
|
fs.mkdirSync(resolve(DIR, `icons-outlined/${strokeName}/all`), { recursive: true })
|
||||||
|
|
||||||
await asyncForEach(icons, async function ({ name, unicode }) {
|
await asyncForEach(icons, async function ({ name, unicode }) {
|
||||||
const iconName = `u${unicode.toUpperCase()}-${name}`
|
const iconName = `u${unicode.toUpperCase()}-${name}`
|
||||||
|
|
||||||
if (fs.existsSync(resolve(DIR, `icons-outlined/${type}/${iconName}.svg`))) {
|
if (fs.existsSync(resolve(DIR, `icons-outlined/${strokeName}/${type}/${iconName}.svg`))) {
|
||||||
// Copy file
|
// Copy file
|
||||||
console.log(`Copy ${iconName} to all directory`)
|
console.log(`Copy ${iconName} to all directory`)
|
||||||
|
|
||||||
fs.copyFileSync(
|
fs.copyFileSync(
|
||||||
resolve(DIR, `icons-outlined/${type}/${iconName}.svg`),
|
resolve(DIR, `icons-outlined/${strokeName}/${type}/${iconName}.svg`),
|
||||||
resolve(DIR, `icons-outlined/all/${iconName}${type !== 'outline' ? `-${type}` : ''}.svg`)
|
resolve(DIR, `icons-outlined/${strokeName}/all/${iconName}${type !== 'outline' ? `-${type}` : ''}.svg`)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
console.log('Done')
|
console.log('Done')
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,12 @@ const p = getPackageJson()
|
|||||||
const DIR = getPackageDir('icons-webfont')
|
const DIR = getPackageDir('icons-webfont')
|
||||||
const fontHeight = 1000
|
const fontHeight = 1000
|
||||||
|
|
||||||
|
const strokes = {
|
||||||
|
200: 1,
|
||||||
|
300: 1.5,
|
||||||
|
400: 2,
|
||||||
|
}
|
||||||
|
|
||||||
const aliases = getAliases(true)
|
const aliases = getAliases(true)
|
||||||
|
|
||||||
fs.mkdirSync(`${DIR}/dist/fonts`, { recursive: true })
|
fs.mkdirSync(`${DIR}/dist/fonts`, { recursive: true })
|
||||||
@@ -26,50 +32,52 @@ const getAlliasesFlat = () => {
|
|||||||
return allAliases
|
return allAliases
|
||||||
}
|
}
|
||||||
|
|
||||||
asyncForEach(types, async type => {
|
for (const strokeName in strokes) {
|
||||||
console.log(`Building webfont for ${type} icons`)
|
asyncForEach(types, async type => {
|
||||||
|
console.log(`Building ${strokeName} webfont for ${type} icons`)
|
||||||
|
|
||||||
await webfont({
|
await webfont({
|
||||||
files: `icons-outlined/${type}/*.svg`,
|
files: `icons-outlined/${strokeName}/${type}/*.svg`,
|
||||||
fontName: 'tabler-icons',
|
fontName: 'tabler-icons',
|
||||||
prependUnicode: true,
|
prependUnicode: true,
|
||||||
formats,
|
formats,
|
||||||
normalize: true,
|
normalize: true,
|
||||||
fontHeight,
|
fontHeight,
|
||||||
descent: 100,
|
descent: 100,
|
||||||
ascent: 900,
|
ascent: 900,
|
||||||
fixedWidth: false
|
fixedWidth: false
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
formats.forEach(format => {
|
formats.forEach(format => {
|
||||||
fs.writeFileSync(`${DIR}/dist/fonts/tabler-icons${type !== 'all' ? `-${type}` : ''}.${format}`, result[format])
|
fs.writeFileSync(`${DIR}/dist/fonts/tabler-icons${strokeName !== "400" ? `-${strokeName}` : ''}${type !== 'all' ? `-${type}` : ''}.${format}`, result[format])
|
||||||
})
|
|
||||||
|
|
||||||
const glyphs = result.glyphsData
|
|
||||||
.map(icon => icon.metadata)
|
|
||||||
.sort(function (a, b) {
|
|
||||||
return ('' + a.name).localeCompare(b.name)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const options = {
|
const glyphs = result.glyphsData
|
||||||
name: `Tabler Icons${type !== 'all' ? ` ${toPascalCase(type)}` : ''}`,
|
.map(icon => icon.metadata)
|
||||||
fileName: `tabler-icons${type !== 'all' ? `-${type}` : ''}`,
|
.sort(function (a, b) {
|
||||||
glyphs,
|
return ('' + a.name).localeCompare(b.name)
|
||||||
v: p.version,
|
})
|
||||||
aliases: (type === 'all' ? getAlliasesFlat() : aliases[type]) || {}
|
|
||||||
}
|
|
||||||
|
|
||||||
//scss
|
const options = {
|
||||||
const compiled = template(fs.readFileSync(`${DIR}/.build/iconfont.scss`).toString())
|
name: `Tabler Icons ${strokeName}${type !== 'all' ? ` ${toPascalCase(type)}` : ''}`,
|
||||||
const resultSCSS = compiled(options)
|
fileName: `tabler-icons${strokeName !== "400" ? `-${strokeName}` : ''}${type !== 'all' ? `-${type}` : ''}`,
|
||||||
fs.writeFileSync(`${DIR}/dist/tabler-icons${type !== 'all' ? `-${type}` : ''}.scss`, resultSCSS)
|
glyphs,
|
||||||
|
v: p.version,
|
||||||
|
aliases: (type === 'all' ? getAlliasesFlat() : aliases[type]) || {}
|
||||||
|
}
|
||||||
|
|
||||||
//html
|
//scss
|
||||||
const compiledHtml = template(fs.readFileSync(`${DIR}/.build/iconfont.html`).toString())
|
const compiled = template(fs.readFileSync(`${DIR}/.build/iconfont.scss`).toString())
|
||||||
const resultHtml = compiledHtml(options)
|
const resultSCSS = compiled(options)
|
||||||
fs.writeFileSync(`${DIR}/dist/tabler-icons${type !== 'all' ? `-${type}` : ''}.html`, resultHtml)
|
fs.writeFileSync(`${DIR}/dist/tabler-icons${strokeName !== "400" ? `-${strokeName}` : ''}${type !== 'all' ? `-${type}` : ''}.scss`, resultSCSS)
|
||||||
})
|
|
||||||
.catch((error) => {
|
//html
|
||||||
throw error;
|
const compiledHtml = template(fs.readFileSync(`${DIR}/.build/iconfont.html`).toString())
|
||||||
});
|
const resultHtml = compiledHtml(options)
|
||||||
})
|
fs.writeFileSync(`${DIR}/dist/tabler-icons${strokeName !== "400" ? `-${strokeName}` : ''}${type !== 'all' ? `-${type}` : ''}.html`, resultHtml)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@@ -21,9 +21,9 @@
|
|||||||
"build:outline": "node .build/build-outline.mjs",
|
"build:outline": "node .build/build-outline.mjs",
|
||||||
"build:webfont": "rm -fd dist/fonts/* && node .build/build-webfont.mjs",
|
"build:webfont": "rm -fd dist/fonts/* && node .build/build-webfont.mjs",
|
||||||
"build:css": "pnpm run build:css:outline && pnpm run build:css:filled && pnpm run build:css:all",
|
"build:css": "pnpm run build:css:outline && pnpm run build:css:filled && pnpm run build:css:all",
|
||||||
"build:css:filled": "sass dist/tabler-icons-filled.scss dist/tabler-icons-filled.css --style expanded && sass dist/tabler-icons-filled.scss dist/tabler-icons-filled.min.css --style compressed",
|
"build:css:filled": "sass dist/tabler-icons-filled.scss dist/tabler-icons-filled.css --style expanded && sass dist/tabler-icons-filled.scss dist/tabler-icons-filled.min.css --style compressed && sass dist/tabler-icons-200-filled.scss dist/tabler-icons-200-filled.css --style expanded && sass dist/tabler-icons-200-filled.scss dist/tabler-icons-200-filled.min.css --style compressed && sass dist/tabler-icons-300-filled.scss dist/tabler-icons-300-filled.css --style expanded && sass dist/tabler-icons-300-filled.scss dist/tabler-icons-300-filled.min.css --style compressed",
|
||||||
"build:css:outline": "sass dist/tabler-icons-outline.scss dist/tabler-icons-outline.css --style expanded && sass dist/tabler-icons-outline.scss dist/tabler-icons-outline.min.css --style compressed",
|
"build:css:outline": "sass dist/tabler-icons-outline.scss dist/tabler-icons-outline.css --style expanded && sass dist/tabler-icons-outline.scss dist/tabler-icons-outline.min.css --style compressed && sass dist/tabler-icons-200-outline.scss dist/tabler-icons-200-outline.css --style expanded && sass dist/tabler-icons-200-outline.scss dist/tabler-icons-200-outline.min.css --style compressed && sass dist/tabler-icons-300-outline.scss dist/tabler-icons-300-outline.css --style expanded && sass dist/tabler-icons-300-outline.scss dist/tabler-icons-300-outline.min.css --style compressed",
|
||||||
"build:css:all": "sass dist/tabler-icons.scss dist/tabler-icons.css --style expanded && sass dist/tabler-icons.scss dist/tabler-icons.min.css --style compressed",
|
"build:css:all": "sass dist/tabler-icons.scss dist/tabler-icons.css --style expanded && sass dist/tabler-icons.scss dist/tabler-icons.min.css --style compressed && sass dist/tabler-icons-200.scss dist/tabler-icons-200.css --style expanded && sass dist/tabler-icons-200.scss dist/tabler-icons-200.min.css --style compressed && sass dist/tabler-icons-300.scss dist/tabler-icons-300.css --style expanded && sass dist/tabler-icons-300.scss dist/tabler-icons-300.min.css --style compressed",
|
||||||
"clean": "rm -rf dist && rm -rf ./icons-outlined",
|
"clean": "rm -rf dist && rm -rf ./icons-outlined",
|
||||||
"copy": "pnpm run copy:license",
|
"copy": "pnpm run copy:license",
|
||||||
"copy:license": "cp ../../LICENSE ./LICENSE"
|
"copy:license": "cp ../../LICENSE ./LICENSE"
|
||||||
|
Reference in New Issue
Block a user