diff --git a/XP.ReportEngine/Documents/FontFilesGuidance.md b/XP.ReportEngine/Documents/FontFilesGuidance.md new file mode 100644 index 0000000..767ae02 --- /dev/null +++ b/XP.ReportEngine/Documents/FontFilesGuidance.md @@ -0,0 +1,54 @@ +# 字体文件目录 | Font Files Directory + +## 所需字体文件 | Required Font Files + +请将以下字体文件手动添加到 `XP.ReportEngine/Fonts/` 目录: + +1. **NotoSansCJKsc-Regular.otf** — 中文字体(CJK 字符支持)| Chinese font (CJK character support) +2. **NotoSans-Regular.ttf** — 西文字体 | Western font + +## 下载地址 | Download Links + +- Noto Sans CJK: https://github.com/googlefonts/noto-cjk/releases + - 下载 `NotoSansCJKsc-Regular.otf`(简体中文版本) + - 文件大小约 16MB +- Noto Sans: https://fonts.google.com/noto/specimen/Noto+Sans + - 下载 Regular 字重的 TTF 文件 + - 文件大小约 500KB + +## 下载步骤 | Download Steps + +### NotoSansCJKsc-Regular.otf + +1. 访问 https://github.com/googlefonts/noto-cjk/releases +2. 找到最新 Release,展开 Assets +3. 下载 `NotoSansCJKsc-Regular.otf`(或从 Sans 包中提取) +4. 将文件放入 `XP.ReportEngine/Fonts/` 目录 + +### NotoSans-Regular.ttf + +1. 访问 https://fonts.google.com/noto/specimen/Noto+Sans +2. 点击 "Download family" +3. 解压后找到 `NotoSans-Regular.ttf` +4. 将文件放入 `XP.ReportEngine/Fonts/` 目录 + +## 配置说明 | Configuration Notes + +字体文件已在 `.csproj` 中配置为嵌入资源(EmbeddedResource),添加文件后无需额外配置。 +Font files are configured as EmbeddedResource in .csproj, no additional configuration needed after adding files. + +```xml + + + + +``` + +## 后备机制 | Fallback Mechanism + +如果字体文件缺失,ITextPdfRenderer 会按以下顺序降级: +1. 尝试加载嵌入的 CJK 字体和西文字体 +2. 加载失败时记录警告日志 +3. 最终使用 iText 内置 Helvetica 字体(不支持中文字符) + +因此,如果需要生成包含中文内容的报告,**必须**添加 NotoSansCJKsc-Regular.otf 字体文件。 diff --git a/XP.ReportEngine/Documents/README.md b/XP.ReportEngine/Documents/README.md new file mode 100644 index 0000000..f4c63ac --- /dev/null +++ b/XP.ReportEngine/Documents/README.md @@ -0,0 +1,214 @@ +# XP.ReportEngine — PDF 报告生成引擎 + +## 概述 + +XP.ReportEngine 是 XplorePlane 平面 CT 检测系统的 PDF 报告生成模块。负责将 XP.ImageProcessing 的检测分析结果(距离测量、BGA 气泡率、空隙测量、通孔填锡率)转换为结构化的 PDF 检测报告。 + +模块采用管线式架构(Pipeline),将报告生成过程分解为五个独立阶段: +**模板加载 → 数据适配 → 数据绑定 → 排版计算 → PDF 渲染** + +## 技术栈 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| .NET 8.0 | net8.0-windows7.0 | 运行时 | +| itext7 | 8.0.5 | PDF 文档生成核心库 | +| itext7.bouncy-castle-adapter | 8.0.5 | iText 7 加密支持 | +| Newtonsoft.Json | 13.0.3 | JSON 模板反序列化 | +| Prism.Wpf | 9.0.537 | 模块化框架与 DI | + +## 项目结构 + +``` +XP.ReportEngine/ +├── Interfaces/ # 核心接口定义 +│ ├── IReportGenerator.cs +│ ├── IReportGeneratorFactory.cs +│ ├── ITemplateEngine.cs +│ ├── IDataBinder.cs +│ ├── ILayoutEngine.cs +│ ├── IPdfRenderer.cs +│ └── IReportDataAdapter.cs +├── Models/ # 数据模型 +│ ├── ReportContext.cs +│ ├── ImageData.cs +│ ├── ReportTemplate.cs +│ ├── TemplateElement.cs +│ ├── LayoutPage.cs +│ ├── ReportResult.cs +│ ├── ReportGenerationOptions.cs +│ └── ProcessorOutput.cs +├── Services/ # 服务实现 +│ ├── PdfReportGenerator.cs # 管线协调器 +│ ├── ReportGeneratorFactory.cs # 工厂 +│ ├── JsonTemplateEngine.cs # JSON 模板加载与验证 +│ ├── ExpressionDataBinder.cs # ${} 表达式数据绑定 +│ ├── PageLayoutEngine.cs # 分页与排版 +│ ├── ITextPdfRenderer.cs # iText 7 PDF 渲染 +│ ├── ProcessorDataAdapter.cs # 处理器数据适配 +│ └── ReportIdGenerator.cs # 报告编号生成 +├── Fonts/ # 嵌入字体文件(需手动添加) +├── Templates/ # JSON 报告模板 +│ └── StandardReportTemplate.json +├── Resources/ # 多语言资源文件 +│ ├── Resources.resx +│ ├── Resources.zh-CN.resx +│ ├── Resources.zh-TW.resx +│ └── Resources.en-US.resx +├── Documents/ # 项目文档 +├── ReportEngineModule.cs # Prism 模块入口 +└── XP.ReportEngine.csproj +``` + +## 快速开始 + +### 1. 添加字体文件 + +将以下字体文件放入 `Fonts/` 目录(详见 `Documents/字体文件说明.md`): +- `NotoSansCJKsc-Regular.otf` — 中文 CJK 字体 +- `NotoSans-Regular.ttf` — 西文字体 + +### 2. 通过 DI 容器调用 + +模块通过 `ReportEngineModule` 自动注册到 Prism DI 容器,其他模块可直接注入 `IReportGenerator` 使用: + +```csharp +public class MyService +{ + private readonly IReportGenerator _reportGenerator; + + public MyService(IReportGenerator reportGenerator) + { + _reportGenerator = reportGenerator; + } + + public async Task GenerateReport() + { + var context = new ReportContext + { + Metadata = new ReportMetadata + { + ReportId = "RPT-20250511-001", + InspectionDate = DateTime.Now, + SampleName = "PCB-001", + OperatorName = "张三" + }, + Properties = new Dictionary + { + ["sampleName"] = "PCB-001" + } + }; + + var options = new ReportGenerationOptions + { + TemplatePath = "Templates/StandardReportTemplate.json", + OutputFilePath = @"D:\Reports\report.pdf", + Format = ReportOutputFormat.Pdf + }; + + var result = await _reportGenerator.GenerateAsync(context, options); + + if (result.IsSuccess) + { + // PDF 已保存到 OutputFilePath + } + else + { + // result.ErrorMessage 包含错误信息 + } + } +} +``` + +### 3. 使用数据适配器 + +将 XP.ImageProcessing 处理器输出转换为 ReportContext: + +```csharp +var adapter = container.Resolve(); + +var processorOutputs = new List +{ + new ProcessorOutput + { + ProcessorType = "BgaVoidRateProcessor", + OutputData = new Dictionary + { + ["BgaCount"] = 120, + ["VoidRate"] = 0.035, + ["Classification"] = "Pass" + } + } +}; + +var metadata = new ReportMetadata +{ + ReportId = "RPT-20250511-001", + InspectionDate = DateTime.Now, + SampleName = "PCB-001", + OperatorName = "张三" +}; + +ReportContext context = adapter.Adapt(processorOutputs, metadata); +``` + +## 核心接口 + +| 接口 | 实现类 | 职责 | +|------|--------|------| +| `IReportGenerator` | `PdfReportGenerator` | 协调管线各阶段,生成 PDF | +| `IReportGeneratorFactory` | `ReportGeneratorFactory` | 根据格式创建对应生成器 | +| `ITemplateEngine` | `JsonTemplateEngine` | JSON 模板加载、反序列化、验证 | +| `IDataBinder` | `ExpressionDataBinder` | `${}` 表达式解析与数据绑定 | +| `ILayoutEngine` | `PageLayoutEngine` | 分页、元素定位、表格跨页 | +| `IPdfRenderer` | `ITextPdfRenderer` | 使用 iText 7 渲染 PDF | +| `IReportDataAdapter` | `ProcessorDataAdapter` | OutputData → ReportContext 转换 | + +## 数据绑定表达式 + +模板中支持以下绑定语法: + +| 语法 | 说明 | 示例 | +|------|------|------| +| `${propertyName}` | 简单属性绑定 | `${sampleName}` | +| `${object.property}` | 嵌套属性路径 | `${metadata.reportId}` | +| `${list[index]}` | 列表索引访问 | `${bgaBalls[0].voidRate}` | +| `${functionName(param)}` | 格式化函数 | `${formatDate(inspectionDate)}` | +| `${loc:ResourceKey}` | 本地化键解析 | `${loc:Report_Title}` | + +内置格式化函数: +- `formatDate(value)` — 根据当前语言格式化日期 +- `formatNumber(value, decimals)` — 根据当前语言格式化数字 +- `formatPercent(value)` — 格式化百分比 + +## 多语言支持 + +支持三种语言:简体中文(zh-CN)、繁体中文(zh-TW)、英文(en-US)。 + +通过 `ILocalizationService` 自动解析 `${loc:Key}` 表达式为当前语言文本。模块在初始化时注册资源源到 Fallback Chain。 + +## 模板定义 + +标准模板位于 `Templates/StandardReportTemplate.json`,包含以下页面类型: +- `homepage` — 报告首页(元数据 + 摘要表格) +- `metricData` — 距离测量数据页 +- `bgaInspection` — BGA 焊球检测页(含数据表格) +- `voidInspection` — 空隙检测页(含数据表格) +- `viaFillInspection` — 通孔填锡检测页 + +模板 JSON 结构需包含三个必需顶层字段:`document`、`pages`、`styles`。 + +## 错误处理 + +模块采用结果对象模式(Result Pattern),所有公共方法返回 `ReportResult` 而非抛出异常: +- `ReportResult.Success(stream)` — 成功,包含 PDF MemoryStream +- `ReportResult.Failure(message, ex)` — 失败,包含错误信息和可选异常 + +非致命性问题(缺失属性、未定义样式、图像缺失)会记录警告日志并继续执行,不会中断报告生成。 + +## 构建 + +```bash +cd XplorePlane +dotnet build XP.ReportEngine/XP.ReportEngine.csproj +``` diff --git a/XP.ReportEngine/Documents/XP.ReportEngine 项目规划.md b/XP.ReportEngine/Documents/XP.ReportEngineDesign.md similarity index 100% rename from XP.ReportEngine/Documents/XP.ReportEngine 项目规划.md rename to XP.ReportEngine/Documents/XP.ReportEngineDesign.md diff --git a/XP.ReportEngine/Documents/XP.ReportEngine 模板定义.md b/XP.ReportEngine/Documents/XP.ReportEngineModelDefine.md similarity index 100% rename from XP.ReportEngine/Documents/XP.ReportEngine 模板定义.md rename to XP.ReportEngine/Documents/XP.ReportEngineModelDefine.md diff --git a/XP.ReportEngine/Fonts/NotoSans-Regular.ttf b/XP.ReportEngine/Fonts/NotoSans-Regular.ttf new file mode 100644 index 0000000..4bac02f Binary files /dev/null and b/XP.ReportEngine/Fonts/NotoSans-Regular.ttf differ diff --git a/XP.ReportEngine/Fonts/NotoSerifCJKtc-Regular.otf b/XP.ReportEngine/Fonts/NotoSerifCJKtc-Regular.otf new file mode 100644 index 0000000..a59de8d Binary files /dev/null and b/XP.ReportEngine/Fonts/NotoSerifCJKtc-Regular.otf differ diff --git a/XP.ReportEngine/Fonts/NotoSerifSC-Regular.otf b/XP.ReportEngine/Fonts/NotoSerifSC-Regular.otf new file mode 100644 index 0000000..be55fbd Binary files /dev/null and b/XP.ReportEngine/Fonts/NotoSerifSC-Regular.otf differ diff --git a/XP.ReportEngine/Fonts/README.md b/XP.ReportEngine/Fonts/README.md deleted file mode 100644 index 0c24001..0000000 --- a/XP.ReportEngine/Fonts/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# 字体文件目录 | Font Files Directory - -## 所需字体文件 | Required Font Files - -请将以下字体文件手动添加到此目录: - -1. **NotoSansCJKsc-Regular.otf** — 中文字体(CJK 字符支持)| Chinese font (CJK character support) -2. **NotoSans-Regular.ttf** — 西文字体 | Western font - -## 下载地址 | Download Links - -- Noto Sans CJK: https://github.com/googlefonts/noto-cjk/releases -- Noto Sans: https://fonts.google.com/noto/specimen/Noto+Sans - -## 配置说明 | Configuration Notes - -字体文件已在 .csproj 中配置为嵌入资源(EmbeddedResource),添加文件后无需额外配置。 -Font files are configured as EmbeddedResource in .csproj, no additional configuration needed after adding files. diff --git a/XP.ReportEngine/Services/ITextPdfRenderer.cs b/XP.ReportEngine/Services/ITextPdfRenderer.cs index 784d6f9..87a0ebd 100644 --- a/XP.ReportEngine/Services/ITextPdfRenderer.cs +++ b/XP.ReportEngine/Services/ITextPdfRenderer.cs @@ -59,7 +59,8 @@ namespace XP.ReportEngine.Services /// private const string EvenRowBackgroundColor = "#F5F5F5"; - private PdfFont _cjkFont; + private PdfFont _cjkScFont; + private PdfFont _cjkTcFont; private PdfFont _westernFont; public ITextPdfRenderer(ILoggerService logger, ILocalizationService localizationService) @@ -255,17 +256,31 @@ namespace XP.ReportEngine.Services /// private void InitializeFonts() { + // 加载简体中文字体 | Load Simplified Chinese font try { - _cjkFont = LoadEmbeddedFont("XP.ReportEngine.Fonts.NotoSansCJKsc-Regular.otf"); - _logger.Info("CJK 字体加载成功 | CJK font loaded successfully"); + _cjkScFont = LoadEmbeddedFont("XP.ReportEngine.Fonts.NotoSerifSC-Regular.otf"); + _logger.Info("简体中文字体加载成功 | Simplified Chinese font loaded successfully"); } catch (Exception ex) { - _logger.Warn("CJK 嵌入字体加载失败,将使用后备字体 | CJK embedded font load failed, will use fallback: {Message}", ex.Message); - _cjkFont = null; + _logger.Warn("简体中文嵌入字体加载失败,将使用后备字体 | SC embedded font load failed, will use fallback: {Message}", ex.Message); + _cjkScFont = null; } + // 加载繁体中文字体 | Load Traditional Chinese font + try + { + _cjkTcFont = LoadEmbeddedFont("XP.ReportEngine.Fonts.NotoSerifCJKtc-Regular.otf"); + _logger.Info("繁体中文字体加载成功 | Traditional Chinese font loaded successfully"); + } + catch (Exception ex) + { + _logger.Warn("繁体中文嵌入字体加载失败,将使用后备字体 | TC embedded font load failed, will use fallback: {Message}", ex.Message); + _cjkTcFont = null; + } + + // 加载西文字体 | Load Western font try { _westernFont = LoadEmbeddedFont("XP.ReportEngine.Fonts.NotoSans-Regular.ttf"); @@ -278,7 +293,7 @@ namespace XP.ReportEngine.Services } // 如果嵌入字体都不可用,使用 iText 内置 Helvetica | If embedded fonts unavailable, use built-in Helvetica - if (_cjkFont == null && _westernFont == null) + if (_cjkScFont == null && _cjkTcFont == null && _westernFont == null) { _logger.Warn("所有嵌入字体不可用,使用 Helvetica 后备字体 | All embedded fonts unavailable, using Helvetica fallback"); } @@ -309,7 +324,7 @@ namespace XP.ReportEngine.Services /// /// 根据当前语言获取合适的字体 | Get appropriate font based on current language - /// zh-CN、zh-TW → CJK 字体;en-US → 西文字体 + /// zh-CN → 简体中文字体;zh-TW → 繁体中文字体;en-US → 西文字体 /// /// PDF 字体 | PDF font private PdfFont GetFontForCurrentLanguage() @@ -320,8 +335,10 @@ namespace XP.ReportEngine.Services switch (language) { case SupportedLanguage.ZhCN: + selectedFont = _cjkScFont; + break; case SupportedLanguage.ZhTW: - selectedFont = _cjkFont; + selectedFont = _cjkTcFont ?? _cjkScFont; // 繁体优先,简体后备 | TC preferred, SC fallback break; case SupportedLanguage.EnUS: default: @@ -335,8 +352,9 @@ namespace XP.ReportEngine.Services return selectedFont; } - // 尝试使用另一种嵌入字体 | Try the other embedded font - if (_cjkFont != null) return _cjkFont; + // 尝试使用其他嵌入字体 | Try other embedded fonts + if (_cjkScFont != null) return _cjkScFont; + if (_cjkTcFont != null) return _cjkTcFont; if (_westernFont != null) return _westernFont; // 最终后备:使用 iText 内置 Helvetica | Final fallback: use built-in Helvetica