1 18 19 package org.apache.batik.gvt.flow; 20 21 import java.awt.geom.Point2D ; 22 import java.awt.font.FontRenderContext ; 23 import java.text.AttributedCharacterIterator ; 24 import java.util.Iterator ; 25 import java.util.List ; 26 import java.util.LinkedList ; 27 28 import org.apache.batik.gvt.font.GVTFont; 29 import org.apache.batik.gvt.font.GVTGlyphVector; 30 import org.apache.batik.gvt.font.GVTLineMetrics; 31 import org.apache.batik.gvt.font.MultiGlyphVector; 32 import org.apache.batik.gvt.text.GlyphLayout; 33 34 import org.apache.batik.gvt.TextNode; 35 36 44 public class FlowGlyphLayout extends GlyphLayout { 45 46 public static final AttributedCharacterIterator.Attribute WORD_LIMIT 47 = TextLineBreaks.WORD_LIMIT; 48 49 50 public FlowGlyphLayout(AttributedCharacterIterator aci, 51 int [] charMap, 52 Point2D offset, 53 FontRenderContext frc) { 54 super(aci, charMap, offset, frc); 55 } 56 57 58 public static boolean textWrapTextChunk 59 (AttributedCharacterIterator [] acis, 60 List chunkLayouts, 61 List flowRects, 62 FontRenderContext frc) { 63 64 int numChunks = acis.length; 65 68 GVTGlyphVector [] gvs = new GVTGlyphVector[acis.length]; 72 WordInfo [][] wordInfos = new WordInfo[acis.length][]; 73 Iterator clIter = chunkLayouts.iterator(); 74 75 float prevBotMargin = 0; 76 int numWords = 0; 77 BlockInfo [] blockInfos = new BlockInfo[acis.length]; 78 float [] topSkip = new float[acis.length]; 79 for (int chunk=0; clIter.hasNext(); chunk++) { 80 AttributedCharacterIterator aci = acis[chunk]; 82 List gvl = new LinkedList (); 83 List layouts = (List )clIter.next(); 84 Iterator iter = layouts.iterator(); 85 while (iter.hasNext()) { 86 GlyphLayout gl = (GlyphLayout)iter.next(); 87 gvl.add(gl.getGlyphVector()); 88 } 89 GVTGlyphVector gv = new MultiGlyphVector(gvl); 90 gvs[chunk] = gv; 91 wordInfos[chunk] = doWordAnalysis(gv, aci, numWords, frc); 92 aci.first(); 93 BlockInfo bi = (BlockInfo)aci.getAttribute(FLOW_PARAGRAPH); 94 bi.initLineInfo(frc); 95 blockInfos[chunk] = bi; 96 if (prevBotMargin > bi.getTopMargin()) 97 topSkip[chunk] = prevBotMargin; 98 else 99 topSkip[chunk] = bi.getTopMargin(); 100 prevBotMargin = bi.getBottomMargin(); 101 numWords += wordInfos[chunk].length; 102 } 103 104 Iterator frIter = flowRects.iterator(); 105 RegionInfo currentRegion = null; 106 int currWord = 0; 107 int chunk = 0; 108 List lineInfos = new LinkedList (); 109 while(frIter.hasNext()) { 110 currentRegion = (RegionInfo) frIter.next(); 111 FlowRegions fr = new FlowRegions(currentRegion.getShape()); 112 113 while (chunk < wordInfos.length) { 114 WordInfo [] chunkInfo = wordInfos[chunk]; 115 BlockInfo bi = blockInfos[chunk]; 116 WordInfo wi = chunkInfo[currWord]; 117 Object flowLine = wi.getFlowLine(); 118 double lh = Math.max(wi.getLineHeight(),bi.getLineHeight()); 119 LineInfo li = new LineInfo(fr, bi, true); 120 double newY = li.getCurrentY()+topSkip[chunk]; 121 topSkip[chunk] = 0; 122 if (li.gotoY(newY)) break; 123 124 while (!li.addWord(wi)) { 125 newY = li.getCurrentY()+lh*.1; 127 if (li.gotoY(newY)) break; 128 } 129 if (fr.done()) break; 130 131 currWord++; 132 for (;currWord < chunkInfo.length;currWord++) { 133 wi = chunkInfo[currWord]; 134 if ((wi.getFlowLine() == flowLine) && (li.addWord(wi))) 135 continue; 136 137 li.layout(); 140 lineInfos.add(li); 141 li = null; 142 143 flowLine = wi.getFlowLine(); 144 lh = Math.max(wi.getLineHeight(),bi.getLineHeight()); 145 if (!fr.newLine(lh)) break; 147 li = new LineInfo(fr, bi, false); 148 while (!li.addWord(wi)) { 149 newY =li.getCurrentY()+lh*.1; 150 if (li.gotoY(newY)) break; 151 } 152 if (fr.done()) break; 153 } 154 if (li != null) { 155 li.setParaEnd(true); 156 li.layout(); 157 } 158 159 if (fr.done()) break; 160 161 chunk++; 162 currWord = 0; 163 164 if (bi.isFlowRegionBreak()) 165 break; 166 167 if (!fr.newLine(lh)) break; 169 } 170 if (chunk == wordInfos.length) 171 break; 172 } 173 174 boolean overflow = (chunk < wordInfos.length); 175 176 while (chunk < wordInfos.length) { 177 WordInfo [] chunkInfo = wordInfos[chunk]; 178 while (currWord < chunkInfo.length) { 179 WordInfo wi = chunkInfo[currWord]; 180 int numGG = wi.getNumGlyphGroups(); 181 for (int gg=0; gg<numGG; gg++) { 182 GlyphGroupInfo ggi = wi.getGlyphGroup(gg); 183 GVTGlyphVector gv = ggi.getGlyphVector(); 184 int end = ggi.getEnd(); 185 for (int g=ggi.getStart(); g <= end; g++) { 186 gv.setGlyphVisible(g, false); 187 } 188 } 189 currWord++; 190 } 191 chunk++; 192 currWord = 0; 193 } 194 195 return overflow; 196 } 197 198 static int [] allocWordMap(int [] wordMap, int sz) { 199 if (wordMap != null) { 200 if (sz <= wordMap.length) 201 return wordMap; 202 if (sz < wordMap.length*2) 203 sz = wordMap.length*2; 204 } 205 206 int [] ret = new int[sz]; 207 int ext=0; 208 if (wordMap != null) 209 ext = wordMap.length; 210 if (sz < ext) ext = sz; 211 int i=0; 212 for (; i<ext; i++) { 213 ret[i] = wordMap[i]; 214 } 215 for (; i<sz; i++) { 216 ret[i] = -1; 217 } 218 return ret; 219 } 220 221 225 static WordInfo[] doWordAnalysis(GVTGlyphVector gv, 226 AttributedCharacterIterator aci, 227 int numWords, 228 FontRenderContext frc) { 229 int numGlyphs = gv.getNumGlyphs(); 230 int [] glyphWords = new int[numGlyphs]; 231 int [] wordMap = allocWordMap(null, 10); 232 int maxWord = 0; 233 int aciIdx = aci.getBeginIndex(); 234 for (int i=0; i<numGlyphs; i++) { 239 int cnt = gv.getCharacterCount(i,i); 240 aci.setIndex(aciIdx); 241 Integer integer = (Integer )aci.getAttribute(WORD_LIMIT); 242 int minWord = integer.intValue()-numWords; 243 if (minWord > maxWord) { 244 maxWord = minWord; 245 wordMap = allocWordMap(wordMap, maxWord+1); 246 } 247 aciIdx++; 248 for (int c=1; c<cnt; c++) { 249 aci.setIndex(aciIdx); 250 integer = (Integer )aci.getAttribute(WORD_LIMIT); 251 int cWord = integer.intValue()-numWords; 252 if (cWord > maxWord) { 253 maxWord = cWord; 254 wordMap = allocWordMap(wordMap, maxWord+1); 255 } 256 if (cWord < minWord) { 259 wordMap[minWord] = cWord; 260 minWord = cWord; 261 } else if (cWord > minWord) { 262 wordMap[cWord] = minWord; 263 } 264 aciIdx++; 265 } 266 glyphWords[i] = minWord; 267 } 268 int words=0; 269 WordInfo [] cWordMap = new WordInfo[maxWord+1]; 270 for (int i=0; i<=maxWord; i++) { 271 int nw = wordMap[i]; 272 if (nw == -1) { 273 cWordMap[i] = new WordInfo(words++); 275 } else { 276 int word = nw; 277 nw = wordMap[i]; 278 while (nw != -1) { 279 word = nw; 280 nw = wordMap[word]; 281 } 282 wordMap[i] = word; cWordMap[i] = cWordMap[word]; 284 } 285 } 286 wordMap = null; 287 WordInfo [] wordInfos = new WordInfo[words]; 288 for (int i=0; i<=maxWord; i++) { 289 WordInfo wi = cWordMap[i]; 290 wordInfos[wi.getIndex()] = cWordMap[i]; 291 } 292 293 aciIdx = aci.getBeginIndex(); 294 int aciEnd = aci.getEndIndex(); 295 char ch = aci.setIndex(aciIdx); 296 297 int aciWordStart = aciIdx; 298 GVTFont gvtFont = (GVTFont)aci.getAttribute(GVT_FONT); 299 float lineHeight = ((Float )aci.getAttribute(LINE_HEIGHT)).floatValue(); 300 int runLimit = aci.getRunLimit(szAtts); 301 WordInfo prevWI = null; 302 float [] lastAdvAdj = new float [numGlyphs]; 303 float [] advAdj = new float [numGlyphs]; 304 boolean [] hideLast = new boolean[numGlyphs]; 305 boolean [] hide = new boolean[numGlyphs]; 306 boolean [] space = new boolean[numGlyphs]; 307 float [] glyphPos = gv.getGlyphPositions(0, numGlyphs+1, null); 308 for (int i=0; i<numGlyphs; i++) { 309 char pch = ch; 310 ch = aci.setIndex(aciIdx); 311 Integer integer = (Integer )aci.getAttribute(WORD_LIMIT); 312 WordInfo theWI = cWordMap[integer.intValue()-numWords]; 313 if (theWI.getFlowLine() == null) 314 theWI.setFlowLine(aci.getAttribute(FLOW_LINE_BREAK)); 315 316 if (prevWI == null) { 317 prevWI = theWI; 318 } else if (prevWI != theWI) { 319 GVTLineMetrics lm = gvtFont.getLineMetrics 320 (aci, aciWordStart, aciIdx, frc); 321 prevWI.addLineMetrics(gvtFont, lm); 322 prevWI.addLineHeight(lineHeight); 323 aciWordStart = aciIdx; 324 prevWI = theWI; 325 } 326 327 int chCnt = gv.getCharacterCount(i,i); 328 if (chCnt == 1) { 329 char nch; 330 float kern; 331 switch(ch) { 332 case SOFT_HYPHEN: 333 hideLast[i] = true; 334 nch = aci.next(); aci.previous(); 335 kern = gvtFont.getHKern(pch, nch); 336 advAdj[i] = -(glyphPos[2*i+2]-glyphPos[2*i]+kern); 337 break; 338 case ZERO_WIDTH_JOINER: 339 hide[i] = true; 340 break; 341 case ZERO_WIDTH_SPACE: 342 hide[i] = true; 343 break; 344 case SPACE: 345 space[i] = true; 346 nch = aci.next(); aci.previous(); 347 kern = gvtFont.getHKern(pch, nch); 348 lastAdvAdj[i] = -(glyphPos[2*i+2]-glyphPos[2*i]+kern); 349 default: 350 } 351 } 352 353 aciIdx += chCnt; 354 if ((aciIdx > runLimit) && (aciIdx < aciEnd)) { 355 GVTLineMetrics lm = gvtFont.getLineMetrics 358 (aci,aciWordStart, runLimit, frc); 359 prevWI.addLineMetrics(gvtFont, lm); 360 prevWI.addLineHeight(lineHeight); 361 prevWI = null; 362 aciWordStart = aciIdx; 363 aci.setIndex(aciIdx); 364 gvtFont = (GVTFont)aci.getAttribute(GVT_FONT); 365 Float f = (Float )aci.getAttribute(LINE_HEIGHT); 366 lineHeight = f.floatValue(); 367 runLimit = aci.getRunLimit(szAtts); 368 } 369 } 370 GVTLineMetrics lm = gvtFont.getLineMetrics 371 (aci,aciWordStart, runLimit, frc); 372 prevWI.addLineMetrics(gvtFont, lm); 373 prevWI.addLineHeight(lineHeight); 374 375 int [] wordGlyphCounts = new int[words]; 376 for (int i=0; i<numGlyphs; i++) { 378 int word = glyphWords[i]; 379 int cWord = cWordMap[word].getIndex(); 380 glyphWords[i] = cWord; 381 wordGlyphCounts[cWord]++; 382 } 383 384 cWordMap = null; 385 int [][]wordGlyphs = new int [words][]; 386 int []wordGlyphGroupsCounts = new int [words]; 387 for (int i=0; i<numGlyphs; i++) { 388 int cWord = glyphWords[i]; 389 int [] wgs = wordGlyphs[cWord]; 391 if (wgs == null) { 392 wgs = wordGlyphs[cWord] 393 = new int[wordGlyphCounts[cWord]]; 394 wordGlyphCounts[cWord] =0; 398 } 399 int cnt = wordGlyphCounts[cWord]; 400 wgs[cnt] = i; 401 if (cnt==0) { 403 wordGlyphGroupsCounts[cWord]++; 404 } else { 405 if (wgs[cnt-1] != i-1) 406 wordGlyphGroupsCounts[cWord]++; 407 } 408 wordGlyphCounts[cWord]++; 409 } 410 411 for (int i=0; i<words; i++) { 412 int cnt = wordGlyphGroupsCounts[i]; 413 GlyphGroupInfo []wordGlyphGroups = new GlyphGroupInfo[cnt]; 415 if (cnt == 1) { 416 int [] glyphs = wordGlyphs[i]; 417 int start = glyphs[0]; 418 int end = glyphs[glyphs.length-1]; 419 wordGlyphGroups[0] = new GlyphGroupInfo 420 (gv, start, end, hide, hideLast[end], 421 glyphPos, advAdj, lastAdvAdj, space); 422 } else { 423 int glyphGroup = 0; 424 int []glyphs = wordGlyphs[i]; 425 int prev = glyphs[0]; 426 int start = prev; 427 for (int j=1; j<glyphs.length; j++) { 428 if (prev+1 != glyphs[j]) { 429 int end = glyphs[j-1]; 430 wordGlyphGroups[glyphGroup] = new GlyphGroupInfo 431 (gv, start, end, hide, hideLast[end], 432 glyphPos, advAdj, lastAdvAdj, space); 433 start = glyphs[j]; 434 glyphGroup++; 435 } 436 prev = glyphs[j]; 437 } 438 int end = glyphs[glyphs.length-1]; 439 wordGlyphGroups[glyphGroup] = new GlyphGroupInfo 440 (gv, start, end, hide, hideLast[end], 441 glyphPos, advAdj, lastAdvAdj, space); 442 } 443 wordInfos[i].setGlyphGroups(wordGlyphGroups); 444 } 445 return wordInfos; 446 } 447 } 448 | Popular Tags |