`
wzucxd
  • 浏览: 26540 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

itext读写pdf的原理

 
阅读更多

之前介绍过使用ASM框架的一些基础api,里面会涉及.class的文件结构,字节码处理其实是修改.class文件的内容,关键内容在于对文件结构和字节码指令的了解

最近也阅读了些pdf相关的java框架,对其标准和原理做了些简单理解,于是以同样的方式,根据itext这个框架来解读分享下pdf的文件结构以及读写pdf的一些原理

首先用itext5.3.4生成一个pdf文件作为学习案例,itext采用事件驱动的方式来设计,创建一个pdf文件其实也很容易,只需要走5步即可,具体可以参见代码示例(创建document对象->定义PdfWriter->打开document->为document添加内容->关闭document)

public classHeaderFooter_ {
 
   public static voidmain(String[] args) {
       try {
           //1.创建一个document
           Document document = new Document();
           
           //2.定义pdfWriter,指明文件输出流输出到一个文件
           PdfWriter.getInstance(document,newFileOutputStream("D:\\test.pdf"));
           
           //3.打开文档
           document.open();
           
           
           //字体
           Font font = new Font();
           font.setFamily("STSongStd-Light");
           //颜色
           font.setColor(BaseColor.BLUE);
           //4.添加内容
           Paragraph content = new Paragraph("xxx!",font);
           
           
           document.add(content);
           
           
           //添加段落
           for(int i=0;i<100;i++){
               document.add(new Paragraph("HelloWorld"+","+"Hello iText"+","+"HelloxDuan"));
           }
           
           //5.关闭
           document.close();
           
       } catch (FileNotFoundException e) {
           e.printStackTrace();
       } catch (DocumentException e) {
           e.printStackTrace();
       }
}


其实作为合格的码仔,学习使用api的速度那是非常快的,基本上很容易上手,基本一看就了解生成了有3页内容的pdf文件,所以使用api不是本次分析的目的,我们会更关注api的背后有什么东西。

好,现在我们来看下生成的test.pdf文件具体是以什么方式存放的,先写一段测试代码,以2进制方式读出pdf内容。

       FileInputStream in = newFileInputStream(new File("D:\\test.pdf"));
       int xx = in.read();
       while(xx!=-1){
           System.out.print((char)xx);
           xx = in.read();
          }

输出结果:



到此基本可以看出,pdf二进制内容大部分能直接读懂,这里要提下pdf文件是支持7位ASCⅡ码和二进制码这两种编码方式的.

从这个文件中,我们已经知道在pdf标准上将这个文件结构进行了分类,如下图,分为4部分:文件头、文件体、交叉引用表、文件尾

pdf文件内容的逻辑组织结构,也很好的反映了文件体中各间接对象间的等级层次关系。PDF的文档结构是一种树型结构,如图二所示。树的根节点也就是PDF文件的根对象,根节点下面有四个子树:页面树(Pages Tree)、书签树(Outline Tree)、线索树(ArticleThreads)和名字树(NamedDestination)。

下面就来一一解读pdf里的意义。

文件头:

第一行内容为“%PDF-1.4\r” -->第0-4位为固定值标识文件类型 %PDF或者%FDF等,第6-8为则是主次版本号

第二行的内容为:%226 227 207 211 \r,将这串2进制值转为16进制得到E2 E3 CF D3,这个是GBK编码“忏嫌”的中文,意思是容忍不满意,作者选择这个能够理解pdf的这种设计。另外文件头中的%还有另外一个含义,那就是注释,这是因为PDF其实是PostScript的缩略版,也是程序,可以自己编程来完成绘图、制作pdf;(注:PostScript是一种描述打印机如何在页面上绘图的语言,每个PostScript文件也可以称为一个矢量图片集,虽然它其实更像程序而不是图片)。

文件体

再下面,我们可以看到如下内容

4 0 obj

<</Parent3 0 R/Contents 2 0 R/Type/Page/Resources<</ProcSet [/PDF /Text /ImageB/ImageC /ImageI]/Font<</F1 1 0 R>>>>/MediaBox[0 0 595842]>>

endobj

6 0 obj

<</Parent3 0 R/Contents 5 0 R/Type/Page/Resources<</ProcSet [/PDF /Text /ImageB/ImageC /ImageI]/Font<</F1 1 0 R>>>>/MediaBox[0 0 595842]>>

endobj

8 0 obj

<</Parent3 0 R/Contents 7 0 R/Type/Page/Resources<</ProcSet [/PDF /Text /ImageB/ImageC /ImageI]/Font<</F1 1 0 R>>>>/MediaBox[0 0 595842]>>

endobj

3 0 obj

<</Type/Pages/Count3/Kids[4 0 R 6 0 R 8 0 R]>>

endobj

文件体里面由若干个的obj对象来组成。

第一个数字称为对象号,来唯一标识一个对象的,第二个是产生号,是用来表明它在被创建后的第几次修改,所有新创建的PDF文件的对象号应该都是0,即第一次被创建以后没有被修改过。上面的例子就标识了该对象的对象号是4,而且创建后没有被修改过。

包含在<<和>>之间的内容就是对象内容,最后以关键字endobj结束.

对象内容中主要为PostScript,在pdf中共有60个页面描述指令,这60个页面描述指令描述了页面上的一系列的图形对象。这些图形对象大致可以分为四类,即路径对象(Path Object)、文本对象(Text Object)、图像对象(Image Object)和外部对象。

上面出现的这种代码(/Parent3 0 R)称为过程函数:过程函数是自定义新指令的方法,由一系列被“{”和“}”括起来的指令构成。例如求x的平方的过程为先复制一个x,再把两个x乘起来,即“{dupmul}”。于是该过程的定义指令为“/square {dup mul} def”。def是一个特殊的指令,可以把名称和数值或者过程函数结合起来并压入字典堆栈。基本格式为

/名称 取值 def

该指令用于定义变量和过程函数。当“取值”是一个过程函数,并且里面用到一些别的指令的时候,可以用

/名称 过程函数bind def

所以/Parent3 0 R表示意思是他的父路径是3 0 obj,从/Kids[40 R 6 0 R 8 0 R]中得知3 0 obj对象有40 obj、6 0 obj、8 0 obj 这3个叶子。即页面树与页面的关系

其实过程函数只是做定义描述,并没与做什么其他事情。

这里我们还能看到/MediaBox[00 595 842]这个内容,这里的数字涉及到基本图形概念:

1.设备坐标:由打印机维护。

2.用户坐标:程序使用的与设备无关的坐标。左下角是(0,0),坐标取值为实数。

当前变换矩阵:用户坐标到设备坐标的变换矩阵,用来实现PostScript的一些更精妙的东西。变换矩阵的一般用法为[a b c d u v]concat,执行后可以实现用户空间的坐标变换

x'=ax+cy+u,y'=bx+dy+v.

于是这里对应程序的像素坐标,即需要写到文件里的文本内容图像的坐标位置在x'=595,y'=842处。

用“[”和“]”括起来的表示序列。序列相当于java程序语言里的数组,但是各分量可以是不同类型的对象。注意程序在读序列时会执行其中遇到的指令(而不像过程函数里那样不操作),因此“[1 2 add]”表示的不是一个有三个对象的序列,而是只含唯一的一个分量3(1和2相加的结果)。

更多的这种指令内容可以参考PostScript的着色模型(Imaging Model),pdf采用和PS一样的方式来表现文字和图形,与PS语言一样,PDF的页面描述指令也是通过将选定的区域着色来绘制页面的。着色的区域可以是字母等的轮廓、直线和曲线定义的区域以及位图,着色的颜色可以是任意的,页面上的任何图形都可以被裁剪成其他形状。页面开始时是全空的,各种指令将不同的图形绘制到页面上,并且新的图形是不透明的,它可以覆盖旧的图形。

交叉引用表

  从例子的内容中可以看到xref开头的一部分类似内存地址索引的代码,其实到这个位置的内容被称为交叉引用表,用来索引各个obj 对象在文档中的位置,以实现随机访问,形式是:

xref

0 11

000000000065535 f

000000098700000 n -->1 0 obj

000000001500000 n -->2 0 obj

000000107500000 n -->3 0 obj

000000019700000 n -->4 0 obj

000000035400000 n -->5 0 obj

000000051900000 n -->6 0 obj

000000067600000 n -->7 0 obj

000000083000000 n -->8 0 obj

000000113800000 n -->9 0 obj

000000118300000 n -->10 0 obj

  xref说明一个交叉引用表的开始,交叉引用表的第一行0 11 说明下面各行所描述的对象号是从0开始,并且有11个对象。

  000000000065535 f,一般每个PDF文件都是以这一行开始交叉应用表的,说明对象0的起始地址为0000000000,产生号(generation number)为65535,也是最大产生号,不可以再进行更改,而且最后对象的表示是f, 表明该对象为free, 这里,大家可以看到,其实这个对象可以看作是文件头。

  000000098700000 n就是表示对象1,0000000987是其偏移地址,00000为5位产生号(最大为65535,0表明该对象未被修改过),n表示该对象在使用,区别与自由对象(f),这个对象可以更改。

文件尾

trailer

<</Root9 0 R/ID[<4fd0598dbf45d0a583b47b578d37e0fb><de82f58f5ac6cee2dd50e9ddb9165017>]/Info10 0 R/Size 11>>

%iText-5.3.4

startxref

1337

%%EOF

  trailer 说明文件尾对象的开始,可以认为是文件尾部分的头文件标识。

  /Size 11说明该PDF文件的对象数有11个。

  /Root 90 R说明根对象的对象号为9。

/Info 10 0 R说明文件的描述信息的对象号在10。

%iText-5.3.4 是itext库插入的注释,无意义

  startxref

  1337 说明交叉引用表的偏移地址,从而可以找到PDF文档中所有的对象的相对地址,进而访问对象。

  %%EOF为文件结束标志。也是注释。

其中10 0 obj包含了文件的作者、创建时间等一系列的文件属性信息。如下:

10 0 obj

<</Producer(iText?5.3.4 ?2000-2012 1T3XT BVBA\(AGPL-version\))/ModDate(D:20121204135101+08'00')/CreationDate(D:20121204135101+08'00')>>

endobj

到此,已经差不多解析完了整个pdf文件结构,我们也可以开始尝试来写一个pdf读写的功能,对pdf做些修改。

比如1.6版本的pdf文件无法在Acrobat 5.0版本的pdf阅读器中打开等版本不兼容情况,这时候就可以通过修改pdf的文件结构来修复pdf,方法:

首先用UE或者notepad++等打开该pdf文件,第一行“%PDF-1.6%忏嫌”修改成“%PDF-1.5%忏嫌”,然后进行保存关闭;再用Acrobat6.0 完全版打开该pdf文件,找到Advanced菜单下PDF Optimizer后,Compatible with下拉框中选择“Acrobat 5.0 and later”,然后点击“OK”,进行另存即可。

分享到:
评论

相关推荐

    itext根据模板导出pdf.zip

    通过IText,开发者可以轻松地进行PDF文档的读写操作,实现高度自定义的PDF生成需求。 **2. PDF模板基础** 模板在PDF生成中起到了蓝图的作用,它定义了文档的布局、样式和内容占位符。在IText中,可以通过创建...

    [iText实战(第2版)].(iText.in.Action).Bruno.Lo

    1. **PDF基础**:了解PDF格式的基本结构和元素,包括页面、字体、图像、链接等,并理解PDF文档的读写原理。 2. **iText入门**:掌握如何引入iText库到Java项目中,以及如何创建一个基本的PDF文档。 3. **文本操作*...

    有關對pdf操作的itext文檔

    本文档将详细介绍如何利用iText进行PDF文档的操作,例如文档的读写、页面设置等功能。 #### PDF文档的基础操作 - **文档的创建与读取**:iText提供了丰富的API来帮助用户创建PDF文档。在文档创建的过程中,可以...

    java pdf 实现电子签章下载

    在这个场景中,我们关注的是使用iTextPDF和PDFBox两个流行的Java库来完成电子签章的功能。这两个库提供了丰富的API,可以方便地对PDF进行读写、编辑和安全操作,包括添加电子签章。 首先,iTextPDF是一个强大的开源...

    java读写word、excle、pdf驱动

    - **iText**:这是一个强大的开源库,用于读写PDF文件。你可以创建`Document`对象,然后使用`PdfWriter`将其写入PDF文件,或者使用`PdfReader`和`PdfStamper`来读取和修改现有PDF。 - **Apache PDFBox**:Apache的...

    java 多个pdf合并,目录生成(支持自定义目录),页码生成(源码)

    而Apache PDFBox则是Apache软件基金会的一个开源项目,同样提供对PDF文档的读写功能。 目录生成是通过分析PDF文档中的标题信息实现的。如果PDF文件包含元数据,这些标题可以从PDF的结构中提取出来。如果没有元数据...

    html生成pdf文件

    它可以添加页眉、页脚、水印、数字签名等高级功能,同时提供了对PDF文档的读写操作,使我们可以对生成的PDF进行编辑和更新。`iText`还支持表格、列表、段落和字体设置,确保PDF内容的可读性和专业性。 在实际应用中...

    PDF合成代码实例

    在处理大文件时,可以使用`readObject`和`writeObject`按需读写PDF对象。 6. 错误处理与优化:合成过程中可能遇到的问题包括文件损坏、权限错误、页面顺序混乱等。需要添加适当的错误处理机制,如异常捕获,以确保...

    java实用组件集 源码 pdf组件

    Java 实用组件集是Java开发中的一类重要资源,它包含了一系列用于简化开发、提高效率的工具类和库。在本资源中,我们重点关注的...同时,理解源码可以帮助深入理解PDF生成的原理,对于进行二次开发或定制功能非常有益。

    java8源码-BlogDemo:我的csdn博客中使用的代码,主要是算法

    java8 源码 BlogDemo csdn blog: 包括博客中的算法、游戏源码等。...使用itext读写pdf示例 pdf 20160705 反射原理及示例 reflect 20160710 多线程使用示例 thread 20160711 使用dom4j读写xml示例 xml

    Word 转Pdf 代码及jar包

    Apache POI是用于读写Microsoft Office格式文件的API,而iText则是一个用于创建和修改Pdf文档的库。 Apache POI允许开发者读取、创建和修改Word文档,它解析Word文件的内部结构并提供API来访问内容。对于转换任务,...

    java生成pdf文档的总结和相关代码,javagenaratepdf.zip

    通过学习以上内容,你应能理解Java中生成PDF的基本原理和实践步骤。结合"pdfDemo-master"的代码,可以更深入地了解实际开发中的应用场景。在实际项目中,根据需求选择合适的库和方法,灵活运用,就能生成满足需求的...

    JAVA导出到excel、PDF的源代码

    Apache POI是一个用于处理Microsoft Office格式(如Excel)的Java API,而iText则是一个用于创建和修改PDF文档的库。 首先,让我们深入了解一下Apache POI。Apache POI提供了HSSF和XSSF接口,分别用于读写旧版的...

    poi将word、PPT、Excel转pdf实现在线预览的jar包

    要将这些文档转换为PDF,通常需要额外的库,如iText或Apache PDFBox,因为Apache POI本身并不直接支持此功能。转换过程可能涉及读取Office文档,解析其内容,然后使用PDF库重新构建PDF文件。这可能包括将文本、图像...

    java操作Pdf文档详情

    Apache PDFBox是一个开源的Java库,它提供了丰富的API用于读写PDF文档。你可以通过Maven或Gradle将其添加到项目依赖中。例如,在Maven的pom.xml文件中添加如下依赖: ```xml &lt;groupId&gt;org.apache.pdfbox ...

    office转PDF

    虽然它的主要功能是生成PDF,但通过与其他工具(如Flying Saucer或iText)结合,可以实现Office到PDF的转换。例如,先用Apache POI读取Office文件,然后使用Flying Saucer将其渲染为HTML,最后用PDFBox将HTML转换为...

    java对word、excel、pdf等操作.doc

    iText 库是一个流行的 Java 库,提供了对 PDF 文件格式的读写支持。使用 iText 库,可以实现生成、编辑 PDF 文档等操作。 在使用 iText 库时,我们需要首先添加依赖项,然后创建一个 Document 对象,该对象用于操作...

    Java Office PDF

    iText主要用于创建、修改和解析PDF文档,而PDFBox是Apache的一个项目,提供PDF文档的读写功能,可以用于添加文本、图像、签名等。 4. **文件I/O操作**:在处理文档时,Java的`java.io`包是不可或缺的,它提供了流和...

    JAVA将PDF转图片代码包含test和jar 包

    1. **PDF处理库**:这个项目可能使用了如PDFBox、iText或者Apache PDFRenderer等开源库来读取和处理PDF文档。PDFRenderer是Apache PDFBox的一个子项目,专门用于将PDF页面渲染成图像。它提供了Java API,使得开发者...

    word转pdf,pdf转jpg.rar

    通过POI,我们可以解析Word文档的内容,然后利用其他库如iText或PDFBox将其导出为PDF格式。然而,根据描述,这里使用的是WPS,一个与Microsoft Office兼容的办公软件,可能是因为它提供了一种更直接的转换方式。WPS...

Global site tag (gtag.js) - Google Analytics