1 | /**
|
2 | * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4 | */
|
5 | import { global } from 'ckeditor5/src/utils.js';
|
6 | /**
|
7 | * Creates a regular expression used to test for image files.
|
8 | *
|
9 | * ```ts
|
10 | * const imageType = createImageTypeRegExp( [ 'png', 'jpeg', 'svg+xml', 'vnd.microsoft.icon' ] );
|
11 | *
|
12 | * console.log( 'is supported image', imageType.test( file.type ) );
|
13 | * ```
|
14 | */
|
15 | export function createImageTypeRegExp(types) {
|
16 | // Sanitize the MIME type name which may include: "+", "-" or ".".
|
17 | const regExpSafeNames = types.map(type => type.replace('+', '\\+'));
|
18 | return new RegExp(`^image\\/(${regExpSafeNames.join('|')})$`);
|
19 | }
|
20 | /**
|
21 | * Creates a promise that fetches the image local source (Base64 or blob) and resolves with a `File` object.
|
22 | *
|
23 | * @param image Image whose source to fetch.
|
24 | * @returns A promise which resolves when an image source is fetched and converted to a `File` instance.
|
25 | * It resolves with a `File` object. If there were any errors during file processing, the promise will be rejected.
|
26 | */
|
27 | export function fetchLocalImage(image) {
|
28 | return new Promise((resolve, reject) => {
|
29 | const imageSrc = image.getAttribute('src');
|
30 | // Fetch works asynchronously and so does not block browser UI when processing data.
|
31 | fetch(imageSrc)
|
32 | .then(resource => resource.blob())
|
33 | .then(blob => {
|
34 | const mimeType = getImageMimeType(blob, imageSrc);
|
35 | const ext = mimeType.replace('image/', '');
|
36 | const filename = `image.${ext}`;
|
37 | const file = new File([blob], filename, { type: mimeType });
|
38 | resolve(file);
|
39 | })
|
40 | .catch(err => {
|
41 | // Fetch fails only, if it can't make a request due to a network failure or if anything prevented the request
|
42 | // from completing, i.e. the Content Security Policy rules. It is not possible to detect the exact cause of failure,
|
43 | // so we are just trying the fallback solution, if general TypeError is thrown.
|
44 | return err && err.name === 'TypeError' ?
|
45 | convertLocalImageOnCanvas(imageSrc).then(resolve).catch(reject) :
|
46 | reject(err);
|
47 | });
|
48 | });
|
49 | }
|
50 | /**
|
51 | * Checks whether a given node is an image element with a local source (Base64 or blob).
|
52 | *
|
53 | * @param node The node to check.
|
54 | */
|
55 | export function isLocalImage(imageUtils, node) {
|
56 | if (!imageUtils.isInlineImageView(node) || !node.getAttribute('src')) {
|
57 | return false;
|
58 | }
|
59 | return !!node.getAttribute('src').match(/^data:image\/\w+;base64,/g) ||
|
60 | !!node.getAttribute('src').match(/^blob:/g);
|
61 | }
|
62 | /**
|
63 | * Extracts an image type based on its blob representation or its source.
|
64 | * @param blob Image blob representation.
|
65 | * @param src Image `src` attribute value.
|
66 | */
|
67 | function getImageMimeType(blob, src) {
|
68 | if (blob.type) {
|
69 | return blob.type;
|
70 | }
|
71 | else if (src.match(/data:(image\/\w+);base64/)) {
|
72 | return src.match(/data:(image\/\w+);base64/)[1].toLowerCase();
|
73 | }
|
74 | else {
|
75 | // Fallback to 'jpeg' as common extension.
|
76 | return 'image/jpeg';
|
77 | }
|
78 | }
|
79 | /**
|
80 | * Creates a promise that converts the image local source (Base64 or blob) to a blob using canvas and resolves
|
81 | * with a `File` object.
|
82 | * @param imageSrc Image `src` attribute value.
|
83 | * @returns A promise which resolves when an image source is converted to a `File` instance.
|
84 | * It resolves with a `File` object. If there were any errors during file processing, the promise will be rejected.
|
85 | */
|
86 | function convertLocalImageOnCanvas(imageSrc) {
|
87 | return getBlobFromCanvas(imageSrc).then(blob => {
|
88 | const mimeType = getImageMimeType(blob, imageSrc);
|
89 | const ext = mimeType.replace('image/', '');
|
90 | const filename = `image.${ext}`;
|
91 | return new File([blob], filename, { type: mimeType });
|
92 | });
|
93 | }
|
94 | /**
|
95 | * Creates a promise that resolves with a `Blob` object converted from the image source (Base64 or blob).
|
96 | * @param imageSrc Image `src` attribute value.
|
97 | */
|
98 | function getBlobFromCanvas(imageSrc) {
|
99 | return new Promise((resolve, reject) => {
|
100 | const image = global.document.createElement('img');
|
101 | image.addEventListener('load', () => {
|
102 | const canvas = global.document.createElement('canvas');
|
103 | canvas.width = image.width;
|
104 | canvas.height = image.height;
|
105 | const ctx = canvas.getContext('2d');
|
106 | ctx.drawImage(image, 0, 0);
|
107 | canvas.toBlob(blob => blob ? resolve(blob) : reject());
|
108 | });
|
109 | image.addEventListener('error', () => reject());
|
110 | image.src = imageSrc;
|
111 | });
|
112 | }
|