Class ShapedText.Builder
- Enclosing class:
ShapedText
ShapedText run by run. Backend-facing: a widget never touches one. Not
thread safe, single use, and meant to be filled and dropped inside one
TextRuler.shape(String, Font, Direction) call.
Runs go in logically and come out visually. A caller supplies the runs in the order the characters appear in the string, each with its bidi embedding level, and the builder does the reordering (Unicode Bidirectional Algorithm rule L2), lays out each run's glyphs from the advances it was given, and derives the caret stops, the split positions and the cumulative advances. That places the highest-risk arithmetic in a module with no natives in it, where it can be unit-tested with no window, no GPU and no font file — and it means the shaping path and the degraded path come through one door, so the two cannot disagree about where a caret goes.
Cluster offsets are absolute. Every index this builder is given is an offset into the whole string, never into the buffer a shaper was handed for one run. Shapers report clusters relative to what they were given and every run boundary shifts that origin; making the seam demand absolute offsets and check them turns that entire class of mistake from a caret that lands one character from the click into an exception at the call that made it.
-
Method Summary
Modifier and TypeMethodDescriptionbuild()Freezes the value: reorders the runs by level, resolves the caret stops and their strong and weak positions, sums the advances into the width and the cumulative table, and derivesShapedText.isSimple().epoch(long epoch) Stamps the epoch the shaping was done under.glyph(int glyphId, int cluster, float advance, float xOffset, float yOffset) Adds one glyph to the open run, in the order the shaper emitted it — which for a right-to-left run is already that run's visual order.lineMetrics(float ascent, float descent, float lineHeight) The line's vertical extents, in logical points, asTextMetricsdefines them.run(int faceId, int charStart, int charEnd, int level) Opens a run and implicitly closes the previous one: one face, one direction, one shaping call, covering the characters[charStart, charEnd).
-
Method Details
-
lineMetrics
The line's vertical extents, in logical points, asTextMetricsdefines them. Required beforebuild(). Width is not among them: it is the extent of the glyphs, so it is derived rather than stated.- Parameters:
ascent- baseline to ascender, positive updescent- baseline to descender, positive downlineHeight- recommended baseline-to-baseline distance
-
epoch
Stamps the epoch the shaping was done under. A ruler that does not set one produces a valueShapedText.matches(java.lang.String, limn.graphics.Font, limn.graphics.ShapedText.Direction, limn.graphics.TextRuler)treats as always current, which is right for a fake and wrong for anything that resolves a face.- Parameters:
epoch- the producing ruler's epoch at the moment of shaping
-
run
Opens a run and implicitly closes the previous one: one face, one direction, one shaping call, covering the characters[charStart, charEnd).Runs are supplied in logical order and must tile the string exactly — no gap, no overlap, the first starting at zero and the last ending at the string's length. A run with no glyphs at all is legal and is what a run of control characters is.
levelis the bidi embedding level, with even levels reading left to right and odd levels right to left, as the Unicode Bidirectional Algorithm numbers them and asjava.text.Bidireports them. It is a level and not a boolean because rule L2 reorders by level and cannot be driven by parity alone once anything nests.- Parameters:
faceId- the producing ruler's own identifier for the face this run's glyph ids belong to; opaque here, and only ever compared for equalitycharStart- first char offset this run coverscharEnd- one past the last, greater thancharStartlevel- the bidi embedding level,0to125- Throws:
IllegalArgumentException- if the range is empty, reversed, out of bounds, does not begin where the previous run ended, or the level is out of range
-
glyph
public ShapedText.Builder glyph(int glyphId, int cluster, float advance, float xOffset, float yOffset) Adds one glyph to the open run, in the order the shaper emitted it — which for a right-to-left run is already that run's visual order.Position is not an argument. The builder places the run and runs the pen, so a caller cannot put a run at the wrong x, and
metrics().width()cannot disagree with where the last glyph was drawn.- Parameters:
glyphId- the glyph index within the run's face, orShapedText.NO_GLYPH; not a code point, and for a ligature not derivable from onecluster- the char offset into the whole string this glyph belongs to; a shaper reports these relative to the buffer it was handed, so the run's start has to be added back exactly onceadvance- how far the pen moves after this glyph, in logical points; zero for an attached markxOffset- horizontal placement relative to the pen, in logical pointsyOffset- vertical placement relative to the baseline, in logical points, positive down as everywhere else on aCanvas- Throws:
IllegalStateException- if no run is openIllegalArgumentException- ifclusterfalls outside the open run, which is the run-origin mistake caught at the call that made it
-
build
Freezes the value: reorders the runs by level, resolves the caret stops and their strong and weak positions, sums the advances into the width and the cumulative table, and derivesShapedText.isSimple().- Throws:
IllegalStateException- if no vertical metrics were supplied, or if the runs do not tile the text exactly
-