@@ -70,7 +70,7 @@ class ImageCropElement extends ReactAdapterElement {
70
70
height
71
71
)
72
72
setCrop ( newcrop ) ;
73
- this . _updateCroppedImage ( newcrop ) ;
73
+ this . _updateCroppedImage ( newcrop ) . catch ( console . error ) ;
74
74
}
75
75
}
76
76
} ;
@@ -123,7 +123,7 @@ class ImageCropElement extends ReactAdapterElement {
123
123
} ;
124
124
125
125
const onComplete = ( c : PixelCrop ) => {
126
- this . _updateCroppedImage ( c ) ;
126
+ this . _updateCroppedImage ( c ) . catch ( console . error ) ;
127
127
} ;
128
128
129
129
return (
@@ -160,11 +160,56 @@ class ImageCropElement extends ReactAdapterElement {
160
160
} )
161
161
) ;
162
162
}
163
+
164
+ /**
165
+ * Attempts to detect the MIME type of given HTMLImageElement.
166
+ *
167
+ * Resolution order:
168
+ * 1. If the image is a data URL, extracts the MIME type directly.
169
+ * 2. Otherwise, sends a HEAD request to the image URL to read
170
+ * the Content-Type header (faster, no body download).
171
+ * 3. If the HEAD request does not provide Content-Type, falls back
172
+ * to fetching the full image as a Blob and using `blob.type`.
173
+ *
174
+ * @param img The HTMLImageElement whose MIME type should be detected.
175
+ * @returns A Promise resolving to the MIME type string (e.g. "image/png"),
176
+ * or null if it cannot be determined.
177
+ */
178
+ async #getImageMimeType( img : HTMLImageElement ) : Promise < string | null > {
179
+ if ( ! img . src ) {
180
+ return null ;
181
+ }
182
+
183
+ // Case 1: data URL (e.g., data:image/png;base64,...)
184
+ if ( img . src . startsWith ( "data:" ) ) {
185
+ const semiIndex = img . src . indexOf ( ";" ) ;
186
+ if ( semiIndex > 5 ) {
187
+ return img . src . substring ( 5 , semiIndex ) ;
188
+ }
189
+ return null ;
190
+ }
191
+
192
+ try {
193
+ // Case 2: try a HEAD request (fast, no body)
194
+ const headRes = await fetch ( img . src , { method : "HEAD" } ) ;
195
+ const mime = headRes . headers . get ( "Content-Type" ) ;
196
+ if ( mime ) {
197
+ return mime ;
198
+ }
199
+
200
+ // Case 3: fallback — fetch full blob
201
+ const blobRes = await fetch ( img . src ) ;
202
+ const blob = await blobRes . blob ( ) ;
203
+ return blob . type || null ;
204
+ } catch ( err ) {
205
+ console . error ( "Error fetching image MIME type:" , err ) ;
206
+ return null ;
207
+ }
208
+ }
163
209
164
- public _updateCroppedImage ( crop : PixelCrop | PercentCrop ) {
210
+ public async _updateCroppedImage ( crop : PixelCrop | PercentCrop ) {
165
211
const image = this . querySelector ( "img" ) ;
166
212
if ( crop && image ) {
167
-
168
213
crop = convertToPixelCrop ( crop , image . width , image . height ) ;
169
214
170
215
// create a canvas element to draw the cropped image
@@ -207,8 +252,10 @@ class ImageCropElement extends ReactAdapterElement {
207
252
208
253
ctx . restore ( ) ;
209
254
255
+ const imgMimeType = await this . #getImageMimeType( image ) || 'image/png' ;
256
+
210
257
// get the cropped image
211
- let croppedImageDataUri = canvas . toDataURL ( "image/png" , 1.0 ) ;
258
+ let croppedImageDataUri = canvas . toDataURL ( imgMimeType , 1.0 ) ;
212
259
213
260
// dispatch the event containing cropped image
214
261
this . fireCroppedImageEvent ( croppedImageDataUri ) ;
0 commit comments