From 7b86fe573c7d30dbe14e067fdac0ef424751cc86 Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Tue, 16 Oct 2018 18:02:32 -0700 Subject: [PATCH] Change add{StyleRun, ReplacementRun} to append{StyleRun, ReplacementRun} Giving any range to the Builder is too much freedum and hard to tell errors. Changes to accept length instead of ranges. By this change, we can prevent and tell errors about - The gap in the text that no Paint is assigned. - The given range is overlapped. - The given range exceeds the text length. Bug: 112327179 Test: TreeHugger Change-Id: Iefc078b1f7be2d006a65b46e0b4ea1018ecb29e7 --- api/current.txt | 4 +- core/java/android/text/MeasuredParagraph.java | 6 +- .../android/graphics/text/LineBreaker.java | 5 +- .../android/graphics/text/MeasuredText.java | 77 +++++++++++++++---- 4 files changed, 72 insertions(+), 20 deletions(-) diff --git a/api/current.txt b/api/current.txt index 878e2e8a3851b..29f14a748038e 100755 --- a/api/current.txt +++ b/api/current.txt @@ -15468,8 +15468,8 @@ package android.graphics.text { public static class MeasuredText.Builder { ctor public MeasuredText.Builder(char[]); - method public android.graphics.text.MeasuredText.Builder addReplacementRun(android.graphics.Paint, int, int, float); - method public android.graphics.text.MeasuredText.Builder addStyleRun(android.graphics.Paint, int, int, boolean); + method public android.graphics.text.MeasuredText.Builder appendReplacementRun(android.graphics.Paint, int, float); + method public android.graphics.text.MeasuredText.Builder appendStyleRun(android.graphics.Paint, int, boolean); method public android.graphics.text.MeasuredText build(); method public android.graphics.text.MeasuredText.Builder setComputeHyphenation(boolean); method public android.graphics.text.MeasuredText.Builder setComputeLayout(boolean); diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java index 0a2d65c9ebffa..f9370a8aa6af0 100644 --- a/core/java/android/text/MeasuredParagraph.java +++ b/core/java/android/text/MeasuredParagraph.java @@ -505,7 +505,7 @@ public class MeasuredParagraph { } mWholeWidth += width; } else { - builder.addReplacementRun(mCachedPaint, start, end, width); + builder.appendReplacementRun(mCachedPaint, end - start, width); } } @@ -520,7 +520,7 @@ public class MeasuredParagraph { mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */, mWidths.getRawArray(), start); } else { - builder.addStyleRun(mCachedPaint, start, end, false /* isRtl */); + builder.appendStyleRun(mCachedPaint, end - start, false /* isRtl */); } } else { // If there is multiple bidi levels, split into individual bidi level and apply style. @@ -536,7 +536,7 @@ public class MeasuredParagraph { mCopiedBuffer, levelStart, levelLength, levelStart, levelLength, isRtl, mWidths.getRawArray(), levelStart); } else { - builder.addStyleRun(mCachedPaint, levelStart, levelEnd, isRtl); + builder.appendStyleRun(mCachedPaint, levelEnd - levelStart, isRtl); } if (levelEnd == end) { break; diff --git a/graphics/java/android/graphics/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java index 8d38f961f4029..16479095a63ad 100644 --- a/graphics/java/android/graphics/text/LineBreaker.java +++ b/graphics/java/android/graphics/text/LineBreaker.java @@ -38,11 +38,14 @@ import java.lang.annotation.RetentionPolicy; *
  * 
  * Paint paint = new Paint();
+ * Paint bigPaint = new Paint();
+ * bigPaint.setTextSize(paint.getTextSize() * 2.0);
  * String text = "Hello, Android.";
  *
  * // Prepare the measured text
  * MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
- *     .addStyleRun(paint, 0, text.length(), false)  // Use paint for whole paragraph.
+ *     .appendStyleRun(paint, 7, false)  // Use paint for "Hello, "
+ *     .appednStyleRun(bigPaint, 8, false)  // Use bigPaint for "Hello, "
  *     .build();
  *
  * LineBreaker lb = new LineBreaker.Builder()
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 852e66e174ca2..3efe655b25654 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -31,6 +31,21 @@ import libcore.util.NativeAllocationRegistry;
 
 /**
  * Result of text shaping of the single paragraph string.
+ *
+ * 

+ *

+ * 
+ * Paint paint = new Paint();
+ * Paint bigPaint = new Paint();
+ * bigPaint.setTextSize(paint.getTextSize() * 2.0);
+ * String text = "Hello, Android.";
+ * MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
+ *      .appendStyleRun(paint, 7, false)  // Use paint for "Hello, "
+ *      .appendStyleRun(bigPaint, 8, false)  // Use bigPaint for "Hello, "
+ *      .build();
+ * 
+ * 
+ *

*/ public class MeasuredText { private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( @@ -136,6 +151,19 @@ public class MeasuredText { /** * Helper class for creating a {@link MeasuredText}. + *

+ *

+     * 
+     * Paint paint = new Paint();
+     * String text = "Hello, Android.";
+     * MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
+     *      .appendStyleRun(paint, text.length, false)
+     *      .build();
+     * 
+     * 
+ *

+ * + * Note: The appendStyle and appendReplacementRun should be called to cover the text length. */ public static class Builder { private long mNativePtr; @@ -143,6 +171,7 @@ public class MeasuredText { private final @NonNull char[] mText; private boolean mComputeHyphenation = false; private boolean mComputeLayout = true; + private int mCurrentOffset = 0; /** * Construct a builder. @@ -159,35 +188,50 @@ public class MeasuredText { } /** - * Apply styles to the given range. + * Apply styles to the given length. + * + * Keeps an internal offset which increases at every append. The initial value for this + * offset is zero. After the style is applied the internal offset is moved to {@code offset + * + length}, and next call will start from this new position. * * @param paint a paint - * @param start an inclusive start index of the range - * @param end an exclusive end index of the range + * @param length a length to be applied with a given paint, can not exceed the length of the + * text * @param isRtl true if the text is in RTL context, otherwise false. */ - public Builder addStyleRun(@NonNull Paint paint, - @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean isRtl) { + public Builder appendStyleRun(@NonNull Paint paint, @IntRange(from = 0) int length, + boolean isRtl) { Preconditions.checkNotNull(paint); - nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl); + Preconditions.checkArgument(length > 0, "length can not be negative"); + final int end = mCurrentOffset + length; + Preconditions.checkArgument(end <= mText.length, "Style exceeds the text length"); + nAddStyleRun(mNativePtr, paint.getNativeInstance(), mCurrentOffset, end, isRtl); + mCurrentOffset = end; return this; } /** - * Used to inform the text layout that the given range is replaced with the object of given + * Used to inform the text layout that the given length is replaced with the object of given * width. * - * Informs the layout engine that the given range should not be processed, instead the + * Keeps an internal offset which increases at every append. The initial value for this + * offset is zero. After the style is applied the internal offset is moved to {@code offset + * + length}, and next call will start from this new position. + * + * Informs the layout engine that the given length should not be processed, instead the * provided width should be used for calculating the width of that range. * - * @param start an inclusive start index of the range - * @param end an exclusive end index of the range + * @param length a length to be replaced with the object, can not exceed the length of the + * text * @param width a replacement width of the range */ - public Builder addReplacementRun(@NonNull Paint paint, - @IntRange(from = 0) int start, @IntRange(from = 0) int end, - @FloatRange(from = 0) float width) { - nAddReplacementRun(mNativePtr, paint.getNativeInstance(), start, end, width); + public Builder appendReplacementRun(@NonNull Paint paint, + @IntRange(from = 0) int length, @FloatRange(from = 0) float width) { + Preconditions.checkArgument(length > 0, "length can not be negative"); + final int end = mCurrentOffset + length; + Preconditions.checkArgument(end <= mText.length, "Replacement exceeds the text length"); + nAddReplacementRun(mNativePtr, paint.getNativeInstance(), mCurrentOffset, end, width); + mCurrentOffset = end; return this; } @@ -230,9 +274,14 @@ public class MeasuredText { * * Once you called build() method, you can't reuse the Builder class again. * @throws IllegalStateException if this Builder is reused. + * @throws IllegalStateException if the whole text is not covered by one or more runs (style + * or replacement) */ public MeasuredText build() { ensureNativePtrNoReuse(); + if (mCurrentOffset != mText.length) { + throw new IllegalStateException("Style info has not been provided for all text."); + } try { long ptr = nBuildMeasuredText(mNativePtr, mText, mComputeHyphenation, mComputeLayout);