正如标题所说,我想过滤掉PDF中超过一定字体大小的所有文本。目前,我正在使用PDFBox库,但我愿意使用任何其他Java免费库。
我的方法是使用PDFStreamParser来迭代令牌。当我通过一个大小大于我的阈值的Tf操作符时,不要添加看到的下一个Tj/Tj。然而,我已经很清楚,这种相对简单的方法不会奏效,因为文本可能会被当前的转换矩阵缩放。
有没有更好的方法可以让我采用,或者让我的方法不变得太复杂?
你的做法
当我通过一个大小大于阈值的Tf运算符时,不要添加看到的下一个Tj/TJ。
这太简单了。
一方面,正如你自己所说,
文本可以通过当前变换矩阵进行缩放。
(实际上不仅通过变换矩阵,还通过文本矩阵!)
因此,您必须跟踪这些矩阵。
另一方面,Tf不仅为看到的下一条文本绘制指令设置基本字体大小,它还会一直设置,直到其他指令显式更改该大小。
此外,文本字体大小和当前转换矩阵是图形状态的一部分;因此,它们受“保存状态”和“恢复状态”指令的约束。
因此,要根据当前状态编辑内容流,必须跟踪大量信息。幸运的是,PDFBox包含了一些类来完成这里的繁重工作,它们是基于PDFStreamEngine
的类层次结构,允许您集中精力完成任务。为了获得尽可能多的可编辑信息,PDFGraphicsStreamEngine
类似乎是一个不错的选择。
因此,让我们从PDFGraphicsStreamEngine
派生出PdfContentStreamEditor
,并添加一些代码来生成替换内容流。
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
。现有的实现只是在指令出现时写入指令,而您可以更改要写入的指令。覆盖nextAction
允许您在应用当前指令之前查看图形状态。
按原样应用编辑器,
PDDocument document = PDDocument.load(SOURCE);
for (PDPage page : document.getDocumentCatalog().getPages()) {
PdfContentStreamEditor identity = new PdfContentStreamEditor(document, page);
identity.processPage(page);
}
document.save(RESULT);
(EditPageContent testtestIdentityInput
)
因此,将创建具有等效内容流的结果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 testtestRemoveBigTextDocument
)
严格地说,完全放弃有问题的指令可能是不够的;取而代之的是,必须用一条指令来替换它,以更改文本矩阵,就像删除的文本绘制指令所做的那样。否则,可能会移动以下未删除的文本。不过,这通常会按原样工作,因为文本矩阵是为以下不同的文本新设置的。让我们在这里保持简单。
此PdfContentStreamEditor仅编辑页面内容流。从那里可以使用当前未经编辑器编辑的XObject和Pattern。不过,在编辑页面内容流之后,递归地迭代XObject和模式并以类似的方式编辑它们应该很容易。
这个PdfContentStreamEditor
本质上是这个答案中的iText 5(.Net/Java)的PdfContentStreamEditor
端口,以及这个答案中的iText 7的PdfCanvasEditor
端口。使用这些编辑器类的示例可能会给出一些提示,说明如何将此PdfContentStreamEditor
用于PDFBox。
在此答案中,HelloSignManipulator类之前使用了类似(但不太通用)的方法。
在这个问题的上下文中,发现了PdfContentStreamEditor
中的一个错误,该错误导致示例PDF中的一些文本行在焦点处被移动。
背景:一些PDF指令是通过其他指令定义的,例如txtyTD被指定与-tyTL txtyTD具有相同的效果。为了简单起见,相应的PDFBoxOperatorProcessor
实现通过将等效指令反馈回流引擎来工作。
在这种情况下,上面实现的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;
问题内容: 如标题所示,我想从PDF中过滤出超过特定字体大小的所有文本。当前,我正在使用PDFBox库,但可以使用其他任何Java免费库。 我的方法是使用PDFStreamParser遍历令牌。当我通过大小大于阈值的Tf运算符时,不要添加下一个看到的Tj / TJ。但是,对我来说很清楚,这种相对简单的方法将不起作用,因为文本可能会被当前的转换矩阵缩放。 有没有我可能会采用的更好的方法,或者使我的方
我有一个ms-word文档,其中Helvetica 13.5是段落的主要字体。不幸的是,它没有绑定到文档中任何特定的预定义样式(文本是从网站复制到文档中的)。此外,你会看到嵌入在段落中的几个字斜体和一些词是“信使新”。 我想做的是浏览文档,查找具有单一字体/大小的文本片段。如果文本是Helvetica 13.5,我想把它改为Times New Roman 12。我不想换新的。斜体字应该保持斜体(但
我的Strings.xml文件的屏幕截图
从数组中移除 falsey 值元素。 使用 Array.filter() 过滤掉数组中所有 假值元素(false, null, 0, "", undefined, 和 NaN)。 const compact = arr => arr.filter(Boolean); compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34]); //
我是iTextSharp来获取文本块来构建单词并从现有PDF中获取位置。我正在使用字体大小来计算“单词”的内联块和大小。这一直运行良好,但最近我遇到了一个作者(ClarityPDF)创建的现有PDF,该作者制作了字体TT1B4t00。当我使用iTextSharp阅读块时,我得到了不同的大小,这给了我一个不正确的位置。 所有的PDF文档都使用Arial Narrow 12pt。我在同一个文档上使用C
问题内容: 我正在编写一个bash脚本,需要分析文件名。 它将需要删除所有特殊字符(包括空格): “!?.-_ ,并将所有大写字母更改为小写字母。类似于: 至: 我已经看到许多问题可以使用许多不同的编程语言来实现,而不能使用bash来实现。有什么好方法吗? 问题答案: 第一个删除特殊字符。表示删除,表示补码(反转字符集)。因此,意味着删除除指定字符外的所有字符。在与包括保持Linux或Window