@overtone-art/canvas-editor-core 0.6.4 → 0.7.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.
- package/dist/chunk-XNGPX7FG.mjs +641 -0
- package/dist/chunk-XNGPX7FG.mjs.map +1 -0
- package/dist/index.d.mts +99 -3
- package/dist/index.d.ts +99 -3
- package/dist/index.global.js +55 -54
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +426 -127
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +87 -138
- package/dist/index.mjs.map +1 -1
- package/dist/node.d.mts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +146 -0
- package/dist/node.js.map +1 -1
- package/dist/node.mjs +3 -1
- package/dist/node.mjs.map +1 -1
- package/dist/{types-DSu2jbiV.d.mts → types-DLrbzRj1.d.mts} +20 -1
- package/dist/{types-DSu2jbiV.d.ts → types-DLrbzRj1.d.ts} +20 -1
- package/package.json +1 -1
- package/dist/chunk-MCBRZQ4M.mjs +0 -261
- package/dist/chunk-MCBRZQ4M.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -47,12 +47,14 @@ __export(index_exports, {
|
|
|
47
47
|
TEXTURE_MASK_IDS: () => TEXTURE_MASK_IDS,
|
|
48
48
|
TEXTURE_MASK_SIZE: () => TEXTURE_MASK_SIZE,
|
|
49
49
|
TextCurveManager: () => TextCurveManager,
|
|
50
|
+
TextWrapManager: () => TextWrapManager,
|
|
50
51
|
TiledPatternObject: () => TiledPatternObject,
|
|
51
52
|
UnitConverter: () => UnitConverter,
|
|
52
53
|
applyAspectLock: () => applyAspectLock,
|
|
53
54
|
applyLayerShadow: () => applyLayerShadow,
|
|
54
55
|
applyObjectSelectionStyle: () => applyObjectSelectionStyle,
|
|
55
56
|
applySelectionStyle: () => applySelectionStyle,
|
|
57
|
+
applyTextWrapToObject: () => applyTextWrapToObject,
|
|
56
58
|
buildCurveLinePaths: () => buildCurveLinePaths,
|
|
57
59
|
buildCurvePathData: () => buildCurvePathData,
|
|
58
60
|
clamp: () => clamp,
|
|
@@ -80,7 +82,9 @@ __export(index_exports, {
|
|
|
80
82
|
isTextureMaskId: () => isTextureMaskId,
|
|
81
83
|
needsAbsoluteSpace: () => needsAbsoluteSpace,
|
|
82
84
|
normalizeTextCurve: () => normalizeTextCurve,
|
|
85
|
+
preWrapWordSplit: () => preWrapWordSplit,
|
|
83
86
|
readLayerShadow: () => readLayerShadow,
|
|
87
|
+
readTextWrap: () => readTextWrap,
|
|
84
88
|
renderTextureMask: () => renderTextureMask,
|
|
85
89
|
resetTransform: () => resetTransform,
|
|
86
90
|
restoreLocks: () => restoreLocks,
|
|
@@ -97,7 +101,7 @@ __export(index_exports, {
|
|
|
97
101
|
module.exports = __toCommonJS(index_exports);
|
|
98
102
|
|
|
99
103
|
// src/editor.ts
|
|
100
|
-
var
|
|
104
|
+
var import_fabric21 = require("fabric");
|
|
101
105
|
|
|
102
106
|
// src/events.ts
|
|
103
107
|
var EventEmitter = class {
|
|
@@ -1235,6 +1239,9 @@ var import_fabric5 = require("fabric");
|
|
|
1235
1239
|
var import_fabric4 = require("fabric");
|
|
1236
1240
|
var TEXT_FIT_SLACK = 0.5;
|
|
1237
1241
|
var MEASURE_WIDTH = 1e5;
|
|
1242
|
+
var GROWTH_FACTOR = 1.1;
|
|
1243
|
+
var GROWTH_TRIES = 8;
|
|
1244
|
+
var SEARCH_TRIES = 24;
|
|
1238
1245
|
function unwrappedWidth(text) {
|
|
1239
1246
|
const authored = text.width;
|
|
1240
1247
|
try {
|
|
@@ -1262,26 +1269,74 @@ function textInk(text) {
|
|
|
1262
1269
|
const flip = text.flipX ? -1 : 1;
|
|
1263
1270
|
return { width, dx: flip * (align.includes("right") ? slack : -slack) };
|
|
1264
1271
|
}
|
|
1272
|
+
function wrapsAt(text, width) {
|
|
1273
|
+
text.set({ width });
|
|
1274
|
+
text.initDimensions?.();
|
|
1275
|
+
const authored = text.text.split("\n").length;
|
|
1276
|
+
return (text._textLines?.length ?? 0) > authored;
|
|
1277
|
+
}
|
|
1278
|
+
function widenPastSoftWrap(text, fitted) {
|
|
1279
|
+
if (!wrapsAt(text, fitted)) return fitted;
|
|
1280
|
+
let low = fitted;
|
|
1281
|
+
let high = fitted;
|
|
1282
|
+
let bracketed = false;
|
|
1283
|
+
for (let tries = 0; tries < GROWTH_TRIES; tries += 1) {
|
|
1284
|
+
low = high;
|
|
1285
|
+
high = high * GROWTH_FACTOR + 1;
|
|
1286
|
+
if (!wrapsAt(text, high)) {
|
|
1287
|
+
bracketed = true;
|
|
1288
|
+
break;
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
if (!bracketed) return high;
|
|
1292
|
+
for (let tries = 0; tries < SEARCH_TRIES && high - low > TEXT_FIT_SLACK; tries += 1) {
|
|
1293
|
+
const mid = (low + high) / 2;
|
|
1294
|
+
if (wrapsAt(text, mid)) low = mid;
|
|
1295
|
+
else high = mid;
|
|
1296
|
+
}
|
|
1297
|
+
if (text.width !== high) wrapsAt(text, high);
|
|
1298
|
+
return high;
|
|
1299
|
+
}
|
|
1300
|
+
function restoreWidth(text, width) {
|
|
1301
|
+
try {
|
|
1302
|
+
text.set({ width });
|
|
1303
|
+
text.initDimensions?.();
|
|
1304
|
+
} catch {
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
function isSettled(text, width) {
|
|
1308
|
+
if (wrapsAt(text, width)) return false;
|
|
1309
|
+
const tight = wrapsAt(text, width - TEXT_FIT_SLACK);
|
|
1310
|
+
wrapsAt(text, width);
|
|
1311
|
+
return tight;
|
|
1312
|
+
}
|
|
1265
1313
|
function fitTextWidth(object) {
|
|
1266
1314
|
const text = asText(object);
|
|
1267
1315
|
if (!text || text.path) return false;
|
|
1268
|
-
const
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1316
|
+
const authored = text.width;
|
|
1317
|
+
try {
|
|
1318
|
+
const before = textInk(text);
|
|
1319
|
+
const fitted = unwrappedWidth(text) + TEXT_FIT_SLACK;
|
|
1320
|
+
if (Math.abs(fitted - authored) < TEXT_FIT_SLACK) return false;
|
|
1321
|
+
const nearFit = authored > fitted && authored <= fitted * GROWTH_FACTOR + 1;
|
|
1322
|
+
if (nearFit && isSettled(text, authored)) return false;
|
|
1323
|
+
const centre = text.getCenterPoint();
|
|
1324
|
+
const settled = widenPastSoftWrap(text, fitted);
|
|
1325
|
+
const after = textInk(text);
|
|
1326
|
+
const shift = (before.dx - after.dx) * (text.scaleX ?? 1);
|
|
1327
|
+
const radians = (text.angle ?? 0) * Math.PI / 180;
|
|
1328
|
+
const moved = new import_fabric4.Point(
|
|
1329
|
+
centre.x + shift * Math.cos(radians),
|
|
1330
|
+
centre.y + shift * Math.sin(radians)
|
|
1331
|
+
);
|
|
1332
|
+
text.setPositionByOrigin(moved, "center", "center");
|
|
1333
|
+
text.setCoords();
|
|
1334
|
+
text.dirty = true;
|
|
1335
|
+
return Math.abs(settled - authored) >= TEXT_FIT_SLACK;
|
|
1336
|
+
} catch (error) {
|
|
1337
|
+
restoreWidth(text, authored);
|
|
1338
|
+
throw error;
|
|
1339
|
+
}
|
|
1285
1340
|
}
|
|
1286
1341
|
|
|
1287
1342
|
// src/pattern/tile-geometry.ts
|
|
@@ -2339,8 +2394,268 @@ var TextCurveManager = class {
|
|
|
2339
2394
|
}
|
|
2340
2395
|
};
|
|
2341
2396
|
|
|
2342
|
-
// src/
|
|
2397
|
+
// src/text-wrap.ts
|
|
2398
|
+
var import_fabric9 = require("fabric");
|
|
2399
|
+
|
|
2400
|
+
// src/text-wrap-split.ts
|
|
2401
|
+
var WHITESPACE = /[ \t\r]/;
|
|
2402
|
+
function preWrapWordSplit(value) {
|
|
2403
|
+
const tokens = [];
|
|
2404
|
+
let index = 0;
|
|
2405
|
+
let first = true;
|
|
2406
|
+
while (index < value.length) {
|
|
2407
|
+
let space = "";
|
|
2408
|
+
while (index < value.length && WHITESPACE.test(value[index])) {
|
|
2409
|
+
space += value[index];
|
|
2410
|
+
index += 1;
|
|
2411
|
+
}
|
|
2412
|
+
let word = "";
|
|
2413
|
+
while (index < value.length && !WHITESPACE.test(value[index])) {
|
|
2414
|
+
word += value[index];
|
|
2415
|
+
index += 1;
|
|
2416
|
+
}
|
|
2417
|
+
tokens.push((first ? space : space.slice(1)) + word);
|
|
2418
|
+
first = false;
|
|
2419
|
+
}
|
|
2420
|
+
return tokens.length > 0 ? tokens : [""];
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
// src/masks/space.ts
|
|
2343
2424
|
var import_fabric8 = require("fabric");
|
|
2425
|
+
function matrixOf(object) {
|
|
2426
|
+
return object.calcTransformMatrix();
|
|
2427
|
+
}
|
|
2428
|
+
function applyMatrix(object, matrix) {
|
|
2429
|
+
const decomposed = import_fabric8.util.qrDecompose(matrix);
|
|
2430
|
+
object.set({
|
|
2431
|
+
flipX: false,
|
|
2432
|
+
flipY: false,
|
|
2433
|
+
originX: "center",
|
|
2434
|
+
originY: "center",
|
|
2435
|
+
left: decomposed.translateX,
|
|
2436
|
+
top: decomposed.translateY,
|
|
2437
|
+
scaleX: decomposed.scaleX,
|
|
2438
|
+
scaleY: decomposed.scaleY,
|
|
2439
|
+
angle: decomposed.angle,
|
|
2440
|
+
skewX: decomposed.skewX,
|
|
2441
|
+
skewY: 0
|
|
2442
|
+
});
|
|
2443
|
+
object.setCoords();
|
|
2444
|
+
}
|
|
2445
|
+
function toCanvasSpace(object, host) {
|
|
2446
|
+
applyMatrix(object, import_fabric8.util.multiplyTransformMatrices(matrixOf(host), matrixOf(object)));
|
|
2447
|
+
}
|
|
2448
|
+
function toHostSpace(object, host) {
|
|
2449
|
+
applyMatrix(
|
|
2450
|
+
object,
|
|
2451
|
+
import_fabric8.util.multiplyTransformMatrices(import_fabric8.util.invertTransform(matrixOf(host)), matrixOf(object))
|
|
2452
|
+
);
|
|
2453
|
+
}
|
|
2454
|
+
function relativeMatrix(object, host) {
|
|
2455
|
+
return import_fabric8.util.multiplyTransformMatrices(import_fabric8.util.invertTransform(matrixOf(host)), matrixOf(object));
|
|
2456
|
+
}
|
|
2457
|
+
function applyRelativeMatrix(object, host, rel) {
|
|
2458
|
+
applyMatrix(object, import_fabric8.util.multiplyTransformMatrices(matrixOf(host), rel));
|
|
2459
|
+
}
|
|
2460
|
+
function asObject(clip) {
|
|
2461
|
+
return clip;
|
|
2462
|
+
}
|
|
2463
|
+
function toMatrix(values) {
|
|
2464
|
+
if (!values || values.length !== 6 || values.some((value) => !Number.isFinite(value)))
|
|
2465
|
+
return null;
|
|
2466
|
+
return [values[0], values[1], values[2], values[3], values[4], values[5]];
|
|
2467
|
+
}
|
|
2468
|
+
function fitToBox(object, box, zoom = 1) {
|
|
2469
|
+
const width = Math.max(1, box.width) * zoom;
|
|
2470
|
+
const height = Math.max(1, box.height) * zoom;
|
|
2471
|
+
object.set({
|
|
2472
|
+
originX: "center",
|
|
2473
|
+
originY: "center",
|
|
2474
|
+
angle: 0,
|
|
2475
|
+
skewX: 0,
|
|
2476
|
+
skewY: 0,
|
|
2477
|
+
left: box.left + box.width / 2,
|
|
2478
|
+
top: box.top + box.height / 2,
|
|
2479
|
+
scaleX: width / Math.max(1, object.width ?? 1),
|
|
2480
|
+
scaleY: height / Math.max(1, object.height ?? 1)
|
|
2481
|
+
});
|
|
2482
|
+
object.setCoords();
|
|
2483
|
+
}
|
|
2484
|
+
function unwrapGroup(group) {
|
|
2485
|
+
const children = group.removeAll();
|
|
2486
|
+
for (const child of children) child.setCoords();
|
|
2487
|
+
return children;
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
// src/text-wrap.ts
|
|
2491
|
+
function patchWordSplit(text) {
|
|
2492
|
+
if (Object.prototype.hasOwnProperty.call(text, "wordSplit")) return;
|
|
2493
|
+
Object.defineProperty(text, "wordSplit", {
|
|
2494
|
+
value: preWrapWordSplit,
|
|
2495
|
+
configurable: true,
|
|
2496
|
+
writable: true
|
|
2497
|
+
});
|
|
2498
|
+
}
|
|
2499
|
+
function unpatchWordSplit(text) {
|
|
2500
|
+
if (Object.prototype.hasOwnProperty.call(text, "wordSplit")) {
|
|
2501
|
+
delete text.wordSplit;
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
function readTextWrap(meta) {
|
|
2505
|
+
return { wrap: meta?.wrap ?? "none", overflow: meta?.overflow ?? "visible" };
|
|
2506
|
+
}
|
|
2507
|
+
function boxClip(text, makeRect) {
|
|
2508
|
+
return makeRect({
|
|
2509
|
+
width: Math.max(1, text.width ?? 1),
|
|
2510
|
+
height: Math.max(1, text.height ?? 1),
|
|
2511
|
+
originX: "center",
|
|
2512
|
+
originY: "center",
|
|
2513
|
+
left: 0,
|
|
2514
|
+
top: 0,
|
|
2515
|
+
objectCaching: false
|
|
2516
|
+
});
|
|
2517
|
+
}
|
|
2518
|
+
function hasMask(meta) {
|
|
2519
|
+
return !!meta.maskPreset || (meta.maskStack?.length ?? 0) > 0;
|
|
2520
|
+
}
|
|
2521
|
+
function applyTextWrapToObject(object, meta, makeRect = (options) => new import_fabric9.Rect(options)) {
|
|
2522
|
+
const text = asText(object);
|
|
2523
|
+
if (!text) return;
|
|
2524
|
+
const state = readTextWrap(meta);
|
|
2525
|
+
if (!text.path) {
|
|
2526
|
+
text.set({ splitByGrapheme: state.wrap === "break-all" });
|
|
2527
|
+
if (state.wrap === "pre-wrap") patchWordSplit(text);
|
|
2528
|
+
else unpatchWordSplit(text);
|
|
2529
|
+
text.initDimensions?.();
|
|
2530
|
+
}
|
|
2531
|
+
const clip = state.overflow === "hidden" ? boxClip(text, makeRect) : void 0;
|
|
2532
|
+
if (meta && hasMask(meta)) {
|
|
2533
|
+
const host = text.clipPath;
|
|
2534
|
+
if (host) {
|
|
2535
|
+
if (clip) toHostSpace(clip, asObject(host));
|
|
2536
|
+
host.clipPath = clip;
|
|
2537
|
+
}
|
|
2538
|
+
} else {
|
|
2539
|
+
text.clipPath = clip;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
var TextWrapManager = class {
|
|
2543
|
+
constructor(canvas, layers, history, events) {
|
|
2544
|
+
this.canvas = canvas;
|
|
2545
|
+
this.layers = layers;
|
|
2546
|
+
this.history = history;
|
|
2547
|
+
this.events = events;
|
|
2548
|
+
this.canvas.on("text:changed", this.onTextChanged);
|
|
2549
|
+
this.events.on("masks:changed", this.onMasksChanged);
|
|
2550
|
+
}
|
|
2551
|
+
canvas;
|
|
2552
|
+
layers;
|
|
2553
|
+
history;
|
|
2554
|
+
events;
|
|
2555
|
+
/**
|
|
2556
|
+
* Typing changes the run the box was fitted to, so an auto-width layer has to
|
|
2557
|
+
* re-fit. No history entry: fabric records the edit when editing exits, and a
|
|
2558
|
+
* save per keystroke would bury every earlier step.
|
|
2559
|
+
*/
|
|
2560
|
+
onTextChanged = (event) => {
|
|
2561
|
+
const layer = event.target ? this.layers.findByObject(event.target) : void 0;
|
|
2562
|
+
if (!layer) return;
|
|
2563
|
+
this.refresh(layer.id, false);
|
|
2564
|
+
};
|
|
2565
|
+
/**
|
|
2566
|
+
* Both mask owners — the preset manager and every mask-stack mutation —
|
|
2567
|
+
* install their clip straight onto `clipPath`, dropping whatever was there,
|
|
2568
|
+
* and neither knows this layer had a box clip. Re-deriving on the one event
|
|
2569
|
+
* they both announce puts it back where it now belongs: nested under the new
|
|
2570
|
+
* mask, or at the top level when the last mask leaves. Waiting for the next
|
|
2571
|
+
* keystroke instead would leave a layer that should clip inside its box
|
|
2572
|
+
* serialized unclipped — which is what the print renderer reads.
|
|
2573
|
+
*/
|
|
2574
|
+
onMasksChanged = ({ target }) => {
|
|
2575
|
+
this.refresh(target);
|
|
2576
|
+
};
|
|
2577
|
+
dispose() {
|
|
2578
|
+
this.canvas.off("text:changed", this.onTextChanged);
|
|
2579
|
+
this.events.off("masks:changed", this.onMasksChanged);
|
|
2580
|
+
}
|
|
2581
|
+
/** Both properties for a text layer, or null when it is not text. */
|
|
2582
|
+
get(layerId) {
|
|
2583
|
+
const layer = this.layers.get(layerId);
|
|
2584
|
+
if (!layer || !asText(layer.fabricObject)) return null;
|
|
2585
|
+
return readTextWrap(layer.meta);
|
|
2586
|
+
}
|
|
2587
|
+
apply(layerId, wrap, save = true) {
|
|
2588
|
+
const layer = this.layers.get(layerId);
|
|
2589
|
+
const text = layer ? asText(layer.fabricObject) : null;
|
|
2590
|
+
if (!layer || !text) return false;
|
|
2591
|
+
if (wrap === "none") {
|
|
2592
|
+
const curved = !!text.path;
|
|
2593
|
+
if (layer.meta.wrapWidth === void 0 && !curved) {
|
|
2594
|
+
layer.meta.wrapWidth = text.width ?? 0;
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
layer.meta.wrap = wrap;
|
|
2598
|
+
return this.derive(layerId, save);
|
|
2599
|
+
}
|
|
2600
|
+
setOverflow(layerId, overflow, save = true) {
|
|
2601
|
+
const layer = this.layers.get(layerId);
|
|
2602
|
+
if (!layer || !asText(layer.fabricObject)) return false;
|
|
2603
|
+
layer.meta.overflow = overflow;
|
|
2604
|
+
return this.derive(layerId, save);
|
|
2605
|
+
}
|
|
2606
|
+
/** Back to the defaults: auto-width, unclipped. */
|
|
2607
|
+
clear(layerId, save = true) {
|
|
2608
|
+
const layer = this.layers.get(layerId);
|
|
2609
|
+
if (!layer || !asText(layer.fabricObject)) return false;
|
|
2610
|
+
delete layer.meta.overflow;
|
|
2611
|
+
return this.apply(layerId, "none", save);
|
|
2612
|
+
}
|
|
2613
|
+
/** Re-derive from the stored mode — after a text, font or size change. */
|
|
2614
|
+
refresh(layerId, save = false) {
|
|
2615
|
+
const layer = this.layers.get(layerId);
|
|
2616
|
+
if (!layer || !asText(layer.fabricObject)) return false;
|
|
2617
|
+
return this.derive(layerId, save);
|
|
2618
|
+
}
|
|
2619
|
+
/**
|
|
2620
|
+
* Re-derive every text layer — used after a state restore.
|
|
2621
|
+
*
|
|
2622
|
+
* Recursive because a template inserts as a group of real child layers, and
|
|
2623
|
+
* text inside one would otherwise keep whatever box it was restored with.
|
|
2624
|
+
*/
|
|
2625
|
+
refreshAll() {
|
|
2626
|
+
const visit = (layers) => {
|
|
2627
|
+
for (const layer of layers) {
|
|
2628
|
+
if (asText(layer.fabricObject)) this.refresh(layer.id);
|
|
2629
|
+
if (layer.children.length > 0) visit(layer.children);
|
|
2630
|
+
}
|
|
2631
|
+
};
|
|
2632
|
+
visit(this.layers.getAll());
|
|
2633
|
+
}
|
|
2634
|
+
derive(layerId, save) {
|
|
2635
|
+
const layer = this.layers.get(layerId);
|
|
2636
|
+
const text = layer ? asText(layer.fabricObject) : null;
|
|
2637
|
+
if (!layer || !text) return false;
|
|
2638
|
+
const state = readTextWrap(layer.meta);
|
|
2639
|
+
if (!text.path) {
|
|
2640
|
+
if (state.wrap === "none") {
|
|
2641
|
+
fitTextWidth(text);
|
|
2642
|
+
} else if (layer.meta.wrapWidth !== void 0) {
|
|
2643
|
+
text.set({ width: layer.meta.wrapWidth });
|
|
2644
|
+
delete layer.meta.wrapWidth;
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
applyTextWrapToObject(text, layer.meta);
|
|
2648
|
+
text.dirty = true;
|
|
2649
|
+
text.setCoords();
|
|
2650
|
+
this.canvas.requestRenderAll();
|
|
2651
|
+
this.events.emit("layer:modified", { layerId });
|
|
2652
|
+
if (save) this.history.save();
|
|
2653
|
+
return true;
|
|
2654
|
+
}
|
|
2655
|
+
};
|
|
2656
|
+
|
|
2657
|
+
// src/mask-presets/manager.ts
|
|
2658
|
+
var import_fabric10 = require("fabric");
|
|
2344
2659
|
|
|
2345
2660
|
// src/mask-presets/shapes.ts
|
|
2346
2661
|
var SHAPE_MASK_IDS = [
|
|
@@ -2573,6 +2888,7 @@ var MaskPresetManager = class {
|
|
|
2573
2888
|
object.dirty = true;
|
|
2574
2889
|
object.setCoords();
|
|
2575
2890
|
this.canvas.requestRenderAll();
|
|
2891
|
+
this.events.emit("masks:changed", { target: layerId });
|
|
2576
2892
|
this.events.emit("layer:modified", { layerId });
|
|
2577
2893
|
if (save) this.history.save();
|
|
2578
2894
|
return true;
|
|
@@ -2607,13 +2923,13 @@ var MaskPresetManager = class {
|
|
|
2607
2923
|
objectCaching: false
|
|
2608
2924
|
};
|
|
2609
2925
|
if (isShapeMaskId(id)) {
|
|
2610
|
-
return new
|
|
2926
|
+
return new import_fabric10.Path(shapeMaskPathData(id), {
|
|
2611
2927
|
...shared,
|
|
2612
2928
|
scaleX: width / SHAPE_MASK_BOX,
|
|
2613
2929
|
scaleY: height / SHAPE_MASK_BOX
|
|
2614
2930
|
});
|
|
2615
2931
|
}
|
|
2616
|
-
return new
|
|
2932
|
+
return new import_fabric10.FabricImage(renderTextureMask(id), {
|
|
2617
2933
|
...shared,
|
|
2618
2934
|
scaleX: width / TEXTURE_MASK_SIZE,
|
|
2619
2935
|
scaleY: height / TEXTURE_MASK_SIZE
|
|
@@ -2622,7 +2938,7 @@ var MaskPresetManager = class {
|
|
|
2622
2938
|
};
|
|
2623
2939
|
|
|
2624
2940
|
// src/shadow.ts
|
|
2625
|
-
var
|
|
2941
|
+
var import_fabric11 = require("fabric");
|
|
2626
2942
|
|
|
2627
2943
|
// src/utils/color.ts
|
|
2628
2944
|
var CSS_COLOR = /^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\([\d.%+\-\s,/]+\)|[a-z]+)$/i;
|
|
@@ -2660,7 +2976,7 @@ function applyLayerShadow(object, config) {
|
|
|
2660
2976
|
}
|
|
2661
2977
|
const color = isCssColor(next.color) ? next.color : DEFAULT_LAYER_SHADOW.color;
|
|
2662
2978
|
object.set({
|
|
2663
|
-
shadow: new
|
|
2979
|
+
shadow: new import_fabric11.Shadow({
|
|
2664
2980
|
color,
|
|
2665
2981
|
blur: Math.max(0, next.blur),
|
|
2666
2982
|
offsetX: next.offsetX,
|
|
@@ -2774,7 +3090,7 @@ var UnitConverter = class {
|
|
|
2774
3090
|
};
|
|
2775
3091
|
|
|
2776
3092
|
// src/serialization.ts
|
|
2777
|
-
var
|
|
3093
|
+
var import_fabric12 = require("fabric");
|
|
2778
3094
|
var VERSION = "2.0.0";
|
|
2779
3095
|
function serializeEditor(editor) {
|
|
2780
3096
|
return {
|
|
@@ -2810,7 +3126,7 @@ async function deserializeEditor(editor, state) {
|
|
|
2810
3126
|
}
|
|
2811
3127
|
const staged = await Promise.all(
|
|
2812
3128
|
state.layers.map(async (serialized) => {
|
|
2813
|
-
const fabricObject = (await
|
|
3129
|
+
const fabricObject = (await import_fabric12.util.enlivenObjects([serialized.fabricObject]))[0];
|
|
2814
3130
|
if (!fabricObject) {
|
|
2815
3131
|
const source = serialized.fabricObject.src;
|
|
2816
3132
|
if (typeof source === "string" && source.startsWith("blob:")) {
|
|
@@ -2821,7 +3137,7 @@ async function deserializeEditor(editor, state) {
|
|
|
2821
3137
|
return { serialized, fabricObject };
|
|
2822
3138
|
})
|
|
2823
3139
|
);
|
|
2824
|
-
const stagedBackground = state.backgroundImage ? (await
|
|
3140
|
+
const stagedBackground = state.backgroundImage ? (await import_fabric12.util.enlivenObjects([state.backgroundImage]))[0] : null;
|
|
2825
3141
|
if (state.backgroundImage && !stagedBackground) {
|
|
2826
3142
|
const source = state.backgroundImage.src;
|
|
2827
3143
|
if (typeof source === "string" && source.startsWith("blob:")) {
|
|
@@ -2847,6 +3163,7 @@ async function deserializeEditor(editor, state) {
|
|
|
2847
3163
|
for (const item of staged) {
|
|
2848
3164
|
restoreLayer(editor, item.serialized, item.fabricObject);
|
|
2849
3165
|
}
|
|
3166
|
+
editor.wrap.refreshAll();
|
|
2850
3167
|
editor.canvas.requestRenderAll();
|
|
2851
3168
|
}
|
|
2852
3169
|
function restoreLayer(editor, serialized, fabricObject) {
|
|
@@ -2873,7 +3190,7 @@ function restoreLayer(editor, serialized, fabricObject) {
|
|
|
2873
3190
|
}
|
|
2874
3191
|
|
|
2875
3192
|
// src/export.ts
|
|
2876
|
-
var
|
|
3193
|
+
var import_fabric13 = require("fabric");
|
|
2877
3194
|
|
|
2878
3195
|
// src/displacement.ts
|
|
2879
3196
|
var CHANNEL_INDEX = {
|
|
@@ -2967,7 +3284,7 @@ async function exportPNG(canvas, options = {}) {
|
|
|
2967
3284
|
}
|
|
2968
3285
|
async function exportIsolatedPNG(source, objects, options = {}) {
|
|
2969
3286
|
const element = source.lowerCanvasEl.ownerDocument.createElement("canvas");
|
|
2970
|
-
const canvas = new
|
|
3287
|
+
const canvas = new import_fabric13.StaticCanvas(element, {
|
|
2971
3288
|
width: options.width ?? source.getWidth(),
|
|
2972
3289
|
height: options.height ?? source.getHeight(),
|
|
2973
3290
|
backgroundColor: options.backgroundColor || void 0
|
|
@@ -2997,7 +3314,7 @@ async function exportPrintArea(source, area, options = {}) {
|
|
|
2997
3314
|
throw new Error("Print area does not overlap the canvas");
|
|
2998
3315
|
}
|
|
2999
3316
|
const element = source.lowerCanvasEl.ownerDocument.createElement("canvas");
|
|
3000
|
-
const canvas = new
|
|
3317
|
+
const canvas = new import_fabric13.StaticCanvas(element, { width, height });
|
|
3001
3318
|
try {
|
|
3002
3319
|
const clones = await Promise.all(source.getObjects().map((object) => object.clone()));
|
|
3003
3320
|
if (clones.length) canvas.add(...clones);
|
|
@@ -3437,7 +3754,7 @@ var ProjectManager = class {
|
|
|
3437
3754
|
};
|
|
3438
3755
|
|
|
3439
3756
|
// src/mask.ts
|
|
3440
|
-
var
|
|
3757
|
+
var import_fabric14 = require("fabric");
|
|
3441
3758
|
var MaskRefinementError = class extends Error {
|
|
3442
3759
|
constructor(code, message, cause) {
|
|
3443
3760
|
super(message);
|
|
@@ -3469,7 +3786,7 @@ var MaskController = class {
|
|
|
3469
3786
|
throw new Error("Mask dimensions must be positive integers");
|
|
3470
3787
|
}
|
|
3471
3788
|
const backing = this.makeCanvas(width, height);
|
|
3472
|
-
const image = new
|
|
3789
|
+
const image = new import_fabric14.FabricImage(backing, {
|
|
3473
3790
|
left: 0,
|
|
3474
3791
|
top: 0,
|
|
3475
3792
|
originX: "left",
|
|
@@ -3695,10 +4012,10 @@ var MaskController = class {
|
|
|
3695
4012
|
};
|
|
3696
4013
|
|
|
3697
4014
|
// src/masks/manager.ts
|
|
3698
|
-
var
|
|
4015
|
+
var import_fabric20 = require("fabric");
|
|
3699
4016
|
|
|
3700
4017
|
// src/masks/compose.ts
|
|
3701
|
-
var
|
|
4018
|
+
var import_fabric15 = require("fabric");
|
|
3702
4019
|
var MODE_OPERATION = {
|
|
3703
4020
|
add: "source-over",
|
|
3704
4021
|
subtract: "destination-out",
|
|
@@ -3708,7 +4025,7 @@ function neutralize(child) {
|
|
|
3708
4025
|
child.set({ opacity: 0, globalCompositeOperation: "source-over" });
|
|
3709
4026
|
}
|
|
3710
4027
|
function baseRect(box) {
|
|
3711
|
-
return new
|
|
4028
|
+
return new import_fabric15.Rect({
|
|
3712
4029
|
left: box.left,
|
|
3713
4030
|
top: box.top,
|
|
3714
4031
|
width: Math.max(1, box.width),
|
|
@@ -3735,7 +4052,7 @@ function composeMaskGroup(children, entries, options) {
|
|
|
3735
4052
|
});
|
|
3736
4053
|
const first = entries.find((entry) => entry.visible);
|
|
3737
4054
|
const withBase = first && first.mode !== "add" ? [baseRect(options.box), ...children] : [...children];
|
|
3738
|
-
return new
|
|
4055
|
+
return new import_fabric15.Group(withBase, {
|
|
3739
4056
|
absolutePositioned: options.absolute,
|
|
3740
4057
|
// Cached, so the children's compositing operations resolve against each
|
|
3741
4058
|
// other instead of against the page underneath the mask.
|
|
@@ -3749,76 +4066,7 @@ function needsAbsoluteSpace(entries) {
|
|
|
3749
4066
|
}
|
|
3750
4067
|
|
|
3751
4068
|
// src/masks/edit.ts
|
|
3752
|
-
var
|
|
3753
|
-
|
|
3754
|
-
// src/masks/space.ts
|
|
3755
|
-
var import_fabric14 = require("fabric");
|
|
3756
|
-
function matrixOf(object) {
|
|
3757
|
-
return object.calcTransformMatrix();
|
|
3758
|
-
}
|
|
3759
|
-
function applyMatrix(object, matrix) {
|
|
3760
|
-
const decomposed = import_fabric14.util.qrDecompose(matrix);
|
|
3761
|
-
object.set({
|
|
3762
|
-
flipX: false,
|
|
3763
|
-
flipY: false,
|
|
3764
|
-
originX: "center",
|
|
3765
|
-
originY: "center",
|
|
3766
|
-
left: decomposed.translateX,
|
|
3767
|
-
top: decomposed.translateY,
|
|
3768
|
-
scaleX: decomposed.scaleX,
|
|
3769
|
-
scaleY: decomposed.scaleY,
|
|
3770
|
-
angle: decomposed.angle,
|
|
3771
|
-
skewX: decomposed.skewX,
|
|
3772
|
-
skewY: 0
|
|
3773
|
-
});
|
|
3774
|
-
object.setCoords();
|
|
3775
|
-
}
|
|
3776
|
-
function toCanvasSpace(object, host) {
|
|
3777
|
-
applyMatrix(object, import_fabric14.util.multiplyTransformMatrices(matrixOf(host), matrixOf(object)));
|
|
3778
|
-
}
|
|
3779
|
-
function toHostSpace(object, host) {
|
|
3780
|
-
applyMatrix(
|
|
3781
|
-
object,
|
|
3782
|
-
import_fabric14.util.multiplyTransformMatrices(import_fabric14.util.invertTransform(matrixOf(host)), matrixOf(object))
|
|
3783
|
-
);
|
|
3784
|
-
}
|
|
3785
|
-
function relativeMatrix(object, host) {
|
|
3786
|
-
return import_fabric14.util.multiplyTransformMatrices(import_fabric14.util.invertTransform(matrixOf(host)), matrixOf(object));
|
|
3787
|
-
}
|
|
3788
|
-
function applyRelativeMatrix(object, host, rel) {
|
|
3789
|
-
applyMatrix(object, import_fabric14.util.multiplyTransformMatrices(matrixOf(host), rel));
|
|
3790
|
-
}
|
|
3791
|
-
function asObject(clip) {
|
|
3792
|
-
return clip;
|
|
3793
|
-
}
|
|
3794
|
-
function toMatrix(values) {
|
|
3795
|
-
if (!values || values.length !== 6 || values.some((value) => !Number.isFinite(value)))
|
|
3796
|
-
return null;
|
|
3797
|
-
return [values[0], values[1], values[2], values[3], values[4], values[5]];
|
|
3798
|
-
}
|
|
3799
|
-
function fitToBox(object, box, zoom = 1) {
|
|
3800
|
-
const width = Math.max(1, box.width) * zoom;
|
|
3801
|
-
const height = Math.max(1, box.height) * zoom;
|
|
3802
|
-
object.set({
|
|
3803
|
-
originX: "center",
|
|
3804
|
-
originY: "center",
|
|
3805
|
-
angle: 0,
|
|
3806
|
-
skewX: 0,
|
|
3807
|
-
skewY: 0,
|
|
3808
|
-
left: box.left + box.width / 2,
|
|
3809
|
-
top: box.top + box.height / 2,
|
|
3810
|
-
scaleX: width / Math.max(1, object.width ?? 1),
|
|
3811
|
-
scaleY: height / Math.max(1, object.height ?? 1)
|
|
3812
|
-
});
|
|
3813
|
-
object.setCoords();
|
|
3814
|
-
}
|
|
3815
|
-
function unwrapGroup(group) {
|
|
3816
|
-
const children = group.removeAll();
|
|
3817
|
-
for (const child of children) child.setCoords();
|
|
3818
|
-
return children;
|
|
3819
|
-
}
|
|
3820
|
-
|
|
3821
|
-
// src/masks/edit.ts
|
|
4069
|
+
var import_fabric16 = require("fabric");
|
|
3822
4070
|
var MaskEditController = class {
|
|
3823
4071
|
constructor(canvas, onCommit) {
|
|
3824
4072
|
this.canvas = canvas;
|
|
@@ -3899,8 +4147,8 @@ var MaskEditController = class {
|
|
|
3899
4147
|
if (!this.handle || !this.child || !this.group) return;
|
|
3900
4148
|
applyMatrix(
|
|
3901
4149
|
this.child,
|
|
3902
|
-
|
|
3903
|
-
|
|
4150
|
+
import_fabric16.util.multiplyTransformMatrices(
|
|
4151
|
+
import_fabric16.util.invertTransform(matrixOf(this.group)),
|
|
3904
4152
|
matrixOf(this.handle)
|
|
3905
4153
|
)
|
|
3906
4154
|
);
|
|
@@ -3911,15 +4159,15 @@ var MaskEditController = class {
|
|
|
3911
4159
|
};
|
|
3912
4160
|
|
|
3913
4161
|
// src/masks/store.ts
|
|
3914
|
-
var
|
|
4162
|
+
var import_fabric19 = require("fabric");
|
|
3915
4163
|
|
|
3916
4164
|
// src/masks/host.ts
|
|
3917
|
-
var
|
|
4165
|
+
var import_fabric17 = require("fabric");
|
|
3918
4166
|
function findCanvasHost(layers) {
|
|
3919
4167
|
return layers.getAll().find((layer) => layer.meta.canvasMask);
|
|
3920
4168
|
}
|
|
3921
4169
|
function createCanvasHost(canvas, layers) {
|
|
3922
|
-
const rect = new
|
|
4170
|
+
const rect = new import_fabric17.Rect({
|
|
3923
4171
|
left: 0,
|
|
3924
4172
|
top: 0,
|
|
3925
4173
|
width: canvas.getWidth(),
|
|
@@ -3968,9 +4216,9 @@ function hostBoxOf(canvas, host, absolute) {
|
|
|
3968
4216
|
}
|
|
3969
4217
|
|
|
3970
4218
|
// src/masks/install.ts
|
|
3971
|
-
var
|
|
4219
|
+
var import_fabric18 = require("fabric");
|
|
3972
4220
|
function convertSpace(host, sources, absolute) {
|
|
3973
|
-
const wasAbsolute = host.clipPath instanceof
|
|
4221
|
+
const wasAbsolute = host.clipPath instanceof import_fabric18.Group ? host.clipPath.absolutePositioned : absolute;
|
|
3974
4222
|
if (absolute === wasAbsolute) return;
|
|
3975
4223
|
for (const source of sources) {
|
|
3976
4224
|
if (absolute) toCanvasSpace(source, host);
|
|
@@ -4050,6 +4298,24 @@ var MaskStackStore = class {
|
|
|
4050
4298
|
this.pinning = false;
|
|
4051
4299
|
}
|
|
4052
4300
|
}
|
|
4301
|
+
/**
|
|
4302
|
+
* Is the host's own `clipPath` the wrap manager's box clip rather than a mask?
|
|
4303
|
+
*
|
|
4304
|
+
* Adoption rescues a clip whose provenance is UNKNOWN — a mask preset, or a
|
|
4305
|
+
* hand-set `clipPath` from before stacks existed — so the first add() cannot
|
|
4306
|
+
* silently throw it away. A text layer's box clip is the opposite case: its
|
|
4307
|
+
* provenance is known exactly (the wrap manager derives it from
|
|
4308
|
+
* `meta.overflow` on every pass and re-installs it under whatever owns the
|
|
4309
|
+
* property next), and it is not a mask. Adopted, it becomes a stack row the
|
|
4310
|
+
* user never made whose 'add' union covers the whole box — swallowing the mask
|
|
4311
|
+
* they actually added.
|
|
4312
|
+
*
|
|
4313
|
+
* Only meaningful with an empty stack: once there are entries the top-level
|
|
4314
|
+
* clip is the composed group and the box clip is nested underneath it.
|
|
4315
|
+
*/
|
|
4316
|
+
holdsBoxClip(meta) {
|
|
4317
|
+
return meta?.overflow === "hidden" && !meta.maskPreset;
|
|
4318
|
+
}
|
|
4053
4319
|
/**
|
|
4054
4320
|
* Take the current geometry back out of the composed clip, entry-aligned.
|
|
4055
4321
|
*
|
|
@@ -4062,7 +4328,13 @@ var MaskStackStore = class {
|
|
|
4062
4328
|
const clip = host.clipPath;
|
|
4063
4329
|
if (!clip) return [];
|
|
4064
4330
|
const entries = this.list(target);
|
|
4065
|
-
|
|
4331
|
+
const meta = this.hostLayer(target)?.meta;
|
|
4332
|
+
if (entries.length === 0 && this.holdsBoxClip(meta)) return [];
|
|
4333
|
+
if (entries.length === 0 || !(clip instanceof import_fabric19.Group)) {
|
|
4334
|
+
const source = asObject(clip);
|
|
4335
|
+
if (meta?.overflow === "hidden") source.clipPath = void 0;
|
|
4336
|
+
return [source];
|
|
4337
|
+
}
|
|
4066
4338
|
const children = unwrapGroup(clip);
|
|
4067
4339
|
const extra = children.length - entries.length;
|
|
4068
4340
|
return extra > 0 ? children.slice(extra) : children;
|
|
@@ -4162,7 +4434,7 @@ var LayerMaskManager = class extends MaskStackStore {
|
|
|
4162
4434
|
if (index === -1) return false;
|
|
4163
4435
|
this.endEdit(false);
|
|
4164
4436
|
this.commit(target, host, [...entries], this.unwrap(target, host), false, true);
|
|
4165
|
-
const group = host.clipPath instanceof
|
|
4437
|
+
const group = host.clipPath instanceof import_fabric20.Group ? host.clipPath : null;
|
|
4166
4438
|
if (!group) return false;
|
|
4167
4439
|
const children = group.getObjects();
|
|
4168
4440
|
const child = children[children.length - entries.length + index];
|
|
@@ -4393,6 +4665,7 @@ var LayerMaskManager = class extends MaskStackStore {
|
|
|
4393
4665
|
const clip = layer?.fabricObject.clipPath;
|
|
4394
4666
|
if (!layer || !clip || layer.meta.pattern) return null;
|
|
4395
4667
|
if ((layer.meta.maskStack ?? []).length > 0) return null;
|
|
4668
|
+
if (this.holdsBoxClip(layer.meta)) return null;
|
|
4396
4669
|
const entry = {
|
|
4397
4670
|
...DEFAULT_ENTRY,
|
|
4398
4671
|
id: generateId(),
|
|
@@ -4445,6 +4718,7 @@ var CanvasEditor = class {
|
|
|
4445
4718
|
crop;
|
|
4446
4719
|
patterns;
|
|
4447
4720
|
curves;
|
|
4721
|
+
wrap;
|
|
4448
4722
|
maskPresets;
|
|
4449
4723
|
/** Stacked boolean masks, per layer and for the design as a whole. */
|
|
4450
4724
|
layerMasks;
|
|
@@ -4472,7 +4746,7 @@ var CanvasEditor = class {
|
|
|
4472
4746
|
const widthPx = this.units.toPixels(config.width);
|
|
4473
4747
|
const heightPx = this.units.toPixels(config.height);
|
|
4474
4748
|
this.designBackground = config.backgroundColor ?? "#ffffff";
|
|
4475
|
-
this.canvas = new
|
|
4749
|
+
this.canvas = new import_fabric21.Canvas(canvasElement, {
|
|
4476
4750
|
width: widthPx,
|
|
4477
4751
|
height: heightPx,
|
|
4478
4752
|
backgroundColor: this.designBackground,
|
|
@@ -4495,6 +4769,7 @@ var CanvasEditor = class {
|
|
|
4495
4769
|
this.crop = new CropController(this.canvas, this.history, this.events);
|
|
4496
4770
|
this.patterns = new PatternManager(this.canvas, this.layers, this.history, this.events);
|
|
4497
4771
|
this.curves = new TextCurveManager(this.canvas, this.layers, this.history, this.events);
|
|
4772
|
+
this.wrap = new TextWrapManager(this.canvas, this.layers, this.history, this.events);
|
|
4498
4773
|
this.maskPresets = new MaskPresetManager(this.canvas, this.layers, this.history, this.events);
|
|
4499
4774
|
this.layerMasks = new LayerMaskManager(this.canvas, this.layers, this.history, this.events);
|
|
4500
4775
|
this.setupCanvasEvents();
|
|
@@ -4506,7 +4781,7 @@ var CanvasEditor = class {
|
|
|
4506
4781
|
// ─── Layer Operations ────────────────────────────────
|
|
4507
4782
|
async addImage(url, options) {
|
|
4508
4783
|
try {
|
|
4509
|
-
const img = await
|
|
4784
|
+
const img = await import_fabric21.FabricImage.fromURL(
|
|
4510
4785
|
url,
|
|
4511
4786
|
{},
|
|
4512
4787
|
{ originX: "left", originY: "top", ...options }
|
|
@@ -4532,7 +4807,7 @@ var CanvasEditor = class {
|
|
|
4532
4807
|
if (this.crop.activeLayerId() === layerId) this.crop.cancel();
|
|
4533
4808
|
const previous = layer.fabricObject;
|
|
4534
4809
|
try {
|
|
4535
|
-
const replacement = await
|
|
4810
|
+
const replacement = await import_fabric21.FabricImage.fromURL(url, {}, { originX: "left", originY: "top" });
|
|
4536
4811
|
replacement.set({
|
|
4537
4812
|
left: previous.left,
|
|
4538
4813
|
top: previous.top,
|
|
@@ -4561,7 +4836,7 @@ var CanvasEditor = class {
|
|
|
4561
4836
|
}
|
|
4562
4837
|
}
|
|
4563
4838
|
addText(text, options) {
|
|
4564
|
-
const textbox = new
|
|
4839
|
+
const textbox = new import_fabric21.Textbox(text, {
|
|
4565
4840
|
fontSize: 32,
|
|
4566
4841
|
fontFamily: "Arial",
|
|
4567
4842
|
fill: "#000000",
|
|
@@ -4692,10 +4967,10 @@ var CanvasEditor = class {
|
|
|
4692
4967
|
const next = { ...previous, ...adjustments };
|
|
4693
4968
|
const clampAdjustment = (value, min = -1) => clamp(value ?? 0, min, 1);
|
|
4694
4969
|
image.filters = [
|
|
4695
|
-
new
|
|
4696
|
-
new
|
|
4697
|
-
new
|
|
4698
|
-
new
|
|
4970
|
+
new import_fabric21.filters.Brightness({ brightness: clampAdjustment(next.brightness) }),
|
|
4971
|
+
new import_fabric21.filters.Contrast({ contrast: clampAdjustment(next.contrast) }),
|
|
4972
|
+
new import_fabric21.filters.Saturation({ saturation: clampAdjustment(next.saturation) }),
|
|
4973
|
+
new import_fabric21.filters.Blur({ blur: clampAdjustment(next.blur, 0) })
|
|
4699
4974
|
];
|
|
4700
4975
|
layer.meta.imageAdjustments = next;
|
|
4701
4976
|
image.applyFilters();
|
|
@@ -4736,7 +5011,7 @@ var CanvasEditor = class {
|
|
|
4736
5011
|
this.layers.detach(layer.id);
|
|
4737
5012
|
}
|
|
4738
5013
|
}
|
|
4739
|
-
const group = new
|
|
5014
|
+
const group = new import_fabric21.Group(objects);
|
|
4740
5015
|
const grouped = this.layers.add("group", group, name);
|
|
4741
5016
|
this.layers.adoptChildren(grouped, children);
|
|
4742
5017
|
this.layers.reorder(grouped.id, insertIndex);
|
|
@@ -4798,7 +5073,7 @@ var CanvasEditor = class {
|
|
|
4798
5073
|
const layer = this.layers.get(id);
|
|
4799
5074
|
if (!layer) throw new Error(`Layer not found: ${id}`);
|
|
4800
5075
|
try {
|
|
4801
|
-
if (options.resolution === "source" && layer.fabricObject instanceof
|
|
5076
|
+
if (options.resolution === "source" && layer.fabricObject instanceof import_fabric21.FabricImage) {
|
|
4802
5077
|
const image = await layer.fabricObject.clone();
|
|
4803
5078
|
image.set({
|
|
4804
5079
|
left: 0,
|
|
@@ -5074,7 +5349,7 @@ var CanvasEditor = class {
|
|
|
5074
5349
|
return;
|
|
5075
5350
|
}
|
|
5076
5351
|
try {
|
|
5077
|
-
const image = await
|
|
5352
|
+
const image = await import_fabric21.FabricImage.fromURL(
|
|
5078
5353
|
url,
|
|
5079
5354
|
{ crossOrigin: options.crossOrigin ?? null, signal: options.signal },
|
|
5080
5355
|
{ originX: "left", originY: "top" }
|
|
@@ -5251,15 +5526,34 @@ var CanvasEditor = class {
|
|
|
5251
5526
|
}
|
|
5252
5527
|
// ─── Text curve ─────────────────────────────────────
|
|
5253
5528
|
applyTextCurve(layerId, config) {
|
|
5254
|
-
|
|
5529
|
+
const applied = this.curves.apply(layerId, config);
|
|
5530
|
+
if (applied) this.wrap.refresh(layerId);
|
|
5531
|
+
return applied;
|
|
5255
5532
|
}
|
|
5256
5533
|
clearTextCurve(layerId) {
|
|
5257
|
-
|
|
5534
|
+
const cleared = this.curves.clear(layerId);
|
|
5535
|
+
if (cleared) this.wrap.refresh(layerId);
|
|
5536
|
+
return cleared;
|
|
5258
5537
|
}
|
|
5259
5538
|
getTextCurve(layerId) {
|
|
5260
5539
|
return this.curves.get(layerId);
|
|
5261
5540
|
}
|
|
5541
|
+
// ─── Text wrap ──────────────────────────────────────
|
|
5542
|
+
applyTextWrap(layerId, wrap) {
|
|
5543
|
+
return this.wrap.apply(layerId, wrap);
|
|
5544
|
+
}
|
|
5545
|
+
setTextOverflow(layerId, overflow) {
|
|
5546
|
+
return this.wrap.setOverflow(layerId, overflow);
|
|
5547
|
+
}
|
|
5548
|
+
getTextWrap(layerId) {
|
|
5549
|
+
return this.wrap.get(layerId);
|
|
5550
|
+
}
|
|
5262
5551
|
// ─── Mask presets ───────────────────────────────────
|
|
5552
|
+
// A preset assigns `clipPath` wholesale, so a text layer clipping to its box
|
|
5553
|
+
// loses that clip the moment a mask lands on it. Putting it back is NOT wired
|
|
5554
|
+
// here: the preset manager announces `masks:changed` and the wrap manager
|
|
5555
|
+
// re-derives from that, which covers this facade, the manager called directly,
|
|
5556
|
+
// and `refreshAll()` on a restore — all with one mechanism.
|
|
5263
5557
|
applyMaskPreset(layerId, id) {
|
|
5264
5558
|
return this.maskPresets.apply(layerId, id);
|
|
5265
5559
|
}
|
|
@@ -5348,6 +5642,7 @@ var CanvasEditor = class {
|
|
|
5348
5642
|
this.history.dispose();
|
|
5349
5643
|
this.patterns.dispose();
|
|
5350
5644
|
this.curves.dispose();
|
|
5645
|
+
this.wrap.dispose();
|
|
5351
5646
|
this.events.removeAllListeners();
|
|
5352
5647
|
this.canvas.dispose();
|
|
5353
5648
|
}
|
|
@@ -5497,12 +5792,14 @@ var AnnotationOverlay = class {
|
|
|
5497
5792
|
TEXTURE_MASK_IDS,
|
|
5498
5793
|
TEXTURE_MASK_SIZE,
|
|
5499
5794
|
TextCurveManager,
|
|
5795
|
+
TextWrapManager,
|
|
5500
5796
|
TiledPatternObject,
|
|
5501
5797
|
UnitConverter,
|
|
5502
5798
|
applyAspectLock,
|
|
5503
5799
|
applyLayerShadow,
|
|
5504
5800
|
applyObjectSelectionStyle,
|
|
5505
5801
|
applySelectionStyle,
|
|
5802
|
+
applyTextWrapToObject,
|
|
5506
5803
|
buildCurveLinePaths,
|
|
5507
5804
|
buildCurvePathData,
|
|
5508
5805
|
clamp,
|
|
@@ -5530,7 +5827,9 @@ var AnnotationOverlay = class {
|
|
|
5530
5827
|
isTextureMaskId,
|
|
5531
5828
|
needsAbsoluteSpace,
|
|
5532
5829
|
normalizeTextCurve,
|
|
5830
|
+
preWrapWordSplit,
|
|
5533
5831
|
readLayerShadow,
|
|
5832
|
+
readTextWrap,
|
|
5534
5833
|
renderTextureMask,
|
|
5535
5834
|
resetTransform,
|
|
5536
5835
|
restoreLocks,
|