Skip to content

Commit

Permalink
Merge pull request #13162 from ckeditor/ck/11993-paste-images-from-word
Browse files Browse the repository at this point in the history
Fix (paste-from-office): Fixes pasting images from MS Word 2016. Closes #11993.
  • Loading branch information
niegowski authored Jan 3, 2023
2 parents 0b249e6 + 407e982 commit e727b69
Show file tree
Hide file tree
Showing 14 changed files with 8,357 additions and 6 deletions.
67 changes: 66 additions & 1 deletion packages/ckeditor5-paste-from-office/src/filters/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
Matcher,
UpcastWriter,
type ViewDocumentFragment,
type ViewElement
type ViewElement,
type ViewNode
} from 'ckeditor5/src/engine';

/**
Expand All @@ -32,6 +33,7 @@ export function replaceImagesSourceWithBase64( documentFragment: ViewDocumentFra
const shapesIds = findAllShapesIds( documentFragment, upcastWriter );

removeAllImgElementsRepresentingShapes( shapesIds, documentFragment, upcastWriter );
insertMissingImgs( shapesIds, documentFragment, upcastWriter );
removeAllShapeElements( documentFragment, upcastWriter );

const images = findAllImageElementsWithLocalSource( documentFragment, upcastWriter );
Expand Down Expand Up @@ -151,6 +153,69 @@ function removeAllShapeElements( documentFragment: ViewDocumentFragment, writer:
}
}

/**
* Inserts `img` tags if there is none after a shape.
*/
function insertMissingImgs( shapeIds: Array<string>, documentFragment: ViewDocumentFragment, writer: UpcastWriter ) {
const range = writer.createRangeIn( documentFragment );

const shapes: Array<ViewElement> = [];

for ( const value of range ) {
if ( value.type == 'elementStart' && value.item.is( 'element', 'v:shape' ) ) {
const id = value.item.getAttribute( 'id' )!;

if ( shapeIds.includes( id ) ) {
continue;
}

if ( !containsMatchingImg( value.item.parent!.getChildren(), id ) ) {
shapes.push( value.item );
}
}
}

for ( const shape of shapes ) {
const attrs: Record<string, unknown> = {
src: findSrc( shape )
};

if ( shape.hasAttribute( 'alt' ) ) {
attrs.alt = shape.getAttribute( 'alt' );
}

const img = writer.createElement( 'img', attrs );

writer.insertChild( shape.index! + 1, img, shape.parent! );
}

function containsMatchingImg( nodes: Iterable<ViewNode>, id: string ): boolean {
for ( const node of nodes ) {
/* istanbul ignore else */
if ( node.is( 'element' ) ) {
if ( node.name == 'img' && node.getAttribute( 'v:shapes' ) == id ) {
return true;
}

if ( containsMatchingImg( node.getChildren(), id ) ) {
return true;
}
}
}

return false;
}

function findSrc( shape: ViewElement ) {
for ( const child of shape.getChildren() ) {
/* istanbul ignore else */
if ( child.is( 'element' ) && child.getAttribute( 'src' ) ) {
return child.getAttribute( 'src' );
}
}
}
}

/**
* Finds all `<img>` elements in a given document fragment which have source pointing to local `file://` resource.
*
Expand Down
34 changes: 29 additions & 5 deletions packages/ckeditor5-paste-from-office/tests/_data/image/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ import reflectionRtfChrome from './reflection/input.chrome.word2016.rtf';
import adjacentGroupsRtfChrome from './adjacent-groups/input.chrome.word2016.rtf';
import onlineOfflineRtfChrome from './online-offline/input.chrome.word2016.rtf';
import shapesOnlineOfflineRtfChrome from './shapes-online-offline/input.chrome.word2016.rtf';
import noImgTagRtfChrome from './no-img-tag/input.chrome.word2016.rtf';
import noImgTagRtfAltTextChrome from './no-img-tag-alt-text/input.chrome.word2016.rtf';

import noImgTagChrome from './no-img-tag/input.chrome.word2016.html';
import noImgTagAltTextChrome from './no-img-tag-alt-text/input.chrome.word2016.html';

import noImgTagNormalizedChrome from './no-img-tag/normalized.chrome.word2016.html';
import noImgTagNormalizedAltTextChrome from './no-img-tag-alt-text/normalized.chrome.word2016.html';

import noImgTagModelChrome from './no-img-tag/model.chrome.word2016.html';
import noImgTagModelAltTextChrome from './no-img-tag-alt-text/model.chrome.word2016.html';

// Firefox
import offlineRtfFirefox from './offline/input.firefox.word2016.rtf';
Expand Down Expand Up @@ -143,9 +154,21 @@ import shapesOnlineOfflineModelSafari from './shapes-online-offline/model.safari

export const browserFixtures = {
chrome: {
input: Object.assign( {}, genericFixtures.input ),
normalized: Object.assign( {}, genericFixtures.normalized ),
model: Object.assign( {}, genericFixtures.model ),
input: {
...genericFixtures.input,
noImgTag: noImgTagChrome,
noImgTagAltText: noImgTagAltTextChrome
},
normalized: {
...genericFixtures.normalized,
noImgTag: noImgTagNormalizedChrome,
noImgTagAltText: noImgTagNormalizedAltTextChrome
},
model: {
...genericFixtures.model,
noImgTag: noImgTagModelChrome,
noImgTagAltText: noImgTagModelAltTextChrome
},
inputRtf: {
offline: offlineRtfChrome,
linked: linkedRtfChrome,
Expand All @@ -155,9 +178,10 @@ export const browserFixtures = {
reflection: reflectionRtfChrome,
adjacentGroups: adjacentGroupsRtfChrome,
onlineOffline: onlineOfflineRtfChrome,
shapesOnlineOffline: shapesOnlineOfflineRtfChrome
shapesOnlineOffline: shapesOnlineOfflineRtfChrome,
noImgTag: noImgTagRtfChrome,
noImgTagAltText: noImgTagRtfAltTextChrome
}

},

firefox: {
Expand Down
Loading

0 comments on commit e727b69

Please sign in to comment.