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
34import java.awt.Color;
35import java.awt.Graphics;
36import java.awt.image.BufferedImage;
37import java.io.*;
38import javax.imageio.*;
39import javax.imageio.metadata.IIOMetadata;
40import javax.imageio.plugins.tiff.*;
41import javax.imageio.stream.ImageInputStream;
42import javax.imageio.stream.ImageOutputStream;
43
44public 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, false);
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}
276