Source Code Cross Referenced for LineBreakMeasurer.java in  » 6.0-JDK-Core » AWT » java » awt » font » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » AWT » java.awt.font 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001        /*
002         * Copyright 1998-2001 Sun Microsystems, Inc.  All Rights Reserved.
003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004         *
005         * This code is free software; you can redistribute it and/or modify it
006         * under the terms of the GNU General Public License version 2 only, as
007         * published by the Free Software Foundation.  Sun designates this
008         * particular file as subject to the "Classpath" exception as provided
009         * by Sun in the LICENSE file that accompanied this code.
010         *
011         * This code is distributed in the hope that it will be useful, but WITHOUT
012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014         * version 2 for more details (a copy is included in the LICENSE file that
015         * accompanied this code).
016         *
017         * You should have received a copy of the GNU General Public License version
018         * 2 along with this work; if not, write to the Free Software Foundation,
019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020         *
021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022         * CA 95054 USA or visit www.sun.com if you need additional information or
023         * have any questions.
024         */
025
026        /*
027         * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
028         * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
029         *
030         * The original version of this source code and documentation is
031         * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
032         * of IBM. These materials are provided under terms of a License
033         * Agreement between Taligent and Sun. This technology is protected
034         * by multiple US and International patents.
035         *
036         * This notice and attribution to Taligent may not be removed.
037         * Taligent is a registered trademark of Taligent, Inc.
038         *
039         */
040
041        package java.awt.font;
042
043        import java.text.BreakIterator;
044        import java.text.CharacterIterator;
045        import java.text.AttributedCharacterIterator;
046        import java.awt.font.FontRenderContext;
047
048        /**
049         * The <code>LineBreakMeasurer</code> class allows styled text to be
050         * broken into lines (or segments) that fit within a particular visual
051         * advance.  This is useful for clients who wish to display a paragraph of
052         * text that fits within a specific width, called the <b>wrapping
053         * width</b>.
054         * <p>
055         * <code>LineBreakMeasurer</code> is constructed with an iterator over
056         * styled text.  The iterator's range should be a single paragraph in the
057         * text.
058         * <code>LineBreakMeasurer</code> maintains a position in the text for the
059         * start of the next text segment.  Initially, this position is the
060         * start of text.  Paragraphs are assigned an overall direction (either
061         * left-to-right or right-to-left) according to the bidirectional
062         * formatting rules.  All segments obtained from a paragraph have the
063         * same direction as the paragraph.
064         * <p>
065         * Segments of text are obtained by calling the method
066         * <code>nextLayout</code>, which returns a {@link TextLayout}
067         * representing the text that fits within the wrapping width.
068         * The <code>nextLayout</code> method moves the current position
069         * to the end of the layout returned from <code>nextLayout</code>.
070         * <p>
071         * <code>LineBreakMeasurer</code> implements the most commonly used
072         * line-breaking policy: Every word that fits within the wrapping
073         * width is placed on the line. If the first word does not fit, then all
074         * of the characters that fit within the wrapping width are placed on the
075         * line.  At least one character is placed on each line.
076         * <p>
077         * The <code>TextLayout</code> instances returned by 
078         * <code>LineBreakMeasurer</code> treat tabs like 0-width spaces.  Clients
079         * who wish to obtain tab-delimited segments for positioning should use
080         * the overload of <code>nextLayout</code> which takes a limiting offset
081         * in the text.
082         * The limiting offset should be the first character after the tab.
083         * The <code>TextLayout</code> objects returned from this method end
084         * at the limit provided (or before, if the text between the current
085         * position and the limit won't fit entirely within the  wrapping
086         * width).
087         * <p>
088         * Clients who are laying out tab-delimited text need a slightly
089         * different line-breaking policy after the first segment has been
090         * placed on a line.  Instead of fitting partial words in the
091         * remaining space, they should place words which don't fit in the
092         * remaining space entirely on the next line.  This change of policy
093         * can be requested in the overload of <code>nextLayout</code> which
094         * takes a <code>boolean</code> parameter.  If this parameter is
095         * <code>true</code>, <code>nextLayout</code> returns 
096         * <code>null</code> if the first word won't fit in
097         * the given space.  See the tab sample below.
098         * <p>
099         * In general, if the text used to construct the 
100         * <code>LineBreakMeasurer</code> changes, a new 
101         * <code>LineBreakMeasurer</code> must be constructed to reflect
102         * the change.  (The old <code>LineBreakMeasurer</code> continues to
103         * function properly, but it won't be aware of the text change.)
104         * Nevertheless, if the text change is the insertion or deletion of a
105         * single character, an existing <code>LineBreakMeasurer</code> can be
106         * 'updated' by calling <code>insertChar</code> or
107         * <code>deleteChar</code>. Updating an existing
108         * <code>LineBreakMeasurer</code> is much faster than creating a new one.
109         * Clients who modify text based on user typing should take advantage
110         * of these methods.
111         * <p>
112         * <strong>Examples</strong>:<p>
113         * Rendering a paragraph in a component
114         * <blockquote>
115         * <pre>
116         * public void paint(Graphics graphics) {
117         *
118         *     Point2D pen = new Point2D(10, 20);
119         *     Graphics2D g2d = (Graphics2D)graphics;
120         *     FontRenderContext frc = g2d.getFontRenderContext();
121         *
122         *     // let styledText be an AttributedCharacterIterator containing at least
123         *     // one character
124         *
125         *     LineBreakMeasurer measurer = new LineBreakMeasurer(styledText, frc);
126         *     float wrappingWidth = getSize().width - 15;
127         *
128         *     while (measurer.getPosition() < fStyledText.length()) {
129         *
130         *         TextLayout layout = measurer.nextLayout(wrappingWidth);
131         *
132         *         pen.y += (layout.getAscent());
133         *         float dx = layout.isLeftToRight() ?
134         *             0 : (wrappingWidth - layout.getAdvance());
135         *
136         *         layout.draw(graphics, pen.x + dx, pen.y);
137         *         pen.y += layout.getDescent() + layout.getLeading();
138         *     }
139         * }
140         * </pre>
141         * </blockquote>
142         * <p>
143         * Rendering text with tabs.  For simplicity, the overall text
144         * direction is assumed to be left-to-right
145         * <blockquote>
146         * <pre>
147         * public void paint(Graphics graphics) {
148         *
149         *     float leftMargin = 10, rightMargin = 310;
150         *     float[] tabStops = { 100, 250 };
151         *
152         *     // assume styledText is an AttributedCharacterIterator, and the number
153         *     // of tabs in styledText is tabCount
154         *
155         *     int[] tabLocations = new int[tabCount+1];
156         *
157         *     int i = 0;
158         *     for (char c = styledText.first(); c != styledText.DONE; c = styledText.next()) {
159         *         if (c == '\t') {
160         *             tabLocations[i++] = styledText.getIndex();
161         *         }
162         *     }
163         *     tabLocations[tabCount] = styledText.getEndIndex() - 1;
164         *
165         *     // Now tabLocations has an entry for every tab's offset in
166         *     // the text.  For convenience, the last entry is tabLocations
167         *     // is the offset of the last character in the text.
168         *
169         *     LineBreakMeasurer measurer = new LineBreakMeasurer(styledText);
170         *     int currentTab = 0;
171         *     float verticalPos = 20;
172         *
173         *     while (measurer.getPosition() < styledText.getEndIndex()) {
174         *
175         *         // Lay out and draw each line.  All segments on a line
176         *         // must be computed before any drawing can occur, since
177         *         // we must know the largest ascent on the line.
178         *         // TextLayouts are computed and stored in a Vector;
179         *         // their horizontal positions are stored in a parallel
180         *         // Vector.
181         *
182         *         // lineContainsText is true after first segment is drawn
183         *         boolean lineContainsText = false;
184         *         boolean lineComplete = false;
185         *         float maxAscent = 0, maxDescent = 0;
186         *         float horizontalPos = leftMargin;
187         *         Vector layouts = new Vector(1);
188         *         Vector penPositions = new Vector(1);
189         *
190         *         while (!lineComplete) {
191         *             float wrappingWidth = rightMargin - horizontalPos;
192         *             TextLayout layout =
193         *                     measurer.nextLayout(wrappingWidth,
194         *                                         tabLocations[currentTab]+1,
195         *                                         lineContainsText);
196         *
197         *             // layout can be null if lineContainsText is true
198         *             if (layout != null) {
199         *                 layouts.addElement(layout);
200         *                 penPositions.addElement(new Float(horizontalPos));
201         *                 horizontalPos += layout.getAdvance();
202         *                 maxAscent = Math.max(maxAscent, layout.getAscent());
203         *                 maxDescent = Math.max(maxDescent,
204         *                     layout.getDescent() + layout.getLeading());
205         *             } else {
206         *                 lineComplete = true;
207         *             }
208         *
209         *             lineContainsText = true;
210         *
211         *             if (measurer.getPosition() == tabLocations[currentTab]+1) {
212         *                 currentTab++;
213         *             }
214         *
215         *             if (measurer.getPosition() == styledText.getEndIndex())
216         *                 lineComplete = true;
217         *             else if (horizontalPos >= tabStops[tabStops.length-1])
218         *                 lineComplete = true;
219         *
220         *             if (!lineComplete) {
221         *                 // move to next tab stop
222         *                 int j;
223         *                 for (j=0; horizontalPos >= tabStops[j]; j++) {}
224         *                 horizontalPos = tabStops[j];
225         *             }
226         *         }
227         *
228         *         verticalPos += maxAscent;
229         *
230         *         Enumeration layoutEnum = layouts.elements();
231         *         Enumeration positionEnum = penPositions.elements();
232         *
233         *         // now iterate through layouts and draw them
234         *         while (layoutEnum.hasMoreElements()) {
235         *             TextLayout nextLayout = (TextLayout) layoutEnum.nextElement();
236         *             Float nextPosition = (Float) positionEnum.nextElement();
237         *             nextLayout.draw(graphics, nextPosition.floatValue(), verticalPos);
238         *         }
239         *
240         *         verticalPos += maxDescent;
241         *     }
242         * }
243         * </pre>
244         * </blockquote>
245         * @see TextLayout
246         */
247
248        public final class LineBreakMeasurer {
249
250            private BreakIterator breakIter;
251            private int start;
252            private int pos;
253            private int limit;
254            private TextMeasurer measurer;
255            private CharArrayIterator charIter;
256
257            /**
258             * Constructs a <code>LineBreakMeasurer</code> for the specified text.
259             *
260             * @param text the text for which this <code>LineBreakMeasurer</code>
261             *       produces <code>TextLayout</code> objects; the text must contain 
262             *       at least one character; if the text available through 
263             *       <code>iter</code> changes, further calls to this 
264             *       <code>LineBreakMeasurer</code> instance are undefined (except,
265             *       in some cases, when <code>insertChar</code> or 
266             *       <code>deleteChar</code> are invoked afterward - see below)
267             * @param frc contains information about a graphics device which is 
268             *       needed to measure the text correctly;
269             *       text measurements can vary slightly depending on the
270             *       device resolution, and attributes such as antialiasing; this
271             *       parameter does not specify a translation between the
272             *       <code>LineBreakMeasurer</code> and user space
273             * @see LineBreakMeasurer#insertChar
274             * @see LineBreakMeasurer#deleteChar
275             */
276            public LineBreakMeasurer(AttributedCharacterIterator text,
277                    FontRenderContext frc) {
278                this (text, BreakIterator.getLineInstance(), frc);
279            }
280
281            /**
282             * Constructs a <code>LineBreakMeasurer</code> for the specified text.
283             *
284             * @param text the text for which this <code>LineBreakMeasurer</code>
285             *     produces <code>TextLayout</code> objects; the text must contain 
286             *     at least one character; if the text available through 
287             *     <code>iter</code> changes, further calls to this 
288             *     <code>LineBreakMeasurer</code> instance are undefined (except,
289             *     in some cases, when <code>insertChar</code> or 
290             *     <code>deleteChar</code> are invoked afterward - see below)
291             * @param breakIter the {@link BreakIterator} which defines line
292             *     breaks
293             * @param frc contains information about a graphics device which is
294             *       needed to measure the text correctly;
295             *       text measurements can vary slightly depending on the
296             *       device resolution, and attributes such as antialiasing; this
297             *       parameter does not specify a translation between the
298             *       <code>LineBreakMeasurer</code> and user space
299             * @throws IllegalArgumentException if the text has less than one character
300             * @see LineBreakMeasurer#insertChar
301             * @see LineBreakMeasurer#deleteChar
302             */
303            public LineBreakMeasurer(AttributedCharacterIterator text,
304                    BreakIterator breakIter, FontRenderContext frc) {
305                if (text.getEndIndex() - text.getBeginIndex() < 1) {
306                    throw new IllegalArgumentException(
307                            "Text must contain at least one character.");
308                }
309
310                this .breakIter = breakIter;
311                this .measurer = new TextMeasurer(text, frc);
312                this .limit = text.getEndIndex();
313                this .pos = this .start = text.getBeginIndex();
314
315                charIter = new CharArrayIterator(measurer.getChars(),
316                        this .start);
317                this .breakIter.setText(charIter);
318            }
319
320            /**
321             * Returns the position at the end of the next layout.  Does NOT
322             * update the current position of this <code>LineBreakMeasurer</code>.
323             *
324             * @param wrappingWidth the maximum visible advance permitted for
325             *    the text in the next layout
326             * @return an offset in the text representing the limit of the
327             *    next <code>TextLayout</code>.
328             */
329            public int nextOffset(float wrappingWidth) {
330                return nextOffset(wrappingWidth, limit, false);
331            }
332
333            /**
334             * Returns the position at the end of the next layout.  Does NOT
335             * update the current position of this <code>LineBreakMeasurer</code>.
336             *
337             * @param wrappingWidth the maximum visible advance permitted for
338             *    the text in the next layout
339             * @param offsetLimit the first character that can not be included
340             *    in the next layout, even if the text after the limit would fit
341             *    within the wrapping width; <code>offsetLimit</code> must be
342             *    greater than the current position
343             * @param requireNextWord if <code>true</code>, the current position
344             *    that is returned if the entire next word does not fit within
345             *    <code>wrappingWidth</code>; if <code>false</code>, the offset
346             *    returned is at least one greater than the current position
347             * @return an offset in the text representing the limit of the
348             *    next <code>TextLayout</code>
349             */
350            public int nextOffset(float wrappingWidth, int offsetLimit,
351                    boolean requireNextWord) {
352
353                int nextOffset = pos;
354
355                if (pos < limit) {
356                    if (offsetLimit <= pos) {
357                        throw new IllegalArgumentException(
358                                "offsetLimit must be after current position");
359                    }
360
361                    int charAtMaxAdvance = measurer.getLineBreakIndex(pos,
362                            wrappingWidth);
363
364                    if (charAtMaxAdvance == limit) {
365                        nextOffset = limit;
366                    } else if (Character
367                            .isWhitespace(measurer.getChars()[charAtMaxAdvance
368                                    - start])) {
369                        nextOffset = breakIter.following(charAtMaxAdvance);
370                    } else {
371                        // Break is in a word;  back up to previous break.
372
373                        // NOTE:  I think that breakIter.preceding(limit) should be
374                        // equivalent to breakIter.last(), breakIter.previous() but
375                        // the authors of BreakIterator thought otherwise...
376                        // If they were equivalent then the first branch would be
377                        // unnecessary.
378                        int testPos = charAtMaxAdvance + 1;
379                        if (testPos == limit) {
380                            breakIter.last();
381                            nextOffset = breakIter.previous();
382                        } else {
383                            nextOffset = breakIter.preceding(testPos);
384                        }
385
386                        if (nextOffset <= pos) {
387                            // first word doesn't fit on line
388                            if (requireNextWord) {
389                                nextOffset = pos;
390                            } else {
391                                nextOffset = Math
392                                        .max(pos + 1, charAtMaxAdvance);
393                            }
394                        }
395                    }
396                }
397
398                if (nextOffset > offsetLimit) {
399                    nextOffset = offsetLimit;
400                }
401
402                return nextOffset;
403            }
404
405            /**
406             * Returns the next layout, and updates the current position.
407             *
408             * @param wrappingWidth the maximum visible advance permitted for
409             *     the text in the next layout
410             * @return a <code>TextLayout</code>, beginning at the current
411             *     position, which represents the next line fitting within 
412             *     <code>wrappingWidth</code>
413             */
414            public TextLayout nextLayout(float wrappingWidth) {
415                return nextLayout(wrappingWidth, limit, false);
416            }
417
418            /**
419             * Returns the next layout, and updates the current position.
420             *
421             * @param wrappingWidth the maximum visible advance permitted
422             *    for the text in the next layout
423             * @param offsetLimit the first character that can not be
424             *    included in the next layout, even if the text after the limit
425             *    would fit within the wrapping width; <code>offsetLimit</code> 
426             *    must be greater than the current position
427             * @param requireNextWord if <code>true</code>, and if the entire word
428             *    at the current position does not fit within the wrapping width,
429             *    <code>null</code> is returned. If <code>false</code>, a valid
430             *    layout is returned that includes at least the character at the
431             *    current position
432             * @return a <code>TextLayout</code>, beginning at the current
433             *    position, that represents the next line fitting within 
434             *    <code>wrappingWidth</code>.  If the current position is at the end 
435             *    of the text used by this <code>LineBreakMeasurer</code>,
436             *    <code>null</code> is returned
437             */
438            public TextLayout nextLayout(float wrappingWidth, int offsetLimit,
439                    boolean requireNextWord) {
440
441                if (pos < limit) {
442                    int layoutLimit = nextOffset(wrappingWidth, offsetLimit,
443                            requireNextWord);
444                    if (layoutLimit == pos) {
445                        return null;
446                    }
447
448                    TextLayout result = measurer.getLayout(pos, layoutLimit);
449                    pos = layoutLimit;
450
451                    return result;
452                } else {
453                    return null;
454                }
455            }
456
457            /**
458             * Returns the current position of this <code>LineBreakMeasurer</code>.
459             *
460             * @return the current position of this <code>LineBreakMeasurer</code>
461             * @see #setPosition
462             */
463            public int getPosition() {
464                return pos;
465            }
466
467            /**
468             * Sets the current position of this <code>LineBreakMeasurer</code>.
469             *
470             * @param newPosition the current position of this
471             *    <code>LineBreakMeasurer</code>; the position should be within the
472             *    text used to construct this <code>LineBreakMeasurer</code> (or in
473             *    the text most recently passed to <code>insertChar</code>
474             *    or <code>deleteChar</code>
475             * @see #getPosition
476             */
477            public void setPosition(int newPosition) {
478                if (newPosition < start || newPosition > limit) {
479                    throw new IllegalArgumentException(
480                            "position is out of range");
481                }
482                pos = newPosition;
483            }
484
485            /**
486             * Updates this <code>LineBreakMeasurer</code> after a single
487             * character is inserted into the text, and sets the current
488             * position to the beginning of the paragraph.
489             *
490             * @param newParagraph the text after the insertion
491             * @param insertPos the position in the text at which the character
492             *    is inserted
493             * @throws IndexOutOfBoundsException if <code>insertPos</code> is less
494             *         than the start of <code>newParagraph</code> or greater than
495             *         or equal to the end of <code>newParagraph</code>
496             * @throws NullPointerException if <code>newParagraph</code> is   
497             *         <code>null</code>
498             * @see #deleteChar
499             */
500            public void insertChar(AttributedCharacterIterator newParagraph,
501                    int insertPos) {
502
503                measurer.insertChar(newParagraph, insertPos);
504
505                limit = newParagraph.getEndIndex();
506                pos = start = newParagraph.getBeginIndex();
507
508                charIter.reset(measurer.getChars(), newParagraph
509                        .getBeginIndex());
510                breakIter.setText(charIter);
511            }
512
513            /**
514             * Updates this <code>LineBreakMeasurer</code> after a single
515             * character is deleted from the text, and sets the current
516             * position to the beginning of the paragraph.
517             * @param newParagraph the text after the deletion
518             * @param deletePos the position in the text at which the character
519             *    is deleted
520             * @throws IndexOutOfBoundsException if <code>deletePos</code> is
521             *         less than the start of <code>newParagraph</code> or greater
522             *         than the end of <code>newParagraph</code>
523             * @throws NullPointerException if <code>newParagraph</code> is
524             *         <code>null</code>
525             * @see #insertChar
526             */
527            public void deleteChar(AttributedCharacterIterator newParagraph,
528                    int deletePos) {
529
530                measurer.deleteChar(newParagraph, deletePos);
531
532                limit = newParagraph.getEndIndex();
533                pos = start = newParagraph.getBeginIndex();
534
535                charIter.reset(measurer.getChars(), start);
536                breakIter.setText(charIter);
537            }
538        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.