@leafer-draw/node 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/node.cjs CHANGED
@@ -687,36 +687,19 @@ core.Platform.render = function(target, canvas, options) {
687
687
  if (options.topList.length) options.topList.forEach(item => item.__render(canvas, topOptions));
688
688
  };
689
689
 
690
- function fillText(ui, canvas) {
691
- const data = ui.__, {rows: rows, decorationY: decorationY} = data.__textDrawData;
692
- if (data.__isPlacehold && data.placeholderColor) canvas.fillStyle = data.placeholderColor;
693
- let row;
694
- for (let i = 0, len = rows.length; i < len; i++) {
695
- row = rows[i];
696
- if (row.text) canvas.fillText(row.text, row.x, row.y); else if (row.data) row.data.forEach(charData => {
697
- canvas.fillText(charData.char, charData.x, row.y);
698
- });
699
- }
700
- if (decorationY) {
701
- const {decorationColor: decorationColor, decorationHeight: decorationHeight} = data.__textDrawData;
702
- if (decorationColor) canvas.fillStyle = decorationColor;
703
- rows.forEach(row => decorationY.forEach(value => canvas.fillRect(row.x, row.y + value, row.width, decorationHeight)));
704
- }
705
- }
706
-
707
- function fill(fill, ui, canvas) {
690
+ function fill(fill, ui, canvas, renderOptions) {
708
691
  canvas.fillStyle = fill;
709
- fillPathOrText(ui, canvas);
692
+ fillPathOrText(ui, canvas, renderOptions);
710
693
  }
711
694
 
712
- function fills(fills, ui, canvas) {
695
+ function fills(fills, ui, canvas, renderOptions) {
713
696
  let item;
714
697
  for (let i = 0, len = fills.length; i < len; i++) {
715
698
  item = fills[i];
716
699
  if (item.image) {
717
- if (draw.PaintImage.checkImage(ui, canvas, item, !ui.__.__font)) continue;
700
+ if (draw.PaintImage.checkImage(item, !ui.__.__font, ui, canvas, renderOptions)) continue;
718
701
  if (!item.style) {
719
- if (!i && item.image.isPlacehold) ui.drawImagePlaceholder(canvas, item.image);
702
+ if (!i && item.image.isPlacehold) ui.drawImagePlaceholder(item.image, canvas, renderOptions);
720
703
  continue;
721
704
  }
722
705
  }
@@ -729,60 +712,137 @@ function fills(fills, ui, canvas) {
729
712
  if (item.scaleFixed === true || item.scaleFixed === "zoom-in" && scaleX > 1 && scaleY > 1) canvas.scale(1 / scaleX, 1 / scaleY);
730
713
  }
731
714
  if (item.blendMode) canvas.blendMode = item.blendMode;
732
- fillPathOrText(ui, canvas);
715
+ fillPathOrText(ui, canvas, renderOptions);
733
716
  canvas.restore();
734
717
  } else {
735
718
  if (item.blendMode) {
736
719
  canvas.saveBlendMode(item.blendMode);
737
- fillPathOrText(ui, canvas);
720
+ fillPathOrText(ui, canvas, renderOptions);
738
721
  canvas.restoreBlendMode();
739
- } else fillPathOrText(ui, canvas);
722
+ } else fillPathOrText(ui, canvas, renderOptions);
740
723
  }
741
724
  }
742
725
  }
743
726
 
744
- function fillPathOrText(ui, canvas) {
745
- ui.__.__font ? fillText(ui, canvas) : ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill();
727
+ function fillPathOrText(ui, canvas, renderOptions) {
728
+ ui.__.__font ? draw.Paint.fillText(ui, canvas, renderOptions) : ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill();
729
+ }
730
+
731
+ function fillText(ui, canvas, _renderOptions) {
732
+ const data = ui.__, {rows: rows, decorationY: decorationY} = data.__textDrawData;
733
+ if (data.__isPlacehold && data.placeholderColor) canvas.fillStyle = data.placeholderColor;
734
+ let row;
735
+ for (let i = 0, len = rows.length; i < len; i++) {
736
+ row = rows[i];
737
+ if (row.text) canvas.fillText(row.text, row.x, row.y); else if (row.data) row.data.forEach(charData => {
738
+ canvas.fillText(charData.char, charData.x, row.y);
739
+ });
740
+ }
741
+ if (decorationY) {
742
+ const {decorationColor: decorationColor, decorationHeight: decorationHeight} = data.__textDrawData;
743
+ if (decorationColor) canvas.fillStyle = decorationColor;
744
+ rows.forEach(row => decorationY.forEach(value => canvas.fillRect(row.x, row.y + value, row.width, decorationHeight)));
745
+ }
746
+ }
747
+
748
+ function stroke(stroke, ui, canvas, renderOptions) {
749
+ const data = ui.__;
750
+ if (!data.__strokeWidth) return;
751
+ if (data.__font) {
752
+ draw.Paint.strokeText(stroke, ui, canvas, renderOptions);
753
+ } else {
754
+ switch (data.strokeAlign) {
755
+ case "center":
756
+ drawCenter$1(stroke, 1, ui, canvas, renderOptions);
757
+ break;
758
+
759
+ case "inside":
760
+ drawInside(stroke, ui, canvas, renderOptions);
761
+ break;
762
+
763
+ case "outside":
764
+ drawOutside(stroke, ui, canvas, renderOptions);
765
+ break;
766
+ }
767
+ }
768
+ }
769
+
770
+ function strokes(strokes, ui, canvas, renderOptions) {
771
+ draw.Paint.stroke(strokes, ui, canvas, renderOptions);
772
+ }
773
+
774
+ function drawCenter$1(stroke, strokeWidthScale, ui, canvas, renderOptions) {
775
+ const data = ui.__;
776
+ if (core.isObject(stroke)) {
777
+ draw.Paint.drawStrokesStyle(stroke, strokeWidthScale, false, ui, canvas, renderOptions);
778
+ } else {
779
+ canvas.setStroke(stroke, data.__strokeWidth * strokeWidthScale, data);
780
+ canvas.stroke();
781
+ }
782
+ if (data.__useArrow) draw.Paint.strokeArrow(stroke, ui, canvas, renderOptions);
783
+ }
784
+
785
+ function drawInside(stroke, ui, canvas, renderOptions) {
786
+ canvas.save();
787
+ canvas.clipUI(ui);
788
+ drawCenter$1(stroke, 2, ui, canvas, renderOptions);
789
+ canvas.restore();
790
+ }
791
+
792
+ function drawOutside(stroke, ui, canvas, renderOptions) {
793
+ const data = ui.__;
794
+ if (data.__fillAfterStroke) {
795
+ drawCenter$1(stroke, 2, ui, canvas, renderOptions);
796
+ } else {
797
+ const {renderBounds: renderBounds} = ui.__layout;
798
+ const out = canvas.getSameCanvas(true, true);
799
+ ui.__drawRenderPath(out);
800
+ drawCenter$1(stroke, 2, ui, out, renderOptions);
801
+ out.clipUI(data);
802
+ out.clearWorld(renderBounds);
803
+ core.LeafHelper.copyCanvasByWorld(ui, canvas, out);
804
+ out.recycle(ui.__nowWorld);
805
+ }
746
806
  }
747
807
 
748
- function strokeText(stroke, ui, canvas) {
808
+ function strokeText(stroke, ui, canvas, renderOptions) {
749
809
  switch (ui.__.strokeAlign) {
750
810
  case "center":
751
- drawCenter$1(stroke, 1, ui, canvas);
811
+ drawCenter(stroke, 1, ui, canvas, renderOptions);
752
812
  break;
753
813
 
754
814
  case "inside":
755
- drawAlign(stroke, "inside", ui, canvas);
815
+ drawAlign(stroke, "inside", ui, canvas, renderOptions);
756
816
  break;
757
817
 
758
818
  case "outside":
759
- ui.__.__fillAfterStroke ? drawCenter$1(stroke, 2, ui, canvas) : drawAlign(stroke, "outside", ui, canvas);
819
+ ui.__.__fillAfterStroke ? drawCenter(stroke, 2, ui, canvas, renderOptions) : drawAlign(stroke, "outside", ui, canvas, renderOptions);
760
820
  break;
761
821
  }
762
822
  }
763
823
 
764
- function drawCenter$1(stroke, strokeWidthScale, ui, canvas) {
824
+ function drawCenter(stroke, strokeWidthScale, ui, canvas, renderOptions) {
765
825
  const data = ui.__;
766
826
  if (core.isObject(stroke)) {
767
- drawStrokesStyle(stroke, strokeWidthScale, true, ui, canvas);
827
+ draw.Paint.drawStrokesStyle(stroke, strokeWidthScale, true, ui, canvas, renderOptions);
768
828
  } else {
769
829
  canvas.setStroke(stroke, data.__strokeWidth * strokeWidthScale, data);
770
- drawTextStroke(ui, canvas);
830
+ draw.Paint.drawTextStroke(ui, canvas, renderOptions);
771
831
  }
772
832
  }
773
833
 
774
- function drawAlign(stroke, align, ui, canvas) {
834
+ function drawAlign(stroke, align, ui, canvas, renderOptions) {
775
835
  const out = canvas.getSameCanvas(true, true);
776
836
  out.font = ui.__.__font;
777
- drawCenter$1(stroke, 2, ui, out);
837
+ drawCenter(stroke, 2, ui, out, renderOptions);
778
838
  out.blendMode = align === "outside" ? "destination-out" : "destination-in";
779
- fillText(ui, out);
839
+ draw.Paint.fillText(ui, out, renderOptions);
780
840
  out.blendMode = "normal";
781
841
  core.LeafHelper.copyCanvasByWorld(ui, canvas, out);
782
842
  out.recycle(ui.__nowWorld);
783
843
  }
784
844
 
785
- function drawTextStroke(ui, canvas) {
845
+ function drawTextStroke(ui, canvas, _renderOptions) {
786
846
  let row, data = ui.__.__textDrawData;
787
847
  const {rows: rows, decorationY: decorationY} = data;
788
848
  for (let i = 0, len = rows.length; i < len; i++) {
@@ -797,13 +857,13 @@ function drawTextStroke(ui, canvas) {
797
857
  }
798
858
  }
799
859
 
800
- function drawStrokesStyle(strokes, strokeWidthScale, isText, ui, canvas) {
860
+ function drawStrokesStyle(strokes, strokeWidthScale, isText, ui, canvas, renderOptions) {
801
861
  let item;
802
862
  const data = ui.__, {__hasMultiStrokeStyle: __hasMultiStrokeStyle} = data;
803
863
  __hasMultiStrokeStyle || canvas.setStroke(undefined, data.__strokeWidth * strokeWidthScale, data);
804
864
  for (let i = 0, len = strokes.length; i < len; i++) {
805
865
  item = strokes[i];
806
- if (item.image && draw.PaintImage.checkImage(ui, canvas, item, false)) continue;
866
+ if (item.image && draw.PaintImage.checkImage(item, false, ui, canvas, renderOptions)) continue;
807
867
  if (item.style) {
808
868
  if (__hasMultiStrokeStyle) {
809
869
  const {strokeStyle: strokeStyle} = item;
@@ -811,75 +871,15 @@ function drawStrokesStyle(strokes, strokeWidthScale, isText, ui, canvas) {
811
871
  } else canvas.strokeStyle = item.style;
812
872
  if (item.blendMode) {
813
873
  canvas.saveBlendMode(item.blendMode);
814
- isText ? drawTextStroke(ui, canvas) : canvas.stroke();
874
+ isText ? draw.Paint.drawTextStroke(ui, canvas, renderOptions) : canvas.stroke();
815
875
  canvas.restoreBlendMode();
816
876
  } else {
817
- isText ? drawTextStroke(ui, canvas) : canvas.stroke();
877
+ isText ? draw.Paint.drawTextStroke(ui, canvas, renderOptions) : canvas.stroke();
818
878
  }
819
879
  }
820
880
  }
821
881
  }
822
882
 
823
- function stroke(stroke, ui, canvas) {
824
- const data = ui.__;
825
- if (!data.__strokeWidth) return;
826
- if (data.__font) {
827
- strokeText(stroke, ui, canvas);
828
- } else {
829
- switch (data.strokeAlign) {
830
- case "center":
831
- drawCenter(stroke, 1, ui, canvas);
832
- break;
833
-
834
- case "inside":
835
- drawInside(stroke, ui, canvas);
836
- break;
837
-
838
- case "outside":
839
- drawOutside(stroke, ui, canvas);
840
- break;
841
- }
842
- }
843
- }
844
-
845
- function strokes(strokes, ui, canvas) {
846
- stroke(strokes, ui, canvas);
847
- }
848
-
849
- function drawCenter(stroke, strokeWidthScale, ui, canvas) {
850
- const data = ui.__;
851
- if (core.isObject(stroke)) {
852
- drawStrokesStyle(stroke, strokeWidthScale, false, ui, canvas);
853
- } else {
854
- canvas.setStroke(stroke, data.__strokeWidth * strokeWidthScale, data);
855
- canvas.stroke();
856
- }
857
- if (data.__useArrow) draw.Paint.strokeArrow(stroke, ui, canvas);
858
- }
859
-
860
- function drawInside(stroke, ui, canvas) {
861
- canvas.save();
862
- canvas.clipUI(ui);
863
- drawCenter(stroke, 2, ui, canvas);
864
- canvas.restore();
865
- }
866
-
867
- function drawOutside(stroke, ui, canvas) {
868
- const data = ui.__;
869
- if (data.__fillAfterStroke) {
870
- drawCenter(stroke, 2, ui, canvas);
871
- } else {
872
- const {renderBounds: renderBounds} = ui.__layout;
873
- const out = canvas.getSameCanvas(true, true);
874
- ui.__drawRenderPath(out);
875
- drawCenter(stroke, 2, ui, out);
876
- out.clipUI(data);
877
- out.clearWorld(renderBounds);
878
- core.LeafHelper.copyCanvasByWorld(ui, canvas, out);
879
- out.recycle(ui.__nowWorld);
880
- }
881
- }
882
-
883
883
  const {getSpread: getSpread, copyAndSpread: copyAndSpread, toOuterOf: toOuterOf, getOuterOf: getOuterOf, getByMove: getByMove, move: move$1, getIntersectData: getIntersectData} = core.BoundsHelper;
884
884
 
885
885
  const tempBounds$1 = {};
@@ -1035,88 +1035,118 @@ const PaintModule = {
1035
1035
  strokes: strokes,
1036
1036
  strokeText: strokeText,
1037
1037
  drawTextStroke: drawTextStroke,
1038
+ drawStrokesStyle: drawStrokesStyle,
1038
1039
  shape: shape
1039
1040
  };
1040
1041
 
1041
- let origin = {}, tempMatrix$1 = core.getMatrixData();
1042
+ let cache, box = new core.Bounds;
1042
1043
 
1043
- 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;
1044
+ const {isSame: isSame} = core.BoundsHelper;
1044
1045
 
1045
- function stretchMode(data, box, scaleX, scaleY) {
1046
- const transform = get$3();
1047
- translate$1(transform, box.x, box.y);
1048
- if (scaleX) scaleHelper(transform, scaleX, scaleY);
1049
- data.transform = transform;
1046
+ function image(ui, attrName, paint, boxBounds, firstUse) {
1047
+ let leafPaint, event;
1048
+ const image = core.ImageManager.get(paint);
1049
+ if (cache && paint === cache.paint && isSame(boxBounds, cache.boxBounds)) {
1050
+ leafPaint = cache.leafPaint;
1051
+ } else {
1052
+ leafPaint = {
1053
+ type: paint.type,
1054
+ image: image
1055
+ };
1056
+ if (image.hasAlphaPixel) leafPaint.isTransparent = true;
1057
+ cache = image.use > 1 ? {
1058
+ leafPaint: leafPaint,
1059
+ paint: paint,
1060
+ boxBounds: box.set(boxBounds)
1061
+ } : null;
1062
+ }
1063
+ if (firstUse || image.loading) event = {
1064
+ image: image,
1065
+ attrName: attrName,
1066
+ attrValue: paint
1067
+ };
1068
+ if (image.ready) {
1069
+ checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds);
1070
+ if (firstUse) {
1071
+ onLoad(ui, event);
1072
+ onLoadSuccess(ui, event);
1073
+ }
1074
+ } else if (image.error) {
1075
+ if (firstUse) onLoadError(ui, event, image.error);
1076
+ } else {
1077
+ if (firstUse) {
1078
+ ignoreRender(ui, true);
1079
+ onLoad(ui, event);
1080
+ }
1081
+ leafPaint.loadId = image.load(() => {
1082
+ ignoreRender(ui, false);
1083
+ if (!ui.destroyed) {
1084
+ if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1085
+ if (image.hasAlphaPixel) ui.__layout.hitCanvasChanged = true;
1086
+ ui.forceUpdate("surface");
1087
+ }
1088
+ onLoadSuccess(ui, event);
1089
+ }
1090
+ leafPaint.loadId = undefined;
1091
+ }, error => {
1092
+ ignoreRender(ui, false);
1093
+ onLoadError(ui, event, error);
1094
+ leafPaint.loadId = undefined;
1095
+ });
1096
+ if (ui.placeholderColor) {
1097
+ if (!ui.placeholderDelay) image.isPlacehold = true; else setTimeout(() => {
1098
+ if (!image.ready) {
1099
+ image.isPlacehold = true;
1100
+ ui.forceUpdate("surface");
1101
+ }
1102
+ }, ui.placeholderDelay);
1103
+ }
1104
+ }
1105
+ return leafPaint;
1050
1106
  }
1051
1107
 
1052
- function fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation) {
1053
- const transform = get$3();
1054
- translate$1(transform, box.x + x, box.y + y);
1055
- scaleHelper(transform, scaleX, scaleY);
1056
- if (rotation) rotateOfOuter$1(transform, {
1057
- x: box.x + box.width / 2,
1058
- y: box.y + box.height / 2
1059
- }, rotation);
1060
- data.transform = transform;
1108
+ function checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds) {
1109
+ if (attrName === "fill" && !ui.__.__naturalWidth) {
1110
+ const data = ui.__;
1111
+ data.__naturalWidth = image.width / data.pixelRatio;
1112
+ data.__naturalHeight = image.height / data.pixelRatio;
1113
+ if (data.__autoSide) {
1114
+ ui.forceUpdate("width");
1115
+ if (ui.__proxyData) {
1116
+ ui.setProxyAttr("width", data.width);
1117
+ ui.setProxyAttr("height", data.height);
1118
+ }
1119
+ return false;
1120
+ }
1121
+ }
1122
+ if (!leafPaint.data) draw.PaintImage.createData(leafPaint, image, paint, boxBounds);
1123
+ return true;
1061
1124
  }
1062
1125
 
1063
- function clipMode(data, box, x, y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY) {
1064
- const transform = get$3();
1065
- layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1066
- if (clipScaleX) {
1067
- if (rotation || skew) {
1068
- set(tempMatrix$1);
1069
- scaleOfOuter$1(tempMatrix$1, box, clipScaleX, clipScaleY);
1070
- multiplyParent(transform, tempMatrix$1);
1071
- } else scaleOfOuter$1(transform, box, clipScaleX, clipScaleY);
1072
- }
1073
- data.transform = transform;
1126
+ function onLoad(ui, event) {
1127
+ emit(ui, core.ImageEvent.LOAD, event);
1074
1128
  }
1075
1129
 
1076
- function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, skew, align, freeTransform) {
1077
- const transform = get$3();
1078
- if (freeTransform) {
1079
- layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1080
- } else {
1081
- if (rotation) {
1082
- if (align === "center") {
1083
- rotateOfOuter$1(transform, {
1084
- x: width / 2,
1085
- y: height / 2
1086
- }, rotation);
1087
- } else {
1088
- rotate(transform, rotation);
1089
- switch (rotation) {
1090
- case 90:
1091
- translate$1(transform, height, 0);
1092
- break;
1130
+ function onLoadSuccess(ui, event) {
1131
+ emit(ui, core.ImageEvent.LOADED, event);
1132
+ }
1093
1133
 
1094
- case 180:
1095
- translate$1(transform, width, height);
1096
- break;
1134
+ function onLoadError(ui, event, error) {
1135
+ event.error = error;
1136
+ ui.forceUpdate("surface");
1137
+ emit(ui, core.ImageEvent.ERROR, event);
1138
+ }
1097
1139
 
1098
- case 270:
1099
- translate$1(transform, 0, width);
1100
- break;
1101
- }
1102
- }
1103
- }
1104
- origin.x = box.x + x;
1105
- origin.y = box.y + y;
1106
- translate$1(transform, origin.x, origin.y);
1107
- if (scaleX) scaleOfOuter$1(transform, origin, scaleX, scaleY);
1108
- }
1109
- data.transform = transform;
1140
+ function emit(ui, type, data) {
1141
+ if (ui.hasEvent(type)) ui.emitEvent(new core.ImageEvent(type, data));
1110
1142
  }
1111
1143
 
1112
- function layout(transform, box, x, y, scaleX, scaleY, rotation, skew) {
1113
- if (rotation) rotate(transform, rotation);
1114
- if (skew) skewHelper(transform, skew.x, skew.y);
1115
- if (scaleX) scaleHelper(transform, scaleX, scaleY);
1116
- translate$1(transform, box.x + x, box.y + y);
1144
+ function ignoreRender(ui, value) {
1145
+ const {leafer: leafer} = ui;
1146
+ if (leafer && leafer.viewReady) leafer.renderer.ignore = value;
1117
1147
  }
1118
1148
 
1119
- const {get: get$2, translate: translate} = core.MatrixHelper;
1149
+ const {get: get$3, translate: translate$1} = core.MatrixHelper;
1120
1150
 
1121
1151
  const tempBox = new core.Bounds;
1122
1152
 
@@ -1129,13 +1159,13 @@ function createData(leafPaint, image, paint, box) {
1129
1159
  if (changeful) leafPaint.changeful = changeful;
1130
1160
  if (sync) leafPaint.sync = sync;
1131
1161
  if (scaleFixed) leafPaint.scaleFixed = scaleFixed;
1132
- leafPaint.data = getPatternData(paint, box, image);
1162
+ leafPaint.data = draw.PaintImage.getPatternData(paint, box, image);
1133
1163
  }
1134
1164
 
1135
1165
  function getPatternData(paint, box, image) {
1136
1166
  if (paint.padding) box = tempBox.set(box).shrink(paint.padding);
1137
1167
  if (paint.mode === "strench") paint.mode = "stretch";
1138
- let {width: width, height: height} = image;
1168
+ const {width: width, height: height} = image;
1139
1169
  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;
1140
1170
  const sameBox = box.width === width && box.height === height;
1141
1171
  const data = {
@@ -1166,8 +1196,8 @@ function getPatternData(paint, box, image) {
1166
1196
  case "stretch":
1167
1197
  if (!sameBox) {
1168
1198
  scaleX = box.width / width, scaleY = box.height / height;
1169
- stretchMode(data, box, scaleX, scaleY);
1170
- }
1199
+ draw.PaintImage.stretchMode(data, box, scaleX, scaleY);
1200
+ } else if (scaleX) scaleX = scaleY = undefined;
1171
1201
  break;
1172
1202
 
1173
1203
  case "normal":
@@ -1175,13 +1205,13 @@ function getPatternData(paint, box, image) {
1175
1205
  if (tempImage.x || tempImage.y || scaleX || clipSize || rotation || skew) {
1176
1206
  let clipScaleX, clipScaleY;
1177
1207
  if (clipSize) clipScaleX = box.width / clipSize.width, clipScaleY = box.height / clipSize.height;
1178
- clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY);
1208
+ draw.PaintImage.clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY);
1179
1209
  if (clipScaleX) scaleX = scaleX ? scaleX * clipScaleX : clipScaleX, scaleY = scaleY ? scaleY * clipScaleY : clipScaleY;
1180
1210
  }
1181
1211
  break;
1182
1212
 
1183
1213
  case "repeat":
1184
- if (!sameBox || scaleX || rotation || skew) repeatMode(data, box, width, height, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, align, paint.freeTransform);
1214
+ if (!sameBox || scaleX || rotation || skew) draw.PaintImage.repeatMode(data, box, width, height, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, align, paint.freeTransform);
1185
1215
  if (!repeat) data.repeat = "repeat";
1186
1216
  const count = core.isObject(repeat);
1187
1217
  if (gap || count) data.gap = getGapData(gap, count && repeat, tempImage.width, tempImage.height, box);
@@ -1190,18 +1220,16 @@ function getPatternData(paint, box, image) {
1190
1220
  case "fit":
1191
1221
  case "cover":
1192
1222
  default:
1193
- if (scaleX) fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1223
+ if (scaleX) draw.PaintImage.fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1194
1224
  }
1195
1225
  if (!data.transform) {
1196
- if (box.x || box.y) translate(data.transform = get$2(), box.x, box.y);
1226
+ if (box.x || box.y) translate$1(data.transform = get$3(), box.x, box.y);
1197
1227
  }
1198
- data.width = width;
1199
- data.height = height;
1200
1228
  if (scaleX) {
1201
1229
  data.scaleX = scaleX;
1202
1230
  data.scaleY = scaleY;
1203
1231
  }
1204
- if (opacity) data.opacity = opacity;
1232
+ if (opacity && opacity < 1) data.opacity = opacity;
1205
1233
  if (filters) data.filters = filters;
1206
1234
  if (repeat) data.repeat = core.isString(repeat) ? repeat === "x" ? "repeat-x" : "repeat-y" : "repeat";
1207
1235
  return data;
@@ -1223,234 +1251,194 @@ function getGapValue(gap, size, totalSize, rows) {
1223
1251
  return gap === "auto" ? value < 0 ? 0 : value : value;
1224
1252
  }
1225
1253
 
1226
- let cache, box = new core.Bounds;
1254
+ let origin = {}, tempMatrix$1 = core.getMatrixData();
1227
1255
 
1228
- const {isSame: isSame} = core.BoundsHelper;
1256
+ const {get: get$2, set: set, rotateOfOuter: rotateOfOuter$1, translate: translate, scaleOfOuter: scaleOfOuter$1, multiplyParent: multiplyParent, scale: scaleHelper, rotate: rotate, skew: skewHelper} = core.MatrixHelper;
1229
1257
 
1230
- function image(ui, attrName, paint, boxBounds, firstUse) {
1231
- let leafPaint, event;
1232
- const image = core.ImageManager.get(paint);
1233
- if (cache && paint === cache.paint && isSame(boxBounds, cache.boxBounds)) {
1234
- leafPaint = cache.leafPaint;
1235
- } else {
1236
- leafPaint = {
1237
- type: paint.type,
1238
- image: image
1239
- };
1240
- if (image.hasAlphaPixel) leafPaint.isTransparent = true;
1241
- cache = image.use > 1 ? {
1242
- leafPaint: leafPaint,
1243
- paint: paint,
1244
- boxBounds: box.set(boxBounds)
1245
- } : null;
1246
- }
1247
- if (firstUse || image.loading) event = {
1248
- image: image,
1249
- attrName: attrName,
1250
- attrValue: paint
1251
- };
1252
- if (image.ready) {
1253
- checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds);
1254
- if (firstUse) {
1255
- onLoad(ui, event);
1256
- onLoadSuccess(ui, event);
1257
- }
1258
- } else if (image.error) {
1259
- if (firstUse) onLoadError(ui, event, image.error);
1260
- } else {
1261
- if (firstUse) {
1262
- ignoreRender(ui, true);
1263
- onLoad(ui, event);
1264
- }
1265
- leafPaint.loadId = image.load(() => {
1266
- ignoreRender(ui, false);
1267
- if (!ui.destroyed) {
1268
- if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1269
- if (image.hasAlphaPixel) ui.__layout.hitCanvasChanged = true;
1270
- ui.forceUpdate("surface");
1271
- }
1272
- onLoadSuccess(ui, event);
1273
- }
1274
- leafPaint.loadId = undefined;
1275
- }, error => {
1276
- ignoreRender(ui, false);
1277
- onLoadError(ui, event, error);
1278
- leafPaint.loadId = undefined;
1279
- });
1280
- if (ui.placeholderColor) {
1281
- if (!ui.placeholderDelay) image.isPlacehold = true; else setTimeout(() => {
1282
- if (!image.ready) {
1283
- image.isPlacehold = true;
1284
- ui.forceUpdate("surface");
1285
- }
1286
- }, ui.placeholderDelay);
1287
- }
1288
- }
1289
- return leafPaint;
1258
+ function stretchMode(data, box, scaleX, scaleY) {
1259
+ const transform = get$2(), {x: x, y: y} = box;
1260
+ if (x || y) translate(transform, x, y); else transform.onlyScale = true;
1261
+ scaleHelper(transform, scaleX, scaleY);
1262
+ data.transform = transform;
1290
1263
  }
1291
1264
 
1292
- function checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds) {
1293
- if (attrName === "fill" && !ui.__.__naturalWidth) {
1294
- const data = ui.__;
1295
- data.__naturalWidth = image.width / data.pixelRatio;
1296
- data.__naturalHeight = image.height / data.pixelRatio;
1297
- if (data.__autoSide) {
1298
- ui.forceUpdate("width");
1299
- if (ui.__proxyData) {
1300
- ui.setProxyAttr("width", data.width);
1301
- ui.setProxyAttr("height", data.height);
1302
- }
1303
- return false;
1304
- }
1305
- }
1306
- if (!leafPaint.data) createData(leafPaint, image, paint, boxBounds);
1307
- return true;
1265
+ function fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation) {
1266
+ const transform = get$2();
1267
+ translate(transform, box.x + x, box.y + y);
1268
+ scaleHelper(transform, scaleX, scaleY);
1269
+ if (rotation) rotateOfOuter$1(transform, {
1270
+ x: box.x + box.width / 2,
1271
+ y: box.y + box.height / 2
1272
+ }, rotation);
1273
+ data.transform = transform;
1308
1274
  }
1309
1275
 
1310
- function onLoad(ui, event) {
1311
- emit(ui, core.ImageEvent.LOAD, event);
1276
+ function clipMode(data, box, x, y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY) {
1277
+ const transform = get$2();
1278
+ layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1279
+ if (clipScaleX) {
1280
+ if (rotation || skew) {
1281
+ set(tempMatrix$1);
1282
+ scaleOfOuter$1(tempMatrix$1, box, clipScaleX, clipScaleY);
1283
+ multiplyParent(transform, tempMatrix$1);
1284
+ } else scaleOfOuter$1(transform, box, clipScaleX, clipScaleY);
1285
+ }
1286
+ data.transform = transform;
1312
1287
  }
1313
1288
 
1314
- function onLoadSuccess(ui, event) {
1315
- emit(ui, core.ImageEvent.LOADED, event);
1316
- }
1289
+ function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, skew, align, freeTransform) {
1290
+ const transform = get$2();
1291
+ if (freeTransform) {
1292
+ layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1293
+ } else {
1294
+ if (rotation) {
1295
+ if (align === "center") {
1296
+ rotateOfOuter$1(transform, {
1297
+ x: width / 2,
1298
+ y: height / 2
1299
+ }, rotation);
1300
+ } else {
1301
+ rotate(transform, rotation);
1302
+ switch (rotation) {
1303
+ case 90:
1304
+ translate(transform, height, 0);
1305
+ break;
1317
1306
 
1318
- function onLoadError(ui, event, error) {
1319
- event.error = error;
1320
- ui.forceUpdate("surface");
1321
- emit(ui, core.ImageEvent.ERROR, event);
1322
- }
1307
+ case 180:
1308
+ translate(transform, width, height);
1309
+ break;
1323
1310
 
1324
- function emit(ui, type, data) {
1325
- if (ui.hasEvent(type)) ui.emitEvent(new core.ImageEvent(type, data));
1311
+ case 270:
1312
+ translate(transform, 0, width);
1313
+ break;
1314
+ }
1315
+ }
1316
+ }
1317
+ origin.x = box.x + x;
1318
+ origin.y = box.y + y;
1319
+ translate(transform, origin.x, origin.y);
1320
+ if (scaleX) scaleOfOuter$1(transform, origin, scaleX, scaleY);
1321
+ }
1322
+ data.transform = transform;
1326
1323
  }
1327
1324
 
1328
- function ignoreRender(ui, value) {
1329
- const {leafer: leafer} = ui;
1330
- if (leafer && leafer.viewReady) leafer.renderer.ignore = value;
1325
+ function layout(transform, box, x, y, scaleX, scaleY, rotation, skew) {
1326
+ if (rotation) rotate(transform, rotation);
1327
+ if (skew) skewHelper(transform, skew.x, skew.y);
1328
+ if (scaleX) scaleHelper(transform, scaleX, scaleY);
1329
+ translate(transform, box.x + x, box.y + y);
1331
1330
  }
1332
1331
 
1333
1332
  const {get: get$1, scale: scale, copy: copy$1} = core.MatrixHelper;
1334
1333
 
1335
- const {floor: floor, ceil: ceil, max: max$1, abs: abs$1} = Math;
1334
+ const {getFloorScale: getFloorScale} = core.MathHelper, {abs: abs$1} = Math;
1336
1335
 
1337
- function createPattern(ui, paint, pixelRatio) {
1338
- let {scaleX: scaleX, scaleY: scaleY} = ui.getRenderScaleData(true, paint.scaleFixed);
1339
- const id = scaleX + "-" + scaleY + "-" + pixelRatio;
1336
+ function createPatternTask(paint, ui, canvas, renderOptions) {
1337
+ if (!paint.patternTask) {
1338
+ paint.patternTask = core.ImageManager.patternTasker.add(() => __awaiter(this, void 0, void 0, function*() {
1339
+ paint.patternTask = null;
1340
+ if (canvas.bounds.hit(ui.__nowWorld)) draw.PaintImage.createPattern(paint, ui, canvas, renderOptions);
1341
+ ui.forceUpdate("surface");
1342
+ }), 300);
1343
+ }
1344
+ }
1345
+
1346
+ function createPattern(paint, ui, canvas, renderOptions) {
1347
+ let {scaleX: scaleX, scaleY: scaleY} = draw.PaintImage.getImageRenderScaleData(paint, ui, canvas, renderOptions), id = scaleX + "-" + scaleY;
1340
1348
  if (paint.patternId !== id && !ui.destroyed) {
1341
- const {image: image, data: data} = paint;
1342
- let imageScale, imageMatrix, {width: width, height: height, scaleX: sx, scaleY: sy, transform: transform, repeat: repeat, gap: gap} = data;
1343
- scaleX *= pixelRatio;
1344
- scaleY *= pixelRatio;
1345
- if (sx) {
1346
- sx = abs$1(sx);
1347
- sy = abs$1(sy);
1348
- imageMatrix = get$1();
1349
- copy$1(imageMatrix, transform);
1350
- scale(imageMatrix, 1 / sx, 1 / sy);
1351
- scaleX *= sx;
1352
- scaleY *= sy;
1353
- }
1354
- width *= scaleX;
1355
- height *= scaleY;
1356
- const size = width * height;
1357
- if (!repeat) {
1358
- if (size > core.Platform.image.maxCacheSize) return false;
1359
- }
1360
- let maxSize = core.Platform.image.maxPatternSize;
1361
- if (image.isSVG) {
1362
- const ws = width / image.width;
1363
- if (ws > 1) imageScale = ws / ceil(ws);
1364
- } else {
1365
- const imageSize = image.width * image.height;
1366
- if (maxSize > imageSize) maxSize = imageSize;
1367
- }
1368
- if (size > maxSize) imageScale = Math.sqrt(size / maxSize);
1369
- if (imageScale) {
1370
- scaleX /= imageScale;
1371
- scaleY /= imageScale;
1372
- width /= imageScale;
1373
- height /= imageScale;
1374
- }
1375
- if (sx) {
1376
- scaleX /= sx;
1377
- scaleY /= sy;
1378
- }
1379
- const xGap = gap && gap.x * scaleX;
1380
- const yGap = gap && gap.y * scaleY;
1381
- if (transform || scaleX !== 1 || scaleY !== 1) {
1382
- const canvasWidth = width + (xGap || 0);
1383
- const canvasHeight = height + (yGap || 0);
1384
- scaleX /= canvasWidth / max$1(floor(canvasWidth), 1);
1385
- scaleY /= canvasHeight / max$1(floor(canvasHeight), 1);
1386
- if (!imageMatrix) {
1349
+ if (!(core.Platform.image.isLarge(paint.image, scaleX, scaleY) && !paint.data.repeat)) {
1350
+ const {image: image, data: data} = paint, {transform: transform, gap: gap} = data, fixScale = draw.PaintImage.getPatternFixScale(paint, scaleX, scaleY);
1351
+ let imageMatrix, xGap, yGap, {width: width, height: height} = image;
1352
+ if (fixScale) scaleX *= fixScale, scaleY *= fixScale;
1353
+ width *= scaleX;
1354
+ height *= scaleY;
1355
+ if (gap) {
1356
+ xGap = gap.x * scaleX / abs$1(data.scaleX || 1);
1357
+ yGap = gap.y * scaleY / abs$1(data.scaleY || 1);
1358
+ }
1359
+ if (transform || scaleX !== 1 || scaleY !== 1) {
1360
+ scaleX *= getFloorScale(width + (xGap || 0));
1361
+ scaleY *= getFloorScale(height + (yGap || 0));
1387
1362
  imageMatrix = get$1();
1388
1363
  if (transform) copy$1(imageMatrix, transform);
1364
+ scale(imageMatrix, 1 / scaleX, 1 / scaleY);
1389
1365
  }
1390
- scale(imageMatrix, 1 / scaleX, 1 / scaleY);
1366
+ const imageCanvas = image.getCanvas(width, height, data.opacity, data.filters, xGap, yGap, ui.leafer && ui.leafer.config.smooth);
1367
+ const pattern = image.getPattern(imageCanvas, data.repeat || (core.Platform.origin.noRepeat || "no-repeat"), imageMatrix, paint);
1368
+ paint.style = pattern;
1369
+ paint.patternId = id;
1391
1370
  }
1392
- const canvas = image.getCanvas(width, height, data.opacity, data.filters, xGap, yGap, ui.leafer && ui.leafer.config.smooth);
1393
- const pattern = image.getPattern(canvas, repeat || (core.Platform.origin.noRepeat || "no-repeat"), imageMatrix, paint);
1394
- paint.style = pattern;
1395
- paint.patternId = id;
1396
- return true;
1371
+ }
1372
+ }
1373
+
1374
+ function getPatternFixScale(paint, imageScaleX, imageScaleY) {
1375
+ const {image: image} = paint;
1376
+ let fixScale, maxSize = core.Platform.image.maxPatternSize, imageSize = image.width * image.height;
1377
+ if (image.isSVG) {
1378
+ if (imageScaleX > 1) fixScale = Math.ceil(imageScaleX) / imageScaleX;
1397
1379
  } else {
1398
- return false;
1380
+ if (maxSize > imageSize) maxSize = imageSize;
1399
1381
  }
1382
+ if ((imageSize *= imageScaleX * imageScaleY) > maxSize) fixScale = Math.sqrt(maxSize / imageSize);
1383
+ return fixScale;
1400
1384
  }
1401
1385
 
1402
- function checkImage(ui, canvas, paint, allowDraw) {
1403
- const {scaleX: scaleX, scaleY: scaleY} = ui.getRenderScaleData(true, paint.scaleFixed);
1404
- const {pixelRatio: pixelRatio} = canvas, {data: data} = paint;
1405
- if (!data || paint.patternId === scaleX + "-" + scaleY + "-" + pixelRatio && !draw.Export.running) {
1386
+ function checkImage(paint, drawImage, ui, canvas, renderOptions) {
1387
+ const {scaleX: scaleX, scaleY: scaleY} = draw.PaintImage.getImageRenderScaleData(paint, ui, canvas, renderOptions);
1388
+ const {image: image, data: data} = paint, {exporting: exporting} = renderOptions;
1389
+ if (!data || paint.patternId === scaleX + "-" + scaleY && !exporting) {
1406
1390
  return false;
1407
1391
  } else {
1408
- if (allowDraw) {
1392
+ if (drawImage) {
1409
1393
  if (data.repeat) {
1410
- allowDraw = false;
1411
- } else if (!(paint.changeful || core.Platform.name === "miniapp" && core.ResizeEvent.isResizing(ui) || draw.Export.running)) {
1412
- let {width: width, height: height} = data;
1413
- width *= scaleX * pixelRatio;
1414
- height *= scaleY * pixelRatio;
1415
- if (data.scaleX) {
1416
- width *= data.scaleX;
1417
- height *= data.scaleY;
1418
- }
1419
- allowDraw = width * height > core.Platform.image.maxCacheSize;
1394
+ drawImage = false;
1395
+ } else if (!(paint.changeful || core.Platform.name === "miniapp" && core.ResizeEvent.isResizing(ui) || exporting)) {
1396
+ drawImage = core.Platform.image.isLarge(image, scaleX, scaleY);
1420
1397
  }
1421
1398
  }
1422
- if (allowDraw) {
1399
+ if (drawImage) {
1423
1400
  if (ui.__.__isFastShadow) {
1424
1401
  canvas.fillStyle = paint.style || "#000";
1425
1402
  canvas.fill();
1426
1403
  }
1427
- drawImage(ui, canvas, paint, data);
1404
+ draw.PaintImage.drawImage(paint, scaleX, scaleY, ui, canvas, renderOptions);
1428
1405
  return true;
1429
1406
  } else {
1430
- if (!paint.style || paint.sync || draw.Export.running) {
1431
- createPattern(ui, paint, pixelRatio);
1432
- } else {
1433
- if (!paint.patternTask) {
1434
- paint.patternTask = core.ImageManager.patternTasker.add(() => __awaiter(this, void 0, void 0, function*() {
1435
- paint.patternTask = null;
1436
- if (canvas.bounds.hit(ui.__nowWorld)) createPattern(ui, paint, pixelRatio);
1437
- ui.forceUpdate("surface");
1438
- }), 300);
1439
- }
1440
- }
1407
+ if (!paint.style || paint.sync || exporting) draw.PaintImage.createPattern(paint, ui, canvas, renderOptions); else draw.PaintImage.createPatternTask(paint, ui, canvas, renderOptions);
1441
1408
  return false;
1442
1409
  }
1443
1410
  }
1444
1411
  }
1445
1412
 
1446
- function drawImage(ui, canvas, paint, data) {
1447
- canvas.save();
1448
- canvas.clipUI(ui);
1449
- if (paint.blendMode) canvas.blendMode = paint.blendMode;
1450
- if (data.opacity) canvas.opacity *= data.opacity;
1451
- if (data.transform) canvas.transform(data.transform);
1452
- canvas.drawImage(paint.image.getFull(data.filters), 0, 0, data.width, data.height);
1453
- canvas.restore();
1413
+ function drawImage(paint, _imageScaleX, _imageScaleY, ui, canvas, _renderOptions) {
1414
+ const {data: data, image: image, blendMode: blendMode} = paint, {opacity: opacity, transform: transform} = data, view = image.getFull(data.filters), u = ui.__;
1415
+ let {width: width, height: height} = image, clipUI;
1416
+ if (transform && !transform.onlyScale || (clipUI = u.path || u.cornerRadius) || opacity || blendMode) {
1417
+ canvas.save();
1418
+ clipUI && canvas.clipUI(ui);
1419
+ blendMode && (canvas.blendMode = blendMode);
1420
+ opacity && (canvas.opacity *= opacity);
1421
+ transform && canvas.transform(transform);
1422
+ canvas.drawImage(view, 0, 0, width, height);
1423
+ canvas.restore();
1424
+ } else {
1425
+ if (data.scaleX) width *= data.scaleX, height *= data.scaleY;
1426
+ canvas.drawImage(view, 0, 0, width, height);
1427
+ }
1428
+ }
1429
+
1430
+ function getImageRenderScaleData(paint, ui, canvas, _renderOptions) {
1431
+ const scaleData = ui.getRenderScaleData(true, paint.scaleFixed), {data: data} = paint;
1432
+ if (canvas) {
1433
+ const {pixelRatio: pixelRatio} = canvas;
1434
+ scaleData.scaleX *= pixelRatio;
1435
+ scaleData.scaleY *= pixelRatio;
1436
+ }
1437
+ if (data && data.scaleX) {
1438
+ scaleData.scaleX *= Math.abs(data.scaleX);
1439
+ scaleData.scaleY *= Math.abs(data.scaleY);
1440
+ }
1441
+ return scaleData;
1454
1442
  }
1455
1443
 
1456
1444
  function recycleImage(attrName, data) {
@@ -1482,8 +1470,12 @@ function recycleImage(attrName, data) {
1482
1470
  const PaintImageModule = {
1483
1471
  image: image,
1484
1472
  checkImage: checkImage,
1485
- createPattern: createPattern,
1473
+ drawImage: drawImage,
1474
+ getImageRenderScaleData: getImageRenderScaleData,
1486
1475
  recycleImage: recycleImage,
1476
+ createPatternTask: createPatternTask,
1477
+ createPattern: createPattern,
1478
+ getPatternFixScale: getPatternFixScale,
1487
1479
  createData: createData,
1488
1480
  getPatternData: getPatternData,
1489
1481
  stretchMode: stretchMode,
@@ -1939,10 +1931,8 @@ function createRows(drawData, content, style) {
1939
1931
  bounds = drawData.bounds;
1940
1932
  findMaxWidth = !bounds.width && !style.autoSizeAlign;
1941
1933
  const {__letterSpacing: __letterSpacing, paraIndent: paraIndent, textCase: textCase} = style;
1942
- const {canvas: canvas} = core.Platform;
1943
- const {width: width, height: height} = bounds;
1944
- const charMode = width || height || __letterSpacing || textCase !== "none";
1945
- if (charMode) {
1934
+ const {canvas: canvas} = core.Platform, {width: width} = bounds;
1935
+ if (style.__isCharMode) {
1946
1936
  const wrap = style.textWrap !== "none";
1947
1937
  const breakAll = style.textWrap === "break";
1948
1938
  paraStart = true;
@@ -2071,12 +2061,19 @@ const TextMode = 2;
2071
2061
  function layoutChar(drawData, style, width, _height) {
2072
2062
  const {rows: rows} = drawData;
2073
2063
  const {textAlign: textAlign, paraIndent: paraIndent, letterSpacing: letterSpacing} = style;
2074
- let charX, addWordWidth, indentWidth, mode, wordChar, wordsLength;
2064
+ const justifyLast = width && textAlign.includes("both");
2065
+ const justify = justifyLast || width && textAlign.includes("justify");
2066
+ const justifyLetter = justify && textAlign.includes("letter");
2067
+ let charX, remainingWidth, addWordWidth, addLetterWidth, indentWidth, mode, wordChar, wordsLength, isLastWord, canJustify;
2075
2068
  rows.forEach(row => {
2076
2069
  if (row.words) {
2077
2070
  indentWidth = paraIndent && row.paraStart ? paraIndent : 0, wordsLength = row.words.length;
2078
- addWordWidth = width && (textAlign === "justify" || textAlign === "both") && wordsLength > 1 ? (width - row.width - indentWidth) / (wordsLength - 1) : 0;
2079
- mode = letterSpacing || row.isOverflow ? CharMode : addWordWidth > .01 ? WordMode : TextMode;
2071
+ if (justify) {
2072
+ canJustify = !row.paraEnd || justifyLast;
2073
+ remainingWidth = width - row.width - indentWidth;
2074
+ if (justifyLetter) addLetterWidth = remainingWidth / (row.words.reduce((total, item) => total + item.data.length, 0) - 1); else addWordWidth = wordsLength > 1 ? remainingWidth / (wordsLength - 1) : 0;
2075
+ }
2076
+ mode = letterSpacing || row.isOverflow || justifyLetter ? CharMode : addWordWidth ? WordMode : TextMode;
2080
2077
  if (row.isOverflow && !letterSpacing) row.textMode = true;
2081
2078
  if (mode === TextMode) {
2082
2079
  row.x += indentWidth;
@@ -2094,11 +2091,15 @@ function layoutChar(drawData, style, width, _height) {
2094
2091
  charX = toWordChar(word.data, charX, wordChar);
2095
2092
  if (row.isOverflow || wordChar.char !== " ") row.data.push(wordChar);
2096
2093
  } else {
2097
- charX = toChar(word.data, charX, row.data, row.isOverflow);
2094
+ charX = toChar(word.data, charX, row.data, row.isOverflow, canJustify && addLetterWidth);
2098
2095
  }
2099
- if (addWordWidth && (!row.paraEnd || textAlign === "both") && index !== wordsLength - 1) {
2100
- charX += addWordWidth;
2101
- row.width += addWordWidth;
2096
+ if (canJustify) {
2097
+ isLastWord = index === wordsLength - 1;
2098
+ if (addWordWidth) {
2099
+ if (!isLastWord) charX += addWordWidth, row.width += addWordWidth;
2100
+ } else if (addLetterWidth) {
2101
+ row.width += addLetterWidth * (word.data.length - (isLastWord ? 1 : 0));
2102
+ }
2102
2103
  }
2103
2104
  });
2104
2105
  }
@@ -2124,13 +2125,14 @@ function toWordChar(data, charX, wordChar) {
2124
2125
  return charX;
2125
2126
  }
2126
2127
 
2127
- function toChar(data, charX, rowData, isOverflow) {
2128
+ function toChar(data, charX, rowData, isOverflow, addLetterWidth) {
2128
2129
  data.forEach(char => {
2129
2130
  if (isOverflow || char.char !== " ") {
2130
2131
  char.x = charX;
2131
2132
  rowData.push(char);
2132
2133
  }
2133
2134
  charX += char.width;
2135
+ addLetterWidth && (charX += addLetterWidth);
2134
2136
  });
2135
2137
  return charX;
2136
2138
  }
@@ -2272,10 +2274,10 @@ function getDrawData(content, style) {
2272
2274
  let x = 0, y = 0;
2273
2275
  let width = style.__getInput("width") || 0;
2274
2276
  let height = style.__getInput("height") || 0;
2275
- const {textDecoration: textDecoration, __font: __font, __padding: padding} = style;
2277
+ const {__padding: padding} = style;
2276
2278
  if (padding) {
2277
- if (width) x = padding[left], width -= padding[right] + padding[left]; else if (!style.autoSizeAlign) x = padding[left];
2278
- if (height) y = padding[top], height -= padding[top] + padding[bottom]; else if (!style.autoSizeAlign) y = padding[top];
2279
+ if (width) x = padding[left], width -= padding[right] + padding[left], !width && (width = .01); else if (!style.autoSizeAlign) x = padding[left];
2280
+ if (height) y = padding[top], height -= padding[top] + padding[bottom], !height && (height = .01); else if (!style.autoSizeAlign) y = padding[top];
2279
2281
  }
2280
2282
  const drawData = {
2281
2283
  bounds: {
@@ -2286,14 +2288,14 @@ function getDrawData(content, style) {
2286
2288
  },
2287
2289
  rows: [],
2288
2290
  paraNumber: 0,
2289
- font: core.Platform.canvas.font = __font
2291
+ font: core.Platform.canvas.font = style.__font
2290
2292
  };
2291
2293
  createRows(drawData, content, style);
2292
2294
  if (padding) padAutoText(padding, drawData, style, width, height);
2293
2295
  layoutText(drawData, style);
2294
- layoutChar(drawData, style, width);
2296
+ if (style.__isCharMode) layoutChar(drawData, style, width);
2295
2297
  if (drawData.overflow) clipText(drawData, style, x, width);
2296
- if (textDecoration !== "none") decorationText(drawData, style);
2298
+ if (style.textDecoration !== "none") decorationText(drawData, style);
2297
2299
  return drawData;
2298
2300
  }
2299
2301