From 016977e3825056e2640e7c637660c6eb847a12fb Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Mon, 14 Feb 2022 22:00:04 +0100 Subject: [PATCH] PNM: New attempt at making the new header parser work on Windows. (cherry picked from commit 4b951c06ccbd8f61806eaf67516ff1de964446c7) --- .../imageio/plugins/pnm/PNMHeaderParser.java | 87 +++++++++++++------ .../plugins/pnm/Plain16BitDecoder.java | 6 +- .../imageio/plugins/pnm/Plain8BitDecoder.java | 6 +- .../plugins/pnm/PNMImageReaderTest.java | 13 ++- 4 files changed, 67 insertions(+), 45 deletions(-) diff --git a/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/PNMHeaderParser.java b/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/PNMHeaderParser.java index 020bda12..458fb0e7 100755 --- a/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/PNMHeaderParser.java +++ b/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/PNMHeaderParser.java @@ -30,8 +30,10 @@ package com.twelvemonkeys.imageio.plugins.pnm; -import javax.imageio.IIOException; +import com.twelvemonkeys.io.FastByteArrayOutputStream; + import javax.imageio.stream.ImageInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -70,45 +72,74 @@ final class PNMHeaderParser extends HeaderParser { List comments = new ArrayList<>(); + StringBuilder tokenBuffer = new StringBuilder(); + while (width == 0 || height == 0 || maxSample == 0) { - String line = input.readLine(); + tokenBuffer.delete(0, tokenBuffer.length()); - if (line == null) { - throw new IIOException("Unexpected end of stream"); - } + while (tokenBuffer.length() < 16) { // Limit reads if we should read across into the binary part... + byte read = input.readByte(); - int commentStart = line.indexOf('#'); - if (commentStart >= 0) { - String comment = line.substring(commentStart + 1).trim(); - if (!comment.isEmpty()) { - comments.add(comment); + if (read == '#') { + // Read rest of the line as comment + String comment = readLineUTF8(input).trim(); + + if (!comment.isEmpty()) { + comments.add(comment); + } + + break; + } + else if (Character.isWhitespace((char) read)) { + if (tokenBuffer.length() > 0) { + break; + } + } + else { + tokenBuffer.append((char) read); } - - line = line.substring(0, commentStart); } - line = line.trim(); + String token = tokenBuffer.toString().trim(); - if (!line.isEmpty()) { + if (!token.isEmpty()) { // We have tokens... - String[] tokens = line.split("\\s"); - for (String token : tokens) { - if (width == 0) { - width = Integer.parseInt(token); - } - else if (height == 0) { - height = Integer.parseInt(token); - } - else if (maxSample == 0) { - maxSample = Integer.parseInt(token); - } - else { - throw new IIOException("Unknown PNM token: " + token); - } + if (width == 0) { + width = Integer.parseInt(token); + } + else if (height == 0) { + height = Integer.parseInt(token); + } + else { + maxSample = Integer.parseInt(token); } } } return new PNMHeader(fileType, tupleType, width, height, tupleType.getSamplesPerPixel(), maxSample, comments); } + + // Similar to DataInput.readLine, except it uses UTF8 encoding + private static String readLineUTF8(final ImageInputStream input) throws IOException { + ByteArrayOutputStream buffer = new FastByteArrayOutputStream(128); + + int value; + do { + switch (value = input.read()) { + case '\r': + // Check for CR + LF pattern and skip, otherwise fall through + if (input.read() != '\n') { + input.seek(input.getStreamPosition() - 1); + } + case '\n': + case -1: + value = -1; + break; + default: + buffer.write(value); + } + } while (value != -1); + + return buffer.toString("UTF8"); + } } diff --git a/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/Plain16BitDecoder.java b/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/Plain16BitDecoder.java index e759ea78..514d5d0e 100755 --- a/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/Plain16BitDecoder.java +++ b/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/Plain16BitDecoder.java @@ -58,17 +58,13 @@ final class Plain16BitDecoder extends InputStream { } // Each number is one byte. Skip whitespace. - if (currentLine == null || !currentLine.hasNext()) { + while (currentLine == null || !currentLine.hasNext()) { String line = reader.readLine(); if (line == null) { return -1; } currentLine = new StringTokenIterator(line); - - if (!currentLine.hasNext()) { - return -1; - } } int next = Integer.parseInt(currentLine.next()) & 0xffff; diff --git a/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/Plain8BitDecoder.java b/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/Plain8BitDecoder.java index 3d419273..da48a9d2 100755 --- a/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/Plain8BitDecoder.java +++ b/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/Plain8BitDecoder.java @@ -50,17 +50,13 @@ final class Plain8BitDecoder extends InputStream { @Override public int read() throws IOException { // Each number is one byte. Skip whitespace. - if (currentLine == null || !currentLine.hasNext()) { + while (currentLine == null || !currentLine.hasNext()) { String line = reader.readLine(); if (line == null) { return -1; } currentLine = new StringTokenIterator(line); - - if (!currentLine.hasNext()) { - return -1; - } } return Integer.parseInt(currentLine.next()) & 0xff; diff --git a/imageio/imageio-pnm/src/test/java/com/twelvemonkeys/imageio/plugins/pnm/PNMImageReaderTest.java b/imageio/imageio-pnm/src/test/java/com/twelvemonkeys/imageio/plugins/pnm/PNMImageReaderTest.java index f9546d1f..2b363906 100755 --- a/imageio/imageio-pnm/src/test/java/com/twelvemonkeys/imageio/plugins/pnm/PNMImageReaderTest.java +++ b/imageio/imageio-pnm/src/test/java/com/twelvemonkeys/imageio/plugins/pnm/PNMImageReaderTest.java @@ -52,13 +52,12 @@ public class PNMImageReaderTest extends ImageReaderAbstractTest @Override protected List getTestData() { return Arrays.asList( - new TestData(getClassLoaderResource("/ppm/lighthouse_rgb48.ppm"), new Dimension(768, 512)), // P6 (PPM RAW, 16 bits/sample) -// new TestData(getClassLoaderResource("/ppm/snail2.pnm"), new Dimension(256, 256)), // P6 (PPM RAW) + new TestData(getClassLoaderResource("/ppm/snail2.pnm"), new Dimension(256, 256)), // P6 (PPM RAW) new TestData(getClassLoaderResource("/ppm/colors.ppm"), new Dimension(3, 2)), // P3 (PPM PLAIN) new TestData(getClassLoaderResource("/pbm/j.pbm"), new Dimension(6, 10)), // P1 (PBM PLAIN) -// new TestData(getClassLoaderResource("/pbm/circle2.pnm"), new Dimension(200, 200)), // P4 (PBM RAW) + new TestData(getClassLoaderResource("/pbm/circle2.pnm"), new Dimension(200, 200)), // P4 (PBM RAW) new TestData(getClassLoaderResource("/pgm/feep.pgm"), new Dimension(24, 7)), // P2 (PGM PLAIN) -// new TestData(getClassLoaderResource("/pgm/rays2.pnm"), new Dimension(200, 200)), // P4 (PGM RAW) + new TestData(getClassLoaderResource("/pgm/rays2.pnm"), new Dimension(200, 200)), // P4 (PGM RAW) new TestData(getClassLoaderResource("/pgm/feep16.pgm"), new Dimension(24, 7)), // P2 (PGM PLAIN, 16 bits/sample) new TestData(getClassLoaderResource("/pgm/house.l.pgm"), new Dimension(367, 241)), // P5 (PGM RAW) new TestData(getClassLoaderResource("/ppm/lighthouse_rgb48.ppm"), new Dimension(768, 512)), // P6 (PPM RAW, 16 bits/sample) @@ -69,12 +68,12 @@ public class PNMImageReaderTest extends ImageReaderAbstractTest @Override protected List getTestDataForAffineTransformOpCompatibility() { return Arrays.asList( -// new TestData(getClassLoaderResource("/ppm/snail2.pnm"), new Dimension(256, 256)), // P6 (PPM RAW) + new TestData(getClassLoaderResource("/ppm/snail2.pnm"), new Dimension(256, 256)), // P6 (PPM RAW) new TestData(getClassLoaderResource("/ppm/colors.ppm"), new Dimension(3, 2)), // P3 (PPM PLAIN) new TestData(getClassLoaderResource("/pbm/j.pbm"), new Dimension(6, 10)), // P1 (PBM PLAIN) -// new TestData(getClassLoaderResource("/pbm/circle2.pnm"), new Dimension(200, 200)), // P4 (PBM RAW) + new TestData(getClassLoaderResource("/pbm/circle2.pnm"), new Dimension(200, 200)), // P4 (PBM RAW) new TestData(getClassLoaderResource("/pgm/feep.pgm"), new Dimension(24, 7)), // P2 (PGM PLAIN) -// new TestData(getClassLoaderResource("/pgm/rays2.pnm"), new Dimension(200, 200)), // P4 (PGM RAW) + new TestData(getClassLoaderResource("/pgm/rays2.pnm"), new Dimension(200, 200)), // P4 (PGM RAW) new TestData(getClassLoaderResource("/pgm/feep16.pgm"), new Dimension(24, 7)), // P2 (PGM PLAIN, 16 bits/sample) new TestData(getClassLoaderResource("/pgm/house.l.pgm"), new Dimension(367, 241)), // P5 (PGM RAW) new TestData(getClassLoaderResource("/ppm/lighthouse_rgb48.ppm"), new Dimension(768, 512)) // P6 (PPM RAW, 16 bits/sample)