KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > gvt > flow > FlowGlyphLayout


1 /*
2
3    Copyright 2004 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17 */

18
19 package org.apache.batik.gvt.flow;
20
21 import java.awt.geom.Point2D JavaDoc;
22 import java.awt.font.FontRenderContext JavaDoc;
23 import java.text.AttributedCharacterIterator JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.LinkedList JavaDoc;
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 /**
37  * One line Class Desc
38  *
39  * Complete Class Desc
40  *
41  * @author <a HREF="mailto:deweese@apache.org">deweese</a>
42  * @version $Id: FlowGlyphLayout.java,v 1.5 2005/03/27 08:58:34 cam Exp $
43  */

44 public class FlowGlyphLayout extends GlyphLayout {
45
46     public static final AttributedCharacterIterator.Attribute JavaDoc WORD_LIMIT
47         = TextLineBreaks.WORD_LIMIT;
48
49
50     public FlowGlyphLayout(AttributedCharacterIterator JavaDoc aci,
51                            int [] charMap,
52                            Point2D JavaDoc offset,
53                            FontRenderContext JavaDoc frc) {
54         super(aci, charMap, offset, frc);
55     }
56
57
58     public static boolean textWrapTextChunk
59         (AttributedCharacterIterator JavaDoc [] acis,
60          List JavaDoc chunkLayouts,
61          List JavaDoc flowRects,
62          FontRenderContext JavaDoc frc) {
63
64         int numChunks = acis.length;
65         // System.out.println("Len: " + acis.length + " Size: " +
66
// chunkLayouts.size());
67

68         // Make a list of the GlyphVectors so we can construct a
69
// multiGlyphVector that makes them all look like one big
70
// glyphVector
71
GVTGlyphVector [] gvs = new GVTGlyphVector[acis.length];
72         WordInfo [][] wordInfos = new WordInfo[acis.length][];
73         Iterator JavaDoc 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             // System.out.println("Chunk: " + chunk);
81
AttributedCharacterIterator JavaDoc aci = acis[chunk];
82             List JavaDoc gvl = new LinkedList JavaDoc();
83             List JavaDoc layouts = (List JavaDoc)clIter.next();
84             Iterator JavaDoc 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 JavaDoc frIter = flowRects.iterator();
105         RegionInfo currentRegion = null;
106         int currWord = 0;
107         int chunk = 0;
108         List JavaDoc lineInfos = new LinkedList JavaDoc();
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 JavaDoc 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                     // step down 1/10 of a line height and try again.
126
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                     // Word didn't fit or we hit end of flowLine elem,
138
// go to a new line.
139
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; // region is done
146

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)) // Region is done.
168
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     /**
222      * This returns an array of glyphs numbers for each glyph
223      * group in each word: ret[word][glyphGroup][glyphNum].
224      */

225     static WordInfo[] doWordAnalysis(GVTGlyphVector gv,
226                                     AttributedCharacterIterator JavaDoc aci,
227                                     int numWords,
228                                     FontRenderContext JavaDoc 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         // First we go through the glyphs seeing if any two ajacent
235
// words need to be collapsed because of a ligature. This
236
// would be an odd case. If it happens we consider the
237
// two words to be one.
238
for (int i=0; i<numGlyphs; i++) {
239             int cnt = gv.getCharacterCount(i,i);
240             aci.setIndex(aciIdx);
241             Integer JavaDoc integer = (Integer JavaDoc)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 JavaDoc)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                 // We always want to use the min word as the main
257
// index for the new composite word.
258
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                 // new word so give it a number.
274
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; // help the next guy out
283
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 JavaDoc)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 JavaDoc integer = (Integer JavaDoc)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                 // Possible font size/style change so record current
356
// line metrics and start fresh.
357
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 JavaDoc f = (Float JavaDoc)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         // Build a mapping from words to glyphs.
377
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             // System.err.println("CW: " + cWord);
390
int [] wgs = wordGlyphs[cWord];
391             if (wgs == null) {
392                 wgs = wordGlyphs[cWord]
393                     = new int[wordGlyphCounts[cWord]];
394                 // We use this to track where the next
395
// glyph should go in wordGlyphs
396
// by the time we are done it should be correct again.
397
wordGlyphCounts[cWord] =0;
398             }
399             int cnt = wordGlyphCounts[cWord];
400             wgs[cnt] = i;
401             // Track the number of glyph groups in this word.
402
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             // System.err.println("WGGC: " + cnt);
414
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