解决CKEditor5无法从WPS复制内容的问题

背景介绍

CKEditor5是个比较流行的富文本编辑器,本身已经支持从 Word、Excel 和 Google Docs 粘贴内容,但是测试发现,从WPS粘贴内容时,文字部分全部变成了空格。查了官方的资料以及GitHub上别人的提问得知官方本身是不兼容从WPS复制内容的,但是WPS在国内又比较流行,该问题还是要解决一下。

网上搜了其他相关帖子,有人写的还是比较好的,但是实操性不太强,在此梳理下自己的解决过程,有需要的同学可以参考一下。

解决过程

步骤1:访问官方提供的工具,来自定义你需要的ckeditor的配置,如果要支持从word复制内容,需要选中该组件,如下图所示:

最后配置完,会生成一个压缩包,解压该压缩包,在sample目录下有个index.html文件,打开该文件,可以看到你配置的组件的样子。

index.html文件位置

index.html打开的样子

可以在这个index页面尝试从word和wps复制内容看看效果。

步骤2:下载CKEditor5的源码,GitHub地址:https://github.com/ckeditor/ckeditor5,选择和步骤1相同的tag分支,在packages目录中找到ckeditor5-paste-from-office,该包即时Paste from Office组件的源码了,我们需要修改相关源码。

步骤3:修改源码,在ckeditor5-paste-from-office/src/filters目录找到space.js,做如下修改

这是原码:

export function normalizeSpacing( htmlString ) {
    // Run normalizeSafariSpaceSpans() two times to cover nested spans.
    return normalizeSafariSpaceSpans( normalizeSafariSpaceSpans( htmlString ) )
        // Remove all \r\n from "spacerun spans" so the last replace line doesn't strip all whitespaces.
        .replace( /(<span\s+style=['"]mso-spacerun:yes['"]>[^\S\r\n]*?)[\r\n]+([^\S\r\n]*<\/span>)/g, '$1$2' )
        .replace( /<span\s+style=['"]mso-spacerun:yes['"]><\/span>/g, '' )
        .replace( / <\//g, '\u00A0</' )
        .replace( / <o:p><\/o:p>/g, '\u00A0<o:p></o:p>' )
        // Remove <o:p> block filler from empty paragraph. Safari uses \u00A0 instead of &nbsp;.
        .replace( /<o:p>(&nbsp;|\u00A0)<\/o:p>/g, '' )
        // Remove all whitespaces when they contain any \r or \n.
        .replace( />([^\S\r\n]*[\r\n]\s*)</g, '><' );
}

这是修改后的代码:

export function normalizeSpacing( htmlString ) {
    // Run normalizeSafariSpaceSpans() two times to cover nested spans.
    return normalizeSafariSpaceSpans( normalizeSafariSpaceSpans( htmlString ) )
        // Remove all \r\n from "spacerun spans" so the last replace line doesn't strip all whitespaces.
        .replace( /(<span\s+style=['"]mso-spacerun:yes['"]>[^\S\r\n]*?)[\r\n]+([^\S\r\n]*<\/span>)/g, '$1$2' )
        .replace( /<span\s+style=['"]mso-spacerun:yes['"]><\/span>/g, '' )
        .replace( / <\//g, '\u00A0</' )
        .replace( / <o:p><\/o:p>/g, '\u00A0<o:p></o:p>' )
        // Remove <o:p> block filler from empty paragraph. Safari uses \u00A0 instead of &nbsp;.
        .replace( /<o:p>(&nbsp;|\u00A0)<\/o:p>/g, '' )
        // Remove all whitespaces when they contain any \r or \n.
        .replace( />([^\S\r\n]*[\r\n]\s*)</g, '><' )
        // 针对WPS的修改,去除空格
        .replace( />(\s+)</g, '><' );
}

这是原码:

export function normalizeSpacerunSpans( htmlDocument ) {
    htmlDocument.querySelectorAll( 'span[style*=spacerun]' ).forEach( el => {
        const innerTextLength = el.innerText.length || 0;

        el.innerText = Array( innerTextLength + 1 ).join( '\u00A0 ' ).substr( 0, innerTextLength );
    } );
}

这是修改后的代码:

export function normalizeSpacerunSpans( htmlDocument ) {
    htmlDocument.querySelectorAll( 'span[style*=spacerun]' ).forEach( el => {
        // 针对 wps 添加的判断
        if ( el.childNodes[ 0 ] && el.childNodes[ 0 ].data ) {
            const innerTextLength = el.innerText.length || 0;
            el.innerText = Array( innerTextLength + 1 ).join( '\u00A0 ' ).substr( 0, innerTextLength );
        }
    } );
}

在ckeditor5-paste-from-office/src/filters目录找到image.js,做如下修改

这是原码:

function extractImageDataFromRtf( rtfData ) {
    if ( !rtfData ) {
        return [];
    }

    const regexPictureHeader = /{\\pict[\s\S]+?\\bliptag-?\d+(\\blipupi-?\d+)?({\\\*\\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 ) {
                result.push( {
                    hex: image.replace( regexPictureHeader, '' ).replace( /[^\da-fA-F]/g, '' ),
                    type: imageType
                } );
            }
        }
    }

    return result;
}

这是修改的代码:

function extractImageDataFromRtf( rtfData ) {
    if ( !rtfData ) {
        return [];
    }

    let regexPictureHeader = /{\\pict[\s\S]+?\\bliptag-?\d+(\\blipupi-?\d+)?({\\\*\\blipuid\s?[\da-fA-F]+)?[\s}]*?/;
    let regexPicture = new RegExp( '(?:(' + regexPictureHeader.source + '))([\\da-fA-F\\s]+)\\}', 'g' );
    let images = rtfData.match( regexPicture );
    const result = [];

    // 针对 wps 添加的判断
    if ( !images ) {
        regexPictureHeader = /{\\pict[\s\S]+?(\\pngblip-?\d+)?(\\wmetafile8-?\d+)?{\\\*\\blipuid\s?[\da-fA-F]+[\s}]*?/;
        regexPicture = new RegExp( '(?:(' + regexPictureHeader.source + '))([\\da-fA-F\\s]+)\\}', 'g' );
        images = rtfData.match( regexPicture );
    }

    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 ) {
                result.push( {
                    hex: image.replace( regexPictureHeader, '' ).replace( /[^\da-fA-F]/g, '' ),
                    type: imageType
                } );
            }
        }
    }

    return result;
}

步骤4:在步骤1下载的工程中创建自定义目录,将ckeditor5-paste-from-office复制到该目录,如下是我的目录结构:

修改ckeditor5-35.4.0/src/ckeditor.js文件

# 将
import PasteFromOffice from '@ckeditor/ckeditor5-paste-from-office/src/pastefromoffice.js';
# 改为(看自己定义的是什么目录名,替换即可)
import PasteFromOffice from '../custom/ckeditor5-paste-from-office/src/pastefromoffice.js';

执行npm install安装依赖

执行npm run build打包,会在build目录重新生成结果文件。

执行结束后,重新打开sample目录下的index.html,再试试从wps复制内容吧

步骤5:将ckeditor5-35.4.0引入到工程,我用的是vue2,在工程根目录执行如下命令即可:

yarn add file:.\ckeditor5-35.4.0\

效果

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值