1 /* 2 * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * (C) Copyright IBM Corp. 1998-2003, All Rights Reserved 28 * 29 */ 30 31 package sun.font; 32 33 import java.awt.Font; 34 import java.awt.Graphics2D; 35 import java.awt.Rectangle; 36 import java.awt.Shape; 37 import java.awt.font.FontRenderContext; 38 import java.awt.font.LineMetrics; 39 import java.awt.font.GraphicAttribute; 40 import java.awt.font.GlyphJustificationInfo; 41 import java.awt.geom.AffineTransform; 42 import java.awt.geom.GeneralPath; 43 import java.awt.geom.Rectangle2D; 44 import java.text.Bidi; 45 import java.util.Map; 46 47 public final class GraphicComponent implements TextLineComponent, 48 Decoration.Label { 49 50 public static final float GRAPHIC_LEADING = 2; 51 52 private GraphicAttribute graphic; 53 private int graphicCount; 54 private int[] charsLtoV; // possibly null 55 private byte[] levels; // possibly null 56 57 // evaluated in computeVisualBounds 58 private Rectangle2D visualBounds = null; 59 60 // used everywhere so we'll cache it 61 private float graphicAdvance; 62 63 private AffineTransform baseTx; 64 65 private CoreMetrics cm; 66 private Decoration decorator; 67 68 69 /** 70 * Create a new GraphicComponent. start and limit are indices 71 * into charLtoV and levels. charsLtoV and levels may be adopted. 72 */ GraphicComponent(GraphicAttribute graphic, Decoration decorator, int[] charsLtoV, byte[] levels, int start, int limit, AffineTransform baseTx)73 public GraphicComponent(GraphicAttribute graphic, 74 Decoration decorator, 75 int[] charsLtoV, 76 byte[] levels, 77 int start, 78 int limit, 79 AffineTransform baseTx) { 80 81 if (limit <= start) { 82 throw new IllegalArgumentException("0 or negative length in GraphicComponent"); 83 } 84 this.graphic = graphic; 85 this.graphicAdvance = graphic.getAdvance(); 86 this.decorator = decorator; 87 this.cm = createCoreMetrics(graphic); 88 this.baseTx = baseTx; 89 90 initLocalOrdering(charsLtoV, levels, start, limit); 91 } 92 GraphicComponent(GraphicComponent parent, int start, int limit, int dir)93 private GraphicComponent(GraphicComponent parent, int start, int limit, int dir) { 94 95 this.graphic = parent.graphic; 96 this.graphicAdvance = parent.graphicAdvance; 97 this.decorator = parent.decorator; 98 this.cm = parent.cm; 99 this.baseTx = parent.baseTx; 100 101 int[] charsLtoV = null; 102 byte[] levels = null; 103 104 if (dir == UNCHANGED) { 105 charsLtoV = parent.charsLtoV; 106 levels = parent.levels; 107 } 108 else if (dir == LEFT_TO_RIGHT || dir == RIGHT_TO_LEFT) { 109 limit -= start; 110 start = 0; 111 if (dir == RIGHT_TO_LEFT) { 112 charsLtoV = new int[limit]; 113 levels = new byte[limit]; 114 for (int i=0; i < limit; i++) { 115 charsLtoV[i] = limit-i-1; 116 levels[i] = (byte) 1; 117 } 118 } 119 } 120 else { 121 throw new IllegalArgumentException("Invalid direction flag"); 122 } 123 124 initLocalOrdering(charsLtoV, levels, start, limit); 125 } 126 127 /** 128 * Initialize graphicCount, also charsLtoV and levels arrays. 129 */ initLocalOrdering(int[] charsLtoV, byte[] levels, int start, int limit)130 private void initLocalOrdering(int[] charsLtoV, 131 byte[] levels, 132 int start, 133 int limit) { 134 135 this.graphicCount = limit - start; // todo: should be codepoints? 136 137 if (charsLtoV == null || charsLtoV.length == graphicCount) { 138 this.charsLtoV = charsLtoV; 139 } 140 else { 141 this.charsLtoV = BidiUtils.createNormalizedMap(charsLtoV, levels, start, limit); 142 } 143 144 if (levels == null || levels.length == graphicCount) { 145 this.levels = levels; 146 } 147 else { 148 this.levels = new byte[graphicCount]; 149 System.arraycopy(levels, start, this.levels, 0, graphicCount); 150 } 151 } 152 isSimple()153 public boolean isSimple() { 154 return false; 155 } 156 getPixelBounds(FontRenderContext frc, float x, float y)157 public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) { 158 throw new InternalError("do not call if isSimple returns false"); 159 } 160 handleGetVisualBounds()161 public Rectangle2D handleGetVisualBounds() { 162 163 Rectangle2D bounds = graphic.getBounds(); 164 165 float width = (float) bounds.getWidth() + 166 graphicAdvance * (graphicCount-1); 167 168 return new Rectangle2D.Float((float) bounds.getX(), 169 (float) bounds.getY(), 170 width, 171 (float) bounds.getHeight()); 172 } 173 getCoreMetrics()174 public CoreMetrics getCoreMetrics() { 175 return cm; 176 } 177 createCoreMetrics(GraphicAttribute graphic)178 public static CoreMetrics createCoreMetrics(GraphicAttribute graphic) { 179 return new CoreMetrics(graphic.getAscent(), 180 graphic.getDescent(), 181 GRAPHIC_LEADING, 182 graphic.getAscent() + graphic.getDescent() + GRAPHIC_LEADING, 183 graphic.getAlignment(), 184 new float[] { 0, -graphic.getAscent() / 2, -graphic.getAscent() }, 185 -graphic.getAscent() / 2, 186 graphic.getAscent() / 12, 187 graphic.getDescent() / 3, 188 graphic.getAscent() / 12, 189 0, // ss offset 190 0); // italic angle -- need api for this 191 } 192 getItalicAngle()193 public float getItalicAngle() { 194 195 return 0; 196 } 197 getVisualBounds()198 public Rectangle2D getVisualBounds() { 199 200 if (visualBounds == null) { 201 visualBounds = decorator.getVisualBounds(this); 202 } 203 Rectangle2D.Float bounds = new Rectangle2D.Float(); 204 bounds.setRect(visualBounds); 205 return bounds; 206 } 207 handleGetOutline(float x, float y)208 public Shape handleGetOutline(float x, float y) { 209 double[] matrix = { 1, 0, 0, 1, x, y }; 210 211 if (graphicCount == 1) { 212 AffineTransform tx = new AffineTransform(matrix); 213 return graphic.getOutline(tx); 214 } 215 216 GeneralPath gp = new GeneralPath(); 217 for (int i = 0; i < graphicCount; ++i) { 218 AffineTransform tx = new AffineTransform(matrix); 219 gp.append(graphic.getOutline(tx), false); 220 matrix[4] += graphicAdvance; 221 } 222 223 return gp; 224 } 225 getBaselineTransform()226 public AffineTransform getBaselineTransform() { 227 return baseTx; 228 } 229 getOutline(float x, float y)230 public Shape getOutline(float x, float y) { 231 232 return decorator.getOutline(this, x, y); 233 } 234 handleDraw(Graphics2D g2d, float x, float y)235 public void handleDraw(Graphics2D g2d, float x, float y) { 236 237 for (int i=0; i < graphicCount; i++) { 238 239 graphic.draw(g2d, x, y); 240 x += graphicAdvance; 241 } 242 } 243 draw(Graphics2D g2d, float x, float y)244 public void draw(Graphics2D g2d, float x, float y) { 245 246 decorator.drawTextAndDecorations(this, g2d, x, y); 247 } 248 getCharVisualBounds(int index)249 public Rectangle2D getCharVisualBounds(int index) { 250 251 return decorator.getCharVisualBounds(this, index); 252 } 253 getNumCharacters()254 public int getNumCharacters() { 255 256 return graphicCount; 257 } 258 getCharX(int index)259 public float getCharX(int index) { 260 261 int visIndex = charsLtoV==null? index : charsLtoV[index]; 262 return graphicAdvance * visIndex; 263 } 264 getCharY(int index)265 public float getCharY(int index) { 266 267 return 0; 268 } 269 getCharAdvance(int index)270 public float getCharAdvance(int index) { 271 272 return graphicAdvance; 273 } 274 caretAtOffsetIsValid(int index)275 public boolean caretAtOffsetIsValid(int index) { 276 277 return true; 278 } 279 handleGetCharVisualBounds(int index)280 public Rectangle2D handleGetCharVisualBounds(int index) { 281 282 Rectangle2D bounds = graphic.getBounds(); 283 // don't modify their rectangle, just in case they don't copy 284 285 Rectangle2D.Float charBounds = new Rectangle2D.Float(); 286 charBounds.setRect(bounds); 287 charBounds.x += graphicAdvance * index; 288 289 return charBounds; 290 } 291 292 // measures characters in context, in logical order getLineBreakIndex(int start, float width)293 public int getLineBreakIndex(int start, float width) { 294 295 int index = (int) (width / graphicAdvance); 296 if (index > graphicCount - start) { 297 index = graphicCount - start; 298 } 299 return index; 300 } 301 302 // measures characters in context, in logical order getAdvanceBetween(int start, int limit)303 public float getAdvanceBetween(int start, int limit) { 304 305 return graphicAdvance * (limit - start); 306 } 307 getLogicalBounds()308 public Rectangle2D getLogicalBounds() { 309 310 float left = 0; 311 float top = -cm.ascent; 312 float width = graphicAdvance * graphicCount; 313 float height = cm.descent - top; 314 315 return new Rectangle2D.Float(left, top, width, height); 316 } 317 getAdvance()318 public float getAdvance() { 319 return graphicAdvance * graphicCount; 320 } 321 getItalicBounds()322 public Rectangle2D getItalicBounds() { 323 return getLogicalBounds(); 324 } 325 getSubset(int start, int limit, int dir)326 public TextLineComponent getSubset(int start, int limit, int dir) { 327 328 if (start < 0 || limit > graphicCount || start >= limit) { 329 throw new IllegalArgumentException("Invalid range. start=" 330 +start+"; limit="+limit); 331 } 332 333 if (start == 0 && limit == graphicCount && dir == UNCHANGED) { 334 return this; 335 } 336 337 return new GraphicComponent(this, start, limit, dir); 338 } 339 toString()340 public String toString() { 341 342 return "[graphic=" + graphic + ":count=" + getNumCharacters() + "]"; 343 } 344 345 /** 346 * Return the number of justification records this uses. 347 */ getNumJustificationInfos()348 public int getNumJustificationInfos() { 349 return 0; 350 } 351 352 /** 353 * Return GlyphJustificationInfo objects for the characters between 354 * charStart and charLimit, starting at offset infoStart. Infos 355 * will be in visual order. All positions between infoStart and 356 * getNumJustificationInfos will be set. If a position corresponds 357 * to a character outside the provided range, it is set to null. 358 */ getJustificationInfos(GlyphJustificationInfo[] infos, int infoStart, int charStart, int charLimit)359 public void getJustificationInfos(GlyphJustificationInfo[] infos, int infoStart, int charStart, int charLimit) { 360 } 361 362 /** 363 * Apply deltas to the data in this component, starting at offset 364 * deltaStart, and return the new component. There are two floats 365 * for each justification info, for a total of 2 * getNumJustificationInfos. 366 * The first delta is the left adjustment, the second is the right 367 * adjustment. 368 * <p> 369 * If flags[0] is true on entry, rejustification is allowed. If 370 * the new component requires rejustification (ligatures were 371 * formed or split), flags[0] will be set on exit. 372 */ applyJustificationDeltas(float[] deltas, int deltaStart, boolean[] flags)373 public TextLineComponent applyJustificationDeltas(float[] deltas, int deltaStart, boolean[] flags) { 374 return this; 375 } 376 } 377