--- /dev/null 2015-11-17 15:39:43.000000000 -0800 +++ new/test/java/awt/font/TextLayout/TestLayoutVsICU.java 2015-11-17 15:39:43.000000000 -0800 @@ -0,0 +1,889 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. 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. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Copyright (C) 2013-2014 IBM Corporation and Others. All Rights Reserved. + */ + +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.FontFormatException; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Paint; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.RenderingHints.Key; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.text.AttributedCharacterIterator; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.TreeMap; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +/** + * This test runs against a test XML file. It opens the fonts and attempts + * to shape and layout glyphs. + * Note that the test is highly environment dependent- you must have + * the same versions of the same fonts available or the test will fail. + * + * It is similar to letest which is part of ICU. + * For reference, here are some reference items: + * ICU's test file: + * http://source.icu-project.org/repos/icu/icu/trunk/source/test/testdata/letest.xml + * ICU's readme for the similar test: + * http://source.icu-project.org/repos/icu/icu/trunk/source/test/letest/readme.html + * + * @bug 8054203 + * @test + * @summary manual test of layout engine behavior. Takes an XML control file. + * @compile TestLayoutVsICU.java + * @author srl + * @run main/manual + */ +public class TestLayoutVsICU { + + public static boolean OPT_DRAW = false; + public static boolean OPT_VERBOSE = false; + public static boolean OPT_FAILMISSING = false; + public static boolean OPT_NOTHROW= false; // if true - don't stop on failure + + public static int docs = 0; // # docs processed + public static int skipped = 0; // cases skipped due to bad font + public static int total = 0; // cases processed + public static int bad = 0; // cases with errs + + public static final String XML_LAYOUT_TESTS = "layout-tests"; // top level + public static final String XML_TEST_CASE = "test-case"; + public static final String XML_TEST_FONT = "test-font"; + public static final String XML_TEST_TEXT = "test-text"; + public static final String XML_RESULT_GLYPHS = "result-glyphs"; + public static final String XML_ID = "id"; + public static final String XML_SCRIPT = "script"; + public static final String XML_NAME = "name"; + public static final String XML_VERSION = "version"; + public static final String XML_CHECKSUM = "checksum"; + public static final String XML_RESULT_INDICES = "result-indices"; + public static final String XML_RESULT_POSITIONS = "result-positions"; + + /** + * @param args + * @throws IOException + * @throws SAXException + * @throws ParserConfigurationException + */ + public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException { + System.out.println("Java " + System.getProperty("java.version") + " from " + System.getProperty("java.vendor")); + TestLayoutVsICU tlvi = null; + for(String arg : args) { + if(arg.equals("-d")) { + OPT_DRAW = true; + } else if(arg.equals("-n")) { + OPT_NOTHROW = true; + } else if(arg.equals("-v")) { + OPT_VERBOSE = true; + } else if(arg.equals("-f")) { + OPT_FAILMISSING = true; + } else { + if(tlvi == null) { + tlvi = new TestLayoutVsICU(); + } + try { + tlvi.show(arg); + } finally { + if(OPT_VERBOSE) { + System.out.println("# done with " + arg); + } + } + } + } + + if(tlvi == null) { + throw new IllegalArgumentException("No XML input. Usage: " + TestLayoutVsICU.class.getSimpleName() + " [-d][-v][-f] letest.xml ..."); + } else { + System.out.println("\n\nRESULTS:\n"); + System.out.println(skipped+"\tskipped due to missing font"); + System.out.println(total+"\ttested of which:"); + System.out.println(bad+"\twere bad"); + + if(bad>0) { + throw new InternalError("One or more failure(s)"); + } + } + } + + String id; + + private void show(String arg) throws ParserConfigurationException, SAXException, IOException { + id = ""; + File xmlFile = new File(arg); + if(!xmlFile.exists()) { + throw new FileNotFoundException("Can't open input XML file " + arg); + } + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + if(OPT_VERBOSE) { + System.out.println("# Parsing " + xmlFile.getAbsolutePath()); + } + Document doc = db.parse(xmlFile); + Element e = doc.getDocumentElement(); + if(!XML_LAYOUT_TESTS.equals(e.getNodeName())) { + throw new IllegalArgumentException("Document " + xmlFile.getAbsolutePath() + " does not have as its base"); + } + + NodeList testCases = e.getElementsByTagName(XML_TEST_CASE); + for(int caseNo=0;caseNo testCaseAttrs = attrs(testCase); + id = testCaseAttrs.get(XML_ID); + final String script = testCaseAttrs.get(XML_SCRIPT); + String testText = null; + Integer[] expectGlyphs = null; + Integer[] expectIndices = null; + Map fontAttrs = null; + if(OPT_VERBOSE) { + System.out.println("#"+caseNo+" id="+id + ", script="+script); + } + NodeList children = testCase.getChildNodes(); + for(int sub=0;sub glyphs = new ArrayList(); + Graphics2D myg2 = new Graphics2D(){ + + @Override + public void draw(Shape s) { + // TODO Auto-generated method stub + + } + + @Override + public boolean drawImage(Image img, AffineTransform xform, + ImageObserver obs) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void drawImage(BufferedImage img, + BufferedImageOp op, int x, int y) { + // TODO Auto-generated method stub + + } + + @Override + public void drawRenderedImage(RenderedImage img, + AffineTransform xform) { + // TODO Auto-generated method stub + + } + + @Override + public void drawRenderableImage(RenderableImage img, + AffineTransform xform) { + // TODO Auto-generated method stub + + } + + @Override + public void drawString(String str, int x, int y) { + // TODO Auto-generated method stub + + } + + @Override + public void drawString(String str, float x, float y) { + // TODO Auto-generated method stub + + } + + @Override + public void drawString( + AttributedCharacterIterator iterator, int x, int y) { + // TODO Auto-generated method stub + + } + + @Override + public void drawString( + AttributedCharacterIterator iterator, float x, + float y) { + // TODO Auto-generated method stub + + } + + @Override + public void drawGlyphVector(GlyphVector g, float x, float y) { + if(x!=0.0 || y!=0.0) { + throw new InternalError("x,y should be 0 but got " + x+","+y); + } + //System.err.println("dGV : " + g.toString() + " @ "+x+","+y); + glyphs.add(g); + } + + @Override + public void fill(Shape s) { + // TODO Auto-generated method stub + + } + + @Override + public boolean hit(Rectangle rect, Shape s, boolean onStroke) { + // TODO Auto-generated method stub + return false; + } + + @Override + public GraphicsConfiguration getDeviceConfiguration() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setComposite(Composite comp) { + // TODO Auto-generated method stub + + } + + @Override + public void setPaint(Paint paint) { + // TODO Auto-generated method stub + + } + + @Override + public void setStroke(Stroke s) { + // TODO Auto-generated method stub + + } + + @Override + public void setRenderingHint(Key hintKey, Object hintValue) { + // TODO Auto-generated method stub + + } + + @Override + public Object getRenderingHint(Key hintKey) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setRenderingHints(Map hints) { + // TODO Auto-generated method stub + + } + + @Override + public void addRenderingHints(Map hints) { + // TODO Auto-generated method stub + + } + + @Override + public RenderingHints getRenderingHints() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void translate(int x, int y) { + // TODO Auto-generated method stub + + } + + @Override + public void translate(double tx, double ty) { + // TODO Auto-generated method stub + + } + + @Override + public void rotate(double theta) { + // TODO Auto-generated method stub + + } + + @Override + public void rotate(double theta, double x, double y) { + // TODO Auto-generated method stub + + } + + @Override + public void scale(double sx, double sy) { + // TODO Auto-generated method stub + + } + + @Override + public void shear(double shx, double shy) { + // TODO Auto-generated method stub + + } + + @Override + public void transform(AffineTransform Tx) { + // TODO Auto-generated method stub + + } + + @Override + public void setTransform(AffineTransform Tx) { + // TODO Auto-generated method stub + + } + + @Override + public AffineTransform getTransform() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Paint getPaint() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Composite getComposite() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setBackground(Color color) { + // TODO Auto-generated method stub + + } + + @Override + public Color getBackground() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Stroke getStroke() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void clip(Shape s) { + // TODO Auto-generated method stub + + } + + @Override + public FontRenderContext getFontRenderContext() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Graphics create() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Color getColor() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setColor(Color c) { + // TODO Auto-generated method stub + + } + + @Override + public void setPaintMode() { + // TODO Auto-generated method stub + + } + + @Override + public void setXORMode(Color c1) { + // TODO Auto-generated method stub + + } + + @Override + public Font getFont() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setFont(Font font) { + // TODO Auto-generated method stub + + } + + @Override + public FontMetrics getFontMetrics(Font f) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Rectangle getClipBounds() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void clipRect(int x, int y, int width, int height) { + // TODO Auto-generated method stub + + } + + @Override + public void setClip(int x, int y, int width, int height) { + // TODO Auto-generated method stub + + } + + @Override + public Shape getClip() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setClip(Shape clip) { + // TODO Auto-generated method stub + + } + + @Override + public void copyArea(int x, int y, int width, int height, + int dx, int dy) { + // TODO Auto-generated method stub + + } + + @Override + public void drawLine(int x1, int y1, int x2, int y2) { + // TODO Auto-generated method stub + + } + + @Override + public void fillRect(int x, int y, int width, int height) { + // TODO Auto-generated method stub + + } + + @Override + public void clearRect(int x, int y, int width, int height) { + // TODO Auto-generated method stub + + } + + @Override + public void drawRoundRect(int x, int y, int width, + int height, int arcWidth, int arcHeight) { + // TODO Auto-generated method stub + + } + + @Override + public void fillRoundRect(int x, int y, int width, + int height, int arcWidth, int arcHeight) { + // TODO Auto-generated method stub + + } + + @Override + public void drawOval(int x, int y, int width, int height) { + // TODO Auto-generated method stub + + } + + @Override + public void fillOval(int x, int y, int width, int height) { + // TODO Auto-generated method stub + + } + + @Override + public void drawArc(int x, int y, int width, int height, + int startAngle, int arcAngle) { + // TODO Auto-generated method stub + + } + + @Override + public void fillArc(int x, int y, int width, int height, + int startAngle, int arcAngle) { + // TODO Auto-generated method stub + + } + + @Override + public void drawPolyline(int[] xPoints, int[] yPoints, + int nPoints) { + // TODO Auto-generated method stub + + } + + @Override + public void drawPolygon(int[] xPoints, int[] yPoints, + int nPoints) { + // TODO Auto-generated method stub + + } + + @Override + public void fillPolygon(int[] xPoints, int[] yPoints, + int nPoints) { + // TODO Auto-generated method stub + + } + + @Override + public boolean drawImage(Image img, int x, int y, + ImageObserver observer) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean drawImage(Image img, int x, int y, + int width, int height, ImageObserver observer) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean drawImage(Image img, int x, int y, + Color bgcolor, ImageObserver observer) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean drawImage(Image img, int x, int y, + int width, int height, Color bgcolor, + ImageObserver observer) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, + int dx2, int dy2, int sx1, int sy1, int sx2, + int sy2, ImageObserver observer) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, + int dx2, int dy2, int sx1, int sy1, int sx2, + int sy2, Color bgcolor, ImageObserver observer) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void dispose() { + // TODO Auto-generated method stub + + } + + }; + tl.draw(myg2, 0, 0); + if(glyphs.size() != 1) { + err("drew " + glyphs.size() + " times - expected 1"); + total++; + bad++; + continue; + } + boolean isBad = false; + GlyphVector gv = glyphs.get(0); + + // GLYPHS + int gotGlyphs[] = gv.getGlyphCodes(0, gv.getNumGlyphs(), new int[gv.getNumGlyphs()]); + + int count = Math.min(gotGlyphs.length, expectGlyphs.length); // go up to this count + + for(int i=0;i> OK: " + gotGlyphs.length + " glyphs"); + } + } + + + if(isBad) { + bad++; + System.out.println("* FAIL: " + id + " /\t" + fontName); + } else { + System.out.println("* OK : " + id + " /\t" + fontName); + } + total++; + } + } + + + private boolean verifyFont(File f, Map fontAttrs) { + InputStream fis = null; + String fontName = fontAttrs.get(XML_NAME); + int count=0; + try { + fis = new BufferedInputStream(new FileInputStream(f)); + + int i = 0; + int r; + try { + while((r=fis.read())!=-1) { + i+=(int)r; + count++; + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + if(OPT_VERBOSE) { + System.out.println("for " + f.getAbsolutePath() + " chks = 0x" + Integer.toHexString(i) + " size=" + count); + } + String theirStr = fontAttrs.get("rchecksum"); + + String ourStr = Integer.toHexString(i).toLowerCase(); + + if(theirStr!=null) { + if(theirStr.startsWith("0x")) { + theirStr = theirStr.substring(2).toLowerCase(); + } else { + theirStr = theirStr.toLowerCase(); + } + long theirs = Integer.parseInt(theirStr, 16); + if(theirs != i) { + err("WARNING: rchecksum for " + fontName + " was " + i + " (0x"+ourStr+") "+ " but file said " + theirs +" (0x"+theirStr+") - perhaps a different font?"); + return false; + } else { + if(OPT_VERBOSE) { + System.out.println(" rchecksum for " + fontName + " OK"); + } + return true; + } + } else { + //if(OPT_VERBOSE) { + System.err.println("WARNING: rchecksum for " + fontName + " was " + i + " (0x"+ourStr+") "+ " but rchecksum was MISSING. Old ICU data?"); + //} + } + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } finally { + try { + fis.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return true; + } + + + private Integer[] parseHexArray(String hex) { + List ret = new ArrayList(); + String items[] = hex.split("[\\s,]"); + for(String i : items) { + if(i.isEmpty()) continue; + if(i.startsWith("0x")) { + i = i.substring(2); + } + ret.add(Integer.parseInt(i, 16)); + } + return ret.toArray(new Integer[0]); + } + + + private void err(String string) { + if(OPT_NOTHROW) { + System.out.println(id+" ERROR: " + string +" (continuing due to -n)"); + } else { + throw new InternalError(id+ ": " + string); + } + } + + + private Font getFont(String fontName, Map fontAttrs) { + Font f; + if(false) + try { + f = Font.getFont(fontName); + if(f!=null) { + if(OPT_VERBOSE) { + System.out.println("Loaded default path to " + fontName); + } + return f; + } + } catch(Throwable t) { + if(OPT_VERBOSE) { + t.printStackTrace(); + System.out.println("problem loading font " + fontName + " - " + t.toString()); + } + } + + File homeDir = new File(System.getProperty("user.home")); + File fontDir = new File(homeDir, "fonts"); + File fontFile = new File(fontDir, fontName); + //System.out.println("## trying " + fontFile.getAbsolutePath()); + if(fontFile.canRead()) { + try { + if(!verifyFont(fontFile,fontAttrs)) { + System.out.println("Warning: failed to verify " + fontName); + } + f = Font.createFont(Font.TRUETYPE_FONT, fontFile); + if(f!=null & OPT_VERBOSE) { + System.out.println("> loaded from " + fontFile.getAbsolutePath() + " - " + f.toString()); + } + return f; + } catch (FontFormatException e) { + if(OPT_VERBOSE) { + e.printStackTrace(); + System.out.println("problem loading font " + fontName + " - " + e.toString()); + } + } catch (IOException e) { + if(OPT_VERBOSE) { + e.printStackTrace(); + System.out.println("problem loading font " + fontName + " - " + e.toString()); + } + } + } + return null; + } + + + private static Map attrs(Node testCase) { + Map rv = new TreeMap(); + NamedNodeMap nnm = testCase.getAttributes(); + for(int i=0;i