瀏覽代碼

Use simple space normalization approach.

Krzysztof Krztoń 7 年之前
父節點
當前提交
f37690f46d

+ 23 - 60
packages/ckeditor5-paste-from-office/src/filters/utils.js

@@ -9,17 +9,11 @@
 
 /* globals DOMParser */
 
-import ViewText from '@ckeditor/ckeditor5-engine/src/view/text';
-import ViewElement from '@ckeditor/ckeditor5-engine/src/view/element';
-import ViewDocumentFragment from '@ckeditor/ckeditor5-engine/src/view/documentfragment';
 import DomConverter from '@ckeditor/ckeditor5-engine/src/view/domconverter';
-import UpcastWriter from '@ckeditor/ckeditor5-engine/src/view/upcastwriter';
 import { NBSP_FILLER } from '@ckeditor/ckeditor5-engine/src/view/filler';
-import isText from '@ckeditor/ckeditor5-utils/src/dom/istext';
 
 const domParser = new DOMParser();
 const domConverter = new DomConverter( { blockFiller: NBSP_FILLER } );
-const upcastWriter = new UpcastWriter();
 
 /**
  * Parses provided HTML extracting contents of `body` and `style` tags.
@@ -35,7 +29,9 @@ const upcastWriter = new UpcastWriter();
  */
 export function parseHtml( htmlString ) {
 	// Parse htmlString as native Document object.
-	const htmlDocument = domParser.parseFromString( htmlString, 'text/html' );
+	const htmlDocument = domParser.parseFromString( normalizeEndTagsPrecedingSpace( htmlString ), 'text/html' );
+
+	normalizeSpacerunSpans( htmlDocument );
 
 	// Get `innerHTML` first as transforming to View modifies the source document.
 	const bodyString = htmlDocument.body.innerHTML;
@@ -66,7 +62,7 @@ function documentToView( htmlDocument ) {
 		fragment.appendChild( nodes[ 0 ] );
 	}
 
-	return domToView( fragment );
+	return domConverter.domToView( fragment );
 }
 
 // Extracts both `CSSStyleSheet` and string representation from all `style` elements available in a provided `htmlDocument`.
@@ -93,60 +89,27 @@ function extractStyles( htmlDocument ) {
 	};
 }
 
-// Converts given DOM structure to a View structure.
+// Replaces last space preceding elements closing tag with ` `. Such operation prevents spaces from being removed
+// during further DOM/View processing (see especially {@link module:engine/view/domconverter~DomConverter#_processDataFromDomText}).
+// This method also takes into account Word specific `<o:p></o:p>` empty tags.
 //
-// @param {Node|DocumentFragment} domNode DOM node or document fragment to transform.
-// @returns {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment|null} Converted node or document fragment
-// or `null` if DOM node is an empty text node or not a `DocumentFragment` or `Element` node type.
-function domToView( domNode ) {
-	if ( isText( domNode ) ) {
-		const textData = normalizeTextNodes( domNode.data );
-
-		return textData === '' ? null : new ViewText( textData );
-	} else if ( domConverter.isDocumentFragment( domNode ) || domConverter.isElement( domNode ) ) {
-		let viewElement;
-
-		if ( domConverter.isDocumentFragment( domNode ) ) {
-			// Create view document fragment.
-			viewElement = new ViewDocumentFragment();
-		} else {
-			// Create view element.
-			const viewName = domNode.tagName.toLowerCase();
-			viewElement = new ViewElement( viewName );
-
-			// Copy element's attributes.
-			const attrs = domNode.attributes;
-
-			for ( let i = attrs.length - 1; i >= 0; i-- ) {
-				upcastWriter.setAttribute( attrs[ i ].name, attrs[ i ].value, viewElement );
-			}
-		}
-
-		const children = [];
-
-		for ( let i = 0; i < domNode.childNodes.length; i++ ) {
-			const viewChild = domToView( domNode.childNodes[ i ] );
-
-			if ( viewChild ) {
-				children.push( viewChild );
-			}
-		}
-		upcastWriter.appendChild( children, viewElement );
-
-		return viewElement;
-	}
-
-	return null;
+// @param {String} htmlString HTML string in which spacing should be normalized.
+// @returns {String} Input HTML with spaces normalized.
+function normalizeEndTagsPrecedingSpace( htmlString ) {
+	return htmlString
+		.replace( / <\//g, '\u00A0</' )
+		.replace( / <o:p><\/o:p>/g, '\u00A0<o:p></o:p>' );
 }
 
-// Normalizes given text nodes by replacing new line characters and non-breaking spaces with regular spaces.
+// Normalizes spacing in special Word `spacerun spans` (`<span style='mso-spacerun:yes'>\s+</span>`) by replacing
+// all spaces with `&nbsp; ` pairs. This prevents spaces from being removed during further DOM/View processing
+// (see especially {@link module:engine/view/domconverter~DomConverter#_processDataFromDomText}).
 //
-// @param {String} text Text to be normalized.
-// @returns {String} Normalized text.
-function normalizeTextNodes( text ) {
-	const normalizedText = text
-		.replace( /[\n\t\r]{1,}/g, ' ' )
-		.replace( /\u00A0/g, ' ' );
-
-	return normalizedText === ' ' ? '' : normalizedText;
+// @param {Document} htmlDocument Native `Document` object in which spacing should be normalized.
+function normalizeSpacerunSpans( htmlDocument ) {
+	htmlDocument.querySelectorAll( 'span[style*=spacerun]' ).forEach( el => {
+		const spacesReplacemanet = Array( el.innerText.length + 1 ).join( '\u00A0 ' ).substr( 0, el.innerText.length );
+
+		el.innerHTML = spacesReplacemanet;
+	} );
 }

+ 5 - 1
packages/ckeditor5-paste-from-office/tests/_utils/utils.js

@@ -85,5 +85,9 @@ export function expectNormalized( actual, expected ) {
 		// Replace line breaks (after closing tags) too.
 		.replace( /[\r\n]/gm, '' );
 
-	expect( stringify( actual ) ).to.equal( normalizeHtml( expectedInlined ) );
+	// We are ok with both spaces and non-breaking spaces in the actual content.
+	// Replace `&nbsp;` with regular spaces to align with expected content (where regular spaces are only used).
+	const actualNormalized = stringify( actual ).replace( /\u00A0/g, ' ' );
+
+	expect( actualNormalized ).to.equal( normalizeHtml( expectedInlined ) );
 }