我目前正在使用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
答案 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();
对于有趣的当前转换和文本矩阵,这也会产生有趣的结果,但上面感兴趣的因素并没有在结果中成倍增加..