Use Tree Navigation
public final class

GraphicComponent

extends Object
implements Decoration.Label TextLineComponent
/*
 * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


/*
 * (C) Copyright IBM Corp. 1998-2003, All Rights Reserved
 *
 */


package sun.font;

import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.font.GraphicAttribute;
import java.awt.font.GlyphJustificationInfo;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.text.Bidi;
import java.util.Map;

public final class GraphicComponent implements TextLineComponent,
                                               
Decoration.Label {

   
public static final float GRAPHIC_LEADING = 2;

   
private GraphicAttribute graphic;
   
private int graphicCount;
   
private int[] charsLtoV;  // possibly null
   
private byte[] levels; // possibly null

   
// evaluated in computeVisualBounds
   
private Rectangle2D visualBounds = null;

   
// used everywhere so we'll cache it
   
private float graphicAdvance;

   
private AffineTransform baseTx;

   
private CoreMetrics cm;
   
private Decoration decorator;


   
/**
     * Create a new GraphicComponent.  start and limit are indices
     * into charLtoV and levels.  charsLtoV and levels may be adopted.
     */

   
public GraphicComponent(GraphicAttribute graphic,
                           
Decoration decorator,
                           
int[] charsLtoV,
                           
byte[] levels,
                           
int start,
                           
int limit,
                           
AffineTransform baseTx) {

       
if (limit <= start) {
           
throw new IllegalArgumentException("0 or negative length in GraphicComponent");
       
}
       
this.graphic = graphic;
       
this.graphicAdvance = graphic.getAdvance();
       
this.decorator = decorator;
       
this.cm = createCoreMetrics(graphic);
       
this.baseTx = baseTx;

        initLocalOrdering
(charsLtoV, levels, start, limit);
   
}

   
private GraphicComponent(GraphicComponent parent, int start, int limit, int dir) {

       
this.graphic = parent.graphic;
       
this.graphicAdvance = parent.graphicAdvance;
       
this.decorator = parent.decorator;
       
this.cm = parent.cm;
       
this.baseTx = parent.baseTx;

       
int[] charsLtoV = null;
       
byte[] levels = null;

       
if (dir == UNCHANGED) {
            charsLtoV
= parent.charsLtoV;
            levels
= parent.levels;
       
}
       
else if (dir == LEFT_TO_RIGHT || dir == RIGHT_TO_LEFT) {
            limit
-= start;
            start
= 0;
           
if (dir == RIGHT_TO_LEFT) {
                charsLtoV
= new int[limit];
                levels
= new byte[limit];
               
for (int i=0; i < limit; i++) {
                    charsLtoV
[i] = limit-i-1;
                    levels
[i] = (byte) 1;
               
}
           
}
       
}
       
else {
           
throw new IllegalArgumentException("Invalid direction flag");
       
}

        initLocalOrdering
(charsLtoV, levels, start, limit);
   
}

   
/**
     * Initialize graphicCount, also charsLtoV and levels arrays.
     */

   
private void initLocalOrdering(int[] charsLtoV,
                                   
byte[] levels,
                                   
int start,
                                   
int limit) {

       
this.graphicCount = limit - start; // todo: should be codepoints?

       
if (charsLtoV == null || charsLtoV.length == graphicCount) {
           
this.charsLtoV = charsLtoV;
       
}
       
else {
           
this.charsLtoV = BidiUtils.createNormalizedMap(charsLtoV, levels, start, limit);
       
}

       
if (levels == null || levels.length == graphicCount) {
           
this.levels = levels;
       
}
       
else {
           
this.levels = new byte[graphicCount];
           
System.arraycopy(levels, start, this.levels, 0, graphicCount);
       
}
   
}

   
public boolean isSimple() {
       
return false;
   
}

   
public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) {
       
throw new InternalError("do not call if isSimple returns false");
   
}

   
public Rectangle2D handleGetVisualBounds() {

       
Rectangle2D bounds = graphic.getBounds();

       
float width = (float) bounds.getWidth() +
                                 graphicAdvance
* (graphicCount-1);

       
return new Rectangle2D.Float((float) bounds.getX(),
                                     
(float) bounds.getY(),
                                     width
,
                                     
(float) bounds.getHeight());
   
}

   
public CoreMetrics getCoreMetrics() {
       
return cm;
   
}

   
public static CoreMetrics createCoreMetrics(GraphicAttribute graphic) {
       
return new CoreMetrics(graphic.getAscent(),
                               graphic
.getDescent(),
                               GRAPHIC_LEADING
,
                               graphic
.getAscent() + graphic.getDescent() + GRAPHIC_LEADING,
                               graphic
.getAlignment(),
                               
new float[] { 0, -graphic.getAscent() / 2, -graphic.getAscent() },
                               
-graphic.getAscent() / 2,
                               graphic
.getAscent() / 12,
                               graphic
.getDescent() / 3,
                               graphic
.getAscent() / 12,
                               
0, // ss offset
                               
0); // italic angle -- need api for this
   
}

   
public float getItalicAngle() {

       
return 0;
   
}

   
public Rectangle2D getVisualBounds() {

       
if (visualBounds == null) {
            visualBounds
= decorator.getVisualBounds(this);
       
}
       
Rectangle2D.Float bounds = new Rectangle2D.Float();
        bounds
.setRect(visualBounds);
       
return bounds;
   
}

   
public Shape handleGetOutline(float x, float y) {
       
double[] matrix = { 1, 0, 0, 1, x, y };

       
if (graphicCount == 1) {
           
AffineTransform tx = new AffineTransform(matrix);
           
return graphic.getOutline(tx);
       
}

       
GeneralPath gp = new GeneralPath();
       
for (int i = 0; i < graphicCount; ++i) {
           
AffineTransform tx = new AffineTransform(matrix);
            gp
.append(graphic.getOutline(tx), false);
            matrix
[4] += graphicAdvance;
       
}

       
return gp;
   
}

   
public AffineTransform getBaselineTransform() {
       
return baseTx;
   
}

   
public Shape getOutline(float x, float y) {

       
return decorator.getOutline(this, x, y);
   
}

   
public void handleDraw(Graphics2D g2d, float x, float y) {

       
for (int i=0; i < graphicCount; i++) {

            graphic
.draw(g2d, x, y);
            x
+= graphicAdvance;
       
}
   
}

   
public void draw(Graphics2D g2d, float x, float y) {

        decorator
.drawTextAndDecorations(this, g2d, x, y);
   
}

   
public Rectangle2D getCharVisualBounds(int index) {

       
return decorator.getCharVisualBounds(this, index);
   
}

   
public int getNumCharacters() {

       
return graphicCount;
   
}

   
public float getCharX(int index) {

       
int visIndex = charsLtoV==null? index : charsLtoV[index];
       
return graphicAdvance * visIndex;
   
}

   
public float getCharY(int index) {

       
return 0;
   
}

   
public float getCharAdvance(int index) {

       
return graphicAdvance;
   
}

   
public boolean caretAtOffsetIsValid(int index) {

       
return true;
   
}

   
public Rectangle2D handleGetCharVisualBounds(int index) {

       
Rectangle2D bounds = graphic.getBounds();
       
// don't modify their rectangle, just in case they don't copy

       
Rectangle2D.Float charBounds = new Rectangle2D.Float();
        charBounds
.setRect(bounds);
        charBounds
.x += graphicAdvance * index;

       
return charBounds;
   
}

   
// measures characters in context, in logical order
   
public int getLineBreakIndex(int start, float width) {

       
int index = (int) (width / graphicAdvance);
       
if (index > graphicCount - start) {
            index
= graphicCount - start;
       
}
       
return index;
   
}

   
// measures characters in context, in logical order
   
public float getAdvanceBetween(int start, int limit) {

       
return graphicAdvance * (limit - start);
   
}

   
public Rectangle2D getLogicalBounds() {

       
float left = 0;
       
float top = -cm.ascent;
       
float width = graphicAdvance * graphicCount;
       
float height = cm.descent - top;

       
return new Rectangle2D.Float(left, top, width, height);
   
}

   
public float getAdvance() {
       
return graphicAdvance * graphicCount;
   
}

   
public Rectangle2D getItalicBounds() {
       
return getLogicalBounds();
   
}

   
public TextLineComponent getSubset(int start, int limit, int dir) {

       
if (start < 0 || limit > graphicCount || start >= limit) {
           
throw new IllegalArgumentException("Invalid range.  start="
                                               
+start+"; limit="+limit);
       
}

       
if (start == 0 && limit == graphicCount && dir == UNCHANGED) {
           
return this;
       
}

       
return new GraphicComponent(this, start, limit, dir);
   
}

   
public String toString() {

       
return "[graphic=" + graphic + ":count=" + getNumCharacters() + "]";
   
}

 
/**
   * Return the number of justification records this uses.
   */

 
public int getNumJustificationInfos() {
   
return 0;
 
}

 
/**
   * Return GlyphJustificationInfo objects for the characters between
   * charStart and charLimit, starting at offset infoStart.  Infos
   * will be in visual order.  All positions between infoStart and
   * getNumJustificationInfos will be set.  If a position corresponds
   * to a character outside the provided range, it is set to null.
   */

 
public void getJustificationInfos(GlyphJustificationInfo[] infos, int infoStart, int charStart, int charLimit) {
 
}

 
/**
   * Apply deltas to the data in this component, starting at offset
   * deltaStart, and return the new component.  There are two floats
   * for each justification info, for a total of 2 * getNumJustificationInfos.
   * The first delta is the left adjustment, the second is the right
   * adjustment.
   * <p>
   * If flags[0] is true on entry, rejustification is allowed.  If
   * the new component requires rejustification (ligatures were
   * formed or split), flags[0] will be set on exit.
   */

 
public TextLineComponent applyJustificationDeltas(float[] deltas, int deltaStart, boolean[] flags) {
   
return this;
 
}
}