pptx-react-viewer 4.25.0 → 4.27.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 (190) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/{AiChatPanel-2OWWIKF5.mjs → AiChatPanel-3J6XGIJ4.mjs} +3 -7
  3. package/dist/AiChatPanel-3J6XGIJ4.mjs.br +0 -0
  4. package/dist/AiChatPanel-3J6XGIJ4.mjs.gz +0 -0
  5. package/dist/{AiChatPanel-3NUGL5VN.js → AiChatPanel-EGTSK4T5.js} +27 -31
  6. package/dist/AiChatPanel-EGTSK4T5.js.br +0 -0
  7. package/dist/AiChatPanel-EGTSK4T5.js.gz +0 -0
  8. package/dist/{Model3DScene-SKCDIM2U.mjs → Model3DScene-2V2GQ6FY.mjs} +2 -2
  9. package/dist/Model3DScene-2V2GQ6FY.mjs.br +0 -0
  10. package/dist/Model3DScene-2V2GQ6FY.mjs.gz +0 -0
  11. package/dist/{Model3DScene-TXD4IOMU.js → Model3DScene-Q3XEZSWV.js} +3 -3
  12. package/dist/Model3DScene-Q3XEZSWV.js.br +2 -0
  13. package/dist/Model3DScene-Q3XEZSWV.js.gz +0 -0
  14. package/dist/{UTIF-CNNXS5Z5.mjs → UTIF-RPFRSSC5.mjs} +3 -3
  15. package/dist/UTIF-RPFRSSC5.mjs.br +0 -0
  16. package/dist/UTIF-RPFRSSC5.mjs.gz +0 -0
  17. package/dist/{UTIF-6SHLXCNF.js → UTIF-XWCKRN6V.js} +21 -21
  18. package/dist/UTIF-XWCKRN6V.js.br +0 -0
  19. package/dist/UTIF-XWCKRN6V.js.gz +0 -0
  20. package/dist/{chunk-M6FVQI5G.mjs → chunk-34AXBLYH.mjs} +51 -104
  21. package/dist/chunk-34AXBLYH.mjs.br +0 -0
  22. package/dist/chunk-34AXBLYH.mjs.gz +0 -0
  23. package/dist/{chunk-MT33XQSF.js → chunk-5RQTZ376.js} +1207 -1185
  24. package/dist/chunk-5RQTZ376.js.br +0 -0
  25. package/dist/chunk-5RQTZ376.js.gz +0 -0
  26. package/dist/{chunk-XGB3TDIC.mjs → chunk-6DZX6EAA.mjs} +1 -5
  27. package/dist/chunk-6DZX6EAA.mjs.br +0 -0
  28. package/dist/chunk-6DZX6EAA.mjs.gz +0 -0
  29. package/dist/{chunk-XDWL6LNV.js → chunk-MQCBMO6A.js} +909 -923
  30. package/dist/chunk-MQCBMO6A.js.br +0 -0
  31. package/dist/chunk-MQCBMO6A.js.gz +0 -0
  32. package/dist/{chunk-4VNS5WPM.js → chunk-QGM4M3NI.js} +0 -5
  33. package/dist/chunk-QGM4M3NI.js.br +0 -0
  34. package/dist/chunk-QGM4M3NI.js.gz +0 -0
  35. package/dist/{chunk-MRJZIBPQ.js → chunk-TYJZJJZ6.mjs} +13062 -118856
  36. package/dist/chunk-TYJZJJZ6.mjs.br +0 -0
  37. package/dist/chunk-TYJZJJZ6.mjs.gz +0 -0
  38. package/dist/{chunk-5FEQK3MW.mjs → chunk-WK6JBJCT.js} +13430 -117602
  39. package/dist/chunk-WK6JBJCT.js.br +0 -0
  40. package/dist/chunk-WK6JBJCT.js.gz +0 -0
  41. package/dist/{chunk-BP7NMPQE.mjs → chunk-ZK7UEEXE.mjs} +71 -92
  42. package/dist/chunk-ZK7UEEXE.mjs.br +0 -0
  43. package/dist/chunk-ZK7UEEXE.mjs.gz +0 -0
  44. package/dist/i18n/de.cjs +1 -0
  45. package/dist/i18n/de.cjs.br +0 -0
  46. package/dist/i18n/de.cjs.gz +0 -0
  47. package/dist/i18n/de.mjs +1 -0
  48. package/dist/i18n/de.mjs.br +0 -0
  49. package/dist/i18n/de.mjs.gz +0 -0
  50. package/dist/i18n/es.cjs +1 -0
  51. package/dist/i18n/es.cjs.br +0 -0
  52. package/dist/i18n/es.cjs.gz +0 -0
  53. package/dist/i18n/es.mjs +1 -0
  54. package/dist/i18n/es.mjs.br +0 -0
  55. package/dist/i18n/es.mjs.gz +0 -0
  56. package/dist/i18n/fr.cjs +1 -0
  57. package/dist/i18n/fr.cjs.br +0 -0
  58. package/dist/i18n/fr.cjs.gz +0 -0
  59. package/dist/i18n/fr.mjs +1 -0
  60. package/dist/i18n/fr.mjs.br +0 -0
  61. package/dist/i18n/fr.mjs.gz +0 -0
  62. package/dist/i18n/zh-CN.cjs +1 -0
  63. package/dist/i18n/zh-CN.cjs.br +0 -0
  64. package/dist/i18n/zh-CN.cjs.gz +0 -0
  65. package/dist/i18n/zh-CN.mjs +1 -0
  66. package/dist/i18n/zh-CN.mjs.br +0 -0
  67. package/dist/i18n/zh-CN.mjs.gz +0 -0
  68. package/dist/i18n.d.ts +1 -46
  69. package/dist/i18n.js +5 -5
  70. package/dist/i18n.js.br +0 -0
  71. package/dist/i18n.js.gz +0 -0
  72. package/dist/i18n.mjs +2 -2
  73. package/dist/i18n.mjs.br +0 -0
  74. package/dist/i18n.mjs.gz +0 -0
  75. package/dist/index.d.ts +11 -3703
  76. package/dist/index.js +96 -97
  77. package/dist/index.js.br +0 -0
  78. package/dist/index.js.gz +0 -0
  79. package/dist/index.mjs +6 -7
  80. package/dist/index.mjs.br +0 -0
  81. package/dist/index.mjs.gz +0 -0
  82. package/dist/internals.d.ts +5 -3847
  83. package/dist/internals.js +114 -115
  84. package/dist/internals.js.br +0 -0
  85. package/dist/internals.js.gz +0 -0
  86. package/dist/internals.mjs +4 -5
  87. package/dist/internals.mjs.br +0 -0
  88. package/dist/internals.mjs.gz +0 -0
  89. package/dist/{chunk-NJYF5KTY.mjs → three.module-A6ACXOES.mjs} +2 -0
  90. package/dist/three.module-A6ACXOES.mjs.br +0 -0
  91. package/dist/three.module-A6ACXOES.mjs.gz +0 -0
  92. package/dist/{chunk-QS3GAPPA.js → three.module-IORLGCSX.js} +2 -0
  93. package/dist/three.module-IORLGCSX.js.br +0 -0
  94. package/dist/three.module-IORLGCSX.js.gz +0 -0
  95. package/dist/viewer/index.d.ts +12 -2633
  96. package/dist/viewer/index.js +35 -37
  97. package/dist/viewer/index.js.br +0 -0
  98. package/dist/viewer/index.js.gz +0 -0
  99. package/dist/viewer/index.mjs +5 -7
  100. package/dist/viewer/index.mjs.br +0 -0
  101. package/dist/viewer/index.mjs.gz +0 -0
  102. package/dist/{y-webrtc-F2H7BRUK.js → y-webrtc-6LID3XZO.js} +5 -5
  103. package/dist/y-webrtc-6LID3XZO.js.br +0 -0
  104. package/dist/y-webrtc-6LID3XZO.js.gz +0 -0
  105. package/dist/{y-webrtc-GAHCEZSS.mjs → y-webrtc-F5IA7T5S.mjs} +1 -1
  106. package/dist/y-webrtc-F5IA7T5S.mjs.br +0 -0
  107. package/dist/y-webrtc-F5IA7T5S.mjs.gz +0 -0
  108. package/dist/{y-websocket-FBMB63F4.js → y-websocket-HOY2HFMH.js} +1 -1
  109. package/dist/y-websocket-HOY2HFMH.js.br +0 -0
  110. package/dist/y-websocket-HOY2HFMH.js.gz +0 -0
  111. package/dist/{y-websocket-MAGDFVLN.mjs → y-websocket-VGLBV2HJ.mjs} +1 -1
  112. package/dist/y-websocket-VGLBV2HJ.mjs.br +0 -0
  113. package/dist/y-websocket-VGLBV2HJ.mjs.gz +0 -0
  114. package/package.json +3 -4
  115. package/dist/AiChatPanel-2OWWIKF5.mjs.br +0 -0
  116. package/dist/AiChatPanel-2OWWIKF5.mjs.gz +0 -0
  117. package/dist/AiChatPanel-3NUGL5VN.js.br +0 -0
  118. package/dist/AiChatPanel-3NUGL5VN.js.gz +0 -0
  119. package/dist/GLTFLoader-O7FNKCHG.mjs +0 -2649
  120. package/dist/GLTFLoader-O7FNKCHG.mjs.br +0 -0
  121. package/dist/GLTFLoader-O7FNKCHG.mjs.gz +0 -0
  122. package/dist/GLTFLoader-UM56GMCK.js +0 -2651
  123. package/dist/GLTFLoader-UM56GMCK.js.br +0 -0
  124. package/dist/GLTFLoader-UM56GMCK.js.gz +0 -0
  125. package/dist/Model3DScene-SKCDIM2U.mjs.br +0 -0
  126. package/dist/Model3DScene-SKCDIM2U.mjs.gz +0 -0
  127. package/dist/Model3DScene-TXD4IOMU.js.br +0 -0
  128. package/dist/Model3DScene-TXD4IOMU.js.gz +0 -0
  129. package/dist/OrbitControls-EXEHIXFI.js +0 -922
  130. package/dist/OrbitControls-EXEHIXFI.js.br +0 -0
  131. package/dist/OrbitControls-EXEHIXFI.js.gz +0 -0
  132. package/dist/OrbitControls-PZQ4CBVS.mjs +0 -920
  133. package/dist/OrbitControls-PZQ4CBVS.mjs.br +0 -0
  134. package/dist/OrbitControls-PZQ4CBVS.mjs.gz +0 -0
  135. package/dist/UTIF-6SHLXCNF.js.br +0 -0
  136. package/dist/UTIF-6SHLXCNF.js.gz +0 -0
  137. package/dist/UTIF-CNNXS5Z5.mjs.br +0 -0
  138. package/dist/UTIF-CNNXS5Z5.mjs.gz +0 -0
  139. package/dist/chunk-4VNS5WPM.js.br +0 -0
  140. package/dist/chunk-4VNS5WPM.js.gz +0 -0
  141. package/dist/chunk-5FEQK3MW.mjs.br +0 -0
  142. package/dist/chunk-5FEQK3MW.mjs.gz +0 -0
  143. package/dist/chunk-7E6EF4GQ.js +0 -3977
  144. package/dist/chunk-7E6EF4GQ.js.br +0 -0
  145. package/dist/chunk-7E6EF4GQ.js.gz +0 -0
  146. package/dist/chunk-BP7NMPQE.mjs.br +0 -0
  147. package/dist/chunk-BP7NMPQE.mjs.gz +0 -0
  148. package/dist/chunk-C7SS65W2.mjs +0 -22197
  149. package/dist/chunk-C7SS65W2.mjs.br +0 -0
  150. package/dist/chunk-C7SS65W2.mjs.gz +0 -0
  151. package/dist/chunk-DC67MV5I.mjs +0 -3915
  152. package/dist/chunk-DC67MV5I.mjs.br +0 -0
  153. package/dist/chunk-DC67MV5I.mjs.gz +0 -0
  154. package/dist/chunk-LKL6DQ55.js +0 -4246
  155. package/dist/chunk-LKL6DQ55.js.br +0 -0
  156. package/dist/chunk-LKL6DQ55.js.gz +0 -0
  157. package/dist/chunk-M6FVQI5G.mjs.br +0 -0
  158. package/dist/chunk-M6FVQI5G.mjs.gz +0 -0
  159. package/dist/chunk-MRJZIBPQ.js.br +0 -0
  160. package/dist/chunk-MRJZIBPQ.js.gz +0 -0
  161. package/dist/chunk-MT33XQSF.js.br +0 -0
  162. package/dist/chunk-MT33XQSF.js.gz +0 -0
  163. package/dist/chunk-NJYF5KTY.mjs.br +0 -0
  164. package/dist/chunk-NJYF5KTY.mjs.gz +0 -0
  165. package/dist/chunk-Q4HRYUEP.mjs +0 -4242
  166. package/dist/chunk-Q4HRYUEP.mjs.br +0 -0
  167. package/dist/chunk-Q4HRYUEP.mjs.gz +0 -0
  168. package/dist/chunk-QS3GAPPA.js.br +0 -0
  169. package/dist/chunk-QS3GAPPA.js.gz +0 -0
  170. package/dist/chunk-QTASICXP.js +0 -22241
  171. package/dist/chunk-QTASICXP.js.br +0 -0
  172. package/dist/chunk-QTASICXP.js.gz +0 -0
  173. package/dist/chunk-XDWL6LNV.js.br +0 -0
  174. package/dist/chunk-XDWL6LNV.js.gz +0 -0
  175. package/dist/chunk-XGB3TDIC.mjs.br +0 -0
  176. package/dist/chunk-XGB3TDIC.mjs.gz +0 -0
  177. package/dist/three.module-BLHG4FHZ.js +0 -1783
  178. package/dist/three.module-BLHG4FHZ.js.br +0 -0
  179. package/dist/three.module-BLHG4FHZ.js.gz +0 -0
  180. package/dist/three.module-ZSSMKKHW.mjs +0 -2
  181. package/dist/three.module-ZSSMKKHW.mjs.br +0 -0
  182. package/dist/three.module-ZSSMKKHW.mjs.gz +0 -0
  183. package/dist/y-webrtc-F2H7BRUK.js.br +0 -0
  184. package/dist/y-webrtc-F2H7BRUK.js.gz +0 -0
  185. package/dist/y-webrtc-GAHCEZSS.mjs.br +0 -0
  186. package/dist/y-webrtc-GAHCEZSS.mjs.gz +0 -0
  187. package/dist/y-websocket-FBMB63F4.js.br +0 -0
  188. package/dist/y-websocket-FBMB63F4.js.gz +0 -0
  189. package/dist/y-websocket-MAGDFVLN.mjs.br +0 -0
  190. package/dist/y-websocket-MAGDFVLN.mjs.gz +0 -0
@@ -1,3915 +0,0 @@
1
- import { SHAPE_PRESET_DEFS, VIEWER_SHORTCUT_REFERENCE, STROKE_DASH_OPTIONS, getAnimationInitialStyle, cloneHistorySnapshot, hasShapeProperties, getImageFitStyle, getImageFillCounterTransform, getComputedFillStyle, isImageLikeElement, getCropShapeClipPath, getImageTilingStyle, mediaPlaybackAttributes, getOnlineVideoEmbed, ONLINE_VIDEO_IFRAME_SANDBOX, ONLINE_VIDEO_IFRAME_ALLOW, buildTextBlockStyle, hasTextProperties, toCssWritingMode, toCssTextOrientation, toCssVerticalDirection, buildTextBodyLayoutStyle, buildTextBody3DSceneStyle, buildParagraphs, followingText, colorWithOpacity, normalizeHexColor, hexToRgbUnit, build3DExtrusionData, getComputedEffectStyle, getComputedStrokeStyle, getAnimationColorBaseStyle, isStrokeOnlyPresetElement, resolveShapeGeometry, getConnectorPathGeometry, normalizeStrokeDashType, getSvgStrokeDasharray, buildSvgGradientDef, buildStrokeOutline, getPresetShapeVectorGeometry, isWedgeCalloutPresetShape, svgGradientFillRef, isCalloutShape, getCalloutLeaderLineGeometry, buildCalloutLeaderLineSvgPath, getCalloutViewBoxBounds, svgLineCap, getCompoundLineOffsets, getCompoundLineWidths, canDrillDown, computeSelectionRect, DEFAULT_FONT_FAMILY, cellStyleToCss, rectToCells, isCellInRect, tableContainerCss, buildChartViewModel, resolveChartKind, chartPreserveAspectRatio, buildNotesPdfBytes, buildTiledSlidesPdfBytes, buildPreviewAnimation, measurePreviewGeometry, buildMotionPathPreview, mediaFallbackVisual, scheduleMediaTrimAndFade, registerCrossSlideAudio, shouldShowMediaFullscreenStopButton, withInlineListDecorationDefaults, segmentStyleToCss, resolveAutoFitFontScale, applyUnderlineVariant, nestedTextDecorationStyle, scaleFontSizeForAutoFit, resolveFontKerning, getSubstituteFontFamily, parsePanoseString, resolveScriptFontSet, hasDistinctScriptFonts, authoredLetterSpacingPx, pieceLetterSpacing, resolveMetricTrackingPx, hollowTextFillStyle, splitsUnderlineIntoWords, stripUnderlineDecoration, buildTabContext, TEXT_BUILD_ID_SEP, buildTextBuildSpec, textBuildSpanStyle, apply3dEffects, customGeometryPathsToSvgSubpaths, buildSubpathPaints, computeColumnBoundaries, getTableCellBandStyle, parseDrawingColor, parseDrawingColorOpacity, getCellDiagonalBorders, xmlText, cellRunStyle, getChartStylePalette, computeChartLegendLayout, mediaFallbackIcon, mediaFallbackLabelKey, MEDIA_CHROME_ATTRIBUTE, convertOmmlToMathMl, sanitizeMathMl, tableCellCss, computeResizedColumnWidths, computeResizedRowHeight, DEFAULT_ROW_HEIGHT, ooxmlGradientAngleToCssDegrees, buildRectPathGradient, buildRunTabLines, chartPartToAttrs, splitWordsForUnderline, splitRunForMetrics, splitEastAsianBreaks, splitRunByScriptFont } from './chunk-5FEQK3MW.mjs';
2
- import { translationsEn } from './chunk-Q4HRYUEP.mjs';
3
- import { clsx } from 'clsx';
4
- import { twMerge } from 'tailwind-merge';
5
- import React8, { useRef, useState, useEffect, useCallback, useMemo, useLayoutEffect } from 'react';
6
- import { LuTriangle, LuPlus, LuMoveRight, LuMinus, LuDiamond, LuDatabase, LuCircle, LuSquare } from 'react-icons/lu';
7
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
- import { useTranslation } from 'react-i18next';
9
-
10
- // src/viewer/utils/animation-effects.ts
11
- function getAnimationInitialStyle2(preset, nativeAnimation) {
12
- return getAnimationInitialStyle(preset, nativeAnimation);
13
- }
14
- function cn(...inputs) {
15
- return twMerge(clsx(inputs));
16
- }
17
-
18
- // src/viewer/utils/clone.ts
19
- function cloneHistorySnapshot2(snapshot) {
20
- return cloneHistorySnapshot(snapshot);
21
- }
22
-
23
- // src/viewer/constants/scalar.ts
24
- var DEFAULT_CANVAS_WIDTH = 1280;
25
- var DEFAULT_CANVAS_HEIGHT = 720;
26
- var MIN_ELEMENT_SIZE = 12;
27
- var EMU_PER_PX = 9525;
28
- var DEFAULT_BODY_INSET_TB_PX = 45720 / 9525;
29
- var DEFAULT_TEXT_COLOR = "#111827";
30
- var DEFAULT_FILL_COLOR = "#3b82f6";
31
- var DEFAULT_STROKE_COLOR = "#1f2937";
32
- var DEFAULT_FONT_FAMILY2 = '"Segoe UI", "Helvetica Neue", Arial, sans-serif';
33
- var DEFAULT_TEXT_FONT_SIZE = 24;
34
- var HYPERLINK_COLOR = "#0563C1";
35
- var DEFAULT_TABLE_ROWS = 3;
36
- var DEFAULT_TABLE_COLUMNS = 3;
37
- var ZOOM_TO_SELECTION_PADDING = 96;
38
- var GRID_SIZE = 8;
39
- var SLIDE_NAV_THUMBNAIL_WIDTH = 156;
40
- var UNGROUPED_SECTION_ID = "__ungrouped__";
41
- var DEFAULT_SECTION_GROUP_ID = "default";
42
-
43
- // src/viewer/constants/toolbar.ts
44
- var TOOLBAR_SECTIONS = [
45
- { id: "file", labelKey: "pptx.ribbon.tab.file" },
46
- { id: "home", labelKey: "pptx.ribbon.tab.home" },
47
- { id: "insert", labelKey: "pptx.ribbon.tab.insert" },
48
- { id: "draw", labelKey: "pptx.ribbon.tab.draw" },
49
- { id: "design", labelKey: "pptx.ribbon.tab.design" },
50
- { id: "transitions", labelKey: "pptx.ribbon.tab.transitions" },
51
- { id: "animations", labelKey: "pptx.ribbon.tab.animations" },
52
- { id: "slideShow", labelKey: "pptx.ribbon.tab.slideShow" },
53
- { id: "record", labelKey: "pptx.ribbon.tab.record" },
54
- { id: "review", labelKey: "pptx.ribbon.tab.review" },
55
- { id: "view", labelKey: "pptx.ribbon.tab.view" },
56
- { id: "help", labelKey: "pptx.ribbon.tab.help" }
57
- ];
58
- var SHORTCUT_REFERENCE_ITEMS = VIEWER_SHORTCUT_REFERENCE;
59
-
60
- // src/viewer/constants/transitions-animations.ts
61
- var ANIMATION_PRESET_OPTIONS = [
62
- { value: "fadeIn", label: "Fade In", i18nKey: "pptx.animation.preset.fadeIn" },
63
- { value: "flyIn", label: "Fly In", i18nKey: "pptx.animation.preset.flyIn" },
64
- { value: "zoomIn", label: "Zoom In", i18nKey: "pptx.animation.preset.zoomIn" },
65
- { value: "fadeOut", label: "Fade Out", i18nKey: "pptx.animation.preset.fadeOut" },
66
- { value: "flyOut", label: "Fly Out", i18nKey: "pptx.animation.preset.flyOut" },
67
- { value: "zoomOut", label: "Zoom Out", i18nKey: "pptx.animation.preset.zoomOut" },
68
- { value: "spin", label: "Spin", i18nKey: "pptx.animation.preset.spin" },
69
- { value: "pulse", label: "Pulse", i18nKey: "pptx.animation.preset.pulse" },
70
- { value: "colorWave", label: "Color Wave", i18nKey: "pptx.animation.preset.colorWave" },
71
- { value: "bounce", label: "Bounce", i18nKey: "pptx.animation.preset.bounce" },
72
- { value: "flash", label: "Flash", i18nKey: "pptx.animation.preset.flash" },
73
- { value: "growShrink", label: "Grow/Shrink", i18nKey: "pptx.animation.preset.growShrink" },
74
- { value: "teeter", label: "Teeter", i18nKey: "pptx.animation.preset.teeter" }
75
- ];
76
- var GLYPH_COMPONENTS = {
77
- square: LuSquare,
78
- circle: LuCircle,
79
- database: LuDatabase,
80
- diamond: LuDiamond,
81
- minus: LuMinus,
82
- moveRight: LuMoveRight,
83
- plus: LuPlus,
84
- triangle: LuTriangle
85
- };
86
- var SHAPE_PRESETS = SHAPE_PRESET_DEFS.map(
87
- (def) => ({
88
- type: def.type,
89
- label: def.label,
90
- i18nKey: def.i18nKey,
91
- icon: React8.createElement(GLYPH_COMPONENTS[def.glyph], {
92
- className: def.glyphClass ? `w-3.5 h-3.5 ${def.glyphClass}` : "w-3.5 h-3.5"
93
- })
94
- })
95
- );
96
-
97
- // src/viewer/constants/connectors-strokes.ts
98
- var CONNECTOR_ARROW_OPTIONS = [
99
- { value: "none", label: "None", i18nKey: "pptx.arrowhead.none" },
100
- { value: "triangle", label: "Triangle", i18nKey: "pptx.arrowhead.triangle" },
101
- { value: "stealth", label: "Stealth", i18nKey: "pptx.arrowhead.stealth" },
102
- { value: "diamond", label: "Diamond", i18nKey: "pptx.arrowhead.diamond" },
103
- { value: "oval", label: "Oval", i18nKey: "pptx.arrowhead.oval" },
104
- { value: "arrow", label: "Open Arrow", i18nKey: "pptx.arrowhead.openArrow" }
105
- ];
106
- var ARROW_SIZE_OPTIONS = [
107
- { value: "sm", label: "Small", i18nKey: "pptx.connectorOptions.sizeSmall" },
108
- { value: "med", label: "Medium", i18nKey: "pptx.connectorOptions.sizeMedium" },
109
- { value: "lg", label: "Large", i18nKey: "pptx.connectorOptions.sizeLarge" }
110
- ];
111
- var STROKE_DASH_OPTIONS2 = STROKE_DASH_OPTIONS;
112
-
113
- // src/viewer/utils/shape-types.tsx
114
- function getShapeType(shapeType) {
115
- if (!shapeType) {
116
- return "rect";
117
- }
118
- const normalized = shapeType.toLowerCase();
119
- if (normalized === "rect") {
120
- return "rect";
121
- }
122
- if (normalized === "roundrect") {
123
- return "roundRect";
124
- }
125
- if (normalized === "ellipse" || normalized === "oval") {
126
- return "ellipse";
127
- }
128
- if (normalized === "cylinder" || normalized === "can") {
129
- return "cylinder";
130
- }
131
- if (normalized === "triangle") {
132
- return "triangle";
133
- }
134
- if (normalized === "rttriangle") {
135
- return "rtTriangle";
136
- }
137
- if (normalized === "diamond") {
138
- return "diamond";
139
- }
140
- if (normalized === "line") {
141
- return "line";
142
- }
143
- if (normalized === "parallelogram") {
144
- return "parallelogram";
145
- }
146
- if (normalized === "trapezoid") {
147
- return "trapezoid";
148
- }
149
- if (normalized === "pentagon") {
150
- return "pentagon";
151
- }
152
- if (normalized === "hexagon") {
153
- return "hexagon";
154
- }
155
- if (normalized === "octagon") {
156
- return "octagon";
157
- }
158
- if (normalized === "chevron") {
159
- return "chevron";
160
- }
161
- if (normalized === "star5") {
162
- return "star5";
163
- }
164
- if (normalized === "star6") {
165
- return "star6";
166
- }
167
- if (normalized === "star8") {
168
- return "star8";
169
- }
170
- if (normalized === "plus") {
171
- return "plus";
172
- }
173
- if (normalized === "heart") {
174
- return "heart";
175
- }
176
- if (normalized === "cloud") {
177
- return "cloud";
178
- }
179
- if (normalized === "sun") {
180
- return "sun";
181
- }
182
- if (normalized === "moon") {
183
- return "moon";
184
- }
185
- if (normalized === "pie") {
186
- return "pie";
187
- }
188
- if (normalized === "plaque") {
189
- return "plaque";
190
- }
191
- if (normalized === "teardrop") {
192
- return "teardrop";
193
- }
194
- if (normalized === "rtarrow" || normalized === "rightarrow") {
195
- return "rtArrow";
196
- }
197
- if (normalized === "leftarrow") {
198
- return "leftArrow";
199
- }
200
- if (normalized === "uparrow") {
201
- return "upArrow";
202
- }
203
- if (normalized === "downarrow") {
204
- return "downArrow";
205
- }
206
- if (normalized.includes("connector")) {
207
- return "connector";
208
- }
209
- if (normalized === "connector") {
210
- return "connector";
211
- }
212
- return "rect";
213
- }
214
-
215
- // src/viewer/utils/element.ts
216
- function isConnectorOrLineElement(element) {
217
- if (element.type === "connector") {
218
- return true;
219
- }
220
- if (!hasShapeProperties(element)) {
221
- return false;
222
- }
223
- const st = getShapeType(element.shapeType);
224
- return st === "connector" || st === "line" || element.shapeType === "line";
225
- }
226
-
227
- // src/viewer/utils/image-style.ts
228
- function getImageMaskStyle(element) {
229
- if (!hasShapeProperties(element)) {
230
- return void 0;
231
- }
232
- const geometry = resolveShapeGeometry(element);
233
- if (geometry.kind === "borderRadius") {
234
- return { borderRadius: geometry.radius };
235
- }
236
- return geometry.kind === "clipPath" ? { clipPath: geometry.clipPath } : void 0;
237
- }
238
- function getImageRenderStyle(element) {
239
- const fit = getImageFitStyle(element);
240
- const counterTransform = getImageFillCounterTransform(element, Boolean(fit.transform));
241
- return {
242
- // A translated/scaled crop, or a rotWithShape=false counter-transform,
243
- // must be masked by the stationary wrapper, not by the moving/counter-
244
- // transformed bitmap itself.
245
- ...fit.transform || counterTransform ? {} : getImageMaskStyle(element) || {},
246
- ...fit,
247
- ...counterTransform ? { transform: counterTransform, transformOrigin: "center" } : {}
248
- };
249
- }
250
- function getImageSurfaceMaskStyle(element) {
251
- const mask = getImageMaskStyle(element);
252
- return {
253
- overflow: "hidden",
254
- borderRadius: mask?.borderRadius,
255
- clipPath: mask?.clipPath ?? getCropShapeClipPath2(element)
256
- };
257
- }
258
- function getImageSurfaceStyle(element) {
259
- const fill = hasShapeProperties(element) ? getComputedFillStyle(element) : void 0;
260
- return {
261
- position: "absolute",
262
- inset: 0,
263
- ...getImageSurfaceMaskStyle(element),
264
- backgroundColor: fill?.backgroundColor ?? "transparent",
265
- backgroundImage: fill?.backgroundImage,
266
- backgroundRepeat: fill?.backgroundRepeat,
267
- backgroundSize: fill?.backgroundSize,
268
- backgroundPosition: fill?.backgroundPosition
269
- };
270
- }
271
- function getCropShapeClipPath2(element) {
272
- if (!isImageLikeElement(element)) {
273
- return void 0;
274
- }
275
- return getCropShapeClipPath(element.cropShape, element.width, element.height);
276
- }
277
- function getImageTilingStyle2(element, nativeSize) {
278
- return getImageTilingStyle(element, nativeSize);
279
- }
280
-
281
- // src/viewer/utils/geometry-image.ts
282
- function shouldRenderFallbackLabel(element, isTextElement) {
283
- if (isTextElement) {
284
- return false;
285
- }
286
- if (element.type === "shape" || element.type === "connector") {
287
- return false;
288
- }
289
- if (element.type === "picture" || element.type === "image") {
290
- return false;
291
- }
292
- if (element.type === "table") {
293
- return false;
294
- }
295
- if (element.type === "media") {
296
- return false;
297
- }
298
- if (element.type === "contentPart") {
299
- return false;
300
- }
301
- if (element.type === "model3d") {
302
- return false;
303
- }
304
- if (element.type === "ole") {
305
- const previewData = element.previewImageData ?? element.previewImage;
306
- return !previewData;
307
- }
308
- return element.type === "chart" || element.type === "smartArt" || element.type === "unknown";
309
- }
310
- function ensureArrayValue(value) {
311
- if (!value) {
312
- return [];
313
- }
314
- return Array.isArray(value) ? value : [value];
315
- }
316
-
317
- // src/viewer/utils/media-persistent-audio.tsx
318
- function buildTrimFragment(element) {
319
- const start = element.trimStartMs;
320
- if (start === void 0 || start <= 0) {
321
- return "";
322
- }
323
- return `#t=${(start / 1e3).toFixed(3)}`;
324
- }
325
- function useMediaMetadataExtraction(mediaRef, element) {
326
- const [metadata, setMetadata] = useState(element.metadata);
327
- useEffect(() => {
328
- const el = mediaRef.current;
329
- if (!el) {
330
- return;
331
- }
332
- if (element.metadata?.duration !== void 0) {
333
- setMetadata(element.metadata);
334
- return;
335
- }
336
- const extract = () => {
337
- if (!Number.isFinite(el.duration) || el.duration === 0) {
338
- return;
339
- }
340
- const meta = {
341
- duration: el.duration
342
- };
343
- if (el instanceof HTMLVideoElement) {
344
- if (el.videoWidth > 0) {
345
- meta.videoWidth = el.videoWidth;
346
- }
347
- if (el.videoHeight > 0) {
348
- meta.videoHeight = el.videoHeight;
349
- }
350
- }
351
- const sourceEl = el.querySelector("source");
352
- if (sourceEl?.type) {
353
- meta.codecInfo = sourceEl.type;
354
- }
355
- element.metadata = meta;
356
- setMetadata(meta);
357
- };
358
- el.addEventListener("loadedmetadata", extract);
359
- if (el.readyState >= 1) {
360
- extract();
361
- }
362
- return () => {
363
- el.removeEventListener("loadedmetadata", extract);
364
- };
365
- }, [mediaRef, element]);
366
- return metadata;
367
- }
368
- function CaptionTrackRenderer({
369
- captionTracks
370
- }) {
371
- return /* @__PURE__ */ jsx(Fragment, { children: captionTracks.map((track) => {
372
- const trackSrc = track.src ?? (track.content ? `data:text/vtt;charset=utf-8,${encodeURIComponent(track.content)}` : void 0);
373
- if (!trackSrc) {
374
- return null;
375
- }
376
- return /* @__PURE__ */ jsx(
377
- "track",
378
- {
379
- kind: track.kind,
380
- label: track.label,
381
- srcLang: track.language,
382
- src: trackSrc,
383
- default: track.isDefault
384
- },
385
- track.id
386
- );
387
- }) });
388
- }
389
- function VideoWithMetadata({
390
- element,
391
- mediaRef,
392
- onPlay,
393
- dataUrl,
394
- trimFragment,
395
- mediaMimeType,
396
- posterUrl,
397
- shouldLoop,
398
- shouldAutoPlay,
399
- isFullScreen,
400
- isPresentationMode,
401
- showTransport
402
- }) {
403
- useMediaMetadataExtraction(mediaRef, element);
404
- const captionTracks = element.captionTracks ?? [];
405
- return /* @__PURE__ */ jsxs(
406
- "video",
407
- {
408
- ref: mediaRef,
409
- className: "w-full h-full pointer-events-auto",
410
- style: isFullScreen ? { objectFit: "cover" } : getImageFitStyle(element),
411
- controls: showTransport,
412
- preload: "metadata",
413
- playsInline: true,
414
- autoPlay: shouldAutoPlay,
415
- muted: shouldAutoPlay && !isPresentationMode,
416
- poster: posterUrl,
417
- loop: shouldLoop,
418
- onPlay,
419
- crossOrigin: captionTracks.length > 0 ? "anonymous" : void 0,
420
- children: [
421
- /* @__PURE__ */ jsx("source", { src: `${dataUrl}${trimFragment}`, type: mediaMimeType || "video/mp4" }),
422
- captionTracks.length > 0 && /* @__PURE__ */ jsx(CaptionTrackRenderer, { captionTracks })
423
- ]
424
- }
425
- );
426
- }
427
- function AudioWithMetadata({
428
- element,
429
- mediaRef,
430
- onPlay,
431
- dataUrl,
432
- trimFragment,
433
- mediaMimeType,
434
- shouldLoop,
435
- shouldAutoPlay,
436
- showTransport
437
- }) {
438
- useMediaMetadataExtraction(mediaRef, element);
439
- return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center p-2 pointer-events-auto", children: /* @__PURE__ */ jsx(
440
- "audio",
441
- {
442
- ref: mediaRef,
443
- className: "w-full",
444
- controls: showTransport,
445
- preload: "metadata",
446
- autoPlay: shouldAutoPlay,
447
- loop: shouldLoop,
448
- onPlay,
449
- children: /* @__PURE__ */ jsx("source", { src: `${dataUrl}${trimFragment}`, type: mediaMimeType || "audio/mpeg" })
450
- }
451
- ) });
452
- }
453
- function PresentationMediaController({
454
- element,
455
- isPresentationMode,
456
- shouldAutoPlay,
457
- resolvedDataUrl,
458
- isFullScreen,
459
- onPlayStateChange,
460
- children
461
- }) {
462
- const { t } = useTranslation();
463
- const mediaRef = useRef(null);
464
- const [isMediaPlaying, setIsMediaPlaying] = useState(false);
465
- const trimStartSec = element.trimStartMs !== void 0 ? element.trimStartMs / 1e3 : 0;
466
- const hideWhenNotPlaying = isPresentationMode && element.hideWhenNotPlaying === true;
467
- const { volume: playbackVolume, playbackRate } = mediaPlaybackAttributes({
468
- loop: element.loop,
469
- volume: element.volume,
470
- playbackSpeed: element.playbackSpeed
471
- });
472
- useEffect(() => {
473
- const el = mediaRef.current;
474
- if (el) {
475
- el.volume = playbackVolume;
476
- }
477
- }, [playbackVolume]);
478
- useEffect(() => {
479
- const el = mediaRef.current;
480
- if (el) {
481
- el.playbackRate = playbackRate;
482
- }
483
- }, [playbackRate]);
484
- useEffect(() => {
485
- const el = mediaRef.current;
486
- if (!el) {
487
- return;
488
- }
489
- const handlePlay = () => {
490
- setIsMediaPlaying(true);
491
- onPlayStateChange?.(true);
492
- };
493
- const handlePause = () => {
494
- setIsMediaPlaying(false);
495
- onPlayStateChange?.(false);
496
- };
497
- const handleEnded = () => {
498
- setIsMediaPlaying(false);
499
- onPlayStateChange?.(false);
500
- };
501
- el.addEventListener("play", handlePlay);
502
- el.addEventListener("pause", handlePause);
503
- el.addEventListener("ended", handleEnded);
504
- return () => {
505
- el.removeEventListener("play", handlePlay);
506
- el.removeEventListener("pause", handlePause);
507
- el.removeEventListener("ended", handleEnded);
508
- };
509
- }, [onPlayStateChange]);
510
- useEffect(() => {
511
- const el = mediaRef.current;
512
- if (!el || !isPresentationMode) {
513
- return;
514
- }
515
- return scheduleMediaTrimAndFade(el, {
516
- trimStartMs: element.trimStartMs,
517
- trimEndMs: element.trimEndMs,
518
- fadeInDuration: element.fadeInDuration,
519
- fadeOutDuration: element.fadeOutDuration,
520
- volume: playbackVolume
521
- });
522
- }, [
523
- isPresentationMode,
524
- element.trimStartMs,
525
- element.trimEndMs,
526
- element.fadeInDuration,
527
- element.fadeOutDuration,
528
- playbackVolume
529
- ]);
530
- useEffect(() => {
531
- if (!isPresentationMode || !shouldAutoPlay) {
532
- return;
533
- }
534
- if (element.playAcrossSlides && element.mediaType === "audio") {
535
- const dataUrl = element.mediaData ?? resolvedDataUrl;
536
- if (registerCrossSlideAudio(element, dataUrl)) {
537
- return;
538
- }
539
- }
540
- const el = mediaRef.current;
541
- if (!el) {
542
- return;
543
- }
544
- const timer = window.setTimeout(() => {
545
- if (trimStartSec > 0) {
546
- el.currentTime = trimStartSec;
547
- }
548
- void el.play().catch(() => {
549
- });
550
- }, 100);
551
- return () => window.clearTimeout(timer);
552
- }, [isPresentationMode, shouldAutoPlay, element, resolvedDataUrl, trimStartSec]);
553
- const wrapperStyle = hideWhenNotPlaying ? {
554
- opacity: isMediaPlaying ? 1 : 0,
555
- transition: "opacity 0.3s ease",
556
- pointerEvents: isMediaPlaying ? "auto" : "none"
557
- } : {};
558
- const handleStopFullScreen = useCallback(() => {
559
- const el = mediaRef.current;
560
- if (el && !el.paused) {
561
- el.pause();
562
- }
563
- }, []);
564
- const noopOnPlay = useCallback(() => {
565
- }, []);
566
- return /* @__PURE__ */ jsxs("div", { className: "w-full h-full", style: wrapperStyle, children: [
567
- children({ mediaRef, onPlay: noopOnPlay, isMediaPlaying }),
568
- shouldShowMediaFullscreenStopButton({
569
- fullScreen: isFullScreen,
570
- presenting: isPresentationMode,
571
- playing: isMediaPlaying
572
- }) && /* @__PURE__ */ jsx(
573
- "button",
574
- {
575
- type: "button",
576
- className: "absolute bottom-3 right-3 z-30 rounded-full bg-black/50 hover:bg-black/70 text-white/80 hover:text-white p-2 transition-colors pointer-events-auto",
577
- onClick: handleStopFullScreen,
578
- "aria-label": t("pptx.media.stopFullscreenAria"),
579
- children: /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", stroke: "none", children: /* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "12", height: "12", rx: "1" }) })
580
- }
581
- )
582
- ] });
583
- }
584
- function label(key) {
585
- return key === void 0 ? void 0 : translationsEn[key];
586
- }
587
- function FallbackIcon({
588
- paths,
589
- size,
590
- className
591
- }) {
592
- if (paths.length === 0) {
593
- return null;
594
- }
595
- return /* @__PURE__ */ jsx(
596
- "svg",
597
- {
598
- width: size,
599
- height: size,
600
- viewBox: "0 0 24 24",
601
- fill: "none",
602
- stroke: "currentColor",
603
- strokeWidth: "1.5",
604
- className,
605
- children: paths.map((d) => /* @__PURE__ */ jsx("path", { d }, d))
606
- }
607
- );
608
- }
609
- function Badge({
610
- visual,
611
- element
612
- }) {
613
- if (visual.badge === "none") {
614
- return null;
615
- }
616
- const isMissing = visual.badge === "missing";
617
- return /* @__PURE__ */ jsxs(
618
- "div",
619
- {
620
- className: `absolute inset-0 flex flex-col items-center justify-center ${isMissing ? "gap-1" : ""}`,
621
- ...{ [MEDIA_CHROME_ATTRIBUTE]: visual.badge },
622
- children: [
623
- /* @__PURE__ */ jsx(
624
- FallbackIcon,
625
- {
626
- paths: mediaFallbackIcon(visual, element.mediaType),
627
- size: isMissing ? 32 : 48,
628
- className: isMissing ? "text-white/60" : "text-white/80 drop-shadow-md"
629
- }
630
- ),
631
- isMissing && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-white/60", children: label(mediaFallbackLabelKey(visual, element.mediaType)) })
632
- ]
633
- }
634
- );
635
- }
636
- function Placeholder({
637
- visual,
638
- element
639
- }) {
640
- if (visual.placeholder === "none") {
641
- return null;
642
- }
643
- const isMissing = visual.placeholder === "missing";
644
- return /* @__PURE__ */ jsxs(
645
- "div",
646
- {
647
- className: `w-full h-full flex flex-col items-center justify-center gap-1 pointer-events-none rounded ${isMissing ? "bg-black/30 border border-dashed border-white/20" : element.mediaType === "audio" ? "bg-black/10" : "bg-black/20"}`,
648
- ...{ [MEDIA_CHROME_ATTRIBUTE]: visual.placeholder },
649
- children: [
650
- /* @__PURE__ */ jsx(
651
- FallbackIcon,
652
- {
653
- paths: mediaFallbackIcon(visual, element.mediaType),
654
- size: isMissing ? 36 : element.mediaType === "audio" ? 24 : 32,
655
- className: isMissing ? "text-white/50" : "text-white/70"
656
- }
657
- ),
658
- /* @__PURE__ */ jsx("span", { className: `text-[10px] ${isMissing ? "text-white/50" : "text-white/70"}`, children: label(mediaFallbackLabelKey(visual, element.mediaType)) })
659
- ]
660
- }
661
- );
662
- }
663
- function renderMediaFallback({
664
- element,
665
- posterUrl,
666
- surface
667
- }) {
668
- const visual = mediaFallbackVisual(surface, {
669
- hasPoster: Boolean(posterUrl),
670
- missing: element.mediaMissing === true
671
- });
672
- if (visual.poster && posterUrl) {
673
- return /* @__PURE__ */ jsxs("div", { className: "w-full h-full relative pointer-events-none", children: [
674
- /* @__PURE__ */ jsx(
675
- "img",
676
- {
677
- src: posterUrl,
678
- alt: visual.dimPoster ? translationsEn["pptx.media.posterAlt"] : translationsEn["pptx.media.videoPosterAlt"],
679
- className: `w-full h-full${visual.dimPoster ? " opacity-50" : ""}`,
680
- style: getImageFitStyle(element)
681
- }
682
- ),
683
- /* @__PURE__ */ jsx(Badge, { visual, element })
684
- ] });
685
- }
686
- return /* @__PURE__ */ jsx(Placeholder, { visual, element });
687
- }
688
- function renderMediaElement(element, mediaDataUrls, options) {
689
- if (element.type !== "media") {
690
- return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[11px] text-white/80 pointer-events-none", children: translationsEn["pptx.media.title"] });
691
- }
692
- const mediaType = element.mediaType;
693
- const mediaPath = element.mediaPath;
694
- const mediaMimeType = element.mediaMimeType;
695
- const dataUrl = element.mediaData ?? (mediaPath ? mediaDataUrls.get(mediaPath) : void 0);
696
- const trimFragment = buildTrimFragment(element);
697
- const posterUrl = element.posterFrameData ?? void 0;
698
- const shouldLoop = mediaPlaybackAttributes(element).loop;
699
- const shouldAutoPlay = options?.autoPlay === true || element.autoPlay === true;
700
- const isFullScreen = options?.fullScreen === true;
701
- const isPresentationMode = options?.isPresentationMode === true;
702
- const showTransport = options?.showTransport ?? !isPresentationMode;
703
- const surface = {
704
- presenting: isPresentationMode,
705
- preview: options?.preview === true
706
- };
707
- if (element.mediaMissing) {
708
- return renderMediaFallback({ element, posterUrl, surface });
709
- }
710
- if (mediaType === "video") {
711
- const onlineVideo = getOnlineVideoEmbed(element);
712
- if (onlineVideo) {
713
- return /* @__PURE__ */ jsx(
714
- "iframe",
715
- {
716
- src: onlineVideo.embedUrl,
717
- title: translationsEn["pptx.media.onlineVideoTitle"],
718
- className: "w-full h-full border-0 pointer-events-auto",
719
- allow: ONLINE_VIDEO_IFRAME_ALLOW,
720
- allowFullScreen: true,
721
- sandbox: ONLINE_VIDEO_IFRAME_SANDBOX
722
- }
723
- );
724
- }
725
- if (dataUrl) {
726
- return /* @__PURE__ */ jsx(
727
- PresentationMediaController,
728
- {
729
- element,
730
- isPresentationMode,
731
- shouldAutoPlay,
732
- resolvedDataUrl: dataUrl,
733
- isFullScreen,
734
- onPlayStateChange: options?.onPlayStateChange,
735
- children: ({ mediaRef, onPlay }) => /* @__PURE__ */ jsx(
736
- VideoWithMetadata,
737
- {
738
- element,
739
- mediaRef,
740
- onPlay,
741
- dataUrl,
742
- trimFragment,
743
- mediaMimeType,
744
- posterUrl,
745
- shouldLoop,
746
- shouldAutoPlay,
747
- isFullScreen,
748
- isPresentationMode,
749
- showTransport
750
- }
751
- )
752
- }
753
- );
754
- }
755
- return renderMediaFallback({ element, posterUrl, surface });
756
- }
757
- if (mediaType === "audio") {
758
- if (dataUrl) {
759
- return /* @__PURE__ */ jsx(
760
- PresentationMediaController,
761
- {
762
- element,
763
- isPresentationMode,
764
- shouldAutoPlay,
765
- resolvedDataUrl: dataUrl,
766
- isFullScreen: false,
767
- onPlayStateChange: options?.onPlayStateChange,
768
- children: ({ mediaRef, onPlay }) => /* @__PURE__ */ jsx(
769
- AudioWithMetadata,
770
- {
771
- element,
772
- mediaRef,
773
- onPlay,
774
- dataUrl,
775
- trimFragment,
776
- mediaMimeType,
777
- shouldLoop,
778
- shouldAutoPlay,
779
- showTransport
780
- }
781
- )
782
- }
783
- );
784
- }
785
- return renderMediaFallback({ element, posterUrl, surface });
786
- }
787
- if (surface.presenting || surface.preview) {
788
- return null;
789
- }
790
- return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[11px] text-white/80 pointer-events-none", children: translationsEn["pptx.media.title"] });
791
- }
792
-
793
- // src/viewer/utils/text-utils.tsx
794
- function getTextStyleForElement(element, fallbackColor) {
795
- return buildTextBlockStyle(element, { fallbackColor });
796
- }
797
-
798
- // src/viewer/utils/text-layout.tsx
799
- function getTextLayoutStyle(element) {
800
- if (!hasTextProperties(element)) {
801
- return {};
802
- }
803
- const writingMode = toCssWritingMode(element.textStyle?.textDirection);
804
- const textOrientation = toCssTextOrientation(element.textStyle?.textDirection);
805
- const verticalDirection = toCssVerticalDirection(element.textStyle?.textDirection);
806
- const textWrapNone = element.textStyle?.textWrap === "none";
807
- const shapeAutoFitTextBox = element.textStyle?.autoFitMode === "shrink" && (element.type === "text" || element.locks?.txBox === true);
808
- const hasParagraphIndents = hasTextProperties(element) && element.paragraphIndents && element.paragraphIndents.length > 0;
809
- const paragraphMarginLeft = element.textStyle?.paragraphMarginLeft;
810
- const paragraphIndent = element.textStyle?.paragraphIndent;
811
- const marginLeft = !hasParagraphIndents && typeof paragraphMarginLeft === "number" && paragraphMarginLeft !== 0 ? paragraphMarginLeft : void 0;
812
- const textIndent = !hasParagraphIndents && typeof paragraphIndent === "number" && paragraphIndent !== 0 ? paragraphIndent : void 0;
813
- const bodyTop = element.textStyle?.bodyInsetTop ?? DEFAULT_BODY_INSET_TB_PX;
814
- const bodyBottom = element.textStyle?.bodyInsetBottom ?? DEFAULT_BODY_INSET_TB_PX;
815
- const bodyLayoutStyle = buildTextBodyLayoutStyle(element);
816
- return {
817
- ...bodyLayoutStyle,
818
- // Body inset only. Paragraph spacing (spcBef/spcAft) is applied
819
- // per-paragraph by the paragraph renderer, not collapsed here.
820
- paddingTop: bodyTop,
821
- paddingBottom: bodyBottom,
822
- writingMode,
823
- textOrientation,
824
- direction: verticalDirection ?? bodyLayoutStyle.direction,
825
- marginLeft: marginLeft ?? bodyLayoutStyle.marginLeft,
826
- textIndent,
827
- ...shapeAutoFitTextBox ? {
828
- width: textWrapNone ? "max-content" : "100%",
829
- minWidth: "100%",
830
- height: "max-content",
831
- minHeight: "100%",
832
- whiteSpace: "pre-wrap",
833
- wordBreak: textWrapNone ? "normal" : "break-word",
834
- overflow: "visible"
835
- } : {},
836
- ...textWrapNone ? {
837
- whiteSpace: "nowrap",
838
- overflow: "visible"
839
- } : {}
840
- };
841
- }
842
-
843
- // src/viewer/utils/text-effects.tsx
844
- function buildTextBody3DSceneStyle2(textStyle, elementSize) {
845
- return buildTextBody3DSceneStyle(textStyle, elementSize);
846
- }
847
-
848
- // src/viewer/utils/text-warp-css.tsx
849
- function getTextWarpStyle(textStyle) {
850
- const preset = textStyle?.textWarpPreset;
851
- if (!preset || preset === "textNoShape" || preset === "textPlain") {
852
- return void 0;
853
- }
854
- switch (preset) {
855
- // Arch shapes
856
- case "textArchUp":
857
- case "textArchUpPour":
858
- return {
859
- transform: "perspective(400px) rotateX(-12deg)",
860
- transformOrigin: "center bottom"
861
- };
862
- case "textArchDown":
863
- case "textArchDownPour":
864
- return {
865
- transform: "perspective(400px) rotateX(12deg)",
866
- transformOrigin: "center top"
867
- };
868
- // Button shapes (concave/convex)
869
- case "textButton":
870
- case "textButtonPour":
871
- return {
872
- transform: "perspective(500px) rotateX(8deg)",
873
- transformOrigin: "center top"
874
- };
875
- // Chevron / arrow
876
- case "textChevron":
877
- case "textChevronInverted":
878
- return {
879
- transform: "perspective(600px) rotateY(6deg)",
880
- transformOrigin: "center center"
881
- };
882
- // Circle
883
- case "textCircle":
884
- case "textCirclePour":
885
- return {
886
- transform: "perspective(350px) rotateX(-5deg) rotateY(5deg)",
887
- borderRadius: "50%"
888
- };
889
- // Slant
890
- case "textSlantUp":
891
- return {
892
- transform: "perspective(500px) rotateY(8deg) skewY(-4deg)",
893
- transformOrigin: "left center"
894
- };
895
- case "textSlantDown":
896
- return {
897
- transform: "perspective(500px) rotateY(-8deg) skewY(4deg)",
898
- transformOrigin: "right center"
899
- };
900
- // Fade
901
- case "textFadeUp":
902
- return {
903
- transform: "perspective(400px) rotateX(-10deg)",
904
- transformOrigin: "center bottom"
905
- };
906
- case "textFadeDown":
907
- return {
908
- transform: "perspective(400px) rotateX(10deg)",
909
- transformOrigin: "center top"
910
- };
911
- case "textFadeLeft":
912
- return {
913
- transform: "perspective(400px) rotateY(10deg)",
914
- transformOrigin: "right center"
915
- };
916
- case "textFadeRight":
917
- return {
918
- transform: "perspective(400px) rotateY(-10deg)",
919
- transformOrigin: "left center"
920
- };
921
- // Inflate / Deflate
922
- case "textInflate":
923
- case "textInflateBottom":
924
- case "textInflateTop":
925
- return {
926
- transform: "scaleY(1.15) scaleX(1.05)",
927
- transformOrigin: "center center"
928
- };
929
- case "textDeflate":
930
- case "textDeflateBottom":
931
- case "textDeflateTop":
932
- return {
933
- transform: "scaleY(0.88) scaleX(0.95)",
934
- transformOrigin: "center center"
935
- };
936
- // Wave
937
- case "textWave1":
938
- case "textWave2":
939
- case "textWave4":
940
- return {
941
- transform: "perspective(600px) rotateX(-3deg) skewX(3deg)",
942
- transformOrigin: "center center"
943
- };
944
- case "textDoubleWave1":
945
- return {
946
- transform: "perspective(600px) rotateX(-2deg) skewX(4deg)",
947
- transformOrigin: "center center"
948
- };
949
- // Triangle / Trapezoid
950
- case "textTriangle":
951
- case "textTriangleInverted":
952
- return {
953
- transform: "perspective(500px) rotateX(-6deg)",
954
- transformOrigin: "center bottom"
955
- };
956
- // Ring
957
- case "textRingInside":
958
- case "textRingOutside":
959
- return {
960
- transform: "perspective(350px) rotateX(-4deg) rotateY(4deg)",
961
- transformOrigin: "center center"
962
- };
963
- // Can
964
- case "textCanUp":
965
- case "textCanDown":
966
- return {
967
- transform: "perspective(400px) rotateX(-6deg)",
968
- transformOrigin: "center center"
969
- };
970
- // Cascade
971
- case "textCascadeUp":
972
- return {
973
- transform: "skewY(-8deg)",
974
- transformOrigin: "left top"
975
- };
976
- case "textCascadeDown":
977
- return {
978
- transform: "skewY(8deg)",
979
- transformOrigin: "left top"
980
- };
981
- // Curve
982
- case "textCurveUp":
983
- return {
984
- transform: "perspective(500px) rotateX(-8deg)",
985
- transformOrigin: "center bottom"
986
- };
987
- case "textCurveDown":
988
- return {
989
- transform: "perspective(500px) rotateX(8deg)",
990
- transformOrigin: "center top"
991
- };
992
- // Inflate/Deflate compound variants
993
- case "textDeflateInflate":
994
- return {
995
- transform: "scaleY(0.92) scaleX(1.04)",
996
- transformOrigin: "center center"
997
- };
998
- case "textDeflateInflateDeflate":
999
- return {
1000
- transform: "scaleY(0.85) scaleX(1.06)",
1001
- transformOrigin: "center center"
1002
- };
1003
- // Stop / Octagon
1004
- case "textStop":
1005
- return {
1006
- transform: "scaleX(0.9) scaleY(0.9)",
1007
- transformOrigin: "center center"
1008
- };
1009
- default:
1010
- return void 0;
1011
- }
1012
- }
1013
- function buildAnimStyle(state) {
1014
- if (!state) {
1015
- return void 0;
1016
- }
1017
- const style = {};
1018
- if (state.visible === false) {
1019
- style.visibility = "hidden";
1020
- }
1021
- if (state.cssAnimation) {
1022
- style.animation = state.cssAnimation;
1023
- }
1024
- return Object.keys(style).length > 0 ? style : void 0;
1025
- }
1026
- function wrapWithTextBuildAnimation(elementId, paraIndex, renderedSegments, paraRuns, subElementAnimStates, renderPiece) {
1027
- if (!subElementAnimStates || subElementAnimStates.size === 0) {
1028
- return renderedSegments;
1029
- }
1030
- const paraKey = `${elementId}${TEXT_BUILD_ID_SEP}p${paraIndex}`;
1031
- const paraState = subElementAnimStates.get(paraKey);
1032
- if (paraState) {
1033
- const style = buildAnimStyle(paraState);
1034
- return /* @__PURE__ */ jsx("span", { "data-anim-id": paraKey, style: { display: "inline", ...style }, children: renderedSegments }, paraKey);
1035
- }
1036
- const spec = buildTextBuildSpec(
1037
- elementId,
1038
- paraIndex,
1039
- paraRuns.map((run) => ({ text: run.text, style: run })),
1040
- subElementAnimStates
1041
- );
1042
- if (!spec?.spans) {
1043
- return renderedSegments;
1044
- }
1045
- return spec.spans.map((span, index) => {
1046
- const content = span.style && renderPiece ? renderPiece(span.style, span.text, String(index)) : span.text;
1047
- if (!span.animId) {
1048
- return /* @__PURE__ */ jsx(React8.Fragment, { children: content }, `ws-${index}`);
1049
- }
1050
- return /* @__PURE__ */ jsx(
1051
- "span",
1052
- {
1053
- "data-anim-id": span.animId,
1054
- style: textBuildSpanStyle(span),
1055
- children: content
1056
- },
1057
- span.animId
1058
- );
1059
- });
1060
- }
1061
- function renderUnderlineWords(text, isUnderlineWords, wordDecoration, metric, keyPrefix, inner) {
1062
- if (!isUnderlineWords || !text) {
1063
- return inner(text, keyPrefix, metric);
1064
- }
1065
- const pieces = splitWordsForUnderline(text);
1066
- if (pieces.length === 0) {
1067
- return inner(text, keyPrefix, metric);
1068
- }
1069
- const gapMetric = metric ? {
1070
- ...metric,
1071
- nestedStyle: metric.nestedStyle ? stripUnderlineDecoration(metric.nestedStyle) : void 0
1072
- } : void 0;
1073
- return pieces.map((piece, i) => {
1074
- const key = `${keyPrefix}-u${i}`;
1075
- const following = followingText(pieces, i, metric?.following);
1076
- if (!piece.underline) {
1077
- return /* @__PURE__ */ jsx(React8.Fragment, { children: inner(piece.text, key, gapMetric && { ...gapMetric, following }) }, key);
1078
- }
1079
- return /* @__PURE__ */ jsx("span", { style: wordDecoration, children: inner(piece.text, key, metric && { ...metric, following }) }, key);
1080
- });
1081
- }
1082
- function makeSegmentPieceRenderer(needsScriptFonts, scriptFonts, baseFontFamily, isUnderlineWords, wordDecoration, metric) {
1083
- return (text, key, following) => renderUnderlineWords(
1084
- text,
1085
- isUnderlineWords,
1086
- wordDecoration,
1087
- metric && { ...metric, following },
1088
- key,
1089
- (subText, subKey, subMetric) => renderMetricPieces(
1090
- subText,
1091
- subMetric,
1092
- subKey,
1093
- (pieceText, pieceKey) => renderScriptAwareText(
1094
- pieceText,
1095
- needsScriptFonts,
1096
- scriptFonts,
1097
- baseFontFamily,
1098
- pieceKey,
1099
- subMetric?.nestedStyle
1100
- )
1101
- )
1102
- );
1103
- }
1104
- function renderTabbedLine(line, ctx, keyPrefix, renderPiece, nestedStyle, underlineWords = false) {
1105
- const [lineLayout] = buildRunTabLines(
1106
- line,
1107
- ctx,
1108
- nestedStyle,
1109
- void 0,
1110
- underlineWords
1111
- );
1112
- return /* @__PURE__ */ jsx("span", { style: { display: "inline-block", whiteSpace: "nowrap" }, children: lineLayout.pieces.map((piece, i) => {
1113
- const key = `${keyPrefix}-tab-${i}`;
1114
- return /* @__PURE__ */ jsxs(React8.Fragment, { children: [
1115
- piece.leaderStyle ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: piece.leaderStyle, children: piece.leaderText }) : null,
1116
- piece.words ? piece.words.map((word, w) => /* @__PURE__ */ jsx("span", { style: word.style, children: renderPiece(word.text, `${key}-w${w}`) }, `${key}-w${w}`)) : /* @__PURE__ */ jsx("span", { style: piece.style, children: renderPiece(piece.text, key) })
1117
- ] }, key);
1118
- }) });
1119
- }
1120
- function renderEastAsianPieces(text, metric, keyPrefix, inner, baseStyle, following) {
1121
- const parts = splitEastAsianBreaks(text, metric.eastAsian, metric.font, following);
1122
- if (parts.length === 1 && !parts[0].style) {
1123
- return inner(parts[0].text, keyPrefix);
1124
- }
1125
- return parts.map((part, i) => {
1126
- const key = `${keyPrefix}-e${i}`;
1127
- return part.style ? /* @__PURE__ */ jsx("span", { style: { ...baseStyle ?? metric.nestedStyle, ...part.style }, children: inner(part.text, key) }, key) : /* @__PURE__ */ jsx(React8.Fragment, { children: inner(part.text, key) }, key);
1128
- });
1129
- }
1130
- function renderMetricPieces(text, metric, keyPrefix, inner) {
1131
- if (!metric || !text) {
1132
- return inner(text, keyPrefix);
1133
- }
1134
- const pieces = splitRunForMetrics(text, metric.font);
1135
- if (pieces.length <= 1) {
1136
- return renderEastAsianPieces(text, metric, keyPrefix, inner, void 0, metric.following);
1137
- }
1138
- return pieces.map((piece, i) => {
1139
- const key = `${keyPrefix}-w${i}`;
1140
- const style = {
1141
- ...metric.nestedStyle,
1142
- letterSpacing: pieceLetterSpacing(metric.authoredPx, piece.tracking)
1143
- };
1144
- return /* @__PURE__ */ jsx("span", { style, children: renderEastAsianPieces(
1145
- piece.text,
1146
- metric,
1147
- key,
1148
- inner,
1149
- style,
1150
- followingText(pieces, i, metric.following)
1151
- ) }, key);
1152
- });
1153
- }
1154
- function renderScriptAwareText(text, needsScriptFonts, scriptFonts, baseFontFamily, keyPrefix, nestedStyle) {
1155
- if (!needsScriptFonts || !text) {
1156
- return text;
1157
- }
1158
- const pieces = splitRunByScriptFont(
1159
- text,
1160
- scriptFonts,
1161
- baseFontFamily,
1162
- nestedStyle
1163
- );
1164
- if (!pieces) {
1165
- return text;
1166
- }
1167
- return pieces.map(
1168
- (piece, i) => piece.style ? /* @__PURE__ */ jsx("span", { style: piece.style, children: piece.text }, `${keyPrefix}-r${i}`) : /* @__PURE__ */ jsx(React8.Fragment, { children: piece.text }, `${keyPrefix}-r${i}`)
1169
- );
1170
- }
1171
- function renderSegmentContent(elementId, segmentIndex, textValue, lines, needsScriptFonts, scriptFonts, baseFontFamily, findHighlights, tabContext, metric, isUnderlineWords = false, wordDecoration) {
1172
- const segHl = findHighlights?.get(segmentIndex);
1173
- const renderLeaf = makeSegmentPieceRenderer(
1174
- needsScriptFonts,
1175
- scriptFonts,
1176
- baseFontFamily,
1177
- isUnderlineWords,
1178
- wordDecoration,
1179
- metric
1180
- );
1181
- if (!segHl || segHl.length === 0) {
1182
- return lines.map((line, lineIndex) => {
1183
- const lineKey = `${elementId}-seg-${segmentIndex}-line-${lineIndex}`;
1184
- return /* @__PURE__ */ jsxs(React8.Fragment, { children: [
1185
- tabContext && line.includes(" ") ? renderTabbedLine(
1186
- line,
1187
- tabContext,
1188
- lineKey,
1189
- renderLeaf,
1190
- metric?.nestedStyle,
1191
- isUnderlineWords
1192
- ) : renderLeaf(line, lineKey, lineIndex < lines.length - 1 ? "\n" : metric?.following),
1193
- lineIndex < lines.length - 1 ? /* @__PURE__ */ jsx("br", {}) : null
1194
- ] }, lineKey);
1195
- });
1196
- }
1197
- const sorted = [...segHl].sort((a, b) => a.startOffset - b.startOffset);
1198
- const chunks = [];
1199
- let cursor = 0;
1200
- for (const hl of sorted) {
1201
- if (hl.startOffset > cursor) {
1202
- chunks.push({
1203
- text: textValue.slice(cursor, hl.startOffset),
1204
- highlighted: false,
1205
- isCurrent: false
1206
- });
1207
- }
1208
- chunks.push({
1209
- text: textValue.slice(hl.startOffset, hl.startOffset + hl.length),
1210
- highlighted: true,
1211
- isCurrent: hl.isCurrent
1212
- });
1213
- cursor = hl.startOffset + hl.length;
1214
- }
1215
- if (cursor < textValue.length) {
1216
- chunks.push({
1217
- text: textValue.slice(cursor),
1218
- highlighted: false,
1219
- isCurrent: false
1220
- });
1221
- }
1222
- return chunks.map((chunk, ci) => {
1223
- const chunkKey = `${elementId}-seg-${segmentIndex}-hl-${ci}`;
1224
- return chunk.highlighted ? /* @__PURE__ */ jsx(
1225
- "mark",
1226
- {
1227
- style: {
1228
- ...metric?.nestedStyle,
1229
- backgroundColor: chunk.isCurrent ? "#f97316" : "#facc15",
1230
- color: "inherit",
1231
- borderRadius: 2
1232
- },
1233
- children: renderLeaf(chunk.text, chunkKey, followingText(chunks, ci, metric?.following))
1234
- },
1235
- chunkKey
1236
- ) : /* @__PURE__ */ jsx(React8.Fragment, { children: renderLeaf(chunk.text, chunkKey, followingText(chunks, ci, metric?.following)) }, chunkKey);
1237
- });
1238
- }
1239
- function renderEquationSegment(elementId, segmentIndex, equationXml, equationNumber, fontSize) {
1240
- const mathml = convertOmmlToMathMl(equationXml);
1241
- const safeMathml = mathml ? sanitizeMathMl(mathml) : "";
1242
- const equationContent = safeMathml ? /* @__PURE__ */ jsx(
1243
- "span",
1244
- {
1245
- className: "inline-block align-middle",
1246
- style: {
1247
- fontFamily: '"Cambria Math", "STIX Two Math", serif'
1248
- },
1249
- dangerouslySetInnerHTML: { __html: safeMathml }
1250
- }
1251
- ) : /* @__PURE__ */ jsx("span", { className: "inline-block px-1 py-0.5 rounded text-xs bg-gray-200/20 text-gray-400 italic", children: translationsEn["pptx.textSegment.equationFallback"] });
1252
- if (equationNumber) {
1253
- return /* @__PURE__ */ jsxs(
1254
- "span",
1255
- {
1256
- style: {
1257
- display: "flex",
1258
- justifyContent: "space-between",
1259
- alignItems: "center",
1260
- width: "100%",
1261
- fontSize
1262
- },
1263
- children: [
1264
- /* @__PURE__ */ jsxs("span", { style: { visibility: "hidden", whiteSpace: "nowrap" }, children: [
1265
- "(",
1266
- equationNumber,
1267
- ")"
1268
- ] }),
1269
- /* @__PURE__ */ jsx("span", { style: { textAlign: "center", flex: 1 }, children: equationContent }),
1270
- /* @__PURE__ */ jsxs(
1271
- "span",
1272
- {
1273
- style: {
1274
- whiteSpace: "nowrap",
1275
- fontFamily: '"Cambria Math", "STIX Two Math", serif'
1276
- },
1277
- children: [
1278
- "(",
1279
- equationNumber,
1280
- ")"
1281
- ]
1282
- }
1283
- )
1284
- ]
1285
- },
1286
- `${elementId}-seg-${segmentIndex}`
1287
- );
1288
- }
1289
- return /* @__PURE__ */ jsx("span", { style: fontSize ? { fontSize } : void 0, children: equationContent }, `${elementId}-seg-${segmentIndex}`);
1290
- }
1291
- function renderHyperlink(run, spanNode, key, ctx) {
1292
- const url = run.hyperlink?.url;
1293
- const onHyperlinkClick = ctx.onHyperlinkClick;
1294
- if (!url || !onHyperlinkClick) {
1295
- return spanNode;
1296
- }
1297
- const requireCtrlClick = ctx.requireCtrlClick;
1298
- const displayUrl = url.startsWith("ppaction://") ? url.replace(/^ppaction:\/\//u, "").split("?")[0] : url;
1299
- const targetFrame = run.hyperlink?.target;
1300
- const follow = (modified) => {
1301
- if (requireCtrlClick && !modified) {
1302
- return false;
1303
- }
1304
- onHyperlinkClick(url, targetFrame);
1305
- return true;
1306
- };
1307
- return /* @__PURE__ */ jsxs(
1308
- "span",
1309
- {
1310
- role: "link",
1311
- tabIndex: 0,
1312
- className: requireCtrlClick ? "group/link relative" : void 0,
1313
- style: { cursor: requireCtrlClick ? void 0 : "pointer", pointerEvents: "auto" },
1314
- title: run.hyperlink?.tooltip,
1315
- onClick: (e) => {
1316
- if (!follow(e.ctrlKey || e.metaKey)) {
1317
- return;
1318
- }
1319
- e.stopPropagation();
1320
- e.preventDefault();
1321
- },
1322
- onKeyDown: (e) => {
1323
- if (e.key !== "Enter" && e.key !== " ") {
1324
- return;
1325
- }
1326
- if (!follow(e.ctrlKey || e.metaKey)) {
1327
- return;
1328
- }
1329
- e.preventDefault();
1330
- e.stopPropagation();
1331
- },
1332
- children: [
1333
- spanNode,
1334
- requireCtrlClick && /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-0 top-full z-[9999] mt-1 max-w-64 opacity-0 transition-opacity duration-150 group-hover/link:opacity-100", children: /* @__PURE__ */ jsxs("span", { className: "flex flex-col rounded border border-border bg-popover px-2.5 py-1.5 shadow-lg", children: [
1335
- /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-foreground", children: displayUrl }),
1336
- /* @__PURE__ */ jsx("span", { className: "mt-0.5 text-[10px] text-muted-foreground", children: /* @__PURE__ */ jsx(FollowLinkHint, {}) })
1337
- ] }) })
1338
- ]
1339
- },
1340
- `${key}-link`
1341
- );
1342
- }
1343
- function FollowLinkHint() {
1344
- const { t } = useTranslation();
1345
- return /* @__PURE__ */ jsx(Fragment, { children: t("pptx.linkTooltip.followLink") });
1346
- }
1347
- function wrapWithTextReflection(run, key, spanNode, spanStyle, baseContent) {
1348
- if (!run.reflection) {
1349
- return spanNode;
1350
- }
1351
- return /* @__PURE__ */ jsxs("span", { style: { position: "relative", display: "inline-block" }, children: [
1352
- spanNode,
1353
- /* @__PURE__ */ jsx(
1354
- "span",
1355
- {
1356
- "aria-hidden": "true",
1357
- className: "pptx-react-text-reflection",
1358
- style: run.reflection,
1359
- children: /* @__PURE__ */ jsx("span", { style: spanStyle, children: baseContent })
1360
- }
1361
- )
1362
- ] }, `${key}-reflwrap`);
1363
- }
1364
- var BASELINE_FONT_SCALE = 0.65;
1365
- function renderParagraphRun(run, segment, ctx, keySuffix = "") {
1366
- const { element, fallbackColor, findHighlights, onHyperlinkClick } = ctx;
1367
- const segmentIndex = run.segmentIndex ?? 0;
1368
- const key = `${element.id}-seg-${segmentIndex}${keySuffix}`;
1369
- if (run.equation) {
1370
- return renderEquationSegment(
1371
- element.id,
1372
- segmentIndex,
1373
- run.equation.xml,
1374
- run.equation.number,
1375
- typeof run.style.fontSize === "string" ? run.style.fontSize : void 0
1376
- );
1377
- }
1378
- const segmentStyle = segment?.style ?? {};
1379
- const textValue = run.text;
1380
- const lines = textValue.split("\n");
1381
- const resolvedSegmentColor = normalizeHexColor(
1382
- segmentStyle.color || element.textStyle?.color,
1383
- run.hyperlink ? HYPERLINK_COLOR : fallbackColor
1384
- );
1385
- const rawBaseline = typeof segmentStyle.baseline === "number" ? segmentStyle.baseline : 0;
1386
- const baselineFraction = rawBaseline === 0 ? 0 : Math.abs(rawBaseline) >= 1e3 ? rawBaseline / 1e5 : rawBaseline / 100;
1387
- const baselineFontScale = baselineFraction !== 0 ? BASELINE_FONT_SCALE : 1;
1388
- const rawFontSize = segmentStyle.fontSize || element.textStyle?.fontSize || DEFAULT_TEXT_FONT_SIZE;
1389
- const baseFontSize = scaleFontSizeForAutoFit(
1390
- rawFontSize,
1391
- resolveAutoFitFontScale(element.textStyle)
1392
- );
1393
- const baselineShift = baselineFraction !== 0 ? `${baselineFraction * baseFontSize}px` : void 0;
1394
- const fontKerning = resolveFontKerning(segmentStyle.kerning, baseFontSize);
1395
- const scriptFallbackWins = Boolean(
1396
- segmentStyle.scriptFallbackFont && segmentStyle.fontFamilyIsCascadeDefault
1397
- );
1398
- const rawFontFamily = segmentStyle.fontFamily || element.textStyle?.fontFamily;
1399
- const baseFontFamily = scriptFallbackWins ? getSubstituteFontFamily(segmentStyle.scriptFallbackFont) : rawFontFamily ? getSubstituteFontFamily(
1400
- rawFontFamily,
1401
- parsePanoseString(segmentStyle.latinFontPanose ?? element.textStyle?.latinFontPanose)
1402
- ) : DEFAULT_FONT_FAMILY2;
1403
- const scriptFonts = resolveScriptFontSet(segmentStyle, element.textStyle, baseFontFamily);
1404
- const needsScriptFonts = hasDistinctScriptFonts(scriptFonts);
1405
- const authoredLetterSpacing = authoredLetterSpacingPx(segmentStyle);
1406
- const metricFont = {
1407
- fontFamily: baseFontFamily,
1408
- fontSizePx: baseFontSize * baselineFontScale,
1409
- bold: Boolean(segmentStyle.bold),
1410
- italic: Boolean(segmentStyle.italic)
1411
- };
1412
- const nestedStyle = nestedTextDecorationStyle(run.style);
1413
- const metricContext = {
1414
- font: metricFont,
1415
- authoredPx: authoredLetterSpacing,
1416
- nestedStyle,
1417
- eastAsian: ctx.eastAsianBreaks,
1418
- following: ctx.eastAsianFollowing
1419
- };
1420
- const spanStyle = { ...run.style };
1421
- spanStyle.color = resolvedSegmentColor;
1422
- spanStyle.fontSize = baseFontSize * baselineFontScale;
1423
- spanStyle.fontFamily = baseFontFamily;
1424
- spanStyle.verticalAlign = baselineShift;
1425
- spanStyle.fontKerning = fontKerning;
1426
- spanStyle.letterSpacing = pieceLetterSpacing(
1427
- authoredLetterSpacing,
1428
- resolveMetricTrackingPx(textValue, metricFont)
1429
- );
1430
- const hollow = hollowTextFillStyle(segmentStyle, {
1431
- color: resolvedSegmentColor,
1432
- textStroke: typeof spanStyle.WebkitTextStroke === "string" ? spanStyle.WebkitTextStroke : void 0
1433
- });
1434
- if (hollow) {
1435
- Object.assign(spanStyle, hollow);
1436
- }
1437
- const runRtl = segmentStyle.rtl;
1438
- if (runRtl !== void 0) {
1439
- spanStyle.direction = runRtl ? "rtl" : "ltr";
1440
- spanStyle.unicodeBidi = runRtl !== ctx.paragraphRtl ? "bidi-override" : "embed";
1441
- }
1442
- const isUnderlineWords = splitsUnderlineIntoWords(segmentStyle);
1443
- const wordDecoration = isUnderlineWords ? nestedStyle ?? run.underlineWordPieces?.find((piece) => piece.style)?.style : void 0;
1444
- if (isUnderlineWords) {
1445
- const stripped = stripUnderlineDecoration(spanStyle);
1446
- spanStyle.textDecoration = stripped.textDecoration;
1447
- delete spanStyle.textDecorationStyle;
1448
- delete spanStyle.textDecorationThickness;
1449
- delete spanStyle.textUnderlineOffset;
1450
- delete spanStyle.textDecorationColor;
1451
- }
1452
- const baseContent = renderSegmentContent(
1453
- element.id,
1454
- segmentIndex,
1455
- textValue,
1456
- lines,
1457
- needsScriptFonts,
1458
- scriptFonts,
1459
- baseFontFamily,
1460
- findHighlights,
1461
- buildTabContext(
1462
- element.textStyle?.tabStops,
1463
- element.textStyle?.defaultTabSize,
1464
- baseFontSize,
1465
- baseFontFamily,
1466
- Boolean(segmentStyle.bold),
1467
- Boolean(segmentStyle.italic),
1468
- { language: segmentStyle.language, rtl: ctx.paragraphRtl === true }
1469
- ),
1470
- metricContext,
1471
- isUnderlineWords,
1472
- wordDecoration
1473
- );
1474
- const spanNode = /* @__PURE__ */ jsx("span", { "data-seg-idx": segmentIndex, style: spanStyle, children: renderRubyOrText(run, baseContent) }, key);
1475
- const reflectedNode = wrapWithTextReflection(run, key, spanNode, spanStyle, baseContent);
1476
- return run.hyperlink && onHyperlinkClick ? renderHyperlink(run, reflectedNode, key, ctx) : reflectedNode;
1477
- }
1478
- function renderRubyOrText(run, baseContent) {
1479
- if (!run.ruby) {
1480
- return baseContent;
1481
- }
1482
- return /* @__PURE__ */ jsxs("ruby", { children: [
1483
- baseContent,
1484
- /* @__PURE__ */ jsx("rp", { children: "(" }),
1485
- /* @__PURE__ */ jsx("rt", { style: run.ruby.style, children: run.ruby.text }),
1486
- /* @__PURE__ */ jsx("rp", { children: ")" })
1487
- ] });
1488
- }
1489
- function renderSeededListRun(seed, runs, segments, context) {
1490
- const sourceRun = runs.find((candidate) => candidate.segmentIndex === seed.segmentIndex);
1491
- const segment = sourceRun ? segments[seed.segmentIndex] : { text: seed.text, style: seed.style };
1492
- const resolvedSegment = {
1493
- ...segment,
1494
- style: withInlineListDecorationDefaults(segment.style, context.element.textStyle)
1495
- };
1496
- const decorationCss = segmentStyleToCss(
1497
- resolvedSegment,
1498
- resolveAutoFitFontScale(context.element.textStyle)
1499
- );
1500
- applyUnderlineVariant(decorationCss, resolvedSegment);
1501
- const run = sourceRun ? { ...sourceRun, style: { ...sourceRun.style, ...nestedTextDecorationStyle(decorationCss) } } : {
1502
- text: seed.text,
1503
- style: decorationCss,
1504
- segmentIndex: seed.segmentIndex
1505
- };
1506
- const rendered = renderParagraphRun(run, resolvedSegment, context);
1507
- if (!React8.isValidElement(rendered)) {
1508
- return rendered;
1509
- }
1510
- return React8.cloneElement(rendered, {
1511
- "data-pptx-list-run": seed.token,
1512
- ...seed.text === "" ? { children: /* @__PURE__ */ jsx("br", {}) } : {}
1513
- });
1514
- }
1515
- function renderTextSegments(element, fallbackColor, emptyFallback, findHighlights, onHyperlinkClick, fieldContext, subElementAnimStates, segmentOverrides, requireCtrlClick, placeholderPrompt, inlineListSeed) {
1516
- if (!hasTextProperties(element)) {
1517
- return emptyFallback || null;
1518
- }
1519
- const segments = segmentOverrides ?? element.textSegments;
1520
- if (!segments || segments.length === 0) {
1521
- if (placeholderPrompt) {
1522
- return /* @__PURE__ */ jsx("span", { style: placeholderPrompt.style, children: placeholderPrompt.text });
1523
- }
1524
- return element.text || emptyFallback || "";
1525
- }
1526
- const paragraphs = buildParagraphs(element, fieldContext, segmentOverrides, {
1527
- preserveTrailingEmpty: inlineListSeed ? true : void 0,
1528
- eastAsianRunPieces: false
1529
- });
1530
- const ctx = {
1531
- element,
1532
- fallbackColor,
1533
- findHighlights,
1534
- onHyperlinkClick,
1535
- requireCtrlClick
1536
- };
1537
- return paragraphs.map(
1538
- (para, paraIndex) => renderParagraph(
1539
- para,
1540
- paraIndex,
1541
- paragraphs.length,
1542
- segments,
1543
- ctx,
1544
- subElementAnimStates,
1545
- inlineListSeed
1546
- )
1547
- );
1548
- }
1549
- function joinRunsBySegment(runs) {
1550
- const out = [];
1551
- for (const run of runs) {
1552
- const last = out[out.length - 1];
1553
- if (last && run.segmentIndex !== void 0 && last.segmentIndex === run.segmentIndex && !last.equation && !run.equation) {
1554
- last.text += run.text;
1555
- continue;
1556
- }
1557
- out.push({ ...run });
1558
- }
1559
- return out;
1560
- }
1561
- function renderParagraph(para, paraIndex, paraCount, segments, ctx, subElementAnimStates, inlineListSeed) {
1562
- const element = ctx.element;
1563
- const runCtx = {
1564
- ...ctx,
1565
- paragraphRtl: para.rtl,
1566
- eastAsianBreaks: para.eastAsianBreaks
1567
- };
1568
- const runs = joinRunsBySegment(para.runs);
1569
- const listParagraph = inlineListSeed?.paragraphs[paraIndex];
1570
- const renderedRuns = listParagraph ? listParagraph.runs.map((run) => renderSeededListRun(run, runs, segments, runCtx)) : runs.map(
1571
- (run, i) => renderParagraphRun(run, segments[run.segmentIndex ?? -1], {
1572
- ...runCtx,
1573
- eastAsianFollowing: para.eastAsianBreaks ? followingText(runs, i) : void 0
1574
- })
1575
- );
1576
- const paraStyle = {
1577
- // `text-align`, BiDi `direction` / `unicode-bidi` and the kinsoku
1578
- // line-break rules, resolved by shared from this paragraph's own `a:pPr`.
1579
- ...para.paragraphStyle
1580
- };
1581
- if (para.strutFontSizePx !== void 0) {
1582
- paraStyle.fontSize = para.strutFontSizePx;
1583
- }
1584
- if (para.spaceBeforePx !== void 0) {
1585
- paraStyle.marginTop = para.spaceBeforePx;
1586
- }
1587
- if (para.spaceAfterPx !== void 0) {
1588
- paraStyle.marginBottom = para.spaceAfterPx;
1589
- }
1590
- if (para.lineHeight !== void 0) {
1591
- paraStyle.lineHeight = para.lineHeight;
1592
- }
1593
- if (para.marginLeftPx !== void 0) {
1594
- if (para.rtl === true) {
1595
- paraStyle.marginRight = para.marginLeftPx;
1596
- } else {
1597
- paraStyle.marginLeft = para.marginLeftPx;
1598
- }
1599
- }
1600
- if (para.textIndentPx !== void 0) {
1601
- paraStyle.textIndent = para.textIndentPx;
1602
- }
1603
- const marker = renderBulletMarker(para, paraIndex, element.id);
1604
- const wrappedContent = wrapWithTextBuildAnimation(
1605
- element.id,
1606
- paraIndex,
1607
- renderedRuns,
1608
- runs,
1609
- subElementAnimStates,
1610
- // A staged build splits the paragraph but must not flatten it: each piece
1611
- // is re-rendered through the SAME run renderer, so it keeps its run's
1612
- // font, size, colour and decoration while it animates.
1613
- (run, text, pieceKey) => renderParagraphRun(
1614
- { ...run, text },
1615
- segments[run.segmentIndex ?? -1],
1616
- runCtx,
1617
- `-build-${pieceKey}`
1618
- )
1619
- );
1620
- const needsWrapper = Boolean(listParagraph) || Object.keys(paraStyle).length > 0 || marker !== null;
1621
- if (!needsWrapper) {
1622
- return /* @__PURE__ */ jsxs(React8.Fragment, { children: [
1623
- wrappedContent,
1624
- paraIndex < paraCount - 1 ? /* @__PURE__ */ jsx("br", {}) : null
1625
- ] }, `${element.id}-para-${paraIndex}`);
1626
- }
1627
- return /* @__PURE__ */ jsxs(
1628
- "div",
1629
- {
1630
- style: paraStyle,
1631
- "data-pptx-list-paragraph": listParagraph?.token,
1632
- children: [
1633
- marker,
1634
- para.isEmpty && !listParagraph ? /* @__PURE__ */ jsx("br", {}) : wrappedContent
1635
- ]
1636
- },
1637
- `${element.id}-para-${paraIndex}`
1638
- );
1639
- }
1640
- function renderBulletMarker(para, paraIndex, elementId) {
1641
- const picture = para.bulletPicture;
1642
- if (picture?.src) {
1643
- return /* @__PURE__ */ jsx(
1644
- "img",
1645
- {
1646
- "data-pptx-bullet-marker": true,
1647
- contentEditable: false,
1648
- src: picture.src,
1649
- alt: picture.accessibleLabel,
1650
- style: {
1651
- width: picture.sizePx,
1652
- height: picture.sizePx,
1653
- display: "inline-block",
1654
- verticalAlign: "middle",
1655
- marginInlineEnd: 4,
1656
- objectFit: "contain"
1657
- }
1658
- },
1659
- `${elementId}-para-${paraIndex}-bullet-img`
1660
- );
1661
- }
1662
- if (para.bulletMarker === void 0) {
1663
- return null;
1664
- }
1665
- return /* @__PURE__ */ jsx(
1666
- "span",
1667
- {
1668
- className: "pptx-bullet",
1669
- "data-pptx-bullet-marker": true,
1670
- contentEditable: false,
1671
- style: para.bulletStyle,
1672
- "aria-label": picture?.accessibleLabel,
1673
- children: para.bulletMarker
1674
- },
1675
- `${elementId}-para-${paraIndex}-bullet`
1676
- );
1677
- }
1678
- function getDuotoneColors(element) {
1679
- if (!isImageLikeElement(element)) {
1680
- return void 0;
1681
- }
1682
- return element.imageEffects?.duotone;
1683
- }
1684
- function buildLineShadowCss(element) {
1685
- if (!hasShapeProperties(element)) {
1686
- return void 0;
1687
- }
1688
- const ss = element.shapeStyle;
1689
- if (!ss?.lineShadowColor || ss.lineShadowColor === "transparent") {
1690
- return void 0;
1691
- }
1692
- const offsetX = typeof ss.lineShadowOffsetX === "number" ? ss.lineShadowOffsetX : 2;
1693
- const offsetY = typeof ss.lineShadowOffsetY === "number" ? ss.lineShadowOffsetY : 2;
1694
- const blur = typeof ss.lineShadowBlur === "number" ? Math.max(0, ss.lineShadowBlur) : 4;
1695
- const opacity = typeof ss.lineShadowOpacity === "number" ? Math.max(0, Math.min(1, ss.lineShadowOpacity)) : 0.35;
1696
- return `${Math.round(offsetX)}px ${Math.round(offsetY)}px ${Math.round(blur)}px ${colorWithOpacity(
1697
- normalizeHexColor(ss.lineShadowColor, "#000000"),
1698
- opacity
1699
- )}`;
1700
- }
1701
- function buildLineGlowFilter(element) {
1702
- if (!hasShapeProperties(element)) {
1703
- return void 0;
1704
- }
1705
- const ss = element.shapeStyle;
1706
- if (!ss?.lineGlowColor || ss.lineGlowColor === "transparent" || !ss.lineGlowRadius) {
1707
- return void 0;
1708
- }
1709
- const glowOpacity = typeof ss.lineGlowOpacity === "number" ? ss.lineGlowOpacity : 0.75;
1710
- const glowRad = Math.round(Math.max(0, ss.lineGlowRadius));
1711
- const glowCol = colorWithOpacity(normalizeHexColor(ss.lineGlowColor, "#ffff00"), glowOpacity);
1712
- return `drop-shadow(0 0 ${glowRad}px ${glowCol})`;
1713
- }
1714
- function getDagDuotoneFilterId(elementId) {
1715
- return `dag-duotone-${elementId}`;
1716
- }
1717
- function hasDagDuotoneEffect(element) {
1718
- if (!hasShapeProperties(element)) {
1719
- return false;
1720
- }
1721
- return Boolean(element.shapeStyle?.dagDuotone);
1722
- }
1723
- function renderDagDuotoneSvgFilter(elementId, color1, color2) {
1724
- const filterId = getDagDuotoneFilterId(elementId);
1725
- const c1 = hexToRgbUnit(color1);
1726
- const c2 = hexToRgbUnit(color2);
1727
- const grayscaleMatrix = [
1728
- 0.2126,
1729
- 0.7152,
1730
- 0.0722,
1731
- 0,
1732
- 0,
1733
- 0.2126,
1734
- 0.7152,
1735
- 0.0722,
1736
- 0,
1737
- 0,
1738
- 0.2126,
1739
- 0.7152,
1740
- 0.0722,
1741
- 0,
1742
- 0,
1743
- 0,
1744
- 0,
1745
- 0,
1746
- 1,
1747
- 0
1748
- ].join(" ");
1749
- return /* @__PURE__ */ jsx(
1750
- "svg",
1751
- {
1752
- width: 0,
1753
- height: 0,
1754
- style: { position: "absolute", overflow: "hidden" },
1755
- "aria-hidden": "true",
1756
- children: /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("filter", { id: filterId, colorInterpolationFilters: "sRGB", children: [
1757
- /* @__PURE__ */ jsx("feColorMatrix", { type: "matrix", values: grayscaleMatrix }),
1758
- /* @__PURE__ */ jsxs("feComponentTransfer", { children: [
1759
- /* @__PURE__ */ jsx("feFuncR", { type: "linear", slope: c2.r - c1.r, intercept: c1.r }),
1760
- /* @__PURE__ */ jsx("feFuncG", { type: "linear", slope: c2.g - c1.g, intercept: c1.g }),
1761
- /* @__PURE__ */ jsx("feFuncB", { type: "linear", slope: c2.b - c1.b, intercept: c1.b })
1762
- ] })
1763
- ] }) })
1764
- }
1765
- );
1766
- }
1767
-
1768
- // src/viewer/utils/shape-visual-3d.ts
1769
- function build3DExtrusionData2(shape3d, scene3d, fillColor, elementWidth, elementHeight) {
1770
- return build3DExtrusionData(
1771
- shape3d,
1772
- scene3d,
1773
- fillColor,
1774
- elementWidth,
1775
- elementHeight
1776
- );
1777
- }
1778
- function apply3dEffects2(base, scene3d, shape3d, fillColorFallback, elementSize, elementId) {
1779
- apply3dEffects(
1780
- base,
1781
- scene3d,
1782
- shape3d,
1783
- fillColorFallback,
1784
- elementSize,
1785
- elementId
1786
- );
1787
- }
1788
-
1789
- // src/viewer/utils/shape-visual-style.ts
1790
- function rendersCustomVectorPath(element) {
1791
- return (element.type === "shape" || element.type === "image" || element.type === "picture") && Boolean(element.pathData) && typeof element.pathWidth === "number" && element.pathWidth > 0 && typeof element.pathHeight === "number" && element.pathHeight > 0;
1792
- }
1793
- function rendersWedgeCalloutVector(element) {
1794
- return element.type === "shape" && isWedgeCalloutPresetShape(element.shapeType);
1795
- }
1796
- function getShapeVisualStyle(element, _hasFill, fillColor, _strokeWidth, _strokeColor, animatesFill, animatesStroke, parentGroupFill) {
1797
- if (element.type === "group") {
1798
- const fx2 = getComputedEffectStyle(element);
1799
- const groupStyle = {};
1800
- if (fx2.filter) {
1801
- groupStyle.filter = fx2.filter;
1802
- }
1803
- if (fx2.overflowVisible) {
1804
- groupStyle.overflow = "visible";
1805
- }
1806
- return groupStyle;
1807
- }
1808
- if (!hasShapeProperties(element)) {
1809
- return {};
1810
- }
1811
- const ss = element.shapeStyle;
1812
- const fill = animatesFill ? void 0 : getComputedFillStyle(element, parentGroupFill);
1813
- const fx = getComputedEffectStyle(element);
1814
- const stroke = getComputedStrokeStyle(element);
1815
- const base = {
1816
- // A gradient fill REPLACES the solid fill, it does not sit on top of it;
1817
- // the whole precedence lives in shared's `getComputedFillStyle`.
1818
- backgroundColor: fill?.backgroundColor ?? "transparent",
1819
- backgroundImage: fill?.backgroundImage,
1820
- backgroundRepeat: fill?.backgroundRepeat,
1821
- backgroundSize: fill?.backgroundSize,
1822
- backgroundPosition: fill?.backgroundPosition,
1823
- // The container always carries `box-sizing: border-box`, so the default
1824
- // `background-origin: padding-box` would size the paint 2px smaller than
1825
- // the shape and make a gradient tile wrap, painting a 1px sliver of its
1826
- // opposite end along the edge. Paint from the border box instead.
1827
- backgroundOrigin: "border-box",
1828
- boxShadow: fx.boxShadow,
1829
- // Reflection is no longer a single CSS property (`-webkit-box-reflect`
1830
- // never worked in Firefox): `ElementRenderer` renders a mirrored sibling
1831
- // node instead, using shared's `getReflectionWrapperStyle` directly (see
1832
- // `ReflectionOverlay`).
1833
- filter: fx.filter,
1834
- opacity: fx.opacity,
1835
- // Only proxy the DAG blend onto the whole element for the blend-only case:
1836
- // with an overlay colour, `ShapeEffectOverlay` paints a separate blended
1837
- // tint layer (shared decides which, and returns only one of the two).
1838
- mixBlendMode: fx.mixBlendMode,
1839
- // An unstroked element must occupy EXACTLY its authored box, so the
1840
- // selection/hover affordance is an `outline` (see `ElementRenderer`) and
1841
- // the border collapses to 0 rather than to a transparent 1px.
1842
- borderWidth: stroke.borderWidth,
1843
- borderColor: animatesStroke ? void 0 : stroke.borderColor,
1844
- borderStyle: stroke.borderStyle,
1845
- // Inherited SVG presentation properties: written on the container so the
1846
- // freeform `<path>` and the stroke overlay both pick them up.
1847
- strokeLinejoin: stroke.strokeLinejoin,
1848
- strokeMiterlimit: stroke.strokeMiterlimit,
1849
- strokeLinecap: stroke.strokeLinecap
1850
- };
1851
- Object.assign(
1852
- base,
1853
- getAnimationColorBaseStyle(element, {
1854
- animatesFill,
1855
- animatesStroke,
1856
- parentGroupFill
1857
- })
1858
- );
1859
- if (element.type === "image" || element.type === "picture") {
1860
- base.borderWidth = 0;
1861
- base.borderColor = void 0;
1862
- base.borderStyle = void 0;
1863
- }
1864
- apply3dEffects2(
1865
- base,
1866
- ss?.scene3d,
1867
- ss?.shape3d,
1868
- ss?.fillColor ?? fillColor,
1869
- {
1870
- width: element.width,
1871
- height: element.height
1872
- },
1873
- element.id
1874
- );
1875
- if (rendersCustomVectorPath(element) || rendersWedgeCalloutVector(element) || isStrokeOnlyPresetElement(element)) {
1876
- base.backgroundColor = "transparent";
1877
- base.backgroundImage = void 0;
1878
- base.borderWidth = void 0;
1879
- base.borderColor = void 0;
1880
- base.borderStyle = void 0;
1881
- }
1882
- if (animatesFill) {
1883
- base.backgroundColor = void 0;
1884
- base.backgroundImage = void 0;
1885
- }
1886
- if (rendersWedgeCalloutVector(element)) {
1887
- return base;
1888
- }
1889
- const geometry = resolveShapeGeometry(element);
1890
- switch (geometry.kind) {
1891
- case "bare":
1892
- return { backgroundColor: "transparent", borderWidth: 0, borderStyle: "none" };
1893
- case "strokeOnly":
1894
- return { ...base, backgroundColor: "transparent", backgroundImage: void 0 };
1895
- case "borderRadius":
1896
- return { ...base, borderRadius: geometry.radius };
1897
- case "clipPath":
1898
- return { ...base, clipPath: geometry.clipPath };
1899
- case "lineEdge":
1900
- return {
1901
- ...base,
1902
- backgroundColor: "transparent",
1903
- borderWidth: 0,
1904
- borderTopWidth: geometry.strokeWidth,
1905
- borderTopColor: stroke.borderColor,
1906
- borderTopStyle: stroke.borderStyle ?? "solid"
1907
- };
1908
- default:
1909
- return base;
1910
- }
1911
- }
1912
- function getConnectorPathGeometry2(element) {
1913
- return getConnectorPathGeometry(element);
1914
- }
1915
- var ARROW_SIZE_SCALE = {
1916
- sm: 0.6,
1917
- med: 1,
1918
- lg: 1.5
1919
- };
1920
- function renderConnectorMarker(markerId, arrowType, color, arrowWidth, arrowLength) {
1921
- if (!arrowType || arrowType === "none") {
1922
- return null;
1923
- }
1924
- const wScale = ARROW_SIZE_SCALE[arrowWidth || "med"] ?? 1;
1925
- const lScale = ARROW_SIZE_SCALE[arrowLength || "med"] ?? 1;
1926
- const mw = Math.round(8 * lScale);
1927
- const mh = Math.round(8 * wScale);
1928
- return /* @__PURE__ */ jsx(
1929
- "marker",
1930
- {
1931
- id: markerId,
1932
- markerWidth: mw,
1933
- markerHeight: mh,
1934
- refX: 8,
1935
- refY: 5,
1936
- orient: "auto-start-reverse",
1937
- viewBox: "0 0 10 10",
1938
- markerUnits: "strokeWidth",
1939
- children: arrowType === "triangle" ? /* @__PURE__ */ jsx("polygon", { points: "0,0 10,5 0,10", fill: color }) : arrowType === "stealth" ? /* @__PURE__ */ jsx("polygon", { points: "0,0 10,5 0,10 3.4,5", fill: color }) : arrowType === "diamond" ? /* @__PURE__ */ jsx("polygon", { points: "0,5 5,0 10,5 5,10", fill: color }) : arrowType === "oval" ? /* @__PURE__ */ jsx("ellipse", { cx: 5, cy: 5, rx: 4, ry: 3.3, fill: color }) : /* @__PURE__ */ jsx("path", { d: "M0 0 L10 5 L0 10", fill: "none", stroke: color, strokeWidth: 1.6 })
1940
- }
1941
- );
1942
- }
1943
- function renderSvgGradientDefs(gradient) {
1944
- if (!gradient) {
1945
- return null;
1946
- }
1947
- if (gradient.kind === "rectPath") {
1948
- return /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("pattern", { id: gradient.id, patternUnits: "objectBoundingBox", width: 1, height: 1, children: /* @__PURE__ */ jsx("image", { href: gradient.href, x: 0, y: 0, width: 1, height: 1, preserveAspectRatio: "none" }) }) });
1949
- }
1950
- const stops = gradient.stops.map((stop, index) => /* @__PURE__ */ jsx(
1951
- "stop",
1952
- {
1953
- offset: stop.offset,
1954
- stopColor: stop.color,
1955
- stopOpacity: typeof stop.opacity === "number" ? stop.opacity : void 0
1956
- },
1957
- index
1958
- ));
1959
- return /* @__PURE__ */ jsx("defs", { children: gradient.kind === "radial" ? /* @__PURE__ */ jsx("radialGradient", { id: gradient.id, cx: gradient.cx, cy: gradient.cy, r: gradient.r, children: stops }) : /* @__PURE__ */ jsx(
1960
- "linearGradient",
1961
- {
1962
- id: gradient.id,
1963
- x1: gradient.x1,
1964
- y1: gradient.y1,
1965
- x2: gradient.x2,
1966
- y2: gradient.y2,
1967
- children: stops
1968
- }
1969
- ) });
1970
- }
1971
- function joinStyle(shapeStyle) {
1972
- const lineJoin = shapeStyle?.lineJoin === "bevel" ? "bevel" : shapeStyle?.lineJoin === "miter" ? "miter" : "round";
1973
- const miterLimit = lineJoin === "miter" && typeof shapeStyle?.miterLimit === "number" ? Math.max(shapeStyle.miterLimit / 1e5, 1) : void 0;
1974
- return { lineJoin, miterLimit };
1975
- }
1976
- function strokeContext(shapeStyle, strokePaint, strokeWidth, dashArray) {
1977
- const { lineJoin, miterLimit } = joinStyle(shapeStyle);
1978
- return {
1979
- strokePaint,
1980
- strokeWidth,
1981
- dashArray,
1982
- lineCap: svgLineCap(shapeStyle?.lineCap),
1983
- lineJoin,
1984
- miterLimit,
1985
- offsets: getCompoundLineOffsets(shapeStyle?.compoundLine, strokeWidth),
1986
- widths: getCompoundLineWidths(shapeStyle?.compoundLine, strokeWidth)
1987
- };
1988
- }
1989
- function strokeStrands(d, keyBase, ctx) {
1990
- return ctx.offsets.map((offset, idx) => /* @__PURE__ */ jsx(
1991
- "path",
1992
- {
1993
- d,
1994
- fill: "none",
1995
- stroke: ctx.strokePaint,
1996
- strokeWidth: Math.max(ctx.widths[idx] ?? ctx.strokeWidth, 1),
1997
- strokeDasharray: ctx.dashArray,
1998
- strokeLinecap: ctx.lineCap,
1999
- strokeLinejoin: ctx.lineJoin,
2000
- strokeMiterlimit: ctx.miterLimit,
2001
- vectorEffect: "non-scaling-stroke",
2002
- style: offset !== 0 ? { transform: `translate(0, ${offset}px)` } : void 0
2003
- },
2004
- `${keyBase}-s${idx}`
2005
- ));
2006
- }
2007
- function renderCustomGeometryVector(pathData, pathWidth, pathHeight, structuredPaths, shapeStyle, hasFill, fillColor, fillOpacity, strokePaint, strokeWidth, dashArray, animatesFill = false, gradient) {
2008
- const ctx = strokeContext(shapeStyle, strokePaint, strokeWidth, dashArray);
2009
- const subpaths = structuredPaths && structuredPaths.length > 0 ? customGeometryPathsToSvgSubpaths(structuredPaths, pathWidth, pathHeight) : void 0;
2010
- const needsPerSubpath = subpaths !== void 0 && subpaths.some((sp) => sp.fillMode && sp.fillMode !== "norm" || sp.stroke === false);
2011
- const gradientPaint = gradient ? svgGradientFillRef(gradient) : void 0;
2012
- const nodes = [];
2013
- if (subpaths && needsPerSubpath) {
2014
- const paints = buildSubpathPaints(subpaths, hasFill, fillColor, fillOpacity);
2015
- paints.forEach((paint, idx) => {
2016
- if (paint.fill !== "none") {
2017
- const mode = subpaths[idx]?.fillMode;
2018
- const useGradient = gradientPaint && (mode === void 0 || mode === "norm");
2019
- nodes.push(
2020
- /* @__PURE__ */ jsx(
2021
- "path",
2022
- {
2023
- d: paint.d,
2024
- fill: animatesFill ? "inherit" : useGradient ? gradientPaint : paint.fill,
2025
- stroke: "none",
2026
- vectorEffect: "non-scaling-stroke"
2027
- },
2028
- `f${idx}`
2029
- )
2030
- );
2031
- }
2032
- if (paint.stroked && strokeWidth > 0) {
2033
- nodes.push(...strokeStrands(paint.d, `p${idx}`, ctx));
2034
- }
2035
- });
2036
- } else {
2037
- if (hasFill) {
2038
- nodes.push(
2039
- /* @__PURE__ */ jsx(
2040
- "path",
2041
- {
2042
- d: pathData,
2043
- fill: animatesFill ? "inherit" : gradientPaint ?? colorWithOpacity(fillColor, fillOpacity),
2044
- stroke: "none",
2045
- vectorEffect: "non-scaling-stroke"
2046
- },
2047
- "fill"
2048
- )
2049
- );
2050
- }
2051
- if (strokeWidth > 0) {
2052
- nodes.push(...strokeStrands(pathData, "agg", ctx));
2053
- }
2054
- }
2055
- return /* @__PURE__ */ jsxs(
2056
- "svg",
2057
- {
2058
- viewBox: `0 0 ${pathWidth} ${pathHeight}`,
2059
- className: "absolute inset-0 w-full h-full pointer-events-none",
2060
- preserveAspectRatio: "none",
2061
- children: [
2062
- animatesFill ? null : renderSvgGradientDefs(gradient),
2063
- nodes
2064
- ]
2065
- }
2066
- );
2067
- }
2068
- function renderVectorShape(element, hasFill, fillColor, strokeWidth, strokeColor, animatesFill, animatesStroke) {
2069
- if (!hasShapeProperties(element)) {
2070
- return null;
2071
- }
2072
- const normalizedType = (element.shapeType || "").toLowerCase();
2073
- const fillPaint = animatesFill ? "inherit" : colorWithOpacity(fillColor, element.shapeStyle?.fillOpacity);
2074
- const strokePaint = animatesStroke ? "inherit" : colorWithOpacity(strokeColor, element.shapeStyle?.strokeOpacity);
2075
- const dashType = normalizeStrokeDashType(element.shapeStyle?.strokeDash);
2076
- const dashArray = getSvgStrokeDasharray(
2077
- dashType,
2078
- Math.max(strokeWidth, 1),
2079
- element.shapeStyle?.customDashSegments
2080
- );
2081
- if (normalizedType === "can" || normalizedType === "cylinder") {
2082
- const width = Math.max(element.width, 1);
2083
- const height = Math.max(element.height, 1);
2084
- const rim = Math.max(Math.min(height * 0.16, 28), 6);
2085
- const stroke = Math.max(strokeWidth, 1);
2086
- return /* @__PURE__ */ jsxs(
2087
- "svg",
2088
- {
2089
- viewBox: `0 0 ${width} ${height}`,
2090
- className: "w-full h-full pointer-events-none",
2091
- preserveAspectRatio: "none",
2092
- children: [
2093
- /* @__PURE__ */ jsx(
2094
- "ellipse",
2095
- {
2096
- cx: width / 2,
2097
- cy: rim / 2,
2098
- rx: width / 2,
2099
- ry: rim / 2,
2100
- fill: hasFill ? fillPaint : "none",
2101
- stroke: strokeWidth > 0 ? strokePaint : "none",
2102
- strokeWidth: stroke,
2103
- strokeDasharray: dashArray,
2104
- vectorEffect: "non-scaling-stroke"
2105
- }
2106
- ),
2107
- /* @__PURE__ */ jsx(
2108
- "rect",
2109
- {
2110
- x: 0,
2111
- y: rim / 2,
2112
- width,
2113
- height: Math.max(height - rim, 1),
2114
- fill: hasFill ? fillPaint : "none",
2115
- stroke: strokeWidth > 0 ? strokePaint : "none",
2116
- strokeWidth: stroke,
2117
- strokeDasharray: dashArray,
2118
- vectorEffect: "non-scaling-stroke"
2119
- }
2120
- ),
2121
- /* @__PURE__ */ jsx(
2122
- "ellipse",
2123
- {
2124
- cx: width / 2,
2125
- cy: height - rim / 2,
2126
- rx: width / 2,
2127
- ry: rim / 2,
2128
- fill: hasFill ? fillPaint : "none",
2129
- stroke: strokeWidth > 0 ? strokePaint : "none",
2130
- strokeWidth: stroke,
2131
- strokeDasharray: dashArray,
2132
- vectorEffect: "non-scaling-stroke"
2133
- }
2134
- )
2135
- ]
2136
- }
2137
- );
2138
- }
2139
- if ((element.type === "shape" || element.type === "image" || element.type === "picture") && element.pathData && element.pathWidth && element.pathHeight && element.pathWidth > 0 && element.pathHeight > 0) {
2140
- return renderCustomGeometryVector(
2141
- element.pathData,
2142
- element.pathWidth,
2143
- element.pathHeight,
2144
- element.customGeometryPaths,
2145
- element.shapeStyle,
2146
- hasFill,
2147
- fillColor,
2148
- element.shapeStyle?.fillOpacity,
2149
- strokePaint,
2150
- strokeWidth,
2151
- dashArray,
2152
- Boolean(animatesFill),
2153
- // A freeform's `a:gradFill` needs a real SVG paint server: the
2154
- // container's `background-image` is suppressed for custom geometry, so
2155
- // without this the shape fell back to the parser's representative solid
2156
- // and lost every fade (issue #132).
2157
- buildSvgGradientDef(element.shapeStyle, element.id)
2158
- );
2159
- }
2160
- if ((element.type === "image" || element.type === "picture") && strokeWidth > 0 && !buildStrokeOutline(element)) {
2161
- const width = Math.max(element.width, 1);
2162
- const height = Math.max(element.height, 1);
2163
- const geometry = getPresetShapeVectorGeometry(
2164
- element.shapeType || "rect",
2165
- width,
2166
- height,
2167
- element.shapeAdjustments
2168
- );
2169
- if (geometry) {
2170
- return /* @__PURE__ */ jsx(
2171
- "svg",
2172
- {
2173
- viewBox: `${geometry.minX} ${geometry.minY} ${geometry.viewWidth} ${geometry.viewHeight}`,
2174
- className: "w-full h-full pointer-events-none",
2175
- preserveAspectRatio: "none",
2176
- children: /* @__PURE__ */ jsx(
2177
- "path",
2178
- {
2179
- d: geometry.d,
2180
- fill: "none",
2181
- stroke: strokePaint,
2182
- strokeWidth: strokeWidth * 2,
2183
- strokeDasharray: dashArray,
2184
- vectorEffect: "non-scaling-stroke"
2185
- }
2186
- )
2187
- }
2188
- );
2189
- }
2190
- }
2191
- if (element.type === "shape" && isWedgeCalloutPresetShape(element.shapeType)) {
2192
- const width = Math.max(element.width, 1);
2193
- const height = Math.max(element.height, 1);
2194
- const geometry = getPresetShapeVectorGeometry(
2195
- element.shapeType,
2196
- width,
2197
- height,
2198
- element.shapeAdjustments
2199
- );
2200
- if (geometry) {
2201
- const gradient = buildSvgGradientDef(element.shapeStyle, element.id);
2202
- const gradientPaint = gradient ? svgGradientFillRef(gradient) : void 0;
2203
- return /* @__PURE__ */ jsxs(
2204
- "svg",
2205
- {
2206
- viewBox: `${geometry.minX} ${geometry.minY} ${geometry.viewWidth} ${geometry.viewHeight}`,
2207
- className: "pointer-events-none",
2208
- preserveAspectRatio: "none",
2209
- style: {
2210
- position: "absolute",
2211
- left: geometry.minX,
2212
- top: geometry.minY,
2213
- width: geometry.viewWidth,
2214
- height: geometry.viewHeight,
2215
- overflow: "visible"
2216
- },
2217
- children: [
2218
- animatesFill ? null : renderSvgGradientDefs(gradient),
2219
- /* @__PURE__ */ jsx(
2220
- "path",
2221
- {
2222
- d: geometry.d,
2223
- fill: hasFill ? animatesFill ? "inherit" : gradientPaint ?? fillPaint : "none",
2224
- stroke: strokeWidth > 0 ? strokePaint : "none",
2225
- strokeWidth,
2226
- strokeDasharray: dashArray,
2227
- vectorEffect: "non-scaling-stroke"
2228
- }
2229
- )
2230
- ]
2231
- }
2232
- );
2233
- }
2234
- }
2235
- if (isCalloutShape(normalizedType)) {
2236
- const width = Math.max(element.width, 1);
2237
- const height = Math.max(element.height, 1);
2238
- const geometry = getCalloutLeaderLineGeometry(
2239
- normalizedType,
2240
- width,
2241
- height,
2242
- element.shapeAdjustments
2243
- );
2244
- if (geometry && geometry.points.length >= 2) {
2245
- const leaderPath = buildCalloutLeaderLineSvgPath(geometry);
2246
- const bounds = getCalloutViewBoxBounds(width, height, geometry);
2247
- const lineStroke = Math.max(strokeWidth, 1);
2248
- const offsetLeft = bounds.minX;
2249
- const offsetTop = bounds.minY;
2250
- return /* @__PURE__ */ jsxs(
2251
- "svg",
2252
- {
2253
- viewBox: `${bounds.minX} ${bounds.minY} ${bounds.viewWidth} ${bounds.viewHeight}`,
2254
- className: "pointer-events-none",
2255
- preserveAspectRatio: "none",
2256
- style: {
2257
- position: "absolute",
2258
- left: offsetLeft,
2259
- top: offsetTop,
2260
- width: bounds.viewWidth,
2261
- height: bounds.viewHeight,
2262
- overflow: "visible"
2263
- },
2264
- children: [
2265
- geometry.hasAccent && /* @__PURE__ */ jsx(
2266
- "line",
2267
- {
2268
- x1: 0,
2269
- y1: 0,
2270
- x2: width,
2271
- y2: 0,
2272
- stroke: strokePaint,
2273
- strokeWidth: lineStroke,
2274
- vectorEffect: "non-scaling-stroke"
2275
- }
2276
- ),
2277
- /* @__PURE__ */ jsx(
2278
- "path",
2279
- {
2280
- d: leaderPath,
2281
- fill: "none",
2282
- stroke: strokePaint,
2283
- strokeWidth: lineStroke,
2284
- strokeDasharray: dashArray,
2285
- strokeLinecap: "round",
2286
- strokeLinejoin: "round",
2287
- vectorEffect: "non-scaling-stroke"
2288
- }
2289
- )
2290
- ]
2291
- }
2292
- );
2293
- }
2294
- }
2295
- if (element.type === "connector" || getShapeType(element.shapeType) === "connector" || element.shapeType === "line") {
2296
- const viewWidth = Math.max(element.width, 1);
2297
- const viewHeight = Math.max(element.height, 1);
2298
- const { pathData } = getConnectorPathGeometry2(element);
2299
- const markerSeed = element.id.replace(/[^a-zA-Z0-9_-]/gu, "_");
2300
- const startMarkerId = `${markerSeed}-start-arrow`;
2301
- const endMarkerId = `${markerSeed}-end-arrow`;
2302
- const startArrow = element.shapeStyle?.connectorStartArrow;
2303
- const endArrow = element.shapeStyle?.connectorEndArrow;
2304
- const startArrowW = element.shapeStyle?.connectorStartArrowWidth;
2305
- const startArrowL = element.shapeStyle?.connectorStartArrowLength;
2306
- const endArrowW = element.shapeStyle?.connectorEndArrowWidth;
2307
- const endArrowL = element.shapeStyle?.connectorEndArrowLength;
2308
- const compoundLine = element.shapeStyle?.compoundLine;
2309
- const connectorLineCap = svgLineCap(element.shapeStyle?.lineCap);
2310
- const hitTargetWidth = Math.max(strokeWidth * 3, 12);
2311
- const offsets = getCompoundLineOffsets(compoundLine, strokeWidth);
2312
- const widths = getCompoundLineWidths(compoundLine, strokeWidth);
2313
- return /* @__PURE__ */ jsxs(
2314
- "svg",
2315
- {
2316
- viewBox: `0 0 ${viewWidth} ${viewHeight}`,
2317
- className: "w-full h-full",
2318
- preserveAspectRatio: "none",
2319
- style: { overflow: "visible", pointerEvents: "none" },
2320
- children: [
2321
- /* @__PURE__ */ jsxs("defs", { children: [
2322
- renderConnectorMarker(startMarkerId, startArrow, strokePaint, startArrowW, startArrowL),
2323
- renderConnectorMarker(endMarkerId, endArrow, strokePaint, endArrowW, endArrowL)
2324
- ] }),
2325
- /* @__PURE__ */ jsx(
2326
- "path",
2327
- {
2328
- d: pathData,
2329
- fill: "none",
2330
- stroke: "transparent",
2331
- strokeWidth: hitTargetWidth,
2332
- strokeLinecap: "round",
2333
- strokeLinejoin: "round",
2334
- style: { pointerEvents: "stroke" }
2335
- }
2336
- ),
2337
- offsets.map((offset, idx) => /* @__PURE__ */ jsx(
2338
- "path",
2339
- {
2340
- d: pathData,
2341
- fill: "none",
2342
- stroke: strokePaint,
2343
- strokeWidth: Math.max(widths[idx] ?? strokeWidth, 1),
2344
- strokeDasharray: dashArray,
2345
- strokeLinecap: connectorLineCap,
2346
- strokeLinejoin: "round",
2347
- markerStart: idx === 0 && startArrow && startArrow !== "none" ? `url(#${startMarkerId})` : void 0,
2348
- markerEnd: idx === offsets.length - 1 && endArrow && endArrow !== "none" ? `url(#${endMarkerId})` : void 0,
2349
- vectorEffect: "non-scaling-stroke",
2350
- style: {
2351
- pointerEvents: "none",
2352
- ...offset !== 0 ? { transform: `translate(0, ${offset}px)` } : {}
2353
- }
2354
- },
2355
- idx
2356
- ))
2357
- ]
2358
- }
2359
- );
2360
- }
2361
- return null;
2362
- }
2363
-
2364
- // src/viewer/utils/table-cell-fill.tsx
2365
- function parseGradientFillCss(gradFill) {
2366
- if (!gradFill) {
2367
- return void 0;
2368
- }
2369
- const gsLstRaw = gradFill["a:gsLst"];
2370
- if (!gsLstRaw) {
2371
- return void 0;
2372
- }
2373
- const stops = ensureArrayValue(gsLstRaw["a:gs"]);
2374
- if (stops.length === 0) {
2375
- return void 0;
2376
- }
2377
- const rawStops = stops.map((gs) => {
2378
- const pos = Number.parseInt(String(gs["@_pos"] || "0"), 10) / 1e3;
2379
- const color = parseDrawingColor(gs) ?? "#888888";
2380
- const opacity = parseDrawingColorOpacity(gs);
2381
- return { position: pos, color, opacity };
2382
- }).sort((a, b) => a.position - b.position);
2383
- const stopStrings = rawStops.map((s) => `${colorWithOpacity(s.color, s.opacity)} ${s.position.toFixed(1)}%`).join(", ");
2384
- const lin = gradFill["a:lin"];
2385
- if (lin) {
2386
- const angRaw = Number.parseInt(String(lin["@_ang"] || "0"), 10);
2387
- const angleDeg = Math.round(ooxmlGradientAngleToCssDegrees(angRaw / 6e4));
2388
- return `linear-gradient(${angleDeg}deg, ${stopStrings})`;
2389
- }
2390
- const path = gradFill["a:path"];
2391
- if (path) {
2392
- const pathType = String(path["@_path"] || "circle");
2393
- const fillToRectXml = path["a:fillToRect"];
2394
- const fillToRect = fillToRectXml ? {
2395
- l: Number.parseInt(String(fillToRectXml["@_l"] || "0"), 10) / 1e5,
2396
- t: Number.parseInt(String(fillToRectXml["@_t"] || "0"), 10) / 1e5,
2397
- r: Number.parseInt(String(fillToRectXml["@_r"] || "0"), 10) / 1e5,
2398
- b: Number.parseInt(String(fillToRectXml["@_b"] || "0"), 10) / 1e5
2399
- } : void 0;
2400
- if (pathType === "rect") {
2401
- return buildRectPathGradient(rawStops, void 0, fillToRect);
2402
- }
2403
- let cx = 50;
2404
- let cy = 50;
2405
- if (fillToRect) {
2406
- const { l, t, r, b } = fillToRect;
2407
- cx = (l + (1 - r)) / 2 * 100;
2408
- cy = (t + (1 - b)) / 2 * 100;
2409
- }
2410
- const posX = `${Math.round(cx)}%`;
2411
- const posY = `${Math.round(cy)}%`;
2412
- if (pathType === "shape") {
2413
- return `radial-gradient(farthest-side at ${posX} ${posY}, ${stopStrings})`;
2414
- }
2415
- return `radial-gradient(circle at ${posX} ${posY}, ${stopStrings})`;
2416
- }
2417
- return `linear-gradient(180deg, ${stopStrings})`;
2418
- }
2419
- function parsePatternFillCss(pattFill) {
2420
- if (!pattFill) {
2421
- return void 0;
2422
- }
2423
- const fgColor = parseDrawingColor(pattFill["a:fgClr"]) ?? "#000000";
2424
- const bgColor = parseDrawingColor(pattFill["a:bgClr"]) ?? "#ffffff";
2425
- const preset = String(pattFill["@_prst"] || "ltDnDiag");
2426
- switch (preset) {
2427
- case "ltHorz":
2428
- case "horz":
2429
- case "narHorz":
2430
- case "dkHorz":
2431
- case "wdUpDiag":
2432
- return `repeating-linear-gradient(0deg, ${bgColor}, ${bgColor} 3px, ${fgColor} 3px, ${fgColor} 4px)`;
2433
- case "ltVert":
2434
- case "vert":
2435
- case "narVert":
2436
- case "dkVert":
2437
- case "wdDnDiag":
2438
- return `repeating-linear-gradient(90deg, ${bgColor}, ${bgColor} 3px, ${fgColor} 3px, ${fgColor} 4px)`;
2439
- case "ltDnDiag":
2440
- case "dnDiag":
2441
- case "dkDnDiag":
2442
- return `repeating-linear-gradient(135deg, ${bgColor}, ${bgColor} 3px, ${fgColor} 3px, ${fgColor} 4px)`;
2443
- case "ltUpDiag":
2444
- case "upDiag":
2445
- case "dkUpDiag":
2446
- return `repeating-linear-gradient(45deg, ${bgColor}, ${bgColor} 3px, ${fgColor} 3px, ${fgColor} 4px)`;
2447
- case "dashHorz":
2448
- case "dashVert":
2449
- case "dashDnDiag":
2450
- case "dashUpDiag":
2451
- return `repeating-linear-gradient(135deg, ${bgColor}, ${bgColor} 4px, ${fgColor} 4px, ${fgColor} 6px, ${bgColor} 6px, ${bgColor} 10px)`;
2452
- case "smCheck":
2453
- case "lgCheck":
2454
- return `repeating-conic-gradient(${fgColor} 0% 25%, ${bgColor} 0% 50%) 0 0 / 8px 8px`;
2455
- case "smGrid":
2456
- case "lgGrid":
2457
- return `repeating-linear-gradient(0deg, ${fgColor} 0px, ${fgColor} 1px, transparent 1px, transparent 6px), repeating-linear-gradient(90deg, ${fgColor} 0px, ${fgColor} 1px, transparent 1px, transparent 6px), ${bgColor}`;
2458
- case "dotGrid":
2459
- case "pct5":
2460
- case "pct10":
2461
- case "pct20":
2462
- case "pct25":
2463
- case "pct30":
2464
- case "pct40":
2465
- case "pct50":
2466
- case "pct60":
2467
- case "pct70":
2468
- case "pct75":
2469
- case "pct80":
2470
- case "pct90":
2471
- return `radial-gradient(${fgColor} 1px, ${bgColor} 1px) 0 0 / 4px 4px`;
2472
- case "smConfetti":
2473
- case "lgConfetti":
2474
- case "zigZag":
2475
- case "wave":
2476
- case "sphere":
2477
- case "divot":
2478
- case "shingle":
2479
- case "weave":
2480
- case "plaid":
2481
- case "trellis":
2482
- return `repeating-linear-gradient(45deg, ${bgColor}, ${bgColor} 2px, ${fgColor} 2px, ${fgColor} 3px)`;
2483
- case "solidDmnd":
2484
- case "openDmnd":
2485
- return `repeating-conic-gradient(${fgColor} 0% 25%, ${bgColor} 0% 50%) 0 0 / 10px 10px`;
2486
- default:
2487
- return `repeating-linear-gradient(135deg, ${bgColor}, ${bgColor} 3px, ${fgColor} 3px, ${fgColor} 4px)`;
2488
- }
2489
- }
2490
- function ooxmlDashToCss(dashVal) {
2491
- if (!dashVal) {
2492
- return "solid";
2493
- }
2494
- switch (dashVal) {
2495
- case "dot":
2496
- case "sysDot":
2497
- return "dotted";
2498
- case "dash":
2499
- case "sysDash":
2500
- case "lgDash":
2501
- return "dashed";
2502
- case "dashDot":
2503
- case "lgDashDot":
2504
- case "sysDashDot":
2505
- case "lgDashDotDot":
2506
- case "sysDashDotDot":
2507
- return "dashed";
2508
- case "solid":
2509
- return "solid";
2510
- default:
2511
- return "solid";
2512
- }
2513
- }
2514
- function parseCellBorders(cellProperties) {
2515
- if (!cellProperties) {
2516
- return {};
2517
- }
2518
- const borderStyle = {};
2519
- const edges = [
2520
- { xml: "a:lnL", prefix: "borderLeft" },
2521
- { xml: "a:lnR", prefix: "borderRight" },
2522
- { xml: "a:lnT", prefix: "borderTop" },
2523
- { xml: "a:lnB", prefix: "borderBottom" }
2524
- ];
2525
- for (const edge of edges) {
2526
- const ln = cellProperties[edge.xml];
2527
- if (!ln) {
2528
- continue;
2529
- }
2530
- const widthEmu = Number.parseInt(String(ln["@_w"] || "0"), 10);
2531
- const widthPx = Math.max(1, Math.round(widthEmu / 12700));
2532
- const prstDash = ln["a:prstDash"];
2533
- const dashVal = prstDash ? String(prstDash["@_val"] || "") : void 0;
2534
- const cssBorderStyle = ooxmlDashToCss(dashVal);
2535
- const color = parseDrawingColor(ln["a:solidFill"]);
2536
- if (color) {
2537
- const opacity = parseDrawingColorOpacity(ln["a:solidFill"]);
2538
- const key = `${edge.prefix}`;
2539
- borderStyle[key] = `${widthPx}px ${cssBorderStyle} ${colorWithOpacity(color, opacity)}`;
2540
- }
2541
- }
2542
- return borderStyle;
2543
- }
2544
- function parseCellTextEffects(runProps) {
2545
- const effectLst = runProps?.["a:effectLst"];
2546
- if (!effectLst) {
2547
- return void 0;
2548
- }
2549
- const textShadowParts = [];
2550
- const outerShdw = effectLst["a:outerShdw"];
2551
- if (outerShdw) {
2552
- const shdwColor = parseDrawingColor(outerShdw);
2553
- if (shdwColor) {
2554
- const blurRad = Number.parseInt(String(outerShdw["@_blurRad"] || "0"), 10);
2555
- const blurPx = blurRad > 0 ? Math.round(blurRad / 12700) : 0;
2556
- const distVal = Number.parseInt(String(outerShdw["@_dist"] || "0"), 10);
2557
- const dirVal = Number.parseInt(String(outerShdw["@_dir"] || "0"), 10);
2558
- let offX = 0;
2559
- let offY = 0;
2560
- if (distVal > 0) {
2561
- const angleRad = dirVal / 6e4 * (Math.PI / 180);
2562
- offX = Math.round(distVal * Math.cos(angleRad) / 12700);
2563
- offY = Math.round(distVal * Math.sin(angleRad) / 12700);
2564
- }
2565
- textShadowParts.push(`${offX}px ${offY}px ${blurPx}px ${shdwColor}`);
2566
- }
2567
- }
2568
- const glow = effectLst["a:glow"];
2569
- if (glow) {
2570
- const glowColor = parseDrawingColor(glow);
2571
- if (glowColor) {
2572
- const glowRad = Number.parseInt(String(glow["@_rad"] || "0"), 10);
2573
- const glowPx = glowRad > 0 ? Math.round(glowRad / 12700) : 2;
2574
- textShadowParts.push(`0px 0px ${glowPx}px ${glowColor}`);
2575
- }
2576
- }
2577
- return textShadowParts.length > 0 ? textShadowParts.join(", ") : void 0;
2578
- }
2579
-
2580
- // src/viewer/utils/table-cell-style.tsx
2581
- function extractCellText(cellXml) {
2582
- if (!cellXml) {
2583
- return "";
2584
- }
2585
- const txBody = cellXml["a:txBody"];
2586
- const paragraphs = ensureArrayValue(txBody?.["a:p"]);
2587
- const paragraphTexts = [];
2588
- paragraphs.forEach((paragraph) => {
2589
- const textParts = [];
2590
- const runs = ensureArrayValue(paragraph["a:r"]);
2591
- runs.forEach((run) => {
2592
- textParts.push(xmlText(run["a:t"]) ?? "");
2593
- });
2594
- const fields = ensureArrayValue(paragraph["a:fld"]);
2595
- fields.forEach((field) => {
2596
- textParts.push(xmlText(field["a:t"]) ?? "");
2597
- });
2598
- paragraphTexts.push(textParts.join(""));
2599
- });
2600
- return paragraphTexts.join("\n");
2601
- }
2602
- function parseParagraphAlignment(value) {
2603
- const alignment = String(value || "").toLowerCase();
2604
- if (alignment === "ctr") {
2605
- return "center";
2606
- }
2607
- if (alignment === "r") {
2608
- return "right";
2609
- }
2610
- if (alignment === "just" || alignment === "justify") {
2611
- return "justify";
2612
- }
2613
- return "left";
2614
- }
2615
- function extractTableCellStyle(cellXml, fallbackStyle, schemeColorOverrides) {
2616
- const { color: _fallbackColor, ...fallbackWithoutColor } = fallbackStyle;
2617
- const cellStyle = { ...fallbackWithoutColor };
2618
- const txBody = cellXml["a:txBody"];
2619
- const paragraphs = ensureArrayValue(txBody?.["a:p"]);
2620
- const firstParagraph = paragraphs[0];
2621
- const paragraphProperties = firstParagraph?.["a:pPr"];
2622
- const runs = ensureArrayValue(firstParagraph?.["a:r"]);
2623
- const firstRun = runs[0];
2624
- const runProps = firstRun?.["a:rPr"] || paragraphProperties?.["a:defRPr"];
2625
- if (runProps?.["@_sz"]) {
2626
- const pointSize = Number.parseInt(String(runProps["@_sz"]), 10) / 100;
2627
- if (Number.isFinite(pointSize) && pointSize > 0) {
2628
- cellStyle.fontSize = pointSize * (96 / 72);
2629
- }
2630
- }
2631
- if (runProps?.["@_b"] !== void 0) {
2632
- cellStyle.fontWeight = runProps["@_b"] === "1" ? 700 : 400;
2633
- }
2634
- if (runProps?.["@_i"] !== void 0) {
2635
- cellStyle.fontStyle = runProps["@_i"] === "1" ? "italic" : "normal";
2636
- }
2637
- if (runProps?.["@_u"] !== void 0) {
2638
- cellStyle.textDecorationLine = runProps["@_u"] === "sng" ? "underline" : "none";
2639
- }
2640
- const runColor = parseDrawingColor(runProps?.["a:solidFill"], schemeColorOverrides) || parseDrawingColor(runProps?.["a:highlight"], schemeColorOverrides);
2641
- if (runColor) {
2642
- const opacity = parseDrawingColorOpacity(runProps?.["a:solidFill"]);
2643
- cellStyle.color = colorWithOpacity(runColor, opacity);
2644
- }
2645
- const fontFamily = runProps?.["a:latin"]?.["@_typeface"] || runProps?.["a:ea"]?.["@_typeface"] || runProps?.["a:cs"]?.["@_typeface"];
2646
- if (fontFamily) {
2647
- cellStyle.fontFamily = fontFamily;
2648
- }
2649
- if (paragraphProperties?.["@_algn"]) {
2650
- cellStyle.textAlign = parseParagraphAlignment(paragraphProperties["@_algn"]);
2651
- }
2652
- const cellProperties = cellXml["a:tcPr"];
2653
- const cellFill = parseDrawingColor(
2654
- cellProperties?.["a:solidFill"],
2655
- schemeColorOverrides
2656
- );
2657
- if (cellFill) {
2658
- const opacity = parseDrawingColorOpacity(
2659
- cellProperties?.["a:solidFill"]
2660
- );
2661
- cellStyle.backgroundColor = colorWithOpacity(cellFill, opacity);
2662
- } else {
2663
- const gradCss = parseGradientFillCss(cellProperties?.["a:gradFill"]);
2664
- if (gradCss) {
2665
- cellStyle.background = gradCss;
2666
- } else {
2667
- const pattCss = parsePatternFillCss(cellProperties?.["a:pattFill"]);
2668
- if (pattCss) {
2669
- cellStyle.background = pattCss;
2670
- }
2671
- }
2672
- }
2673
- const edgeBorders = parseCellBorders(cellProperties);
2674
- Object.assign(cellStyle, edgeBorders);
2675
- if (!cellStyle.borderLeftColor && !cellStyle.borderRightColor && !cellStyle.borderTopColor && !cellStyle.borderBottomColor) {
2676
- const borderColor = parseDrawingColor(
2677
- cellProperties?.["a:lnL"]?.["a:solidFill"],
2678
- schemeColorOverrides
2679
- ) || parseDrawingColor(
2680
- cellProperties?.["a:lnR"]?.["a:solidFill"],
2681
- schemeColorOverrides
2682
- ) || parseDrawingColor(
2683
- cellProperties?.["a:lnT"]?.["a:solidFill"],
2684
- schemeColorOverrides
2685
- ) || parseDrawingColor(
2686
- cellProperties?.["a:lnB"]?.["a:solidFill"],
2687
- schemeColorOverrides
2688
- );
2689
- if (borderColor) {
2690
- cellStyle.borderColor = borderColor;
2691
- }
2692
- }
2693
- if (cellProperties) {
2694
- const marL = Number.parseInt(String(cellProperties["@_marL"] || ""), 10);
2695
- const marR = Number.parseInt(String(cellProperties["@_marR"] || ""), 10);
2696
- const marT = Number.parseInt(String(cellProperties["@_marT"] || ""), 10);
2697
- const marB = Number.parseInt(String(cellProperties["@_marB"] || ""), 10);
2698
- const toPx = (emu) => Math.round(emu / 12700);
2699
- if (Number.isFinite(marL) && marL > 0) {
2700
- cellStyle.paddingLeft = toPx(marL);
2701
- }
2702
- if (Number.isFinite(marR) && marR > 0) {
2703
- cellStyle.paddingRight = toPx(marR);
2704
- }
2705
- if (Number.isFinite(marT) && marT > 0) {
2706
- cellStyle.paddingTop = toPx(marT);
2707
- }
2708
- if (Number.isFinite(marB) && marB > 0) {
2709
- cellStyle.paddingBottom = toPx(marB);
2710
- }
2711
- }
2712
- if (cellProperties?.["@_anchor"]) {
2713
- const anchor = String(cellProperties["@_anchor"]);
2714
- if (anchor === "ctr") {
2715
- cellStyle.verticalAlign = "middle";
2716
- } else if (anchor === "b") {
2717
- cellStyle.verticalAlign = "bottom";
2718
- } else {
2719
- cellStyle.verticalAlign = "top";
2720
- }
2721
- }
2722
- if (cellProperties?.["@_vert"]) {
2723
- const vert = String(cellProperties["@_vert"]);
2724
- switch (vert) {
2725
- case "vert":
2726
- case "eaVert":
2727
- case "wordArtVert":
2728
- case "wordArtVertRtl":
2729
- cellStyle.writingMode = "vertical-rl";
2730
- break;
2731
- case "vert270":
2732
- case "mongolianVert":
2733
- cellStyle.writingMode = "vertical-lr";
2734
- break;
2735
- }
2736
- if (vert === "wordArtVert") {
2737
- cellStyle.textOrientation = "upright";
2738
- } else if (cellStyle.writingMode) {
2739
- cellStyle.textOrientation = "mixed";
2740
- }
2741
- if (vert === "wordArtVertRtl") {
2742
- cellStyle.direction = "rtl";
2743
- }
2744
- }
2745
- const textShadow = parseCellTextEffects(runProps);
2746
- if (textShadow) {
2747
- cellStyle.textShadow = textShadow;
2748
- }
2749
- return cellStyle;
2750
- }
2751
-
2752
- // src/viewer/utils/table-band-style.tsx
2753
- function getTableCellBandStyle2(element, rowIndex, cellIndex, rowCount, columnCount, styleCtx) {
2754
- if (element.type !== "table") {
2755
- return void 0;
2756
- }
2757
- return getTableCellBandStyle(element.tableData, rowIndex, cellIndex, rowCount, columnCount, {
2758
- tableStyleMap: styleCtx?.tableStyleMap,
2759
- colorScheme: styleCtx?.theme?.colorScheme,
2760
- fontScheme: styleCtx?.theme?.fontScheme
2761
- });
2762
- }
2763
-
2764
- // src/viewer/utils/table-data-parse.tsx
2765
- function getGraphicDataFromElement(element) {
2766
- return element.rawXml?.["a:graphic"]?.["a:graphicData"];
2767
- }
2768
- function getTableXmlFromElement(element) {
2769
- return getGraphicDataFromElement(element)?.["a:tbl"];
2770
- }
2771
- function parseTableElementData(element, fallbackTextStyle) {
2772
- const table = getTableXmlFromElement(element);
2773
- if (!table) {
2774
- return null;
2775
- }
2776
- const rows = ensureArrayValue(table["a:tr"]);
2777
- if (rows.length === 0) {
2778
- return null;
2779
- }
2780
- const tableGrid = table["a:tblGrid"];
2781
- const gridColumns = ensureArrayValue(
2782
- tableGrid?.["a:gridCol"]
2783
- );
2784
- const columnWidths = gridColumns.map((column) => {
2785
- const width = Number.parseInt(String(column?.["@_w"] || ""), 10);
2786
- return Number.isFinite(width) && width > 0 ? width : 0;
2787
- });
2788
- const totalColumnWidth = columnWidths.reduce((sum, width) => sum + width, 0);
2789
- let columnPercentages = totalColumnWidth > 0 ? columnWidths.map((width) => width / totalColumnWidth * 100) : [];
2790
- const modelWidths = element.type === "table" ? element.tableData?.columnWidths : void 0;
2791
- if (modelWidths?.length && modelWidths.length === gridColumns.length && modelWidths.every((width) => Number.isFinite(width) && width >= 0) && Math.abs(modelWidths.reduce((sum, width) => sum + width, 0) - 1) <= Number.EPSILON * modelWidths.length * 4) {
2792
- columnPercentages = modelWidths.map((width) => width * 100);
2793
- }
2794
- const cells = [];
2795
- rows.forEach((row, rowIndex) => {
2796
- const rowCells = ensureArrayValue(row["a:tc"]);
2797
- rowCells.forEach((cell, columnIndex) => {
2798
- cells.push({
2799
- rowIndex,
2800
- columnIndex,
2801
- text: extractCellText(cell),
2802
- style: extractTableCellStyle(cell, fallbackTextStyle),
2803
- rawCell: cell
2804
- });
2805
- });
2806
- });
2807
- const columnCount = Math.max(
2808
- columnPercentages.length,
2809
- rows.reduce((maxCount, row) => {
2810
- const rowCells = ensureArrayValue(row["a:tc"]);
2811
- return Math.max(maxCount, rowCells.length);
2812
- }, 0)
2813
- );
2814
- return {
2815
- rowCount: rows.length,
2816
- columnCount,
2817
- rows,
2818
- columnPercentages,
2819
- cells
2820
- };
2821
- }
2822
- function renderTableCellContent(cell, fallbackText) {
2823
- const runs = cell?.textRuns;
2824
- if (!runs || runs.length === 0) {
2825
- return fallbackText || "\xA0";
2826
- }
2827
- return runs.map((run, index) => {
2828
- if (run.isParagraphBreak) {
2829
- return /* @__PURE__ */ jsx("div", { style: { display: "block", height: 0 } }, `p-${index}`);
2830
- }
2831
- if (run.isLineBreak) {
2832
- return /* @__PURE__ */ jsx("br", {}, `br-${index}`);
2833
- }
2834
- return /* @__PURE__ */ jsx(
2835
- "span",
2836
- {
2837
- style: { position: "relative", ...cellRunStyle(run) },
2838
- children: run.text
2839
- },
2840
- `r-${index}`
2841
- );
2842
- });
2843
- }
2844
- function getCellDiagonalBorders2(style, tableData, pos, styleCtx) {
2845
- return getCellDiagonalBorders(style, tableData, pos, {
2846
- tableStyleMap: styleCtx?.tableStyleMap,
2847
- colorScheme: styleCtx?.theme?.colorScheme,
2848
- fontScheme: styleCtx?.theme?.fontScheme
2849
- });
2850
- }
2851
- function TableCellDiagonalBorders({
2852
- diag
2853
- }) {
2854
- const hasDown = Boolean(diag.diagDownColor && diag.diagDownWidth);
2855
- const hasUp = Boolean(diag.diagUpColor && diag.diagUpWidth);
2856
- if (!hasDown && !hasUp) {
2857
- return null;
2858
- }
2859
- return /* @__PURE__ */ jsxs(
2860
- "svg",
2861
- {
2862
- "aria-hidden": "true",
2863
- style: {
2864
- position: "absolute",
2865
- inset: 0,
2866
- width: "100%",
2867
- height: "100%",
2868
- pointerEvents: "none",
2869
- overflow: "visible"
2870
- },
2871
- children: [
2872
- hasDown && /* @__PURE__ */ jsx(
2873
- "line",
2874
- {
2875
- x1: "0",
2876
- y1: "0",
2877
- x2: "100%",
2878
- y2: "100%",
2879
- stroke: diag.diagDownColor,
2880
- strokeWidth: diag.diagDownWidth
2881
- }
2882
- ),
2883
- hasUp && /* @__PURE__ */ jsx(
2884
- "line",
2885
- {
2886
- x1: "0",
2887
- y1: "100%",
2888
- x2: "100%",
2889
- y2: "0",
2890
- stroke: diag.diagUpColor,
2891
- strokeWidth: diag.diagUpWidth
2892
- }
2893
- )
2894
- ]
2895
- }
2896
- );
2897
- }
2898
- function TableCellInput({
2899
- initialText,
2900
- style,
2901
- onCommit,
2902
- onCancel
2903
- }) {
2904
- const inputRef = useRef(null);
2905
- const committedRef = useRef(false);
2906
- const onCommitRef = useRef(onCommit);
2907
- onCommitRef.current = onCommit;
2908
- useEffect(() => {
2909
- const el = inputRef.current;
2910
- if (el) {
2911
- el.focus();
2912
- el.select();
2913
- }
2914
- }, []);
2915
- useEffect(() => {
2916
- const input = inputRef.current;
2917
- return () => {
2918
- if (!committedRef.current && input) {
2919
- onCommitRef.current(input.value);
2920
- }
2921
- };
2922
- }, []);
2923
- const doCommit = (text) => {
2924
- if (committedRef.current) {
2925
- return;
2926
- }
2927
- committedRef.current = true;
2928
- onCommit(text);
2929
- };
2930
- const doCancel = () => {
2931
- committedRef.current = true;
2932
- onCancel();
2933
- };
2934
- return /* @__PURE__ */ jsx(
2935
- "input",
2936
- {
2937
- ref: inputRef,
2938
- type: "text",
2939
- defaultValue: initialText,
2940
- className: "w-full bg-transparent outline-none",
2941
- style: {
2942
- ...style,
2943
- padding: 0,
2944
- margin: 0,
2945
- border: "none"
2946
- },
2947
- onPointerDown: (e) => e.stopPropagation(),
2948
- onMouseDown: (e) => e.stopPropagation(),
2949
- onClick: (e) => e.stopPropagation(),
2950
- onDoubleClick: (e) => e.stopPropagation(),
2951
- onBlur: (e) => doCommit(e.currentTarget.value),
2952
- onKeyDown: (e) => {
2953
- e.stopPropagation();
2954
- if (e.key === "Escape") {
2955
- e.preventDefault();
2956
- doCancel();
2957
- } else if (e.key === "Enter") {
2958
- e.preventDefault();
2959
- doCommit(e.currentTarget.value);
2960
- } else if (e.key === "Tab") {
2961
- e.preventDefault();
2962
- doCommit(e.currentTarget.value);
2963
- }
2964
- }
2965
- }
2966
- );
2967
- }
2968
- function TableResizeOverlay({
2969
- children,
2970
- columnWidths,
2971
- editable,
2972
- onResizeColumns,
2973
- onResizeRow
2974
- }) {
2975
- const containerRef = useRef(null);
2976
- const [rowBounds, setRowBounds] = useState([]);
2977
- const dragRef = useRef(null);
2978
- const colBoundaries = useMemo(() => computeColumnBoundaries(columnWidths), [columnWidths]);
2979
- const measureRows = useCallback(() => {
2980
- const container = containerRef.current;
2981
- if (!container) {
2982
- return;
2983
- }
2984
- const table = container.querySelector("table");
2985
- if (!table) {
2986
- return;
2987
- }
2988
- const trs = table.querySelectorAll("tbody > tr");
2989
- const bounds = [];
2990
- let cumHeight = 0;
2991
- trs.forEach((tr, i) => {
2992
- cumHeight += tr.offsetHeight;
2993
- if (i < trs.length - 1) {
2994
- bounds.push(cumHeight);
2995
- }
2996
- });
2997
- setRowBounds((prev) => {
2998
- if (prev.length === bounds.length && prev.every((v, i) => v === bounds[i])) {
2999
- return prev;
3000
- }
3001
- return bounds;
3002
- });
3003
- }, []);
3004
- useLayoutEffect(() => {
3005
- measureRows();
3006
- });
3007
- useEffect(() => {
3008
- const handleMouseMove = (e) => {
3009
- const drag = dragRef.current;
3010
- if (!drag) {
3011
- return;
3012
- }
3013
- e.preventDefault();
3014
- const delta = drag.type === "col" ? e.clientX - drag.startPos : e.clientY - drag.startPos;
3015
- drag.handleEl.style.transform = drag.type === "col" ? `translateX(${delta}px)` : `translateY(${delta}px)`;
3016
- };
3017
- const handleMouseUp = (e) => {
3018
- const drag = dragRef.current;
3019
- if (!drag || !containerRef.current) {
3020
- return;
3021
- }
3022
- const rect = containerRef.current.getBoundingClientRect();
3023
- if (e.clientX !== drag.startPos && drag.type === "col" && drag.initialWidths && onResizeColumns) {
3024
- const deltaProp = (e.clientX - drag.startPos) / rect.width;
3025
- onResizeColumns(computeResizedColumnWidths(drag.initialWidths, drag.index, deltaProp));
3026
- } else if (e.clientY !== drag.startPos && drag.type === "row" && onResizeRow) {
3027
- const deltaY = e.clientY - drag.startPos;
3028
- const newHeight = computeResizedRowHeight(
3029
- drag.initialRowHeight ?? DEFAULT_ROW_HEIGHT,
3030
- deltaY
3031
- );
3032
- onResizeRow(drag.index, newHeight);
3033
- }
3034
- drag.handleEl.style.transform = "";
3035
- document.body.style.cursor = "";
3036
- document.body.style.userSelect = "";
3037
- dragRef.current = null;
3038
- };
3039
- document.addEventListener("mousemove", handleMouseMove);
3040
- document.addEventListener("mouseup", handleMouseUp);
3041
- return () => {
3042
- document.removeEventListener("mousemove", handleMouseMove);
3043
- document.removeEventListener("mouseup", handleMouseUp);
3044
- };
3045
- }, [onResizeColumns, onResizeRow]);
3046
- if (!editable) {
3047
- return children;
3048
- }
3049
- const startColDrag = (e, index) => {
3050
- e.preventDefault();
3051
- e.stopPropagation();
3052
- document.body.style.cursor = "col-resize";
3053
- document.body.style.userSelect = "none";
3054
- dragRef.current = {
3055
- type: "col",
3056
- index,
3057
- startPos: e.clientX,
3058
- handleEl: e.currentTarget,
3059
- initialWidths: [...columnWidths]
3060
- };
3061
- };
3062
- const startRowDrag = (e, index) => {
3063
- e.preventDefault();
3064
- e.stopPropagation();
3065
- const table = containerRef.current?.querySelector("table");
3066
- const tr = table?.querySelectorAll("tbody > tr")[index];
3067
- const actualHeight = tr?.offsetHeight ?? DEFAULT_ROW_HEIGHT;
3068
- document.body.style.cursor = "row-resize";
3069
- document.body.style.userSelect = "none";
3070
- dragRef.current = {
3071
- type: "row",
3072
- index,
3073
- startPos: e.clientY,
3074
- handleEl: e.currentTarget,
3075
- initialRowHeight: actualHeight
3076
- };
3077
- };
3078
- return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative w-full h-full", children: [
3079
- children,
3080
- colBoundaries.map((leftPct, i) => /* @__PURE__ */ jsx(
3081
- "div",
3082
- {
3083
- className: "absolute top-0 bottom-0 w-[6px] cursor-col-resize z-10 pointer-events-auto group",
3084
- style: { left: `calc(${leftPct}% - 3px)` },
3085
- onMouseDown: (e) => startColDrag(e, i),
3086
- children: /* @__PURE__ */ jsx("div", { className: "w-px h-full mx-auto bg-transparent group-hover:bg-blue-400/60 transition-colors" })
3087
- },
3088
- `col-h-${i}`
3089
- )),
3090
- rowBounds.map((topPx, i) => /* @__PURE__ */ jsx(
3091
- "div",
3092
- {
3093
- className: "absolute left-0 right-0 h-[6px] cursor-row-resize z-10 pointer-events-auto group",
3094
- style: { top: `${topPx - 3}px` },
3095
- onMouseDown: (e) => startRowDrag(e, i),
3096
- children: /* @__PURE__ */ jsx("div", { className: "h-px w-full my-auto bg-transparent group-hover:bg-blue-400/60 transition-colors" })
3097
- },
3098
- `row-h-${i}`
3099
- ))
3100
- ] });
3101
- }
3102
- function renderTableFromTableData(element, textStyle, options) {
3103
- const tableData = element.tableData;
3104
- const rowCount = tableData.rows.length;
3105
- const columnCount = tableData.columnWidths.length;
3106
- const selectedCell = options?.selectedCell || null;
3107
- const isEditable = Boolean(options?.editable) && canDrillDown(element);
3108
- const hasCellSelectionHandler = typeof options?.onSelectCell === "function";
3109
- const sharedCtx = {
3110
- tableStyleMap: options?.styleCtx?.tableStyleMap,
3111
- colorScheme: options?.styleCtx?.theme?.colorScheme,
3112
- fontScheme: options?.styleCtx?.theme?.fontScheme
3113
- };
3114
- const cellCss = (cell, rowIndex, cellIndex) => tableCellCss(
3115
- tableData,
3116
- cell,
3117
- { rowIndex, cellIndex, rowCount, columnCount },
3118
- sharedCtx,
3119
- textStyle
3120
- );
3121
- const selectionRect = (() => {
3122
- if (!selectedCell?.selectedCells || selectedCell.selectedCells.length < 2) {
3123
- return void 0;
3124
- }
3125
- const first = selectedCell.selectedCells[0];
3126
- const last = selectedCell.selectedCells[selectedCell.selectedCells.length - 1];
3127
- return computeSelectionRect(first.row, first.col, last.row, last.col, tableData);
3128
- })();
3129
- return /* @__PURE__ */ jsx(
3130
- TableResizeOverlay,
3131
- {
3132
- columnWidths: tableData.columnWidths,
3133
- editable: isEditable,
3134
- onResizeColumns: options?.onResizeColumns,
3135
- onResizeRow: options?.onResizeRow,
3136
- children: /* @__PURE__ */ jsx(
3137
- "div",
3138
- {
3139
- className: cn(
3140
- "w-full h-full overflow-hidden",
3141
- isEditable && hasCellSelectionHandler ? "pointer-events-auto" : "pointer-events-none"
3142
- ),
3143
- children: /* @__PURE__ */ jsxs(
3144
- "table",
3145
- {
3146
- className: "w-full h-full border-collapse table-fixed",
3147
- style: {
3148
- fontFamily: DEFAULT_FONT_FAMILY,
3149
- // `a:tblPr@rtl` mirrors the column order for RTL decks.
3150
- ...tableContainerCss(tableData)
3151
- },
3152
- children: [
3153
- tableData.columnWidths.length > 0 && /* @__PURE__ */ jsx("colgroup", { children: tableData.columnWidths.map((w, ci) => /* @__PURE__ */ jsx(
3154
- "col",
3155
- {
3156
- style: { width: `${(w * 100).toFixed(2)}%` }
3157
- },
3158
- `${element.id}-col-${ci}`
3159
- )) }),
3160
- /* @__PURE__ */ jsx("tbody", { children: tableData.rows.map((row, rowIndex) => /* @__PURE__ */ jsx(
3161
- "tr",
3162
- {
3163
- style: row.height ? { height: row.height } : void 0,
3164
- children: row.cells.map((cell, cellIndex) => {
3165
- if (cell.hMerge || cell.vMerge) {
3166
- return null;
3167
- }
3168
- const isCellSelected = selectedCell?.rowIndex === rowIndex && selectedCell?.columnIndex === cellIndex;
3169
- const isInMultiSelection = isCellInRect(rowIndex, cellIndex, selectionRect);
3170
- const isCellEditing = isCellSelected && selectedCell?.isEditing;
3171
- const diag = getCellDiagonalBorders2(
3172
- cell.style,
3173
- tableData,
3174
- { rowIndex, cellIndex, rowCount, columnCount },
3175
- options?.styleCtx
3176
- );
3177
- return /* @__PURE__ */ jsxs(
3178
- "td",
3179
- {
3180
- className: cn(
3181
- "px-1 py-0.5 align-top",
3182
- // No border of its own: a side the deck gives no border renders with
3183
- // none, as PowerPoint does. Editing keeps a faint grid.
3184
- isEditable && hasCellSelectionHandler ? "border border-blue-200/70 cursor-cell" : null,
3185
- isCellSelected ? "ring-1 ring-inset ring-blue-500" : null,
3186
- isInMultiSelection && !isCellSelected ? "bg-blue-500/15 ring-1 ring-inset ring-blue-400/50" : null
3187
- ),
3188
- colSpan: cell.gridSpan && cell.gridSpan > 1 ? cell.gridSpan : void 0,
3189
- rowSpan: cell.rowSpan && cell.rowSpan > 1 ? cell.rowSpan : void 0,
3190
- style: {
3191
- ...cellCss(cell, rowIndex, cellIndex),
3192
- ...diag ? { position: "relative" } : void 0
3193
- },
3194
- onClick: (event) => {
3195
- if (!isEditable || !hasCellSelectionHandler) {
3196
- return;
3197
- }
3198
- event.stopPropagation();
3199
- if (event.shiftKey && selectedCell) {
3200
- const rect = computeSelectionRect(
3201
- selectedCell.rowIndex,
3202
- selectedCell.columnIndex,
3203
- rowIndex,
3204
- cellIndex,
3205
- tableData
3206
- );
3207
- options?.onSelectCell?.({
3208
- rowIndex: selectedCell.rowIndex,
3209
- columnIndex: selectedCell.columnIndex,
3210
- selectedCells: rectToCells(rect)
3211
- });
3212
- } else {
3213
- options?.onSelectCell?.({
3214
- rowIndex,
3215
- columnIndex: cellIndex
3216
- });
3217
- }
3218
- },
3219
- onDoubleClick: (event) => {
3220
- if (!isEditable || !hasCellSelectionHandler) {
3221
- return;
3222
- }
3223
- event.stopPropagation();
3224
- options?.onSelectCell?.({
3225
- rowIndex,
3226
- columnIndex: cellIndex,
3227
- isEditing: true
3228
- });
3229
- },
3230
- children: [
3231
- diag ? /* @__PURE__ */ jsx(TableCellDiagonalBorders, { diag }) : null,
3232
- isCellEditing ? /* @__PURE__ */ jsx(
3233
- TableCellInput,
3234
- {
3235
- initialText: cell.text ?? "",
3236
- style: cellCss(cell, rowIndex, cellIndex),
3237
- onCommit: (text) => {
3238
- options?.onCommitCellEdit?.(rowIndex, cellIndex, text);
3239
- },
3240
- onCancel: () => {
3241
- options?.onSelectCell?.({
3242
- rowIndex,
3243
- columnIndex: cellIndex
3244
- });
3245
- }
3246
- }
3247
- ) : renderTableCellContent(cell, cell.text || "\xA0")
3248
- ]
3249
- },
3250
- `${element.id}-cell-${rowIndex}-${cellIndex}`
3251
- );
3252
- })
3253
- },
3254
- `${element.id}-row-${rowIndex}`
3255
- )) })
3256
- ]
3257
- }
3258
- )
3259
- }
3260
- )
3261
- }
3262
- );
3263
- }
3264
- function renderTableElement(element, textStyle, options) {
3265
- const parsedTable = parseTableElementData(element, textStyle);
3266
- if (!parsedTable) {
3267
- const tableEl2 = element;
3268
- if (tableEl2.tableData && tableEl2.tableData.rows.length > 0) {
3269
- return renderTableFromTableData(tableEl2, textStyle, options);
3270
- }
3271
- return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[11px] text-white/80 pointer-events-none", children: translationsEn["pptx.table.title"] });
3272
- }
3273
- const selectedCell = options?.selectedCell || null;
3274
- const isEditable = Boolean(options?.editable) && canDrillDown(element);
3275
- const hasCellSelectionHandler = typeof options?.onSelectCell === "function";
3276
- const tableEl = element;
3277
- const xmlSelectionRect = (() => {
3278
- if (!selectedCell?.selectedCells || selectedCell.selectedCells.length < 2) {
3279
- return void 0;
3280
- }
3281
- if (!tableEl.tableData) {
3282
- return void 0;
3283
- }
3284
- const first = selectedCell.selectedCells[0];
3285
- const last = selectedCell.selectedCells[selectedCell.selectedCells.length - 1];
3286
- return computeSelectionRect(first.row, first.col, last.row, last.col, tableEl.tableData);
3287
- })();
3288
- const schemeOverrides = options?.styleCtx?.theme?.colorScheme ? options.styleCtx.theme.colorScheme : void 0;
3289
- return /* @__PURE__ */ jsx(
3290
- TableResizeOverlay,
3291
- {
3292
- columnWidths: parsedTable.columnPercentages.map((p) => p / 100),
3293
- editable: isEditable,
3294
- onResizeColumns: options?.onResizeColumns,
3295
- onResizeRow: options?.onResizeRow,
3296
- children: /* @__PURE__ */ jsx(
3297
- "div",
3298
- {
3299
- className: cn(
3300
- "w-full h-full overflow-hidden",
3301
- isEditable && hasCellSelectionHandler ? "pointer-events-auto" : "pointer-events-none"
3302
- ),
3303
- children: /* @__PURE__ */ jsxs(
3304
- "table",
3305
- {
3306
- className: "w-full h-full border-collapse table-fixed",
3307
- style: {
3308
- fontFamily: DEFAULT_FONT_FAMILY,
3309
- // `a:tblPr@rtl` mirrors the column order for RTL decks.
3310
- ...tableContainerCss(tableEl.tableData)
3311
- },
3312
- children: [
3313
- parsedTable.columnPercentages.length > 0 ? /* @__PURE__ */ jsx("colgroup", { children: parsedTable.columnPercentages.map((percentage, columnIndex) => /* @__PURE__ */ jsx(
3314
- "col",
3315
- {
3316
- style: { width: `${percentage.toFixed(2)}%` }
3317
- },
3318
- `${element.id}-col-${columnIndex}`
3319
- )) }) : null,
3320
- /* @__PURE__ */ jsx("tbody", { children: parsedTable.rows.map((row, rowIndex) => {
3321
- const cells = ensureArrayValue(row["a:tc"]);
3322
- const rowHeightRaw = Number.parseInt(String(row["@_h"] || ""), 10);
3323
- const rowHeight = Number.isFinite(rowHeightRaw) && rowHeightRaw > 0 ? Math.max(16, rowHeightRaw / EMU_PER_PX) : void 0;
3324
- return /* @__PURE__ */ jsx(
3325
- "tr",
3326
- {
3327
- style: rowHeight ? { height: rowHeight } : void 0,
3328
- children: cells.map((cell, cellIndex) => {
3329
- const isHMerged = cell["@_hMerge"] === "1";
3330
- const isVMerged = cell["@_vMerge"] === "1";
3331
- if (isHMerged || isVMerged) {
3332
- return null;
3333
- }
3334
- const gridSpanRaw = Number.parseInt(String(cell["@_gridSpan"] || ""), 10);
3335
- const colSpan = Number.isFinite(gridSpanRaw) && gridSpanRaw > 1 ? gridSpanRaw : void 0;
3336
- const rowSpanRaw = Number.parseInt(String(cell["@_rowSpan"] || ""), 10);
3337
- const rSpan = Number.isFinite(rowSpanRaw) && rowSpanRaw > 1 ? rowSpanRaw : void 0;
3338
- const bandStyle = getTableCellBandStyle2(
3339
- element,
3340
- rowIndex,
3341
- cellIndex,
3342
- parsedTable.rowCount,
3343
- parsedTable.columnCount,
3344
- options?.styleCtx
3345
- );
3346
- const xmlCellStyle = extractTableCellStyle(cell, textStyle, schemeOverrides);
3347
- const tdCellOverride = tableEl.tableData?.rows[rowIndex]?.cells[cellIndex]?.style;
3348
- const diag = getCellDiagonalBorders2(
3349
- tdCellOverride,
3350
- tableEl.tableData,
3351
- {
3352
- rowIndex,
3353
- cellIndex,
3354
- rowCount: parsedTable.rowCount,
3355
- columnCount: parsedTable.columnCount
3356
- },
3357
- options?.styleCtx
3358
- );
3359
- const mergedStyle = {
3360
- // Lowest-priority fallback: `xmlCellStyle` never carries a
3361
- // `color` of its own (see `extractTableCellStyle`), so this
3362
- // is the only source when neither the table style band nor
3363
- // an explicit run/cell colour applies.
3364
- color: textStyle.color,
3365
- ...bandStyle,
3366
- ...xmlCellStyle,
3367
- ...tdCellOverride ? cellStyleToCss(tdCellOverride) : void 0,
3368
- ...diag ? { position: "relative" } : void 0
3369
- };
3370
- return /* @__PURE__ */ jsxs(
3371
- "td",
3372
- {
3373
- className: cn(
3374
- "px-1 py-0.5 align-top",
3375
- // No border of its own: a side the deck gives no border renders
3376
- // with none, as PowerPoint does. Editing keeps a faint grid.
3377
- isEditable && hasCellSelectionHandler ? "border border-blue-200/70 cursor-cell" : null,
3378
- selectedCell?.rowIndex === rowIndex && selectedCell?.columnIndex === cellIndex ? "ring-1 ring-inset ring-blue-500" : null,
3379
- isCellInRect(rowIndex, cellIndex, xmlSelectionRect) && !(selectedCell?.rowIndex === rowIndex && selectedCell?.columnIndex === cellIndex) ? "bg-blue-500/15 ring-1 ring-inset ring-blue-400/50" : null
3380
- ),
3381
- colSpan,
3382
- rowSpan: rSpan,
3383
- style: mergedStyle,
3384
- onClick: (event) => {
3385
- if (!isEditable || !hasCellSelectionHandler) {
3386
- return;
3387
- }
3388
- event.stopPropagation();
3389
- if (event.shiftKey && selectedCell && tableEl.tableData) {
3390
- const rect = computeSelectionRect(
3391
- selectedCell.rowIndex,
3392
- selectedCell.columnIndex,
3393
- rowIndex,
3394
- cellIndex,
3395
- tableEl.tableData
3396
- );
3397
- options?.onSelectCell?.({
3398
- rowIndex: selectedCell.rowIndex,
3399
- columnIndex: selectedCell.columnIndex,
3400
- selectedCells: rectToCells(rect)
3401
- });
3402
- } else {
3403
- options?.onSelectCell?.({
3404
- rowIndex,
3405
- columnIndex: cellIndex
3406
- });
3407
- }
3408
- },
3409
- onDoubleClick: (event) => {
3410
- if (!isEditable || !hasCellSelectionHandler) {
3411
- return;
3412
- }
3413
- event.stopPropagation();
3414
- options?.onSelectCell?.({
3415
- rowIndex,
3416
- columnIndex: cellIndex,
3417
- isEditing: true
3418
- });
3419
- },
3420
- children: [
3421
- diag ? /* @__PURE__ */ jsx(TableCellDiagonalBorders, { diag }) : null,
3422
- selectedCell?.rowIndex === rowIndex && selectedCell?.columnIndex === cellIndex && selectedCell?.isEditing ? /* @__PURE__ */ jsx(
3423
- TableCellInput,
3424
- {
3425
- initialText: extractCellText(cell) ?? "",
3426
- style: mergedStyle,
3427
- onCommit: (text) => {
3428
- options?.onCommitCellEdit?.(rowIndex, cellIndex, text);
3429
- },
3430
- onCancel: () => {
3431
- options?.onSelectCell?.({
3432
- rowIndex,
3433
- columnIndex: cellIndex
3434
- });
3435
- }
3436
- }
3437
- ) : renderTableCellContent(
3438
- tableEl.tableData?.rows[rowIndex]?.cells[cellIndex],
3439
- extractCellText(cell) || "\xA0"
3440
- )
3441
- ]
3442
- },
3443
- `${element.id}-cell-${rowIndex}-${cellIndex}`
3444
- );
3445
- })
3446
- },
3447
- `${element.id}-row-${rowIndex}`
3448
- );
3449
- }) })
3450
- ]
3451
- }
3452
- )
3453
- }
3454
- )
3455
- }
3456
- );
3457
- }
3458
- function renderGradientStops(stops) {
3459
- return stops.map((stop, i) => /* @__PURE__ */ jsx("stop", { offset: stop.offset, stopColor: stop.color, stopOpacity: stop.opacity }, i));
3460
- }
3461
- function partAttrs(part) {
3462
- return part ? chartPartToAttrs(part) : void 0;
3463
- }
3464
- function resolveReactPalette(chartData) {
3465
- if (chartData.colorPalette && chartData.colorPalette.length > 0) {
3466
- return chartData.colorPalette;
3467
- }
3468
- return [...getChartStylePalette(chartData.style?.styleId)];
3469
- }
3470
- function buildReactChartViewModel(element) {
3471
- if (element.type !== "chart" || !element.chartData) {
3472
- return buildChartViewModel(element);
3473
- }
3474
- const palette = resolveReactPalette(element.chartData), themedElement = {
3475
- ...element,
3476
- chartData: { ...element.chartData, colorPalette: palette }
3477
- };
3478
- return buildChartViewModel(themedElement);
3479
- }
3480
- function renderPrimitive(prim, key) {
3481
- switch (prim.kind) {
3482
- case "rect": {
3483
- const r = prim;
3484
- return /* @__PURE__ */ jsx(
3485
- "rect",
3486
- {
3487
- x: r.x,
3488
- y: r.y,
3489
- width: r.w,
3490
- height: r.h,
3491
- fill: r.fill,
3492
- rx: r.rx ?? 0,
3493
- opacity: r.opacity ?? 1,
3494
- ...partAttrs(r.part),
3495
- children: r.title !== void 0 ? /* @__PURE__ */ jsx("title", { children: r.title }) : null
3496
- },
3497
- key
3498
- );
3499
- }
3500
- case "path": {
3501
- const p = prim;
3502
- return /* @__PURE__ */ jsx(
3503
- "path",
3504
- {
3505
- d: p.d,
3506
- fill: p.fill,
3507
- stroke: p.stroke ?? "none",
3508
- strokeWidth: p.strokeWidth ?? 0,
3509
- strokeDasharray: p.dashArray,
3510
- fillOpacity: p.opacity ?? 1,
3511
- ...partAttrs(p.part),
3512
- children: p.title !== void 0 ? /* @__PURE__ */ jsx("title", { children: p.title }) : null
3513
- },
3514
- key
3515
- );
3516
- }
3517
- case "polyline": {
3518
- const p = prim;
3519
- return /* @__PURE__ */ jsx(
3520
- "polyline",
3521
- {
3522
- points: p.points,
3523
- stroke: p.stroke,
3524
- strokeWidth: p.strokeWidth,
3525
- fill: p.fill,
3526
- opacity: p.opacity ?? 1,
3527
- ...partAttrs(p.part),
3528
- children: p.title !== void 0 ? /* @__PURE__ */ jsx("title", { children: p.title }) : null
3529
- },
3530
- key
3531
- );
3532
- }
3533
- case "circle": {
3534
- const c = prim;
3535
- return /* @__PURE__ */ jsx(
3536
- "circle",
3537
- {
3538
- cx: c.cx,
3539
- cy: c.cy,
3540
- r: c.r,
3541
- fill: c.fill,
3542
- opacity: c.opacity ?? 1,
3543
- ...partAttrs(c.part),
3544
- children: c.title !== void 0 ? /* @__PURE__ */ jsx("title", { children: c.title }) : null
3545
- },
3546
- key
3547
- );
3548
- }
3549
- case "line": {
3550
- const l = prim;
3551
- return /* @__PURE__ */ jsx(
3552
- "line",
3553
- {
3554
- x1: l.x1,
3555
- y1: l.y1,
3556
- x2: l.x2,
3557
- y2: l.y2,
3558
- stroke: l.stroke,
3559
- strokeWidth: l.strokeWidth,
3560
- strokeDasharray: l.dashArray,
3561
- opacity: l.opacity ?? 1,
3562
- transform: l.transform,
3563
- children: l.title !== void 0 ? /* @__PURE__ */ jsx("title", { children: l.title }) : null
3564
- },
3565
- key
3566
- );
3567
- }
3568
- case "polygon": {
3569
- const p = prim;
3570
- return /* @__PURE__ */ jsx(
3571
- "polygon",
3572
- {
3573
- points: p.points,
3574
- fill: p.fill,
3575
- stroke: p.stroke,
3576
- strokeWidth: p.strokeWidth,
3577
- opacity: p.opacity ?? 1,
3578
- strokeDasharray: p.dashArray,
3579
- transform: p.transform,
3580
- ...partAttrs(p.part),
3581
- children: p.title !== void 0 ? /* @__PURE__ */ jsx("title", { children: p.title }) : null
3582
- },
3583
- key
3584
- );
3585
- }
3586
- case "text": {
3587
- const t = prim;
3588
- return /* @__PURE__ */ jsx(
3589
- "text",
3590
- {
3591
- x: t.x,
3592
- y: t.y,
3593
- textAnchor: t.textAnchor,
3594
- fontSize: t.fontSize,
3595
- fill: t.fill,
3596
- fontWeight: t.fontWeight ?? "normal",
3597
- dominantBaseline: t.dominantBaseline,
3598
- opacity: t.opacity ?? 1,
3599
- transform: t.transform,
3600
- children: t.text
3601
- },
3602
- key
3603
- );
3604
- }
3605
- default:
3606
- return null;
3607
- }
3608
- }
3609
- function renderText(t, key) {
3610
- return /* @__PURE__ */ jsx(
3611
- "text",
3612
- {
3613
- x: t.x,
3614
- y: t.y,
3615
- textAnchor: t.textAnchor,
3616
- fontSize: t.fontSize,
3617
- fill: t.fill,
3618
- fontWeight: t.fontWeight ?? "normal",
3619
- fontStyle: t.fontStyle ?? "normal",
3620
- fontFamily: t.fontFamily,
3621
- dominantBaseline: t.dominantBaseline,
3622
- opacity: t.opacity ?? 1,
3623
- transform: t.transform,
3624
- children: t.text
3625
- },
3626
- key
3627
- );
3628
- }
3629
- function renderChartViewModel(elementId, vm, preserveAspectRatio = "none") {
3630
- const legendItems = computeChartLegendLayout(vm);
3631
- return /* @__PURE__ */ jsxs(
3632
- "svg",
3633
- {
3634
- className: "w-full h-full pointer-events-none",
3635
- viewBox: `0 0 ${vm.svgWidth} ${vm.svgHeight}`,
3636
- preserveAspectRatio,
3637
- children: [
3638
- vm.defs && vm.defs.length > 0 && /* @__PURE__ */ jsx("defs", { children: vm.defs.map(
3639
- (def, i) => def.kind === "linearGradient" ? /* @__PURE__ */ jsx(
3640
- "linearGradient",
3641
- {
3642
- id: def.id,
3643
- x1: def.x1,
3644
- y1: def.y1,
3645
- x2: def.x2,
3646
- y2: def.y2,
3647
- children: renderGradientStops(def.stops)
3648
- },
3649
- `${elementId}-def-${i}`
3650
- ) : def.kind === "radialGradient" ? /* @__PURE__ */ jsx(
3651
- "radialGradient",
3652
- {
3653
- id: def.id,
3654
- cx: def.cx,
3655
- cy: def.cy,
3656
- r: def.r,
3657
- children: renderGradientStops(def.stops)
3658
- },
3659
- `${elementId}-def-${i}`
3660
- ) : /* @__PURE__ */ jsx(
3661
- "pattern",
3662
- {
3663
- id: def.id,
3664
- patternUnits: def.patternUnits,
3665
- x: def.x,
3666
- y: def.y,
3667
- width: def.width,
3668
- height: def.height,
3669
- children: /* @__PURE__ */ jsx(
3670
- "image",
3671
- {
3672
- href: def.href,
3673
- x: 0,
3674
- y: 0,
3675
- width: def.width,
3676
- height: def.height,
3677
- preserveAspectRatio: def.preserveAspectRatio
3678
- }
3679
- )
3680
- },
3681
- `${elementId}-def-${i}`
3682
- )
3683
- ) }),
3684
- vm.areaFill && /* @__PURE__ */ jsx(
3685
- "rect",
3686
- {
3687
- x: 0,
3688
- y: 0,
3689
- width: vm.svgWidth,
3690
- height: vm.svgHeight,
3691
- rx: vm.areaRadius,
3692
- fill: vm.areaFill
3693
- }
3694
- ),
3695
- vm.title && /* @__PURE__ */ jsx(
3696
- "text",
3697
- {
3698
- x: vm.titleX,
3699
- y: vm.titleY,
3700
- textAnchor: "middle",
3701
- fontSize: vm.titleStyle?.fontSize ?? 12,
3702
- fontWeight: vm.titleStyle?.fontWeight ?? 600,
3703
- fontFamily: vm.titleStyle?.fontFamily,
3704
- fill: vm.titleStyle?.fill ?? "#1e293b",
3705
- "data-chart-part": "title",
3706
- children: vm.titleRunSpans && vm.titleRunSpans.length > 0 ? vm.titleRunSpans.map((run, i) => /* @__PURE__ */ jsx(
3707
- "tspan",
3708
- {
3709
- fontSize: run.fontSize,
3710
- fontWeight: run.fontWeight,
3711
- fontStyle: run.fontStyle,
3712
- fontFamily: run.fontFamily,
3713
- fill: run.fill,
3714
- children: run.text
3715
- },
3716
- `${elementId}-title-run-${i}`
3717
- )) : vm.title
3718
- }
3719
- ),
3720
- vm.gridlines.map((gl, i) => /* @__PURE__ */ jsx(
3721
- "line",
3722
- {
3723
- x1: gl.x1,
3724
- y1: gl.y1,
3725
- x2: gl.x2,
3726
- y2: gl.y2,
3727
- stroke: gl.stroke,
3728
- strokeWidth: gl.strokeWidth
3729
- },
3730
- `${elementId}-gl-${i}`
3731
- )),
3732
- (vm.secondaryGridlines ?? []).map((gl, i) => /* @__PURE__ */ jsx(
3733
- "line",
3734
- {
3735
- x1: gl.x1,
3736
- y1: gl.y1,
3737
- x2: gl.x2,
3738
- y2: gl.y2,
3739
- stroke: gl.stroke,
3740
- strokeWidth: gl.strokeWidth,
3741
- strokeDasharray: gl.dashArray,
3742
- opacity: gl.opacity ?? 1
3743
- },
3744
- `${elementId}-sgl-${i}`
3745
- )),
3746
- vm.axisLabels.map((lbl, i) => renderText(lbl, `${elementId}-al-${i}`)),
3747
- (vm.secondaryAxisLabels ?? []).map((lbl, i) => renderText(lbl, `${elementId}-sal-${i}`)),
3748
- vm.zeroLine && /* @__PURE__ */ jsx(
3749
- "line",
3750
- {
3751
- x1: vm.zeroLine.x1,
3752
- y1: vm.zeroLine.y1,
3753
- x2: vm.zeroLine.x2,
3754
- y2: vm.zeroLine.y2,
3755
- stroke: vm.zeroLine.stroke,
3756
- strokeWidth: vm.zeroLine.strokeWidth
3757
- }
3758
- ),
3759
- vm.categoryLabels.map((lbl, i) => renderText(lbl, `${elementId}-cl-${i}`)),
3760
- vm.primitives.map((prim, i) => renderPrimitive(prim, `${elementId}-p-${i}`)),
3761
- vm.dataLabels.map((dl, i) => renderText(dl, `${elementId}-dl-${i}`)),
3762
- legendItems.map((item, i) => /* @__PURE__ */ jsxs(
3763
- "g",
3764
- {
3765
- transform: `translate(${item.x.toFixed(1)},${item.y.toFixed(1)})`,
3766
- children: [
3767
- item.lineSwatch ? item.lineSwatch.primitives.map(
3768
- (prim, si) => renderPrimitive(prim, `${elementId}-lg-${i}-sw-${si}`)
3769
- ) : /* @__PURE__ */ jsx("rect", { x: 0, y: -7, width: 10, height: 10, rx: 2, fill: item.color }),
3770
- /* @__PURE__ */ jsx(
3771
- "text",
3772
- {
3773
- x: 13,
3774
- y: 3,
3775
- fontSize: item.fontSize,
3776
- fill: item.fill,
3777
- fontWeight: item.fontWeight,
3778
- fontStyle: item.fontStyle,
3779
- fontFamily: item.fontFamily,
3780
- children: item.label
3781
- }
3782
- )
3783
- ]
3784
- },
3785
- `${elementId}-lg-${i}`
3786
- ))
3787
- ]
3788
- }
3789
- );
3790
- }
3791
- function renderChartElement(element) {
3792
- if (element.type !== "chart") {
3793
- return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[11px] text-white/80 pointer-events-none", children: translationsEn["pptx.chart.heading"] });
3794
- }
3795
- const chartData = element.chartData;
3796
- if (!chartData || chartData.series.length === 0) {
3797
- return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[11px] text-white/80 pointer-events-none", children: translationsEn["pptx.chart.heading"] });
3798
- }
3799
- const kind = resolveChartKind(chartData.chartType ?? "bar");
3800
- return renderChartViewModel(
3801
- element.id,
3802
- buildReactChartViewModel(element),
3803
- chartPreserveAspectRatio(kind)
3804
- );
3805
- }
3806
-
3807
- // src/viewer/utils/pdf-builder-notes-builder.ts
3808
- function buildNotesPdf(pages) {
3809
- const images = pages.map((page) => {
3810
- const dataUrl = page.canvas.toDataURL("image/jpeg", 0.92);
3811
- const base64 = dataUrl.split(",")[1] ?? "";
3812
- const raw = atob(base64);
3813
- const bytes2 = new Uint8Array(raw.length);
3814
- for (let i = 0; i < raw.length; i++) {
3815
- bytes2[i] = raw.charCodeAt(i);
3816
- }
3817
- return { bytes: bytes2, w: page.canvas.width, h: page.canvas.height };
3818
- });
3819
- const meta = pages.map((page) => ({
3820
- notes: page.notes,
3821
- slideNumber: page.slideNumber
3822
- }));
3823
- const bytes = buildNotesPdfBytes(images, meta);
3824
- const blob = new Blob([bytes], { type: "application/pdf" });
3825
- return URL.createObjectURL(blob);
3826
- }
3827
-
3828
- // src/viewer/utils/pdf-builder-slides.ts
3829
- function buildPdfFromTiledImageData(pages) {
3830
- const bytes = buildTiledSlidesPdfBytes(pages);
3831
- const blob = new Blob([bytes], { type: "application/pdf" });
3832
- return URL.createObjectURL(blob);
3833
- }
3834
-
3835
- // src/viewer/utils/animation-preview.ts
3836
- var activePreview = null;
3837
- function startPreviewAnimation(elementId, preset, options) {
3838
- stopPreviewAnimation();
3839
- const domEl = document.querySelector(`[data-element-id="${elementId}"]`);
3840
- const descriptor = buildPreviewAnimation(preset, {
3841
- ...options,
3842
- geometry: domEl ? measurePreviewGeometry(domEl) : void 0
3843
- });
3844
- if (!descriptor) {
3845
- return;
3846
- }
3847
- playDescriptor(elementId, descriptor);
3848
- }
3849
- function startMotionPathPreview(elementId, path, options) {
3850
- stopPreviewAnimation();
3851
- const domEl = document.querySelector(`[data-element-id="${elementId}"]`);
3852
- if (!domEl) {
3853
- return;
3854
- }
3855
- const stage = domEl.offsetParent;
3856
- const descriptor = buildMotionPathPreview({
3857
- path,
3858
- slideWidth: stage?.offsetWidth || DEFAULT_PREVIEW_SLIDE_WIDTH,
3859
- slideHeight: stage?.offsetHeight || DEFAULT_PREVIEW_SLIDE_HEIGHT,
3860
- durationMs: options?.durationMs,
3861
- delayMs: options?.delayMs,
3862
- timingCurve: options?.timingCurve
3863
- });
3864
- if (!descriptor) {
3865
- return;
3866
- }
3867
- playDescriptor(elementId, descriptor);
3868
- }
3869
- var DEFAULT_PREVIEW_SLIDE_WIDTH = 1280;
3870
- var DEFAULT_PREVIEW_SLIDE_HEIGHT = 720;
3871
- function playDescriptor(elementId, descriptor) {
3872
- const domEl = document.querySelector(`[data-element-id="${elementId}"]`);
3873
- if (!domEl) {
3874
- return;
3875
- }
3876
- const styleEl = document.createElement("style");
3877
- styleEl.textContent = descriptor.keyframesCss;
3878
- document.head.appendChild(styleEl);
3879
- const originalAnimation = domEl.style.animation;
3880
- const originalVisibility = domEl.style.visibility;
3881
- domEl.style.visibility = "visible";
3882
- domEl.style.animation = descriptor.cssAnimation;
3883
- const timeoutId = setTimeout(() => {
3884
- domEl.style.animation = originalAnimation;
3885
- domEl.style.visibility = originalVisibility;
3886
- styleEl.remove();
3887
- if (activePreview?.elementId === elementId) {
3888
- activePreview = null;
3889
- }
3890
- }, descriptor.durationMs + 100);
3891
- activePreview = {
3892
- elementId,
3893
- timeoutId,
3894
- styleEl,
3895
- originalAnimation,
3896
- originalVisibility
3897
- };
3898
- }
3899
- function stopPreviewAnimation() {
3900
- if (!activePreview) {
3901
- return;
3902
- }
3903
- clearTimeout(activePreview.timeoutId);
3904
- activePreview.styleEl.remove();
3905
- const domEl = document.querySelector(
3906
- `[data-element-id="${activePreview.elementId}"]`
3907
- );
3908
- if (domEl) {
3909
- domEl.style.animation = activePreview.originalAnimation;
3910
- domEl.style.visibility = activePreview.originalVisibility;
3911
- }
3912
- activePreview = null;
3913
- }
3914
-
3915
- export { ANIMATION_PRESET_OPTIONS, ARROW_SIZE_OPTIONS, CONNECTOR_ARROW_OPTIONS, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, DEFAULT_FILL_COLOR, DEFAULT_FONT_FAMILY2 as DEFAULT_FONT_FAMILY, DEFAULT_SECTION_GROUP_ID, DEFAULT_STROKE_COLOR, DEFAULT_TABLE_COLUMNS, DEFAULT_TABLE_ROWS, DEFAULT_TEXT_COLOR, DEFAULT_TEXT_FONT_SIZE, GRID_SIZE, HYPERLINK_COLOR, MIN_ELEMENT_SIZE, SHAPE_PRESETS, SHORTCUT_REFERENCE_ITEMS, SLIDE_NAV_THUMBNAIL_WIDTH, STROKE_DASH_OPTIONS2 as STROKE_DASH_OPTIONS, TOOLBAR_SECTIONS, UNGROUPED_SECTION_ID, ZOOM_TO_SELECTION_PADDING, build3DExtrusionData2 as build3DExtrusionData, buildLineGlowFilter, buildLineShadowCss, buildNotesPdf, buildPdfFromTiledImageData, buildReactChartViewModel, buildTextBody3DSceneStyle2 as buildTextBody3DSceneStyle, cloneHistorySnapshot2 as cloneHistorySnapshot, cn, getAnimationInitialStyle2 as getAnimationInitialStyle, getConnectorPathGeometry2 as getConnectorPathGeometry, getCropShapeClipPath2 as getCropShapeClipPath, getDuotoneColors, getImageRenderStyle, getImageSurfaceMaskStyle, getImageSurfaceStyle, getImageTilingStyle2 as getImageTilingStyle, getShapeVisualStyle, getTextLayoutStyle, getTextStyleForElement, getTextWarpStyle, hasDagDuotoneEffect, isConnectorOrLineElement, renderChartElement, renderConnectorMarker, renderDagDuotoneSvgFilter, renderMediaElement, renderTableElement, renderTextSegments, renderVectorShape, shouldRenderFallbackLabel, startMotionPathPreview, startPreviewAnimation, stopPreviewAnimation };