gpt4 book ai didi

java - 从PDF过滤出超过特定字体大小的所有文本

转载 作者:行者123 更新时间:2023-12-02 01:16:25 54 4
gpt4 key购买 nike

如标题所示,我想从某个字体大小以上的PDF过滤掉所有文本。当前,我正在使用PDFBox库,但可以使用其他任何Java免费库。

我的方法是使用PDFStreamParser遍历令牌。当我通过一个大小大于我的阈值的Tf运算符时,不要添加下一个看到的Tj / TJ。但是,对我来说很清楚,这种相对简单的方法将不起作用,因为文本可能会被当前的转换矩阵缩放。

有没有我可能会采用的更好的方法,或者使我的方法行得通而不复杂的方法?

最佳答案

你的方法


  当我通过一个大小大于我的阈值的Tf运算符时,不要添加下一个看到的Tj / TJ。


太简单了。

一方面,当您评论自己时,


  文本可以通过当前的变换矩阵进行缩放。


(实际上不仅是通过转换矩阵,而且还通过文本矩阵!)

因此,您必须跟踪这些矩阵。

另一方面,Tf不仅为下一个看到的文本绘制指令设置基本字体大小,还设置它直到字体大小被其他指令显式更改为止。

此外,文本字体大小和当前的转换矩阵是图形状态的一部分。因此,它们受保存状态和恢复状态指令的约束。

因此,要针对当前状态编辑内容流,您必须跟踪许多信息。幸运的是,PDFBox包含的类可以在这里进行繁重的工作,基于PDFStreamEngine的类层次结构使您可以专注于自己的任务。为了使尽可能多的信息可用于编辑,PDFGraphicsStreamEngine类似乎是一个很好的选择。

通用内容流编辑器类

因此,让我们从PdfContentStreamEditor派生PDFGraphicsStreamEngine并添加一些代码以生成替换内容流。

public class PdfContentStreamEditor extends PDFGraphicsStreamEngine {
public PdfContentStreamEditor(PDDocument document, PDPage page) {
super(page);
this.document = document;
}

/**
* <p>
* This method retrieves the next operation before its registered
* listener is called. The default does nothing.
* </p>
* <p>
* Override this method to retrieve state information from before the
* operation execution.
* </p>
*/
protected void nextOperation(Operator operator, List<COSBase> operands) {

}

/**
* <p>
* This method writes content stream operations to the target canvas. The default
* implementation writes them as they come, so it essentially generates identical
* copies of the original instructions {@link #processOperator(Operator, List)}
* forwards to it.
* </p>
* <p>
* Override this method to achieve some fancy editing effect.
* </p>
*/
protected void write(ContentStreamWriter contentStreamWriter, Operator operator, List<COSBase> operands) throws IOException {
contentStreamWriter.writeTokens(operands);
contentStreamWriter.writeToken(operator);
}

// stub implementation of PDFGraphicsStreamEngine abstract methods
@Override
public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException { }

@Override
public void drawImage(PDImage pdImage) throws IOException { }

@Override
public void clip(int windingRule) throws IOException { }

@Override
public void moveTo(float x, float y) throws IOException { }

@Override
public void lineTo(float x, float y) throws IOException { }

@Override
public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException { }

@Override
public Point2D getCurrentPoint() throws IOException { return null; }

@Override
public void closePath() throws IOException { }

@Override
public void endPath() throws IOException { }

@Override
public void strokePath() throws IOException { }

@Override
public void fillPath(int windingRule) throws IOException { }

@Override
public void fillAndStrokePath(int windingRule) throws IOException { }

@Override
public void shadingFill(COSName shadingName) throws IOException { }

// PDFStreamEngine overrides to allow editing
@Override
public void processPage(PDPage page) throws IOException {
PDStream stream = new PDStream(document);
replacement = new ContentStreamWriter(replacementStream = stream.createOutputStream(COSName.FLATE_DECODE));
super.processPage(page);
replacementStream.close();
page.setContents(stream);
replacement = null;
replacementStream = null;
}

@Override
public void showForm(PDFormXObject form) throws IOException {
// DON'T descend into XObjects
// super.showForm(form);
}

@Override
protected void processOperator(Operator operator, List<COSBase> operands) throws IOException {
nextOperation(operator, operands);
super.processOperator(operator, operands);
write(replacement, operator, operands);
}

final PDDocument document;
OutputStream replacementStream = null;
ContentStreamWriter replacement = null;
}


PdfContentStreamEditor类)

此代码将覆盖 processPage以创建新的页面内容流,并最终用其替换旧的内容流。并且它覆盖 processOperator以提供已处理的指令进行编辑。

为了进行编辑,只需在此处覆盖 write即可。现有的实现只是简单地编写指令,而您可以更改要编写的指令。覆盖 nextOperation允许您在应用当前指令之前先查看图形状态。

按原样应用编辑器,

PDDocument document = PDDocument.load(SOURCE);
for (PDPage page : document.getDocumentCatalog().getPages()) {
PdfContentStreamEditor identity = new PdfContentStreamEditor(document, page);
identity.processPage(page);
}
document.save(RESULT);


EditPageContent测试 testIdentityInput

因此,将创建具有等效内容流的结果PDF。

为您的用例定制内容流编辑器

你想要


  从PDF中过滤出所有超过特定字体大小的文本。


因此,我们必须检查 write当前指令是否为文本绘制指令,如果是,则必须检查当前有效字体大小,即由文本矩阵和当前转换矩阵转换的基本字体大小。如果有效字体太大,我们必须删除指令。

可以按以下步骤完成:

PDDocument document = PDDocument.load(SOURCE);
for (PDPage page : document.getDocumentCatalog().getPages()) {
PdfContentStreamEditor identity = new PdfContentStreamEditor(document, page) {
@Override
protected void write(ContentStreamWriter contentStreamWriter, Operator operator, List<COSBase> operands) throws IOException {
String operatorString = operator.getName();

if (TEXT_SHOWING_OPERATORS.contains(operatorString))
{
float fs = getGraphicsState().getTextState().getFontSize();
Matrix matrix = getTextMatrix().multiply(getGraphicsState().getCurrentTransformationMatrix());
Point2D.Float transformedFsVector = matrix.transformPoint(0, fs);
Point2D.Float transformedOrigin = matrix.transformPoint(0, 0);
double transformedFs = transformedFsVector.distance(transformedOrigin);
if (transformedFs > 100)
return;
}

super.write(contentStreamWriter, operator, operands);
}

final List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
};
identity.processPage(page);
}
document.save(RESULT);


EditPageContent测试 testRemoveBigTextDocument

严格说来,完全放弃所讨论的指令可能是不够的;取而代之的是,必须像替换放置的文本绘制指令那样,用替换文本矩阵的指令替换它。否则,以下未删除的文本可能会被移动。通常,这确实可以正常工作,因为为以下不同文本重新设置了文本矩阵。因此,让我们在这里保持简单。

约束和说明

PdfContentStreamEditor仅编辑页面内容流。从那里开始,可以使用XObjects和Patterns,这些对象当前未被编辑器编辑。但是,在编辑页面内容流之后,应该很容易地递归地迭代XObjects和Patterns并以类似的方式对其进行编辑。

PdfContentStreamEditor本质上是 this answer中用于iText 5(.Net / Java)的 PdfContentStreamEditorthis answer中用于iText 7的 PdfCanvasEditor的端口。使用这些编辑器类的示例可能会提示如何将 PdfContentStreamEditor用于PDFBox。

先前在 HelloSignManipulator中的 this answer类中使用了类似(但不太通用)的方法。

修正错误

this question上下文中,发现了 PdfContentStreamEditor中的一个错误,该错误导致该示例PDF中的某些文本行被移动了。

背景:某些PDF指令是通过其他指令定义的,例如指定tx ty TD与-ty TL tx ty Td具有相同的效果。为了简化工作,相应的PDFBox OperatorProcessor实现通过将等效指令反馈回流引擎来工作。

如上所述,在这种情况下实现的 PdfContentStreamEditor会同时获取替换指令和原始指令的信号,并将它们全部写回到结果流中。因此,这些指令的效果加倍。例如。如果是TD指令,则文本插入点将前移两行,而不是前一行。

因此,我们必须忽略更换指示。为此,将上面的方法 processOperator替换为

@Override
protected void processOperator(Operator operator, List<COSBase> operands) throws IOException {
if (inOperator) {
super.processOperator(operator, operands);
} else {
inOperator = true;
nextOperation(operator, operands);
super.processOperator(operator, operands);
write(replacement, operator, operands);
inOperator = false;
}
}

boolean inOperator = false;

关于java - 从PDF过滤出超过特定字体大小的所有文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58475104/

54 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com