diff --git a/XP.ReportEngine/Interfaces/IPdfRenderer.cs b/XP.ReportEngine/Interfaces/IPdfRenderer.cs index fa159e1..3679c0a 100644 --- a/XP.ReportEngine/Interfaces/IPdfRenderer.cs +++ b/XP.ReportEngine/Interfaces/IPdfRenderer.cs @@ -14,7 +14,8 @@ namespace XP.ReportEngine.Interfaces /// /// 排版后的页面列表 | Laid-out pages /// 生成选项 | Generation options + /// 绑定后的模板(用于页眉页脚配置)| Bound template (for header/footer config) /// PDF 内存流 | PDF memory stream - MemoryStream Render(List pages, ReportGenerationOptions options); + MemoryStream Render(List pages, ReportGenerationOptions options, ReportTemplate template = null); } } diff --git a/XP.ReportEngine/Models/LayoutPage.cs b/XP.ReportEngine/Models/LayoutPage.cs index 6658e97..9abcac2 100644 --- a/XP.ReportEngine/Models/LayoutPage.cs +++ b/XP.ReportEngine/Models/LayoutPage.cs @@ -8,6 +8,13 @@ namespace XP.ReportEngine.Models public class LayoutPage { public int PageNumber { get; set; } + + /// + /// 页面类型(来自模板定义:homepage / metricData / bgaInspection 等) + /// Page type from template definition + /// + public string PageType { get; set; } + public List Elements { get; set; } = new(); } diff --git a/XP.ReportEngine/Models/ReportTemplate.cs b/XP.ReportEngine/Models/ReportTemplate.cs index 56e2ba5..e2a2218 100644 --- a/XP.ReportEngine/Models/ReportTemplate.cs +++ b/XP.ReportEngine/Models/ReportTemplate.cs @@ -17,6 +17,16 @@ namespace XP.ReportEngine.Models public string PageSize { get; set; } = "A4"; public string Orientation { get; set; } = "Portrait"; public MarginSettings Margins { get; set; } = new(); + + /// + /// 页眉配置(仅内容页显示,首页不显示)| Header config (content pages only, not homepage) + /// + public HeaderFooterSettings Header { get; set; } + + /// + /// 页脚配置(仅内容页显示,首页不显示)| Footer config (content pages only, not homepage) + /// + public HeaderFooterSettings Footer { get; set; } } public class MarginSettings @@ -27,6 +37,52 @@ namespace XP.ReportEngine.Models public float Right { get; set; } = 20f; } + /// + /// 页眉/页脚配置 | Header/Footer settings + /// + public class HeaderFooterSettings + { + /// + /// 是否启用 | Whether enabled + /// + public bool Enabled { get; set; } = true; + + /// + /// 左侧文本行(支持 ${} 绑定表达式)| Left-side text lines with binding expressions + /// + public List Left { get; set; } = new(); + + /// + /// 右侧文本行(支持 ${} 绑定表达式)| Right-side text lines with binding expressions + /// + public List Right { get; set; } = new(); + + /// + /// 右侧图像 dataKey(如 logo)| Right-side image dataKey (e.g. logo) + /// + public string RightImageKey { get; set; } + + /// + /// 左侧图像 dataKey | Left-side image dataKey + /// + public string LeftImageKey { get; set; } + + /// + /// 字体大小 | Font size + /// + public float FontSize { get; set; } = 8f; + + /// + /// 字体颜色 | Font color + /// + public string Color { get; set; } = "#666666"; + + /// + /// 是否显示分隔线 | Whether to show separator line + /// + public bool ShowLine { get; set; } = true; + } + public class TemplatePage { /// diff --git a/XP.ReportEngine/Models/TemplateElement.cs b/XP.ReportEngine/Models/TemplateElement.cs index b807db5..9b3dd27 100644 --- a/XP.ReportEngine/Models/TemplateElement.cs +++ b/XP.ReportEngine/Models/TemplateElement.cs @@ -5,7 +5,7 @@ namespace XP.ReportEngine.Models public class TemplateElement { /// - /// 元素类型:text / image / table / divider / grid + /// 元素类型:text / image / table / divider / spacer / row / grid /// public string Type { get; set; } @@ -49,6 +49,31 @@ namespace XP.ReportEngine.Models /// public string Positioning { get; set; } = "absolute"; + /// + /// 子元素列表(用于 row 类型的水平布局容器) + /// Child elements (for row type horizontal layout container) + /// + public List Children { get; set; } + + /// + /// 水平对齐方式(用于 row 子元素):left / center / right + /// Horizontal alignment for row children + /// + public string Align { get; set; } + + /// + /// 列宽比例数组(用于 row 类型,如 [7, 3] 表示 7:3 比例) + /// Column width ratios for row type (e.g. [7, 3] means 7:3 ratio) + /// + public float[] Widths { get; set; } + + /// + /// 条件颜色规则(用于 text 元素,根据内容中包含的关键词匹配颜色) + /// Conditional color rules for text elements (match keywords in content to color) + /// 格式:{ "Pass": "#008000", "Fail": "#FF0000" } + /// + public Dictionary ColorRules { get; set; } + /// /// 表格数据行(数据绑定阶段填充,用于排版计算和渲染) /// Table data rows (populated during data binding phase, used for layout calculation and rendering) @@ -69,6 +94,13 @@ namespace XP.ReportEngine.Models public string Field { get; set; } public float Width { get; set; } public string Align { get; set; } = "left"; + + /// + /// 条件颜色规则(根据单元格值匹配颜色) + /// Conditional color rules (match cell value to color) + /// 格式:{ "Pass": "#008000", "Fail": "#FF0000" } + /// + public Dictionary ColorRules { get; set; } } public class StyleDefinition @@ -80,5 +112,26 @@ namespace XP.ReportEngine.Models public string Color { get; set; } = "#000000"; public string Align { get; set; } = "left"; public string BackgroundColor { get; set; } + + /// + /// 上边距(mm)| Top margin in mm + /// + public float MarginTop { get; set; } + + /// + /// 下边距(mm)| Bottom margin in mm + /// + public float MarginBottom { get; set; } + + /// + /// 左缩进(mm)| Left indent/padding in mm + /// + public float PaddingLeft { get; set; } + + /// + /// 行高倍数(1.0 = 单倍行距,1.5 = 1.5倍行距,0 表示使用默认) + /// Line height multiplier (1.0 = single spacing, 1.5 = 1.5x spacing, 0 = use default) + /// + public float LineHeight { get; set; } } } diff --git a/XP.ReportEngine/Services/PageLayoutEngine.cs b/XP.ReportEngine/Services/PageLayoutEngine.cs index 5e2257d..5bf2d19 100644 --- a/XP.ReportEngine/Services/PageLayoutEngine.cs +++ b/XP.ReportEngine/Services/PageLayoutEngine.cs @@ -88,6 +88,7 @@ namespace XP.ReportEngine.Services var currentPage = new LayoutPage { PageNumber = currentPageNumber, + PageType = templatePage.Type, Elements = new List() }; pages.Add(currentPage); @@ -107,13 +108,28 @@ namespace XP.ReportEngine.Services var elementHeight = CalculateElementHeight(element); var elementWidth = CalculateElementWidth(element, availableWidth); + // 强制分页元素 | Forced page break element + if (string.Equals(element.Type, "pagebreak", StringComparison.OrdinalIgnoreCase)) + { + currentPageNumber++; + currentPage = new LayoutPage + { + PageNumber = currentPageNumber, + PageType = templatePage.Type, + Elements = new List() + }; + pages.Add(currentPage); + currentY = margins.Top; + continue; + } + // 检查是否需要分页 | Check if pagination is needed if (element.Type == "table" && element.Columns != null) { // 表格跨页拆分逻辑 | Table page-split logic currentY = ProcessTableWithPageSplit( element, template, margins, availableHeight, availableWidth, - currentY, pages, ref currentPage, ref currentPageNumber); + currentY, pages, ref currentPage, ref currentPageNumber, templatePage.Type); } else { @@ -125,6 +141,7 @@ namespace XP.ReportEngine.Services currentPage = new LayoutPage { PageNumber = currentPageNumber, + PageType = templatePage.Type, Elements = new List() }; pages.Add(currentPage); @@ -240,7 +257,7 @@ namespace XP.ReportEngine.Services TemplateElement element, ReportTemplate template, MarginSettings margins, float availableHeight, float availableWidth, float currentY, List pages, - ref LayoutPage currentPage, ref int currentPageNumber) + ref LayoutPage currentPage, ref int currentPageNumber, string pageType) { var resolvedStyle = _templateEngine.ResolveStyle(template, element.Style); var tableWidth = element.Size != null && element.Size.Length > 0 ? element.Size[0] : availableWidth; @@ -262,6 +279,7 @@ namespace XP.ReportEngine.Services currentPage = new LayoutPage { PageNumber = currentPageNumber, + PageType = pageType, Elements = new List() }; pages.Add(currentPage); @@ -324,6 +342,7 @@ namespace XP.ReportEngine.Services currentPage = new LayoutPage { PageNumber = currentPageNumber, + PageType = pageType, Elements = new List() }; pages.Add(currentPage); @@ -359,6 +378,7 @@ namespace XP.ReportEngine.Services currentPage = new LayoutPage { PageNumber = currentPageNumber, + PageType = pageType, Elements = new List() }; pages.Add(currentPage); @@ -423,6 +443,9 @@ namespace XP.ReportEngine.Services { "text" => DefaultTextHeight, "divider" => DefaultDividerHeight, + "spacer" => element.Size is { Length: >= 2 } ? element.Size[1] : DefaultTextHeight, + "row" => CalculateRowHeight(element), + "pagebreak" => 0f, "image" => DefaultTextHeight, "table" => CalculateTableHeight(element), _ => DefaultTextHeight @@ -442,6 +465,36 @@ namespace XP.ReportEngine.Services return DefaultRowHeight + (dataRowCount * DefaultRowHeight); } + /// + /// 计算 Row 容器高度 | Calculate row container height + /// 取子元素中最大高度,如果有 Size 定义则优先使用 + /// Uses max child height, or Size definition if available + /// + private float CalculateRowHeight(TemplateElement element) + { + // 如果 row 本身有 Size[1] 定义,直接使用 | If row has Size[1], use it directly + if (element.Size != null && element.Size.Length > 1 && element.Size[1] > 0) + { + return element.Size[1]; + } + + // 否则取子元素中最大高度 | Otherwise use max child height + if (element.Children == null || element.Children.Count == 0) + return DefaultTextHeight; + + float maxHeight = 0; + foreach (var child in element.Children) + { + float childHeight = DefaultTextHeight; + if (child.Size != null && child.Size.Length > 1 && child.Size[1] > 0) + { + childHeight = child.Size[1]; + } + if (childHeight > maxHeight) maxHeight = childHeight; + } + return maxHeight > 0 ? maxHeight : DefaultTextHeight; + } + /// /// 计算元素宽度 | Calculate element width /// diff --git a/XP.ReportEngine/Services/PdfReportGenerator.cs b/XP.ReportEngine/Services/PdfReportGenerator.cs index 9eba04f..81c31ce 100644 --- a/XP.ReportEngine/Services/PdfReportGenerator.cs +++ b/XP.ReportEngine/Services/PdfReportGenerator.cs @@ -87,7 +87,7 @@ namespace XP.ReportEngine.Services // 阶段 4:PDF 渲染 | Phase 4: PDF rendering _logger.Info("阶段 4:PDF 渲染 | Phase 4: PDF rendering"); - var stream = _pdfRenderer.Render(pages, options); + var stream = _pdfRenderer.Render(pages, options, boundTemplate); _logger.Info("阶段 4 完成:PDF 渲染成功 | Phase 4 completed: PDF rendering successful"); // 阶段 5:保存文件(可选)| Phase 5: Save file (optional)