--- /dev/null	2011-07-20 10:54:36.268271016 +0200
+++ openjdk/jdk/test/java/awt/font/InternationalFonts/InternationalFontsRendering.java	2011-09-16 17:18:14.000000000 +0200
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2010, 2011 Red Hat, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+
+import javax.imageio.ImageIO;
+
+/**
+ * @test
+ * @run main InternationalFontsRendering
+ * @author Pavel Tisnovsky
+ *
+ * @summary This test check if all required fonts are properly installed and configured.
+ *
+ * This regression test checks if all required fonts are properly installed and
+ * that .src.properties font configuration is correct. The test is based on
+ * rendering certain characters from selected code pages into BufferedImage.
+ *
+ * When the font does not exists or font configuration is broken, only empty
+ * rectangle is rendered instead of the selected character shape. This rectangle
+ * is filtered and then the destination image is tested whether it is empty
+ * (=white). Because all long horizontal and vertical lines are filtered, it's
+ * difficult to test some characters containing only these shapes, gor example:
+ * 'H', 'I', 'T' etc. (similar characters are included in Cyrillic and Green
+ * alphabets too).
+ *
+ * If test images with rendered characters needs to be created use following flag:
+ * -create-images
+ *
+ * This test checks regression for bugs:
+ * -  https://bugzilla.redhat.com/show_bug.cgi?id=643674
+ *    (Bug 643674 Misconfigured Path for Asian Font)
+ * -  https://bugzilla.redhat.com/show_bug.cgi?id=727195
+ *    (Bug 727195 Japanese font mappings are broken)
+ */
+public class InternationalFontsRendering
+{
+    private static int WIDTH = 200;
+    private static int HEIGHT = 200;
+
+    private static final int FONT_SIZE = 160;
+
+    private static final int MINIMUM_HORIZONTAL_LINE_LENGTH = 70;
+    private static final int MINIMUM_VERTICAL_LINE_LENGTH = 80;
+    private static final int BLACK_WHITE_THRESHOLD = 128;
+    private static final int BLACK_PIXEL_COUNT_THRESHOLD = WIDTH * HEIGHT / 1000;
+
+    /**
+      * Structure which represents range of characters in Unicode mapping
+      */
+    static class UnicodeRange
+    {
+        /**
+          * Name of a range (Latin, Green, etc. etc.)
+          */
+        private String name;
+
+        /**
+          * First character code in a range
+          */
+        private int code1;
+
+        /**
+          * Last character code in a range
+          */
+        private int code2;
+
+        /**
+          * Constructor
+          */
+        public UnicodeRange(String name, int code1, int code2) {
+            this.name = name;
+            this.code1 = code1;
+            this.code2 = code2;
+        }
+
+        /**
+          * Getter for an attribute 'name'
+          */
+        public String getName() {
+            return this.name;
+        }
+
+        /**
+          * Getter for an attribute 'code1'
+          */
+        public int getCode1() {
+            return this.code1;
+        }
+
+        /**
+          * Getter for an attribute 'code2'
+          */
+        public int getCode2() {
+            return this.code2;
+        }
+    }
+
+    /**
+      * Series of strings checked by this test
+      */
+    private static final String[][] testedStrings = {
+        {"Latin-1",    "abcdefABCDEF"},
+        {"Latin-2",    "\u011b\u0161\u010d\u0159\u017e\u00fd\u00e1\u00ed\u00e9\u00fa\u016f\u013e\u0148"},
+        {"Cyrillic",   "\u0414\u0417\u0418\u0472\u0424\u0425\u0470\u0460\u0426"},
+        {"Greek",      "\u03b2\u03be\u03b3\u03b4\u03c0\u03b5\u03c1\u03b6\u03a3\u03c3\u03c2\u03b7\u03c4\u03b8\u03c5\u03a9"},
+        {"Asia-Test1", "\u4f53\u4e2d\u6587\u7b26\u53f7"},
+        {"Asia-Test2", "\u7e41\u9ad4\u4e2d\u6587\u7b26\u865f"},
+        {"Asia-Test3", "\u65e5\u672c\u306e\u8c61\u5fb4"},
+        {"Asia-Test4", "\ud55c\uad6d\uc5b4\uae30\ud638"},
+    };
+
+    /**
+      * Series of Unicode ranges checked by this test
+      */
+    private static final UnicodeRange[] testedRanges = {
+        new UnicodeRange("Latin-ExtendedA1", 0x0100, 0x0130),           // see http://en.wikipedia.org/wiki/Latin_Extended-A
+        new UnicodeRange("Latin-ExtendedA2", 0x0132, 0x017f),           // see http://en.wikipedia.org/wiki/Latin_Extended-A
+        new UnicodeRange("Latin-ExtendedB1", 0x0180, 0x01bf),           // see http://en.wikipedia.org/wiki/Latin_Extended-B
+        new UnicodeRange("Latin-ExtendedB2", 0x01c2, 0x024f),           // see http://en.wikipedia.org/wiki/Latin_Extended-B
+        new UnicodeRange("Greek",  0x03b0, 0x03ce),           // see http://en.wikipedia.org/wiki/Greek_alphabet#Greek_in_Unicode
+        new UnicodeRange("Coptic", 0x03d0, 0x03ff),           // see http://en.wikipedia.org/wiki/Coptic_alphabet#Unicode
+        new UnicodeRange("Japan-Katakana", 0x30a1, 0x30fe),   // see http://en.wikipedia.org/wiki/Katakana#Unicode
+        new UnicodeRange("Japan-Hiragana", 0x3041, 0x308f),   // see http://en.wikipedia.org/wiki/HiraganaUnicode
+    };
+
+    /**
+     * Creates test image a renders one big character to it.
+     * @param code positon of character in Unicode
+     * @return image containing rendered character
+     */
+    private BufferedImage createTestImage(int code) {
+        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_BYTE_GRAY);
+        Graphics2D gc = image.createGraphics();
+        gc.setBackground(Color.WHITE);
+        gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
+        gc.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
+        gc.clearRect(0, 0, WIDTH, HEIGHT);
+        gc.setFont(new Font(Font.DIALOG, Font.PLAIN, FONT_SIZE));
+        gc.setColor(Color.BLACK);
+        String str = Character.valueOf((char)code).toString();
+        gc.drawString(str, 0, HEIGHT - 40);
+        gc.dispose();
+        return image;
+    }
+
+    /**
+     * Creates destination image and then copies data from source image to it
+     * @param src source image
+     * @return image containing rendered and filtered character
+     */
+    private BufferedImage createDestinationImage(BufferedImage src) {
+        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_BYTE_GRAY);
+        src.copyData(image.getRaster());
+        return image;
+    }
+
+    /**
+     * Filter which removes long horizontal lines from an image
+     */
+    private void removeHorizontalLines(BufferedImage image) {
+        for (int y = 0; y < image.getHeight(); y++) {
+            int startX = -1, endX = -1;
+            for (int x = 0; x < image.getWidth(); x++) {
+                int color = image.getRaster().getSample(x, y, 0);
+                if (startX < 0 && color <= BLACK_WHITE_THRESHOLD) {
+                    startX = x;
+                    //System.out.println("> " + y + "\t" + x);
+                }
+                if (startX > 0 && endX < 0 && color > BLACK_WHITE_THRESHOLD) {
+                    endX = x;
+                    //System.out.println("< " + y + "\t" + x);
+                }
+            }
+            // remove long horizontal line, but only if this line found detected in image
+            if (startX > 0 && endX > 0 && (endX - startX) > MINIMUM_HORIZONTAL_LINE_LENGTH) {
+                for (int x = startX; x < endX; x++) {
+                    image.getRaster().setSample(x, y, 0, 255);
+                }
+            }
+        }
+    }
+
+    /**
+     * Filter which removes long vertical lines from an image
+     */
+    private void removeVerticalLines(BufferedImage image) {
+        for (int x = 0; x < image.getWidth(); x++) {
+            int startY = -1, endY = -1;
+            for (int y = 0; y < image.getHeight(); y++) {
+                int color = image.getRaster().getSample(x, y, 0);
+                if (startY < 0 && color <= BLACK_WHITE_THRESHOLD) {
+                    startY = y;
+                }
+                if (startY > 0 && endY < 0 && color > BLACK_WHITE_THRESHOLD) {
+                    endY = y;
+                    //System.out.println("< " + y + "\t" + x);
+                }
+            }
+            // remove long vertical line, but only if this line found detected in image
+            if (startY > 0 && endY > 0 && (endY - startY) > MINIMUM_VERTICAL_LINE_LENGTH) {
+                for (int y = startY; y < endY; y++) {
+                    image.getRaster().setSample(x, y, 0, 255);
+                }
+            }
+        }
+    }
+
+    /**
+     * Test if image is almost empty (one large white area)
+     * @param image image to be tested
+     * @return true if image is almost empty
+     */
+    private boolean isImageAlmostEmpty(BufferedImage image) {
+        int blackPixelCount = 0;
+        for (int y = 0; y < image.getHeight(); y++) {
+            for (int x = 0; x < image.getWidth(); x++) {
+                if (image.getRaster().getSample(x, y, 0) < BLACK_WHITE_THRESHOLD) {
+                    blackPixelCount++;
+                }
+            }
+        }
+        return blackPixelCount < BLACK_PIXEL_COUNT_THRESHOLD;
+    }
+
+    /**
+      * Test if given character is rendered correctly.
+      * @param code character code
+      * @param createImages if true, image with rendered character is stored to a fs
+      * @return true if character is rendered correctly, false instead
+      */
+    private boolean testRenderingOfCharacter(int code, boolean createImages)
+        throws IOException
+    {
+        System.out.print(code + "\t");
+        BufferedImage src = createTestImage(code);
+        BufferedImage dst = createDestinationImage(src);
+        removeHorizontalLines(dst);
+        removeVerticalLines(dst);
+
+        if (createImages /* || true*/) {
+            ImageIO.write(src, "png", new File(code + "_src.png"));
+            ImageIO.write(dst, "png", new File(code + "_dst.png"));
+        }
+        return !isImageAlmostEmpty(dst);
+    }
+
+    /**
+      * Test if all characters stored in a string are rendered correctly.
+      * @param testedStrings array of string containing tested characters
+      * @param createImages if true, image with rendered character is stored to a fs
+      * @return list of wrong rendered characters
+      */
+    private List<Integer> testCharactersStoredInAString(String[][] testedStrings, boolean createImages)
+        throws IOException
+    {
+        List<Integer> badCharacters = new ArrayList<Integer>();
+        for (String[] testedString : testedStrings) {
+            System.out.println("\nTesting " + testedString[0]);
+            for (int i = 0; i < testedString[1].length(); i++) {
+                String str = testedString[1].substring(i, 1+i);
+                int code = str.charAt(0);
+                if (!testRenderingOfCharacter(code, createImages)) {
+                    System.err.println("Bad rendering of character with code: " + code);
+                    badCharacters.add(Integer.valueOf(code));
+                }
+            }
+        }
+        return badCharacters;
+    }
+
+    /**
+      * Test if all characters are rendered correctly.
+      * @param unicodeRanges array of Unicode ranges
+      * @param createImages if true, image with rendered character is stored to a fs
+      * @return list of wrong rendered characters
+      */
+    private List<Integer> testCharactersStoredAsUnicodeRanges(UnicodeRange[] unicodeRanges, boolean createImages)
+        throws IOException
+    {
+        List<Integer> badCharacters = new ArrayList<Integer>();
+        for (UnicodeRange unicodeRange : unicodeRanges) {
+            badCharacters.addAll(testCharactersSpecifiedByUnicodeRange(unicodeRange, createImages));
+        }
+        return badCharacters;
+    }
+
+    /**
+      * Test if all characters are rendered correctly.
+      * @param unicodeRange ranges of Unicode codes
+      * @param createImages if true, image with rendered character is stored to a fs
+      * @return list of wrong rendered characters
+      */
+    private List<Integer> testCharactersSpecifiedByUnicodeRange(UnicodeRange unicodeRange, boolean createImages)
+        throws IOException
+    {
+        List<Integer> badCharacters = new ArrayList<Integer>();
+        System.out.println("\nTesting " + unicodeRange.getName());
+        for (int code = unicodeRange.getCode1(); code <= unicodeRange.getCode2(); code++) {
+            if (!testRenderingOfCharacter(code, createImages)) {
+                System.err.format("Bad rendering of character with code: %d   hex: %x\n", code, code);
+                badCharacters.add(Integer.valueOf(code));
+            }
+        }
+        return badCharacters;
+    }
+
+    /**
+      * Entry point to the test.
+      * @param createImages if true, image with rendered character is stored to a fs
+      */
+    public void runTest(boolean createImages) throws IOException
+    {
+        List<Integer> badCharacters = new ArrayList<Integer>();
+        badCharacters.addAll(testCharactersStoredInAString(testedStrings, createImages));
+        badCharacters.addAll(testCharactersStoredAsUnicodeRanges(testedRanges, createImages));
+        if (!badCharacters.isEmpty()) {
+            throw new RuntimeException("Error in rendering of character(s) with code(s): " + badCharacters.toString());
+        }
+    }
+
+    public static void main(String[] args) throws IOException {
+        new InternationalFontsRendering().runTest(Arrays.asList(args).contains("-create-images"));
+    }
+}
+
