editor-svg 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/.editorconfig +9 -0
  2. package/.eslintrc +46 -0
  3. package/.prettierrc +8 -0
  4. package/AGENTS.md +186 -0
  5. package/CHANGELOG.md +2543 -0
  6. package/CLAUDE.md +110 -0
  7. package/LICENSE +21 -0
  8. package/README.md +110 -0
  9. package/cypress/e2e/control/checkbox.cy.ts +46 -0
  10. package/cypress/e2e/control/select.cy.ts +56 -0
  11. package/cypress/e2e/control/text.cy.ts +43 -0
  12. package/cypress/e2e/editor.cy.ts +67 -0
  13. package/cypress/e2e/menus/block.cy.ts +38 -0
  14. package/cypress/e2e/menus/checkbox.cy.ts +33 -0
  15. package/cypress/e2e/menus/codeblock.cy.ts +34 -0
  16. package/cypress/e2e/menus/date.cy.ts +28 -0
  17. package/cypress/e2e/menus/format.cy.ts +40 -0
  18. package/cypress/e2e/menus/hyperlink.cy.ts +39 -0
  19. package/cypress/e2e/menus/image.cy.ts +25 -0
  20. package/cypress/e2e/menus/latex.cy.ts +34 -0
  21. package/cypress/e2e/menus/pagebreak.cy.ts +21 -0
  22. package/cypress/e2e/menus/painter.cy.ts +53 -0
  23. package/cypress/e2e/menus/print.cy.ts +25 -0
  24. package/cypress/e2e/menus/row.cy.ts +103 -0
  25. package/cypress/e2e/menus/search.cy.ts +112 -0
  26. package/cypress/e2e/menus/separator.cy.ts +32 -0
  27. package/cypress/e2e/menus/table.cy.ts +25 -0
  28. package/cypress/e2e/menus/text.cy.ts +304 -0
  29. package/cypress/e2e/menus/title.cy.ts +43 -0
  30. package/cypress/e2e/menus/undoRedo.cy.ts +49 -0
  31. package/cypress/e2e/menus/watermark.cy.ts +64 -0
  32. package/cypress/fixtures/example.json +3 -0
  33. package/cypress/fixtures/test.png +0 -0
  34. package/cypress/global.d.ts +13 -0
  35. package/cypress/support/commands.ts +5 -0
  36. package/cypress/support/e2e.ts +1 -0
  37. package/cypress/tsconfig.json +21 -0
  38. package/cypress.config.ts +10 -0
  39. package/docs/.vitepress/config.ts +191 -0
  40. package/docs/.vitepress/theme/components/DeepWikiBadge.vue +21 -0
  41. package/docs/.vitepress/theme/components/ZreadBadge.vue +21 -0
  42. package/docs/.vitepress/theme/index.ts +27 -0
  43. package/docs/en/guide/api-common.md +49 -0
  44. package/docs/en/guide/api-instance.md +34 -0
  45. package/docs/en/guide/command-execute.md +1167 -0
  46. package/docs/en/guide/command-get.md +355 -0
  47. package/docs/en/guide/contextmenu-custom.md +44 -0
  48. package/docs/en/guide/contextmenu-internal.md +61 -0
  49. package/docs/en/guide/eventbus.md +260 -0
  50. package/docs/en/guide/i18n.md +112 -0
  51. package/docs/en/guide/listener.md +126 -0
  52. package/docs/en/guide/option.md +214 -0
  53. package/docs/en/guide/override.md +57 -0
  54. package/docs/en/guide/plugin-custom.md +25 -0
  55. package/docs/en/guide/plugin-internal.md +125 -0
  56. package/docs/en/guide/schema.md +237 -0
  57. package/docs/en/guide/shortcut-custom.md +22 -0
  58. package/docs/en/guide/shortcut-internal.md +189 -0
  59. package/docs/en/guide/start.md +97 -0
  60. package/docs/en/index.md +43 -0
  61. package/docs/guide/api-common.md +49 -0
  62. package/docs/guide/api-instance.md +34 -0
  63. package/docs/guide/command-execute.md +1157 -0
  64. package/docs/guide/command-get.md +353 -0
  65. package/docs/guide/contextmenu-custom.md +44 -0
  66. package/docs/guide/contextmenu-internal.md +61 -0
  67. package/docs/guide/eventbus.md +260 -0
  68. package/docs/guide/i18n.md +111 -0
  69. package/docs/guide/listener.md +126 -0
  70. package/docs/guide/option.md +214 -0
  71. package/docs/guide/override.md +57 -0
  72. package/docs/guide/plugin-custom.md +25 -0
  73. package/docs/guide/plugin-internal.md +125 -0
  74. package/docs/guide/schema.md +237 -0
  75. package/docs/guide/shortcut-custom.md +22 -0
  76. package/docs/guide/shortcut-internal.md +189 -0
  77. package/docs/guide/start.md +97 -0
  78. package/docs/index.md +43 -0
  79. package/docs/public/favicon.png +0 -0
  80. package/favicon.png +0 -0
  81. package/index.html +435 -0
  82. package/package.json +55 -0
  83. package/pnpm-lock.yaml +4113 -0
  84. package/scripts/release.js +41 -0
  85. package/scripts/verifyCommit.js +19 -0
  86. package/src/assets/images/alignment.svg +1 -0
  87. package/src/assets/images/arrow-left.svg +1 -0
  88. package/src/assets/images/arrow-right.svg +1 -0
  89. package/src/assets/images/block.svg +1 -0
  90. package/src/assets/images/bold.svg +1 -0
  91. package/src/assets/images/catalog.svg +1 -0
  92. package/src/assets/images/center.svg +1 -0
  93. package/src/assets/images/checkbox.svg +1 -0
  94. package/src/assets/images/close.svg +1 -0
  95. package/src/assets/images/codeblock.svg +1 -0
  96. package/src/assets/images/color.svg +1 -0
  97. package/src/assets/images/control.svg +1 -0
  98. package/src/assets/images/date.svg +1 -0
  99. package/src/assets/images/exit-fullscreen.svg +1 -0
  100. package/src/assets/images/format.svg +1 -0
  101. package/src/assets/images/highlight.svg +1 -0
  102. package/src/assets/images/hyperlink.svg +1 -0
  103. package/src/assets/images/image.svg +1 -0
  104. package/src/assets/images/italic.svg +1 -0
  105. package/src/assets/images/justify.svg +7 -0
  106. package/src/assets/images/latex.svg +1 -0
  107. package/src/assets/images/left.svg +1 -0
  108. package/src/assets/images/line-dash-dot-dot.svg +1 -0
  109. package/src/assets/images/line-dash-dot.svg +1 -0
  110. package/src/assets/images/line-dash-large-gap.svg +1 -0
  111. package/src/assets/images/line-dash-small-gap.svg +1 -0
  112. package/src/assets/images/line-dot.svg +1 -0
  113. package/src/assets/images/line-double.svg +1 -0
  114. package/src/assets/images/line-single.svg +1 -0
  115. package/src/assets/images/line-wavy.svg +1 -0
  116. package/src/assets/images/list.svg +1 -0
  117. package/src/assets/images/option.svg +1 -0
  118. package/src/assets/images/page-break.svg +1 -0
  119. package/src/assets/images/page-mode.svg +1 -0
  120. package/src/assets/images/page-scale-add.svg +1 -0
  121. package/src/assets/images/page-scale-minus.svg +1 -0
  122. package/src/assets/images/painter.svg +1 -0
  123. package/src/assets/images/paper-direction.svg +1 -0
  124. package/src/assets/images/paper-margin.svg +1 -0
  125. package/src/assets/images/paper-size.svg +1 -0
  126. package/src/assets/images/print.svg +1 -0
  127. package/src/assets/images/radio.svg +4 -0
  128. package/src/assets/images/redo.svg +1 -0
  129. package/src/assets/images/request-fullscreen.svg +1 -0
  130. package/src/assets/images/right.svg +1 -0
  131. package/src/assets/images/row-margin.svg +1 -0
  132. package/src/assets/images/search.svg +1 -0
  133. package/src/assets/images/separator.svg +1 -0
  134. package/src/assets/images/signature-undo.svg +1 -0
  135. package/src/assets/images/signature.svg +1 -0
  136. package/src/assets/images/size-add.svg +1 -0
  137. package/src/assets/images/size-minus.svg +1 -0
  138. package/src/assets/images/strikeout.svg +1 -0
  139. package/src/assets/images/subscript.svg +1 -0
  140. package/src/assets/images/superscript.svg +1 -0
  141. package/src/assets/images/table.svg +1 -0
  142. package/src/assets/images/title.svg +1 -0
  143. package/src/assets/images/trash.svg +1 -0
  144. package/src/assets/images/underline.svg +1 -0
  145. package/src/assets/images/undo.svg +1 -0
  146. package/src/assets/images/watermark.svg +1 -0
  147. package/src/assets/images/word-tool.svg +1 -0
  148. package/src/assets/snapshots/main_v0.2.1.png +0 -0
  149. package/src/assets/snapshots/main_v0.2.2.png +0 -0
  150. package/src/assets/snapshots/main_v0.3.0.png +0 -0
  151. package/src/assets/snapshots/main_v0.3.1.png +0 -0
  152. package/src/assets/snapshots/main_v0.5.0.png +0 -0
  153. package/src/assets/snapshots/main_v0.5.1.png +0 -0
  154. package/src/assets/snapshots/main_v0.6.0.png +0 -0
  155. package/src/assets/snapshots/main_v0.6.1.png +0 -0
  156. package/src/assets/snapshots/main_v0.7.0.png +0 -0
  157. package/src/assets/snapshots/main_v0.7.1.png +0 -0
  158. package/src/assets/snapshots/main_v0.7.2.png +0 -0
  159. package/src/assets/snapshots/main_v0.7.3.png +0 -0
  160. package/src/assets/snapshots/main_v0.7.4.png +0 -0
  161. package/src/assets/snapshots/main_v0.7.6.png +0 -0
  162. package/src/assets/snapshots/main_v0.7.7.png +0 -0
  163. package/src/assets/snapshots/main_v0.8.0.png +0 -0
  164. package/src/assets/snapshots/main_v0.8.5.png +0 -0
  165. package/src/assets/snapshots/main_v0.8.6.png +0 -0
  166. package/src/assets/snapshots/main_v0.8.7.png +0 -0
  167. package/src/assets/snapshots/main_v0.8.8.png +0 -0
  168. package/src/assets/snapshots/main_v0.9.0.png +0 -0
  169. package/src/assets/snapshots/main_v0.9.1.png +0 -0
  170. package/src/assets/snapshots/main_v0.9.2.png +0 -0
  171. package/src/assets/snapshots/main_v0.9.23.png +0 -0
  172. package/src/assets/snapshots/main_v0.9.28.png +0 -0
  173. package/src/assets/snapshots/main_v0.9.29.png +0 -0
  174. package/src/assets/snapshots/main_v0.9.3.png +0 -0
  175. package/src/assets/snapshots/main_v0.9.30.png +0 -0
  176. package/src/assets/snapshots/main_v0.9.32.png +0 -0
  177. package/src/assets/snapshots/main_v0.9.35.png +0 -0
  178. package/src/assets/snapshots/main_v0.9.4.png +0 -0
  179. package/src/assets/snapshots/main_v0.9.5.png +0 -0
  180. package/src/assets/snapshots/main_v0.9.6.png +0 -0
  181. package/src/assets/snapshots/main_v0.9.8.png +0 -0
  182. package/src/components/dialog/Dialog.ts +171 -0
  183. package/src/components/dialog/dialog.css +131 -0
  184. package/src/components/signature/Signature.ts +340 -0
  185. package/src/components/signature/signature.css +132 -0
  186. package/src/editor/assets/css/block/block.css +21 -0
  187. package/src/editor/assets/css/contextmenu/contextmenu.css +196 -0
  188. package/src/editor/assets/css/control/calculator.css +85 -0
  189. package/src/editor/assets/css/control/select.css +44 -0
  190. package/src/editor/assets/css/date/datePicker.css +233 -0
  191. package/src/editor/assets/css/hyperlink/hyperlink.css +26 -0
  192. package/src/editor/assets/css/index.css +78 -0
  193. package/src/editor/assets/css/previewer/previewer.css +122 -0
  194. package/src/editor/assets/css/resizer/resizer.css +74 -0
  195. package/src/editor/assets/css/table/table.css +155 -0
  196. package/src/editor/assets/css/zone/zone.css +61 -0
  197. package/src/editor/assets/images/close.svg +1 -0
  198. package/src/editor/assets/images/delete-col.svg +1 -0
  199. package/src/editor/assets/images/delete-row-col.svg +1 -0
  200. package/src/editor/assets/images/delete-row.svg +1 -0
  201. package/src/editor/assets/images/delete-table.svg +1 -0
  202. package/src/editor/assets/images/image-change.svg +1 -0
  203. package/src/editor/assets/images/image-download.svg +1 -0
  204. package/src/editor/assets/images/image-next.svg +1 -0
  205. package/src/editor/assets/images/image-pre.svg +1 -0
  206. package/src/editor/assets/images/image.svg +1 -0
  207. package/src/editor/assets/images/insert-bottom-row.svg +1 -0
  208. package/src/editor/assets/images/insert-left-col.svg +1 -0
  209. package/src/editor/assets/images/insert-right-col.svg +1 -0
  210. package/src/editor/assets/images/insert-row-col.svg +1 -0
  211. package/src/editor/assets/images/insert-top-row.svg +1 -0
  212. package/src/editor/assets/images/merge-cancel-cell.svg +1 -0
  213. package/src/editor/assets/images/merge-cell.svg +1 -0
  214. package/src/editor/assets/images/original-size.svg +1 -0
  215. package/src/editor/assets/images/print.svg +1 -0
  216. package/src/editor/assets/images/rotate.svg +1 -0
  217. package/src/editor/assets/images/submenu-dropdown.svg +1 -0
  218. package/src/editor/assets/images/table-border-all.svg +1 -0
  219. package/src/editor/assets/images/table-border-dash.svg +1 -0
  220. package/src/editor/assets/images/table-border-empty.svg +1 -0
  221. package/src/editor/assets/images/table-border-external.svg +1 -0
  222. package/src/editor/assets/images/table-border-internal.svg +1 -0
  223. package/src/editor/assets/images/table-border-td-back.svg +1 -0
  224. package/src/editor/assets/images/table-border-td-bottom.svg +1 -0
  225. package/src/editor/assets/images/table-border-td-forward.svg +1 -0
  226. package/src/editor/assets/images/table-border-td-left.svg +1 -0
  227. package/src/editor/assets/images/table-border-td-right.svg +1 -0
  228. package/src/editor/assets/images/table-border-td-top.svg +1 -0
  229. package/src/editor/assets/images/table-border-td.svg +1 -0
  230. package/src/editor/assets/images/vertical-align-bottom.svg +1 -0
  231. package/src/editor/assets/images/vertical-align-middle.svg +1 -0
  232. package/src/editor/assets/images/vertical-align-top.svg +1 -0
  233. package/src/editor/assets/images/vertical-align.svg +1 -0
  234. package/src/editor/assets/images/zoom-in.svg +1 -0
  235. package/src/editor/assets/images/zoom-out.svg +1 -0
  236. package/src/editor/core/actuator/Actuator.ts +21 -0
  237. package/src/editor/core/actuator/handlers/positionContextChange.ts +13 -0
  238. package/src/editor/core/command/Command.ts +312 -0
  239. package/src/editor/core/command/CommandAdapt.ts +2733 -0
  240. package/src/editor/core/contextmenu/ContextMenu.ts +363 -0
  241. package/src/editor/core/contextmenu/menus/controlMenus.ts +25 -0
  242. package/src/editor/core/contextmenu/menus/globalMenus.ts +66 -0
  243. package/src/editor/core/contextmenu/menus/hyperlinkMenus.ts +58 -0
  244. package/src/editor/core/contextmenu/menus/imageMenus.ts +134 -0
  245. package/src/editor/core/contextmenu/menus/tableMenus.ts +331 -0
  246. package/src/editor/core/cursor/Cursor.ts +248 -0
  247. package/src/editor/core/cursor/CursorAgent.ts +75 -0
  248. package/src/editor/core/draw/Draw.ts +2934 -0
  249. package/src/editor/core/draw/control/Control.ts +1767 -0
  250. package/src/editor/core/draw/control/checkbox/CheckboxControl.ts +154 -0
  251. package/src/editor/core/draw/control/date/DateControl.ts +363 -0
  252. package/src/editor/core/draw/control/interactive/ControlSearch.ts +214 -0
  253. package/src/editor/core/draw/control/number/Calculator.ts +183 -0
  254. package/src/editor/core/draw/control/number/NumberControl.ts +183 -0
  255. package/src/editor/core/draw/control/radio/RadioControl.ts +68 -0
  256. package/src/editor/core/draw/control/richtext/Border.ts +52 -0
  257. package/src/editor/core/draw/control/select/SelectControl.ts +567 -0
  258. package/src/editor/core/draw/control/text/TextControl.ts +280 -0
  259. package/src/editor/core/draw/frame/Background.ts +117 -0
  260. package/src/editor/core/draw/frame/Badge.ts +88 -0
  261. package/src/editor/core/draw/frame/Footer.ts +155 -0
  262. package/src/editor/core/draw/frame/Header.ts +158 -0
  263. package/src/editor/core/draw/frame/LineNumber.ts +43 -0
  264. package/src/editor/core/draw/frame/Margin.ts +53 -0
  265. package/src/editor/core/draw/frame/PageBorder.ts +47 -0
  266. package/src/editor/core/draw/frame/PageNumber.ts +88 -0
  267. package/src/editor/core/draw/frame/Placeholder.ts +114 -0
  268. package/src/editor/core/draw/frame/Watermark.ts +188 -0
  269. package/src/editor/core/draw/graffiti/Graffiti.ts +125 -0
  270. package/src/editor/core/draw/interactive/Area.ts +312 -0
  271. package/src/editor/core/draw/interactive/Group.ts +198 -0
  272. package/src/editor/core/draw/interactive/Search.ts +527 -0
  273. package/src/editor/core/draw/particle/CheckboxParticle.ts +111 -0
  274. package/src/editor/core/draw/particle/HyperlinkParticle.ts +86 -0
  275. package/src/editor/core/draw/particle/ImageParticle.ts +280 -0
  276. package/src/editor/core/draw/particle/LabelParticle.ts +79 -0
  277. package/src/editor/core/draw/particle/LineBreakParticle.ts +55 -0
  278. package/src/editor/core/draw/particle/ListParticle.ts +255 -0
  279. package/src/editor/core/draw/particle/PageBreakParticle.ts +54 -0
  280. package/src/editor/core/draw/particle/RadioParticle.ts +99 -0
  281. package/src/editor/core/draw/particle/SeparatorParticle.ts +37 -0
  282. package/src/editor/core/draw/particle/SubscriptParticle.ts +23 -0
  283. package/src/editor/core/draw/particle/SuperscriptParticle.ts +23 -0
  284. package/src/editor/core/draw/particle/TextParticle.ts +174 -0
  285. package/src/editor/core/draw/particle/WhiteSpaceParticle.ts +32 -0
  286. package/src/editor/core/draw/particle/block/BlockParticle.ts +76 -0
  287. package/src/editor/core/draw/particle/block/modules/BaseBlock.ts +280 -0
  288. package/src/editor/core/draw/particle/block/modules/IFrameBlock.ts +47 -0
  289. package/src/editor/core/draw/particle/block/modules/VideoBlock.ts +61 -0
  290. package/src/editor/core/draw/particle/date/DateParticle.ts +111 -0
  291. package/src/editor/core/draw/particle/date/DatePicker.ts +577 -0
  292. package/src/editor/core/draw/particle/latex/LaTexParticle.ts +43 -0
  293. package/src/editor/core/draw/particle/latex/utils/LaTexUtils.ts +1196 -0
  294. package/src/editor/core/draw/particle/latex/utils/hershey.ts +1632 -0
  295. package/src/editor/core/draw/particle/latex/utils/symbols.ts +318 -0
  296. package/src/editor/core/draw/particle/previewer/Previewer.ts +582 -0
  297. package/src/editor/core/draw/particle/table/TableOperate.ts +988 -0
  298. package/src/editor/core/draw/particle/table/TableParticle.ts +558 -0
  299. package/src/editor/core/draw/particle/table/TableTool.ts +551 -0
  300. package/src/editor/core/draw/richtext/AbstractRichText.ts +59 -0
  301. package/src/editor/core/draw/richtext/Highlight.ts +24 -0
  302. package/src/editor/core/draw/richtext/Strikeout.ts +28 -0
  303. package/src/editor/core/draw/richtext/Underline.ts +106 -0
  304. package/src/editor/core/event/CanvasEvent.ts +215 -0
  305. package/src/editor/core/event/GlobalEvent.ts +173 -0
  306. package/src/editor/core/event/eventbus/EventBus.ts +50 -0
  307. package/src/editor/core/event/handlers/click.ts +234 -0
  308. package/src/editor/core/event/handlers/composition.ts +45 -0
  309. package/src/editor/core/event/handlers/copy.ts +72 -0
  310. package/src/editor/core/event/handlers/cut.ts +47 -0
  311. package/src/editor/core/event/handlers/drag.ts +66 -0
  312. package/src/editor/core/event/handlers/drop.ts +28 -0
  313. package/src/editor/core/event/handlers/input.ts +129 -0
  314. package/src/editor/core/event/handlers/keydown/backspace.ts +161 -0
  315. package/src/editor/core/event/handlers/keydown/delete.ts +119 -0
  316. package/src/editor/core/event/handlers/keydown/end.ts +69 -0
  317. package/src/editor/core/event/handlers/keydown/enter.ts +126 -0
  318. package/src/editor/core/event/handlers/keydown/home.ts +69 -0
  319. package/src/editor/core/event/handlers/keydown/index.ts +85 -0
  320. package/src/editor/core/event/handlers/keydown/left.ts +162 -0
  321. package/src/editor/core/event/handlers/keydown/right.ts +178 -0
  322. package/src/editor/core/event/handlers/keydown/tab.ts +41 -0
  323. package/src/editor/core/event/handlers/keydown/updown.ts +342 -0
  324. package/src/editor/core/event/handlers/mousedown.ts +262 -0
  325. package/src/editor/core/event/handlers/mouseleave.ts +14 -0
  326. package/src/editor/core/event/handlers/mousemove.ts +133 -0
  327. package/src/editor/core/event/handlers/mouseup.ts +341 -0
  328. package/src/editor/core/event/handlers/paste.ts +231 -0
  329. package/src/editor/core/history/HistoryManager.ts +61 -0
  330. package/src/editor/core/i18n/I18n.ts +51 -0
  331. package/src/editor/core/i18n/lang/en.json +92 -0
  332. package/src/editor/core/i18n/lang/zh-CN.json +92 -0
  333. package/src/editor/core/listener/Listener.ts +41 -0
  334. package/src/editor/core/observer/ImageObserver.ts +19 -0
  335. package/src/editor/core/observer/MouseObserver.ts +56 -0
  336. package/src/editor/core/observer/ScrollObserver.ts +88 -0
  337. package/src/editor/core/observer/SelectionObserver.ts +143 -0
  338. package/src/editor/core/override/Override.ts +14 -0
  339. package/src/editor/core/plugin/Plugin.ts +17 -0
  340. package/src/editor/core/position/Position.ts +870 -0
  341. package/src/editor/core/range/RangeManager.ts +723 -0
  342. package/src/editor/core/register/Register.ts +28 -0
  343. package/src/editor/core/shortcut/Shortcut.ts +80 -0
  344. package/src/editor/core/shortcut/keys/listKeys.ts +22 -0
  345. package/src/editor/core/shortcut/keys/richtextKeys.ts +102 -0
  346. package/src/editor/core/shortcut/keys/titleKeys.ts +62 -0
  347. package/src/editor/core/worker/WorkerManager.ts +96 -0
  348. package/src/editor/core/worker/works/catalog.ts +189 -0
  349. package/src/editor/core/worker/works/group.ts +34 -0
  350. package/src/editor/core/worker/works/value.ts +32 -0
  351. package/src/editor/core/worker/works/wordCount.ts +132 -0
  352. package/src/editor/core/zone/Zone.ts +183 -0
  353. package/src/editor/core/zone/ZoneTip.ts +108 -0
  354. package/src/editor/dataset/constant/Background.ts +10 -0
  355. package/src/editor/dataset/constant/Badge.ts +6 -0
  356. package/src/editor/dataset/constant/Checkbox.ts +12 -0
  357. package/src/editor/dataset/constant/Common.ts +44 -0
  358. package/src/editor/dataset/constant/ContextMenu.ts +61 -0
  359. package/src/editor/dataset/constant/Control.ts +14 -0
  360. package/src/editor/dataset/constant/Cursor.ts +11 -0
  361. package/src/editor/dataset/constant/Editor.ts +19 -0
  362. package/src/editor/dataset/constant/Element.ts +173 -0
  363. package/src/editor/dataset/constant/Footer.ts +10 -0
  364. package/src/editor/dataset/constant/Graffiti.ts +6 -0
  365. package/src/editor/dataset/constant/Group.ts +10 -0
  366. package/src/editor/dataset/constant/Header.ts +10 -0
  367. package/src/editor/dataset/constant/ImgCaption.ts +8 -0
  368. package/src/editor/dataset/constant/Label.ts +8 -0
  369. package/src/editor/dataset/constant/LineBreak.ts +7 -0
  370. package/src/editor/dataset/constant/LineNumber.ts +11 -0
  371. package/src/editor/dataset/constant/List.ts +26 -0
  372. package/src/editor/dataset/constant/PageBorder.ts +8 -0
  373. package/src/editor/dataset/constant/PageBreak.ts +7 -0
  374. package/src/editor/dataset/constant/PageNumber.ts +22 -0
  375. package/src/editor/dataset/constant/Placeholder.ts +9 -0
  376. package/src/editor/dataset/constant/Radio.ts +12 -0
  377. package/src/editor/dataset/constant/Regular.ts +23 -0
  378. package/src/editor/dataset/constant/Separator.ts +6 -0
  379. package/src/editor/dataset/constant/Shortcut.ts +3 -0
  380. package/src/editor/dataset/constant/Table.ts +9 -0
  381. package/src/editor/dataset/constant/Title.ts +38 -0
  382. package/src/editor/dataset/constant/Watermark.ts +17 -0
  383. package/src/editor/dataset/constant/WhiteSpace.ts +7 -0
  384. package/src/editor/dataset/constant/Zone.ts +5 -0
  385. package/src/editor/dataset/enum/Area.ts +5 -0
  386. package/src/editor/dataset/enum/Background.ts +11 -0
  387. package/src/editor/dataset/enum/Block.ts +4 -0
  388. package/src/editor/dataset/enum/Common.ts +30 -0
  389. package/src/editor/dataset/enum/Control.ts +39 -0
  390. package/src/editor/dataset/enum/Editor.ts +51 -0
  391. package/src/editor/dataset/enum/Element.ts +21 -0
  392. package/src/editor/dataset/enum/ElementStyle.ts +12 -0
  393. package/src/editor/dataset/enum/Event.ts +5 -0
  394. package/src/editor/dataset/enum/KeyMap.ts +85 -0
  395. package/src/editor/dataset/enum/LineNumber.ts +4 -0
  396. package/src/editor/dataset/enum/List.ts +23 -0
  397. package/src/editor/dataset/enum/Observer.ts +6 -0
  398. package/src/editor/dataset/enum/Row.ts +7 -0
  399. package/src/editor/dataset/enum/Text.ts +13 -0
  400. package/src/editor/dataset/enum/Title.ts +8 -0
  401. package/src/editor/dataset/enum/VerticalAlign.ts +5 -0
  402. package/src/editor/dataset/enum/Watermark.ts +4 -0
  403. package/src/editor/dataset/enum/table/Table.ts +19 -0
  404. package/src/editor/dataset/enum/table/TableTool.ts +4 -0
  405. package/src/editor/index.ts +241 -0
  406. package/src/editor/interface/Area.ts +68 -0
  407. package/src/editor/interface/Background.ts +9 -0
  408. package/src/editor/interface/Badge.ts +17 -0
  409. package/src/editor/interface/Block.ts +18 -0
  410. package/src/editor/interface/Catalog.ts +11 -0
  411. package/src/editor/interface/Checkbox.ts +17 -0
  412. package/src/editor/interface/Command.ts +3 -0
  413. package/src/editor/interface/Common.ts +43 -0
  414. package/src/editor/interface/Control.ts +250 -0
  415. package/src/editor/interface/Cursor.ts +7 -0
  416. package/src/editor/interface/Draw.ts +86 -0
  417. package/src/editor/interface/Editor.ts +172 -0
  418. package/src/editor/interface/Element.ts +273 -0
  419. package/src/editor/interface/Event.ts +27 -0
  420. package/src/editor/interface/EventBus.ts +46 -0
  421. package/src/editor/interface/Footer.ts +9 -0
  422. package/src/editor/interface/Graffiti.ts +15 -0
  423. package/src/editor/interface/Group.ts +8 -0
  424. package/src/editor/interface/Header.ts +9 -0
  425. package/src/editor/interface/Label.ts +8 -0
  426. package/src/editor/interface/LineBreak.ts +5 -0
  427. package/src/editor/interface/LineNumber.ts +10 -0
  428. package/src/editor/interface/Listener.ts +88 -0
  429. package/src/editor/interface/Margin.ts +1 -0
  430. package/src/editor/interface/PageBorder.ts +8 -0
  431. package/src/editor/interface/PageBreak.ts +5 -0
  432. package/src/editor/interface/PageNumber.ts +16 -0
  433. package/src/editor/interface/Placeholder.ts +7 -0
  434. package/src/editor/interface/Plugin.ts +8 -0
  435. package/src/editor/interface/Position.ts +113 -0
  436. package/src/editor/interface/Previewer.ts +15 -0
  437. package/src/editor/interface/Radio.ts +17 -0
  438. package/src/editor/interface/Range.ts +61 -0
  439. package/src/editor/interface/Row.ts +25 -0
  440. package/src/editor/interface/Search.ts +36 -0
  441. package/src/editor/interface/Separator.ts +4 -0
  442. package/src/editor/interface/Text.ts +15 -0
  443. package/src/editor/interface/Title.ts +32 -0
  444. package/src/editor/interface/Watermark.ts +16 -0
  445. package/src/editor/interface/WhiteSpace.ts +5 -0
  446. package/src/editor/interface/Zone.ts +3 -0
  447. package/src/editor/interface/contextmenu/ContextMenu.ts +76 -0
  448. package/src/editor/interface/i18n/I18n.ts +7 -0
  449. package/src/editor/interface/shortcut/Shortcut.ts +14 -0
  450. package/src/editor/interface/table/Colgroup.ts +4 -0
  451. package/src/editor/interface/table/Table.ts +9 -0
  452. package/src/editor/interface/table/Td.ts +36 -0
  453. package/src/editor/interface/table/Tr.ts +11 -0
  454. package/src/editor/types/index.d.ts +5 -0
  455. package/src/editor/utils/clipboard.ts +94 -0
  456. package/src/editor/utils/element.ts +1877 -0
  457. package/src/editor/utils/hotkey.ts +5 -0
  458. package/src/editor/utils/index.ts +445 -0
  459. package/src/editor/utils/option.ts +253 -0
  460. package/src/editor/utils/paragraph.ts +28 -0
  461. package/src/editor/utils/print.ts +99 -0
  462. package/src/editor/utils/ua.ts +13 -0
  463. package/src/main.ts +2053 -0
  464. package/src/mock.ts +611 -0
  465. package/src/plugins/copy/index.ts +30 -0
  466. package/src/plugins/markdown/index.ts +118 -0
  467. package/src/style.css +1079 -0
  468. package/src/utils/index.ts +45 -0
  469. package/src/utils/prism.ts +89 -0
  470. package/src/vite-env.d.ts +1 -0
  471. package/tsconfig.json +25 -0
  472. package/vite.config.ts +46 -0
@@ -0,0 +1,2934 @@
1
+ import { version } from '../../../../package.json'
2
+ import { ZERO } from '../../dataset/constant/Common'
3
+ import { RowFlex } from '../../dataset/enum/Row'
4
+ import {
5
+ IAppendElementListOption,
6
+ IComputeRowListPayload,
7
+ IDrawFloatPayload,
8
+ IDrawOption,
9
+ IDrawPagePayload,
10
+ IDrawRowPayload,
11
+ IGetImageOption,
12
+ IGetOriginValueOption,
13
+ IGetValueOption,
14
+ IPainterOption
15
+ } from '../../interface/Draw'
16
+ import {
17
+ IEditorData,
18
+ IEditorOption,
19
+ IEditorResult,
20
+ ISetValueOption
21
+ } from '../../interface/Editor'
22
+ import {
23
+ IElement,
24
+ IElementMetrics,
25
+ IElementFillRect,
26
+ IElementStyle,
27
+ ISpliceElementListOption,
28
+ IInsertElementListOption
29
+ } from '../../interface/Element'
30
+ import { IRow, IRowElement } from '../../interface/Row'
31
+ import { deepClone, getUUID, nextTick } from '../../utils'
32
+ import { Cursor } from '../cursor/Cursor'
33
+ import { CanvasEvent } from '../event/CanvasEvent'
34
+ import { GlobalEvent } from '../event/GlobalEvent'
35
+ import { HistoryManager } from '../history/HistoryManager'
36
+ import { Listener } from '../listener/Listener'
37
+ import { Position } from '../position/Position'
38
+ import { RangeManager } from '../range/RangeManager'
39
+ import { Background } from './frame/Background'
40
+ import { Highlight } from './richtext/Highlight'
41
+ import { Margin } from './frame/Margin'
42
+ import { Search } from './interactive/Search'
43
+ import { Strikeout } from './richtext/Strikeout'
44
+ import { Underline } from './richtext/Underline'
45
+ import { ElementType } from '../../dataset/enum/Element'
46
+ import { ImageParticle } from './particle/ImageParticle'
47
+ import { LaTexParticle } from './particle/latex/LaTexParticle'
48
+ import { TextParticle } from './particle/TextParticle'
49
+ import { PageNumber } from './frame/PageNumber'
50
+ import { ScrollObserver } from '../observer/ScrollObserver'
51
+ import { SelectionObserver } from '../observer/SelectionObserver'
52
+ import { TableParticle } from './particle/table/TableParticle'
53
+ import { TableTool } from './particle/table/TableTool'
54
+ import { HyperlinkParticle } from './particle/HyperlinkParticle'
55
+ import { LabelParticle } from './particle/LabelParticle'
56
+ import { Header } from './frame/Header'
57
+ import { SuperscriptParticle } from './particle/SuperscriptParticle'
58
+ import { SubscriptParticle } from './particle/SubscriptParticle'
59
+ import { SeparatorParticle } from './particle/SeparatorParticle'
60
+ import { PageBreakParticle } from './particle/PageBreakParticle'
61
+ import { Watermark } from './frame/Watermark'
62
+ import {
63
+ EditorComponent,
64
+ EditorMode,
65
+ EditorZone,
66
+ PageMode,
67
+ PaperDirection,
68
+ WordBreak
69
+ } from '../../dataset/enum/Editor'
70
+ import { Control } from './control/Control'
71
+ import {
72
+ deleteSurroundElementList,
73
+ getIsBlockElement,
74
+ getSlimCloneElementList,
75
+ pickSurroundElementList,
76
+ zipElementList
77
+ } from '../../utils/element'
78
+ import { CheckboxParticle } from './particle/CheckboxParticle'
79
+ import { RadioParticle } from './particle/RadioParticle'
80
+ import { DeepRequired, IPadding } from '../../interface/Common'
81
+ import {
82
+ ControlComponent,
83
+ ControlIndentation
84
+ } from '../../dataset/enum/Control'
85
+ import { formatElementList } from '../../utils/element'
86
+ import { WorkerManager } from '../worker/WorkerManager'
87
+ import { Previewer } from './particle/previewer/Previewer'
88
+ import { DateParticle } from './particle/date/DateParticle'
89
+ import { IMargin } from '../../interface/Margin'
90
+ import { BlockParticle } from './particle/block/BlockParticle'
91
+ import { EDITOR_COMPONENT, EDITOR_PREFIX } from '../../dataset/constant/Editor'
92
+ import { I18n } from '../i18n/I18n'
93
+ import { ImageObserver } from '../observer/ImageObserver'
94
+ import { Zone } from '../zone/Zone'
95
+ import { Footer } from './frame/Footer'
96
+ import {
97
+ IMAGE_ELEMENT_TYPE,
98
+ TEXTLIKE_ELEMENT_TYPE
99
+ } from '../../dataset/constant/Element'
100
+ import { ListParticle } from './particle/ListParticle'
101
+ import { Placeholder } from './frame/Placeholder'
102
+ import { EventBus } from '../event/eventbus/EventBus'
103
+ import { EventBusMap } from '../../interface/EventBus'
104
+ import { Group } from './interactive/Group'
105
+ import { Override } from '../override/Override'
106
+ import { FlexDirection, ImageDisplay } from '../../dataset/enum/Common'
107
+ import {
108
+ PUNCTUATION_REG,
109
+ WHITE_SPACE_REG
110
+ } from '../../dataset/constant/Regular'
111
+ import { LineBreakParticle } from './particle/LineBreakParticle'
112
+ import { WhiteSpaceParticle } from './particle/WhiteSpaceParticle'
113
+ import { MouseObserver } from '../observer/MouseObserver'
114
+ import { LineNumber } from './frame/LineNumber'
115
+ import { PageBorder } from './frame/PageBorder'
116
+ import { ITd } from '../../interface/table/Td'
117
+ import { Actuator } from '../actuator/Actuator'
118
+ import { TableOperate } from './particle/table/TableOperate'
119
+ import { Area } from './interactive/Area'
120
+ import { Badge } from './frame/Badge'
121
+ import { Graffiti } from './graffiti/Graffiti'
122
+
123
+ export class Draw {
124
+ private container: HTMLDivElement
125
+ private pageContainer: HTMLDivElement
126
+ private pageList: HTMLCanvasElement[]
127
+ private ctxList: CanvasRenderingContext2D[]
128
+ private pageNo: number
129
+ private renderCount: number
130
+ private pagePixelRatio: number | null
131
+ private mode: EditorMode
132
+ private options: DeepRequired<IEditorOption>
133
+ private position: Position
134
+ private zone: Zone
135
+ private elementList: IElement[]
136
+ private listener: Listener
137
+ private eventBus: EventBus<EventBusMap>
138
+ private override: Override
139
+
140
+ private i18n: I18n
141
+ private canvasEvent: CanvasEvent
142
+ private globalEvent: GlobalEvent
143
+ private cursor: Cursor
144
+ private range: RangeManager
145
+ private margin: Margin
146
+ private background: Background
147
+ private badge: Badge
148
+ private search: Search
149
+ private group: Group
150
+ private area: Area
151
+ private underline: Underline
152
+ private strikeout: Strikeout
153
+ private highlight: Highlight
154
+ private historyManager: HistoryManager
155
+ private previewer: Previewer
156
+ private imageParticle: ImageParticle
157
+ private laTexParticle: LaTexParticle
158
+ private textParticle: TextParticle
159
+ private tableParticle: TableParticle
160
+ private tableTool: TableTool
161
+ private tableOperate: TableOperate
162
+ private pageNumber: PageNumber
163
+ private lineNumber: LineNumber
164
+ private waterMark: Watermark
165
+ private placeholder: Placeholder
166
+ private header: Header
167
+ private footer: Footer
168
+ private hyperlinkParticle: HyperlinkParticle
169
+ private labelParticle: LabelParticle
170
+ private dateParticle: DateParticle
171
+ private separatorParticle: SeparatorParticle
172
+ private pageBreakParticle: PageBreakParticle
173
+ private superscriptParticle: SuperscriptParticle
174
+ private subscriptParticle: SubscriptParticle
175
+ private checkboxParticle: CheckboxParticle
176
+ private radioParticle: RadioParticle
177
+ private blockParticle: BlockParticle
178
+ private listParticle: ListParticle
179
+ private lineBreakParticle: LineBreakParticle
180
+ private whiteSpaceParticle: WhiteSpaceParticle
181
+ private control: Control
182
+ private pageBorder: PageBorder
183
+ private workerManager: WorkerManager
184
+ private scrollObserver: ScrollObserver
185
+ private selectionObserver: SelectionObserver
186
+ private imageObserver: ImageObserver
187
+ private graffiti: Graffiti
188
+
189
+ private LETTER_REG: RegExp
190
+ private WORD_LIKE_REG: RegExp
191
+ private rowList: IRow[]
192
+ private pageRowList: IRow[][]
193
+ private painterStyle: IElementStyle | null
194
+ private painterOptions: IPainterOption | null
195
+ private visiblePageNoList: number[]
196
+ private intersectionPageNo: number
197
+ private lazyRenderIntersectionObserver: IntersectionObserver | null
198
+ private printModeData: Required<Omit<IEditorData, 'graffiti'>> | null
199
+
200
+ constructor(
201
+ rootContainer: HTMLElement,
202
+ options: DeepRequired<IEditorOption>,
203
+ data: IEditorData,
204
+ listener: Listener,
205
+ eventBus: EventBus<EventBusMap>,
206
+ override: Override
207
+ ) {
208
+ this.container = this._wrapContainer(rootContainer)
209
+ this.pageList = []
210
+ this.ctxList = []
211
+ this.pageNo = 0
212
+ this.renderCount = 0
213
+ this.pagePixelRatio = null
214
+ this.mode = options.mode
215
+ this.options = options
216
+ this.elementList = data.main
217
+ this.listener = listener
218
+ this.eventBus = eventBus
219
+ this.override = override
220
+
221
+ this._formatContainer()
222
+ this.pageContainer = this._createPageContainer()
223
+ this._createPage(0)
224
+
225
+ this.i18n = new I18n(options.locale)
226
+ this.historyManager = new HistoryManager(this)
227
+ this.position = new Position(this)
228
+ this.zone = new Zone(this)
229
+ this.range = new RangeManager(this)
230
+ this.margin = new Margin(this)
231
+ this.background = new Background(this)
232
+ this.badge = new Badge(this)
233
+ this.search = new Search(this)
234
+ this.group = new Group(this)
235
+ this.area = new Area(this)
236
+ this.underline = new Underline(this)
237
+ this.strikeout = new Strikeout(this)
238
+ this.highlight = new Highlight(this)
239
+ this.previewer = new Previewer(this)
240
+ this.imageParticle = new ImageParticle(this)
241
+ this.laTexParticle = new LaTexParticle(this)
242
+ this.textParticle = new TextParticle(this)
243
+ this.tableParticle = new TableParticle(this)
244
+ this.tableTool = new TableTool(this)
245
+ this.tableOperate = new TableOperate(this)
246
+ this.pageNumber = new PageNumber(this)
247
+ this.lineNumber = new LineNumber(this)
248
+ this.waterMark = new Watermark(this)
249
+ this.placeholder = new Placeholder(this)
250
+ this.header = new Header(this, data.header)
251
+ this.footer = new Footer(this, data.footer)
252
+ this.hyperlinkParticle = new HyperlinkParticle(this)
253
+ this.labelParticle = new LabelParticle(this)
254
+ this.dateParticle = new DateParticle(this)
255
+ this.separatorParticle = new SeparatorParticle(this)
256
+ this.pageBreakParticle = new PageBreakParticle(this)
257
+ this.superscriptParticle = new SuperscriptParticle()
258
+ this.subscriptParticle = new SubscriptParticle()
259
+ this.checkboxParticle = new CheckboxParticle(this)
260
+ this.radioParticle = new RadioParticle(this)
261
+ this.blockParticle = new BlockParticle(this)
262
+ this.listParticle = new ListParticle(this)
263
+ this.lineBreakParticle = new LineBreakParticle(this)
264
+ this.whiteSpaceParticle = new WhiteSpaceParticle(this)
265
+ this.control = new Control(this)
266
+ this.pageBorder = new PageBorder(this)
267
+ this.graffiti = new Graffiti(this, data.graffiti)
268
+
269
+ this.scrollObserver = new ScrollObserver(this)
270
+ this.selectionObserver = new SelectionObserver(this)
271
+ this.imageObserver = new ImageObserver()
272
+ new MouseObserver(this)
273
+
274
+ this.canvasEvent = new CanvasEvent(this)
275
+ this.cursor = new Cursor(this, this.canvasEvent)
276
+ this.canvasEvent.register()
277
+ this.globalEvent = new GlobalEvent(this, this.canvasEvent)
278
+ this.globalEvent.register()
279
+
280
+ this.workerManager = new WorkerManager(this)
281
+ new Actuator(this)
282
+
283
+ const { letterClass } = options
284
+ this.LETTER_REG = new RegExp(`[${letterClass.join('')}]`)
285
+ this.WORD_LIKE_REG = new RegExp(
286
+ `${letterClass.map(letter => `[^${letter}][${letter}]`).join('|')}`
287
+ )
288
+ this.rowList = []
289
+ this.pageRowList = []
290
+ this.painterStyle = null
291
+ this.painterOptions = null
292
+ this.visiblePageNoList = []
293
+ this.intersectionPageNo = 0
294
+ this.lazyRenderIntersectionObserver = null
295
+ this.printModeData = null
296
+
297
+ // 打印模式优先设置打印数据
298
+ if (this.mode === EditorMode.PRINT) {
299
+ this.setPrintData()
300
+ }
301
+ this.render({
302
+ isInit: true,
303
+ isSetCursor: false,
304
+ isFirstRender: true
305
+ })
306
+ }
307
+
308
+ // 设置打印数据
309
+ public setPrintData() {
310
+ this.printModeData = {
311
+ header: this.header.getElementList(),
312
+ main: this.elementList,
313
+ footer: this.footer.getElementList()
314
+ }
315
+ // 过滤控件辅助元素
316
+ const clonePrintModeData = deepClone(this.printModeData)
317
+ const editorDataKeys: (keyof Omit<IEditorData, 'graffiti'>)[] = [
318
+ 'header',
319
+ 'main',
320
+ 'footer'
321
+ ]
322
+ editorDataKeys.forEach(key => {
323
+ clonePrintModeData[key] = this.control.filterAssistElement(
324
+ clonePrintModeData[key]
325
+ )
326
+ })
327
+ this.setEditorData(clonePrintModeData)
328
+ }
329
+
330
+ // 还原打印数据
331
+ public clearPrintData() {
332
+ if (this.printModeData) {
333
+ this.setEditorData(this.printModeData)
334
+ this.printModeData = null
335
+ }
336
+ }
337
+
338
+ public getLetterReg(): RegExp {
339
+ return this.LETTER_REG
340
+ }
341
+
342
+ public getMode(): EditorMode {
343
+ return this.mode
344
+ }
345
+
346
+ public setMode(payload: EditorMode) {
347
+ if (this.mode === payload) return
348
+ // 设置打印模式
349
+ if (payload === EditorMode.PRINT) {
350
+ this.setPrintData()
351
+ }
352
+ // 取消打印模式
353
+ if (this.mode === EditorMode.PRINT) {
354
+ this.clearPrintData()
355
+ }
356
+ this.clearSideEffect()
357
+ this.range.clearRange()
358
+ this.mode = payload
359
+ this.options.mode = payload
360
+ this.render({
361
+ isSetCursor: false,
362
+ isSubmitHistory: false
363
+ })
364
+ }
365
+
366
+ public isReadonly() {
367
+ if (this.area.getActiveAreaInfo()?.area?.mode) {
368
+ return this.area.isReadonly()
369
+ }
370
+ switch (this.mode) {
371
+ case EditorMode.DESIGN:
372
+ return false
373
+ case EditorMode.READONLY:
374
+ case EditorMode.PRINT:
375
+ case EditorMode.GRAFFITI:
376
+ return true
377
+ case EditorMode.FORM:
378
+ return !this.control.getIsRangeWithinControl()
379
+ default:
380
+ return false
381
+ }
382
+ }
383
+
384
+ public isDisabled() {
385
+ if (this.mode === EditorMode.DESIGN) return false
386
+ const { startIndex, endIndex } = this.range.getRange()
387
+ const elementList = this.getElementList()
388
+ // 优先判断表格单元格
389
+ if (this.getTd()?.disabled) return true
390
+ if (startIndex === endIndex) {
391
+ const startElement = elementList[startIndex]
392
+ const nextElement = elementList[startIndex + 1]
393
+ return !!(
394
+ (startElement?.title?.disabled &&
395
+ nextElement?.title?.disabled &&
396
+ startElement.titleId === nextElement.titleId) ||
397
+ (startElement?.control?.disabled &&
398
+ nextElement?.control?.disabled &&
399
+ startElement.controlId === nextElement.controlId)
400
+ )
401
+ }
402
+ const selectionElementList = elementList.slice(startIndex + 1, endIndex + 1)
403
+ return selectionElementList.some(
404
+ element => element.title?.disabled || element.control?.disabled
405
+ )
406
+ }
407
+
408
+ public isDesignMode() {
409
+ return this.mode === EditorMode.DESIGN
410
+ }
411
+
412
+ public isPrintMode() {
413
+ return this.mode === EditorMode.PRINT
414
+ }
415
+
416
+ public isGraffitiMode() {
417
+ return this.mode === EditorMode.GRAFFITI
418
+ }
419
+
420
+ public getOriginalWidth(): number {
421
+ const { paperDirection, width, height } = this.options
422
+ return paperDirection === PaperDirection.VERTICAL ? width : height
423
+ }
424
+
425
+ public getOriginalHeight(): number {
426
+ const { paperDirection, width, height } = this.options
427
+ return paperDirection === PaperDirection.VERTICAL ? height : width
428
+ }
429
+
430
+ public getWidth(): number {
431
+ return Math.floor(this.getOriginalWidth() * this.options.scale)
432
+ }
433
+
434
+ public getHeight(): number {
435
+ return Math.floor(this.getOriginalHeight() * this.options.scale)
436
+ }
437
+
438
+ public getMainHeight(): number {
439
+ const pageHeight = this.getHeight()
440
+ return pageHeight - this.getMainOuterHeight()
441
+ }
442
+
443
+ public getMainOuterHeight(): number {
444
+ const margins = this.getMargins()
445
+ const headerExtraHeight = this.header.getExtraHeight()
446
+ const footerExtraHeight = this.footer.getExtraHeight()
447
+ return margins[0] + margins[2] + headerExtraHeight + footerExtraHeight
448
+ }
449
+
450
+ public getCanvasWidth(pageNo = -1): number {
451
+ const page = this.getPage(pageNo)
452
+ return page.width
453
+ }
454
+
455
+ public getCanvasHeight(pageNo = -1): number {
456
+ const page = this.getPage(pageNo)
457
+ return page.height
458
+ }
459
+
460
+ public getInnerWidth(): number {
461
+ const width = this.getWidth()
462
+ const margins = this.getMargins()
463
+ return width - margins[1] - margins[3]
464
+ }
465
+
466
+ public getOriginalInnerWidth(): number {
467
+ const width = this.getOriginalWidth()
468
+ const margins = this.getOriginalMargins()
469
+ return width - margins[1] - margins[3]
470
+ }
471
+
472
+ public getContextInnerWidth(): number {
473
+ const positionContext = this.position.getPositionContext()
474
+ if (positionContext.isTable) {
475
+ const { index, trIndex, tdIndex } = positionContext
476
+ const elementList = this.getOriginalElementList()
477
+ const td = elementList[index!].trList![trIndex!].tdList[tdIndex!]
478
+ const tdPadding = this.getTdPadding()
479
+ return td!.width! - tdPadding[1] - tdPadding[3]
480
+ }
481
+ return this.getOriginalInnerWidth()
482
+ }
483
+
484
+ public getMargins(): IMargin {
485
+ return <IMargin>this.getOriginalMargins().map(m => m * this.options.scale)
486
+ }
487
+
488
+ public getOriginalMargins(): number[] {
489
+ const { margins, paperDirection } = this.options
490
+ return paperDirection === PaperDirection.VERTICAL
491
+ ? margins
492
+ : [margins[1], margins[2], margins[3], margins[0]]
493
+ }
494
+
495
+ public getPageGap(): number {
496
+ return this.options.pageGap * this.options.scale
497
+ }
498
+
499
+ public getOriginalPageGap(): number {
500
+ return this.options.pageGap
501
+ }
502
+
503
+ public getPageNumberBottom(): number {
504
+ const {
505
+ pageNumber: { bottom },
506
+ scale
507
+ } = this.options
508
+ return bottom * scale
509
+ }
510
+
511
+ public getMarginIndicatorSize(): number {
512
+ return this.options.marginIndicatorSize * this.options.scale
513
+ }
514
+
515
+ public getDefaultBasicRowMarginHeight(): number {
516
+ return this.options.defaultBasicRowMarginHeight * this.options.scale
517
+ }
518
+
519
+ public getHighlightMarginHeight(): number {
520
+ return this.options.highlightMarginHeight * this.options.scale
521
+ }
522
+
523
+ public getTdPadding(): IPadding {
524
+ const {
525
+ table: { tdPadding },
526
+ scale
527
+ } = this.options
528
+ return <IPadding>tdPadding.map(m => m * scale)
529
+ }
530
+
531
+ public getContainer(): HTMLDivElement {
532
+ return this.container
533
+ }
534
+
535
+ public getPageContainer(): HTMLDivElement {
536
+ return this.pageContainer
537
+ }
538
+
539
+ public getVisiblePageNoList(): number[] {
540
+ return this.visiblePageNoList
541
+ }
542
+
543
+ public setVisiblePageNoList(payload: number[]) {
544
+ this.visiblePageNoList = payload
545
+ if (this.listener.visiblePageNoListChange) {
546
+ this.listener.visiblePageNoListChange(this.visiblePageNoList)
547
+ }
548
+ if (this.eventBus.isSubscribe('visiblePageNoListChange')) {
549
+ this.eventBus.emit('visiblePageNoListChange', this.visiblePageNoList)
550
+ }
551
+ }
552
+
553
+ public getIntersectionPageNo(): number {
554
+ return this.intersectionPageNo
555
+ }
556
+
557
+ public setIntersectionPageNo(payload: number) {
558
+ this.intersectionPageNo = payload
559
+ if (this.listener.intersectionPageNoChange) {
560
+ this.listener.intersectionPageNoChange(this.intersectionPageNo)
561
+ }
562
+ if (this.eventBus.isSubscribe('intersectionPageNoChange')) {
563
+ this.eventBus.emit('intersectionPageNoChange', this.intersectionPageNo)
564
+ }
565
+ }
566
+
567
+ public getPageNo(): number {
568
+ return this.pageNo
569
+ }
570
+
571
+ public setPageNo(payload: number) {
572
+ this.pageNo = payload
573
+ }
574
+
575
+ public getRenderCount(): number {
576
+ return this.renderCount
577
+ }
578
+
579
+ public getPage(pageNo = -1): HTMLCanvasElement {
580
+ return this.pageList[~pageNo ? pageNo : this.pageNo]
581
+ }
582
+
583
+ public getPageList(): HTMLCanvasElement[] {
584
+ return this.pageList
585
+ }
586
+
587
+ public getPageCount(): number {
588
+ return this.pageList.length
589
+ }
590
+
591
+ public getTableRowList(sourceElementList: IElement[]): IRow[] {
592
+ const positionContext = this.position.getPositionContext()
593
+ const { index, trIndex, tdIndex } = positionContext
594
+ return sourceElementList[index!].trList![trIndex!].tdList[tdIndex!].rowList!
595
+ }
596
+
597
+ public getOriginalRowList() {
598
+ const zoneManager = this.getZone()
599
+ if (zoneManager.isHeaderActive()) {
600
+ return this.header.getRowList()
601
+ }
602
+ if (zoneManager.isFooterActive()) {
603
+ return this.footer.getRowList()
604
+ }
605
+ return this.rowList
606
+ }
607
+
608
+ public getRowList(): IRow[] {
609
+ const positionContext = this.position.getPositionContext()
610
+ return positionContext.isTable
611
+ ? this.getTableRowList(this.getOriginalElementList())
612
+ : this.getOriginalRowList()
613
+ }
614
+
615
+ public getPageRowList(): IRow[][] {
616
+ return this.pageRowList
617
+ }
618
+
619
+ public getCtx(): CanvasRenderingContext2D {
620
+ return this.ctxList[this.pageNo]
621
+ }
622
+
623
+ public getOptions(): DeepRequired<IEditorOption> {
624
+ return this.options
625
+ }
626
+
627
+ public getSearch(): Search {
628
+ return this.search
629
+ }
630
+
631
+ public getGroup(): Group {
632
+ return this.group
633
+ }
634
+
635
+ public getArea(): Area {
636
+ return this.area
637
+ }
638
+
639
+ public getBadge(): Badge {
640
+ return this.badge
641
+ }
642
+
643
+ public getHistoryManager(): HistoryManager {
644
+ return this.historyManager
645
+ }
646
+
647
+ public getPosition(): Position {
648
+ return this.position
649
+ }
650
+
651
+ public getZone(): Zone {
652
+ return this.zone
653
+ }
654
+
655
+ public getRange(): RangeManager {
656
+ return this.range
657
+ }
658
+
659
+ public getLineBreakParticle(): LineBreakParticle {
660
+ return this.lineBreakParticle
661
+ }
662
+
663
+ public getTextParticle(): TextParticle {
664
+ return this.textParticle
665
+ }
666
+
667
+ public getHeaderElementList(): IElement[] {
668
+ return this.header.getElementList()
669
+ }
670
+
671
+ public getTableElementList(sourceElementList: IElement[]): IElement[] {
672
+ const positionContext = this.position.getPositionContext()
673
+ const { index, trIndex, tdIndex } = positionContext
674
+ return (
675
+ sourceElementList[index!].trList?.[trIndex!].tdList[tdIndex!].value || []
676
+ )
677
+ }
678
+
679
+ public getElementList(): IElement[] {
680
+ const positionContext = this.position.getPositionContext()
681
+ const elementList = this.getOriginalElementList()
682
+ return positionContext.isTable
683
+ ? this.getTableElementList(elementList)
684
+ : elementList
685
+ }
686
+
687
+ public getMainElementList(): IElement[] {
688
+ const positionContext = this.position.getPositionContext()
689
+ return positionContext.isTable
690
+ ? this.getTableElementList(this.elementList)
691
+ : this.elementList
692
+ }
693
+
694
+ public getOriginalElementList() {
695
+ const zoneManager = this.getZone()
696
+ if (zoneManager.isHeaderActive()) {
697
+ return this.getHeaderElementList()
698
+ }
699
+ if (zoneManager.isFooterActive()) {
700
+ return this.getFooterElementList()
701
+ }
702
+ return this.elementList
703
+ }
704
+
705
+ public getOriginalMainElementList(): IElement[] {
706
+ return this.elementList
707
+ }
708
+
709
+ public getFooterElementList(): IElement[] {
710
+ return this.footer.getElementList()
711
+ }
712
+
713
+ public getTd(): ITd | null {
714
+ const positionContext = this.position.getPositionContext()
715
+ const { index, trIndex, tdIndex, isTable } = positionContext
716
+ if (isTable) {
717
+ const elementList = this.getOriginalElementList()
718
+ return elementList[index!].trList![trIndex!].tdList[tdIndex!]
719
+ }
720
+ return null
721
+ }
722
+
723
+ public insertElementList(
724
+ payload: IElement[],
725
+ options: IInsertElementListOption = {}
726
+ ) {
727
+ if (!payload.length || !this.range.getIsCanInput()) return
728
+ const { startIndex, endIndex } = this.range.getRange()
729
+ if (!~startIndex && !~endIndex) return
730
+ const { isSubmitHistory = true } = options
731
+ formatElementList(payload, {
732
+ isHandleFirstElement: false,
733
+ editorOptions: this.options
734
+ })
735
+ let curIndex = -1
736
+ // 判断是否在控件内
737
+ let activeControl = this.control.getActiveControl()
738
+ // 光标在控件内如果当前没有被激活,需要手动激活
739
+ if (!activeControl && this.control.getIsRangeWithinControl()) {
740
+ this.control.initControl()
741
+ activeControl = this.control.getActiveControl()
742
+ }
743
+ if (activeControl && this.control.getIsRangeWithinControl()) {
744
+ curIndex = activeControl.setValue(payload, undefined, {
745
+ isIgnoreDisabledRule: true
746
+ })
747
+ this.control.emitControlContentChange()
748
+ } else {
749
+ const elementList = this.getElementList()
750
+ const isCollapsed = startIndex === endIndex
751
+ const start = startIndex + 1
752
+ if (!isCollapsed) {
753
+ this.spliceElementList(elementList, start, endIndex - startIndex)
754
+ }
755
+ this.spliceElementList(elementList, start, 0, payload)
756
+ curIndex = startIndex + payload.length
757
+ // 列表前如有换行符则删除-因为列表内已存在
758
+ const preElement = elementList[start - 1]
759
+ if (
760
+ payload[0].listId &&
761
+ preElement &&
762
+ !preElement.listId &&
763
+ preElement?.value === ZERO &&
764
+ (!preElement.type || preElement.type === ElementType.TEXT)
765
+ ) {
766
+ elementList.splice(startIndex, 1)
767
+ curIndex -= 1
768
+ }
769
+ }
770
+ if (~curIndex) {
771
+ this.range.setRange(curIndex, curIndex)
772
+ this.render({
773
+ curIndex,
774
+ isSubmitHistory
775
+ })
776
+ }
777
+ }
778
+
779
+ public appendElementList(
780
+ elementList: IElement[],
781
+ options: IAppendElementListOption = {}
782
+ ) {
783
+ if (!elementList.length) return
784
+ formatElementList(elementList, {
785
+ isHandleFirstElement: false,
786
+ editorOptions: this.options
787
+ })
788
+ let curIndex: number
789
+ const { isPrepend, isSubmitHistory = true } = options
790
+ if (isPrepend) {
791
+ this.elementList.splice(1, 0, ...elementList)
792
+ curIndex = elementList.length
793
+ } else {
794
+ this.elementList.push(...elementList)
795
+ curIndex = this.elementList.length - 1
796
+ }
797
+ this.range.setRange(curIndex, curIndex)
798
+ this.render({
799
+ curIndex,
800
+ isSubmitHistory
801
+ })
802
+ }
803
+
804
+ public spliceElementList(
805
+ elementList: IElement[],
806
+ start: number,
807
+ deleteCount: number,
808
+ items?: IElement[],
809
+ options?: ISpliceElementListOption
810
+ ) {
811
+ const { isIgnoreDeletedRule = false } = options || {}
812
+ const { group, modeRule } = this.options
813
+ if (deleteCount > 0) {
814
+ // 当最后元素与开始元素列表信息不一致时:清除当前列表信息
815
+ const endIndex = start + deleteCount
816
+ const endElement = elementList[endIndex]
817
+ const endElementListId = endElement?.listId
818
+ if (
819
+ endElementListId &&
820
+ elementList[start - 1]?.listId !== endElementListId
821
+ ) {
822
+ let startIndex = endIndex
823
+ while (startIndex < elementList.length) {
824
+ const curElement = elementList[startIndex]
825
+ if (
826
+ curElement.listId !== endElementListId ||
827
+ curElement.value === ZERO
828
+ ) {
829
+ break
830
+ }
831
+ delete curElement.listId
832
+ delete curElement.listType
833
+ delete curElement.listStyle
834
+ startIndex++
835
+ }
836
+ }
837
+ // 非明确忽略删除规则 && 非设计模式 && 非光标在控件内(控件内控制) =》 校验删除规则
838
+ if (
839
+ !isIgnoreDeletedRule &&
840
+ !this.isDesignMode() &&
841
+ !this.control.getIsRangeWithinControl()
842
+ ) {
843
+ const tdDeletable = this.getTd()?.deletable
844
+ let deleteIndex = endIndex - 1
845
+ while (deleteIndex >= start) {
846
+ const deleteElement = elementList[deleteIndex]
847
+ if (
848
+ deleteElement?.hide ||
849
+ deleteElement?.control?.hide ||
850
+ deleteElement?.area?.hide ||
851
+ (tdDeletable !== false &&
852
+ deleteElement?.control?.deletable !== false &&
853
+ (!deleteElement.controlId ||
854
+ this.mode !== EditorMode.FORM ||
855
+ !modeRule[this.mode].controlDeletableDisabled) &&
856
+ deleteElement?.title?.deletable !== false &&
857
+ (group.deletable !== false || !deleteElement.groupIds?.length) &&
858
+ (deleteElement?.area?.deletable !== false ||
859
+ deleteElement?.areaIndex !== 0))
860
+ ) {
861
+ elementList.splice(deleteIndex, 1)
862
+ }
863
+ deleteIndex--
864
+ }
865
+ } else {
866
+ elementList.splice(start, deleteCount)
867
+ }
868
+ }
869
+ // 循环添加,避免使用解构影响性能
870
+ if (items?.length) {
871
+ for (let i = 0; i < items.length; i++) {
872
+ elementList.splice(start + i, 0, items[i])
873
+ }
874
+ }
875
+ }
876
+
877
+ public getCanvasEvent(): CanvasEvent {
878
+ return this.canvasEvent
879
+ }
880
+
881
+ public getGlobalEvent(): GlobalEvent {
882
+ return this.globalEvent
883
+ }
884
+
885
+ public getListener(): Listener {
886
+ return this.listener
887
+ }
888
+
889
+ public getEventBus(): EventBus<EventBusMap> {
890
+ return this.eventBus
891
+ }
892
+
893
+ public getOverride(): Override {
894
+ return this.override
895
+ }
896
+
897
+ public getCursor(): Cursor {
898
+ return this.cursor
899
+ }
900
+
901
+ public getPreviewer(): Previewer {
902
+ return this.previewer
903
+ }
904
+
905
+ public getImageParticle(): ImageParticle {
906
+ return this.imageParticle
907
+ }
908
+
909
+ public getTableTool(): TableTool {
910
+ return this.tableTool
911
+ }
912
+
913
+ public getTableOperate(): TableOperate {
914
+ return this.tableOperate
915
+ }
916
+
917
+ public getTableParticle(): TableParticle {
918
+ return this.tableParticle
919
+ }
920
+
921
+ public getHeader(): Header {
922
+ return this.header
923
+ }
924
+
925
+ public getFooter(): Footer {
926
+ return this.footer
927
+ }
928
+
929
+ public getHyperlinkParticle(): HyperlinkParticle {
930
+ return this.hyperlinkParticle
931
+ }
932
+
933
+ public getDateParticle(): DateParticle {
934
+ return this.dateParticle
935
+ }
936
+
937
+ public getListParticle(): ListParticle {
938
+ return this.listParticle
939
+ }
940
+
941
+ public getCheckboxParticle(): CheckboxParticle {
942
+ return this.checkboxParticle
943
+ }
944
+
945
+ public getRadioParticle(): RadioParticle {
946
+ return this.radioParticle
947
+ }
948
+
949
+ public getControl(): Control {
950
+ return this.control
951
+ }
952
+
953
+ public getWorkerManager(): WorkerManager {
954
+ return this.workerManager
955
+ }
956
+
957
+ public getImageObserver(): ImageObserver {
958
+ return this.imageObserver
959
+ }
960
+
961
+ public getI18n(): I18n {
962
+ return this.i18n
963
+ }
964
+
965
+ public getGraffiti(): Graffiti {
966
+ return this.graffiti
967
+ }
968
+
969
+ public getRowCount(): number {
970
+ return this.getRowList().length
971
+ }
972
+
973
+ public async getDataURL(payload: IGetImageOption = {}): Promise<string[]> {
974
+ const { pixelRatio, mode } = payload
975
+ // 放大像素比
976
+ if (pixelRatio) {
977
+ this.setPagePixelRatio(pixelRatio)
978
+ }
979
+ // 不同模式
980
+ const currentMode = this.mode
981
+ const isSwitchMode = !!mode && currentMode !== mode
982
+ if (isSwitchMode) {
983
+ this.setMode(mode)
984
+ }
985
+ this.render({
986
+ isLazy: false,
987
+ isCompute: false,
988
+ isSetCursor: false,
989
+ isSubmitHistory: false
990
+ })
991
+ await this.imageObserver.allSettled()
992
+ const dataUrlList = this.pageList.map(c => c.toDataURL())
993
+ // 还原
994
+ if (pixelRatio) {
995
+ this.setPagePixelRatio(null)
996
+ }
997
+ if (isSwitchMode) {
998
+ this.setMode(currentMode)
999
+ }
1000
+ return dataUrlList
1001
+ }
1002
+
1003
+ public getPainterStyle(): IElementStyle | null {
1004
+ return this.painterStyle && Object.keys(this.painterStyle).length
1005
+ ? this.painterStyle
1006
+ : null
1007
+ }
1008
+
1009
+ public getPainterOptions(): IPainterOption | null {
1010
+ return this.painterOptions
1011
+ }
1012
+
1013
+ public setPainterStyle(
1014
+ payload: IElementStyle | null,
1015
+ options?: IPainterOption
1016
+ ) {
1017
+ this.painterStyle = payload
1018
+ this.painterOptions = options || null
1019
+ if (this.getPainterStyle()) {
1020
+ this.pageList.forEach(c => (c.style.cursor = 'copy'))
1021
+ }
1022
+ }
1023
+
1024
+ public setDefaultRange() {
1025
+ if (!this.elementList.length) return
1026
+ setTimeout(() => {
1027
+ const curIndex = this.elementList.length - 1
1028
+ this.range.setRange(curIndex, curIndex)
1029
+ this.range.setRangeStyle()
1030
+ })
1031
+ }
1032
+
1033
+ public getIsPagingMode(): boolean {
1034
+ return this.options.pageMode === PageMode.PAGING
1035
+ }
1036
+
1037
+ public setPageMode(payload: PageMode) {
1038
+ if (!payload || this.options.pageMode === payload) return
1039
+ this.options.pageMode = payload
1040
+ // 纸张大小重置
1041
+ if (payload === PageMode.PAGING) {
1042
+ const { height } = this.options
1043
+ const dpr = this.getPagePixelRatio()
1044
+ const canvas = this.pageList[0]
1045
+ canvas.style.height = `${height}px`
1046
+ canvas.height = height * dpr
1047
+ // canvas尺寸发生变化,上下文被重置
1048
+ this._initPageContext(this.ctxList[0])
1049
+ } else {
1050
+ // 连页模式:移除懒加载监听&清空页眉页脚计算数据
1051
+ this._disconnectLazyRender()
1052
+ this.header.recovery()
1053
+ this.footer.recovery()
1054
+ this.zone.setZone(EditorZone.MAIN)
1055
+ }
1056
+ const { startIndex } = this.range.getRange()
1057
+ const isCollapsed = this.range.getIsCollapsed()
1058
+ this.render({
1059
+ isSetCursor: true,
1060
+ curIndex: startIndex,
1061
+ isSubmitHistory: false
1062
+ })
1063
+ // 重新定位避免事件监听丢失
1064
+ if (!isCollapsed) {
1065
+ this.cursor.drawCursor({
1066
+ isShow: false
1067
+ })
1068
+ }
1069
+ // 回调
1070
+ setTimeout(() => {
1071
+ if (this.listener.pageModeChange) {
1072
+ this.listener.pageModeChange(payload)
1073
+ }
1074
+ if (this.eventBus.isSubscribe('pageModeChange')) {
1075
+ this.eventBus.emit('pageModeChange', payload)
1076
+ }
1077
+ })
1078
+ }
1079
+
1080
+ public setPageScale(payload: number) {
1081
+ const dpr = this.getPagePixelRatio()
1082
+ this.options.scale = payload
1083
+ const width = this.getWidth()
1084
+ const height = this.getHeight()
1085
+ this.container.style.width = `${width}px`
1086
+ this.pageList.forEach((p, i) => {
1087
+ p.width = width * dpr
1088
+ p.height = height * dpr
1089
+ p.style.width = `${width}px`
1090
+ p.style.height = `${height}px`
1091
+ p.style.marginBottom = `${this.getPageGap()}px`
1092
+ this._initPageContext(this.ctxList[i])
1093
+ })
1094
+ const cursorPosition = this.position.getCursorPosition()
1095
+ this.render({
1096
+ isSubmitHistory: false,
1097
+ isSetCursor: !!cursorPosition,
1098
+ curIndex: cursorPosition?.index
1099
+ })
1100
+ if (this.listener.pageScaleChange) {
1101
+ this.listener.pageScaleChange(payload)
1102
+ }
1103
+ if (this.eventBus.isSubscribe('pageScaleChange')) {
1104
+ this.eventBus.emit('pageScaleChange', payload)
1105
+ }
1106
+ }
1107
+
1108
+ public getPagePixelRatio(): number {
1109
+ return this.pagePixelRatio || window.devicePixelRatio
1110
+ }
1111
+
1112
+ public setPagePixelRatio(payload: number | null) {
1113
+ if (
1114
+ (!this.pagePixelRatio && payload === window.devicePixelRatio) ||
1115
+ payload === this.pagePixelRatio
1116
+ ) {
1117
+ return
1118
+ }
1119
+ this.pagePixelRatio = payload
1120
+ this.setPageDevicePixel()
1121
+ }
1122
+
1123
+ public setPageDevicePixel() {
1124
+ const dpr = this.getPagePixelRatio()
1125
+ const width = this.getWidth()
1126
+ const height = this.getHeight()
1127
+ this.pageList.forEach((p, i) => {
1128
+ p.width = width * dpr
1129
+ p.height = height * dpr
1130
+ this._initPageContext(this.ctxList[i])
1131
+ })
1132
+ this.render({
1133
+ isSubmitHistory: false,
1134
+ isSetCursor: false
1135
+ })
1136
+ }
1137
+
1138
+ public setPaperSize(width: number, height: number) {
1139
+ this.options.width = width
1140
+ this.options.height = height
1141
+ const dpr = this.getPagePixelRatio()
1142
+ const realWidth = this.getWidth()
1143
+ const realHeight = this.getHeight()
1144
+ this.container.style.width = `${realWidth}px`
1145
+ this.pageList.forEach((p, i) => {
1146
+ p.width = realWidth * dpr
1147
+ p.height = realHeight * dpr
1148
+ p.style.width = `${realWidth}px`
1149
+ p.style.height = `${realHeight}px`
1150
+ this._initPageContext(this.ctxList[i])
1151
+ })
1152
+ this.render({
1153
+ isSubmitHistory: false,
1154
+ isSetCursor: false
1155
+ })
1156
+ }
1157
+
1158
+ public setPaperDirection(payload: PaperDirection) {
1159
+ const dpr = this.getPagePixelRatio()
1160
+ this.options.paperDirection = payload
1161
+ const width = this.getWidth()
1162
+ const height = this.getHeight()
1163
+ this.container.style.width = `${width}px`
1164
+ this.pageList.forEach((p, i) => {
1165
+ p.width = width * dpr
1166
+ p.height = height * dpr
1167
+ p.style.width = `${width}px`
1168
+ p.style.height = `${height}px`
1169
+ this._initPageContext(this.ctxList[i])
1170
+ })
1171
+ this.render({
1172
+ isSubmitHistory: false,
1173
+ isSetCursor: false
1174
+ })
1175
+ }
1176
+
1177
+ public setPaperMargin(payload: IMargin) {
1178
+ this.options.margins = payload
1179
+ this.render({
1180
+ isSubmitHistory: false,
1181
+ isSetCursor: false
1182
+ })
1183
+ }
1184
+
1185
+ public getOriginValue(
1186
+ options: IGetOriginValueOption = {}
1187
+ ): Required<IEditorData> {
1188
+ const { pageNo } = options
1189
+ let mainElementList = this.elementList
1190
+ if (
1191
+ Number.isInteger(pageNo) &&
1192
+ pageNo! >= 0 &&
1193
+ pageNo! < this.pageRowList.length
1194
+ ) {
1195
+ mainElementList = this.pageRowList[pageNo!].flatMap(
1196
+ row => row.elementList
1197
+ )
1198
+ }
1199
+ const data: Required<IEditorData> = {
1200
+ header: this.getHeaderElementList(),
1201
+ main: mainElementList,
1202
+ footer: this.getFooterElementList(),
1203
+ graffiti: this.graffiti.getValue()
1204
+ }
1205
+ return data
1206
+ }
1207
+
1208
+ public getValue(options: IGetValueOption = {}): IEditorResult {
1209
+ const originData = this.getOriginValue(options)
1210
+ const { extraPickAttrs } = options
1211
+ const data: IEditorData = {
1212
+ header: zipElementList(originData.header, {
1213
+ extraPickAttrs
1214
+ }),
1215
+ main: zipElementList(originData.main, {
1216
+ extraPickAttrs,
1217
+ isClassifyArea: true
1218
+ }),
1219
+ footer: zipElementList(originData.footer, {
1220
+ extraPickAttrs
1221
+ }),
1222
+ graffiti: originData.graffiti
1223
+ }
1224
+ return {
1225
+ version,
1226
+ data,
1227
+ options: deepClone(this.options)
1228
+ }
1229
+ }
1230
+
1231
+ public setValue(payload: Partial<IEditorData>, options?: ISetValueOption) {
1232
+ const { header, main, footer } = deepClone(payload)
1233
+ if (!header && !main && !footer) return
1234
+ const { isSetCursor = false } = options || {}
1235
+ const pageComponentData = [header, main, footer]
1236
+ pageComponentData.forEach(data => {
1237
+ if (!data) return
1238
+ formatElementList(data, {
1239
+ editorOptions: this.options,
1240
+ isForceCompensation: true
1241
+ })
1242
+ })
1243
+ this.setEditorData({
1244
+ header,
1245
+ main,
1246
+ footer
1247
+ })
1248
+ // 渲染&计算&清空历史记录
1249
+ this.historyManager.recovery()
1250
+ const curIndex = isSetCursor
1251
+ ? main?.length
1252
+ ? main.length - 1
1253
+ : 0
1254
+ : undefined
1255
+ if (curIndex !== undefined) {
1256
+ this.range.setRange(curIndex, curIndex)
1257
+ }
1258
+ this.render({
1259
+ curIndex,
1260
+ isSetCursor,
1261
+ isFirstRender: true
1262
+ })
1263
+ }
1264
+
1265
+ public setEditorData(payload: Partial<Omit<IEditorData, 'graffiti'>>) {
1266
+ const { header, main, footer } = payload
1267
+ if (header) {
1268
+ this.header.setElementList(header)
1269
+ }
1270
+ if (main) {
1271
+ this.elementList = main
1272
+ }
1273
+ if (footer) {
1274
+ this.footer.setElementList(footer)
1275
+ }
1276
+ }
1277
+
1278
+ private _wrapContainer(rootContainer: HTMLElement): HTMLDivElement {
1279
+ const container = document.createElement('div')
1280
+ rootContainer.append(container)
1281
+ return container
1282
+ }
1283
+
1284
+ private _formatContainer() {
1285
+ // 容器宽度需跟随纸张宽度
1286
+ this.container.style.position = 'relative'
1287
+ this.container.style.width = `${this.getWidth()}px`
1288
+ this.container.setAttribute(EDITOR_COMPONENT, EditorComponent.MAIN)
1289
+ }
1290
+
1291
+ private _createPageContainer(): HTMLDivElement {
1292
+ const pageContainer = document.createElement('div')
1293
+ pageContainer.classList.add(`${EDITOR_PREFIX}-page-container`)
1294
+ this.container.append(pageContainer)
1295
+ return pageContainer
1296
+ }
1297
+
1298
+ private _createPage(pageNo: number) {
1299
+ const width = this.getWidth()
1300
+ const height = this.getHeight()
1301
+ const canvas = document.createElement('canvas')
1302
+ canvas.style.width = `${width}px`
1303
+ canvas.style.height = `${height}px`
1304
+ canvas.style.display = 'block'
1305
+ canvas.style.backgroundColor = '#ffffff'
1306
+ canvas.style.marginBottom = `${this.getPageGap()}px`
1307
+ canvas.setAttribute('data-index', String(pageNo))
1308
+ this.pageContainer.append(canvas)
1309
+ // 调整分辨率
1310
+ const dpr = this.getPagePixelRatio()
1311
+ canvas.width = width * dpr
1312
+ canvas.height = height * dpr
1313
+ canvas.style.cursor = 'text'
1314
+ const ctx = canvas.getContext('2d')!
1315
+ // 初始化上下文配置
1316
+ this._initPageContext(ctx)
1317
+ // 缓存上下文
1318
+ this.pageList.push(canvas)
1319
+ this.ctxList.push(ctx)
1320
+ }
1321
+
1322
+ private _initPageContext(ctx: CanvasRenderingContext2D) {
1323
+ const dpr = this.getPagePixelRatio()
1324
+ ctx.scale(dpr, dpr)
1325
+ // 重置以下属性是因部分浏览器(chrome)会应用css样式
1326
+ ctx.letterSpacing = '0px'
1327
+ ctx.wordSpacing = '0px'
1328
+ ctx.direction = 'ltr'
1329
+ }
1330
+
1331
+ public getElementFont(el: IElement, scale = 1): string {
1332
+ const { defaultSize, defaultFont } = this.options
1333
+ const font = el.font || defaultFont
1334
+ const size = el.actualSize || el.size || defaultSize
1335
+ return `${el.italic ? 'italic ' : ''}${el.bold ? 'bold ' : ''}${
1336
+ size * scale
1337
+ }px ${font}`
1338
+ }
1339
+
1340
+ public getElementSize(el: IElement) {
1341
+ return el.actualSize || el.size || this.options.defaultSize
1342
+ }
1343
+
1344
+ public getElementRowMargin(el: IElement) {
1345
+ const {
1346
+ defaultSize,
1347
+ defaultBasicRowMarginHeight,
1348
+ defaultRowMargin,
1349
+ scale
1350
+ } = this.options
1351
+ // 字体在12-30之间,行间距不变,小于12按比例缩小,大于30按比例放大
1352
+ const fontSize = el.size || defaultSize
1353
+ let ratio = 1
1354
+ if (fontSize < 12) {
1355
+ ratio = fontSize / 12
1356
+ } else if (fontSize > 30) {
1357
+ ratio = 1 + (fontSize - 30) / 30
1358
+ }
1359
+ return (
1360
+ defaultBasicRowMarginHeight *
1361
+ ratio *
1362
+ (el.rowMargin ?? defaultRowMargin) *
1363
+ scale
1364
+ )
1365
+ }
1366
+
1367
+ public computeRowList(payload: IComputeRowListPayload) {
1368
+ const {
1369
+ innerWidth,
1370
+ elementList,
1371
+ isPagingMode = false,
1372
+ isFromTable = false,
1373
+ startX = 0,
1374
+ startY = 0,
1375
+ pageHeight = 0,
1376
+ mainOuterHeight = 0,
1377
+ surroundElementList = []
1378
+ } = payload
1379
+ const {
1380
+ defaultSize,
1381
+ scale,
1382
+ imgCaption,
1383
+ table: { tdPadding, defaultTrMinHeight },
1384
+ defaultTabWidth
1385
+ } = this.options
1386
+ const defaultBasicRowMarginHeight = this.getDefaultBasicRowMarginHeight()
1387
+ const canvas = document.createElement('canvas')
1388
+ const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
1389
+ // 计算列表偏移宽度
1390
+ const listStyleMap = this.listParticle.computeListStyle(ctx, elementList)
1391
+ const rowList: IRow[] = []
1392
+ if (elementList.length) {
1393
+ rowList.push({
1394
+ width: 0,
1395
+ height: 0,
1396
+ ascent: 0,
1397
+ elementList: [],
1398
+ startIndex: 0,
1399
+ rowIndex: 0,
1400
+ rowFlex: elementList?.[0]?.rowFlex || elementList?.[1]?.rowFlex
1401
+ })
1402
+ }
1403
+ // 起始位置及页码计算
1404
+ let x = startX
1405
+ let y = startY
1406
+ let pageNo = 0
1407
+ // 列表位置
1408
+ let listId: string | undefined
1409
+ let listIndex = 0
1410
+ // 控件最小宽度
1411
+ let controlRealWidth = 0
1412
+ for (let i = 0; i < elementList.length; i++) {
1413
+ const curRow: IRow = rowList[rowList.length - 1]
1414
+ const element = elementList[i]
1415
+ const rowMargin = this.getElementRowMargin(element)
1416
+ const metrics: IElementMetrics = {
1417
+ width: 0,
1418
+ height: 0,
1419
+ boundingBoxAscent: 0,
1420
+ boundingBoxDescent: 0
1421
+ }
1422
+ // 实际可用宽度
1423
+ const offsetX =
1424
+ curRow.offsetX ||
1425
+ (element.listId && listStyleMap.get(element.listId)) ||
1426
+ 0
1427
+ const availableWidth = innerWidth - offsetX
1428
+ // 增加起始位置坐标偏移量
1429
+ const isStartElement = curRow.elementList.length === 1
1430
+ x += isStartElement ? offsetX : 0
1431
+ y += isStartElement ? curRow.offsetY || 0 : 0
1432
+ if (
1433
+ (element.hide || element.control?.hide || element.area?.hide) &&
1434
+ !this.isDesignMode()
1435
+ ) {
1436
+ const preElement = curRow.elementList[curRow.elementList.length - 1]
1437
+ metrics.height =
1438
+ preElement?.metrics.height || this.options.defaultSize * scale
1439
+ metrics.boundingBoxAscent = preElement?.metrics.boundingBoxAscent || 0
1440
+ metrics.boundingBoxDescent = preElement?.metrics.boundingBoxDescent || 0
1441
+ } else if (
1442
+ element.type === ElementType.IMAGE ||
1443
+ element.type === ElementType.LATEX
1444
+ ) {
1445
+ // 浮动图片无需计算数据
1446
+ if (
1447
+ element.imgDisplay === ImageDisplay.SURROUND ||
1448
+ element.imgDisplay === ImageDisplay.FLOAT_TOP ||
1449
+ element.imgDisplay === ImageDisplay.FLOAT_BOTTOM
1450
+ ) {
1451
+ metrics.width = 0
1452
+ metrics.height = 0
1453
+ metrics.boundingBoxDescent = 0
1454
+ } else {
1455
+ const elementWidth = element.width! * scale
1456
+ const elementHeight = element.height! * scale
1457
+ // 图片超出尺寸后自适应(图片大小大于可用宽度时)
1458
+ if (elementWidth > availableWidth) {
1459
+ const adaptiveHeight =
1460
+ (elementHeight * availableWidth) / elementWidth
1461
+ element.width = availableWidth / scale
1462
+ element.height = adaptiveHeight / scale
1463
+ metrics.width = availableWidth
1464
+ metrics.height = adaptiveHeight
1465
+ metrics.boundingBoxDescent = adaptiveHeight
1466
+ } else {
1467
+ metrics.width = elementWidth
1468
+ metrics.height = elementHeight
1469
+ metrics.boundingBoxDescent = elementHeight
1470
+ }
1471
+ // 增加题注高度
1472
+ if (element.imgCaption?.value) {
1473
+ const fontSize = element.imgCaption.size || imgCaption.size
1474
+ const captionTop = element.imgCaption.top ?? imgCaption.top
1475
+ const captionHeight = (fontSize + captionTop) * scale
1476
+ metrics.boundingBoxAscent += captionHeight
1477
+ }
1478
+ }
1479
+ } else if (element.type === ElementType.TABLE) {
1480
+ const tdPaddingWidth = tdPadding[1] + tdPadding[3]
1481
+ const tdPaddingHeight = tdPadding[0] + tdPadding[2]
1482
+ // 表格分页处理进度:https://github.com/Hufe921/canvas-editor/issues/41
1483
+ // 查看后续表格是否属于同一个源表格-存在即合并
1484
+ if (element.pagingId) {
1485
+ let tableIndex = i + 1
1486
+ let combineCount = 0
1487
+ while (tableIndex < elementList.length) {
1488
+ const nextElement = elementList[tableIndex]
1489
+ if (nextElement.pagingId === element.pagingId) {
1490
+ const nexTrList = nextElement.trList!.filter(
1491
+ tr => !tr.pagingRepeat
1492
+ )
1493
+ element.trList!.push(...nexTrList)
1494
+ element.height! += nextElement.height!
1495
+ tableIndex++
1496
+ combineCount++
1497
+ } else {
1498
+ break
1499
+ }
1500
+ }
1501
+ if (combineCount) {
1502
+ elementList.splice(i + 1, combineCount)
1503
+ }
1504
+ }
1505
+ element.pagingIndex = element.pagingIndex ?? 0
1506
+ const trList = element.trList!
1507
+ // 计算前移除上一次的高度
1508
+ for (let t = 0; t < trList.length; t++) {
1509
+ const tr = trList[t]
1510
+ tr.height = tr.minHeight || defaultTrMinHeight
1511
+ tr.minHeight = tr.height
1512
+ }
1513
+ // 计算表格行列
1514
+ this.tableParticle.computeRowColInfo(element)
1515
+ // 计算表格内元素信息
1516
+ for (let t = 0; t < trList.length; t++) {
1517
+ const tr = trList[t]
1518
+ for (let d = 0; d < tr.tdList.length; d++) {
1519
+ const td = tr.tdList[d]
1520
+ const rowList = this.computeRowList({
1521
+ innerWidth: (td.width! - tdPaddingWidth) * scale,
1522
+ elementList: td.value,
1523
+ isFromTable: true,
1524
+ isPagingMode
1525
+ })
1526
+ const rowHeight = rowList.reduce((pre, cur) => pre + cur.height, 0)
1527
+ td.rowList = rowList
1528
+ // 移除缩放导致的行高变化-渲染时会进行缩放调整
1529
+ const curTdHeight = rowHeight / scale + tdPaddingHeight
1530
+ // 内容高度大于当前单元格高度需增加
1531
+ if (td.height! < curTdHeight) {
1532
+ const extraHeight = curTdHeight - td.height!
1533
+ const changeTr = trList[t + td.rowspan - 1]
1534
+ changeTr.height += extraHeight
1535
+ changeTr.tdList.forEach(changeTd => {
1536
+ changeTd.height! += extraHeight
1537
+ if (!changeTd.realHeight) {
1538
+ changeTd.realHeight = changeTd.height!
1539
+ } else {
1540
+ changeTd.realHeight! += extraHeight
1541
+ }
1542
+ })
1543
+ }
1544
+ // 当前单元格最小高度及真实高度(包含跨列)
1545
+ let curTdMinHeight = 0
1546
+ let curTdRealHeight = 0
1547
+ let i = 0
1548
+ while (i < td.rowspan) {
1549
+ const curTr = trList[i + t] || trList[t]
1550
+ curTdMinHeight += curTr.minHeight!
1551
+ curTdRealHeight += curTr.height!
1552
+ i++
1553
+ }
1554
+ td.realMinHeight = curTdMinHeight
1555
+ td.realHeight = curTdRealHeight
1556
+ td.mainHeight = curTdHeight
1557
+ }
1558
+ }
1559
+ // 单元格高度大于实际内容高度需减少
1560
+ const reduceTrList = this.tableParticle.getTrListGroupByCol(trList)
1561
+ for (let t = 0; t < reduceTrList.length; t++) {
1562
+ const tr = reduceTrList[t]
1563
+ let reduceHeight = -1
1564
+ for (let d = 0; d < tr.tdList.length; d++) {
1565
+ const td = tr.tdList[d]
1566
+ const curTdRealHeight = td.realHeight!
1567
+ const curTdHeight = td.mainHeight!
1568
+ const curTdMinHeight = td.realMinHeight!
1569
+ // 获取最大可减少高度
1570
+ const curReduceHeight =
1571
+ curTdHeight < curTdMinHeight
1572
+ ? curTdRealHeight - curTdMinHeight
1573
+ : curTdRealHeight - curTdHeight
1574
+ if (!~reduceHeight || curReduceHeight < reduceHeight) {
1575
+ reduceHeight = curReduceHeight
1576
+ }
1577
+ }
1578
+ if (reduceHeight > 0) {
1579
+ const changeTr = trList[t]
1580
+ changeTr.height -= reduceHeight
1581
+ changeTr.tdList.forEach(changeTd => {
1582
+ changeTd.height! -= reduceHeight
1583
+ changeTd.realHeight! -= reduceHeight
1584
+ })
1585
+ }
1586
+ }
1587
+ // 需要重新计算表格内值
1588
+ this.tableParticle.computeRowColInfo(element)
1589
+ // 计算出表格高度
1590
+ const tableHeight = this.tableParticle.getTableHeight(element)
1591
+ const tableWidth = this.tableParticle.getTableWidth(element)
1592
+ element.width = tableWidth
1593
+ element.height = tableHeight
1594
+ const elementWidth = tableWidth * scale
1595
+ const elementHeight = tableHeight * scale
1596
+ metrics.width = elementWidth
1597
+ metrics.height = elementHeight
1598
+ metrics.boundingBoxDescent = elementHeight
1599
+ metrics.boundingBoxAscent = -rowMargin
1600
+ // 后一个元素也是表格则移除行间距
1601
+ if (elementList[i + 1]?.type === ElementType.TABLE) {
1602
+ metrics.boundingBoxAscent -= rowMargin
1603
+ }
1604
+ // 表格分页处理(拆分表格)
1605
+ if (isPagingMode) {
1606
+ const height = this.getHeight()
1607
+ const marginHeight = this.getMainOuterHeight()
1608
+ let curPagePreHeight = marginHeight
1609
+ for (let r = 0; r < rowList.length; r++) {
1610
+ const row = rowList[r]
1611
+ const rowOffsetY = row.offsetY || 0
1612
+ if (
1613
+ row.height + curPagePreHeight + rowOffsetY > height ||
1614
+ rowList[r - 1]?.isPageBreak
1615
+ ) {
1616
+ curPagePreHeight = marginHeight + row.height + rowOffsetY
1617
+ } else {
1618
+ curPagePreHeight += row.height + rowOffsetY
1619
+ }
1620
+ }
1621
+ // 当前剩余高度是否能容下当前表格第一行(可拆分)的高度,排除掉表头类型
1622
+ // 前面元素为换页符时重新计算高度
1623
+ const rowMarginHeight = rowMargin * 2 * scale
1624
+ const firstTrHeight = element.trList![0].height! * scale
1625
+ if (
1626
+ curPagePreHeight + firstTrHeight + rowMarginHeight > height ||
1627
+ (element.pagingIndex !== 0 && element.trList![0].pagingRepeat) ||
1628
+ elementList[i - 1]?.type === ElementType.PAGE_BREAK
1629
+ ) {
1630
+ // 无可拆分行则切换至新页
1631
+ curPagePreHeight = marginHeight
1632
+ }
1633
+ // 表格高度超过页面高度开始截断行
1634
+ if (curPagePreHeight + rowMarginHeight + elementHeight > height) {
1635
+ const trList = element.trList!
1636
+ // 计算需要移除的行数
1637
+ let deleteStart = 0
1638
+ let deleteCount = 0
1639
+ let preTrHeight = 0
1640
+ // 大于一行时再拆分避免循环
1641
+ if (trList.length > 1) {
1642
+ for (let r = 0; r < trList.length; r++) {
1643
+ const tr = trList[r]
1644
+ const trHeight = tr.height * scale
1645
+ if (
1646
+ curPagePreHeight + rowMarginHeight + preTrHeight + trHeight >
1647
+ height
1648
+ ) {
1649
+ // 当前行存在跨行中断-暂时忽略分页
1650
+ const rowColCount = tr.tdList.reduce(
1651
+ (pre, cur) => pre + cur.colspan,
1652
+ 0
1653
+ )
1654
+ if (element.colgroup?.length !== rowColCount) {
1655
+ deleteCount = 0
1656
+ }
1657
+ break
1658
+ } else {
1659
+ deleteStart = r + 1
1660
+ deleteCount = trList.length - deleteStart
1661
+ preTrHeight += trHeight
1662
+ }
1663
+ }
1664
+ }
1665
+ if (deleteCount) {
1666
+ const cloneTrList = trList.splice(deleteStart, deleteCount)
1667
+ const cloneTrHeight = cloneTrList.reduce(
1668
+ (pre, cur) => pre + cur.height,
1669
+ 0
1670
+ )
1671
+ const cloneTrRealHeight = cloneTrHeight * scale
1672
+ const pagingId = element.pagingId || getUUID()
1673
+ element.pagingId = pagingId
1674
+ element.height -= cloneTrHeight
1675
+ metrics.height -= cloneTrRealHeight
1676
+ metrics.boundingBoxDescent -= cloneTrRealHeight
1677
+ // 追加拆分表格
1678
+ const cloneElement = deepClone(element)
1679
+ cloneElement.pagingId = pagingId
1680
+ cloneElement.pagingIndex = element.pagingIndex! + 1
1681
+ // 处理分页重复表头
1682
+ const repeatTrList = trList.filter(tr => tr.pagingRepeat)
1683
+ if (repeatTrList.length) {
1684
+ const cloneRepeatTrList = deepClone(repeatTrList)
1685
+ cloneRepeatTrList.forEach(tr => (tr.id = getUUID()))
1686
+ cloneTrList.unshift(...cloneRepeatTrList)
1687
+ }
1688
+ cloneElement.trList = cloneTrList
1689
+ cloneElement.id = getUUID()
1690
+ this.spliceElementList(elementList, i + 1, 0, [cloneElement])
1691
+ }
1692
+ }
1693
+ // 表格经过分页处理-需要处理上下文
1694
+ if (element.pagingId) {
1695
+ const positionContext = this.position.getPositionContext()
1696
+ if (positionContext.isTable) {
1697
+ // 查找光标所在表格索引(根据trId搜索)
1698
+ let newPositionContextIndex = -1
1699
+ let newPositionContextTrIndex = -1
1700
+ let tableIndex = i
1701
+ while (tableIndex < elementList.length) {
1702
+ const curElement = elementList[tableIndex]
1703
+ if (curElement.pagingId !== element.pagingId) break
1704
+ const trIndex = curElement.trList!.findIndex(
1705
+ r => r.id === positionContext.trId
1706
+ )
1707
+ if (~trIndex) {
1708
+ newPositionContextIndex = tableIndex
1709
+ newPositionContextTrIndex = trIndex
1710
+ break
1711
+ }
1712
+ tableIndex++
1713
+ }
1714
+ if (~newPositionContextIndex) {
1715
+ positionContext.index = newPositionContextIndex
1716
+ positionContext.trIndex = newPositionContextTrIndex
1717
+ this.position.setPositionContext(positionContext)
1718
+ }
1719
+ }
1720
+ }
1721
+ }
1722
+ } else if (element.type === ElementType.SEPARATOR) {
1723
+ const {
1724
+ separator: { lineWidth: defaultLineWidth }
1725
+ } = this.options
1726
+ const lineWidth = element.lineWidth || defaultLineWidth
1727
+ element.width = availableWidth / scale
1728
+ metrics.width = availableWidth
1729
+ metrics.height = lineWidth * scale
1730
+ metrics.boundingBoxAscent = -rowMargin
1731
+ metrics.boundingBoxDescent = -rowMargin + metrics.height
1732
+ } else if (element.type === ElementType.PAGE_BREAK) {
1733
+ element.width = availableWidth / scale
1734
+ metrics.width = availableWidth
1735
+ metrics.height = defaultSize
1736
+ } else if (
1737
+ element.type === ElementType.RADIO ||
1738
+ element.controlComponent === ControlComponent.RADIO
1739
+ ) {
1740
+ const { width, height, gap } = this.options.radio
1741
+ const elementWidth = width + gap * 2
1742
+ element.width = elementWidth
1743
+ metrics.width = elementWidth * scale
1744
+ metrics.height = height * scale
1745
+ } else if (
1746
+ element.type === ElementType.CHECKBOX ||
1747
+ element.controlComponent === ControlComponent.CHECKBOX
1748
+ ) {
1749
+ const { width, height, gap } = this.options.checkbox
1750
+ const elementWidth = width + gap * 2
1751
+ element.width = elementWidth
1752
+ metrics.width = elementWidth * scale
1753
+ metrics.height = height * scale
1754
+ } else if (element.type === ElementType.TAB) {
1755
+ metrics.width = defaultTabWidth * scale
1756
+ metrics.height = defaultSize * scale
1757
+ metrics.boundingBoxDescent = 0
1758
+ metrics.boundingBoxAscent =
1759
+ this.textParticle.getBasisWordBoundingBoxAscent(ctx, ctx.font)
1760
+ } else if (element.type === ElementType.BLOCK) {
1761
+ if (!element.width) {
1762
+ metrics.width = availableWidth
1763
+ } else {
1764
+ const elementWidth = element.width * scale
1765
+ metrics.width = Math.min(elementWidth, availableWidth)
1766
+ }
1767
+ metrics.height = element.height! * scale
1768
+ metrics.boundingBoxDescent = metrics.height
1769
+ metrics.boundingBoxAscent = 0
1770
+ } else if (element.type === ElementType.LABEL) {
1771
+ const {
1772
+ defaultSize,
1773
+ label: { defaultPadding }
1774
+ } = this.options
1775
+ ctx.font = this.getElementFont(element)
1776
+ const fontMetrics = this.textParticle.measureText(ctx, element)
1777
+ metrics.width =
1778
+ (fontMetrics.width + defaultPadding[1] + defaultPadding[3]) * scale
1779
+ metrics.height = (element.size || defaultSize) * scale
1780
+ metrics.boundingBoxDescent = 0
1781
+ metrics.boundingBoxAscent =
1782
+ (defaultPadding[0] + fontMetrics.actualBoundingBoxAscent) * scale
1783
+ } else {
1784
+ // 设置上下标真实字体尺寸
1785
+ const size = element.size || defaultSize
1786
+ if (
1787
+ element.type === ElementType.SUPERSCRIPT ||
1788
+ element.type === ElementType.SUBSCRIPT
1789
+ ) {
1790
+ element.actualSize = Math.ceil(size * 0.6)
1791
+ }
1792
+ metrics.height = (element.actualSize || size) * scale
1793
+ ctx.font = this.getElementFont(element)
1794
+ const fontMetrics = this.textParticle.measureText(ctx, element)
1795
+ metrics.width = fontMetrics.width * scale
1796
+ if (element.letterSpacing) {
1797
+ metrics.width += element.letterSpacing * scale
1798
+ }
1799
+ // 使用基于字体的基准度量以确保一致的行高,避免字符特定度量导致的布局跳动
1800
+ const basisMetrics = this.textParticle.measureBasisWord(
1801
+ ctx,
1802
+ element.font!
1803
+ )
1804
+ metrics.boundingBoxAscent = basisMetrics.actualBoundingBoxAscent * scale
1805
+ metrics.boundingBoxDescent =
1806
+ basisMetrics.actualBoundingBoxDescent * scale
1807
+ if (element.type === ElementType.SUPERSCRIPT) {
1808
+ metrics.boundingBoxAscent += metrics.height / 2
1809
+ } else if (element.type === ElementType.SUBSCRIPT) {
1810
+ metrics.boundingBoxDescent += metrics.height / 2
1811
+ }
1812
+ }
1813
+ const ascent =
1814
+ !element.hide &&
1815
+ ((element.imgDisplay !== ImageDisplay.INLINE &&
1816
+ element.type === ElementType.IMAGE) ||
1817
+ element.type === ElementType.LATEX)
1818
+ ? metrics.height + rowMargin
1819
+ : metrics.boundingBoxAscent + rowMargin
1820
+ const height =
1821
+ rowMargin +
1822
+ metrics.boundingBoxAscent +
1823
+ metrics.boundingBoxDescent +
1824
+ rowMargin
1825
+ const rowElement: IRowElement = Object.assign(element, {
1826
+ metrics,
1827
+ left: 0,
1828
+ style: this.getElementFont(element, scale)
1829
+ })
1830
+ // 暂时只考虑非换行场景:控件开始时统计宽度,结束时消费宽度及还原
1831
+ if (rowElement.control?.minWidth) {
1832
+ if (rowElement.controlComponent) {
1833
+ controlRealWidth += metrics.width
1834
+ }
1835
+ if (rowElement.controlComponent === ControlComponent.POSTFIX) {
1836
+ // 设置最小宽度控件属性(字符偏移量)
1837
+ this.control.setMinWidthControlInfo({
1838
+ row: curRow,
1839
+ rowElement,
1840
+ availableWidth,
1841
+ controlRealWidth
1842
+ })
1843
+ controlRealWidth = 0
1844
+ }
1845
+ }
1846
+ // 超过限定宽度
1847
+ const preElement = elementList[i - 1]
1848
+ let nextElement = elementList[i + 1]
1849
+ // 累计行宽 + 当前元素宽度 + 排版宽度(英文单词整体宽度 + 后面标点符号宽度)
1850
+ let curRowWidth = curRow.width + metrics.width
1851
+ if (this.options.wordBreak === WordBreak.BREAK_WORD) {
1852
+ if (
1853
+ (!preElement?.type || preElement?.type === ElementType.TEXT) &&
1854
+ (!element.type || element.type === ElementType.TEXT)
1855
+ ) {
1856
+ // 英文单词
1857
+ const word = `${preElement?.value || ''}${element.value}`
1858
+ if (this.WORD_LIKE_REG.test(word)) {
1859
+ const { width, endElement } = this.textParticle.measureWord(
1860
+ ctx,
1861
+ elementList,
1862
+ i
1863
+ )
1864
+ // 后面存在元素 && 单词宽度大于行可用宽度,无需折行
1865
+ const wordWidth = width * scale
1866
+ if (endElement && wordWidth <= availableWidth) {
1867
+ curRowWidth += wordWidth
1868
+ nextElement = endElement
1869
+ }
1870
+ }
1871
+ // 标点符号
1872
+ const punctuationWidth = this.textParticle.measurePunctuationWidth(
1873
+ ctx,
1874
+ nextElement
1875
+ )
1876
+ curRowWidth += punctuationWidth * scale
1877
+ }
1878
+ }
1879
+ // 列表信息
1880
+ if (element.listId) {
1881
+ if (element.listId !== listId) {
1882
+ listIndex = 0
1883
+ } else if (element.value === ZERO && !element.listWrap) {
1884
+ listIndex++
1885
+ }
1886
+ }
1887
+ listId = element.listId
1888
+ // 计算四周环绕导致的元素偏移量
1889
+ const surroundPosition = this.position.setSurroundPosition({
1890
+ pageNo,
1891
+ rowElement,
1892
+ row: curRow,
1893
+ rowElementRect: {
1894
+ x,
1895
+ y,
1896
+ height,
1897
+ width: metrics.width
1898
+ },
1899
+ availableWidth,
1900
+ surroundElementList
1901
+ })
1902
+ x = surroundPosition.x
1903
+ curRowWidth += surroundPosition.rowIncreaseWidth
1904
+ x += metrics.width
1905
+ // 是否强制换行
1906
+ const isForceBreak =
1907
+ element.type === ElementType.SEPARATOR ||
1908
+ element.type === ElementType.TABLE ||
1909
+ preElement?.type === ElementType.TABLE ||
1910
+ preElement?.type === ElementType.BLOCK ||
1911
+ element.type === ElementType.BLOCK ||
1912
+ preElement?.imgDisplay === ImageDisplay.INLINE ||
1913
+ element.imgDisplay === ImageDisplay.INLINE ||
1914
+ preElement?.listId !== element.listId ||
1915
+ (preElement?.areaId !== element.areaId && !element.area?.hide) ||
1916
+ (element.control?.flexDirection === FlexDirection.COLUMN &&
1917
+ (element.controlComponent === ControlComponent.CHECKBOX ||
1918
+ element.controlComponent === ControlComponent.RADIO) &&
1919
+ preElement?.controlComponent === ControlComponent.VALUE) ||
1920
+ (i !== 0 && element.value === ZERO && !element.area?.hide)
1921
+ // 是否宽度不足导致换行
1922
+ const isWidthNotEnough = curRowWidth > availableWidth
1923
+ const isWrap = isForceBreak || isWidthNotEnough
1924
+ // 新行数据处理
1925
+ if (isWrap) {
1926
+ const row: IRow = {
1927
+ width: metrics.width,
1928
+ height,
1929
+ startIndex: i,
1930
+ elementList: [rowElement],
1931
+ ascent,
1932
+ rowIndex: curRow.rowIndex + 1,
1933
+ rowFlex: elementList[i]?.rowFlex || elementList[i + 1]?.rowFlex,
1934
+ isPageBreak: element.type === ElementType.PAGE_BREAK
1935
+ }
1936
+ // 控件缩进
1937
+ if (
1938
+ rowElement.controlComponent !== ControlComponent.PREFIX &&
1939
+ rowElement.control?.indentation === ControlIndentation.VALUE_START
1940
+ ) {
1941
+ // 查找到非前缀的第一个元素位置
1942
+ const preStartIndex = curRow.elementList.findIndex(
1943
+ el =>
1944
+ el.controlId === rowElement.controlId &&
1945
+ el.controlComponent !== ControlComponent.PREFIX
1946
+ )
1947
+ if (~preStartIndex) {
1948
+ const preRowPositionList = this.position.computeRowPosition({
1949
+ row: curRow,
1950
+ innerWidth: this.getInnerWidth()
1951
+ })
1952
+ const valueStartPosition = preRowPositionList[preStartIndex]
1953
+ if (valueStartPosition) {
1954
+ row.offsetX = valueStartPosition.coordinate.leftTop[0]
1955
+ }
1956
+ }
1957
+ }
1958
+ // 列表缩进
1959
+ if (element.listId) {
1960
+ row.isList = true
1961
+ row.offsetX = listStyleMap.get(element.listId!)
1962
+ row.listIndex = listIndex
1963
+ }
1964
+ // Y轴偏移量
1965
+ row.offsetY =
1966
+ !isFromTable &&
1967
+ element.area?.top &&
1968
+ element.areaId !== elementList[i - 1]?.areaId
1969
+ ? element.area.top * scale
1970
+ : 0
1971
+ rowList.push(row)
1972
+ } else {
1973
+ curRow.width += metrics.width
1974
+ // 减小块元素前第一行空行行高
1975
+ if (
1976
+ i === 0 &&
1977
+ (getIsBlockElement(elementList[1]) || !!elementList[1]?.areaId)
1978
+ ) {
1979
+ curRow.height = defaultBasicRowMarginHeight
1980
+ curRow.ascent = defaultBasicRowMarginHeight
1981
+ } else if (curRow.height < height) {
1982
+ curRow.height = height
1983
+ curRow.ascent = ascent
1984
+ }
1985
+ curRow.elementList.push(rowElement)
1986
+ }
1987
+ // 行结束时逻辑
1988
+ if (isWrap || i === elementList.length - 1) {
1989
+ // 换行原因:宽度不足
1990
+ curRow.isWidthNotEnough = isWidthNotEnough && !isForceBreak
1991
+ // 两端对齐、分散对齐
1992
+ if (
1993
+ !curRow.isSurround &&
1994
+ (preElement?.rowFlex === RowFlex.JUSTIFY ||
1995
+ (preElement?.rowFlex === RowFlex.ALIGNMENT &&
1996
+ curRow.isWidthNotEnough))
1997
+ ) {
1998
+ // 忽略换行符及尾部元素间隔设置
1999
+ const rowElementList =
2000
+ curRow.elementList[0]?.value === ZERO
2001
+ ? curRow.elementList.slice(1)
2002
+ : curRow.elementList
2003
+ const gap =
2004
+ (availableWidth - curRow.width) / (rowElementList.length - 1)
2005
+ for (let e = 0; e < rowElementList.length - 1; e++) {
2006
+ const el = rowElementList[e]
2007
+ el.metrics.width += gap
2008
+ }
2009
+ curRow.width = availableWidth
2010
+ }
2011
+ }
2012
+ // 重新计算坐标、页码、下一行首行元素环绕交叉
2013
+ if (isWrap) {
2014
+ x = startX
2015
+ y += curRow.height
2016
+ if (
2017
+ isPagingMode &&
2018
+ !isFromTable &&
2019
+ pageHeight &&
2020
+ (y - startY + mainOuterHeight + height > pageHeight ||
2021
+ element.type === ElementType.PAGE_BREAK)
2022
+ ) {
2023
+ y = startY
2024
+ // 删除多余四周环绕型元素
2025
+ deleteSurroundElementList(surroundElementList, pageNo)
2026
+ pageNo += 1
2027
+ }
2028
+ // 计算下一行第一个元素是否存在环绕交叉
2029
+ rowElement.left = 0
2030
+ const nextRow = rowList[rowList.length - 1]
2031
+ const surroundPosition = this.position.setSurroundPosition({
2032
+ pageNo,
2033
+ rowElement,
2034
+ row: nextRow,
2035
+ rowElementRect: {
2036
+ x,
2037
+ y,
2038
+ height,
2039
+ width: metrics.width
2040
+ },
2041
+ availableWidth,
2042
+ surroundElementList
2043
+ })
2044
+ x = surroundPosition.x
2045
+ x += metrics.width
2046
+ }
2047
+ }
2048
+ return rowList
2049
+ }
2050
+
2051
+ private _computePageList(): IRow[][] {
2052
+ const pageRowList: IRow[][] = [[]]
2053
+ const {
2054
+ pageMode,
2055
+ pageNumber: { maxPageNo }
2056
+ } = this.options
2057
+ const height = this.getHeight()
2058
+ const marginHeight = this.getMainOuterHeight()
2059
+ let pageHeight = marginHeight
2060
+ let pageNo = 0
2061
+ if (pageMode === PageMode.CONTINUITY) {
2062
+ pageRowList[0] = this.rowList
2063
+ // 重置高度
2064
+ pageHeight += this.rowList.reduce(
2065
+ (pre, cur) => pre + cur.height + (cur.offsetY || 0),
2066
+ 0
2067
+ )
2068
+ const dpr = this.getPagePixelRatio()
2069
+ const pageDom = this.pageList[0]
2070
+ const pageDomHeight = Number(pageDom.style.height.replace('px', ''))
2071
+ if (pageHeight > pageDomHeight) {
2072
+ pageDom.style.height = `${pageHeight}px`
2073
+ pageDom.height = pageHeight * dpr
2074
+ } else {
2075
+ const reduceHeight = pageHeight < height ? height : pageHeight
2076
+ pageDom.style.height = `${reduceHeight}px`
2077
+ pageDom.height = reduceHeight * dpr
2078
+ }
2079
+ this._initPageContext(this.ctxList[0])
2080
+ } else {
2081
+ for (let i = 0; i < this.rowList.length; i++) {
2082
+ const row = this.rowList[i]
2083
+ const rowOffsetY = row.offsetY || 0
2084
+ if (
2085
+ row.height + rowOffsetY + pageHeight > height ||
2086
+ this.rowList[i - 1]?.isPageBreak
2087
+ ) {
2088
+ if (Number.isInteger(maxPageNo) && pageNo >= maxPageNo!) {
2089
+ this.elementList = this.elementList.slice(0, row.startIndex)
2090
+ break
2091
+ }
2092
+ pageHeight = marginHeight + row.height + rowOffsetY
2093
+ pageRowList.push([row])
2094
+ pageNo++
2095
+ } else {
2096
+ pageHeight += row.height + rowOffsetY
2097
+ pageRowList[pageNo].push(row)
2098
+ }
2099
+ }
2100
+ }
2101
+ return pageRowList
2102
+ }
2103
+
2104
+ private _drawHighlight(
2105
+ ctx: CanvasRenderingContext2D,
2106
+ payload: IDrawRowPayload
2107
+ ) {
2108
+ const { rowList, positionList, elementList } = payload
2109
+ const marginHeight = this.getDefaultBasicRowMarginHeight()
2110
+ const highlightMarginHeight = this.getHighlightMarginHeight()
2111
+ for (let i = 0; i < rowList.length; i++) {
2112
+ const curRow = rowList[i]
2113
+ for (let j = 0; j < curRow.elementList.length; j++) {
2114
+ const element = curRow.elementList[j]
2115
+ const preElement = curRow.elementList[j - 1]
2116
+ // 高亮配置:元素 > 控件配置
2117
+ const highlight =
2118
+ element.highlight ||
2119
+ this.control.getControlHighlight(elementList, curRow.startIndex + j)
2120
+ if (highlight) {
2121
+ // 高亮元素相连需立即绘制,并记录下一元素坐标
2122
+ if (
2123
+ preElement &&
2124
+ preElement.highlight &&
2125
+ preElement.highlight !== element.highlight
2126
+ ) {
2127
+ this.highlight.render(ctx)
2128
+ }
2129
+ // 当前元素位置信息记录
2130
+ const {
2131
+ coordinate: {
2132
+ leftTop: [x, y]
2133
+ }
2134
+ } = positionList[curRow.startIndex + j]
2135
+ // 元素向左偏移量
2136
+ const offsetX = element.left || 0
2137
+ this.highlight.recordFillInfo(
2138
+ ctx,
2139
+ x - offsetX,
2140
+ y + marginHeight - highlightMarginHeight, // 先减去行margin,再加上高亮margin
2141
+ element.metrics.width + offsetX,
2142
+ curRow.height - 2 * marginHeight + 2 * highlightMarginHeight,
2143
+ highlight
2144
+ )
2145
+ } else if (preElement?.highlight) {
2146
+ // 之前是高亮元素,当前不是需立即绘制
2147
+ this.highlight.render(ctx)
2148
+ }
2149
+ }
2150
+ this.highlight.render(ctx)
2151
+ }
2152
+ }
2153
+
2154
+ public drawRow(ctx: CanvasRenderingContext2D, payload: IDrawRowPayload) {
2155
+ // 优先绘制高亮元素
2156
+ this._drawHighlight(ctx, payload)
2157
+ // 绘制元素、下划线、删除线、选区
2158
+ const {
2159
+ scale,
2160
+ table: { tdPadding },
2161
+ group,
2162
+ lineBreak,
2163
+ whiteSpace
2164
+ } = this.options
2165
+ const {
2166
+ rowList,
2167
+ pageNo,
2168
+ elementList,
2169
+ positionList,
2170
+ startIndex,
2171
+ zone,
2172
+ isDrawLineBreak = !lineBreak.disabled,
2173
+ isDrawWhiteSpace = !whiteSpace.disabled
2174
+ } = payload
2175
+ const isPrintMode = this.isPrintMode()
2176
+ const isGraffitiMode = this.isGraffitiMode()
2177
+ const { isCrossRowCol, tableId } = this.range.getRange()
2178
+ let index = startIndex
2179
+ for (let i = 0; i < rowList.length; i++) {
2180
+ const curRow = rowList[i]
2181
+ // 选区绘制记录
2182
+ const rangeRecord: IElementFillRect = {
2183
+ x: 0,
2184
+ y: 0,
2185
+ width: 0,
2186
+ height: 0
2187
+ }
2188
+ let tableRangeElement: IElement | null = null
2189
+ for (let j = 0; j < curRow.elementList.length; j++) {
2190
+ const element = curRow.elementList[j]
2191
+ const metrics = element.metrics
2192
+ // 当前元素位置信息
2193
+ const {
2194
+ ascent: offsetY,
2195
+ coordinate: {
2196
+ leftTop: [x, y]
2197
+ }
2198
+ } = positionList[curRow.startIndex + j]
2199
+ const preElement = curRow.elementList[j - 1]
2200
+ // 元素绘制
2201
+ if (
2202
+ (element.hide || element.control?.hide || element.area?.hide) &&
2203
+ !this.isDesignMode()
2204
+ ) {
2205
+ // 控件隐藏时不绘制
2206
+ this.textParticle.complete()
2207
+ } else if (element.type === ElementType.IMAGE) {
2208
+ this.textParticle.complete()
2209
+ // 浮动图片单独绘制
2210
+ if (
2211
+ element.imgDisplay !== ImageDisplay.SURROUND &&
2212
+ element.imgDisplay !== ImageDisplay.FLOAT_TOP &&
2213
+ element.imgDisplay !== ImageDisplay.FLOAT_BOTTOM
2214
+ ) {
2215
+ this.imageParticle.render(ctx, element, x, y + offsetY)
2216
+ }
2217
+ } else if (element.type === ElementType.LATEX) {
2218
+ this.textParticle.complete()
2219
+ this.laTexParticle.render(ctx, element, x, y + offsetY)
2220
+ } else if (element.type === ElementType.TABLE) {
2221
+ if (isCrossRowCol) {
2222
+ rangeRecord.x = x
2223
+ rangeRecord.y = y
2224
+ tableRangeElement = element
2225
+ }
2226
+ this.tableParticle.render(ctx, element, x, y)
2227
+ } else if (element.type === ElementType.HYPERLINK) {
2228
+ this.textParticle.complete()
2229
+ this.hyperlinkParticle.render(ctx, element, x, y + offsetY)
2230
+ } else if (element.type === ElementType.LABEL) {
2231
+ this.textParticle.complete()
2232
+ this.labelParticle.render(ctx, element, x, y + offsetY)
2233
+ } else if (element.type === ElementType.DATE) {
2234
+ const nextElement = curRow.elementList[j + 1]
2235
+ // 释放之前的
2236
+ if (!preElement || preElement.dateId !== element.dateId) {
2237
+ this.textParticle.complete()
2238
+ }
2239
+ this.textParticle.record(ctx, element, x, y + offsetY)
2240
+ if (!nextElement || nextElement.dateId !== element.dateId) {
2241
+ // 手动触发渲染
2242
+ this.textParticle.complete()
2243
+ }
2244
+ } else if (element.type === ElementType.SUPERSCRIPT) {
2245
+ this.textParticle.complete()
2246
+ this.superscriptParticle.render(ctx, element, x, y + offsetY)
2247
+ } else if (element.type === ElementType.SUBSCRIPT) {
2248
+ this.underline.render(ctx)
2249
+ this.textParticle.complete()
2250
+ this.subscriptParticle.render(ctx, element, x, y + offsetY)
2251
+ } else if (element.type === ElementType.SEPARATOR) {
2252
+ this.separatorParticle.render(ctx, element, x, y)
2253
+ } else if (element.type === ElementType.PAGE_BREAK) {
2254
+ if (this.mode !== EditorMode.CLEAN && !isPrintMode) {
2255
+ this.pageBreakParticle.render(ctx, element, x, y)
2256
+ }
2257
+ } else if (
2258
+ element.type === ElementType.CHECKBOX ||
2259
+ element.controlComponent === ControlComponent.CHECKBOX
2260
+ ) {
2261
+ this.textParticle.complete()
2262
+ this.checkboxParticle.render({
2263
+ ctx,
2264
+ x,
2265
+ y: y + offsetY,
2266
+ index: j,
2267
+ row: curRow
2268
+ })
2269
+ } else if (
2270
+ element.type === ElementType.RADIO ||
2271
+ element.controlComponent === ControlComponent.RADIO
2272
+ ) {
2273
+ this.textParticle.complete()
2274
+ this.radioParticle.render({
2275
+ ctx,
2276
+ x,
2277
+ y: y + offsetY,
2278
+ index: j,
2279
+ row: curRow
2280
+ })
2281
+ } else if (element.type === ElementType.TAB) {
2282
+ this.textParticle.complete()
2283
+ } else if (
2284
+ element.rowFlex === RowFlex.ALIGNMENT ||
2285
+ element.rowFlex === RowFlex.JUSTIFY
2286
+ ) {
2287
+ // 如果是两端对齐,因canvas目前不支持letterSpacing需单独绘制文本
2288
+ this.textParticle.record(ctx, element, x, y + offsetY)
2289
+ this.textParticle.complete()
2290
+ } else if (element.type === ElementType.BLOCK) {
2291
+ this.textParticle.complete()
2292
+ this.blockParticle.render(ctx, pageNo, element, x, y + offsetY)
2293
+ } else {
2294
+ // 如果当前元素设置左偏移,则上一元素立即绘制
2295
+ if (element.left) {
2296
+ this.textParticle.complete()
2297
+ }
2298
+ this.textParticle.record(ctx, element, x, y + offsetY)
2299
+ // 如果设置字宽、字间距、标点符号(避免浏览器排版缩小间距)需单独绘制
2300
+ if (
2301
+ element.width ||
2302
+ element.letterSpacing ||
2303
+ PUNCTUATION_REG.test(element.value)
2304
+ ) {
2305
+ this.textParticle.complete()
2306
+ }
2307
+ }
2308
+ // 换行符绘制
2309
+ if (
2310
+ isDrawLineBreak &&
2311
+ !isPrintMode &&
2312
+ this.mode !== EditorMode.CLEAN &&
2313
+ !curRow.isWidthNotEnough &&
2314
+ j === curRow.elementList.length - 1
2315
+ ) {
2316
+ this.lineBreakParticle.render(ctx, element, x, y + curRow.height / 2)
2317
+ }
2318
+ // 空白符绘制
2319
+ if (isDrawWhiteSpace && WHITE_SPACE_REG.test(element.value)) {
2320
+ this.whiteSpaceParticle.render(ctx, element, x, y + curRow.height / 2)
2321
+ }
2322
+ // 边框绘制(目前仅支持控件)
2323
+ if (element.control?.border) {
2324
+ // 不同控件边框立刻绘制
2325
+ if (
2326
+ preElement?.control?.border &&
2327
+ preElement.controlId !== element.controlId
2328
+ ) {
2329
+ this.control.drawBorder(ctx)
2330
+ }
2331
+ // 当前元素位置信息记录
2332
+ const rowMargin = this.getElementRowMargin(element)
2333
+ this.control.recordBorderInfo(
2334
+ x,
2335
+ y + rowMargin,
2336
+ element.metrics.width,
2337
+ curRow.height - 2 * rowMargin
2338
+ )
2339
+ } else if (preElement?.control?.border) {
2340
+ this.control.drawBorder(ctx)
2341
+ }
2342
+ // 下划线记录
2343
+ if (element.underline || element.control?.underline) {
2344
+ // 下标元素下划线单独绘制
2345
+ if (
2346
+ preElement?.type === ElementType.SUBSCRIPT &&
2347
+ element.type !== ElementType.SUBSCRIPT
2348
+ ) {
2349
+ this.underline.render(ctx)
2350
+ }
2351
+ // 行间距
2352
+ const rowMargin = this.getElementRowMargin(element)
2353
+ // 元素向左偏移量
2354
+ const offsetX = element.left || 0
2355
+ // 下标元素y轴偏移值
2356
+ let offsetY = 0
2357
+ if (element.type === ElementType.SUBSCRIPT) {
2358
+ offsetY = this.subscriptParticle.getOffsetY(element)
2359
+ }
2360
+ // 占位符不参与颜色计算
2361
+ const color = element.control?.underline
2362
+ ? this.options.underlineColor
2363
+ : element.color
2364
+ this.underline.recordFillInfo(
2365
+ ctx,
2366
+ x - offsetX,
2367
+ y + curRow.height - rowMargin + offsetY,
2368
+ metrics.width + offsetX,
2369
+ 0,
2370
+ color,
2371
+ element.textDecoration?.style
2372
+ )
2373
+ } else if (preElement?.underline || preElement?.control?.underline) {
2374
+ this.underline.render(ctx)
2375
+ }
2376
+ // 删除线记录
2377
+ if (element.strikeout) {
2378
+ // 仅文本类元素支持删除线
2379
+ if (!element.type || TEXTLIKE_ELEMENT_TYPE.includes(element.type)) {
2380
+ // 字体大小不同时需立即绘制
2381
+ if (
2382
+ preElement &&
2383
+ ((preElement.type === ElementType.SUBSCRIPT &&
2384
+ element.type !== ElementType.SUBSCRIPT) ||
2385
+ (preElement.type === ElementType.SUPERSCRIPT &&
2386
+ element.type !== ElementType.SUPERSCRIPT) ||
2387
+ this.getElementSize(preElement) !==
2388
+ this.getElementSize(element))
2389
+ ) {
2390
+ this.strikeout.render(ctx)
2391
+ }
2392
+ // 基线文字测量信息
2393
+ const standardMetrics = this.textParticle.measureBasisWord(
2394
+ ctx,
2395
+ this.getElementFont(element)
2396
+ )
2397
+ // 文字渲染位置 + 基线文字下偏移量 - 一半文字高度
2398
+ let adjustY =
2399
+ y +
2400
+ offsetY +
2401
+ standardMetrics.actualBoundingBoxDescent * scale -
2402
+ metrics.height / 2
2403
+ // 上下标位置调整
2404
+ if (element.type === ElementType.SUBSCRIPT) {
2405
+ adjustY += this.subscriptParticle.getOffsetY(element)
2406
+ } else if (element.type === ElementType.SUPERSCRIPT) {
2407
+ adjustY += this.superscriptParticle.getOffsetY(element)
2408
+ }
2409
+ this.strikeout.recordFillInfo(ctx, x, adjustY, metrics.width)
2410
+ }
2411
+ } else if (preElement?.strikeout) {
2412
+ this.strikeout.render(ctx)
2413
+ }
2414
+ // 选区记录
2415
+ const {
2416
+ zone: currentZone,
2417
+ startIndex,
2418
+ endIndex
2419
+ } = this.range.getRange()
2420
+ if (
2421
+ currentZone === zone &&
2422
+ startIndex !== endIndex &&
2423
+ startIndex <= index &&
2424
+ index <= endIndex
2425
+ ) {
2426
+ const positionContext = this.position.getPositionContext()
2427
+ // 表格需限定上下文
2428
+ if (
2429
+ (!positionContext.isTable && !element.tdId) ||
2430
+ positionContext.tdId === element.tdId
2431
+ ) {
2432
+ // 从行尾开始-绘制最小宽度
2433
+ if (startIndex === index) {
2434
+ const nextElement = elementList[startIndex + 1]
2435
+ if (nextElement && nextElement.value === ZERO) {
2436
+ rangeRecord.x = x + metrics.width
2437
+ rangeRecord.y = y
2438
+ rangeRecord.height = curRow.height
2439
+ rangeRecord.width += this.options.rangeMinWidth
2440
+ }
2441
+ } else {
2442
+ let rangeWidth = metrics.width
2443
+ // 最小选区宽度
2444
+ if (rangeWidth === 0 && curRow.elementList.length === 1) {
2445
+ rangeWidth = this.options.rangeMinWidth
2446
+ }
2447
+ // 记录第一次位置、行高
2448
+ if (!rangeRecord.width) {
2449
+ rangeRecord.x = x
2450
+ rangeRecord.y = y
2451
+ rangeRecord.height = curRow.height
2452
+ }
2453
+ rangeRecord.width += rangeWidth
2454
+ }
2455
+ }
2456
+ }
2457
+ // 组信息记录
2458
+ if (!group.disabled && element.groupIds) {
2459
+ this.group.recordFillInfo(element, x, y, metrics.width, curRow.height)
2460
+ }
2461
+ index++
2462
+ // 绘制表格内元素
2463
+ if (element.type === ElementType.TABLE && !element.hide) {
2464
+ const tdPaddingWidth = tdPadding[1] + tdPadding[3]
2465
+ for (let t = 0; t < element.trList!.length; t++) {
2466
+ const tr = element.trList![t]
2467
+ for (let d = 0; d < tr.tdList!.length; d++) {
2468
+ const td = tr.tdList[d]
2469
+ this.drawRow(ctx, {
2470
+ elementList: td.value,
2471
+ positionList: td.positionList!,
2472
+ rowList: td.rowList!,
2473
+ pageNo,
2474
+ startIndex: 0,
2475
+ innerWidth: (td.width! - tdPaddingWidth) * scale,
2476
+ zone,
2477
+ isDrawLineBreak
2478
+ })
2479
+ }
2480
+ }
2481
+ }
2482
+ }
2483
+ // 绘制列表样式
2484
+ if (curRow.isList) {
2485
+ this.listParticle.drawListStyle(
2486
+ ctx,
2487
+ curRow,
2488
+ positionList[curRow.startIndex]
2489
+ )
2490
+ }
2491
+ // 绘制文字、边框、下划线、删除线
2492
+ this.textParticle.complete()
2493
+ this.control.drawBorder(ctx)
2494
+ this.underline.render(ctx)
2495
+ this.strikeout.render(ctx)
2496
+ // 绘制批注样式
2497
+ this.group.render(ctx)
2498
+ // 绘制选区
2499
+ if (!isPrintMode && !isGraffitiMode) {
2500
+ if (rangeRecord.width && rangeRecord.height) {
2501
+ const { x, y, width, height } = rangeRecord
2502
+ this.range.render(ctx, x, y, width, height)
2503
+ }
2504
+ if (
2505
+ isCrossRowCol &&
2506
+ tableRangeElement &&
2507
+ tableRangeElement.id === tableId
2508
+ ) {
2509
+ const {
2510
+ coordinate: {
2511
+ leftTop: [x, y]
2512
+ }
2513
+ } = positionList[curRow.startIndex]
2514
+ this.tableParticle.drawRange(ctx, tableRangeElement, x, y)
2515
+ }
2516
+ }
2517
+ }
2518
+ }
2519
+
2520
+ private _drawFloat(
2521
+ ctx: CanvasRenderingContext2D,
2522
+ payload: IDrawFloatPayload
2523
+ ) {
2524
+ const { scale } = this.options
2525
+ const floatPositionList = this.position.getFloatPositionList()
2526
+ const { imgDisplays, pageNo } = payload
2527
+ for (let e = 0; e < floatPositionList.length; e++) {
2528
+ const floatPosition = floatPositionList[e]
2529
+ const element = floatPosition.element
2530
+ if (
2531
+ (pageNo === floatPosition.pageNo ||
2532
+ floatPosition.zone === EditorZone.HEADER ||
2533
+ floatPosition.zone == EditorZone.FOOTER) &&
2534
+ element.imgDisplay &&
2535
+ imgDisplays.includes(element.imgDisplay) &&
2536
+ element.type === ElementType.IMAGE
2537
+ ) {
2538
+ const imgFloatPosition = element.imgFloatPosition!
2539
+ this.imageParticle.render(
2540
+ ctx,
2541
+ element,
2542
+ imgFloatPosition.x * scale,
2543
+ imgFloatPosition.y * scale
2544
+ )
2545
+ }
2546
+ }
2547
+ }
2548
+
2549
+ private _clearPage(pageNo: number) {
2550
+ const ctx = this.ctxList[pageNo]
2551
+ const pageDom = this.pageList[pageNo]
2552
+ ctx.clearRect(
2553
+ 0,
2554
+ 0,
2555
+ Math.max(pageDom.width, this.getWidth()),
2556
+ Math.max(pageDom.height, this.getHeight())
2557
+ )
2558
+ this.blockParticle.clear()
2559
+ }
2560
+
2561
+ private _drawPage(payload: IDrawPagePayload) {
2562
+ const { elementList, positionList, rowList, pageNo } = payload
2563
+ const {
2564
+ inactiveAlpha,
2565
+ pageMode,
2566
+ header,
2567
+ footer,
2568
+ pageNumber,
2569
+ lineNumber,
2570
+ pageBorder
2571
+ } = this.options
2572
+ const isPrintMode = this.mode === EditorMode.PRINT
2573
+ const innerWidth = this.getInnerWidth()
2574
+ const ctx = this.ctxList[pageNo]
2575
+ // 判断当前激活区域-非正文区域时元素透明度降低
2576
+ ctx.globalAlpha = !this.zone.isMainActive() ? inactiveAlpha : 1
2577
+ this._clearPage(pageNo)
2578
+ // 绘制背景
2579
+ if (
2580
+ !isPrintMode ||
2581
+ !this.options.modeRule[EditorMode.PRINT]?.backgroundDisabled
2582
+ ) {
2583
+ this.background.render(ctx, pageNo)
2584
+ }
2585
+ // 绘制区域
2586
+ if (!isPrintMode) {
2587
+ this.area.render(ctx, pageNo)
2588
+ }
2589
+ // 绘制水印
2590
+ if (pageMode !== PageMode.CONTINUITY && this.options.watermark.data) {
2591
+ this.waterMark.render(ctx, pageNo)
2592
+ }
2593
+ // 绘制页边距
2594
+ if (!isPrintMode) {
2595
+ this.margin.render(ctx, pageNo)
2596
+ }
2597
+ // 渲染衬于文字下方元素
2598
+ this._drawFloat(ctx, {
2599
+ pageNo,
2600
+ imgDisplays: [ImageDisplay.FLOAT_BOTTOM]
2601
+ })
2602
+ // 控件高亮
2603
+ if (!isPrintMode) {
2604
+ this.control.renderHighlightList(ctx, pageNo)
2605
+ }
2606
+ // 渲染元素
2607
+ const index = rowList[0]?.startIndex
2608
+ this.drawRow(ctx, {
2609
+ elementList,
2610
+ positionList,
2611
+ rowList,
2612
+ pageNo,
2613
+ startIndex: index,
2614
+ innerWidth,
2615
+ zone: EditorZone.MAIN
2616
+ })
2617
+ if (this.getIsPagingMode()) {
2618
+ // 绘制页眉
2619
+ if (!header.disabled) {
2620
+ this.header.render(ctx, pageNo)
2621
+ }
2622
+ // 绘制页码
2623
+ if (!pageNumber.disabled) {
2624
+ this.pageNumber.render(ctx, pageNo)
2625
+ }
2626
+ // 绘制页脚
2627
+ if (!footer.disabled) {
2628
+ this.footer.render(ctx, pageNo)
2629
+ }
2630
+ }
2631
+ // 渲染浮于文字上方元素
2632
+ this._drawFloat(ctx, {
2633
+ pageNo,
2634
+ imgDisplays: [ImageDisplay.FLOAT_TOP, ImageDisplay.SURROUND]
2635
+ })
2636
+ // 搜索匹配绘制
2637
+ if (!isPrintMode && this.search.getSearchKeyword()) {
2638
+ this.search.render(ctx, pageNo)
2639
+ }
2640
+ // 绘制空白占位符
2641
+ if (this.elementList.length <= 1 && !this.elementList[0]?.listId) {
2642
+ this.placeholder.render(ctx)
2643
+ }
2644
+ // 渲染行数
2645
+ if (!lineNumber.disabled) {
2646
+ this.lineNumber.render(ctx, pageNo)
2647
+ }
2648
+ // 绘制页面边框
2649
+ if (!pageBorder.disabled) {
2650
+ this.pageBorder.render(ctx)
2651
+ }
2652
+ // 绘制签章
2653
+ this.badge.render(ctx, pageNo)
2654
+ // 绘制涂鸦
2655
+ if (this.isGraffitiMode()) {
2656
+ this.graffiti.render(ctx, pageNo)
2657
+ }
2658
+ }
2659
+
2660
+ private _disconnectLazyRender() {
2661
+ this.lazyRenderIntersectionObserver?.disconnect()
2662
+ }
2663
+
2664
+ private _lazyRender() {
2665
+ const positionList = this.position.getOriginalMainPositionList()
2666
+ const elementList = this.getOriginalMainElementList()
2667
+ this._disconnectLazyRender()
2668
+ this.lazyRenderIntersectionObserver = new IntersectionObserver(entries => {
2669
+ entries.forEach(entry => {
2670
+ if (entry.isIntersecting) {
2671
+ const index = Number((<HTMLCanvasElement>entry.target).dataset.index)
2672
+ this._drawPage({
2673
+ elementList,
2674
+ positionList,
2675
+ rowList: this.pageRowList[index],
2676
+ pageNo: index
2677
+ })
2678
+ }
2679
+ })
2680
+ })
2681
+ this.pageList.forEach(el => {
2682
+ this.lazyRenderIntersectionObserver!.observe(el)
2683
+ })
2684
+ }
2685
+
2686
+ private _immediateRender() {
2687
+ const positionList = this.position.getOriginalMainPositionList()
2688
+ const elementList = this.getOriginalMainElementList()
2689
+ for (let i = 0; i < this.pageRowList.length; i++) {
2690
+ this._drawPage({
2691
+ elementList,
2692
+ positionList,
2693
+ rowList: this.pageRowList[i],
2694
+ pageNo: i
2695
+ })
2696
+ }
2697
+ }
2698
+
2699
+ public render(payload?: IDrawOption) {
2700
+ this.renderCount++
2701
+ const { header, footer } = this.options
2702
+ const {
2703
+ isSubmitHistory = true,
2704
+ isSetCursor = true,
2705
+ isCompute = true,
2706
+ isLazy = true,
2707
+ isInit = false,
2708
+ isSourceHistory = false,
2709
+ isFirstRender = false
2710
+ } = payload || {}
2711
+ let { curIndex } = payload || {}
2712
+ const innerWidth = this.getInnerWidth()
2713
+ const isPagingMode = this.getIsPagingMode()
2714
+ // 缓存当前页数信息
2715
+ const oldPageSize = this.pageRowList.length
2716
+ // 计算文档信息
2717
+ if (isCompute) {
2718
+ // 清空浮动元素位置信息
2719
+ this.position.setFloatPositionList([])
2720
+ if (isPagingMode) {
2721
+ // 页眉信息
2722
+ if (!header.disabled) {
2723
+ this.header.compute()
2724
+ }
2725
+ // 页脚信息
2726
+ if (!footer.disabled) {
2727
+ this.footer.compute()
2728
+ }
2729
+ }
2730
+ // 行信息
2731
+ const margins = this.getMargins()
2732
+ const pageHeight = this.getHeight()
2733
+ const extraHeight = this.header.getExtraHeight()
2734
+ const mainOuterHeight = this.getMainOuterHeight()
2735
+ const startX = margins[3]
2736
+ const startY = margins[0] + extraHeight
2737
+ const surroundElementList = pickSurroundElementList(this.elementList)
2738
+ this.rowList = this.computeRowList({
2739
+ startX,
2740
+ startY,
2741
+ pageHeight,
2742
+ mainOuterHeight,
2743
+ isPagingMode,
2744
+ innerWidth,
2745
+ surroundElementList,
2746
+ elementList: this.elementList
2747
+ })
2748
+ // 页面信息
2749
+ this.pageRowList = this._computePageList()
2750
+ // 位置信息
2751
+ this.position.computePositionList()
2752
+ // 区域信息
2753
+ this.area.compute()
2754
+ if (!this.isPrintMode()) {
2755
+ // 搜索信息
2756
+ const searchKeyword = this.search.getSearchKeyword()
2757
+ if (searchKeyword) {
2758
+ this.search.compute(searchKeyword)
2759
+ }
2760
+ // 控件关键词高亮
2761
+ this.control.computeHighlightList()
2762
+ }
2763
+ // 涂鸦信息
2764
+ if (this.isGraffitiMode()) {
2765
+ this.graffiti.compute()
2766
+ }
2767
+ }
2768
+ // 清除光标等副作用
2769
+ this.imageObserver.clearAll()
2770
+ this.cursor.recoveryCursor()
2771
+ // 创建纸张
2772
+ for (let i = 0; i < this.pageRowList.length; i++) {
2773
+ if (!this.pageList[i]) {
2774
+ this._createPage(i)
2775
+ }
2776
+ }
2777
+ // 移除多余页
2778
+ const curPageCount = this.pageRowList.length
2779
+ const prePageCount = this.pageList.length
2780
+ if (prePageCount > curPageCount) {
2781
+ const deleteCount = prePageCount - curPageCount
2782
+ this.ctxList.splice(curPageCount, deleteCount)
2783
+ this.pageList
2784
+ .splice(curPageCount, deleteCount)
2785
+ .forEach(page => page.remove())
2786
+ }
2787
+ // 绘制元素
2788
+ // 连续页因为有高度的变化会导致canvas渲染空白,需立即渲染,否则会出现闪动
2789
+ if (isLazy && isPagingMode) {
2790
+ this._lazyRender()
2791
+ } else {
2792
+ this._immediateRender()
2793
+ }
2794
+ // 光标重绘
2795
+ if (isSetCursor) {
2796
+ curIndex = this.setCursor(curIndex)
2797
+ } else if (this.range.getIsSelection()) {
2798
+ // 存在选区时仅定位避免事件无法捕获
2799
+ this.cursor.focus()
2800
+ }
2801
+ // 历史记录用于undo、redo(非首次渲染内容变更 || 第一次存在光标时)
2802
+ if (
2803
+ (isSubmitHistory && !isFirstRender) ||
2804
+ (curIndex !== undefined && this.historyManager.isStackEmpty())
2805
+ ) {
2806
+ this.submitHistory(curIndex)
2807
+ }
2808
+ // 信息变动回调
2809
+ nextTick(() => {
2810
+ // 选区样式
2811
+ this.range.setRangeStyle()
2812
+ // 重新唤起弹窗类控件
2813
+ if (isCompute && this.control.getActiveControl()) {
2814
+ this.control.reAwakeControl()
2815
+ }
2816
+ // 表格工具重新渲染
2817
+ if (
2818
+ isCompute &&
2819
+ !this.isReadonly() &&
2820
+ this.position.getPositionContext().isTable
2821
+ ) {
2822
+ this.tableTool.render()
2823
+ }
2824
+ // 页眉指示器重新渲染
2825
+ if (isCompute && !this.zone.isMainActive()) {
2826
+ this.zone.drawZoneIndicator()
2827
+ }
2828
+ // 页数改变
2829
+ if (oldPageSize !== this.pageRowList.length) {
2830
+ if (this.listener.pageSizeChange) {
2831
+ this.listener.pageSizeChange(this.pageRowList.length)
2832
+ }
2833
+ if (this.eventBus.isSubscribe('pageSizeChange')) {
2834
+ this.eventBus.emit('pageSizeChange', this.pageRowList.length)
2835
+ }
2836
+ }
2837
+ // 文档内容改变
2838
+ if ((isSubmitHistory || isSourceHistory) && !isInit) {
2839
+ if (this.listener.contentChange) {
2840
+ this.listener.contentChange()
2841
+ }
2842
+ if (this.eventBus.isSubscribe('contentChange')) {
2843
+ this.eventBus.emit('contentChange')
2844
+ }
2845
+ }
2846
+ })
2847
+ }
2848
+
2849
+ public setCursor(curIndex: number | undefined) {
2850
+ const positionContext = this.position.getPositionContext()
2851
+ const positionList = this.position.getPositionList()
2852
+ if (positionContext.isTable) {
2853
+ const { index, trIndex, tdIndex } = positionContext
2854
+ const elementList = this.getOriginalElementList()
2855
+ const tablePositionList =
2856
+ elementList[index!].trList?.[trIndex!].tdList[tdIndex!].positionList
2857
+ if (curIndex === undefined && tablePositionList) {
2858
+ curIndex = tablePositionList.length - 1
2859
+ }
2860
+ const tablePosition = tablePositionList?.[curIndex!]
2861
+ this.position.setCursorPosition(tablePosition || null)
2862
+ } else {
2863
+ this.position.setCursorPosition(
2864
+ curIndex !== undefined ? positionList[curIndex] : null
2865
+ )
2866
+ }
2867
+ // 定位到图片元素并且位置发生变化
2868
+ let isShowCursor = true
2869
+ if (
2870
+ curIndex !== undefined &&
2871
+ positionContext.isImage &&
2872
+ positionContext.isDirectHit
2873
+ ) {
2874
+ const elementList = this.getElementList()
2875
+ const element = elementList[curIndex]
2876
+ if (IMAGE_ELEMENT_TYPE.includes(element.type!)) {
2877
+ isShowCursor = false
2878
+ const position = this.position.getCursorPosition()
2879
+ this.previewer.updateResizer(element, position)
2880
+ }
2881
+ }
2882
+ this.cursor.drawCursor({
2883
+ isShow: isShowCursor
2884
+ })
2885
+ return curIndex
2886
+ }
2887
+
2888
+ public submitHistory(curIndex: number | undefined) {
2889
+ const positionContext = this.position.getPositionContext()
2890
+ const oldElementList = getSlimCloneElementList(this.elementList)
2891
+ const oldHeaderElementList = getSlimCloneElementList(
2892
+ this.header.getElementList()
2893
+ )
2894
+ const oldFooterElementList = getSlimCloneElementList(
2895
+ this.footer.getElementList()
2896
+ )
2897
+ const oldRange = deepClone(this.range.getRange())
2898
+ const pageNo = this.pageNo
2899
+ const oldPositionContext = deepClone(positionContext)
2900
+ const zone = this.zone.getZone()
2901
+ this.historyManager.execute(() => {
2902
+ this.zone.setZone(zone)
2903
+ this.setPageNo(pageNo)
2904
+ this.position.setPositionContext(deepClone(oldPositionContext))
2905
+ this.header.setElementList(deepClone(oldHeaderElementList))
2906
+ this.footer.setElementList(deepClone(oldFooterElementList))
2907
+ this.elementList = deepClone(oldElementList)
2908
+ this.range.replaceRange(deepClone(oldRange))
2909
+ this.render({
2910
+ curIndex,
2911
+ isSubmitHistory: false,
2912
+ isSourceHistory: true
2913
+ })
2914
+ })
2915
+ }
2916
+
2917
+ public destroy() {
2918
+ this.container.remove()
2919
+ this.globalEvent.removeEvent()
2920
+ this.scrollObserver.removeEvent()
2921
+ this.selectionObserver.removeEvent()
2922
+ }
2923
+
2924
+ public clearSideEffect() {
2925
+ // 预览工具组件
2926
+ this.getPreviewer().clearResizer()
2927
+ // 表格工具组件
2928
+ this.getTableTool().dispose()
2929
+ // 超链接弹窗
2930
+ this.getHyperlinkParticle().clearHyperlinkPopup()
2931
+ // 日期控件
2932
+ this.getDateParticle().clearDatePicker()
2933
+ }
2934
+ }