@overtone-art/canvas-editor-core 0.6.1 → 0.6.3
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/index.d.mts +64 -4
- package/dist/index.d.ts +64 -4
- package/dist/index.global.js +52 -52
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +279 -73
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +229 -24
- package/dist/index.mjs.map +1 -1
- package/dist/node.d.mts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/{types-LM7pOkkj.d.mts → types-DSu2jbiV.d.mts} +8 -0
- package/dist/{types-LM7pOkkj.d.ts → types-DSu2jbiV.d.ts} +8 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -71,6 +71,7 @@ __export(index_exports, {
|
|
|
71
71
|
exportPNG: () => exportPNG,
|
|
72
72
|
exportPrintArea: () => exportPrintArea,
|
|
73
73
|
exportSVG: () => exportSVG,
|
|
74
|
+
fitTextWidth: () => fitTextWidth,
|
|
74
75
|
fitToBox: () => fitToBox,
|
|
75
76
|
generateId: () => generateId,
|
|
76
77
|
isCssColor: () => isCssColor,
|
|
@@ -96,7 +97,7 @@ __export(index_exports, {
|
|
|
96
97
|
module.exports = __toCommonJS(index_exports);
|
|
97
98
|
|
|
98
99
|
// src/editor.ts
|
|
99
|
-
var
|
|
100
|
+
var import_fabric20 = require("fabric");
|
|
100
101
|
|
|
101
102
|
// src/events.ts
|
|
102
103
|
var EventEmitter = class {
|
|
@@ -294,6 +295,19 @@ var LayerManager = class {
|
|
|
294
295
|
this.layers = this.layers.filter((l) => l.id !== group.id);
|
|
295
296
|
this.syncZOrder();
|
|
296
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Move existing layer records under a group, and announce the new tree.
|
|
300
|
+
* Assigning `group.children` directly emits nothing, and the reorder that
|
|
301
|
+
* follows a grouping is a no-op whenever the group already sits where it
|
|
302
|
+
* belongs — leaving `layers:changed` describing a childless group, so a panel
|
|
303
|
+
* built on it renders the group with nothing inside.
|
|
304
|
+
*/
|
|
305
|
+
adoptChildren(group, children) {
|
|
306
|
+
for (const child of children) child.parentId = group.id;
|
|
307
|
+
group.children = children;
|
|
308
|
+
this.emitChanged();
|
|
309
|
+
this.onPropertyChanged?.();
|
|
310
|
+
}
|
|
297
311
|
/**
|
|
298
312
|
* Re-attach child records to an enlivened group. `data[i]` describes
|
|
299
313
|
* `group._objects[i]` — the order fabric serializes and restores them in. A
|
|
@@ -1212,10 +1226,63 @@ var CropController = class {
|
|
|
1212
1226
|
var STROKE2 = "#22c55e";
|
|
1213
1227
|
|
|
1214
1228
|
// src/pattern/pattern-manager.ts
|
|
1215
|
-
var
|
|
1229
|
+
var import_fabric6 = require("fabric");
|
|
1216
1230
|
|
|
1217
1231
|
// src/pattern/tiled-pattern-object.ts
|
|
1232
|
+
var import_fabric5 = require("fabric");
|
|
1233
|
+
|
|
1234
|
+
// src/text-fit.ts
|
|
1218
1235
|
var import_fabric4 = require("fabric");
|
|
1236
|
+
var TEXT_FIT_SLACK = 0.5;
|
|
1237
|
+
var MEASURE_WIDTH = 1e5;
|
|
1238
|
+
function unwrappedWidth(text) {
|
|
1239
|
+
const authored = text.width;
|
|
1240
|
+
try {
|
|
1241
|
+
text.set({ width: MEASURE_WIDTH });
|
|
1242
|
+
text.initDimensions?.();
|
|
1243
|
+
const measured = text.calcTextWidth?.() ?? authored;
|
|
1244
|
+
return Number.isFinite(measured) && measured > 0 ? measured : authored;
|
|
1245
|
+
} finally {
|
|
1246
|
+
text.set({ width: authored });
|
|
1247
|
+
text.initDimensions?.();
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
function asText(object) {
|
|
1251
|
+
if (!object) return null;
|
|
1252
|
+
const text = object;
|
|
1253
|
+
return typeof text.text === "string" && typeof text.calcTextWidth === "function" ? text : null;
|
|
1254
|
+
}
|
|
1255
|
+
function textInk(text) {
|
|
1256
|
+
const boxWidth = Math.max(0, text.width ?? 0);
|
|
1257
|
+
const measured = text.calcTextWidth?.() ?? boxWidth;
|
|
1258
|
+
const width = Math.max(1, Math.min(boxWidth, Number.isFinite(measured) ? measured : boxWidth));
|
|
1259
|
+
const slack = (boxWidth - width) / 2;
|
|
1260
|
+
const align = text.textAlign ?? "left";
|
|
1261
|
+
if (align.includes("center")) return { width, dx: 0 };
|
|
1262
|
+
const flip = text.flipX ? -1 : 1;
|
|
1263
|
+
return { width, dx: flip * (align.includes("right") ? slack : -slack) };
|
|
1264
|
+
}
|
|
1265
|
+
function fitTextWidth(object) {
|
|
1266
|
+
const text = asText(object);
|
|
1267
|
+
if (!text || text.path) return false;
|
|
1268
|
+
const before = textInk(text);
|
|
1269
|
+
const fitted = unwrappedWidth(text) + TEXT_FIT_SLACK;
|
|
1270
|
+
if (Math.abs(fitted - text.width) < TEXT_FIT_SLACK) return false;
|
|
1271
|
+
const centre = text.getCenterPoint();
|
|
1272
|
+
text.set({ width: fitted });
|
|
1273
|
+
text.initDimensions?.();
|
|
1274
|
+
const after = textInk(text);
|
|
1275
|
+
const shift = (before.dx - after.dx) * (text.scaleX ?? 1);
|
|
1276
|
+
const radians = (text.angle ?? 0) * Math.PI / 180;
|
|
1277
|
+
const moved = new import_fabric4.Point(
|
|
1278
|
+
centre.x + shift * Math.cos(radians),
|
|
1279
|
+
centre.y + shift * Math.sin(radians)
|
|
1280
|
+
);
|
|
1281
|
+
text.setPositionByOrigin(moved, "center", "center");
|
|
1282
|
+
text.setCoords();
|
|
1283
|
+
text.dirty = true;
|
|
1284
|
+
return true;
|
|
1285
|
+
}
|
|
1219
1286
|
|
|
1220
1287
|
// src/pattern/tile-geometry.ts
|
|
1221
1288
|
var MAX_TILES_PER_AXIS = 200;
|
|
@@ -1228,13 +1295,13 @@ function computeTilePositions(config, targetW, targetH, baseW, baseH, origin) {
|
|
|
1228
1295
|
const tileH = Math.max(minTile, baseH * (1 + config.verticalSpacing / 100));
|
|
1229
1296
|
const cols = Math.ceil(span / tileW) + 2;
|
|
1230
1297
|
const rows = Math.ceil(span / tileH) + 2;
|
|
1231
|
-
const
|
|
1232
|
-
const
|
|
1298
|
+
const [colFrom, colTo] = axisRange(config.horizontalLimit, Math.ceil(cols / 2));
|
|
1299
|
+
const [rowFrom, rowTo] = axisRange(config.verticalLimit, Math.ceil(rows / 2));
|
|
1233
1300
|
const shiftX = tileW * (clampOffset(config.offsetX) / 100);
|
|
1234
1301
|
const shiftY = tileH * (clampOffset(config.offsetY) / 100);
|
|
1235
1302
|
const placements = [];
|
|
1236
|
-
for (let j =
|
|
1237
|
-
for (let i =
|
|
1303
|
+
for (let j = rowFrom; j <= rowTo; j++) {
|
|
1304
|
+
for (let i = colFrom; i <= colTo; i++) {
|
|
1238
1305
|
let x = i * tileW + shiftX;
|
|
1239
1306
|
let y = j * tileH + shiftY;
|
|
1240
1307
|
if (config.mode === "brick-horizontal" && mod2(j) === 1) {
|
|
@@ -1264,6 +1331,12 @@ function drawTiles(ctx, img, config, targetW, targetH, baseW, baseH, origin, dra
|
|
|
1264
1331
|
}
|
|
1265
1332
|
ctx.restore();
|
|
1266
1333
|
}
|
|
1334
|
+
function axisRange(limit, half) {
|
|
1335
|
+
const count = Number.isFinite(limit) ? Math.floor(limit) : 0;
|
|
1336
|
+
if (!count || count <= 0) return [-half, half];
|
|
1337
|
+
const from = -Math.floor((count - 1) / 2);
|
|
1338
|
+
return [from, from + count - 1];
|
|
1339
|
+
}
|
|
1267
1340
|
function cornerRadius(origin, w, h) {
|
|
1268
1341
|
const dx = Math.max(Math.abs(origin.x), Math.abs(w - origin.x));
|
|
1269
1342
|
const dy = Math.max(Math.abs(origin.y), Math.abs(h - origin.y));
|
|
@@ -1282,7 +1355,7 @@ var MAX_SNAPSHOT_PIXELS = 16e6;
|
|
|
1282
1355
|
var MAX_SNAPSHOT_SCALE = 8;
|
|
1283
1356
|
var SNAPSHOT_SHRINK_FACTOR = 2;
|
|
1284
1357
|
var TILE_BLEED_DEVICE_PX = 2;
|
|
1285
|
-
var TiledPatternObject = class _TiledPatternObject extends
|
|
1358
|
+
var TiledPatternObject = class _TiledPatternObject extends import_fabric5.FabricObject {
|
|
1286
1359
|
static type = "TiledPattern";
|
|
1287
1360
|
/** The layer's real object. Never rendered directly (it is kept at opacity 0). */
|
|
1288
1361
|
source;
|
|
@@ -1335,7 +1408,7 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric4.Fabric
|
|
|
1335
1408
|
*/
|
|
1336
1409
|
syncBox() {
|
|
1337
1410
|
const { tileW, tileH } = this.baseTile();
|
|
1338
|
-
const origin = this.source.getCenterPoint();
|
|
1411
|
+
const origin = this.source.getCenterPoint().add(this.inkOffset());
|
|
1339
1412
|
const anchor = this.anchorFromOrigin(origin, tileW, tileH, this.config.angle);
|
|
1340
1413
|
this.set({
|
|
1341
1414
|
left: anchor.x,
|
|
@@ -1365,7 +1438,8 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric4.Fabric
|
|
|
1365
1438
|
const { tileW, tileH } = this.liveTile();
|
|
1366
1439
|
const centre = this.getCenterPoint();
|
|
1367
1440
|
const shift = this.shiftVector(tileW, tileH, this.liveAngle());
|
|
1368
|
-
|
|
1441
|
+
const ink = this.inkOffset();
|
|
1442
|
+
return new import_fabric5.Point(centre.x - shift.x - ink.x, centre.y - shift.y - ink.y);
|
|
1369
1443
|
}
|
|
1370
1444
|
_render(ctx) {
|
|
1371
1445
|
const { width, height } = this.area;
|
|
@@ -1380,8 +1454,7 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric4.Fabric
|
|
|
1380
1454
|
ctx.scale(1 / (this.scaleX || 1), 1 / (this.scaleY || 1));
|
|
1381
1455
|
ctx.rotate(-angle * Math.PI / 180);
|
|
1382
1456
|
ctx.translate(-centre.x, -centre.y);
|
|
1383
|
-
const
|
|
1384
|
-
const origin = { x: centre.x - shift.x, y: centre.y - shift.y };
|
|
1457
|
+
const origin = { x: centre.x, y: centre.y };
|
|
1385
1458
|
const config = { ...this.config, angle, offsetX: 0, offsetY: 0 };
|
|
1386
1459
|
drawTiles(
|
|
1387
1460
|
ctx,
|
|
@@ -1410,7 +1483,6 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric4.Fabric
|
|
|
1410
1483
|
if (!snapshot) return [];
|
|
1411
1484
|
const centre = this.getCenterPoint();
|
|
1412
1485
|
const angle = this.liveAngle();
|
|
1413
|
-
const shift = this.shiftVector(tileW, tileH, angle);
|
|
1414
1486
|
drawTiles(
|
|
1415
1487
|
ctx,
|
|
1416
1488
|
snapshot,
|
|
@@ -1419,7 +1491,7 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric4.Fabric
|
|
|
1419
1491
|
height,
|
|
1420
1492
|
tileW,
|
|
1421
1493
|
tileH,
|
|
1422
|
-
{ x: centre.x
|
|
1494
|
+
{ x: centre.x, y: centre.y },
|
|
1423
1495
|
// The fallback raster is built at 1:1, so a device pixel is a canvas unit.
|
|
1424
1496
|
this.drawSize(tileW, tileH, TILE_BLEED_DEVICE_PX)
|
|
1425
1497
|
);
|
|
@@ -1462,7 +1534,7 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric4.Fabric
|
|
|
1462
1534
|
return Promise.resolve(copy);
|
|
1463
1535
|
}
|
|
1464
1536
|
/**
|
|
1465
|
-
* The source's tile footprint:
|
|
1537
|
+
* The source's tile footprint: the art it paints, minus a stroke it reserves
|
|
1466
1538
|
* room for but never paints.
|
|
1467
1539
|
*
|
|
1468
1540
|
* fabric keeps `strokeWidth` inside an object's box whether or not a `stroke`
|
|
@@ -1470,11 +1542,18 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric4.Fabric
|
|
|
1470
1542
|
* stroke. Stepping the grid by that box puts a transparent seam between every
|
|
1471
1543
|
* pair of neighbours at zero spacing: the artwork is a pixel narrower than the
|
|
1472
1544
|
* box it is stepped by.
|
|
1545
|
+
*
|
|
1546
|
+
* Text is the same problem an order of magnitude larger — see {@link textInk}.
|
|
1473
1547
|
*/
|
|
1474
1548
|
sourceBox() {
|
|
1549
|
+
const text = asText(this.source);
|
|
1550
|
+
const paints = paintsStroke(this.source);
|
|
1475
1551
|
const rect = this.source.getBoundingRect();
|
|
1476
|
-
if (
|
|
1477
|
-
const
|
|
1552
|
+
if (paints && !text) return { width: rect.width, height: rect.height };
|
|
1553
|
+
const options = {};
|
|
1554
|
+
if (!paints) options.strokeWidth = 0;
|
|
1555
|
+
if (text) options.width = textInk(text).width;
|
|
1556
|
+
const dims = this.source._getTransformedDimensions(options);
|
|
1478
1557
|
const radians = (this.source.angle ?? 0) * Math.PI / 180;
|
|
1479
1558
|
const cos = Math.abs(Math.cos(radians));
|
|
1480
1559
|
const sin = Math.abs(Math.sin(radians));
|
|
@@ -1483,6 +1562,22 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric4.Fabric
|
|
|
1483
1562
|
height: dims.x * sin + dims.y * cos
|
|
1484
1563
|
};
|
|
1485
1564
|
}
|
|
1565
|
+
/**
|
|
1566
|
+
* Vector from the source's centre to the centre of the art it paints.
|
|
1567
|
+
*
|
|
1568
|
+
* Zero for everything except text that does not fill its box: the tile has to
|
|
1569
|
+
* sit on the glyphs, or the frame the user drags frames empty space and the
|
|
1570
|
+
* grid registers off the run by half the padding.
|
|
1571
|
+
*/
|
|
1572
|
+
inkOffset() {
|
|
1573
|
+
const text = asText(this.source);
|
|
1574
|
+
if (!text) return new import_fabric5.Point(0, 0);
|
|
1575
|
+
const { dx } = textInk(text);
|
|
1576
|
+
if (!dx) return new import_fabric5.Point(0, 0);
|
|
1577
|
+
const scaled = dx * (this.source.scaleX ?? 1);
|
|
1578
|
+
const radians = (this.source.angle ?? 0) * Math.PI / 180;
|
|
1579
|
+
return new import_fabric5.Point(scaled * Math.cos(radians), scaled * Math.sin(radians));
|
|
1580
|
+
}
|
|
1486
1581
|
/** The source's on-canvas width, before the tile scale. */
|
|
1487
1582
|
baseW() {
|
|
1488
1583
|
return Math.max(1, this.sourceBox().width);
|
|
@@ -1542,11 +1637,11 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric4.Fabric
|
|
|
1542
1637
|
const radians = angle * Math.PI / 180;
|
|
1543
1638
|
const cos = Math.cos(radians);
|
|
1544
1639
|
const sin = Math.sin(radians);
|
|
1545
|
-
return new
|
|
1640
|
+
return new import_fabric5.Point(dx * cos - dy * sin, dx * sin + dy * cos);
|
|
1546
1641
|
}
|
|
1547
1642
|
anchorFromOrigin(origin, tileW, tileH, angle) {
|
|
1548
1643
|
const shift = this.shiftVector(tileW, tileH, angle);
|
|
1549
|
-
return new
|
|
1644
|
+
return new import_fabric5.Point(origin.x + shift.x, origin.y + shift.y);
|
|
1550
1645
|
}
|
|
1551
1646
|
/**
|
|
1552
1647
|
* Snapshot the source at (at least) `scale`, reusing the cached one while it
|
|
@@ -1559,7 +1654,15 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric4.Fabric
|
|
|
1559
1654
|
const source = this.source;
|
|
1560
1655
|
const opacity = source.opacity;
|
|
1561
1656
|
source.opacity = 1;
|
|
1657
|
+
const text = asText(source);
|
|
1658
|
+
const authoredWidth = text?.width;
|
|
1659
|
+
const fitted = text ? textInk(text).width + TEXT_FIT_SLACK : 0;
|
|
1660
|
+
const hug = text !== null && authoredWidth !== void 0 && fitted < authoredWidth;
|
|
1562
1661
|
try {
|
|
1662
|
+
if (hug && text) {
|
|
1663
|
+
text.set({ width: fitted });
|
|
1664
|
+
text.initDimensions?.();
|
|
1665
|
+
}
|
|
1563
1666
|
const el = source.toCanvasElement({ multiplier: wanted, enableRetinaScaling: false });
|
|
1564
1667
|
if (!el.width || !el.height) return null;
|
|
1565
1668
|
this.snapshotEl = el;
|
|
@@ -1568,6 +1671,10 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric4.Fabric
|
|
|
1568
1671
|
return this.snapshotEl;
|
|
1569
1672
|
} finally {
|
|
1570
1673
|
source.opacity = opacity;
|
|
1674
|
+
if (hug && text && authoredWidth !== void 0) {
|
|
1675
|
+
text.set({ width: authoredWidth });
|
|
1676
|
+
text.initDimensions?.();
|
|
1677
|
+
}
|
|
1571
1678
|
source.dirty = false;
|
|
1572
1679
|
}
|
|
1573
1680
|
return this.snapshotEl;
|
|
@@ -1806,7 +1913,7 @@ async function unbakeLegacyLayer(layer, state) {
|
|
|
1806
1913
|
cropY: state.original.cropY,
|
|
1807
1914
|
angle: state.original.angle
|
|
1808
1915
|
});
|
|
1809
|
-
image.clipPath = state.originalClip ? (await
|
|
1916
|
+
image.clipPath = state.originalClip ? (await import_fabric6.util.enlivenObjects([state.originalClip]))[0] : void 0;
|
|
1810
1917
|
restoreLocks(image, state.originalLocks);
|
|
1811
1918
|
image.setCoords();
|
|
1812
1919
|
}
|
|
@@ -1824,7 +1931,7 @@ function restoreLocks(obj, locks) {
|
|
|
1824
1931
|
}
|
|
1825
1932
|
|
|
1826
1933
|
// src/text-curve.ts
|
|
1827
|
-
var
|
|
1934
|
+
var import_fabric7 = require("fabric");
|
|
1828
1935
|
|
|
1829
1936
|
// src/text-curve-geometry.ts
|
|
1830
1937
|
var DEFAULT_TEXT_CURVE = {
|
|
@@ -1944,17 +2051,55 @@ function buildCurvePathData(config, width, fontSize) {
|
|
|
1944
2051
|
}
|
|
1945
2052
|
|
|
1946
2053
|
// src/text-curve.ts
|
|
1947
|
-
var
|
|
2054
|
+
var MEASURE_WIDTH2 = 1e5;
|
|
1948
2055
|
var PATH_SLACK = 0.06;
|
|
1949
2056
|
var FALLBACK_LINE_HEIGHT = 1.16;
|
|
1950
2057
|
var patches = /* @__PURE__ */ new WeakMap();
|
|
2058
|
+
var dimensionPatches = /* @__PURE__ */ new WeakMap();
|
|
2059
|
+
var authoredCaching = /* @__PURE__ */ new WeakMap();
|
|
2060
|
+
var GLYPH_HALF_HEIGHT = 0.7;
|
|
2061
|
+
function curvedInkSize(text) {
|
|
2062
|
+
const lineCount = text._textLines?.length ?? 0;
|
|
2063
|
+
for (let index = 0; index < lineCount; index += 1) text.getLineWidth?.(index);
|
|
2064
|
+
const lines = text.__charBounds;
|
|
2065
|
+
if (!lines?.length) return null;
|
|
2066
|
+
let halfWidth = 0;
|
|
2067
|
+
let halfHeight = 0;
|
|
2068
|
+
for (const line of lines) {
|
|
2069
|
+
for (const box of line ?? []) {
|
|
2070
|
+
const { renderLeft, renderTop } = box;
|
|
2071
|
+
if (typeof renderLeft !== "number" || typeof renderTop !== "number") continue;
|
|
2072
|
+
const halfGlyphWidth = (box.kernedWidth ?? box.width ?? 0) / 2;
|
|
2073
|
+
const halfGlyphHeight = (box.height ?? text.fontSize) * GLYPH_HALF_HEIGHT;
|
|
2074
|
+
const angle = box.angle ?? 0;
|
|
2075
|
+
const cos = Math.abs(Math.cos(angle));
|
|
2076
|
+
const sin = Math.abs(Math.sin(angle));
|
|
2077
|
+
halfWidth = Math.max(
|
|
2078
|
+
halfWidth,
|
|
2079
|
+
Math.abs(renderLeft) + halfGlyphWidth * cos + halfGlyphHeight * sin
|
|
2080
|
+
);
|
|
2081
|
+
halfHeight = Math.max(
|
|
2082
|
+
halfHeight,
|
|
2083
|
+
Math.abs(renderTop) + halfGlyphWidth * sin + halfGlyphHeight * cos
|
|
2084
|
+
);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
if (halfWidth === 0 && halfHeight === 0) return null;
|
|
2088
|
+
return { width: halfWidth * 2, height: halfHeight * 2 };
|
|
2089
|
+
}
|
|
2090
|
+
function growBoxToInk(text) {
|
|
2091
|
+
const ink = curvedInkSize(text);
|
|
2092
|
+
if (!ink) return;
|
|
2093
|
+
if (ink.width > (text.width ?? 0)) text.width = ink.width;
|
|
2094
|
+
if (ink.height > (text.height ?? 0)) text.height = ink.height;
|
|
2095
|
+
}
|
|
1951
2096
|
function isCurvable(object) {
|
|
1952
2097
|
return !!object && typeof object.text === "string";
|
|
1953
2098
|
}
|
|
1954
2099
|
function measureText(text) {
|
|
1955
2100
|
const authored = text.width;
|
|
1956
2101
|
try {
|
|
1957
|
-
text.set({ width:
|
|
2102
|
+
text.set({ width: MEASURE_WIDTH2 });
|
|
1958
2103
|
text.initDimensions?.();
|
|
1959
2104
|
return Math.max(1, text.calcTextWidth?.() ?? text.width ?? 1);
|
|
1960
2105
|
} finally {
|
|
@@ -1971,7 +2116,7 @@ function lineHeightOf(text) {
|
|
|
1971
2116
|
return text.fontSize * (text.lineHeight ?? 1) * FALLBACK_LINE_HEIGHT;
|
|
1972
2117
|
}
|
|
1973
2118
|
function toFabricPath(curve) {
|
|
1974
|
-
return new
|
|
2119
|
+
return new import_fabric7.Path(curve.data, { visible: false, objectCaching: false });
|
|
1975
2120
|
}
|
|
1976
2121
|
var TextCurveManager = class {
|
|
1977
2122
|
constructor(canvas, layers, history, events) {
|
|
@@ -1979,11 +2124,29 @@ var TextCurveManager = class {
|
|
|
1979
2124
|
this.layers = layers;
|
|
1980
2125
|
this.history = history;
|
|
1981
2126
|
this.events = events;
|
|
2127
|
+
this.canvas.on("text:changed", this.onTextChanged);
|
|
1982
2128
|
}
|
|
1983
2129
|
canvas;
|
|
1984
2130
|
layers;
|
|
1985
2131
|
history;
|
|
1986
2132
|
events;
|
|
2133
|
+
/**
|
|
2134
|
+
* Typing on the canvas changes the run the paths were built for. Fabric drops
|
|
2135
|
+
* a glyph whose centre falls past the end of the path, so without rebuilding
|
|
2136
|
+
* here a longer replacement simply vanishes — select-all-and-retype rendered
|
|
2137
|
+
* an empty box — and a shorter one piles up on a path built for the old text.
|
|
2138
|
+
*
|
|
2139
|
+
* No history entry: fabric records the edit when editing exits, and one save
|
|
2140
|
+
* per keystroke would bury every earlier step under the typing.
|
|
2141
|
+
*/
|
|
2142
|
+
onTextChanged = (event) => {
|
|
2143
|
+
const layer = event.target ? this.layers.findByObject(event.target) : void 0;
|
|
2144
|
+
if (!layer?.meta.curve) return;
|
|
2145
|
+
this.refresh(layer.id, false);
|
|
2146
|
+
};
|
|
2147
|
+
dispose() {
|
|
2148
|
+
this.canvas.off("text:changed", this.onTextChanged);
|
|
2149
|
+
}
|
|
1987
2150
|
/** Curve parameters for a layer, or null when it is not curved text. */
|
|
1988
2151
|
get(layerId) {
|
|
1989
2152
|
const layer = this.layers.get(layerId);
|
|
@@ -2068,6 +2231,8 @@ var TextCurveManager = class {
|
|
|
2068
2231
|
* outside the layer's box — where object caching clips it away.
|
|
2069
2232
|
*/
|
|
2070
2233
|
attach(text, curves) {
|
|
2234
|
+
if (!authoredCaching.has(text)) authoredCaching.set(text, text.objectCaching);
|
|
2235
|
+
text.set({ objectCaching: false });
|
|
2071
2236
|
const paths = curves.map(toFabricPath);
|
|
2072
2237
|
const union = paths.length === 1 ? paths[0] : toFabricPath({ data: curves.map((c) => c.data).join(" "), length: 0 });
|
|
2073
2238
|
text.set({
|
|
@@ -2079,6 +2244,35 @@ var TextCurveManager = class {
|
|
|
2079
2244
|
pathStartOffset: 0
|
|
2080
2245
|
});
|
|
2081
2246
|
this.patchLineMeasure(text, paths, union);
|
|
2247
|
+
this.patchDimensions(text);
|
|
2248
|
+
}
|
|
2249
|
+
/**
|
|
2250
|
+
* Re-apply {@link growBoxToInk} after every one of fabric's own dimension
|
|
2251
|
+
* passes. Typing, a font change, a style edit and a state restore all call
|
|
2252
|
+
* `initDimensions`, and each one puts the box back to the path's bounding box
|
|
2253
|
+
* — so growing it once, here, would hold only until the next keystroke.
|
|
2254
|
+
*/
|
|
2255
|
+
patchDimensions(text) {
|
|
2256
|
+
if (dimensionPatches.has(text)) return;
|
|
2257
|
+
const original = text.initDimensions;
|
|
2258
|
+
if (typeof original !== "function") return;
|
|
2259
|
+
const installed = function() {
|
|
2260
|
+
original.call(this);
|
|
2261
|
+
growBoxToInk(this);
|
|
2262
|
+
};
|
|
2263
|
+
const ownedOriginal = Object.prototype.hasOwnProperty.call(text, "initDimensions");
|
|
2264
|
+
text.initDimensions = installed;
|
|
2265
|
+
dimensionPatches.set(text, { original, installed, ownedOriginal });
|
|
2266
|
+
}
|
|
2267
|
+
/** Restore fabric's own dimension pass, if this manager replaced it. */
|
|
2268
|
+
unpatchDimensions(text) {
|
|
2269
|
+
const patch = dimensionPatches.get(text);
|
|
2270
|
+
if (!patch) return;
|
|
2271
|
+
if (text.initDimensions === patch.installed) {
|
|
2272
|
+
if (patch.ownedOriginal) text.initDimensions = patch.original;
|
|
2273
|
+
else delete text.initDimensions;
|
|
2274
|
+
}
|
|
2275
|
+
dimensionPatches.delete(text);
|
|
2082
2276
|
}
|
|
2083
2277
|
/**
|
|
2084
2278
|
* Fabric lays every line of a text object along `this.path`, from one
|
|
@@ -2130,13 +2324,19 @@ var TextCurveManager = class {
|
|
|
2130
2324
|
}
|
|
2131
2325
|
detach(text, authoredWidth) {
|
|
2132
2326
|
this.unpatchLineMeasure(text);
|
|
2327
|
+
this.unpatchDimensions(text);
|
|
2328
|
+
const caching = authoredCaching.get(text);
|
|
2329
|
+
if (caching !== void 0) {
|
|
2330
|
+
text.set({ objectCaching: caching });
|
|
2331
|
+
authoredCaching.delete(text);
|
|
2332
|
+
}
|
|
2133
2333
|
text.set({ path: null, pathStartOffset: 0 });
|
|
2134
2334
|
if (authoredWidth !== void 0 && authoredWidth > 0) text.set({ width: authoredWidth });
|
|
2135
2335
|
}
|
|
2136
2336
|
};
|
|
2137
2337
|
|
|
2138
2338
|
// src/mask-presets/manager.ts
|
|
2139
|
-
var
|
|
2339
|
+
var import_fabric8 = require("fabric");
|
|
2140
2340
|
|
|
2141
2341
|
// src/mask-presets/shapes.ts
|
|
2142
2342
|
var SHAPE_MASK_IDS = [
|
|
@@ -2403,13 +2603,13 @@ var MaskPresetManager = class {
|
|
|
2403
2603
|
objectCaching: false
|
|
2404
2604
|
};
|
|
2405
2605
|
if (isShapeMaskId(id)) {
|
|
2406
|
-
return new
|
|
2606
|
+
return new import_fabric8.Path(shapeMaskPathData(id), {
|
|
2407
2607
|
...shared,
|
|
2408
2608
|
scaleX: width / SHAPE_MASK_BOX,
|
|
2409
2609
|
scaleY: height / SHAPE_MASK_BOX
|
|
2410
2610
|
});
|
|
2411
2611
|
}
|
|
2412
|
-
return new
|
|
2612
|
+
return new import_fabric8.FabricImage(renderTextureMask(id), {
|
|
2413
2613
|
...shared,
|
|
2414
2614
|
scaleX: width / TEXTURE_MASK_SIZE,
|
|
2415
2615
|
scaleY: height / TEXTURE_MASK_SIZE
|
|
@@ -2418,7 +2618,7 @@ var MaskPresetManager = class {
|
|
|
2418
2618
|
};
|
|
2419
2619
|
|
|
2420
2620
|
// src/shadow.ts
|
|
2421
|
-
var
|
|
2621
|
+
var import_fabric9 = require("fabric");
|
|
2422
2622
|
|
|
2423
2623
|
// src/utils/color.ts
|
|
2424
2624
|
var CSS_COLOR = /^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\([\d.%+\-\s,/]+\)|[a-z]+)$/i;
|
|
@@ -2456,7 +2656,7 @@ function applyLayerShadow(object, config) {
|
|
|
2456
2656
|
}
|
|
2457
2657
|
const color = isCssColor(next.color) ? next.color : DEFAULT_LAYER_SHADOW.color;
|
|
2458
2658
|
object.set({
|
|
2459
|
-
shadow: new
|
|
2659
|
+
shadow: new import_fabric9.Shadow({
|
|
2460
2660
|
color,
|
|
2461
2661
|
blur: Math.max(0, next.blur),
|
|
2462
2662
|
offsetX: next.offsetX,
|
|
@@ -2570,7 +2770,7 @@ var UnitConverter = class {
|
|
|
2570
2770
|
};
|
|
2571
2771
|
|
|
2572
2772
|
// src/serialization.ts
|
|
2573
|
-
var
|
|
2773
|
+
var import_fabric10 = require("fabric");
|
|
2574
2774
|
var VERSION = "2.0.0";
|
|
2575
2775
|
function serializeEditor(editor) {
|
|
2576
2776
|
return {
|
|
@@ -2606,7 +2806,7 @@ async function deserializeEditor(editor, state) {
|
|
|
2606
2806
|
}
|
|
2607
2807
|
const staged = await Promise.all(
|
|
2608
2808
|
state.layers.map(async (serialized) => {
|
|
2609
|
-
const fabricObject = (await
|
|
2809
|
+
const fabricObject = (await import_fabric10.util.enlivenObjects([serialized.fabricObject]))[0];
|
|
2610
2810
|
if (!fabricObject) {
|
|
2611
2811
|
const source = serialized.fabricObject.src;
|
|
2612
2812
|
if (typeof source === "string" && source.startsWith("blob:")) {
|
|
@@ -2617,7 +2817,7 @@ async function deserializeEditor(editor, state) {
|
|
|
2617
2817
|
return { serialized, fabricObject };
|
|
2618
2818
|
})
|
|
2619
2819
|
);
|
|
2620
|
-
const stagedBackground = state.backgroundImage ? (await
|
|
2820
|
+
const stagedBackground = state.backgroundImage ? (await import_fabric10.util.enlivenObjects([state.backgroundImage]))[0] : null;
|
|
2621
2821
|
if (state.backgroundImage && !stagedBackground) {
|
|
2622
2822
|
const source = state.backgroundImage.src;
|
|
2623
2823
|
if (typeof source === "string" && source.startsWith("blob:")) {
|
|
@@ -2669,7 +2869,7 @@ function restoreLayer(editor, serialized, fabricObject) {
|
|
|
2669
2869
|
}
|
|
2670
2870
|
|
|
2671
2871
|
// src/export.ts
|
|
2672
|
-
var
|
|
2872
|
+
var import_fabric11 = require("fabric");
|
|
2673
2873
|
|
|
2674
2874
|
// src/displacement.ts
|
|
2675
2875
|
var CHANNEL_INDEX = {
|
|
@@ -2763,7 +2963,7 @@ async function exportPNG(canvas, options = {}) {
|
|
|
2763
2963
|
}
|
|
2764
2964
|
async function exportIsolatedPNG(source, objects, options = {}) {
|
|
2765
2965
|
const element = source.lowerCanvasEl.ownerDocument.createElement("canvas");
|
|
2766
|
-
const canvas = new
|
|
2966
|
+
const canvas = new import_fabric11.StaticCanvas(element, {
|
|
2767
2967
|
width: options.width ?? source.getWidth(),
|
|
2768
2968
|
height: options.height ?? source.getHeight(),
|
|
2769
2969
|
backgroundColor: options.backgroundColor || void 0
|
|
@@ -2793,7 +2993,7 @@ async function exportPrintArea(source, area, options = {}) {
|
|
|
2793
2993
|
throw new Error("Print area does not overlap the canvas");
|
|
2794
2994
|
}
|
|
2795
2995
|
const element = source.lowerCanvasEl.ownerDocument.createElement("canvas");
|
|
2796
|
-
const canvas = new
|
|
2996
|
+
const canvas = new import_fabric11.StaticCanvas(element, { width, height });
|
|
2797
2997
|
try {
|
|
2798
2998
|
const clones = await Promise.all(source.getObjects().map((object) => object.clone()));
|
|
2799
2999
|
if (clones.length) canvas.add(...clones);
|
|
@@ -3233,7 +3433,7 @@ var ProjectManager = class {
|
|
|
3233
3433
|
};
|
|
3234
3434
|
|
|
3235
3435
|
// src/mask.ts
|
|
3236
|
-
var
|
|
3436
|
+
var import_fabric12 = require("fabric");
|
|
3237
3437
|
var MaskRefinementError = class extends Error {
|
|
3238
3438
|
constructor(code, message, cause) {
|
|
3239
3439
|
super(message);
|
|
@@ -3265,7 +3465,7 @@ var MaskController = class {
|
|
|
3265
3465
|
throw new Error("Mask dimensions must be positive integers");
|
|
3266
3466
|
}
|
|
3267
3467
|
const backing = this.makeCanvas(width, height);
|
|
3268
|
-
const image = new
|
|
3468
|
+
const image = new import_fabric12.FabricImage(backing, {
|
|
3269
3469
|
left: 0,
|
|
3270
3470
|
top: 0,
|
|
3271
3471
|
originX: "left",
|
|
@@ -3491,10 +3691,10 @@ var MaskController = class {
|
|
|
3491
3691
|
};
|
|
3492
3692
|
|
|
3493
3693
|
// src/masks/manager.ts
|
|
3494
|
-
var
|
|
3694
|
+
var import_fabric19 = require("fabric");
|
|
3495
3695
|
|
|
3496
3696
|
// src/masks/compose.ts
|
|
3497
|
-
var
|
|
3697
|
+
var import_fabric13 = require("fabric");
|
|
3498
3698
|
var MODE_OPERATION = {
|
|
3499
3699
|
add: "source-over",
|
|
3500
3700
|
subtract: "destination-out",
|
|
@@ -3504,7 +3704,7 @@ function neutralize(child) {
|
|
|
3504
3704
|
child.set({ opacity: 0, globalCompositeOperation: "source-over" });
|
|
3505
3705
|
}
|
|
3506
3706
|
function baseRect(box) {
|
|
3507
|
-
return new
|
|
3707
|
+
return new import_fabric13.Rect({
|
|
3508
3708
|
left: box.left,
|
|
3509
3709
|
top: box.top,
|
|
3510
3710
|
width: Math.max(1, box.width),
|
|
@@ -3531,7 +3731,7 @@ function composeMaskGroup(children, entries, options) {
|
|
|
3531
3731
|
});
|
|
3532
3732
|
const first = entries.find((entry) => entry.visible);
|
|
3533
3733
|
const withBase = first && first.mode !== "add" ? [baseRect(options.box), ...children] : [...children];
|
|
3534
|
-
return new
|
|
3734
|
+
return new import_fabric13.Group(withBase, {
|
|
3535
3735
|
absolutePositioned: options.absolute,
|
|
3536
3736
|
// Cached, so the children's compositing operations resolve against each
|
|
3537
3737
|
// other instead of against the page underneath the mask.
|
|
@@ -3545,15 +3745,15 @@ function needsAbsoluteSpace(entries) {
|
|
|
3545
3745
|
}
|
|
3546
3746
|
|
|
3547
3747
|
// src/masks/edit.ts
|
|
3548
|
-
var
|
|
3748
|
+
var import_fabric15 = require("fabric");
|
|
3549
3749
|
|
|
3550
3750
|
// src/masks/space.ts
|
|
3551
|
-
var
|
|
3751
|
+
var import_fabric14 = require("fabric");
|
|
3552
3752
|
function matrixOf(object) {
|
|
3553
3753
|
return object.calcTransformMatrix();
|
|
3554
3754
|
}
|
|
3555
3755
|
function applyMatrix(object, matrix) {
|
|
3556
|
-
const decomposed =
|
|
3756
|
+
const decomposed = import_fabric14.util.qrDecompose(matrix);
|
|
3557
3757
|
object.set({
|
|
3558
3758
|
flipX: false,
|
|
3559
3759
|
flipY: false,
|
|
@@ -3570,19 +3770,19 @@ function applyMatrix(object, matrix) {
|
|
|
3570
3770
|
object.setCoords();
|
|
3571
3771
|
}
|
|
3572
3772
|
function toCanvasSpace(object, host) {
|
|
3573
|
-
applyMatrix(object,
|
|
3773
|
+
applyMatrix(object, import_fabric14.util.multiplyTransformMatrices(matrixOf(host), matrixOf(object)));
|
|
3574
3774
|
}
|
|
3575
3775
|
function toHostSpace(object, host) {
|
|
3576
3776
|
applyMatrix(
|
|
3577
3777
|
object,
|
|
3578
|
-
|
|
3778
|
+
import_fabric14.util.multiplyTransformMatrices(import_fabric14.util.invertTransform(matrixOf(host)), matrixOf(object))
|
|
3579
3779
|
);
|
|
3580
3780
|
}
|
|
3581
3781
|
function relativeMatrix(object, host) {
|
|
3582
|
-
return
|
|
3782
|
+
return import_fabric14.util.multiplyTransformMatrices(import_fabric14.util.invertTransform(matrixOf(host)), matrixOf(object));
|
|
3583
3783
|
}
|
|
3584
3784
|
function applyRelativeMatrix(object, host, rel) {
|
|
3585
|
-
applyMatrix(object,
|
|
3785
|
+
applyMatrix(object, import_fabric14.util.multiplyTransformMatrices(matrixOf(host), rel));
|
|
3586
3786
|
}
|
|
3587
3787
|
function asObject(clip) {
|
|
3588
3788
|
return clip;
|
|
@@ -3695,8 +3895,8 @@ var MaskEditController = class {
|
|
|
3695
3895
|
if (!this.handle || !this.child || !this.group) return;
|
|
3696
3896
|
applyMatrix(
|
|
3697
3897
|
this.child,
|
|
3698
|
-
|
|
3699
|
-
|
|
3898
|
+
import_fabric15.util.multiplyTransformMatrices(
|
|
3899
|
+
import_fabric15.util.invertTransform(matrixOf(this.group)),
|
|
3700
3900
|
matrixOf(this.handle)
|
|
3701
3901
|
)
|
|
3702
3902
|
);
|
|
@@ -3707,15 +3907,15 @@ var MaskEditController = class {
|
|
|
3707
3907
|
};
|
|
3708
3908
|
|
|
3709
3909
|
// src/masks/store.ts
|
|
3710
|
-
var
|
|
3910
|
+
var import_fabric18 = require("fabric");
|
|
3711
3911
|
|
|
3712
3912
|
// src/masks/host.ts
|
|
3713
|
-
var
|
|
3913
|
+
var import_fabric16 = require("fabric");
|
|
3714
3914
|
function findCanvasHost(layers) {
|
|
3715
3915
|
return layers.getAll().find((layer) => layer.meta.canvasMask);
|
|
3716
3916
|
}
|
|
3717
3917
|
function createCanvasHost(canvas, layers) {
|
|
3718
|
-
const rect = new
|
|
3918
|
+
const rect = new import_fabric16.Rect({
|
|
3719
3919
|
left: 0,
|
|
3720
3920
|
top: 0,
|
|
3721
3921
|
width: canvas.getWidth(),
|
|
@@ -3764,9 +3964,9 @@ function hostBoxOf(canvas, host, absolute) {
|
|
|
3764
3964
|
}
|
|
3765
3965
|
|
|
3766
3966
|
// src/masks/install.ts
|
|
3767
|
-
var
|
|
3967
|
+
var import_fabric17 = require("fabric");
|
|
3768
3968
|
function convertSpace(host, sources, absolute) {
|
|
3769
|
-
const wasAbsolute = host.clipPath instanceof
|
|
3969
|
+
const wasAbsolute = host.clipPath instanceof import_fabric17.Group ? host.clipPath.absolutePositioned : absolute;
|
|
3770
3970
|
if (absolute === wasAbsolute) return;
|
|
3771
3971
|
for (const source of sources) {
|
|
3772
3972
|
if (absolute) toCanvasSpace(source, host);
|
|
@@ -3858,7 +4058,7 @@ var MaskStackStore = class {
|
|
|
3858
4058
|
const clip = host.clipPath;
|
|
3859
4059
|
if (!clip) return [];
|
|
3860
4060
|
const entries = this.list(target);
|
|
3861
|
-
if (entries.length === 0 || !(clip instanceof
|
|
4061
|
+
if (entries.length === 0 || !(clip instanceof import_fabric18.Group)) return [asObject(clip)];
|
|
3862
4062
|
const children = unwrapGroup(clip);
|
|
3863
4063
|
const extra = children.length - entries.length;
|
|
3864
4064
|
return extra > 0 ? children.slice(extra) : children;
|
|
@@ -3917,8 +4117,8 @@ var LayerMaskManager = class extends MaskStackStore {
|
|
|
3917
4117
|
onLayersChanged = () => this.pin();
|
|
3918
4118
|
// Selecting a layer means the user has moved on from the mask they had open;
|
|
3919
4119
|
// leaving both selected would show mask controls for an unrelated layer.
|
|
3920
|
-
onLayerSelected = () => {
|
|
3921
|
-
if (this.selected) this.select(null, null);
|
|
4120
|
+
onLayerSelected = ({ layerId }) => {
|
|
4121
|
+
if (layerId && this.selected) this.select(null, null);
|
|
3922
4122
|
};
|
|
3923
4123
|
onObjectModified = (event) => {
|
|
3924
4124
|
const object = event.target;
|
|
@@ -3958,7 +4158,7 @@ var LayerMaskManager = class extends MaskStackStore {
|
|
|
3958
4158
|
if (index === -1) return false;
|
|
3959
4159
|
this.endEdit(false);
|
|
3960
4160
|
this.commit(target, host, [...entries], this.unwrap(target, host), false, true);
|
|
3961
|
-
const group = host.clipPath instanceof
|
|
4161
|
+
const group = host.clipPath instanceof import_fabric19.Group ? host.clipPath : null;
|
|
3962
4162
|
if (!group) return false;
|
|
3963
4163
|
const children = group.getObjects();
|
|
3964
4164
|
const child = children[children.length - entries.length + index];
|
|
@@ -3978,6 +4178,7 @@ var LayerMaskManager = class extends MaskStackStore {
|
|
|
3978
4178
|
}
|
|
3979
4179
|
select(target, maskId) {
|
|
3980
4180
|
this.selected = target && maskId ? { target, maskId } : null;
|
|
4181
|
+
if (this.selected) this.layers.select(null);
|
|
3981
4182
|
this.events.emit("mask:selected", { target, maskId: this.selected ? maskId : null });
|
|
3982
4183
|
}
|
|
3983
4184
|
// ─── Mutations ───────────────────────────────────────
|
|
@@ -4264,7 +4465,7 @@ var CanvasEditor = class {
|
|
|
4264
4465
|
const widthPx = this.units.toPixels(config.width);
|
|
4265
4466
|
const heightPx = this.units.toPixels(config.height);
|
|
4266
4467
|
this.designBackground = config.backgroundColor ?? "#ffffff";
|
|
4267
|
-
this.canvas = new
|
|
4468
|
+
this.canvas = new import_fabric20.Canvas(canvasElement, {
|
|
4268
4469
|
width: widthPx,
|
|
4269
4470
|
height: heightPx,
|
|
4270
4471
|
backgroundColor: this.designBackground,
|
|
@@ -4298,7 +4499,7 @@ var CanvasEditor = class {
|
|
|
4298
4499
|
// ─── Layer Operations ────────────────────────────────
|
|
4299
4500
|
async addImage(url, options) {
|
|
4300
4501
|
try {
|
|
4301
|
-
const img = await
|
|
4502
|
+
const img = await import_fabric20.FabricImage.fromURL(
|
|
4302
4503
|
url,
|
|
4303
4504
|
{},
|
|
4304
4505
|
{ originX: "left", originY: "top", ...options }
|
|
@@ -4324,7 +4525,7 @@ var CanvasEditor = class {
|
|
|
4324
4525
|
if (this.crop.activeLayerId() === layerId) this.crop.cancel();
|
|
4325
4526
|
const previous = layer.fabricObject;
|
|
4326
4527
|
try {
|
|
4327
|
-
const replacement = await
|
|
4528
|
+
const replacement = await import_fabric20.FabricImage.fromURL(url, {}, { originX: "left", originY: "top" });
|
|
4328
4529
|
replacement.set({
|
|
4329
4530
|
left: previous.left,
|
|
4330
4531
|
top: previous.top,
|
|
@@ -4353,7 +4554,7 @@ var CanvasEditor = class {
|
|
|
4353
4554
|
}
|
|
4354
4555
|
}
|
|
4355
4556
|
addText(text, options) {
|
|
4356
|
-
const textbox = new
|
|
4557
|
+
const textbox = new import_fabric20.Textbox(text, {
|
|
4357
4558
|
fontSize: 32,
|
|
4358
4559
|
fontFamily: "Arial",
|
|
4359
4560
|
fill: "#000000",
|
|
@@ -4484,10 +4685,10 @@ var CanvasEditor = class {
|
|
|
4484
4685
|
const next = { ...previous, ...adjustments };
|
|
4485
4686
|
const clampAdjustment = (value, min = -1) => clamp(value ?? 0, min, 1);
|
|
4486
4687
|
image.filters = [
|
|
4487
|
-
new
|
|
4488
|
-
new
|
|
4489
|
-
new
|
|
4490
|
-
new
|
|
4688
|
+
new import_fabric20.filters.Brightness({ brightness: clampAdjustment(next.brightness) }),
|
|
4689
|
+
new import_fabric20.filters.Contrast({ contrast: clampAdjustment(next.contrast) }),
|
|
4690
|
+
new import_fabric20.filters.Saturation({ saturation: clampAdjustment(next.saturation) }),
|
|
4691
|
+
new import_fabric20.filters.Blur({ blur: clampAdjustment(next.blur, 0) })
|
|
4491
4692
|
];
|
|
4492
4693
|
layer.meta.imageAdjustments = next;
|
|
4493
4694
|
image.applyFilters();
|
|
@@ -4528,10 +4729,9 @@ var CanvasEditor = class {
|
|
|
4528
4729
|
this.layers.detach(layer.id);
|
|
4529
4730
|
}
|
|
4530
4731
|
}
|
|
4531
|
-
const group = new
|
|
4732
|
+
const group = new import_fabric20.Group(objects);
|
|
4532
4733
|
const grouped = this.layers.add("group", group, name);
|
|
4533
|
-
|
|
4534
|
-
grouped.children = children;
|
|
4734
|
+
this.layers.adoptChildren(grouped, children);
|
|
4535
4735
|
this.layers.reorder(grouped.id, insertIndex);
|
|
4536
4736
|
this.layers.select(grouped.id);
|
|
4537
4737
|
this.history.save();
|
|
@@ -4591,7 +4791,7 @@ var CanvasEditor = class {
|
|
|
4591
4791
|
const layer = this.layers.get(id);
|
|
4592
4792
|
if (!layer) throw new Error(`Layer not found: ${id}`);
|
|
4593
4793
|
try {
|
|
4594
|
-
if (options.resolution === "source" && layer.fabricObject instanceof
|
|
4794
|
+
if (options.resolution === "source" && layer.fabricObject instanceof import_fabric20.FabricImage) {
|
|
4595
4795
|
const image = await layer.fabricObject.clone();
|
|
4596
4796
|
image.set({
|
|
4597
4797
|
left: 0,
|
|
@@ -4867,7 +5067,7 @@ var CanvasEditor = class {
|
|
|
4867
5067
|
return;
|
|
4868
5068
|
}
|
|
4869
5069
|
try {
|
|
4870
|
-
const image = await
|
|
5070
|
+
const image = await import_fabric20.FabricImage.fromURL(
|
|
4871
5071
|
url,
|
|
4872
5072
|
{ crossOrigin: options.crossOrigin ?? null, signal: options.signal },
|
|
4873
5073
|
{ originX: "left", originY: "top" }
|
|
@@ -5140,6 +5340,7 @@ var CanvasEditor = class {
|
|
|
5140
5340
|
this.crop.dispose();
|
|
5141
5341
|
this.history.dispose();
|
|
5142
5342
|
this.patterns.dispose();
|
|
5343
|
+
this.curves.dispose();
|
|
5143
5344
|
this.events.removeAllListeners();
|
|
5144
5345
|
this.canvas.dispose();
|
|
5145
5346
|
}
|
|
@@ -5199,11 +5400,15 @@ var DEFAULT_PATTERN_CONFIG = {
|
|
|
5199
5400
|
horizontalSpacing: 0,
|
|
5200
5401
|
verticalSpacing: 0,
|
|
5201
5402
|
angle: 0,
|
|
5202
|
-
|
|
5403
|
+
// Half a tile: the offset only bites in the brick modes, and a brick course
|
|
5404
|
+
// that is not half-shifted is just a grid with extra steps.
|
|
5405
|
+
horizontalOffset: 50,
|
|
5203
5406
|
offsetX: 0,
|
|
5204
5407
|
offsetY: 0,
|
|
5205
5408
|
rotationStepH: 0,
|
|
5206
|
-
rotationStepV: 0
|
|
5409
|
+
rotationStepV: 0,
|
|
5410
|
+
horizontalLimit: 0,
|
|
5411
|
+
verticalLimit: 0
|
|
5207
5412
|
};
|
|
5208
5413
|
|
|
5209
5414
|
// src/presets.ts
|
|
@@ -5309,6 +5514,7 @@ var AnnotationOverlay = class {
|
|
|
5309
5514
|
exportPNG,
|
|
5310
5515
|
exportPrintArea,
|
|
5311
5516
|
exportSVG,
|
|
5517
|
+
fitTextWidth,
|
|
5312
5518
|
fitToBox,
|
|
5313
5519
|
generateId,
|
|
5314
5520
|
isCssColor,
|