14 KiB
14 KiB
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模板结构:
{ "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 模板引擎实现
-
模板加载与解析:
public class TemplateEngine : ITemplateEngine { public ReportTemplate LoadTemplate(string templatePath) { var json = File.ReadAllText(templatePath); return JsonConvert.DeserializeObject<ReportTemplate>(json); } public List<PageElement> ParseTemplate(ReportTemplate template, ReportContext context) { var elements = new List<PageElement>(); 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 数据绑定实现
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 排版服务实现
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 图像转换服务
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. 生成步骤
- 加载模板: 从Templates目录加载JSON模板
- 解析上下文: 将MeasurementData和图片列表转换为ReportContext
- 数据绑定: 将上下文数据绑定到模板中的占位符
- 计算布局: 确定每个元素在PDF页面上的精确位置
- 生成PDF: 使用iTextSharp绘制内容到PDF文档
- 输出结果: 返回MemoryStream或保存为文件
2. 核心生成服务
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检测报告的专业需求,同时保持系统的扩展性和易用性。