[更文挑战]iText番盘-PDF神器-21

175 阅读3分钟

「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战

PDF神器iText英文版翻译与学习

作者: 薛大郎.
英文原版: iText in Action 2nd Edition.pdf

坚持. 分享. 成长. 利他 !

一. 前言

我以前是干什么的来着

经过一个春节假期, 如果你很快乐. 而且制定了学习计划的你, 还是一如既往的忘了一干二净的. 远离了电脑, 也少有同事找你处理问题. 导致一上班, 都怀疑自己 我以前是干什么的来着.

卷王- 小傅哥 又开始了出征了. 只要你上了他的车, 就算你不卷, 你也会觉得了解了很多名词, 如果你卷了, 那你将收获满满. 这个男人卷的路线图, 项目, 和技术点 九浅一深, 助你终成正果.

二.正文.

1.1 PdfReader类源码分析

1644295779(1).png

首先分析构造方法. PdfReader类的构造方法有很多的重载, 能够让我们非常方便的获取一个Reader类. 这里只说一个 这个RandomAccessSource的默认操作, 这里运用了工厂模式和策略模式.

    RandomAccessSource byteSource = new RandomAccessSourceFactory()
        .setForceRead(false)
        .setUsePlainRandomAccess(Document.plainRandomAccess)
        .createBestSource(filename)
        
        
    // RandomAccessSourceFactory#createBestSource(String fileName)
    public RandomAccessSource createBestSource(String filename) throws IOException{
       // 如果可以获取到这个文件类, 而不抛出IOexception.
       File file = new File(filename);
       // 先看是否可读, 不可读进行返回其他resource
       if (!file.canRead()){
            if (filename.startsWith("file:/")
                  || filename.startsWith("http://") 
                    || filename.startsWith("https://")
                    || filename.startsWith("jar:")
                    || filename.startsWith("wsjar:")
                    || filename.startsWith("wsjar:")
                    || filename.startsWith("vfszip:")) {
               // 其实就是获取InputStream然后获取byte[]然后构造ArrayRandomAccessSource(byte[] array)
               return createSource(new URL(filename));
            } else {
                // 也是获取InputStream然后获取byte[]然后构造ArrayRandomAccessSource(byte[] array)
               return createByReadingToMemory(filename);
            }
       }
       // 上边默认的设置为 false
       if (forceRead){
           return createByReadingToMemory(new FileInputStream(filename));
       }

       String openMode = exclusivelyLockFile ? "rw" : "r";

       RandomAccessFile raf = new RandomAccessFile(file, openMode);
           if (exclusivelyLockFile){
              raf.getChannel().lock();
           }
           // 默认Document.plainRandomAccess = false;
           if (usePlainRandomAccess){
               return new RAFRandomAccessSource(raf);
           }

       try{
           // if this throws, the RAF will be closed by the FileChannelRandomAccessSource or PagedChannelRandomAccessSource
          return createBestSource(raf.getChannel());
       } catch (MapFailedException e){
           // if this throws, the RAF will be closed by the RAFRandomAccessSource
          return new RAFRandomAccessSource(raf);
       }  catch (IllegalArgumentException iae) {   //pdf files with zero or negative length stay opened without this catch
               return new RAFRandomAccessSource(raf);
           }
    }

RandomAccessSource的实现类如下: image.png

在构造函数中还有一个关键的参数为: boolean partialRead, 如果使用 PdfReader(String filename), 默认为全部读取.

    if (partialRead){
       // 只读部分
       readPdfPartial();
    } else {
       // 全部都读到内存中.
       readPdf();
    }

这就需要我们去斟酌了, 一般都是采用都读到内存中, 方便使用, 但是我们在处理大型Pdf文件的时候, 使用PdfReader的时候, 就要使用自己构造一个RandomAccessFileOrArray, 并将参数 partialRead 改为true.

另一种可以减少内存使用的方法是我们同样也都读取到内存中, 但是可以 PdfReader#selectPages(String ranges);

这个ranges的语法为:

[!][o][odd][e][even]start[-end]

[!] 代表非 可选

[o] 代表奇数 可选

[e] 代表偶数 可选

start 代表起始页 必选 包含此页

[-end] 代表终止页 可选 包含此页

public void selectPages(final String ranges) {
    selectPages(SequenceList.expand(ranges, getNumberOfPages()));
}

public void selectPages(final List<Integer> pagesToKeep) {
    pageRefs.selectPages(pagesToKeep);
    // 也会将无用的页码 去掉, 并重新排序页码数为第一页, 依此类推.
    removeUnusedObjects();
}

这就是今天这一篇的内容了. 咱们明天继续.