Skip to content

Commit 9deccfa

Browse files
committed
Merge branch 'feat/heic-converter' into chore/all-my-stuffs
# Conflicts: # package.json # pnpm-lock.yaml # src/tools/index.ts
2 parents 9a38169 + aebc313 commit 9deccfa

File tree

6 files changed

+168
-1
lines changed

6 files changed

+168
-1
lines changed

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@
9999
"js-base64": "^3.7.7",
100100
"jsbarcode": "^3.11.6",
101101
"iconv-lite": "^0.6.3",
102+
"heic-convert": "^2.1.0",
103+
"highlight.js": "^11.7.0",
104+
"iarna-toml-esm": "^3.0.5",
105+
"ibantools": "^4.3.3",
102106
"js-base64": "^3.7.7",
103107
"json5": "^2.2.3",
104108
"jwt-decode": "^3.1.2",

pnpm-lock.yaml

Lines changed: 33 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
declare module 'heic-convert/browser' {
2+
interface ConversionOptions {
3+
/**
4+
* the HEIC file buffer
5+
*/
6+
buffer: ArrayBufferLike;
7+
/**
8+
* output format
9+
*/
10+
format: "JPEG" | "PNG";
11+
/**
12+
* the JPEG compression quality, between 0 and 1
13+
* @default 0.92
14+
*/
15+
quality?: number;
16+
}
17+
18+
interface Convertible {
19+
convert(): Promise<ArrayBuffer>;
20+
}
21+
22+
/** @async */
23+
declare function convert(image: ConversionOptions): Promise<ArrayBuffer>;
24+
declare namespace convert {
25+
/** @async */
26+
function all(image: ConversionOptions): Promise<Convertible[]>;
27+
}
28+
29+
export default convert;
30+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<script setup lang="ts">
2+
import { Base64 } from 'js-base64';
3+
import heicConvert from 'heic-convert/browser';
4+
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
5+
6+
const status = ref<'idle' | 'done' | 'error' | 'processing'>('idle');
7+
const file = ref<File | null>(null);
8+
9+
const base64OutputImage = ref('');
10+
const fileName = ref('');
11+
const format = ref('jpg');
12+
const formats = [
13+
{ value: 'jpg', label: 'JPEG' },
14+
{ value: 'png', label: 'PNG' },
15+
];
16+
const { download } = useDownloadFileFromBase64(
17+
{
18+
source: base64OutputImage,
19+
filename: fileName,
20+
});
21+
22+
async function onFileUploaded(uploadedFile: File) {
23+
file.value = uploadedFile;
24+
const fileBuffer = await uploadedFile.arrayBuffer();
25+
26+
fileName.value = `${uploadedFile.name}.${format.value}`;
27+
status.value = 'processing';
28+
try {
29+
let convertFormat;
30+
if (format.value === 'jpg') {
31+
convertFormat = 'JPEG';
32+
}
33+
else if (format.value === 'png') {
34+
convertFormat = 'PNG';
35+
}
36+
else {
37+
throw new Error('unknown format');
38+
}
39+
40+
const outputBuffer = await heicConvert({
41+
buffer: new Uint8Array(fileBuffer),
42+
format: convertFormat as ('JPEG' | 'PNG'),
43+
quality: 0.98,
44+
});
45+
base64OutputImage.value = `data:image/${convertFormat.toLowerCase()};base64,${Base64.fromUint8Array(new Uint8Array(outputBuffer))}`;
46+
47+
status.value = 'done';
48+
49+
download();
50+
}
51+
catch (e) {
52+
status.value = 'error';
53+
}
54+
}
55+
</script>
56+
57+
<template>
58+
<div>
59+
<c-select
60+
v-model:value="format"
61+
:options="formats"
62+
label="Output format"
63+
/>
64+
65+
<div style="flex: 0 0 100%" mt-3>
66+
<div mx-auto max-w-600px>
67+
<c-file-upload
68+
title="Drag and drop a HEIC file here, or click to select a file"
69+
accept=".heic,.heif" @file-upload="onFileUploaded"
70+
/>
71+
</div>
72+
</div>
73+
<div mt-3 flex justify-center>
74+
<img :src="base64OutputImage" max-w-300px>
75+
</div>
76+
77+
<div mt-3 flex justify-center>
78+
<c-alert v-if="status === 'error'" type="error">
79+
An error occured processing {{ fileName }}. HEIC/HEIF is invalid.
80+
</c-alert>
81+
<n-spin
82+
v-if="status === 'processing'"
83+
size="small"
84+
/>
85+
</div>
86+
</div>
87+
</template>

src/tools/heic-converter/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Photo } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'HEIC Converter',
6+
path: '/heic-converter',
7+
description: 'HEIC Converter to JPEG or PNG',
8+
keywords: ['heic', 'heif', 'convert', 'decoder', 'converter'],
9+
component: () => import('./heic-converter.vue'),
10+
icon: Photo,
11+
createdAt: new Date('2024-04-28'),
12+
});

src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { tool as curlConverter } from './curl-converter';
3636
import { tool as durationCalculator } from './duration-calculator';
3737
import { tool as folderStructureDiagram } from './folder-structure-diagram';
3838
import { tool as hddCalculator } from './hdd-calculator';
39+
import { tool as heicConverter } from './heic-converter';
3940
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
4041
import { tool as numeronymGenerator } from './numeronym-generator';
4142
import { tool as macAddressGenerator } from './mac-address-generator';
@@ -196,6 +197,7 @@ export const toolsByCategory: ToolCategory[] = [
196197
cameraRecorder,
197198
barcodeReader,
198199
barcodeGenerator,
200+
heicConverter,
199201
],
200202
},
201203
{

0 commit comments

Comments
 (0)