1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @ignore 8148454 27 * @bug 8152183 8148454 28 * @author a.stepanov 29 * @summary check that TIFFields are derived properly for multi-page tiff 30 * @run main MultiPageImageTIFFFieldTest 31 */ 32 33 import java.awt.*; 34 import java.awt.color.*; 35 import java.awt.image.BufferedImage; 36 import java.io.*; 37 import javax.imageio.*; 38 import javax.imageio.metadata.*; 39 import javax.imageio.stream.*; 40 import javax.imageio.plugins.tiff.*; 41 42 43 public class MultiPageImageTIFFFieldTest { 44 45 private final static String FILENAME = "test.tiff"; 46 private final static int W1 = 20, H1 = 40, W2 = 100, H2 = 15; 47 private final static Color C1 = Color.BLACK, C2 = Color.RED; 48 49 private final static int N_WIDTH = BaselineTIFFTagSet.TAG_IMAGE_WIDTH; 50 private final static int N_HEIGHT = BaselineTIFFTagSet.TAG_IMAGE_LENGTH; 51 52 private static final String DESCRIPTION_1[] = {"Description-1", "abc ABC"}; 53 private static final String DESCRIPTION_2[] = {"Description-2", "1-2-3"}; 54 private final static int N_DESCRIPTION = 55 BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION; 56 57 private final static String EXIF_DATA_1[] = {"2001:01:01 00:00:01"}; 58 private final static String EXIF_DATA_2[] = {"2002:02:02 00:00:02"}; 59 private final static int N_EXIF = ExifTIFFTagSet.TAG_DATE_TIME_ORIGINAL; 60 61 private final static String GPS_DATA[] = { 62 ExifGPSTagSet.STATUS_MEASUREMENT_IN_PROGRESS}; 63 private final static int N_GPS = ExifGPSTagSet.TAG_GPS_STATUS; 64 65 private final static short FAX_DATA = 66 FaxTIFFTagSet.CLEAN_FAX_DATA_ERRORS_UNCORRECTED; 67 private final static int N_FAX = FaxTIFFTagSet.TAG_CLEAN_FAX_DATA; 68 69 private static final byte[] ICC_PROFILE_2 = 70 ICC_ProfileRGB.getInstance(ColorSpace.CS_sRGB).getData(); 71 private static final int N_ICC = BaselineTIFFTagSet.TAG_ICC_PROFILE; 72 73 private static final int N_BPS = BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE; 74 75 private static final int 76 COMPRESSION_1 = BaselineTIFFTagSet.COMPRESSION_DEFLATE, 77 COMPRESSION_2 = BaselineTIFFTagSet.COMPRESSION_LZW; 78 private static final int N_COMPRESSION = BaselineTIFFTagSet.TAG_COMPRESSION; 79 80 private static final int 81 GRAY_1 = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO, 82 GRAY_2 = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO, 83 RGB = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB; 84 85 private static final int N_PHOTO = 86 BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION; 87 88 private ImageWriter getTIFFWriter() { 89 90 java.util.Iterator<ImageWriter> writers = 91 ImageIO.getImageWritersByFormatName("TIFF"); 92 if (!writers.hasNext()) { 93 throw new RuntimeException("No writers available for TIFF format"); 94 } 95 return writers.next(); 96 } 97 98 private ImageReader getTIFFReader() { 99 100 java.util.Iterator<ImageReader> readers = 101 ImageIO.getImageReadersByFormatName("TIFF"); 102 if (!readers.hasNext()) { 103 throw new RuntimeException("No readers available for TIFF format"); 104 } 105 return readers.next(); 106 } 107 108 private void addASCIIField(TIFFDirectory d, 109 String name, 110 String data[], 111 int num) { 112 113 d.addTIFFField(new TIFFField( 114 new TIFFTag(name, num, 1 << TIFFTag.TIFF_ASCII), 115 TIFFTag.TIFF_ASCII, data.length, data)); 116 } 117 118 private void checkASCIIField(TIFFDirectory d, 119 String what, 120 String data[], 121 int num) { 122 123 String notFound = what + " field was not found"; 124 check(d.containsTIFFField(num), notFound); 125 TIFFField f = d.getTIFFField(num); 126 check(f.getType() == TIFFTag.TIFF_ASCII, "field type != ASCII"); 127 check(f.getCount() == data.length, "invalid " + what + " data count"); 128 for (int i = 0; i < data.length; i++) { 129 check(f.getValueAsString(i).equals(data[i]), 130 "invalid " + what + " data"); 131 } 132 } 133 134 private void writeImage() throws Exception { 135 136 OutputStream s = new BufferedOutputStream(new FileOutputStream(FILENAME)); 137 try (ImageOutputStream ios = ImageIO.createImageOutputStream(s)) { 138 139 ImageWriter writer = getTIFFWriter(); 140 writer.setOutput(ios); 141 142 BufferedImage img1 = 143 new BufferedImage(W1, H1, BufferedImage.TYPE_BYTE_GRAY); 144 Graphics g = img1.getGraphics(); 145 g.setColor(C1); 146 g.fillRect(0, 0, W1, H1); 147 g.dispose(); 148 149 BufferedImage img2 = 150 new BufferedImage(W2, H2, BufferedImage.TYPE_INT_RGB); 151 g = img2.getGraphics(); 152 g.setColor(C2); 153 g.fillRect(0, 0, W2, H2); 154 g.dispose(); 155 156 ImageWriteParam param1 = writer.getDefaultWriteParam(); 157 param1.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); 158 param1.setCompressionType("Deflate"); 159 param1.setCompressionQuality(0.5f); 160 161 ImageWriteParam param2 = writer.getDefaultWriteParam(); 162 param2.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); 163 param2.setCompressionType("LZW"); 164 param2.setCompressionQuality(0.5f); 165 166 IIOMetadata 167 md1 = writer.getDefaultImageMetadata( 168 new ImageTypeSpecifier(img1), param1), 169 md2 = writer.getDefaultImageMetadata( 170 new ImageTypeSpecifier(img2), param2); 171 172 TIFFDirectory 173 dir1 = TIFFDirectory.createFromMetadata(md1), 174 dir2 = TIFFDirectory.createFromMetadata(md2); 175 176 addASCIIField(dir1, "ImageDescription", DESCRIPTION_1, N_DESCRIPTION); 177 addASCIIField(dir2, "ImageDescription", DESCRIPTION_2, N_DESCRIPTION); 178 179 addASCIIField(dir1, "GPSStatus", GPS_DATA, N_GPS); 180 addASCIIField(dir2, "GPSStatus", GPS_DATA, N_GPS); 181 182 addASCIIField(dir1, "DateTimeOriginal", EXIF_DATA_1, N_EXIF); 183 addASCIIField(dir2, "DateTimeOriginal", EXIF_DATA_2, N_EXIF); 184 185 TIFFTag faxTag = new TIFFTag( 186 "CleanFaxData", N_FAX, 1 << TIFFTag.TIFF_SHORT); 187 dir1.addTIFFField(new TIFFField(faxTag, FAX_DATA)); 188 dir2.addTIFFField(new TIFFField(faxTag, FAX_DATA)); 189 190 dir2.addTIFFField(new TIFFField( 191 new TIFFTag("ICC Profile", N_ICC, 1 << TIFFTag.TIFF_UNDEFINED), 192 TIFFTag.TIFF_UNDEFINED, ICC_PROFILE_2.length, ICC_PROFILE_2)); 193 194 writer.prepareWriteSequence(null); 195 writer.writeToSequence( 196 new IIOImage(img1, null, dir1.getAsMetadata()), param1); 197 writer.writeToSequence( 198 new IIOImage(img2, null, dir2.getAsMetadata()), param2); 199 writer.endWriteSequence(); 200 201 ios.flush(); 202 writer.dispose(); 203 } 204 s.close(); 205 } 206 207 private void checkBufferedImages(BufferedImage im1, BufferedImage im2) { 208 209 check(im1.getWidth() == W1, "invalid width for image 1"); 210 check(im1.getHeight() == H1, "invalid height for image 1"); 211 check(im2.getWidth() == W2, "invalid width for image 2"); 212 check(im2.getHeight() == H2, "invalid height for image 2"); 213 214 Color 215 c1 = new Color(im1.getRGB(W1 / 2, H1 / 2)), 216 c2 = new Color(im2.getRGB(W2 / 2, H2 / 2)); 217 218 check(c1.equals(C1), "invalid image 1 color"); 219 check(c2.equals(C2), "invalid image 2 color"); 220 } 221 222 private void readAndCheckImage() throws Exception { 223 224 ImageReader reader = getTIFFReader(); 225 226 ImageInputStream s = ImageIO.createImageInputStream(new File(FILENAME)); 227 reader.setInput(s, false, true); 228 229 int ni = reader.getNumImages(true); 230 check(ni == 2, "invalid number of images"); 231 232 // check TIFFImageReadParam for multipage image 233 TIFFImageReadParam 234 param1 = new TIFFImageReadParam(), param2 = new TIFFImageReadParam(); 235 236 param1.addAllowedTagSet(ExifTIFFTagSet.getInstance()); 237 param1.addAllowedTagSet(ExifGPSTagSet.getInstance()); 238 239 param2.addAllowedTagSet(ExifTIFFTagSet.getInstance()); 240 param2.addAllowedTagSet(GeoTIFFTagSet.getInstance()); 241 242 // FaxTIFFTagSet is allowed by default 243 param2.removeAllowedTagSet(FaxTIFFTagSet.getInstance()); 244 245 246 // read images and metadata 247 IIOImage i1 = reader.readAll(0, param1), i2 = reader.readAll(1, param2); 248 BufferedImage 249 bi1 = (BufferedImage) i1.getRenderedImage(), 250 bi2 = (BufferedImage) i2.getRenderedImage(); 251 252 // check rendered images, just in case 253 checkBufferedImages(bi1, bi2); 254 255 TIFFDirectory 256 dir1 = TIFFDirectory.createFromMetadata(i1.getMetadata()), 257 dir2 = TIFFDirectory.createFromMetadata(i2.getMetadata()); 258 259 // check ASCII fields 260 checkASCIIField( 261 dir1, "image 1 description", DESCRIPTION_1, N_DESCRIPTION); 262 checkASCIIField( 263 dir2, "image 2 description", DESCRIPTION_2, N_DESCRIPTION); 264 265 checkASCIIField(dir1, "image 1 datetime", EXIF_DATA_1, N_EXIF); 266 checkASCIIField(dir2, "image 2 datetime", EXIF_DATA_2, N_EXIF); 267 268 // check sizes 269 TIFFField f = dir1.getTIFFField(N_WIDTH); 270 check((f.getCount() == 1) && (f.getAsInt(0) == W1), 271 "invalid width field for image 1"); 272 f = dir2.getTIFFField(N_WIDTH); 273 check((f.getCount() == 1) && (f.getAsInt(0) == W2), 274 "invalid width field for image 2"); 275 276 f = dir1.getTIFFField(N_HEIGHT); 277 check((f.getCount() == 1) && (f.getAsInt(0) == H1), 278 "invalid height field for image 1"); 279 f = dir2.getTIFFField(N_HEIGHT); 280 check((f.getCount() == 1) && (f.getAsInt(0) == H2), 281 "invalid height field for image 2"); 282 283 // check fax data 284 check(dir1.containsTIFFField(N_FAX), "image 2 TIFF directory " + 285 "must contain clean fax data"); 286 f = dir1.getTIFFField(N_FAX); 287 check( 288 (f.getCount() == 1) && f.isIntegral() && (f.getAsInt(0) == FAX_DATA), 289 "invalid clean fax data"); 290 291 check(!dir2.containsTIFFField(N_FAX), "image 2 TIFF directory " + 292 "must not contain fax fields"); 293 294 // check GPS data 295 checkASCIIField(dir1, "GPS status", GPS_DATA, N_GPS); 296 297 check(!dir2.containsTIFFField(N_GPS), "image 2 TIFF directory " + 298 "must not contain GPS fields"); 299 300 // check ICC profile data 301 check(!dir1.containsTIFFField(N_ICC), "image 1 TIFF directory " 302 + "must not contain ICC Profile field"); 303 check(dir2.containsTIFFField(N_ICC), "image 2 TIFF directory " 304 + "must contain ICC Profile field"); 305 306 f = dir2.getTIFFField(N_ICC); 307 check(f.getType() == TIFFTag.TIFF_UNDEFINED, 308 "invalid ICC profile field type"); 309 int cnt = f.getCount(); 310 byte icc[] = f.getAsBytes(); 311 check((cnt == ICC_PROFILE_2.length) && (cnt == icc.length), 312 "invalid ICC profile"); 313 for (int i = 0; i < cnt; i++) { 314 check(icc[i] == ICC_PROFILE_2[i], "invalid ICC profile data"); 315 } 316 317 // check component sizes 318 check(dir1.getTIFFField(N_BPS).isIntegral() && 319 dir2.getTIFFField(N_BPS).isIntegral(), 320 "invalid bits per sample type"); 321 int sz1[] = bi1.getColorModel().getComponentSize(), 322 sz2[] = bi2.getColorModel().getComponentSize(), 323 bps1[] = dir1.getTIFFField(N_BPS).getAsInts(), 324 bps2[] = dir2.getTIFFField(N_BPS).getAsInts(); 325 326 check((bps1.length == sz1.length) && (bps2.length == sz2.length), 327 "invalid component size count"); 328 329 for (int i = 0; i < bps1.length; i++) { 330 check(bps1[i] == sz1[i], "image 1: invalid bits per sample data"); 331 } 332 333 for (int i = 0; i < bps2.length; i++) { 334 check(bps2[i] == sz2[i], "image 2: invalid bits per sample data"); 335 } 336 337 // check compression data 338 check(dir1.containsTIFFField(N_COMPRESSION) && 339 dir2.containsTIFFField(N_COMPRESSION), 340 "compression info lost"); 341 f = dir1.getTIFFField(N_COMPRESSION); 342 check(f.isIntegral() && (f.getCount() == 1) && 343 (f.getAsInt(0) == COMPRESSION_1), "invalid image 1 compression data"); 344 345 f = dir2.getTIFFField(N_COMPRESSION); 346 check(f.isIntegral() && (f.getCount() == 1) && 347 (f.getAsInt(0) == COMPRESSION_2), "invalid image 2 compression data"); 348 349 // check photometric interpretation 350 f = dir1.getTIFFField(N_PHOTO); 351 check(f.isIntegral() && (f.getCount() == 1) && 352 ((f.getAsInt(0) == GRAY_1) || (f.getAsInt(0) == GRAY_2)), 353 "invalid photometric interpretation for image 1"); 354 355 f = dir2.getTIFFField(N_PHOTO); 356 check(f.isIntegral() && (f.getCount() == 1) && (f.getAsInt(0) == RGB), 357 "invalid photometric interpretation for image 2"); 358 } 359 360 public void run() { 361 362 try { 363 writeImage(); 364 readAndCheckImage(); 365 } catch (Exception e) { 366 throw new RuntimeException(e); 367 } 368 } 369 370 private void check(boolean ok, String msg) { 371 372 if (!ok) { throw new RuntimeException(msg); } 373 } 374 375 public static void main(String[] args) { 376 (new MultiPageImageTIFFFieldTest()).run(); 377 } 378 }