KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > extension > svg > GlyphIterator


1 /*
2
3    Copyright 2002-2003 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 package org.apache.batik.extension.svg;
19
20 import java.awt.font.FontRenderContext JavaDoc;
21 import java.awt.geom.Point2D JavaDoc;
22 import java.awt.geom.Rectangle2D JavaDoc;
23 import java.text.AttributedCharacterIterator JavaDoc;
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 JavaDoc PREFORMATTED
33         = GVTAttributedCharacterIterator.TextAttribute.PREFORMATTED;
34
35     public static final AttributedCharacterIterator.Attribute JavaDoc FLOW_LINE_BREAK
36         = GVTAttributedCharacterIterator.TextAttribute.FLOW_LINE_BREAK;
37
38     public static final AttributedCharacterIterator.Attribute JavaDoc
39         TEXT_COMPOUND_DELIMITER
40         = GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER;
41     public static final
42         AttributedCharacterIterator.Attribute JavaDoc 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     // Glyph index of current glyph
50
int idx = -1;
51     // Glyph index of last 'printing' glyph.
52
int chIdx = -1;
53     int lineIdx = -1;
54
55     // The ACI index of current glyph.
56
int aciIdx = -1;
57     // The number of characters in ACI for current Glyph.
58
int charCount = -1;
59
60     // The total advance for line including last non-space glyph
61
float adv = 0;
62     // The total advance for line including spaces at end of line.
63
float adj = 0;
64     // The runLimit for current font
65
int runLimit = 0;
66
67     // The runLimit for current line element (need a line break at end).
68
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     // The current char (from ACI)
80
char ch = 0;
81     // The number of glyphs in gv.
82
int numGlyphs = 0;
83     // The AttributedCharacterIterator.
84
AttributedCharacterIterator JavaDoc aci;
85     // The GVTGlyphVector for this Text chunk.
86
GVTGlyphVector gv;
87     // The GlyphPositions for this GlyphVector (Shared)
88
float [] gp;
89     // The font render context for this GylphVector.
90
FontRenderContext JavaDoc frc;
91
92     // The Indexes of new leftShift amounts (soft-hyphens)
93
int [] leftShiftIdx = null;
94     // The amount of new leftShifts (soft-hyphen adv)
95
float [] leftShiftAmt = null;
96     // The current left shift (inherited from previous line).
97
int leftShift = 0;
98
99     Point2D JavaDoc gvBase = null;
100
101     
102     public GlyphIterator(AttributedCharacterIterator JavaDoc 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         // Figure out where the font size might change again...
125
this.runLimit = aci.getRunLimit(TEXT_COMPOUND_DELIMITER);
126         
127         this.lineBreakRunLimit = aci.getRunLimit(FLOW_LINE_BREAK);
128         Object JavaDoc 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 JavaDoc(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     /**
180      * @return The index into glyph vector for current character.
181      */

182     public int getGlyphIndex() { return idx; }
183
184     /**
185      * @return the current character.
186      */

187     public char getChar() { return ch; }
188
189     /**
190      * @return The index into Attributed Character iterator for
191      * current character.
192      */

193     public int getACIIndex() { return aciIdx; }
194
195     /**
196      * @return The current advance for the line, this is the 'visual width'
197      * of the current line.
198      */

199     public float getAdv() { return adv; }
200
201     /**
202      * @return The origin of the glyph vector (the point all glyphs are
203      * layed out with respect to).
204      */

205     public Point2D JavaDoc getOrigin() { return gvBase; }
206
207     /**
208      * @return The current adjustment for the line. This is the ammount
209      * that needs to be subracted from the following line to get it back
210      * to the start of the next line.
211      */

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             // Next char is outside this line element so break after
273
// the current char.
274
ret = lineBreakCount;
275
276             // Update the lineBreakRunLimit, this is a bit tricky since
277
// The attribute doesn't change until the next glyph...
278
aci.setIndex(aciIdx+charCount);
279             lineBreakRunLimit = aci.getRunLimit(FLOW_LINE_BREAK);
280             aci.setIndex(aciIdx); // Restore location...
281

282             Object JavaDoc o = aci.getAttribute(FLOW_LINE_BREAK);
283             lineBreakCount = (o == null)?0:1;
284         }
285         return ret;
286     }
287
288     /**
289      * Move iterator to the next char.
290      */

291     public void nextChar() {
292         if ((ch == SOFT_HYPHEN) ||
293             (ch == ZERO_WIDTH_SPACE) ||
294             (ch == ZERO_WIDTH_JOINER)) {
295             // Special handling for soft hyphens and zero-width spaces
296
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 JavaDoc loc,
360                             float lineWidth,
361                             boolean partial,
362                             Point2D.Float JavaDoc 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 JavaDoc(gp[2*ci]-leftShift,
378                                                       gp[2*ci+1]));
379         }
380         leftShiftIdx = null;
381         leftShiftAmt = null;
382
383         float lineInfoChW;
384         int hideIdx;
385         // System.out.println("ChIdx: " + chIdx);
386
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; // Disable soft-hyphen/ZWS advance adjustment.
406
nextChar();
407
408           if (isPrinting()) break;
409
410           lineInfoIdx = idx+1;
411           lineInfoAdj += adj;
412         }
413
414         // hide trailing spaces if any
415
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     /**
437      * Get the advance associated with the current glyph
438      */

439     public float getCharAdvance() {
440         return getCharAdvance(idx);
441     }
442
443     /**
444      * Get the visual advance associated with the current glyph.
445      * This is the distance from the location of the glyph to
446      * the rightmost part of the glyph.
447      */

448     public float getCharWidth() {
449         return getCharWidth(idx);
450     }
451
452     /**
453      * Get the advance associated with any glyph
454      */

455     protected float getCharAdvance(int gvIdx) {
456         return gp[2*gvIdx+2] - gp[2*gvIdx];
457     }
458
459     /**
460      * Get the visual advance associated with the current glyph.
461      * This is the distance from the location of the glyph to
462      * the rightmost part of the glyph.
463      */

464     protected float getCharWidth(int gvIdx) {
465         Rectangle2D JavaDoc lcBound = gv.getGlyphVisualBounds(gvIdx).getBounds2D();
466         Point2D JavaDoc lcLoc = gv.getGlyphPosition(gvIdx);
467         return (float)(lcBound.getX()+lcBound.getWidth()- lcLoc.getX());
468     }
469 }
470
Popular Tags