1

Java-Version:
openjdk version "11" 2018-09-25
OpenJDK Runtime Environment 18.9 (build 11+28)
OpenJDK 64-Bit Server VM 18.9 (build 11+28, mixed mode)

TwelveMonkeys ImageIO Dependencies:

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

Case:
I'm trying to save an subimage as a tif with dpi information.
Sometimes it works, sometimes I get a strange exception.

First of all, here's my code how I create the subimage:

//Reading JPG as BufferedImage
BufferedImage bi = ImageIO.read(new FileImageInputStream(jpg.toFile()));
//Create Subimage (r is an java.awt.Rectangle)
BufferedImage subimage = bi.getSubimage(r.x - 10, r.y - 10, r.width + 20, r.height + 20);
saveImage(subimage, new File("destfile.tif"));

It is ensured, that this subimage is valid.

Now the method "saveImage", inspired by this post
https://github.com/haraldk/TwelveMonkeys/issues/439#issue-355278313

   public static void saveImage(BufferedImage image, File destFile) throws IOException {
        String format = "tif";

        Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);

        if (!writers.hasNext()) {
            throw new IllegalArgumentException("No writer for: " + format);
        }

        ImageWriter writer = writers.next();

        try {
            // Create output stream (in try-with-resource block to avoid leaks)
            try (ImageOutputStream output = ImageIO.createImageOutputStream(destFile)) {
                writer.setOutput(output);

                // set the resolution of the target image to 200 dpi
                final List<Entry> entries = new ArrayList<Entry>();
                entries.add(new TIFFEntry(TIFF.TAG_X_RESOLUTION, new Rational(200)));
                entries.add(new TIFFEntry(TIFF.TAG_Y_RESOLUTION, new Rational(200)));

                final IIOMetadata tiffImageMetadata = new TIFFImageMetadata(entries);

                writer.write(new IIOImage(image, null, tiffImageMetadata));
            }

        }
        finally {
            // Dispose writer in finally block to avoid memory leaks
            writer.dispose();
        }
    }

Sometimes, saving this TIF works like a charm without any problems. But in some cases, I receive the following exception. In this case, I have to restart the application and give it another try:

java.lang.StringIndexOutOfBoundsException: begin 0, end -1, length 3
    at java.base/java.lang.String.checkBoundsBeginEnd(String.java:3319)
    at java.base/java.lang.String.substring(String.java:1874)
    at com.github.jaiimageio.plugins.tiff.TIFFField.initData(TIFFField.java:406)
    at com.github.jaiimageio.plugins.tiff.TIFFField.createFromMetadataNode(TIFFField.java:486)
    at com.github.jaiimageio.impl.plugins.tiff.TIFFImageMetadata.parseIFD(TIFFImageMetadata.java:1588)
    at com.github.jaiimageio.impl.plugins.tiff.TIFFImageMetadata.mergeNativeTree(TIFFImageMetadata.java:1612)
    at com.github.jaiimageio.impl.plugins.tiff.TIFFImageMetadata.mergeTree(TIFFImageMetadata.java:1636)
    at java.desktop/javax.imageio.metadata.IIOMetadata.setFromTree(IIOMetadata.java:752)
    at com.github.jaiimageio.impl.plugins.tiff.TIFFImageWriter.convertNativeImageMetadata(TIFFImageWriter.java:515)
    at com.github.jaiimageio.impl.plugins.tiff.TIFFImageWriter.write(TIFFImageWriter.java:2551)
    at com.github.jaiimageio.impl.plugins.tiff.TIFFImageWriter.write(TIFFImageWriter.java:2383)
    at java.desktop/javax.imageio.ImageWriter.write(ImageWriter.java:595)
    at de.buerotex.util.BufferedImageUtil.saveImage(BufferedImageUtil.java:64)

I looked in the source and saw, that this class is trying to split a value at this point:
at com.github.jaiimageio.plugins.tiff.TIFFField.initData(TIFFField.java:406)

case TIFFTag.TIFF_RATIONAL:
   slashPos = value.indexOf("/");
   numerator = value.substring(0, slashPos);
   denominator = value.substring(slashPos + 1);

where "value" = 200.

I do not know where and why this constant "TIFF_RATIONAL" is beeing set.

Where does this error come from and why? When I disable setting the tiffImageMetadata in my save-method by setting the third parameter to null:

writer.write(new IIOImage(image, null, null));

everything is working fine. But my tif-images don't have any dpi-values being set.

HarleyDavidson
  • 559
  • 6
  • 17
  • Does this answer your question? [Java: StringIndexOutOfBoundsException](https://stackoverflow.com/questions/47684377/java-stringindexoutofboundsexception) – fantaghirocco Sep 17 '21 at 09:31
  • The method you have posted is not `saveSubImage()` but `saveImage()`. Is this the real code? And I don't understand why you are selecting a Writer based on the image format and then doing more processing to get another output stream for the same image format. Belt and braces? You seem to be encoding an already encoded image as an image here. – user207421 Sep 17 '21 at 09:37
  • @user207421 my fault, I corrected the method name. I used the snippet provided by https://github.com/haraldk/TwelveMonkeys#advanced-usage , second code block. – HarleyDavidson Sep 17 '21 at 09:47
  • @fantaghiroccocametoRome no. The exception occurs because of the library is setting the type "TIFF_RATIONAL" and I do not know why. – HarleyDavidson Sep 17 '21 at 09:52
  • *"Sometimes, saving this TIF works like a charm without any problems. But in some cases, I receive the following exception. In this case, I have to restart the application and give it another try"* -- This may be a symptom that you have multiple TIFF plugins installed (JAI and TwelveMonkeys?), and that they are chosen somewhat random... – Harald K Sep 17 '21 at 10:21
  • It seems that your code example is using the TwelveMonkeys API directly for metadata, but the stack trace indicates you are using JAI for the actual writing. In theory, this *should* work (as the TwelveMonkeys TIFF plugin emulates the JAI TIFF metadata format), however, you seem to have run into a case where it doesn't fully work. Fell free to report this as an issue. But the easy fix is just to not use JAI in this case. – Harald K Sep 17 '21 at 10:42
  • @HaraldK so this is because I am using jdk11 with native tiff support? I will give it a try to disable JAI. – HarleyDavidson Sep 17 '21 at 10:57
  • Well, then you have 3 TIFF plugins... But no, I don't think JDK 11 is a problem. The TwelveMonkeys TIFF plugin does some magic to make sure it's preferred over the JDK one. It's possible to do the same for JAI, see [this answer](https://stackoverflow.com/a/38198991/1428606), but if you can, just removing it is easier. – Harald K Sep 17 '21 at 11:08
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/237208/discussion-between-harleydavidson-and-harald-k). – HarleyDavidson Sep 17 '21 at 11:17
  • 1
    FYI: https://github.com/haraldk/TwelveMonkeys/issues/628 (fixed but not yet released). – Harald K Sep 17 '21 at 18:37

1 Answers1

0

I think I found the answer by myself after crawling through the code.

When creating a new Object of TIFFEntry

new TIFFEntry(TIFF.TAG_X_RESOLUTION, new Rational(200));

I saw that in the constructor it is trying to guess the type. Because of I provided a Rational-object (like in the code-snippet I mentioned in the link in the question) it is setting the wrong type.
After setting the value as integer

new TIFFEntry(TIFF.TAG_X_RESOLUTION, 200)

it seems so work properly.

Update:

After talking to the developer @HaraldK he gave me the hint I needed:
I also using tess4j in my project, which has JAI Core in its dependencies.

So I updated my code that the right Writer is picked up:

Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
        if (!writers.hasNext()) {
            throw new IllegalArgumentException("No writer for: " + format);
        }

        ImageWriter writer = null;
        while (writers.hasNext()) {
            writer = writers.next();
            // Find the right writer
            if (writer.getClass().getName().contains("twelvemonkeys")) {
                break;
            }
            writer = null;
        }

        if (Objects.isNull(writer)) {
            throw new IOException("Could not find twelvemonkeys Writer");
        }
HarleyDavidson
  • 559
  • 6
  • 17