From 6b749b8b5389933baa1687a9e8ba0425d2f9b85c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=90=B4=E5=A4=9A=E7=9B=8A?=
Date: Thu, 13 Apr 2023 21:11:02 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20Word=20=E6=94=AF=E6=8C=81=E5=88=86?=
=?UTF-8?q?=E9=A1=B5=E6=B8=B2=E6=9F=93=20(#6606)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* 优化分页渲染的支持
* 补充一些注释
* 补充单元测试
* 优化单元测试,减少样式冗余
---
docs/zh-CN/components/office-viewer.md | 38 +-
examples/static/page.docx | Bin 0 -> 14166 bytes
.../amis-editor/src/plugin/OfficeViewer.tsx | 59 +-
.../__tests__/docx/simple/break-page.xml | 1367 ++++++++++++
.../__snapshots__/all-shape1.test.ts.snap | 1423 ++++++++++++
.../__snapshots__/all-shape2.test.ts.snap | 1918 +++++++++++++++++
.../__snapshots__/alt-text.test.ts.snap | 26 +
.../simple/__snapshots__/bold.test.ts.snap | 635 +-----
.../simple/__snapshots__/br.test.ts.snap | 139 +-
.../__snapshots__/break-page.test.ts.snap | 120 ++
.../simple/__snapshots__/cr.test.ts.snap | 30 +
.../__snapshots__/drop-cap.test.ts.snap | 135 +-
.../simple/__snapshots__/em.test.ts.snap | 201 +-
.../__snapshots__/embed-font.test.ts.snap | 122 +-
.../__snapshots__/hideMark.test.ts.snap | 328 +--
.../__snapshots__/highlight.test.ts.snap | 261 +--
.../simple/__snapshots__/image.test.ts.snap | 122 +-
.../simple/__snapshots__/info.test.ts.snap | 1075 ++++-----
.../simple/__snapshots__/link.test.ts.snap | 159 +-
.../simple/__snapshots__/list.test.ts.snap | 402 +---
.../__snapshots__/noBreakHyphen.test.ts.snap | 178 +-
.../simple/__snapshots__/pinyin.test.ts.snap | 173 +-
.../simple/__snapshots__/sdt.test.ts.snap | 278 +--
.../simple/__snapshots__/svg.test.ts.snap | 127 +-
.../simple/__snapshots__/sym.test.ts.snap | 125 +-
.../__snapshots__/tableborder.test.ts.snap | 455 ++--
.../__snapshots__/tablestyle.test.ts.snap | 1820 +++++-----------
.../textbox-background.test.ts.snap | 762 +------
.../textbox-behindDoc.test.ts.snap | 207 +-
.../textbox-rotation.test.ts.snap | 171 +-
.../__snapshots__/textbox-vert.test.ts.snap | 539 ++---
.../simple/__snapshots__/textbox.test.ts.snap | 281 +--
.../simple/__snapshots__/tooltip.test.ts.snap | 194 +-
.../simple/__snapshots__/w.test.ts.snap | 112 +-
.../__tests__/simple/all-shape1.test.ts | 5 +
.../__tests__/simple/all-shape2.test.ts | 5 +
.../__tests__/simple/alt-text.test.ts | 5 +
.../__tests__/simple/break-page.test.ts | 5 +
.../ooxml-viewer/__tests__/simple/cr.test.ts | 5 +
.../ooxml-viewer/__tests__/snapShotTest.ts | 3 +-
packages/ooxml-viewer/examples/app.ts | 79 +-
.../ooxml-viewer/examples/static/css/app.css | 64 +-
packages/ooxml-viewer/index.html | 13 +-
packages/ooxml-viewer/src/Word.ts | 88 +-
.../ooxml-viewer/src/openxml/word/Body.ts | 4 +
packages/ooxml-viewer/src/openxml/word/Run.ts | 4 +-
.../ooxml-viewer/src/openxml/word/Section.ts | 21 +-
.../ooxml-viewer/src/render/renderBody.ts | 238 +-
packages/ooxml-viewer/src/render/renderBr.ts | 6 +-
.../ooxml-viewer/src/render/renderDocument.ts | 11 +-
packages/ooxml-viewer/src/render/renderRun.ts | 2 +-
.../ooxml-viewer/src/render/renderSection.ts | 41 +-
.../ooxml-viewer/src/render/renderStyle.ts | 6 -
packages/ooxml-viewer/src/util/dom.ts | 16 +-
54 files changed, 7830 insertions(+), 6773 deletions(-)
create mode 100644 examples/static/page.docx
create mode 100644 packages/ooxml-viewer/__tests__/docx/simple/break-page.xml
create mode 100644 packages/ooxml-viewer/__tests__/simple/__snapshots__/all-shape1.test.ts.snap
create mode 100644 packages/ooxml-viewer/__tests__/simple/__snapshots__/all-shape2.test.ts.snap
create mode 100644 packages/ooxml-viewer/__tests__/simple/__snapshots__/alt-text.test.ts.snap
create mode 100644 packages/ooxml-viewer/__tests__/simple/__snapshots__/break-page.test.ts.snap
create mode 100644 packages/ooxml-viewer/__tests__/simple/__snapshots__/cr.test.ts.snap
create mode 100644 packages/ooxml-viewer/__tests__/simple/all-shape1.test.ts
create mode 100644 packages/ooxml-viewer/__tests__/simple/all-shape2.test.ts
create mode 100644 packages/ooxml-viewer/__tests__/simple/alt-text.test.ts
create mode 100644 packages/ooxml-viewer/__tests__/simple/break-page.test.ts
create mode 100644 packages/ooxml-viewer/__tests__/simple/cr.test.ts
diff --git a/docs/zh-CN/components/office-viewer.md b/docs/zh-CN/components/office-viewer.md
index e11e40b57..2207a6f1d 100644
--- a/docs/zh-CN/components/office-viewer.md
+++ b/docs/zh-CN/components/office-viewer.md
@@ -21,8 +21,9 @@ order: 23
- 文本框
- 形状
- 数学公式(依赖 MathML,需要比较新的浏览器,或者试试 [polyfill](https://github.com/w3c/mathml-polyfills))
+- 分页渲染
-不支持的功能:分页符、艺术字、域、对象、目录
+不支持的功能:艺术字、域、对象、目录
## 基本用法
@@ -63,14 +64,43 @@ order: 23
| forceLineHeight | `string` | | 设置段落行高,忽略文档中的设置 |
| enableVar | `boolean` | true | 是否开启变量替换功能 |
+#### 分页渲染
+
+> 2.10.0 及以上版本
+
+默认情况下 word 文档渲染使用流式布局,这样能更好融入到已有页面中,支持分栏显示,且展现上会和原先的文档有较大差异,如果希望能看起来更像桌面端的效果,可以通过 `page` 配置开启分页渲染
+
+```schema: scope="body"
+{
+ "type": "office-viewer",
+ "id": "office-viewer-page",
+ "wordOptions": {
+ "page": true
+ },
+ "src": "/examples/static/page.docx",
+}
+```
+
+分页渲染的其它设置项
+
+| 属性名 | 类型 | 默认值 | 说明 |
+| ------------------ | --------- | --------- | ------------------------------------------------ |
+| page | `boolean` | false | 是否开启分页渲染 |
+| pageMarginBottom | `number` | 20 | 页面上下间距 |
+| pageBackground | `string` | '#FFF' | 页面内背景色 |
+| pageShadow | `boolean` | true | 是否显示阴影 |
+| pageWrap | `boolean` | true | 是否显示页面包裹,开启这个后才能设置包裹的背景色 |
+| pageWrap | `boolean` | true | 是否显示页面包裹 |
+| pageWrapBackground | `string` | '#ECECEC' | 是否显示页面包裹 |
+| zoom | `number` | | 缩放比例,取值 0-1 之间 |
+| zoomFitWidth | `boolean` | false | 自适应宽度缩放,如果设置了 zoom 将不会生效 |
+
### 关于渲染效果差异
目前的实现难以保证和本地 Word 渲染完全一致,会遇到以下问题:
1. 字体大小不一致
1. 单元格宽度不一致,表格完全依赖浏览器渲染
-1. 分页显示,目前的渲染不会分页,而是内容有多长就有多高
-1. 分栏显示,这个是因为没有分页导致的,不限制高度没法分栏
如果追求完整效果打印,目前只能使用下载文件的方式用本地 Word 进行打印。
@@ -78,7 +108,7 @@ order: 23
默认情况下列表左侧的符号使用字体渲染,这样能做到最接近 Word 渲染效果,但如果用户的系统中没有这些字体就会显示乱码,为了解决这个问题需要手动在 amis 渲染的页面里导入对应的字体,比如
-```html
+```css
-
-
-
+
+
-
+
+
-
-
-
- 立项
-
-
- 依据
-
-
-
+
-
-
-
- 项目背景
-
-
-
-
-
-
-
+ 项目背景
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/br.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/br.test.ts.snap
index 00907bd50..75ad2befe 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/br.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/br.test.ts.snap
@@ -1,123 +1,30 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`br 1`] = `
-
-
-
-
-
+
+
-
-
- 换行
+ 换行
+
+
+
+
+ 工具
-
-
-
- 工具
-
-
-
-
-
-
-
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/break-page.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/break-page.test.ts.snap
new file mode 100644
index 000000000..128c8255a
--- /dev/null
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/break-page.test.ts.snap
@@ -0,0 +1,120 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`break-page 1`] = `
+
+
+
+
+ Word
+
+
+ 分栏的好处主要有:
+
+
+
+
+
+
+
+
+
+
+ 提高文档的阅读性:将版面分成多栏,可以提高文档的可读性,使版面显得更加生动活泼。
+
+
+
+
+
+
+
+ 控制栏数、栏宽及栏间距:可以控制栏数、栏宽及栏间距,使版面更加美观。
+
+
+
+
+
+
+
+ 设置分栏长度:可以很方便地设置分栏长度,使版面更加灵活。
+
+
+
+
+
+
+
+ 总之,
+
+
+ Word
+
+
+ 分栏可以提高文档的可读性和美观度,控制文档的页面布局和大小,方便用户查找和管理文档内容。
+
+
+
+
+
+
+
+
+
+
+
+ 第二页
+
+
+
+
+
+`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/cr.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/cr.test.ts.snap
new file mode 100644
index 000000000..925d5b1af
--- /dev/null
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/cr.test.ts.snap
@@ -0,0 +1,30 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`cr 1`] = `
+
+
+
+
+ 换行
+
+
+
+
+ 工具
+
+
+
+
+
+`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/drop-cap.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/drop-cap.test.ts.snap
index fb0877578..a1bf3e9b9 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/drop-cap.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/drop-cap.test.ts.snap
@@ -1,119 +1,30 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`drop-cap 1`] = `
-
-
-
-
-
+
+
-
-
- 首
-
-
-
+
+
+
-
- 字下沉
-
-
-
-
-
-
+ 字下沉
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/em.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/em.test.ts.snap
index 5a0c327fb..a83736cf3 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/em.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/em.test.ts.snap
@@ -1,151 +1,62 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`em 1`] = `
-
-
-
-
-
+
+
-
-
- 一边
-
-
- 欣
-
-
- 一边
-
-
- 欣
-
-
- 一边
-
-
- 欣
-
-
- 一边
-
-
- 欣
-
-
-
-
-
-
+ 一边
+
+
+ 欣
+
+
+ 一边
+
+
+ 欣
+
+
+ 一边
+
+
+ 欣
+
+
+ 一边
+
+
+ 欣
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/embed-font.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/embed-font.test.ts.snap
index ad92b217d..99b030094 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/embed-font.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/embed-font.test.ts.snap
@@ -1,114 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`embed-font 1`] = `
-
+ Embed font
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/hideMark.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/hideMark.test.ts.snap
index ff9b02f41..4b7db9166 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/hideMark.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/hideMark.test.ts.snap
@@ -1,238 +1,112 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`hideMark 1`] = `
-
-
-
-
-
-
+
+
-
-
-
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
-
-
+ |
+
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+
+
-
- |
-
+ |
+
+
-
- |
-
+ |
+
+
-
- |
-
-
-
-
-
-
- |
-
-
-
-
- |
-
-
-
-
- |
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/highlight.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/highlight.test.ts.snap
index bba4b28d0..140ca159b 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/highlight.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/highlight.test.ts.snap
@@ -1,189 +1,100 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`highlight 1`] = `
-
-
-
-
-
+
+
-
-
- 聚
-
-
-
+
+
+
-
- 聚
-
-
-
+
+
+
-
- 聚
-
-
-
+
+
+
-
- 聚
-
-
-
+
+
+
-
- 聚
-
-
-
+
+
+
-
- 聚
-
-
-
+
+
+
-
- 聚
-
-
-
+
+
+
-
- 聚
-
-
-
+
+
+
-
- 聚
-
-
-
-
-
-
+ 聚
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/image.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/image.test.ts.snap
index 647b74f26..7c037361a 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/image.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/image.test.ts.snap
@@ -1,110 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`image 1`] = `
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/info.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/info.test.ts.snap
index 9b456bafe..083a89e18 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/info.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/info.test.ts.snap
@@ -1,642 +1,523 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`info 1`] = `
-
-
-
-
-
-
+
+
-
-
- 员工人事资料卡
-
-
-
+
+
+
-
- 日期
-
-
-
-
-
- 编号
-
-
-
+
-
-
-
+
+ 编号
+
+
+
+
+
+
+
-
-
- 姓
-
-
-
-
-
- 名
-
-
- |
-
+
+
+
+
+ 名
+
+
+ |
+
+
-
-
- {
-
-
- {name}}
-
-
- |
-
+
+ {name}}
+
+
+ |
+
+
-
+ 性
+
+
+
+
+
+ 别
+
+
+ |
+
+
+
+ 出
+
+
-
- 性
-
-
-
-
- 别
-
-
- |
-
+
+ 生
+
+
+
+
+
+ 日
+
+
+
+
+
+ 期
+
+
+ |
+
+
-
-
- 出
-
-
-
-
-
- 生
-
-
-
-
-
- 日
-
-
-
-
-
- 期
-
-
- |
-
+
+
-
-
- 相
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 片
-
-
- |
-
-
-
+
-
-
-
- |
-
+
-
+
+
+
+
+
+
+
+
+ 片
+
+
+ |
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+ 年
+
+
-
-
-
-
- 年
-
-
-
-
-
- 月
-
-
-
-
-
- 日
-
-
-
-
- |
-
-
-
-
+
-
- 籍
-
-
-
-
-
- 贯
-
-
-
-
-
- |
-
-
+
+
+
+
+ 日
+
+
+
+
+
+ |
+
+
+
+
+
+ 籍
+
+
+
+
+
+ 贯
+
+
-
-
-
-
- 省
-
-
-
-
- 市
-
-
- |
-
-
-
+
+ |
+
+
-
-
- 现住址
-
-
- |
-
+
+ 省
+
+
+
+
+
+ 市
+
+
+ |
+
+
+
+
-
-
-
- |
-
-
-
+
+ |
+
+
-
-
- 身份证号码
-
-
- |
-
+ |
+
+
+
+
-
-
-
- |
-
-
-
+
+ |
+
+
-
-
- 手机号
-
-
- |
-
+ |
+
+
+
+
-
-
- {
-
-
- {phone}}
-
-
- |
-
-
-
+
+ |
+
+
-
-
- 邮箱
-
-
- |
-
+
+ {phone}}
+
+
+ |
+
+
+
+
-
-
- {
-
-
- {email}}
-
-
- |
-
-
-
+
+ |
+
+
-
-
- 日期
-
-
- |
-
+
+ {email}}
+
+
+ |
+
+
+
+
-
-
- {{DATETOSTR(TODAY(), 'YYYY-MM-DD')}}
-
-
- |
-
+
+ |
+
+
-
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
+ {{DATETOSTR(TODAY(), 'YYYY-MM-DD')}}
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/link.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/link.test.ts.snap
index 1d7019cdb..f03f15c4b 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/link.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/link.test.ts.snap
@@ -1,142 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`link 1`] = `
-
+ a
+
+
+ mis
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/list.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/list.test.ts.snap
index 49d5165a5..6ab6f2ac5 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/list.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/list.test.ts.snap
@@ -1,325 +1,99 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`list 1`] = `
-
-
-
-
-
+
+
-
+
+
+
-
-
-
-
- 总体结论
-
-
-
+
+
+
+
+
+
-
-
-
-
- 整体情况
-
-
-
+
+
+
+
+
+
-
-
-
-
- 差异对比
-
-
-
+
+
+
+
+
+
-
-
-
-
- Doc
-
-
-
+
+
+
+
+
+
-
-
-
-
- 总体结论
-
-
-
+
+
+
+
+
+
-
-
-
-
- Doc文档
-
-
-
-
-
-
-
-
-
+ Doc文档
+
+
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/noBreakHyphen.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/noBreakHyphen.test.ts.snap
index 36f8d1f7f..20cceb84b 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/noBreakHyphen.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/noBreakHyphen.test.ts.snap
@@ -1,157 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`noBreakHyphen 1`] = `
-
-
-
-
+
+
-
-
- Number of the form “999
+ Number of the form “999
+
+
+
+ –
-
-
- –
-
-
- 99
-
+
+ 99
-
-
- –
-
-
- 9999”, where
-
+
+
+
+ –
-
-
-
-
-
+
+ 9999”, where
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/pinyin.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/pinyin.test.ts.snap
index 1a67187c9..ebf8995c6 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/pinyin.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/pinyin.test.ts.snap
@@ -1,145 +1,56 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`pinyin 1`] = `
-
-
-
-
-
+
+
-
-
-
+
+
+ 拼
+
+
+
-
-
-
+
+
+
+
+
+
+
+ 音
+
+
+
- 音
+ yīn
-
-
-
- yīn
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/sdt.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/sdt.test.ts.snap
index acb429401..56cb7a2c5 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/sdt.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/sdt.test.ts.snap
@@ -1,204 +1,86 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`sdt 1`] = `
-
-
-
-
+
+
-
-
- start
-
-
-
-
-
-
-
- (sdt)
-
-
-
-
-
-
-
-
- (embed sdt)
-
-
-
-
-
-
-
-
- (Blank 2010, T. Bogich 2014)
-
-
-
- .
-
-
- end
-
-
-
-
-
-
+ start
+
+
+
+
+
+
+
+ (sdt)
+
+
+
+
+
+
+
+
+ (embed sdt)
+
+
+
+
+
+
+
+
+ (Blank 2010, T. Bogich 2014)
+
+
+
+ .
+
+
+ end
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/svg.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/svg.test.ts.snap
index 23d21fb73..f07f25e38 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/svg.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/svg.test.ts.snap
@@ -1,115 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`svg 1`] = `
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/sym.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/sym.test.ts.snap
index 6393979a1..408c2e9de 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/sym.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/sym.test.ts.snap
@@ -1,118 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`sym 1`] = `
-
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/tableborder.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/tableborder.test.ts.snap
index aa226d78f..e585c9d10 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/tableborder.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/tableborder.test.ts.snap
@@ -1,307 +1,188 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`tableborder 1`] = `
-
-
-
-
-
-
+
+
-
-
-
-
+ |
+
+
-
-
- 版本
-
-
- |
-
+
+ |
+
+
-
-
- 注释
-
-
- |
-
+
+ |
+
+
-
-
- 作者
-
-
- |
-
+
+ |
+
+
-
-
- 时间
-
-
- |
-
-
-
+
+ |
+
+
+
+
-
-
-
- |
-
+ |
+
+
-
-
- ERP系统架构设计方案
-
-
- 初稿
-
-
- |
-
+
+ 初稿
+
+
+ |
+
+
-
-
- nwind
-
-
- |
-
+
+ |
+
+
-
-
- 201
-
-
- 9
-
-
- -
-
-
- 05
-
-
- -
-
-
- 28
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ 201
+
+
+ 9
+
+
+ -
+
+
+ 05
+
+
+ -
+
+
+ 28
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/tablestyle.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/tablestyle.test.ts.snap
index 2d48d7e72..3cd209a92 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/tablestyle.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/tablestyle.test.ts.snap
@@ -1,1356 +1,572 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`tablestyle 1`] = `
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
-
-
+ |
+
+
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
-
-
+ |
+
+
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
-
-
+ |
+
+
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
-
-
+ |
+
+
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
-
-
-
-
-
+ |
+
+
+
+
+ |
+
+
-
-
-
- |
-
-
-
+ |
+
+
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
-
-
+ |
+
+
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
-
-
+ |
+
+
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
-
-
+ |
+
+
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
-
-
+ |
+
+
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
+ |
+
+
-
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-background.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-background.test.ts.snap
index 478915803..a8f6db096 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-background.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-background.test.ts.snap
@@ -1,746 +1,32 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`textbox-background 1`] = `
-
+
+
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-behindDoc.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-behindDoc.test.ts.snap
index b81e23e9b..ea6fd7e56 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-behindDoc.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-behindDoc.test.ts.snap
@@ -1,160 +1,71 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`textbox-behindDoc 1`] = `
-
-
-
-
-
+
+
-
-
-
-
+
+
+
-
-
-
+
-
- t
-
-
- ext
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-rotation.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-rotation.test.ts.snap
index a7bd69205..c8c122c69 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-rotation.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-rotation.test.ts.snap
@@ -1,140 +1,51 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`textbox-rotation 1`] = `
-
-
-
-
-
+
+
-
-
-
-
+
+
+
-
-
-
+
-
- Text
-
-
- withou
-
-
- t outline
-
-
-
-
-
-
-
-
-
+ withou
+
+
+ t outline
+
+
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-vert.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-vert.test.ts.snap
index 148e5f966..9cf073c73 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-vert.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox-vert.test.ts.snap
@@ -1,346 +1,253 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`text 1`] = `
-
-
-
-
-
+
+
-
-
-
-
+
+
+
-
-
-
+
-
- 合格证
-
-
- a
-
-
- 270
-
-
-
-
-
+
+ 270
+
+
+
+
+
+
-
-
+
+
+
-
-
-
+
-
- 合格证
-
-
- 2
-
-
- 70
-
-
-
-
-
+
+ 70
+
+
+
+
+
+
-
-
+
+
+
-
-
-
+
-
- 合格证
-
-
- 9
-
-
- 0
-
-
-
-
-
+
+ 0
+
+
+
+
+
+
-
-
+
+
+
-
-
-
-
- 合格证
-
-
-
-
-
+
+
+
+
+
-
-
+
+
+
-
-
-
-
- 合格证
-
-
-
-
-
+
+
+
+
+
-
-
+
+
+
-
-
-
-
- 合格证
-
-
-
-
-
+
+
+
+
+
-
-
+
+
+
-
-
-
-
- 合格证
-
-
-
-
-
-
-
-
-
+ 合格证
+
+
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox.test.ts.snap
index eb9c2245f..b2b2e3ea8 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/textbox.test.ts.snap
@@ -1,202 +1,113 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`textbox 1`] = `
-
-
-
-
-
+
+
-
-
-
-
+
+
+
-
-
-
+
-
- Text box
-
-
- color
-
-
-
-
-
+
+
+
+
+
-
-
+
+
+
-
-
-
-
- Text box
-
-
-
-
-
+
+
+
+
+
-
-
+
+
+
-
-
-
+
-
- Text
-
-
- withou
-
-
- t outline
-
-
-
-
-
-
-
-
-
+ withou
+
+
+ t outline
+
+
+
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/tooltip.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/tooltip.test.ts.snap
index ee4961180..d2382f9ea 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/tooltip.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/tooltip.test.ts.snap
@@ -1,169 +1,45 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`tooltip 1`] = `
-
+ .
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/__snapshots__/w.test.ts.snap b/packages/ooxml-viewer/__tests__/simple/__snapshots__/w.test.ts.snap
index 07ac06218..ac2025f91 100644
--- a/packages/ooxml-viewer/__tests__/simple/__snapshots__/w.test.ts.snap
+++ b/packages/ooxml-viewer/__tests__/simple/__snapshots__/w.test.ts.snap
@@ -1,104 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`w 1`] = `
-
+ 中
+
+
+
+
`;
diff --git a/packages/ooxml-viewer/__tests__/simple/all-shape1.test.ts b/packages/ooxml-viewer/__tests__/simple/all-shape1.test.ts
new file mode 100644
index 000000000..9ea0f723a
--- /dev/null
+++ b/packages/ooxml-viewer/__tests__/simple/all-shape1.test.ts
@@ -0,0 +1,5 @@
+import {snapShotTest} from '../snapShotTest';
+
+test('all-shape-1', async () => {
+ snapShotTest('./docx/simple/all-shape-1.xml');
+});
diff --git a/packages/ooxml-viewer/__tests__/simple/all-shape2.test.ts b/packages/ooxml-viewer/__tests__/simple/all-shape2.test.ts
new file mode 100644
index 000000000..ffd8d2cbb
--- /dev/null
+++ b/packages/ooxml-viewer/__tests__/simple/all-shape2.test.ts
@@ -0,0 +1,5 @@
+import {snapShotTest} from '../snapShotTest';
+
+test('all-shape-2', async () => {
+ snapShotTest('./docx/simple/all-shape-2.xml');
+});
diff --git a/packages/ooxml-viewer/__tests__/simple/alt-text.test.ts b/packages/ooxml-viewer/__tests__/simple/alt-text.test.ts
new file mode 100644
index 000000000..8ab2e4cd7
--- /dev/null
+++ b/packages/ooxml-viewer/__tests__/simple/alt-text.test.ts
@@ -0,0 +1,5 @@
+import {snapShotTest} from '../snapShotTest';
+
+test('alt-text', async () => {
+ snapShotTest('./docx/simple/alt-text.xml');
+});
diff --git a/packages/ooxml-viewer/__tests__/simple/break-page.test.ts b/packages/ooxml-viewer/__tests__/simple/break-page.test.ts
new file mode 100644
index 000000000..ddbfa233d
--- /dev/null
+++ b/packages/ooxml-viewer/__tests__/simple/break-page.test.ts
@@ -0,0 +1,5 @@
+import {snapShotTest} from '../snapShotTest';
+
+test('break-page', async () => {
+ snapShotTest('./docx/simple/break-page.xml');
+});
diff --git a/packages/ooxml-viewer/__tests__/simple/cr.test.ts b/packages/ooxml-viewer/__tests__/simple/cr.test.ts
new file mode 100644
index 000000000..a0e6f160a
--- /dev/null
+++ b/packages/ooxml-viewer/__tests__/simple/cr.test.ts
@@ -0,0 +1,5 @@
+import {snapShotTest} from '../snapShotTest';
+
+test('cr', async () => {
+ snapShotTest('./docx/simple/cr.xml');
+});
diff --git a/packages/ooxml-viewer/__tests__/snapShotTest.ts b/packages/ooxml-viewer/__tests__/snapShotTest.ts
index d94e458a2..cd8dc5a6d 100644
--- a/packages/ooxml-viewer/__tests__/snapShotTest.ts
+++ b/packages/ooxml-viewer/__tests__/snapShotTest.ts
@@ -24,5 +24,6 @@ export async function snapShotTest(filePath: string) {
const word = createWord(filePath, {});
await word.render(root);
- expect(root).toMatchSnapshot();
+ // 样式后续单独测试,不然太多冗余了
+ expect(root.getElementsByTagName('article')[0]).toMatchSnapshot();
}
diff --git a/packages/ooxml-viewer/examples/app.ts b/packages/ooxml-viewer/examples/app.ts
index 4ae6096ea..a870b7dd1 100644
--- a/packages/ooxml-viewer/examples/app.ts
+++ b/packages/ooxml-viewer/examples/app.ts
@@ -30,7 +30,6 @@ const fileLists = {
'shadow.xml',
'shape-ellipse.xml',
'shape-custom.xml',
- 'shape-custom.xml',
'all-shape-1.xml',
'all-shape-2.xml',
'tableborder.xml',
@@ -64,47 +63,67 @@ const fileLists = {
]
};
+// local storage 里的 key
+const pageKey = 'page';
+
+const page = !!localStorage.getItem(pageKey) || false;
+
+if (page) {
+ // 不知道为啥,大概是 vite 的问题
+ setTimeout(() => {
+ const pageSwitch = document.getElementById(
+ 'switchPage'
+ )! as HTMLInputElement;
+ pageSwitch.checked = true;
+ }, 0);
+}
+
+(window as any).switchPage = (checked: boolean) => {
+ if (checked) {
+ localStorage.setItem(pageKey, 'true');
+ } else {
+ localStorage.removeItem(pageKey);
+ }
+
+ location.reload();
+};
+
/**
* 生成左侧文件列表
*/
-(function genFileList() {
- const fileListElement = document.getElementById('fileList')!;
- for (const dirName in fileLists) {
- fileListElement.innerHTML += `${dirName}
`;
- const dir = dirName as keyof typeof fileLists;
- for (const file of fileLists[dir]) {
- const fileName = file.split('.')[0];
- fileListElement.innerHTML += `${fileName}
`;
- }
- }
- document.querySelectorAll('.file').forEach(file => {
- file.addEventListener('click', elm => {
- const fileName = (elm.target as Element).getAttribute('data-path')!;
- history.pushState({fileName}, fileName, `?file=${fileName}`);
- renderDocx(fileName);
- });
+const fileListElement = document.getElementById('fileList')!;
+for (const dirName in fileLists) {
+ fileListElement.innerHTML += `${dirName}
`;
+ const dir = dirName as keyof typeof fileLists;
+ for (const file of fileLists[dir]) {
+ const fileName = file.split('.')[0];
+ fileListElement.innerHTML += `${fileName}
`;
+ }
+}
+
+document.querySelectorAll('.file').forEach(file => {
+ file.addEventListener('click', elm => {
+ const fileName = (elm.target as Element).getAttribute('data-path')!;
+ history.pushState({fileName}, fileName, `?file=${fileName}`);
+ renderDocx(fileName);
});
-})();
+});
const data = {
var: 'amis'
};
-
-function replaceText(text: string) {
- // 将 {{xxx}} 替换成 ${xxx},为啥要这样呢,因为输入 $ 可能会变成两段文本
- text = text.replace(/{{/g, '${').replace(/}}/g, '}');
- return text;
-}
+const renderOptions = {
+ debug: true,
+ page,
+ zoomFitWidth: true
+};
async function renderDocx(fileName: string) {
const filePath = `${testDir}/${fileName}`;
const file = await (await fetch(filePath)).arrayBuffer();
let word: Word;
- const renderOptions = {
- debug: true
- // replaceText
- };
+
if (filePath.endsWith('.xml')) {
word = new Word(file, renderOptions, new XMLPackageParser());
} else {
@@ -158,9 +177,9 @@ function renderWord(file: File) {
const data = reader.result as ArrayBuffer;
let word;
if (file.name.endsWith('.xml')) {
- word = new Word(data, {}, new XMLPackageParser());
+ word = new Word(data, renderOptions, new XMLPackageParser());
} else {
- word = new Word(data, {});
+ word = new Word(data, renderOptions);
}
word.render(viewerElement);
};
diff --git a/packages/ooxml-viewer/examples/static/css/app.css b/packages/ooxml-viewer/examples/static/css/app.css
index 6f07227dd..4cdd26566 100644
--- a/packages/ooxml-viewer/examples/static/css/app.css
+++ b/packages/ooxml-viewer/examples/static/css/app.css
@@ -28,7 +28,7 @@ body {
.file-list {
flex: none;
- width: 140px;
+ width: 100px;
padding-top: 4px;
padding-left: 4px;
white-space: nowrap;
@@ -43,3 +43,65 @@ body {
.file:hover {
color: hsl(217, 71%, 53%);
}
+
+/* The switch - the box around the slider */
+.switch {
+ position: relative;
+ display: inline-block;
+ width: 30px;
+ height: 18px;
+}
+
+/* Hide default HTML checkbox */
+.switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+/* The slider */
+.slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #ccc;
+ -webkit-transition: 0.4s;
+ transition: 0.4s;
+}
+
+.slider:before {
+ position: absolute;
+ content: '';
+ height: 16px;
+ width: 16px;
+ left: 1px;
+ bottom: 1px;
+ background-color: white;
+ -webkit-transition: 0.4s;
+ transition: 0.4s;
+}
+
+input:checked + .slider {
+ background-color: #2196f3;
+}
+
+input:focus + .slider {
+ box-shadow: 0 0 1px #2196f3;
+}
+
+input:checked + .slider:before {
+ -webkit-transform: translateX(12px);
+ transform: translateX(12px);
+}
+
+/* Rounded sliders */
+.slider.round {
+ border-radius: 34px;
+}
+
+.slider.round:before {
+ border-radius: 50%;
+}
diff --git a/packages/ooxml-viewer/index.html b/packages/ooxml-viewer/index.html
index 65dd575e5..623ba1c95 100644
--- a/packages/ooxml-viewer/index.html
+++ b/packages/ooxml-viewer/index.html
@@ -9,8 +9,19 @@
-
+
+
+ page
+
+
diff --git a/packages/ooxml-viewer/src/Word.ts b/packages/ooxml-viewer/src/Word.ts
index e8fcfe76c..ddb1a34a7 100644
--- a/packages/ooxml-viewer/src/Word.ts
+++ b/packages/ooxml-viewer/src/Word.ts
@@ -42,11 +42,6 @@ export interface WordRenderOptions {
*/
bulletUseFont: boolean;
- /**
- * 是否包裹出页面效果
- */
- inWrap: boolean;
-
/**
* 是否忽略文档宽度设置
*/
@@ -104,18 +99,71 @@ export interface WordRenderOptions {
* 打印等待时间,单位毫秒,可能有的文档有很多图片,如果等待时间太短图片还没加载完,所以加这个配置项可控
*/
printWaitTime?: number;
+
+ /**
+ * 是否使用分页的方式来渲染内容,使用这种方式还原度更高,但不支持打印功能
+ * 设置后会自动将 ignoreHeight 和 ignoreWidth 设置为 false
+ */
+ page?: boolean;
+
+ /**
+ * 每页之间的间距
+ */
+ pageMarginBottom?: number;
+
+ /**
+ * 页面背景色
+ */
+ pageBackground?: string;
+
+ /**
+ * 是否显示页面阴影,只有在 page 为 true 的时候才生效
+ */
+ pageShadow?: boolean;
+
+ /**
+ * 显示页面包裹效果,只有在 page 为 true 的时候才生效
+ */
+ pageWrap?: boolean;
+
+ /**
+ * 页面包裹宽度
+ */
+ pageWrapPadding?: number;
+
+ /**
+ * 页面包裹背景色
+ */
+ pageWrapBackground?: string;
+
+ /**
+ * 缩放比例,取值 0-1 之间
+ */
+ zoom?: number;
+
+ /**
+ * 自适应宽度,如果设置了 zoom,那么 zoom 优先级更高,这个设置只在 ignoreWidth 为 false 的时候生效
+ */
+ zoomFitWidth?: boolean;
}
const defaultRenderOptions: WordRenderOptions = {
classPrefix: 'docx-viewer',
- inWrap: true,
+ page: false,
+ pageWrap: true,
bulletUseFont: true,
ignoreHeight: true,
ignoreWidth: false,
minLineHeight: 1.0,
enableVar: false,
debug: false,
+ pageWrapPadding: 20,
+ pageMarginBottom: 20,
+ pageShadow: true,
+ pageBackground: '#FFFFFF',
+ pageWrapBackground: '#ECECEC',
printWaitTime: 100,
+ zoomFitWidth: false,
data: {},
evalVar: t => {
return t;
@@ -227,9 +275,19 @@ export default class Word {
this.id = Word.globalId++;
this.parser = parser;
this.renderOptions = {...defaultRenderOptions, ...renderOptions};
+ if (this.renderOptions.page) {
+ this.renderOptions.ignoreHeight = false;
+ this.renderOptions.ignoreWidth = false;
+ }
}
inited = false;
+
+ /**
+ * 分页标记,如果为 true,那么在渲染的时候会强制分页
+ */
+ breakPage = false;
+
/**
* 初始化一些公共资源,比如
*/
@@ -578,7 +636,8 @@ export default class Word {
document.body.appendChild(iframe);
iframe.contentDocument?.write('
');
await this.render(
- iframe.contentDocument?.getElementById('print') as HTMLElement
+ iframe.contentDocument?.getElementById('print') as HTMLElement,
+ {page: false, pageWrap: false}
);
setTimeout(function () {
iframe.focus();
@@ -592,10 +651,14 @@ export default class Word {
* 渲染文档入口
*
* @param root 渲染的根节点
+ * @param renderOptionsOverride 临时覆盖某些渲选项
*/
- async render(root: HTMLElement) {
+ async render(
+ root: HTMLElement,
+ renderOptionsOverride: Partial
= {}
+ ) {
this.init();
- const renderOptions = this.renderOptions;
+ const renderOptions = {...this.renderOptions, ...renderOptionsOverride};
const isDebug = renderOptions.debug;
isDebug && console.log('init', this);
@@ -614,10 +677,13 @@ export default class Word {
const document = WDocument.fromXML(this, documentData);
isDebug && console.log('document', document);
- const documentElement = renderDocument(this, document);
+
+ const documentElement = renderDocument(root, this, document, renderOptions);
root.classList.add(this.getClassPrefix());
- if (renderOptions.inWrap) {
+ if (renderOptions.page && renderOptions.pageWrap) {
root.classList.add(this.wrapClassName);
+ root.style.padding = `${renderOptions.pageWrapPadding || 0}px`;
+ root.style.background = renderOptions.pageWrapBackground || '#ECECEC';
}
appendChild(root, renderStyle(this));
diff --git a/packages/ooxml-viewer/src/openxml/word/Body.ts b/packages/ooxml-viewer/src/openxml/word/Body.ts
index 3122739cc..e270f2a47 100644
--- a/packages/ooxml-viewer/src/openxml/word/Body.ts
+++ b/packages/ooxml-viewer/src/openxml/word/Body.ts
@@ -62,6 +62,10 @@ export class Body {
}
}
+ // 过滤掉没内容的 section,一般是最后一个
+ body.sections = body.sections.filter(
+ section => section.children.length > 0
+ );
return body;
}
}
diff --git a/packages/ooxml-viewer/src/openxml/word/Run.ts b/packages/ooxml-viewer/src/openxml/word/Run.ts
index baf2a8fd0..a4b264bf0 100644
--- a/packages/ooxml-viewer/src/openxml/word/Run.ts
+++ b/packages/ooxml-viewer/src/openxml/word/Run.ts
@@ -104,7 +104,9 @@ export class Run {
break;
case 'w:lastRenderedPageBreak':
- // 目前也不支持分页显示
+ const pageBreak = new Break();
+ pageBreak.type = 'page';
+ run.addChild(pageBreak);
break;
case 'w:pict':
diff --git a/packages/ooxml-viewer/src/openxml/word/Section.ts b/packages/ooxml-viewer/src/openxml/word/Section.ts
index febd9255a..fbc509ad2 100644
--- a/packages/ooxml-viewer/src/openxml/word/Section.ts
+++ b/packages/ooxml-viewer/src/openxml/word/Section.ts
@@ -11,6 +11,7 @@ import {Hyperlink} from './Hyperlink';
import {Paragraph} from './Paragraph';
import {Table} from './Table';
import {Body} from './Body';
+import {getAttrNumber} from '../../OpenXML';
export type PageSize = {
width: string;
@@ -28,9 +29,12 @@ export type PageMargin = {
gutter?: string;
};
+/**
+ * 列设置,其实这里支持
+ */
export interface Column {
- width?: number;
- space?: number;
+ num?: number;
+ space?: string;
}
export type SectionChild = Paragraph | Table | Hyperlink;
@@ -38,6 +42,7 @@ export type SectionChild = Paragraph | Table | Hyperlink;
export interface SectionPr {
pageSize?: PageSize;
pageMargin?: PageMargin;
+ cols?: Column;
}
export class Section {
@@ -78,7 +83,6 @@ export class Section {
const headerType = child.getAttribute('w:type');
const headerId = child.getAttribute('r:id');
// 目前只支持 default 且只支持背景图
- // TODO: 这里 rel 不对,需要用 "/word/_rels/header1.xml.rels,后面得想想怎么改
if (headerType === 'default' && headerId) {
const headerRel = word.getDocumentRels(headerId);
if (headerRel) {
@@ -92,6 +96,17 @@ export class Section {
}
break;
+ case 'w:cols':
+ const cols: Column = {};
+ const num = getAttrNumber(child, 'w:num', 1);
+ cols.num = num;
+ const space = parseSize(child, 'w:space');
+ if (space) {
+ cols.space = space;
+ }
+ properties.cols = cols;
+ break;
+
default:
break;
}
diff --git a/packages/ooxml-viewer/src/render/renderBody.ts b/packages/ooxml-viewer/src/render/renderBody.ts
index b8296b0dd..bc670ed06 100644
--- a/packages/ooxml-viewer/src/render/renderBody.ts
+++ b/packages/ooxml-viewer/src/render/renderBody.ts
@@ -2,34 +2,246 @@
* 渲染 body 节点
*/
-import {createElement, appendChild} from '../util/dom';
-import Word from '../Word';
+import {createElement, appendChild, removeChild} from '../util/dom';
+import Word, {WordRenderOptions} from '../Word';
import {Body} from '../openxml/word/Body';
import {Paragraph} from '../openxml/word/Paragraph';
import {Table} from '../openxml/word/Table';
-import {Hyperlink} from '../openxml/word/Hyperlink';
import renderParagraph from './renderParagraph';
import {renderSection} from './renderSection';
import renderTable from './renderTable';
+import {Section} from '../openxml/word/Section';
-export default function renderBody(
+/**
+ * 判断是否需要创建一个新 section,包括强制分页和超出了 section 的高宽或宽度
+ */
+function createNewSection(
word: Word,
- parent: HTMLElement,
- body: Body
+ sectionEnd: SectionEnd,
+ child: HTMLElement
) {
- for (const section of body.sections) {
- const sectionEl = renderSection(word, section);
- appendChild(parent, sectionEl);
+ // 支持插入分页符
+ if (word.breakPage) {
+ word.breakPage = false;
+ return true;
+ }
+ const childBound = child.getBoundingClientRect();
+ return (
+ childBound.top + childBound.height > sectionEnd.bottom ||
+ // 注意这里没有 + childBound.width,因为 width 一般都是 100% 导致容易超出
+ childBound.left > sectionEnd.right
+ );
+}
+
+/**
+ * 添加到 section 里,如果超出了就创建一个新的 section
+ */
+function appendToSection(
+ word: Word,
+ renderOptions: WordRenderOptions,
+ bodyEl: HTMLElement,
+ sectionEl: HTMLElement,
+ sectionEnd: SectionEnd,
+ section: Section,
+ child: HTMLElement
+) {
+ // 首先尝试写入
+ appendChild(sectionEl, child);
+
+ // 如果超出了就新建一个 section
+ if (createNewSection(word, sectionEnd, child)) {
+ const newChild = child.cloneNode(true) as HTMLElement;
+ removeChild(sectionEl, child);
+ let newSectionEl = renderSection(word, section, renderOptions);
+ appendChild(bodyEl, newSectionEl);
+ appendChild(newSectionEl, newChild);
+ sectionEnd = getSectionEnd(section, newSectionEl);
+ return {sectionEl: newSectionEl, sectionEnd};
+ }
+
+ return {sectionEl, sectionEnd};
+}
+
+type SectionEnd = {
+ bottom: number;
+ right: number;
+};
+
+/**
+ * 获取 section 结束的位置,也就是最后能放下子元素的位置
+ */
+function getSectionEnd(section: Section, sectionEl: HTMLElement): SectionEnd {
+ const sectionBound = sectionEl.getBoundingClientRect();
+ const pageMargin = section.properties.pageMargin;
+ let bottom = sectionBound.top + sectionBound.height;
+ if (pageMargin?.bottom) {
+ bottom = bottom - parseInt(pageMargin.bottom.replace('px', ''), 10);
+ }
+ let right = sectionBound.left + sectionBound.width;
+ if (pageMargin?.right) {
+ right = right - parseInt(pageMargin.right.replace('px', ''), 10);
+ }
+ return {bottom, right};
+}
+
+/**
+ * 获取缩放比例
+ */
+function getTransform(
+ rootWidth: number,
+ section: Section,
+ renderOptions: WordRenderOptions
+) {
+ const props = section.properties;
+ const pageSize = props.pageSize;
+ if (renderOptions.zoomFitWidth && !renderOptions.ignoreWidth) {
+ const pageWidth = pageSize?.width;
+ if (rootWidth && pageWidth) {
+ let pageWidthNum = parseInt(pageWidth.replace('px', ''), 10);
+
+ if (props.pageMargin) {
+ const pageMargin = props.pageMargin;
+ pageWidthNum += pageMargin.left
+ ? parseInt(pageMargin.left.replace('px', ''), 10)
+ : 0;
+ pageWidthNum += pageMargin.right
+ ? parseInt(pageMargin.right.replace('px', ''), 10)
+ : 0;
+ }
+ const zoomWidth = rootWidth / pageWidthNum;
+
+ return zoomWidth;
+ }
+ }
+ return 1;
+}
+
+/**
+ * 分页渲染
+ * @param isLastSection 是否是最后一节
+ */
+function renderSectionInPage(
+ word: Word,
+ bodyEl: HTMLElement,
+ renderOptions: WordRenderOptions,
+ sectionEl: HTMLElement,
+ section: Section,
+ isLastSection: boolean
+) {
+ // 如果不 setTimeout 取到的位置信息不对
+ setTimeout(() => {
+ let sectionEnd = getSectionEnd(section, sectionEl);
for (const child of section.children) {
if (child instanceof Paragraph) {
const p = renderParagraph(word, child);
- appendChild(sectionEl, p);
+ const appendResult = appendToSection(
+ word,
+ renderOptions,
+
+ bodyEl,
+ sectionEl,
+ sectionEnd,
+ section,
+ p
+ );
+ sectionEl = appendResult.sectionEl;
+ sectionEnd = appendResult.sectionEnd;
} else if (child instanceof Table) {
- appendChild(sectionEl, renderTable(word, child));
+ const table = renderTable(word, child);
+ const appendResult = appendToSection(
+ word,
+ renderOptions,
+
+ bodyEl,
+ sectionEl,
+ sectionEnd,
+ section,
+ table
+ );
+ sectionEl = appendResult.sectionEl;
+ sectionEnd = appendResult.sectionEnd;
} else {
console.warn('unknown child', child);
}
}
- appendChild(parent, sectionEl);
- }
+
+ if (isLastSection) {
+ sectionEl.style.marginBottom = '0';
+ }
+ }, 0);
+}
+
+/**
+ * 渲染文档主体
+ */
+export default function renderBody(
+ root: HTMLElement,
+ word: Word,
+ bodyEl: HTMLElement,
+ body: Body,
+ renderOptions: WordRenderOptions
+) {
+ const page = renderOptions.page || false;
+
+ const rootWidth =
+ root.getBoundingClientRect().width -
+ (renderOptions.pageWrapPadding || 0) * 2;
+
+ const zooms: number[] = [];
+
+ let index = 0;
+ const sections = body.sections;
+ const sectionLength = sections.length;
+ // 用于最后一个 section 不加 margin-bottom
+ let isLastSection = false;
+ for (const section of sections) {
+ zooms.push(getTransform(rootWidth, section, renderOptions));
+ let sectionEl = renderSection(word, section, renderOptions);
+ appendChild(bodyEl, sectionEl);
+
+ index = index + 1;
+ if (index === sectionLength) {
+ isLastSection = true;
+ }
+ if (page) {
+ renderSectionInPage(
+ word,
+ bodyEl,
+
+ renderOptions,
+ sectionEl,
+ section,
+ isLastSection
+ );
+ } else {
+ for (const child of section.children) {
+ if (child instanceof Paragraph) {
+ const p = renderParagraph(word, child);
+ appendChild(sectionEl, p);
+ } else if (child instanceof Table) {
+ const table = renderTable(word, child);
+ appendChild(sectionEl, table);
+ } else {
+ console.warn('unknown child', child);
+ }
+ }
+ }
+ }
+
+ setTimeout(() => {
+ if (renderOptions.zoom) {
+ // 固定缩放
+ bodyEl.style.transformOrigin = '0 0';
+ bodyEl.style.transform = `scale(${renderOptions.zoom})`;
+ } else if (
+ renderOptions.page &&
+ renderOptions.zoomFitWidth &&
+ !renderOptions.ignoreWidth
+ ) {
+ // 自适应宽度的缩放
+ const minZoom = Math.min(...zooms);
+ bodyEl.style.transformOrigin = '0 0';
+ bodyEl.style.transform = `scale(${minZoom})`;
+ }
+ }, 0);
}
diff --git a/packages/ooxml-viewer/src/render/renderBr.ts b/packages/ooxml-viewer/src/render/renderBr.ts
index 4dfe802ca..07a3a8958 100644
--- a/packages/ooxml-viewer/src/render/renderBr.ts
+++ b/packages/ooxml-viewer/src/render/renderBr.ts
@@ -1,3 +1,4 @@
+import Word from '../Word';
import {Break} from '../openxml/word/Break';
import {createElement} from '../util/dom';
@@ -6,7 +7,10 @@ import {createElement} from '../util/dom';
* 其实还有 column 和 page,但目前还没实现分页渲染,所以目前先简单处理,后续再看看如何处理
* @returns 生成的 dom 结构
*/
-export function renderBr(brak: Break) {
+export function renderBr(word: Word, brak: Break) {
+ if (brak.type === 'page') {
+ word.breakPage = true;
+ }
const br = createElement('br');
return br;
}
diff --git a/packages/ooxml-viewer/src/render/renderDocument.ts b/packages/ooxml-viewer/src/render/renderDocument.ts
index 7bdfafd1a..93b9ecf39 100644
--- a/packages/ooxml-viewer/src/render/renderDocument.ts
+++ b/packages/ooxml-viewer/src/render/renderDocument.ts
@@ -1,5 +1,5 @@
import {createElement} from '../util/dom';
-import Word from '../Word';
+import Word, {WordRenderOptions} from '../Word';
import renderBody from './renderBody';
import {WDocument} from '../openxml/word/WDocument';
@@ -8,8 +8,13 @@ import {WDocument} from '../openxml/word/WDocument';
* 渲染 document 主要入口
* http://webapp.docx4java.org/OnlineDemo/ecma376/WordML/document.html
*/
-export default function renderDocument(word: Word, document: WDocument) {
+export default function renderDocument(
+ root: HTMLElement,
+ word: Word,
+ document: WDocument,
+ renderOptions: WordRenderOptions
+) {
const doc = createElement('article');
- renderBody(word, doc, document.body);
+ renderBody(root, word, doc, document.body, renderOptions);
return doc;
}
diff --git a/packages/ooxml-viewer/src/render/renderRun.ts b/packages/ooxml-viewer/src/render/renderRun.ts
index cc9e2ab25..f26ca5d2a 100644
--- a/packages/ooxml-viewer/src/render/renderRun.ts
+++ b/packages/ooxml-viewer/src/render/renderRun.ts
@@ -102,7 +102,7 @@ export default function renderRun(
renderText(newSpan, word, child.text, paragraph);
appendChild(span, newSpan);
} else if (child instanceof Break) {
- const br = renderBr(child);
+ const br = renderBr(word, child);
appendChild(span, br);
} else if (child instanceof Drawing) {
appendChild(span, renderDrawing(word, child));
diff --git a/packages/ooxml-viewer/src/render/renderSection.ts b/packages/ooxml-viewer/src/render/renderSection.ts
index 1e9ad7054..84d145e34 100644
--- a/packages/ooxml-viewer/src/render/renderSection.ts
+++ b/packages/ooxml-viewer/src/render/renderSection.ts
@@ -1,29 +1,49 @@
import {Section} from '../openxml/word/Section';
import {createElement} from '../util/dom';
-import Word from '../Word';
+import Word, {WordRenderOptions} from '../Word';
/**
* 渲染「节」,在 word 中每个节都可以有自己的页边距和页面大小设置,但目前其实并没有实现分页展现,等后续再考虑
*/
-export function renderSection(word: Word, section: Section) {
+export function renderSection(
+ word: Word,
+ section: Section,
+ renderOptions: WordRenderOptions
+) {
const sectionEl = createElement('section') as HTMLElement;
// 用于后续绝对定位
sectionEl.style.position = 'relative';
+
+ if (renderOptions.page) {
+ sectionEl.style.overflow = 'hidden';
+ if (renderOptions.pageMarginBottom) {
+ sectionEl.style.marginBottom = renderOptions.pageMarginBottom + 'px';
+ }
+
+ if (renderOptions.pageShadow) {
+ sectionEl.style.boxShadow = '0 0 8px rgba(0, 0, 0, 0.5)';
+ }
+
+ if (renderOptions.pageBackground) {
+ sectionEl.style.background = renderOptions.pageBackground;
+ }
+ }
+
const props = section.properties;
const pageSize = props.pageSize;
if (pageSize) {
- if (!word.renderOptions.ignoreWidth) {
+ if (!renderOptions.ignoreWidth) {
sectionEl.style.width = pageSize.width;
}
- if (!word.renderOptions.ignoreHeight) {
+ if (!renderOptions.ignoreHeight) {
sectionEl.style.height = pageSize.height;
}
}
// 强制控制 padding
- if (word.renderOptions.padding) {
- sectionEl.style.padding = word.renderOptions.padding;
+ if (renderOptions.padding) {
+ sectionEl.style.padding = renderOptions.padding;
} else {
const pageMargin = props.pageMargin;
if (pageMargin) {
@@ -34,5 +54,14 @@ export function renderSection(word: Word, section: Section) {
}
}
+ if (props.cols) {
+ if (props.cols.num && props.cols.num > 1) {
+ sectionEl.style.columnCount = '' + props.cols.num;
+ if (props.cols.space) {
+ sectionEl.style.columnGap = props.cols.space;
+ }
+ }
+ }
+
return sectionEl;
}
diff --git a/packages/ooxml-viewer/src/render/renderStyle.ts b/packages/ooxml-viewer/src/render/renderStyle.ts
index ae55746d0..11b97339d 100644
--- a/packages/ooxml-viewer/src/render/renderStyle.ts
+++ b/packages/ooxml-viewer/src/render/renderStyle.ts
@@ -27,13 +27,7 @@ function generateDefaultStyle(word: Word) {
const classPrefix = word.getClassPrefix();
return `
- .${word.wrapClassName} {
- }
-
- .${word.wrapClassName} > article > section {
- background: white;
- }
/** docDefaults **/
diff --git a/packages/ooxml-viewer/src/util/dom.ts b/packages/ooxml-viewer/src/util/dom.ts
index 69f9b1bd4..891a36201 100644
--- a/packages/ooxml-viewer/src/util/dom.ts
+++ b/packages/ooxml-viewer/src/util/dom.ts
@@ -47,13 +47,6 @@ export function createSVGElement(tagName: string): SVGElement {
return document.createElementNS('http://www.w3.org/2000/svg', tagName);
}
-/**
- * 创建片段
- */
-export function createDocumentFragment() {
- return document.createDocumentFragment();
-}
-
/**
* 添加子节点,会做一些判断避免报错
*/
@@ -63,6 +56,15 @@ export function appendChild(parent: HTMLElement, child?: Node | null): void {
}
}
+/**
+ * 删除子节点,会做一些判断避免报错
+ */
+export function removeChild(parent: HTMLElement, child?: Node | null): void {
+ if (parent && child) {
+ parent.removeChild(child);
+ }
+}
+
/**
* 添加注释节点
*/