什么是PDF文件快照2.0中的PDFTextStripper

时间:2015-06-26 03:01:05

标签: java pdf pdfbox

我目前正在使用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数据。

如何将所有TextPostion数据与2.0中的1.8相同?

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

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

在1.8中它大约是2.49 - 字符宽度大约是5

在2.0中它大约是27.5 - 字符宽度大约是5

显然27.5在2.0中是错误的

只需运行以下测试即可看到

 @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()for 2.0而不是getCharacter())

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

以下是测试中使用的PDF链接: Hello World

1 个答案:

答案 0 :(得分:0)

当前计算空间宽度时确实存在错误。 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();

(修订版1688116中的PDFTextStreamEngine.java

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

因此,字体大小和水平缩放都是空间宽度的两倍。此外,当前变换矩阵完全乘以textRenderingMatrix并部分用作ctm.getScalingFactorX();这可以达到最有趣的综合结果。

最有可能将这些值作为spaceWidthDisplay

PDFTextStreamEngine.showGlyph(Matrix, PDFont, int, String, Vector)计算中的显式因素删除

在版本1.8.9中,文本空间宽度在PDFStreamEngine.processEncodedText(byte[])

中计算如下
float spaceWidthDisp = spaceWidthText * fontSizeText * horizontalScalingText 
                        * textMatrix.getXScale() * ctm.getXScale();

对于有趣的当前转换和文本矩阵,这也会产生有趣的结果,但上面感兴趣的因素并没有在结果中成倍增加..