Aspose.word实现表格每页固定表头、最后一行填满整个页面
这篇文章参考自poi-tl、aspose实现word中表在每页携带表头表尾_poi-tl 表格超出纸张大小第二页任然需要表头-CSDN博客
先说一下目前所做的表格情况:检测项目是动态的,但是最后一行要求在最后填充整个页面

以前的我做法是:项目行的行高是固定的27.5,一页面大概就是25个项目行,最后备注行算还差多少行合并就行。
但是现在要改成
(1)项目行是自适应的,不能固定了,
(2)项目分页,每页要显示表头
所以我就只能判断整个页面表格能显示的最大高度,然后算项目行使用了多少高度,再进行填充
先看一下全部代码
/// <summary>
/// 导出
/// </summary>
public static string SetReportExportTest(int type, M.agAgricul agModel, M.agReportTask taskModel, M.agReport reportModel, List<usPersonInfo>? personList, string folderPath, int? exportType)
{Aspose.Words.Document doc = new Aspose.Words.Document("xxxxxxxxxxxx");var builder = new DocumentBuilder(doc);int index = 0;int cellFixCout = 6;//一行6列int nowIndex = 0;if (taskModel.agReportDetail_FKs.Count > 0){taskModel.agReportDetail_FKs.ForEach(j =>{int remarkRow = 0;//备注所在行nowIndex++;//插入分页符//builder.InsertBreak(BreakType.SectionBreakNewPage);//会把页码截断builder.InsertBreak(BreakType.PageBreak);//插入纵页builder.PageSetup.Orientation = Aspose.Words.Orientation.Portrait;#region 格式(标题)Aspose.Words.Font font = builder.Font;font.Size = 14;font.Bold = false;font.Color = System.Drawing.Color.Black;font.Name = "宋体";ParagraphFormat paragraphFormat = builder.ParagraphFormat;paragraphFormat.Alignment = ParagraphAlignment.Center;paragraphFormat.KeepTogether = true;#endregionbuilder.Writeln("人类三体文明牛马质量检测处\r\n检验检测报告\r\n");#region 格式(编号、名称)builder.Font.Size = 10;builder.Font.Name = "宋体";builder.Font.Bold = false;#endregionvar table = builder.StartTable();builder.ParagraphFormat.ClearFormatting();builder.InsertCell();// 编号左对齐builder.CellFormat.Width = 285; // 设置单元格宽度builder.ParagraphFormat.Alignment = ParagraphAlignment.Left;builder.Write($"编号:NM0000123");// 名称右对齐builder.InsertCell();builder.CellFormat.Width = 285;builder.ParagraphFormat.Alignment = ParagraphAlignment.Right;builder.Write($"名称:牛马");builder.EndRow();builder.EndTable();// 隐藏所有边框table.SetBorders(Aspose.Words.LineStyle.None, 0, Color.Black);// 开始构建表Aspose.Words.Tables.Table tableNow = builder.StartTable();#region 格式(表头)//设置高度并定义标题行的高度规则。builder.RowFormat.Height = 55;builder.RowFormat.HeightRule = HeightRule.AtLeast;builder.ParagraphFormat.ClearFormatting();builder.ParagraphFormat.Alignment = ParagraphAlignment.Center;int projectFontsize = 10;builder.Font.Size = 11;builder.Font.Name = "宋体";builder.Font.Bold = false;builder.CellFormat.VerticalAlignment = CellVerticalAlignment.Center;//我们不需要指定此单元格的宽度,因为它是从前一个单元格继承的。#endregion//builder.RowFormat.HeadingFormat = true;builder.InsertCell();builder.Write("检测项目");builder.InsertCell();builder.Write("单位");builder.InsertCell();builder.Write("检测结果");builder.InsertCell();builder.Write("检测依据");builder.InsertCell();builder.Write("标准要求");builder.InsertCell();builder.Write("单项判定");builder.InsertCell();builder.Write("判断依据");//调用以下方法结束行并开始新行。builder.EndRow();remarkRow++;if (projects.Count > 0){projects.ForEach(project =>{#region 格式(检测项目)//设置高度并定义标题行的高度规则。builder.RowFormat.Height = 27;builder.RowFormat.HeightRule = HeightRule.AtLeast;builder.ParagraphFormat.Alignment = ParagraphAlignment.Center;builder.Font.Size = projectFontsize;builder.Font.Name = "宋体";builder.Font.Bold = false;builder.CellFormat.VerticalAlignment = CellVerticalAlignment.Center;//我们不需要指定此单元格的宽度,因为它是从前一个单元格继承的。#endregionbuilder.InsertCell();builder.Write(project);builder.InsertCell();builder.Write("");builder.InsertCell();builder.Write("");builder.InsertCell();builder.Write("");builder.InsertCell();builder.Write("— —");builder.InsertCell();builder.Write("— —");builder.InsertCell();builder.Write("— —");builder.EndRow();remarkRow++;//备注行后移});}//int allPageRows = 25;//去掉表头(5行)//int finalRow = projects.Count + jcCount + 5 + 1;//整页使用了多少行(按25行的来算)5行:题头+检测项目(3行) 1行:备注所占行//int n = finalRow / allPageRows > 0 ? (finalRow % allPageRows > 0 ? ((finalRow / allPageRows) + 1) : (finalRow / allPageRows)) : 1;//int m = allPageRows * n - finalRow;//还有多少行填满一页#region 格式化(备注行)builder.ParagraphFormat.Alignment = ParagraphAlignment.Left;builder.CellFormat.VerticalAlignment = CellVerticalAlignment.Top;#endregionbuilder.InsertCell();builder.Write("以下空白");for (int ii = 1; ii <= cellFixCout; ii++){builder.InsertCell();}builder.EndRow();#region 设置行高//// 获取页面总高度//double totalPageHeight = newSection.PageSetup.PageHeight;//// 获取上下边距//double topMargin = newSection.PageSetup.TopMargin;//double bottomMargin = newSection.PageSetup.BottomMargin;//// 计算可用高度(实际内容区域)//int usableHeight = (int)(totalPageHeight - topMargin - bottomMargin);////int usableHeight = 675;//(int)(totalPageHeight - topMargin - bottomMargin);////double pageHeight = 540;//一页高675,项目20行共540,表头行55,题头80 110//double usedHeight = tableNow.Rows.Select(h => (Aspose.Words.Tables.Row)h).Sum(h => h.RowFormat.Height) + 80;//int nn = (int)(usedHeight / usableHeight > 0 ? (usedHeight % usableHeight > 0 ? ((usedHeight / usableHeight) + 1) : (usedHeight / usableHeight)) : 1);//double mm = usableHeight * nn - usedHeight;//还有多少高填满一页//var dsgds = ((Aspose.Words.Tables.Row)tableNow.Rows[remarkRow - 1]).RowFormat.Height;//tableNow.Rows[remarkRow].RowFormat.Height = mm;#endregion#region 宽度赋值//设置表格的首选宽度tableNow.PreferredWidth = PreferredWidth.FromPoints(570); // 首选宽度,根据总宽度调整,没有设置就会导致只会按下面比例生成不可控的宽度foreach (Aspose.Words.Tables.Row row in tableNow.Rows){row.Cells[0].CellFormat.Width = 80;row.Cells[1].CellFormat.Width = 60;row.Cells[2].CellFormat.Width = 70;row.Cells[3].CellFormat.Width = 105;row.Cells[4].CellFormat.Width = 70;row.Cells[5].CellFormat.Width = 70;row.Cells[6].CellFormat.Width = 115;}#endregion#region 合并//备注行合并for (int c = 0; c <= cellFixCout; c++){if (c == 0){tableNow.Rows[remarkRow].Cells[c].CellFormat.HorizontalMerge = Aspose.Words.Tables.CellMerge.First;}else{tableNow.Rows[remarkRow].Cells[c].CellFormat.HorizontalMerge = Aspose.Words.Tables.CellMerge.Previous;}}#endregion//表示我们已经完成了构建表的信号。builder.EndTable();#region 对动态表格进行题头、最后一行满页填充处理doc.FirstSection.Body.AppendChild(tableNow);doc.UpdatePageLayout();int inddd = doc.GetChildNodes(NodeType.Table, true).IndexOf(tableNow);Aspose.Words.Tables.Table tableNow22 = (Aspose.Words.Tables.Table)doc.GetChildNodes(NodeType.Table, true)[nowIndex * 2 + 1];DealTableCopyRow(doc, nowIndex * 2 + 1);#endregion});}//导出类型(1pdf 2word)string savePath = "";if (exportType == 1){savePath = "报告.pdf";if (System.IO.File.Exists(savePath))System.IO.File.Delete(savePath);doc.Save(savePath, SaveFormat.Pdf);}if (exportType == 2){savePath = "报告.doc";if (System.IO.File.Exists(savePath))System.IO.File.Delete(savePath);doc.Save(savePath, SaveFormat.Doc);}return savePath;
}
效果如下:
一页的效果

多页的效果:

之前的做法是固定项目行高来算的
int allPageRows = 25;//去掉表头(5行)int finalRow = projects.Count + jcCount + 5 + 1;//整页使用了多少行(按25行的来算)5行:题头+检测项目(3行) 1行:备注所占行int n = finalRow / allPageRows > 0 ? (finalRow % allPageRows > 0 ? ((finalRow / allPageRows) + 1) : (finalRow / allPageRows)) : 1;int m = allPageRows * n - finalRow;//还有多少行填满一页//备注行之后的空白行for (int ii = 0; ii < m; ii++){builder.InsertCell();builder.InsertCell();builder.InsertCell();builder.InsertCell();builder.InsertCell();builder.InsertCell();builder.InsertCell();}//合并备注行和空白行达到填充整页的目的for (int r = remarkRow; r <= (remarkRow + m); r++){for (int c = 0; c <= cellFixCout; c++){if (r == remarkRow)//备注第一行{if (c == 0){tableNow.Rows[r].Cells[c].CellFormat.VerticalMerge = Aspose.Words.Tables.CellMerge.First;tableNow.Rows[r].Cells[c].CellFormat.HorizontalMerge = Aspose.Words.Tables.CellMerge.First;}else{tableNow.Rows[r].Cells[c].CellFormat.VerticalMerge = Aspose.Words.Tables.CellMerge.First;tableNow.Rows[r].Cells[c].CellFormat.HorizontalMerge = Aspose.Words.Tables.CellMerge.Previous;}}else{if (c == 0)//每行第一个单元格{tableNow.Rows[r].Cells[c].CellFormat.VerticalMerge = Aspose.Words.Tables.CellMerge.Previous;tableNow.Rows[r].Cells[c].CellFormat.HorizontalMerge = Aspose.Words.Tables.CellMerge.First;}else{tableNow.Rows[r].Cells[c].CellFormat.VerticalMerge = Aspose.Words.Tables.CellMerge.Previous;tableNow.Rows[r].Cells[c].CellFormat.HorizontalMerge = Aspose.Words.Tables.CellMerge.Previous;}}}}
想按照每行高度来算,然后发现在构建表中获取的行高都是预设的高度,不是实际高度,即需要获取渲染后的行高,由于我使用预设的行高27,直接使用RowFormat.Height来获取高度,只会获取到预设的27
#region 格式(检测项目)
//设置高度并定义标题行的高度规则。
builder.RowFormat.Height = 27;
builder.RowFormat.HeightRule = HeightRule.AtLeast;
builder.ParagraphFormat.Alignment = ParagraphAlignment.Center;builder.Font.Size = projectFontsize;
builder.Font.Name = "宋体";
builder.Font.Bold = false;builder.CellFormat.VerticalAlignment = CellVerticalAlignment.Center;
//我们不需要指定此单元格的宽度,因为它是从前一个单元格继承的。
#endregionbuilder.InsertCell();
builder.Write(project);
展示一下开始尝试的错误代码
#region 设置行高// 获取页面总高度double totalPageHeight = newSection.PageSetup.PageHeight;// 获取上下边距double topMargin = newSection.PageSetup.TopMargin;double bottomMargin = newSection.PageSetup.BottomMargin;// 计算可用高度(实际内容区域)int usableHeight = (int)(totalPageHeight - topMargin - bottomMargin);//int usableHeight = 675;//(int)(totalPageHeight - topMargin - bottomMargin);//调试出的大概数值就是:一页高675,项目20行共540,表头行55,题头80或110//其实这里面是错误的,会随项目数的不同,题头的数值一直在变动,时而80,时而100double usedHeight = tableNow.Rows.Select(h => (Aspose.Words.Tables.Row)h).Sum(h => h.RowFormat.Height) + 80;int nn = (int)(usedHeight / usableHeight > 0 ? (usedHeight % usableHeight > 0 ? ((usedHeight / usableHeight) + 1) : (usedHeight / usableHeight)) : 1);double mm = usableHeight * nn - usedHeight;//还有多少高填满一页//设置备注行高度tableNow.Rows[remarkRow].RowFormat.Height = mm;#endregion
由于获取不到真实的项目行所占据的高度,导致导出的结果一直不是很理想
怎么获取构建表格行的真实高度?
其实这里所要获取的是基于实际渲染结果的精确行高
使用的主要代码如下
doc.UpdatePageLayout(); // 必须调用,否则布局信息可能不准确
//建立文档节点与布局实体之间的映射关系
LayoutCollector collector = new LayoutCollector(doc);//用于遍历和访问文档的布局实体(如页、行、单元格等)LayoutEnumerator enumerator = new LayoutEnumerator(doc);/* 定位到目标元素* table.Rows[0].Cells[0].FirstParagraph: 获取表格第一行第一个单元格的第一个段落* collector.GetEntity(): 通过收集器找到该段落对应的布局实体* enumerator.Current =: 将枚举器设置到该布局实体*/enumerator.Current = collector.GetEntity(table.Rows[0].Cells[0].FirstParagraph);//向上遍历直至找到ROW实体while (enumerator.Type != LayoutEntityType.Row){enumerator.MoveParent();
}
double Y = enumerator.Rectangle.Y;//以双精度返回框架矩形左上角的Y坐标。
double height = enumerator.Rectangle.Height;//以双精度返回框架矩形的高度。
这里解释一下
enumerator.Current = collector.GetEntity(table.Rows[0].Cells[0].FirstParagraph);这里定位当前枚举了,此时布局层级可能是 Paragraph (段落)↳ Line (行)↳ Span (文本范围)↳ ...
所以需要向上遍历找到行,遍历过程可能是
开始: Paragraph (段落) 第一次 MoveParent(): Line (文本行) 第二次 MoveParent(): Cell (单元格) 第三次 MoveParent(): Row (表格行) ← 找到目标!
在构建完表之后调用处理方法(一定要builder.EndTable()之后)
#region 对动态表格进行题头、最后一行满页填充处理
doc.FirstSection.Body.AppendChild(tableNow);
doc.UpdatePageLayout();int inddd = doc.GetChildNodes(NodeType.Table, true).IndexOf(tableNow);
Aspose.Words.Tables.Table tableNow22 = (Aspose.Words.Tables.Table)doc.GetChildNodes(NodeType.Table, true)[nowIndex * 2 + 1];DealTableCopyRow(doc, nowIndex * 2 + 1);
#endregion
具体的方法如下
/// <summary>/// 页面复制表头、填充满页处理/// </summary>/// <param name="tableN">文档中的第几个表格</param>public static void DealTableCopyRow(Document doc ,int tableN){//Document doc = new Document("testEndAddOutOnePage.docx");Aspose.Words.Tables.Table table = doc.GetChildNodes(NodeType.Table, true)[tableN] as Aspose.Words.Tables.Table; double tableHeadHeight = GetTableHeadHeight(doc, 1, tableN);//表头//double tableTailHeight = GetTableTailHeight(doc, 4, tableN);//表尾double tableHeightMax = GetTableHeightMax(doc);//word中table适配的最高高度if (tableHeightMax == 0.0){//需要处理的word文件不足一页 直接返回原文件return;}double tableCommonRowHeight = GetTableCommonRowHeight(doc, 1, 0, tableN);//获取自匹配插入数据的大众行高int commonRow = 0;//获取自匹配插入数据的大众行的代表行索引int numRows = 0;//最后一页补充条数LayoutCollector collector = new LayoutCollector(doc);//获取实际行高int pages = 1;//标记是不是第一页double sumHeight = 0;//手动记录总行高int count = table.Rows.Count;//总行数int tableHeadIndex = 0;//表头行索引double ttHeadRowHeight = 95;//题头高度,有方法返回,但是我是调试出了一个具体的值,就直接用了//循环跳过表头一行for (int row = 1; row < count; row++){string text = table.Rows[row].Cells[0].GetText();double height = FindRowHeight(collector, row, table, doc);//以双精度返回框架矩形的实际高度//第一页处理if (pages == 1){//跳过表头一行//寻找第一个符合标准行的行,作为后续克隆的行if (height == tableCommonRowHeight && commonRow == 0){commonRow = row; //此时的row还未row++ 就是具体行的索引}sumHeight = sumHeight + height;//该页已经满了如果加上次行后表尾位置不够 判断为不能插入到此页 调整上 一行 样式进行适配//if (tableHeightMax - sumHeight - tableTailHeight < 0)if (tableHeightMax - sumHeight - tableHeadHeight - ttHeadRowHeight < 0){double proRowHeight = FindRowHeight(collector, row - 1, table, doc);//获取到上行的行高table.Rows[row - 1].RowFormat.Height = proRowHeight + (tableHeightMax - sumHeight - tableHeadHeight - ttHeadRowHeight) + height;#region 插入表头一行Node node = table.Rows[0].Clone(true);table.Rows.Insert(row, node);//插入到第几行上方 因为刚刚说明过是多加了 所以这里把表尾加到此行上方是正确的row++;//插入航后 指针也要相应的向下移动count++;//总行数也跟着增加#endregionsumHeight = height;//表尾没有空间将这行放下 所以需要放在下一页pages++;}}else//后续页处理{sumHeight = sumHeight + height;//该页已经满了如果加上次行后表尾位置不够 判断为不能插入到此页 调整上一行样式进行适配if (tableHeightMax - sumHeight - tableHeadHeight < 0){double proRowHeight = FindRowHeight(collector, row - 1, table, doc);//获取到上行的行高table.Rows[row - 1].RowFormat.Height = proRowHeight + (tableHeightMax - sumHeight - tableHeadHeight) + height;#region 插入表头一行Node node = table.Rows[tableHeadIndex].Clone(true);table.Rows.Insert(row, node);//插入到第几行上方 因为刚刚说明过是多加了 所以这里把表尾加到此行上方是正确的doc.UpdatePageLayout();row++;//插入航后 指针也要相应的向下移动count++;//总行数也跟着增加#endregionsumHeight = height;//表尾没有空间将这行放下 所以需要放在下一页pages++;}}}//最后一页填充逻辑if (pages == 1){//第一页不同的是多了个题头double residuePlace = tableHeightMax - sumHeight - tableHeadHeight - ttHeadRowHeight;//剩余空间=总高度-项目行总高-表头高-题头高double proRowHeight = FindRowHeight(collector, count - 1, table, doc);//获取到上行的行高table.Rows[count - 1].RowFormat.Height = proRowHeight + (tableHeightMax - sumHeight - tableHeadHeight);}else{double residuePlace = tableHeightMax - sumHeight - tableHeadHeight;//最后一页剩余空间double proRowHeight = FindRowHeight(collector, count - 1, table, doc);//获取到上行的行高table.Rows[count - 1].RowFormat.Height = proRowHeight + (tableHeightMax - sumHeight - tableHeadHeight);}collector.Clear();doc.UpdatePageLayout();//更新布局}
/// <summary>/// 根据传入表计算表头总高度/// </summary>/// <param name="doc"></param>/// <param name="tableHead">表头行数</param>/// <param name="tableN">第几个表</param>/// <returns></returns>public static double GetTableHeadHeight(Document doc, int tableHead, int tableN){var lsittables= doc.GetChildNodes(NodeType.Table, true);Aspose.Words.Tables.Table table = (Aspose.Words.Tables.Table)doc.GetChildNodes(NodeType.Table, true)[tableN] ;//表头总高度double headHigh = 0;//获取实际行高LayoutCollector collector = new LayoutCollector(doc);LayoutEnumerator enumerator = new LayoutEnumerator(doc);int count = 0;//for (Aspose.Words.Tables.Row row : table.Rows)for (int i = 0; i < table.Rows.Count; i++){enumerator.Current = collector.GetEntity(table.Rows[i].Cells[0].FirstParagraph);//在每一层中寻找row类型的实际参数while (enumerator.Type != LayoutEntityType.Row){enumerator.MoveParent();}//以双精度返回框架矩形左上角的Y坐标。double Y = enumerator.Rectangle.Y;//以双精度返回框架矩形的高度。double height = enumerator.Rectangle.Height;if (count < tableHead){headHigh = headHigh + height;}else{break;}count++;}return headHigh;}
/// <summary>/// 获取表格在任意一页中占据的最大实际高度/// </summary>/// <param name="doc"></param>/// <param name="tableHead">表头行数</param>/// <returns></returns>public static double GetTableHeightMax(Document doc){//页面高度包括了上下边框、页眉页脚高度和内容高度double sumHeightMax = 0;//每页最大行数Section newSection = doc.LastSection;// 获取页面总高度double totalPageHeight = newSection.PageSetup.PageHeight;// 获取上下边距double topMargin = newSection.PageSetup.TopMargin;double bottomMargin = newSection.PageSetup.BottomMargin;// 主要页眉double headerHeight = GetHeaderHeight(doc);//主要页脚double footerHeight = GetFooterHeight(doc);// 计算可用高度(实际内容区域)sumHeightMax = totalPageHeight - topMargin - bottomMargin - headerHeight - footerHeight;return sumHeightMax;}
/// <summary>/// 获取页眉页脚的实际高度/// </summary>public static double GetHeaderFooterHeight(Aspose.Words.HeaderFooter headerFooter, Document doc){doc.UpdatePageLayout();LayoutCollector collector = new LayoutCollector(doc);try{LayoutEnumerator enumerator = new LayoutEnumerator(doc);enumerator.Current = collector.GetEntity(headerFooter);// 向上查找直到找到HeaderFooter的布局实体while (enumerator.Type != LayoutEntityType.HeaderFooter && enumerator.Type != LayoutEntityType.None){if (!enumerator.MoveParent()) break;}if (enumerator.Type == LayoutEntityType.HeaderFooter){return enumerator.Rectangle.Height;}}catch{// 如果无法获取实际高度,返回估算值}// 估算高度:根据内容行数估算int lineCount = 0;foreach (Paragraph para in headerFooter.GetChildNodes(NodeType.Paragraph, true)){if (!string.IsNullOrEmpty(para.GetText().Trim())){lineCount++;}}return lineCount * 16; // 每行大约16点}/// <summary>/// 获取页眉高度/// </summary>public static double GetHeaderHeight(Document doc){double headerHeight = 0;foreach (Section section in doc.Sections){if (section.HeadersFooters[HeaderFooterType.HeaderPrimary] != null){// 需要更新布局后获取实际高度doc.UpdatePageLayout();LayoutCollector collector = new LayoutCollector(doc);var header = section.HeadersFooters[HeaderFooterType.HeaderPrimary];if (header != null && header.GetChildNodes(NodeType.Any, false).Count > 0){// 估算或获取实际高度headerHeight = Math.Max(headerHeight, GetHeaderFooterHeight(header, doc));}}}return headerHeight;}/// <summary>/// 获取页脚高度/// </summary>public static double GetFooterHeight(Document doc){double footerHeight = 0;foreach (Section section in doc.Sections){if (section.HeadersFooters[HeaderFooterType.FooterPrimary] != null){var footer = section.HeadersFooters[HeaderFooterType.FooterPrimary];if (footer != null && footer.GetChildNodes(NodeType.Any, false).Count > 0){footerHeight = Math.Max(footerHeight, GetHeaderFooterHeight(footer, doc));}}}return footerHeight;}
还有俩个方法没怎么用上,自行斟酌选用
/// <summary>/// 获取首页除表格外的题头等所占高度/// </summary>/// <param name="doc"></param>/// <param name="tableHead">表头行数</param>/// <returns></returns>public static double GetPageTitleHeight(Document doc, double tablePageMax){double titleHeight = 0;Section newSection = doc.LastSection;// 获取页面总高度double totalPageHeight = newSection.PageSetup.PageHeight;// 获取上下边距double topMargin = newSection.PageSetup.TopMargin;double bottomMargin = newSection.PageSetup.BottomMargin;// 计算可用高度(实际内容区域)int usableHeight = (int)(totalPageHeight - topMargin - bottomMargin);//int usableHeight = 675;//(int)(totalPageHeight - topMargin - bottomMargin);titleHeight = usableHeight - tablePageMax;return titleHeight;}/// <summary>/// 获取表格中自动匹配list数据中普通行的行高/// </summary>/// <param name="doc"></param>/// <param name="tableHead">表头</param>/// <param name="tableTail">表尾</param>/// <param name="tableN"></param>/// <returns></returns>public static double GetTableCommonRowHeight(Document doc, int tableHead, int tableTail, int tableN){Aspose.Words.Tables.Table table = doc.GetChildNodes(NodeType.Table, true)[tableN] as Aspose.Words.Tables.Table; ;int rows = table.Rows.Count;//获取实际行高LayoutCollector collector = new LayoutCollector(doc);LayoutEnumerator enumerator = new LayoutEnumerator(doc);List<Double> heightList = new List<Double>();for (int i = tableHead - 1; i < rows - tableTail; i++){enumerator.Current = collector.GetEntity(table.Rows[i - 1].Cells[0].FirstParagraph);//enumerator.setCurrent(collector.getEntity(table.getRows().get(i - 1).getFirstCell().getFirstParagraph()));//在每一层中寻找row类型的实际参数while (enumerator.Type != LayoutEntityType.Row){enumerator.MoveParent();}//以双精度返回框架矩形的高度。double height = enumerator.Rectangle.Height;heightList.Add(height);// System.out.println("\n高度: " + height + "\n" +// "第" + i + "\n内容:" + table.getRows().get(i-1).getText());}// 使用LINQ来按元素分组,并计算每个元素的出现次数var elementCountMap = heightList.GroupBy(h => h).ToDictionary(g => g.Key, g => g.LongCount());// 找出出现次数最多的元素var max = elementCountMap.OrderByDescending(pair => pair.Value).FirstOrDefault();// 获取keydouble key = max.Key;//// 使用Collectors.groupingBy来按元素分组,并计算每个元素的出现次数//Map<Double, Long> elementCountMap = heightList.stream()// .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));//// 找出出现次数最多的元素//Optional<Map.Entry<Double, Long>> max = elementCountMap.entrySet().stream()// .max(Map.Entry.comparingByValue());////获取key//Double key = max.get().getKey();return key;}
再展示一下多页的效果

