Merge pull request #1 from sharevb/feat/heic-converter

Feat/heic converter
This commit is contained in:
David Hayes 2024-08-07 19:04:45 -06:00 committed by GitHub
commit ae99f42540
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 284 additions and 89 deletions

View file

@ -0,0 +1,30 @@
declare module 'heic-convert/browser' {
interface ConversionOptions {
/**
* the HEIC file buffer
*/
buffer: ArrayBufferLike;
/**
* output format
*/
format: "JPEG" | "PNG";
/**
* the JPEG compression quality, between 0 and 1
* @default 0.92
*/
quality?: number;
}
interface Convertible {
convert(): Promise<ArrayBuffer>;
}
/** @async */
declare function convert(image: ConversionOptions): Promise<ArrayBuffer>;
declare namespace convert {
/** @async */
function all(image: ConversionOptions): Promise<Convertible[]>;
}
export default convert;
}

View file

@ -0,0 +1,87 @@
<script setup lang="ts">
import { Base64 } from 'js-base64';
import heicConvert from 'heic-convert/browser';
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
const status = ref<'idle' | 'done' | 'error' | 'processing'>('idle');
const file = ref<File | null>(null);
const base64OutputImage = ref('');
const fileName = ref('');
const format = ref('jpg');
const formats = [
{ value: 'jpg', label: 'JPEG' },
{ value: 'png', label: 'PNG' },
];
const { download } = useDownloadFileFromBase64(
{
source: base64OutputImage,
filename: fileName,
});
async function onFileUploaded(uploadedFile: File) {
file.value = uploadedFile;
const fileBuffer = await uploadedFile.arrayBuffer();
fileName.value = `${uploadedFile.name}.${format.value}`;
status.value = 'processing';
try {
let convertFormat;
if (format.value === 'jpg') {
convertFormat = 'JPEG';
}
else if (format.value === 'png') {
convertFormat = 'PNG';
}
else {
throw new Error('unknown format');
}
const outputBuffer = await heicConvert({
buffer: new Uint8Array(fileBuffer),
format: convertFormat as ('JPEG' | 'PNG'),
quality: 0.98,
});
base64OutputImage.value = `data:image/${convertFormat.toLowerCase()};base64,${Base64.fromUint8Array(new Uint8Array(outputBuffer))}`;
status.value = 'done';
download();
}
catch (e) {
status.value = 'error';
}
}
</script>
<template>
<div>
<c-select
v-model:value="format"
:options="formats"
label="Output format"
/>
<div style="flex: 0 0 100%" mt-3>
<div mx-auto max-w-600px>
<c-file-upload
title="Drag and drop a HEIC file here, or click to select a file"
accept=".heic,.heif" @file-upload="onFileUploaded"
/>
</div>
</div>
<div mt-3 flex justify-center>
<img :src="base64OutputImage" max-w-300px>
</div>
<div mt-3 flex justify-center>
<c-alert v-if="status === 'error'" type="error">
An error occured processing {{ fileName }}. HEIC/HEIF is invalid.
</c-alert>
<n-spin
v-if="status === 'processing'"
size="small"
/>
</div>
</div>
</template>

View file

@ -0,0 +1,12 @@
import { Photo } from '@vicons/tabler';
import { defineTool } from '../tool';
export const tool = defineTool({
name: 'HEIC Converter',
path: '/heic-converter',
description: 'HEIC Converter to JPEG or PNG',
keywords: ['heic', 'heif', 'convert', 'decoder', 'converter'],
component: () => import('./heic-converter.vue'),
icon: Photo,
createdAt: new Date('2024-04-28'),
});

View file

@ -6,6 +6,7 @@ import { tool as asciiTextDrawer } from './ascii-text-drawer';
import { tool as textToUnicode } from './text-to-unicode';
import { tool as safelinkDecoder } from './safelink-decoder';
import { tool as heicConverter } from './heic-converter';
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
import { tool as numeronymGenerator } from './numeronym-generator';
import { tool as macAddressGenerator } from './mac-address-generator';
@ -132,7 +133,13 @@ export const toolsByCategory: ToolCategory[] = [
},
{
name: 'Images and videos',
components: [qrCodeGenerator, wifiQrCodeGenerator, svgPlaceholderGenerator, cameraRecorder],
components: [
qrCodeGenerator,
wifiQrCodeGenerator,
svgPlaceholderGenerator,
cameraRecorder,
heicConverter,
],
},
{
name: 'Development',