fabricjs-document-engine 1.2.0 → 1.3.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 (81) hide show
  1. package/README.md +31 -0
  2. package/README.zh-CN.md +32 -0
  3. package/dist/commands/text-edit.cjs +223 -0
  4. package/dist/commands/text-edit.d.cts +23 -0
  5. package/dist/commands/text-edit.d.ts +23 -0
  6. package/dist/commands/text-edit.js +218 -0
  7. package/dist/fabric/object-registry.cjs +7 -1
  8. package/dist/fabric/object-registry.js +7 -1
  9. package/dist/filters/filter-worker.cjs +281 -0
  10. package/dist/filters/filter-worker.d.cts +26 -0
  11. package/dist/filters/filter-worker.d.ts +26 -0
  12. package/dist/filters/filter-worker.js +281 -0
  13. package/dist/filters/pipeline.cjs +91 -0
  14. package/dist/filters/pipeline.d.cts +35 -0
  15. package/dist/filters/pipeline.d.ts +35 -0
  16. package/dist/filters/pipeline.js +87 -0
  17. package/dist/filters/worker-entry.cjs +78 -0
  18. package/dist/filters/worker-entry.d.cts +29 -0
  19. package/dist/filters/worker-entry.d.ts +29 -0
  20. package/dist/filters/worker-entry.js +78 -0
  21. package/dist/filters.cjs +9 -0
  22. package/dist/filters.d.cts +3 -0
  23. package/dist/filters.d.ts +3 -0
  24. package/dist/filters.js +3 -0
  25. package/dist/import/svg-import.cjs +102 -0
  26. package/dist/import/svg-import.d.cts +1 -0
  27. package/dist/import/svg-import.d.ts +1 -0
  28. package/dist/import/svg-import.js +103 -1
  29. package/dist/index.cjs +7 -0
  30. package/dist/index.d.cts +2 -1
  31. package/dist/index.d.ts +2 -1
  32. package/dist/index.js +2 -1
  33. package/dist/performance/batch.cjs +31 -0
  34. package/dist/performance/batch.d.cts +4 -0
  35. package/dist/performance/batch.d.ts +4 -0
  36. package/dist/performance/batch.js +31 -0
  37. package/dist/performance/dirty-regions.cjs +378 -0
  38. package/dist/performance/dirty-regions.d.cts +23 -0
  39. package/dist/performance/dirty-regions.d.ts +23 -0
  40. package/dist/performance/dirty-regions.js +378 -0
  41. package/dist/performance/monitor.cjs +60 -0
  42. package/dist/performance/monitor.d.cts +20 -0
  43. package/dist/performance/monitor.d.ts +20 -0
  44. package/dist/performance/monitor.js +60 -0
  45. package/dist/performance/spatial-index.cjs +78 -0
  46. package/dist/performance/spatial-index.d.cts +21 -0
  47. package/dist/performance/spatial-index.d.ts +21 -0
  48. package/dist/performance/spatial-index.js +78 -0
  49. package/dist/performance.cjs +9 -0
  50. package/dist/performance.d.cts +5 -0
  51. package/dist/performance.d.ts +5 -0
  52. package/dist/performance.js +5 -0
  53. package/dist/text/bounded-textbox.cjs +306 -0
  54. package/dist/text/bounded-textbox.d.cts +53 -0
  55. package/dist/text/bounded-textbox.d.ts +53 -0
  56. package/dist/text/bounded-textbox.js +306 -0
  57. package/dist/text/mobile-input.cjs +146 -0
  58. package/dist/text/mobile-input.d.cts +19 -0
  59. package/dist/text/mobile-input.d.ts +19 -0
  60. package/dist/text/mobile-input.js +145 -0
  61. package/dist/text/register.cjs +17 -0
  62. package/dist/text/register.d.cts +5 -0
  63. package/dist/text/register.d.ts +5 -0
  64. package/dist/text/register.js +16 -0
  65. package/dist/text/shaping.cjs +125 -0
  66. package/dist/text/shaping.d.cts +28 -0
  67. package/dist/text/shaping.d.ts +28 -0
  68. package/dist/text/shaping.js +121 -0
  69. package/dist/text/vertical-orientation.cjs +192 -0
  70. package/dist/text/vertical-orientation.d.cts +10 -0
  71. package/dist/text/vertical-orientation.d.ts +10 -0
  72. package/dist/text/vertical-orientation.js +190 -0
  73. package/dist/text/vertical-text.cjs +336 -0
  74. package/dist/text/vertical-text.d.cts +54 -0
  75. package/dist/text/vertical-text.d.ts +54 -0
  76. package/dist/text/vertical-text.js +336 -0
  77. package/dist/text.cjs +21 -0
  78. package/dist/text.d.cts +7 -0
  79. package/dist/text.d.ts +7 -0
  80. package/dist/text.js +7 -0
  81. package/package.json +31 -1
@@ -0,0 +1,78 @@
1
+ //#region src/performance/spatial-index.ts
2
+ const OFFSET = 1 << 20;
3
+ const keyOf = (column, row) => (column + OFFSET) * 2 ** 21 + (row + OFFSET);
4
+ function createSpatialIndex(options = {}) {
5
+ const cellSize = options.cellSize ?? 256;
6
+ const maxCells = options.maxCellsPerItem ?? 64;
7
+ const cells = /* @__PURE__ */ new Map();
8
+ const placed = /* @__PURE__ */ new Map();
9
+ const large = /* @__PURE__ */ new Set();
10
+ const span = (bounds) => [
11
+ Math.floor(bounds.left / cellSize),
12
+ Math.floor(bounds.top / cellSize),
13
+ Math.floor((bounds.left + Math.max(0, bounds.width)) / cellSize),
14
+ Math.floor((bounds.top + Math.max(0, bounds.height)) / cellSize)
15
+ ];
16
+ function remove(item) {
17
+ const where = placed.get(item);
18
+ if (where === void 0) return false;
19
+ placed.delete(item);
20
+ if (where === "large") {
21
+ large.delete(item);
22
+ return true;
23
+ }
24
+ for (const key of where) {
25
+ const bucket = cells.get(key);
26
+ bucket.delete(item);
27
+ if (bucket.size === 0) cells.delete(key);
28
+ }
29
+ return true;
30
+ }
31
+ return {
32
+ set(item, bounds) {
33
+ remove(item);
34
+ const [left, top, right, bottom] = span(bounds);
35
+ const count = (right - left + 1) * (bottom - top + 1);
36
+ if (!Number.isFinite(count) || count > maxCells) {
37
+ large.add(item);
38
+ placed.set(item, "large");
39
+ return;
40
+ }
41
+ const keys = [];
42
+ for (let column = left; column <= right; column += 1) for (let row = top; row <= bottom; row += 1) {
43
+ const key = keyOf(column, row);
44
+ let bucket = cells.get(key);
45
+ if (!bucket) cells.set(key, bucket = /* @__PURE__ */ new Set());
46
+ bucket.add(item);
47
+ keys.push(key);
48
+ }
49
+ placed.set(item, keys);
50
+ },
51
+ delete: remove,
52
+ has: (item) => placed.has(item),
53
+ query(bounds) {
54
+ const found = new Set(large);
55
+ const [left, top, right, bottom] = span(bounds);
56
+ if ((right - left + 1) * (bottom - top + 1) > cells.size) {
57
+ for (const [key, bucket] of cells) {
58
+ const column = Math.floor(key / 2 ** 21) - OFFSET;
59
+ const row = key % 2 ** 21 - OFFSET;
60
+ if (column >= left && column <= right && row >= top && row <= bottom) bucket.forEach((item) => found.add(item));
61
+ }
62
+ return found;
63
+ }
64
+ for (let column = left; column <= right; column += 1) for (let row = top; row <= bottom; row += 1) cells.get(keyOf(column, row))?.forEach((item) => found.add(item));
65
+ return found;
66
+ },
67
+ clear() {
68
+ cells.clear();
69
+ placed.clear();
70
+ large.clear();
71
+ },
72
+ get size() {
73
+ return placed.size;
74
+ }
75
+ };
76
+ }
77
+ //#endregion
78
+ export { createSpatialIndex };
@@ -0,0 +1,9 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_batch = require("./performance/batch.cjs");
3
+ const require_spatial_index = require("./performance/spatial-index.cjs");
4
+ const require_dirty_regions = require("./performance/dirty-regions.cjs");
5
+ const require_monitor = require("./performance/monitor.cjs");
6
+ exports.batchCanvasUpdates = require_batch.batchCanvasUpdates;
7
+ exports.createPerformanceMonitor = require_monitor.createPerformanceMonitor;
8
+ exports.createSpatialIndex = require_spatial_index.createSpatialIndex;
9
+ exports.enableDirtyRegionRendering = require_dirty_regions.enableDirtyRegionRendering;
@@ -0,0 +1,5 @@
1
+ import { batchCanvasUpdates } from "./performance/batch.cjs";
2
+ import { Rect, SpatialIndex, SpatialIndexOptions, createSpatialIndex } from "./performance/spatial-index.cjs";
3
+ import { DirtyRegionOptions, DirtyRegionRenderer, DirtyRegionStats, enableDirtyRegionRendering } from "./performance/dirty-regions.cjs";
4
+ import { FrameStats, PerformanceMonitor, PerformanceMonitorOptions, createPerformanceMonitor } from "./performance/monitor.cjs";
5
+ export { type DirtyRegionOptions, type DirtyRegionRenderer, type DirtyRegionStats, type FrameStats, type PerformanceMonitor, type PerformanceMonitorOptions, type Rect, type SpatialIndex, type SpatialIndexOptions, batchCanvasUpdates, createPerformanceMonitor, createSpatialIndex, enableDirtyRegionRendering };
@@ -0,0 +1,5 @@
1
+ import { batchCanvasUpdates } from "./performance/batch.js";
2
+ import { Rect, SpatialIndex, SpatialIndexOptions, createSpatialIndex } from "./performance/spatial-index.js";
3
+ import { DirtyRegionOptions, DirtyRegionRenderer, DirtyRegionStats, enableDirtyRegionRendering } from "./performance/dirty-regions.js";
4
+ import { FrameStats, PerformanceMonitor, PerformanceMonitorOptions, createPerformanceMonitor } from "./performance/monitor.js";
5
+ export { type DirtyRegionOptions, type DirtyRegionRenderer, type DirtyRegionStats, type FrameStats, type PerformanceMonitor, type PerformanceMonitorOptions, type Rect, type SpatialIndex, type SpatialIndexOptions, batchCanvasUpdates, createPerformanceMonitor, createSpatialIndex, enableDirtyRegionRendering };
@@ -0,0 +1,5 @@
1
+ import { batchCanvasUpdates } from "./performance/batch.js";
2
+ import { createSpatialIndex } from "./performance/spatial-index.js";
3
+ import { enableDirtyRegionRendering } from "./performance/dirty-regions.js";
4
+ import { createPerformanceMonitor } from "./performance/monitor.js";
5
+ export { batchCanvasUpdates, createPerformanceMonitor, createSpatialIndex, enableDirtyRegionRendering };
@@ -0,0 +1,306 @@
1
+ const require_shaping = require("./shaping.cjs");
2
+ let fabric = require("fabric");
3
+ //#region src/text/bounded-textbox.ts
4
+ const BOUNDED_PROPERTIES = [
5
+ "breakWords",
6
+ "maxHeight",
7
+ "overflow",
8
+ "fit",
9
+ "minFontSize",
10
+ "shaping"
11
+ ];
12
+ const ELLIPSIS = "…";
13
+ let clipCounter = 0;
14
+ function internals(text) {
15
+ return text;
16
+ }
17
+ var BoundedTextbox = class BoundedTextbox extends fabric.Textbox {
18
+ static getDefaults() {
19
+ return {
20
+ ...super.getDefaults(),
21
+ ...BoundedTextbox.boundedDefaults
22
+ };
23
+ }
24
+ constructor(text, options = {}) {
25
+ super(text, {
26
+ ...BoundedTextbox.boundedDefaults,
27
+ ...options
28
+ });
29
+ }
30
+ getBaseFontSize() {
31
+ return this.baseFontSize;
32
+ }
33
+ initDimensions() {
34
+ if (!internals(this).initialized) return;
35
+ this.midWordBreaks ?? (this.midWordBreaks = /* @__PURE__ */ new Set());
36
+ this.overflowCache = void 0;
37
+ this.fitScale = 1;
38
+ super.initDimensions();
39
+ if (this.fit === "shrink" && this.maxHeight !== void 0 && this.calcTextHeight() > this.maxHeight) this.shrinkToFit();
40
+ if (this.maxHeight !== void 0 && this.overflow !== "visible") this.height = Math.min(this.height, this.maxHeight);
41
+ }
42
+ shrinkToFit() {
43
+ const base = this.baseFontSize;
44
+ const maxHeight = this.maxHeight;
45
+ let low = Math.max(1, Math.ceil(Math.min(this.minFontSize, base) * 2));
46
+ let high = Math.floor(base * 2) - 1;
47
+ let best = low;
48
+ const layOut = (halfPoints) => {
49
+ this.fitScale = halfPoints / 2 / base;
50
+ fabric.Textbox.prototype.initDimensions.call(this);
51
+ return this.calcTextHeight();
52
+ };
53
+ while (low <= high) {
54
+ const middle = low + high >> 1;
55
+ if (layOut(middle) <= maxHeight) {
56
+ best = middle;
57
+ low = middle + 1;
58
+ } else high = middle - 1;
59
+ }
60
+ if (this.fitScale !== best / 2 / base) layOut(best);
61
+ }
62
+ getValueOfPropertyAt(lineIndex, charIndex, property) {
63
+ const value = super.getValueOfPropertyAt(lineIndex, charIndex, property);
64
+ if (property === "fontSize" && this.fitScale !== 1 && this.hasOwnFontSize(lineIndex, charIndex)) return value * this.fitScale;
65
+ return value;
66
+ }
67
+ getCompleteStyleDeclaration(lineIndex, charIndex) {
68
+ const style = super.getCompleteStyleDeclaration(lineIndex, charIndex);
69
+ if (this.fitScale !== 1 && this.hasOwnFontSize(lineIndex, charIndex)) style.fontSize *= this.fitScale;
70
+ return style;
71
+ }
72
+ hasOwnFontSize(lineIndex, charIndex) {
73
+ return internals(this)._getStyleDeclaration(lineIndex, charIndex)?.fontSize !== void 0;
74
+ }
75
+ _wrapText(lines, desiredWidth) {
76
+ const self = internals(this);
77
+ this.midWordBreaks = /* @__PURE__ */ new Set();
78
+ self.isWrapping = true;
79
+ const data = self.getGraphemeDataForRender(lines);
80
+ const wrapped = [];
81
+ for (let index = 0; index < data.wordsData.length; index += 1) {
82
+ this.lineBase = wrapped.length;
83
+ wrapped.push(...this._wrapLine(index, desiredWidth, data));
84
+ }
85
+ self.isWrapping = false;
86
+ return wrapped;
87
+ }
88
+ _wrapLine(lineIndex, desiredWidth, data, reservedSpace = 0) {
89
+ if (this.breakWords === "never" || this.splitByGrapheme) return super._wrapLine(lineIndex, desiredWidth, data, reservedSpace);
90
+ const self = internals(this);
91
+ const { wordsData } = data;
92
+ const additionalSpace = self._getWidthOfCharSpacing();
93
+ const available = desiredWidth - reservedSpace;
94
+ const lines = [];
95
+ let line = [];
96
+ let lineWidth = 0;
97
+ let offset = 0;
98
+ let infixWidth = 0;
99
+ let lineJustStarted = true;
100
+ let widestGrapheme = 0;
101
+ const words = wordsData[lineIndex] ?? [];
102
+ for (const { word, width: wordWidth } of words) {
103
+ const wordStart = offset;
104
+ offset += word.length;
105
+ if (wordWidth - additionalSpace > available && word.length > 1) {
106
+ if (!lineJustStarted) lines.push(line);
107
+ line = [];
108
+ lineWidth = 0;
109
+ let previous;
110
+ for (let index = 0; index < word.length; index += 1) {
111
+ const grapheme = word[index];
112
+ const graphemeWidth = self._getGraphemeBox(grapheme, lineIndex, wordStart + index, previous, true).kernedWidth;
113
+ widestGrapheme = Math.max(widestGrapheme, graphemeWidth - additionalSpace);
114
+ if (line.length > 0 && lineWidth + graphemeWidth - additionalSpace > available) {
115
+ this.midWordBreaks.add(this.lineBase + lines.length);
116
+ lines.push(line);
117
+ line = [];
118
+ lineWidth = 0;
119
+ }
120
+ line.push(grapheme);
121
+ lineWidth += graphemeWidth;
122
+ previous = grapheme;
123
+ }
124
+ } else {
125
+ if (word.length === 1) widestGrapheme = Math.max(widestGrapheme, wordWidth - additionalSpace);
126
+ lineWidth += infixWidth + wordWidth - additionalSpace;
127
+ if (lineWidth > available && !lineJustStarted) {
128
+ lines.push(line);
129
+ line = [];
130
+ lineWidth = wordWidth;
131
+ lineJustStarted = true;
132
+ } else lineWidth += additionalSpace;
133
+ if (!lineJustStarted) line.push(" ");
134
+ line = line.concat(word);
135
+ }
136
+ infixWidth = self._measureWord([" "], lineIndex, offset);
137
+ offset += 1;
138
+ lineJustStarted = false;
139
+ }
140
+ if (words.length > 0) lines.push(line);
141
+ self.dynamicMinWidth = Math.max(self.dynamicMinWidth, widestGrapheme);
142
+ return lines;
143
+ }
144
+ _measureLine(lineIndex) {
145
+ const measured = super._measureLine(lineIndex);
146
+ return this.shaping ? require_shaping.shapeLine(this, lineIndex, measured) : measured;
147
+ }
148
+ _measureWord(word, lineIndex, charOffset = 0) {
149
+ const width = super._measureWord(word, lineIndex, charOffset);
150
+ return this.shaping ? require_shaping.shapeWord(this, word, lineIndex, charOffset, width) : width;
151
+ }
152
+ missingNewlineOffset(lineIndex, skipWrapping) {
153
+ if (!skipWrapping && this.midWordBreaks?.has(lineIndex)) return 0;
154
+ return super.missingNewlineOffset(lineIndex, skipWrapping);
155
+ }
156
+ overflowLayout() {
157
+ if (this.overflowCache !== void 0) return this.overflowCache;
158
+ const maxHeight = this.maxHeight;
159
+ if (maxHeight === void 0 || this.overflow === "visible") return this.overflowCache = null;
160
+ const lineCount = internals(this)._textLines.length;
161
+ let visibleLines = 0;
162
+ let top = 0;
163
+ for (let index = 0; index < lineCount; index += 1) {
164
+ const lineHeight = this.getHeightOfLine(index);
165
+ if (top + lineHeight / this.lineHeight > maxHeight + .5) break;
166
+ visibleLines = index + 1;
167
+ top += lineHeight;
168
+ }
169
+ const clipped = visibleLines < lineCount;
170
+ const layout = {
171
+ visibleLines,
172
+ clipped,
173
+ visibleHeight: top
174
+ };
175
+ if (clipped && visibleLines > 0 && this.overflow === "ellipsis" && !this.isEditing && this.direction !== "rtl") layout.ellipsis = this.placeEllipsis(visibleLines - 1, top - this.getHeightOfLine(visibleLines - 1));
176
+ return this.overflowCache = layout;
177
+ }
178
+ placeEllipsis(line, lineTop) {
179
+ const self = internals(this);
180
+ this.getLineWidth(line);
181
+ const bounds = self.__charBounds[line] ?? [];
182
+ const graphemes = self._textLines[line] ?? [];
183
+ const lineLeft = self._getLineLeftOffset(line);
184
+ const styleAt = (index) => this.getCompleteStyleDeclaration(line, Math.max(0, Math.min(index, graphemes.length - 1)));
185
+ const ellipsisWidth = self._measureChar(ELLIPSIS, styleAt(graphemes.length - 1)).width;
186
+ let low = 0;
187
+ let high = graphemes.length;
188
+ while (low < high) {
189
+ const middle = low + high + 1 >> 1;
190
+ if (lineLeft + (bounds[middle]?.left ?? 0) + ellipsisWidth <= this.width) low = middle;
191
+ else high = middle - 1;
192
+ }
193
+ let cut = low;
194
+ while (cut > 0 && /\s/.test(graphemes[cut - 1])) cut -= 1;
195
+ const lineHeight = this.getHeightOfLine(line) / this.lineHeight;
196
+ return {
197
+ line,
198
+ cut,
199
+ x: self._getLeftOffset() + lineLeft + (bounds[cut]?.left ?? 0),
200
+ baseline: self._getTopOffset() + lineTop + lineHeight * (1 - self._fontSizeFraction),
201
+ style: styleAt(cut - 1)
202
+ };
203
+ }
204
+ clipRects(layout) {
205
+ const self = internals(this);
206
+ const left = self._getLeftOffset();
207
+ const top = self._getTopOffset();
208
+ const wide = this.width + this.fontSize * 2;
209
+ const x = left - this.fontSize;
210
+ if (!layout.ellipsis) {
211
+ const bottom = this.overflow === "clip" || this.isEditing ? this.maxHeight : layout.visibleHeight;
212
+ return [[
213
+ x,
214
+ top - this.fontSize,
215
+ wide,
216
+ bottom + this.fontSize
217
+ ]];
218
+ }
219
+ const { line, x: cutX } = layout.ellipsis;
220
+ const lineTop = layout.visibleHeight - this.getHeightOfLine(line);
221
+ const rects = [];
222
+ if (lineTop > 0) rects.push([
223
+ x,
224
+ top - this.fontSize,
225
+ wide,
226
+ lineTop + this.fontSize
227
+ ]);
228
+ rects.push([
229
+ x,
230
+ top + lineTop,
231
+ cutX - x,
232
+ this.getHeightOfLine(line)
233
+ ]);
234
+ return rects;
235
+ }
236
+ _render(ctx) {
237
+ const layout = this.overflowLayout();
238
+ if (!layout || !layout.clipped && this.overflow !== "clip") {
239
+ super._render(ctx);
240
+ return;
241
+ }
242
+ ctx.save();
243
+ ctx.beginPath();
244
+ for (const [x, y, width, height] of this.clipRects(layout)) ctx.rect(x, y, width, height);
245
+ ctx.clip();
246
+ super._render(ctx);
247
+ ctx.restore();
248
+ const ellipsis = layout.ellipsis;
249
+ if (!ellipsis) return;
250
+ ctx.save();
251
+ ctx.font = internals(this)._getFontDeclaration(ellipsis.style);
252
+ ctx.textBaseline = "alphabetic";
253
+ ctx.textAlign = "left";
254
+ ctx.fillStyle = typeof ellipsis.style.fill === "string" ? ellipsis.style.fill : typeof this.fill === "string" ? this.fill : "#000";
255
+ ctx.fillText(ELLIPSIS, ellipsis.x, ellipsis.baseline - (ellipsis.style.deltaY ?? 0));
256
+ ctx.restore();
257
+ }
258
+ _toSVG() {
259
+ const markup = super._toSVG();
260
+ const layout = this.overflowLayout();
261
+ if (!layout || !layout.clipped && this.overflow !== "clip") return markup;
262
+ clipCounter += 1;
263
+ const id = `fde-bounded-text-${clipCounter}`;
264
+ const result = [
265
+ `<clipPath id="${id}">${this.clipRects(layout).map(([x, y, width, height]) => `<rect x="${x}" y="${y}" width="${width}" height="${height}" />`).join("")}</clipPath>\n<g clip-path="url(#${id})">\n`,
266
+ ...markup,
267
+ "</g>\n"
268
+ ];
269
+ const ellipsis = layout.ellipsis;
270
+ if (ellipsis) {
271
+ const style = ellipsis.style;
272
+ const fill = typeof style.fill === "string" ? style.fill : typeof this.fill === "string" ? this.fill : "#000";
273
+ const family = String(style.fontFamily ?? this.fontFamily).replace(/"/g, "'");
274
+ result.push(`<text xml:space="preserve" x="${ellipsis.x}" y="${ellipsis.baseline - (style.deltaY ?? 0)}" font-family="${family}" font-size="${style.fontSize}" font-style="${style.fontStyle ?? "normal"}" font-weight="${style.fontWeight ?? "normal"}" fill="${fill}">${ELLIPSIS}</text>\n`);
275
+ }
276
+ return result;
277
+ }
278
+ toObject(propertiesToInclude = []) {
279
+ const object = super.toObject([...BOUNDED_PROPERTIES, ...propertiesToInclude]);
280
+ if ("fontSize" in object) object.fontSize = this.baseFontSize;
281
+ return object;
282
+ }
283
+ };
284
+ BoundedTextbox.type = "BoundedTextbox";
285
+ BoundedTextbox.textLayoutProperties = [...fabric.Textbox.textLayoutProperties, ...BOUNDED_PROPERTIES];
286
+ BoundedTextbox.cacheProperties = [...fabric.Textbox.cacheProperties, ...BOUNDED_PROPERTIES];
287
+ BoundedTextbox.boundedDefaults = {
288
+ breakWords: "anywhere",
289
+ maxHeight: void 0,
290
+ overflow: "visible",
291
+ fit: "none",
292
+ minFontSize: 6,
293
+ shaping: false
294
+ };
295
+ Object.defineProperty(BoundedTextbox.prototype, "fontSize", {
296
+ configurable: true,
297
+ enumerable: true,
298
+ get() {
299
+ return this.baseFontSize * (this.fitScale ?? 1);
300
+ },
301
+ set(value) {
302
+ this.baseFontSize = value;
303
+ }
304
+ });
305
+ //#endregion
306
+ exports.BoundedTextbox = BoundedTextbox;
@@ -0,0 +1,53 @@
1
+ import { TextStyleDeclaration, Textbox } from "fabric";
2
+ //#region src/text/bounded-textbox.d.ts
3
+ export type BreakWords = "anywhere" | "never";
4
+ export type TextOverflow = "visible" | "clip" | "ellipsis";
5
+ export type TextFit = "none" | "shrink";
6
+ export interface BoundedTextboxProps {
7
+ breakWords: BreakWords;
8
+ maxHeight: number | undefined;
9
+ overflow: TextOverflow;
10
+ fit: TextFit;
11
+ minFontSize: number;
12
+ shaping: boolean;
13
+ }
14
+ export declare class BoundedTextbox extends Textbox {
15
+ static override type: string;
16
+ static override textLayoutProperties: string[];
17
+ static override cacheProperties: string[];
18
+ static boundedDefaults: BoundedTextboxProps;
19
+ static override getDefaults(): Record<string, unknown>;
20
+ breakWords: BreakWords;
21
+ maxHeight: number | undefined;
22
+ overflow: TextOverflow;
23
+ fit: TextFit;
24
+ minFontSize: number;
25
+ shaping: boolean;
26
+ fitScale: number;
27
+ private baseFontSize;
28
+ private midWordBreaks;
29
+ private lineBase;
30
+ private overflowCache;
31
+ constructor(text: string, options?: Partial<BoundedTextboxProps> & ConstructorParameters<typeof Textbox>[1]);
32
+ getBaseFontSize(): number;
33
+ override initDimensions(): void;
34
+ private shrinkToFit;
35
+ override getValueOfPropertyAt<T extends keyof TextStyleDeclaration>(lineIndex: number, charIndex: number, property: T): this[T];
36
+ override getCompleteStyleDeclaration(lineIndex: number, charIndex: number): ReturnType<Textbox["getCompleteStyleDeclaration"]>;
37
+ private hasOwnFontSize;
38
+ override _wrapText(lines: string[], desiredWidth: number): string[][];
39
+ override _wrapLine(lineIndex: number, desiredWidth: number, data: never, reservedSpace?: number): string[][];
40
+ override _measureLine(lineIndex: number): {
41
+ width: number;
42
+ numOfSpaces: number;
43
+ };
44
+ override _measureWord(word: string[], lineIndex: number, charOffset?: number): number;
45
+ override missingNewlineOffset(lineIndex: number, skipWrapping?: boolean): 0 | 1;
46
+ private overflowLayout;
47
+ private placeEllipsis;
48
+ private clipRects;
49
+ override _render(ctx: CanvasRenderingContext2D): void;
50
+ override _toSVG(): string[];
51
+ override toObject(propertiesToInclude?: PropertyKey[]): any;
52
+ }
53
+ //#endregion
@@ -0,0 +1,53 @@
1
+ import { TextStyleDeclaration, Textbox } from "fabric";
2
+ //#region src/text/bounded-textbox.d.ts
3
+ export type BreakWords = "anywhere" | "never";
4
+ export type TextOverflow = "visible" | "clip" | "ellipsis";
5
+ export type TextFit = "none" | "shrink";
6
+ export interface BoundedTextboxProps {
7
+ breakWords: BreakWords;
8
+ maxHeight: number | undefined;
9
+ overflow: TextOverflow;
10
+ fit: TextFit;
11
+ minFontSize: number;
12
+ shaping: boolean;
13
+ }
14
+ export declare class BoundedTextbox extends Textbox {
15
+ static override type: string;
16
+ static override textLayoutProperties: string[];
17
+ static override cacheProperties: string[];
18
+ static boundedDefaults: BoundedTextboxProps;
19
+ static override getDefaults(): Record<string, unknown>;
20
+ breakWords: BreakWords;
21
+ maxHeight: number | undefined;
22
+ overflow: TextOverflow;
23
+ fit: TextFit;
24
+ minFontSize: number;
25
+ shaping: boolean;
26
+ fitScale: number;
27
+ private baseFontSize;
28
+ private midWordBreaks;
29
+ private lineBase;
30
+ private overflowCache;
31
+ constructor(text: string, options?: Partial<BoundedTextboxProps> & ConstructorParameters<typeof Textbox>[1]);
32
+ getBaseFontSize(): number;
33
+ override initDimensions(): void;
34
+ private shrinkToFit;
35
+ override getValueOfPropertyAt<T extends keyof TextStyleDeclaration>(lineIndex: number, charIndex: number, property: T): this[T];
36
+ override getCompleteStyleDeclaration(lineIndex: number, charIndex: number): ReturnType<Textbox["getCompleteStyleDeclaration"]>;
37
+ private hasOwnFontSize;
38
+ override _wrapText(lines: string[], desiredWidth: number): string[][];
39
+ override _wrapLine(lineIndex: number, desiredWidth: number, data: never, reservedSpace?: number): string[][];
40
+ override _measureLine(lineIndex: number): {
41
+ width: number;
42
+ numOfSpaces: number;
43
+ };
44
+ override _measureWord(word: string[], lineIndex: number, charOffset?: number): number;
45
+ override missingNewlineOffset(lineIndex: number, skipWrapping?: boolean): 0 | 1;
46
+ private overflowLayout;
47
+ private placeEllipsis;
48
+ private clipRects;
49
+ override _render(ctx: CanvasRenderingContext2D): void;
50
+ override _toSVG(): string[];
51
+ override toObject(propertiesToInclude?: PropertyKey[]): any;
52
+ }
53
+ //#endregion