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  * @bug     8149028
  27  * @author  a.stepanov
  28  * @summary check TIFFDirectory manipulation
  29  *          by means of TIFFImageReadParam
  30  * @run     main TIFFImageReadParamTest
  31  */
  32 
  33 
  34 import java.awt.Color;
  35 import java.awt.Graphics;
  36 import java.awt.image.BufferedImage;
  37 import java.io.*;
  38 import javax.imageio.*;
  39 import javax.imageio.metadata.IIOMetadata;
  40 import javax.imageio.plugins.tiff.*;
  41 import javax.imageio.stream.ImageInputStream;
  42 import javax.imageio.stream.ImageOutputStream;
  43 
  44 public class TIFFImageReadParamTest {
  45 
  46     private final static String FILENAME = "test.tiff";
  47     private final static int SZ = 100;
  48     private final static Color C = Color.RED;
  49 
  50     private final static String GEO_DATA = "test params";
  51     private final static int GEO_N = GeoTIFFTagSet.TAG_GEO_ASCII_PARAMS;
  52 
  53     private final static String EXIF_DATA = "2000:01:01 00:00:01";
  54     private final static int EXIF_N = ExifTIFFTagSet.TAG_DATE_TIME_ORIGINAL;
  55 
  56     private final static String GPS_DATA =
  57         ExifGPSTagSet.STATUS_MEASUREMENT_IN_PROGRESS;
  58     private final static int GPS_N = ExifGPSTagSet.TAG_GPS_STATUS;
  59 
  60     private final static short FAX_DATA =
  61         FaxTIFFTagSet.CLEAN_FAX_DATA_ERRORS_UNCORRECTED;
  62     private final static int FAX_N = FaxTIFFTagSet.TAG_CLEAN_FAX_DATA;
  63 
  64     private ImageWriter getTIFFWriter() {
  65 
  66         java.util.Iterator<ImageWriter> writers =
  67             ImageIO.getImageWritersByFormatName("TIFF");
  68         if (!writers.hasNext()) {
  69             throw new RuntimeException("No writers available for TIFF format");
  70         }
  71         return writers.next();
  72     }
  73 
  74     private ImageReader getTIFFReader() {
  75 
  76         java.util.Iterator<ImageReader> readers =
  77             ImageIO.getImageReadersByFormatName("TIFF");
  78         if (!readers.hasNext()) {
  79             throw new RuntimeException("No readers available for TIFF format");
  80         }
  81         return readers.next();
  82     }
  83 
  84     private void check(boolean ok, String msg) {
  85         if (!ok) { throw new RuntimeException(msg); }
  86     }
  87 
  88     private void addASCIIField(TIFFDirectory d,
  89                                String        name,
  90                                String        data,
  91                                int           num) {
  92 
  93         String a[] = {data};
  94         d.addTIFFField(new TIFFField(
  95             new TIFFTag(name, num, 1 << TIFFTag.TIFF_ASCII),
  96                 TIFFTag.TIFF_ASCII, 1, a));
  97     }
  98 
  99     private void checkASCIIValue(TIFFDirectory d,
 100                                  String        what,
 101                                  String        data,
 102                                  int           num) {
 103 
 104         TIFFField f = d.getTIFFField(num);
 105         check(f.getType() == TIFFTag.TIFF_ASCII, "field type != ASCII");
 106         check(f.getCount() == 1, "invalid " + what + " data count");
 107         check(f.getValueAsString(0).equals(data),
 108             "invalid " + what + " data");
 109     }
 110 
 111 
 112     private void writeImage() throws Exception {
 113 
 114         OutputStream s = new BufferedOutputStream(new FileOutputStream(FILENAME));
 115         try (ImageOutputStream ios = ImageIO.createImageOutputStream(s)) {
 116             ImageWriter writer = getTIFFWriter();
 117             writer.setOutput(ios);
 118 
 119             BufferedImage img =
 120                 new BufferedImage(SZ, SZ, BufferedImage.TYPE_INT_RGB);
 121             Graphics g = img.getGraphics();
 122             g.setColor(C);
 123             g.fillRect(0, 0, SZ, SZ);
 124             g.dispose();
 125 
 126             IIOMetadata metadata = writer.getDefaultImageMetadata(
 127                 new ImageTypeSpecifier(img), writer.getDefaultWriteParam());
 128 
 129             TIFFDirectory dir = TIFFDirectory.createFromMetadata(metadata);
 130 
 131             // add some extension tags
 132             addASCIIField(dir, "GeoAsciiParamsTag", GEO_DATA, GEO_N);
 133             addASCIIField(dir, "DateTimeOriginal", EXIF_DATA, EXIF_N);
 134             addASCIIField(dir, "GPSStatus", GPS_DATA, GPS_N);
 135 
 136             dir.addTIFFField(new TIFFField(new TIFFTag(
 137                 "CleanFaxData", FAX_N, 1 << TIFFTag.TIFF_SHORT), FAX_DATA));
 138 
 139             IIOMetadata data = dir.getAsMetadata();
 140 
 141             writer.write(new IIOImage(img, null, data));
 142 
 143             ios.flush();
 144             writer.dispose();
 145         }
 146     }
 147 
 148     private void checkImage(BufferedImage img) {
 149 
 150         check(img.getWidth() == SZ, "invalid image width");
 151         check(img.getHeight() == SZ, "invalid image height");
 152         Color c = new Color(img.getRGB(SZ / 2, SZ / 2));
 153         check(c.equals(C), "invalid image color");
 154     }
 155 
 156     private TIFFDirectory getDir(TIFFTagSet[] add,
 157                                  TIFFTagSet[] remove) throws Exception {
 158 
 159         ImageReader reader = getTIFFReader();
 160 
 161         ImageInputStream s = ImageIO.createImageInputStream(new File(FILENAME));
 162         reader.setInput(s, false, true);
 163 
 164         int ni = reader.getNumImages(true);
 165         check(ni == 1, "invalid number of images: " + ni);
 166 
 167         TIFFImageReadParam param = new TIFFImageReadParam();
 168         for (TIFFTagSet ts: add) { param.addAllowedTagSet(ts); }
 169         for (TIFFTagSet ts: remove) { param.removeAllowedTagSet(ts); }
 170 
 171         IIOImage img = reader.readAll(0, param);
 172 
 173         // just in case, check image
 174         checkImage((BufferedImage) img.getRenderedImage());
 175 
 176         IIOMetadata metadata = img.getMetadata();
 177         TIFFDirectory dir = TIFFDirectory.createFromMetadata(metadata);
 178 
 179         reader.dispose();
 180         s.close();
 181 
 182         return dir;
 183     }
 184 
 185     private void simpleChecks() {
 186 
 187         TIFFImageReadParam param = new TIFFImageReadParam();
 188 
 189         java.util.List<TIFFTagSet> allowed = param.getAllowedTagSets();
 190 
 191         // see docs
 192         check(allowed.contains(BaselineTIFFTagSet.getInstance()),
 193             "must contain BaselineTIFFTagSet");
 194         check(allowed.contains(FaxTIFFTagSet.getInstance()),
 195             "must contain FaxTIFFTagSet");
 196         check(allowed.contains(ExifParentTIFFTagSet.getInstance()),
 197             "must contain ExifParentTIFFTagSet");
 198         check(allowed.contains(GeoTIFFTagSet.getInstance()),
 199             "must contain GeoTIFFTagSet");
 200 
 201         TIFFTagSet gps = ExifGPSTagSet.getInstance();
 202         param.addAllowedTagSet(gps);
 203         check(param.getAllowedTagSets().contains(gps),
 204             "must contain ExifGPSTagSet");
 205 
 206         param.removeAllowedTagSet(gps);
 207         check(!param.getAllowedTagSets().contains(gps),
 208             "must not contain ExifGPSTagSet");
 209 
 210         // check that repeating remove goes properly
 211         param.removeAllowedTagSet(gps);
 212 
 213         boolean ok = false;
 214         try { param.addAllowedTagSet(null); }
 215         catch (IllegalArgumentException e) { ok = true; }
 216         check(ok, "must not be able to add null tag set");
 217 
 218         ok = false;
 219         try { param.removeAllowedTagSet(null); }
 220         catch (IllegalArgumentException e) { ok = true; }
 221         check(ok, "must not be able to remove null tag set");
 222     }
 223 
 224     private void run() {
 225 
 226         simpleChecks();
 227 
 228         try {
 229 
 230             writeImage();
 231 
 232             TIFFTagSet
 233                 empty[] = {},
 234                 geo[]   = {  GeoTIFFTagSet.getInstance() },
 235                 exif[]  = { ExifTIFFTagSet.getInstance() },
 236                 gps[]   = {  ExifGPSTagSet.getInstance() },
 237                 fax[]   = {  FaxTIFFTagSet.getInstance() };
 238 
 239             // default param state
 240             TIFFDirectory dir = getDir(empty, empty);
 241             // Geo and Fax are default allowed tag sets
 242             check(dir.containsTIFFField(GEO_N), "must contain Geo field");
 243             checkASCIIValue(dir, "Geo", GEO_DATA, GEO_N);
 244             check(dir.containsTIFFField(FAX_N), "must contain Fax field");
 245             check(
 246                 (dir.getTIFFField(FAX_N).getCount() == 1) &&
 247                 (dir.getTIFFField(FAX_N).getAsInt(0) == FAX_DATA),
 248                 "invalid Fax field value");
 249 
 250             // corresponding tag sets are non-default
 251             check(!dir.containsTIFFField(EXIF_N), "must not contain Geo field");
 252             check(!dir.containsTIFFField(GPS_N), "must not contain GPS field");
 253 
 254             // remove Fax
 255             dir = getDir(empty, fax);
 256             check(!dir.containsTIFFField(FAX_N), "must not contain Fax field");
 257 
 258             // add EXIF, remove Geo
 259             dir = getDir(exif, geo);
 260             check(dir.containsTIFFField(EXIF_N), "must contain EXIF field");
 261             checkASCIIValue(dir, "EXIF", EXIF_DATA, EXIF_N);
 262             check(!dir.containsTIFFField(GEO_N), "must not contain Geo field");
 263 
 264             // add GPS
 265             dir = getDir(gps, empty);
 266             check(dir.containsTIFFField(GPS_N), "must contain GPS field");
 267             checkASCIIValue(dir, "GPS", GPS_DATA, GPS_N);
 268 
 269         } catch (Exception e) { throw new RuntimeException(e); }
 270     }
 271 
 272     public static void main(String[] args) {
 273         (new TIFFImageReadParamTest()).run();
 274     }
 275 }