尝试使用 dpi 保存 tif 时出现奇怪的异常

Strange exception when trying to save tif with dpi

Java-版本:
openjdk 版本“11”2018-09-25
OpenJDK 运行时环境 18.9(内部版本 11+28)
OpenJDK 64 位服务器 VM 18.9(构建 11+28,混合模式)

TwelveMonkeys ImageIO 依赖项:

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

案例:
我正在尝试将子图像保存为带有 dpi 信息的 tif。
有时它有效,有时我得到一个奇怪的异常。

首先,这是我创建子图像的代码:

//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"));

确保此子图像有效。

现在方法“saveImage”,受此启发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();
        }
    }

有时,保存此 TIF 非常有效,没有任何问题。 但在某些情况下,我会收到以下异常。在这种情况下,我必须重新启动应用程序并再次尝试:

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)

我查看了源代码,发现这个 class 此时正试图拆分一个值:
在 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);

其中“值”= 200。

我不知道这个常量“TIFF_RATIONAL”在哪里以及为什么被设置。

这个错误从何而来,为什么?当我通过将第三个参数设置为 null 来禁用在我的保存方法中设置 tiffImageMetadata 时:

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

一切正常。但是我的 tif 图像没有设置任何 dpi 值。

我想我是通过代码爬取后自己找到了答案的。

创建 TIFFEntry 的新对象时

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

我看到它在构造函数中试图猜测类型。因为我提供了一个 Rational 对象(就像我在问题 link 中提到的代码片段中一样),它设置了错误的类型。
将值设置为整数后

new TIFFEntry(TIFF.TAG_X_RESOLUTION, 200)

看起来很正常。

更新:

在与开发人员@HaraldK 交谈后,他给了我需要的提示:
我还在我的项目中使用了 tess4j,它的依赖项中有 JAI Core。

所以我更新了我的代码,选择了正确的 Writer:

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");
        }