3

I'm interested in taking a tif image and adding a layer to it that contains text with Java, preferably with the Twelve Monkeys image library if possible.

I can tweak the code from here to either add text to a tif or create a new tif of the same size with only text, but not save them as a multi-page tif. For example:

import javax.imageio.*;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.*;
import java.io.*;

public class ImageUtil {

public static void main(String[] args) throws Exception {

    BufferedImage src = ImageIO.read(new File("/path/to/main.tif"));
    BufferedImage text = createTextLayer(src);
    BufferedImage[] images = new BufferedImage[]{src, text};
    createMultiPage(images);

}

private static BufferedImage createTextLayer(BufferedImage src) {
    int w = src.getWidth();
    int h = src.getHeight();
    BufferedImage img = new BufferedImage(
            w, h, BufferedImage.TYPE_INT_ARGB);

    Graphics2D g2d = img.createGraphics();
    g2d.drawImage(img, 0, 0, null);

    g2d.setPaint(Color.red);
    g2d.setFont(new Font("Serif", Font.BOLD, 200));
    String s = "Hello, world!";
    FontMetrics fm = g2d.getFontMetrics();
    int x = img.getWidth() - fm.stringWidth(s) - 5;
    int y = fm.getHeight() * 5;
    g2d.drawString(s, x, y);
    g2d.dispose();
    return img;
}

private static void createMultiPage(BufferedImage[] images) throws IOException {

    File tempFile = new File("/new/file/path.tif");
    //I also tried passing in stream var below to the try, but also receive java.lang.UnsupportedOperationException: Unsupported write variant!
    //OutputStream stream = new FileOutputStream(tempFile);

    // Obtain a TIFF writer
    ImageWriter writer = ImageIO.getImageWritersByFormatName("TIFF").next();

    try (ImageOutputStream output = ImageIO.createImageOutputStream(tempFile)) {
        writer.setOutput(output);

        ImageWriteParam params = writer.getDefaultWriteParam();
        params.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        params.setCompressionType("None");

        //error here: java.lang.UnsupportedOperationException: Unsupported write variant!
        writer.prepareWriteSequence(null);

        for (int i = 0; i < images.length; i++){
            writer.writeToSequence(new IIOImage(images[i], null, null), params);
        }

        // We're done
        writer.endWriteSequence();
    }

}
}

Maven:

<dependency>
    <groupId>com.twelvemonkeys.imageio</groupId>
    <artifactId>imageio-tiff</artifactId>
    <version>3.2.1</version>
</dependency>

How can I create a multi-page tif from an image and the generated text-image?

I was able to get the following code to run for jpgs, but jpgs don't have layers.

public static void testWriteSequence() throws IOException {
    BufferedImage[] images = new BufferedImage[] {
            new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB),
            new BufferedImage(110, 100, BufferedImage.TYPE_INT_RGB),
            new BufferedImage(120, 100, BufferedImage.TYPE_INT_RGB),
            new BufferedImage(130, 100, BufferedImage.TYPE_INT_RGB)
    };

    Color[] colors = {Color.BLUE, Color.GREEN, Color.RED, Color.ORANGE};

    for (int i = 0; i < images.length; i++) {
        BufferedImage image = images[i];
        Graphics2D g2d = image.createGraphics();
        try {
            g2d.setColor(colors[i]);
            g2d.fillRect(0, 0, 100, 100);
        }
        finally {
            g2d.dispose();
        }
    }

    //ImageWriter writer = createImageWriter();
    ImageWriter writer = ImageIO.getImageWritersByFormatName("JPEG").next();

    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    try (ImageOutputStream output = ImageIO.createImageOutputStream(buffer)) {
        writer.setOutput(output);

        ImageWriteParam params = writer.getDefaultWriteParam();
        params.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);

        writer.prepareWriteSequence(null);

        params.setCompressionType("JPEG");
        writer.writeToSequence(new IIOImage(images[0], null, null), params);

        params.setCompressionType("JPEG");
        writer.writeToSequence(new IIOImage(images[1], null, null), params);

        params.setCompressionType("JPEG");
        writer.writeToSequence(new IIOImage(images[2], null, null), params);

        params.setCompressionType("JPEG");
        writer.writeToSequence(new IIOImage(images[3], null, null), params);

        writer.endWriteSequence();

        File tempFile = new File("/path/to/new/file.jpg");
        OutputStream out = new FileOutputStream(tempFile);

        buffer.writeTo(out);
    }
}

Thank you.

Harald K
  • 26,314
  • 7
  • 65
  • 111
jonD02
  • 125
  • 1
  • 11
  • if I put "Create Multi-Page Tiff with Java " on the search here will give me the answer; is that hard labor?? – gpasch Dec 08 '16 at 00:10
  • @gpasch Care to share a link so I can evaluate what you are seeing, or are you just the StackOverflow police? – jonD02 Dec 08 '16 at 00:40
  • @gpasch If you think this question is already answered, you should vote to close and mark it as a duplicate. It may cost you more than a random comment and a down vote, but in return it actually helps the community. – Harald K Dec 09 '16 at 07:55
  • @jonD02 The problem with your updated code is that you use `ImageIO.createImageOutputStream(...)` twice... First passing the file (correct), then the second passing an `ImageOutputStream` (doesn't work, and returns `null`). My code didn't do that. If you remove the `stream` variable, and instead pass `tempFile` inside the `try` you'll be good. – Harald K Dec 11 '16 at 10:44
  • @haraldK Thanks for the update. I tried using an OutputStream and passing the tempFile into the try and still no luck, receiving UnsupportedOperationException: Unsupported write variant!. I updated my post with the current code. I also added a jpg example to the end of the post that I modified from the TwelveMonkeys testWriteSequence method which I was able to run successfully, but I can't get it to work for tifs. – jonD02 Dec 11 '16 at 18:46
  • 1
    @haraldK updating to 3.3.1 solved the issue, thanks! – jonD02 Dec 11 '16 at 20:34
  • @haraldK related question, is there a way to generate the tif pages so Photoshop will treat them as layers? – jonD02 Dec 11 '16 at 20:50
  • @haraldK Looking at the layered tif in a text editor I'm not seeing a tag, so must be in the encrypted data. I made a new [post](http://stackoverflow.com/questions/41110630/create-layered-tif-with-java-for-use-in-photoshop), thanks. – jonD02 Dec 12 '16 at 22:18

1 Answers1

10

You can write multi-page images (in formats that supports it, like TIFF), using the standard ImageIO API. Now that Java ImageIO comes with a TIFF plugin bundled, starting from Java 9, the below should just work, with no extra dependencies. For Java 8 and earlier, you still need a TIFF plugin, like JAI or TwelveMonkeys as mentioned.

See for example the TIFFImageWriterTest.testWriteSequence method from the TwelveMonkeys ImageIO project's test cases, for an example of how to do it.

The important part:

BufferedImage[] images = ...
OutputStream stream = ... // May also use File here, as output

// Obtain a TIFF writer
ImageWriter writer = ImageIO.getImageWritersByFormatName("TIFF").next();

try (ImageOutputStream output = ImageIO.createImageOutputStream(stream)) {
    writer.setOutput(output);

    ImageWriteParam params = writer.getDefaultWriteParam();
    params.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);

    // Compression: None, PackBits, ZLib, Deflate, LZW, JPEG and CCITT variants allowed
    // (different plugins may use a different set of compression type names)
    params.setCompressionType("Deflate");

    writer.prepareWriteSequence(null);

    for (BufferedImage image : images) {
        writer.writeToSequence(new IIOImage(image, null, null), params);
    }

    // We're done
    writer.endWriteSequence();
}

writer.dispose();
Harald K
  • 26,314
  • 7
  • 65
  • 111
  • thanks for the response, appreciate it! I've been trying the code in various ways, but I'm receiving an IllegalStateException: getOutput() == null exception at writer.prepareWriteSequence. I updated my code above to where I'm at after checking your link and searching for a solution. Do you know what is missing from my example above? Thanks. – jonD02 Dec 09 '16 at 04:06
  • 3
    @jonD02 You made a small mistake, I explained in a comment to your question. Note that `stream` in my code is a `java.io.OutputStream`, but in your code it is a `javax.imageio.stream.ImageOutputStream`, which is a different thing, and is the reason you get the exception. – Harald K Dec 11 '16 at 10:46