pdfbox快照2.0中PDFTextStripper的等价物是什么

What is the equivalent of PDFTextStripper in pdfbox snapshot 2.0

我目前正在使用 pdfbox 1.8 来分析 PDF 文档。下面是我正在做的一个非常精简的例子。

 import java.util.List;
 import java.io.IOException;
 import javax.swing.JFileChooser;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.PDPage;
 import org.apache.pdfbox.pdmodel.common.PDStream;

 public class Main 
 {
   private static PDDocument reader;

   public static void main(String[] args)
   {
       JFileChooser chooser = new JFileChooser();
       int result = chooser.showOpenDialog(null);
       if(result == JFileChooser.APPROVE_OPTION)
       {
           try
           {
               reader = PDDocument.load(chooser.getSelectedFile());
               for(int pagenum = 1; pagenum <= reader.getNumberOfPages(); pagenum++)
               {
                   System.out.println("===== Page:" + pagenum + " ======");
                   System.out.println(extract(pagenum));
               }

           }
           catch(Exception e) { e.printStackTrace(); }

       }
   }

   public static String extract(int pagenum) throws IOException
   {
       List allPages = reader.getDocumentCatalog().getAllPages();
       PDPage page = (PDPage) allPages.get(pagenum-1);
       PDStream contents = page.getContents();
       CustomPDFTextStripper stripper = new CustomPDFTextStripper();        
       if (contents != null) 
       {
           stripper.processStream(page, page.findResources(), page.getContents().getStream());
       }
       return stripper.getContents();
   }
 }

 import org.apache.pdfbox.util.PDFTextStripper;
 import java.io.IOException;
 import org.apache.pdfbox.util.TextPosition;

 public class CustomPDFTextStripper extends PDFTextStripper
 {
   private final StringBuilder builder;
   private float lastBase;
   public CustomPDFTextStripper() throws IOException
   {
       super.setSortByPosition(true);
       builder = new StringBuilder();
       lastBase = Float.MAX_VALUE;
   }

   public String getContents() { return builder.toString(); }

   @Override
   protected void processTextPosition(TextPosition textPos)
   {
       float ascent = textPos.getY();
       if(ascent > lastBase)
           builder.append("\n");
       lastBase = textPos.getY() + textPos.getHeight();
       builder.append(textPos.getCharacter());
       // I want to be able to do stuff here and
       // I need to read spaces and newline characters
   }
 }

我似乎无法在 pdfbox 2.0 快照中找到等效的解决方案(我知道它不稳定并且尚未发布)。我尝试使用类似的东西:

 CustomPDFTextStripper stripper = new CustomPDFTextStripper();        
 StringWriter dummy = new StringWriter();
 stripper.setPageStart(""+(pagenum-1));
 stripper.setPageEnd(""+(pagenum-1));
 stripper.writeText(reader, dummy);

但它不处理空格或在 processTextPostion 方法中给出准确的 textPos 数据。

关于如何在 2.0 中获得与 1.8 相同的所有 TextPostion 数据有什么想法吗?

========== 编辑 26JUN2015 8:00 PM CST ===========

好的,我有时间看了一下,发现了问题所在。 getWidthOfSpace() returns 1.8 和 2.0 之间截然不同的结果。

在 1.8 中约为 2.49 - 字符宽度约为 5

在 2.0 中约为 27.5 - 字符宽度约为 5

显然2.0中的27.5是错误的

只需运行下面的测试,你就会看到

 @Override
 protected void processTextPosition(TextPosition textPos)
 {
    float spaceWidth = textPos.getWidthOfSpace();
    float width = textPos.getWidth();
    System.out.println(textPos.getCharacter() + " - Width of Space=" + spaceWidth + " - width=" + width);
    builder.append(textPos.getCharacter());
 }

(当然 getUnicode() 对于 2.0 而不是 getCharacter())

===== 编辑 2015 年 6 月 27 日 8:00 下午 CST ======

这里是 link PDF 用于测试: Hello World

当前space的宽度计算确实有误。 PDFTextStreamEngine.showGlyph(Matrix, PDFont, int, String, Vector)目前(这是一个快照,今晚的情况可能会有所不同)这样计算宽度:

float horizontalScalingText = getGraphicsState().getTextState().getHorizontalScaling()/100f;
[...]
// the space width has to be transformed into display units
float spaceWidthDisplay = spaceWidthText * fontSizeText * horizontalScalingText *
        textRenderingMatrix.getScalingFactorX()  * ctm.getScalingFactorX();

PDFTextStreamEngine.java 修订版 1688116)

但是 textRenderingMatrix 是在 PDFStreamEngine.showText(byte[]) 中使用以下方法计算的:

float horizontalScaling = textState.getHorizontalScaling() / 100f;
[...]
Matrix parameters = new Matrix(
        fontSize * horizontalScaling, 0, // 0
        0, fontSize,                     // 0
        0, textState.getRise());         // 1
[...]
Matrix textRenderingMatrix = parameters.multiply(textMatrix).multiply(ctm);

(修订版 1688116 中的PDFStreamEngine.java

因此,字体大小和水平缩放比例都乘以 space 宽度的两倍。此外,当前变换矩阵既完全乘以textRenderingMatrix又部分乘以ctm.getScalingFactorX();这可以构成最有趣的组合结果。

最有可能从 PDFTextStreamEngine.showGlyph(Matrix, PDFont, int, String, Vector)

中的 spaceWidthDisplay 计算中将这些值作为显式因素删除就足够了

在版本 1.8.9 中文本 space 宽度在 PDFStreamEngine.processEncodedText(byte[]) 中计算如下:

float spaceWidthDisp = spaceWidthText * fontSizeText * horizontalScalingText 
                        * textMatrix.getXScale() * ctm.getXScale();

对于有趣的当前转换和文本矩阵,这也会产生有趣的结果,但上述感兴趣的因素并未乘以两次结果。