Skip to content

Commit 34510db

Browse files
committed
Fix the issue where large diagrams were not able to get inserted into google docs
1 parent e08dc67 commit 34510db

4 files changed

Lines changed: 83 additions & 6 deletions

File tree

src/client/create-diagram-dialog/components/create-diagram-dialog.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { useEffect, useState } from 'react';
2-
import { buildUrl, handleDialogClose } from '../../utils/helpers';
2+
import {
3+
buildUrl,
4+
handleDialogClose,
5+
compressBase64Image,
6+
} from '../../utils/helpers';
37
import { serverFunctions } from '../../utils/serverFunctions';
48
import useAuth from '../../hooks/useAuth';
59
import { CircularProgress, Container, Typography, Box } from '@mui/material';
@@ -41,8 +45,10 @@ const CreateDiagramDialog = () => {
4145
});
4246

4347
try {
48+
const compressedImage = await compressBase64Image(data.diagramImage);
49+
4450
await serverFunctions.insertBase64ImageWithMetadata(
45-
data.diagramImage,
51+
compressedImage,
4652
metadata.toString()
4753
);
4854
handleDialogClose();

src/client/edit-diagram-dialog/components/edit-diagram-dialog.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { useEffect, useState } from 'react';
22
import { serverFunctions } from '../../utils/serverFunctions';
3-
import { buildUrl, handleDialogClose } from '../../utils/helpers';
3+
import {
4+
buildUrl,
5+
handleDialogClose,
6+
compressBase64Image,
7+
} from '../../utils/helpers';
48
import useAuth from '../../hooks/useAuth';
59
import { showAlertDialog } from '../../utils/alert';
610
import LoadingOverlay from '../../components/loading-overlay';
@@ -59,8 +63,10 @@ const EditDiagramDialog = () => {
5963
minor: data.minor,
6064
});
6165
try {
66+
const compressedImage = await compressBase64Image(data.diagramImage);
67+
6268
await serverFunctions.replaceSelectedImageWithBase64AndSize(
63-
data.diagramImage,
69+
compressedImage,
6470
metadata.toString()
6571
);
6672
handleDialogClose();

src/client/select-diagram-dialog/components/select-diagram-dialog.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { useEffect, useState } from 'react';
2-
import { buildUrl, handleDialogClose } from '../../utils/helpers';
2+
import {
3+
buildUrl,
4+
handleDialogClose,
5+
compressBase64Image,
6+
} from '../../utils/helpers';
37
import { serverFunctions } from '../../utils/serverFunctions';
48
import useAuth from '../../hooks/useAuth';
59
import { CircularProgress, Container, Typography, Box } from '@mui/material';
@@ -50,8 +54,10 @@ const SelectDiagramDialog = () => {
5054
});
5155

5256
try {
57+
const compressedImage = await compressBase64Image(data.diagramImage);
58+
5359
await serverFunctions.insertBase64ImageWithMetadata(
54-
data.diagramImage,
60+
compressedImage,
5561
metadata.toString()
5662
);
5763
handleDialogClose();

src/client/utils/helpers.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,62 @@ export const handleDialogClose = () => {
1111
window.parent?.postMessage('closeDialog', '*');
1212
}
1313
};
14+
15+
/**
16+
* Resizes a base64 image if it exceeds the maximum dimension limit.
17+
* Enforces a maximum dimension of 4000px to prevent canvas issues.
18+
* @param base64Image - The base64 encoded image string (with or without data URI prefix)
19+
* @returns Promise<string> - The processed base64 image string (without data URI prefix)
20+
*/
21+
export const compressBase64Image = async (
22+
base64Image: string
23+
): Promise<string> => {
24+
try {
25+
const base64Data = base64Image.replace(/^data:image\/\w+;base64,/, '');
26+
27+
const img = new Image();
28+
const loadPromise = new Promise<HTMLImageElement>((resolve, reject) => {
29+
img.onload = () => resolve(img);
30+
img.onerror = reject;
31+
});
32+
33+
img.src = `data:image/png;base64,${base64Data}`;
34+
await loadPromise;
35+
36+
const maxDimension = 4000;
37+
38+
if (img.width <= maxDimension && img.height <= maxDimension) {
39+
return base64Data;
40+
}
41+
42+
const scale = maxDimension / Math.max(img.width, img.height);
43+
const newWidth = Math.floor(img.width * scale);
44+
const newHeight = Math.floor(img.height * scale);
45+
const canvas = document.createElement('canvas');
46+
canvas.width = newWidth;
47+
canvas.height = newHeight;
48+
49+
const ctx = canvas.getContext('2d');
50+
if (!ctx) {
51+
throw new Error('Could not get canvas context');
52+
}
53+
54+
ctx.drawImage(img, 0, 0, newWidth, newHeight);
55+
56+
const resizedDataUrl = canvas.toDataURL('image/png');
57+
let resizedBase64 = resizedDataUrl.replace(/^data:image\/\w+;base64,/, '');
58+
59+
while (resizedBase64.length % 4 !== 0) {
60+
resizedBase64 += '=';
61+
}
62+
63+
return resizedBase64;
64+
} catch (error) {
65+
console.error('Error processing image:', error);
66+
let original = base64Image.replace(/^data:image\/\w+;base64,/, '');
67+
while (original.length % 4 !== 0) {
68+
original += '=';
69+
}
70+
return original;
71+
}
72+
};

0 commit comments

Comments
 (0)