TextLayout
is an immutable graphical representation of styled
character data.
It provides the following capabilities:
- implicit bidirectional analysis and reordering,
- cursor positioning and movement, including split cursors for
mixed directional text,
- highlighting, including both logical and visual highlighting
for mixed directional text,
- multiple baselines (roman, hanging, and centered),
- hit testing,
- justification,
- default font substitution,
- metric information such as ascent, descent, and advance, and
- rendering
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:
- paragraph attributes are extracted and examined,
- text is analyzed for bidirectional reordering, and reordering
information is computed if needed,
- text is segmented into style runs
- fonts are chosen for style runs, first by using a font if the
attribute
TextAttribute.FONT
is present, otherwise by computing
a default font using the attributes that have been defined
- if text is on multiple baselines, the runs or subruns are further
broken into subruns sharing a common baseline,
- glyphvectors are generated for each run using the chosen font,
- final bidirectional reordering is performed on the glyphvectors
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.
Note: Font rotations can cause text baselines to be rotated, and
multiple runs with different rotations can cause the baseline to
bend or zig-zag. In order to account for this (rare) possibility,
some APIs are specified to return metrics and take parameters 'in
baseline-relative coordinates' (e.g. ascent, advance), and others
are in 'in standard coordinates' (e.g. getBounds). Values in
baseline-relative coordinates map the 'x' coordinate to the
distance along the baseline, (positive x is forward along the
baseline), and the 'y' coordinate to a distance along the
perpendicular to the baseline at 'x' (postitive y is 90 degrees
clockwise from the baseline vector). Values in standard
coordinates are measured along the x and y axes, with 0,0 at the
origin of the TextLayout. Documentation for each relevant API
indicates what values are in what coordinate system. In general,
measurement-related APIs are in baseline-relative coordinates,
while display-related APIs are in standard coordinates.