Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setXORMode / setPaintMode not implemented #14

Closed
gredler opened this issue Dec 6, 2018 · 6 comments
Closed

setXORMode / setPaintMode not implemented #14

gredler opened this issue Dec 6, 2018 · 6 comments

Comments

@gredler
Copy link

gredler commented Dec 6, 2018

The setXORMode method sets an instance variable, but it never appears to be used. I'd submit a pull request, but I'm not sure how to implement this (aside from a rough idea that the exclusion and difference blend modes might be useful).

@rototor
Copy link
Owner

rototor commented Dec 7, 2018

Yes, it's not used at the moment. I just completely forgot about it ...

Responsible for setting the paint stuff is the PdfBoxGraphics2DPaintApplier, to get the info about the XOR Mode into it you can just extend the IPaintEnv with a "boolean isXORMode()" method. Within the PdfBoxGraphics2DPaintApplier#applyMethod() you would do a

if( env.isXORMode() ) {
state.ensureExtendedState();
state.pdExtendedGraphicsState.getCOSObject().setItem(COSName.BM, COSName.????);
}

That should be all you need to do. Please provide a test for this too.

@gredler
Copy link
Author

gredler commented Dec 8, 2018

Do you know what the blend mode would be, though? My local tests haven't been very productive, and I'm a little worried after running across this paragraph on the Skia website (PDF Theory of Operation):

PDF supports some of the xfer modes used in Skia directly. For those, it is simply a matter of setting the blend mode in the graphic state to the appropriate value (Normal/SrcOver, Multiply, Screen, Overlay, Darken, Lighten, !ColorDOdge, ColorBurn, HardLight, SoftLight, Difference, Exclusion). Aside from the standard SrcOver mode, PDF does not directly support the porter-duff xfer modes though. Most of them (Clear, SrcMode, DstMode, DstOver, SrcIn, DstIn, SrcOut, DstOut) can be emulated by various means, mostly by creating form x-objects out of part of the content and drawing it with a another form x-object as a mask. I have not figured out how to emulate the following modes: SrcATop, DstATop, Xor, Plus.

@rototor
Copy link
Owner

rototor commented Dec 8, 2018

Hmm, I would try Exclusive and ColorDodge - if those do not create an similar effect like XOR then we are out of luck... I can document the fact that XOR mode is not working due to technical PDF limits.

@gredler
Copy link
Author

gredler commented Dec 12, 2018

I've tried to test the absolute simplest scenario (monochrome black and white) and Difference seems like it should work like XOR mode in this simple scenario. However, while painting white over white results in black as expected, painting white over black does not result in white, but rather some brownish color. This doesn't make sense to me based on the my understanding of the color space (RGB) and the Difference blend mode function ( B(cb, cs) = | cb - cs| ). Any idea why it wouldn't work even in this simplest of test cases?

Vanilla PDFBox test code which generates the attached test PDF file:

    @Test
    public void testAllBlendingModes() throws Exception {
        List< COSName > blendModeNames = Arrays.asList(COSName.NORMAL, COSName.MULTIPLY, COSName.SCREEN, COSName.OVERLAY,
                        COSName.DARKEN, COSName.LIGHTEN, COSName.COLOR_DODGE, COSName.COLOR_BURN, COSName.HARD_LIGHT,
                        COSName.SOFT_LIGHT, COSName.DIFFERENCE, COSName.EXCLUSION, COSName.HUE, COSName.SATURATION,
                        COSName.COLOR, COSName.LUMINOSITY);
        PDDocument document = new PDDocument();
        PDPage page = new PDPage(new PDRectangle(400, 800));
        document.addPage(page);
        try (PDPageContentStream contentStream = new PDPageContentStream(document, page, AppendMode.APPEND, false)) {
            contentStream.setNonStrokingColor(Color.WHITE);
            contentStream.addRect(0, 0, 400, 800);
            contentStream.fill();
            contentStream.setNonStrokingColor(Color.BLACK);
            contentStream.addRect(0, 0, 200, 800);
            contentStream.fill();
            int y = 10;
            for (int i = 0; i < blendModeNames.size(); i++) {
                COSName blendModeName = blendModeNames.get(i);
                drawRow(contentStream, Color.BLACK, blendModeName, y);
                y += 20;
            }
            for (int i = 0; i < blendModeNames.size(); i++) {
                COSName blendModeName = blendModeNames.get(i);
                drawRow(contentStream, Color.WHITE, blendModeName, y);
                y += 20;
            }
        }
        document.save(new File("blending.pdf"));
    }

    private static void drawRow(PDPageContentStream contentStream, Color color, COSName blendModeName, int y) throws IOException {
        contentStream.setNonStrokingColor(color);
        contentStream.setGraphicsStateParameters(blendingMode(blendModeName));
        contentStream.addRect(100, y, 300, 10);
        contentStream.fill();
        contentStream.beginText();
        contentStream.setNonStrokingColor(Color.WHITE);
        contentStream.setGraphicsStateParameters(blendingMode(COSName.NORMAL));
        contentStream.newLineAtOffset(0, y);
        contentStream.setFont(PDType1Font.HELVETICA, 9);
        contentStream.showText(blendModeName.getName() + " (" + (Color.WHITE.equals(color) ? "white" : "black") + ")");
        contentStream.endText();
    }

    private static PDExtendedGraphicsState blendingMode(COSName blendModeName) throws IOException {
        COSDictionary dict = new COSDictionary();
        dict.setItem(COSName.TYPE, COSName.EXT_G_STATE);
        if (blendModeName != null) {
            dict.setItem(COSName.BM, blendModeName);
        }
        return new PDExtendedGraphicsState(dict);
    }

blending.pdf

@rototor
Copy link
Owner

rototor commented Dec 16, 2018

I had a look into the PDF spec, and I also don't think that there is a way to correctly implement the XOR mode. I've now documented this in the README. Thanks for pointing this out.

Beside this I see the XOR mode as something very useful for interactive UI stuff (e.g. drag boxes, ...) but nothing I would normally want in a PDF. What kind of stuff do you want to draw into the PDF which needs the XOR mode?

@gredler
Copy link
Author

gredler commented Dec 18, 2018

Yep, it's looking that way to me, as well. I was thinking it would at least be possible for monochrome and grayscale color spaces (with difference blending mode), but it's looking less and less likely... the worst part is not understanding why, though :-)

@gredler gredler closed this as completed Dec 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants