diff --git a/package.json b/package.json index db0961c9..ec26a1d5 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,9 @@ "plausible-tracker": "^0.3.8", "qrcode": "^1.5.1", "randexp": "^0.5.3", + "roboto-base64": "^0.1.2", "sql-formatter": "^13.0.0", + "svg2png-wasm": "^1.4.1", "ua-parser-js": "^1.0.35", "ulid": "^2.3.0", "unicode-emoji-json": "^0.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3ef9c90..4262b87b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -161,9 +161,15 @@ dependencies: randexp: specifier: ^0.5.3 version: 0.5.3 + roboto-base64: + specifier: ^0.1.2 + version: 0.1.2 sql-formatter: specifier: ^13.0.0 version: 13.0.0 + svg2png-wasm: + specifier: ^1.4.1 + version: 1.4.1 ua-parser-js: specifier: ^1.0.35 version: 1.0.35 @@ -7990,6 +7996,10 @@ packages: glob: 7.2.3 dev: true + /roboto-base64@0.1.2: + resolution: {integrity: sha512-vXOGVIresupkks/WVNy4upNA4OorZx6XbHJt5ZQaat1tHBdJxui3oI101n9XV9Yg6HWIQKeAGnQbJJaUa8mfvA==} + dev: false + /rollup-plugin-terser@7.0.2(rollup@2.79.1): resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser @@ -8441,6 +8451,10 @@ packages: resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} dev: true + /svg2png-wasm@1.4.1: + resolution: {integrity: sha512-ZFy1NtwZVAsslaTQoI+/QqX2sg0vjmgJ/jGAuLZZvYcRlndI54hLPiwLC9JzXlFBerfxN5JiS7kpEUG0mrXS3Q==} + dev: false + /svgo@3.0.2: resolution: {integrity: sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==} engines: {node: '>=14.0.0'} diff --git a/public/svg2png_wasm_bg.wasm b/public/svg2png_wasm_bg.wasm new file mode 100644 index 00000000..1bef3f1f Binary files /dev/null and b/public/svg2png_wasm_bg.wasm differ diff --git a/src/tools/image-converter/image-converter.vue b/src/tools/image-converter/image-converter.vue index fedda776..4e4a9fb7 100644 --- a/src/tools/image-converter/image-converter.vue +++ b/src/tools/image-converter/image-converter.vue @@ -3,12 +3,24 @@ import { Base64 } from 'js-base64'; import type { MemoryImage } from 'image-in-browser'; import { decodeImage, encodeBmp, encodeGif, encodeIco, encodeJpg, encodePng, encodePvr, encodeTga, encodeTiff } from 'image-in-browser'; import { arrayBufferToWebP } from 'webp-converter-browser'; +import { createSvg2png, initialize } from 'svg2png-wasm'; +import { normal as robotoBase64 } from 'roboto-base64'; import { useDownloadFileFromBase64 } from '@/composable/downloadBase64'; import { useQueryParamOrStorage } from '@/composable/queryParams'; +function readAsText(file: File) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsText(file); + reader.onload = () => resolve(reader.result?.toString() ?? ''); + reader.onerror = error => reject(error); + }); +} + const status = ref<'idle' | 'done' | 'error' | 'processing'>('idle'); const file = ref(null); +const svgScale = ref(2); const base64OutputFile = ref(''); const fileName = ref(''); const fileExtension = ref(''); @@ -64,10 +76,12 @@ const outputFormatHasQuality = computed(() => { return outputFormat.value === 'jpg'; }); +const svgWasmLoaded = ref(false); + async function onFileUploaded(uploadedFile: File) { const outputFormatValue = outputFormat.value; file.value = uploadedFile; - const fileBuffer = new Uint8Array(await uploadedFile.arrayBuffer()); + let fileBuffer = new Uint8Array(await uploadedFile.arrayBuffer()); fileName.value = `${uploadedFile.name}`; status.value = 'processing'; @@ -78,6 +92,17 @@ async function onFileUploaded(uploadedFile: File) { base64OutputFile.value = `data:image/webp;base64,${Base64.fromUint8Array(new Uint8Array(await encodedImage.arrayBuffer()))}`; } else { + if (uploadedFile.type === 'image/svg+xml') { + if (!svgWasmLoaded.value) { + await initialize(fetch('/svg2png_wasm_bg.wasm')); + svgWasmLoaded.value = true; + } + const svg2png = createSvg2png({ + fonts: [Base64.toUint8Array(robotoBase64)], + }); + fileBuffer = await svg2png(await readAsText(uploadedFile), { scale: svgScale.value }); + svg2png.dispose(); + } const decodedImage = decodeImage({ data: fileBuffer, }); @@ -122,9 +147,15 @@ async function onFileUploaded(uploadedFile: File) { placeholder="Select output format" mb-2 /> - - - + +
+ + + + + + +
diff --git a/src/tools/image-converter/roboto-base64.d.ts b/src/tools/image-converter/roboto-base64.d.ts new file mode 100644 index 00000000..248d11b3 --- /dev/null +++ b/src/tools/image-converter/roboto-base64.d.ts @@ -0,0 +1,3 @@ +declare module 'roboto-base64' { + export const normal: string; +} \ No newline at end of file