pdfreader 的主要部分
package com.itextpdf.text.pdf
public class PdfReader implements PdfViewerPreferences {
// 这个方法里面有很多对象
protected void readPdf() throws IOException {
try {
this.fileLength = this.tokens.getFile().length(); // 这个token 就是上文中的 PRTokeniser 对象,已读取了pdf文件,file 就是 RandomAccessFileOrArray 对象,此处调用了 RandomAccessFileOrArray.length() 方法,返回文件的bytes长度
this.pdfVersion = this.tokens.checkPdfHeader(); // 读取文件的前1024,确定pdf的版本
try {
this.readXref(); // 重点方法1
} catch (Exception var13) {
try {
this.rebuilt = true;
this.rebuildXref();
this.lastXref = -1;
} catch (Exception var12) {
throw new InvalidPdfException(MessageLocalization.getComposedMessage("rebuild.failed.1.original.message.2", new Object[]{var12.getMessage(), var13.getMessage()}));
}
}
try {
this.readDocObj(); // 重点方法2
} catch (Exception var14) {
if (var14 instanceof BadPasswordException) {
throw new BadPasswordException(var14.getMessage());
}
if (this.rebuilt || this.encryptionError) {
throw new InvalidPdfException(var14.getMessage());
}
this.rebuilt = true;
this.encrypted = false;
this.rebuildXref();
this.lastXref = -1;
this.readDocObj();
}
this.strings.clear();
this.readPages();
this.eliminateSharedStreams();
this.removeUnusedObjects();
} finally {
try {
this.tokens.close();
} catch (Exception var11) {
;
}
}
}
//
protected void readXref() throws IOException {
this.hybridXref = false;
this.newXrefType = false;
this.tokens.seek(this.tokens.getStartxref()); // getStartxref()获取pdf内容的开始位置,开始位置以“startxref“标识,并通过seek移动到指定位置
this.tokens.nextToken(); // 此处第一个需要精读的方法。这里可以先看后面的 nextToken 方法解读
if (!this.tokens.getStringValue().equals("startxref")) {
throw new InvalidPdfException(MessageLocalization.getComposedMessage("startxref.not.found", new Object[0]));
} else {
this.tokens.nextToken();
if (this.tokens.getTokenType() != TokenType.NUMBER) {
throw new InvalidPdfException(MessageLocalization.getComposedMessage("startxref.is.not.followed.by.a.number", new Object[0]));
} else {
int startxref = this.tokens.intValue();
this.lastXref = startxref;
this.eofPos = this.tokens.getFilePointer(); // 获取文件的偏移量
try {
if (this.readXRefStream(startxref)) { // 实际的读取对象 ,控制图形中的外部参照 的方法
this.newXrefType = true;
return;
}
} catch (Exception var4) {
;
}
this.xref = null;
this.tokens.seek(startxref);
this.trailer = this.readXrefSection();
PdfDictionary trailer2 = this.trailer;
while(true) {
PdfNumber prev = (PdfNumber)trailer2.get(PdfName.PREV);
if (prev == null) {
return;
}
this.tokens.seek(prev.intValue());
trailer2 = this.readXrefSection();
}
}
}
}
}
nextToken方法:
ch的判断:(数据类型)
37 % 注释 TokenType.COMMENT,则继续读取
40 ( 字符串 TokenType.STRING,则用定义 StringBuffer ,并用有限自动机算法读取文本内容
47 / 名字 TokenType.Name
60 < 类似json TokenType.START_DIC
62 > 类似json TokenType.END_DIC
91 [ 数组类 TokenType.START_ARRAY
93 ] 数组类 TokenType.END_ARRAY
否则 TokenType.OTHER
那说明这个方法是用来编译pdf数据类型的代码:
所以,认识一下pdf的数据类型
boolean(布尔型) :关键字为“true”和“false”
numberic(数值型) :integer(整数)和real(浮点型)
string(字符串型) : () 或 <> 。 其中,‘\’ 的用法包含:1.字符串分行 2.转义
name(PdfName类型,pdf中的一种特殊类型) :‘/’ 开头,不允许出现空白,区分大小写,其中的内容也可以使用’#’加2个16进制表示特殊符号。
例如:/Type, /Page /Colors
array(数组) :[],只支持一维数组,不过内部可以嵌套。
dictionary(字典) :《》 字典;key属性是name,value属性是任意的。一般都包含“Type”名字对象。
stream(数据流) :“stream”和“endstream”之间的数据块组成。所有的stream必须是indirect对象,而stream中的字典必须是direct对象。
包含属性:
Length(必选),数据块长度:整形
Filter,对数据块进行压缩:名字或数组
DecodeParms,压缩方式:字典或数组
F,stream数据块的内容将被保存在一个文件中:文件描述对象
FFilter, 与“Filter”的规则相同,它用来描述外部文件数据: 名字或数组
FDecodeParms, 与“DecodeParms”的规则相同,它用来描述外部文件数据: 字典或数组
DL, 非负整数,它表示解压缩之后的数据块长度: 整型
null(空类型) :类似与java中“null“,表示空值: 空对象
indirect(间接对象) :PDF中的任何对象都可以封装成一个间接引用对象; 该类型对象,由一个对象号(索引号),一个版本号,”obj“关键字,”endobj“关键字组成。
这一段的整体方法如下:
package com.itextpdf.text.pdf
class PRTokeniser{
public static final boolean[] delims = new boolean[]{true, true, false, false, false, .../*长度一共257*/};
public boolean nextToken() throws IOException {
boolean var1 = false;
int ch;
do {
ch = this.file.read(); // 读取第一个 char
} while(ch != -1 && isWhitespace(ch));
if (ch == -1) { // 如果 -1,则表示到达文件尾部
this.type = PRTokeniser.TokenType.ENDOFFILE;
return false;
} else {
StringBuffer outBuf;
outBuf = null;
this.stringValue = "";
int v1;
label222:
switch(ch) {
case 37:
this.type = PRTokeniser.TokenType.COMMENT;
while(true) {
ch = this.file.read();
if (ch == -1 || ch == 13 || ch == 10) {
break label222;
}
}
case 40:
outBuf = new StringBuffer();
this.type = PRTokeniser.TokenType.STRING;
this.hexString = false;
v1 = 0;
label150:
while(true) {
while(true) {
ch = this.file.read();
if (ch == -1) {
break label150;
}
if (ch == 40) {
++v1;
break;
}
if (ch == 41) {
--v1;
break;
}
if (ch != 92) {
if (ch == 13) {
ch = this.file.read();
if (ch < 0) {
break label150;
}
if (ch != 10) {
this.backOnePosition(ch);
ch = 10;
}
}
break;
}
boolean lineBreak = false;
ch = this.file.read();
switch(ch) {
case 10:
lineBreak = true;
break;
case 13:
lineBreak = true;
ch = this.file.read();
if (ch != 10) {
this.backOnePosition(ch);
}
case 40:
case 41:
case 92:
break;
case 98:
ch = 8;
break;
case 102:
ch = 12;
break;
case 110:
ch = 10;
break;
case 114:
ch = 13;
break;
case 116:
ch = 9;
break;
default:
if (ch >= 48 && ch <= 55) {
int octal = ch - 48;
ch = this.file.read();
if (ch >= 48 && ch <= 55) {
octal = (octal << 3) + ch - 48;
ch = this.file.read();
if (ch >= 48 && ch <= 55) {
octal = (octal << 3) + ch - 48;
ch = octal & 255;
} else {
this.backOnePosition(ch);
ch = octal;
}
} else {
this.backOnePosition(ch);
ch = octal;
}
}
}
if (!lineBreak) {
if (ch < 0) {
break label150;
}
break;
}
}
if (v1 == -1) {
break;
}
outBuf.append((char)ch);
}
if (ch == -1) {
this.throwError(MessageLocalization.getComposedMessage("error.reading.string", new Object[0]));
}
break;
case 47:
outBuf = new StringBuffer();
this.type = PRTokeniser.TokenType.NAME;
while(true) {
ch = this.file.read();
if (delims[ch + 1]) {
this.backOnePosition(ch);
break label222;
}
if (ch == 35) {
ch = (getHex(this.file.read()) << 4) + getHex(this.file.read());
}
outBuf.append((char)ch);
}
case 60:
v1 = this.file.read();
if (v1 == 60) {
this.type = PRTokeniser.TokenType.START_DIC;
} else {
outBuf = new StringBuffer();
this.type = PRTokeniser.TokenType.STRING;
this.hexString = true;
int v2 = 0;
while(true) {
while(isWhitespace(v1)) {
v1 = this.file.read();
}
if (v1 == 62) {
break;
}
v1 = getHex(v1);
if (v1 < 0) {
break;
}
for(v2 = this.file.read(); isWhitespace(v2); v2 = this.file.read()) {
;
}
if (v2 == 62) {
ch = v1 << 4;
outBuf.append((char)ch);
break;
}
v2 = getHex(v2);
if (v2 < 0) {
break;
}
ch = (v1 << 4) + v2;
outBuf.append((char)ch);
v1 = this.file.read();
}
if (v1 < 0 || v2 < 0) {
this.throwError(MessageLocalization.getComposedMessage("error.reading.string", new Object[0]));
}
}
break;
case 62:
ch = this.file.read();
if (ch != 62) {
this.throwError(MessageLocalization.getComposedMessage("greaterthan.not.expected", new Object[0]));
}
this.type = PRTokeniser.TokenType.END_DIC;
break;
case 91:
this.type = PRTokeniser.TokenType.START_ARRAY;
break;
case 93:
this.type = PRTokeniser.TokenType.END_ARRAY;
break;
default:
outBuf = new StringBuffer();
if (ch != 45 && ch != 43 && ch != 46 && (ch < 48 || ch > 57)) {
this.type = PRTokeniser.TokenType.OTHER;
do {
outBuf.append((char)ch);
ch = this.file.read();
} while(!delims[ch + 1]);
} else {
this.type = PRTokeniser.TokenType.NUMBER;
label166:
do {
do {
outBuf.append((char)ch);
ch = this.file.read();
if (ch == -1) {
break label166;
}
} while(ch >= 48 && ch <= 57);
} while(ch == 46);
}
this.backOnePosition(ch);
}
if (outBuf != null) {
this.stringValue = outBuf.toString();
}
return true;
}
}
}
我们一段一段的分析,先分析 default 部分,这部分可以取出字符、数字,并将结果存入 stringValue
// 代码段
{
case 93:
...
default:
outBuf = new StringBuffer();
// 48-57 之间是数字,此处代表非数字
if (ch != 45 && ch != 43 && ch != 46 && (ch < 48 || ch > 57)) {
this.type = PRTokeniser.TokenType.OTHER;
do {
outBuf.append((char)ch);
ch = this.file.read();
} while(!delims[ch + 1]); // delims 是一个长257的数组,此处代表只要ch不属于 !&)*0=?\^ ,就继续循环
} else { // 否则说明是数字,将数字拼接起来,最终跳出循环
this.type = PRTokeniser.TokenType.NUMBER;
do {
do {
outBuf.append((char)ch);
ch = this.file.read();
if (ch == -1) {
break;
}
} while(ch >= 48 && ch <= 57);
} while(ch == 46); // 46小数点,表示小数
}
this.backOnePosition(ch);
}
}
delims 是一个长257, 数据如下的结构,!&)*0=?^ 字符对应的位置为 true
public static final boolean[] delims = new boolean[]{true, true, false, false, false, false, false, false, false, false, true, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, true, false, false, true, true, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false};
这里有一个 this.backOnePosition(ch); 方法
{
public static void backOnePosition(ch){
if(ch!=-1){
// this.file.pushBack((byte)ch);
this.file.pushBack((byte)ch){
this.file.back = ch; // 存储ch的值
this.file.isBack = true; // 表示当前ch超出255,需要 && 255
};
}
};
}
这里再看一下read方法,就懂了
public int read() throws IOException {
if (this.isBack) {
this.isBack = false;
return this.back & 255;
} else if (this.arrayIn == null) {
return this.plainRandomAccess ? this.trf.read() : this.rf.read();
} else {
return this.arrayInPtr >= this.arrayIn.length ? -1 : this.arrayIn[this.arrayInPtr++] & 255;
}
}
**下一篇我们再来分析 case40 **