如何在 PDF 渲染器中反转参考底图颜色

How to invert underlaying colors in a PDF Renderer

提问人:malz 提问时间:10/31/2023 最后编辑:Tilman Hausherrmalz 更新时间:11/1/2023 访问量:55

问:

我目前正在使用 Apache PDFBox 3.0.0 在 Java 中实现 PDF 渲染器,我想添加使用 XOR(独占 OR)模式绘制 PDF 元素(如字符串、矩形和线条)的功能,以反转基础元素的颜色。此效果通常用于突出显示或反转图形。

例:

enter image description here

PDFRenderer:

public class PDFRenderer {
    private PDDocument pdDocument;
    private PDPageContentStream contentStream;

    private float originY;
    private float originX;

    public PDFRenderer() {
        this.pdDocument = new PDDocument();

        PDPage pdPage = new PDPage();
        this.pdDocument.addPage(pdPage);

        try {
            this.contentStream = new PDPageContentStream(pdDocument, pdPage, AppendMode.APPEND, false, false);
        } catch (IOException e) {
            e.printStackTrace();
        }

        originY = pdPage.getMediaBox().getHeight();
        originX = 0;
    }

    /**
     * Draws a string on the canvas.
     *
     * @param text The text to draw.
     * @param x The x-coordinate of the top-left corner of the text.
     * @param y The y-coordinate of the top-left corner of the text.
     * @param width The width of the text box.
     * @param height The height of the text box.
     * @param color The color of the text.
     * @param font The font to use.
     */
    public void drawString(String text, int x, int y, int width, int height, CustomColor color, CustomFont font) {
        try {
            setStrokingColor(color);

            int fontHeight = Math.round((font.getFont().getFontDescriptor().getCapHeight()) / 1000 * height);
            float textWidth = font.getFont().getStringWidth(text) / 1000 * height;

            contentStream.setFont(font.getFont(), height);
            contentStream.setHorizontalScaling(100 + width);
            contentStream.beginText();
            contentStream.newLineAtOffset(originX + x, originY - fontHeight - y);
            contentStream.showText(text);
            contentStream.endText();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Draws a rectangle on the canvas.
     *
     * @param x The x-coordinate of the top-left corner of the rectangle.
     * @param y The y-coordinate of the top-left corner of the rectangle.
     * @param width The width of the rectangle.
     * @param height The height of the rectangle.
     * @param borderThickness The thickness of the rectangle's border.
     * @param color The color of the rectangle.
     * @param degreeOfCornerRounding The degree of corner rounding for the rectangle accepted are values from 1 to 8.
     * @param colorInverted Indicates if the rectangle color is inverted.
     */
    public void drawRectangle(int x, int y, int width, int height, int borderThickness, CustomColor color, int degreeOfCornerRounding, boolean colorInverted) {
        try {
            setStrokingColor(color);

            if (borderThickness > width / 2) {
                borderThickness = width / 2;
            } else if (borderThickness > height / 2) {
                borderThickness = height / 2;
            }

            float borderOffset = borderThickness / 2.0f;
            float adjustedX = originX + x + borderOffset;
            float adjustedY = originY - y - height + borderOffset;
            float adjustedWidth = width - borderThickness;
            float adjustedHeight = height - borderThickness;

            contentStream.setLineWidth(borderThickness);

            contentStream.addRect(adjustedX, adjustedY, adjustedWidth, adjustedHeight);

            contentStream.stroke();
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Draws a line on the canvas.
     *
     * @param x The x-coordinate of the starting point of the line.
     * @param y The y-coordinate of the starting point of the line.
     * @param width The horizontal length of the line.
     * @param height The vertical length of the line.
     * @param thickness The thickness of the line.
     * @param color The color of the line.
     */
    public void drawLine(int x, int y, int width, int height, int thickness, CustomColor color) {
        try {
            setStrokingColor(color);

            float adjustedX = originX + x;
            float adjustedY = originY - y;

            contentStream.setLineWidth(thickness);
            contentStream.moveTo(adjustedX, adjustedY);
            contentStream.lineTo(adjustedX + width, adjustedY - height);
            contentStream.stroke();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void setStrokingColor(CustomColor color) throws NumberFormatException, IOException {
        contentStream.setStrokingColor(
                Integer.parseInt(color.getHexadecimal().substring(1, 3), 16) / 255f,
                Integer.parseInt(color.getHexadecimal().substring(3, 5), 16) / 255f,
                Integer.parseInt(color.getHexadecimal().substring(5, 7), 16) / 255f
        );
    }
}

用法:

PDFRenderer pdfRenderer = new PDFRenderer();

pdfRenderer.drawRectangle(50, 50, 100, 100, 100, CustomColortim.BLACK, 2);
pdfRenderer.drawLine(50, 500, 700, 3, 3, CustomColor.BLACK);
pdfRenderer.drawString("Hello, World!", 220, 195, 100, 30, CustomColor.BLACK, CustomFont.A);

具体问题

  1. 在 Java 中使用 Apache PDFBox 3.0.0 绘制矩形、线条和文本等 PDF 元素时,如何修改 PDFRenderer 类以实现异或颜色反转?

  2. PDFBox 中是否有任何内置函数或方法支持用于颜色处理的 XOR 模式?

  3. 您能否提供在此上下文中实现 XOR 颜色反转的代码示例或建议?

Java PDF框

评论

0赞 Tilman Hausherr 10/31/2023
这在混合模式下可能是可能的,但我不知道其中是否有任何一种支持 XOR 或做类似的事情。
2赞 mkl 10/31/2023
首先,PDF 颜色模型(主要)基于浮点颜色分量值。因此,异或是没有意义的。尽管如此,正如@Tilman刚才提到的,混合模式可能是您的一种选择。如果是 RGB 颜色,则“差异”和“排除”模式看起来与您想要的相似,请参阅 ISO 32000-2 第 11.3.5.1 节中的示例图像。
0赞 Tilman Hausherr 10/31/2023
以下是有关如何使用混合模式的示例: svn.apache.org/viewvc/pdfbox/branches/2.0/examples/src/main/...

答:

1赞 K J 11/1/2023 #1

在 PDF 中更改颜色的一个常见问题是,您无法确定其他查看器可能会呈现哪种颜色模式。经常有人问我,为什么有些观众将图案显示为青色和绿色,而另一些观众则显示单色红色,两者可以是不同的形状。

这很大程度上归因于连续修改的“图形状态”的复杂性,然后添加了“扩展”设置!这是一个文件,不同的PDF查看器,不,Acrobat不是正确的。正确的是左上角。右上角是简单的反转,因此所有RGBW颜色都显示为CMYK,只需按下反转按钮。 因此,所有的“粉红色面孔”都变成了“蓝精灵蓝”,并抱怨只有一些颜色需要改变,但是哪种简单的方法可以过滤图像像素?

左下角的 Acrobat 可以过滤某些有限类型的对象,因此文本已指定为“绿色”,或者可以通过设置停止结束颜色来扭曲整个范围以方便访问。右下角的“黑是红”“白是绿”。

然而,要做到这一点,在复杂文件中逐项执行通常会导致对象丢失,因为有些对象自然是 RGB,有些是 CMYK。因此,每个对象和运行状态都需要分析,以免被否定而被遗忘。

enter image description here