Skip to content

Commit 72e88fb

Browse files
committed
add heic/heif support on convert-to-jpg
1 parent d22b7e5 commit 72e88fb

File tree

1 file changed

+59
-26
lines changed
  • src/pages/tools/image/generic/convert-to-jpg

1 file changed

+59
-26
lines changed

src/pages/tools/image/generic/convert-to-jpg/index.tsx

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import React, { useState } from 'react';
77
import * as Yup from 'yup';
88
import ToolContent from '@components/ToolContent';
99
import { ToolComponentProps } from '@tools/defineTool';
10+
import heic2any from 'heic2any';
1011

1112
const initialValues = {
1213
quality: 85,
@@ -18,6 +19,13 @@ const validationSchema = Yup.object({
1819
backgroundColor: Yup.string().required('Background color is required')
1920
});
2021

22+
function isHeicLike(file: File) {
23+
if (['heic', 'heif'].includes(file.type)) return true;
24+
25+
const name = file.name.toLowerCase();
26+
return name.endsWith('.heic') || name.endsWith('.heif');
27+
}
28+
2129
export default function ConvertToJpg({ title }: ToolComponentProps) {
2230
const [input, setInput] = useState<File | null>(null);
2331
const [result, setResult] = useState<File | null>(null);
@@ -33,14 +41,39 @@ export default function ConvertToJpg({ title }: ToolComponentProps) {
3341
quality: number,
3442
backgroundColor: string
3543
) => {
36-
const canvas = document.createElement('canvas');
37-
const ctx = canvas.getContext('2d');
38-
if (ctx == null) return;
44+
try {
45+
let workingBlob: Blob = file;
46+
let workingName = file.name;
3947

40-
const img = new Image();
41-
img.src = URL.createObjectURL(file);
48+
if (isHeicLike(file)) {
49+
try {
50+
const converted = await heic2any({
51+
blob: file,
52+
toType: 'image/png',
53+
quality: 1
54+
});
55+
const blobOut = Array.isArray(converted) ? converted[0] : converted;
56+
57+
workingBlob = blobOut as Blob;
58+
workingName = file.name.replace(/\.[^/.]+$/, '') + '.png';
59+
const pngFile = new File([workingBlob], workingName, {
60+
type: 'image/png'
61+
});
62+
setInput(pngFile);
63+
} catch (e) {
64+
console.error('heic2any conversion failed:', e);
65+
throw e;
66+
}
67+
}
68+
69+
const canvas = document.createElement('canvas');
70+
const ctx = canvas.getContext('2d');
71+
if (!ctx) return;
72+
73+
const objectUrl = URL.createObjectURL(workingBlob);
74+
const img = new Image();
75+
img.src = objectUrl;
4276

43-
try {
4477
await img.decode();
4578

4679
canvas.width = img.width;
@@ -51,34 +84,34 @@ export default function ConvertToJpg({ title }: ToolComponentProps) {
5184
try {
5285
//@ts-ignore
5386
bgColor = Color(backgroundColor).rgb().array();
54-
} catch (err) {
87+
} catch {
5588
bgColor = [255, 255, 255]; // Default to white
5689
}
57-
5890
ctx.fillStyle = `rgb(${bgColor[0]}, ${bgColor[1]}, ${bgColor[2]})`;
5991
ctx.fillRect(0, 0, canvas.width, canvas.height);
60-
61-
// Draw the image on top
6292
ctx.drawImage(img, 0, 0);
6393

64-
// Convert to JPG with specified quality
65-
canvas.toBlob(
66-
(blob) => {
67-
if (blob) {
68-
const fileName = file.name.replace(/\.[^/.]+$/, '') + '.jpg';
69-
const newFile = new File([blob], fileName, {
70-
type: 'image/jpeg'
71-
});
72-
setResult(newFile);
73-
}
74-
},
75-
'image/jpeg',
76-
quality / 100
77-
);
94+
URL.revokeObjectURL(objectUrl);
95+
96+
await new Promise<void>((resolve) => {
97+
canvas.toBlob(
98+
(blob) => {
99+
if (blob) {
100+
const baseName = workingName.replace(/\.[^/.]+$/, '');
101+
const outName = baseName + '.jpg';
102+
const newFile = new File([blob], outName, {
103+
type: 'image/jpeg'
104+
});
105+
setResult(newFile);
106+
}
107+
resolve();
108+
},
109+
'image/jpeg',
110+
quality / 100
111+
);
112+
});
78113
} catch (error) {
79114
console.error('Error processing image:', error);
80-
} finally {
81-
URL.revokeObjectURL(img.src);
82115
}
83116
};
84117

0 commit comments

Comments
 (0)