376 lines · 11.5 KB · Java
RawDownload
/* * 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;  }}