@overtone-art/canvas-editor-core 0.6.3 → 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 +434 -128
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +95 -139
- 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
|
|
@@ -1961,7 +2016,11 @@ function normalizeTextCurve(config) {
|
|
|
1961
2016
|
shape: config.shape ?? inferred,
|
|
1962
2017
|
arc,
|
|
1963
2018
|
wave,
|
|
1964
|
-
waveLength: clamp(
|
|
2019
|
+
waveLength: clamp(
|
|
2020
|
+
config.waveLength ?? DEFAULT_TEXT_CURVE.waveLength,
|
|
2021
|
+
MIN_WAVE_LENGTH,
|
|
2022
|
+
MAX_WAVE_LENGTH
|
|
2023
|
+
),
|
|
1965
2024
|
offset: clamp(config.offset ?? 0, -100, 100),
|
|
1966
2025
|
centerOffset: clamp(config.centerOffset ?? 0, -100, 100)
|
|
1967
2026
|
};
|
|
@@ -2335,8 +2394,268 @@ var TextCurveManager = class {
|
|
|
2335
2394
|
}
|
|
2336
2395
|
};
|
|
2337
2396
|
|
|
2338
|
-
// 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
|
|
2339
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");
|
|
2340
2659
|
|
|
2341
2660
|
// src/mask-presets/shapes.ts
|
|
2342
2661
|
var SHAPE_MASK_IDS = [
|
|
@@ -2569,6 +2888,7 @@ var MaskPresetManager = class {
|
|
|
2569
2888
|
object.dirty = true;
|
|
2570
2889
|
object.setCoords();
|
|
2571
2890
|
this.canvas.requestRenderAll();
|
|
2891
|
+
this.events.emit("masks:changed", { target: layerId });
|
|
2572
2892
|
this.events.emit("layer:modified", { layerId });
|
|
2573
2893
|
if (save) this.history.save();
|
|
2574
2894
|
return true;
|
|
@@ -2603,13 +2923,13 @@ var MaskPresetManager = class {
|
|
|
2603
2923
|
objectCaching: false
|
|
2604
2924
|
};
|
|
2605
2925
|
if (isShapeMaskId(id)) {
|
|
2606
|
-
return new
|
|
2926
|
+
return new import_fabric10.Path(shapeMaskPathData(id), {
|
|
2607
2927
|
...shared,
|
|
2608
2928
|
scaleX: width / SHAPE_MASK_BOX,
|
|
2609
2929
|
scaleY: height / SHAPE_MASK_BOX
|
|
2610
2930
|
});
|
|
2611
2931
|
}
|
|
2612
|
-
return new
|
|
2932
|
+
return new import_fabric10.FabricImage(renderTextureMask(id), {
|
|
2613
2933
|
...shared,
|
|
2614
2934
|
scaleX: width / TEXTURE_MASK_SIZE,
|
|
2615
2935
|
scaleY: height / TEXTURE_MASK_SIZE
|
|
@@ -2618,7 +2938,7 @@ var MaskPresetManager = class {
|
|
|
2618
2938
|
};
|
|
2619
2939
|
|
|
2620
2940
|
// src/shadow.ts
|
|
2621
|
-
var
|
|
2941
|
+
var import_fabric11 = require("fabric");
|
|
2622
2942
|
|
|
2623
2943
|
// src/utils/color.ts
|
|
2624
2944
|
var CSS_COLOR = /^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\([\d.%+\-\s,/]+\)|[a-z]+)$/i;
|
|
@@ -2656,7 +2976,7 @@ function applyLayerShadow(object, config) {
|
|
|
2656
2976
|
}
|
|
2657
2977
|
const color = isCssColor(next.color) ? next.color : DEFAULT_LAYER_SHADOW.color;
|
|
2658
2978
|
object.set({
|
|
2659
|
-
shadow: new
|
|
2979
|
+
shadow: new import_fabric11.Shadow({
|
|
2660
2980
|
color,
|
|
2661
2981
|
blur: Math.max(0, next.blur),
|
|
2662
2982
|
offsetX: next.offsetX,
|
|
@@ -2770,7 +3090,7 @@ var UnitConverter = class {
|
|
|
2770
3090
|
};
|
|
2771
3091
|
|
|
2772
3092
|
// src/serialization.ts
|
|
2773
|
-
var
|
|
3093
|
+
var import_fabric12 = require("fabric");
|
|
2774
3094
|
var VERSION = "2.0.0";
|
|
2775
3095
|
function serializeEditor(editor) {
|
|
2776
3096
|
return {
|
|
@@ -2806,7 +3126,7 @@ async function deserializeEditor(editor, state) {
|
|
|
2806
3126
|
}
|
|
2807
3127
|
const staged = await Promise.all(
|
|
2808
3128
|
state.layers.map(async (serialized) => {
|
|
2809
|
-
const fabricObject = (await
|
|
3129
|
+
const fabricObject = (await import_fabric12.util.enlivenObjects([serialized.fabricObject]))[0];
|
|
2810
3130
|
if (!fabricObject) {
|
|
2811
3131
|
const source = serialized.fabricObject.src;
|
|
2812
3132
|
if (typeof source === "string" && source.startsWith("blob:")) {
|
|
@@ -2817,7 +3137,7 @@ async function deserializeEditor(editor, state) {
|
|
|
2817
3137
|
return { serialized, fabricObject };
|
|
2818
3138
|
})
|
|
2819
3139
|
);
|
|
2820
|
-
const stagedBackground = state.backgroundImage ? (await
|
|
3140
|
+
const stagedBackground = state.backgroundImage ? (await import_fabric12.util.enlivenObjects([state.backgroundImage]))[0] : null;
|
|
2821
3141
|
if (state.backgroundImage && !stagedBackground) {
|
|
2822
3142
|
const source = state.backgroundImage.src;
|
|
2823
3143
|
if (typeof source === "string" && source.startsWith("blob:")) {
|
|
@@ -2843,6 +3163,7 @@ async function deserializeEditor(editor, state) {
|
|
|
2843
3163
|
for (const item of staged) {
|
|
2844
3164
|
restoreLayer(editor, item.serialized, item.fabricObject);
|
|
2845
3165
|
}
|
|
3166
|
+
editor.wrap.refreshAll();
|
|
2846
3167
|
editor.canvas.requestRenderAll();
|
|
2847
3168
|
}
|
|
2848
3169
|
function restoreLayer(editor, serialized, fabricObject) {
|
|
@@ -2869,7 +3190,7 @@ function restoreLayer(editor, serialized, fabricObject) {
|
|
|
2869
3190
|
}
|
|
2870
3191
|
|
|
2871
3192
|
// src/export.ts
|
|
2872
|
-
var
|
|
3193
|
+
var import_fabric13 = require("fabric");
|
|
2873
3194
|
|
|
2874
3195
|
// src/displacement.ts
|
|
2875
3196
|
var CHANNEL_INDEX = {
|
|
@@ -2963,7 +3284,7 @@ async function exportPNG(canvas, options = {}) {
|
|
|
2963
3284
|
}
|
|
2964
3285
|
async function exportIsolatedPNG(source, objects, options = {}) {
|
|
2965
3286
|
const element = source.lowerCanvasEl.ownerDocument.createElement("canvas");
|
|
2966
|
-
const canvas = new
|
|
3287
|
+
const canvas = new import_fabric13.StaticCanvas(element, {
|
|
2967
3288
|
width: options.width ?? source.getWidth(),
|
|
2968
3289
|
height: options.height ?? source.getHeight(),
|
|
2969
3290
|
backgroundColor: options.backgroundColor || void 0
|
|
@@ -2993,7 +3314,7 @@ async function exportPrintArea(source, area, options = {}) {
|
|
|
2993
3314
|
throw new Error("Print area does not overlap the canvas");
|
|
2994
3315
|
}
|
|
2995
3316
|
const element = source.lowerCanvasEl.ownerDocument.createElement("canvas");
|
|
2996
|
-
const canvas = new
|
|
3317
|
+
const canvas = new import_fabric13.StaticCanvas(element, { width, height });
|
|
2997
3318
|
try {
|
|
2998
3319
|
const clones = await Promise.all(source.getObjects().map((object) => object.clone()));
|
|
2999
3320
|
if (clones.length) canvas.add(...clones);
|
|
@@ -3433,7 +3754,7 @@ var ProjectManager = class {
|
|
|
3433
3754
|
};
|
|
3434
3755
|
|
|
3435
3756
|
// src/mask.ts
|
|
3436
|
-
var
|
|
3757
|
+
var import_fabric14 = require("fabric");
|
|
3437
3758
|
var MaskRefinementError = class extends Error {
|
|
3438
3759
|
constructor(code, message, cause) {
|
|
3439
3760
|
super(message);
|
|
@@ -3465,7 +3786,7 @@ var MaskController = class {
|
|
|
3465
3786
|
throw new Error("Mask dimensions must be positive integers");
|
|
3466
3787
|
}
|
|
3467
3788
|
const backing = this.makeCanvas(width, height);
|
|
3468
|
-
const image = new
|
|
3789
|
+
const image = new import_fabric14.FabricImage(backing, {
|
|
3469
3790
|
left: 0,
|
|
3470
3791
|
top: 0,
|
|
3471
3792
|
originX: "left",
|
|
@@ -3691,10 +4012,10 @@ var MaskController = class {
|
|
|
3691
4012
|
};
|
|
3692
4013
|
|
|
3693
4014
|
// src/masks/manager.ts
|
|
3694
|
-
var
|
|
4015
|
+
var import_fabric20 = require("fabric");
|
|
3695
4016
|
|
|
3696
4017
|
// src/masks/compose.ts
|
|
3697
|
-
var
|
|
4018
|
+
var import_fabric15 = require("fabric");
|
|
3698
4019
|
var MODE_OPERATION = {
|
|
3699
4020
|
add: "source-over",
|
|
3700
4021
|
subtract: "destination-out",
|
|
@@ -3704,7 +4025,7 @@ function neutralize(child) {
|
|
|
3704
4025
|
child.set({ opacity: 0, globalCompositeOperation: "source-over" });
|
|
3705
4026
|
}
|
|
3706
4027
|
function baseRect(box) {
|
|
3707
|
-
return new
|
|
4028
|
+
return new import_fabric15.Rect({
|
|
3708
4029
|
left: box.left,
|
|
3709
4030
|
top: box.top,
|
|
3710
4031
|
width: Math.max(1, box.width),
|
|
@@ -3731,7 +4052,7 @@ function composeMaskGroup(children, entries, options) {
|
|
|
3731
4052
|
});
|
|
3732
4053
|
const first = entries.find((entry) => entry.visible);
|
|
3733
4054
|
const withBase = first && first.mode !== "add" ? [baseRect(options.box), ...children] : [...children];
|
|
3734
|
-
return new
|
|
4055
|
+
return new import_fabric15.Group(withBase, {
|
|
3735
4056
|
absolutePositioned: options.absolute,
|
|
3736
4057
|
// Cached, so the children's compositing operations resolve against each
|
|
3737
4058
|
// other instead of against the page underneath the mask.
|
|
@@ -3745,76 +4066,7 @@ function needsAbsoluteSpace(entries) {
|
|
|
3745
4066
|
}
|
|
3746
4067
|
|
|
3747
4068
|
// src/masks/edit.ts
|
|
3748
|
-
var
|
|
3749
|
-
|
|
3750
|
-
// src/masks/space.ts
|
|
3751
|
-
var import_fabric14 = require("fabric");
|
|
3752
|
-
function matrixOf(object) {
|
|
3753
|
-
return object.calcTransformMatrix();
|
|
3754
|
-
}
|
|
3755
|
-
function applyMatrix(object, matrix) {
|
|
3756
|
-
const decomposed = import_fabric14.util.qrDecompose(matrix);
|
|
3757
|
-
object.set({
|
|
3758
|
-
flipX: false,
|
|
3759
|
-
flipY: false,
|
|
3760
|
-
originX: "center",
|
|
3761
|
-
originY: "center",
|
|
3762
|
-
left: decomposed.translateX,
|
|
3763
|
-
top: decomposed.translateY,
|
|
3764
|
-
scaleX: decomposed.scaleX,
|
|
3765
|
-
scaleY: decomposed.scaleY,
|
|
3766
|
-
angle: decomposed.angle,
|
|
3767
|
-
skewX: decomposed.skewX,
|
|
3768
|
-
skewY: 0
|
|
3769
|
-
});
|
|
3770
|
-
object.setCoords();
|
|
3771
|
-
}
|
|
3772
|
-
function toCanvasSpace(object, host) {
|
|
3773
|
-
applyMatrix(object, import_fabric14.util.multiplyTransformMatrices(matrixOf(host), matrixOf(object)));
|
|
3774
|
-
}
|
|
3775
|
-
function toHostSpace(object, host) {
|
|
3776
|
-
applyMatrix(
|
|
3777
|
-
object,
|
|
3778
|
-
import_fabric14.util.multiplyTransformMatrices(import_fabric14.util.invertTransform(matrixOf(host)), matrixOf(object))
|
|
3779
|
-
);
|
|
3780
|
-
}
|
|
3781
|
-
function relativeMatrix(object, host) {
|
|
3782
|
-
return import_fabric14.util.multiplyTransformMatrices(import_fabric14.util.invertTransform(matrixOf(host)), matrixOf(object));
|
|
3783
|
-
}
|
|
3784
|
-
function applyRelativeMatrix(object, host, rel) {
|
|
3785
|
-
applyMatrix(object, import_fabric14.util.multiplyTransformMatrices(matrixOf(host), rel));
|
|
3786
|
-
}
|
|
3787
|
-
function asObject(clip) {
|
|
3788
|
-
return clip;
|
|
3789
|
-
}
|
|
3790
|
-
function toMatrix(values) {
|
|
3791
|
-
if (!values || values.length !== 6 || values.some((value) => !Number.isFinite(value)))
|
|
3792
|
-
return null;
|
|
3793
|
-
return [values[0], values[1], values[2], values[3], values[4], values[5]];
|
|
3794
|
-
}
|
|
3795
|
-
function fitToBox(object, box, zoom = 1) {
|
|
3796
|
-
const width = Math.max(1, box.width) * zoom;
|
|
3797
|
-
const height = Math.max(1, box.height) * zoom;
|
|
3798
|
-
object.set({
|
|
3799
|
-
originX: "center",
|
|
3800
|
-
originY: "center",
|
|
3801
|
-
angle: 0,
|
|
3802
|
-
skewX: 0,
|
|
3803
|
-
skewY: 0,
|
|
3804
|
-
left: box.left + box.width / 2,
|
|
3805
|
-
top: box.top + box.height / 2,
|
|
3806
|
-
scaleX: width / Math.max(1, object.width ?? 1),
|
|
3807
|
-
scaleY: height / Math.max(1, object.height ?? 1)
|
|
3808
|
-
});
|
|
3809
|
-
object.setCoords();
|
|
3810
|
-
}
|
|
3811
|
-
function unwrapGroup(group) {
|
|
3812
|
-
const children = group.removeAll();
|
|
3813
|
-
for (const child of children) child.setCoords();
|
|
3814
|
-
return children;
|
|
3815
|
-
}
|
|
3816
|
-
|
|
3817
|
-
// src/masks/edit.ts
|
|
4069
|
+
var import_fabric16 = require("fabric");
|
|
3818
4070
|
var MaskEditController = class {
|
|
3819
4071
|
constructor(canvas, onCommit) {
|
|
3820
4072
|
this.canvas = canvas;
|
|
@@ -3895,8 +4147,8 @@ var MaskEditController = class {
|
|
|
3895
4147
|
if (!this.handle || !this.child || !this.group) return;
|
|
3896
4148
|
applyMatrix(
|
|
3897
4149
|
this.child,
|
|
3898
|
-
|
|
3899
|
-
|
|
4150
|
+
import_fabric16.util.multiplyTransformMatrices(
|
|
4151
|
+
import_fabric16.util.invertTransform(matrixOf(this.group)),
|
|
3900
4152
|
matrixOf(this.handle)
|
|
3901
4153
|
)
|
|
3902
4154
|
);
|
|
@@ -3907,15 +4159,15 @@ var MaskEditController = class {
|
|
|
3907
4159
|
};
|
|
3908
4160
|
|
|
3909
4161
|
// src/masks/store.ts
|
|
3910
|
-
var
|
|
4162
|
+
var import_fabric19 = require("fabric");
|
|
3911
4163
|
|
|
3912
4164
|
// src/masks/host.ts
|
|
3913
|
-
var
|
|
4165
|
+
var import_fabric17 = require("fabric");
|
|
3914
4166
|
function findCanvasHost(layers) {
|
|
3915
4167
|
return layers.getAll().find((layer) => layer.meta.canvasMask);
|
|
3916
4168
|
}
|
|
3917
4169
|
function createCanvasHost(canvas, layers) {
|
|
3918
|
-
const rect = new
|
|
4170
|
+
const rect = new import_fabric17.Rect({
|
|
3919
4171
|
left: 0,
|
|
3920
4172
|
top: 0,
|
|
3921
4173
|
width: canvas.getWidth(),
|
|
@@ -3964,9 +4216,9 @@ function hostBoxOf(canvas, host, absolute) {
|
|
|
3964
4216
|
}
|
|
3965
4217
|
|
|
3966
4218
|
// src/masks/install.ts
|
|
3967
|
-
var
|
|
4219
|
+
var import_fabric18 = require("fabric");
|
|
3968
4220
|
function convertSpace(host, sources, absolute) {
|
|
3969
|
-
const wasAbsolute = host.clipPath instanceof
|
|
4221
|
+
const wasAbsolute = host.clipPath instanceof import_fabric18.Group ? host.clipPath.absolutePositioned : absolute;
|
|
3970
4222
|
if (absolute === wasAbsolute) return;
|
|
3971
4223
|
for (const source of sources) {
|
|
3972
4224
|
if (absolute) toCanvasSpace(source, host);
|
|
@@ -4046,6 +4298,24 @@ var MaskStackStore = class {
|
|
|
4046
4298
|
this.pinning = false;
|
|
4047
4299
|
}
|
|
4048
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
|
+
}
|
|
4049
4319
|
/**
|
|
4050
4320
|
* Take the current geometry back out of the composed clip, entry-aligned.
|
|
4051
4321
|
*
|
|
@@ -4058,7 +4328,13 @@ var MaskStackStore = class {
|
|
|
4058
4328
|
const clip = host.clipPath;
|
|
4059
4329
|
if (!clip) return [];
|
|
4060
4330
|
const entries = this.list(target);
|
|
4061
|
-
|
|
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
|
+
}
|
|
4062
4338
|
const children = unwrapGroup(clip);
|
|
4063
4339
|
const extra = children.length - entries.length;
|
|
4064
4340
|
return extra > 0 ? children.slice(extra) : children;
|
|
@@ -4158,7 +4434,7 @@ var LayerMaskManager = class extends MaskStackStore {
|
|
|
4158
4434
|
if (index === -1) return false;
|
|
4159
4435
|
this.endEdit(false);
|
|
4160
4436
|
this.commit(target, host, [...entries], this.unwrap(target, host), false, true);
|
|
4161
|
-
const group = host.clipPath instanceof
|
|
4437
|
+
const group = host.clipPath instanceof import_fabric20.Group ? host.clipPath : null;
|
|
4162
4438
|
if (!group) return false;
|
|
4163
4439
|
const children = group.getObjects();
|
|
4164
4440
|
const child = children[children.length - entries.length + index];
|
|
@@ -4339,6 +4615,9 @@ var LayerMaskManager = class extends MaskStackStore {
|
|
|
4339
4615
|
if (resolved === layerId) return null;
|
|
4340
4616
|
const object = layer.fabricObject;
|
|
4341
4617
|
if (!object.fill && object.type !== "image") object.set({ fill: "#000000" });
|
|
4618
|
+
const stackHost = this.host(resolved);
|
|
4619
|
+
const absolute = resolved === CANVAS_MASK_TARGET || needsAbsoluteSpace(this.list(resolved));
|
|
4620
|
+
if (!absolute && stackHost) toHostSpace(object, stackHost);
|
|
4342
4621
|
this.history.beginTransaction();
|
|
4343
4622
|
try {
|
|
4344
4623
|
if (this.canvas.getActiveObject() === object) this.canvas.discardActiveObject();
|
|
@@ -4386,6 +4665,7 @@ var LayerMaskManager = class extends MaskStackStore {
|
|
|
4386
4665
|
const clip = layer?.fabricObject.clipPath;
|
|
4387
4666
|
if (!layer || !clip || layer.meta.pattern) return null;
|
|
4388
4667
|
if ((layer.meta.maskStack ?? []).length > 0) return null;
|
|
4668
|
+
if (this.holdsBoxClip(layer.meta)) return null;
|
|
4389
4669
|
const entry = {
|
|
4390
4670
|
...DEFAULT_ENTRY,
|
|
4391
4671
|
id: generateId(),
|
|
@@ -4438,6 +4718,7 @@ var CanvasEditor = class {
|
|
|
4438
4718
|
crop;
|
|
4439
4719
|
patterns;
|
|
4440
4720
|
curves;
|
|
4721
|
+
wrap;
|
|
4441
4722
|
maskPresets;
|
|
4442
4723
|
/** Stacked boolean masks, per layer and for the design as a whole. */
|
|
4443
4724
|
layerMasks;
|
|
@@ -4465,7 +4746,7 @@ var CanvasEditor = class {
|
|
|
4465
4746
|
const widthPx = this.units.toPixels(config.width);
|
|
4466
4747
|
const heightPx = this.units.toPixels(config.height);
|
|
4467
4748
|
this.designBackground = config.backgroundColor ?? "#ffffff";
|
|
4468
|
-
this.canvas = new
|
|
4749
|
+
this.canvas = new import_fabric21.Canvas(canvasElement, {
|
|
4469
4750
|
width: widthPx,
|
|
4470
4751
|
height: heightPx,
|
|
4471
4752
|
backgroundColor: this.designBackground,
|
|
@@ -4488,6 +4769,7 @@ var CanvasEditor = class {
|
|
|
4488
4769
|
this.crop = new CropController(this.canvas, this.history, this.events);
|
|
4489
4770
|
this.patterns = new PatternManager(this.canvas, this.layers, this.history, this.events);
|
|
4490
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);
|
|
4491
4773
|
this.maskPresets = new MaskPresetManager(this.canvas, this.layers, this.history, this.events);
|
|
4492
4774
|
this.layerMasks = new LayerMaskManager(this.canvas, this.layers, this.history, this.events);
|
|
4493
4775
|
this.setupCanvasEvents();
|
|
@@ -4499,7 +4781,7 @@ var CanvasEditor = class {
|
|
|
4499
4781
|
// ─── Layer Operations ────────────────────────────────
|
|
4500
4782
|
async addImage(url, options) {
|
|
4501
4783
|
try {
|
|
4502
|
-
const img = await
|
|
4784
|
+
const img = await import_fabric21.FabricImage.fromURL(
|
|
4503
4785
|
url,
|
|
4504
4786
|
{},
|
|
4505
4787
|
{ originX: "left", originY: "top", ...options }
|
|
@@ -4525,7 +4807,7 @@ var CanvasEditor = class {
|
|
|
4525
4807
|
if (this.crop.activeLayerId() === layerId) this.crop.cancel();
|
|
4526
4808
|
const previous = layer.fabricObject;
|
|
4527
4809
|
try {
|
|
4528
|
-
const replacement = await
|
|
4810
|
+
const replacement = await import_fabric21.FabricImage.fromURL(url, {}, { originX: "left", originY: "top" });
|
|
4529
4811
|
replacement.set({
|
|
4530
4812
|
left: previous.left,
|
|
4531
4813
|
top: previous.top,
|
|
@@ -4554,7 +4836,7 @@ var CanvasEditor = class {
|
|
|
4554
4836
|
}
|
|
4555
4837
|
}
|
|
4556
4838
|
addText(text, options) {
|
|
4557
|
-
const textbox = new
|
|
4839
|
+
const textbox = new import_fabric21.Textbox(text, {
|
|
4558
4840
|
fontSize: 32,
|
|
4559
4841
|
fontFamily: "Arial",
|
|
4560
4842
|
fill: "#000000",
|
|
@@ -4685,10 +4967,10 @@ var CanvasEditor = class {
|
|
|
4685
4967
|
const next = { ...previous, ...adjustments };
|
|
4686
4968
|
const clampAdjustment = (value, min = -1) => clamp(value ?? 0, min, 1);
|
|
4687
4969
|
image.filters = [
|
|
4688
|
-
new
|
|
4689
|
-
new
|
|
4690
|
-
new
|
|
4691
|
-
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) })
|
|
4692
4974
|
];
|
|
4693
4975
|
layer.meta.imageAdjustments = next;
|
|
4694
4976
|
image.applyFilters();
|
|
@@ -4729,7 +5011,7 @@ var CanvasEditor = class {
|
|
|
4729
5011
|
this.layers.detach(layer.id);
|
|
4730
5012
|
}
|
|
4731
5013
|
}
|
|
4732
|
-
const group = new
|
|
5014
|
+
const group = new import_fabric21.Group(objects);
|
|
4733
5015
|
const grouped = this.layers.add("group", group, name);
|
|
4734
5016
|
this.layers.adoptChildren(grouped, children);
|
|
4735
5017
|
this.layers.reorder(grouped.id, insertIndex);
|
|
@@ -4791,7 +5073,7 @@ var CanvasEditor = class {
|
|
|
4791
5073
|
const layer = this.layers.get(id);
|
|
4792
5074
|
if (!layer) throw new Error(`Layer not found: ${id}`);
|
|
4793
5075
|
try {
|
|
4794
|
-
if (options.resolution === "source" && layer.fabricObject instanceof
|
|
5076
|
+
if (options.resolution === "source" && layer.fabricObject instanceof import_fabric21.FabricImage) {
|
|
4795
5077
|
const image = await layer.fabricObject.clone();
|
|
4796
5078
|
image.set({
|
|
4797
5079
|
left: 0,
|
|
@@ -5067,7 +5349,7 @@ var CanvasEditor = class {
|
|
|
5067
5349
|
return;
|
|
5068
5350
|
}
|
|
5069
5351
|
try {
|
|
5070
|
-
const image = await
|
|
5352
|
+
const image = await import_fabric21.FabricImage.fromURL(
|
|
5071
5353
|
url,
|
|
5072
5354
|
{ crossOrigin: options.crossOrigin ?? null, signal: options.signal },
|
|
5073
5355
|
{ originX: "left", originY: "top" }
|
|
@@ -5244,15 +5526,34 @@ var CanvasEditor = class {
|
|
|
5244
5526
|
}
|
|
5245
5527
|
// ─── Text curve ─────────────────────────────────────
|
|
5246
5528
|
applyTextCurve(layerId, config) {
|
|
5247
|
-
|
|
5529
|
+
const applied = this.curves.apply(layerId, config);
|
|
5530
|
+
if (applied) this.wrap.refresh(layerId);
|
|
5531
|
+
return applied;
|
|
5248
5532
|
}
|
|
5249
5533
|
clearTextCurve(layerId) {
|
|
5250
|
-
|
|
5534
|
+
const cleared = this.curves.clear(layerId);
|
|
5535
|
+
if (cleared) this.wrap.refresh(layerId);
|
|
5536
|
+
return cleared;
|
|
5251
5537
|
}
|
|
5252
5538
|
getTextCurve(layerId) {
|
|
5253
5539
|
return this.curves.get(layerId);
|
|
5254
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
|
+
}
|
|
5255
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.
|
|
5256
5557
|
applyMaskPreset(layerId, id) {
|
|
5257
5558
|
return this.maskPresets.apply(layerId, id);
|
|
5258
5559
|
}
|
|
@@ -5341,6 +5642,7 @@ var CanvasEditor = class {
|
|
|
5341
5642
|
this.history.dispose();
|
|
5342
5643
|
this.patterns.dispose();
|
|
5343
5644
|
this.curves.dispose();
|
|
5645
|
+
this.wrap.dispose();
|
|
5344
5646
|
this.events.removeAllListeners();
|
|
5345
5647
|
this.canvas.dispose();
|
|
5346
5648
|
}
|
|
@@ -5490,12 +5792,14 @@ var AnnotationOverlay = class {
|
|
|
5490
5792
|
TEXTURE_MASK_IDS,
|
|
5491
5793
|
TEXTURE_MASK_SIZE,
|
|
5492
5794
|
TextCurveManager,
|
|
5795
|
+
TextWrapManager,
|
|
5493
5796
|
TiledPatternObject,
|
|
5494
5797
|
UnitConverter,
|
|
5495
5798
|
applyAspectLock,
|
|
5496
5799
|
applyLayerShadow,
|
|
5497
5800
|
applyObjectSelectionStyle,
|
|
5498
5801
|
applySelectionStyle,
|
|
5802
|
+
applyTextWrapToObject,
|
|
5499
5803
|
buildCurveLinePaths,
|
|
5500
5804
|
buildCurvePathData,
|
|
5501
5805
|
clamp,
|
|
@@ -5523,7 +5827,9 @@ var AnnotationOverlay = class {
|
|
|
5523
5827
|
isTextureMaskId,
|
|
5524
5828
|
needsAbsoluteSpace,
|
|
5525
5829
|
normalizeTextCurve,
|
|
5830
|
+
preWrapWordSplit,
|
|
5526
5831
|
readLayerShadow,
|
|
5832
|
+
readTextWrap,
|
|
5527
5833
|
renderTextureMask,
|
|
5528
5834
|
resetTransform,
|
|
5529
5835
|
restoreLocks,
|