当前位置: 首页 > article >正文

# Excel模板转PDF合并单元格边框全乱了?逐个格子读取边线信息再还原

Excel模板转PDF合并单元格边框全乱了逐个格子读取边线信息再还原非科班野生程序员深耕政务信息化20年。从VC到PB再到Java自研框架browise也打磨了十几年。最近整理框架代码发现不少有趣的决策写出来和大家聊聊。最后感谢豆包、智谱、OpenCode决策是我做的代码是我搓的文字是他们总结的。问题政务报表的流程是业务人员用 Excel 画模板表头、合并单元格、边框样式系统读取 Excel 填数据转 PDF 给用户预览或打印。但 Excel 转 PDF 时合并单元格的边框处理是个大坑。jxl 读取 Excel 时合并区域只有左上角那个格子里有值其他格子是空的。转成 PDF 的 PdfPTable 时合并单元格的上下左右边线信息需要从正确的位置取取错了边框就乱了。实现思路第一步从 Excel 读出每个格子的完整信息publicvoidxlsToPDF(Workbookworkbook,Stringpage,Stringfile)throwsException{Sheetsheetworkbook.getSheet(0);Range[]msheet.getMergedCells();// 拿到所有合并区域for(inti0;isheet.getRows();i){StringhString.valueOf(sheet.getRowView(i).getSize()/18);ArrayListHashMapString,StringcontentnewArrayListHashMapString,String();for(intj0;jsheet.getColumns();j){HashMapString,StringmapnewHashMapString,String();Cellcellsheet.getCell(j,i);Stringstrcell.getContents();// 数字格式化保留两位小数if(cell.getType()CellType.NUMBER||cell.getType()CellType.NUMBER_FORMULA){strString.format(%.2f,Double.valueOf(str));}// 读取格式信息jxl.format.CellFormatftcell.getCellFormat();Stringalign,valign,bold0;intfontsize0;Stringst0,tl0,bl0,ll0,rl0;if(ft!null){// 对齐方式if(ft.getAlignment()jxl.format.Alignment.CENTRE)alignc;elseif(ft.getAlignment()jxl.format.Alignment.LEFT)alignl;elsealignr;// 垂直对齐if(ft.getVerticalAlignment()jxl.format.VerticalAlignment.CENTRE)valignm;elseif(ft.getVerticalAlignment()jxl.format.VerticalAlignment.BOTTOM)valignb;elsevalignt;// 字号和加粗jxl.format.Fonttfft.getFont();if(tf!null){fontsizetf.getPointSize();if(tf.getBoldWeight()700)bold1;}// 四个方向的边线tlString.valueOf(ft.getBorder(Border.TOP).getValue());blString.valueOf(ft.getBorder(Border.BOTTOM).getValue());llString.valueOf(ft.getBorder(Border.LEFT).getValue());rlString.valueOf(ft.getBorder(Border.RIGHT).getValue());// 只要有一个方向有线就算有边框if(1.equals(tl)||1.equals(bl)||1.equals(ll)||1.equals(rl))st1;}// 存到 HashMapmap.put(val,str);map.put(fs,String.valueOf(fontsize));map.put(line,st);map.put(lb,ll);// left bordermap.put(tb,tl);// top bordermap.put(rb,rl);// right bordermap.put(bb,bl);// bottom bordermap.put(h,h);map.put(a,align);map.put(v,valign);map.put(bold,bold);content.add(map);}tableContent.add(content);}}每个格子读出12个属性值、字号、四个方向的边线、行高、水平对齐、垂直对齐、是否加粗、是否超长文本。第二步合并单元格的边线要从正确的位置取这是关键部分。Excel 合并区域里只有左上角格子有完整格式。但合并后的底边线实际在区域最底行右边线实际在最右列for(Rangeobj:m){intrsobj.getTopLeft().getRow();intreobj.getBottomRight().getRow();intcsobj.getTopLeft().getColumn();intceobj.getBottomRight().getColumn();if((irsire)(jcsjce)){if(irsjcs){// 只处理左上角if(rsre){// 底边线要从最底行取Cellcell2sheet.getCell(j,ire-rs);jxl.format.CellFormatft2cell2.getCellFormat();blString.valueOf(ft2.getBorder(Border.RIGHT).getValue());}if(csce){// 右边线要从最右列取Cellcell2sheet.getCell(jce-cs,i);jxl.format.CellFormatft2cell2.getCellFormat();rlString.valueOf(ft2.getBorder(Border.BOTTOM).getValue());}break;}else{break;}}}这段代码虽然短但解决了核心问题合并区域的边界线要从实际边界位置的格子读取而不是从左上角格子读取。第三步写 PDF 时还原合并和边框for(ArrayListHashMapString,Strings:tableContent){for(HashMapString,Stringt:s){booleancfalse;// 处理合并for(Rangeobj:m){intrsobj.getTopLeft().getRow();intreobj.getBottomRight().getRow();intcsobj.getTopLeft().getColumn();intceobj.getBottomRight().getColumn();if((irsire)(jcsjce)){if(irsjcs){if(rsre)cell.setRowspan(re-rs1);if(csce)cell.setColspan(ce-cs1);break;}else{ctrue;// 被合并的格子跳过break;}}}if(c){j;continue;}// 还原边框if(0.equals(t.get(line))){cell.disableBorderSide(15);// 全部无边框}// ... 以下根据四个方向的边线状态组合处理}}第四步边框组合的排列处理这部分代码最多——根据上、下、左、右四条边线的有无组合出各种情况调用 iText 的disableBorderSideelseif(0.equals(t.get(lb))0.equals(t.get(rb))0.equals(t.get(bb)))cell.disableBorderSide(14);// 隐藏下、左、右elseif(0.equals(t.get(lb))0.equals(t.get(rb))0.equals(t.get(tb)))cell.disableBorderSide(13);// 隐藏上、左、右elseif(0.equals(t.get(lb))0.equals(t.get(rb)))cell.disableBorderSide(12);// 隐藏左、右// ... 十几种组合 ...elseif(0.equals(t.get(tb)))cell.disableBorderSide(1);// 只隐藏上每个组合对应一个数字是 iText 的Rectangle.LEFT、Rectangle.RIGHT等常量的位运算组合值。这段代码看着啰嗦但每一种组合都是实测踩出来的。第五步加水印PDF 生成后还能叠加图片水印publicvoidaddWaterMarkIncludeWords(StringinputFile,StringoutputFile,StringimageFilePath,intwidth,intheight){PdfReaderreadernewPdfReader(inputFile);PdfStamperstampernewPdfStamper(reader,newFileOutputStream(outputFile));inttotalreader.getNumberOfPages()1;ImageimageImage.getInstance(imageFilePath);image.setAbsolutePosition(width,height);image.scalePercent(35f);for(inti1;itotal;i){stamper.getUnderContent(i).addImage(image);}stamper.close();newFile(inputFile).delete();// 删除无水印版本}政务报表经常要加内部文件之类的印章水印。小结ToPDF 这个类445行大部分代码在处理合并单元格的边框还原。核心思路就是从 Excel 逐格子读取格式信息包括四条边的线写到 PDF 时逐格子还原。合并区域的边界线从实际边界位置取不是从左上角取。做报表的同学有没有更好的 Excel 转 PDF 方案评论区聊聊。标签#Java #Excel转PDF #iText #jxl #合并单元格 #政务报表 #自研框架

相关文章:

# Excel模板转PDF合并单元格边框全乱了?逐个格子读取边线信息再还原

Excel模板转PDF合并单元格边框全乱了?逐个格子读取边线信息再还原 非科班野生程序员,深耕政务信息化20年。从VC到PB再到Java,自研框架browise也打磨了十几年。最近整理框架代码,发现不少有趣的决策,写出来和大家聊聊。…...

2025最权威的六大降AI率平台实际效果

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 在学术写作这块领域当中,AI论文工具正一步步地变成研究者的得力助手,…...

# MyBatis Mapper文件不想手动注册?启动时自动扫描conf目录,动态拼配置

MyBatis Mapper文件不想手动注册?启动时自动扫描conf目录,动态拼配置 非科班野生程序员,深耕政务信息化20年。从VC到PB再到Java,自研框架browise也打磨了十几年。最近整理框架代码,发现不少有趣的决策,写出…...

STM32智能水产养殖系统设计与实践

1. 项目概述这个基于STM32的智能水产养殖大棚监测与控制系统,是我去年为一个淡水虾养殖场设计的实际项目。传统的水产养殖大棚主要依赖人工经验进行环境调控,不仅劳动强度大,而且难以实现精准控制。这套系统通过传感器网络实时采集水质参数&a…...

Arduboy光线投射渲染库:8位MCU上的实时3D引擎

1. ArduboyRaycast 库概述ArduboyRaycast 是一个专为 Arduboy 平台设计的轻量级光线投射(Raycasting)渲染库,面向资源极度受限的 8-bit AVR 微控制器(ATmega32U4,16MHz,2.5KB RAM,32KB Flash&am…...

三维点云障碍物检测与聚类算法对比实现

三维点云障碍物检测与聚类算法对比实现 项目概述 本项目实现了一个完整的三维点云障碍物检测系统,集成了K-means和DBSCAN两种经典聚类算法,并对它们的性能进行了对比分析。系统包含点云数据生成、预处理、聚类检测、结果可视化和性能评估等模块。代码设计遵循模块化原则,注…...

RemoteSerial:ESP32/ESP8266 Web串口调试库详解

1. RemoteSerial 库深度解析:面向 ESP8266/ESP32 的嵌入式 Web 串口监控系统RemoteSerial 是一个专为 ESP8266 和 ESP32 平台设计的轻量级、高实时性的 Web 串口监控库。它并非简单地将Serial对象映射到网页,而是构建了一套完整的异步 WebSocket 通信栈&…...

AI时代的算法思维:大经典排序学习驯

引言 在现代软件开发中,性能始终是衡量应用质量的重要指标之一。无论是企业级应用、云服务还是桌面程序,性能优化都能显著提升用户体验、降低基础设施成本并增强系统的可扩展性。对于使用 C# 开发的应用程序而言,性能优化涉及多个层面&#x…...

金融PHP支付代码审计清单(含OWASP ASVS 4.0对标项):27个必查风险点,附自动化检测脚本

第一章:金融PHP支付接口安全代码审计导论金融级PHP支付接口是资金流转的核心通道,其安全性直接关系到用户资产、商户信誉与平台合规性。不同于通用Web应用,支付接口需同时满足PCI DSS基础要求、中国《金融行业网络安全等级保护基本要求》&…...

突破重复任务困境:星穹铁道玩家的自动化效率解决方案

突破重复任务困境:星穹铁道玩家的自动化效率解决方案 【免费下载链接】March7thAssistant 崩坏:星穹铁道全自动 三月七小助手 项目地址: https://gitcode.com/gh_mirrors/ma/March7thAssistant 在《崩坏:星穹铁道》的冒险旅程中&#…...

AI编程实战:从零到一搭建全栈项目睹

1. 核心概念 在 Antigravity 中,技能系统分为两层: Skills (全局库):实际的代码、脚本和指南,存储在系统级目录(如 ~/.gemini/antigravity/skills)。它们是“能力”的本体。 Workflows (项目级)&#xff1a…...

2025届学术党必备的十大AI论文平台实测分析

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 要使得文本的AIGC检出率能够有效降低,就得从语言风格、句法结构以及逻辑组织这三…...

InnoDB存储结构全解析:行页区段与单表W行的关系谌

一、背景与问题缘起 MySQL 5.6.51 版本下 2000 万行核心业务表开展新增字段操作,需求为新增BIGINT(19) NOT NULL DEFAULT 0 COMMENT 注释(因业务实际需要存储大数值关联字段)。 表的核心特性为Java 多线程密集读写,业务请求持续高…...

Arduino GIGA R1 非阻塞 WAV 播放库 GigaAudio 深度解析

1. GigaAudio 库概述:面向 Arduino GIGA R1 的嵌入式 WAV 播放引擎GigaAudio 是专为 Arduino GIGA R1 开发板设计的轻量级、非阻塞式 WAV 音频播放库。其核心定位并非通用音频框架,而是针对 GIGA R1 硬件平台(基于 ARM Cortex-M7 的 NXP i.MX…...

AI开发-python-langchain框架(--langchain与milvus的结合 )逝

一、 什么是 AI Skills:从工具级到框架级的演化 AI Skills(AI 技能) 的概念最早在 Claude Code 等前沿 Agent 实践中被强化。最初,Skills 被视为“工具级”的增强,如简单的文件读写或终端操作,方便用户快速…...

一个简洁易用的 Delphi JSON 封装库,基于 System.JSON`单元封装,提供更直观的 API拥

一、前言:什么是 OFA VQA 模型? OFA(One For All)是字节跳动提出的多模态预训练模型,支持视觉问答、图像描述、图像编辑等多种任务,其中视觉问答(VQA)是最常用的功能之一——输入一张…...

别再数据线了!用FastAPI 分钟搭个局域网文件+剪贴板神器俚

为 HagiCode 添加 GitHub Pages 自动部署支持 本项目早期代号为 PCode,现已正式更名为 HagiCode。本文记录了如何为项目引入自动化静态站点部署能力,让内容发布像喝水一样简单。 背景/引言 在 HagiCode 的开发过程中,我们遇到了一个很现实的问…...

从零入门性能测试:理论+JMETER实操,看完就能上手婪

一、环境准备 Free Spire.Doc for Python 是免费 Python 文档处理库,无需依赖 Microsoft Word,支持 Word 文档的创建、编辑、转换等操作,其中内置的 Markdown 解析能力,能高效实现 Markdown 到 Doc/Docx 格式的转换,且…...

突破设备救援瓶颈:MTKClient五维解决方案实现联发科设备底层修复

突破设备救援瓶颈:MTKClient五维解决方案实现联发科设备底层修复 【免费下载链接】mtkclient MTK reverse engineering and flash tool 项目地址: https://gitcode.com/gh_mirrors/mt/mtkclient 当你按下电源键,屏幕毫无反应;当系统更…...

C# 14 原生 AOT 编译 Dify 客户端到底难在哪?92%候选人栽在第4个内存模型问题上(AOT冷启动真相)

第一章:C# 14 原生 AOT 编译 Dify 客户端面试概览C# 14 引入的原生 AOT(Ahead-of-Time)编译能力,为构建轻量、启动极速、无运行时依赖的 Dify 客户端提供了全新路径。与传统 JIT 或托管 AOT 不同,原生 AOT 可将 C# 代码…...

单片机时序图解析与调试实战指南

1. 时序图基础认知第一次接触单片机编程时,看到芯片手册里那些密密麻麻的时序图,我的反应和大多数新手一样——完全看不懂那些高低起伏的波形到底在表达什么。直到有一次调试I2C通信失败,花了整整三天时间才意识到是时序不匹配导致的问题&…...

浏览器扩展提升文档效率:Markdown本地预览解决方案

浏览器扩展提升文档效率:Markdown本地预览解决方案 【免费下载链接】markdown-viewer Markdown Viewer / Browser Extension 项目地址: https://gitcode.com/gh_mirrors/ma/markdown-viewer 在技术文档阅读和编写过程中,你是否经常遇到本地Markdo…...

为什么你的GraalVM镜像内存比JVM还高?揭秘3类动态反射未注册、2种资源未预加载、1个ClassLoader残留的致命组合

第一章:Java GraalVM 静态镜像内存优化 如何实现快速接入GraalVM 的 Native Image 功能可将 Java 应用编译为独立、零依赖的静态可执行文件,显著降低启动延迟与运行时内存开销。但默认生成的镜像常因反射、动态代理或资源加载未显式配置而触发运行时回退…...

WeChatMsg:如何从微信聊天记录中提取个人数据宝藏并生成年度报告?

WeChatMsg:如何从微信聊天记录中提取个人数据宝藏并生成年度报告? 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/Gi…...

Python 算法题必备基础操作(高频速查版)

这是刷算法题、笔试、面试最常用的 Python 基础操作合集,覆盖数组、字符串、链表、哈希、栈队列、排序、遍历、边界处理等核心场景,直接背会就能写代码。 一、输入输出(笔试必用) 1. 标准输入 # 单个整数 n int(input())# 一行多…...

分子编码解锁电脑:电化学测序技术

利用分子编码的密文登录电脑 日期:2025年5月16日 来源:某机构出版社 摘要:像DNA这样的分子能够在不依赖能源的情况下存储大量数据,但访问这些分子数据成本高且耗时。研究人员现已开发出一种替代方法,将信息编码在合成分…...

突然发现一个现象:HR 原本要给 34 岁员工降薪50%,发现他单身无牵挂,马上转向36岁有两个孩子在上学,最不敢轻易离职的同事

最近听到一个无比扎心,却又异常真实的职场鬼故事。公司要缩减人力成本。HR原本盯上了一个34岁的男同事,打算一刀砍掉他50%的薪水,逼他自己滚蛋。结果稍微摸了一下底发现:这老哥单身,没买房,没车贷&#xff…...

亚马逊科学家近期荣誉与技术贡献

某机构科学家近期荣誉与奖项 研究人员因其对科学界的贡献而获得荣誉。 作者:Staff writer | 2023年2月22日 | 阅读时间8分钟林明当选美国国家发明家科学院院士 林明,某机构学者、马里兰大学计算机科学杰出大学教授,当选美国国家发明家科学院院…...

上班多年后才知道,原来提完离职后,第二天公司就同意你走,不是人情味,而是铜臭味。

昨天有个前同事找我喝酒,说提了离职,原本做好了交接一个月的心理准备。结果第二天HR就笑眯眯地跑来,说工作已经安排人接手,今天办完手续就可以走人了。这哥们感动坏了,觉得公司通情达理,不耽误他去下家报到…...

从付费软件到自主开发:我用AI和FFmpeg实现了一个录屏工具杖

我为什么会发出这个疑问呢?是因为我研究Web开发中的一个问题时,HTTP请求体在 Filter(过滤器)处被读取了之后,在 Controller(控制层)就读不到值了,使用 RequestBody 的时候。 无论是字…...