KickJava   Java API By Example, From Geeks To Geeks.

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


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 org.apache.batik.gvt.font.GVTGlyphVector;
23
24 /**
25  * One line Class Desc
26  *
27  * Complete Class Desc
28  *
29  * @author <a HREF="mailto:deweese@apache.org">deweese</a>
30  * @version $Id: LineInfo.java,v 1.5 2005/03/27 08:58:34 cam Exp $
31  */

32 public class LineInfo {
33     FlowRegions fr;
34     double lineHeight = -1;
35     double ascent = -1;
36     double descent = -1;
37     double hLeading = -1;
38     double baseline;
39     int numGlyphs;
40     int words = 0;
41
42     int size=0;
43     GlyphGroupInfo [] ggis=null;
44     int newSize=0;
45     GlyphGroupInfo [] newGGIS=null;
46
47     int numRanges;
48     double [] ranges;
49     double [] rangeAdv;
50
51     BlockInfo bi = null;
52     boolean paraStart;
53     boolean paraEnd;
54
55     protected final static int FULL_WORD = 0;
56     protected final static int FULL_ADV = 1;
57
58
59     public LineInfo(FlowRegions fr, BlockInfo bi, boolean paraStart) {
60         this.fr = fr;
61         this.bi = bi;
62         this.lineHeight = bi.getLineHeight();
63         this.ascent = bi.getAscent();
64         this.descent = bi.getDescent();
65         this.hLeading = (lineHeight-(ascent+descent))/2;
66         this.baseline = (float)(fr.getCurrentY()+hLeading+ascent);
67         this.paraStart = paraStart;
68         this.paraEnd = false;
69         if (lineHeight > 0) {
70             fr.newLineHeight(lineHeight);
71             updateRangeInfo();
72         }
73
74     }
75
76     public void setParaEnd(boolean paraEnd) {
77         this.paraEnd = paraEnd;
78     }
79
80     public boolean addWord(WordInfo wi) {
81         double nlh = wi.getLineHeight();
82         if (nlh <= lineHeight)
83             return insertWord(wi);
84         fr.newLineHeight(nlh);
85
86         if (!updateRangeInfo()) {
87             if (lineHeight > 0) // restore old LH
88
fr.newLineHeight(lineHeight);
89             return false;
90         }
91             
92         if (!insertWord(wi)) {
93             if (lineHeight > 0) // Failure, restore old line Height.
94
setLineHeight(lineHeight);
95             return false;
96         }
97             
98         // Success, word fits on line.
99
lineHeight = nlh;
100         if (wi.getAscent() > ascent)
101             ascent = wi.getAscent();
102         if (wi.getDescent() > descent)
103             descent = wi.getDescent();
104         hLeading = (nlh-(ascent+descent))/2;
105         baseline = (float)(fr.getCurrentY()+hLeading+ascent);
106         return true;
107     }
108
109     public boolean insertWord(WordInfo wi) {
110         // Merge wi's glyph groups into the current GGI's.
111
// This puts them into newGGIS, so if it fails we can
112
// retain to the old ggis array.
113
mergeGlyphGroups(wi);
114         
115         if (!assignGlyphGroupRanges(newSize, newGGIS))
116             return false;
117         
118         // Swap in to GG info.
119
swapGlyphGroupInfo();
120
121         return true;
122     }
123     static final float MAX_COMPRESS=0.1f;
124     static final float COMRESS_SCALE=3;
125
126     public boolean assignGlyphGroupRanges(int ggSz, GlyphGroupInfo []ggis) {
127         int i=0, r=0;
128         while (r<numRanges) {
129             double range = ranges[2*r+1]-ranges[2*r];
130             float adv=0;
131             float rangeAdvance = 0;
132             double pdelta = range;
133
134             while (i<ggSz) {
135                 GlyphGroupInfo ggi = ggis[i];
136                 ggi.setRange(r);
137                 adv = ggi.getAdvance();
138                 double delta = range-(rangeAdvance + adv);
139                 if (delta < 0) break;
140                 
141                 i++;
142                 rangeAdvance += adv;
143             }
144
145             // Check last glyphGroup anyways...
146
if (i == ggSz) {
147                 i--;
148                 rangeAdvance -= adv;
149             }
150
151             GlyphGroupInfo ggi = ggis[i];
152             float ladv = ggi.getLastAdvance();
153             while (rangeAdvance + ladv > range) {
154                 // "i" can't fit in this region see if "i-1" can.
155
i--;
156                 ladv = 0;
157                 if (i < 0) break;
158                 ggi = ggis[i];
159                 if (r != ggi.getRange()) // Not from this range nothing fits.
160
break;
161
162                 rangeAdvance -= ggi.getAdvance();
163                 ladv = ggi.getLastAdvance();
164             }
165
166             i++;
167             rangeAdv[r] = rangeAdvance + ladv;
168             r++;
169             if (i == ggSz) return true;
170         }
171         return false;
172     }
173
174     /**
175      * This method updates the line height and recalculates
176      * the available flow ranges for the line.
177      */

178     public boolean setLineHeight(double lh) {
179         fr.newLineHeight(lh);
180
181         if (updateRangeInfo()) {
182             lineHeight = lh;
183             return true;
184         }
185
186         // restore line height.
187
if (lineHeight > 0)
188             fr.newLineHeight(lineHeight);
189         return false;
190     }
191
192     public double getCurrentY() {
193         return fr.getCurrentY();
194     }
195
196     public boolean gotoY(double y) {
197         if (fr.gotoY(y))
198             return true;
199         if (lineHeight > 0)
200             updateRangeInfo();
201         this.baseline = (float)(fr.getCurrentY()+hLeading+ascent);
202         return false;
203     }
204
205     protected boolean updateRangeInfo() {
206         fr.resetRange();
207         int nr = fr.getNumRangeOnLine();
208         if (nr == 0)
209             return false;
210
211         numRanges = nr;
212
213         if (ranges == null) {
214             rangeAdv = new double[numRanges];
215             ranges = new double[2*numRanges];
216         } else if (numRanges > rangeAdv.length) {
217             int sz = 2*rangeAdv.length;
218             if (sz < numRanges) sz = numRanges;
219             rangeAdv = new double[sz];
220             ranges = new double[2*sz];
221         }
222
223         for (int r=0; r<numRanges; r++) {
224             double [] rangeBounds = fr.nextRange();
225             // System.err.println("RG["+r+"]: [" +
226
// rangeBounds[0] + "," + rangeBounds[1] +"]");
227
double r0 = rangeBounds[0];
228             if (r == 0) {
229                 double delta = bi.getLeftMargin();
230                 if (paraStart) {
231                     double indent = bi.getIndent();
232                     // Limit indent to the amount of margin we have.
233
if (delta < -indent) delta = 0;
234                     else delta += indent;
235                 }
236                 r0 += delta;
237             }
238
239             double r1 = rangeBounds[1];
240             if (r == numRanges-1)
241                 r1 -= bi.getRightMargin();
242             ranges[2*r] = r0;
243             ranges[2*r+1] = r1;
244         }
245
246         return true;
247     }
248
249     protected void swapGlyphGroupInfo() {
250         GlyphGroupInfo [] tmp = ggis;
251         ggis = newGGIS;
252         newGGIS = tmp;
253
254         size = newSize;
255         newSize = 0;
256     }
257
258     /**
259      * This function merges the glyph groups from <tt>wi<tt/>
260      * into the glyph groups that are already on this line.
261      * It does no fit checking, just adds them in the
262      * proper place in the <tt>newGGIS</tt> data member.
263      */

264     protected void mergeGlyphGroups(WordInfo wi) {
265         int numGG = wi.getNumGlyphGroups();
266         newSize = 0;
267         if (ggis == null) {
268             // first glyph group on line just add them.
269
newSize = numGG;
270             newGGIS = new GlyphGroupInfo[numGG];
271             for (int i=0; i< numGG; i++)
272                 newGGIS[i] = wi.getGlyphGroup(i);
273         } else {
274             // We need to merge the new glyph groups with the
275
// existing glyph Groups.
276
int s = 0;
277             int i = 0;
278             GlyphGroupInfo nggi = wi.getGlyphGroup(i);
279             int nStart = nggi.getStart();
280
281             GlyphGroupInfo oggi = ggis[size-1];
282             int oStart = oggi.getStart();
283
284             newGGIS = assureSize(newGGIS, size+numGG);
285             if (nStart < oStart) {
286                 oggi = ggis[s];
287                 oStart = oggi.getStart();
288                 while((s<size)&&(i<numGG)) {
289                     if (nStart < oStart) {
290                         newGGIS[newSize++] = nggi;
291                         i++;
292                         if (i<numGG) {
293                             nggi = wi.getGlyphGroup(i);
294                             nStart = nggi.getStart();
295                         }
296                     } else {
297                         newGGIS[newSize++] = oggi;
298                         s++;
299                         if (s<size) {
300                             oggi = ggis[s];
301                             oStart = oggi.getStart();
302                         }
303                     }
304                 }
305             }
306             while(s<size) {
307                 newGGIS[newSize++] = ggis[s++];
308             }
309             while(i<numGG) {
310                 newGGIS[newSize++] = wi.getGlyphGroup(i++);
311             }
312         }
313         // for (int i=0; i<newSize; i++) {
314
// System.err.println("GGIS["+i+"]: " + newGGIS[i].start + " -> " +
315
// newGGIS[i].end);
316
// }
317
}
318
319
320     public void layout() {
321         if (size == 0) return;
322
323         // This is needed because we know that in most cases
324
// the addition of the last word failed. In the case of
325
// BIDI this will mess up region assignments.
326
// If one wanted to you could check on BIDI, and/or
327
// lastPara.
328
assignGlyphGroupRanges(size, ggis);
329
330         GVTGlyphVector gv = ggis[0].getGlyphVector();
331         int justType = FULL_WORD;
332         double ggAdv = 0;
333         double gAdv = 0;
334
335         // Calculate the number of Glyph Groups and the number
336
// of glpyhs in each range for use with full justification.
337
int []rangeGG = new int[numRanges];
338         int []rangeG = new int[numRanges];
339         GlyphGroupInfo []rangeLastGGI = new GlyphGroupInfo[numRanges];
340         GlyphGroupInfo ggi = ggis[0];
341         int r = ggi.getRange();
342         rangeGG[r]++;
343         rangeG [r] += ggi.getGlyphCount();
344         for (int i=1; i<size; i++) {
345             ggi = ggis[i];
346             r = ggi.getRange();
347             if ((rangeLastGGI[r]==null) || !rangeLastGGI[r].getHideLast())
348                 rangeGG[r]++;
349             rangeLastGGI[r] = ggi;
350
351             rangeG [r] += ggi.getGlyphCount();
352
353             GlyphGroupInfo pggi = ggis[i-1];
354             int pr = pggi.getRange();
355             if (r != pr)
356                 rangeG[pr]+= pggi.getLastGlyphCount()-pggi.getGlyphCount();
357         }
358         rangeG[r]+= ggi.getLastGlyphCount()-ggi.getGlyphCount();
359
360         int currRange = -1;
361         double locX=0, range=0, rAdv=0;
362         r=-1;
363         ggi = null;
364         for (int i=0; i<size; i++) {
365             GlyphGroupInfo pggi = ggi;
366             int prevRange = currRange;
367
368             ggi = ggis[i];
369             currRange = ggi.getRange();
370             
371             if (currRange != prevRange) {
372                 locX = ranges[2*currRange];
373                 range = ranges[2*currRange+1]-locX;
374                 rAdv = rangeAdv[currRange];
375                 int textAlign = bi.getTextAlignment();
376                 if ((paraEnd) && (textAlign == BlockInfo.ALIGN_FULL))
377                     textAlign = BlockInfo.ALIGN_START;
378
379                 switch (textAlign) {
380                 default:
381                 case BlockInfo.ALIGN_FULL: {
382                     double delta = range-rAdv;
383                     if (justType == FULL_WORD) {
384                         int numSp = rangeGG[currRange]-1;
385                         if (numSp >= 1)
386                             ggAdv = delta/numSp;
387                     } else {
388                         int numSp = rangeG[currRange]-1;
389                         if (numSp >= 1) gAdv = delta/numSp;
390                     }
391                 } break;
392                 case BlockInfo.ALIGN_START: break;
393                 case BlockInfo.ALIGN_MIDDLE: locX += (range-rAdv)/2; break;
394                 case BlockInfo.ALIGN_END: locX += (range-rAdv); break;
395                 }
396             } else if ((pggi!= null) && pggi.getHideLast()) {
397                 // Hide last glyph from prev glyph group (soft hyphen etc).
398
gv.setGlyphVisible(pggi.getEnd(), false);
399            }
400
401             int start = ggi.getStart();
402             int end = ggi.getEnd();
403             boolean [] hide = ggi.getHide();
404             Point2D JavaDoc p2d = gv.getGlyphPosition(start);
405             double deltaX = p2d.getX();
406             double advAdj = 0;
407             for (int g=start; g<=end; g++) {
408                 Point2D JavaDoc np2d = gv.getGlyphPosition(g+1);
409                 if (hide[g-start]) {
410                     gv.setGlyphVisible(g, false);
411                     advAdj += np2d.getX()-p2d.getX();
412                 } else {
413                     gv.setGlyphVisible(g, true);
414                 }
415                 p2d.setLocation(p2d.getX()-deltaX-advAdj+locX,
416                                 p2d.getY()+baseline);
417                 gv.setGlyphPosition(g, p2d);
418                 p2d = np2d;
419                 advAdj -= gAdv;
420             }
421             if (ggi.getHideLast())
422                 locX += ggi.getAdvance()-advAdj;
423             else
424                 locX += ggi.getAdvance()-advAdj+ggAdv;
425         }
426     }
427
428     public static GlyphGroupInfo [] assureSize
429         (GlyphGroupInfo [] ggis, int sz) {
430         if (ggis == null) {
431             if (sz < 10) sz = 10;
432             return new GlyphGroupInfo[sz];
433         }
434         if (sz <= ggis.length)
435             return ggis;
436
437         int nsz = ggis.length*2;
438         if (nsz < sz) nsz = sz;
439         return new GlyphGroupInfo[nsz];
440     }
441 };
442
Popular Tags