TextLayout
is an immutable graphical representation of styled
character data.
It provides the following capabilities:
A TextLayout
object can be rendered using
its draw
method.
TextLayout
can be constructed either directly or through
the use of a LineBreakMeasurer
. When constructed directly, the
source text represents a single paragraph. LineBreakMeasurer
allows styled text to be broken into lines that fit within a particular
width. See the LineBreakMeasurer
documentation for more
information.
TextLayout
construction logically proceeds as follows:
All graphical information returned from a TextLayout
object's methods is relative to the origin of the
TextLayout
, which is the intersection of the
TextLayout
object's baseline with its left edge. Also,
coordinates passed into a TextLayout
object's methods
are assumed to be relative to the TextLayout
object's
origin. Clients usually need to translate between a
TextLayout
object's coordinate system and the coordinate
system in another object (such as a
Graphics
object).
TextLayout
objects are constructed from styled text,
but they do not retain a reference to their source text. Thus,
changes in the text previously used to generate a TextLayout
do not affect the TextLayout
.
Three methods on a TextLayout
object
(getNextRightHit
, getNextLeftHit
, and
hitTestChar
) return instances of TextHitInfo
.
The offsets contained in these TextHitInfo
objects
are relative to the start of the TextLayout
, not
to the text used to create the TextLayout
. Similarly,
TextLayout
methods that accept TextHitInfo
instances as parameters expect the TextHitInfo
object's
offsets to be relative to the TextLayout
, not to any
underlying text storage model.
Examples:
Constructing and drawing a TextLayout
and its bounding
rectangle:
Graphics2D g = ...; Point2D loc = ...; Font font = Font.getFont("Helvetica-bold-italic"); FontRenderContext frc = g.getFontRenderContext(); TextLayout layout = new TextLayout("This is a string", font, frc); layout.draw(g, (float)loc.getX(), (float)loc.getY()); Rectangle2D bounds = layout.getBounds(); bounds.setRect(bounds.getX()+loc.getX(), bounds.getY()+loc.getY(), bounds.getWidth(), bounds.getHeight()); g.draw(bounds);
Hit-testing a TextLayout
(determining which character is at
a particular graphical location):
Point2D click = ...; TextHitInfo hit = layout.hitTestChar( (float) (click.getX() - loc.getX()), (float) (click.getY() - loc.getY()));
Responding to a right-arrow key press:
int insertionIndex = ...; TextHitInfo next = layout.getNextRightHit(insertionIndex); if (next != null) { // translate graphics to origin of layout on screen g.translate(loc.getX(), loc.getY()); Shape[] carets = layout.getCaretShapes(next.getInsertionIndex()); g.draw(carets[0]); if (carets[1] != null) { g.draw(carets[1]); } }
Drawing a selection range corresponding to a substring in the source text. The selected area may not be visually contiguous:
// selStart, selLimit should be relative to the layout, // not to the source text int selStart = ..., selLimit = ...; Color selectionColor = ...; Shape selection = layout.getLogicalHighlightShape(selStart, selLimit); // selection may consist of disjoint areas // graphics is assumed to be tranlated to origin of layout g.setColor(selectionColor); g.fill(selection);
Drawing a visually contiguous selection range. The selection range may
correspond to more than one substring in the source text. The ranges of
the corresponding source text substrings can be obtained with
getLogicalRangesForVisualSelection()
:
TextHitInfo selStart = ..., selLimit = ...; Shape selection = layout.getVisualHighlightShape(selStart, selLimit); g.setColor(selectionColor); g.fill(selection); int[] ranges = getLogicalRangesForVisualSelection(selStart, selLimit); // ranges[0], ranges[1] is the first selection range, // ranges[2], ranges[3] is the second selection range, etc.
TextLayout
from a String
and a Font
. All the text is styled using the specified
Font
.
The String
must specify a single paragraph of text,
because an entire paragraph is required for the bidirectional
algorithm.
TextLayout
from a String
and an attribute set.
All the text is styled using the provided attributes.
string
must specify a single paragraph of text because an
entire paragraph is required for the bidirectional algorithm.
TextLayout
from an iterator over styled text.
The iterator must specify a single paragraph of text because an entire paragraph is required for the bidirectional algorithm.
CaretPolicy
is used when a policy is not specified
by the client. With this policy, a hit on a character whose direction
is the same as the line direction is stronger than a hit on a
counterdirectional character. If the characters' directions are
the same, a hit on the leading edge of a character is stronger
than a hit on the trailing edge of a character.TextLayout
at the specified location in
the specified Graphics2D
context.
The origin of the layout is placed at x, y. Rendering may touch
any point within getBounds()
of this position. This
leaves the g2
unchanged.true
if the specified Object
is a
TextLayout
object and if the specified Object
equals this TextLayout
.true
if the two layouts are equal.
Two layouts are equal if they contain equal glyphvectors in the same order.TextLayout
.
The advance is the distance from the origin to the advance of the
rightmost (bottommost) character measuring in the line direction.TextLayout
.
The ascent is the distance from the top (right) of the
TextLayout
to the baseline. It is always either
positive or zero. The ascent is sufficient to
accomodate superscripted text and is the maximum of the sum of the
ascent, offset, and baseline of each glyph.TextLayout
.
The baseline is one of the values defined in Font
,
which are roman, centered and hanging. Ascent and descent are
relative to this baseline. The baselineOffsets
are also relative to this baseline.TextLayout
.
The array is indexed by one of the values defined in
Font
, which are roman, centered and hanging. The
values are relative to this TextLayout
object's
baseline, so that getBaselineOffsets[getBaseline()] == 0
.
Offsets are added to the position of the TextLayout
object's baseline to get the position for the new baseline.
TextLayout
.
The bounds contains all of the pixels the TextLayout
can draw. It might not coincide exactly with the ascent, descent,
origin or advance of the TextLayout
.hit
.
This method is a convenience overload of getCaretInfo
and
uses the natural bounds of this TextLayout
.hit
.
The first element of the array is the intersection of the caret with
the baseline. The second element of the array is the inverse slope
(run/rise) of the caret.
This method is meant for informational use. To display carets, it
is better to use getCaretShapes
.
Shape
representing the caret at the specified
hit inside the natural bounds of this TextLayout
.getCaretShapes
that uses the default caret policy and this TextLayout
object's natural bounds.getCaretShapes
that uses the default caret policy.TextLayout
.index
.
Indices -1 and characterCount
are assigned the base
level of this TextLayout
.TextLayout
.
The descent is the distance from the baseline to the bottom (left) of
the TextLayout
. It is always either positive or zero.
The descent is sufficient to accomodate subscripted text and is the
maximum of the sum of the descent, offset, and baseline of each glyph.TextLayout
justified to the
specified width.
If this TextLayout
has already been justified, an
exception is thrown. If this TextLayout
object's
justification ratio is zero, a TextLayout
identical
to this TextLayout
is returned.
TextLayout
.
The leading is the suggested interline spacing for this
TextLayout
.
The leading is computed from the leading, descent, and baseline
of all glyphvectors in the TextLayout
. The algorithm
is roughly as follows:
maxD = 0; maxDL = 0; for (GlyphVector g in all glyphvectors) { maxD = max(maxD, g.getDescent() + offsets[g.getBaseline()]); maxDL = max(maxDL, g.getDescent() + g.getLeading() + offsets[g.getBaseline()]); } return maxDL - maxD;
Shape
enclosing the logical selection in the
specified range, extended to the natural bounds of this
TextLayout
. This method is a convenience overload of
getLogicalHighlightShape
that uses the natural bounds of
this TextLayout
.Shape
enclosing the logical selection in the
specified range, extended to the specified bounds
.
If the selection range includes the first logical character, the
selection is extended to the portion of bounds
before
the start of this TextLayout
. If the range includes
the last logical character, the selection is extended to the portion
of bounds
after the end of this TextLayout
.
The height (width on vertical lines) of the selection is always
extended to bounds
.
The selection can be discontiguous on lines with mixed-direction text.
Only those characters in the logical range between start and limit
appear selected. For example, consider the text 'ABCdef' where capital
letters indicate right-to-left text, rendered on a right-to-left line,
with a logical selection from 0 to 4 ('ABCd'). The text appears as
follows, with bold standing in for the selection, and underlining for
the extension:
defCBAThe selection is discontiguous because the selected characters are visually discontiguous. Also note that since the range includes the first logical character (A), the selection is extended to the portion of the
bounds
before the start of the layout, which in
this case (a right-to-left line) is the right portion of the
bounds
.null
. The hit is to the left of
the strong caret at the specified offset, as determined by the
default policy.
The returned hit is the stronger of the two possible
hits, as determined by the default policy.null
. The hit is to the left of
the strong caret at the specified offset, as determined by the
specified policy.
The returned hit is the stronger of the two possible
hits, as determined by the specified policy.null
.
If the hit character index is out of bounds, an
IllegalArgumentException
is thrown.null
. The hit is to the right of
the strong caret at the specified offset, as determined by the
default policy.
The returned hit is the stronger of the two possible
hits, as determined by the default policy.null
. The hit is to the right of
the strong caret at the specified offset, as determined by the
specified policy.
The returned hit is the stronger of the two possible
hits, as determined by the specified policy.null
.
If the hit character index is out of bounds, an
IllegalArgumentException
is thrown.Shape
representing the outline of this
TextLayout
.TextLayout
, minus trailing
whitespace.Shape
enclosing the visual selection in the
specified range, extended to the bounds. This method is a
convenience overload of getVisualHighlightShape
that
uses the natural bounds of this TextLayout
.bounds
.
If the selection includes the leftmost (topmost) position, the selection
is extended to the left (top) of bounds
. If the
selection includes the rightmost (bottommost) position, the selection
is extended to the right (bottom) of the bounds. The height
(width on vertical lines) of the selection is always extended to
bounds
.
Although the selection is always contiguous, the logically selected
text can be discontiguous on lines with mixed-direction text. The
logical ranges of text selected can be retrieved using
getLogicalRangesForVisualSelection
. For example,
consider the text 'ABCdef' where capital letters indicate
right-to-left text, rendered on a right-to-left line, with a visual
selection from 0L (the leading edge of 'A') to 3T (the trailing edge
of 'd'). The text appears as follows, with bold underlined areas
representing the selection:
defCBAThe logical selection ranges are 0-3, 4-6 (ABC, ef) because the visually contiguous text is logically discontiguous. Also note that since the rightmost position on the layout (to the right of 'A') is selected, the selection is extended to the right of the bounds.
TextLayout
.TextHitInfo
corresponding to the
specified point. This method is a convenience overload of
hitTestChar
that uses the natural bounds of this
TextLayout
.TextHitInfo
corresponding to the
specified point.
Coordinates outside the bounds of the TextLayout
map to hits on the leading edge of the first logical character,
or the trailing edge of the last logical character, as appropriate,
regardless of the position of that character in the line. Only the
direction along the baseline is used to make this evaluation.true
if this TextLayout
has
a left-to-right base direction or false
if it has
a right-to-left base direction. The TextLayout
has a base direction of either left-to-right (LTR) or
right-to-left (RTL). The base direction is independent of the
actual direction of text on the line, which may be either LTR,
RTL, or mixed. Left-to-right layouts by default should position
flush left. If the layout is on a tabbed line, the
tabs run left to right, so that logically successive layouts position
left to right. The opposite is true for RTL layouts. By default they
should position flush left, and tabs run right-to-left.true
if this TextLayout
is vertical.wait
methods.
The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object.
This method should only be called by a thread that is the owner of this object's monitor. A thread becomes the owner of the object's monitor in one of three ways:
synchronized
statement
that synchronizes on the object.
Class,
by executing a
synchronized static method of that class.
Only one thread at a time can own an object's monitor.
wait
methods.
The awakened threads will not be able to proceed until the current thread relinquishes the lock on this object. The awakened threads will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened threads enjoy no reliable privilege or disadvantage in being the next thread to lock this object.
This method should only be called by a thread that is the owner
of this object's monitor. See the notify
method for a
description of the ways in which a thread can become the owner of
a monitor.
TextLayout
.
The current thread must own this object's monitor. The thread
releases ownership of this monitor and waits until another thread
notifies threads waiting on this object's monitor to wake up
either through a call to the notify
method or the
notifyAll
method. The thread then waits until it can
re-obtain ownership of the monitor and resumes execution.
As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:
synchronized (obj) { while (<condition does not hold>) obj.wait(); ... // Perform action appropriate to condition }This method should only be called by a thread that is the owner of this object's monitor. See the
notify
method for a
description of the ways in which a thread can become the owner of
a monitor.The current thread must own this object's monitor.
This method causes the current thread (call it T) to place itself in the wait set for this object and then to relinquish any and all synchronization claims on this object. Thread T becomes disabled for thread scheduling purposes and lies dormant until one of four things happens:
A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one:
synchronized (obj) { while (<condition does not hold>) obj.wait(timeout); ... // Perform action appropriate to condition }(For more information on this topic, see Section 3.2.3 in Doug Lea's "Concurrent Programming in Java (Second Edition)" (Addison-Wesley, 2000), or Item 50 in Joshua Bloch's "Effective Java Programming Language Guide" (Addison-Wesley, 2001).
If the current thread is interrupted by another thread while it is waiting, then an InterruptedException is thrown. This exception is not thrown until the lock status of this object has been restored as described above.
Note that the wait method, as it places the current thread into the wait set for this object, unlocks only this object; any other objects on which the current thread may be synchronized remain locked while the thread waits.
This method should only be called by a thread that is the owner
of this object's monitor. See the notify
method for a
description of the ways in which a thread can become the owner of
a monitor.
This method is similar to the wait
method of one
argument, but it allows finer control over the amount of time to
wait for a notification before giving up. The amount of real time,
measured in nanoseconds, is given by:
1000000*timeout+nanos
In all other respects, this method does the same thing as the method of one argument. In particular, wait(0, 0) means the same thing as wait(0).
The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until either of the following two conditions has occurred:
notify
method
or the notifyAll
method.
timeout
milliseconds plus nanos
nanoseconds arguments, has
elapsed.
The thread then waits until it can re-obtain ownership of the monitor and resumes execution.
As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:
synchronized (obj) { while (<condition does not hold>) obj.wait(timeout, nanos); ... // Perform action appropriate to condition }This method should only be called by a thread that is the owner of this object's monitor. See the
notify
method for a
description of the ways in which a thread can become the owner of
a monitor.