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

  1. /*
  2.  * @(#)TextLayoutGraphic.java    1.5 98/03/18
  3.  *
  4.  * Copyright 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.font;
  31.  
  32. import java.awt.Image;
  33. import java.awt.Shape;
  34. import java.awt.Rectangle;
  35. import java.awt.Graphics;
  36. import java.awt.Graphics2D;
  37. import java.awt.Font;
  38.  
  39. import java.text.AttributedCharacterIterator;
  40.  
  41. import java.awt.geom.Rectangle2D;
  42.  
  43. class TextLayoutGraphic implements TextLayoutComponent {
  44.  
  45.     /*
  46.      * Leading shared by all graphics.
  47.      */
  48.     static final float GRAPHIC_LEADING = 2;
  49.  
  50.     /*
  51.      * The graphic this component represents.
  52.      */
  53.     private /*final*/ GraphicAttribute fGraphic;
  54.  
  55.     /*
  56.      * The number of graphics in this component.
  57.      */
  58.     private int fGraphicCount;
  59.  
  60.     /*
  61.      * Advance of a single graphic.
  62.      */
  63.     private /*final*/ float fGraphicAdvance;
  64.  
  65.     /*
  66.      * Array of advances of graphics.  If null advances are fGraphicAdvance.
  67.      */
  68.     private float[] fAdvances = null;
  69.  
  70.     /*
  71.      * Total advance of this component.
  72.      */
  73.     private float fTotalAdvance;
  74.  
  75.     /*
  76.      * Amount to adjust drawing.  Set in justification.
  77.      */
  78.     private float fAdjust = 0;
  79.  
  80.     /*
  81.      * Levels of graphics.  If null levels are all 0.
  82.      */
  83.     private byte[] fLevels;
  84.  
  85.     /*
  86.      * Visual order of graphics.  If null graphics are left-to-right.
  87.      */
  88.     private int[] fVisualOrder;
  89.  
  90.     /*
  91.      * GlyphMetrics appropriate to graphic.  Lazily evaluated
  92.      * in getGlyphMetrics().
  93.      */
  94.     private GlyphMetrics fGlyphMetrics = null;
  95.  
  96.     /*
  97.      * GlyphJustificationInfo appropriate to graphic.  Lazily
  98.      * evaluated in getGlyphJustificationInfo.
  99.      */
  100.     private GlyphJustificationInfo fGlyphJustInfo = null;
  101.  
  102.     // Several methods construct a copy of themselves and
  103.     // mutate the copy.
  104.  
  105.     public static byte getBaselineFromGraphic(GraphicAttribute graphic) {
  106.  
  107.         byte alignment = (byte) graphic.getAlignment();
  108.  
  109.         if (alignment == GraphicAttribute.BOTTOM_ALIGNMENT ||
  110.                 alignment == GraphicAttribute.TOP_ALIGNMENT) {
  111.  
  112.             return Font.ROMAN_BASELINE;
  113.         }
  114.         else {
  115.             return alignment;
  116.         }
  117.     }
  118.  
  119.     private TextLayoutGraphic(TextLayoutGraphic rhs) {
  120.  
  121.         fGraphic = rhs.fGraphic;
  122.         fGraphicCount = rhs.fGraphicCount;
  123.         fGraphicAdvance = rhs.fGraphicAdvance;
  124.         fAdvances = rhs.fAdvances;
  125.         fTotalAdvance = rhs.fTotalAdvance;
  126.         fAdjust = rhs.fAdjust;
  127.         fLevels = rhs.fLevels;
  128.         fVisualOrder = rhs.fVisualOrder;
  129.         fGlyphMetrics = rhs.fGlyphMetrics;
  130.         fGlyphJustInfo = rhs.fGlyphJustInfo;
  131.     }
  132.  
  133.     public TextLayoutGraphic(int contextStart,
  134.                              GraphicAttribute graphic,
  135.                              int runStart,
  136.                              int runLimit,
  137.                              int[] logicalOrder,
  138.                              byte[] levels) {
  139.  
  140.         runStart -= contextStart;
  141.         runLimit -= contextStart;
  142.  
  143.         fGraphic = graphic;
  144.  
  145.         fGraphicCount = runLimit - runStart;
  146.  
  147.         if (logicalOrder != null) {
  148.             int[] subOrder = GlyphSet.getNormalizedOrder(
  149.                                 logicalOrder, levels, runStart, runLimit);
  150.             fVisualOrder = GlyphSet.getInverseOrder(subOrder);
  151.         }
  152.         else {
  153.             fVisualOrder = null;
  154.         }
  155.  
  156.         if (levels != null) {
  157.             fLevels = new byte[fGraphicCount];
  158.             System.arraycopy(levels, runStart, fLevels, 0, fGraphicCount);
  159.         }
  160.         else {
  161.             fLevels = null;
  162.         }
  163.  
  164.         fGraphicAdvance = fGraphic.getAdvance();
  165.         fTotalAdvance = fGraphicAdvance * fGraphicCount;
  166.     }
  167.  
  168.     private final void checkIndex(int index) {
  169.  
  170.         if (index < 0 || index >= fGraphicCount) {
  171.             throw new IllegalArgumentException("index is out of range.");
  172.         }
  173.     }
  174.  
  175.     public float getAdvance() {
  176.  
  177.         return fTotalAdvance;
  178.     }
  179.  
  180.     public float getAscent() {
  181.  
  182.         byte alignment = (byte) fGraphic.getAlignment();
  183.  
  184.         if (alignment == GraphicAttribute.BOTTOM_ALIGNMENT ||
  185.                 alignment == GraphicAttribute.TOP_ALIGNMENT) {
  186.  
  187.             return 0;
  188.         }
  189.         else {
  190.             return fGraphic.getAscent();
  191.         }
  192.     }
  193.  
  194.     public float getDescent(float ascent) {
  195.  
  196.         byte alignment = (byte) fGraphic.getAlignment();
  197.  
  198.         if (alignment == GraphicAttribute.BOTTOM_ALIGNMENT ||
  199.                 alignment == GraphicAttribute.TOP_ALIGNMENT) {
  200.  
  201.             float height = fGraphic.getAscent() + fGraphic.getDescent();
  202.             return Math.max(0, height-ascent);
  203.         }
  204.         else {
  205.  
  206.             return fGraphic.getDescent();
  207.         }
  208.     }
  209.  
  210.     public float getLeading() {
  211.  
  212.         return GRAPHIC_LEADING;
  213.     }
  214.  
  215.     public int getNumGlyphs() {
  216.  
  217.         return fGraphicCount;
  218.     }
  219.  
  220.     public int getNumCharacters() {
  221.  
  222.         return fGraphicCount;
  223.     }
  224.  
  225.     public byte getBaseline() {
  226.  
  227.         return getBaselineFromGraphic(fGraphic);
  228.     }
  229.  
  230.     public float getXAdjust() {
  231.  
  232.         return fAdjust;
  233.     }
  234.  
  235.     public float getGlyphXAdvance(int index) {
  236.  
  237.         return getGlyphAdvance(index);
  238.     }
  239.  
  240.     public float getYAdjust() {
  241.  
  242.         return 0;
  243.     }
  244.  
  245.     public float getGlyphYAdvance(int index) {
  246.  
  247.         checkIndex(index);
  248.         return 0;
  249.     }
  250.  
  251.     public float getGlyphAdvance(int index) {
  252.  
  253.         checkIndex(index);
  254.         if (fAdvances == null) {
  255.             return fGraphicAdvance;
  256.         }
  257.         else {
  258.             return fAdvances[index];
  259.         }
  260.     }
  261.  
  262.     public int visualToLogicalIndex(int index) {
  263.  
  264.         checkIndex(index);
  265.         if (fVisualOrder == null) {
  266.             return index;
  267.         }
  268.         else {
  269.             return fVisualOrder[index];
  270.         }
  271.     }
  272.  
  273.     public byte getLevel(int index) {
  274.  
  275.         checkIndex(index);
  276.         if (fLevels == null) {
  277.             return 0;
  278.         }
  279.         else {
  280.             return fLevels[index];
  281.         }
  282.     }
  283.  
  284.     public boolean isCompletelyLTR() {
  285.  
  286.         return (fLevels == null);
  287.     }
  288.  
  289.     public TextLayoutComponent applyJustification(
  290.                     float[] deltas, int index, boolean[] shouldRejustify) {
  291.  
  292.         shouldRejustify[0] = false;
  293.  
  294.         TextLayoutGraphic justified = new TextLayoutGraphic(this);
  295.  
  296.         justified.fTotalAdvance += deltas[index];
  297.         justified.fAdjust = deltas[index++];
  298.  
  299.         if (justified.fAdvances == null) {
  300.             justified.fAdvances = new float[fGraphicCount];
  301.             for (int i=0; i < fGraphicCount; i++) {
  302.                 justified.fAdvances[i] = fGraphicAdvance;
  303.             }
  304.         }
  305.  
  306.         for (int i=0; i < fGraphicCount-1; i++) {
  307.  
  308.             justified.fTotalAdvance += deltas[index];
  309.             justified.fAdvances[i] += deltas[index++];
  310.  
  311.             justified.fTotalAdvance += deltas[index];
  312.             justified.fAdvances[i] += deltas[index++];
  313.         }
  314.  
  315.         justified.fTotalAdvance += deltas[index];
  316.         justified.fAdvances[fGraphicCount-1] += deltas[index];
  317.  
  318.         return justified;
  319.     }
  320.  
  321.     public float getAdvanceBetween(int start, int limit) {
  322.  
  323.         if ((start < 0) || (start > limit) || (limit > fGraphicCount)) {
  324.             throw new IllegalArgumentException(
  325.                 "Bad range passed to TextLayoutGraphic.getAdvanceBetween.");
  326.         }
  327.  
  328.         float rval = 0;
  329.  
  330.         if (fAdvances == null) {
  331.             rval += fGraphicAdvance * (limit-start);
  332.         }
  333.         else {
  334.             for (int i=start; i<limit; i++) {
  335.                 rval += fAdvances[i];
  336.             }
  337.         }
  338.  
  339.         return rval;
  340.     }
  341.  
  342.     public int getLineBreakIndex(int start, float hitAdvance) {
  343.  
  344.         checkIndex(start);
  345.  
  346.         if (hitAdvance >= fTotalAdvance)
  347.             return fGraphicCount;
  348.  
  349.         if (start == 0) {
  350.             hitAdvance -= fAdjust;
  351.         }
  352.  
  353.         if (fAdvances == null) {
  354.  
  355.             int numGraphics = (int) (hitAdvance / fGraphicAdvance);
  356.             return start + numGraphics;
  357.         }
  358.         else {
  359.  
  360.             int i;
  361.             for (i = start; i < fGraphicCount; i++) {
  362.  
  363.                 hitAdvance -= fAdvances[i];
  364.                 if (hitAdvance < 0) {
  365.                     return i;
  366.                 }
  367.             }
  368.  
  369.             return i;
  370.         }
  371.     }
  372.  
  373.     public TextLayoutComponent subset(int start, int limit) {
  374.  
  375.         if ((start < 0) || (start >= limit) || (limit > fGraphicCount)) {
  376.             throw new IllegalArgumentException(
  377.                 "Bad range passed to TextLayoutGraphic.subset.");
  378.         }
  379.  
  380.         if (start == 0 && limit == fGraphicCount) {
  381.             return this;
  382.         }
  383.  
  384.         TextLayoutGraphic subset = new TextLayoutGraphic(this);
  385.  
  386.         subset.fGraphicCount = (limit-start);
  387.  
  388.         // copy advances, levels, visualOrder if they exist
  389.         if (fAdvances != null) {
  390.  
  391.             subset.fAdvances = new float[limit-start];
  392.             System.arraycopy(fAdvances, start, subset.fAdvances, 0, (limit-start));
  393.         }
  394.  
  395.         if (fLevels != null) {
  396.  
  397.             subset.fLevels = new byte[limit-start];
  398.             System.arraycopy(fLevels, start, subset.fLevels, 0, (limit-start));
  399.         }
  400.  
  401.         if (fVisualOrder != null) {
  402.  
  403.             int[] inverse = GlyphSet.getInverseOrder(fVisualOrder);
  404.             int[] normalizedLogToVis =
  405.                 GlyphSet.getNormalizedOrder(inverse, fLevels, start, limit);
  406.  
  407.             subset.fVisualOrder = GlyphSet.getInverseOrder(normalizedLogToVis);
  408.         }
  409.  
  410.         // compute total advance
  411.         subset.fAdjust = (start==0)? fAdjust : 0;
  412.         float subsetAdvance = subset.fAdjust;
  413.  
  414.         if (fAdvances == null) {
  415.             subsetAdvance += fGraphicAdvance * fGraphicCount;
  416.         }
  417.         else {
  418.             for (int i=start; i < limit; i++) {
  419.                 subsetAdvance += fAdvances[i];
  420.             }
  421.         }
  422.  
  423.         subset.fTotalAdvance = subsetAdvance;
  424.  
  425.         return subset;
  426.     }
  427.  
  428.     public TextLayoutComponent insertChar(
  429.                                     AttributedCharacterIterator newText,
  430.                                     int start, int limit,
  431.                                     int insertPos,
  432.                                     int[] order,
  433.                                     byte[] levels) {
  434.  
  435.         if (insertPos < start || insertPos >= limit) {
  436.             throw new IllegalArgumentException("insertPos is out of range.");
  437.         }
  438.  
  439.         int newLength = limit - start;
  440.  
  441.         if (newLength != fGraphicCount + 1) {
  442.             throw new IllegalArgumentException(
  443.                 "TextLayoutGraphic.insertChar can only insert a single character.");
  444.         }
  445.  
  446.         TextLayoutGraphic newGraphic = new TextLayoutGraphic(this);
  447.  
  448.         // set new count, copy order and levels, modify advances array,
  449.         // recompute fTotalAdvance
  450.  
  451.         newGraphic.fGraphicCount = newLength;
  452.  
  453.         // make start, limit, insertPos relative to context start
  454.         {
  455.             int textStart = newText.getBeginIndex();
  456.             start -= textStart;
  457.             limit -= textStart;
  458.             insertPos -= textStart;
  459.         }
  460.  
  461.         if (order == null) {
  462.             newGraphic.fVisualOrder = null;
  463.         }
  464.         else {
  465.             int[] logToVis =
  466.                     GlyphSet.getNormalizedOrder(order, levels, start, limit);
  467.             newGraphic.fVisualOrder = GlyphSet.getInverseOrder(logToVis);
  468.         }
  469.  
  470.         if (levels == null) {
  471.             newGraphic.fLevels = null;
  472.         }
  473.         else {
  474.             byte[] newLevels = new byte[newLength];
  475.             System.arraycopy(levels, start, newLevels, 0, newLength);
  476.             newGraphic.fLevels = newLevels;
  477.         }
  478.  
  479.         if (fAdvances != null) {
  480.             float[] newAdvances = new float[newLength];
  481.             System.arraycopy(fAdvances, 0, newAdvances, 0, insertPos-start);
  482.             System.arraycopy(fAdvances, insertPos-start,
  483.                                 newAdvances, insertPos-start+1, limit-insertPos-1);
  484.             newAdvances[insertPos] = fGraphicAdvance;
  485.             newGraphic.fAdvances = newAdvances;
  486.         }
  487.  
  488.         newGraphic.fTotalAdvance += fGraphicAdvance;
  489.  
  490.         return newGraphic;
  491.     }
  492.  
  493.     public TextLayoutComponent deleteChar(
  494.                                     AttributedCharacterIterator newText,
  495.                                     int start, int limit,
  496.                                     int deletePos,
  497.                                     int[] order,
  498.                                     byte[] levels) {
  499.  
  500.         if (deletePos < start || deletePos > limit) {
  501.             throw new IllegalArgumentException(
  502.                     "insertPos is out of range in TextLayoutComponent.deleteChar.");
  503.         }
  504.  
  505.         int newLength = limit-start;
  506.  
  507.         if (newLength != fGraphicCount-1) {
  508.             throw new IllegalArgumentException(
  509.                 "TextLayoutComponent.deleteChar can only delete a single character.");
  510.         }
  511.  
  512.         if (newLength == 0) {
  513.             throw new IllegalArgumentException(
  514.                 "TextLayoutComponent.deleteChar was asked to produce a 0-length glyphset.");
  515.         }
  516.  
  517.         TextLayoutGraphic newGraphic = new TextLayoutGraphic(this);
  518.  
  519.         // set new count, copy order and levels, modify advances array,
  520.         // recompute fTotalAdvance
  521.  
  522.         newGraphic.fGraphicCount = newLength;
  523.  
  524.         // make start, limit, insertPos relative to context start
  525.         {
  526.             int textStart = newText.getBeginIndex();
  527.             start -= textStart;
  528.             limit -= textStart;
  529.             deletePos -= textStart;
  530.         }
  531.  
  532.         if (order == null) {
  533.             newGraphic.fVisualOrder = null;
  534.         }
  535.         else {
  536.             int[] logToVis =
  537.                     GlyphSet.getNormalizedOrder(order, levels, start, limit);
  538.             newGraphic.fVisualOrder = GlyphSet.getInverseOrder(logToVis);
  539.         }
  540.  
  541.  
  542.         if (levels == null) {
  543.             newGraphic.fLevels = null;
  544.         }
  545.         else {
  546.             byte[] newLevels = new byte[newLength];
  547.             System.arraycopy(levels, start, newLevels, 0, newLength);
  548.             newGraphic.fLevels = newLevels;
  549.         }
  550.  
  551.         if (fAdvances == null) {
  552.             newGraphic.fTotalAdvance -= fGraphicAdvance;
  553.         }
  554.         else {
  555.             newGraphic.fTotalAdvance -= fAdvances[deletePos-start];
  556.             float[] newAdvances = new float[newLength];
  557.             int gsDeletePos = deletePos-start;
  558.  
  559.             System.arraycopy(fAdvances, 0, newAdvances, 0, gsDeletePos);
  560.             System.arraycopy(fAdvances, gsDeletePos+1,
  561.                             newAdvances, gsDeletePos, newLength-gsDeletePos);
  562.             newGraphic.fAdvances = newAdvances;
  563.         }
  564.  
  565.         if (deletePos != start) {
  566.             newGraphic.fTotalAdvance -= newGraphic.fAdjust;
  567.             newGraphic.fAdjust = 0;
  568.         }
  569.  
  570.         return newGraphic;
  571.     }
  572.  
  573.  
  574.     public TextLayoutComponent reshape(
  575.                                     AttributedCharacterIterator newText,
  576.                                     int start, int limit,
  577.                                     int changePos,
  578.                                     int[] order,
  579.                                     byte[] levels) {
  580.  
  581.         return this;
  582.     }
  583.  
  584.     private float getGraphicYShift(TextLayout hostLayout) {
  585.  
  586.         float dy = 0;
  587.  
  588.         byte alignment = (byte) fGraphic.getAlignment();
  589.  
  590.         if (alignment == GraphicAttribute.BOTTOM_ALIGNMENT ||
  591.                 alignment == GraphicAttribute.TOP_ALIGNMENT) {
  592.  
  593.             byte baseline = getBaseline();
  594.             float[] baselineOffsets = hostLayout.getBaselineOffsets();
  595.  
  596.             if (alignment == GraphicAttribute.TOP_ALIGNMENT) {
  597.                 dy = baselineOffsets[baseline] -
  598.                         hostLayout.getAscent() +
  599.                         fGraphic.getAscent();
  600.             }
  601.             else {
  602.                 dy = baselineOffsets[baseline] +
  603.                         hostLayout.getDescent() -
  604.                         fGraphic.getDescent();
  605.             }
  606.         }
  607.  
  608.         return dy;
  609.     }
  610.  
  611.     public void draw(Graphics2D graphics, float x, float y, TextLayout hostLayout) {
  612.  
  613.         float locX = x, locY = y;
  614.  
  615.         locY += getGraphicYShift(hostLayout);
  616.  
  617.         for (int i=0; i < fGraphicCount; i++) {
  618.  
  619.             fGraphic.draw(graphics, x, y);
  620.  
  621.             if (fAdvances != null) {
  622.                 if (fVisualOrder != null) {
  623.                     locX += fAdvances[fVisualOrder[i]];
  624.                 }
  625.                 else {
  626.                     locX += fAdvances[i];
  627.                 }
  628.             }
  629.             else {
  630.                 locX += fGraphicAdvance;
  631.             }
  632.         }
  633.     }
  634.  
  635.     public Rectangle2D getBounds(TextLayout hostLayout) {
  636.  
  637.         Rectangle2D graphicBounds = fGraphic.getBounds();
  638.         float left = fAdjust + (float) graphicBounds.getLeft();
  639.  
  640.         float right = fAdjust + getAdvanceBetween(0, fGraphicCount-1);
  641.         right += graphicBounds.getRight();
  642.  
  643.         float dy = getGraphicYShift(hostLayout);
  644.  
  645.         return new Rectangle2D.Float(
  646.                         left, (float) (graphicBounds.getTop()+dy),
  647.                         right-left, (float) (graphicBounds.getHeight()));
  648.     }
  649.  
  650.     public TextLayoutComponent setDirection(boolean leftToRight) {
  651.  
  652.         if (leftToRight && fLevels == null && fVisualOrder == null) {
  653.             return this;
  654.         }
  655.  
  656.         TextLayoutGraphic dir = new TextLayoutGraphic(this);
  657.  
  658.         if (leftToRight) {
  659.             dir.fLevels = null;
  660.             dir.fVisualOrder = null;
  661.         }
  662.         else {
  663.             dir.fLevels = new byte[fGraphicCount];
  664.             dir.fVisualOrder = new int[fGraphicCount];
  665.  
  666.             for (int i=0; i < fGraphicCount; i++) {
  667.                 dir.fLevels[i] = (byte) 1;
  668.                 dir.fVisualOrder[i] = (fGraphicCount-i-1);
  669.             }
  670.         }
  671.  
  672.         return dir;
  673.     }
  674.  
  675.     /**
  676.      * Return GlyphMetrics for glyph at index.
  677.      * Index is NOT a glyph code.
  678.      */
  679.     public GlyphMetrics getGlyphMetrics(int index, TextLayout hostLayout) {
  680.  
  681.         checkIndex(index);
  682.  
  683.         if (fGlyphMetrics == null) {
  684.  
  685.             Rectangle2D bounds = fGraphic.getBounds();
  686.  
  687.             float dy = getGraphicYShift(hostLayout);
  688.  
  689.             if (dy != 0) {
  690.                 // really protect lazy subclassers of GraphicAttribute?
  691.                 // ok since we only do this once
  692.  
  693.                 // copy ctor would be nice...
  694.                 Rectangle2D temp = new Rectangle2D.Float();
  695.                 temp.setRect(bounds);
  696.                 bounds = temp;
  697.  
  698.                 bounds.setRect(bounds.getX(), bounds.getY()+dy,
  699.                                 bounds.getWidth(), bounds.getHeight());
  700.             }
  701.  
  702.             fGlyphMetrics = new GlyphMetrics(fGraphicAdvance,
  703.                                              bounds,
  704.                                              GlyphMetrics.STANDARD);
  705.         }
  706.  
  707.         return fGlyphMetrics;
  708.     }
  709.  
  710.     public boolean isComponent(int index) {
  711.  
  712.         checkIndex(index);
  713.         return false;
  714.     }
  715.  
  716.     /**
  717.      * Return GlyphJustificationInfo for glyph at index.
  718.      * Index is NOT a glyph code.
  719.      */
  720.     public GlyphJustificationInfo getGlyphJustificationInfo(int index) {
  721.  
  722.         checkIndex(index);
  723.         return fGraphic.getJustificationInfo();
  724.     }
  725.  
  726.  
  727.     public int hashCode() {
  728.  
  729.         return fGraphic.hashCode();
  730.     }
  731.  
  732.     public boolean equals(TextLayoutComponent set) {
  733.  
  734.         try {
  735.             return equals((TextLayoutGraphic) set);
  736.         }
  737.         catch(ClassCastException e) {
  738.             return false;
  739.         }
  740.     }
  741.  
  742.     public boolean equals(TextLayoutGraphic rhs) {
  743.  
  744.         if (rhs == null) {
  745.             return false;
  746.         }
  747.  
  748.         if (rhs == this) {
  749.             return true;
  750.         }
  751.  
  752.         // compare graphics, graphic count, advances (if any),
  753.         // total advance, adjust, levels (if any),
  754.         // visual order (if any)
  755.  
  756.         if (!fGraphic.equals(rhs.fGraphic)) {
  757.             return false;
  758.         }
  759.  
  760.         if (fGraphicCount != rhs.fGraphicCount) {
  761.             return false;
  762.         }
  763.  
  764.         if (fAdjust != rhs.fAdjust) {
  765.             return false;
  766.         }
  767.  
  768.         if (fTotalAdvance != rhs.fTotalAdvance) {
  769.             return false;
  770.         }
  771.  
  772.         if (fAdvances == null ^ rhs.fAdvances == null) {
  773.             return false;
  774.         }
  775.  
  776.         if (fLevels == null ^ rhs.fLevels == null) {
  777.             return false;
  778.         }
  779.  
  780.         if (fVisualOrder == null ^ rhs.fVisualOrder == null) {
  781.             return false;
  782.         }
  783.  
  784.         if (fAdvances != null) {
  785.             if (fAdvances != rhs.fAdvances) {
  786.                 for (int i=0; i < fGraphicCount; i++) {
  787.                     if (fAdvances[i] != rhs.fAdvances[i]) {
  788.                         return false;
  789.                     }
  790.                 }
  791.             }
  792.         }
  793.  
  794.         if (fLevels != null) {
  795.             if (fLevels != rhs.fLevels) {
  796.                 for (int i=0; i < fGraphicCount; i++) {
  797.                     if (fLevels[i] != rhs.fLevels[i]) {
  798.                         return false;
  799.                     }
  800.                 }
  801.             }
  802.         }
  803.  
  804.         if (fVisualOrder != null) {
  805.             if (fVisualOrder != rhs.fVisualOrder) {
  806.                 for (int i=0; i < fGraphicCount; i++) {
  807.                     if (fVisualOrder[i] != rhs.fVisualOrder[i]) {
  808.                         return false;
  809.                     }
  810.                 }
  811.             }
  812.         }
  813.  
  814.         return true;
  815.     }
  816.  
  817.     public float getItalicAngle() {
  818.  
  819.         return 0;
  820.     }
  821. }
  822.