embroidery-qc-image 1.0.20 → 1.0.22

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.js CHANGED
@@ -730,10 +730,30 @@ const renderSide = (ctx, side, startY, width, scaleFactor, imageRefs) => {
730
730
  ctx.restore();
731
731
  // Compute uniform properties
732
732
  const textPositions = side.positions.filter((p) => p.type === "TEXT");
733
- const uniformProps = computeUniformProperties(textPositions);
734
- // Render uniform labels (only if more than 1 TEXT position)
735
- if (textPositions.length > 1) {
736
- currentY += renderUniformLabels(ctx, uniformProps, padding, currentY, sideWidth, scaleFactor, imageRefs, textPositions);
733
+ const iconColorPositions = side.positions.filter((p) => p.type === "ICON" && (!p.layer_colors?.length || p.layer_colors.length === 1));
734
+ const iconColorValues = iconColorPositions
735
+ .map((p) => {
736
+ if (p.layer_colors?.length === 1)
737
+ return p.layer_colors[0];
738
+ return p.color ?? null;
739
+ })
740
+ .filter((color) => Boolean(color));
741
+ const uniformProps = computeUniformProperties(textPositions, {
742
+ additionalColorValues: iconColorValues,
743
+ });
744
+ const hasMultipleTextPositions = textPositions.length > 1;
745
+ const hasSharedIconColor = iconColorValues.length > 0;
746
+ const shouldRenderSharedColorLabel = !hasMultipleTextPositions &&
747
+ hasSharedIconColor &&
748
+ uniformProps.isUniform.color &&
749
+ Boolean(uniformProps.values.color);
750
+ const shouldShowUniformLabels = hasMultipleTextPositions || shouldRenderSharedColorLabel;
751
+ const uniformLabelFields = hasMultipleTextPositions
752
+ ? undefined
753
+ : { color: true };
754
+ // Render uniform labels (when applicable)
755
+ if (shouldShowUniformLabels) {
756
+ currentY += renderUniformLabels(ctx, uniformProps, padding, currentY, sideWidth, scaleFactor, imageRefs, textPositions, uniformLabelFields);
737
757
  }
738
758
  // Group text positions by common properties
739
759
  const textGroups = groupTextPositions(textPositions);
@@ -747,7 +767,12 @@ const renderSide = (ctx, side, startY, width, scaleFactor, imageRefs) => {
747
767
  }
748
768
  // If only 1 TEXT position, show all labels (no uniform labels rendered)
749
769
  const showLabels = textPositions.length === 1
750
- ? { font: true, shape: true, floral: true, color: true }
770
+ ? {
771
+ font: true,
772
+ shape: true,
773
+ floral: true,
774
+ color: !shouldRenderSharedColorLabel,
775
+ }
751
776
  : {
752
777
  font: !uniformProps.isUniform.font,
753
778
  shape: !uniformProps.isUniform.shape,
@@ -765,7 +790,17 @@ const renderSide = (ctx, side, startY, width, scaleFactor, imageRefs) => {
765
790
  currentY += LAYOUT.LINE_GAP * scaleFactor;
766
791
  side.positions.forEach((position) => {
767
792
  if (position.type === "ICON") {
768
- currentY += renderIconPosition(ctx, position, padding, currentY, sideWidth, scaleFactor, imageRefs);
793
+ const layerCount = position.layer_colors?.length ?? 0;
794
+ const iconUsesSingleColor = layerCount === 0 || layerCount === 1;
795
+ const iconColorValue = layerCount === 1
796
+ ? position.layer_colors?.[0] ?? null
797
+ : position.color ?? null;
798
+ const hideColor = shouldShowUniformLabels &&
799
+ uniformProps.isUniform.color &&
800
+ iconColorValue !== null &&
801
+ uniformProps.values.color === iconColorValue &&
802
+ iconUsesSingleColor;
803
+ currentY += renderIconPosition(ctx, position, padding, currentY, sideWidth, scaleFactor, imageRefs, { hideColor });
769
804
  currentY += (LAYOUT.LINE_GAP / 3) * scaleFactor;
770
805
  }
771
806
  });
@@ -802,56 +837,67 @@ const groupTextPositions = (textPositions) => {
802
837
  }
803
838
  return groups;
804
839
  };
805
- const computeUniformProperties = (textPositions) => {
806
- if (textPositions.length === 0) {
807
- return {
808
- values: { font: null, shape: null, floral: null, color: null },
809
- isUniform: { font: false, shape: false, floral: false, color: false },
810
- };
811
- }
840
+ const computeUniformProperties = (textPositions, options) => {
841
+ const defaults = {
842
+ values: { font: null, shape: null, floral: null, color: null },
843
+ isUniform: { font: false, shape: false, floral: false, color: false },
844
+ };
812
845
  const fonts = new Set(textPositions.map((p) => p.font));
813
846
  const shapes = new Set(textPositions.map((p) => p.text_shape));
814
847
  const florals = new Set(textPositions.map((p) => p.floral_pattern ?? "None"));
815
- const colors = new Set(textPositions.map((p) => p.character_colors?.length
816
- ? p.character_colors.join(",")
817
- : p.color ?? "None"));
848
+ const colorSources = [
849
+ ...textPositions.map((p) => p.character_colors?.length
850
+ ? p.character_colors.join(",")
851
+ : p.color ?? "None"),
852
+ ...(options?.additionalColorValues?.map((color) => color ?? "None") ?? []),
853
+ ];
854
+ if (textPositions.length === 0 &&
855
+ (!options?.additionalColorValues || options.additionalColorValues.length === 0)) {
856
+ return defaults;
857
+ }
858
+ const colors = new Set(colorSources);
818
859
  return {
819
860
  values: {
820
861
  font: fonts.size === 1 ? [...fonts][0] : null,
821
862
  shape: shapes.size === 1 ? [...shapes][0] : null,
822
863
  floral: florals.size === 1 ? [...florals][0] : null,
823
- color: colors.size === 1 ? [...colors][0] : null,
864
+ color: colorSources.length > 0 && colors.size === 1 ? [...colors][0] : null,
824
865
  },
825
866
  isUniform: {
826
867
  font: fonts.size === 1,
827
868
  shape: shapes.size === 1,
828
869
  floral: florals.size === 1,
829
- color: colors.size === 1,
870
+ color: colorSources.length > 0 && colors.size === 1,
830
871
  },
831
872
  };
832
873
  };
833
- const renderUniformLabels = (ctx, uniformProps, x, y, maxWidth, scaleFactor, imageRefs, textPositions) => {
874
+ const renderUniformLabels = (ctx, uniformProps, x, y, maxWidth, scaleFactor, imageRefs, textPositions, fields) => {
834
875
  const { values } = uniformProps;
835
876
  const fontSize = LAYOUT.OTHER_FONT_SIZE * scaleFactor;
836
877
  const lineGap = LAYOUT.LINE_GAP * scaleFactor;
878
+ const shouldRenderField = (field) => {
879
+ if (!fields)
880
+ return true;
881
+ return fields[field] === true;
882
+ };
837
883
  ctx.save();
838
884
  ctx.font = `${fontSize}px ${LAYOUT.FONT_FAMILY}`;
839
885
  ctx.fillStyle = LAYOUT.LABEL_COLOR;
840
886
  let cursorY = y;
841
887
  let rendered = 0;
842
- if (values.font) {
888
+ if (values.font && shouldRenderField("font")) {
843
889
  const allDefault = textPositions.every((p) => p.is_font_default === true);
844
- const fontLabel = allDefault ? `Font: ${values.font} (Mặc định)` : `Font: ${values.font}`;
890
+ const fontLabel = allDefault ? `Font: ${values.font} (Mặc định)` : `Font: ${values.font} (Custom)`;
845
891
  const result = wrapText(ctx, fontLabel, x, cursorY, maxWidth, fontSize + lineGap);
846
892
  cursorY += result.height;
847
893
  rendered++;
848
894
  }
849
- if (values.shape && values.shape !== "None") {
895
+ if (values.shape && values.shape !== "None" && shouldRenderField("shape")) {
850
896
  const result = wrapText(ctx, `Kiểu chữ: ${values.shape}`, x, cursorY, maxWidth, fontSize + lineGap);
851
897
  cursorY += result.height;
852
898
  rendered++;
853
899
  }
854
- if (values.color && values.color !== "None") {
900
+ if (values.color && values.color !== "None" && shouldRenderField("color")) {
855
901
  const textMaxWidth = Math.max(LAYOUT.MIN_TEXT_WIDTH * scaleFactor, maxWidth - LAYOUT.SWATCH_RESERVED_SPACE * scaleFactor);
856
902
  const result = wrapText(ctx, `Màu chỉ: ${values.color}`, x, cursorY, textMaxWidth, fontSize + lineGap);
857
903
  const swatchH = Math.floor(fontSize * LAYOUT.SWATCH_HEIGHT_RATIO);
@@ -866,7 +912,7 @@ const renderUniformLabels = (ctx, uniformProps, x, y, maxWidth, scaleFactor, ima
866
912
  cursorY += result.height;
867
913
  rendered++;
868
914
  }
869
- if (values.floral && values.floral !== "None") {
915
+ if (values.floral && values.floral !== "None" && shouldRenderField("floral")) {
870
916
  const result = wrapText(ctx, `Mẫu hoa: ${values.floral}`, x, cursorY, maxWidth, fontSize + lineGap);
871
917
  cursorY += result.height;
872
918
  rendered++;
@@ -934,7 +980,7 @@ const renderTextPosition = (ctx, position, x, y, maxWidth, displayIndex, showLab
934
980
  if (showLabels.font && position.font) {
935
981
  const fontLabel = position.is_font_default === true
936
982
  ? `Font: ${position.font} (Mặc định)`
937
- : `Font: ${position.font}`;
983
+ : `Font: ${position.font} (Custom)`;
938
984
  const result = wrapText(ctx, fontLabel, x, currentY, maxWidth, otherFontSize + lineGap);
939
985
  currentY += result.height;
940
986
  drawnHeight += result.height;
@@ -964,7 +1010,7 @@ const renderTextPosition = (ctx, position, x, y, maxWidth, displayIndex, showLab
964
1010
  ctx.restore();
965
1011
  return drawnHeight;
966
1012
  };
967
- const renderIconPosition = (ctx, position, x, y, maxWidth, scaleFactor, imageRefs) => {
1013
+ const renderIconPosition = (ctx, position, x, y, maxWidth, scaleFactor, imageRefs, options) => {
968
1014
  const iconFontSize = LAYOUT.OTHER_FONT_SIZE * scaleFactor;
969
1015
  const lineGap = LAYOUT.LINE_GAP * scaleFactor;
970
1016
  ctx.save();
@@ -997,7 +1043,10 @@ const renderIconPosition = (ctx, position, x, y, maxWidth, scaleFactor, imageRef
997
1043
  : position.color
998
1044
  ? [position.color]
999
1045
  : null;
1000
- if (iconColors?.length) {
1046
+ const layerCount = position.layer_colors?.length ?? 0;
1047
+ const hasMultiLayerColors = layerCount > 1;
1048
+ const shouldSkipColorSection = options?.hideColor && !hasMultiLayerColors;
1049
+ if (iconColors?.length && !shouldSkipColorSection) {
1001
1050
  const textMaxWidth = Math.max(LAYOUT.MIN_TEXT_WIDTH * scaleFactor, maxWidth - LAYOUT.SWATCH_RESERVED_SPACE * scaleFactor);
1002
1051
  const colorResult = wrapText(ctx, `Màu chỉ: ${iconColors.join(", ")}`, x, cursorY, textMaxWidth, iconFontSize + lineGap);
1003
1052
  const swatchH = Math.floor(iconFontSize * LAYOUT.SWATCH_HEIGHT_RATIO);