文章18 | 阅读 10890 | 点赞0
在1-4章中,我们使用了iText7来创建PDF文档。在5-6章中,我们操作和重用了现有的PDF文档。在这些章节中我们操作的PDF文档都是在ISO 32000规范下的,是PDF文件的核心标准。ISO 32000并不是PDF的唯一ISO标准,还有很多为了特定原因创建的子标准。在本章中,我们着重关注两个:
在本章中,我们通过创建一系列的PDF/A和PDF//UA文件会学习PDF/A和PDF/UA相关的知识。
在我们开始PDF/UA例子之前,我们来看一下我们要解决的问题。在第1章,我们已经创建了带有图片的文档,在句子"Quick brown fox jumps over the lazy dog"中,我们把"dag"和"fox"替换为相应的图片,当这个文件被读入的时候,一个机器不能知道第一张图片代码一个fox,第二张图片代表dog,因此这个文件会被认为:“Quick brown jumps over the lazy”。
在一个普通的PDF中,内容会被画入画布(canvas)中。我们可能会使用高级的对象,例如List
和Table
,但是一旦PDF被创建,这些对象不会保存。一个List
是一系列行组成的,但是在list元素中一个文本片段并不知道它是list的一部分。一个Table
由一群先和特定位置的文本组成,同样的,一个文本片段并不知道它属于特定行和列。
除非我们让一个PDF变成加带标签的PDF,否则这个文档不会包含任何的语义上的结构。当一个文档没有语义结构存储的时候,我们就说这个PDF无法感知/理解(isn’t accessible)。为了可感知/理解,这个文档需要能够能够区分一个页面上哪些部分是真实的内容,哪些部分不是真实的内容(例如页眉,页码),一行文本如果不是paragraph
的一部分的话,需要知道自己是否是一个title
,当然还有其他一些要求。我们可以通过一种方式来添加所有的信息到一个页面,这个方式就是创建结构树(structure)
和把内容定义为带标签的内容
。这个可能听起来比较复杂,但是如果我们使用iText7的高级对象,我们可以高效的使用setTagged()
来达到这一目标。
通过定义PdfDocument
为带标签的文档,List
、Table
和Paragraph
等带结构的对象被引入后,会反映在带标签的PDF中。
当然这只为了PDF感知(accessible,是实在不知道翻译成啥比较好,就暂且翻译成感知吧)的其中一个要求,下面的代码可以帮我们理解其他的要求:
PdfDocument pdf = new PdfDocument(new PdfWriter(dest, new WriterProperties().addXmpMetadata()));
Document document = new Document(pdf);
//Setting some required parameters
pdf.setTagged();
pdf.getCatalog().setLang(new PdfString("en-US"));
pdf.getCatalog().setViewerPreferences(
new PdfViewerPreferences().setDisplayDocTitle(true));
PdfDocumentInfo info = pdf.getDocumentInfo();
info.setTitle("iText7 PDF/UA example");
//Fonts need to be embedded
PdfFont font = PdfFontFactory.createFont(FONT, PdfEncodings.WINANSI, true);
Paragraph p = new Paragraph();
p.setFont(font);
p.add(new Text("The quick brown "));
Image foxImage = new Image(ImageFactory.getImage(FOX));
//PDF/UA: Set alt text
foxImage.getAccessibilityProperties().setAlternateDescription("Fox");
p.add(foxImage);
p.add(" jumps over the lazy ");
Image dogImage = new Image(ImageFactory.getImage(DOG));
//PDF/UA: Set alt text
dogImage.getAccessibilityProperties().setAlternateDescription("Dog");
p.add(dogImage);
document.add(p);
document.close();
创建一个PdfDocument
和Document
,但是这次我们使用WriterProperties
的addXmpMetadata()
来自动添加XMP元数据。在PDF/UA中,必须在PDF中以XML格式存储相同的元数据。XML可能不是压缩的。不熟悉PDF内容格式的处理者/处理程序必须能够探测这个XMP元数据并能正确处理它。一个XMP数据流会在Info字典(Info dictionary)条目中自动创建。这个Info字典是一个PDF对象,它包含诸如文档标题之类的数据。除了添加XMP数据流以后,我们还需求进行以下操作来使之符合PDF/UA标准:
PdfDocument
设置为带标签的(行4)现在我们已经完成了创建PDF/UA的工作。结果如下两图1和图2所示,可能与之前的差别并不是很明显,但是如果我们打开Tags版面(一定要用Adobe Acrobat Pro,用Adobe Acrobat Reader DC不行的哟):
图1. 一个PDF/UA文档和它的结构
图2. ctrl+d文档属性
我们可以看到<Document>
标签里面有<P>
标签,<P>
标签由两个<Span>
和两个<Figures>
组成。我们会在这章的后面创建更加复杂的PDF/UA文档,现在我们先来看看PDF/A怎么创建。
ISO 19005的Part 1是在2005年发布的。它在Adobe PDF 1.4声明官方中被定义(那时候这份声明并不是ISO标准)。SO 19005-1引入了一系列的义务和限制:
SO 19005-1:2005 (PDF/A-1)定义了两种符合性级别:
下面的代码展示了如何把我们之前创建的"Quick brown fox"的PDF变成符合PDF/A-1b标准:
//Initialize PDFA document with output intent
PdfADocument pdf = new PdfADocument(new PdfWriter(dest),
PdfAConformanceLevel.PDF_A_1B,
new PdfOutputIntent("Custom", "", "http://www.color.org",
"sRGB IEC61966-2.1", new FileInputStream(INTENT)));
Document document = new Document(pdf);
//Fonts need to be embedded
PdfFont font = PdfFontFactory.createFont(FONT, PdfEncodings.WINANSI, true);
Paragraph p = new Paragraph();
p.setFont(font);
p.add(new Text("The quick brown "));
Image foxImage = new Image(ImageFactory.getImage(FOX));
p.add(foxImage);
p.add(" jumps over the lazy ");
Image dogImage = new Image(ImageFactory.getImage(DOG));
p.add(dogImage);
document.add(p);
document.close();
我们可以看到,我们不再使用PdfDocument
实例,相反,我们使用的是PdfADocument
实例。首先我们创建了一个PdfADocument
实例,PdfADocument
实例构造函数第一个参数是一个PdfWriter
,第二个参数是符合性级别(在这里就是PdfAConformanceLevel.PDF_A_1B
),第三个参数是一个PdfOutpuyIntext
,这个输出意图告诉文档如何解读这个文档里面存储的颜色。在第10行,我们确保字体被嵌入。
产生的PDF的样子如下图3:
图3. 一篇PDF/A-1B标准的文档
由上图我们可以看见一个带有"这个文件符合PDF/A标准规范,且已在只读模式下打开以防被修改"的小蓝条。对此我们从两个方法来解读这句话:
然后我们来看看怎么创建PDF/A-1a,代码如下:
//Initialize PDFA document with output intent
PdfADocument pdf = new PdfADocument(new PdfWriter(dest),
PdfAConformanceLevel.PDF_A_1A,
new PdfOutputIntent("Custom", "", "http://www.color.org",
"sRGB IEC61966-2.1", new FileInputStream(INTENT)));
Document document = new Document(pdf);
//Setting some required parameters
pdf.setTagged();
//Fonts need to be embedded
PdfFont font = PdfFontFactory.createFont(FONT, PdfEncodings.WINANSI, true);
Paragraph p = new Paragraph();
p.setFont(font);
p.add(new Text("The quick brown "));
Image foxImage = new Image(ImageFactory.getImage(FOX));
//Set alt text
foxImage.getAccessibilityProperties().setAlternateDescription("Fox");
p.add(foxImage);
p.add(" jumps over the lazy ");
Image dogImage = new Image(ImageFactory.getImage(DOG));
//Set alt text
dogImage.getAccessibilityProperties().setAlternateDescription("Dog");
p.add(dogImage);
document.add(p);
document.close();
让我们来解读代码,在第3行中,我们把PdfConformanceLevel.PDF_A1B
变为了PdfConformanceLevel.PDF_A1A
。在第8行中,把这个PdfADocument
变成了带标签的PDF,然后加入了图片的文字描述信息,最后结果如下图4所示:
图4. 一篇PDF/A-1A标准文档
我们打开标准面板,可以看出Adobe Acrobat Pro把这个文件认为是PDF/A-1A和PDF/UA-1,但是这次并没有验证符合性链接,所以我需要借助于印前检查工具(英文版的是Preflight,晕,中文版的找了半天才找到,我在这里就分享给大家吧,估计大家都是用的中文,具体步骤为:打开工具里面的PDF标准→印前检查(或者直接左边点击打开印前检查)→找到PDF/A规范下面的PDF/A-1b规范→分析),如下图5:
图5. 印前检查工具查看验证符合性连接
我们继续看英文版的那张图,可以看出来结果是没有发现任何错误。我们无法验证PDF/UA符合性,因为PDF/UA涉及一些无法通过本地计算机验证的要求。例如:如果我们将狐狸形象的描述与狗的形象描述交换,机器就不会注意到。这将使文件无法访问,因为文件会根据屏幕阅读器向人们传播虚假信息。无论如何,只需知道我们创建文档不符合PDF/UA标准,因为我们省略了一些基本要素(如语言,第一个例子里面就同时设置了语言)。
从一开始就确定ISO 19005的认可部分永远不会失效。新的,后续的部分只会定义新的有用的功能。这些后续定义的就是我们即将介绍的PDF/A-2和PDF/A-3。
ISO 19005-2:2011(PDF/A-2)是根据ISO标准(而不是Adobe的PDF官方文档)被添加到PDF/A标准中的。PDF/A-2在PDF1.5,1.6.1.7中很多特性和提升:
PDF/A-2在符合性方面,除了原有的Level A和Level B以外,还定义了额外的level:
ISO 19005-3:2012 (PDF/A-3)几乎与PDF/A-2一毛一样。唯一的区别就是:在PDF/A-3中,附件不需要一定是PDF/A格式的。你可以把任何格式的文件当前是PFA/A-3的附件,例如可以把一个excel格式的文件当作是这个文档用到的结果,一个word格式文件用来创建一个PDF文档,等等。文档本身需要符合PDF/A规范的所有义务和限制,但这些义务和限制不适用于其附件。
在下面的例子中,我们会创建同时符合PDF/UA和PDF/A-3A标准,我们之所以会选择PDF/A-3,是因为要用到CSV文件来创建PDF,代码如下:
PdfADocument pdf = new PdfADocument(new PdfWriter(dest),
PdfAConformanceLevel.PDF_A_3A,
new PdfOutputIntent("Custom", "", "http://www.color.org",
"sRGB IEC61966-2.1", new FileInputStream(INTENT)));
Document document = new Document(pdf, PageSize.A4.rotate());
//Setting some required parameters
pdf.setTagged();
pdf.getCatalog().setLang(new PdfString("en-US"));
pdf.getCatalog().setViewerPreferences(
new PdfViewerPreferences().setDisplayDocTitle(true));
PdfDocumentInfo info = pdf.getDocumentInfo();
info.setTitle("iText7 PDF/A-3 example");
//Add attachment
PdfDictionary parameters = new PdfDictionary();
parameters.put(PdfName.ModDate, new PdfDate().getPdfObject());
PdfFileSpec fileSpec = PdfFileSpec.createEmbeddedFileSpec(
pdf, Files.readAllBytes(Paths.get(DATA)), "united_states.csv",
"united_states.csv", new PdfName("text/csv"), parameters,
PdfName.Data, false);
fileSpec.put(new PdfName("AFRelationship"), new PdfName("Data"));
pdf.addFileAttachment("united_states.csv", fileSpec);
PdfArray array = new PdfArray();
array.add(fileSpec.getPdfObject().getIndirectReference());
pdf.getCatalog().put(new PdfName("AF"), array);
//Embed fonts
PdfFont font = PdfFontFactory.createFont(FONT, true);
PdfFont bold = PdfFontFactory.createFont(BOLD_FONT, true);
// Create content
Table table = new Table(new float[]{4, 1, 3, 4, 3, 3, 3, 3, 1});
table.setWidthPercent(100);
BufferedReader br = new BufferedReader(new FileReader(DATA));
String line = br.readLine();
process(table, line, bold, true);
while ((line = br.readLine()) != null) {
process(table, line, font, false);
}
br.close();
document.add(table);
//Close document
document.close();
让我们逐行解释代码:
PdfADocument
(类型为PdfAConformanceLevel.PDF_A_3A)
)和Document
如下图6,我们可以看到我们用Table
和Cell
对象添加到文档里在标签面板里面,被保存了Table数据结构了,有点像HTML:
图6. 一篇PDF/A-3A标准的文档
同时,我们打开附件面板,我们可以看见CSV源文件,并且可以轻松提取出来,如下图7:
图7. 一篇PDF/A-3A标准文档和它的附件
通过上述的例子,与一般的PDF文件相比,我们创建符合PDF/UA或者PDF/A文档的时候需要添加另外的信息,*“我们是否能用iText改把现有的普通的PDF文档转换成符合PDF/UA或者PDF/A标准的文档呢?”*是在论坛和咨询里面问得最多的问题。我们希望通过这一章让大家明白iText是不能自动转换的,原因如下:
当然这只是不能自动转换的两个小原因。让一个PDF展示小蓝条说这个文档貌似符合PDF/A标准是很容易,但是并不是所有的声明都是正确的。
最后,我们来看看PDF/A文档的拼接。
当拼接PDF/A文件的时候,最值得我们注意的是,我们拼接的各个文档必须都是PDF/A文件,不能一个是PDF/A文件,一个是普通文件,而且PDF/A的Level也要一样,不能一个是A,一个是B,因为一个有结构树,一个没有,拼接在一起会导致结果错误。
我们把之前两个PDF/A A级的文档拼接起来,生成的文件如下图8所示:
图8. 拼接2篇PDF/A-A
通过标签面板我们看到一个<P>
,紧接着是<Table>
,如下代码展示了如何创建这个文档:
PdfADocument pdf = new PdfADocument(new PdfWriter(dest),
PdfAConformanceLevel.PDF_A_1A,
new PdfOutputIntent("Custom", "", "http://www.color.org",
"sRGB IEC61966-2.1", new FileInputStream(INTENT)));
//Setting some required parameters
pdf.setTagged();
pdf.getCatalog().setLang(new PdfString("en-US"));
pdf.getCatalog().setViewerPreferences(
new PdfViewerPreferences().setDisplayDocTitle(true));
PdfDocumentInfo info = pdf.getDocumentInfo();
info.setTitle("iText7 PDF/A-1a example");
//Create PdfMerger instance
PdfMerger merger = new PdfMerger(pdf);
//Add pages from the first document
PdfDocument firstSourcePdf = new PdfDocument(new PdfReader(SRC1));
merger.addPages(firstSourcePdf, 1, firstSourcePdf.getNumberOfPages());
//Add pages from the second pdf document
PdfDocument secondSourcePdf = new PdfDocument(new PdfReader(SRC2));
merger.addPages(secondSourcePdf, 1, secondSourcePdf.getNumberOfPages());
//Merge
merger.merge();
//Close the documents
firstSourcePdf.close();
secondSourcePdf.
整体上,这段代码可以说和之前的例子很像:
PdfMerger
的时候,我们传入的是PdfADocument
对象,之后往这个PdfMerger
对象添加的是PdfDocument
类型的,如果是PdfADocument
类型的话,会检查文档的合法性。关于PDF/UA和PDF/A标准还有很多讨论,当然还有其他子标准,例如在PDF/A-3中有一个德语发音的ZUGFeRD的标准,会在别的系列里面讲述(这个是官方文档里面说的,个人的话看需求喽,如果有时间我就开这个坑)
在本章,我们探讨了符合其他PDF标准的文档的创建和拼接,学会了创建PDF/UA和PDF/A的文档,本系列也就在此结束了,当然我们还需要一些其他的系列来深入的了解iText7。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/u012397189/article/details/78882454
内容来源于网络,如有侵权,请联系作者删除!