fix:字体加粗

This commit is contained in:
dzc committed 2026-09-07 15:17:51 +08:00
1 parent 906a970b08
commit 5460d1471d
2 files changed
+56 -34

No files matched your search

@@ -75,26 +75,31 @@ public class ThermalReceiptImage {
* @return 当前绘制器
*/
public ThermalReceiptImage title(String text) {
setFont(Font.BOLD, 34);
setFont(Font.BOLD, 42);
drawCentered(text, 16);
return this;
}
/**
* 绘制带黑色边框的大号打印次数提示。
* 绘制带黑色边框的大号打印次数提示,文字超过可用纸宽时自动换行。
*
* @param text 提示文本,例如“第 1 次打印”
* @return 当前绘制器
*/
public ThermalReceiptImage banner(String text) {
setFont(Font.BOLD, 38);
setFont(Font.BOLD, 44);
FontMetrics metrics = graphics.getFontMetrics();
int height = metrics.getHeight() + 30;
List<String> lines = wrap(text == null ? "" : text, contentWidth() - 32, metrics);
int lineHeight = metrics.getHeight() + 4;
int height = lines.size() * lineHeight + 26;
ensureCapacity(height + 20);
graphics.setStroke(new BasicStroke(3F));
graphics.drawRect(HORIZONTAL_PADDING, cursorY, contentWidth(), height);
int x = HORIZONTAL_PADDING + Math.max(0, (contentWidth() - metrics.stringWidth(text)) / 2);
graphics.drawString(text, x, cursorY + 15 + metrics.getAscent());
for (int index = 0; index < lines.size(); index++) {
String line = lines.get(index);
int x = HORIZONTAL_PADDING + Math.max(0, (contentWidth() - metrics.stringWidth(line)) / 2);
graphics.drawString(line, x, cursorY + 13 + index * lineHeight + metrics.getAscent());
}
cursorY += height + 20;
return this;
}
@@ -106,34 +111,38 @@ public class ThermalReceiptImage {
* @return 当前绘制器
*/
public ThermalReceiptImage verificationCode(String shortCode) {
setFont(Font.BOLD, 52);
setFont(Font.BOLD, 64);
drawCentered(shortCode == null || shortCode.isBlank() ? "--" : shortCode, 18);
return this;
}
/**
* 绘制一行左右键值信息,值过长时在下一行继续换行。
* 绘制一行左右键值信息,值无法完整同行显示时整段从下一行开始并继续换行。
*
* @param label 左侧字段名
* @param value 右侧字段值
* @return 当前绘制器
*/
public ThermalReceiptImage keyValue(String label, String value) {
setFont(Font.PLAIN, 25);
setFont(Font.PLAIN, 33);
String safeLabel = label == null ? "" : label;
String safeValue = value == null || value.isBlank() ? "--" : value;
FontMetrics metrics = graphics.getFontMetrics();
int labelWidth = metrics.stringWidth(safeLabel + ":");
int valueWidth = contentWidth() - labelWidth - 12;
List<String> lines = wrap(safeValue, Math.max(120, valueWidth), metrics);
int inlineValueWidth = contentWidth() - labelWidth - 12;
boolean stackValue = metrics.stringWidth(safeValue) > inlineValueWidth;
int valueWidth = stackValue ? contentWidth() : Math.max(120, inlineValueWidth);
List<String> lines = wrap(safeValue, valueWidth, metrics);
int valueLineOffset = stackValue ? 1 : 0;
int lineHeight = metrics.getHeight() + 6;
ensureCapacity(lines.size() * lineHeight + 10);
ensureCapacity((lines.size() + valueLineOffset) * lineHeight + 10);
graphics.drawString(safeLabel + ":", HORIZONTAL_PADDING, cursorY + metrics.getAscent());
for (int index = 0; index < lines.size(); index++) {
int x = index == 0 ? HORIZONTAL_PADDING + labelWidth + 12 : HORIZONTAL_PADDING + labelWidth + 12;
graphics.drawString(lines.get(index), x, cursorY + index * lineHeight + metrics.getAscent());
int x = stackValue ? HORIZONTAL_PADDING : HORIZONTAL_PADDING + labelWidth + 12;
graphics.drawString(lines.get(index), x,
cursorY + (index + valueLineOffset) * lineHeight + metrics.getAscent());
}
cursorY += lines.size() * lineHeight + 6;
cursorY += (lines.size() + valueLineOffset) * lineHeight + 6;
return this;
}
@@ -144,8 +153,8 @@ public class ThermalReceiptImage {
* @return 当前绘制器
*/
public ThermalReceiptImage sectionTitle(String text) {
setFont(Font.BOLD, 28);
ensureCapacity(48);
setFont(Font.BOLD, 36);
ensureCapacity(60);
graphics.drawString(text == null ? "" : text, HORIZONTAL_PADDING, cursorY + graphics.getFontMetrics().getAscent());
cursorY += graphics.getFontMetrics().getHeight() + 10;
return this;
@@ -173,12 +182,12 @@ public class ThermalReceiptImage {
String summary = "订餐日期:" + (orderDate == null || orderDate.isBlank() ? "--" : orderDate)
+ " 餐段:" + (mealName == null || mealName.isBlank() ? "--" : mealName);
setFont(Font.BOLD, 20);
setFont(Font.BOLD, 25);
FontMetrics tagMetrics = graphics.getFontMetrics();
int tagWidth = tagMetrics.stringWidth(tag) + 20;
int tagHeight = tagMetrics.getHeight() + 6;
setFont(Font.BOLD, 24);
setFont(Font.BOLD, 31);
FontMetrics orderMetrics = graphics.getFontMetrics();
int orderX = HORIZONTAL_PADDING + tagWidth + 14;
int orderWidth = PAPER_WIDTH_PIXELS - HORIZONTAL_PADDING - orderX;
@@ -186,7 +195,7 @@ public class ThermalReceiptImage {
int orderLineHeight = orderMetrics.getHeight() + 4;
int headerHeight = Math.max(tagHeight, orderLines.size() * orderLineHeight);
setFont(Font.PLAIN, 21);
setFont(Font.PLAIN, 29);
FontMetrics summaryMetrics = graphics.getFontMetrics();
List<String> summaryLines = wrap(summary, contentWidth(), summaryMetrics);
int summaryLineHeight = summaryMetrics.getHeight() + 4;
@@ -196,20 +205,20 @@ public class ThermalReceiptImage {
int blockTop = cursorY;
graphics.setColor(Color.BLACK);
setFont(Font.BOLD, 20);
setFont(Font.BOLD, 25);
graphics.fillRect(HORIZONTAL_PADDING, blockTop, tagWidth, tagHeight);
graphics.setColor(Color.WHITE);
graphics.drawString(tag, HORIZONTAL_PADDING + 10, blockTop + 3 + tagMetrics.getAscent());
graphics.setColor(Color.BLACK);
setFont(Font.BOLD, 24);
setFont(Font.BOLD, 31);
for (int lineIndex = 0; lineIndex < orderLines.size(); lineIndex++) {
graphics.drawString(orderLines.get(lineIndex), orderX,
blockTop + lineIndex * orderLineHeight + orderMetrics.getAscent());
}
int summaryTop = blockTop + headerHeight + 10;
setFont(Font.PLAIN, 21);
setFont(Font.PLAIN, 29);
for (int lineIndex = 0; lineIndex < summaryLines.size(); lineIndex++) {
graphics.drawString(summaryLines.get(lineIndex), HORIZONTAL_PADDING,
summaryTop + lineIndex * summaryLineHeight + summaryMetrics.getAscent());
@@ -273,17 +282,17 @@ public class ThermalReceiptImage {
.filter(item -> item != null && !item.isBlank()).toList();
int innerWidth = contentWidth() - PRODUCT_CARD_PADDING * 2;
setFont(Font.BOLD, 21);
setFont(Font.BOLD, 28);
FontMetrics tagMetrics = graphics.getFontMetrics();
int tagHeight = tagMetrics.getHeight() + 6;
int tagWidth = tagMetrics.stringWidth(typeLabel) + 20;
setFont(Font.BOLD, 26);
setFont(Font.BOLD, 40);
FontMetrics nameMetrics = graphics.getFontMetrics();
List<String> nameLines = wrap(safeName, innerWidth, nameMetrics);
int nameLineHeight = nameMetrics.getHeight() + 4;
setFont(Font.PLAIN, 21);
setFont(Font.PLAIN, 29);
FontMetrics priceMetrics = graphics.getFontMetrics();
String priceAndQuantity = "单价 ¥" + safeUnitPrice + " x" + Math.max(0, quantity);
String subtotalText = "小计 ¥" + safeSubtotal;
@@ -291,7 +300,7 @@ public class ThermalReceiptImage {
+ priceMetrics.stringWidth(subtotalText) + 24 > innerWidth;
int priceLineHeight = priceMetrics.getHeight() + 4;
setFont(Font.PLAIN, 21);
setFont(Font.PLAIN, 29);
FontMetrics componentMetrics = graphics.getFontMetrics();
int componentLineHeight = componentMetrics.getHeight() + 3;
List<List<String>> componentLines = new ArrayList<>();
@@ -313,7 +322,7 @@ public class ThermalReceiptImage {
graphics.setStroke(new BasicStroke(combo ? 4F : 2F));
graphics.drawRect(HORIZONTAL_PADDING, cardTop, contentWidth(), cardHeight);
setFont(Font.BOLD, 21);
setFont(Font.BOLD, 28);
if (combo) {
graphics.fillRect(contentX, contentY, tagWidth, tagHeight);
graphics.setColor(Color.WHITE);
@@ -326,14 +335,14 @@ public class ThermalReceiptImage {
}
contentY += tagHeight + 10;
setFont(Font.BOLD, 26);
setFont(Font.BOLD, 40);
for (String line : nameLines) {
graphics.drawString(line, contentX, contentY + nameMetrics.getAscent());
contentY += nameLineHeight;
}
contentY += 8;
setFont(Font.PLAIN, 21);
setFont(Font.PLAIN, 29);
graphics.drawString(priceAndQuantity, contentX, contentY + priceMetrics.getAscent());
if (splitPriceLine) {
contentY += priceLineHeight;
@@ -350,10 +359,10 @@ public class ThermalReceiptImage {
graphics.drawLine(contentX, contentY,
PAPER_WIDTH_PIXELS - HORIZONTAL_PADDING - PRODUCT_CARD_PADDING, contentY);
contentY += 10;
setFont(Font.BOLD, 21);
setFont(Font.BOLD, 29);
graphics.drawString("组合内容", contentX, contentY + componentMetrics.getAscent());
contentY += componentLineHeight;
setFont(Font.PLAIN, 21);
setFont(Font.PLAIN, 29);
for (List<String> lines : componentLines) {
for (String line : lines) {
graphics.drawString(line, contentX + CHILD_INDENT,
@@ -376,7 +385,7 @@ public class ThermalReceiptImage {
String safeAmount = totalAmount == null || totalAmount.isBlank() ? "--" : totalAmount;
String label = "商品合计";
String amount = "¥" + safeAmount;
setFont(Font.BOLD, 30);
setFont(Font.BOLD, 42);
FontMetrics metrics = graphics.getFontMetrics();
int blockHeight = metrics.getHeight() + 30;
ensureCapacity(blockHeight + 14);
@@ -405,7 +414,7 @@ public class ThermalReceiptImage {
*/
private ThermalReceiptImage item(String name, String trailing, boolean child, boolean emphasized,
int trailingWidth) {
setFont(emphasized ? Font.BOLD : Font.PLAIN, child ? 23 : 26);
setFont(emphasized ? Font.BOLD : Font.PLAIN, child ? 30 : 34);
FontMetrics metrics = graphics.getFontMetrics();
int indent = child ? CHILD_INDENT : 0;
String prefix = child ? "- " : "";
@@ -45,6 +45,19 @@ class ThermalReceiptImageTest {
assertEquals(576, voucher.getWidth());
}
/** 放大后的打印提示必须按纸宽自动换行,不能裁掉多订单数量等关键信息。 */
@Test
void largeBannerWrapsLongTextAndGrowsCanvas() throws Exception {
BufferedImage shortBanner = ImageIO.read(new ByteArrayInputStream(new ThermalReceiptImage()
.banner("第 1 次打印").toPngBytes()));
BufferedImage longBanner = ImageIO.read(new ByteArrayInputStream(new ThermalReceiptImage()
.banner("第 12 次打印 · 100 笔预约订单合并打印").toPngBytes()));
assertEquals(ThermalReceiptImage.PAPER_WIDTH_PIXELS, longBanner.getWidth());
assertTrue(longBanner.getHeight() > shortBanner.getHeight(),
"长提示换行后必须增加票据高度,不能越过右侧纸张边界");
}
/** 兑换券商品卡片应完整换行长名称和套餐组成,并随内容动态增高。 */
@Test
void voucherProductCardsWrapNamesAndComboComponents() throws Exception {