# XP.ReportEngine 项目规划 ## 一、项目结构优化 **1. 完整目录结构** ``` XP.ReportEngine/ ├── Interfaces/ │ ├── IReportGenerator.cs // 报告生成器核心接口 │ ├── ITemplateEngine.cs // 模板引擎接口 │ └── IDataBinder.cs // 数据绑定接口 ├── Models/ │ ├── ReportContext.cs // 报告上下文(包含测量数据、图片列表) │ ├── ReportTemplate.cs // 报告模板定义 │ ├── TemplateElement.cs // 模板元素定义 │ └── LayoutSettings.cs // 布局配置参数 ├── Services/ │ ├── MeasurementReportBuilder.cs // 测量报告构建器 │ ├── ImageLayoutService.cs // 图片排版服务(计算坐标、缩放) │ ├── TemplateEngine.cs // 模板引擎实现 │ ├── DataBinder.cs // 数据绑定实现 │ └── PdfGenerationService.cs // PDF生成核心服务 ├── Templates/ │ └── StandardReportTemplate.json // 标准报告模板配置 └── Extensions/ └── ImageExtensions.cs // 图像处理扩展方法 ``` ## 二、核心模块设计 ### 1. 报告模板引擎 **1.1 模板定义规范** - **JSON模板结构**: ```json { "document": { "pageSize": "A4", "margins": { "top": 20, "bottom": 20, "left": 20, "right": 20 } }, "pages": [ { "type": "title", "elements": [ { "type": "text", "content": "工业CT检测报告", "style": "title", "position": [10, 10] }, { "type": "text", "content": "报告日期: ${reportDate}", "style": "subtitle", "position": [10, 30] } ] }, { "type": "measurement", "elements": [ { "type": "table", "dataKey": "measurements", "position": [10, 10], "size": [190, 100] }, { "type": "image", "dataKey": "overviewImage", "position": [10, 120], "size": [190, 100] } ] }, { "type": "defectDetail", "elements": [ { "type": "image", "dataKey": "defectImage", "position": [10, 10], "size": [90, 90] }, { "type": "text", "content": "缺陷类型: ${defectType}", "style": "normal", "position": [10, 100] } ] } ], "styles": { "title": { "font": "Arial", "size": 24, "bold": true, "color": "#000000" }, "subtitle": { "font": "Arial", "size": 16, "italic": true, "color": "#666666" }, "normal": { "font": "Arial", "size": 12, "color": "#000000" } } } ``` **1.2 模板引擎实现** - **模板加载与解析**: ```csharp public class TemplateEngine : ITemplateEngine { public ReportTemplate LoadTemplate(string templatePath) { var json = File.ReadAllText(templatePath); return JsonConvert.DeserializeObject(json); } public List ParseTemplate(ReportTemplate template, ReportContext context) { var elements = new List(); foreach (var page in template.Pages) { foreach (var element in page.Elements) { // 处理数据绑定 var boundElement = BindData(element, context); elements.Add(boundElement); } } return elements; } private PageElement BindData(PageElement element, ReportContext context) { // 实现数据绑定逻辑 if (element.Type == "text" && element.Content.Contains("${")) { element.Content = DataBinder.Bind(element.Content, context); } return element; } } ``` ### 2. 数据绑定系统 **2.1 数据绑定机制** - **支持的绑定语法**: - `${propertyName}` - 基本属性绑定 - `${object.property}` - 对象属性绑定 - `${list[index]}` - 列表索引访问 - `${function(param)}` - 函数调用(如日期格式化) **2.2 数据绑定实现** ```csharp public class DataBinder : IDataBinder { public string Bind(string template, ReportContext context) { // 使用正则表达式匹配绑定表达式 var pattern = @"\$\{([^\}]+)\}"; return Regex.Replace(template, pattern, match => { var expression = match.Groups[1].Value; return EvaluateExpression(expression, context); }); } private string EvaluateExpression(string expression, ReportContext context) { // 解析表达式并获取值 if (expression.Contains(".")) { // 处理对象属性 var parts = expression.Split('.'); var obj = context.GetType().GetProperty(parts[0])?.GetValue(context); return obj?.GetType().GetProperty(parts[1])?.GetValue(obj)?.ToString() ?? string.Empty; } else if (expression.Contains("[")) { // 处理列表索引 var index = int.Parse(expression.Split('[')[1].TrimEnd(']')); return context.Images.Count > index ? "Image" : string.Empty; } else { // 处理基本属性 return context.GetType().GetProperty(expression)?.GetValue(context)?.ToString() ?? string.Empty; } } } ``` ### 3. 自动排版功能 **3.1 布局引擎设计** - **核心功能**: - **页面分隔**: 当内容超出一页时自动创建新页 - **元素定位**: 根据模板定义计算元素在页面上的精确位置 - **尺寸计算**: 自动调整元素大小以适应内容 - **响应式布局**: 根据页面尺寸动态调整元素位置 **3.2 排版服务实现** ```csharp public class ImageLayoutService { public LayoutResult CalculateLayout(ReportTemplate template, ReportContext context) { var result = new LayoutResult(); var currentPage = 0; var currentY = template.Document.Margins.Top; foreach (var page in template.Pages) { foreach (var element in page.Elements) { // 计算元素位置 var position = new Point( template.Document.Margins.Left + element.Position[0], currentY + element.Position[1] ); // 计算元素尺寸 var size = new Size(element.Size[0], element.Size[1]); // 检查是否需要换页 if (position.Y + size.Height > PageSize.A4.Height - template.Document.Margins.Bottom) { currentPage++; currentY = template.Document.Margins.Top; position = new Point( template.Document.Margins.Left + element.Position[0], currentY + element.Position[1] ); } // 添加到布局结果 result.Elements.Add(new LayoutElement { Page = currentPage, Element = element, Position = position, Size = size }); // 更新当前Y位置 currentY = position.Y + size.Height; } } return result; } } ``` ### 4. 图表嵌入实现 **4.1 WPF图表转PDF图像** - **实现方案**: - 使用`RenderTargetBitmap`将WPF控件渲染为位图 - 支持高质量图像输出(300 DPI) - 提供图像压缩选项,平衡质量与文件大小 **4.2 图像转换服务** ```csharp public static class ImageExtensions { public static byte[] ToPdfImage(this ImageSource image, double width, double height) { var renderTarget = new RenderTargetBitmap( (int)width, (int)height, 96, 96, PixelFormats.Pbgra32); renderTarget.Render(image); using (var stream = new MemoryStream()) { var encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(renderTarget)); encoder.Save(stream); return stream.ToArray(); } } public static byte[] ToPdfImage(this FrameworkElement element, double width, double height) { var renderTarget = new RenderTargetBitmap( (int)width, (int)height, 96, 96, PixelFormats.Pbgra32); renderTarget.Render(element); using (var stream = new MemoryStream()) { var encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(renderTarget)); encoder.Save(stream); return stream.ToArray(); } } } ``` ## 三、PDF生成核心流程 ### 1. 生成步骤 1. **加载模板**: 从Templates目录加载JSON模板 2. **解析上下文**: 将MeasurementData和图片列表转换为ReportContext 3. **数据绑定**: 将上下文数据绑定到模板中的占位符 4. **计算布局**: 确定每个元素在PDF页面上的精确位置 5. **生成PDF**: 使用iTextSharp绘制内容到PDF文档 6. **输出结果**: 返回MemoryStream或保存为文件 ### 2. 核心生成服务 ```csharp public class PdfGenerationService { public MemoryStream GeneratePdf(ReportContext context, string templatePath) { // 1. 加载模板 var templateEngine = new TemplateEngine(); var template = templateEngine.LoadTemplate(templatePath); // 2. 解析模板元素 var elements = templateEngine.ParseTemplate(template, context); // 3. 计算布局 var layoutService = new ImageLayoutService(); var layout = layoutService.CalculateLayout(template, context); // 4. 创建PDF文档 var document = new Document(PageSize.A4, template.Document.Margins.Left, template.Document.Margins.Right, template.Document.Margins.Top, template.Document.Margins.Bottom); var stream = new MemoryStream(); var writer = PdfWriter.GetInstance(document, stream); document.Open(); // 5. 绘制内容 DrawContent(document, layout, context); // 6. 关闭文档 document.Close(); return stream; } private void DrawContent(Document document, LayoutResult layout, ReportContext context) { // 实现内容绘制逻辑 foreach (var element in layout.Elements) { switch (element.Element.Type) { case "text": DrawText(document, element); break; case "image": DrawImage(document, element, context); break; case "table": DrawTable(document, element, context); break; } } } private void DrawText(Document document, LayoutElement element) { var paragraph = new Paragraph(element.Element.Content); paragraph.SetLocation(element.Position.X, document.PageSize.Height - element.Position.Y); document.Add(paragraph); } private void DrawImage(Document document, LayoutElement element, ReportContext context) { // 获取图像数据 var imageData = context.Images.FirstOrDefault(); if (imageData == null) return; // 创建iTextSharp图像对象 var image = Image.GetInstance(imageData); image.SetAbsolutePosition(element.Position.X, document.PageSize.Height - element.Position.Y - element.Size.Height); image.ScaleToFit(element.Size.Width, element.Size.Height); document.Add(image); } private void DrawTable(Document document, LayoutElement element, ReportContext context) { // 创建表格 var table = new PdfPTable(3); // 假设3列 table.WidthPercentage = 100; // 添加表头 table.AddCell(new PdfPCell(new Phrase("参数", new Font(Font.FontFamily.HELVETICA, 12, Font.BOLD)))); table.AddCell(new PdfPCell(new Phrase("测量值", new Font(Font.FontFamily.HELVETICA, 12, Font.BOLD)))); table.AddCell(new PdfPCell(new Phrase("标准值", new Font(Font.FontFamily.HELVETICA, 12, Font.BOLD)))); // 添加数据行 foreach (var measurement in context.Measurements) { table.AddCell(new PdfPCell(new Phrase(measurement.Parameter))); table.AddCell(new PdfPCell(new Phrase(measurement.Value.ToString()))); table.AddCell(new PdfPCell(new Phrase(measurement.StandardValue.ToString()))); } document.Add(table); } } ``` ## 四、最佳实践与建议 ### 1. 性能优化 - **图像处理**: 对大尺寸图像进行**压缩和缩放**,避免PDF文件过大 - **内存管理**: 使用`using`语句确保资源及时释放 - **批量处理**: 对大量数据采用**分批处理**策略,避免UI冻结 ### 2. 错误处理 - 实现**完善的异常捕获**机制,特别是图像转换和PDF生成环节 - 提供**友好的错误提示**,便于问题排查 - 添加**日志记录**功能,跟踪报告生成过程 ### 3. 样式管理 - 创建**样式库**,统一字体、颜色、间距等 - 支持**主题切换**,适应不同客户的品牌要求 - 确保**打印友好**,考虑黑白打印时的可读性 ### 4. 扩展性考虑 - **插件架构**: 设计可扩展的插件系统,便于添加新功能 - **多格式输出**: 基于相同架构,扩展支持Word、Excel等格式输出 - **云服务集成**: 考虑与云存储集成,自动上传生成的报告 通过以上设计,XP.ReportEngine将成为一个**灵活、高效、可维护**的PDF报告生成系统,满足工业CT检测报告的专业需求,同时保持系统的扩展性和易用性。