fix: 箱唛增加箱子总数,QuestPDF 注册中文字体防乱码
All checks were successful
Build and Deploy / deploy (push) Successful in 1m1s

入库箱唛字段 boxCount;渲染时嵌入 CJK 字体,镜像安装 fonts-noto-cjk。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zzh 2026-10-23 12:44:49 +08:00
parent ca95e19186
commit 1cef872823
5 changed files with 103 additions and 0 deletions

View File

@ -0,0 +1,88 @@
using QuestPDF.Drawing;
namespace Oms.Application.Labeling.Hiprint;
/// <summary>
/// 面单 PDF 中文字体:QuestPDF 默认字体无 CJK,不注册会显示 □ / ?。
/// </summary>
public static class LabelPdfFontRegistrar
{
public const string FamilyName = "OmsLabelSans";
private static int _state; // 0=未试 1=成功 2=失败
private static string? _sourcePath;
/// <summary>
/// 确保已注册可用中文字体;成功返回 <see cref="FamilyName"/>,否则 null(仅西文)。
/// </summary>
public static string? EnsureRegistered()
{
var state = Volatile.Read(ref _state);
if (state == 1)
{
return FamilyName;
}
if (state == 2)
{
return null;
}
foreach (var path in EnumerateCandidates())
{
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
{
continue;
}
try
{
using var stream = File.OpenRead(path);
FontManager.RegisterFontWithCustomName(FamilyName, stream);
_sourcePath = path;
Interlocked.Exchange(ref _state, 1);
return FamilyName;
}
catch
{
// 试下一个候选
}
}
Interlocked.Exchange(ref _state, 2);
return null;
}
/// <summary>已成功注册时的字体文件路径(诊断用)。</summary>
public static string? RegisteredSourcePath => _sourcePath;
private static IEnumerable<string> EnumerateCandidates()
{
var fromEnv = Environment.GetEnvironmentVariable("OMS_LABEL_FONT");
if (!string.IsNullOrWhiteSpace(fromEnv))
{
yield return fromEnv.Trim();
}
var baseDir = AppContext.BaseDirectory;
yield return Path.Combine(baseDir, "Fonts", "NotoSansSC-Regular.otf");
yield return Path.Combine(baseDir, "Fonts", "NotoSansSC-Regular.ttf");
yield return Path.Combine(baseDir, "Fonts", "NotoSansCJKsc-Regular.otf");
// Windows
var winFonts = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
if (!string.IsNullOrWhiteSpace(winFonts))
{
yield return Path.Combine(winFonts, "msyh.ttc");
yield return Path.Combine(winFonts, "msyhbd.ttc");
yield return Path.Combine(winFonts, "simhei.ttf");
yield return Path.Combine(winFonts, "simsun.ttc");
}
// Debian/Ubuntu(Dockerfile 安装 fonts-noto-cjk)
yield return "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc";
yield return "/usr/share/fonts/opentype/noto/NotoSansCJKsc-Regular.otf";
yield return "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc";
yield return "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc";
}
}

View File

@ -23,6 +23,8 @@ public sealed class QuestPdfLabelRenderer(HiprintTemplateParser parser) : ILabel
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
EnsureLicense(); EnsureLicense();
// SKU 名称等含中文;未注册 CJK 字体会输出 □
_ = LabelPdfFontRegistrar.EnsureRegistered();
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
if (printDataPages == null || printDataPages.Count == 0) if (printDataPages == null || printDataPages.Count == 0)
@ -90,6 +92,12 @@ public sealed class QuestPdfLabelRenderer(HiprintTemplateParser parser) : ILabel
.FontSize(text.FontSizePt) .FontSize(text.FontSizePt)
.FontColor(ParseColor(text.ColorHex)); .FontColor(ParseColor(text.ColorHex));
var fontFamily = LabelPdfFontRegistrar.EnsureRegistered();
if (!string.IsNullOrEmpty(fontFamily))
{
span.FontFamily(fontFamily);
}
if (text.Bold) if (text.Bold)
{ {
span.Bold(); span.Bold();

View File

@ -61,6 +61,7 @@ public static class InboundBoxLabelPrintDataBuilder
["boxId"] = box.Id, ["boxId"] = box.Id,
["boxSeqNo"] = box.BoxSeqNo, ["boxSeqNo"] = box.BoxSeqNo,
["boxCount"] = pageCount,
["markBoxNo"] = box.MarkBoxNo, ["markBoxNo"] = box.MarkBoxNo,
["barcode"] = barcode, ["barcode"] = barcode,
@ -103,6 +104,7 @@ public static class InboundBoxLabelPrintDataBuilder
["remark"] = null, ["remark"] = null,
["boxId"] = 1, ["boxId"] = 1,
["boxSeqNo"] = "1", ["boxSeqNo"] = "1",
["boxCount"] = 2,
["markBoxNo"] = "BOX-001", ["markBoxNo"] = "BOX-001",
["barcode"] = "BOX-001", ["barcode"] = "BOX-001",
["warehouseName"] = "SZ-01", ["warehouseName"] = "SZ-01",

View File

@ -85,6 +85,7 @@ public static class LabelFieldDictionary
Field("markReference", "唛头参考"), Field("markReference", "唛头参考"),
Field("markBoxNo", "箱唛号"), Field("markBoxNo", "箱唛号"),
Field("boxSeqNo", "箱序号"), Field("boxSeqNo", "箱序号"),
Field("boxCount", "箱子总数"),
Field("barcode", "条码内容"), Field("barcode", "条码内容"),
Field("boxIndexText", "箱序页码"), Field("boxIndexText", "箱序页码"),
Field("skuSummary", "SKU 摘要"), Field("skuSummary", "SKU 摘要"),

View File

@ -24,6 +24,10 @@ RUN dotnet publish "SimpleAdmin.Api.csproj" -c $BUILD_CONFIGURATION -o /app/publ
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app WORKDIR /app
# 面单 QuestPDF 需 CJK 字体,否则中文 SKU 等渲染为 □
RUN apt-get update \
&& apt-get install -y --no-install-recommends fonts-noto-cjk \
&& rm -rf /var/lib/apt/lists/*
COPY --from=build /app/publish . COPY --from=build /app/publish .
# Local 存储落盘 wwwroot;非 root 用户需可写 uploads(Provider=Cos 时可不依赖此目录) # Local 存储落盘 wwwroot;非 root 用户需可写 uploads(Provider=Cos 时可不依赖此目录)
RUN mkdir -p /app/wwwroot/uploads \ RUN mkdir -p /app/wwwroot/uploads \