home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / awt / StandardGlyphSet.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  48.1 KB  |  1,482 lines

  1. /*
  2.  * @(#)StandardGlyphSet.java    1.13 98/03/18
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. /*
  16.  * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
  17.  * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
  18.  *
  19.  * The original version of this source code and documentation is
  20.  * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
  21.  * of IBM. These materials are provided under terms of a License
  22.  * Agreement between Taligent and Sun. This technology is protected
  23.  * by multiple US and International patents.
  24.  *
  25.  * This notice and attribution to Taligent may not be removed.
  26.  * Taligent is a registered trademark of Taligent, Inc.
  27.  *
  28.  */
  29.  
  30. package java.awt;
  31.  
  32. import java.awt.font.GlyphSet;
  33. import java.awt.font.GlyphMetrics;
  34. import java.awt.font.TextAttributeSet;
  35. import java.awt.geom.Rectangle2D;
  36. import java.awt.geom.Point2D;
  37. import java.text.AttributedCharacterIterator;
  38. import sun.awt.font.Ligaturizer;
  39.  
  40. final class StandardGlyphSet extends GlyphSet {
  41.  
  42.     static final boolean DEBUG = false;
  43.  
  44.     private Font font;
  45.     private int glyphCodes[];
  46.     private float xAdjust;
  47.     private float xAdvances[];
  48.     private float yAdjust;
  49.     private float yAdvances[];
  50.     private int visualOrder[];
  51.     private byte baseline;
  52.     private byte levels[];
  53.     private int glyphCount;
  54.     private int characterCount;
  55.     private boolean isVerticalLine; // cache from font, needed?
  56.     private boolean hasComponentGlyphs; // optimization
  57.  
  58.     // cache
  59.     private float advance; // advance == 0 means cache is invalid
  60.     private float ascent;
  61.     private float descent;
  62.  
  63.     /**
  64.      * Construct a glyph set.
  65.      *
  66.      * A GlyphSet represents a cache of glyphs used for rendering and
  67.      * measuring.  Its line direction is either horizontal or vertical based
  68.      * on the font's line direction.
  69.      *
  70.      * @param font the font used for all the glyph codes.  It must be non-null.
  71.      * @param glyphCodes the array of glyph codes in logical order.  It must
  72.      * be non-null and of length >= glyphCount;
  73.      * @param baseline the baseline to which to align this glyphset
  74.      * @param xAdjust an offset by which to adjust the position of the initial
  75.      * glyph drawn.
  76.      * @param xAdvances the array of advances in the x direction in logical
  77.      * order.  It may be null, if non-null its length must be >= glyphCount.
  78.      * @param yAdjust an offset by which to adjust the position of the initial
  79.      * glyph drawn.
  80.      * @param yAdvances the array of advances in the y direction in logical
  81.      * order.  It may be  null, if non-null its length must be >= glyphCount.
  82.      * @param visualOrder a reordering array that maps from visual position to
  83.      * logical position. For example, visualOrder[0] = 5 means that the first
  84.      * glyph drawn is at position five in the glyph codes, advances, and
  85.      * levels arrays. VisualOrder may be null, in which case the logical
  86.      * position is the same as the visual position.  If non-null its length
  87.      * must be >= glyphCount.  The visual order must map positions one to one.
  88.      * @param levels an array indicating bidirectional levels, in logical
  89.      * order.  It may be null, in which case all characters are of level 0.
  90.      * If non-null its length must be >= glyphCount.  Levels range from 0 to 15
  91.      * inclusive, even values are left to right, odd are right to left.
  92.      * @param glyphCount the number of glyphs in the set.
  93.      */
  94.     public StandardGlyphSet(Font font, int glyphCodes[], byte baseline,
  95.                             float xAdjust, float xAdvances[],
  96.                             float yAdjust, float yAdvances[],
  97.                             int visualOrder[], byte levels[],
  98.                             int glyphCount)
  99.     {
  100.         if (font == null) {
  101.             throw new IllegalArgumentException("GlyphSet constructor passed null font");
  102.         }
  103.         if (glyphCodes == null) {
  104.             throw new IllegalArgumentException("GlyphSet constructor passed null glyphCodes");
  105.         }
  106.         if (glyphCodes.length < glyphCount) {
  107.             throw new IllegalArgumentException("GlyphSet constructor passed short glyphCodes array");
  108.         }
  109.         if (xAdvances != null && xAdvances.length < glyphCount) {
  110.             throw new IllegalArgumentException("GlyphSet constructor passed short x advances array");
  111.         }
  112.         if (yAdvances != null && yAdvances.length < glyphCount) {
  113.             throw new IllegalArgumentException("GlyphSet constructor passed short y advances array");
  114.         }
  115.         if (visualOrder != null && visualOrder.length < glyphCount) {
  116.             throw new IllegalArgumentException("GlyphSet constructor passed short visualOrder array");
  117.         }
  118.         if (levels != null && levels.length < glyphCount) {
  119.             throw new IllegalArgumentException("GlyphSet constructor passed short levels array");
  120.         }
  121.         if (glyphCount < 1) {
  122.             throw new IllegalArgumentException("GlyphSet constructor passed bad glyphCount");
  123.         }
  124.  
  125.         // !!! DEBUGGING
  126.         // Caller must normalize visual order, this check is expensive so we may wish to leave it
  127.         // out for production code.
  128.  
  129.         if (DEBUG && (visualOrder != null)) {
  130.             // like a selection sort.  If at any point the next value found is not the
  131.             // same as the position we're at, we don't have a 1-1 ordering over the range.
  132.             //
  133.             int temp[] = new int[glyphCount];
  134.             System.arraycopy(visualOrder, 0, temp, 0, glyphCount);
  135.  
  136.             outer: for (int i = 0; i < glyphCount; i++) {
  137.                 for (int j = i; j < glyphCount; j++) {
  138.                     if (temp[j] == i) {
  139.                         temp[j] = temp[i];
  140.                         continue outer;
  141.                     }
  142.                 }
  143.  
  144.                 throw new IllegalArgumentException("Font.getGlyphSet passed bad visualOrder");
  145.             }
  146.         }
  147.  
  148.         this.font = font;
  149.         this.glyphCodes = glyphCodes;
  150.         this.baseline = baseline;
  151.         this.xAdjust = xAdjust;
  152.         this.xAdvances = xAdvances;
  153.         this.yAdjust = yAdjust;
  154.         this.yAdvances = yAdvances;
  155.         this.visualOrder = visualOrder;
  156.         this.levels = levels;
  157.         this.glyphCount = glyphCount;
  158.         this.isVerticalLine = font.isVerticalBaseline(); // cache
  159.  
  160.         computeCache();
  161.     }
  162.  
  163.     /**
  164.      * Create a copy of this glyphset.
  165.      *
  166.      * All clone arrays are of length == getNumGlyphs().
  167.      */
  168.     public Object clone() {
  169.  
  170.         try {
  171.             StandardGlyphSet result = (StandardGlyphSet)super.clone();
  172.  
  173.             // font;
  174.  
  175.             result.glyphCodes = new int[glyphCount];
  176.             System.arraycopy(glyphCodes, 0, result.glyphCodes, 0, glyphCount);
  177.  
  178.             // baseline
  179.             // xAdjust
  180.  
  181.             if (xAdvances != null) {
  182.                 result.xAdvances = new float[glyphCount];
  183.                 System.arraycopy(xAdvances, 0, result.xAdvances, 0, glyphCount);
  184.             }
  185.  
  186.             // yAdjust
  187.  
  188.             if (yAdvances != null) {
  189.                 result.yAdvances = new float[glyphCount];
  190.                 System.arraycopy(yAdvances, 0, result.yAdvances, 0, glyphCount);
  191.             }
  192.  
  193.             if (visualOrder != null) {
  194.                 result.visualOrder = new int[glyphCount];
  195.                 System.arraycopy(visualOrder, 0, result.visualOrder, 0,
  196.                                  glyphCount);
  197.             }
  198.  
  199.             if (levels != null) {
  200.                 result.levels = new byte[glyphCount];
  201.                 System.arraycopy(levels, 0, result.levels, 0, glyphCount);
  202.             }
  203.  
  204.             // advance;
  205.             // ascent;
  206.             // descent;
  207.             // glyphCount;
  208.             // characterCount;
  209.             // isVerticalLine;
  210.  
  211.             computeCache();
  212.  
  213.             return result;
  214.         }
  215.         catch (CloneNotSupportedException e) {
  216.             throw new InternalError();
  217.         }
  218.     }
  219.  
  220.     /*
  221.      * Compute cached advance, ascent, and descent, characterCount, and the
  222.      * hasComponentGlyphs flag.
  223.      *
  224.      * Walk through the visual locations accumulating the base position of each
  225.      * glyph based on the advances.  Offset the glyph bounding box by this
  226.      * position.
  227.      * Compute the top and bottom (for horizontal lines) or left and right (for
  228.      * vertical lines) of the union of these boxes.
  229.      *
  230.      * On horizontal lines, ascent is positive going towards lower y values, so
  231.      * invert ascent.  On vertical lines, descent is positive going towards
  232.      * lower x values, so invert descent.
  233.      *
  234.      * Advance is just the sum of the advances in the appropriate direction.
  235.      */
  236.  
  237.     private void computeCache() {
  238.  
  239.         advance = 0;
  240.         ascent = 0;
  241.         descent = 0;
  242.         int componentGlyphs = 0;
  243.  
  244.         float dx = xAdjust;
  245.         float dy = yAdjust;
  246.  
  247.         for (int vi = 0; vi < glyphCount; vi++) {
  248.             int i = visualOrder == null ? vi : visualOrder[vi];
  249.             GlyphMetrics metrics = font.getGlyphMetrics(glyphCodes[i]);
  250.  
  251.             if (metrics.isComponent()) {
  252.                 componentGlyphs++;
  253.             }
  254.  
  255.             Rectangle2D glyphBounds = metrics.getBounds2D();
  256.             float boundsX = (float) glyphBounds.getX();
  257.             float boundsY = (float) glyphBounds.getY();
  258.             float boundsWidth = (float) glyphBounds.getWidth();
  259.             float boundsHeight = (float) glyphBounds.getHeight();
  260.  
  261.             if (isVerticalLine) {
  262.                 ascent = Math.max(ascent, boundsX + boundsWidth + dx);
  263.                 descent = Math.min(descent, boundsX + dx);
  264.             } else {
  265.                 ascent = Math.min(ascent, boundsY + dy);
  266.                 descent = Math.max(descent, boundsY + boundsHeight + dy);
  267.             }
  268.  
  269.             if (xAdvances != null) {
  270.                 dx += xAdvances[i];
  271.             } else if (!isVerticalLine) {
  272.                 dx += metrics.getAdvance();
  273.             }
  274.  
  275.             if (yAdvances != null) {
  276.                 dy += yAdvances[i];
  277.             }
  278.             else if (isVerticalLine) {
  279.                 dy += metrics.getAdvance();
  280.             }
  281.         }
  282.  
  283.         if (isVerticalLine) {
  284.             advance = dy;
  285.             descent = -descent;
  286.         } else {
  287.             advance = dx;
  288.             ascent = -ascent;
  289.         }
  290.  
  291.         characterCount = glyphCount - componentGlyphs;
  292.         hasComponentGlyphs = componentGlyphs > 0;
  293.     }
  294.  
  295.     /**
  296.      * Return the advance of the glyphset.
  297.      */
  298.     public float getAdvance() {
  299.  
  300.         return advance;
  301.     }
  302.  
  303.     /**
  304.      * Return the ascent of the glyphset relative to the baseline.
  305.      *
  306.      * This is the distance above (to the right of) the baseline.  The ascent
  307.      * is always positive or zero.
  308.      * @see #getBaseline
  309.      */
  310.     public float getAscent() {
  311.  
  312.         return ascent;
  313.     }
  314.  
  315.     /**
  316.      * return the descent of the glyphset relative to the baseline.
  317.      *
  318.      * This is the distance below (to the left of) the baseline.  The descent
  319.      * is always positive or zero.
  320.      * @see #getBaseline
  321.      */
  322.     public float getDescent() {
  323.  
  324.         return descent;
  325.     }
  326.  
  327.     /**
  328.      * Return the font that created this glyphset.
  329.      */
  330.     public Font getFont() {
  331.  
  332.         return font;
  333.     }
  334.  
  335.     /**
  336.      * Return the number of glyphs in this glyphset.
  337.      */
  338.     public int getNumGlyphs() {
  339.  
  340.         return glyphCount;
  341.     }
  342.  
  343.     /**
  344.      * Return the number of characters represented by this glyphset.
  345.      *
  346.      * Kashida justification may lead to there being more glyphs than
  347.      * characters.  There are never fewer glyphs than characters.
  348.      */
  349.     public int getNumCharacters() {
  350.  
  351.         return characterCount;
  352.     }
  353.  
  354.     private void checkIndex(int index) {
  355.  
  356.         if (index < 0 || index >= glyphCount) {
  357.             throw new IllegalArgumentException("index is out of range.");
  358.         }
  359.     }
  360.  
  361.     /**
  362.      * Return an array of the glyphcodes in this glyphset.
  363.      *
  364.      * The glyph codes are in logical order, not display order.  The actual
  365.      * number of valid codes can be determined by getNumGlyphs, the array
  366.      * may be longer.
  367.      *
  368.      * @see #getNumGlyphs
  369.      */
  370.     public int[] getGlyphCodes() {
  371.  
  372.         return glyphCodes;
  373.     }
  374.  
  375.     public int getGlyphCode(int index) {
  376.  
  377.         checkIndex(index);
  378.         return glyphCodes[index];
  379.     }
  380.  
  381.     /**
  382.      * Return the baseline used by this glyphset.  All glyphs in a single
  383.      * glyphset align to the same baseline.
  384.      * @see Font#getBaselineFor
  385.      */
  386.     public byte getBaseline() {
  387.  
  388.         return baseline;
  389.     }
  390.  
  391.     /**
  392.      * Return the adjustment to the x position of the glyphset.
  393.      *
  394.      * The first visual glyph in the glyphset is offset by x, y from the
  395.      * origin of the glyphset.  This supports such features as superscript
  396.      * and hanging punctuation.
  397.      * @see #getYAdjust
  398.      */
  399.     public float getXAdjust() {
  400.  
  401.         return xAdjust;
  402.     }
  403.  
  404.     /**
  405.      * Return the array of x position advances.
  406.      *
  407.      * The array is in logical order, not visual order.  It differs from a
  408.      * simple array of advance widths in that kerning adjustments may have
  409.      * been made to the glyphs.  On vertical lines, this array is often null,
  410.      * indicating a zero x advance for all glyphs.
  411.      * <p>
  412.      * The array may be null, in which case the advance widths or zero will
  413.      * be used.  Callers may indicate that they want the array to be filled
  414.      * in, in which case the advance widths will be used to fill in the array.
  415.      *  A null array is an optimization used to save space.
  416.      * <p>
  417.      * The array may be longer than necessary.  The actual number of valid
  418.      * values in the array is provided by getNumGlyphs().
  419.      *
  420.      * @param mustBeNonNull if true, always return a non-null result.
  421.      * @see #getYAdvances
  422.      * @see #getNumGlyphs
  423.      */
  424.     public float[] getXAdvances(boolean mustBeNonNull) {
  425.  
  426.         if (mustBeNonNull && (xAdvances == null)) {
  427.             xAdvances = new float[glyphCount];
  428.             if (!isVerticalLine) { // otherwise all zero
  429.                 for (int i = 0; i < glyphCount; i++) {
  430.                     xAdvances[i] = font.
  431.                         getGlyphMetrics(glyphCodes[i]).getAdvance();
  432.                 }
  433.             }
  434.         }
  435.  
  436.         return xAdvances;
  437.     }
  438.  
  439.     public float getGlyphXAdvance(int index) {
  440.  
  441.         checkIndex(index);
  442.  
  443.         // hmm, should we compute all advances now?
  444.         float[] advs = getXAdvances(true);
  445.         return advs[index];
  446.     }
  447.  
  448.     /**
  449.      * Return the adjustment to the y position of the glyphset.
  450.      *
  451.      * The first visual glyph in the glyphset is offset by x, y from the
  452.      * origin of the glyphset.  This supports such features as superscript and
  453.      * hanging punctuation.
  454.      * @see #getXAdjust
  455.      */
  456.     public float getYAdjust() {
  457.  
  458.         return yAdjust;
  459.     }
  460.  
  461.     /**
  462.      * Return the array of y position advances.
  463.      *
  464.      * The array is in logical order, not visual order.  It differs from a
  465.      * simple array of advance widths in that kerning adjustments may have
  466.      * been made to the glyphs.  On horizontal lines, this array is often null,
  467.      * indicating a zero y advance for all glyphs.
  468.      * <p>
  469.      * The array may be null, in which case the advance widths or zero will be
  470.      * used.  Callers may indicate that they want the array to be filled in,
  471.      * in which case the advance widths will be used to fill in the array.
  472.      * A null array is an optimization used to save space.
  473.      * <p>
  474.      * The array may be longer than necessary.  The actual number of valid
  475.      * values in the array is provided by getNumGlyphs().
  476.      *
  477.      * @param mustBeNonNull if true, always return a non-null result.
  478.      * @see #getXAdvances
  479.      * @see #getNumGlyphs
  480.      */
  481.     public float[] getYAdvances(boolean mustBeNonNull) {
  482.  
  483.         if (mustBeNonNull && (yAdvances == null)) {
  484.             yAdvances = new float[glyphCount];
  485.             if (isVerticalLine) { // otherwise all zero
  486.                 for (int i = 0; i < glyphCount; i++) {
  487.                     yAdvances[i] = font.getGlyphMetrics(glyphCodes[i]).getAdvance();
  488.                 }
  489.             }
  490.         }
  491.         return yAdvances;
  492.     }
  493.  
  494.     public float getGlyphYAdvance(int index) {
  495.  
  496.         checkIndex(index);
  497.         float[] advs = getYAdvances(true);
  498.         return advs[index];
  499.     }
  500.  
  501.     /*
  502.      * Return XAdvances on a horizontal set, and YAdvances on a vertical set.
  503.      * This overload will never return a null set.
  504.      * @see #getXAdvances
  505.      * @see #getYAdvances
  506.      */
  507.     public float[] getAdvances() {
  508.  
  509.         return isVerticalLine ? getYAdvances(true) : getXAdvances(true);
  510.     }
  511.  
  512.     public float getGlyphAdvance(int index) {
  513.  
  514.         return isVerticalLine ? getGlyphYAdvance(index) :
  515.             getGlyphXAdvance(index);
  516.     }
  517.  
  518.     /*
  519.      * Return XAdvances on a horizontal set, and YAdvances on a vertical set.
  520.      * @param mustBeNonNull if true, always return a non-null result.
  521.      * @see #getXAdvances
  522.      * @see #getYAdvances
  523.      */
  524.     public float[] getAdvances(boolean mustNotBeNull) {
  525.  
  526.         return isVerticalLine ? getYAdvances(mustNotBeNull) :
  527.             getXAdvances(mustNotBeNull);
  528.     }
  529.  
  530.     /**
  531.      * Return a mapping array from visual position to logical position.
  532.      *
  533.      * For example, visualOrder[0] = 5 means that the first glyph drawn is at
  534.      * position five in the glyphCodes, levels, and advances arrays. The
  535.      * mapping may be null, in which case the logical  position is the same
  536.      * as the visual position.  If non-null it maps positions one to one.
  537.      * <p>
  538.      * The array may be longer than necessary.  The actual number of valid
  539.      * values in the array is provided by getNumGlyphs().
  540.      * @see #getNumGlyphs
  541.      */
  542.     public int[] getVisualOrder() {
  543.  
  544.         return visualOrder;
  545.     }
  546.  
  547.     public int visualToLogicalIndex(int index) {
  548.  
  549.         checkIndex(index);
  550.         return (visualOrder==null)? index : visualOrder[index];
  551.     }
  552.  
  553.     /**
  554.      * Return an array indicating the direction levels of each glyph.
  555.      *
  556.      * Glyphs whose level is even run from left to right (top to bottom),
  557.      * glyphs whose level is odd run from right to left (bottom to top).
  558.      * This array may be null, in which case the level of each glyph is zero.
  559.      * <p>
  560.      * The array may be longer than necessary.  The actual number of valid
  561.      * values in the array is provided by getNumGlyphs().
  562.      * @see #getNumGlyphs
  563.      */
  564.     public byte[] getLevels() {
  565.  
  566.         return levels;
  567.     }
  568.  
  569.     public byte getLevel(int index) {
  570.  
  571.         checkIndex(index);
  572.         return (byte) ((levels == null)? 0x0 : levels[index]);
  573.     }
  574.  
  575.     // {jbr} incremental editing wants to know when glyphsets are uniformly LTR.  It
  576.     // used to check the level array, now it uses this.  This can change if people
  577.     // think it's too ugly.
  578.     // Note that check can be expensive
  579.     public boolean isCompletelyLTR() {
  580.  
  581.         if (levels == null) {
  582.             return true;
  583.         }
  584.  
  585.         for (int i=0; i < glyphCount; i++) {
  586.             if ((levels[i] & (byte)0x1) != 0) {
  587.                 return false;
  588.             }
  589.         }
  590.  
  591.         return true;
  592.     }
  593.  
  594.     /**
  595.      * Return a justified copy of this glyphset.
  596.      *
  597.      * @see Font#handleApplyJustification
  598.      */
  599.     public GlyphSet applyJustification(float[] deltas, int index,
  600.                                        boolean[] shouldRejustify)
  601.     {
  602.  
  603.         /*
  604.          * if we can cause rejustification, and we have ligatures, and their
  605.          * space limit is exceeded, then break the ligatures and request
  606.          * rejustification.  otherwise, just apply space between the glyphs.
  607.          */
  608.  
  609.         int[] codes = glyphCodes;
  610.         int[] vorder = visualOrder;
  611.  
  612.         if (shouldRejustify[0] == true) {
  613.             shouldRejustify[0] = false; // assume we won't be breaking any
  614.             for (int vi = 0; vi < glyphCount; vi++) {
  615.                 int i = vorder == null ? vi : vorder[vi];
  616.  
  617.                 GlyphMetrics gm = font.getGlyphMetrics(codes[i]);
  618.                 if (gm.isLigature()) { // check ligature glyphs
  619.                     float ls = deltas[index + vi * 2];
  620.                     float rs = deltas[index + vi * 2 + 1];
  621.                     boolean grow = ls + rs > 0; // !!! hack
  622.  
  623.                     if (grow && (ls > font.getGlyphJustificationInfo(codes[i]).
  624.                                  growLeftLimit ||
  625.                                  rs > font.getGlyphJustificationInfo(codes[i]).
  626.                                  growRightLimit)) {
  627.                         //
  628.                         shouldRejustify[0] = true;
  629.  
  630.                         int[] chars = Ligaturizer.glyphChars(codes[i]);
  631.                         for (int j = 0; j < chars.length; ++j)
  632.                             codes[i + j] = chars[j];
  633.                     }
  634.                 }
  635.             }
  636.  
  637.             if (shouldRejustify[0]) {
  638.                 return new StandardGlyphSet(font, codes, baseline,
  639.                                         xAdjust, null,
  640.                                         yAdjust, null,
  641.                                         vorder, levels, glyphCount);
  642.             }
  643.         }
  644.  
  645.         float[] advances = new float[glyphCount];
  646.         System.arraycopy(getAdvances(), 0, advances, 0, advances.length);
  647.  
  648.         // skip left of first char, we'll offset entire glyphset
  649.         float adjust = deltas[index++];
  650.  
  651.         for (int vi = 0; vi < glyphCount; vi++) {
  652.             int i = vorder == null ? vi : vorder[vi];
  653.  
  654.             advances[i] += deltas[index++]; // add right of char
  655.  
  656.             if (vi < glyphCount - 1) {
  657.                 advances[i] += deltas[index++]; // add left of following char
  658.             }
  659.         }
  660.  
  661.         if (font.isVerticalBaseline()) {
  662.             return new StandardGlyphSet(font, codes, baseline,
  663.                                         xAdjust, null,
  664.                                         adjust, advances,
  665.                                         vorder, levels, glyphCount);
  666.         }
  667.         else {
  668.             return new StandardGlyphSet(font, codes, baseline,
  669.                                         adjust, advances,
  670.                                         yAdjust, null,
  671.                                         vorder, levels, glyphCount);
  672.         }
  673.     }
  674.  
  675.     /**
  676.      * Return the character index for a given glyph index.
  677.      *
  678.      * Combining glyphs refer to the same character as the first logically
  679.      * previous non-component glyph.
  680.      *
  681.      * This method should probably only be called internally and by the
  682.      * GlyphSet's font.
  683.      * We'll see...
  684.      */
  685.     protected int glyphToCharacterIndex(int glyphIndex) {
  686.  
  687.         if (hasComponentGlyphs) {
  688.  
  689.             int charCount = 0;
  690.             for (int i=0; i < glyphIndex; i++) {
  691.                 if (!font.getGlyphMetrics(glyphCodes[i]).isComponent()) {
  692.                     charCount++;
  693.                 }
  694.             }
  695.  
  696.             return charCount;
  697.         }
  698.  
  699.         return glyphIndex;
  700.     }
  701.  
  702.     /**
  703.      * Return the glyph index for a given character index.
  704.      *
  705.      * A character index will map to the first logically subsequent
  706.      * non-combining glyph.
  707.      *
  708.      * This method should probably only be called internally and by the
  709.      * GlyphSet's font.
  710.      * We'll see...
  711.      */
  712.     protected int characterToGlyphIndex(int charIndex) {
  713.  
  714.         if (hasComponentGlyphs) {
  715.  
  716.             int glyphNum;
  717.             for (glyphNum = 0; charIndex > 0; glyphNum++) {
  718.                 if (!font.getGlyphMetrics(glyphCodes[glyphNum]).isComponent()) {
  719.                     charIndex--;
  720.                 }
  721.             }
  722.  
  723.             while (glyphNum < glyphCount) {
  724.                 if (font.getGlyphMetrics(glyphCodes[glyphNum]).isComponent()) {
  725.                     glyphNum++;
  726.                 }
  727.                 else {
  728.                     break;
  729.                 }
  730.             }
  731.  
  732.             return glyphNum;
  733.         }
  734.  
  735.         return charIndex;
  736.     }
  737.  
  738.     //--------------------------- hey {jf} - how about this?
  739.  
  740.     /**
  741.      * Return the sum of the advances for the glyphs corresponding to characters
  742.      * in logical positions from start up to limit.  Note these are character
  743.      * indices, not glyph indices.  They will be mapped internally to glyph
  744.      * indices.
  745.      *
  746.      * @param start the character index at which to start measuring
  747.      * @param limit the character index at which to stop measuring
  748.      */
  749.     public float getAdvanceBetween(int start, int limit) {
  750.  
  751.         if (start < 0 || limit < start || limit > characterCount) {
  752.             throw new IllegalArgumentException("GlyphSet.advanceUpTo was passed a bad start and limit");
  753.         }
  754.  
  755.         start = characterToGlyphIndex(start);
  756.         limit = characterToGlyphIndex(limit);
  757.  
  758.         float result = 0.0f;
  759.         float[] advances = getAdvances();
  760.         for (int i = start; i < limit; i++) {
  761.             result += advances[i];
  762.         }
  763.  
  764.         return result;
  765.     }
  766.  
  767.     /**
  768.      * Return the logical position of a possible break.  This adds up advances
  769.      * of glyphs in logical order starting from the glyph for the character
  770.      * at start, stopping before the first spacing glyph whose advance would
  771.      * cause hitAdvance to be exceeded.  The character position of that glyph is
  772.      * returned.  If no glyph would reach the hitAdvance, the number of
  773.      * characters in the set is returned.
  774.      * This will not break between combining or component glyphs and their
  775.      * base glyph.
  776.      */
  777.     public int getLineBreakIndex(int start, float hitAdvance) {
  778.  
  779.         if (start < 0 || start >= characterCount) {
  780.             throw new IllegalArgumentException("GlyphSet.getLineBreakIndex was passed a bad start index");
  781.         }
  782.  
  783.         if (hitAdvance >= getAdvance()) { // optimize
  784.             return characterCount;
  785.         }
  786.  
  787.         if (hitAdvance > 0) {
  788.             int result = characterToGlyphIndex(start);
  789.  
  790.             float[] advances = getAdvances();
  791.             while (result < glyphCount &&
  792.                    ((hitAdvance -= advances[result]) >= 0)) {
  793.                 result++;
  794.             }
  795.  
  796.             if (result < glyphCount) {
  797.                 do {
  798.                     byte basetype = (byte)(font.getGlyphMetrics(glyphCodes[result]).getType() & 0x3);
  799.                     if ((basetype != GlyphMetrics.COMBINING) &&
  800.                         (basetype != GlyphMetrics.COMPONENT)) {
  801.                         break;
  802.                     }
  803.                     --result;
  804.                 } while (result >= 0);
  805.             }
  806.  
  807.             return glyphToCharacterIndex(result);
  808.         }
  809.  
  810.         return start;
  811.     }
  812.  
  813.     /**
  814.      * Generate a GlyphSet that contains the subset of this from logical start
  815.      * up to limit. The new glyphset shares the same baseline, xAdjust,
  816.      * and yAdjust as the previous one.
  817.      */
  818.     public final GlyphSet subset(int start, int limit) {
  819.  
  820.       if (start < 0 || limit <= start || limit > getNumCharacters()) {
  821.             throw new IllegalArgumentException("GlyphSet.handleSubset was passed invalid range: " + start + "," + limit + " (" + getNumCharacters() + ")");
  822.       }
  823.         // might be able to return gs:
  824.         if (start == 0 && limit == getNumCharacters()) {
  825.             return this;
  826.         }
  827.  
  828.         int glyphStart = characterToGlyphIndex(start);
  829.         int glyphLimit = characterToGlyphIndex(limit);
  830.         int newGlyphCount = glyphLimit - glyphStart;
  831.  
  832.         int newGlyphCodes[] = new int[newGlyphCount];
  833.         System.arraycopy(glyphCodes, glyphStart, newGlyphCodes, 0,
  834.                          newGlyphCount);
  835.  
  836.         float newXAdvances[] = null;
  837.         if (xAdvances != null) {
  838.             newXAdvances = new float[newGlyphCount];
  839.             System.arraycopy(xAdvances, glyphStart, newXAdvances,
  840.                              0, newGlyphCount);
  841.         }
  842.  
  843.         float newYAdvances[] = null;
  844.         if (yAdvances != null) {
  845.             newYAdvances = new float[newGlyphCount];
  846.             System.arraycopy(yAdvances, glyphStart, newYAdvances,
  847.                              0, newGlyphCount);
  848.         }
  849.  
  850.         byte newLevels[] = null;
  851.         if (levels != null) {
  852.             newLevels = new byte[newGlyphCount];
  853.             System.arraycopy(levels, start, newLevels, 0, newGlyphCount);
  854.         }
  855.  
  856.         int newVisualOrder[] = null;
  857.  
  858.         if (visualOrder != null) {
  859.             // normalize the subrange to fit the new logical positions
  860.             int[] inverse = getInverseOrder(visualOrder);
  861.             newVisualOrder = getNormalizedOrder(inverse, levels, glyphStart, glyphLimit);
  862.             newVisualOrder = getInverseOrder(newVisualOrder);
  863.         }
  864.  
  865.         return new StandardGlyphSet(font, newGlyphCodes, baseline,
  866.                                     xAdjust, newXAdvances,
  867.                                     yAdjust, newYAdvances,
  868.                                     newVisualOrder, newLevels, newGlyphCount);
  869.     }
  870.  
  871.     public GlyphSet setDirection(boolean leftToRight) {
  872.         if (leftToRight && visualOrder == null && levels == null) {
  873.             return this;
  874.         }
  875.  
  876.         byte[] levels = null;
  877.         int[] visualOrder = null;
  878.  
  879.         if (!leftToRight) {
  880.             byte val = (byte) 0x1;
  881.             levels = new byte[glyphCount];
  882.             visualOrder = new int[glyphCount];
  883.  
  884.             for (int i=0; i < glyphCount; i++) {
  885.                 levels[i] = val;
  886.                 visualOrder[i] = glyphCount - i - 1;
  887.             }
  888.         }
  889.  
  890.         return new StandardGlyphSet(font, glyphCodes, baseline,
  891.                                     xAdjust, xAdvances,
  892.                                     yAdjust, yAdvances,
  893.                                     visualOrder, levels, glyphCount);
  894.  
  895.     }
  896.  
  897.     /**
  898.      * Return a hashCode for this glyphset.
  899.      */
  900.     public int hashCode() {
  901.         return (glyphCount  * 31) ^ glyphCodes[0];
  902.     }
  903.  
  904.     /**
  905.      * Test for full equality.  Font, glyphs, baseline, advances, adjusts,
  906.      * order, levels, count must all match.
  907.      */
  908.     public boolean equals(Object set) {
  909.  
  910.         try {
  911.             return equals((StandardGlyphSet) set);
  912.         }
  913.         catch(ClassCastException e) {
  914.             return false;
  915.         }
  916.     }
  917.  
  918.     public boolean equals(GlyphSet set) {
  919.  
  920.         try {
  921.             return equals((StandardGlyphSet) set);
  922.         }
  923.         catch(ClassCastException e) {
  924.             return false;
  925.         }
  926.     }
  927.  
  928.     protected boolean equals(StandardGlyphSet set) {
  929.         if (set == null) {
  930.             return false;
  931.         }
  932.         if (this == set) {
  933.             return true;
  934.         }
  935.         if (this.hashCode() != set.hashCode()) {
  936.             return false;
  937.         }
  938.  
  939.         // quick checks
  940.         if (glyphCount != set.glyphCount) {
  941.             return false;
  942.         }
  943.         if (characterCount != set.characterCount) {
  944.             return false;
  945.         }
  946.         if (baseline != set.baseline) {
  947.             return false;
  948.         }
  949.         if (xAdjust != set.xAdjust) {
  950.             return false;
  951.         }
  952.         if (yAdjust != set.yAdjust) {
  953.             return false;
  954.         }
  955.         if ((visualOrder != null) ^ (set.visualOrder != null)) {
  956.             return false;
  957.         }
  958.         if ((levels != null) ^ (set.levels != null)) {
  959.             return false;
  960.         }
  961.         // can't check advances, state is lazy evaluated
  962.  
  963.         if (!font.equals(set.font)) return false;
  964.  
  965.         for (int i = 0; i < glyphCount; i++) {
  966.             if (glyphCodes[i] != set.glyphCodes[i]) {
  967.                 return false;
  968.             }
  969.         }
  970.  
  971.  
  972.         if (xAdvances != null || set.xAdvances != null) {
  973.             float[] myx = getXAdvances(true);
  974.             float[] itx = set.getXAdvances(true);
  975.  
  976.             for (int i = 0; i < glyphCount; i++) {
  977.                 if (myx[i] != itx[i]) {
  978.                     return false;
  979.                 }
  980.             }
  981.         }
  982.  
  983.         if (yAdvances != null || set.yAdvances != null) {
  984.             float[] myy = getYAdvances(true);
  985.             float[] ity = set.getYAdvances(true);
  986.  
  987.             for (int i = 0; i < glyphCount; i++) {
  988.                 if (myy[i] != ity[i]) {
  989.                     return false;
  990.                 }
  991.             }
  992.         }
  993.  
  994.         if (visualOrder != null) {
  995.             for (int i = 0; i < glyphCount; i++) {
  996.                 if (visualOrder[i] != set.visualOrder[i]) {
  997.                     return false;
  998.                 }
  999.             }
  1000.         }
  1001.  
  1002.         if (levels != null) {
  1003.             for (int i = 0; i < glyphCount; i++) {
  1004.                 if (levels[i] != set.levels[i]) {
  1005.                     return false;
  1006.                 }
  1007.             }
  1008.         }
  1009.  
  1010.         return true;
  1011.     }
  1012.  
  1013.     /**
  1014.      * Display the glyphcodes to System.out, for debugging purposes.
  1015.      */
  1016.     public void printDebug()
  1017.     {
  1018.         for (int vi = 0; vi < glyphCount; vi++) {
  1019.             int i = visualOrder == null ? vi : visualOrder[vi];
  1020.             System.out.print((char)glyphCodes[i]);
  1021.         }
  1022.     }
  1023.  
  1024.     /**
  1025.      * Display the glyphset as a string, for debugging purposes.
  1026.      */
  1027.     public String toString() {
  1028.  
  1029.         StringBuffer buf = new StringBuffer();
  1030.         buf.append(super.toString());
  1031.         buf.append("\n");
  1032.  
  1033.         buf.append("font=");
  1034.         buf.append(font.toString());
  1035.         buf.append("\n");
  1036.  
  1037.         buf.append("glyphCount=");
  1038.         buf.append(glyphCount);
  1039.         buf.append("\n");
  1040.  
  1041.         buf.append("glyphCodes={");
  1042.         buf.append(glyphCodes[0]);
  1043.         for (int i = 1; i < glyphCount; i++) {
  1044.             buf.append(",");
  1045.             buf.append(glyphCodes[i]);
  1046.         }
  1047.         buf.append("}\n");
  1048.  
  1049.         buf.append("xAdvances=");
  1050.         if (xAdvances == null) {
  1051.             buf.append("null");
  1052.         } else {
  1053.             buf.append("{");
  1054.             buf.append(xAdvances[0]);
  1055.             for (int i = 1; i < glyphCount; i++) {
  1056.                 buf.append(",");
  1057.                 buf.append(xAdvances[i]);
  1058.             }
  1059.             buf.append("}");
  1060.         }
  1061.         buf.append("\n");
  1062.  
  1063.         buf.append("yAdvances=");
  1064.         if (yAdvances == null) {
  1065.             buf.append("null");
  1066.         } else {
  1067.             buf.append("{");
  1068.             buf.append(yAdvances[0]);
  1069.             for (int i = 1; i < glyphCount; i++) {
  1070.                 buf.append(",");
  1071.                 buf.append(yAdvances[i]);
  1072.             }
  1073.             buf.append("}");
  1074.         }
  1075.         buf.append("\n");
  1076.  
  1077.         buf.append("visualOrder=");
  1078.         if (visualOrder == null) {
  1079.             buf.append("null");
  1080.         } else {
  1081.             buf.append("{");
  1082.             buf.append(visualOrder[0]);
  1083.             for (int i = 1; i < glyphCount; i++) {
  1084.                 buf.append(",");
  1085.                 buf.append(visualOrder[i]);
  1086.             }
  1087.             buf.append("}");
  1088.         }
  1089.         buf.append("\n");
  1090.  
  1091.         buf.append("levels=");
  1092.         if (levels == null) {
  1093.             buf.append("null");
  1094.         } else {
  1095.             buf.append("{");
  1096.             buf.append(levels[0]);
  1097.             for (int i = 1; i < glyphCount; i++) {
  1098.                 buf.append(",");
  1099.                 buf.append(levels[i]);
  1100.             }
  1101.             buf.append("}");
  1102.         }
  1103.         buf.append("\n");
  1104.  
  1105.         return buf.toString();
  1106.     }
  1107.  
  1108.     public GlyphSet insertChar(AttributedCharacterIterator newText,
  1109.                                int start, int limit, int insertPos,
  1110.                                int[] order, byte[] levels)
  1111.     {
  1112.  
  1113.         /*
  1114.          * This method must return a glyphset over newText with the given
  1115.          * order and level arrays. It can opt to incrementally modify oldSet,
  1116.          * or it can make a whole new one.
  1117.          */
  1118.  
  1119.         if (insertPos < start || insertPos >= limit) {
  1120.             throw new IllegalArgumentException("insertPos is out of range in GlyphSet.insertChar.");
  1121.         }
  1122.  
  1123.         if (order != null || levels != null || glyphCount != characterCount) {
  1124.             return font.getGlyphSet(newText, start, limit, baseline, order, levels);
  1125.         }
  1126.  
  1127.         int newLength = limit - start;
  1128.  
  1129.         if (characterCount + 1 != newLength) {
  1130.             throw new IllegalArgumentException("GlyphSet.insertChar can only insert a single character.");
  1131.         }
  1132.  
  1133.         // look to see if we're breaking an old ligature, or forming a new one.
  1134.         // Neither case is one we really want to deal with.
  1135.  
  1136.         char insertedChar = newText.setIndex(insertPos);
  1137.  
  1138.         byte insCharType = Ligaturizer.charType(insertedChar);
  1139.         byte prevCharType, nextCharType;
  1140.  
  1141.         if (insertPos == newText.getBeginIndex()) {
  1142.             prevCharType = Ligaturizer.NONCOMBINING;
  1143.         } else {
  1144.             prevCharType = Ligaturizer.charType(newText.previous());
  1145.         }
  1146.  
  1147.         if (insertPos == newText.getEndIndex()) {
  1148.             nextCharType = Ligaturizer.NONCOMBINING;
  1149.         } else {
  1150.             char ch = newText.setIndex(insertPos + 1);
  1151.             nextCharType = Ligaturizer.charType(ch);
  1152.         }
  1153.  
  1154.         boolean useGetGlyphSet = false;
  1155.  
  1156.         if (prevCharType != Ligaturizer.NONCOMBINING &&
  1157.             nextCharType != Ligaturizer.NONCOMBINING) {
  1158.             useGetGlyphSet = true;
  1159.         }
  1160.  
  1161.         if (insCharType != Ligaturizer.NONCOMBINING) {
  1162.             if (prevCharType != Ligaturizer.NONCOMBINING ||
  1163.                 nextCharType != Ligaturizer.NONCOMBINING) {
  1164.                 useGetGlyphSet = true;
  1165.             }
  1166.         }
  1167.  
  1168.         if (useGetGlyphSet == true) {
  1169.             return font.getGlyphSet(newText, start, limit, baseline, order, levels);
  1170.         }
  1171.  
  1172.         int[] oldCodes = glyphCodes, newCodes = new int[newLength];
  1173.         float[] oldAdvances = xAdvances, newAdvances = null;
  1174.  
  1175.         int gsInsertPos = insertPos - start;
  1176.         System.arraycopy(oldCodes, 0, newCodes, 0, gsInsertPos);
  1177.         System.arraycopy(oldCodes, gsInsertPos, newCodes,
  1178.                          gsInsertPos+1, newLength-gsInsertPos-1);
  1179.  
  1180.         // REMIND jk. painfully evident that there seems to be no difference
  1181.         // between characters and glyphs. Need to decide soon with df and jr
  1182.         // on what should Font do in order to make this code simpler
  1183.         //newCodes[gsInsertPos] = insertedChar;//old code
  1184.         // float insCharAdvance = font.getGlyphMetrics(insertedChar).getAdvance();
  1185.         char[] chArray = {insertedChar};
  1186.         String str = new String(chArray);
  1187.         GlyphSet gls = font.getGlyphSet(str);
  1188.         int[] codes = gls.getGlyphCodes();
  1189.         newCodes[gsInsertPos] = codes[0];
  1190.         float insCharAdvance = font.getGlyphMetrics(codes[0]).getAdvance();
  1191.  
  1192.         if (oldAdvances != null) {
  1193.             newAdvances = new float[newLength];
  1194.             System.arraycopy(oldAdvances, 0, newAdvances, 0, gsInsertPos);
  1195.             System.arraycopy(oldAdvances, gsInsertPos, newAdvances,
  1196.                              gsInsertPos+1, newLength-gsInsertPos-1);
  1197.             newAdvances[gsInsertPos] = insCharAdvance;
  1198.         }
  1199.  
  1200.         return new StandardGlyphSet(font, newCodes, baseline,
  1201.                                 xAdjust, newAdvances,
  1202.                                 yAdjust, null,
  1203.                                 order, // visual order
  1204.                                 levels, // levels
  1205.                                 newLength); // glyph count
  1206.  
  1207.     }
  1208.  
  1209.     public GlyphSet deleteChar(AttributedCharacterIterator newText,
  1210.                                int start, int limit, int deletePos,
  1211.                                int[] order, byte[] levels)
  1212.     {
  1213.  
  1214.         if (deletePos < start || deletePos > limit) {
  1215.             throw new IllegalArgumentException("insertPos is out of range in GlyphSet.insertChar.");
  1216.         }
  1217.  
  1218.         int newLength = limit - start;
  1219.  
  1220.         if (characterCount - 1 != newLength) {
  1221.             throw new IllegalArgumentException("GlyphSet.deleteChar can only delete a single character.");
  1222.         }
  1223.  
  1224.         if (newLength == 0) {
  1225.             throw new IllegalArgumentException("GlyphSet.deleteChar was asked to produce a 0-length glyphset.");
  1226.         }
  1227.  
  1228.         if (order != null || levels != null || characterCount != glyphCount) {
  1229.             return font.getGlyphSet(newText, start, limit, baseline, order, levels);
  1230.         }
  1231.  
  1232.         byte beforeCharType, afterCharType;
  1233.  
  1234.         if (deletePos > newText.getBeginIndex()) {
  1235.             beforeCharType = Ligaturizer.charType(newText.setIndex(deletePos-1));
  1236.         } else {
  1237.             beforeCharType = Ligaturizer.NONCOMBINING;
  1238.         }
  1239.  
  1240.         if (deletePos < newText.getEndIndex()) {
  1241.             afterCharType = Ligaturizer.charType(newText.setIndex(deletePos));
  1242.         } else {
  1243.             afterCharType = Ligaturizer.NONCOMBINING;
  1244.         }
  1245.  
  1246.         if (beforeCharType != Ligaturizer.NONCOMBINING ||
  1247.             afterCharType != Ligaturizer.NONCOMBINING) {
  1248.             return font.getGlyphSet(newText, start, limit, baseline,
  1249.                                     order, levels);
  1250.         }
  1251.  
  1252.         int[] oldCodes = glyphCodes;
  1253.     int[] newCodes = new int[newLength];
  1254.         int gsDeletePos = deletePos - start;
  1255.         System.arraycopy(oldCodes, 0, newCodes, 0, gsDeletePos);
  1256.         System.arraycopy(oldCodes, gsDeletePos+1, newCodes, gsDeletePos,
  1257.                          newLength-gsDeletePos);
  1258.  
  1259.         float[] oldAdvances = xAdvances;
  1260.     float[] newAdvances = null;
  1261.  
  1262.         if (oldAdvances != null) {
  1263.             newAdvances = new float[newLength];
  1264.             System.arraycopy(oldAdvances, 0, newAdvances, 0, gsDeletePos);
  1265.             System.arraycopy(oldAdvances, gsDeletePos+1, newAdvances,
  1266.                              gsDeletePos, newLength-gsDeletePos);
  1267.         }
  1268.  
  1269.         return new StandardGlyphSet(font, newCodes, baseline,
  1270.                                 xAdjust, newAdvances,
  1271.                                 yAdjust, null,
  1272.                                 order, // visual order
  1273.                                 levels, // levels
  1274.                                 newLength); // glyph count
  1275.  
  1276.     }
  1277.  
  1278.     public GlyphSet reshape(AttributedCharacterIterator newText,
  1279.                             int start, int limit, int changePos, int[] order,
  1280.                             byte[] levels)
  1281.     {
  1282.  
  1283.         if (limit - start != getNumCharacters()) {
  1284.             throw new IllegalArgumentException("Range size is wrong in GlyphSet.reshape.");
  1285.         }
  1286.  
  1287.         if (changePos != start-1 && changePos != limit) {
  1288.             throw new IllegalArgumentException("changePos is not at range endpoint in Font2.reshape.");
  1289.         }
  1290.  
  1291.         /*
  1292.          * TextLayout passes in previous and/or following glyphsets when an
  1293.          * edit occurs on a glyphset boundary.  This is to allow the Font to
  1294.          * redo special shaping around the character which changed.  Since
  1295.          * this font doesn't shape, just return the old glyphset.
  1296.          */
  1297.  
  1298.         return this;
  1299.     }
  1300.  
  1301.     public void draw(Graphics2D g2, float x, float y) {
  1302.         Graphics g = (Graphics)g2;
  1303.  
  1304.         // REMIND jk df the font shouldn't be handling the color.
  1305.         Color oldColor = null;
  1306.         Color newColor = (Color)font.getRequestedAttributes().
  1307.             get(TextAttributeSet.FOREGROUND);
  1308.  
  1309.         if (newColor != null) {
  1310.             oldColor = g.getColor();
  1311.             g.setColor(newColor);
  1312.         }
  1313.  
  1314.  
  1315.         Font oldFont = g.getFont();
  1316.         g.setFont(font);
  1317.  
  1318.         FontMetrics fm = g.getFontMetrics();
  1319.         float oldx = x;
  1320.         float oldy = y;
  1321.  
  1322.         x += xAdjust;
  1323.         y += yAdjust;
  1324.  
  1325.         // do 'fast path' check.
  1326.         // of course, we should have a special glyphset that just calls this directly without our
  1327.         // having to do the check each time we render
  1328.         boolean fast = !isVerticalLine &&
  1329.             xAdvances == null &&
  1330.             yAdvances == null &&
  1331.             visualOrder == null &&
  1332.             glyphCount == characterCount;
  1333.         if (fast) {
  1334.             for (int i = 0; i < glyphCount; i++) {
  1335.                 if (!font.getGlyphMetrics(glyphCodes[i]).isStandard()) {
  1336.                     fast = false;
  1337.                     break;
  1338.                 }
  1339.             }
  1340.         }
  1341.         if (fast) {
  1342.             char[] chars = new char[glyphCount];
  1343.             for (int i = 0; i < glyphCount; i++) {
  1344.                 chars[i] = (char)glyphCodes[i];
  1345.             }
  1346.             g2.drawString(this, x, y);
  1347.         } else if (!isVerticalLine &&
  1348.                    xAdvances == null &&
  1349.                    yAdvances == null &&
  1350.                    glyphCount == characterCount) 
  1351.         {
  1352.             char[] chars = new char[glyphCount];
  1353.             for (int i = 0; i < glyphCount; i++) {
  1354.                 chars[i] = (char)glyphCodes[visualOrder[i]];
  1355.             }
  1356.             for (int i = 0; i < glyphCount; i++) {
  1357.                 glyphCodes[i] = chars[i];
  1358.             }
  1359.             g2.drawString(this, x, y); 
  1360.             //REMIND jk. reverse the glyphCode changes.
  1361.         } else {
  1362.             char[] cc = new char[1];
  1363.  
  1364.             for (int i = 0; i < glyphCount; ++i) {
  1365.                 int vi = visualOrder == null ? i : visualOrder[i];
  1366.                 int code = glyphCodes[vi];
  1367.                 GlyphMetrics gm = font.getGlyphMetrics(code);
  1368.  
  1369.                 float dx;
  1370.                 if (xAdvances == null) {
  1371.                     if (isVerticalLine) {
  1372.                         dx = 0;
  1373.                     } else {
  1374.                         dx = gm.getAdvance();
  1375.                     }
  1376.                 } else {
  1377.                     dx = xAdvances[vi];
  1378.                 }
  1379.  
  1380.                 float dy;
  1381.                 if (yAdvances == null) {
  1382.                     if (isVerticalLine) {
  1383.                         dy = gm.getAdvance();
  1384.                     } else {
  1385.                         dy = 0;
  1386.                     }
  1387.                 } else {
  1388.                     dy = yAdvances[vi];
  1389.                 }
  1390.  
  1391.         int lx;
  1392.         int ly;
  1393.                 if (isVerticalLine) {
  1394.                 /*
  1395.                  * REMIND jk df, have to hack 'natural' vertical glyph origin
  1396.                  * back to where graphics expects it
  1397.                  */
  1398.                     lx = (int)(x + gm.getBounds2D().getX());
  1399.                     ly = (int)(y + fm.getAscent());
  1400.                 } else {
  1401.             lx = (int)x;
  1402.             ly = (int)y;
  1403.         }
  1404.  
  1405.                 if (gm.isStandard()) {
  1406.                     if (code != 0 && code != '\n' && code != '\r' && code != '\t') {
  1407.                 // ignore 'ligature' code, undrawable codes
  1408.                         cc[0] = (char)code;
  1409.                         g2.drawChars(cc, 0, 1, lx, ly);
  1410.                     }
  1411.                 } else if (gm.isLigature()) {
  1412.                     int[] tempgc = Ligaturizer.glyphChars(code);
  1413.  
  1414.                     // REMIND jk. this is a hack and should be removed soon
  1415.                     char[] gc = new char[tempgc.length];
  1416.                     for (int mm = 0; mm < tempgc.length; ++mm) {
  1417.                         gc[mm] = (char)tempgc[mm];
  1418.                     }
  1419.                     // REMIND jk . end of hack
  1420.                     for (int ii = 0; ii < gc.length; ++ii) {
  1421.                         g2.drawChars(gc, ii, 1, lx, ly);
  1422.                         lx += (int)(fm.charWidth(gc[ii]) - 4);
  1423.                     }
  1424.                 } else if (gm.isComponent()) {
  1425.                     cc[0] = '_';
  1426.                     g2.drawChars(cc, 0, 1, lx, ly);
  1427.                 }
  1428.  
  1429.                 x += dx;
  1430.                 y += dy;
  1431.             }
  1432.         }
  1433.  
  1434.         float dx = isVerticalLine ? 0 : advance;
  1435.         float dy = isVerticalLine ? advance : 0;
  1436.  
  1437.         if (isVerticalLine) {
  1438.             oldx += xAdjust;
  1439.         } else {
  1440.             oldy += yAdjust;
  1441.         }
  1442.  
  1443.         /*
  1444.          * If we want uniform underline and strikethrough across an entire
  1445.          * line, then font can't handle this.  In fact, TextLayout can't
  1446.          * either because you can have multiple layouts on a line.  So for
  1447.          * now, let's let font handle it.
  1448.          */
  1449.         String uff = (String)font.getRequestedAttributes().
  1450.             get(TextAttributeSet.UNDERLINE);
  1451.         if (uff != null) {
  1452.             float uline = font.getUnderlineOffsetFor(' '); // !!!
  1453.             float nx = isVerticalLine ? oldx - uline : oldx;
  1454.             float ny = isVerticalLine ? oldy : oldy + uline;
  1455.  
  1456.             g.drawLine((int)nx, (int)ny, (int)(nx + dx), (int)(ny + dy));
  1457.         }
  1458.  
  1459.         String sff = (String)font.getRequestedAttributes().
  1460.             get(TextAttributeSet.STRIKETHROUGH);
  1461.         if (sff != null) {
  1462.             float sline = font.getStrikethroughOffsetFor(' '); // !!!
  1463.             float nx = isVerticalLine ? oldx - sline : oldx;
  1464.             float ny = isVerticalLine ? oldy : oldy + sline;
  1465.  
  1466.             g.drawLine((int)nx, (int)ny, (int)(nx + dx), (int)(ny + dy));
  1467.         }
  1468.  
  1469.         g.setFont(oldFont);
  1470.         if (oldColor != null) {
  1471.             g.setColor(oldColor);
  1472.         }
  1473.     }
  1474.  
  1475.     public Rectangle2D getBounds() {
  1476.  
  1477.         // remind:  compute real bounds
  1478.         return new Rectangle2D.Float(
  1479.                         xAdjust, -ascent, advance-xAdjust, ascent+descent);
  1480.     }
  1481. }
  1482.