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