poi-tl、aspose实现word中表在每页携带表头表尾

news2024/9/25 23:24:00

实现word中表在每页携带表头表尾(第一版)

word中的表格如果只有一页时表头表尾都很好处理,当中间内容足够多时,表尾只会出现在最后一页,表头也只会出现在第一页,之前想过用word自带的页眉页尾来处理但是,效果不尽人意!因为内容与页眉页尾有一定的间隔,有一种割裂感!
在这里插入图片描述
如上图,就是使用页眉页尾的方法解决的,一般场景时够用了!程序员都是有些追求的,不能尽善尽美也要尽可能的完美,接下来是用aspse实现的无缝隙的完美版。

准备工作

需要项目引入aspose(支持正版)不会引入的自己去查maven本地加载jar包
然后接下来咱们要了解一下aspose的概念
aspose中虽然 页 操作很少,但是每一个元素的Y坐标都是按照页来的 咱们可以根据这个来判断是第几页了
每一页刚开头的元素Y值一定小于上一页最后一个元素的Y值

//以双精度返回框架矩形左上角的Y坐标。
            double Y = enumerator.getRectangle().getY();

要注意的是咱们要获取到word表的实际高度,后面有具体代码

实现过程

进行处理,分为三个阶段,我的注解给的很详细,再不懂的就留言吧!
第一为首页数据进行排序处理
第二处理除首尾两页的数据
第三处理最后一页的数据

/**
     * aspose实现复制行
     * 没有页的概念 需要手动算总行高 (页面大小包括了 页眉页尾)
     * 演示测试方法,表头为6行 表尾为4行
     * @throws Exception
     */
    @Test
    void asposeTableCopyRow() throws Exception {
        Document doc = new Document("testEndAddOutOnePage.docx");
        Table table = (Table) doc.getChild(NodeType.TABLE, 1, true);
        double tableHeadHeight = getTableHeadHeight(doc, 6);//表头
        double tableTailHeight = getTableTailHeight(doc, 4);//表尾
        double tableHeightMax = getTableHeightMax(doc, 6);//word中table适配的最高高度
        if (tableHeightMax == 0.0){
            //需要处理的word文件不足一页 直接返回原文件
            return;
        }
        double tableCommonRowHeight = getTableCommonRowHeight(doc, 6, 4);//获取自匹配插入数据的大众行高
        int commonRow = 0;//获取自匹配插入数据的大众行的代表行索引
        int numRows = 0;//最后一页补充条数
        LayoutCollector collector = new LayoutCollector(doc);//获取实际行高
        int pages = 1;//标记是不是第一页
        double sumHeight = 0;//手动记录总行高
        int count = table.getRows().getCount();//总行数

        for (int row = 0 ; row < count - 4 ; row++) {//循环跳过表尾
            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){//该页已经满了如果加上次行后表尾位置不够 判断为不能插入到此页 调整上 一行 样式进行适配
                    double proRowHeight = findRowHeight(collector, row - 1, table, doc);//获取到上行的行高
                    table.getRows().get(row-1).getRowFormat().setHeight(proRowHeight + (tableHeightMax - sumHeight - tableTailHeight) + height);
                    for (int i = 0; i < 4; i++) {
                        Node node = table.getRows().get(table.getRows().getCount() - 4 + i).deepClone(true);
                        table.getRows().insert(row ,node);//插入到第几行上方 因为刚刚说明过是多加了 所以这里把表尾加到此行上方是正确的
                        row++;//插入航后 指针也要相应的向下移动
                        count++;//总行数也跟着增加
                    }
                    sumHeight = height;//表尾没有空间将这行放下 所以需要放在下一页
                    pages++;
                }
            }else { //不是首页多了一个表头
                sumHeight = sumHeight + height;
                if (tableHeightMax - sumHeight - tableHeadHeight - tableTailHeight < 0){//该页已经满了如果加上次行后表尾位置不够 判断为不能插入到此页 调整上一行样式进行适配
                    double proRowHeight = findRowHeight(collector, row - 1, table, doc);//获取到上行的行高
                    table.getRows().get(row-1).getRowFormat().setHeight(proRowHeight + (tableHeightMax - sumHeight - tableHeadHeight - tableTailHeight) + height);
                    for (int i = 0; i < 4; i++) {
                        Node node = table.getRows().get(table.getRows().getCount() - 4 + i).deepClone(true);
                        table.getRows().insert(row ,node);
                        row++;
                        count++;
                    }
                    sumHeight = height;//表尾没有空间将这行放下 所以需要放在下一页
                    pages++;
                }
            }
        }

        //上面的for循环结束后就是到最后一页的处理逻辑
        if (pages == 1){//可能信息很少第一页都没装满
            //如果只有一页 直接返回原文件
        }else {
            double residuePlace = tableHeightMax - sumHeight - tableHeadHeight - tableTailHeight;//最后一页剩余空间
            numRows = (int) (residuePlace/tableCommonRowHeight);//向下取整 看最多还能插入几条数据
            for (int i = 0; i < numRows; i++) {
                Node node = table.getRows().get(commonRow).deepClone(true);
                table.getRows().insert(count- 4,node);//因为索引是从0开始的 行数是从1开始 insert函数是在此行的上方插入所以这里可以正好抵消
                count++;
                sumHeight = sumHeight + tableCommonRowHeight;//由于插入的新行是复制的大众行 这个行高是已知的
            }
            table.getRows().get(count - 5).getRowFormat().setHeight(tableCommonRowHeight + (tableHeightMax - sumHeight - tableHeadHeight - tableTailHeight));//因为是向上取整获得的行数 所以很可能会有一段留白这里补上
        }

        clearLastRowData(numRows, table, doc);//清除最后一页补充的信息
        collector.clear();
        doc.updatePageLayout();
        doc.save("xxx_new.docx");
    }
		double tableHeadHeight = getTableHeadHeight(doc, 6);//表头
        double tableTailHeight = getTableTailHeight(doc, 4);//表尾
        double tableHeightMax = getTableHeightMax(doc, 6);//word中table适配的最高高度
        if (tableHeightMax == 0.0){
            //需要处理的word文件不足一页 直接返回原文件
            return;
        }
        double tableCommonRowHeight = getTableCommonRowHeight(doc, 6, 4);//获取自匹配插入数据的大众行高

这部分数据是根据你们需要使用的数据进行个性化处理的。
如果需要这部分代码等我有空了,去除敏感信息后把代码附上·······

该方法使用到的方法如下

获取实际行高:

//获取实际行高
    double findRowHeight(LayoutCollector collector, int row, Table table, Document doc) throws Exception {
        //获取实际行高
        LayoutEnumerator enumerator = new LayoutEnumerator(doc);
        enumerator.setCurrent(collector.getEntity(table.getRows().get(row).getFirstCell().getFirstParagraph()));
        //在每一层中寻找row类型的实际参数
        while (enumerator.getType()!= LayoutEntityType.ROW) {
            enumerator.moveParent();
        }
        //以双精度返回框架矩形的高度。
        return enumerator.getRectangle().getHeight();
    }

多余行内容清除:

void clearLastRowData(int numRows, Table table, Document doc){
        for (int i = 0; i < numRows; i++) {
            for (Cell cell : table.getRows().get(table.getRows().getCount() - 5 - i)) {
                //将单元格中的第一个段落移除
                cell.getFirstParagraph().remove();
                //新建一个段落
                Paragraph p = new Paragraph(doc);
                //设置一个string的值
                String value = " ";
                //把设置的值赋给之前新建的段落
                p.appendChild(new Run(doc, value));
                //将此段落加到单元格内
                cell.appendChild(p);
            }
        }
    }

遇到的困难:

之前认为把表覆盖整个页面时表高就等于页面高度了!

//页面总高度
        double pageHeight = doc.getFirstSection().getPageSetup().getPageHeight();

文档里是有方法可以直接获取到页面高度的
想当然的后果就是所有数据都计算好了渲染时总是会多一行或者少一行!而且是找不到原因!
最后自己写方法计算了一页中表最大能多高,再配合计算就能分毫不差了。

还有就是inset函数是将新行插入到索引的上方!其实也好理解文档里两个方法可以在已有表中插入新行,分别为insert和add
其中insert是可以指定索引的,我一般喜欢用这个,他的意思就是把新行插入到这个索引位置,当然原本在这个位置的行就会往后排,这样想就理解了,当时是死记的:insert是插入到索引行上的一行。就导致遇到误差是否认你自己!很耽误时间!
add就好理解了 在别的最后加上新行 他不能指定索引。
最后效果如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1909847.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【中项第三版】系统集成项目管理工程师 | 第 4 章 信息系统架构③ | 4.6

前言 第4章对应的内容选择题和案例分析都会进行考查&#xff0c;这一章节属于技术相关的内容&#xff0c;学习要以教材为准。本章分值预计在4-5分。 目录 4.6 网络架构 4.6.1 基本原则 4.6.2 局域网架构 4.6.3 广域网架构 4.6.4 移动通信网架构 4.6.5 软件定义网络 4.6…

云动态摘要 2024-07-09

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 数据库上云优选 阿里云 2024-07-04 RDS、PolarDB、Redis、MongoDB 全系产品新用户低至首年6折起&#xff01; [免费体验]智能助手ChatBI上线 腾讯云 2024-07-02 基于混元大模型打造&…

【面试】高频面试点:从源码角度一篇文章带你搞懂128陷阱!

要理解什么是“128陷阱”&#xff0c;首先来看一段代码&#xff1a; public static void main(String... strings) {Integer integer1 3;Integer integer2 3;if (integer1 integer2)System.out.println("integer1 integer2");elseSystem.out.println("inte…

07-7.3.2 平衡二叉树(AVL)

&#x1f44b; Hi, I’m Beast Cheng &#x1f440; I’m interested in photography, hiking, landscape… &#x1f331; I’m currently learning python, javascript, kotlin… &#x1f4eb; How to reach me --> 458290771qq.com 喜欢《数据结构》部分笔记的小伙伴可以…

【Linux】:服务器用户的登陆、删除、密码修改

用Xshell登录云服务器。 1.登录云服务器 先打开Xshell。弹出的界面点。 在终端上输入命令ssh usernameip_address&#xff0c;其中username为要登录的用户名&#xff0c;ip_address为Linux系统的IP地址或主机名。 然后输入密码进行登录。 具体如下&#xff1a; 找到新建会话…

提高项目效率必备:探索2024年10大最佳需求管理系统

本文将分享2024年10款高效需求管理工具&#xff1a;PingCode、Worktile、Tapd、禅道、Teambition、ClickUp、Tower、Asana、Jira 和 monday.com。 在快速变化的软件开发环境中&#xff0c;选择合适的需求管理工具变得至关重要。项目失败往往源于需求不明确或管理不善&#xff0…

linux权限深度解析——探索原理

前言&#xff1a;本节内容主要讲述的是linux权限相关的内容&#xff0c; linux的权限如果使用root账号是感受不到的&#xff0c; 所以我们要使用普通账号对本节相关内容进行学习&#xff0c;以及一些实验的测试。 然后&#xff0c; 通过linux权限的学习我们可以知道为什么有时候…

记一次 .NET某酒业业务系统 崩溃分析

一&#xff1a;背景 1. 讲故事 前些天有位朋友找到我&#xff0c;说他的程序每次关闭时就会自动崩溃&#xff0c;一直找不到原因让我帮忙看一下怎么回事&#xff0c;这位朋友应该是第二次找我了&#xff0c;分析了下 dump 还是挺经典的&#xff0c;拿出来给大家分享一下吧。 …

成都欣丰洪泰文化传媒有限公司电商服务领航者

在当今数字化浪潮中&#xff0c;电商行业正以前所未有的速度蓬勃发展。作为这片蓝海中的佼佼者&#xff0c;成都欣丰洪泰文化传媒有限公司凭借其专业的电商服务能力和对市场的敏锐洞察力&#xff0c;成为众多品牌信赖的合作伙伴。今天&#xff0c;就让我们一起走进成都欣丰洪泰…

大屏自适应容器组件 v-scale-screen

在vue中&#xff0c;v-scale-screen可用于大屏项目开发&#xff0c;实现屏幕自适应&#xff0c;可根据宽度自适应&#xff0c;高度自适应&#xff0c;和宽高等比例自适应&#xff0c;全屏自适应。 仓库地址&#xff1a;github国内地址&#xff1a;gitee 一、安装 npm instal…

Mysql中存储引擎简介、修改、查询、选择

场景 数据库存储引擎 数据库存储引擎是数据库底层软件组件&#xff0c;数据库管理系统&#xff08;DBMS &#xff09;使用数据引擎进行创建、查询、更新和删除数据的操作。 不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能&#xff0c;使用不同的存储引擎还可以…

【C++ 】-vector:新时代动态数组的革新与未来

目录 1. vector的介绍及使用 1.1 vector的介绍 1.1.1 vector是什么 1.1.2 vector的存储机制 1.2 vector的使用 1.2.1 定义和构造函数 1.2.2 迭代器 1.2.3 容量相关操作 1.2.4 元素访问和修改 1.3 迭代器失效问题 2. vector深度剖析及模拟实现 2.1 std::vector的模拟…

【笔记】finalshell中使用nano编辑器GNU

ctrl O 保存 enter 确定 ctrl X 退出 nano编辑 能不用就不用吧 因为我真用不习惯 nano编辑的文件也可以用vim编辑的

网页提示“非私密连接”怎么办?

当网页提示“非私密连接”或“您与该网站的连接不是私密连接”&#xff0c;这通常意味着浏览器无法建立一个安全的HTTPS连接。HTTPS协议是HTTP协议的安全版本&#xff0c;通过SSL协议加密数据传输&#xff0c;以保护用户的数据免受中间人攻击或监听。主要有下面几个原因&#x…

In Search of Lost Online Test-time Adaptation: A Survey--论文笔记

论文笔记 资料 1.代码地址 https://github.com/jo-wang/otta_vit_survey 2.论文地址 https://arxiv.org/abs/2310.20199 3.数据集地址 1论文摘要的翻译 本文介绍了在线测试时间适应(online test-time adaptation,OTTA)的全面调查&#xff0c;OTTA是一种专注于使机器学习…

Apache配置与应用(优化apache)

Apache配置解析&#xff08;配置优化&#xff09; Apache链接保持 KeepAlive&#xff1a;决定是否打开连接保持功能&#xff0c;后面接 OFF 表示关闭&#xff0c;接 ON 表示打开 KeepAliveTimeout&#xff1a;表示一次连接多次请求之间的最大间隔时间&#xff0c;即两次请求之间…

一.5 高速缓存至关重要

这个简单的示例揭示了一个重要的问题&#xff0c;即系统花费了大量的时间把信息从一个地方挪到另一个地方。hello程序的机器指令最初是存放在硬盘上&#xff0c;当程序加载时&#xff0c;它们被复制到主存&#xff1b;当处理器运行程序时&#xff0c;指令又从主存复制到处理器。…

C++报警:warning: zero as null pointer constantstddef.h

源码和警告内容 解决办法&#xff1a; select(0,nullptr,nullptr,nullptr,&delay); 关于NULL和nullptr的区别&#xff1a; 在C中&#xff0c;nullptr和null&#xff08;通常指的是NULL宏&#xff0c;因为C标准中并没有直接定义null关键字&#xff09;都用于表示空指针&am…

基于eBPF的procstat软件追踪等待锁和持有锁的时间

在并发编程中&#xff0c;锁的使用是保证线程安全的重要手段。然而&#xff0c;过度使用锁或者锁竞争可能导致性能瓶颈。为了分析程序中锁的使用情况&#xff0c;我们可以借助procstat软件来追踪程序加锁时间和等待锁的时间。procstat是一个基于eBPF&#xff08;extended Berke…

uniapp安卓端实现语音合成播报

最初尝试使用讯飞语音合成方式,能获取到语音数据,但是数据是base64格式的,在安卓端无法播放,网上有说通过转成blob格式的url可以播放,但是uniapp不支持转换的api;于是后面又想其他办法,使用安卓插件播报原生安卓语音播报插件 - DCloud 插件市场 方案一(讯飞语音合成) 1.在讯飞…