leafer-draw 1.9.12 → 1.10.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/web.cjs CHANGED
@@ -868,36 +868,19 @@ core.Platform.render = function(target, canvas, options) {
868
868
  if (options.topList.length) options.topList.forEach(item => item.__render(canvas, topOptions));
869
869
  };
870
870
 
871
- function fillText(ui, canvas) {
872
- const data = ui.__, {rows: rows, decorationY: decorationY} = data.__textDrawData;
873
- if (data.__isPlacehold && data.placeholderColor) canvas.fillStyle = data.placeholderColor;
874
- let row;
875
- for (let i = 0, len = rows.length; i < len; i++) {
876
- row = rows[i];
877
- if (row.text) canvas.fillText(row.text, row.x, row.y); else if (row.data) row.data.forEach(charData => {
878
- canvas.fillText(charData.char, charData.x, row.y);
879
- });
880
- }
881
- if (decorationY) {
882
- const {decorationColor: decorationColor, decorationHeight: decorationHeight} = data.__textDrawData;
883
- if (decorationColor) canvas.fillStyle = decorationColor;
884
- rows.forEach(row => decorationY.forEach(value => canvas.fillRect(row.x, row.y + value, row.width, decorationHeight)));
885
- }
886
- }
887
-
888
- function fill(fill, ui, canvas) {
871
+ function fill(fill, ui, canvas, renderOptions) {
889
872
  canvas.fillStyle = fill;
890
- fillPathOrText(ui, canvas);
873
+ fillPathOrText(ui, canvas, renderOptions);
891
874
  }
892
875
 
893
- function fills(fills, ui, canvas) {
876
+ function fills(fills, ui, canvas, renderOptions) {
894
877
  let item;
895
878
  for (let i = 0, len = fills.length; i < len; i++) {
896
879
  item = fills[i];
897
880
  if (item.image) {
898
- if (draw.PaintImage.checkImage(ui, canvas, item, !ui.__.__font)) continue;
881
+ if (draw.PaintImage.checkImage(item, !ui.__.__font, ui, canvas, renderOptions)) continue;
899
882
  if (!item.style) {
900
- if (!i && item.image.isPlacehold) ui.drawImagePlaceholder(canvas, item.image);
883
+ if (!i && item.image.isPlacehold) ui.drawImagePlaceholder(item.image, canvas, renderOptions);
901
884
  continue;
902
885
  }
903
886
  }
@@ -910,60 +893,137 @@ function fills(fills, ui, canvas) {
910
893
  if (item.scaleFixed === true || item.scaleFixed === "zoom-in" && scaleX > 1 && scaleY > 1) canvas.scale(1 / scaleX, 1 / scaleY);
911
894
  }
912
895
  if (item.blendMode) canvas.blendMode = item.blendMode;
913
- fillPathOrText(ui, canvas);
896
+ fillPathOrText(ui, canvas, renderOptions);
914
897
  canvas.restore();
915
898
  } else {
916
899
  if (item.blendMode) {
917
900
  canvas.saveBlendMode(item.blendMode);
918
- fillPathOrText(ui, canvas);
901
+ fillPathOrText(ui, canvas, renderOptions);
919
902
  canvas.restoreBlendMode();
920
- } else fillPathOrText(ui, canvas);
903
+ } else fillPathOrText(ui, canvas, renderOptions);
904
+ }
905
+ }
906
+ }
907
+
908
+ function fillPathOrText(ui, canvas, renderOptions) {
909
+ ui.__.__font ? draw.Paint.fillText(ui, canvas, renderOptions) : ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill();
910
+ }
911
+
912
+ function fillText(ui, canvas, _renderOptions) {
913
+ const data = ui.__, {rows: rows, decorationY: decorationY} = data.__textDrawData;
914
+ if (data.__isPlacehold && data.placeholderColor) canvas.fillStyle = data.placeholderColor;
915
+ let row;
916
+ for (let i = 0, len = rows.length; i < len; i++) {
917
+ row = rows[i];
918
+ if (row.text) canvas.fillText(row.text, row.x, row.y); else if (row.data) row.data.forEach(charData => {
919
+ canvas.fillText(charData.char, charData.x, row.y);
920
+ });
921
+ }
922
+ if (decorationY) {
923
+ const {decorationColor: decorationColor, decorationHeight: decorationHeight} = data.__textDrawData;
924
+ if (decorationColor) canvas.fillStyle = decorationColor;
925
+ rows.forEach(row => decorationY.forEach(value => canvas.fillRect(row.x, row.y + value, row.width, decorationHeight)));
926
+ }
927
+ }
928
+
929
+ function stroke(stroke, ui, canvas, renderOptions) {
930
+ const data = ui.__;
931
+ if (!data.__strokeWidth) return;
932
+ if (data.__font) {
933
+ draw.Paint.strokeText(stroke, ui, canvas, renderOptions);
934
+ } else {
935
+ switch (data.strokeAlign) {
936
+ case "center":
937
+ drawCenter$1(stroke, 1, ui, canvas, renderOptions);
938
+ break;
939
+
940
+ case "inside":
941
+ drawInside(stroke, ui, canvas, renderOptions);
942
+ break;
943
+
944
+ case "outside":
945
+ drawOutside(stroke, ui, canvas, renderOptions);
946
+ break;
921
947
  }
922
948
  }
923
949
  }
924
950
 
925
- function fillPathOrText(ui, canvas) {
926
- ui.__.__font ? fillText(ui, canvas) : ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill();
951
+ function strokes(strokes, ui, canvas, renderOptions) {
952
+ draw.Paint.stroke(strokes, ui, canvas, renderOptions);
953
+ }
954
+
955
+ function drawCenter$1(stroke, strokeWidthScale, ui, canvas, renderOptions) {
956
+ const data = ui.__;
957
+ if (core.isObject(stroke)) {
958
+ draw.Paint.drawStrokesStyle(stroke, strokeWidthScale, false, ui, canvas, renderOptions);
959
+ } else {
960
+ canvas.setStroke(stroke, data.__strokeWidth * strokeWidthScale, data);
961
+ canvas.stroke();
962
+ }
963
+ if (data.__useArrow) draw.Paint.strokeArrow(stroke, ui, canvas, renderOptions);
964
+ }
965
+
966
+ function drawInside(stroke, ui, canvas, renderOptions) {
967
+ canvas.save();
968
+ canvas.clipUI(ui);
969
+ drawCenter$1(stroke, 2, ui, canvas, renderOptions);
970
+ canvas.restore();
927
971
  }
928
972
 
929
- function strokeText(stroke, ui, canvas) {
973
+ function drawOutside(stroke, ui, canvas, renderOptions) {
974
+ const data = ui.__;
975
+ if (data.__fillAfterStroke) {
976
+ drawCenter$1(stroke, 2, ui, canvas, renderOptions);
977
+ } else {
978
+ const {renderBounds: renderBounds} = ui.__layout;
979
+ const out = canvas.getSameCanvas(true, true);
980
+ ui.__drawRenderPath(out);
981
+ drawCenter$1(stroke, 2, ui, out, renderOptions);
982
+ out.clipUI(data);
983
+ out.clearWorld(renderBounds);
984
+ core.LeafHelper.copyCanvasByWorld(ui, canvas, out);
985
+ out.recycle(ui.__nowWorld);
986
+ }
987
+ }
988
+
989
+ function strokeText(stroke, ui, canvas, renderOptions) {
930
990
  switch (ui.__.strokeAlign) {
931
991
  case "center":
932
- drawCenter$1(stroke, 1, ui, canvas);
992
+ drawCenter(stroke, 1, ui, canvas, renderOptions);
933
993
  break;
934
994
 
935
995
  case "inside":
936
- drawAlign(stroke, "inside", ui, canvas);
996
+ drawAlign(stroke, "inside", ui, canvas, renderOptions);
937
997
  break;
938
998
 
939
999
  case "outside":
940
- ui.__.__fillAfterStroke ? drawCenter$1(stroke, 2, ui, canvas) : drawAlign(stroke, "outside", ui, canvas);
1000
+ ui.__.__fillAfterStroke ? drawCenter(stroke, 2, ui, canvas, renderOptions) : drawAlign(stroke, "outside", ui, canvas, renderOptions);
941
1001
  break;
942
1002
  }
943
1003
  }
944
1004
 
945
- function drawCenter$1(stroke, strokeWidthScale, ui, canvas) {
1005
+ function drawCenter(stroke, strokeWidthScale, ui, canvas, renderOptions) {
946
1006
  const data = ui.__;
947
1007
  if (core.isObject(stroke)) {
948
- drawStrokesStyle(stroke, strokeWidthScale, true, ui, canvas);
1008
+ draw.Paint.drawStrokesStyle(stroke, strokeWidthScale, true, ui, canvas, renderOptions);
949
1009
  } else {
950
1010
  canvas.setStroke(stroke, data.__strokeWidth * strokeWidthScale, data);
951
- drawTextStroke(ui, canvas);
1011
+ draw.Paint.drawTextStroke(ui, canvas, renderOptions);
952
1012
  }
953
1013
  }
954
1014
 
955
- function drawAlign(stroke, align, ui, canvas) {
1015
+ function drawAlign(stroke, align, ui, canvas, renderOptions) {
956
1016
  const out = canvas.getSameCanvas(true, true);
957
1017
  out.font = ui.__.__font;
958
- drawCenter$1(stroke, 2, ui, out);
1018
+ drawCenter(stroke, 2, ui, out, renderOptions);
959
1019
  out.blendMode = align === "outside" ? "destination-out" : "destination-in";
960
- fillText(ui, out);
1020
+ draw.Paint.fillText(ui, out, renderOptions);
961
1021
  out.blendMode = "normal";
962
1022
  core.LeafHelper.copyCanvasByWorld(ui, canvas, out);
963
1023
  out.recycle(ui.__nowWorld);
964
1024
  }
965
1025
 
966
- function drawTextStroke(ui, canvas) {
1026
+ function drawTextStroke(ui, canvas, _renderOptions) {
967
1027
  let row, data = ui.__.__textDrawData;
968
1028
  const {rows: rows, decorationY: decorationY} = data;
969
1029
  for (let i = 0, len = rows.length; i < len; i++) {
@@ -978,13 +1038,13 @@ function drawTextStroke(ui, canvas) {
978
1038
  }
979
1039
  }
980
1040
 
981
- function drawStrokesStyle(strokes, strokeWidthScale, isText, ui, canvas) {
1041
+ function drawStrokesStyle(strokes, strokeWidthScale, isText, ui, canvas, renderOptions) {
982
1042
  let item;
983
1043
  const data = ui.__, {__hasMultiStrokeStyle: __hasMultiStrokeStyle} = data;
984
1044
  __hasMultiStrokeStyle || canvas.setStroke(undefined, data.__strokeWidth * strokeWidthScale, data);
985
1045
  for (let i = 0, len = strokes.length; i < len; i++) {
986
1046
  item = strokes[i];
987
- if (item.image && draw.PaintImage.checkImage(ui, canvas, item, false)) continue;
1047
+ if (item.image && draw.PaintImage.checkImage(item, false, ui, canvas, renderOptions)) continue;
988
1048
  if (item.style) {
989
1049
  if (__hasMultiStrokeStyle) {
990
1050
  const {strokeStyle: strokeStyle} = item;
@@ -992,75 +1052,15 @@ function drawStrokesStyle(strokes, strokeWidthScale, isText, ui, canvas) {
992
1052
  } else canvas.strokeStyle = item.style;
993
1053
  if (item.blendMode) {
994
1054
  canvas.saveBlendMode(item.blendMode);
995
- isText ? drawTextStroke(ui, canvas) : canvas.stroke();
1055
+ isText ? draw.Paint.drawTextStroke(ui, canvas, renderOptions) : canvas.stroke();
996
1056
  canvas.restoreBlendMode();
997
1057
  } else {
998
- isText ? drawTextStroke(ui, canvas) : canvas.stroke();
1058
+ isText ? draw.Paint.drawTextStroke(ui, canvas, renderOptions) : canvas.stroke();
999
1059
  }
1000
1060
  }
1001
1061
  }
1002
1062
  }
1003
1063
 
1004
- function stroke(stroke, ui, canvas) {
1005
- const data = ui.__;
1006
- if (!data.__strokeWidth) return;
1007
- if (data.__font) {
1008
- strokeText(stroke, ui, canvas);
1009
- } else {
1010
- switch (data.strokeAlign) {
1011
- case "center":
1012
- drawCenter(stroke, 1, ui, canvas);
1013
- break;
1014
-
1015
- case "inside":
1016
- drawInside(stroke, ui, canvas);
1017
- break;
1018
-
1019
- case "outside":
1020
- drawOutside(stroke, ui, canvas);
1021
- break;
1022
- }
1023
- }
1024
- }
1025
-
1026
- function strokes(strokes, ui, canvas) {
1027
- stroke(strokes, ui, canvas);
1028
- }
1029
-
1030
- function drawCenter(stroke, strokeWidthScale, ui, canvas) {
1031
- const data = ui.__;
1032
- if (core.isObject(stroke)) {
1033
- drawStrokesStyle(stroke, strokeWidthScale, false, ui, canvas);
1034
- } else {
1035
- canvas.setStroke(stroke, data.__strokeWidth * strokeWidthScale, data);
1036
- canvas.stroke();
1037
- }
1038
- if (data.__useArrow) draw.Paint.strokeArrow(stroke, ui, canvas);
1039
- }
1040
-
1041
- function drawInside(stroke, ui, canvas) {
1042
- canvas.save();
1043
- canvas.clipUI(ui);
1044
- drawCenter(stroke, 2, ui, canvas);
1045
- canvas.restore();
1046
- }
1047
-
1048
- function drawOutside(stroke, ui, canvas) {
1049
- const data = ui.__;
1050
- if (data.__fillAfterStroke) {
1051
- drawCenter(stroke, 2, ui, canvas);
1052
- } else {
1053
- const {renderBounds: renderBounds} = ui.__layout;
1054
- const out = canvas.getSameCanvas(true, true);
1055
- ui.__drawRenderPath(out);
1056
- drawCenter(stroke, 2, ui, out);
1057
- out.clipUI(data);
1058
- out.clearWorld(renderBounds);
1059
- core.LeafHelper.copyCanvasByWorld(ui, canvas, out);
1060
- out.recycle(ui.__nowWorld);
1061
- }
1062
- }
1063
-
1064
1064
  const {getSpread: getSpread, copyAndSpread: copyAndSpread, toOuterOf: toOuterOf, getOuterOf: getOuterOf, getByMove: getByMove, move: move$1, getIntersectData: getIntersectData} = core.BoundsHelper;
1065
1065
 
1066
1066
  const tempBounds$1 = {};
@@ -1216,88 +1216,118 @@ const PaintModule = {
1216
1216
  strokes: strokes,
1217
1217
  strokeText: strokeText,
1218
1218
  drawTextStroke: drawTextStroke,
1219
+ drawStrokesStyle: drawStrokesStyle,
1219
1220
  shape: shape
1220
1221
  };
1221
1222
 
1222
- let origin = {}, tempMatrix$1 = core.getMatrixData();
1223
+ let cache, box = new core.Bounds;
1223
1224
 
1224
- const {get: get$3, set: set, rotateOfOuter: rotateOfOuter$1, translate: translate$1, scaleOfOuter: scaleOfOuter$1, multiplyParent: multiplyParent, scale: scaleHelper, rotate: rotate, skew: skewHelper} = core.MatrixHelper;
1225
+ const {isSame: isSame} = core.BoundsHelper;
1225
1226
 
1226
- function stretchMode(data, box, scaleX, scaleY) {
1227
- const transform = get$3();
1228
- translate$1(transform, box.x, box.y);
1229
- if (scaleX) scaleHelper(transform, scaleX, scaleY);
1230
- data.transform = transform;
1227
+ function image(ui, attrName, paint, boxBounds, firstUse) {
1228
+ let leafPaint, event;
1229
+ const image = core.ImageManager.get(paint);
1230
+ if (cache && paint === cache.paint && isSame(boxBounds, cache.boxBounds)) {
1231
+ leafPaint = cache.leafPaint;
1232
+ } else {
1233
+ leafPaint = {
1234
+ type: paint.type,
1235
+ image: image
1236
+ };
1237
+ if (image.hasAlphaPixel) leafPaint.isTransparent = true;
1238
+ cache = image.use > 1 ? {
1239
+ leafPaint: leafPaint,
1240
+ paint: paint,
1241
+ boxBounds: box.set(boxBounds)
1242
+ } : null;
1243
+ }
1244
+ if (firstUse || image.loading) event = {
1245
+ image: image,
1246
+ attrName: attrName,
1247
+ attrValue: paint
1248
+ };
1249
+ if (image.ready) {
1250
+ checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds);
1251
+ if (firstUse) {
1252
+ onLoad(ui, event);
1253
+ onLoadSuccess(ui, event);
1254
+ }
1255
+ } else if (image.error) {
1256
+ if (firstUse) onLoadError(ui, event, image.error);
1257
+ } else {
1258
+ if (firstUse) {
1259
+ ignoreRender(ui, true);
1260
+ onLoad(ui, event);
1261
+ }
1262
+ leafPaint.loadId = image.load(() => {
1263
+ ignoreRender(ui, false);
1264
+ if (!ui.destroyed) {
1265
+ if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1266
+ if (image.hasAlphaPixel) ui.__layout.hitCanvasChanged = true;
1267
+ ui.forceUpdate("surface");
1268
+ }
1269
+ onLoadSuccess(ui, event);
1270
+ }
1271
+ leafPaint.loadId = undefined;
1272
+ }, error => {
1273
+ ignoreRender(ui, false);
1274
+ onLoadError(ui, event, error);
1275
+ leafPaint.loadId = undefined;
1276
+ });
1277
+ if (ui.placeholderColor) {
1278
+ if (!ui.placeholderDelay) image.isPlacehold = true; else setTimeout(() => {
1279
+ if (!image.ready) {
1280
+ image.isPlacehold = true;
1281
+ ui.forceUpdate("surface");
1282
+ }
1283
+ }, ui.placeholderDelay);
1284
+ }
1285
+ }
1286
+ return leafPaint;
1231
1287
  }
1232
1288
 
1233
- function fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation) {
1234
- const transform = get$3();
1235
- translate$1(transform, box.x + x, box.y + y);
1236
- scaleHelper(transform, scaleX, scaleY);
1237
- if (rotation) rotateOfOuter$1(transform, {
1238
- x: box.x + box.width / 2,
1239
- y: box.y + box.height / 2
1240
- }, rotation);
1241
- data.transform = transform;
1289
+ function checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds) {
1290
+ if (attrName === "fill" && !ui.__.__naturalWidth) {
1291
+ const data = ui.__;
1292
+ data.__naturalWidth = image.width / data.pixelRatio;
1293
+ data.__naturalHeight = image.height / data.pixelRatio;
1294
+ if (data.__autoSide) {
1295
+ ui.forceUpdate("width");
1296
+ if (ui.__proxyData) {
1297
+ ui.setProxyAttr("width", data.width);
1298
+ ui.setProxyAttr("height", data.height);
1299
+ }
1300
+ return false;
1301
+ }
1302
+ }
1303
+ if (!leafPaint.data) draw.PaintImage.createData(leafPaint, image, paint, boxBounds);
1304
+ return true;
1242
1305
  }
1243
1306
 
1244
- function clipMode(data, box, x, y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY) {
1245
- const transform = get$3();
1246
- layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1247
- if (clipScaleX) {
1248
- if (rotation || skew) {
1249
- set(tempMatrix$1);
1250
- scaleOfOuter$1(tempMatrix$1, box, clipScaleX, clipScaleY);
1251
- multiplyParent(transform, tempMatrix$1);
1252
- } else scaleOfOuter$1(transform, box, clipScaleX, clipScaleY);
1253
- }
1254
- data.transform = transform;
1307
+ function onLoad(ui, event) {
1308
+ emit(ui, core.ImageEvent.LOAD, event);
1255
1309
  }
1256
1310
 
1257
- function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, skew, align, freeTransform) {
1258
- const transform = get$3();
1259
- if (freeTransform) {
1260
- layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1261
- } else {
1262
- if (rotation) {
1263
- if (align === "center") {
1264
- rotateOfOuter$1(transform, {
1265
- x: width / 2,
1266
- y: height / 2
1267
- }, rotation);
1268
- } else {
1269
- rotate(transform, rotation);
1270
- switch (rotation) {
1271
- case 90:
1272
- translate$1(transform, height, 0);
1273
- break;
1311
+ function onLoadSuccess(ui, event) {
1312
+ emit(ui, core.ImageEvent.LOADED, event);
1313
+ }
1274
1314
 
1275
- case 180:
1276
- translate$1(transform, width, height);
1277
- break;
1315
+ function onLoadError(ui, event, error) {
1316
+ event.error = error;
1317
+ ui.forceUpdate("surface");
1318
+ emit(ui, core.ImageEvent.ERROR, event);
1319
+ }
1278
1320
 
1279
- case 270:
1280
- translate$1(transform, 0, width);
1281
- break;
1282
- }
1283
- }
1284
- }
1285
- origin.x = box.x + x;
1286
- origin.y = box.y + y;
1287
- translate$1(transform, origin.x, origin.y);
1288
- if (scaleX) scaleOfOuter$1(transform, origin, scaleX, scaleY);
1289
- }
1290
- data.transform = transform;
1321
+ function emit(ui, type, data) {
1322
+ if (ui.hasEvent(type)) ui.emitEvent(new core.ImageEvent(type, data));
1291
1323
  }
1292
1324
 
1293
- function layout(transform, box, x, y, scaleX, scaleY, rotation, skew) {
1294
- if (rotation) rotate(transform, rotation);
1295
- if (skew) skewHelper(transform, skew.x, skew.y);
1296
- if (scaleX) scaleHelper(transform, scaleX, scaleY);
1297
- translate$1(transform, box.x + x, box.y + y);
1325
+ function ignoreRender(ui, value) {
1326
+ const {leafer: leafer} = ui;
1327
+ if (leafer && leafer.viewReady) leafer.renderer.ignore = value;
1298
1328
  }
1299
1329
 
1300
- const {get: get$2, translate: translate} = core.MatrixHelper;
1330
+ const {get: get$3, translate: translate$1} = core.MatrixHelper;
1301
1331
 
1302
1332
  const tempBox = new core.Bounds;
1303
1333
 
@@ -1310,13 +1340,13 @@ function createData(leafPaint, image, paint, box) {
1310
1340
  if (changeful) leafPaint.changeful = changeful;
1311
1341
  if (sync) leafPaint.sync = sync;
1312
1342
  if (scaleFixed) leafPaint.scaleFixed = scaleFixed;
1313
- leafPaint.data = getPatternData(paint, box, image);
1343
+ leafPaint.data = draw.PaintImage.getPatternData(paint, box, image);
1314
1344
  }
1315
1345
 
1316
1346
  function getPatternData(paint, box, image) {
1317
1347
  if (paint.padding) box = tempBox.set(box).shrink(paint.padding);
1318
1348
  if (paint.mode === "strench") paint.mode = "stretch";
1319
- let {width: width, height: height} = image;
1349
+ const {width: width, height: height} = image;
1320
1350
  const {opacity: opacity, mode: mode, align: align, offset: offset, scale: scale, size: size, rotation: rotation, skew: skew, clipSize: clipSize, repeat: repeat, gap: gap, filters: filters} = paint;
1321
1351
  const sameBox = box.width === width && box.height === height;
1322
1352
  const data = {
@@ -1347,8 +1377,8 @@ function getPatternData(paint, box, image) {
1347
1377
  case "stretch":
1348
1378
  if (!sameBox) {
1349
1379
  scaleX = box.width / width, scaleY = box.height / height;
1350
- stretchMode(data, box, scaleX, scaleY);
1351
- }
1380
+ draw.PaintImage.stretchMode(data, box, scaleX, scaleY);
1381
+ } else if (scaleX) scaleX = scaleY = undefined;
1352
1382
  break;
1353
1383
 
1354
1384
  case "normal":
@@ -1356,13 +1386,13 @@ function getPatternData(paint, box, image) {
1356
1386
  if (tempImage.x || tempImage.y || scaleX || clipSize || rotation || skew) {
1357
1387
  let clipScaleX, clipScaleY;
1358
1388
  if (clipSize) clipScaleX = box.width / clipSize.width, clipScaleY = box.height / clipSize.height;
1359
- clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY);
1389
+ draw.PaintImage.clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY);
1360
1390
  if (clipScaleX) scaleX = scaleX ? scaleX * clipScaleX : clipScaleX, scaleY = scaleY ? scaleY * clipScaleY : clipScaleY;
1361
1391
  }
1362
1392
  break;
1363
1393
 
1364
1394
  case "repeat":
1365
- if (!sameBox || scaleX || rotation || skew) repeatMode(data, box, width, height, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, align, paint.freeTransform);
1395
+ if (!sameBox || scaleX || rotation || skew) draw.PaintImage.repeatMode(data, box, width, height, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, align, paint.freeTransform);
1366
1396
  if (!repeat) data.repeat = "repeat";
1367
1397
  const count = core.isObject(repeat);
1368
1398
  if (gap || count) data.gap = getGapData(gap, count && repeat, tempImage.width, tempImage.height, box);
@@ -1371,18 +1401,16 @@ function getPatternData(paint, box, image) {
1371
1401
  case "fit":
1372
1402
  case "cover":
1373
1403
  default:
1374
- if (scaleX) fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1404
+ if (scaleX) draw.PaintImage.fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1375
1405
  }
1376
1406
  if (!data.transform) {
1377
- if (box.x || box.y) translate(data.transform = get$2(), box.x, box.y);
1407
+ if (box.x || box.y) translate$1(data.transform = get$3(), box.x, box.y);
1378
1408
  }
1379
- data.width = width;
1380
- data.height = height;
1381
1409
  if (scaleX) {
1382
1410
  data.scaleX = scaleX;
1383
1411
  data.scaleY = scaleY;
1384
1412
  }
1385
- if (opacity) data.opacity = opacity;
1413
+ if (opacity && opacity < 1) data.opacity = opacity;
1386
1414
  if (filters) data.filters = filters;
1387
1415
  if (repeat) data.repeat = core.isString(repeat) ? repeat === "x" ? "repeat-x" : "repeat-y" : "repeat";
1388
1416
  return data;
@@ -1404,180 +1432,82 @@ function getGapValue(gap, size, totalSize, rows) {
1404
1432
  return gap === "auto" ? value < 0 ? 0 : value : value;
1405
1433
  }
1406
1434
 
1407
- let cache, box = new core.Bounds;
1408
-
1409
- const {isSame: isSame} = core.BoundsHelper;
1410
-
1411
- function image(ui, attrName, paint, boxBounds, firstUse) {
1412
- let leafPaint, event;
1413
- const image = core.ImageManager.get(paint);
1414
- if (cache && paint === cache.paint && isSame(boxBounds, cache.boxBounds)) {
1415
- leafPaint = cache.leafPaint;
1416
- } else {
1417
- leafPaint = {
1418
- type: paint.type,
1419
- image: image
1420
- };
1421
- if (image.hasAlphaPixel) leafPaint.isTransparent = true;
1422
- cache = image.use > 1 ? {
1423
- leafPaint: leafPaint,
1424
- paint: paint,
1425
- boxBounds: box.set(boxBounds)
1426
- } : null;
1427
- }
1428
- if (firstUse || image.loading) event = {
1429
- image: image,
1430
- attrName: attrName,
1431
- attrValue: paint
1432
- };
1433
- if (image.ready) {
1434
- checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds);
1435
- if (firstUse) {
1436
- onLoad(ui, event);
1437
- onLoadSuccess(ui, event);
1438
- }
1439
- } else if (image.error) {
1440
- if (firstUse) onLoadError(ui, event, image.error);
1441
- } else {
1442
- if (firstUse) {
1443
- ignoreRender(ui, true);
1444
- onLoad(ui, event);
1445
- }
1446
- leafPaint.loadId = image.load(() => {
1447
- ignoreRender(ui, false);
1448
- if (!ui.destroyed) {
1449
- if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1450
- if (image.hasAlphaPixel) ui.__layout.hitCanvasChanged = true;
1451
- ui.forceUpdate("surface");
1452
- }
1453
- onLoadSuccess(ui, event);
1454
- }
1455
- leafPaint.loadId = undefined;
1456
- }, error => {
1457
- ignoreRender(ui, false);
1458
- onLoadError(ui, event, error);
1459
- leafPaint.loadId = undefined;
1460
- });
1461
- if (ui.placeholderColor) {
1462
- if (!ui.placeholderDelay) image.isPlacehold = true; else setTimeout(() => {
1463
- if (!image.ready) {
1464
- image.isPlacehold = true;
1465
- ui.forceUpdate("surface");
1466
- }
1467
- }, ui.placeholderDelay);
1468
- }
1469
- }
1470
- return leafPaint;
1471
- }
1472
-
1473
- function checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds) {
1474
- if (attrName === "fill" && !ui.__.__naturalWidth) {
1475
- const data = ui.__;
1476
- data.__naturalWidth = image.width / data.pixelRatio;
1477
- data.__naturalHeight = image.height / data.pixelRatio;
1478
- if (data.__autoSide) {
1479
- ui.forceUpdate("width");
1480
- if (ui.__proxyData) {
1481
- ui.setProxyAttr("width", data.width);
1482
- ui.setProxyAttr("height", data.height);
1483
- }
1484
- return false;
1485
- }
1486
- }
1487
- if (!leafPaint.data) createData(leafPaint, image, paint, boxBounds);
1488
- return true;
1489
- }
1490
-
1491
- function onLoad(ui, event) {
1492
- emit(ui, core.ImageEvent.LOAD, event);
1493
- }
1435
+ let origin = {}, tempMatrix$1 = core.getMatrixData();
1494
1436
 
1495
- function onLoadSuccess(ui, event) {
1496
- emit(ui, core.ImageEvent.LOADED, event);
1497
- }
1437
+ const {get: get$2, set: set, rotateOfOuter: rotateOfOuter$1, translate: translate, scaleOfOuter: scaleOfOuter$1, multiplyParent: multiplyParent, scale: scaleHelper, rotate: rotate, skew: skewHelper} = core.MatrixHelper;
1498
1438
 
1499
- function onLoadError(ui, event, error) {
1500
- event.error = error;
1501
- ui.forceUpdate("surface");
1502
- emit(ui, core.ImageEvent.ERROR, event);
1439
+ function stretchMode(data, box, scaleX, scaleY) {
1440
+ const transform = get$2(), {x: x, y: y} = box;
1441
+ if (x || y) translate(transform, x, y); else transform.onlyScale = true;
1442
+ scaleHelper(transform, scaleX, scaleY);
1443
+ data.transform = transform;
1503
1444
  }
1504
1445
 
1505
- function emit(ui, type, data) {
1506
- if (ui.hasEvent(type)) ui.emitEvent(new core.ImageEvent(type, data));
1446
+ function fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation) {
1447
+ const transform = get$2();
1448
+ translate(transform, box.x + x, box.y + y);
1449
+ scaleHelper(transform, scaleX, scaleY);
1450
+ if (rotation) rotateOfOuter$1(transform, {
1451
+ x: box.x + box.width / 2,
1452
+ y: box.y + box.height / 2
1453
+ }, rotation);
1454
+ data.transform = transform;
1507
1455
  }
1508
1456
 
1509
- function ignoreRender(ui, value) {
1510
- const {leafer: leafer} = ui;
1511
- if (leafer && leafer.viewReady) leafer.renderer.ignore = value;
1457
+ function clipMode(data, box, x, y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY) {
1458
+ const transform = get$2();
1459
+ layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1460
+ if (clipScaleX) {
1461
+ if (rotation || skew) {
1462
+ set(tempMatrix$1);
1463
+ scaleOfOuter$1(tempMatrix$1, box, clipScaleX, clipScaleY);
1464
+ multiplyParent(transform, tempMatrix$1);
1465
+ } else scaleOfOuter$1(transform, box, clipScaleX, clipScaleY);
1466
+ }
1467
+ data.transform = transform;
1512
1468
  }
1513
1469
 
1514
- const {get: get$1, scale: scale, copy: copy$1} = core.MatrixHelper;
1470
+ function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, skew, align, freeTransform) {
1471
+ const transform = get$2();
1472
+ if (freeTransform) {
1473
+ layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1474
+ } else {
1475
+ if (rotation) {
1476
+ if (align === "center") {
1477
+ rotateOfOuter$1(transform, {
1478
+ x: width / 2,
1479
+ y: height / 2
1480
+ }, rotation);
1481
+ } else {
1482
+ rotate(transform, rotation);
1483
+ switch (rotation) {
1484
+ case 90:
1485
+ translate(transform, height, 0);
1486
+ break;
1515
1487
 
1516
- const {floor: floor, ceil: ceil, max: max$1, abs: abs$1} = Math;
1488
+ case 180:
1489
+ translate(transform, width, height);
1490
+ break;
1517
1491
 
1518
- function createPattern(ui, paint, pixelRatio) {
1519
- let {scaleX: scaleX, scaleY: scaleY} = ui.getRenderScaleData(true, paint.scaleFixed);
1520
- const id = scaleX + "-" + scaleY + "-" + pixelRatio;
1521
- if (paint.patternId !== id && !ui.destroyed) {
1522
- const {image: image, data: data} = paint;
1523
- let imageScale, imageMatrix, {width: width, height: height, scaleX: sx, scaleY: sy, transform: transform, repeat: repeat, gap: gap} = data;
1524
- scaleX *= pixelRatio;
1525
- scaleY *= pixelRatio;
1526
- if (sx) {
1527
- sx = abs$1(sx);
1528
- sy = abs$1(sy);
1529
- imageMatrix = get$1();
1530
- copy$1(imageMatrix, transform);
1531
- scale(imageMatrix, 1 / sx, 1 / sy);
1532
- scaleX *= sx;
1533
- scaleY *= sy;
1534
- }
1535
- width *= scaleX;
1536
- height *= scaleY;
1537
- const size = width * height;
1538
- if (!repeat) {
1539
- if (size > core.Platform.image.maxCacheSize) return false;
1540
- }
1541
- let maxSize = core.Platform.image.maxPatternSize;
1542
- if (image.isSVG) {
1543
- const ws = width / image.width;
1544
- if (ws > 1) imageScale = ws / ceil(ws);
1545
- } else {
1546
- const imageSize = image.width * image.height;
1547
- if (maxSize > imageSize) maxSize = imageSize;
1548
- }
1549
- if (size > maxSize) imageScale = Math.sqrt(size / maxSize);
1550
- if (imageScale) {
1551
- scaleX /= imageScale;
1552
- scaleY /= imageScale;
1553
- width /= imageScale;
1554
- height /= imageScale;
1555
- }
1556
- if (sx) {
1557
- scaleX /= sx;
1558
- scaleY /= sy;
1559
- }
1560
- const xGap = gap && gap.x * scaleX;
1561
- const yGap = gap && gap.y * scaleY;
1562
- if (transform || scaleX !== 1 || scaleY !== 1) {
1563
- const canvasWidth = width + (xGap || 0);
1564
- const canvasHeight = height + (yGap || 0);
1565
- scaleX /= canvasWidth / max$1(floor(canvasWidth), 1);
1566
- scaleY /= canvasHeight / max$1(floor(canvasHeight), 1);
1567
- if (!imageMatrix) {
1568
- imageMatrix = get$1();
1569
- if (transform) copy$1(imageMatrix, transform);
1492
+ case 270:
1493
+ translate(transform, 0, width);
1494
+ break;
1495
+ }
1570
1496
  }
1571
- scale(imageMatrix, 1 / scaleX, 1 / scaleY);
1572
1497
  }
1573
- const canvas = image.getCanvas(width, height, data.opacity, data.filters, xGap, yGap, ui.leafer && ui.leafer.config.smooth);
1574
- const pattern = image.getPattern(canvas, repeat || (core.Platform.origin.noRepeat || "no-repeat"), imageMatrix, paint);
1575
- paint.style = pattern;
1576
- paint.patternId = id;
1577
- return true;
1578
- } else {
1579
- return false;
1498
+ origin.x = box.x + x;
1499
+ origin.y = box.y + y;
1500
+ translate(transform, origin.x, origin.y);
1501
+ if (scaleX) scaleOfOuter$1(transform, origin, scaleX, scaleY);
1580
1502
  }
1503
+ data.transform = transform;
1504
+ }
1505
+
1506
+ function layout(transform, box, x, y, scaleX, scaleY, rotation, skew) {
1507
+ if (rotation) rotate(transform, rotation);
1508
+ if (skew) skewHelper(transform, skew.x, skew.y);
1509
+ if (scaleX) scaleHelper(transform, scaleX, scaleY);
1510
+ translate(transform, box.x + x, box.y + y);
1581
1511
  }
1582
1512
 
1583
1513
  function __awaiter(thisArg, _arguments, P, generator) {
@@ -1613,58 +1543,116 @@ typeof SuppressedError === "function" ? SuppressedError : function(error, suppre
1613
1543
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
1614
1544
  };
1615
1545
 
1616
- function checkImage(ui, canvas, paint, allowDraw) {
1617
- const {scaleX: scaleX, scaleY: scaleY} = ui.getRenderScaleData(true, paint.scaleFixed);
1618
- const {pixelRatio: pixelRatio} = canvas, {data: data} = paint;
1619
- if (!data || paint.patternId === scaleX + "-" + scaleY + "-" + pixelRatio && !draw.Export.running) {
1546
+ const {get: get$1, scale: scale, copy: copy$1} = core.MatrixHelper;
1547
+
1548
+ const {getFloorScale: getFloorScale} = core.MathHelper, {abs: abs$1} = Math;
1549
+
1550
+ function createPatternTask(paint, ui, canvas, renderOptions) {
1551
+ if (!paint.patternTask) {
1552
+ paint.patternTask = core.ImageManager.patternTasker.add(() => __awaiter(this, void 0, void 0, function*() {
1553
+ paint.patternTask = null;
1554
+ if (canvas.bounds.hit(ui.__nowWorld)) draw.PaintImage.createPattern(paint, ui, canvas, renderOptions);
1555
+ ui.forceUpdate("surface");
1556
+ }), 300);
1557
+ }
1558
+ }
1559
+
1560
+ function createPattern(paint, ui, canvas, renderOptions) {
1561
+ let {scaleX: scaleX, scaleY: scaleY} = draw.PaintImage.getImageRenderScaleData(paint, ui, canvas, renderOptions), id = scaleX + "-" + scaleY;
1562
+ if (paint.patternId !== id && !ui.destroyed) {
1563
+ if (!(core.Platform.image.isLarge(paint.image, scaleX, scaleY) && !paint.data.repeat)) {
1564
+ const {image: image, data: data} = paint, {transform: transform, gap: gap} = data, fixScale = draw.PaintImage.getPatternFixScale(paint, scaleX, scaleY);
1565
+ let imageMatrix, xGap, yGap, {width: width, height: height} = image;
1566
+ if (fixScale) scaleX *= fixScale, scaleY *= fixScale;
1567
+ width *= scaleX;
1568
+ height *= scaleY;
1569
+ if (gap) {
1570
+ xGap = gap.x * scaleX / abs$1(data.scaleX || 1);
1571
+ yGap = gap.y * scaleY / abs$1(data.scaleY || 1);
1572
+ }
1573
+ if (transform || scaleX !== 1 || scaleY !== 1) {
1574
+ scaleX *= getFloorScale(width + (xGap || 0));
1575
+ scaleY *= getFloorScale(height + (yGap || 0));
1576
+ imageMatrix = get$1();
1577
+ if (transform) copy$1(imageMatrix, transform);
1578
+ scale(imageMatrix, 1 / scaleX, 1 / scaleY);
1579
+ }
1580
+ const imageCanvas = image.getCanvas(width, height, data.opacity, data.filters, xGap, yGap, ui.leafer && ui.leafer.config.smooth);
1581
+ const pattern = image.getPattern(imageCanvas, data.repeat || (core.Platform.origin.noRepeat || "no-repeat"), imageMatrix, paint);
1582
+ paint.style = pattern;
1583
+ paint.patternId = id;
1584
+ }
1585
+ }
1586
+ }
1587
+
1588
+ function getPatternFixScale(paint, imageScaleX, imageScaleY) {
1589
+ const {image: image} = paint;
1590
+ let fixScale, maxSize = core.Platform.image.maxPatternSize, imageSize = image.width * image.height;
1591
+ if (image.isSVG) {
1592
+ if (imageScaleX > 1) fixScale = Math.ceil(imageScaleX) / imageScaleX;
1593
+ } else {
1594
+ if (maxSize > imageSize) maxSize = imageSize;
1595
+ }
1596
+ if ((imageSize *= imageScaleX * imageScaleY) > maxSize) fixScale = Math.sqrt(maxSize / imageSize);
1597
+ return fixScale;
1598
+ }
1599
+
1600
+ function checkImage(paint, drawImage, ui, canvas, renderOptions) {
1601
+ const {scaleX: scaleX, scaleY: scaleY} = draw.PaintImage.getImageRenderScaleData(paint, ui, canvas, renderOptions);
1602
+ const {image: image, data: data} = paint, {exporting: exporting} = renderOptions;
1603
+ if (!data || paint.patternId === scaleX + "-" + scaleY && !exporting) {
1620
1604
  return false;
1621
1605
  } else {
1622
- if (allowDraw) {
1606
+ if (drawImage) {
1623
1607
  if (data.repeat) {
1624
- allowDraw = false;
1625
- } else if (!(paint.changeful || core.Platform.name === "miniapp" && core.ResizeEvent.isResizing(ui) || draw.Export.running)) {
1626
- let {width: width, height: height} = data;
1627
- width *= scaleX * pixelRatio;
1628
- height *= scaleY * pixelRatio;
1629
- if (data.scaleX) {
1630
- width *= data.scaleX;
1631
- height *= data.scaleY;
1632
- }
1633
- allowDraw = width * height > core.Platform.image.maxCacheSize;
1608
+ drawImage = false;
1609
+ } else if (!(paint.changeful || core.Platform.name === "miniapp" && core.ResizeEvent.isResizing(ui) || exporting)) {
1610
+ drawImage = core.Platform.image.isLarge(image, scaleX, scaleY);
1634
1611
  }
1635
1612
  }
1636
- if (allowDraw) {
1613
+ if (drawImage) {
1637
1614
  if (ui.__.__isFastShadow) {
1638
1615
  canvas.fillStyle = paint.style || "#000";
1639
1616
  canvas.fill();
1640
1617
  }
1641
- drawImage(ui, canvas, paint, data);
1618
+ draw.PaintImage.drawImage(paint, scaleX, scaleY, ui, canvas, renderOptions);
1642
1619
  return true;
1643
1620
  } else {
1644
- if (!paint.style || paint.sync || draw.Export.running) {
1645
- createPattern(ui, paint, pixelRatio);
1646
- } else {
1647
- if (!paint.patternTask) {
1648
- paint.patternTask = core.ImageManager.patternTasker.add(() => __awaiter(this, void 0, void 0, function*() {
1649
- paint.patternTask = null;
1650
- if (canvas.bounds.hit(ui.__nowWorld)) createPattern(ui, paint, pixelRatio);
1651
- ui.forceUpdate("surface");
1652
- }), 300);
1653
- }
1654
- }
1621
+ if (!paint.style || paint.sync || exporting) draw.PaintImage.createPattern(paint, ui, canvas, renderOptions); else draw.PaintImage.createPatternTask(paint, ui, canvas, renderOptions);
1655
1622
  return false;
1656
1623
  }
1657
1624
  }
1658
1625
  }
1659
1626
 
1660
- function drawImage(ui, canvas, paint, data) {
1661
- canvas.save();
1662
- canvas.clipUI(ui);
1663
- if (paint.blendMode) canvas.blendMode = paint.blendMode;
1664
- if (data.opacity) canvas.opacity *= data.opacity;
1665
- if (data.transform) canvas.transform(data.transform);
1666
- canvas.drawImage(paint.image.getFull(data.filters), 0, 0, data.width, data.height);
1667
- canvas.restore();
1627
+ function drawImage(paint, _imageScaleX, _imageScaleY, ui, canvas, _renderOptions) {
1628
+ const {data: data, image: image, blendMode: blendMode} = paint, {opacity: opacity, transform: transform} = data, view = image.getFull(data.filters), u = ui.__;
1629
+ let {width: width, height: height} = image, clipUI;
1630
+ if (transform && !transform.onlyScale || (clipUI = u.path || u.cornerRadius) || opacity || blendMode) {
1631
+ canvas.save();
1632
+ clipUI && canvas.clipUI(ui);
1633
+ blendMode && (canvas.blendMode = blendMode);
1634
+ opacity && (canvas.opacity *= opacity);
1635
+ transform && canvas.transform(transform);
1636
+ canvas.drawImage(view, 0, 0, width, height);
1637
+ canvas.restore();
1638
+ } else {
1639
+ if (data.scaleX) width *= data.scaleX, height *= data.scaleY;
1640
+ canvas.drawImage(view, 0, 0, width, height);
1641
+ }
1642
+ }
1643
+
1644
+ function getImageRenderScaleData(paint, ui, canvas, _renderOptions) {
1645
+ const scaleData = ui.getRenderScaleData(true, paint.scaleFixed), {data: data} = paint;
1646
+ if (canvas) {
1647
+ const {pixelRatio: pixelRatio} = canvas;
1648
+ scaleData.scaleX *= pixelRatio;
1649
+ scaleData.scaleY *= pixelRatio;
1650
+ }
1651
+ if (data && data.scaleX) {
1652
+ scaleData.scaleX *= Math.abs(data.scaleX);
1653
+ scaleData.scaleY *= Math.abs(data.scaleY);
1654
+ }
1655
+ return scaleData;
1668
1656
  }
1669
1657
 
1670
1658
  function recycleImage(attrName, data) {
@@ -1696,8 +1684,12 @@ function recycleImage(attrName, data) {
1696
1684
  const PaintImageModule = {
1697
1685
  image: image,
1698
1686
  checkImage: checkImage,
1699
- createPattern: createPattern,
1687
+ drawImage: drawImage,
1688
+ getImageRenderScaleData: getImageRenderScaleData,
1700
1689
  recycleImage: recycleImage,
1690
+ createPatternTask: createPatternTask,
1691
+ createPattern: createPattern,
1692
+ getPatternFixScale: getPatternFixScale,
1701
1693
  createData: createData,
1702
1694
  getPatternData: getPatternData,
1703
1695
  stretchMode: stretchMode,
@@ -2153,10 +2145,8 @@ function createRows(drawData, content, style) {
2153
2145
  bounds = drawData.bounds;
2154
2146
  findMaxWidth = !bounds.width && !style.autoSizeAlign;
2155
2147
  const {__letterSpacing: __letterSpacing, paraIndent: paraIndent, textCase: textCase} = style;
2156
- const {canvas: canvas} = core.Platform;
2157
- const {width: width, height: height} = bounds;
2158
- const charMode = width || height || __letterSpacing || textCase !== "none";
2159
- if (charMode) {
2148
+ const {canvas: canvas} = core.Platform, {width: width} = bounds;
2149
+ if (style.__isCharMode) {
2160
2150
  const wrap = style.textWrap !== "none";
2161
2151
  const breakAll = style.textWrap === "break";
2162
2152
  paraStart = true;
@@ -2285,12 +2275,19 @@ const TextMode = 2;
2285
2275
  function layoutChar(drawData, style, width, _height) {
2286
2276
  const {rows: rows} = drawData;
2287
2277
  const {textAlign: textAlign, paraIndent: paraIndent, letterSpacing: letterSpacing} = style;
2288
- let charX, addWordWidth, indentWidth, mode, wordChar, wordsLength;
2278
+ const justifyLast = width && textAlign.includes("both");
2279
+ const justify = justifyLast || width && textAlign.includes("justify");
2280
+ const justifyLetter = justify && textAlign.includes("letter");
2281
+ let charX, remainingWidth, addWordWidth, addLetterWidth, indentWidth, mode, wordChar, wordsLength, isLastWord, canJustify;
2289
2282
  rows.forEach(row => {
2290
2283
  if (row.words) {
2291
2284
  indentWidth = paraIndent && row.paraStart ? paraIndent : 0, wordsLength = row.words.length;
2292
- addWordWidth = width && (textAlign === "justify" || textAlign === "both") && wordsLength > 1 ? (width - row.width - indentWidth) / (wordsLength - 1) : 0;
2293
- mode = letterSpacing || row.isOverflow ? CharMode : addWordWidth > .01 ? WordMode : TextMode;
2285
+ if (justify) {
2286
+ canJustify = !row.paraEnd || justifyLast;
2287
+ remainingWidth = width - row.width - indentWidth;
2288
+ if (justifyLetter) addLetterWidth = remainingWidth / (row.words.reduce((total, item) => total + item.data.length, 0) - 1); else addWordWidth = wordsLength > 1 ? remainingWidth / (wordsLength - 1) : 0;
2289
+ }
2290
+ mode = letterSpacing || row.isOverflow || justifyLetter ? CharMode : addWordWidth ? WordMode : TextMode;
2294
2291
  if (row.isOverflow && !letterSpacing) row.textMode = true;
2295
2292
  if (mode === TextMode) {
2296
2293
  row.x += indentWidth;
@@ -2308,11 +2305,15 @@ function layoutChar(drawData, style, width, _height) {
2308
2305
  charX = toWordChar(word.data, charX, wordChar);
2309
2306
  if (row.isOverflow || wordChar.char !== " ") row.data.push(wordChar);
2310
2307
  } else {
2311
- charX = toChar(word.data, charX, row.data, row.isOverflow);
2308
+ charX = toChar(word.data, charX, row.data, row.isOverflow, canJustify && addLetterWidth);
2312
2309
  }
2313
- if (addWordWidth && (!row.paraEnd || textAlign === "both") && index !== wordsLength - 1) {
2314
- charX += addWordWidth;
2315
- row.width += addWordWidth;
2310
+ if (canJustify) {
2311
+ isLastWord = index === wordsLength - 1;
2312
+ if (addWordWidth) {
2313
+ if (!isLastWord) charX += addWordWidth, row.width += addWordWidth;
2314
+ } else if (addLetterWidth) {
2315
+ row.width += addLetterWidth * (word.data.length - (isLastWord ? 1 : 0));
2316
+ }
2316
2317
  }
2317
2318
  });
2318
2319
  }
@@ -2338,13 +2339,14 @@ function toWordChar(data, charX, wordChar) {
2338
2339
  return charX;
2339
2340
  }
2340
2341
 
2341
- function toChar(data, charX, rowData, isOverflow) {
2342
+ function toChar(data, charX, rowData, isOverflow, addLetterWidth) {
2342
2343
  data.forEach(char => {
2343
2344
  if (isOverflow || char.char !== " ") {
2344
2345
  char.x = charX;
2345
2346
  rowData.push(char);
2346
2347
  }
2347
2348
  charX += char.width;
2349
+ addLetterWidth && (charX += addLetterWidth);
2348
2350
  });
2349
2351
  return charX;
2350
2352
  }
@@ -2486,10 +2488,10 @@ function getDrawData(content, style) {
2486
2488
  let x = 0, y = 0;
2487
2489
  let width = style.__getInput("width") || 0;
2488
2490
  let height = style.__getInput("height") || 0;
2489
- const {textDecoration: textDecoration, __font: __font, __padding: padding} = style;
2491
+ const {__padding: padding} = style;
2490
2492
  if (padding) {
2491
- if (width) x = padding[left], width -= padding[right] + padding[left]; else if (!style.autoSizeAlign) x = padding[left];
2492
- if (height) y = padding[top], height -= padding[top] + padding[bottom]; else if (!style.autoSizeAlign) y = padding[top];
2493
+ if (width) x = padding[left], width -= padding[right] + padding[left], !width && (width = .01); else if (!style.autoSizeAlign) x = padding[left];
2494
+ if (height) y = padding[top], height -= padding[top] + padding[bottom], !height && (height = .01); else if (!style.autoSizeAlign) y = padding[top];
2493
2495
  }
2494
2496
  const drawData = {
2495
2497
  bounds: {
@@ -2500,14 +2502,14 @@ function getDrawData(content, style) {
2500
2502
  },
2501
2503
  rows: [],
2502
2504
  paraNumber: 0,
2503
- font: core.Platform.canvas.font = __font
2505
+ font: core.Platform.canvas.font = style.__font
2504
2506
  };
2505
2507
  createRows(drawData, content, style);
2506
2508
  if (padding) padAutoText(padding, drawData, style, width, height);
2507
2509
  layoutText(drawData, style);
2508
- layoutChar(drawData, style, width);
2510
+ if (style.__isCharMode) layoutChar(drawData, style, width);
2509
2511
  if (drawData.overflow) clipText(drawData, style, x, width);
2510
- if (textDecoration !== "none") decorationText(drawData, style);
2512
+ if (style.textDecoration !== "none") decorationText(drawData, style);
2511
2513
  return drawData;
2512
2514
  }
2513
2515