@printine/designer 0.1.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 (199) hide show
  1. package/README.md +146 -0
  2. package/dist/DesignerRuntimeContext.d.ts +3 -0
  3. package/dist/PrintineDesigner.d.ts +9 -0
  4. package/dist/api/client.d.ts +8 -0
  5. package/dist/api/client.js +82 -0
  6. package/dist/api/common-endpoints.d.ts +12 -0
  7. package/dist/api/design-endpoints.d.ts +18 -0
  8. package/dist/api/endpoints.d.ts +33 -0
  9. package/dist/api/endpoints.js +46 -0
  10. package/dist/api/files-endpoints.d.ts +1 -0
  11. package/dist/assets/icons/aspect_ratio.d.ts +3 -0
  12. package/dist/assets/icons/delete.d.ts +3 -0
  13. package/dist/assets/icons/fit_screen.d.ts +3 -0
  14. package/dist/assets/icons/index.d.ts +11 -0
  15. package/dist/assets/icons/loading_1.d.ts +3 -0
  16. package/dist/assets/icons/loading_2.d.ts +3 -0
  17. package/dist/assets/icons/loading_3.d.ts +3 -0
  18. package/dist/assets/icons/pan.d.ts +3 -0
  19. package/dist/assets/icons/setting.d.ts +3 -0
  20. package/dist/assets/icons/text_color.d.ts +3 -0
  21. package/dist/assets/icons/text_h.d.ts +3 -0
  22. package/dist/assets/icons/text_v.d.ts +3 -0
  23. package/dist/bundled-assets.d.ts +4 -0
  24. package/dist/components/add-design.d.ts +8 -0
  25. package/dist/components/assets-dialog.d.ts +21 -0
  26. package/dist/components/canvas-context-menu.d.ts +6 -0
  27. package/dist/components/color-picker/index.d.ts +10 -0
  28. package/dist/components/context.d.ts +24 -0
  29. package/dist/components/design.d.ts +9 -0
  30. package/dist/components/fonts.d.ts +26 -0
  31. package/dist/components/image-crop.d.ts +4 -0
  32. package/dist/components/image-edit.d.ts +1 -0
  33. package/dist/components/layout/aside.d.ts +3 -0
  34. package/dist/components/layout/bottom-nav/element-menu-sheet.d.ts +11 -0
  35. package/dist/components/layout/bottom-nav/element-sheet.d.ts +11 -0
  36. package/dist/components/layout/bottom-nav/index.d.ts +1 -0
  37. package/dist/components/layout/header.d.ts +5 -0
  38. package/dist/components/loading.d.ts +3 -0
  39. package/dist/components/menus/ai.d.ts +5 -0
  40. package/dist/components/menus/background.d.ts +6 -0
  41. package/dist/components/menus/clipart.d.ts +6 -0
  42. package/dist/components/menus/files.d.ts +7 -0
  43. package/dist/components/menus/image.d.ts +8 -0
  44. package/dist/components/menus/index.d.ts +13 -0
  45. package/dist/components/menus/layer.d.ts +5 -0
  46. package/dist/components/menus/product.d.ts +3 -0
  47. package/dist/components/menus/sca.d.ts +5 -0
  48. package/dist/components/menus/shapes.d.ts +6 -0
  49. package/dist/components/menus/templates.d.ts +6 -0
  50. package/dist/components/menus/text-effects.d.ts +5 -0
  51. package/dist/components/menus/text.d.ts +6 -0
  52. package/dist/components/placement-tab.d.ts +7 -0
  53. package/dist/components/preview/3d.d.ts +7 -0
  54. package/dist/components/preview/graphic.d.ts +7 -0
  55. package/dist/components/preview/index.d.ts +18 -0
  56. package/dist/components/preview/open.d.ts +7 -0
  57. package/dist/components/preview/real-time/3d.d.ts +12 -0
  58. package/dist/components/preview/real-time/graphic.d.ts +18 -0
  59. package/dist/components/preview/real-time/index.d.ts +11 -0
  60. package/dist/components/scale-tools.d.ts +7 -0
  61. package/dist/components/semi-deisgn-config.d.ts +13 -0
  62. package/dist/components/semi-design-link.d.ts +9 -0
  63. package/dist/components/store.d.ts +1 -0
  64. package/dist/components/toolbar.d.ts +2 -0
  65. package/dist/config-D2qm60gf.js +16 -0
  66. package/dist/contracts/api-design.d.ts +176 -0
  67. package/dist/contracts/design.d.ts +86 -0
  68. package/dist/design-7zRpRtv9.js +20331 -0
  69. package/dist/engine/canvas-designer.d.ts +101 -0
  70. package/dist/engine/canvas-designer.js +313 -0
  71. package/dist/engine/constants.d.ts +27 -0
  72. package/dist/engine/constants.js +148 -0
  73. package/dist/engine/editor.d.ts +186 -0
  74. package/dist/engine/editor.js +734 -0
  75. package/dist/engine/events.d.ts +34 -0
  76. package/dist/engine/events.js +7 -0
  77. package/dist/engine/extensions/curved-text.d.ts +59 -0
  78. package/dist/engine/fabric.d.ts +4 -0
  79. package/dist/engine/fabric.js +187 -0
  80. package/dist/engine/history.d.ts +27 -0
  81. package/dist/engine/history.js +56 -0
  82. package/dist/engine/managers/event.d.ts +101 -0
  83. package/dist/engine/managers/event.js +410 -0
  84. package/dist/engine/managers/resize.d.ts +24 -0
  85. package/dist/engine/managers/resize.js +63 -0
  86. package/dist/engine/managers/snapline.d.ts +70 -0
  87. package/dist/engine/managers/snapline.js +124 -0
  88. package/dist/engine/managers/workspace.d.ts +26 -0
  89. package/dist/engine/managers/workspace.js +53 -0
  90. package/dist/engine/processors/image.d.ts +88 -0
  91. package/dist/engine/processors/image.js +473 -0
  92. package/dist/engine/processors/template.d.ts +45 -0
  93. package/dist/engine/processors/template.js +314 -0
  94. package/dist/engine/processors/text.d.ts +74 -0
  95. package/dist/engine/processors/text.js +215 -0
  96. package/dist/engine/semi-export-editor.d.ts +199 -0
  97. package/dist/engine/types.d.ts +28 -0
  98. package/dist/engine/utils/common.d.ts +25 -0
  99. package/dist/engine/utils/common.js +68 -0
  100. package/dist/engine/utils/dialog.d.ts +25 -0
  101. package/dist/engine/utils/font.d.ts +5 -0
  102. package/dist/engine/utils/font.js +39 -0
  103. package/dist/engine/utils/image.d.ts +8 -0
  104. package/dist/engine/utils/image.js +50 -0
  105. package/dist/engine/utils/loading.d.ts +17 -0
  106. package/dist/engine/utils/loading.js +71 -0
  107. package/dist/engine/utils/measure.d.ts +6 -0
  108. package/dist/engine/utils/measure.js +6 -0
  109. package/dist/engine/utils/mockup.d.ts +59 -0
  110. package/dist/engine/utils/setting.d.ts +4 -0
  111. package/dist/engine/utils/setting.js +73 -0
  112. package/dist/fabric-types.d.ts +59 -0
  113. package/dist/full-designer.js +14 -0
  114. package/dist/hooks/pagination.d.ts +19 -0
  115. package/dist/hooks/use-adobe-express.d.ts +50 -0
  116. package/dist/hooks/use-before-unload.d.ts +1 -0
  117. package/dist/hooks/use-image-drop.d.ts +18 -0
  118. package/dist/hooks/use-oss-upload.d.ts +34 -0
  119. package/dist/hooks/use-screen.d.ts +1 -0
  120. package/dist/i18n/index.d.ts +12 -0
  121. package/dist/images/design/asset_empty.png +0 -0
  122. package/dist/images/design/design/background.svg +3 -0
  123. package/dist/images/design/design/color-picker.svg +15 -0
  124. package/dist/images/design/design/computer.svg +1 -0
  125. package/dist/images/design/design/copy.svg +1 -0
  126. package/dist/images/design/design/image.svg +10 -0
  127. package/dist/images/design/design/line.svg +1 -0
  128. package/dist/images/design/design/text.svg +1 -0
  129. package/dist/images/design/design/title.svg +1 -0
  130. package/dist/images/design/empty.png +0 -0
  131. package/dist/images/design/layer_empty.png +0 -0
  132. package/dist/images/design/menu/ai.svg +1 -0
  133. package/dist/images/design/menu/back.svg +4 -0
  134. package/dist/images/design/menu/background.svg +6 -0
  135. package/dist/images/design/menu/clipart.svg +6 -0
  136. package/dist/images/design/menu/file.svg +4 -0
  137. package/dist/images/design/menu/fill.svg +1 -0
  138. package/dist/images/design/menu/image.svg +7 -0
  139. package/dist/images/design/menu/layer.svg +6 -0
  140. package/dist/images/design/menu/product.svg +3 -0
  141. package/dist/images/design/menu/sca.svg +5 -0
  142. package/dist/images/design/menu/shapes.svg +5 -0
  143. package/dist/images/design/menu/templates.svg +6 -0
  144. package/dist/images/design/menu/text.svg +6 -0
  145. package/dist/images/design/menu/uploads.svg +7 -0
  146. package/dist/images/design/spinner.svg +3 -0
  147. package/dist/images/design/spinners.svg +17 -0
  148. package/dist/images/design/transparent_pattern.svg +5 -0
  149. package/dist/index.d.ts +39 -0
  150. package/dist/index.js +127 -0
  151. package/dist/lib/analytics.d.ts +17 -0
  152. package/dist/lib/cn.d.ts +3 -0
  153. package/dist/lib/common.d.ts +16 -0
  154. package/dist/lib/config.d.ts +14 -0
  155. package/dist/lib/navigation.d.ts +2 -0
  156. package/dist/renderer/ThreeDPreview.d.ts +14 -0
  157. package/dist/renderer/sticker.js +1731 -0
  158. package/dist/renderer/three-d-preview.js +32 -0
  159. package/dist/state/designer-store.d.ts +43 -0
  160. package/dist/state/designer-store.js +85 -0
  161. package/dist/style.css +1 -0
  162. package/dist/types.d.ts +184 -0
  163. package/dist/ui/accordion.d.ts +7 -0
  164. package/dist/ui/button-group.d.ts +11 -0
  165. package/dist/ui/button.d.ts +10 -0
  166. package/dist/ui/checkbox.d.ts +4 -0
  167. package/dist/ui/command.d.ts +18 -0
  168. package/dist/ui/confirm-dialog.d.ts +15 -0
  169. package/dist/ui/context-menu.d.ts +25 -0
  170. package/dist/ui/dialog.d.ts +15 -0
  171. package/dist/ui/drawer.d.ts +15 -0
  172. package/dist/ui/editable-combobox.d.ts +10 -0
  173. package/dist/ui/hint.d.ts +8 -0
  174. package/dist/ui/image.d.ts +24 -0
  175. package/dist/ui/input-group.d.ts +16 -0
  176. package/dist/ui/input-with-addon.d.ts +17 -0
  177. package/dist/ui/input.d.ts +3 -0
  178. package/dist/ui/label.d.ts +4 -0
  179. package/dist/ui/loading-button.d.ts +15 -0
  180. package/dist/ui/popover.d.ts +9 -0
  181. package/dist/ui/scroll-area.d.ts +5 -0
  182. package/dist/ui/select.d.ts +22 -0
  183. package/dist/ui/separator.d.ts +4 -0
  184. package/dist/ui/shared-loading-button.d.ts +20 -0
  185. package/dist/ui/simple-dialog.d.ts +19 -0
  186. package/dist/ui/simple-popover.d.ts +13 -0
  187. package/dist/ui/simple-select.d.ts +20 -0
  188. package/dist/ui/skeleton.d.ts +2 -0
  189. package/dist/ui/slider.d.ts +4 -0
  190. package/dist/ui/spinner-box.d.ts +12 -0
  191. package/dist/ui/spinner.d.ts +2 -0
  192. package/dist/ui/switch.d.ts +6 -0
  193. package/dist/ui/tab/index.d.ts +15 -0
  194. package/dist/ui/textarea.d.ts +3 -0
  195. package/dist/ui/tooltip.d.ts +7 -0
  196. package/dist/workers/image-resize.worker.d.ts +1 -0
  197. package/dist/workers/mockup.worker.d.ts +5 -0
  198. package/dist/workers/render.worker.d.ts +1 -0
  199. package/package.json +201 -0
@@ -0,0 +1,314 @@
1
+ import "../fabric.js";
2
+ import { urlToDataURL as P, cloneObject as I } from "../utils/common.js";
3
+ import { fabric as m } from "fabric";
4
+ class X {
5
+ constructor(s) {
6
+ this.editor = s;
7
+ }
8
+ /**
9
+ * 获取当前选中的对象,将其组合转为临时模板
10
+ */
11
+ async saveAsTemplate() {
12
+ const e = this.editor.canvas.getActiveObjects();
13
+ if (!e.length) return null;
14
+ const n = this._getSelectionBoundingBox(e);
15
+ if (!n) return null;
16
+ const l = await this._generatePreviewImage(e, n), i = await Promise.all(
17
+ e.map(async (t) => {
18
+ const o = this._calculateRelativePosition(t, n);
19
+ if (t.type?.includes("i-text")) {
20
+ const a = t, c = typeof a.__lastCurvedWidth == "number" ? a.__lastCurvedWidth : t.width, g = typeof a.__lastCurvedHeight == "number" ? a.__lastCurvedHeight : t.height;
21
+ return {
22
+ type: t.type || "",
23
+ position: {
24
+ rotation: t.angle || 0,
25
+ width: (c || 0) * (t.scaleX || 1),
26
+ height: (g || 0) * (t.scaleY || 1),
27
+ left: o.left,
28
+ top: o.top
29
+ },
30
+ text: {
31
+ text: t.text || "",
32
+ color: t.fill?.toString() || "",
33
+ fontFamily: t.fontFamily || "",
34
+ fontWeight: t.fontWeight || 400,
35
+ fontStyle: t.fontStyle || "",
36
+ fontPath: t.fontPath || "",
37
+ fontSize: t.fontSize || 0,
38
+ textAlign: t.textAlign || "",
39
+ // 弧形字段:普通 IText 默认为 0
40
+ radius: typeof a.radius == "number" ? a.radius : 0,
41
+ spacing: typeof a.spacing == "number" ? a.spacing : 0
42
+ }
43
+ };
44
+ }
45
+ if (t.type === "image" && t.clipPath) {
46
+ const a = await this._rasterizeClippedImage(t, n, 2);
47
+ if (a)
48
+ return {
49
+ type: "image",
50
+ position: {
51
+ rotation: 0,
52
+ width: a.width,
53
+ height: a.height,
54
+ left: a.left,
55
+ top: a.top
56
+ },
57
+ image: {
58
+ url: a.dataUrl,
59
+ width: a.width,
60
+ height: a.height,
61
+ flipX: !1,
62
+ flipY: !1
63
+ }
64
+ };
65
+ }
66
+ return {
67
+ type: t.type || "",
68
+ position: {
69
+ rotation: t.angle || 0,
70
+ width: t.width * (t.scaleX || 1),
71
+ height: t.height * (t.scaleY || 1),
72
+ left: o.left,
73
+ top: o.top
74
+ },
75
+ image: {
76
+ url: t.originSrc || t.getSrc?.() || "",
77
+ width: t.width || 0,
78
+ height: t.height || 0,
79
+ flipX: t.flipX || !1,
80
+ flipY: t.flipY || !1,
81
+ fill: t.fill
82
+ }
83
+ };
84
+ })
85
+ );
86
+ return {
87
+ areaWidth: n.width,
88
+ areaHeight: n.height,
89
+ urlPreview: l || "",
90
+ elements: i
91
+ };
92
+ }
93
+ /**
94
+ * 计算选中对象的总边界框
95
+ */
96
+ _getSelectionBoundingBox(s) {
97
+ if (!s.length) return null;
98
+ let e = 0, n;
99
+ if (s.forEach((t) => {
100
+ const o = t.getBoundingRect(!0, !0), a = o.width * o.height;
101
+ a > e && (e = a, n = t);
102
+ }), !n) return null;
103
+ const l = n.getBoundingRect(!0, !0), i = {
104
+ left: l.left,
105
+ top: l.top,
106
+ width: l.width,
107
+ height: l.height,
108
+ right: l.left + l.width,
109
+ bottom: l.top + l.height
110
+ };
111
+ return s.forEach((t) => {
112
+ if (t === n) return;
113
+ const o = t.getBoundingRect(!0, !0);
114
+ i.left = Math.min(i.left, o.left), i.top = Math.min(i.top, o.top);
115
+ const a = o.left + o.width, c = o.top + o.height;
116
+ i.right = Math.max(i.right, a), i.bottom = Math.max(i.bottom, c);
117
+ }), i.width = i.right - i.left, i.height = i.bottom - i.top, i;
118
+ }
119
+ /**
120
+ * 计算对象相对于边界框的位置
121
+ */
122
+ _calculateRelativePosition(s, e) {
123
+ const n = s.getBoundingRect(!0, !0);
124
+ return {
125
+ left: n.left - e.left,
126
+ top: n.top - e.top
127
+ };
128
+ }
129
+ /**
130
+ * 生成预览图片
131
+ */
132
+ async _generatePreviewImage(s, e) {
133
+ const n = document.createElement("canvas");
134
+ n.width = e.width, n.height = e.height, n.className = "template-preview-canvas";
135
+ const l = new m.Canvas(n, {
136
+ backgroundColor: "transparent",
137
+ width: e.width,
138
+ height: e.height,
139
+ enableRetinaScaling: !1
140
+ });
141
+ try {
142
+ return (await Promise.all(
143
+ s.map(
144
+ (t) => new Promise((o) => {
145
+ try {
146
+ if (t.type === "image") {
147
+ const a = t.getSrc ? t.getSrc() : "";
148
+ m.Image.fromURL(
149
+ a,
150
+ async (c) => {
151
+ const g = await P(a);
152
+ let h;
153
+ t.clipPath && (h = await I(t.clipPath, ["absolutePositioned"]), h.set({
154
+ left: h.left - e.left,
155
+ top: h.top - e.top
156
+ })), c.setSrc(g, () => {
157
+ c.set({
158
+ left: t.left - e.left,
159
+ top: t.top - e.top,
160
+ angle: t.angle,
161
+ flipX: t.flipX,
162
+ flipY: t.flipY,
163
+ scaleX: t.scaleX,
164
+ scaleY: t.scaleY,
165
+ clipPath: h
166
+ }), o(c);
167
+ });
168
+ },
169
+ { crossOrigin: "anonymous" }
170
+ );
171
+ } else {
172
+ const a = t.getBoundingRect(!0, !0);
173
+ t.cloneAsImage(
174
+ (c) => {
175
+ c.set({
176
+ left: a.left - e.left,
177
+ top: a.top - e.top,
178
+ scaleX: 0.25,
179
+ scaleY: 0.25,
180
+ angle: 0,
181
+ selectable: !1,
182
+ hasControls: !1,
183
+ hasBorders: !1
184
+ }), o(c);
185
+ },
186
+ { multiplier: 4 }
187
+ );
188
+ }
189
+ } catch {
190
+ const c = new m.Text("Load error", {
191
+ left: t.left - e.left,
192
+ top: t.top - e.top,
193
+ fill: "red",
194
+ fontSize: 16
195
+ });
196
+ o(c);
197
+ }
198
+ })
199
+ )
200
+ )).forEach((t) => l.add(t)), new Promise((t) => {
201
+ setTimeout(() => {
202
+ l.renderAll();
203
+ try {
204
+ const o = l.toSVG({
205
+ viewBox: {
206
+ x: 0,
207
+ y: 0,
208
+ width: e.width,
209
+ height: e.height
210
+ },
211
+ width: e.width,
212
+ height: e.height
213
+ }), a = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(o)));
214
+ l.dispose(), t(a);
215
+ } catch {
216
+ t(
217
+ "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iI2Y1ZjVmNSIvPjx0ZXh0IHg9IjUwIiB5PSI1MCIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjE0IiBmaWxsPSIjOTk5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBkeT0iLjNlbSI+6ZSZ6K+vPC90ZXh0Pjwvc3ZnPg=="
218
+ );
219
+ }
220
+ }, 100);
221
+ });
222
+ } catch {
223
+ return null;
224
+ }
225
+ }
226
+ /**
227
+ * 将带裁剪的图片对象栅格化为裁剪后的位图
228
+ * 离屏画布尺寸按裁剪外接框(非整图),避免透明区域“占位置”
229
+ * 返回的数据中的 left/top 为模板局部坐标(相对 boundingBox 左上角)
230
+ */
231
+ async _rasterizeClippedImage(s, e, n = 2) {
232
+ try {
233
+ s.setCoords();
234
+ const l = s.clipPath, i = l ? l.getBoundingRect(!0, !0) : s.getBoundingRect(!0, !0), t = (s.width || 1) * (s.scaleX || 1), o = (s.height || 1) * (s.scaleY || 1), a = (s.width || 1) / Math.max(1, t), c = (s.height || 1) / Math.max(1, o), g = Math.max(1, Math.round(i.width * a * n)), h = Math.max(1, Math.round(i.height * c * n)), u = new m.StaticCanvas(null, {
235
+ width: g,
236
+ height: h,
237
+ enableRetinaScaling: !1,
238
+ backgroundColor: "transparent"
239
+ }), p = await new Promise((r) => s.clone((d) => r(d)));
240
+ let f;
241
+ l && (f = await new Promise((r) => l.clone((d) => r(d)))), p.set({ originX: "left", originY: "top" }), f && f.set({ originX: "left", originY: "top" }), p.set({
242
+ left: (s.left - i.left) * a * n,
243
+ top: (s.top - i.top) * c * n,
244
+ angle: s.angle || 0,
245
+ flipX: s.flipX || !1,
246
+ flipY: s.flipY || !1,
247
+ skewX: s.skewX || 0,
248
+ skewY: s.skewY || 0,
249
+ scaleX: (s.scaleX || 1) * a * n,
250
+ scaleY: (s.scaleY || 1) * c * n,
251
+ selectable: !1,
252
+ evented: !1
253
+ }), f ? (l?.absolutePositioned ? f.set({
254
+ left: ((l?.left || 0) - i.left) * a * n,
255
+ top: ((l?.top || 0) - i.top) * c * n,
256
+ scaleX: (l?.scaleX || 1) * a * n,
257
+ scaleY: (l?.scaleY || 1) * c * n,
258
+ angle: l?.angle || 0,
259
+ selectable: !1,
260
+ evented: !1,
261
+ absolutePositioned: !0
262
+ }) : f.set({ selectable: !1, evented: !1, absolutePositioned: !1 }), p.set({ clipPath: f })) : p.set({ clipPath: void 0 }), u.add(p), u.renderAll();
263
+ const w = u.toDataURL({ format: "png" });
264
+ return u.dispose(), {
265
+ dataUrl: w,
266
+ left: i.left - e.left,
267
+ top: i.top - e.top,
268
+ width: Math.round(i.width),
269
+ height: Math.round(i.height)
270
+ };
271
+ } catch {
272
+ return null;
273
+ }
274
+ }
275
+ /**
276
+ * 将模板添加到画布中
277
+ */
278
+ async addTemplate(s) {
279
+ const e = this.editor.workspaceManager.dimension, n = s.areaWidth, l = s.areaHeight;
280
+ if (!n || !l) return;
281
+ let i = 1;
282
+ n > e.width && (i = e.width / n), l * i > e.height && (i = Math.min(i, e.height / l));
283
+ const t = this.editor.workspaceManager.scale, o = this.editor.workspaceManager.printAreaSize, a = Math.min(o.width, o.height), c = [];
284
+ for (const r of s.elements) {
285
+ if (r.type !== "image" || !r.image?.url) continue;
286
+ const d = a * t, v = Math.max(r.position.width, r.position.height) * i;
287
+ v > 0 && c.push(d / v);
288
+ }
289
+ const g = c.length ? Math.min(...c) : 1, h = i * Math.min(1, g), u = e.left + (e.width - n * h) / 2, p = e.top + (e.height - l * h) / 2, f = this.editor.canvas.getObjects().length;
290
+ let w = await Promise.all(
291
+ s.elements.map(async (r, d) => r.text ? this.editor.textProcessor.addTextFromTemplate(r, {
292
+ left: u,
293
+ top: p,
294
+ s: h,
295
+ zIndex: f + d
296
+ }) : await this.editor.imageProcessor.addImageFromTemplate(r, {
297
+ left: u,
298
+ top: p,
299
+ s: h,
300
+ zIndex: f + d
301
+ }))
302
+ );
303
+ if (w = w.filter((r) => r !== null), w.length) {
304
+ const r = new m.ActiveSelection(w, {
305
+ canvas: this.editor.canvas
306
+ });
307
+ this.editor.canvas.setActiveObject(r);
308
+ }
309
+ }
310
+ }
311
+ export {
312
+ X as TemplateProcessor,
313
+ X as default
314
+ };
@@ -0,0 +1,74 @@
1
+ import fabric from '../fabric';
2
+ import '../../fabric-types';
3
+ import type { TemplateElement } from '../../contracts/api-design';
4
+ import type { Editor } from '../editor';
5
+ /**
6
+ * Text authoring for the portable editor.
7
+ *
8
+ * The serialized output stays identical to the current Printine editor so that
9
+ * existing `design_data` documents keep loading without a migration step.
10
+ */
11
+ export declare class TextProcessor {
12
+ static DEFAULT_TEXT_CONFIG: {
13
+ selectable: boolean;
14
+ hasControls: boolean;
15
+ lineHeight: number;
16
+ strokeWidth: number;
17
+ editingBorderColor: string;
18
+ };
19
+ private editor;
20
+ constructor(editor: Editor);
21
+ /**
22
+ * 按字体配置按需加载 font-face(有缓存时直接跳过)
23
+ */
24
+ ensureFontLoaded(config: Record<string, any>): Promise<void>;
25
+ /**
26
+ * 添加文本
27
+ */
28
+ addText(config: Record<string, any>): Promise<fabric.AdvancedIText | null>;
29
+ /**
30
+ * 更新文本显示属性:大小、颜色、字体
31
+ */
32
+ updateTextDisplay(config: fabric.ITextOptions): Promise<void>;
33
+ /**
34
+ * 更新文本阴影
35
+ */
36
+ updateTextShadow(config: fabric.IShadowOptions | null): Promise<void>;
37
+ /**
38
+ * 更新文本描边
39
+ */
40
+ updateTextStroke(config: {
41
+ stroke: string;
42
+ strokeWidth: number;
43
+ }): Promise<void>;
44
+ /**
45
+ * 更新文本弧度
46
+ */
47
+ updateTextCurved(config: {
48
+ radius?: number;
49
+ spacing?: number;
50
+ orientation?: 'vertical' | 'horizontal';
51
+ }): Promise<void>;
52
+ /**
53
+ * 更新文本排列方向
54
+ */
55
+ updateTextOrientation(orientation: 'vertical' | 'horizontal'): Promise<void>;
56
+ /**
57
+ * 将旧的 fabric.IText 升级为 fabric.AdvancedIText
58
+ * - 保留:文本内容、视觉样式、变换、zIndex、id/name 等自定义字段
59
+ * - 触发:object:removed + object:added(历史记录会自动入栈)
60
+ */
61
+ private __upgradeITextToAdvanced;
62
+ /** 从模板中添加文本 */
63
+ addTextFromTemplate(el: TemplateElement, area: {
64
+ left: number;
65
+ top: number;
66
+ s: number;
67
+ zIndex: number;
68
+ }): Promise<fabric.AdvancedIText>;
69
+ /**
70
+ * 从 state 中提取并预加载所有字体
71
+ */
72
+ preloadFontsFromState(state: Record<string, any>): Promise<void>;
73
+ }
74
+ export default TextProcessor;
@@ -0,0 +1,215 @@
1
+ import "../fabric.js";
2
+ import { fontCache as m, loadFontface as u } from "../utils/font.js";
3
+ import { HistoryManager as _ } from "../history.js";
4
+ import { inchToPixel as A } from "../utils/measure.js";
5
+ import { toVerticalStack as F, toHorizontalRaw as S } from "../utils/common.js";
6
+ import { fabric as g } from "fabric";
7
+ const p = class p {
8
+ constructor(e) {
9
+ this.editor = e;
10
+ }
11
+ /**
12
+ * 按字体配置按需加载 font-face(有缓存时直接跳过)
13
+ */
14
+ async ensureFontLoaded(e) {
15
+ if (!e?.fontPath) return;
16
+ const t = `${e.fontFamily}_${e.fontWeight}_${e.fontStyle}`;
17
+ m.has(t) || await u({
18
+ family: e.fontFamily,
19
+ weight: e.fontWeight || 400,
20
+ style: e.fontStyle || "normal",
21
+ url: e.fontPath
22
+ });
23
+ }
24
+ /**
25
+ * 添加文本
26
+ */
27
+ async addText(e) {
28
+ const { width: t, height: i, center: n, left: o } = this.editor.workspaceManager.dimension, s = this.editor.workspaceManager.scale, r = Math.min(t, i), c = `${e.family}_${e.weight}_${e.style}`, a = 0.25;
29
+ let h;
30
+ try {
31
+ m.has(c) || (h = this.editor.createLoading({
32
+ left: o + 20,
33
+ top: n.y - 40 / 2,
34
+ width: t - 40,
35
+ height: 40,
36
+ scaleX: 1,
37
+ scaleY: 1
38
+ }), h.addLoadingGroup(), await u({
39
+ family: e.family,
40
+ weight: e.weight,
41
+ style: e.style,
42
+ url: e.url
43
+ }), h.removeLoadingGroup());
44
+ const d = new g.AdvancedIText("Entry Text", {
45
+ ...p.DEFAULT_TEXT_CONFIG,
46
+ fontFamily: e.family,
47
+ fontWeight: e.weight,
48
+ fontStyle: e.style,
49
+ fontPath: e.url
50
+ }), l = Math.max(d.width, d.height), x = l < r / s ? s : r / s / l, y = A(a) / d.height, f = l * y > t ? t / l : Math.max(x, y);
51
+ return d.set({
52
+ scaleX: f,
53
+ scaleY: f,
54
+ left: n.x - d.width * f / 2,
55
+ top: n.y - d.height * f / 2
56
+ }), this.editor.addObject(d), d.enterEditing().selectAll(), d;
57
+ } catch {
58
+ return h && h.removeLoadingGroup(), null;
59
+ }
60
+ }
61
+ /**
62
+ * 更新文本显示属性:大小、颜色、字体
63
+ */
64
+ async updateTextDisplay(e) {
65
+ const t = this.editor.activeObject;
66
+ t && (await this.ensureFontLoaded(e), t.set(e), this.editor.canvas.requestRenderAll(), this.editor.canvas.fire("object:modified", {
67
+ target: t
68
+ }));
69
+ }
70
+ /**
71
+ * 更新文本阴影
72
+ */
73
+ async updateTextShadow(e) {
74
+ const t = this.editor.activeObject;
75
+ if (!t) return;
76
+ const i = e ? new g.Shadow(e) : void 0;
77
+ t.set({
78
+ shadow: i
79
+ }), this.editor.canvas.requestRenderAll(), this.editor.canvas.fire("object:modified", { target: t });
80
+ }
81
+ /**
82
+ * 更新文本描边
83
+ */
84
+ async updateTextStroke(e) {
85
+ const t = this.editor.activeObject;
86
+ t && (t.set({
87
+ ...e
88
+ }), this.editor.canvas.requestRenderAll(), this.editor.canvas.fire("object:modified", { target: t }));
89
+ }
90
+ /**
91
+ * 更新文本弧度
92
+ */
93
+ async updateTextCurved(e) {
94
+ const t = this.editor.activeObject;
95
+ if (!t || !t.type || !t.type.includes("i-text")) return;
96
+ t.isEditing && t.exitEditing();
97
+ const i = t.type === "advanced-i-text" ? t : this.__upgradeITextToAdvanced(t);
98
+ i.set(e), i.setCoords(), this.editor.canvas.requestRenderAll(), this.editor.canvas.fire("object:modified", { target: i });
99
+ }
100
+ /**
101
+ * 更新文本排列方向
102
+ */
103
+ async updateTextOrientation(e) {
104
+ const t = this.editor.activeObject;
105
+ if (!t || !t.type || !t.type.includes("i-text")) return;
106
+ t.isEditing && t.exitEditing();
107
+ const i = t.getCenterPoint(), n = {
108
+ orientation: e
109
+ };
110
+ if (e === "vertical")
111
+ t.__rawText = t.text || "", n.text = F(t.__rawText), t.set(n);
112
+ else {
113
+ const o = t.text || "", s = t.__rawText || S(o);
114
+ n.text = s, t.set(n);
115
+ }
116
+ t.setPositionByOrigin(i, "center", "center"), this.editor.canvas.requestRenderAll(), this.editor.canvas.fire("object:modified", { target: t });
117
+ }
118
+ /**
119
+ * 将旧的 fabric.IText 升级为 fabric.AdvancedIText
120
+ * - 保留:文本内容、视觉样式、变换、zIndex、id/name 等自定义字段
121
+ * - 触发:object:removed + object:added(历史记录会自动入栈)
122
+ */
123
+ __upgradeITextToAdvanced(e) {
124
+ const t = this.editor.canvas;
125
+ if (e.group)
126
+ return e;
127
+ const n = t.getObjects().indexOf(e), o = e.id, s = e.name, r = e.toObject(_.DEFAULT_SAVE_PROPERTIES);
128
+ delete r.type, delete r.version;
129
+ const c = new g.AdvancedIText(e.text || "", {
130
+ ...r,
131
+ radius: 0,
132
+ spacing: 0
133
+ });
134
+ return c.id = o, c.name = s, t.remove(e), t.add(c), n >= 0 && t.moveTo(c, n), t.setActiveObject(c), c.setCoords(), t.requestRenderAll(), c;
135
+ }
136
+ /** 从模板中添加文本 */
137
+ async addTextFromTemplate(e, t) {
138
+ const i = e.text, n = `${i.fontFamily}_${i.fontWeight}_${i.fontStyle}`;
139
+ m.has(n) || await u({
140
+ family: i.fontFamily,
141
+ weight: Number.parseInt(i.fontWeight.toString()),
142
+ style: i.fontStyle,
143
+ url: i.fontPath
144
+ });
145
+ const o = t.left + e.position.left * t.s, s = t.top + e.position.top * t.s, r = e.position.width * t.s, c = e.position.height * t.s, a = new g.AdvancedIText(i.text || "", {
146
+ ...p.DEFAULT_TEXT_CONFIG,
147
+ fontFamily: i.fontFamily,
148
+ fontWeight: Number.parseInt(i.fontWeight.toString()),
149
+ fontStyle: i.fontStyle || "normal",
150
+ fontPath: i.fontPath,
151
+ fill: i.color || "#000",
152
+ textAlign: i.textAlign || "left",
153
+ left: 0,
154
+ top: 0,
155
+ angle: e.position.rotation || 0,
156
+ radius: 0,
157
+ spacing: 0
158
+ }), h = typeof i.radius == "number" ? i.radius : 0, d = typeof i.spacing == "number" ? i.spacing : 0;
159
+ (h !== 0 || d !== 0) && a.set({ radius: h, spacing: d }), a.setCoords();
160
+ let l = a.getBoundingRect(!0, !0);
161
+ const x = l.width > 0 ? r / l.width : 1, v = l.height > 0 ? c / l.height : 1, y = Math.min(x, v);
162
+ a.set({ scaleX: y, scaleY: y }), a.setCoords(), l = a.getBoundingRect(!0, !0);
163
+ const f = o - l.left, T = s - l.top;
164
+ return a.set({
165
+ left: (a.left || 0) + f,
166
+ top: (a.top || 0) + T
167
+ }), a.setCoords(), this.editor.addObject(a, !1), this.editor.canvas.moveTo(a, t.zIndex), this.editor.canvas.requestRenderAll(), a;
168
+ }
169
+ /**
170
+ * 从 state 中提取并预加载所有字体
171
+ */
172
+ async preloadFontsFromState(e) {
173
+ if (!e.objects || !Array.isArray(e.objects))
174
+ return;
175
+ const t = [], i = /* @__PURE__ */ new Set();
176
+ e.objects.forEach((n) => {
177
+ if (n.type.includes("text") && n.fontPath) {
178
+ const o = {
179
+ family: n.fontFamily || "Arial",
180
+ weight: n.fontWeight || 400,
181
+ style: n.fontStyle || "normal",
182
+ url: n.fontPath
183
+ }, s = `${o.family}_${o.weight}_${o.style}`;
184
+ !m.has(s) && !i.has(s) && (i.add(s), t.push(
185
+ u(o).catch((r) => (console.warn(`字体加载失败: ${o.family}`, r), Promise.resolve()))
186
+ ));
187
+ }
188
+ n.type === "group" && n.objects && Array.isArray(n.objects) && n.objects.forEach((o) => {
189
+ if ((o.type === "i-text" || o.type === "text") && o.fontPath) {
190
+ const s = {
191
+ family: o.fontFamily || "Arial",
192
+ weight: o.fontWeight || 400,
193
+ style: o.fontStyle || "normal",
194
+ url: o.fontPath
195
+ }, r = `${s.family}_${s.weight}_${s.style}`;
196
+ !m.has(r) && !i.has(r) && (i.add(r), t.push(
197
+ u(s).catch((c) => (console.warn(`字体加载失败: ${s.family}`, c), Promise.resolve()))
198
+ ));
199
+ }
200
+ });
201
+ }), t.length > 0 && await Promise.all(t);
202
+ }
203
+ };
204
+ p.DEFAULT_TEXT_CONFIG = {
205
+ selectable: !0,
206
+ hasControls: !0,
207
+ lineHeight: 1,
208
+ strokeWidth: 0,
209
+ editingBorderColor: "#67c23a"
210
+ };
211
+ let w = p;
212
+ export {
213
+ w as TextProcessor,
214
+ w as default
215
+ };