1 /*
   2  * Copyright (c) 2015, 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  * Copyright (C) 2013-2014 IBM Corporation and Others. All Rights Reserved.
  26  */
  27 
  28 import java.awt.Color;
  29 import java.awt.Composite;
  30 import java.awt.Font;
  31 import java.awt.FontFormatException;
  32 import java.awt.FontMetrics;
  33 import java.awt.Graphics;
  34 import java.awt.Graphics2D;
  35 import java.awt.GraphicsConfiguration;
  36 import java.awt.Image;
  37 import java.awt.Paint;
  38 import java.awt.Rectangle;
  39 import java.awt.RenderingHints;
  40 import java.awt.RenderingHints.Key;
  41 import java.awt.Shape;
  42 import java.awt.Stroke;
  43 import java.awt.font.FontRenderContext;
  44 import java.awt.font.GlyphVector;
  45 import java.awt.font.TextLayout;
  46 import java.awt.geom.AffineTransform;
  47 import java.awt.image.BufferedImage;
  48 import java.awt.image.BufferedImageOp;
  49 import java.awt.image.ImageObserver;
  50 import java.awt.image.RenderedImage;
  51 import java.awt.image.renderable.RenderableImage;
  52 import java.io.BufferedInputStream;
  53 import java.io.File;
  54 import java.io.FileInputStream;
  55 import java.io.FileNotFoundException;
  56 import java.io.IOException;
  57 import java.io.InputStream;
  58 import java.text.AttributedCharacterIterator;
  59 import java.util.ArrayList;
  60 import java.util.List;
  61 import java.util.Map;
  62 import java.util.MissingResourceException;
  63 import java.util.TreeMap;
  64 
  65 import javax.xml.parsers.DocumentBuilder;
  66 import javax.xml.parsers.DocumentBuilderFactory;
  67 import javax.xml.parsers.ParserConfigurationException;
  68 
  69 import org.w3c.dom.Document;
  70 import org.w3c.dom.Element;
  71 import org.w3c.dom.NamedNodeMap;
  72 import org.w3c.dom.Node;
  73 import org.w3c.dom.NodeList;
  74 import org.xml.sax.SAXException;
  75 
  76 /**
  77  * This test runs against a test XML file. It opens the fonts and attempts
  78  * to shape and layout glyphs.
  79  * Note that the test is highly environment dependent- you must have
  80  * the same versions of the same fonts available or the test will fail.
  81  *
  82  * It is similar to letest which is part of ICU.
  83  * For reference, here are some reference items:
  84  * ICU's test file:
  85  *  http://source.icu-project.org/repos/icu/icu/trunk/source/test/testdata/letest.xml
  86  * ICU's readme for the similar test:
  87  *  http://source.icu-project.org/repos/icu/icu/trunk/source/test/letest/readme.html
  88  *
  89  * @bug 8054203
  90  * @test
  91  * @summary manual test of layout engine behavior. Takes an XML control file.
  92  * @compile TestLayoutVsICU.java
  93  * @author srl
  94  * @run main/manual
  95  */
  96 public class TestLayoutVsICU {
  97 
  98     public static boolean OPT_DRAW = false;
  99     public static boolean OPT_VERBOSE = false;
 100     public static boolean OPT_FAILMISSING = false;
 101     public static boolean OPT_NOTHROW= false; // if true - don't stop on failure
 102 
 103     public static int docs = 0; // # docs processed
 104     public static int skipped = 0; // cases skipped due to bad font
 105     public static int total = 0; // cases processed
 106     public static int bad = 0; // cases with errs
 107 
 108     public static final String XML_LAYOUT_TESTS = "layout-tests"; // top level
 109     public static final String XML_TEST_CASE = "test-case";
 110     public static final String XML_TEST_FONT = "test-font";
 111     public static final String XML_TEST_TEXT = "test-text";
 112     public static final String XML_RESULT_GLYPHS = "result-glyphs";
 113     public static final String XML_ID = "id";
 114     public static final String XML_SCRIPT = "script";
 115     public static final String XML_NAME = "name";
 116     public static final String XML_VERSION = "version";
 117     public static final String XML_CHECKSUM = "checksum";
 118     public static final String XML_RESULT_INDICES = "result-indices";
 119     public static final String XML_RESULT_POSITIONS = "result-positions";
 120 
 121     /**
 122      * @param args
 123      * @throws IOException
 124      * @throws SAXException
 125      * @throws ParserConfigurationException
 126      */
 127     public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
 128         System.out.println("Java " + System.getProperty("java.version") + " from " + System.getProperty("java.vendor"));
 129         TestLayoutVsICU tlvi = null;
 130         for(String arg : args) {
 131             if(arg.equals("-d")) {
 132                 OPT_DRAW = true;
 133             } else if(arg.equals("-n")) {
 134                 OPT_NOTHROW = true;
 135             } else if(arg.equals("-v")) {
 136                 OPT_VERBOSE = true;
 137             } else if(arg.equals("-f")) {
 138                 OPT_FAILMISSING = true;
 139             } else {
 140                 if(tlvi == null) {
 141                     tlvi = new TestLayoutVsICU();
 142                 }
 143                 try {
 144                     tlvi.show(arg);
 145                 } finally {
 146                     if(OPT_VERBOSE) {
 147                         System.out.println("# done with " + arg);
 148                     }
 149                 }
 150             }
 151         }
 152 
 153         if(tlvi == null) {
 154             throw new IllegalArgumentException("No XML input. Usage: " + TestLayoutVsICU.class.getSimpleName() + " [-d][-v][-f] letest.xml ...");
 155         } else {
 156             System.out.println("\n\nRESULTS:\n");
 157             System.out.println(skipped+"\tskipped due to missing font");
 158             System.out.println(total+"\ttested of which:");
 159             System.out.println(bad+"\twere bad");
 160 
 161             if(bad>0) {
 162                 throw new InternalError("One or more failure(s)");
 163             }
 164         }
 165     }
 166 
 167     String id;
 168 
 169     private void show(String arg) throws ParserConfigurationException, SAXException, IOException {
 170         id = "<none>";
 171         File xmlFile = new File(arg);
 172         if(!xmlFile.exists()) {
 173             throw new FileNotFoundException("Can't open input XML file " + arg);
 174         }
 175         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 176         DocumentBuilder db = dbf.newDocumentBuilder();
 177         if(OPT_VERBOSE) {
 178             System.out.println("# Parsing " + xmlFile.getAbsolutePath());
 179         }
 180         Document doc = db.parse(xmlFile);
 181         Element e = doc.getDocumentElement();
 182         if(!XML_LAYOUT_TESTS.equals(e.getNodeName())) {
 183             throw new IllegalArgumentException("Document " + xmlFile.getAbsolutePath() + " does not have <layout-tests> as its base");
 184         }
 185 
 186         NodeList testCases = e.getElementsByTagName(XML_TEST_CASE);
 187         for(int caseNo=0;caseNo<testCases.getLength();caseNo++) {
 188             final Node testCase = testCases.item(caseNo);
 189             final Map<String,String> testCaseAttrs = attrs(testCase);
 190             id = testCaseAttrs.get(XML_ID);
 191             final String script = testCaseAttrs.get(XML_SCRIPT);
 192             String testText = null;
 193             Integer[] expectGlyphs = null;
 194             Integer[] expectIndices = null;
 195             Map<String,String> fontAttrs = null;
 196             if(OPT_VERBOSE) {
 197                 System.out.println("#"+caseNo+" id="+id + ", script="+script);
 198             }
 199             NodeList children = testCase.getChildNodes();
 200             for(int sub=0;sub<children.getLength();sub++) {
 201                 Node n = children.item(sub);
 202                 if(n.getNodeType()!=Node.ELEMENT_NODE) continue;
 203                 String nn = n.getNodeName();
 204                 if(nn.equals(XML_TEST_FONT)) {
 205                     fontAttrs = attrs(n);
 206                 } else if(nn.equals(XML_TEST_TEXT)) {
 207                     testText = n.getTextContent();
 208                 } else if(nn.equals(XML_RESULT_GLYPHS)) {
 209                     String hex = n.getTextContent();
 210                     expectGlyphs = parseHexArray(hex);
 211                 } else if(nn.equals(XML_RESULT_INDICES)) {
 212                     String hex = n.getTextContent();
 213                     expectIndices = parseHexArray(hex);
 214                 } else if(OPT_VERBOSE) {
 215                     System.out.println("Ignoring node " + nn);
 216                 }
 217             }
 218             if(fontAttrs == null) {
 219                 throw new IllegalArgumentException(id + " Missing node " + XML_TEST_FONT);
 220             }
 221             if(testText == null) {
 222                 throw new IllegalArgumentException(id + " Missing node " + XML_TEST_TEXT);
 223             }
 224 
 225             String fontName = fontAttrs.get(XML_NAME);
 226             Font f = getFont(fontName, fontAttrs);
 227             if(f==null) {
 228                 if(OPT_FAILMISSING) {
 229                     throw new MissingResourceException("Missing font,  abort test", Font.class.getName(), fontName);
 230                 }
 231                 System.out.println("Skipping " + id + " because font is missing: " + fontName);
 232                 skipped++;
 233                 continue;
 234             }
 235             FontRenderContext frc = new FontRenderContext(null, true, true);
 236             TextLayout tl = new TextLayout(testText,f,frc);
 237             final List<GlyphVector> glyphs = new ArrayList<GlyphVector>();
 238             Graphics2D myg2 = new Graphics2D(){
 239 
 240                     @Override
 241                     public void draw(Shape s) {
 242                         // TODO Auto-generated method stub
 243 
 244                     }
 245 
 246                     @Override
 247                     public boolean drawImage(Image img, AffineTransform xform,
 248                                              ImageObserver obs) {
 249                         // TODO Auto-generated method stub
 250                         return false;
 251                     }
 252 
 253                     @Override
 254                     public void drawImage(BufferedImage img,
 255                                           BufferedImageOp op, int x, int y) {
 256                         // TODO Auto-generated method stub
 257 
 258                     }
 259 
 260                     @Override
 261                     public void drawRenderedImage(RenderedImage img,
 262                                                   AffineTransform xform) {
 263                         // TODO Auto-generated method stub
 264 
 265                     }
 266 
 267                     @Override
 268                     public void drawRenderableImage(RenderableImage img,
 269                                                     AffineTransform xform) {
 270                         // TODO Auto-generated method stub
 271 
 272                     }
 273 
 274                     @Override
 275                     public void drawString(String str, int x, int y) {
 276                         // TODO Auto-generated method stub
 277 
 278                     }
 279 
 280                     @Override
 281                     public void drawString(String str, float x, float y) {
 282                         // TODO Auto-generated method stub
 283 
 284                     }
 285 
 286                     @Override
 287                     public void drawString(
 288                                            AttributedCharacterIterator iterator, int x, int y) {
 289                         // TODO Auto-generated method stub
 290 
 291                     }
 292 
 293                     @Override
 294                     public void drawString(
 295                                            AttributedCharacterIterator iterator, float x,
 296                                            float y) {
 297                         // TODO Auto-generated method stub
 298 
 299                     }
 300 
 301                     @Override
 302                     public void drawGlyphVector(GlyphVector g, float x, float y) {
 303                         if(x!=0.0 || y!=0.0) {
 304                             throw new InternalError("x,y should be 0 but got " + x+","+y);
 305                         }
 306                         //System.err.println("dGV : " + g.toString() + " @ "+x+","+y);
 307                         glyphs.add(g);
 308                     }
 309 
 310                     @Override
 311                     public void fill(Shape s) {
 312                         // TODO Auto-generated method stub
 313 
 314                     }
 315 
 316                     @Override
 317                     public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
 318                         // TODO Auto-generated method stub
 319                         return false;
 320                     }
 321 
 322                     @Override
 323                     public GraphicsConfiguration getDeviceConfiguration() {
 324                         // TODO Auto-generated method stub
 325                         return null;
 326                     }
 327 
 328                     @Override
 329                     public void setComposite(Composite comp) {
 330                         // TODO Auto-generated method stub
 331 
 332                     }
 333 
 334                     @Override
 335                     public void setPaint(Paint paint) {
 336                         // TODO Auto-generated method stub
 337 
 338                     }
 339 
 340                     @Override
 341                     public void setStroke(Stroke s) {
 342                         // TODO Auto-generated method stub
 343 
 344                     }
 345 
 346                     @Override
 347                     public void setRenderingHint(Key hintKey, Object hintValue) {
 348                         // TODO Auto-generated method stub
 349 
 350                     }
 351 
 352                     @Override
 353                     public Object getRenderingHint(Key hintKey) {
 354                         // TODO Auto-generated method stub
 355                         return null;
 356                     }
 357 
 358                     @Override
 359                     public void setRenderingHints(Map<?, ?> hints) {
 360                         // TODO Auto-generated method stub
 361 
 362                     }
 363 
 364                     @Override
 365                     public void addRenderingHints(Map<?, ?> hints) {
 366                         // TODO Auto-generated method stub
 367 
 368                     }
 369 
 370                     @Override
 371                     public RenderingHints getRenderingHints() {
 372                         // TODO Auto-generated method stub
 373                         return null;
 374                     }
 375 
 376                     @Override
 377                     public void translate(int x, int y) {
 378                         // TODO Auto-generated method stub
 379 
 380                     }
 381 
 382                     @Override
 383                     public void translate(double tx, double ty) {
 384                         // TODO Auto-generated method stub
 385 
 386                     }
 387 
 388                     @Override
 389                     public void rotate(double theta) {
 390                         // TODO Auto-generated method stub
 391 
 392                     }
 393 
 394                     @Override
 395                     public void rotate(double theta, double x, double y) {
 396                         // TODO Auto-generated method stub
 397 
 398                     }
 399 
 400                     @Override
 401                     public void scale(double sx, double sy) {
 402                         // TODO Auto-generated method stub
 403 
 404                     }
 405 
 406                     @Override
 407                     public void shear(double shx, double shy) {
 408                         // TODO Auto-generated method stub
 409 
 410                     }
 411 
 412                     @Override
 413                     public void transform(AffineTransform Tx) {
 414                         // TODO Auto-generated method stub
 415 
 416                     }
 417 
 418                     @Override
 419                     public void setTransform(AffineTransform Tx) {
 420                         // TODO Auto-generated method stub
 421 
 422                     }
 423 
 424                     @Override
 425                     public AffineTransform getTransform() {
 426                         // TODO Auto-generated method stub
 427                         return null;
 428                     }
 429 
 430                     @Override
 431                     public Paint getPaint() {
 432                         // TODO Auto-generated method stub
 433                         return null;
 434                     }
 435 
 436                     @Override
 437                     public Composite getComposite() {
 438                         // TODO Auto-generated method stub
 439                         return null;
 440                     }
 441 
 442                     @Override
 443                     public void setBackground(Color color) {
 444                         // TODO Auto-generated method stub
 445 
 446                     }
 447 
 448                     @Override
 449                     public Color getBackground() {
 450                         // TODO Auto-generated method stub
 451                         return null;
 452                     }
 453 
 454                     @Override
 455                     public Stroke getStroke() {
 456                         // TODO Auto-generated method stub
 457                         return null;
 458                     }
 459 
 460                     @Override
 461                     public void clip(Shape s) {
 462                         // TODO Auto-generated method stub
 463 
 464                     }
 465 
 466                     @Override
 467                     public FontRenderContext getFontRenderContext() {
 468                         // TODO Auto-generated method stub
 469                         return null;
 470                     }
 471 
 472                     @Override
 473                     public Graphics create() {
 474                         // TODO Auto-generated method stub
 475                         return null;
 476                     }
 477 
 478                     @Override
 479                     public Color getColor() {
 480                         // TODO Auto-generated method stub
 481                         return null;
 482                     }
 483 
 484                     @Override
 485                     public void setColor(Color c) {
 486                         // TODO Auto-generated method stub
 487 
 488                     }
 489 
 490                     @Override
 491                     public void setPaintMode() {
 492                         // TODO Auto-generated method stub
 493 
 494                     }
 495 
 496                     @Override
 497                     public void setXORMode(Color c1) {
 498                         // TODO Auto-generated method stub
 499 
 500                     }
 501 
 502                     @Override
 503                     public Font getFont() {
 504                         // TODO Auto-generated method stub
 505                         return null;
 506                     }
 507 
 508                     @Override
 509                     public void setFont(Font font) {
 510                         // TODO Auto-generated method stub
 511 
 512                     }
 513 
 514                     @Override
 515                     public FontMetrics getFontMetrics(Font f) {
 516                         // TODO Auto-generated method stub
 517                         return null;
 518                     }
 519 
 520                     @Override
 521                     public Rectangle getClipBounds() {
 522                         // TODO Auto-generated method stub
 523                         return null;
 524                     }
 525 
 526                     @Override
 527                     public void clipRect(int x, int y, int width, int height) {
 528                         // TODO Auto-generated method stub
 529 
 530                     }
 531 
 532                     @Override
 533                     public void setClip(int x, int y, int width, int height) {
 534                         // TODO Auto-generated method stub
 535 
 536                     }
 537 
 538                     @Override
 539                     public Shape getClip() {
 540                         // TODO Auto-generated method stub
 541                         return null;
 542                     }
 543 
 544                     @Override
 545                     public void setClip(Shape clip) {
 546                         // TODO Auto-generated method stub
 547 
 548                     }
 549 
 550                     @Override
 551                     public void copyArea(int x, int y, int width, int height,
 552                                          int dx, int dy) {
 553                         // TODO Auto-generated method stub
 554 
 555                     }
 556 
 557                     @Override
 558                     public void drawLine(int x1, int y1, int x2, int y2) {
 559                         // TODO Auto-generated method stub
 560 
 561                     }
 562 
 563                     @Override
 564                     public void fillRect(int x, int y, int width, int height) {
 565                         // TODO Auto-generated method stub
 566 
 567                     }
 568 
 569                     @Override
 570                     public void clearRect(int x, int y, int width, int height) {
 571                         // TODO Auto-generated method stub
 572 
 573                     }
 574 
 575                     @Override
 576                     public void drawRoundRect(int x, int y, int width,
 577                                               int height, int arcWidth, int arcHeight) {
 578                         // TODO Auto-generated method stub
 579 
 580                     }
 581 
 582                     @Override
 583                     public void fillRoundRect(int x, int y, int width,
 584                                               int height, int arcWidth, int arcHeight) {
 585                         // TODO Auto-generated method stub
 586 
 587                     }
 588 
 589                     @Override
 590                     public void drawOval(int x, int y, int width, int height) {
 591                         // TODO Auto-generated method stub
 592 
 593                     }
 594 
 595                     @Override
 596                     public void fillOval(int x, int y, int width, int height) {
 597                         // TODO Auto-generated method stub
 598 
 599                     }
 600 
 601                     @Override
 602                     public void drawArc(int x, int y, int width, int height,
 603                                         int startAngle, int arcAngle) {
 604                         // TODO Auto-generated method stub
 605 
 606                     }
 607 
 608                     @Override
 609                     public void fillArc(int x, int y, int width, int height,
 610                                         int startAngle, int arcAngle) {
 611                         // TODO Auto-generated method stub
 612 
 613                     }
 614 
 615                     @Override
 616                     public void drawPolyline(int[] xPoints, int[] yPoints,
 617                                              int nPoints) {
 618                         // TODO Auto-generated method stub
 619 
 620                     }
 621 
 622                     @Override
 623                     public void drawPolygon(int[] xPoints, int[] yPoints,
 624                                             int nPoints) {
 625                         // TODO Auto-generated method stub
 626 
 627                     }
 628 
 629                     @Override
 630                     public void fillPolygon(int[] xPoints, int[] yPoints,
 631                                             int nPoints) {
 632                         // TODO Auto-generated method stub
 633 
 634                     }
 635 
 636                     @Override
 637                     public boolean drawImage(Image img, int x, int y,
 638                                              ImageObserver observer) {
 639                         // TODO Auto-generated method stub
 640                         return false;
 641                     }
 642 
 643                     @Override
 644                     public boolean drawImage(Image img, int x, int y,
 645                                              int width, int height, ImageObserver observer) {
 646                         // TODO Auto-generated method stub
 647                         return false;
 648                     }
 649 
 650                     @Override
 651                     public boolean drawImage(Image img, int x, int y,
 652                                              Color bgcolor, ImageObserver observer) {
 653                         // TODO Auto-generated method stub
 654                         return false;
 655                     }
 656 
 657                     @Override
 658                     public boolean drawImage(Image img, int x, int y,
 659                                              int width, int height, Color bgcolor,
 660                                              ImageObserver observer) {
 661                         // TODO Auto-generated method stub
 662                         return false;
 663                     }
 664 
 665                     @Override
 666                     public boolean drawImage(Image img, int dx1, int dy1,
 667                                              int dx2, int dy2, int sx1, int sy1, int sx2,
 668                                              int sy2, ImageObserver observer) {
 669                         // TODO Auto-generated method stub
 670                         return false;
 671                     }
 672 
 673                     @Override
 674                     public boolean drawImage(Image img, int dx1, int dy1,
 675                                              int dx2, int dy2, int sx1, int sy1, int sx2,
 676                                              int sy2, Color bgcolor, ImageObserver observer) {
 677                         // TODO Auto-generated method stub
 678                         return false;
 679                     }
 680 
 681                     @Override
 682                     public void dispose() {
 683                         // TODO Auto-generated method stub
 684 
 685                     }
 686 
 687                 };
 688             tl.draw(myg2, 0, 0);
 689             if(glyphs.size() != 1) {
 690                 err("drew " + glyphs.size() + " times - expected 1");
 691                 total++;
 692                 bad++;
 693                 continue;
 694             }
 695             boolean isBad = false;
 696             GlyphVector gv = glyphs.get(0);
 697 
 698             // GLYPHS
 699             int gotGlyphs[] = gv.getGlyphCodes(0, gv.getNumGlyphs(), new int[gv.getNumGlyphs()]);
 700 
 701             int count = Math.min(gotGlyphs.length, expectGlyphs.length); // go up to this count
 702 
 703             for(int i=0;i<count;i++) {
 704                 if(gotGlyphs[i]!=expectGlyphs[i]) {
 705                     err("@"+i+" - got \tglyph 0x" + Integer.toHexString(gotGlyphs[i]) + " wanted 0x" + Integer.toHexString(expectGlyphs[i]));
 706                     isBad=true;
 707                     break;
 708                 }
 709             }
 710 
 711             // INDICES
 712             int gotIndices[] = gv.getGlyphCharIndices(0, gv.getNumGlyphs(), new int[gv.getNumGlyphs()]);
 713             for(int i=0;i<count;i++) {
 714                 if(gotIndices[i]!=expectIndices[i]) {
 715                     err("@"+i+" - got \tindex 0x" + Integer.toHexString(gotGlyphs[i]) + " wanted 0x" + Integer.toHexString(expectGlyphs[i]));
 716                     isBad=true;
 717                     break;
 718                 }
 719             }
 720 
 721 
 722             // COUNT
 723             if(gotGlyphs.length != expectGlyphs.length) {
 724                 System.out.println("Got " + gotGlyphs.length + " wanted " + expectGlyphs.length + " glyphs");
 725                 isBad=true;
 726             } else {
 727                 if(OPT_VERBOSE) {
 728                     System.out.println(">> OK: " + gotGlyphs.length + " glyphs");
 729                 }
 730             }
 731 
 732 
 733             if(isBad) {
 734                 bad++;
 735                 System.out.println("* FAIL: " + id + "  /\t" + fontName);
 736             } else {
 737                 System.out.println("* OK  : " + id + "  /\t" + fontName);
 738             }
 739             total++;
 740         }
 741     }
 742 
 743 
 744     private boolean verifyFont(File f, Map<String, String> fontAttrs) {
 745         InputStream fis = null;
 746         String fontName = fontAttrs.get(XML_NAME);
 747         int count=0;
 748         try {
 749             fis = new BufferedInputStream(new FileInputStream(f));
 750 
 751             int i = 0;
 752             int r;
 753             try {
 754                 while((r=fis.read())!=-1) {
 755                     i+=(int)r;
 756                     count++;
 757                 }
 758             } catch (IOException e) {
 759                 // TODO Auto-generated catch block
 760                 e.printStackTrace();
 761                 return false;
 762             }
 763             if(OPT_VERBOSE) {
 764                 System.out.println("for " + f.getAbsolutePath() + " chks = 0x" + Integer.toHexString(i) + " size=" + count);
 765             }
 766             String theirStr = fontAttrs.get("rchecksum");
 767 
 768             String ourStr = Integer.toHexString(i).toLowerCase();
 769 
 770             if(theirStr!=null) {
 771                 if(theirStr.startsWith("0x")) {
 772                     theirStr = theirStr.substring(2).toLowerCase();
 773                 } else {
 774                     theirStr = theirStr.toLowerCase();
 775                 }
 776                 long theirs = Integer.parseInt(theirStr, 16);
 777                 if(theirs != i) {
 778                     err("WARNING: rchecksum for " + fontName + " was " + i  + " (0x"+ourStr+") "+ " but file said " + theirs +" (0x"+theirStr+")  - perhaps a different font?");
 779                     return false;
 780                 } else {
 781                     if(OPT_VERBOSE) {
 782                         System.out.println(" rchecksum for " + fontName + " OK");
 783                     }
 784                     return true;
 785                 }
 786             } else {
 787                 //if(OPT_VERBOSE) {
 788                 System.err.println("WARNING: rchecksum for " + fontName + " was " + i  + " (0x"+ourStr+") "+ " but rchecksum was MISSING. Old ICU data?");
 789                 //}
 790             }
 791         } catch (FileNotFoundException e) {
 792             // TODO Auto-generated catch block
 793             e.printStackTrace();
 794             return false;
 795         } finally {
 796             try {
 797                 fis.close();
 798             } catch (IOException e) {
 799                 // TODO Auto-generated catch block
 800                 e.printStackTrace();
 801             }
 802         }
 803         return true;
 804     }
 805 
 806 
 807     private Integer[] parseHexArray(String hex) {
 808         List<Integer> ret = new ArrayList<Integer>();
 809         String items[] = hex.split("[\\s,]");
 810         for(String i : items) {
 811             if(i.isEmpty()) continue;
 812             if(i.startsWith("0x")) {
 813                 i = i.substring(2);
 814             }
 815             ret.add(Integer.parseInt(i, 16));
 816         }
 817         return ret.toArray(new Integer[0]);
 818     }
 819 
 820 
 821     private void err(String string) {
 822         if(OPT_NOTHROW) {
 823             System.out.println(id+" ERROR: " + string +" (continuing due to -n)");
 824         } else {
 825             throw new InternalError(id+ ": " + string);
 826         }
 827     }
 828 
 829 
 830     private Font getFont(String fontName, Map<String, String> fontAttrs) {
 831         Font f;
 832         if(false)
 833             try {
 834                 f = Font.getFont(fontName);
 835                 if(f!=null)  {
 836                     if(OPT_VERBOSE) {
 837                         System.out.println("Loaded default path to " + fontName);
 838                     }
 839                     return f;
 840                 }
 841             } catch(Throwable t) {
 842                 if(OPT_VERBOSE) {
 843                     t.printStackTrace();
 844                     System.out.println("problem loading font " + fontName + " - " + t.toString());
 845                 }
 846             }
 847 
 848         File homeDir = new File(System.getProperty("user.home"));
 849         File fontDir = new File(homeDir, "fonts");
 850         File fontFile = new File(fontDir, fontName);
 851         //System.out.println("## trying " + fontFile.getAbsolutePath());
 852         if(fontFile.canRead()) {
 853             try {
 854                 if(!verifyFont(fontFile,fontAttrs)) {
 855                     System.out.println("Warning: failed to verify " + fontName);
 856                 }
 857                 f = Font.createFont(Font.TRUETYPE_FONT, fontFile);
 858                 if(f!=null & OPT_VERBOSE) {
 859                     System.out.println("> loaded from " + fontFile.getAbsolutePath() + " - " + f.toString());
 860                 }
 861                 return f;
 862             } catch (FontFormatException e) {
 863                 if(OPT_VERBOSE) {
 864                     e.printStackTrace();
 865                     System.out.println("problem loading font " + fontName + " - " + e.toString());
 866                 }
 867             } catch (IOException e) {
 868                 if(OPT_VERBOSE) {
 869                     e.printStackTrace();
 870                     System.out.println("problem loading font " + fontName + " - " + e.toString());
 871                 }
 872             }
 873         }
 874         return null;
 875     }
 876 
 877 
 878     private static Map<String, String> attrs(Node testCase) {
 879         Map<String,String> rv = new TreeMap<String,String>();
 880         NamedNodeMap nnm = testCase.getAttributes();
 881         for(int i=0;i<nnm.getLength();i++) {
 882             Node n = nnm.item(i);
 883             String k = n.getNodeName();
 884             String v = n.getNodeValue();
 885             rv.put(k, v);
 886         }
 887         return rv;
 888     }
 889 }