UNPKG

@ckeditor/ckeditor5-image

Version:

Image feature for CKEditor 5.

113 lines (112 loc) 4.7 kB
/** * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ import { global } from 'ckeditor5/src/utils'; /** * Creates a regular expression used to test for image files. * * ```ts * const imageType = createImageTypeRegExp( [ 'png', 'jpeg', 'svg+xml', 'vnd.microsoft.icon' ] ); * * console.log( 'is supported image', imageType.test( file.type ) ); * ``` */ export function createImageTypeRegExp(types) { // Sanitize the MIME type name which may include: "+", "-" or ".". const regExpSafeNames = types.map(type => type.replace('+', '\\+')); return new RegExp(`^image\\/(${regExpSafeNames.join('|')})$`); } /** * Creates a promise that fetches the image local source (Base64 or blob) and resolves with a `File` object. * * @param image Image whose source to fetch. * @returns A promise which resolves when an image source is fetched and converted to a `File` instance. * It resolves with a `File` object. If there were any errors during file processing, the promise will be rejected. */ export function fetchLocalImage(image) { return new Promise((resolve, reject) => { const imageSrc = image.getAttribute('src'); // Fetch works asynchronously and so does not block browser UI when processing data. fetch(imageSrc) .then(resource => resource.blob()) .then(blob => { const mimeType = getImageMimeType(blob, imageSrc); const ext = mimeType.replace('image/', ''); const filename = `image.${ext}`; const file = new File([blob], filename, { type: mimeType }); resolve(file); }) .catch(err => { // Fetch fails only, if it can't make a request due to a network failure or if anything prevented the request // from completing, i.e. the Content Security Policy rules. It is not possible to detect the exact cause of failure, // so we are just trying the fallback solution, if general TypeError is thrown. return err && err.name === 'TypeError' ? convertLocalImageOnCanvas(imageSrc).then(resolve).catch(reject) : reject(err); }); }); } /** * Checks whether a given node is an image element with a local source (Base64 or blob). * * @param node The node to check. */ export function isLocalImage(imageUtils, node) { if (!imageUtils.isInlineImageView(node) || !node.getAttribute('src')) { return false; } return !!node.getAttribute('src').match(/^data:image\/\w+;base64,/g) || !!node.getAttribute('src').match(/^blob:/g); } /** * Extracts an image type based on its blob representation or its source. * @param blob Image blob representation. * @param src Image `src` attribute value. */ function getImageMimeType(blob, src) { if (blob.type) { return blob.type; } else if (src.match(/data:(image\/\w+);base64/)) { return src.match(/data:(image\/\w+);base64/)[1].toLowerCase(); } else { // Fallback to 'jpeg' as common extension. return 'image/jpeg'; } } /** * Creates a promise that converts the image local source (Base64 or blob) to a blob using canvas and resolves * with a `File` object. * @param imageSrc Image `src` attribute value. * @returns A promise which resolves when an image source is converted to a `File` instance. * It resolves with a `File` object. If there were any errors during file processing, the promise will be rejected. */ function convertLocalImageOnCanvas(imageSrc) { return getBlobFromCanvas(imageSrc).then(blob => { const mimeType = getImageMimeType(blob, imageSrc); const ext = mimeType.replace('image/', ''); const filename = `image.${ext}`; return new File([blob], filename, { type: mimeType }); }); } /** * Creates a promise that resolves with a `Blob` object converted from the image source (Base64 or blob). * @param imageSrc Image `src` attribute value. */ function getBlobFromCanvas(imageSrc) { return new Promise((resolve, reject) => { const image = global.document.createElement('img'); image.addEventListener('load', () => { const canvas = global.document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; const ctx = canvas.getContext('2d'); ctx.drawImage(image, 0, 0); canvas.toBlob(blob => blob ? resolve(blob) : reject()); }); image.addEventListener('error', () => reject()); image.src = imageSrc; }); }