wangeditor5 粘贴 WPS Word 是否可行?

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 群,进群私聊群主。

分类:
前端