Switch to @resvg/resvg-wasm
for generate-images
(#35415)
Use the WASM module of [`resvg-js`](https://github.com/thx/resvg-js) to replace `fabric` and the problematic native `canvas` dependency. WASM works cross-platform so we can include it in the main `package.json`.
1
.gitignore
vendored
@@ -74,7 +74,6 @@ cpu.out
|
|||||||
/tests/*.ini
|
/tests/*.ini
|
||||||
/tests/**/*.git/**/*.sample
|
/tests/**/*.git/**/*.sample
|
||||||
/node_modules
|
/node_modules
|
||||||
/tools/node_modules
|
|
||||||
/.venv
|
/.venv
|
||||||
/yarn.lock
|
/yarn.lock
|
||||||
/yarn-error.log
|
/yarn-error.log
|
||||||
|
8
Makefile
@@ -230,7 +230,7 @@ node-check:
|
|||||||
|
|
||||||
.PHONY: clean-all
|
.PHONY: clean-all
|
||||||
clean-all: clean ## delete backend, frontend and integration files
|
clean-all: clean ## delete backend, frontend and integration files
|
||||||
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules tools/node_modules
|
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean: ## delete backend and integration files
|
clean: ## delete backend and integration files
|
||||||
@@ -847,10 +847,6 @@ node_modules: pnpm-lock.yaml
|
|||||||
pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
@touch node_modules
|
@touch node_modules
|
||||||
|
|
||||||
tools/node_modules: tools/package.json
|
|
||||||
cd tools && pnpm install
|
|
||||||
@touch tools/node_modules
|
|
||||||
|
|
||||||
.venv: uv.lock
|
.venv: uv.lock
|
||||||
uv sync
|
uv sync
|
||||||
@touch .venv
|
@touch .venv
|
||||||
@@ -925,7 +921,7 @@ generate-gitignore: ## update gitignore files
|
|||||||
$(GO) run build/generate-gitignores.go
|
$(GO) run build/generate-gitignores.go
|
||||||
|
|
||||||
.PHONY: generate-images
|
.PHONY: generate-images
|
||||||
generate-images: | node_modules tools/node_modules ## generate images (requires cairo development packages)
|
generate-images: | node_modules ## generate images
|
||||||
cd tools && node generate-images.js $(TAGS)
|
cd tools && node generate-images.js $(TAGS)
|
||||||
|
|
||||||
.PHONY: generate-manpage
|
.PHONY: generate-manpage
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
"@github/text-expander-element": "2.9.2",
|
"@github/text-expander-element": "2.9.2",
|
||||||
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
||||||
"@primer/octicons": "19.15.5",
|
"@primer/octicons": "19.15.5",
|
||||||
|
"@resvg/resvg-wasm": "2.6.2",
|
||||||
"@silverwind/vue3-calendar-heatmap": "2.0.6",
|
"@silverwind/vue3-calendar-heatmap": "2.0.6",
|
||||||
"@techknowlogick/license-checker-webpack-plugin": "0.3.0",
|
"@techknowlogick/license-checker-webpack-plugin": "0.3.0",
|
||||||
"add-asset-webpack-plugin": "3.0.0",
|
"add-asset-webpack-plugin": "3.0.0",
|
||||||
|
9
pnpm-lock.yaml
generated
@@ -35,6 +35,9 @@ importers:
|
|||||||
'@primer/octicons':
|
'@primer/octicons':
|
||||||
specifier: 19.15.5
|
specifier: 19.15.5
|
||||||
version: 19.15.5
|
version: 19.15.5
|
||||||
|
'@resvg/resvg-wasm':
|
||||||
|
specifier: 2.6.2
|
||||||
|
version: 2.6.2
|
||||||
'@silverwind/vue3-calendar-heatmap':
|
'@silverwind/vue3-calendar-heatmap':
|
||||||
specifier: 2.0.6
|
specifier: 2.0.6
|
||||||
version: 2.0.6(tippy.js@6.3.7)(vue@3.5.18(typescript@5.8.3))
|
version: 2.0.6(tippy.js@6.3.7)(vue@3.5.18(typescript@5.8.3))
|
||||||
@@ -797,6 +800,10 @@ packages:
|
|||||||
'@primer/octicons@19.15.5':
|
'@primer/octicons@19.15.5':
|
||||||
resolution: {integrity: sha512-FCXPTlXlHvAS3rRBd1C/xVBYSYzPPwS8tNcUxnvUYK6L4/d+zUy2KExLtzW+L9xKo2z8J9uY+c1VCsNRf+b4MQ==}
|
resolution: {integrity: sha512-FCXPTlXlHvAS3rRBd1C/xVBYSYzPPwS8tNcUxnvUYK6L4/d+zUy2KExLtzW+L9xKo2z8J9uY+c1VCsNRf+b4MQ==}
|
||||||
|
|
||||||
|
'@resvg/resvg-wasm@2.6.2':
|
||||||
|
resolution: {integrity: sha512-FqALmHI8D4o6lk/LRWDnhw95z5eO+eAa6ORjVg09YRR7BkcM6oPHU9uyC0gtQG5vpFLvgpeU4+zEAz2H8APHNw==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
|
||||||
'@rolldown/pluginutils@1.0.0-beta.29':
|
'@rolldown/pluginutils@1.0.0-beta.29':
|
||||||
resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==}
|
resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==}
|
||||||
|
|
||||||
@@ -5481,6 +5488,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
object-assign: 4.1.1
|
object-assign: 4.1.1
|
||||||
|
|
||||||
|
'@resvg/resvg-wasm@2.6.2': {}
|
||||||
|
|
||||||
'@rolldown/pluginutils@1.0.0-beta.29': {}
|
'@rolldown/pluginutils@1.0.0-beta.29': {}
|
||||||
|
|
||||||
'@rollup/plugin-commonjs@22.0.2(rollup@2.79.2)':
|
'@rollup/plugin-commonjs@22.0.2(rollup@2.79.2)':
|
||||||
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 13 KiB |
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
import {loadSVGFromString, Canvas, Rect, util} from 'fabric/node'; // eslint-disable-line import-x/no-unresolved
|
import {initWasm, Resvg} from '@resvg/resvg-wasm';
|
||||||
import {optimize} from 'svgo';
|
import {optimize} from 'svgo';
|
||||||
import {readFile, writeFile} from 'node:fs/promises';
|
import {readFile, writeFile} from 'node:fs/promises';
|
||||||
import {argv, exit} from 'node:process';
|
import {argv, exit} from 'node:process';
|
||||||
@@ -27,37 +27,23 @@ async function generate(svg, path, {size, bg}) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {objects, options} = await loadSVGFromString(svg);
|
const resvgJS = new Resvg(svg, {
|
||||||
const canvas = new Canvas();
|
fitTo: {
|
||||||
canvas.setDimensions({width: size, height: size});
|
mode: 'width',
|
||||||
const ctx = canvas.getContext('2d');
|
value: size,
|
||||||
ctx.scale(options.width ? (size / options.width) : 1, options.height ? (size / options.height) : 1);
|
},
|
||||||
|
...(bg && {background: 'white'}),
|
||||||
if (bg) {
|
});
|
||||||
canvas.add(new Rect({
|
const renderedImage = resvgJS.render();
|
||||||
left: 0,
|
const pngBytes = renderedImage.asPng();
|
||||||
top: 0,
|
await writeFile(outputFile, Buffer.from(pngBytes));
|
||||||
height: size * (1 / (size / options.height)),
|
|
||||||
width: size * (1 / (size / options.width)),
|
|
||||||
fill: 'white',
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.add(util.groupSVGElements(objects, options));
|
|
||||||
canvas.renderAll();
|
|
||||||
|
|
||||||
let png = Buffer.from([]);
|
|
||||||
for await (const chunk of canvas.createPNGStream()) {
|
|
||||||
png = Buffer.concat([png, chunk]);
|
|
||||||
}
|
|
||||||
|
|
||||||
await writeFile(outputFile, png);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const gitea = argv.slice(2).includes('gitea');
|
const gitea = argv.slice(2).includes('gitea');
|
||||||
const logoSvg = await readFile(new URL('../assets/logo.svg', import.meta.url), 'utf8');
|
const logoSvg = await readFile(new URL('../assets/logo.svg', import.meta.url), 'utf8');
|
||||||
const faviconSvg = await readFile(new URL('../assets/favicon.svg', import.meta.url), 'utf8');
|
const faviconSvg = await readFile(new URL('../assets/favicon.svg', import.meta.url), 'utf8');
|
||||||
|
await initWasm(await readFile(new URL(import.meta.resolve('@resvg/resvg-wasm/index_bg.wasm'))));
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
generate(logoSvg, '../public/assets/img/logo.svg', {size: 32}),
|
generate(logoSvg, '../public/assets/img/logo.svg', {size: 32}),
|
||||||
|
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "gitea-tools",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Build tools for Gitea",
|
|
||||||
"type": "module",
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"fabric": "^6.7.1",
|
|
||||||
"svgo": "^4.0.0",
|
|
||||||
"fast-glob": "^3.3.3"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"canvas": "^3.2.0"
|
|
||||||
},
|
|
||||||
"pnpm": {
|
|
||||||
"onlyBuiltDependencies": ["canvas"],
|
|
||||||
"overrides": {
|
|
||||||
"canvas": "3.2.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|