test/jdk/javax/imageio/plugins/png/ItxtUtf8Test.java
changeset 47216 71c04702a3d5
parent 31160 426afa1c5005
child 52030 57862a02bf4b
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2008, 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 6541476 6782079
       
    27  * @summary Write and read a PNG file including an non-latin1 iTXt chunk
       
    28  *          Test also verifies that trunkated png images does not cause
       
    29  *          an OoutOfMemory error.
       
    30  *
       
    31  * @run main ItxtUtf8Test
       
    32  *
       
    33  * @run main/othervm/timeout=10 -Xmx4m ItxtUtf8Test truncate
       
    34  */
       
    35 
       
    36 import java.awt.image.BufferedImage;
       
    37 import java.io.ByteArrayInputStream;
       
    38 import java.io.ByteArrayOutputStream;
       
    39 import java.io.OutputStream;
       
    40 import java.util.Arrays;
       
    41 import java.util.List;
       
    42 import javax.imageio.IIOException;
       
    43 import javax.imageio.IIOImage;
       
    44 import javax.imageio.ImageIO;
       
    45 import javax.imageio.ImageReader;
       
    46 import javax.imageio.ImageTypeSpecifier;
       
    47 import javax.imageio.ImageWriter;
       
    48 import javax.imageio.metadata.IIOMetadata;
       
    49 import javax.imageio.stream.ImageInputStream;
       
    50 import javax.imageio.stream.ImageOutputStream;
       
    51 import javax.imageio.stream.MemoryCacheImageInputStream;
       
    52 import javax.imageio.stream.MemoryCacheImageOutputStream;
       
    53 import org.w3c.dom.DOMImplementation;
       
    54 import org.w3c.dom.Document;
       
    55 import org.w3c.dom.Element;
       
    56 import org.w3c.dom.Node;
       
    57 import org.w3c.dom.bootstrap.DOMImplementationRegistry;
       
    58 
       
    59 public class ItxtUtf8Test {
       
    60 
       
    61     public static final String
       
    62     TEXT = "\u24c9\u24d4\u24e7\u24e3" +
       
    63       "\ud835\udc13\ud835\udc1e\ud835\udc31\ud835\udc2d" +
       
    64       "\u24c9\u24d4\u24e7\u24e3", // a repetition for compression
       
    65     VERBATIM = "\u24e5\u24d4\u24e1\u24d1\u24d0\u24e3\u24d8\u24dc",
       
    66     COMPRESSED = "\u24d2\u24de\u24dc\u24df\u24e1\u24d4\u24e2\u24e2\u24d4\u24d3";
       
    67 
       
    68     public static final byte[]
       
    69     VBYTES = {
       
    70         (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x56, // chunk length
       
    71         (byte)0x69, (byte)0x54, (byte)0x58, (byte)0x74, // chunk type "iTXt"
       
    72         (byte)0x76, (byte)0x65, (byte)0x72, (byte)0x62,
       
    73         (byte)0x61, (byte)0x74, (byte)0x69, (byte)0x6d, // keyword "verbatim"
       
    74         (byte)0x00, // separator terminating keyword
       
    75         (byte)0x00, // compression flag
       
    76         (byte)0x00, // compression method, must be zero
       
    77         (byte)0x78, (byte)0x2d, (byte)0x63, (byte)0x69,
       
    78         (byte)0x72, (byte)0x63, (byte)0x6c, (byte)0x65,
       
    79         (byte)0x64, // language tag "x-circled"
       
    80         (byte)0x00, // separator terminating language tag
       
    81         (byte)0xe2, (byte)0x93, (byte)0xa5, // '\u24e5'
       
    82         (byte)0xe2, (byte)0x93, (byte)0x94, // '\u24d4'
       
    83         (byte)0xe2, (byte)0x93, (byte)0xa1, // '\u24e1'
       
    84         (byte)0xe2, (byte)0x93, (byte)0x91, // '\u24d1'
       
    85         (byte)0xe2, (byte)0x93, (byte)0x90, // '\u24d0'
       
    86         (byte)0xe2, (byte)0x93, (byte)0xa3, // '\u24e3'
       
    87         (byte)0xe2, (byte)0x93, (byte)0x98, // '\u24d8'
       
    88         (byte)0xe2, (byte)0x93, (byte)0x9c, // '\u24dc'
       
    89         (byte)0x00, // separator terminating the translated keyword
       
    90         (byte)0xe2, (byte)0x93, (byte)0x89, // '\u24c9'
       
    91         (byte)0xe2, (byte)0x93, (byte)0x94, // '\u24d4'
       
    92         (byte)0xe2, (byte)0x93, (byte)0xa7, // '\u24e7'
       
    93         (byte)0xe2, (byte)0x93, (byte)0xa3, // '\u24e3'
       
    94         (byte)0xf0, (byte)0x9d, (byte)0x90, (byte)0x93, // '\ud835\udc13'
       
    95         (byte)0xf0, (byte)0x9d, (byte)0x90, (byte)0x9e, // '\ud835\udc1e'
       
    96         (byte)0xf0, (byte)0x9d, (byte)0x90, (byte)0xb1, // '\ud835\udc31'
       
    97         (byte)0xf0, (byte)0x9d, (byte)0x90, (byte)0xad, // '\ud835\udc2d'
       
    98         (byte)0xe2, (byte)0x93, (byte)0x89, // '\u24c9'
       
    99         (byte)0xe2, (byte)0x93, (byte)0x94, // '\u24d4'
       
   100         (byte)0xe2, (byte)0x93, (byte)0xa7, // '\u24e7'
       
   101         (byte)0xe2, (byte)0x93, (byte)0xa3, // '\u24e3'
       
   102         (byte)0xb5, (byte)0xcc, (byte)0x97, (byte)0x56 // CRC
       
   103     },
       
   104     CBYTES = {
       
   105         // we don't want to check the chunk length,
       
   106         // as this might depend on implementation.
       
   107         (byte)0x69, (byte)0x54, (byte)0x58, (byte)0x74, // chunk type "iTXt"
       
   108         (byte)0x63, (byte)0x6f, (byte)0x6d, (byte)0x70,
       
   109         (byte)0x72, (byte)0x65, (byte)0x73, (byte)0x73,
       
   110         (byte)0x65, (byte)0x64, // keyword "compressed"
       
   111         (byte)0x00, // separator terminating keyword
       
   112         (byte)0x01, // compression flag
       
   113         (byte)0x00, // compression method, 0=deflate
       
   114         (byte)0x78, (byte)0x2d, (byte)0x63, (byte)0x69,
       
   115         (byte)0x72, (byte)0x63, (byte)0x6c, (byte)0x65,
       
   116         (byte)0x64, // language tag "x-circled"
       
   117         (byte)0x00, // separator terminating language tag
       
   118         // we don't want to check the actual compressed data,
       
   119         // as this might depend on implementation.
       
   120     };
       
   121 /*
       
   122 */
       
   123 
       
   124     public static void main(String[] args) throws Exception {
       
   125         List argList = Arrays.asList(args);
       
   126         if (argList.contains("truncate")) {
       
   127             try {
       
   128                 runTest(false, true);
       
   129                 throw new AssertionError("Expect an error for truncated file");
       
   130             }
       
   131             catch (IIOException e) {
       
   132                 // expected an error for a truncated image file.
       
   133             }
       
   134         }
       
   135         else {
       
   136             runTest(argList.contains("dump"), false);
       
   137         }
       
   138     }
       
   139 
       
   140     public static void runTest(boolean dump, boolean truncate)
       
   141         throws Exception
       
   142     {
       
   143         String format = "javax_imageio_png_1.0";
       
   144         BufferedImage img =
       
   145             new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB);
       
   146         ImageWriter iw = ImageIO.getImageWritersByMIMEType("image/png").next();
       
   147         ByteArrayOutputStream os = new ByteArrayOutputStream();
       
   148         ImageOutputStream ios = new MemoryCacheImageOutputStream(os);
       
   149         iw.setOutput(ios);
       
   150         IIOMetadata meta =
       
   151             iw.getDefaultImageMetadata(new ImageTypeSpecifier(img), null);
       
   152         DOMImplementationRegistry registry;
       
   153         registry = DOMImplementationRegistry.newInstance();
       
   154         DOMImplementation impl = registry.getDOMImplementation("XML 3.0");
       
   155         Document doc = impl.createDocument(null, format, null);
       
   156         Element root, itxt, entry;
       
   157         root = doc.getDocumentElement();
       
   158         root.appendChild(itxt = doc.createElement("iTXt"));
       
   159         itxt.appendChild(entry = doc.createElement("iTXtEntry"));
       
   160         entry.setAttribute("keyword", "verbatim");
       
   161         entry.setAttribute("compressionFlag", "false");
       
   162         entry.setAttribute("compressionMethod", "0");
       
   163         entry.setAttribute("languageTag", "x-circled");
       
   164         entry.setAttribute("translatedKeyword", VERBATIM);
       
   165         entry.setAttribute("text", TEXT);
       
   166         itxt.appendChild(entry = doc.createElement("iTXtEntry"));
       
   167         entry.setAttribute("keyword", "compressed");
       
   168         entry.setAttribute("compressionFlag", "true");
       
   169         entry.setAttribute("compressionMethod", "0");
       
   170         entry.setAttribute("languageTag", "x-circled");
       
   171         entry.setAttribute("translatedKeyword", COMPRESSED);
       
   172         entry.setAttribute("text", TEXT);
       
   173         meta.mergeTree(format, root);
       
   174         iw.write(new IIOImage(img, null, meta));
       
   175         iw.dispose();
       
   176 
       
   177         byte[] bytes = os.toByteArray();
       
   178         if (dump)
       
   179             System.out.write(bytes);
       
   180         if (findBytes(VBYTES, bytes) < 0)
       
   181             throw new AssertionError("verbatim block not found");
       
   182         if (findBytes(CBYTES, bytes) < 0)
       
   183             throw new AssertionError("compressed block not found");
       
   184         int length = bytes.length;
       
   185         if (truncate)
       
   186             length = findBytes(VBYTES, bytes) + 32;
       
   187 
       
   188         ImageReader ir = ImageIO.getImageReader(iw);
       
   189         ByteArrayInputStream is = new ByteArrayInputStream(bytes, 0, length);
       
   190         ImageInputStream iis = new MemoryCacheImageInputStream(is);
       
   191         ir.setInput(iis);
       
   192         meta = ir.getImageMetadata(0);
       
   193         Node node = meta.getAsTree(format);
       
   194         for (node = node.getFirstChild();
       
   195              !"iTXt".equals(node.getNodeName());
       
   196              node = node.getNextSibling());
       
   197         boolean verbatimSeen = false, compressedSeen = false;
       
   198         for (node = node.getFirstChild();
       
   199              node != null;
       
   200              node = node.getNextSibling()) {
       
   201             entry = (Element)node;
       
   202             String keyword = entry.getAttribute("keyword");
       
   203             String translatedKeyword = entry.getAttribute("translatedKeyword");
       
   204             String text = entry.getAttribute("text");
       
   205             if ("verbatim".equals(keyword)) {
       
   206                 if (verbatimSeen) throw new AssertionError("Duplicate");
       
   207                 verbatimSeen = true;
       
   208                 if (!VERBATIM.equals(translatedKeyword))
       
   209                     throw new AssertionError("Wrong translated keyword");
       
   210                 if (!TEXT.equals(text))
       
   211                     throw new AssertionError("Wrong text");
       
   212             }
       
   213             else if ("compressed".equals(keyword)) {
       
   214                 if (compressedSeen) throw new AssertionError("Duplicate");
       
   215                 compressedSeen = true;
       
   216                 if (!COMPRESSED.equals(translatedKeyword))
       
   217                     throw new AssertionError("Wrong translated keyword");
       
   218                 if (!TEXT.equals(text))
       
   219                     throw new AssertionError("Wrong text");
       
   220             }
       
   221             else {
       
   222                 throw new AssertionError("Unexpected keyword");
       
   223             }
       
   224         }
       
   225         if (!(verbatimSeen && compressedSeen))
       
   226             throw new AssertionError("Missing chunk");
       
   227     }
       
   228 
       
   229     private static final int findBytes(byte[] needle, byte[] haystack) {
       
   230         HAYSTACK: for (int h = 0; h <= haystack.length - needle.length; ++h) {
       
   231             for (int n = 0; n < needle.length; ++n) {
       
   232                 if (needle[n] != haystack[h + n]) {
       
   233                     continue HAYSTACK;
       
   234                 }
       
   235             }
       
   236             return h;
       
   237         }
       
   238         return -1;
       
   239     }
       
   240 
       
   241 }