Processing: Save as PDF is not preserving blendMode

Created on 31 May 2017  路  10Comments  路  Source: processing/processing

OS: Windows 7 64 bits
Processing version: 3.3.3

PDF library is not saving info about blendMode. In this case the pdf will be saved ignoring the blendMode instruction:

import processing.pdf.*;

void setup() {
  size(100, 100);
  noLoop();
}

void draw() {
  beginRecord(PDF, "export.pdf");
  blendMode(MULTIPLY);
  background(255);
  stroke(#6A05FA);
  strokeWeight(30);
  line(25, 25, 75, 75);
  stroke(#05FA65);
  line(75, 25, 25, 75);
  endRecord();
}

Is there a workaround?

Most helpful comment

@danieltorrer @GKFX I have discovered a way to set the blend mode of a PDF file. This means that once you end the recording, you can change the blend mode of the file to correspond to the blend mode displayed in processing. I haven't found any discrepancies between how blending looks in the sketch and in the PDF once this is done to it.

First you need to import the library PDFBox. Under the "Libraries of each subproject" heading, download each .jar file. You also need commons-logging, under the "Binaries" heading, download the zip labeled "commons-logging-1.2-bin.zip".

Put the library together in this folder structure:
PDFBox
--library
----commons-logging-1.2.jar
----fontbox-2.0.8.jar
----PDFBox.jar (rename the file pdfbox-2.0.8.jar to PDFBox.jar)
----pdfbox-debugger-2.0.8.jar
----pdfbox-tools-2.0.8.jar
----preflight-2.0.8.jar
----xmpbox-2.0.8.jar
Then, put the PDFBox folder into your processing libraries folder.

Here is the code I used:

import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.graphics.*;
import org.apache.pdfbox.cos.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
void setup() {
    size(200, 200);
    File file = new File(dataPath("YOURPDFHERE.pdf")); //replace with the name of your PDF that is located in the data folder of this sketch
    correctPDFBlendMode(file, COSName.MULTIPLY); //runs method to change the blend mode of PDF
}

void correctPDFBlendMode(File pdf, COSName bm) {
    PDDocument doc = null; //Instantiates a PDDocument object
    try {
        doc = PDDocument.load(pdf); //Loads our pdf
        PDPage page = (PDPage) doc.getPage(0);  //Gets the page inside of the PDF document. Assumes your PDF has a single page.
        for (COSName c : page.getResources().getExtGStateNames()) { //Foreach of the graphics states...
            page.getResources().getExtGState(c).getCOSObject().setItem(COSName.BM, bm); //Change the blend mode
        }
        doc.removePage(0); //Deletes the old first page
        doc.addPage(page); //Adds in our modified page with changed blend mode
        doc.save(pdf); //Saves the document to the same file location
        println("done");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (doc != null) {
            try {
                doc.close();
            } catch (IOException e) {
                println("Problem when closing doc: " + e.getMessage());
            }
        }
    }
}

That's it. Feel free to ask any questions if I didn't make anything clear enough or if you have problems. Hope this helps!

All 10 comments

It's been a while but I believe one was able to do this inside a PGraphics object and then draw the (rasterized) result into the PDF.

It's not supported in PDF. So the bug here is that you should be seeing a message that says "blendMode() is not available."

Any progress on this? I have been attempting to edit pdf's blend modes through libraries like PDFBox with no success. If anyone has discovered a workaround/solution that doesn't involve rasterizing, let me know.

No, unfortunately I never got round to seeing if this could be made to work. Might be worthwhile reading https://www.pdflib.com/pdflib-cookbook/color/blendmode/ (linked above) to see if that does it for you?

@danieltorrer @GKFX I have discovered a way to set the blend mode of a PDF file. This means that once you end the recording, you can change the blend mode of the file to correspond to the blend mode displayed in processing. I haven't found any discrepancies between how blending looks in the sketch and in the PDF once this is done to it.

First you need to import the library PDFBox. Under the "Libraries of each subproject" heading, download each .jar file. You also need commons-logging, under the "Binaries" heading, download the zip labeled "commons-logging-1.2-bin.zip".

Put the library together in this folder structure:
PDFBox
--library
----commons-logging-1.2.jar
----fontbox-2.0.8.jar
----PDFBox.jar (rename the file pdfbox-2.0.8.jar to PDFBox.jar)
----pdfbox-debugger-2.0.8.jar
----pdfbox-tools-2.0.8.jar
----preflight-2.0.8.jar
----xmpbox-2.0.8.jar
Then, put the PDFBox folder into your processing libraries folder.

Here is the code I used:

import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.graphics.*;
import org.apache.pdfbox.cos.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
void setup() {
    size(200, 200);
    File file = new File(dataPath("YOURPDFHERE.pdf")); //replace with the name of your PDF that is located in the data folder of this sketch
    correctPDFBlendMode(file, COSName.MULTIPLY); //runs method to change the blend mode of PDF
}

void correctPDFBlendMode(File pdf, COSName bm) {
    PDDocument doc = null; //Instantiates a PDDocument object
    try {
        doc = PDDocument.load(pdf); //Loads our pdf
        PDPage page = (PDPage) doc.getPage(0);  //Gets the page inside of the PDF document. Assumes your PDF has a single page.
        for (COSName c : page.getResources().getExtGStateNames()) { //Foreach of the graphics states...
            page.getResources().getExtGState(c).getCOSObject().setItem(COSName.BM, bm); //Change the blend mode
        }
        doc.removePage(0); //Deletes the old first page
        doc.addPage(page); //Adds in our modified page with changed blend mode
        doc.save(pdf); //Saves the document to the same file location
        println("done");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (doc != null) {
            try {
                doc.close();
            } catch (IOException e) {
                println("Problem when closing doc: " + e.getMessage());
            }
        }
    }
}

That's it. Feel free to ask any questions if I didn't make anything clear enough or if you have problems. Hope this helps!

That looks like a good workaround for individual users, but for that to work in Processing out-of-the-box it would need to migrate away from the current PDF library to PDFBox. Migrating is probably a good idea though; the current PDF backend is not actively developed under an appropriate license.
One potential issue (reading https://apache.org/licenses/GPL-compatibility.html) is that it might not be possible to use an Apache library in a GPLv2 project such as Processing. (Although it鈥檚 possible that the core library being LGPL changes things.)
Anyway the decision of what might be done is nothing to do with me, let鈥檚 see what the team think.

Yes, this was intended to help individual users, I have no interest in getting this built into processing. If they could make the standard blendMode() function work with pdf's, that would be great, but in the meantime this will hopefully help some people accomplish something similar.

@ZachHofmeister This is great, I'm going to try it out.

Added the error message for 3.3.7/3.4.

The feature request part of it is here: https://github.com/processing/processing/issues/5438

Was this page helpful?
0 / 5 - 0 ratings