iText - 在不清理整行的情况下清理矩形中的文本
iText - Cleaning Up Text in Rectangle without cleaning full row
我正在尝试使用 iText 清理 pdf 文档中矩形内的文本。
以下是我使用的代码片段:
PdfReader pdfReader = null;
PdfStamper stamper = null;
try
{
int pageNo = 1;
List<Float> linkBounds = new ArrayList<Float>();
linkBounds.add(0, (float) 202.3);
linkBounds.add(1, (float) 588.6);
linkBounds.add(2, (float) 265.8);
linkBounds.add(3, (float) 599.7);
pdfReader = new PdfReader("Test1.pdf");
stamper = new PdfStamper(pdfReader, new FileOutputStream("Test2.pdf"));
Rectangle linkLocation = new Rectangle(linkBounds.get(0), linkBounds.get(1), linkBounds.get(2), linkBounds.get(3));
List<PdfCleanUpLocation> cleanUpLocations = new ArrayList<PdfCleanUpLocation>();
cleanUpLocations.add(new PdfCleanUpLocation(pageNo, linkLocation, BaseColor.GRAY));
PdfCleanUpProcessor cleaner = new PdfCleanUpProcessor(cleanUpLocations, stamper);
cleaner.cleanUp();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try {
stamper.close();
}
catch (Exception e) {
e.printStackTrace();
}
pdfReader.close();
}
执行这段代码后,它会清除整行文本,而不是只清除给定矩形内的文本。
为了更好地解释事情,我附上了 pdf 文档。
在输入的 pdf 中,我突出显示了文本以显示我指定用于清理的矩形。
并且,在输出 pdf 中,您可以清楚地看到有灰色矩形,但如果您注意到它清理了整行文本。
我们将不胜感激。
OP 最初提供的文件 input.pdf
和 output.pdf
不允许重现该问题,但似乎根本不匹配。因此,有一个原始答案基本上表明该问题无法重现。
第二组文件 Test1.pdf
和 Test2.pdf
确实允许重现问题,从而产生更新的答案...
参考 OP 的第二组示例文件的更新答案
当前(最高 5.5.8)iText 清理代码中确实存在一个问题:对于标记文件,此处使用的 PdfContentByte
的一些方法在内容流中引入了额外的指令,实际上破坏了它并重新定位了 PDF 查看者眼中的一些文本,而忽略了损坏。
更详细:
PdfCleanUpContentOperator.writeTextChunks
使用 canvas.setCharacterSpacing(0)
和 canvas.setWordSpacing(0)
将字符和单词间距初始设置为 0。不幸的是,这些方法在标记文件的情况下检查 canvas 是否在构造当前在一个文本对象中,并且(如果不是)启动一个文本对象。此检查取决于 beginText
设置的本地标志;但在清理文本对象期间不会使用该方法启动。因此,writeTextChunks
在这里插入一个额外的 "BT 1 0 0 1 0 0 Tm"
序列破坏流并重新定位以下文本。
private void writeTextChunks(Map<Integer, Float> structuredTJoperands, List<PdfCleanUpContentChunk> chunks, PdfContentByte canvas,
float characterSpacing, float wordSpacing, float fontSize, float horizontalScaling) throws IOException {
canvas.setCharacterSpacing(0);
canvas.setWordSpacing(0);
...
PdfCleanUpContentOperator.writeTextChunks
应该使用手工制作的 Tc
和 Tw
指令来避免触发此副作用。
private void writeTextChunks(Map<Integer, Float> structuredTJoperands, List<PdfCleanUpContentChunk> chunks, PdfContentByte canvas,
float characterSpacing, float wordSpacing, float fontSize, float horizontalScaling) throws IOException {
if (Float.compare(characterSpacing, 0.0f) != 0 && Float.compare(characterSpacing, -0.0f) != 0) {
new PdfNumber(0).toPdf(canvas.getPdfWriter(), canvas.getInternalBuffer());
canvas.getInternalBuffer().append(Tc);
}
if (Float.compare(wordSpacing, 0.0f) != 0 && Float.compare(wordSpacing, -0.0f) != 0) {
new PdfNumber(0).toPdf(canvas.getPdfWriter(), canvas.getInternalBuffer());
canvas.getInternalBuffer().append(Tw);
}
canvas.getInternalBuffer().append((byte) '[');
有了这个改变,OP 的新示例文件 "Test1.pdf" 被示例代码正确编辑了
@Test
public void testRedactJavishsTest1() throws IOException, DocumentException
{
try ( InputStream resource = getClass().getResourceAsStream("Test1.pdf");
OutputStream result = new FileOutputStream(new File(OUTPUTDIR, "Test1-redactedJavish.pdf")) )
{
PdfReader reader = new PdfReader(resource);
PdfStamper stamper = new PdfStamper(reader, result);
List<Float> linkBounds = new ArrayList<Float>();
linkBounds.add(0, (float) 202.3);
linkBounds.add(1, (float) 588.6);
linkBounds.add(2, (float) 265.8);
linkBounds.add(3, (float) 599.7);
Rectangle linkLocation1 = new Rectangle(linkBounds.get(0), linkBounds.get(1), linkBounds.get(2), linkBounds.get(3));
List<PdfCleanUpLocation> cleanUpLocations = new ArrayList<PdfCleanUpLocation>();
cleanUpLocations.add(new PdfCleanUpLocation(1, linkLocation1, BaseColor.GRAY));
PdfCleanUpProcessor cleaner = new PdfCleanUpProcessor(cleanUpLocations, stamper);
cleaner.cleanUp();
stamper.close();
reader.close();
}
}
参考 OP 原始示例文件的原始答案
我刚刚尝试使用此测试方法重现您的问题
@Test
public void testRedactJavishsText() throws IOException, DocumentException
{
try ( InputStream resource = getClass().getResourceAsStream("input.pdf");
OutputStream result = new FileOutputStream(new File(OUTPUTDIR, "input-redactedJavish.pdf")) )
{
PdfReader reader = new PdfReader(resource);
PdfStamper stamper = new PdfStamper(reader, result);
List<Float> linkBounds = new ArrayList<Float>();
linkBounds.add(0, (float) 200.7);
linkBounds.add(1, (float) 547.3);
linkBounds.add(2, (float) 263.3);
linkBounds.add(3, (float) 558.4);
Rectangle linkLocation1 = new Rectangle(linkBounds.get(0), linkBounds.get(1), linkBounds.get(2), linkBounds.get(3));
List<PdfCleanUpLocation> cleanUpLocations = new ArrayList<PdfCleanUpLocation>();
cleanUpLocations.add(new PdfCleanUpLocation(1, linkLocation1, BaseColor.GRAY));
PdfCleanUpProcessor cleaner = new PdfCleanUpProcessor(cleanUpLocations, stamper);
cleaner.cleanUp();
stamper.close();
reader.close();
}
}
您的源 PDF 看起来像这样
结果是
而不是你的
我什至使用您在评论中提到的 iText 版本 5.5.5 和 5.5.4 重新测试,但在所有情况下我都得到了正确的结果。
因此,我无法重现您的问题。
我仔细看了你的output.pdf。它有点特殊,特别是它不包含由当前 iText 版本创建或操作的 PDF 的某些典型块。此外,内容流看起来非常不同。
因此,我假设在 iText 编辑了您的文件后,一些其他工具 post- 处理并在这样做时损坏了它。
特别是准备插入编辑行的页面内容说明在您的 input.pdf:
中看起来像这样
q
0.24 0 0 0.24 113.7055 548.04 cm
BT
0.0116 Tc
45 0 0 45 0 0 Tm
/TT5 1 Tf
[...] TJ
在我直接从 iText 收到的版本中像这样:
q
0.24 0 0 0.24 113.7055 548.04 cm
BT
0.0116 Tc
45 0 0 45 0 0 Tm
/TT5 1 Tf
0 Tc
0 Tw
[...] TJ
但是你的 output.pdf 中的相应行看起来像这样
BT
1 0 0 1 113.3 548.5 Tm
0 Tc
BT
1 0 0 1 0 0 Tm
0 Tc
[...] TJ
您的output.pdf中的说明是
- 无效,因为在文本对象中
BT ... ET
可能没有其他文本对象,但是您有两个 BT
操作彼此相继,中间没有 ET
;
- 有效地将文本定位在 0, 0 如果 PDF 查看器忽略上述错误。
事实上,如果您查看 output.pdf 页面的底部,您会看到:
因此,如果我假设还有一些其他程序 post- 处理 iText 结果是正确的,您应该修复那个 post-处理器。
如果没有这样的 post 处理器,您似乎没有正式发布的 iText 版本,而是完全不同的东西。
我正在尝试使用 iText 清理 pdf 文档中矩形内的文本。
以下是我使用的代码片段:
PdfReader pdfReader = null;
PdfStamper stamper = null;
try
{
int pageNo = 1;
List<Float> linkBounds = new ArrayList<Float>();
linkBounds.add(0, (float) 202.3);
linkBounds.add(1, (float) 588.6);
linkBounds.add(2, (float) 265.8);
linkBounds.add(3, (float) 599.7);
pdfReader = new PdfReader("Test1.pdf");
stamper = new PdfStamper(pdfReader, new FileOutputStream("Test2.pdf"));
Rectangle linkLocation = new Rectangle(linkBounds.get(0), linkBounds.get(1), linkBounds.get(2), linkBounds.get(3));
List<PdfCleanUpLocation> cleanUpLocations = new ArrayList<PdfCleanUpLocation>();
cleanUpLocations.add(new PdfCleanUpLocation(pageNo, linkLocation, BaseColor.GRAY));
PdfCleanUpProcessor cleaner = new PdfCleanUpProcessor(cleanUpLocations, stamper);
cleaner.cleanUp();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try {
stamper.close();
}
catch (Exception e) {
e.printStackTrace();
}
pdfReader.close();
}
执行这段代码后,它会清除整行文本,而不是只清除给定矩形内的文本。
为了更好地解释事情,我附上了 pdf 文档。
在输入的 pdf 中,我突出显示了文本以显示我指定用于清理的矩形。
并且,在输出 pdf 中,您可以清楚地看到有灰色矩形,但如果您注意到它清理了整行文本。
我们将不胜感激。
OP 最初提供的文件 input.pdf
和 output.pdf
不允许重现该问题,但似乎根本不匹配。因此,有一个原始答案基本上表明该问题无法重现。
第二组文件 Test1.pdf
和 Test2.pdf
确实允许重现问题,从而产生更新的答案...
参考 OP 的第二组示例文件的更新答案
当前(最高 5.5.8)iText 清理代码中确实存在一个问题:对于标记文件,此处使用的 PdfContentByte
的一些方法在内容流中引入了额外的指令,实际上破坏了它并重新定位了 PDF 查看者眼中的一些文本,而忽略了损坏。
更详细:
PdfCleanUpContentOperator.writeTextChunks
使用 canvas.setCharacterSpacing(0)
和 canvas.setWordSpacing(0)
将字符和单词间距初始设置为 0。不幸的是,这些方法在标记文件的情况下检查 canvas 是否在构造当前在一个文本对象中,并且(如果不是)启动一个文本对象。此检查取决于 beginText
设置的本地标志;但在清理文本对象期间不会使用该方法启动。因此,writeTextChunks
在这里插入一个额外的 "BT 1 0 0 1 0 0 Tm"
序列破坏流并重新定位以下文本。
private void writeTextChunks(Map<Integer, Float> structuredTJoperands, List<PdfCleanUpContentChunk> chunks, PdfContentByte canvas,
float characterSpacing, float wordSpacing, float fontSize, float horizontalScaling) throws IOException {
canvas.setCharacterSpacing(0);
canvas.setWordSpacing(0);
...
PdfCleanUpContentOperator.writeTextChunks
应该使用手工制作的 Tc
和 Tw
指令来避免触发此副作用。
private void writeTextChunks(Map<Integer, Float> structuredTJoperands, List<PdfCleanUpContentChunk> chunks, PdfContentByte canvas,
float characterSpacing, float wordSpacing, float fontSize, float horizontalScaling) throws IOException {
if (Float.compare(characterSpacing, 0.0f) != 0 && Float.compare(characterSpacing, -0.0f) != 0) {
new PdfNumber(0).toPdf(canvas.getPdfWriter(), canvas.getInternalBuffer());
canvas.getInternalBuffer().append(Tc);
}
if (Float.compare(wordSpacing, 0.0f) != 0 && Float.compare(wordSpacing, -0.0f) != 0) {
new PdfNumber(0).toPdf(canvas.getPdfWriter(), canvas.getInternalBuffer());
canvas.getInternalBuffer().append(Tw);
}
canvas.getInternalBuffer().append((byte) '[');
有了这个改变,OP 的新示例文件 "Test1.pdf" 被示例代码正确编辑了
@Test
public void testRedactJavishsTest1() throws IOException, DocumentException
{
try ( InputStream resource = getClass().getResourceAsStream("Test1.pdf");
OutputStream result = new FileOutputStream(new File(OUTPUTDIR, "Test1-redactedJavish.pdf")) )
{
PdfReader reader = new PdfReader(resource);
PdfStamper stamper = new PdfStamper(reader, result);
List<Float> linkBounds = new ArrayList<Float>();
linkBounds.add(0, (float) 202.3);
linkBounds.add(1, (float) 588.6);
linkBounds.add(2, (float) 265.8);
linkBounds.add(3, (float) 599.7);
Rectangle linkLocation1 = new Rectangle(linkBounds.get(0), linkBounds.get(1), linkBounds.get(2), linkBounds.get(3));
List<PdfCleanUpLocation> cleanUpLocations = new ArrayList<PdfCleanUpLocation>();
cleanUpLocations.add(new PdfCleanUpLocation(1, linkLocation1, BaseColor.GRAY));
PdfCleanUpProcessor cleaner = new PdfCleanUpProcessor(cleanUpLocations, stamper);
cleaner.cleanUp();
stamper.close();
reader.close();
}
}
参考 OP 原始示例文件的原始答案
我刚刚尝试使用此测试方法重现您的问题
@Test
public void testRedactJavishsText() throws IOException, DocumentException
{
try ( InputStream resource = getClass().getResourceAsStream("input.pdf");
OutputStream result = new FileOutputStream(new File(OUTPUTDIR, "input-redactedJavish.pdf")) )
{
PdfReader reader = new PdfReader(resource);
PdfStamper stamper = new PdfStamper(reader, result);
List<Float> linkBounds = new ArrayList<Float>();
linkBounds.add(0, (float) 200.7);
linkBounds.add(1, (float) 547.3);
linkBounds.add(2, (float) 263.3);
linkBounds.add(3, (float) 558.4);
Rectangle linkLocation1 = new Rectangle(linkBounds.get(0), linkBounds.get(1), linkBounds.get(2), linkBounds.get(3));
List<PdfCleanUpLocation> cleanUpLocations = new ArrayList<PdfCleanUpLocation>();
cleanUpLocations.add(new PdfCleanUpLocation(1, linkLocation1, BaseColor.GRAY));
PdfCleanUpProcessor cleaner = new PdfCleanUpProcessor(cleanUpLocations, stamper);
cleaner.cleanUp();
stamper.close();
reader.close();
}
}
您的源 PDF 看起来像这样
结果是
而不是你的
我什至使用您在评论中提到的 iText 版本 5.5.5 和 5.5.4 重新测试,但在所有情况下我都得到了正确的结果。
因此,我无法重现您的问题。
我仔细看了你的output.pdf。它有点特殊,特别是它不包含由当前 iText 版本创建或操作的 PDF 的某些典型块。此外,内容流看起来非常不同。
因此,我假设在 iText 编辑了您的文件后,一些其他工具 post- 处理并在这样做时损坏了它。
特别是准备插入编辑行的页面内容说明在您的 input.pdf:
中看起来像这样q
0.24 0 0 0.24 113.7055 548.04 cm
BT
0.0116 Tc
45 0 0 45 0 0 Tm
/TT5 1 Tf
[...] TJ
在我直接从 iText 收到的版本中像这样:
q
0.24 0 0 0.24 113.7055 548.04 cm
BT
0.0116 Tc
45 0 0 45 0 0 Tm
/TT5 1 Tf
0 Tc
0 Tw
[...] TJ
但是你的 output.pdf 中的相应行看起来像这样
BT
1 0 0 1 113.3 548.5 Tm
0 Tc
BT
1 0 0 1 0 0 Tm
0 Tc
[...] TJ
您的output.pdf中的说明是
- 无效,因为在文本对象中
BT ... ET
可能没有其他文本对象,但是您有两个BT
操作彼此相继,中间没有ET
; - 有效地将文本定位在 0, 0 如果 PDF 查看器忽略上述错误。
事实上,如果您查看 output.pdf 页面的底部,您会看到:
因此,如果我假设还有一些其他程序 post- 处理 iText 结果是正确的,您应该修复那个 post-处理器。
如果没有这样的 post 处理器,您似乎没有正式发布的 iText 版本,而是完全不同的东西。