Add paste from word base

This commit is contained in:
Sauli Anto 2019-09-29 04:14:02 +03:00
parent eebd28631e
commit 7013be6825
4 changed files with 276 additions and 1 deletions

10
package-lock.json generated
View File

@ -90,6 +90,16 @@
"@ckeditor/ckeditor5-undo": "^11.0.5"
}
},
"@ckeditor/ckeditor5-paste-from-office": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-paste-from-office/-/ckeditor5-paste-from-office-11.1.0.tgz",
"integrity": "sha512-LcXmJSlr0fzi+hccdyTnHuKh/K9nWV5OiuWqcOpX3IucczSYzGgykG4MUZVlBLv1mkP0AsjJ1oSPGXCkVkgJkg==",
"requires": {
"@ckeditor/ckeditor5-clipboard": "^12.0.2",
"@ckeditor/ckeditor5-core": "^12.3.0",
"@ckeditor/ckeditor5-engine": "^14.0.0"
}
},
"@ckeditor/ckeditor5-typing": {
"version": "12.2.0",
"resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-typing/-/ckeditor5-typing-12.2.0.tgz",

View File

@ -13,6 +13,7 @@
"@ckeditor/ckeditor5-clipboard": "^12.0.2",
"@ckeditor/ckeditor5-core": "^12.3.0",
"@ckeditor/ckeditor5-engine": "^14.0.0",
"@ckeditor/ckeditor5-paste-from-office": "^11.1.0",
"@ckeditor/ckeditor5-ui": "^14.0.0",
"@ckeditor/ckeditor5-undo": "^11.0.5",
"@ckeditor/ckeditor5-utils": "^14.0.0",

View File

@ -4,10 +4,11 @@ import Widget from '@ckeditor/ckeditor5-widget/src/widget';
import MathUI from './mathui';
import MathEditing from './mathediting';
import AutoMath from './automath';
import MathPasteFromOffice from './mathpastefromoffice';
export default class Math extends Plugin {
static get requires() {
return [ MathEditing, MathUI, Widget, AutoMath ];
return [ MathEditing, MathUI, Widget, AutoMath, MathPasteFromOffice ];
}
static get pluginName() {

263
src/mathpastefromoffice.js Normal file
View File

@ -0,0 +1,263 @@
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
import UpcastWriter from '@ckeditor/ckeditor5-engine/src/view/upcastwriter';
import Matcher from '@ckeditor/ckeditor5-engine/src/view/matcher';
// import Element from '@ckeditor/ckeditor5-engine/src/view/element';
import { normalizeSpacing, normalizeSpacerunSpans } from '@ckeditor/ckeditor5-paste-from-office/src/filters/space';
const BULLET_REGEXP = /(<span style=['"]mso-list:((.||\r?\n)*?)Ignore['"]>((.|\r?\n)*?)<\/span>)/g;
export default class MathPasteFromOffice extends Plugin {
static get pluginName() {
return 'MathPasteFromOffice';
}
static get requires() {
return [ Clipboard ];
}
init() {
const editor = this.editor;
editor.plugins.get( 'Clipboard' ).on(
'inputTransformation',
( evt, data ) => {
console.log( 'isTransformedWithPasteFromOffice', data.isTransformedWithPasteFromOffice ); // eslint-disable-line
console.log( data.content ); // eslint-disable-line
if ( data.isTransformedWithMathPasteFromOffice ) {
return;
}
if ( !data.isTransformedWithPasteFromOffice ) {
console.warn( 'Paste from office must be set before this' );// eslint-disable-line
return;
}
const domParser = new DOMParser(); // eslint-disable-line
/*
console.log(htmlString); // eslint-disable-line
const { body } = parseHtml( htmlString );
console.log(body); // eslint-disable-line
*/
const plainString = data.dataTransfer.getData( 'text/plain' );
const lines = plainString.split( /\r?\n/ ); // split by new lines
if ( lines.length > 1 ) {
lines.pop(); // Remove last new line
}
// console.log(plainString); // eslint-disable-line
const htmlMimeType = 'text/html';
let htmlString = data.dataTransfer.getData( htmlMimeType );
// console.log(htmlString); // eslint-disable-line
htmlString = htmlString.replace( /<!--\[if gte vml 1]>/g, '' );
htmlString = htmlString.replace( BULLET_REGEXP, '' );
const normalizedHtml = normalizeSpacing( cleanContentAfterBody( htmlString ) );
const htmlDocument = domParser.parseFromString( normalizedHtml, htmlMimeType );
normalizeSpacerunSpans( htmlDocument );
// const htmlDocument = domParser.parseFromString( htmlString, htmlMimeType );
// console.log(htmlDocument); // eslint-disable-line
const parts = htmlDocument.body.children;
// console.log(parts); // eslint-disable-line
const partsByLines = [];
for ( let i = 0; i < parts.length; i++ ) {
const part = parts[ i ];
if ( part.tagName.toLowerCase() === 'table' ) {
// console.log(part); // eslint-disable-line
const table = part;
const rows = table.getElementsByTagName( 'tr' );
for ( let j = 0; j < rows.length; j++ ) {
const row = rows[ j ];
partsByLines.push( row );
}
// console.log(rows); // eslint-disable-line
} else {
partsByLines.push( part );
}
}
// console.log(lines); // eslint-disable-line
// console.log(parts); // eslint-disable-line
// console.log(lines.length, partsByLines.length); // eslint-disable-line
if ( lines.length === partsByLines.length ) {
for ( let i = 0; i < partsByLines.length; i++ ) {
const part = partsByLines[ i ];
const line = lines[ i ];
const nodes = part.childNodes;
// console.log( nodes ); // eslint-disable-line
let lineFromNode = '';
let equationCounter = 0;
for ( let j = 0; j < nodes.length; j++ ) {
const node = nodes[ j ];
// If node is comment
const commentType = Node.COMMENT_NODE; // eslint-disable-line
if ( node.nodeType === commentType ) {
const commentNode = node;
// console.log( comment ); // eslint-disable-line
const comment = commentNode.textContent;
// console.log(comment); // eslint-disable-line
if ( comment.match( /\[if gte msEquation 12]>/g ) ) {
equationCounter++;
}
}
if ( node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE ) { // eslint-disable-line
// console.log( node.nodeType, node.textContent ); // eslint-disable-line
lineFromNode += node.textContent;
}
}
// Skip if don't have any equation
if ( equationCounter === 0 ) {
continue;
}
const equations = collectEquations( line, lineFromNode, equationCounter );
// console.log( part, equations ); // eslint-disable-line
const images = part.getElementsByTagName( 'img' );
// console.log( images ); // eslint-disable-line
for ( let j = 0; j < images.length; j++ ) {
const img = images[ j ];
// console.log( img.src ); // eslint-disable-line
const mathtex = document.createElement( 'span' ); // eslint-disable-line
mathtex.classList.add( 'math-tex' );
mathtex.innerHTML = '\\[' + equations[ j ] + '\\]';
const parent = img.parentNode;
parent.replaceChild( mathtex, img );
// console.log( parent ); // eslint-disable-line
}
}
const writer = new UpcastWriter();
const documentFragment = data.content;
const itemLikeElements = findAllItemLikeElements( documentFragment, writer );
console.log( itemLikeElements ); // eslint-disable-line
if ( !itemLikeElements.length ) {
return;
}
itemLikeElements.forEach( ( itemLikeElement, i ) => {
console.log( i, itemLikeElement, itemLikeElement.element ); // eslint-disable-line
// const item = new Element( 'span' );
} );
} else {
console.warn( 'Lines length is wrong', lines.length + ' !== ' + partsByLines.length ); // eslint-disable-line
}
data.isTransformedWithMathPasteFromOffice = true;
},
{ priority: 'high' }
);
}
}
function collectEquations( line, lineFromNode, equationCounter ) {
const equations = [];
let equation = '';
let charCounter = 0;
let newEquation = true;
// Replace all spaces to non breaking spaces. (Cannot be in equation)
let lineFiltered = line;
lineFiltered = lineFiltered.replace( /&nbsp;/g, ' ' );
lineFiltered = lineFiltered.trim();
// Remove '\ ' chars
lineFiltered = lineFiltered.replace( /\\ /g, '' ); // Remove all extra spaces.
// Try remove all spaces in equation
lineFiltered = lineFiltered.replace( /{(?=.*?)(?=\\+).*?}/g, match => {
return match.replace( /\s+/g, '' );
} ); // Remove all extra spaces.
lineFiltered = lineFiltered.replace( /\s/g, '\u00A0' ); // Replcae all spaces to non breaking spaces
let lineFromNodeFiltered = lineFromNode;
lineFromNodeFiltered = lineFromNodeFiltered.replace( /\r?\n|\r/g, ' ' ); // Line break is same as space
lineFromNodeFiltered = lineFromNodeFiltered.replace( /&nbsp;/g, ' ' );
lineFromNodeFiltered = lineFromNodeFiltered.trim();
lineFromNodeFiltered = lineFromNodeFiltered.replace( /\s/g, '\u00A0' ); // Replcae all spaces to non breaking spaces
// eslint-disable-next-line
// console.log( '%c' + lineFiltered + ' %c' + lineFromNodeFiltered + ' %c', 'color:green', 'color:red','color:white' );
// Let check line char by char
for ( let j = 0; j < lineFiltered.length; j++ ) {
const charFromLine = lineFiltered.charAt( j );
// console.log( charFromLine, lineFiltered.charCodeAt( j ) ); // eslint-disable-line
const charFromNode = lineFromNodeFiltered.charAt( j - charCounter );
// Jump to next iteration if same char
// console.log( charFromLine, charFromNode ); // eslint-disable-line
if ( charFromLine === charFromNode ) {
if ( !newEquation ) {
equations.push( equation.trim() );
equation = '';
newEquation = true;
}
continue;
} else {
equation += charFromLine;
charCounter++;
if ( newEquation ) {
newEquation = false;
}
}
}
// Add last one
if ( !newEquation ) {
equations.push( equation.trim() );
equation = '';
newEquation = true;
}
if ( equations.length !== equationCounter ) {
console.warn( 'Couldn\'t parse all equations' ); // eslint-disable-line
}
return equations;
}
function cleanContentAfterBody( htmlString ) {
const regexp = /<\/body>(.*?)(<\/html>|$)/;
const match = htmlString.match( regexp );
if ( match && match[ 1 ] ) {
htmlString = htmlString.slice( 0, match.index ) + htmlString.slice( match.index ).replace( match[ 1 ], '' );
}
return htmlString;
}
function findAllItemLikeElements( documentFragment, writer ) {
const range = writer.createRangeIn( documentFragment );
// Matcher for finding list-like elements.
const itemLikeElementsMatcher = new Matcher( {
name: /^img$/
} );
const itemLikeElements = [];
for ( const value of range ) {
if ( value.type === 'elementStart' && itemLikeElementsMatcher.match( value.item ) ) {
itemLikeElements.push( value.item );
}
}
return itemLikeElements;
}