feat: add SVG as possible input format

Fix #1338
This commit is contained in:
ShareVB 2024-10-26 10:02:52 +02:00
parent bac8c8dc15
commit cd045934aa
5 changed files with 54 additions and 4 deletions

View file

@ -88,7 +88,9 @@
"plausible-tracker": "^0.3.8", "plausible-tracker": "^0.3.8",
"qrcode": "^1.5.1", "qrcode": "^1.5.1",
"randexp": "^0.5.3", "randexp": "^0.5.3",
"roboto-base64": "^0.1.2",
"sql-formatter": "^13.0.0", "sql-formatter": "^13.0.0",
"svg2png-wasm": "^1.4.1",
"ua-parser-js": "^1.0.35", "ua-parser-js": "^1.0.35",
"ulid": "^2.3.0", "ulid": "^2.3.0",
"unicode-emoji-json": "^0.4.0", "unicode-emoji-json": "^0.4.0",

14
pnpm-lock.yaml generated
View file

@ -161,9 +161,15 @@ dependencies:
randexp: randexp:
specifier: ^0.5.3 specifier: ^0.5.3
version: 0.5.3 version: 0.5.3
roboto-base64:
specifier: ^0.1.2
version: 0.1.2
sql-formatter: sql-formatter:
specifier: ^13.0.0 specifier: ^13.0.0
version: 13.0.0 version: 13.0.0
svg2png-wasm:
specifier: ^1.4.1
version: 1.4.1
ua-parser-js: ua-parser-js:
specifier: ^1.0.35 specifier: ^1.0.35
version: 1.0.35 version: 1.0.35
@ -7990,6 +7996,10 @@ packages:
glob: 7.2.3 glob: 7.2.3
dev: true dev: true
/roboto-base64@0.1.2:
resolution: {integrity: sha512-vXOGVIresupkks/WVNy4upNA4OorZx6XbHJt5ZQaat1tHBdJxui3oI101n9XV9Yg6HWIQKeAGnQbJJaUa8mfvA==}
dev: false
/rollup-plugin-terser@7.0.2(rollup@2.79.1): /rollup-plugin-terser@7.0.2(rollup@2.79.1):
resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==}
deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser 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==} resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
dev: true dev: true
/svg2png-wasm@1.4.1:
resolution: {integrity: sha512-ZFy1NtwZVAsslaTQoI+/QqX2sg0vjmgJ/jGAuLZZvYcRlndI54hLPiwLC9JzXlFBerfxN5JiS7kpEUG0mrXS3Q==}
dev: false
/svgo@3.0.2: /svgo@3.0.2:
resolution: {integrity: sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==} resolution: {integrity: sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}

BIN
public/svg2png_wasm_bg.wasm Normal file

Binary file not shown.

View file

@ -3,12 +3,24 @@ import { Base64 } from 'js-base64';
import type { MemoryImage } from 'image-in-browser'; import type { MemoryImage } from 'image-in-browser';
import { decodeImage, encodeBmp, encodeGif, encodeIco, encodeJpg, encodePng, encodePvr, encodeTga, encodeTiff } 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 { arrayBufferToWebP } from 'webp-converter-browser';
import { createSvg2png, initialize } from 'svg2png-wasm';
import { normal as robotoBase64 } from 'roboto-base64';
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64'; import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
import { useQueryParamOrStorage } from '@/composable/queryParams'; import { useQueryParamOrStorage } from '@/composable/queryParams';
function readAsText(file: File) {
return new Promise<string>((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 status = ref<'idle' | 'done' | 'error' | 'processing'>('idle');
const file = ref<File | null>(null); const file = ref<File | null>(null);
const svgScale = ref(2);
const base64OutputFile = ref(''); const base64OutputFile = ref('');
const fileName = ref(''); const fileName = ref('');
const fileExtension = ref(''); const fileExtension = ref('');
@ -64,10 +76,12 @@ const outputFormatHasQuality = computed(() => {
return outputFormat.value === 'jpg'; return outputFormat.value === 'jpg';
}); });
const svgWasmLoaded = ref(false);
async function onFileUploaded(uploadedFile: File) { async function onFileUploaded(uploadedFile: File) {
const outputFormatValue = outputFormat.value; const outputFormatValue = outputFormat.value;
file.value = uploadedFile; file.value = uploadedFile;
const fileBuffer = new Uint8Array(await uploadedFile.arrayBuffer()); let fileBuffer = new Uint8Array(await uploadedFile.arrayBuffer());
fileName.value = `${uploadedFile.name}`; fileName.value = `${uploadedFile.name}`;
status.value = 'processing'; 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()))}`; base64OutputFile.value = `data:image/webp;base64,${Base64.fromUint8Array(new Uint8Array(await encodedImage.arrayBuffer()))}`;
} }
else { 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({ const decodedImage = decodeImage({
data: fileBuffer, data: fileBuffer,
}); });
@ -122,9 +147,15 @@ async function onFileUploaded(uploadedFile: File) {
placeholder="Select output format" placeholder="Select output format"
mb-2 mb-2
/> />
<n-form-item v-if="outputFormatHasQuality" label="Output quality:" label-placement="left" mb-2>
<n-input-number v-model:value="outputQuality" :max="100" :min="0" w-full /> <div mb-2 flex justify-center>
</n-form-item> <n-form-item v-if="outputFormatHasQuality" label="Output quality:" label-placement="left">
<n-input-number v-model:value="outputQuality" :max="100" :min="0" w-full />
</n-form-item>
<n-form-item label="SVG scaling:" label-placement="left">
<n-input-number v-model:value="svgScale" :min="0" />
</n-form-item>
</div>
<div mt-3 flex justify-center> <div mt-3 flex justify-center>
<c-alert v-if="status === 'error'" type="error"> <c-alert v-if="status === 'error'" type="error">

View file

@ -0,0 +1,3 @@
declare module 'roboto-base64' {
export const normal: string;
}