【问题标题】:Traverse whole PDF and change blue color to black ( Change color of underlines as well) + iText遍历整个PDF并将蓝色更改为黑色(也更改下划线的颜色)+ iText
【发布时间】:2019-09-18 13:19:34
【问题描述】:

我正在使用以下代码从 pdf 文本中删除蓝色。它工作正常。但它不是改变下划线颜色,而是正确改变文本颜色。

原始文件部分:

操纵文件:

正如您在上面处理的文件中看到的,下划线颜色没有改变。

两周以来我一直在寻找解决此问题的方法,任何人都可以帮助解决这个问题。以下是我的更改颜色代码:

public void testChangeBlackTextToGreenDocument(String source, String filename) throws IOException {
    try (InputStream resource = getClass().getResourceAsStream(source);
            PdfReader pdfReader = new PdfReader(source);
            OutputStream result = new FileOutputStream(filename);
            PdfWriter pdfWriter = new PdfWriter(result);
            PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter);) {
        PdfCanvasEditor editor = new PdfCanvasEditor() {

            @Override
            protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands) {
                
                String operatorString = operator.toString();

                if (TEXT_SHOWING_OPERATORS.contains(operatorString)) {
                    List<PdfObject> listobj = new ArrayList<>();
                    listobj.add(new PdfNumber(0));
                    listobj.add(new PdfNumber(0));
                    listobj.add(new PdfNumber(0));
                    listobj.add(new PdfLiteral("rg"));
                    if (currentlyReplacedBlack == null) {
                        Color currentFillColor =getGraphicsState().getFillColor();
                        if (ColorConstants.GREEN.equals(currentFillColor) || ColorConstants.CYAN.equals(currentFillColor) || ColorConstants.BLUE.equals(currentFillColor)) {
                            currentlyReplacedBlack = currentFillColor;
                            super.write(processor, new PdfLiteral("rg"), listobj);
                        }
                    }
                } else if (currentlyReplacedBlack != null) {
                    if (currentlyReplacedBlack instanceof DeviceCmyk) {
                        List<PdfObject> listobj = new ArrayList<>();
                        listobj.add(new PdfNumber(0));
                        listobj.add(new PdfNumber(0));
                        listobj.add(new PdfNumber(0));
                        listobj.add(new PdfNumber(0));
                        listobj.add(new PdfLiteral("k"));
                        super.write(processor, new PdfLiteral("k"), listobj);
                    } else if (currentlyReplacedBlack instanceof DeviceGray) {
                        List<PdfObject> listobj = new ArrayList<>();
                        listobj.add(new PdfNumber(0));
                        listobj.add(new PdfLiteral("g"));
                        super.write(processor, new PdfLiteral("g"), listobj);
                    } else {
                        List<PdfObject> listobj = new ArrayList<>();
                        listobj.add(new PdfNumber(0));
                        listobj.add(new PdfNumber(0));
                        listobj.add(new PdfNumber(0));
                        listobj.add(new PdfLiteral("rg"));
                        super.write(processor, new PdfLiteral("rg"), listobj);
                    }
                    currentlyReplacedBlack = null;
                }

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

            Color currentlyReplacedBlack = null;

            final List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
        };
        for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) {
            editor.editPage(pdfDocument, i);
        }
    }
    File file = new File(source);
    file.delete();
}

这是原始文件。 https://raad-dev-test.s3.ap-south-1.amazonaws.com/36/2019-08-30/originalFile.pdf

相关链接:

Traverse whole PDF and change some attribute with some object in it using iText

Removing Watermark from PDF iTextSharp

Maven 依赖细节:

        <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>itext7-core</artifactId>
        <version>7.1.5</version>
        <type>pom</type>
    </dependency>
    
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>itextpdf</artifactId>
        <version>5.0.6</version>
    </dependency>

已编辑:

接受的答案不适用于以下文件:

https://raad-dev-test.s3.ap-south-1.amazonaws.com/36/2019-08-30/021549Orig1s025_aprepitant_clinpharm_prea_Mac.pdf(第41页)

https://raad-dev-test.s3.ap-south-1.amazonaws.com/36/2019-08-30/400_206494S5_avibactam_and_ceftazidine_unireview_prea_Mac.pdf(第 60 页)。

请帮忙。

【问题讨论】:

  • 或者谁能给我一个代码,我只提供这种类型的文件,它将所有超链接的颜色从蓝色变为黑色,并删除下划线。谢谢。
  • 创建代码的答案是指一个关于改变文本颜色而不是线条颜色的问题。因此,您的观察是意料之中的。如果将特殊蓝色的所有内容都替换为黑色,则代码很容易更改;但总的来说,文本下划线和任意线条图形之间没有区别。因此,仅重新着色蓝色下划线而不是其他蓝色线需要更具体的知识。
  • 你是对的@mkl,但它没有删除行,我已经尝试了所有可能的解决方案。
  • 我看到您使用 iText 7.x 的代码;另一方面,您既没有标记您的问题itext7,也没有提及您问题中的版本。那么您在寻找 iText 7.x 解决方案吗?还是 iText 5.x 解决方案?或者任何一个都可以?

标签: pdf itext itext7


【解决方案1】:

(此处的示例代码使用 iText 7 for Java。您在标签或问题文本中未提及 iText 版本或您的编程环境,但您的示例代码似乎表明这是您的选择组合。) em>

替换蓝色填充颜色

您基于原始代码的测试仅尝试明确更改文本颜色。但是,文档中的“下划线”(就 PDF 绘图而言)不是文本的一部分,而是作为简单路径绘制的。因此,原始代码没有明确地触及下划线,它必须适应您的任务。

但实际上,您的任务是将所有内容从蓝色变为黑色,比仅更改蓝色文本(例如

更容易实施
try (   PdfReader pdfReader = new PdfReader(SOURCE_PDF);
        PdfWriter pdfWriter = new PdfWriter(RESULT_PDF);
        PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter) )
{
    PdfCanvasEditor editor = new PdfCanvasEditor()
    {
        @Override
        protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
        {
            String operatorString = operator.toString();

            if (SET_FILL_RGB.equals(operatorString) && operands.size() == 4) {
                if (isApproximatelyEqual(operands.get(0), 0) &&
                        isApproximatelyEqual(operands.get(1), 0) &&
                        isApproximatelyEqual(operands.get(2), 1)) {
                    super.write(processor, new PdfLiteral("g"), Arrays.asList(new PdfNumber(0), new PdfLiteral("g")));
                    return;
                }
            }
            
            super.write(processor, operator, operands);
        }

        boolean isApproximatelyEqual(PdfObject number, float reference) {
            return number instanceof PdfNumber && Math.abs(reference - ((PdfNumber)number).floatValue()) < 0.01f;
        }

        final String SET_FILL_RGB = "rg";
    };
    for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++)
    {
        editor.editPage(pdfDocument, i);
    }
}

(ChangeColor 测试testChangeFillRgbBlueToBlack)

请注意,这只是概念验证,而不是最终和完整的解决方案。特别是:

  • 它只查看填充(非描边)颜色。在您的情况下,您的文本(像往常一样)和下划线仅使用填充颜色就足够了 - 下划线实际上不是绘制为 描边线,而是绘制为 细长的填充矩形。
  • 只有 RGB 蓝色(并且只有使用 rg 指令设置的蓝色,而不是使用 scscn 设置的蓝色,更不用说蓝色组合了其他颜色使用时髦的混合模式)被考虑。这可能是一个问题,尤其是在明确设计用于打印的文档(可能使用 CMYK 颜色)的情况下。
  • PdfCanvasEditor 只检查和编辑页面本身的内容流,而不是显示的表单 XObjects 或模式的内容流;因此,可能找不到某些内容。它可以很容易地概括。

结果:

替换蓝色填充和描边颜色

测试上面的代码,您很快就会发现下划线没有更改的文档。事实证明,这些下划线实际上是绘制为描边线,而不是像上面那样填充的矩形。

因此,要正确编辑此类文档,您不仅必须编辑填充颜色,还必须编辑描边颜色,例如像这样:

try (   PdfReader pdfReader = new PdfReader(SOURCE_PDF);
        PdfWriter pdfWriter = new PdfWriter(RESULT_PDF);
        PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter) )
{
    PdfCanvasEditor editor = new PdfCanvasEditor()
    {
        @Override
        protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
        {
            String operatorString = operator.toString();

            if (SET_FILL_RGB.equals(operatorString) && operands.size() == 4) {
                if (isApproximatelyEqual(operands.get(0), 0) &&
                        isApproximatelyEqual(operands.get(1), 0) &&
                        isApproximatelyEqual(operands.get(2), 1)) {
                    super.write(processor, new PdfLiteral("g"), Arrays.asList(new PdfNumber(0), new PdfLiteral("g")));
                    return;
                }
            }

            if (SET_STROKE_RGB.equals(operatorString) && operands.size() == 4) {
                if (isApproximatelyEqual(operands.get(0), 0) &&
                        isApproximatelyEqual(operands.get(1), 0) &&
                        isApproximatelyEqual(operands.get(2), 1)) {
                    super.write(processor, new PdfLiteral("G"), Arrays.asList(new PdfNumber(0), new PdfLiteral("G")));
                    return;
                }
            }

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

        boolean isApproximatelyEqual(PdfObject number, float reference) {
            return number instanceof PdfNumber && Math.abs(reference - ((PdfNumber)number).floatValue()) < 0.01f;
        }

        final String SET_FILL_RGB = "rg";
        final String SET_STROKE_RGB = "RG";
    };
    for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++)
    {
        editor.editPage(pdfDocument, i);
    }
}

ChangeColor 测试 testChangeRgbBlueToBlackControlOfNitrosamineImpuritiesInSartansRevtestChangeRgbBlueToBlackEdqmReportsIssuesOfNonComplianceWithToothMac

结果:

从其他 RGB 色彩空间中替换不同深浅的蓝色

再次测试上面的代码,您发现蓝色没有改变的文档。事实证明,这些蓝色不是来自 DeviceRGB 标准 RGB,而是来自 ICCBased 颜色空间,更准确地说是配置 RGB 颜色空间。特别是比以前使用了其他颜色设置运算符,sc / scn 而不是 rg。此外,在一个文档中,使用的不是纯蓝色0 0 1,而是.17255 .3098 .63529蓝色

如果我们假设带有三个数字参数的 scscn 指令设置了一些 RGB 颜色的风格,就像这里一样(通常这是一个过度简化,Lab 和其他颜色空间也可以带有 4 个组件,但是您的文档似乎是面向 RGB 的)并且在识别蓝色方面不太严格,我们可以将上面的代码概括如下:

class AllRgbBlueToBlackConverter extends PdfCanvasEditor {
    @Override
    protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
    {
        String operatorString = operator.toString();

        if (RGB_SETTER_CANDIDATES.contains(operatorString) && operands.size() == 4) {
            if (isBlue(operands.get(0), operands.get(1), operands.get(2))) {
                PdfNumber number0 = new PdfNumber(0);
                operands.set(0, number0);
                operands.set(1, number0);
                operands.set(2, number0);
            }
        }

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

    boolean isBlue(PdfObject red, PdfObject green, PdfObject blue) {
        if (red instanceof PdfNumber && green instanceof PdfNumber && blue instanceof PdfNumber) {
            float r = ((PdfNumber)red).floatValue();
            float g = ((PdfNumber)green).floatValue();
            float b = ((PdfNumber)blue).floatValue();
            return b > .5f && r < .9f*b && g < .9f*b;
        }
        return false;
    }

    final Set<String> RGB_SETTER_CANDIDATES = new HashSet<>(Arrays.asList("rg", "RG", "sc", "SC", "scn", "SCN"));
}

(ChangeColor 助手类)

这样使用

try (   PdfReader pdfReader = new PdfReader(INPUT);
        PdfWriter pdfWriter = new PdfWriter(OUTPUT);
        PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter) ) {
    PdfCanvasEditor editor = new AllRgbBlueToBlackConverter();
    for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++)
    {
        editor.editPage(pdfDocument, i);
    }
}

我们得到

【讨论】:

  • 你救了我。它适用于上述文件。但是我必须提出一些一般性的建议,它不适用于某些文件。这是链接。你能调查一下吗?我将非常感谢你。谢谢回复。以下是此代码无法正常工作的文件raad-dev-test.s3.ap-south-1.amazonaws.com/36/2019-08-30/…raad-dev-test.s3.ap-south-1.amazonaws.com/36/2019-08-30/…
  • 我在上面添加了依赖细节。谢谢
  • @AsadRao 这两个文件是限制的例子“只有这样的蓝色设置使用 rg 指令,而不是使用 sc scn”我在回答中警告过。这些蓝色不是来自 DeviceRGB 标准 RGB,而是来自 ICCBased 颜色空间。作为一种变通方法,您可以假设所有具有三个数字参数的 scscn 指令基本上设置 RGB 颜色并按此方式处理它们。但对于某些文档,这是一个最终会失败的假设。或者,您可以开始解析 ICC 配置文件。
  • @AsadRao 我并不是色彩空间转换等方面的专家。对于您的两个示例文档,很容易在此处调整代码(假设三个数字 cs 和 csn 指令为 RGB 和软化蓝色测试)。但是您应该首先询问为您分配这些任务(将蓝色变为黑色/删除蓝线)的人,您的代码应该支持哪种蓝色以及构建蓝色的方法。因为如果对方说“全部”,您可以回答说这可能需要几个月或几年的工作。
  • 明天我会调查的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-08
相关资源
最近更新 更多