Skip to content

Commit

Permalink
Merge pull request #4291 from ckeditor/t/2800
Browse files Browse the repository at this point in the history
Improve importing images from Microsoft Word
  • Loading branch information
f1ames authored Oct 16, 2020
2 parents c977f3f + 597c28f commit 4f8ca46
Show file tree
Hide file tree
Showing 156 changed files with 1,022,907 additions and 256 deletions.
14 changes: 14 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@ CKEditor 4 Changelog

## CKEditor 4.16

New Features:

* [#2800](https://github.com/ckeditor/ckeditor4/issues/2800): Unsupported image formats are now gracefully handled by the [Paste from Word](https://ckeditor.com/cke4/addon/pastefromword) plugin on paste, additionally showing descriptive error messages.
* [#2800](https://github.com/ckeditor/ckeditor4/issues/2800): Unsupported image formats are now gracefully handled by the [Paste from LibreOffice](https://ckeditor.com/cke4/addon/pastefromlibreoffice) plugin on paste, additionally showing descriptive error messages.

Fixed Issues:

* [#2800](https://github.com/ckeditor/ckeditor4/issues/2800): Fixed: No images are imported from Microsoft Word when content pasted by the [Paste from Word](https://ckeditor.com/cke4/addon/pastefromword) plugin if there is at least one image in unsupported format.

API Changes:

* [#3782](https://github.com/ckeditor/ckeditor4/issues/3782): Moved [`CKEDITOR.plugins.pastetool.filters.word.images`](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_plugins_pastetools_filters_word_images.html) filters to [`CKEDITOR.plugins.pastetools.filters.image`](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_plugins_pastetools_filters_image.html) namespace.
* [#4297](https://github.com/ckeditor/ckeditor4/issues/4297): All [`CKEDITOR.plugins.pastetools.filters`](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_plugins_pastetools_filters.html) are now available under [`CKEDITOR.pasteTools`](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR.html#property-pasteTools) alias.

## CKEditor 4.15

New features:
Expand Down
109 changes: 33 additions & 76 deletions plugins/pastefromword/filter/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -1520,82 +1520,6 @@
};
List = plug.lists;

/**
* Namespace containing a set of image helper methods.
*
* @private
* @since 4.13.0
* @member CKEDITOR.plugins.pastetools.filters.word
*/
plug.images = {
/**
* Parses RTF content to find embedded images. Please be aware that this method should only return `png` and `jpeg` images.
*
* @private
* @since 4.13.0
* @param {String} rtfContent RTF content to be checked for images.
* @returns {Object[]} An array of images found in the `rtfContent`.
* @returns {String} return.hex Hexadecimal string of an image embedded in `rtfContent`.
* @returns {String} return.type A string representing the image type. Allowed values: 'image/png', 'image/jpeg'.
* @member CKEDITOR.plugins.pastetools.filters.word.images
*/
extractFromRtf: function( rtfContent ) {
var ret = [],
rePictureHeader = /\{\\pict[\s\S]+?\\bliptag\-?\d+(\\blipupi\-?\d+)?(\{\\\*\\blipuid\s?[\da-fA-F]+)?[\s\}]*?/,
rePicture = new RegExp( '(?:(' + rePictureHeader.source + '))([\\da-fA-F\\s]+)\\}', 'g' ),
wholeImages,
imageType;

wholeImages = rtfContent.match( rePicture );
if ( !wholeImages ) {
return ret;
}

for ( var i = 0; i < wholeImages.length; i++ ) {
if ( rePictureHeader.test( wholeImages[ i ] ) ) {
if ( wholeImages[ i ].indexOf( '\\pngblip' ) !== -1 ) {
imageType = 'image/png';
} else if ( wholeImages[ i ].indexOf( '\\jpegblip' ) !== -1 ) {
imageType = 'image/jpeg';
} else {
continue;
}

ret.push( {
hex: imageType ? wholeImages[ i ].replace( rePictureHeader, '' ).replace( /[^\da-fA-F]/g, '' ) : null,
type: imageType
} );
}
}

return ret;
},

/**
* Extracts an array of `src`` attributes in `<img>` tags from the given HTML. `<img>` tags belonging to VML shapes are removed.
*
* CKEDITOR.plugins.pastefromword.images.extractTagsFromHtml( html );
* // Returns: [ 'http://example-picture.com/random.png', 'http://example-picture.com/another.png' ]
*
* @private
* @since 4.13.0
* @param {String} html A string representing HTML code.
* @returns {String[]} An array of strings representing the `src` attribute of the `<img>` tags found in `html`.
* @member CKEDITOR.plugins.pastetools.filters.word.images
*/
extractTagsFromHtml: function( html ) {
var regexp = /<img[^>]+src="([^"]+)[^>]+/g,
ret = [],
item;

while ( item = regexp.exec( html ) ) {
ret.push( item[ 1 ] );
}

return ret;
}
};

/**
* Namespace containing methods used to process the pasted content using heuristics.
*
Expand Down Expand Up @@ -1901,10 +1825,41 @@
* @property {Object} images
* @private
* @deprecated 4.13.0
* @removed 4.16.0
* @since 4.8.0
* @member CKEDITOR.plugins.pastefromword
*/

/**
* See {@link CKEDITOR.plugins.pastetools.filters.image}.
*
* @property {Object} images
* @private
* @removed 4.16.0
* @since 4.13.0
* @member CKEDITOR.plugins.pastetools.filters.word
*/

/**
* See {@link CKEDITOR.plugins.pastetools.filters.image#extractFromRtf}.
*
* @property {Function} extractFromRtf
* @private
* @removed 4.16.0
* @since 4.13.0
* @member CKEDITOR.plugins.pastetools.filters.word.images
*/

/**
* See {@link CKEDITOR.plugins.pastetools.filters.image#extractTagsFromHtml}.
*
* @property {Function} extractTagsFromHtml
* @private
* @removed 4.16.0
* @since 4.13.0
* @member CKEDITOR.plugins.pastetools.filters.word.images
*/

/**
* See {@link CKEDITOR.plugins.pastetools.filters.word.heuristics}.
*
Expand All @@ -1925,6 +1880,8 @@
* @member CKEDITOR.plugins.pastefromword
*/



/**
* See {@link #pasteTools_removeFontStyles}.
*
Expand Down
59 changes: 10 additions & 49 deletions plugins/pastefromword/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
configInlineImages = editor.config.pasteFromWord_inlineImages === undefined ? true : editor.config.pasteFromWord_inlineImages,
defaultFilters = [
CKEDITOR.getUrl( pastetoolsPath + 'filter/common.js' ),
CKEDITOR.getUrl( pastetoolsPath + 'filter/image.js' ),
CKEDITOR.getUrl( path + 'filter/default.js' )
];

Expand Down Expand Up @@ -102,6 +103,15 @@
if ( forceFromWord || confirmCleanUp() ) {
pfwEvtData.dataValue = CKEDITOR.cleanWord( pfwEvtData.dataValue, editor );

// Paste From Word Image:
// RTF clipboard is required for embedding images.
// If img tags are not allowed there is no point to process images.
// Also skip embedding images if image filter is not loaded.
if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported && configInlineImages &&
CKEDITOR.pasteFilters.image ) {
pfwEvtData.dataValue = CKEDITOR.pasteFilters.image( pfwEvtData.dataValue, editor, dataTransferRtf );
}

editor.fire( 'afterPasteFromWord', pfwEvtData );

data.dataValue = pfwEvtData.dataValue;
Expand All @@ -127,56 +137,7 @@
}
}
} );

// Paste From Word Image:
// RTF clipboard is required for embedding images.
// If img tags are not allowed there is no point to process images.
if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported && configInlineImages ) {
editor.on( 'afterPasteFromWord', imagePastingListener );
}

function imagePastingListener( evt ) {
var pfw = CKEDITOR.plugins.pastefromword && CKEDITOR.plugins.pastefromword.images,
imgTags,
hexImages,
newSrcValues = [],
i;

// If pfw images namespace is unavailable or img tags are not allowed we simply skip adding images.
if ( !pfw || !evt.editor.filter.check( 'img[src]' ) ) {
return;
}

function createSrcWithBase64( img ) {
return img.type ? 'data:' + img.type + ';base64,' + CKEDITOR.tools.convertBytesToBase64( CKEDITOR.tools.convertHexStringToBytes( img.hex ) ) : null;
}

imgTags = pfw.extractTagsFromHtml( evt.data.dataValue );
if ( imgTags.length === 0 ) {
return;
}

hexImages = pfw.extractFromRtf( evt.data.dataTransfer[ 'text/rtf' ] );
if ( hexImages.length === 0 ) {
return;
}

CKEDITOR.tools.array.forEach( hexImages, function( img ) {
newSrcValues.push( createSrcWithBase64( img ) );
}, this );

// Assuming there is equal amount of Images in RTF and HTML source, so we can match them accordingly to the existing order.
if ( imgTags.length === newSrcValues.length ) {
for ( i = 0; i < imgTags.length; i++ ) {
// Replace only `file` urls of images ( shapes get newSrcValue with null ).
if ( ( imgTags[ i ].indexOf( 'file://' ) === 0 ) && newSrcValues[ i ] ) {
evt.data.dataValue = evt.data.dataValue.replace( imgTags[ i ], newSrcValues[ i ] );
}
}
}
}
}

} );
} )();

Expand Down
Loading

0 comments on commit 4f8ca46

Please sign in to comment.