iText7高级教程之构建基础块——3.使用ILeafElement实现类
Posted CuteXiaoKe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iText7高级教程之构建基础块——3.使用ILeafElement实现类相关的知识,希望对你有一定的参考价值。
ElementPropertyContainer
类有三个子类:Style
、RootElement
、和AbstractElement
。在第一章里面我们已经简明地讨论了Style
类。在上一章我们讨论了RootElement
的子类Canvas
和Document
。在接下来的是三章我们将围绕AbstractElement
类来讨论:
- 我们将在本章讲述
ILeafElement
的实现类:Tab
、Link
、Text
和Image
; - 在下一章我们会着重讲述
BlockElement
对象:Div
、LineSeparator
、List
、ListItem
和Paragraph
; - 在章节5我们讲述
Table
和Cell
对象结束对BlockElement
对象的探讨;
注意在第二章我们已经讨论了AreaBreak
对象,一直到第五章为止,我们会涵盖所有构件基础块的类;
在上一章中,我们使用了一个txt文件来创建PDF文档。同样的在本章中我们使用一个CSV文件来当做数据输入,如图3.1所示:
在上图我们可以清晰的看到,此 CSV 文件可以解释为包含由 6 个字段组成的记录的数据库表:
- IMDB电影编号-这些编号是基于英国小说家罗伯特·路易斯·史蒂文森写的《Jekyll and Hyde》所改编的电影;
- 年份-电影制作的年份
- 标题-电影标题名称
- 导演
- 产地
- 时长
我们会使用工具类来解析这个文件,这个文件以UTF-8格式存储,读取并保存在二位列表List<List<String>>
,代码如下:
public static final List<List<String>> convert(String src, String separator) throws IOException
List<List<String>> resultSet = new ArrayList<>();
BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(src), "UTF8"));
String line;
List record;
while ((line = br.readLine()) != null)
StringTokenizer tokenizer = new StringTokenizer(line, separator);
record = new ArrayList<>();
while (tokenizer.hasMoreTokens())
record.add(tokenizer.nextToken());
resultSet.add(record);
return resultSet;
在本章,我们会使用Tab
类来渲染二维列表到PDF中。
1. 使用Tab元素
我们直接看下面一段代码:
List<List<String>> resultSet = CsvTo2DList.convert(SRC, "|");
for (List<String> record : resultSet)
Paragraph p = new Paragraph();
p.add(record.get(0).trim()).add(new Tab())
.add(record.get(1).trim()).add(new Tab())
.add(record.get(2).trim()).add(new Tab())
.add(record.get(3).trim()).add(new Tab())
.add(record.get(4).trim()).add(new Tab())
.add(record.get(5).trim());
document.add(p);
在第1行,我们使用CsvTo2DList
工具类创建了二维列表resultSet。第2行开始,我们对这个结果进行循环,在循环体中,我们对每一行的数据进行分割,每一列中添加Tab
对象;
如图3.2展示了结果的PDF文件:
在下列代码中我们添加了多余的线来展示默认的Tab位置。
PdfCanvas pdfCanvas = new PdfCanvas(pdf.addNewPage());
for (int i = 1; i <= 10; i++)
pdfCanvas.moveTo(document.getLeftMargin() + i * 50, 0);
pdfCanvas.lineTo(document.getLeftMargin() + i * 50, 595);
pdfCanvas.stroke();
默认情况下,在页面左边距开始,每个分割线是50用户单位(默认情况下为50pt,用户单位详见itext第3章)。前三列(“IMDB”,“Year”,“Title”)的Tab位置出现的很恰当,但是从"Director(s)"列开始就没有对齐了,我们尝试修复并得出如图3.3所示的结果:
以下是修复位置后的代码,其中我们使用了TabStop
类:
float[] stops = new float[]80, 120, 430, 640, 720;
List<TabStop> tabstops = new ArrayList();
PdfCanvas pdfCanvas = new PdfCanvas(pdf.addNewPage());
for (int i = 0; i < stops.length; i++)
tabstops.add(new TabStop(stops[i]));
pdfCanvas.moveTo(document.getLeftMargin() + stops[i], 0);
pdfCanvas.lineTo(document.getLeftMargin() + stops[i], 595);
pdfCanvas.stroke();
在行1中我们定义了一个float数组,里面包含了5个Tab位置。行2定了一个TabStop
列表,从行4开始我们开始循环float数组,行5是往TabStop
列表中添加元素。同样我们会在第一页把Tab的线画出来,方便我们视觉上判断Tab是否正确。
接下来的代码和之前的几乎一样:
List<List<String>> resultSet = CsvTo2DList.convert(SRC, "|");
for (List<String> record : resultSet)
Paragraph p = new Paragraph();
p.addTabStops(tabstops);
p.add(record.get(0).trim()).add(new Tab())
.add(record.get(1).trim()).add(new Tab())
.add(record.get(2).trim()).add(new Tab())
.add(record.get(3).trim()).add(new Tab())
.add(record.get(4).trim()).add(new Tab())
.add(record.get(5).trim());
document.add(p);
document.close();
第4行是唯一的区别:我们使用addTabStops()
方法来把List
对象添加到Paragraph
中。每一列的内容都是相对于制表符定义的位置对齐方式是左对齐的。
如果我们修改一下对齐方式,如图3.4所示:
完成上图效果所需的代码如下:
float[] stops = new float[]80, 120, 580, 590, 720;
List tabstops = new ArrayList();
tabstops.add(new TabStop(stops[0], TabAlignment.CENTER));
tabstops.add(new TabStop(stops[1], TabAlignment.LEFT));
tabstops.add(new TabStop(stops[2], TabAlignment.RIGHT));
tabstops.add(new TabStop(stops[3], TabAlignment.LEFT));
TabStop anchor = new TabStop(stops[4], TabAlignment.ANCHOR);
anchor.setTabAnchor(' ');
tabstops.add(anchor);
上面定义了5中tabstops(制表符/制表位):
- 第一个tabstop会把
年份
集中在位置80上面,为此我们使用的是TabAlignment.CENTER
; - 第二个tabstop会确保
标题
会从位置120开始,为此我们使用的是TabAlignment.LEFT
; - 第三个tabstop会确保
导演
会在位置580结束,为此我们使用的是TabAlignment.RIGHT
; - 第四个tabstop会确保
国家
会在位置590开始; - 第五个tabstop会把对齐到空格出现的位置,为此我们使用的是
TabAlignment.ANCHOR
,并且使用setTabAnchor()
方法来定义一个tab anchor。
在CSV文件的时长
字段里面并没有空格符,所以我们在试验的时候会把内容后面加上空格\\
,如下代码所示:
List<List<String>> resultSet = CsvTo2DList.convert(SRC, "|");
for (List<String> record : resultSet)
Paragraph p = new Paragraph();
p.addTabStops(tabstops);
p.add(record.get(0).trim()).add(new Tab())
.add(record.get(1).trim()).add(new Tab())
.add(record.get(2).trim()).add(new Tab())
.add(record.get(3).trim()).add(new Tab())
.add(record.get(4).trim()).add(new Tab())
.add(record.get(5).trim() + " \\'");
document.add(p);
document.close();
接着,让我们来看看另一个变种的样例,如图3.5所示:
讲一讲如何添加前导字符的实例代码:
float[] stops = new float[]80, 120, 580, 590, 720;
List tabstops = new ArrayList();
tabstops.add(new TabStop(stops[0], TabAlignment.CENTER, new DottedLine()));
tabstops.add(new TabStop(stops[1], TabAlignment.LEFT));
tabstops.add(new TabStop(stops[2], TabAlignment.RIGHT, new SolidLine(0.5f)));
tabstops.add(new TabStop(stops[3], TabAlignment.LEFT));
TabStop anchor = new TabStop(stops[4], TabAlignment.ANCHOR, new DashedLine());
anchor.setTabAnchor(' ');
tabstops.add(anchor);
一个前导字符的定义使用了ILineDrawer
接口。我们在IMDB电影编号
和年份
之间添加了虚线,在标题
和导演
之间添加实线,在国家
和时长
之间添加了短划线。
我们可以实现ILineDrawer
接口来画任何一种线。但是iText中已经附带了三种实现:SolidLine
、DottedLine
和DashedLine
。这些实现类可以让你改变线的宽度和颜色。DottedLine
类还允许您更改点之间的间距。在接下来的章节里面,我们也会使用这些类和LineSeparator
类一起绘制线分隔符。
乍一看,使用Tab对象似乎是以表格形式呈现内容的一种很好的方法,但是有一些严格的限制。
2. Tab功能的限制
前面的例子我们通过使用制表位很好的展示了文字的内容。当然这里面有个前提就是我们使用了横向A4纸,并预留了足够的空间。遇到空间不足的情况咋办,例如如图3.6所示,我们在纵向A4纸上面添加内容:
由上图可见,整体效果不错,除了第一行的"Country"和"Duration"挤在了一起。
Tab 相关功能之前在 iText 7.0.0 中包含一些bug。 由于舍入错误,某些文本没有以看似随机的方式来选择对齐。 此问题已在 iText 7.0.1 中修复。
iText 7.0.1 中修复的另一个错误与 SolidLine(实现) 类有关。 在 iText 7.0.0 中,SolidLine 的线宽被忽略。
我们往下滚动文档,当没有足够的空间将标题
和导演
放在一起时,我们会看到一个更严重的问题。 导演
“Charles Lamont”将国家
的值推到时长
列,分钟数显示在第二行。如图3.7所示:
我们可以通过使用 Table 和 Cell 类以表格形式组织数据来解决这些问题。 这些对象将在接下来的第 5 章中讨论。 现在,我们将继续一些更多的 ILeafElement 实现类。
3. 添加链接
在之前的样例中,我们展示的内容里面有IMDB电影编号
。这个电影编号能让我们可以在IMDB官网(需fan墙)上面找到对应的影片信息。如下图3.8所示,我们不会显示电影编号,但是当我们点击电影标题
的时候会跳转到IMDB相应的网址。
整体的代码如下:
List<List<String>> resultSet = CsvTo2DList.convert(SRC, "|");
for (List<String> record : resultSet)
Paragraph p = new Paragraph();
p.addTabStops(tabstops);
PdfAction uri = PdfAction.createURI(
String.format("http://www.imdb.com/title/tt%s", record.get(0)));
Link link = new Link(record.get(2).trim(), uri);
p.add(record.get(1).trim()).add(new Tab())
.add(link).add(new Tab())
.add(record.get(3).trim()).add(new Tab())
.add(record.get(4).trim()).add(new Tab())
.add(record.get(5).trim() + " \\'");
document.add(p);
document.close();
在第 5-6 行,我们创建了一个链接到 URL 的 PdfAction 对象。 此 URL 由 https://www.imdb.com/title/tt/ 和 IMDB ID 组成。 在第 7 行,我们使用包含电影标题的 String 和 PdfAction 创建一个 Link 对象。 因此,您将能够在单击标题时跳转到相应的 IMDB 页面。
PDF交互性的是通过annotations
(注释)来实现的。注释不是真实内容的一部分。 它们是添加在内容之上的对象。上面的例子就是链接注释的使用方法。当然还有其他很多类型的注释,在本章中我们不会讲述。当然还有许多其他类型的Action
(动作),目前我们只使用了URI动作,我们将在第6章用到更多的动作。
Link 类扩展了 Text 类。此文档列出了一系列可用于 Link 和 Text 类的方法,用于更改字体、更改背景颜色、添加等。
4. 在Text中的额外方法
在之前的章节中我们已经多次使用了Text
类,但让我们仔细看看一些我们还没有讨论过的 Text
功能。
如上图3.9所示,并结合下述代码,一开始的文字是正常的,但是对于"Dr. Jekyll"字母,我们定义了文本上升,并且水平缩放了单词"and",最后我们歪曲了"Mr. Hyde."
Text t1 = new Text("The Strange Case of ");
Text t2 = new Text("Dr. Jekyll").setTextRise(5);
Text t3 = new Text(" and "以上是关于iText7高级教程之构建基础块——3.使用ILeafElement实现类的主要内容,如果未能解决你的问题,请参考以下文章
iText7高级教程之构建基础块——4.使用AbstractElement对象(part 1)
iText7高级教程之构建基础块——4.使用AbstractElement对象(part 1)
iText7高级教程之构建基础块——5.使用AbstractElement对象(part 2)
iText7高级教程之构建基础块——5.使用AbstractElement对象(part 2)