embroidery-qc-image 1.0.29 → 1.0.30

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.
@@ -1 +1 @@
1
- {"version":3,"file":"EmbroideryQCImage.d.ts","sourceRoot":"","sources":["../../src/components/EmbroideryQCImage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAY,MAAM,UAAU,CAAC;AAChF,OAAO,yBAAyB,CAAC;AAiKjC,MAAM,WAAW,8BAA8B;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,WAAW,GAAG,YAAY,GAAG,YAAY,CAAC;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAofD,QAAA,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAmHvD,CAAC;AAo9CF,eAAO,MAAM,6BAA6B,GACxC,QAAQ,kBAAkB,EAC1B,UAAS,8BAAmC,KAC3C,OAAO,CAAC,IAAI,GAAG,IAAI,CAuBrB,CAAC;AAEF,eAAO,MAAM,6BAA6B,GACxC,QAAQ,kBAAkB,EAC1B,UAAS,8BAAmC,KAC3C,OAAO,CAAC,MAAM,GAAG,IAAI,CAuBvB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"EmbroideryQCImage.d.ts","sourceRoot":"","sources":["../../src/components/EmbroideryQCImage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAY,MAAM,UAAU,CAAC;AAChF,OAAO,yBAAyB,CAAC;AAiKjC,MAAM,WAAW,8BAA8B;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,WAAW,GAAG,YAAY,GAAG,YAAY,CAAC;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAofD,QAAA,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAmHvD,CAAC;AA2kDF,eAAO,MAAM,6BAA6B,GACxC,QAAQ,kBAAkB,EAC1B,UAAS,8BAAmC,KAC3C,OAAO,CAAC,IAAI,GAAG,IAAI,CAuBrB,CAAC;AAEF,eAAO,MAAM,6BAA6B,GACxC,QAAQ,kBAAkB,EAC1B,UAAS,8BAAmC,KAC3C,OAAO,CAAC,MAAM,GAAG,IAAI,CAuBvB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
package/dist/index.esm.js CHANGED
@@ -1160,25 +1160,123 @@ displayIndex, showLabels, scaleFactor, imageRefs, mockupBounds = null) => {
1160
1160
  currentY += result.height;
1161
1161
  }
1162
1162
  if (showLabels.font && position.font) {
1163
- // Render "Font: " với font mặc định
1164
1163
  const prefix = "Font: ";
1165
- ctx.font = `${otherFontSize}px ${LAYOUT.FONT_FAMILY}`;
1166
- const prefixWidth = ctx.measureText(prefix).width;
1167
- let currentX = x + prefixWidth;
1168
- ctx.fillText(prefix, x, currentY);
1169
- // Render tên font với font từ config
1170
- ctx.font = `${otherFontSize}px ${position.font}`;
1171
- const fontNameWidth = ctx.measureText(position.font).width;
1172
- ctx.fillText(position.font, currentX, currentY);
1173
- currentX += fontNameWidth;
1174
- // Render "(Mặc định)" hoặc "(Custom)" với font mặc định
1175
1164
  const suffix = position.is_font_default === true ? " (Mặc định)" : " (Custom)";
1176
- ctx.font = `${otherFontSize}px ${LAYOUT.FONT_FAMILY}`;
1177
- ctx.measureText(suffix).width;
1178
- ctx.fillText(suffix, currentX, currentY);
1179
- // Tính toán height và di chuyển cursorY
1165
+ const fontName = position.font;
1166
+ const fullText = `${prefix}${fontName}${suffix}`;
1180
1167
  const lineHeight = otherFontSize + lineGap;
1181
- currentY += lineHeight;
1168
+ const textTopY = currentY;
1169
+ const effectiveMaxWidth = mockupBounds
1170
+ ? getEffectiveMaxWidth(x, textTopY, lineHeight, maxWidth, mockupBounds)
1171
+ : maxWidth;
1172
+ const MIN_FONT_FONT_SIZE = otherFontSize * 0.5;
1173
+ const measureFontTextWidth = (fontSize) => {
1174
+ ctx.font = `${fontSize}px ${LAYOUT.FONT_FAMILY}`;
1175
+ const prefixWidth = ctx.measureText(prefix).width;
1176
+ ctx.font = `${fontSize}px ${position.font}`;
1177
+ const fontNameWidth = ctx.measureText(fontName).width;
1178
+ ctx.font = `${fontSize}px ${LAYOUT.FONT_FAMILY}`;
1179
+ const suffixWidth = ctx.measureText(suffix).width;
1180
+ return prefixWidth + fontNameWidth + suffixWidth;
1181
+ };
1182
+ const checkFontSizeFits = (fontSize) => {
1183
+ const textTopY = currentY;
1184
+ const lineHeight = fontSize + lineGap;
1185
+ const effectiveMaxWidth = mockupBounds
1186
+ ? getEffectiveMaxWidth(x, textTopY, lineHeight, maxWidth, mockupBounds)
1187
+ : maxWidth;
1188
+ const textWidth = measureFontTextWidth(fontSize);
1189
+ return textWidth <= effectiveMaxWidth;
1190
+ };
1191
+ let effectiveFontSize = otherFontSize;
1192
+ let needsWrap = false;
1193
+ // Bước 1: Thử giảm font size, kiểm tra xem có vừa chiều ngang không
1194
+ const baseMaxWidth = measureFontTextWidth(otherFontSize);
1195
+ if (baseMaxWidth > effectiveMaxWidth) {
1196
+ // Binary search để tìm font size lớn nhất mà vẫn vừa
1197
+ let left = MIN_FONT_FONT_SIZE;
1198
+ let right = otherFontSize;
1199
+ let bestFontSize = MIN_FONT_FONT_SIZE;
1200
+ while (right - left > 0.1) {
1201
+ const mid = (left + right) / 2;
1202
+ if (checkFontSizeFits(mid)) {
1203
+ bestFontSize = mid;
1204
+ left = mid;
1205
+ }
1206
+ else {
1207
+ right = mid;
1208
+ }
1209
+ }
1210
+ if (checkFontSizeFits(bestFontSize)) {
1211
+ // Bước 1 thành công: font size đã shrink vừa chiều ngang
1212
+ effectiveFontSize = Math.floor(bestFontSize);
1213
+ }
1214
+ else {
1215
+ // Bước 1 thất bại: đã shrink đến MIN nhưng vẫn không vừa, sang bước 2
1216
+ needsWrap = true;
1217
+ effectiveFontSize = otherFontSize;
1218
+ }
1219
+ }
1220
+ if (needsWrap) {
1221
+ // Bước 2: Xuống dòng với font size gốc
1222
+ ctx.font = `${effectiveFontSize}px ${LAYOUT.FONT_FAMILY}`;
1223
+ const wrappedLines = buildWrappedLines(ctx, fullText, effectiveMaxWidth, x, textTopY, lineHeight, mockupBounds);
1224
+ wrappedLines.forEach((line, index) => {
1225
+ const lineY = textTopY + index * lineHeight;
1226
+ const lineEffectiveMaxWidth = mockupBounds
1227
+ ? getEffectiveMaxWidth(x, lineY, lineHeight, maxWidth, mockupBounds)
1228
+ : maxWidth;
1229
+ let lineToRender = line;
1230
+ const lineWidth = ctx.measureText(lineToRender).width;
1231
+ if (lineWidth > lineEffectiveMaxWidth) {
1232
+ while (ctx.measureText(lineToRender).width > lineEffectiveMaxWidth && lineToRender.length > 0) {
1233
+ lineToRender = lineToRender.slice(0, -1);
1234
+ }
1235
+ }
1236
+ ctx.fillText(lineToRender, x, lineY);
1237
+ });
1238
+ currentY += wrappedLines.length * lineHeight;
1239
+ }
1240
+ else {
1241
+ // Bước 1 thành công: Render với font size đã shrink (1 dòng)
1242
+ const shrunkTextTopY = currentY;
1243
+ const shrunkLineHeight = effectiveFontSize + lineGap;
1244
+ const shrunkEffectiveMaxWidth = mockupBounds
1245
+ ? getEffectiveMaxWidth(x, shrunkTextTopY, shrunkLineHeight, maxWidth, mockupBounds)
1246
+ : maxWidth;
1247
+ ctx.font = `${effectiveFontSize}px ${LAYOUT.FONT_FAMILY}`;
1248
+ const prefixWidth = ctx.measureText(prefix).width;
1249
+ let currentX = x + prefixWidth;
1250
+ ctx.fillText(prefix, x, currentY);
1251
+ ctx.font = `${effectiveFontSize}px ${position.font}`;
1252
+ const fontNameWidth = ctx.measureText(fontName).width;
1253
+ const totalWidth = prefixWidth + fontNameWidth;
1254
+ if (totalWidth > shrunkEffectiveMaxWidth) {
1255
+ // Cần cắt font name
1256
+ let truncatedFontName = fontName;
1257
+ while (ctx.measureText(truncatedFontName).width > shrunkEffectiveMaxWidth - prefixWidth && truncatedFontName.length > 0) {
1258
+ truncatedFontName = truncatedFontName.slice(0, -1);
1259
+ }
1260
+ ctx.fillText(truncatedFontName, currentX, currentY);
1261
+ currentX += ctx.measureText(truncatedFontName).width;
1262
+ }
1263
+ else {
1264
+ ctx.fillText(fontName, currentX, currentY);
1265
+ currentX += fontNameWidth;
1266
+ }
1267
+ ctx.font = `${effectiveFontSize}px ${LAYOUT.FONT_FAMILY}`;
1268
+ const remainingWidth = shrunkEffectiveMaxWidth - (currentX - x);
1269
+ if (remainingWidth > 0) {
1270
+ let truncatedSuffix = suffix;
1271
+ while (ctx.measureText(truncatedSuffix).width > remainingWidth && truncatedSuffix.length > 0) {
1272
+ truncatedSuffix = truncatedSuffix.slice(0, -1);
1273
+ }
1274
+ if (truncatedSuffix.length > 0) {
1275
+ ctx.fillText(truncatedSuffix, currentX, currentY);
1276
+ }
1277
+ }
1278
+ currentY += shrunkLineHeight;
1279
+ }
1182
1280
  }
1183
1281
  if (showLabels.color) {
1184
1282
  const colorValue = position.character_colors?.join(", ") || position.color;
@@ -1324,126 +1422,115 @@ const renderIconPosition = (ctx, position, x, y, maxWidth, scaleFactor, imageRef
1324
1422
  }
1325
1423
  }
1326
1424
  }
1327
- // Tính available width cho icon value (trừ đi khoảng trống cho icon_image)
1328
1425
  const availableWidth = Math.max(1, maxWidth - labelWidth - iconImageReservedWidth);
1329
- // Tính font-size hiệu dụng cho icon value
1330
- // Giới hạn thu nhỏ tối đa 50% (tối thiểu = iconFontSize * 0.5)
1426
+ const textCenterY = cursorY + iconImageHeight / 2;
1427
+ const valueStartX = x + labelWidth;
1428
+ const textTopY = textCenterY - iconFontSize / 2;
1429
+ const lineHeight = iconFontSize;
1430
+ const effectiveMaxWidth = mockupBounds
1431
+ ? getEffectiveMaxWidth(valueStartX, textTopY, lineHeight, availableWidth, mockupBounds)
1432
+ : availableWidth;
1331
1433
  const MIN_ICON_VALUE_FONT_SIZE = iconFontSize * 0.5;
1332
1434
  const measureIconValueWidth = (fontSize) => {
1333
1435
  ctx.font = `${fontSize}px ${LAYOUT.FONT_FAMILY}`;
1334
1436
  return ctx.measureText(` ${iconValue}`).width;
1335
1437
  };
1438
+ const checkFontSizeFits = (fontSize) => {
1439
+ const textTopY = textCenterY - fontSize / 2;
1440
+ const lineHeight = fontSize;
1441
+ const effectiveMaxWidth = mockupBounds
1442
+ ? getEffectiveMaxWidth(valueStartX, textTopY, lineHeight, availableWidth, mockupBounds)
1443
+ : availableWidth;
1444
+ const textWidth = measureIconValueWidth(fontSize);
1445
+ return textWidth <= effectiveMaxWidth;
1446
+ };
1336
1447
  let effectiveIconValueFontSize = iconFontSize;
1337
- const baseMaxWidth = measureIconValueWidth(iconFontSize);
1338
1448
  let needsWrap = false;
1339
- if (baseMaxWidth > availableWidth) {
1340
- const shrinkRatio = availableWidth / baseMaxWidth;
1341
- effectiveIconValueFontSize = Math.max(MIN_ICON_VALUE_FONT_SIZE, iconFontSize * shrinkRatio);
1342
- // Kiểm tra xem sau khi thu nhỏ đến 50% vẫn overflow không
1343
- const minMaxWidth = measureIconValueWidth(MIN_ICON_VALUE_FONT_SIZE);
1344
- if (minMaxWidth > availableWidth) {
1345
- // Vẫn overflow, cần dùng wrap text
1449
+ // Bước 1: Thử giảm font size, kiểm tra xem có vừa chiều ngang không
1450
+ const baseMaxWidth = measureIconValueWidth(iconFontSize);
1451
+ if (baseMaxWidth > effectiveMaxWidth) {
1452
+ // Binary search để tìm font size lớn nhất vẫn vừa
1453
+ let left = MIN_ICON_VALUE_FONT_SIZE;
1454
+ let right = iconFontSize;
1455
+ let bestFontSize = MIN_ICON_VALUE_FONT_SIZE;
1456
+ while (right - left > 0.1) {
1457
+ const mid = (left + right) / 2;
1458
+ if (checkFontSizeFits(mid)) {
1459
+ bestFontSize = mid;
1460
+ left = mid;
1461
+ }
1462
+ else {
1463
+ right = mid;
1464
+ }
1465
+ }
1466
+ if (checkFontSizeFits(bestFontSize)) {
1467
+ // Bước 1 thành công: font size đã shrink vừa chiều ngang
1468
+ effectiveIconValueFontSize = Math.floor(bestFontSize);
1469
+ }
1470
+ else {
1471
+ // Bước 1 thất bại: đã shrink đến MIN nhưng vẫn không vừa, sang bước 2
1346
1472
  needsWrap = true;
1347
- effectiveIconValueFontSize = MIN_ICON_VALUE_FONT_SIZE;
1473
+ effectiveIconValueFontSize = iconFontSize;
1348
1474
  }
1349
1475
  }
1350
- // Tính line height block height cho icon value
1351
- const valueLineHeight = effectiveIconValueFontSize;
1352
- let allWrappedLines = [];
1353
- // Text align center: căn giữa theo chiều dọc trong block
1354
- const textCenterY = cursorY + iconImageHeight / 2;
1355
- const valueStartX = x + labelWidth;
1356
- if (needsWrap) {
1357
- // Dùng wrap text logic
1358
- const wrappedLines = buildWrappedLines(ctx, iconValue, availableWidth, valueStartX, textCenterY, valueLineHeight, mockupBounds);
1359
- allWrappedLines = wrappedLines;
1360
- wrappedLines.length * valueLineHeight;
1361
- }
1362
- else {
1363
- // Không cần wrap, chỉ một dòng
1364
- allWrappedLines = [iconValue];
1365
- }
1366
- // Vẽ label với textBaseline = middle để align center với value
1476
+ // Bước 2: Nếu bước 1 thất bại, xuống dòng với font size gốc
1367
1477
  ctx.textBaseline = "middle";
1368
1478
  ctx.font = `bold ${iconFontSize}px ${LAYOUT.FONT_FAMILY}`;
1369
1479
  ctx.fillStyle = LAYOUT.LABEL_COLOR;
1370
1480
  ctx.fillText(iconLabel, x, textCenterY);
1371
- // Vẽ icon value với align center
1372
1481
  ctx.font = `${effectiveIconValueFontSize}px ${LAYOUT.FONT_FAMILY}`;
1373
1482
  ctx.fillStyle = DEFAULT_ERROR_COLOR;
1374
1483
  let maxValueLineWidth = 0;
1375
- // Vẽ icon value, căn giữa theo chiều dọc
1484
+ let totalTextHeight = iconImageHeight;
1376
1485
  if (needsWrap) {
1377
- // nhiều dòng sau khi wrap
1378
- allWrappedLines.forEach((line, index) => {
1379
- const lineY = textCenterY - (allWrappedLines.length - 1) / 2 * valueLineHeight + index * valueLineHeight;
1380
- // Kiểm tra overlap với mockup và điều chỉnh text nếu cần
1381
- if (mockupBounds) {
1382
- const effectiveMaxWidth = getEffectiveMaxWidth(valueStartX, lineY, valueLineHeight, availableWidth, mockupBounds);
1383
- const lineWidth = ctx.measureText(line).width;
1384
- if (lineWidth > effectiveMaxWidth) {
1385
- // Cắt text cho đến khi vừa với effectiveMaxWidth
1386
- let truncatedLine = line;
1387
- while (ctx.measureText(truncatedLine).width > effectiveMaxWidth && truncatedLine.length > 0) {
1388
- truncatedLine = truncatedLine.slice(0, -1);
1389
- }
1390
- ctx.fillText(truncatedLine, valueStartX, lineY);
1391
- const w = ctx.measureText(truncatedLine).width;
1392
- if (w > maxValueLineWidth)
1393
- maxValueLineWidth = w;
1394
- }
1395
- else {
1396
- ctx.fillText(line, valueStartX, lineY);
1397
- const w = ctx.measureText(line).width;
1398
- if (w > maxValueLineWidth)
1399
- maxValueLineWidth = w;
1486
+ // Bước 2: Xuống dòng với font size gốc
1487
+ const wrappedLines = buildWrappedLines(ctx, iconValue, effectiveMaxWidth, valueStartX, textTopY, lineHeight, mockupBounds);
1488
+ // Tính height dựa trên số dòng thực tế
1489
+ totalTextHeight = Math.max(iconImageHeight, wrappedLines.length * lineHeight);
1490
+ wrappedLines.forEach((line, index) => {
1491
+ const lineTopY = textTopY + index * lineHeight;
1492
+ const lineCenterY = lineTopY + lineHeight / 2;
1493
+ const lineEffectiveMaxWidth = mockupBounds
1494
+ ? getEffectiveMaxWidth(valueStartX, lineTopY, lineHeight, availableWidth, mockupBounds)
1495
+ : availableWidth;
1496
+ let lineToRender = line;
1497
+ const lineWidth = ctx.measureText(lineToRender).width;
1498
+ if (lineWidth > lineEffectiveMaxWidth) {
1499
+ while (ctx.measureText(lineToRender).width > lineEffectiveMaxWidth && lineToRender.length > 0) {
1500
+ lineToRender = lineToRender.slice(0, -1);
1400
1501
  }
1401
1502
  }
1402
- else {
1403
- ctx.fillText(line, valueStartX, lineY);
1404
- const w = ctx.measureText(line).width;
1405
- if (w > maxValueLineWidth)
1406
- maxValueLineWidth = w;
1407
- }
1503
+ ctx.fillText(lineToRender, valueStartX, lineCenterY);
1504
+ const w = ctx.measureText(lineToRender).width;
1505
+ if (w > maxValueLineWidth)
1506
+ maxValueLineWidth = w;
1408
1507
  });
1409
1508
  }
1410
1509
  else {
1411
- // Chỉ một dòng, căn giữa
1510
+ // Bước 1 thành công: Render với font size đã shrink (1 dòng)
1511
+ const shrunkTextTopY = textCenterY - effectiveIconValueFontSize / 2;
1512
+ const shrunkLineHeight = effectiveIconValueFontSize;
1513
+ const shrunkEffectiveMaxWidth = mockupBounds
1514
+ ? getEffectiveMaxWidth(valueStartX, shrunkTextTopY, shrunkLineHeight, availableWidth, mockupBounds)
1515
+ : availableWidth;
1412
1516
  const lineText = ` ${iconValue}`;
1413
- // Kiểm tra overlap với mockup và điều chỉnh text nếu cần
1414
- if (mockupBounds) {
1415
- const effectiveMaxWidth = getEffectiveMaxWidth(valueStartX, textCenterY, valueLineHeight, availableWidth, mockupBounds);
1416
- const lineWidth = ctx.measureText(lineText).width;
1417
- if (lineWidth > effectiveMaxWidth) {
1418
- // Cắt text cho đến khi vừa với effectiveMaxWidth
1419
- let truncatedLine = lineText;
1420
- while (ctx.measureText(truncatedLine).width > effectiveMaxWidth && truncatedLine.length > 0) {
1421
- truncatedLine = truncatedLine.slice(0, -1);
1422
- }
1423
- ctx.fillText(truncatedLine, valueStartX, textCenterY);
1424
- const w = ctx.measureText(truncatedLine).width;
1425
- if (w > maxValueLineWidth)
1426
- maxValueLineWidth = w;
1427
- }
1428
- else {
1429
- ctx.fillText(lineText, valueStartX, textCenterY);
1430
- const w = ctx.measureText(lineText).width;
1431
- if (w > maxValueLineWidth)
1432
- maxValueLineWidth = w;
1517
+ let lineToRender = lineText;
1518
+ const lineWidth = ctx.measureText(lineToRender).width;
1519
+ if (lineWidth > shrunkEffectiveMaxWidth) {
1520
+ while (ctx.measureText(lineToRender).width > shrunkEffectiveMaxWidth && lineToRender.length > 0) {
1521
+ lineToRender = lineToRender.slice(0, -1);
1433
1522
  }
1434
1523
  }
1435
- else {
1436
- ctx.fillText(lineText, valueStartX, textCenterY);
1437
- const w = ctx.measureText(lineText).width;
1438
- if (w > maxValueLineWidth)
1439
- maxValueLineWidth = w;
1440
- }
1524
+ ctx.fillText(lineToRender, valueStartX, textCenterY);
1525
+ const w = ctx.measureText(lineToRender).width;
1526
+ if (w > maxValueLineWidth)
1527
+ maxValueLineWidth = w;
1441
1528
  }
1442
1529
  ctx.fillStyle = LAYOUT.LABEL_COLOR;
1443
1530
  // Reset textBaseline về top cho các phần tiếp theo
1444
1531
  ctx.textBaseline = LAYOUT.TEXT_BASELINE;
1445
1532
  const iconResult = {
1446
- height: iconImageHeight + lineGap,
1533
+ height: totalTextHeight + lineGap,
1447
1534
  // tổng width của cả label + value, dùng để canh icon image lệch sang phải
1448
1535
  lastLineWidth: labelWidth + maxValueLineWidth};
1449
1536
  // Kiểm tra xem phần icon image có vượt quá canvas không trước khi render