• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics.text;
18 
19 import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
20 import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
21 import static com.android.text.flags.Flags.FLAG_MISSING_GETTER_APIS;
22 
23 
24 import android.annotation.FlaggedApi;
25 import android.annotation.FloatRange;
26 import android.annotation.IntDef;
27 import android.annotation.IntRange;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.annotation.Px;
31 import android.text.Layout;
32 
33 import dalvik.annotation.optimization.CriticalNative;
34 import dalvik.annotation.optimization.FastNative;
35 
36 import libcore.util.NativeAllocationRegistry;
37 
38 import java.lang.annotation.Retention;
39 import java.lang.annotation.RetentionPolicy;
40 
41 /**
42  * Provides automatic line breaking for a <em>single</em> paragraph.
43  *
44  * <p>
45  * <pre>
46  * <code>
47  * Paint paint = new Paint();
48  * Paint bigPaint = new Paint();
49  * bigPaint.setTextSize(paint.getTextSize() * 2.0);
50  * String text = "Hello, Android.";
51  *
52  * // Prepare the measured text
53  * MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
54  *     .appendStyleRun(paint, 7, false)  // Use paint for "Hello, "
55  *     .appednStyleRun(bigPaint, 8, false)  // Use bigPaint for "Hello, "
56  *     .build();
57  *
58  * LineBreaker lb = new LineBreaker.Builder()
59  *     // Use simple line breaker
60  *     .setBreakStrategy(LineBreaker.BREAK_STRATEGY_SIMPLE)
61  *     // Do not add hyphenation.
62  *     .setHyphenationFrequency(LineBreaker.HYPHENATION_FREQUENCY_NONE)
63  *     // Build the LineBreaker
64  *     .build();
65  *
66  * ParagraphConstraints c = new ParagraphConstraints();
67  * c.setWidth(240);  // Set the line wieth as 1024px
68  *
69  * // Do the line breaking
70  * Result r = lb.computeLineBreaks(mt, c, 0);
71  *
72  * // Compute the total height of the text.
73  * float totalHeight = 0;
74  * for (int i = 0; i < r.getLineCount(); ++i) {  // iterate over the lines
75  *    totalHeight += r.getLineDescent(i) - r.getLineAscent(i);
76  * }
77  *
78  * // Draw text to the canvas
79  * Bitmap bmp = Bitmap.createBitmap(240, totalHeight, Bitmap.Config.ARGB_8888);
80  * Canvas c = new Canvas(bmp);
81  * float yOffset = 0f;
82  * int prevOffset = 0;
83  * for (int i = 0; i < r.getLineCount(); ++i) {  // iterate over the lines
84  *     int nextOffset = r.getLineBreakOffset(i);
85  *     c.drawText(text, prevOffset, nextOffset, 0f, yOffset, paint);
86  *
87  *     prevOffset = nextOffset;
88  *     yOffset += r.getLineDescent(i) - r.getLineAscent(i);
89  * }
90  * </code>
91  * </pre>
92  * </p>
93  */
94 @android.ravenwood.annotation.RavenwoodKeepWholeClass
95 public class LineBreaker {
96     /** @hide */
97     @IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
98             BREAK_STRATEGY_SIMPLE,
99             BREAK_STRATEGY_HIGH_QUALITY,
100             BREAK_STRATEGY_BALANCED
101     })
102     @Retention(RetentionPolicy.SOURCE)
103     public @interface BreakStrategy {}
104 
105     /**
106      * Value for break strategy indicating simple line breaking.
107      *
108      * The line breaker puts words to the line as much as possible and breaks line if no more words
109      * can fit into the same line. Automatic hyphens are only added when a line has a single word
110      * and that word is longer than line width. This is the fastest break strategy and ideal for
111      * editor.
112      */
113     public static final int BREAK_STRATEGY_SIMPLE = 0;
114 
115     /**
116      * Value for break strategy indicating high quality line breaking.
117      *
118      * With this option line breaker does whole-paragraph optimization for more readable text, and
119      * also applies automatic hyphenation when required.
120      */
121     public static final int BREAK_STRATEGY_HIGH_QUALITY = 1;
122 
123     /**
124      * Value for break strategy indicating balanced line breaking.
125      *
126      * The line breaker does whole-paragraph optimization for making all lines similar length, and
127      * also applies automatic hyphenation when required. This break strategy is good for small
128      * screen devices such as watch screens.
129      */
130     public static final int BREAK_STRATEGY_BALANCED = 2;
131 
132     /** @hide */
133     @IntDef(prefix = { "HYPHENATION_FREQUENCY_" }, value = {
134             HYPHENATION_FREQUENCY_NORMAL,
135             HYPHENATION_FREQUENCY_FULL,
136             HYPHENATION_FREQUENCY_NONE
137     })
138     @Retention(RetentionPolicy.SOURCE)
139     public @interface HyphenationFrequency {}
140 
141     /**
142      * Value for hyphenation frequency indicating no automatic hyphenation.
143      *
144      * Using this option disables auto hyphenation which results in better text layout performance.
145      * A word may be broken without hyphens when a line has a single word and that word is longer
146      * than line width. Soft hyphens are ignored and will not be used as suggestions for potential
147      * line breaks.
148      */
149     public static final int HYPHENATION_FREQUENCY_NONE = 0;
150 
151     /**
152      * Value for hyphenation frequency indicating a light amount of automatic hyphenation.
153      *
154      * This hyphenation frequency is useful for informal cases, such as short sentences or chat
155      * messages.
156      */
157     public static final int HYPHENATION_FREQUENCY_NORMAL = 1;
158 
159     /**
160      * Value for hyphenation frequency indicating the full amount of automatic hyphenation.
161      *
162      * This hyphenation frequency is useful for running text and where it's important to put the
163      * maximum amount of text in a screen with limited space.
164      */
165     public static final int HYPHENATION_FREQUENCY_FULL = 2;
166 
167     /** @hide */
168     @IntDef(prefix = { "JUSTIFICATION_MODE_" }, value = {
169             JUSTIFICATION_MODE_NONE,
170             JUSTIFICATION_MODE_INTER_WORD,
171             JUSTIFICATION_MODE_INTER_CHARACTER,
172     })
173     @Retention(RetentionPolicy.SOURCE)
174     public @interface JustificationMode {}
175 
176     /**
177      * Value for justification mode indicating no justification.
178      */
179     public static final int JUSTIFICATION_MODE_NONE = 0;
180 
181     /**
182      * Value for justification mode indicating the text is justified by stretching word spacing.
183      */
184     public static final int JUSTIFICATION_MODE_INTER_WORD = 1;
185 
186     /**
187      * Value for justification mode indicating the text is justified by stretching letter spacing.
188      */
189     @FlaggedApi(FLAG_LETTER_SPACING_JUSTIFICATION)
190     public static final int JUSTIFICATION_MODE_INTER_CHARACTER = 2;
191 
192     /**
193      * Helper class for creating a {@link LineBreaker}.
194      */
195     public static final class Builder {
196         private @BreakStrategy int mBreakStrategy = BREAK_STRATEGY_SIMPLE;
197         private @HyphenationFrequency int mHyphenationFrequency = HYPHENATION_FREQUENCY_NONE;
198         private @JustificationMode int mJustificationMode = JUSTIFICATION_MODE_NONE;
199         private @Nullable int[] mIndents = null;
200         private boolean mUseBoundsForWidth = false;
201 
202         /**
203          * Set break strategy.
204          *
205          * You can change the line breaking behavior by setting break strategy. The default value is
206          * {@link #BREAK_STRATEGY_SIMPLE}.
207          */
setBreakStrategy(@reakStrategy int breakStrategy)208         public @NonNull Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
209             mBreakStrategy = breakStrategy;
210             return this;
211         }
212 
213         /**
214          * Set hyphenation frequency.
215          *
216          * You can change the amount of automatic hyphenation used. The default value is
217          * {@link #HYPHENATION_FREQUENCY_NONE}.
218          */
setHyphenationFrequency( @yphenationFrequency int hyphenationFrequency)219         public @NonNull Builder setHyphenationFrequency(
220                 @HyphenationFrequency int hyphenationFrequency) {
221             mHyphenationFrequency = hyphenationFrequency;
222             return this;
223         }
224 
225         /**
226          * Set whether the text is justified.
227          *
228          * By setting {@link #JUSTIFICATION_MODE_INTER_WORD}, the line breaker will change the
229          * internal parameters for justification.
230          * The default value is {@link #JUSTIFICATION_MODE_NONE}
231          */
setJustificationMode(@ustificationMode int justificationMode)232         public @NonNull Builder setJustificationMode(@JustificationMode int justificationMode) {
233             mJustificationMode = justificationMode;
234             return this;
235         }
236 
237         /**
238          * Set indents.
239          *
240          * The supplied array provides the total amount of indentation per line, in pixel. This
241          * amount is the sum of both left and right indentations. For lines past the last element in
242          * the array, the indentation amount of the last element is used.
243          */
setIndents(@ullable int[] indents)244         public @NonNull Builder setIndents(@Nullable int[] indents) {
245             mIndents = indents;
246             return this;
247         }
248 
249         /**
250          * Set true for using width of bounding box as a source of automatic line breaking.
251          *
252          * If this value is false, the automatic line breaking uses total amount of advances as text
253          * widths. By setting true, it uses joined all glyph bound's width as a width of the text.
254          *
255          * If the font has glyphs that have negative bearing X or its xMax is greater than advance,
256          * the glyph clipping can happen because the drawing area may be bigger. By setting this to
257          * true, the line breaker will break line based on bounding box, so clipping can be
258          * prevented.
259          *
260          * @param useBoundsForWidth True for using bounding box, false for advances.
261          * @return this builder instance
262          * @see Layout#getUseBoundsForWidth()
263          * @see android.text.StaticLayout.Builder#setUseBoundsForWidth(boolean)
264          */
265         @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
setUseBoundsForWidth(boolean useBoundsForWidth)266         public @NonNull Builder setUseBoundsForWidth(boolean useBoundsForWidth) {
267             mUseBoundsForWidth = useBoundsForWidth;
268             return this;
269         }
270 
271         /**
272          * Build a new LineBreaker with given parameters.
273          *
274          * You can reuse the Builder instance even after calling this method.
275          */
build()276         public @NonNull LineBreaker build() {
277             return new LineBreaker(mBreakStrategy, mHyphenationFrequency, mJustificationMode,
278                     mIndents, mUseBoundsForWidth);
279         }
280     }
281 
282     /**
283      * Line breaking constraints for single paragraph.
284      */
285     public static class ParagraphConstraints {
286         private @FloatRange(from = 0.0f) float mWidth = 0;
287         private @FloatRange(from = 0.0f) float mFirstWidth = 0;
288         private @IntRange(from = 0) int mFirstWidthLineCount = 0;
289         private @Nullable float[] mVariableTabStops = null;
290         private @FloatRange(from = 0) float mDefaultTabStop = 0;
291 
ParagraphConstraints()292         public ParagraphConstraints() {}
293 
294         /**
295          * Set width for this paragraph.
296          *
297          * @see #getWidth()
298          */
setWidth(@x @loatRangefrom = 0.0f) float width)299         public void setWidth(@Px @FloatRange(from = 0.0f) float width) {
300             mWidth = width;
301         }
302 
303         /**
304          * Set indent for this paragraph.
305          *
306          * @param firstWidth the line width of the starting of the paragraph
307          * @param firstWidthLineCount the number of lines that applies the firstWidth
308          * @see #getFirstWidth()
309          * @see #getFirstWidthLineCount()
310          */
setIndent(@x @loatRangefrom = 0.0f) float firstWidth, @Px @IntRange(from = 0) int firstWidthLineCount)311         public void setIndent(@Px @FloatRange(from = 0.0f) float firstWidth,
312                 @Px @IntRange(from = 0) int firstWidthLineCount) {
313             mFirstWidth = firstWidth;
314             mFirstWidthLineCount = firstWidthLineCount;
315         }
316 
317         /**
318          * Set tab stops for this paragraph.
319          *
320          * @param tabStops the array of pixels of tap stopping position
321          * @param defaultTabStop pixels of the default tab stopping position
322          * @see #getTabStops()
323          * @see #getDefaultTabStop()
324          */
setTabStops(@ullable float[] tabStops, @Px @FloatRange(from = 0) float defaultTabStop)325         public void setTabStops(@Nullable float[] tabStops,
326                 @Px @FloatRange(from = 0) float defaultTabStop) {
327             mVariableTabStops = tabStops;
328             mDefaultTabStop = defaultTabStop;
329         }
330 
331         /**
332          * Return the width for this paragraph in pixels.
333          *
334          * @see #setWidth(float)
335          */
getWidth()336         public @Px @FloatRange(from = 0.0f) float getWidth() {
337             return mWidth;
338         }
339 
340         /**
341          * Return the first line's width for this paragraph in pixel.
342          *
343          * @see #setIndent(float, int)
344          */
getFirstWidth()345         public @Px @FloatRange(from = 0.0f) float getFirstWidth() {
346             return mFirstWidth;
347         }
348 
349         /**
350          * Return the number of lines to apply the first line's width.
351          *
352          * @see #setIndent(float, int)
353          */
getFirstWidthLineCount()354         public @Px @IntRange(from = 0) int getFirstWidthLineCount() {
355             return mFirstWidthLineCount;
356         }
357 
358         /**
359          * Returns the array of tab stops in pixels.
360          *
361          * @see #setTabStops
362          */
getTabStops()363         public @Nullable float[] getTabStops() {
364             return mVariableTabStops;
365         }
366 
367         /**
368          * Returns the default tab stops in pixels.
369          *
370          * @see #setTabStops
371          */
getDefaultTabStop()372         public @Px @FloatRange(from = 0) float getDefaultTabStop() {
373             return mDefaultTabStop;
374         }
375     }
376 
377     /**
378      * Holds the result of the {@link LineBreaker#computeLineBreaks line breaking algorithm}.
379      * @see LineBreaker#computeLineBreaks
380      */
381     public static class Result {
382         // Following two constants must be synced with minikin's line breaker.
383         // TODO(nona): Remove these constants by introducing native methods.
384         private static final int TAB_MASK = 0x20000000;
385         private static final int HYPHEN_MASK = 0xFF;
386         private static final int START_HYPHEN_MASK = 0x18;  // 0b11000
387         private static final int END_HYPHEN_MASK = 0x7;  // 0b00111
388         private static final int START_HYPHEN_BITS_SHIFT = 3;
389 
390         private static final NativeAllocationRegistry sRegistry =
391                 NativeAllocationRegistry.createMalloced(
392                 Result.class.getClassLoader(), nGetReleaseResultFunc());
393         private final long mPtr;
394 
Result(long ptr)395         private Result(long ptr) {
396             mPtr = ptr;
397             sRegistry.registerNativeAllocation(this, mPtr);
398         }
399 
400         /**
401          * Returns the number of lines in the paragraph.
402          *
403          * @return number of lines
404          */
getLineCount()405         public @IntRange(from = 0) int getLineCount() {
406             return nGetLineCount(mPtr);
407         }
408 
409         /**
410          * Returns character offset of the break for a given line.
411          *
412          * @param lineIndex an index of the line.
413          * @return the break offset.
414          */
getLineBreakOffset(@ntRangefrom = 0) int lineIndex)415         public @IntRange(from = 0) int getLineBreakOffset(@IntRange(from = 0) int lineIndex) {
416             return nGetLineBreakOffset(mPtr, lineIndex);
417         }
418 
419         /**
420          * Returns width of a given line in pixels.
421          *
422          * @param lineIndex an index of the line.
423          * @return width of the line in pixels
424          */
getLineWidth(@ntRangefrom = 0) int lineIndex)425         public @Px float getLineWidth(@IntRange(from = 0) int lineIndex) {
426             return nGetLineWidth(mPtr, lineIndex);
427         }
428 
429         /**
430          * Returns font ascent of the line in pixels.
431          *
432          * @param lineIndex an index of the line.
433          * @return an entier font ascent of the line in pixels.
434          */
getLineAscent(@ntRangefrom = 0) int lineIndex)435         public @Px float getLineAscent(@IntRange(from = 0) int lineIndex) {
436             return nGetLineAscent(mPtr, lineIndex);
437         }
438 
439         /**
440          * Returns font descent of the line in pixels.
441          *
442          * @param lineIndex an index of the line.
443          * @return an entier font descent of the line in pixels.
444          */
getLineDescent(@ntRangefrom = 0) int lineIndex)445         public @Px float getLineDescent(@IntRange(from = 0) int lineIndex) {
446             return nGetLineDescent(mPtr, lineIndex);
447         }
448 
449         /**
450          * Returns true if the line has a TAB character.
451          *
452          * @param lineIndex an index of the line.
453          * @return true if the line has a TAB character
454          */
hasLineTab(int lineIndex)455         public boolean hasLineTab(int lineIndex) {
456             return (nGetLineFlag(mPtr, lineIndex) & TAB_MASK) != 0;
457         }
458 
459         /**
460          * Returns a start hyphen edit for the line.
461          *
462          * @param lineIndex an index of the line.
463          * @return a start hyphen edit for the line.
464          *
465          * @see android.graphics.Paint#setStartHyphenEdit
466          * @see android.graphics.Paint#getStartHyphenEdit
467          */
getStartLineHyphenEdit(int lineIndex)468         public int getStartLineHyphenEdit(int lineIndex) {
469             return (nGetLineFlag(mPtr, lineIndex) & START_HYPHEN_MASK) >> START_HYPHEN_BITS_SHIFT;
470         }
471 
472         /**
473          * Returns an end hyphen edit for the line.
474          *
475          * @param lineIndex an index of the line.
476          * @return an end hyphen edit for the line.
477          *
478          * @see android.graphics.Paint#setEndHyphenEdit
479          * @see android.graphics.Paint#getEndHyphenEdit
480          */
getEndLineHyphenEdit(int lineIndex)481         public int getEndLineHyphenEdit(int lineIndex) {
482             return nGetLineFlag(mPtr, lineIndex) & END_HYPHEN_MASK;
483         }
484     }
485 
486     private static class NoImagePreloadHolder {
487         private static final NativeAllocationRegistry sRegistry =
488                 NativeAllocationRegistry.createMalloced(
489                         LineBreaker.class.getClassLoader(), nGetReleaseFunc());
490     }
491 
492     private final long mNativePtr;
493 
494     private final @BreakStrategy int mBreakStrategy;
495     private final @HyphenationFrequency int mHyphenationFrequency;
496     private final @JustificationMode int mJustificationMode;
497     private final int[] mIndents;
498     private final boolean mUseBoundsForWidth;
499 
500     /**
501      * Use Builder instead.
502      */
LineBreaker(@reakStrategy int breakStrategy, @HyphenationFrequency int hyphenationFrequency, @JustificationMode int justify, @Nullable int[] indents, boolean useBoundsForWidth)503     private LineBreaker(@BreakStrategy int breakStrategy,
504             @HyphenationFrequency int hyphenationFrequency, @JustificationMode int justify,
505             @Nullable int[] indents, boolean useBoundsForWidth) {
506         mNativePtr = nInit(breakStrategy, hyphenationFrequency,
507                 justify == JUSTIFICATION_MODE_INTER_WORD, indents, useBoundsForWidth);
508         NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePtr);
509 
510         mBreakStrategy = breakStrategy;
511         mHyphenationFrequency = hyphenationFrequency;
512         mJustificationMode = justify;
513         mIndents = indents;
514         mUseBoundsForWidth = useBoundsForWidth;
515     }
516 
517     /**
518      * Returns the break strategy used for this line breaker.
519      *
520      * @return the break strategy used for this line breaker.
521      * @see Builder#setBreakStrategy(int)
522      */
523     @FlaggedApi(FLAG_MISSING_GETTER_APIS)
getBreakStrategy()524     public @BreakStrategy int getBreakStrategy() {
525         return mBreakStrategy;
526     }
527 
528     /**
529      * Returns the hyphenation frequency used for this line breaker.
530      *
531      * @return the hyphenation frequency used for this line breaker.
532      * @see Builder#setHyphenationFrequency(int)
533      */
534     @FlaggedApi(FLAG_MISSING_GETTER_APIS)
getHyphenationFrequency()535     public @HyphenationFrequency int getHyphenationFrequency() {
536         return mHyphenationFrequency;
537     }
538 
539     /**
540      * Returns the justification mode used for this line breaker.
541      *
542      * @return the justification mode used for this line breaker.
543      * @see Builder#setJustificationMode(int)
544      */
545     @FlaggedApi(FLAG_MISSING_GETTER_APIS)
getJustificationMode()546     public @JustificationMode int getJustificationMode() {
547         return mJustificationMode;
548     }
549 
550     /**
551      * Returns the indents used for this line breaker.
552      *
553      * @return the indents used for this line breaker.
554      * @see Builder#setIndents(int[])
555      */
556     @FlaggedApi(FLAG_MISSING_GETTER_APIS)
getIndents()557     public @Nullable int[] getIndents() {
558         return mIndents;
559     }
560 
561     /**
562      * Returns true if this line breaker uses bounds as width for line breaking.
563      *
564      * @return true if this line breaker uses bounds as width for line breaking.
565      * @see Builder#setUseBoundsForWidth(boolean)
566      */
567     @FlaggedApi(FLAG_MISSING_GETTER_APIS)
getUseBoundsForWidth()568     public boolean getUseBoundsForWidth() {
569         return mUseBoundsForWidth;
570     }
571 
572     /**
573      * Break paragraph into lines.
574      *
575      * The result is filled to out param.
576      *
577      * @param measuredPara a result of the text measurement
578      * @param constraints for a single paragraph
579      * @param lineNumber a line number of this paragraph
580      */
computeLineBreaks( @onNull MeasuredText measuredPara, @NonNull ParagraphConstraints constraints, @IntRange(from = 0) int lineNumber)581     public @NonNull Result computeLineBreaks(
582             @NonNull MeasuredText measuredPara,
583             @NonNull ParagraphConstraints constraints,
584             @IntRange(from = 0) int lineNumber) {
585         return new Result(nComputeLineBreaks(
586                 mNativePtr,
587 
588                 // Inputs
589                 measuredPara.getChars(),
590                 measuredPara.getNativePtr(),
591                 measuredPara.getChars().length,
592                 constraints.mFirstWidth,
593                 constraints.mFirstWidthLineCount,
594                 constraints.mWidth,
595                 constraints.mVariableTabStops,
596                 constraints.mDefaultTabStop,
597                 lineNumber));
598     }
599 
600     @FastNative
nInit(@reakStrategy int breakStrategy, @HyphenationFrequency int hyphenationFrequency, boolean isJustified, @Nullable int[] indents, boolean useBoundsForWidth)601     private static native long nInit(@BreakStrategy int breakStrategy,
602             @HyphenationFrequency int hyphenationFrequency, boolean isJustified,
603             @Nullable int[] indents, boolean useBoundsForWidth);
604 
605     @CriticalNative
nGetReleaseFunc()606     private static native long nGetReleaseFunc();
607 
608     // populates LineBreaks and returns the number of breaks found
609     //
610     // the arrays inside the LineBreaks objects are passed in as well
611     // to reduce the number of JNI calls in the common case where the
612     // arrays do not have to be resized
613     // The individual character widths will be returned in charWidths. The length of
614     // charWidths must be at least the length of the text.
nComputeLineBreaks( long nativePtr, @NonNull char[] text, long measuredTextPtr, @IntRange(from = 0) int length, @FloatRange(from = 0.0f) float firstWidth, @IntRange(from = 0) int firstWidthLineCount, @FloatRange(from = 0.0f) float restWidth, @Nullable float[] variableTabStops, float defaultTabStop, @IntRange(from = 0) int indentsOffset)615     private static native long nComputeLineBreaks(
616             /* non zero */ long nativePtr,
617 
618             // Inputs
619             @NonNull char[] text,
620             /* Non Zero */ long measuredTextPtr,
621             @IntRange(from = 0) int length,
622             @FloatRange(from = 0.0f) float firstWidth,
623             @IntRange(from = 0) int firstWidthLineCount,
624             @FloatRange(from = 0.0f) float restWidth,
625             @Nullable float[] variableTabStops,
626             float defaultTabStop,
627             @IntRange(from = 0) int indentsOffset);
628 
629     // Result accessors
630     @CriticalNative
nGetLineCount(long ptr)631     private static native int nGetLineCount(long ptr);
632     @CriticalNative
nGetLineBreakOffset(long ptr, int idx)633     private static native int nGetLineBreakOffset(long ptr, int idx);
634     @CriticalNative
nGetLineWidth(long ptr, int idx)635     private static native float nGetLineWidth(long ptr, int idx);
636     @CriticalNative
nGetLineAscent(long ptr, int idx)637     private static native float nGetLineAscent(long ptr, int idx);
638     @CriticalNative
nGetLineDescent(long ptr, int idx)639     private static native float nGetLineDescent(long ptr, int idx);
640     @CriticalNative
nGetLineFlag(long ptr, int idx)641     private static native int nGetLineFlag(long ptr, int idx);
642     @CriticalNative
nGetReleaseResultFunc()643     private static native long nGetReleaseResultFunc();
644 }
645