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