1. itext7史上最全实战总结

1.1. 前言

最近有个需求需要我用Java手动写一份PDF报告,经过考察几种pdf开源代码,最终选取了itext7,此版本为7.1.11,由于发现网上关于该工具的博文比较少,特别是实战博文几乎没有,在我踩完各种坑,最终把PDF成型后,打算把经验分享出来,本文通过摘录解释来说明,内容来自本人GitHub itext-pdf

1.2. 配置文件

项目采用了Spring Cloud config所以配置在git上,仅仅研究itext7不需要用到数据库等功能,请直接运行PdfMain类的main方法,即可生成模拟的PDF报告

1.3. 版本POM

itext7相关pom

7.1.11

com.itextpdf

kernel

${itext.version}

com.itextpdf

io

${itext.version}

com.itextpdf

layout

${itext.version}

com.itextpdf

forms

${itext.version}

com.itextpdf

pdfa

${itext.version}

com.itextpdf

pdftest

${itext.version}

com.itextpdf

font-asian

${itext.version}

org.slf4j

slf4j-log4j12

1.7.18

com.itextpdf

html2pdf

3.0.0

1.4. 干货

itext7语义本身和前端css很像,所以有点前端基础还是比较容易掌握的

1.4.1. 添加图片

读取项目中图片文件

设置边距

设置宽高扩大缩小

Image indexImage = new Image(ImageDataFactory.create(GenoReportBuilder.class.getClassLoader().getResource("image/gene.png")));

indexImage.setMargins(-50, -60, -60, -60);

indexImage.scale(1, 1.05f);

1.4.2. 添加指定空白页

添加第2页为空白页,立即刷新后再继续添加

pdf.addNewPage(2).flush();

1.4.3. Div、Paragraph

Div div = new Div();

div.setWidth(UnitValue.createPercentValue(100));

div.setHeight(UnitValue.createPercentValue(100));

div.setHorizontalAlignment(HorizontalAlignment.CENTER);

Paragraph p1 = new Paragraph();

p1.setHorizontalAlignment(HorizontalAlignment.CENTER);

p1.setMaxWidth(UnitValue.createPercentValue(75));

p1.setMarginTop(180f);

p1.setCharacterSpacing(0.4f);

Style large = new Style();

large.setFontSize(22);

large.setFontColor(GenoColor.getThemeColor());

p1.add(new Text("尊敬的 ").addStyle(large));

...

Paragraph p2 = new Paragraph();

...

div.add(p1);

div.add(p2);

整块的内容用Div包裹,这里整块包裹的好处是什么?一方面排版分明成体系,另一方面若需求是整块的内容必须在同一个版面,你可以对Div设置div.setKeepTogether(true);,尽量保证若整块的内容超出了一页,那这块内容会自动整块出现在下一页,上一页剩下的就留白了

可以看到Div,Paragraph可以设置很多属性,实际上我们常用的组件除了这两种,还有Table,Cell,List,他们大部分的属性都是一样的,只是部分属性只在部分组件起效果,所以当你设置某个属性没起效果也不用奇怪

Paragraph需要特别注意的一点,想要段落文字居中,不要用setHorizontalAlignment(HorizontalAlignment.CENTER);这是组件的居中对段落无效,甚至对段落里你放Text也无效,需要改用setTextAlignment(TextAlignment.CENTER);

Paragraph段落的行距也是个高频问题,这里给出官方我看到的解释,参考https://itextpdf.com/en/resources/books/itext-7-building-blocks/chapter-4-adding-abstractelement-objects-part-1,搜关键字setFixedLeading,我的理解该方法设值行高绝对值,官方解释是两行文字中间基线之间的距离

如果想了解详细的什么属性哪里能起作用哪里不行,请访问该地址

1.4.4. Table

useAllAvailableWidth表示页面有多宽,我就有多宽

table.startNewRow();表示新起一行,table每画一行都要新起一行

同样table内容需要居中,和段落一样,请设置new Cell().setTextAlignment(TextAlignment.CENTER)

每个table中cell都有默认高度,会比实际输入字体高些,此时设置setHeight,若更大没有问题,若高度小于或接近字体大小文字可能就消失了,若想让Cell高度更接近文字高度,请设置Cell的padding,即cell.setPadding(-2),设置负值即可

1.4.5. Tab,\t

itext7中如果要表示段落前的空格,不能使用\t,但换行可以使用\n

若要实现Tab效果可以有多个方法

\u00a0符号,大概7、8个该符号可表示tab,可能不是很准确

p1.add(new Text("\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0壹基因衷心祝愿您身体健康、享受品质生活!"));

p1.setFirstLineIndent(24),表示段落前留多少空,需要知道一个字多大,设置成两倍就行

Tab也是集成AbstractElement的组件,通过以下方式也可实现相同的效果

p2.add(new Tab());

p2.addTabStops(new TabStop(20, TabAlignment.LEFT));

1.4.6. 换页

我常用的换页方法为如下,该方法可保证立即换页

doc.add(new AreaBreak(AreaBreakType.NEXT_PAGE));

当然PdfDocument有addNewPage其实也可以用,但有时候你没把握好刷新时间可能导致某些混乱

1.4.7. 画图或画文字

能画出多么复杂的图形看是谁画了,在我的PDF中,我画的最复杂的图形如下

该图形由多个弧形区域加线段加文字组成,包括数字上的小箭头也是画出来的,画这个的代码过多,想要了解详细的可以自行下载研究,这里介绍API功能

lineTo画线段

roundRectangle可用来画角是弧形的方形,也可以用来画圆

showText用来画文字

以上几种结合填充即可把三角形,多边形画出来了

PdfPage page = pdf.getPage(pdf.getNumberOfPages());

pageSize = pdf.getDefaultPageSize();

PdfCanvas pdfCanvas = new PdfCanvas(page);

pdfCanvas.saveState().moveTo(pageSize.getWidth() / 2 - 100 + i * 40, yOffset - 203)

.lineTo(pageSize.getWidth() / 2 - 100 + i * 40, yOffset - 208)

.stroke().restoreState();

pdfCanvas.setLineWidth(2);

pdfCanvas.setStrokeColor(color);

pdfCanvas.roundRectangle(pageSize.getWidth() / 2 - 3 + posXOffset, yOffset - 188, 6, 6, 3)

.stroke();

pdfCanvas.beginText()

.setFontAndSize(font, 12)

.moveText(pageSize.getWidth() / 2 - text.length() * 12 / 2, yOffset - 45);

pdfCanvas.showText(text);

pdfCanvas.endText();

1.4.8. Html段落转Pdf段落

我们可能遇到把一段Html文本转换成itext7的段落放进来,此时需要用到它的htmlToPdf模块,该模块对应POM

com.itextpdf

html2pdf

3.0.0

至于使用,设置好配置属性,使用也很简单,通常我们需要支持中文,所有配置如下,字体可以自己换

ConverterProperties proper = new ConverterProperties();

//字体设置,解决中文不显示问题

FontSet fontSet = new FontSet();

fontSet.addFont(GenoReportBuilder.class.getClassLoader().getResource("font/SourceHanSansCN-Regular.ttf").getPath(), PdfEncodings.IDENTITY_H);

FontProvider fontProvider = new FontProvider(fontSet);

proper.setFontProvider(fontProvider);

String content = "html内容";

List elements = HtmlConverter.convertToElements(content, proper);

转换的内容是IElement集合,而IElement是什么呢?给张图就了解了

也就是说只要你的html内容是

如下是我的处理例子供参考,我把输入html内容样式进行了一定修改后转成itext7组件,这里特别提心,html转过来的itext7组件可能会不支持部分样式的修改,所以需要在html中进行css样式的添加,这里我就把字体和高度统一用css设值了

Div overall = new Div();

java.util.List iElements = getFixContent(value);

for (IElement iElement : iElements) {

Style style = new Style();

style.setFontSize(10);

style.setCharacterSpacing(0.7f);

if (iElement instanceof Div) {

Div div = (Div) iElement;

java.util.List children = div.getChildren();

// 全部段落改成相同样式

this.addParagraphStyleCircle(style, children);

overall.add(div);

} else if (iElement instanceof Paragraph) {

Paragraph element = (Paragraph) iElement;

overall.add(element.addStyle(style));

}

}

doc.add(overall);

getFixContent

private java.util.List getFixContent(String content) {

if (content.startsWith("

")) {

content = content.replaceAll("

", "

");

} else {

content = "

" + content + "

";

}

return HtmlConverter.convertToElements(content, proper);

}

addParagraphStyleCircle

private void addParagraphStyleCircle(Style style, java.util.List children) {

for (IElement child : children) {

if (child instanceof Paragraph) {

Paragraph element = (Paragraph) child;

element.addStyle(style);

java.util.List children1 = element.getChildren();

this.addParagraphStyleCircle(style, children1);

}

if (child instanceof Div) {

Div div = (Div) child;

java.util.List children1 = div.getChildren();

this.addParagraphStyleCircle(style, children1);

}

if (child instanceof Text) {

Text text = (Text) child;

text.addStyle(style);

}

}

}

1.4.9. 监听事件

在编写pdf的时候,比如一篇整体的文章,我们需要在页眉位置添加关于这篇文章的固定文本或者图形,类似于打个标签,表示你翻了这么多页一直在看这篇文章,当第二篇文章的时候就换一个,举个例子

第一页

第二页

这种需求我们如何实现呢?思路分析发现,我们需要知道什么时候文章内容一页写不起了,换了一页的时候我们需要添加一个同样的页眉。这样我们就需要知道页是何时添加的,监听事件就是处理这种问题的

pdf是PdfDocument,可添加的事件有START_PAGE,INSERT_PAGE,REMOVE_PAGE,END_PAGE共四个,如上需求我们需要监听START_PAGE事件,在事件处理中做相应的处理,我在事件中使用PdfCanvas画了头部内容

HeaderTextEvent headerTextEvent = new HeaderTextEvent(title, font);

pdf.addEventHandler(PdfDocumentEvent.START_PAGE, headerTextEvent);

HeaderTextEvent类,Painting仅仅是封装了PdfCanvas

public class HeaderTextEvent implements IEventHandler {

private String text;

private PdfFont font;

public HeaderTextEvent(String text,PdfFont font) {

this.text = text;

this.font = font;

}

@Override

public void handleEvent(Event event) {

PdfDocumentEvent docEvent = (PdfDocumentEvent) event;

PdfDocument pdfDoc = docEvent.getDocument();

Painting painting = new Painting(pdfDoc, font);

painting.drawHeader();

painting.drawHeaderText(text);

painting.close();

}

}

在添加内容前添加相应事件,同时需要记得在不需要的时候移除

// 移除监听器

pdf.removeEventHandler(PdfDocumentEvent.START_PAGE, headerTextEvent);

1.4.10. 添加目录

我没有找到itext7原生是否有目录添加,根据我自己的需求,我用Table组件来实现了自定义目录,由于我的PDF是用来打印的,所以我并没有给目录添加Link,也就是页面跳转,不过当你彻底理解了我的项目,我想这个需求实现也不难

实现效果如下,随着内容的增长,目录自动增长

先说下遇到的困难,目录顾明思意,必须要有内容才会有目录,所以实际上目录是最后添加的,但如果我们添加内容到最后再跳转到前面的页面来添加目录,有三个问题:

目录有几页如何知道?

目录有几页不知道,如何知道内容在第几页?

由于目录不确定,所以后续内容的页码其实也是不确定的,也就是说页码也不是一页页可以添加过去的

而经过实践你会发现,我们不能够回到前几页去修改已存在的页面,因为会提示你已经flush了,不能修改。

这时我看到了movePage这个方法,也就是可以通过移动页面,把目录在内容之后生成,后再移动到前几页,但是页码还是不能修改,发现脑袋不够想了只能用上屁股,灵光一闪,不能一遍生成为什么不能二次渲染呢?于是研究读取原pdf在原pdf上修改,二次渲染的时候填上页码及移动页面,主要代码如下,包括了读取中间文件,移动目录,添加每页页码

PdfReader reader = null;

PdfWriter writer = null;

String inPath = getInPath();

try {

reader = new PdfReader(new File(inPath));

writer = new PdfWriter(new File(outPath));

} catch (IOException e) {

e.printStackTrace();

}

PdfDocument pdf = new PdfDocument(reader, writer);

Document doc = new Document(pdf);

int startPage = 7;

int numberOfPages = pdf.getNumberOfPages();

for (int i = 0; i < catalogSize; i++) {

pdf.movePage(numberOfPages, startPage);

}

String forbidPage = properties.getProperty("forbidPage");

for (int pageNumber = 1; pageNumber < numberOfPages + 1; pageNumber++) {

if (pageNumber > 6 + catalogSize && pageNumber != 8 + catalogSize) {

if (forbidPage != null && (pageNumber - catalogSize) >= Integer.parseInt(forbidPage)) {

continue;

}

PageSize pageSize = pdf.getDefaultPageSize();

doc.showTextAligned(new Paragraph(String.format("- %d -", pageNumber)), pageSize.getWidth() / 2, 30, pageNumber, TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0);

}

}

1.5. 总结

经过上述总结,我基本上把项目中的大多基本点和难点都概括进去了,初次用itext7写PDF的同学基本会遇到的问题基本都在上述这些,不理解的就把项目下下来运行Main方法慢慢调试,理解透我这个项目,还有其它问题那基本只能翻官网了

itext 添加空格_itext7史上最全实战总结相关推荐

  1. itext7读取pdf 中文_itext7史上最全实战总结

    1. itext7史上最全实战总结 1.1. 前言 最近有个需求需要我用Java手动写一份PDF报告,经过考察几种pdf开源代码,最终选取了itext7,此版本为7.1.11,由于发现网上关于该工具的 ...

  2. javascript array添加图片_史上最全的web前端面试题汇总及答案JavaScript之二(二)...

    作者:樱桃小丸子儿 链接:https://www.jianshu.com/p/abadcc84e2a4 JavaScript JS的基本数据类型 number,string,boolean,objec ...

  3. Mysql增删改查|SQL语句(史上最全|实战教学)

    文章目录 关于数据库的操作 1.查看数据库 2.创建数据库 3.选择要操作的数据库 4.查看自己所处的位置及默认所在的位置 5.在命令行选择默认的数据库 6.删除数据库 关于表的操作 1. 查看库有哪 ...

  4. 史上最全实战资源,机器学习框架、高分练手项目及数据集汇总

    机器学习领域,最常讨论到的一个话题就是机器学习项目. 学习或从事这个领域的小伙伴都会想要找一些机器学习的项目来进行练手,做项目好比练题,孰能生巧,能够在机器学习这个领域获取更多的知识和技能. 本篇目录 ...

  5. java spring框架 注解_史上最全的java spring注解

    史上最全的java spring注解,没有之一 注解是个好东西,但好东西我们也是看见过,整理过,理解过,用过才知道好.不求我们每个都记住,但求保有印象,在需要的时候能提取出来再查找相关资料,平时工作就 ...

  6. 10_史上最全的Markdown使用教程(没有之一)(20190115)

    我用markdown写博客已经有半年之久了吧,但是还是会出现有些你想用的功能很难找,或者你尝试使用它不好使,为了帮助那些和我一样热衷于使用markdown的乘客,我决定自己造一架航班,让我们一起搭乘M ...

  7. VS Code:史上最全的VS Code快捷键+分门别类(中英文对照版)

    VS Code:史上最全的VS Code快捷键+分门别类(中英文对照版) 目录 基础编辑 Basic editing 导航 Navigation 搜索和替换 Search and replace 多光 ...

  8. @async注解_史上最全的java spring注解

    史上最全的java spring注解,没有之一 注解是个好东西,但好东西我们也是看见过,整理过,理解过,用过才知道好.不求我们每个都记住,但求保有印象,在需要的时候能提取出来再查找相关资料,平时工作就 ...

  9. 史上最全的Linux常用命令汇总①收藏这一篇就够了!(超全,超详细)

    史上最全的Linux常用命令汇总①(超全面!超详细!)收藏这一篇就够了! Linux命令基础 Shell Linux命令分类 Linux命令行的格式 编辑Linux命令行的辅助操作 获取命令帮助的方法 ...

最新文章

  1. spring-boot环境搭建
  2. Android 重新编译资源文件
  3. java语言特点 字符串不变_面试必问:Java中String类型为什么设计成不可变的?
  4. 容器编排技术 -- Kubernetes 垃圾收集
  5. CVPR 2019|手写签名认证的逆鉴别网络
  6. select语句一些要点(一)
  7. 主机_云服务器vps价格比对[博]
  8. iSPRINT:Google 最高能的创新加速课程,产品一次就成的助推器!
  9. If-Modified-Since If-None-Match
  10. vbs中的WScript.Network[属性与方法]
  11. C++中名字隐藏,名字查找优先于类型检查
  12. 商业分析方法与工具总结
  13. 计算机视觉—车道线检测
  14. win7 旗舰版系统激活方法
  15. 企业微信和钉钉的区别以及企业微信的功能
  16. 概率论在实际生活的例子_生活中有趣的概率论例子
  17. 2021-04-12
  18. SSR在天猫优品大促会场的探索实践
  19. SDL库的安装,spca5xx的安装,spcaview 的安装,摄像头的查看,及常见问题的解决方法。
  20. httpclient 设置短连接_关于HTTP的长连接和短连接那些事

热门文章

  1. javascript语法_JavaScript传播语法简介
  2. 云服务器 存放 文件夹,云服务器 存放 文件夹
  3. des加密的c语言程序,C++中四种加密算法之DES源代码
  4. js获取数组最大值的索引_数组中最大值的返回索引
  5. C# LDAP认证登录类参考
  6. 免费持久的天气预报web service
  7. 使用jQuery的blockUI来实现页面回传数据时的等待页面
  8. 漫步最优化三十四——高斯-牛顿法
  9. 漫步数理统计五——条件概率与独立(上)
  10. tensorflow中用saver保存模型