PdfReader 2:this.readPdf() - 1;

7 篇文章 0 订阅

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 **

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值