@hprint/plugins 0.0.7 → 0.0.9-alpha.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 (46) hide show
  1. package/dist/index.js +44 -44
  2. package/dist/index.mjs +6957 -6550
  3. package/dist/src/index.d.ts.map +1 -1
  4. package/dist/src/plugins/ActualContentLayoutPlugin.d.ts +29 -0
  5. package/dist/src/plugins/ActualContentLayoutPlugin.d.ts.map +1 -0
  6. package/dist/src/plugins/CopyPlugin.d.ts.map +1 -1
  7. package/dist/src/plugins/ImageTextListPlugin.d.ts +68 -0
  8. package/dist/src/plugins/ImageTextListPlugin.d.ts.map +1 -0
  9. package/package.json +3 -3
  10. package/src/assets/style/resizePlugin.css +27 -27
  11. package/src/index.ts +11 -5
  12. package/src/objects/Arrow.js +47 -47
  13. package/src/objects/ThinTailArrow.js +50 -50
  14. package/src/plugins/ActualContentLayoutPlugin.ts +276 -0
  15. package/src/plugins/ControlsPlugin.ts +413 -413
  16. package/src/plugins/ControlsRotatePlugin.ts +111 -111
  17. package/src/plugins/CopyPlugin.ts +260 -258
  18. package/src/plugins/DeleteHotKeyPlugin.ts +57 -57
  19. package/src/plugins/DrawLinePlugin.ts +162 -162
  20. package/src/plugins/DrawPolygonPlugin.ts +205 -205
  21. package/src/plugins/DringPlugin.ts +125 -125
  22. package/src/plugins/FlipPlugin.ts +59 -59
  23. package/src/plugins/FontPlugin.ts +165 -165
  24. package/src/plugins/FreeDrawPlugin.ts +49 -49
  25. package/src/plugins/GroupPlugin.ts +82 -82
  26. package/src/plugins/GroupTextEditorPlugin.ts +198 -198
  27. package/src/plugins/HistoryPlugin.ts +181 -181
  28. package/src/plugins/ImageStroke.ts +121 -121
  29. package/src/plugins/ImageTextListPlugin.ts +540 -0
  30. package/src/plugins/LayerPlugin.ts +108 -108
  31. package/src/plugins/MaskPlugin.ts +155 -155
  32. package/src/plugins/MaterialPlugin.ts +224 -224
  33. package/src/plugins/MiddleMousePlugin.ts +45 -45
  34. package/src/plugins/MoveHotKeyPlugin.ts +46 -46
  35. package/src/plugins/PathTextPlugin.ts +89 -89
  36. package/src/plugins/PolygonModifyPlugin.ts +224 -224
  37. package/src/plugins/PrintPlugin.ts +81 -81
  38. package/src/plugins/PsdPlugin.ts +52 -52
  39. package/src/plugins/SimpleClipImagePlugin.ts +244 -244
  40. package/src/types/eventType.ts +11 -11
  41. package/src/utils/psd.js +432 -432
  42. package/src/utils/ruler/guideline.ts +145 -145
  43. package/src/utils/ruler/index.ts +91 -91
  44. package/src/utils/ruler/utils.ts +162 -162
  45. package/tsconfig.json +10 -10
  46. package/vite.config.ts +29 -29
@@ -0,0 +1,276 @@
1
+ import { fabric } from '@hprint/core';
2
+ import type { IEditor, IPluginTempl } from '@hprint/core';
3
+
4
+ export interface ActualContentLayoutSettings {
5
+ actualContentLayout?: boolean;
6
+ overflowMode?: 'clip' | 'expand';
7
+ }
8
+
9
+ type IPlugin = Pick<ActualContentLayoutPlugin, 'applyActualContentLayout'>;
10
+
11
+ declare module '@hprint/core' {
12
+ interface IEditor extends IPlugin {}
13
+ }
14
+
15
+ type LayoutEntry = {
16
+ object: fabric.Object;
17
+ index: number;
18
+ originalTop: number;
19
+ originalBottom: number;
20
+ };
21
+
22
+ class ActualContentLayoutPlugin implements IPluginTempl {
23
+ static pluginName = 'ActualContentLayoutPlugin';
24
+ static apis = ['applyActualContentLayout'];
25
+
26
+ constructor(
27
+ public canvas: fabric.Canvas,
28
+ public editor: IEditor
29
+ ) {}
30
+
31
+ private layoutOrigins = new WeakMap<
32
+ fabric.Object,
33
+ { top: number; height: number }
34
+ >();
35
+
36
+ async hookTransformObjectEnd(...args: unknown[]) {
37
+ const { originObject, fabricObject } = args[0] as {
38
+ originObject: any;
39
+ fabricObject: fabric.Object;
40
+ };
41
+ if (!this.isPrintableObject(originObject)) return;
42
+
43
+ const mmPerPx = Number(this.editor.getSizeByUnit?.(1, 'mm')) || 1;
44
+ this.layoutOrigins.set(fabricObject, {
45
+ top: this.getOriginalTop(originObject, fabricObject, mmPerPx),
46
+ height: this.getOriginalHeight(
47
+ originObject,
48
+ fabricObject,
49
+ mmPerPx
50
+ ),
51
+ });
52
+ }
53
+
54
+ applyActualContentLayout(templateContent: any, templateHeight: number) {
55
+ const settings = (templateContent?.templateSettings ||
56
+ {}) as ActualContentLayoutSettings;
57
+ if (!settings.actualContentLayout) return templateHeight;
58
+
59
+ const fabricObjects = this.canvas
60
+ .getObjects()
61
+ .filter(this.isPrintableObject);
62
+ const mmPerPx = Number(this.editor.getSizeByUnit?.(1, 'mm')) || 1;
63
+
64
+ fabricObjects.forEach((object) => this.preparePrintLayoutObject(object));
65
+
66
+ const entries = fabricObjects.map((object, index) => {
67
+ const origin = this.layoutOrigins.get(object);
68
+ const originalTop =
69
+ origin?.top ?? Number(object.top || 0) * mmPerPx;
70
+ const originalHeight =
71
+ origin?.height ??
72
+ Number(object.getScaledHeight?.() || object.height || 0) *
73
+ mmPerPx;
74
+ return {
75
+ object,
76
+ index,
77
+ originalTop,
78
+ originalBottom: originalTop + originalHeight,
79
+ };
80
+ }) as LayoutEntry[];
81
+
82
+ entries.sort(
83
+ (a, b) => a.originalTop - b.originalTop || a.index - b.index
84
+ );
85
+ const rows: LayoutEntry[][] = [];
86
+ const sameRowTolerance = 0.01;
87
+ entries.forEach((entry) => {
88
+ const row = rows[rows.length - 1];
89
+ if (
90
+ row &&
91
+ Math.abs(row[0].originalTop - entry.originalTop) <=
92
+ sameRowTolerance
93
+ ) {
94
+ row.push(entry);
95
+ } else {
96
+ rows.push([entry]);
97
+ }
98
+ });
99
+
100
+ let previousOriginalBottom: number | undefined;
101
+ let previousActualBottom: number | undefined;
102
+ let pendingGap = 0;
103
+ let lastVisibleOriginalBottom = 0;
104
+
105
+ rows.forEach((row) => {
106
+ const rowOriginalTop = row[0].originalTop;
107
+ const rowOriginalBottom = Math.max(
108
+ ...row.map((entry) => entry.originalBottom)
109
+ );
110
+ if (previousOriginalBottom !== undefined) {
111
+ pendingGap += Math.max(
112
+ 0,
113
+ rowOriginalTop - previousOriginalBottom
114
+ );
115
+ }
116
+ previousOriginalBottom = rowOriginalBottom;
117
+
118
+ const visibleEntries = row.filter(
119
+ (entry) => !this.isEmptyLayoutObject(entry.object)
120
+ );
121
+ if (!visibleEntries.length) return;
122
+
123
+ const targetTop =
124
+ previousActualBottom === undefined
125
+ ? rowOriginalTop
126
+ : previousActualBottom + pendingGap;
127
+ visibleEntries.forEach((entry) => {
128
+ entry.object.set('top', targetTop / mmPerPx);
129
+ entry.object.setCoords();
130
+ });
131
+ previousActualBottom = Math.max(
132
+ ...visibleEntries.map(
133
+ (entry) =>
134
+ (Number(entry.object.top || 0) +
135
+ this.getActualLayoutHeight(entry.object)) *
136
+ mmPerPx
137
+ )
138
+ );
139
+ lastVisibleOriginalBottom = rowOriginalBottom;
140
+ pendingGap = 0;
141
+ });
142
+
143
+ this.canvas.requestRenderAll();
144
+ if (
145
+ settings.overflowMode !== 'expand' ||
146
+ previousActualBottom === undefined
147
+ ) {
148
+ return templateHeight;
149
+ }
150
+ const bottomSpace = Math.max(
151
+ 0,
152
+ templateHeight - lastVisibleOriginalBottom
153
+ );
154
+ return Math.max(
155
+ templateHeight,
156
+ previousActualBottom + bottomSpace
157
+ );
158
+ }
159
+
160
+ private getOriginalTop(
161
+ source: any,
162
+ object: fabric.Object,
163
+ mmPerPx: number
164
+ ) {
165
+ const top = Number(source?.top);
166
+ if (Number.isFinite(top)) return top * mmPerPx;
167
+ return this.getOriginMmValue(
168
+ source,
169
+ 'top',
170
+ Number(object.top || 0) * mmPerPx
171
+ );
172
+ }
173
+
174
+ private getOriginalHeight(
175
+ source: any,
176
+ object: fabric.Object,
177
+ mmPerPx: number
178
+ ) {
179
+ const height = Number(source?.height);
180
+ const scaleY = Number(source?.scaleY ?? 1);
181
+ if (Number.isFinite(height) && Number.isFinite(scaleY)) {
182
+ return Math.abs(height * scaleY) * mmPerPx;
183
+ }
184
+ return this.getOriginMmValue(
185
+ source,
186
+ 'height',
187
+ Number(object.getScaledHeight?.() || object.height || 0) *
188
+ mmPerPx
189
+ );
190
+ }
191
+
192
+ private getOriginMmValue(
193
+ source: any,
194
+ field: 'top' | 'height',
195
+ fallback: number
196
+ ) {
197
+ const originalValue = source?._originSize?.mm?.[field];
198
+ if (
199
+ originalValue === undefined ||
200
+ originalValue === null ||
201
+ originalValue === ''
202
+ ) {
203
+ return fallback;
204
+ }
205
+ const value = Number(originalValue);
206
+ return Number.isFinite(value) ? value : fallback;
207
+ }
208
+
209
+ private preparePrintLayoutObject(object: any) {
210
+ if (object?.extensionType !== 'imageTextList') return;
211
+ if (object.extension?._clipContent === true) return;
212
+
213
+ // Design canvases can opt into clipping, but print/layout canvases should
214
+ // measure and render the natural image-text content height.
215
+ if (object.clipPath) object.set('clipPath', undefined);
216
+ object.set({
217
+ objectCaching: false,
218
+ dirty: true,
219
+ });
220
+ }
221
+
222
+ private isPrintableObject(object: any) {
223
+ return (
224
+ object?.id !== 'workspace' &&
225
+ object?.id !== 'coverMask' &&
226
+ object?.type !== 'GuideLine'
227
+ );
228
+ }
229
+
230
+ private isEmptyLayoutObject(object: any) {
231
+ if (object?.visible === false) return true;
232
+ const field = object?.extension?._field_;
233
+ if (!field) return false;
234
+ if (object.type === 'textbox') {
235
+ return String(object.text ?? '') === '';
236
+ }
237
+ if (['barcode', 'qrcode'].includes(object.extensionType)) {
238
+ return String(object.extension?.value ?? '') === '';
239
+ }
240
+ if (object.extensionType === 'imageTextList') {
241
+ return (
242
+ !Array.isArray(object.extension?.items) ||
243
+ object.extension.items.length === 0
244
+ );
245
+ }
246
+ return false;
247
+ }
248
+
249
+ private getActualLayoutHeight(object: any) {
250
+ if (object?.extensionType !== 'imageTextList') {
251
+ return Number(
252
+ object.getScaledHeight?.() || object.height || 0
253
+ );
254
+ }
255
+
256
+ const children = object.getObjects?.() || object._objects || [];
257
+ const contentChildren = children.slice(1);
258
+ if (!contentChildren.length) return 0;
259
+
260
+ const groupHeight = Number(object.height || 0);
261
+ const groupScaleY = Math.abs(Number(object.scaleY ?? 1));
262
+ const contentBottom = Math.max(
263
+ ...contentChildren.map(
264
+ (child: any) =>
265
+ Number(child.top || 0) +
266
+ groupHeight / 2 +
267
+ Number(
268
+ child.getScaledHeight?.() || child.height || 0
269
+ )
270
+ )
271
+ );
272
+ return Math.max(0, contentBottom) * groupScaleY;
273
+ }
274
+ }
275
+
276
+ export default ActualContentLayoutPlugin;