pdfbox - 通过使用 PDFBox 在 PDF 中使用文本 postiton 将图像放置在文本上。
问题描述
结果是图像未正确放置在文本上。我的文字位置错了吗?
这是一个关于如何获取 PDF 中每个字符的 x/y 坐标和大小的示例
public class MyClass extends PDFTextStripper {
pdocument = PDDocument.load(new File(fileName));
stripper = new GetCharLocationAndSize();
stripper.setSortByPosition(true);
stripper.setStartPage(0);
stripper.setEndPage(pdocument.getNumberOfPages());
Writer dummy = new OutputStreamWriter(new
ByteArrayOutputStream());
stripper.writeText(pdocument, dummy);
/*
* Override the default functionality of PDFTextStripper.writeString()
*/
@Override
protected void WriteString(String string, List<TextPosition>
textPositions) throws IOException {
String imagePath = "image.jpg";
PDImageXObject pdImage =
PDImageXObject.createFromFile(imagePath,pdocument);
PDPageContentStream contentStream = new
PDPageContentStream(pdocument, stripper.getCurrentPage(), true,
true);
for (TextPosition text : textPositions) {
if (text.getUnicode().equals("a")) {
contentStream.drawImage(pdImage, text.getXDirAdj(),
text.getYDirAdj(), text.getWidthDirAdj(),text.getHeightDir());
}
}
contentStream.close();
pdocument.save("newdoc.pdf");
}
}
解决方案
检索合理坐标
您在内容流中使用text.getXDirAdj()
andtext.getYDirAdj()
作为x和y坐标。这是行不通的,因为 PDFBox 在文本提取期间使用的坐标已转换为他们喜欢用于文本提取目的的坐标系,参见。JavaDocs:
/**
* This will get the text direction adjusted x position of the character.
* This is adjusted based on text direction so that the first character
* in that direction is in the upper left at 0,0.
*
* @return The x coordinate of the text.
*/
public float getXDirAdj()
/**
* This will get the y position of the text, adjusted so that 0,0 is upper left and it is
* adjusted based on the text direction.
*
* @return The adjusted y coordinate of the character.
*/
public float getYDirAdj()
对于 aTextPosition text
你应该使用
text.getTextMatrix().getTranslatex()
和
text.getTextMatrix().getTranslateY()
但即使是这些数字也可能需要更正,参见。这个答案,因为 PDFBox 已将矩阵乘以平移,使裁剪框的左下角成为原点。
因此,如果PDRectangle cropBox
是当前页面的裁剪框,则使用
text.getTextMatrix().getTranslatex() + cropBox.getLowerLeftX()
和
text.getTextMatrix().getTranslateY() + cropBox.getLowerLeftY()
(PDFBox 的这种坐标归一化对于任何真正想要使用文本坐标的人来说都是一个 PITA...)
其他问题
您的代码还有其他一些问题,其中一个问题在您共享的文档中变得清晰:您附加到页面内容流而不重置图形上下文:
PDPageContentStream contentStream = new PDPageContentStream(pdocument,
stripper.getCurrentPage(), true, true);
具有此签名的构造函数假定您不想重置上下文。使用带有附加boolean
参数的参数并将其设置true
为请求上下文重置:
PDPageContentStream contentStream = new PDPageContentStream(pdocument,
stripper.getCurrentPage(), true, true, true);
现在上下文被重置并且位置再次确定。
但是,这两个构造函数都已弃用,因此不应使用。在开发分支中,它们已经被删除。而是使用
PDPageContentStream contentStream = new PDPageContentStream(pdocument,
stripper.getCurrentPage(), AppendMode.APPEND, true, true);
但是,这引入了另一个问题:您PDPageContentStream
为每个writeString
调用创建一个新的。如果每次都重置上下文,saveGraphicsState/restoreGraphicsState 对的嵌套可能会变得非常深。因此,您应该只为每个页面创建一个这样的内容流,并在writeString
对该页面的所有调用中使用它。
因此,您的文本剥离器子类可能如下所示:
class CoverCharByImage extends PDFTextStripper {
public CoverCharByImage(PDImageXObject pdImage) throws IOException {
super();
this.pdImage = pdImage;
}
final PDImageXObject pdImage;
PDPageContentStream contentStream = null;
@Override
public void processPage(PDPage page) throws IOException {
super.processPage(page);
if (contentStream != null) {
contentStream.close();
contentStream = null;
}
}
@Override
protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
if (contentStream == null)
contentStream = new PDPageContentStream(document, getCurrentPage(), AppendMode.APPEND, true, true);
PDRectangle cropBox = getCurrentPage().getCropBox();
for (TextPosition text : textPositions) {
if (text.getUnicode().equals("a")) {
contentStream.drawImage(pdImage, text.getTextMatrix().getTranslateX() + cropBox.getLowerLeftX(),
text.getTextMatrix().getTranslateY() + cropBox.getLowerLeftY(),
text.getWidthDirAdj(), text.getHeightDir());
}
}
}
}
它可以像这样使用:
PDDocument pdocument = PDDocument.load(...);
String imagePath = ...;
PDImageXObject pdImage = PDImageXObject.createFromFile(imagePath, pdocument);
CoverCharByImage stripper = new CoverCharByImage(pdImage);
stripper.setSortByPosition(true);
Writer dummy = new OutputStreamWriter(new ByteArrayOutputStream());
stripper.writeText(pdocument, dummy);
pdocument.save(...);
( CoverCharacterByImage测试testCoverLikeLez
)
导致
等等
推荐阅读
- c++ - 非类型模板参数中的 C++ 类类型:推导指南失败
- c# - 如何使用 EmguCV 检测自定义对象
- sql - REPLACE showing error that the column name is invalid
- arrays - VBA数组公共方法不能被组件修改
- python - 无法创建 Flake8 插件来检查函数名称
- excel - 一张 Excel 表上的两个更改事件
- r - PLS-DA 处理缺失值
- python-3.x - 将列表中的所有字符串转换为浮点数。适用于单个列表,但不适用于数据框
- angular - 功能模块中的角度路由逻辑
- docker - Pulumi - 如何从私有注册表中提取 docker 镜像?