作者:CuteXiaoKe
微信公众号:CuteXiaoKe

  在之前的章节中,我们讨论了实现AbstractElement类的5个类。在章节2中,我们讨论了AreaBreak类。在章节3中我们讨论了实现ILeafElement类的4个类——TabLinkTextImage类。在这章节,我们会着重于其他一系列实现AbstractElement的类。如用于分组元素的Div类和用于在元素之间划线的LineSeparator。我们在之前的章节中已经多次使用过Paragraph类,但是在本章中我们将会重新讨论它。最后我们将讨论List类和ListItem类,而TableCell类将会在下一章节中讲述。

1. 使用Div类分组元素

  Div类是BlockElement类的实现,可以用来分组不同的元素。在图4.1中,我们可以看到Jekyll and Hyde系列电影的概览。其中每个条目包含最多三种元素:

  • 一个Paragraph显示电影的标题;
  • 一个Paragraph显示导演、国家和年份;
  • 一个Image显示电影海报(如果有的话);

  我们将这个三个元素组合在一个Div中,并为这个Div定义了左边框、左填充和下边距。

图4.1 在一个div中组合元素

  整体代码如下:

public void createPdf(String dest) throws IOException {PdfDocument pdf = new PdfDocument(new PdfWriter(dest));Document document = new Document(pdf);List> resultSet = CsvTo2DList.convert(SRC, "|");resultSet.remove(0);for (List record : resultSet) {Div div = new Div().setBorderLeft(new SolidBorder(2)).setPaddingLeft(3).setMarginBottom(10);String url = String.format("https://www.imdb.com/title/tt%s", record.get(0));Link movie = new Link(record.get(2), PdfAction.createURI(url));div.add(new Paragraph(movie.setFontSize(14))).add(new Paragraph(String.format("Directed by %s (%s, %s)",record.get(3), record.get(4), record.get(1))));File file = new File(String.format("src/main/resources/img/%s.jpg", record.get(0)));if (file.exists()) {Image img = new Image(ImageDataFactory.create(file.getPath()));img.scaleToFit(10000, 120);div.add(img);}document.add(div);}document.close();
}

  和往常一样,我们创建一个PdfDocument 和一个Document实例(第2-3行)。其中CSV文件我们重用了前一章节介绍的CSV文件,并遍历这个文件,列出所有电影,不包括标题行(行4-6)。我们创建另一个新的Div对象(行7),将它的左边框定义为2个用户单位的实心边框(行8),并将做填充设置为3个用户单位(行9),下边距设置为10个用户单位(行10)。紧接着我们电影标题Paragraph(行14)和其他信息(行15-17)添加到这个Div中。如果我们可以找到到电影海报,我们会添加Image对象(行24)。最后添加每一个DivDocument中并且关闭这个Document

  我们把目光放在第一页的底部和第二页的顶部,也就是电影标题是Dr. Jekyll and Mr. Hyde,导演是John S. RobersonDiv分布到两页上。电影海报在第一页上放不下,所以被放到了第二页。当然这可能不是我们所希望,也许我们希望添加到同一个Div的元素保持在一起,如图4.2所示:

图4.2 让一个div保持在一个页面

  我们只需要用一个额外的方法来做到如此,代码如下:

Div div = new Div().setKeepTogether(true).setBorderLeft(new SolidBorder(2)).setPaddingLeft(3).setMarginBottom(10);

  通过添加setKeepTogether(true),告诉iText尝试将Div的内容保持在同一页面上。如果该Div的内容适合在下一页,则该Div中所有的元素将被转发到下一页。在图4.2中就是这种情况,上述我们提到Dr. Jekyll and Mr. Hyde电影的标题和信息被转发到下一页。

  如果Div的内容不适合下一页,那么此方法将会不起作用。在这种情况下,元素分部在当前页面和后续页面上,就好像未使用 setKeepTogether() 方法一样。 如果真的想将一个元素与下一个元素保持在同一页面上,有一种解决方法。 在我们讨论了 LineSeparator对象之后,我们将看一个演示此解决方法的示例。

2. 使用 LineSeparator对象绘制水平线

  相信大家也看到了哈,iText里面的构建块与HTML里面的标签很类似。Text大致对应 (空标签),Paragraph对应<p>标签,Div对应<div>标签,以此类推。同理LineSeparator最好的对应标签为<hr>图 4.3 显示了一条由红线组成的水平线,1 个用户单位粗,占可用宽度的 50%,为此定义了 5 个用户单位的上边距。

图4.3 添加分割线

  代码如下:

SolidLine line = new SolidLine(1f);
line.setColor(Color.RED);
LineSeparator ls = new LineSeparator(line);
ls.setWidthPercent(50);
ls.setMarginTop(5);

  首先我们传递了厚度参数来创建了一个SolidLine对象。在这里我们回顾一下,在上一章中,SolidLineILineDrawer 接口的实现之一。 然后将其颜色设置为红色,并使用此 ILineDrawer 创建 LineSeparator 实例。 接着使用 setWidthPercent() 方法定义线条的宽度。 当然我们也可以使用 setWidth() 方法来定义以用户单位表示的绝对宽度。 最后,我们将上边距设置为 5 个用户单位。

  我们将 ls 对象添加到包含电影信息的Div元素中。

div.add(ls);

  关于LineSeparator基本上就是这些内容。我们只需要记住我们应该使用正确的方法来设置属性。例如:在LineSeparator层面不能改变线的颜色,需要在ILineDrawer层面设置。显得宽度也是如此。我们可以查验附录B来看LineSeparator 类实现了哪些 AbstractElement 方法,以及忽略了哪些方法。

3. 让内容保持在一起

  在前面的示例中,我们已经多次使用 Paragraph 类。 例如:在第 2 章中,我们使用 Paragraph 类将文本文件转换为 PDF,方法是为文本文件中的每一行创建一个 Paragraph 对象,并将所有这些 Paragraph 对象添加到 Document 实例中 . 前几章的屏幕截图表明,我们可以制作一些非常好的 PDF 文档,但总有改进的余地。

  如图4.4中展示了我们需要修复的一个缺陷:在第3也上面有个标题,但是那这章的内容却在第4也上面。

图4.4 一个孤独的标题

  这种问题我们应该尽量避免,让标题和内容在同一页面上,首先让我们做第一次尝试,采用的代码如下:

public void createPdf(String dest) throws IOException {PdfDocument pdf = new PdfDocument(new PdfWriter(dest));Document document = new Document(pdf);PdfFont font = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN);PdfFont bold = PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD);document.setTextAlignment(TextAlignment.JUSTIFIED).setHyphenation(new HyphenationConfig("en", "uk", 3, 3));BufferedReader br = new BufferedReader(new FileReader(SRC));String line;Div div = new Div();while ((line = br.readLine()) != null) {Paragraph title = new Paragraph(line).setFont(bold).setFontSize(12).setMarginBottom(0);div = new Div().add(title).setFont(font).setFontSize(11).setMarginBottom(18);while ((line = br.readLine()) != null) {div.add(new Paragraph(line).setMarginBottom(0).setFirstLineIndent(36));if (line.isEmpty()) {document.add(div);break;}}}document.add(div);document.close();
}

  上面代码和我们第2章写的很相似,最主要的区别就是我们不再直接把Paragrah添加到Document中。相反,我们把Paragrah添加到Div中,然后在每个章节的末尾把Div添加到Paragrah中。但是最终我们发现并能做到我们想要的结果。

  我们可以在第 15 行和第 16 行之间尝试添加 .setKeepTogether(true) ,但这不会产生任何影响,因为 Div 里面全部内容在单个页面里面放不下。 这个也提到过了, setKeepTogether() 方法会被忽略。 iText 上就如何解决这个问题进行了长时间的研究, 最终定避免寡居对象的最优雅方法是引入 setKeepWithNext() 方法。
  setKeepWithNext() 方法是在 iText 7.0.1 中引入的。 第一个 iText 7 版本中找不到这个方法。 iText正在研究是否可以支持嵌套对象的方法, 但是不愿意这样做,因为这可能会对库的整体性能产生重大的负面影响。

  如下代码展示了如何使用:

BufferedReader br = new BufferedReader(new FileReader(SRC));
String line;
Div div = new Div();
while ((line = br.readLine()) != null) {document.add(new Paragraph(line).setFont(bold).setFontSize(12).setMarginBottom(0).setKeepWithNext(true));div = new Div().setFont(font).setFontSize(11).setMarginBottom(18);while ((line = br.readLine()) != null) {div.add(new Paragraph(line).setMarginBottom(0).setFirstLineIndent(36));if (line.isEmpty()) {document.add(div);break;}}
}
document.add(div);

  首先把一个Paragraph直接添加到Document(行5);然后创建一个Div来容纳章节剩余内容(行9)。通过添加 setKeepWithNext(true) 来表明 Paragraph 需要与 Div 的(第一部分)保持在同一页面上。 结果如图 4.5所示。 与图 4.4相比,标题“SEARCH FOR MR. HYDE”现在被转发到下一页。

图4.5 让标题和文本保持在一起

  setKeepWithNext() 方法可以与除 Cell 之外的所有其他 AbstractElement 的实现类一起使用。 该方法仅适用于直接添加到 Document 实例的元素。 它不适用于嵌套对象,例如始终添加到表格而不直接添加到文档的Cell。 在上面示例中,如果标题 Paragraph 被添加到 Div 而不是 Document,它将不起作用。

4. 更改段落行距

  Paragraph 类除了实现 AbstractElement 级别定义的方法之上还有一些额外的方法。 我们已经使用了上一章中涉及 TabStops 的方法(制表符/制表位)。 上面的例子里面还引用了 setFirstLineIndent() 方法(改变缩进)。 现在我们将目光放在如何研究行距。
  行距-英文leading这个词发音为ledding,它来自铅(金属)这个词。 当为印刷机手动设置字体时,铅条被放置在字体行之间以增加空间。 这个词最初指的是放置在线条之间的这些铅条的厚度。 PDF 标准将前导重新定义为“相邻文本行的基线之间的垂直距离”(ISO-32000-1,第 9.3.5 节)。

  有两种方法可以改变段落的行距:

  • setFixedLeading() —— 将行距更改为绝对值。 例如:如果定义 18 的固定行距,则两行文本的基线之间的距离将为 18 个用户单位。
  • setMultipliedLeading ——将行距更改为相对于字体大小的值。 例如,如果定义乘以 1.5f 的行距并且字体为 12 pt,那么前导将为 18 个用户单位(即 1.5 乘以 12)。

  这两个方法是相互排斥的。 如果在同一个段落中同时使用这两种方法,则以最后调用的方法为准。 图 4.6 展示了读取这个故事的另一种转换: 总页数较低,因为我们通过添加 .setMultipliedLeading(1.2f) 更改了行之间的距离。

图4.6 改变缩进和行距

  实现上述的代码除了以下代码片段以外,和之前的例子几乎一样:

div.add(new Paragraph(line).setMarginBottom(0).setFirstLineIndent(36).setMultipliedLeading(1.2f)
);

  当我们直接或间接(例如通过Div)将对象添加到 Document 时,iText 使用适当的 IRenderer 将此对象呈现为 PDF。 在本系列第0章的图 4 里面展示了不同渲染器的概览。一般情况下我们使用 iText 几乎不需要创建自定义渲染器,但我们将看一个示例,在这个示例中我们创建了一个继承(extends)默认 ParagraphRendererMyParagraphRenderer

5. 创建自定义渲染器

  看如图4.7所示,有两个不同背景的Paragraphs。对于第一个Paragraph,我们使用了.setBackgroundColor()方法,这个方法基于段落的位置画了一个矩形,对于第二个Paragraph,我想要画一个圆角矩形。但是iText 7里面没有方法来做到,做到这我们需要写一个自定义ParagraphRenderer类。

图4.7 段落的默认和自定义背景

  让我们看看实现上述效果的代码片段,首先是第一个Paragraph,代码如此:

Paragraph p1 = new Paragraph("The Strange Case of Dr. Jekyll and Mr. Hyde");
p1.setBackgroundColor(Color.ORANGE);
document.add(p1);

  然后是第二个Paragraph,代码如此:

Paragraph p2 = new Paragraph("The Strange Case of Dr. Jekyll and Mr. Hyde");
p2.setBackgroundColor(Color.ORANGE);
p2.setNextRenderer(new MyParagraphRenderer(p2));
document.add(p2);

  其中用到的MyParagraphRenderer的定义如下:

class MyParagraphRenderer extends ParagraphRenderer {public MyParagraphRenderer(Paragraph modelElement) {super(modelElement);}@Overridepublic void drawBackground(DrawContext drawContext) {Background background =this.<Background>getProperty(Property.BACKGROUND);if (background != null) {Rectangle bBox = getOccupiedAreaBBox();boolean isTagged =drawContext.isTaggingEnabled()&& getModelElement() instanceof IAccessibleElement;if (isTagged) {drawContext.getCanvas().openTag(new CanvasArtifact());}Rectangle bgArea = applyMargins(bBox, false);if (bgArea.getWidth() <= 0 || bgArea.getHeight() <= 0) {return;}drawContext.getCanvas().saveState().setFillColor(background.getColor()).roundRectangle((double)bgArea.getX() - background.getExtraLeft(),(double)bgArea.getY() - background.getExtraBottom(),(double)bgArea.getWidth()+ background.getExtraLeft() + background.getExtraRight(),(double)bgArea.getHeight()+ background.getExtraTop() + background.getExtraBottom(),5).fill().restoreState();if (isTagged) {drawContext.getCanvas().closeTag();}}}
}

  我们继承了现有的类ParagraphRenderer并且值覆写了一个方法。从AbstractRenderer类中copy了原始的drawBackground()方法,并且使用了roundRectangle()(行23)代替了rectangle()方法。行24-29里面我们注意到矩形的尺寸可以通过左侧、右侧、顶部和底部的额外空间进行微调。这些额外空间可以通过不同参数的setBackgroundColor()方法来设置(具体为带有4个额外浮点数的变量:extraLeft, extraTop, extraRight, 和extraBottom)。

  最后让我们看看ListListItem的例子并结束本篇章。

5. 列表和列表符号

  如图4.8显示了默认可用的不同类型的列表。其中有编号列表(罗马数字和阿拉伯数字)、带有字母的列表(小写、大写、拉丁、希腊)等等。

图4.8 不同类型的列表

  下面代码是前面三种列表如何添加:

List list = new List();
list.add("Dr. Jekyll");
list.add("Mr. Hyde");
document.add(list);
list = new List(ListNumberingType.DECIMAL);
list.add("Dr. Jekyll");
list.add("Mr. Hyde");
document.add(list);
list = new List(ListNumberingType.ENGLISH_LOWER);
list.add("Dr. Jekyll");
list.add("Mr. Hyde");
document.add(list);

  行1我们首先声明了没有类型的List。默认情况下,这将生成一个以连字符作(-)为列表符号的列表。 然后在第 2-3 行快速 添加了2个列表项; 然后我们将List添加到 Document(第 4 行)。之后就是多次重复这四行,例如定义一个十进制数字列表(第 5 行),定义一个带有小写字母的字母列表(第 9 行)。

  创建不同类型的列表的参数是一个枚举类:ListNumberingType,这个类有以下几种值:

  • DECIMAL——列表符号是阿拉伯数字:1、2、3、4、5、…
  • ROMAN_LOWER——列表符号是小写罗马数字:i、ii、iii、iv、v、…
  • ROMAN_UPPER——列表符号为大写罗马数字:I、II、III、IV、V、…
  • ENGLISH_LOWER —— 列表符号是小写字母(使用英文字母):a、b、c、d、e、…
  • ENGLISH_UPPER ——列表符号是大写字母(使用英文字母):A、B、C、D、E、…
  • GREEK_LOWER ——列表符号为小写希腊字母:α, β, γ, δ, ε,…
  • GREEK_UPPER ——列表符号为大写希腊字母:Α、Β、Γ、Δ、Ε、…
  • ZAPF_DINGBATS_1 ——列表符号是来自 Zapfdingbats 字体的项目符号,更具体地说是 [172; 181]范围内的字符。
  • ZAPF_DINGBATS_2——列表符号是 Zapfdingbats 字体的项目符号,更具体地说是 [182;191]范围内的字符。
  • ZAPF_DINGBATS_3 ——列表符号是来自 Zapfdingbats 字体的项目符号,更具体地说是 [192; 201]范围内的字符。
  • ZAPF_DINGBATS_4 ——列表符号是来自 Zapfdingbats 字体的项目符号,更具体地说是 [202; 221]范围内的字符。

  显然,我们也可以定义自己的自定义列表符号,或者我们可以使用默认列表符号(例如数字)的组合并将它们与前缀或后缀组合。 效果如图 4.9 所示。

图4.9 自定义列表符号

  首先我们看一下如何引入一个简单的项目符号作为列表符号,而不是默认的连字符。代码片段如下:

List list = new List();
list.setListSymbol("\u2022");
list.add("Dr. Jekyll");
list.add("Mr. Hyde");
document.add(list);

  我们创建一个 List 并使用 setListSymbol() 方法来更改列表符号,其中可以使用任何String作为列表符号。在我们的例子中,我们想要一个像子弹一样的原点,对应的Unicode值为/u2022。 但是注意到子弹符号与列表项的内容非常接近。 做到这样可以通过使用 setSymbolIndent() 方法定义缩进来改变这一点,就像下面例子里面一样:

list = new List();
PdfFont font = PdfFontFactory.createFont(FontConstants.ZAPFDINGBATS);
list.setListSymbol(new Text("*").setFont(font).setFontColor(Color.ORANGE));
list.setSymbolIndent(10);
list.add("Dr. Jekyll");
list.add("Mr. Hyde");
document.add(list);

  在这里我们将列表符号设置为*,但使用的是Text对象而不是String。 然后将字体设置为 ZapfDingbats。最后还将字体颜色更改为橙色。 最终展示的效果会产生一个看起来像橙色手指的列表符号。 在下一个片段中,我们使用Image对象作为列表符号:

Image info = new Image(ImageDataFactory.create(INFO));
info.scaleAbsolute(12, 12);
list = new List().setSymbolIndent(3);
list.setListSymbol(info);
list.add("Dr. Jekyll");
list.add("Mr. Hyde");
document.add(list);

  第1行创建了一个Image对象,其中INFO指向了蓝色信息提示的图片。然后缩放图像,使其尺寸为 12 x 12 个用户单位,并将图像作为setListSymbol()方法的参数传递。

  在默认列表类型中,iText 总是在编号列表的列表符号后添加一个点:a.、b.、c. 等。 有的时候我们不想要这个点或者用其他符号替换“.”,例如:a-、b-、c- 等等。 下面的代码片段展示了如何实现这一点:

list = new List();
list.setListSymbol(ListNumberingType.ENGLISH_LOWER);
list.setPostSymbolText("- ");
list.add("Dr. Jekyll");
list.add("Mr. Hyde");
document.add(list);

  第 1 行和第 2 行相当于list = new List(ListNumberingType.ENGLISH_LOWER); 它会生成一个使用英文字母的编号列表。 然后我们使用setPostSymbolText(方法将每个字母后自动添加的点替换为“-”。

  还有一个setPreSymbolText()方法可以在默认列表符号前面添加文本。 以下代码片段创建了一个十进制列表(1.、2.、3.、…),但通过添加前符号和后符号,列表符号已成为如下所示的列表标签:Part 1: ,Part 2:,Part 3 :,等等。

list = new List(ListNumberingType.DECIMAL);
list.setPreSymbolText("Part ");
list.setPostSymbolText(": ");
list.add("Dr. Jekyll");
list.add("Mr. Hyde");
document.add(list);

  并非每个编号列表都需要以 1、i、a 等开头。 你还可以使用setItemStartIndex()方法选择以更大的数字(或字母)开头。 在以下代码示例中,我们从 5 开始计数。

list = new List(ListNumberingType.DECIMAL);
list.setItemStartIndex(5);
list.add("Dr. Jekyll");
list.add("Mr. Hyde");
document.add(list);

  最后,我们将使用setListSymbolAlignment()来更改标签的对齐方式。 例如图 4.8中的小写罗马数字列表与图 4.9 中的列表进行比较,你会发现列表标签的对齐方式有所不同。

list = new List(ListNumberingType.ROMAN_LOWER);
list.setListSymbolAlignment(ListSymbolAlignment.LEFT);
for (int i = 0; i < 6; i++) {list.add("Dr. Jekyll");list.add("Mr. Hyde");
}
document.add(list);

  到目前为止,我们一直使用字符串将列表项添加到列表中, 其实这些String 值在内部更改为ListItems

7. 将 ListItem 对象添加到List

  在第0章的类图里面,我们可以看到ListItem类是Div类的子类。和Div一样类似,我们可以在列表上下文里面添加不同对象。

  以本章第一个例子为引,我们不用Div而使用ListItems,展现的效果如图10所示

图4.10 List items

  代码和Div的例子很像:

public void createPdf(String dest) throws IOException {PdfDocument pdf = new PdfDocument(new PdfWriter(dest));Document document = new Document(pdf);List<List<String>> resultSet = CsvTo2DList.convert(SRC, "|");resultSet.remove(0);com.itextpdf.layout.element.List list =new com.itextpdf.layout.element.List(ListNumberingType.DECIMAL);for (List record : resultSet) {ListItem li = new ListItem();li.setKeepTogether(true);String url = String.format("https://www.imdb.com/title/tt%s", record.get(0));Link movie = new Link(record.get(2), PdfAction.createURI(url));li.add(new Paragraph(movie.setFontSize(14))).add(new Paragraph(String.format("Directed by %s (%s, %s)",record.get(3), record.get(4), record.get(1))));File file = new File(String.format("src/main/resources/img/%s.jpg", record.get(0)));if (file.exists()) {Image img = new Image(ImageDataFactory.create(file.getPath()));img.scaleToFit(10000, 120);li.add(img);}list.add(li);}document.add(list);document.close();
}

  先解析数据到java.util.List(行4),然后声明了全路径com.itextpdf.layout.element.List来避免编译器混淆(行6),并创建了数字列表(行7)。接着为java.util.List中的每个项目创建一个ListItem(行9)。然后添加Paragraphs和Image(如果有的话)添加到这个ListItem中(行11-24)。最后我们把每个ListItem添加到List中(行25),最终把这个List添加到Document中。

8. 嵌套列表

  本章最后一个例子是创建嵌套列表,展示的效果如图4.11所示:

图4.11 嵌套列表

  这个例子里面变量很难多,看起来比较绕,又复杂,所以我们简单拆成几部分来讲。首先我们从一个常规列表开始,变量名称为list

List list = new List();

  我们创建一个数字列表list1(行1)。这个列表有两个ListItems,liEL(行5)和liEU(行11)。同时对应这两个ListItem创建对应的ListlistEL(行2,小写英文字母)和listEU(行8,大写英文字母),并为每个List添加列表元素“Dr. Jekyll”和“Mr. Hyde”。(行3-4,行9-10)。

List list1 = new List(ListNumberingType.DECIMAL);
List listEL = new List(ListNumberingType.ENGLISH_LOWER);
listEL.add("Dr. Jekyll");
listEL.add("Mr. Hyde");
ListItem liEL = new ListItem();
liEL.add(listEL);
list1.add(liEL);
List listEU = new List(ListNumberingType.ENGLISH_UPPER);
listEU.add("Dr. Jekyll");
listEU.add("Mr. Hyde");
ListItem liEU = new ListItem();
liUL.add(listEU);
list1.add(liEU);
ListItem li1 = new ListItem();
li1.add(list1);
list.add(li1);

  对应图4.11所示,首先是分割符,然后可以看见数字列表符号1. 和 2. ,同时内部嵌套了两个使用小写英文字母的列表元素的列表。

  在下一个代码片段,我们创建了另一个属于listListItem,对应变量名称为li(行1)。然后我们往这个ListItem添加4个列表:listGL(行2),listGU(行6),listRL(行10)和listRU(行14)。这些列表(希腊字母小写,希腊字母大写,罗马数字小写,罗马数字大写)依次添加到默认到具有默认列表符号的列表项。

ListItem li = new ListItem();
List listGL = new List(ListNumberingType.GREEK_LOWER);
listGL.add("Dr. Jekyll");
listGL.add("Mr. Hyde");
li.add(listGL);
List listGU = new List(ListNumberingType.GREEK_UPPER);
listGU.add("Dr. Jekyll");
listGU.add("Mr. Hyde");
li.add(listGU);
List listRL = new List(ListNumberingType.ROMAN_LOWER);
listRL.add("Dr. Jekyll");
listRL.add("Mr. Hyde");
li.add(listRL);
List listRU = new List(ListNumberingType.ROMAN_UPPER);
listRU.add("Dr. Jekyll");
listRU.add("Mr. Hyde");
li.add(listRU);
list.add(li);

  此外,我们创建了一个带有编号的ZapfDingbats项目符号列表(listZ1),并把这个列表添加到列表元素(变量名为liZ1):

List listZ1 = new List(ListNumberingType.ZAPF_DINGBATS_1);
listZ1.add("Dr. Jekyll");
listZ1.add("Mr. Hyde");
ListItem liZ1 = new ListItem();
liZ1.add(listZ1);

  接着创建第二个带有不同ZapfDingbats项目符号的列表(listZ2),并把这个列表添加到列表元素(变量名为liZ2):

List listZ2 = new List(ListNumberingType.ZAPF_DINGBATS_2);
listZ2.add("Dr. Jekyll");
listZ2.add("Mr. Hyde");
ListItem liZ2 = new ListItem();
liZ2.add(listZ2);

  接着创建第三个带有不同ZapfDingbats项目符号的列表(listZ3),并把这个列表添加到列表元素(变量名为liZ3):

List listZ3 = new List(ListNumberingType.ZAPF_DINGBATS_3);
listZ3.add("Dr. Jekyll");
listZ3.add("Mr. Hyde");
ListItem liZ3 = new ListItem();
liZ3.add(listZ3);

  最后创建第四个带有不同ZapfDingbats项目符号的列表(listZ4),并把这个列表添加到列表元素(变量名为liZ4):

List listZ4 = new List(ListNumberingType.ZAPF_DINGBATS_4);
listZ4.add("Dr. Jekyll");
listZ4.add("Mr. Hyde");
ListItem liZ4 = new ListItem();
liZ4.add(listZ4);

  接着我们做如下的事情来完成嵌套:

  • liZ4添加到listZ3,其中listZ3已经被添加到liZ3
  • liZ3添加到listZ2,其中listZ2已经被添加到liZ2
  • liZ2添加到listZ1,其中listZ1已经被添加到liZ1
  • liZ1添加到list,也就是一开始我们创建的默认分隔符的列表;

  最后我们把list添加到Document

listZ3.add(liZ4);
listZ2.add(liZ3);
listZ1.add(liZ2);
list.add(liZ1);
document.add(list);

  最终的效果如上图所属,列表的某个元素可以为列表。

9. 总结

  在本章中,我们讨论了构建块DivLineSeparatorParagraphListListItem。 我们使用Div对其他构建块进行分组,使用LineSeparator绘制水平线。 我们解决了第 2 章示例中可能遇到的问题:如何将特定元素放在通一个页面上。 同时我们没有详细介绍IRenderer实现,但是罗列一个示例——更改了段落绘制背景的方式。 为此创建了一个自定义的ParagraphRenderer来实现这一点。 最后,我们创建了一些List示例来演示不同类型的列表(编号、未编号、直截了当方式添加、嵌套添加等)。

  下一章我们把目光聚集在表格,具体就是 TableCell 类。

iText7高级教程之构建基础块源码下载-CSDN

本章代码资源下载地址:

  1. 关注我的微信公众号CuteXiaoKe,点击代码资源-iText官网代码即可
  2. 或者直接点击微信文章

iText7高级教程之构建基础块——4.使用AbstractElement对象(part 1)相关推荐

  1. iText7高级教程之构建基础块——2.添加内容到Canvas或Document

    作者:CuteXiaoKe 微信公众号:CuteXiaoKe 在这一章中,我们通过添加BlockElement和Image对象添加到RootElement实例的方式来创建PDF文档.RootEleme ...

  2. iText7高级教程之构建基础块——1.引入字体

    作者:CuteXiaoKe 微信公众号:CuteXiaoKe 本章我们开始讲述一些使用不同字体展示标题和作者的例子,在这里会引入一些类,例如FontProgram和PdfFont. 本章内容偏长,请耐 ...

  3. iText7高级教程之构建基础块——1.引入字体实践

    本章的例子,请参考我翻译的博文:iText7高级教程之构建基础块--1.引入字体,里面有详细的解释,有什么不懂得也可以评论或者私信我! 例子1:创建不嵌入的三种字体的文档 三种不同的字体来创建带有标题 ...

  4. iText7高级教程之构建基础块——7.处理事件,设置阅读器首选项和打印属性

    作者:CuteXiaoKe 微信公众号:CuteXiaoKe   整个教程从关于字体的第一章开始.在随后的章节中,我们讨论了每个元素的默认行为,这些元素为:Paragraph.Text.Image等. ...

  5. iText7高级教程之构建基础块——3.使用ILeafElement实现类

    作者:CuteXiaoKe 微信公众号:CuteXiaoKe   ElementPropertyContainer 类有三个子类:Style.RootElement.和AbstractElement. ...

  6. Arduino 高级教程 01:基础篇

    我与 Arduino,以及为什么要写这个系列的文章 Arduino 这个已经火了好多年了,早就不是什么新鲜的技术.如果有人还不清楚 Arduino 是个什么东西,对不起,请自行搜索,随便翻开哪个维基百 ...

  7. DOS批处理高级教程(一) 批处理基础

    前言 批处理主要是用于脚本的编写, 是为了减少重复劳动力而建立的一个工具;. DOS批处理已经慢慢淡出大家的视线,在windowswindows中我们完全可以写shellshell, python s ...

  8. iText7高级教程之html2pdf——6.在pdfHTML中使用字体

    作者:CuteXiaoKe 微信公众号:CuteXiaoKe   到目前为止,我们还没有花太多的精力来研究将HTML转换为PDF时使用的字体.我们知道Helvetica是iText在没有指定字体时使用 ...

  9. iText7高级教程之html2pdf——1.从Hello HTML开始

    作者:CuteXiaoKe 微信公众号:CuteXiaoKe   在本章,我们通过不同的方法把一个简单的HTML文件转换为PDF文件.HTML文件的内容包含一个"TEST"标题,一 ...

最新文章

  1. 量子计算,后摩尔时代计算能力提升的解决方案
  2. 前端三十四:内嵌框架
  3. vc操作windows服务(services.msc)
  4. 从netty-example分析Netty组件
  5. 数据分析工具篇——数据读写
  6. 姚班天才少年鬲融凭非凸优化研究成果获得斯隆研究奖
  7. 学习Linux第一天
  8. LoadRunner常见错误代码解决方案
  9. Windows 7 7601.24291(2019版) 全补丁集成版ISO映像,编译于2018年11月10日
  10. c#将byte转为int_C# int转byte[],byte[]转int
  11. 数据结构系列,二叉平衡树的构建
  12. 求质数(素数)的方法
  13. 娱乐至死?不,我偏要活着
  14. (转载)从鼠尾草凋谢看中国花花世界的阴影(附EmilMatthew的评论)
  15. Jarvis oj level3
  16. 在API 中,常用的code码
  17. kali 2020 换源、中文支持
  18. 汉语自动分词研究评述
  19. 编程学习文档 参考资料和文档
  20. 背包问题(Knapsack Problem)

热门文章

  1. StarRocks开源——携手未来,星辰大海!
  2. 事业单位工作人员工资的构成
  3. 头孢替唑钠, Ceftezole sodium,CAS:41136-22-5,活性氧物种
  4. 华为的员工为什么愿意艰苦奋斗?
  5. 中华传统文化题材网页设计主题:基于HTML+CSS设计放飞青春梦想网页【学生网页设计作业源码】
  6. 10.14 brid
  7. VS 程序打包成一个独立的exe - Enigma Virtual Box
  8. TFN PM5100 高性能无线电综合测试仪 更大的测试范围
  9. 毕业设计 基于Stm32的蓝牙便携手环 - 物联网 单片机
  10. Java Math.log10()方法