wangeditor5 粘贴 WPS Word 是否可行?

2,685 阅读4分钟

1、前言

之前我有写过一篇《富文本编辑器粘贴 WPS Word 解决方案》,最近 wangeditor5 发布了公测版本,于是作者就尝试给 v5 版本实现这一功能。 更多详情请查看 《国产开源富文本编辑器 wangEditor 新版 公开测试

2、从 RTF 中提取图片

在上一篇文章《富文本编辑器粘贴 WPS Word 解决方案》中,我提到了将 RTF 中的 二进制/十六进制 图片提取为 base64。而这一次不仅提供了 base64 数据,同时还提供 File 数据,以供用户可以将图片上传到服务器。

下面是返回给用户的图片实例:

// picture.d.ts
export default class Picture {
    /** 该图片类型内置支持解析为 base64 和 File */
    parsed: boolean;
    
    /** 图片的文件类型 */
    mime: string;
    
    /** 图片的 ArrayBuffer 对象 */
    buffer: ArrayBuffer;
    
    /** 图片的 二进制 数据字符串 */
    binary: string;
    
    /** 图片配置关键字和图片数据 */
    keywords: string[];
    
    /**
     * @param keywords 图片配置关键字和图片数据构成的集合
     */
    constructor(keywords: string[]);
    
    /**
     * 获取图片的 base64 字符串
     */
    get base64(): string | null;
    
    /**
     * 获取图片的 File 文件对象
     */
    get file(): File | null;
}

至于解析 RTF 的过程我这里就不赘述了,那就是一个观察规律并根据规律写一段文本处理逻辑代码而已。更多详情请查看 RTF V1.7 规范 中文版

3、转换 HTML 为 wangeditor5 节点数据结构

文中后续中我将使用 “module” 代指 “节点数据结构

3.1、wangeditor5 module

我们先通过官方文档初步了解一下什么是节点数据

为了便于理解,我将需要的一些节点数据结构进行了提取整合并重命名,如下:

interface EmptyTextModule {
  text: ''
}

/**
 * #text mark
 */
export interface TextMark {
  color?: string
  bgColor?: string
  fontSize?: string
  fontFamily?: string
  bold?: true
  italic?: true
  through?: true
  underline?: true
  code?: true
  sup?: true
  sub?: true
}

/**
 * #text module
 */
export interface TextModule extends TextMark {
  text: string
}

/**
 * anchor module
 */
export interface AnchorModule {
  type: 'link'
  url: string
  target?: string
  children: TextModule[]
}

/**
 * image module.style
 */
interface ImageStyle {
  width?: string
  height?: string
}

/**
 * img module
 */
export interface ImageModule {
  type: 'image'
  src: string
  children: [EmptyTextModule]
  alt?: string
  href?: string
  style?: ImageStyle
}

/**
 * h1~6、p 的公共样式
 */
export interface ParagraphMark {
  indent?: string
  textAlign?: string
  lineHeight?: string
}

/**
 * h6、p module
 */
export interface ParagraphModule extends ParagraphMark {
  type: 'paragraph'
  children: (TextModule | AnchorModule | ImageModule)[]
}

/**
 * h1~h5 module.type
 */
export type Header = 'header1' | 'header2' | 'header3' | 'header4' | 'header5'

/**
 * h1~h5 module
 */
export interface HeaderModule extends ParagraphMark {
  type: Header
  children: (TextModule | AnchorModule)[]
}

/**
 * th、td module
 */
export interface TableCellModule {
  type: 'table-cell'
  colSpan?: number
  rowSpan?: number
  children: (TextModule | AnchorModule | ImageModule)[]
}

/**
 * tr module
 */
export interface TableRowModule {
  type: 'table-row'
  children: TableCellModule[]
}

/**
 * table module
 */
export interface TableModule {
  type: 'table'
  withHeader?: boolean // 是否显示表头
  fullWidth?: boolean // 是否宽度 100%
  children: TableRowModule[]
}

/**
 * ul module
 */
export interface ULModule {
  type: 'bulleted-list'
  children: LIModule[]
}

/**
 * ol module
 */
export interface OLModule {
  type: 'numbered-list'
  children: LIModule[]
}

/**
 * li module
 */
export interface LIModule {
  type: 'list-item'
  children: (TextModule | AnchorModule)[]
}

在 v5 中,标题只支持 <h1~5>,所以我将 <h6><p> 归类在了一起。而且在这些数据结构中,引用、分割线、代码块儿和视频在 WPS Word 中是没有的,所以可以忽略掉。

3.2、HTML 转 module

在 《富文本编辑器粘贴 WPS Word 解决方案》 一文的示例中我们使用的是 DOMParser 来辅助 HTML 的解析,这里依旧如此。

其实将 HTML 转换为 v5 module 的过程就是将整个 DOM 树拍扁的过程。至于怎么拍扁呢?其实很简单,就是将特殊的标签转换为特定 module。比如将 <p> 标签转换为 ParagraphModule,将 <b> 标签转换为 TextModule 中的 bold 属性。

具体的思路如下:
第一步:依赖收集,遍历整个 DOM tree,将符合条件的树梢节点的 DOM tree 路径进行缓存收集
第二步:将收集的路径集合转换为 v5 module

3.2.1、DEMO

我们先来看一段 HTML 代码:

<div>
    <b>
        <u>A</u>
        <del>B</del>
    </b>
    <a href="http://baidu.com">
        C
        <b>D</b>
        E
    </a>
</div>

上面的 DOM 树经依赖收集我们将得到:

[
    [<div>, <b>, <u>, A],
    [<div>, <b>, <del>, B],
    [<div>, <a>, C],
    [<div>, <a>, <b>, D],
    [<div>, <a>, E],
]

然后我们遍历这个二维数组,将 DOM 路径转换为 v5 module 我们将得到:

[
    {
        type: 'paragraph',
        children: [
            { text: 'A', bold: true, italic: true },
            { text: 'B', bold: true, through: true },
        ],
    },
    {
        type: 'paragraph',
        children: [{
            type: 'link',
            children: [
                { text: 'C' }, 
                { text: 'D', bold: true },
                { text: 'E' },
            ]
        }],
    },
]

3.2.2、效果预览

20211029_121627.gif

4、最后

虽然初步完成了 DOM treemodule 的转换,但是还有一些特殊的情况是无法处理的,可能需要与使用者手动配合才能达到完美的效果。

比如 WPS Word 中的文本框浮雕文字,它们在 HTML 中是会以图片的形式存在,而在 RTF 中却不是,这必将导致从 RTF 中提取的图片数量比 HTML 中的图片数量少。这个时候我们就需要使用者对图片进行手动定位了,然后抛弃与文本框浮雕文字所对应的图片。

加入我们

wangEditor 官网找到我们的 QQ 群,进群私聊群主。