1 18 package org.apache.batik.extension.svg; 19 20 import java.awt.font.FontRenderContext ; 21 import java.awt.geom.Point2D ; 22 import java.awt.geom.Rectangle2D ; 23 import java.text.AttributedCharacterIterator ; 24 25 import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; 26 import org.apache.batik.gvt.font.AWTGVTFont; 27 import org.apache.batik.gvt.font.GVTFont; 28 import org.apache.batik.gvt.font.GVTGlyphVector; 29 import org.apache.batik.gvt.font.GVTLineMetrics; 30 31 public class GlyphIterator { 32 public static final AttributedCharacterIterator.Attribute PREFORMATTED 33 = GVTAttributedCharacterIterator.TextAttribute.PREFORMATTED; 34 35 public static final AttributedCharacterIterator.Attribute FLOW_LINE_BREAK 36 = GVTAttributedCharacterIterator.TextAttribute.FLOW_LINE_BREAK; 37 38 public static final AttributedCharacterIterator.Attribute 39 TEXT_COMPOUND_DELIMITER 40 = GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER; 41 public static final 42 AttributedCharacterIterator.Attribute GVT_FONT 43 = GVTAttributedCharacterIterator.TextAttribute.GVT_FONT; 44 45 public static final char SOFT_HYPHEN = 0x00AD; 46 public static final char ZERO_WIDTH_SPACE = 0x200B; 47 public static final char ZERO_WIDTH_JOINER = 0x200D; 48 49 int idx = -1; 51 int chIdx = -1; 53 int lineIdx = -1; 54 55 int aciIdx = -1; 57 int charCount = -1; 59 60 float adv = 0; 62 float adj = 0; 64 int runLimit = 0; 66 67 int lineBreakRunLimit = 0; 69 int lineBreakCount = 0; 70 71 72 GVTFont font = null; 73 int fontStart = 0; 74 float maxAscent = 0; 75 float maxDescent = 0; 76 float maxFontSize = 0; 77 78 float width = 0; 79 char ch = 0; 81 int numGlyphs = 0; 83 AttributedCharacterIterator aci; 85 GVTGlyphVector gv; 87 float [] gp; 89 FontRenderContext frc; 91 92 int [] leftShiftIdx = null; 94 float [] leftShiftAmt = null; 96 int leftShift = 0; 98 99 Point2D gvBase = null; 100 101 102 public GlyphIterator(AttributedCharacterIterator aci, 103 GVTGlyphVector gv) { 104 this.aci = aci; 105 this.gv = gv; 106 107 this.idx = 0; 108 this.chIdx = 0; 109 this.lineIdx = 0; 110 this.aciIdx = aci.getBeginIndex(); 111 this.charCount = gv.getCharacterCount(idx, idx); 112 this.ch = aci.first(); 113 this.frc = gv.getFontRenderContext(); 114 115 this.font = (GVTFont)aci.getAttribute(GVT_FONT); 116 if (font == null) { 117 font = new AWTGVTFont(aci.getAttributes()); 118 } 119 fontStart = aciIdx; 120 this.maxFontSize = -Float.MAX_VALUE; 121 this.maxAscent = -Float.MAX_VALUE; 122 this.maxDescent = -Float.MAX_VALUE; 123 124 this.runLimit = aci.getRunLimit(TEXT_COMPOUND_DELIMITER); 126 127 this.lineBreakRunLimit = aci.getRunLimit(FLOW_LINE_BREAK); 128 Object o = aci.getAttribute(FLOW_LINE_BREAK); 129 this.lineBreakCount = (o == null)?0:1; 130 131 132 this.numGlyphs = gv.getNumGlyphs(); 133 this.gp = gv.getGlyphPositions(0, this.numGlyphs+1, null); 134 this.gvBase = new Point2D.Float (gp[0], gp[1]); 135 this.adv = getCharWidth(); 136 this.adj = getCharAdvance(); 137 } 138 139 public GlyphIterator(GlyphIterator gi) { 140 gi.copy(this); 141 } 142 143 public GlyphIterator copy() { 144 return new GlyphIterator(this); 145 } 146 147 public GlyphIterator copy(GlyphIterator gi) { 148 if (gi == null) 149 return new GlyphIterator(this); 150 151 gi.idx = this.idx; 152 gi.chIdx = this.chIdx; 153 gi.aciIdx = this.aciIdx; 154 gi.charCount = this.charCount; 155 gi.adv = this.adv; 156 gi.adj = this.adj; 157 gi.runLimit = this.runLimit; 158 gi.ch = this.ch; 159 gi.numGlyphs = this.numGlyphs; 160 gi.gp = this.gp; 161 gi.gvBase = this.gvBase; 162 163 gi.lineBreakRunLimit = this.lineBreakRunLimit; 164 gi.lineBreakCount = this.lineBreakCount; 165 166 gi.frc = this.frc; 167 gi.font = this.font; 168 gi.fontStart = this.fontStart; 169 gi.maxAscent = this.maxAscent; 170 gi.maxDescent = this.maxDescent; 171 gi.maxFontSize = this.maxFontSize; 172 173 gi.leftShift = this.leftShift; 174 gi.leftShiftIdx = this.leftShiftIdx; 175 gi.leftShiftAmt = this.leftShiftAmt; 176 return gi; 177 } 178 179 182 public int getGlyphIndex() { return idx; } 183 184 187 public char getChar() { return ch; } 188 189 193 public int getACIIndex() { return aciIdx; } 194 195 199 public float getAdv() { return adv; } 200 201 205 public Point2D getOrigin() { return gvBase; } 206 207 212 public float getAdj() { return adj; } 213 214 public float getMaxFontSize() { 215 if (aciIdx >= fontStart) { 216 int newFS = aciIdx + charCount; 217 updateLineMetrics(newFS); 218 fontStart = newFS; 219 } 220 return maxFontSize; 221 } 222 223 public float getMaxAscent() { 224 if (aciIdx >= fontStart) { 225 int newFS = aciIdx + charCount; 226 updateLineMetrics(newFS); 227 fontStart = newFS; 228 } 229 return maxAscent; 230 } 231 232 public float getMaxDescent() { 233 if (aciIdx >= fontStart) { 234 int newFS = aciIdx + charCount; 235 updateLineMetrics(newFS); 236 fontStart = newFS; 237 } 238 return maxDescent; 239 } 240 241 public boolean isLastChar() { 242 return (idx == (numGlyphs-1)); 243 } 244 245 public boolean done() { 246 return (idx >= numGlyphs); 247 } 248 249 public boolean isBreakChar() { 250 switch (ch) { 251 case GlyphIterator.ZERO_WIDTH_SPACE: return true; 252 case GlyphIterator.ZERO_WIDTH_JOINER: return false; 253 case GlyphIterator.SOFT_HYPHEN: return true; 254 case ' ': case '\t': return true; 255 default: return false; 256 } 257 } 258 259 protected boolean isPrinting(char tstCH) { 260 switch (ch) { 261 case GlyphIterator.ZERO_WIDTH_SPACE: return false; 262 case GlyphIterator.ZERO_WIDTH_JOINER: return false; 263 case GlyphIterator.SOFT_HYPHEN: return true; 264 case ' ': case '\t': return false; 265 default: return true; 266 } 267 } 268 269 public int getLineBreaks() { 270 int ret = 0; 271 if (aciIdx+charCount >= lineBreakRunLimit) { 272 ret = lineBreakCount; 275 276 aci.setIndex(aciIdx+charCount); 279 lineBreakRunLimit = aci.getRunLimit(FLOW_LINE_BREAK); 280 aci.setIndex(aciIdx); 282 Object o = aci.getAttribute(FLOW_LINE_BREAK); 283 lineBreakCount = (o == null)?0:1; 284 } 285 return ret; 286 } 287 288 291 public void nextChar() { 292 if ((ch == SOFT_HYPHEN) || 293 (ch == ZERO_WIDTH_SPACE) || 294 (ch == ZERO_WIDTH_JOINER)) { 295 gv.setGlyphVisible(idx, false); 297 float chAdv = getCharAdvance(); 298 adj -= chAdv; 299 addLeftShift(idx, chAdv); 300 } 301 302 aciIdx += charCount; 303 ch = aci.setIndex(aciIdx); 304 idx++; 305 charCount = gv.getCharacterCount(idx,idx); 306 if (idx == numGlyphs) return; 307 308 if (aciIdx >= runLimit) { 309 updateLineMetrics(aciIdx); 310 runLimit = aci.getRunLimit(TEXT_COMPOUND_DELIMITER); 311 font = (GVTFont)aci.getAttribute(GVT_FONT); 312 if (font == null) { 313 font = new AWTGVTFont(aci.getAttributes()); 314 } 315 fontStart = aciIdx; 316 } 317 318 float chAdv = getCharAdvance(); 319 adj += chAdv; 320 if (isPrinting()) { 321 chIdx = idx; 322 float chW = getCharWidth(); 323 adv = adj-(chAdv-chW); 324 } 325 } 326 327 protected void addLeftShift(int idx, float chAdv) { 328 if (leftShiftIdx == null) { 329 leftShiftIdx = new int[1]; 330 leftShiftIdx[0] = idx; 331 leftShiftAmt = new float[1]; 332 leftShiftAmt[0] = chAdv; 333 } else { 334 int [] newLeftShiftIdx = new int[leftShiftIdx.length+1]; 335 for (int i=0; i<leftShiftIdx.length; i++) 336 newLeftShiftIdx[i] = leftShiftIdx[i]; 337 newLeftShiftIdx[leftShiftIdx.length] = idx; 338 leftShiftIdx = newLeftShiftIdx; 339 340 float [] newLeftShiftAmt = new float[leftShiftAmt.length+1]; 341 for (int i=0; i<leftShiftAmt.length; i++) 342 newLeftShiftAmt[i] = leftShiftAmt[i]; 343 newLeftShiftAmt[leftShiftAmt.length] = chAdv; 344 leftShiftAmt = newLeftShiftAmt; 345 } 346 } 347 348 protected void updateLineMetrics(int end) { 349 GVTLineMetrics glm = font.getLineMetrics 350 (aci, fontStart, end, frc); 351 float ascent = glm.getAscent(); 352 float descent = glm.getDescent(); 353 float fontSz = font.getSize(); 354 if (ascent > maxAscent) maxAscent = ascent; 355 if (descent > maxDescent) maxDescent = descent; 356 if (fontSz > maxFontSize) maxFontSize = fontSz; 357 } 358 359 public LineInfo newLine(Point2D.Float loc, 360 float lineWidth, 361 boolean partial, 362 Point2D.Float verticalAlignOffset) { 363 if (ch == SOFT_HYPHEN) { 364 gv.setGlyphVisible(idx, true); 365 } 366 367 int lsi = 0; 368 int nextLSI; 369 if (leftShiftIdx != null) nextLSI = leftShiftIdx[lsi]; 370 else nextLSI = idx+1; 371 for (int ci=lineIdx; ci<=idx; ci++) { 372 if (ci == nextLSI) { 373 leftShift += leftShiftAmt[lsi++]; 374 if (lsi < leftShiftIdx.length) 375 nextLSI = leftShiftIdx[lsi]; 376 } 377 gv.setGlyphPosition(ci, new Point2D.Float (gp[2*ci]-leftShift, 378 gp[2*ci+1])); 379 } 380 leftShiftIdx = null; 381 leftShiftAmt = null; 382 383 float lineInfoChW; 384 int hideIdx; 385 if ((chIdx != 0) || (isPrinting())) { 387 lineInfoChW = getCharWidth(chIdx); 388 hideIdx = chIdx+1; 389 } else { 390 lineInfoChW = 0; 391 hideIdx = 0; 392 } 393 394 int lineInfoIdx = idx+1; 395 float lineInfoAdv = adv; 396 float lineInfoAdj = adj; 397 while (!done()) { 398 adv=0; 399 adj=0; 400 401 if ((ch == ZERO_WIDTH_SPACE) || 402 (ch == ZERO_WIDTH_JOINER)) 403 gv.setGlyphVisible(idx, false); 404 405 ch = 0; nextChar(); 407 408 if (isPrinting()) break; 409 410 lineInfoIdx = idx+1; 411 lineInfoAdj += adj; 412 } 413 414 for (int i = hideIdx; i<lineInfoIdx; i++) { 416 gv.setGlyphVisible(i, false); 417 } 418 419 maxAscent = -Float.MAX_VALUE; 420 maxDescent = -Float.MAX_VALUE; 421 maxFontSize = -Float.MAX_VALUE; 422 LineInfo ret = new LineInfo(loc, aci, gv, lineIdx, lineInfoIdx, 423 lineInfoAdj, lineInfoAdv, lineInfoChW, 424 lineWidth, partial, verticalAlignOffset); 425 lineIdx = idx; 426 427 return ret; 428 } 429 430 public boolean isPrinting() { 431 if (aci.getAttribute(PREFORMATTED) == Boolean.TRUE) 432 return true; 433 return isPrinting(ch); 434 } 435 436 439 public float getCharAdvance() { 440 return getCharAdvance(idx); 441 } 442 443 448 public float getCharWidth() { 449 return getCharWidth(idx); 450 } 451 452 455 protected float getCharAdvance(int gvIdx) { 456 return gp[2*gvIdx+2] - gp[2*gvIdx]; 457 } 458 459 464 protected float getCharWidth(int gvIdx) { 465 Rectangle2D lcBound = gv.getGlyphVisualBounds(gvIdx).getBounds2D(); 466 Point2D lcLoc = gv.getGlyphPosition(gvIdx); 467 return (float)(lcBound.getX()+lcBound.getWidth()- lcLoc.getX()); 468 } 469 } 470 | Popular Tags |