Aspose.word实现表格每页固定表头、最后一行填满整个页面

这篇文章参考自poi-tl、aspose实现word中表在每页携带表头表尾_poi-tl 表格超出纸张大小第二页任然需要表头-CSDN博客

先说一下目前所做的表格情况:检测项目是动态的,但是最后一行要求在最后填充整个页面

以前的我做法是:项目行的行高是固定的27.5,一页面大概就是25个项目行,最后备注行算还差多少行合并就行。

但是现在要改成

(1)项目行是自适应的,不能固定了,

(2)项目分页,每页要显示表头

所以我就只能判断整个页面表格能显示的最大高度,然后算项目行使用了多少高度,再进行填充

先看一下全部代码

cs 复制代码
/// <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;
                    #endregion
            builder.Writeln("人类三体文明牛马质量检测处\r\n检验检测报告\r\n");

            #region 格式(编号、名称)
                    builder.Font.Size = 10;
                    builder.Font.Name = "宋体";
                    builder.Font.Bold = false;
                    #endregion
            var 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;
                    //我们不需要指定此单元格的宽度,因为它是从前一个单元格继承的。
                    #endregion

                    builder.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;
            #endregion
            builder.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;
}

效果如下:

一页的效果

多页的效果:

之前的做法是固定项目行高来算的

cs 复制代码
            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

cs 复制代码
#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;
//我们不需要指定此单元格的宽度,因为它是从前一个单元格继承的。
#endregion

builder.InsertCell();
builder.Write(project);

展示一下开始尝试的错误代码

cs 复制代码
            #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,时而100
            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;//还有多少高填满一页

            //设置备注行高度
            tableNow.Rows[remarkRow].RowFormat.Height = mm;
            #endregion

由于获取不到真实的项目行所占据的高度,导致导出的结果一直不是很理想

怎么获取构建表格行的真实高度?

其实这里所要获取的是基于实际渲染结果的精确行高

使用的主要代码如下

cs 复制代码
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()之后)

cs 复制代码
#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

具体的方法如下

cs 复制代码
        /// <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++;//总行数也跟着增加
                        #endregion
                        sumHeight = 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++;//总行数也跟着增加
                        #endregion

                        sumHeight = 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();//更新布局
        }
cs 复制代码
        /// <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;
        }
cs 复制代码
        /// <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;
        }
cs 复制代码
        /// <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;
        }

还有俩个方法没怎么用上,自行斟酌选用

cs 复制代码
        /// <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();

            // 获取key
            double 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;
        }

再展示一下多页的效果

相关推荐
code bean2 小时前
【C#笔记】Newtonsoft.Json 中 `[JsonIgnore]` 的作用详解
笔记·c#·json
gCode Teacher 格码致知2 小时前
Python教学基础:用Python和openpyxl结合Word模板域写入数据-由Deepseek产生
python·word
饼干,2 小时前
第5天python内容
开发语言·python
froginwe112 小时前
Ruby 发送邮件 - SMTP
开发语言
DKunYu2 小时前
1.多线程初阶
java·开发语言
ccut 第一混2 小时前
用c# 制作一个扑克牌小游戏
开发语言·c#
听风吟丶2 小时前
Java 9 + 模块化系统实战:从 Jar 地狱到模块解耦的架构升级
开发语言·python·pycharm
做怪小疯子3 小时前
JavaScript 中Array 整理
开发语言·前端·javascript
旭编3 小时前
牛客周赛 Round 117
java·开发语言