Background
The company’s recent upgrade project requires rich text to support the copying and pasting of word content containing multiple pictures. There is no other way. For the sake of user experience, we can only find a solution. Through various data searches, we have sorted out an effective solution for personal testing.
Plan: 1. Listen to the paste event and obtain the information in the clipboard through e.clipboardData
2. Get the local path of the image and find the image data through the RTF content.
3. Convert the image to base64 (not uploaded to the server, will be added later if necessary)
Implementation steps (the codes are all modified in the ueditor.all.js file)
1. Define the variables and find the code for the ueditor initialization event (you can directly search for the “_initEvents” method)
var wordImg = [];
_initEvents: function () { var me = this, doc = me.document, win = me.window; //************************New content**************************** **** //Monitor word copy and paste function doc.addEventListener("paste", function (e) { if (!(e.clipboardData & amp; & amp; e.clipboardData.items)) { return; } /*** * Paste the copied image, or paste the screenshot * */ const items = (e.clipboardData || window.clipboardData).items; let file = null; for (let i = 0; i < items.length; i + + ) { if (items[i].type.indexOf("image") !== -1) { file = items[i].getAsFile(); break; } } me.fileToBase64(file,me) /** * Paste word document * */ const clipboardData = e.clipboardData; //HTML text in the pasteboard let copyStr = clipboardData.getData('text/html'); //RTF data in the pasteboard let rtf = clipboardData.getData('text/rtf'); //Get the number of pictures in the pasteboard let imgs = me.findAllImageElementsWithLocalSource(copyStr); me.replaceImagesFileSourceWithInlineRepresentation(imgs, me.extractImageDataFromRtf(rtf)) }) //************************New content**************************** **** me._proxyDomEvent = utils.bind(me._proxyDomEvent, me); domUtils.on(doc, ['click', 'contextmenu', 'mousedown', 'keydown', 'keyup', 'keypress', 'mouseup', 'mouseover', 'mouseout', 'selectstart'], me. _proxyDomEvent); domUtils.on(win, ['focus', 'blur'], me._proxyDomEvent); domUtils.on(me.body, 'drop', function (e) { //Prevent the default pop-up of a new page under ff to open the picture if (browser.gecko & amp; & amp; e.stopPropagation) { e.stopPropagation(); } me.fireEvent('contentchange') }); domUtils.on(doc, ['mouseup', 'keydown'], function (evt) { //Special keys do not trigger selectionchange if (evt.type == 'keydown' & amp; & amp; (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)) { return; } if (evt.button == 2) return; me._selectionChange(250, evt); }); },
2. Copy the method into
//Get the number of pictures in the pasteboard findAllImageElementsWithLocalSource: function (htmlData) { let imgReg = /<img.*?(?:>|\/>)/gi; //Match the img tag in the image let srcReg = /src=['"]?([^'"]*)['"]?/i; // Match src in the image let arr = htmlData.match(imgReg); //Filter out all img if (!arr || (Array.isArray(arr) & amp; & amp; !arr.length)) { return false; } let srcArr = []; for (let i = 0; i < arr.length; i + + ) { let src = arr[i].match(srcReg); // Get the image address srcArr.push(src[1]); } return srcArr; }, //Process image information--find image data from rtf content extractImageDataFromRtf: function (rtfData, ignoreHeadersFooters = true) { if (!rtfData) { return []; } const regexPictureHeader = /{\pict[\s\S] + ?({\\*\blipuid\s?[\da-fA-F] + )[\s}]*/ const regexPicture = new RegExp('(?:(' + regexPictureHeader.source + '))([\da-fA-F\s] + )\}', 'g'); const images = rtfData.match(regexPicture); const result = []; if (images) { for (const image of images) { let imageType = false; if (image.includes('\pngblip')) { imageType = 'image/png'; } else if (image.includes('\jpegblip')) { imageType = 'image/jpeg'; } if (imageType) { //Whether to skip headers and footers if (ignoreHeadersFooters) { const headerFooterRegex = /{\header[\s\S] + ?}\par|{\footer[\s\S] + ?}\par/g; if (headerFooterRegex.test(image)) { continue; } } result.push({ hex: image.replace(regexPictureHeader, '').replace(/[^\da-fA-F]/g, ''), type: imageType }); } } } console.log(result) return result; }, //Convert hexadecimal to base64 _convertHexToBase64: function (hexString) { return btoa(hexString.match(/\w{2}/g).map(char => { return String.fromCharCode(parseInt(char, 16)); }).join('')); }, //Storage image resources replaceImagesFileSourceWithInlineRepresentation: function (imageElements, imagesHexSources) { //If there are equal amounts of image elements and image HEX sources, they can be matched accordingly based on the existing order. if (imageElements.length === imagesHexSources.length) { for (let i = 0; i < imageElements.length; i + + ) { const newSrc = `data:${imagesHexSources[i].type};base64,${this._convertHexToBase64(imagesHexSources[i].hex)}`; wordImg.push(newSrc); } } }, fileToBase64:function(file,_me) { return new Promise((resolve, reject) => { //Create a new FileReader object const reader = new FileReader(); //Read File object reader.readAsDataURL(file); //After loading is complete reader.onload = function () { // Convert the read data to a base64 encoded string const base64String ='data:image/jpeg;base64,' + reader.result.split(",")[1]; // Parse into a Promise object and return a base64 encoded string // resolve(base64String); _me.execCommand('inserthtml',`<img src=${base64String}>`); }; //When loading fails reader.onerror = function () { reject(new Error("Failed to load file")); }; }); },
3. Search for //todo base64 and temporarily remove it
case 'img':
//Todo base64 is temporarily removed. After uploading remote images later, remove this
if (val = node.getAttr('src')) {
}
node.setAttr('_src', node.getAttr('src'));
break;
4. Replace inputRule content
inputRule : function (root) {
utils.each(root.getNodesByTagName(‘img’), function (img, key) {
var attrs = img.attrs,
flag = parseInt(attrs.width) < 128 || parseInt(attrs.height) < 43,
opt = me.options,
src = opt.UEDITOR_HOME_URL + ‘themes/default/images/spacer.gif’;
if (attrs[‘src’] & amp; & amp; /^(?:(file:\/ + ))/.test(attrs[‘src’])) {
img.setAttr({
width:attrs.width,
height:attrs.height,
alt:attrs.alt,
src:wordImg[key],
‘style’:’background:url(‘ + ( flag ? opt.themePath + opt.theme + ‘/images/word.gif’ : opt.langPath + opt.lang + ‘/images/localimage.png’) + ‘ ) no-repeat center center;border:1px solid #ddd’
})
}
})
//Clear usage records
wordImg = [];}
At this point, the entire UE editor can support word image and text pasting. It should be noted that this modification does not upload images to the backend server.