(这原本是另一个问题的答案(日期为2015年2月6日),该问题的提问者删除了包括所有答案。由于时代久远,答案中的代码仍基于PDFBox 1.8.x,因此可能需要一些更改才能使其运行于PDFBox 2.0.x。)
在评论中,提问者表达了对扩展PDFBox PDFTextStripper
以返回尝试反映PDF文件布局的文本行的解决方案感兴趣,这可能有助于解决该问题。
该类的概念验证如下:
public class LayoutTextStripper extends PDFTextStripper
{
public LayoutTextStripper() throws IOException
{
super();
}
@Override
protected void startPage(PDPage page) throws IOException
{
super.startPage(page);
cropBox = page.findCropBox();
pageLeft = cropBox.getLowerLeftX();
beginLine();
}
@Override
protected void writeString(String text, List<TextPosition> textPositions) throws IOException
{
float recentEnd = 0;
for (TextPosition textPosition: textPositions)
{
String textHere = textPosition.getCharacter();
if (textHere.trim().length() == 0)
continue;
float start = textPosition.getTextPos().getXPosition();
boolean spacePresent = endsWithWS | textHere.startsWith(" ");
if (needsWS | spacePresent | Math.abs(start - recentEnd) > 1)
{
int spacesToInsert = insertSpaces(chars, start, needsWS & !spacePresent);
for (; spacesToInsert > 0; spacesToInsert--)
{
writeString(" ");
chars++;
}
}
writeString(textHere);
chars += textHere.length();
needsWS = false;
endsWithWS = textHere.endsWith(" ");
try
{
recentEnd = getEndX(textPosition);
}
catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e)
{
throw new IOException("Failure retrieving endX of TextPosition", e);
}
}
}
@Override
protected void writeLineSeparator() throws IOException
{
super.writeLineSeparator();
beginLine();
}
@Override
protected void writeWordSeparator() throws IOException
{
needsWS = true;
}
void beginLine()
{
endsWithWS = true;
needsWS = false;
chars = 0;
}
int insertSpaces(int charsInLineAlready, float chunkStart, boolean spaceRequired)
{
int indexNow = charsInLineAlready;
int indexToBe = (int)((chunkStart - pageLeft) / fixedCharWidth);
int spacesToInsert = indexToBe - indexNow;
if (spacesToInsert < 1 && spaceRequired)
spacesToInsert = 1;
return spacesToInsert;
}
float getEndX(TextPosition textPosition) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException
{
Field field = textPosition.getClass().getDeclaredField("endX");
field.setAccessible(true);
return field.getFloat(textPosition);
}
public float fixedCharWidth = 3;
boolean endsWithWS = true;
boolean needsWS = false;
int chars = 0;
PDRectangle cropBox = null;
float pageLeft = 0;
}
它的使用方式如下:
PDDocument document = PDDocument.load(PDF)
LayoutTextStripper stripper = new LayoutTextStripper()
stripper.setSortByPosition(true)
stripper.fixedCharWidth = charWidth
String text = stripper.getText(document)
“fixedCharWidth”是假定的字符宽度。根据所涉及PDF中的书写,可能需要不同的值。在我的示例文档中,3到6的值比较有用。
它本质上模拟了iText中类似的解决方案(请参见
this answer)。然而,结果有些不同,因为iText文本提取将文本块向前移动,而PDFBox文本提取将单个字符向前移动。
请注意,这只是一个概念验证。它尤其没有考虑任何旋转。
PDFTextStripper
的变体感兴趣,它会尝试在PDF中存在大间隙的地方插入额外的空格,我将复制我曾经回答过的一个已被删除的问题,其中包含这样的变体。 - mkl