Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7347b89f authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Update TextShaper APIs to address API council feedback"

parents cde91282 c489d627
Loading
Loading
Loading
Loading
+14 −28
Original line number Diff line number Diff line
@@ -16536,23 +16536,6 @@ package android.graphics.pdf {
package android.graphics.text {
  public class GlyphStyle {
    ctor public GlyphStyle(@ColorInt int, @FloatRange(from=0) float, @FloatRange(from=0) float, @FloatRange(from=0) float, int);
    ctor public GlyphStyle(@NonNull android.graphics.Paint);
    method public void applyToPaint(@NonNull android.graphics.Paint);
    method @ColorInt public int getColor();
    method public int getFlags();
    method @FloatRange(from=0) public float getFontSize();
    method @FloatRange(from=0) public float getScaleX();
    method @FloatRange(from=0) public float getSkewX();
    method public void setColor(@ColorInt int);
    method public void setFlags(int);
    method public void setFontSize(@FloatRange(from=0) float);
    method public void setFromPaint(@NonNull android.graphics.Paint);
    method public void setScaleX(@FloatRange(from=0) float);
    method public void setSkewX(@FloatRange(from=0) float);
  }
  public class LineBreaker {
    method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int);
    field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -16614,20 +16597,19 @@ package android.graphics.text {
  }
  public final class PositionedGlyphs {
    method public float getAdvance();
    method public float getAscent();
    method public float getDescent();
    method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int);
    method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int);
    method public float getOriginX();
    method public float getOriginY();
    method public float getPositionX(@IntRange(from=0) int);
    method public float getPositionY(@IntRange(from=0) int);
    method @NonNull public android.graphics.text.GlyphStyle getStyle();
    method public float getTotalAdvance();
    method public float getGlyphX(@IntRange(from=0) int);
    method public float getGlyphY(@IntRange(from=0) int);
    method public float getOffsetX();
    method public float getOffsetY();
    method @IntRange(from=0) public int glyphCount();
  }
  public class TextShaper {
  public class TextRunShaper {
    method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull char[], int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
    method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull CharSequence, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
  }
@@ -50075,10 +50057,6 @@ package android.text {
    method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean);
  }
  public class StyledTextShaper {
    method @NonNull public static java.util.List<android.graphics.text.PositionedGlyphs> shapeText(@NonNull CharSequence, int, int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint);
  }
  public interface TextDirectionHeuristic {
    method public boolean isRtl(char[], int, int);
    method public boolean isRtl(CharSequence, int, int);
@@ -50108,6 +50086,14 @@ package android.text {
    field @Px public float underlineThickness;
  }
  public class TextShaper {
    method public static void shapeText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint, @NonNull android.text.TextShaper.GlyphsConsumer);
  }
  public static interface TextShaper.GlyphsConsumer {
    method public void accept(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.text.PositionedGlyphs, @NonNull android.text.TextPaint);
  }
  public class TextUtils {
    method @Deprecated public static CharSequence commaEllipsize(CharSequence, android.text.TextPaint, float, String, String);
    method public static CharSequence concat(java.lang.CharSequence...);
+0 −67
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.text;

import android.annotation.NonNull;
import android.graphics.Paint;
import android.graphics.text.PositionedGlyphs;
import android.graphics.text.TextShaper;

import java.util.List;

/**
 * Provides text shaping for multi-styled text.
 *
 * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
 * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
 * @see StyledTextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint)
 */
public class StyledTextShaper {
    private StyledTextShaper() {}


    /**
     * Shape multi-styled text.
     *
     * @param text a styled text.
     * @param start a start index of shaping target in the text.
     * @param count a length of shaping target in the text.
     * @param dir a text direction.
     * @param paint a paint
     * @return a shape result.
     */
    public static @NonNull List<PositionedGlyphs> shapeText(
            @NonNull CharSequence text, int start, int count,
            @NonNull TextDirectionHeuristic dir, @NonNull TextPaint paint) {
        MeasuredParagraph mp = MeasuredParagraph.buildForBidi(
                text, start, start + count, dir, null);
        TextLine tl = TextLine.obtain();
        try {
            tl.set(paint, text, start, start + count,
                    mp.getParagraphDir(),
                    mp.getDirections(start, start + count),
                    false /* tabstop is not supported */,
                    null,
                    -1, -1 // ellipsis is not supported.
            );
            return tl.shape();
        } finally {
            TextLine.recycle(tl);
        }
    }

}
+25 −25
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.text.PositionedGlyphs;
import android.graphics.text.TextShaper;
import android.graphics.text.TextRunShaper;
import android.os.Build;
import android.text.Layout.Directions;
import android.text.Layout.TabStops;
@@ -37,7 +37,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * Represents a line of styled text, for measuring in visual order and
@@ -312,8 +311,7 @@ public class TextLine {
    /**
     * Shape the TextLine.
     */
    List<PositionedGlyphs> shape() {
        List<PositionedGlyphs> glyphs = new ArrayList<>();
    void shape(TextShaper.GlyphsConsumer consumer) {
        float horizontal = 0;
        float x = 0;
        final int runCount = mDirections.getRunCount();
@@ -326,7 +324,7 @@ public class TextLine {
            int segStart = runStart;
            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
                if (j == runLimit || charAt(j) == TAB_CHAR) {
                    horizontal += shapeRun(glyphs, segStart, j, runIsRtl, x + horizontal,
                    horizontal += shapeRun(consumer, segStart, j, runIsRtl, x + horizontal,
                            runIndex != (runCount - 1) || j != mLen);

                    if (j != runLimit) {  // charAt(j) == TAB_CHAR
@@ -336,7 +334,6 @@ public class TextLine {
                }
            }
        }
        return glyphs;
    }

    /**
@@ -546,7 +543,7 @@ public class TextLine {
    /**
     * Shape a unidirectional (but possibly multi-styled) run of text.
     *
     * @param glyphs the output positioned glyphs list
     * @param consumer the consumer of the shape result
     * @param start the line-relative start
     * @param limit the line-relative limit
     * @param runIsRtl true if the run is right-to-left
@@ -555,16 +552,17 @@ public class TextLine {
     * @return the signed width of the run, based on the paragraph direction.
     * Only valid if needWidth is true.
     */
    private float shapeRun(List<PositionedGlyphs> glyphs, int start,
    private float shapeRun(TextShaper.GlyphsConsumer consumer, int start,
            int limit, boolean runIsRtl, float x, boolean needWidth) {

        if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
            float w = -measureRun(start, limit, limit, runIsRtl, null);
            handleRun(start, limit, limit, runIsRtl, null, glyphs, x + w, 0, 0, 0, null, false);
            handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null, false);
            return w;
        }

        return handleRun(start, limit, limit, runIsRtl, null, glyphs, x, 0, 0, 0, null, needWidth);
        return handleRun(start, limit, limit, runIsRtl, null, consumer, x, 0, 0, 0, null,
                needWidth);
    }


@@ -899,7 +897,7 @@ public class TextLine {
     * @param end the end of the text
     * @param runIsRtl true if the run is right-to-left
     * @param c the canvas, can be null if rendering is not needed
     * @param glyphs the output positioned glyph list, can be null if not necessary
     * @param consumer the output positioned glyph list, can be null if not necessary
     * @param x the edge of the run closest to the leading margin
     * @param top the top of the line
     * @param y the baseline
@@ -913,7 +911,7 @@ public class TextLine {
     */
    private float handleText(TextPaint wp, int start, int end,
            int contextStart, int contextEnd, boolean runIsRtl,
            Canvas c, List<PositionedGlyphs> glyphs, float x, int top, int y, int bottom,
            Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom,
            FontMetricsInt fmi, boolean needWidth, int offset,
            @Nullable ArrayList<DecorationInfo> decorations) {

@@ -946,8 +944,8 @@ public class TextLine {
            rightX = x + totalWidth;
        }

        if (glyphs != null) {
            shapeTextRun(glyphs, wp, start, end, contextStart, contextEnd, runIsRtl, leftX);
        if (consumer != null) {
            shapeTextRun(consumer, wp, start, end, contextStart, contextEnd, runIsRtl, leftX);
        }

        if (c != null) {
@@ -1135,7 +1133,7 @@ public class TextLine {
     * @param limit the limit of the run
     * @param runIsRtl true if the run is right-to-left
     * @param c the canvas, can be null
     * @param glyphs the output positioned glyphs, can be null
     * @param consumer the output positioned glyphs, can be null
     * @param x the end of the run closest to the leading margin
     * @param top the top of the line
     * @param y the baseline
@@ -1147,7 +1145,7 @@ public class TextLine {
     */
    private float handleRun(int start, int measureLimit,
            int limit, boolean runIsRtl, Canvas c,
            List<PositionedGlyphs> glyphs, float x, int top, int y,
            TextShaper.GlyphsConsumer consumer, float x, int top, int y,
            int bottom, FontMetricsInt fmi, boolean needWidth) {

        if (measureLimit < start || measureLimit > limit) {
@@ -1180,7 +1178,7 @@ public class TextLine {
            wp.set(mPaint);
            wp.setStartHyphenEdit(adjustStartHyphenEdit(start, wp.getStartHyphenEdit()));
            wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit()));
            return handleText(wp, start, limit, start, limit, runIsRtl, c, glyphs, x, top,
            return handleText(wp, start, limit, start, limit, runIsRtl, c, consumer, x, top,
                    y, bottom, fmi, needWidth, measureLimit, null);
        }

@@ -1262,7 +1260,7 @@ public class TextLine {
                    activePaint.setEndHyphenEdit(
                            adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
                    x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c,
                            glyphs, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
                            consumer, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
                            Math.min(activeEnd, mlimit), mDecorations);

                    activeStart = j;
@@ -1288,7 +1286,7 @@ public class TextLine {
                    adjustStartHyphenEdit(activeStart, mPaint.getStartHyphenEdit()));
            activePaint.setEndHyphenEdit(
                    adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
            x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, glyphs, x,
            x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, consumer, x,
                    top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
                    Math.min(activeEnd, mlimit), mDecorations);
        }
@@ -1327,7 +1325,7 @@ public class TextLine {
    /**
     * Shape a text run with the set-up paint.
     *
     * @param glyphs the output positioned glyphs list
     * @param consumer the output positioned glyphs list
     * @param paint the paint used to render the text
     * @param start the start of the run
     * @param end the end of the run
@@ -1336,30 +1334,32 @@ public class TextLine {
     * @param runIsRtl true if the run is right-to-left
     * @param x the x position of the left edge of the run
     */
    private void shapeTextRun(List<PositionedGlyphs> glyphs, TextPaint paint,
    private void shapeTextRun(TextShaper.GlyphsConsumer consumer, TextPaint paint,
            int start, int end, int contextStart, int contextEnd, boolean runIsRtl, float x) {

        int count = end - start;
        int contextCount = contextEnd - contextStart;
        PositionedGlyphs glyphs;
        if (mCharsValid) {
            glyphs.add(TextShaper.shapeTextRun(
            glyphs = TextRunShaper.shapeTextRun(
                    mChars,
                    start, count,
                    contextStart, contextCount,
                    x, 0f,
                    runIsRtl,
                    paint
            ));
            );
        } else {
            glyphs.add(TextShaper.shapeTextRun(
            glyphs = TextRunShaper.shapeTextRun(
                    mText,
                    mStart + start, count,
                    mStart + contextStart, contextCount,
                    x, 0f,
                    runIsRtl,
                    paint
            ));
            );
        }
        consumer.accept(start, count, glyphs, paint);
    }


+229 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.text;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.graphics.Paint;
import android.graphics.text.PositionedGlyphs;
import android.graphics.text.TextRunShaper;

/**
 * Provides text shaping for multi-styled text.
 *
 * Here is an example of animating text size and letter spacing for simple text.
 * <pre>
 * <code>
 * // In this example, shape the text once for start and end state, then animate between two shape
 * // result without re-shaping in each frame.
 * class SimpleAnimationView @JvmOverloads constructor(
 *         context: Context,
 *         attrs: AttributeSet? = null,
 *         defStyleAttr: Int = 0
 * ) : View(context, attrs, defStyleAttr) {
 *     private val textDir = TextDirectionHeuristics.LOCALE
 *     private val text = "Hello, World."  // The text to be displayed
 *
 *     // Class for keeping drawing parameters.
 *     data class DrawStyle(val textSize: Float, val alpha: Int)
 *
 *     // The start and end text shaping result. This class will animate between these two.
 *     private val start = mutableListOf&lt;Pair&lt;PositionedGlyphs, DrawStyle&gt;&gt;()
 *     private val end = mutableListOf&lt;Pair&lt;PositionedGlyphs, DrawStyle&gt;&gt;()
 *
 *     init {
 *         val startPaint = TextPaint().apply {
 *             alpha = 0 // Alpha only affect text drawing but not text shaping
 *             textSize = 36f // TextSize affect both text shaping and drawing.
 *             letterSpacing = 0f // Letter spacing only affect text shaping but not drawing.
 *         }
 *
 *         val endPaint = TextPaint().apply {
 *             alpha = 255
 *             textSize =128f
 *             letterSpacing = 0.1f
 *         }
 *
 *         TextShaper.shapeText(text, 0, text.length, textDir, startPaint) { _, _, glyphs, paint ->
 *             start.add(Pair(glyphs, DrawStyle(paint.textSize, paint.alpha)))
 *         }
 *         TextShaper.shapeText(text, 0, text.length, textDir, endPaint) { _, _, glyphs, paint ->
 *             end.add(Pair(glyphs, DrawStyle(paint.textSize, paint.alpha)))
 *         }
 *     }
 *
 *     override fun onDraw(canvas: Canvas) {
 *         super.onDraw(canvas)
 *
 *         // Set the baseline to the vertical center of the view.
 *         canvas.translate(0f, height / 2f)
 *
 *         // Assume the number of PositionedGlyphs are the same. If different, you may want to
 *         // animate in a different way, e.g. cross fading.
 *         start.zip(end) { (startGlyphs, startDrawStyle), (endGlyphs, endDrawStyle) ->
 *             // Tween the style and set to paint.
 *             paint.textSize = lerp(startDrawStyle.textSize, endDrawStyle.textSize, progress)
 *             paint.alpha = lerp(startDrawStyle.alpha, endDrawStyle.alpha, progress)
 *
 *             // Assume the number of glyphs are the same. If different, you may want to animate in
 *             // a different way, e.g. cross fading.
 *             require(startGlyphs.glyphCount() == endGlyphs.glyphCount())
 *
 *             if (startGlyphs.glyphCount() == 0) return@zip
 *
 *             var curFont = startGlyphs.getFont(0)
 *             var drawStart = 0
 *             for (i in 1 until startGlyphs.glyphCount()) {
 *                 // Assume the pair of Glyph ID and font is the same. If different, you may want
 *                 // to animate in a different way, e.g. cross fading.
 *                 require(startGlyphs.getGlyphId(i) == endGlyphs.getGlyphId(i))
 *                 require(startGlyphs.getFont(i) === endGlyphs.getFont(i))
 *
 *                 val font = startGlyphs.getFont(i)
 *                 if (curFont != font) {
 *                     drawGlyphs(canvas, startGlyphs, endGlyphs, drawStart, i, curFont, paint)
 *                     curFont = font
 *                     drawStart = i
 *                 }
 *             }
 *             if (drawStart != startGlyphs.glyphCount() - 1) {
 *                 drawGlyphs(canvas, startGlyphs, endGlyphs, drawStart, startGlyphs.glyphCount(),
 *                         curFont, paint)
 *             }
 *         }
 *     }
 *
 *     // Draws Glyphs for the same font run.
 *     private fun drawGlyphs(canvas: Canvas, startGlyph: PositionedGlyphs,
 *                            endGlyph: PositionedGlyphs, start: Int, end: Int, font: Font,
 *                            paint: Paint) {
 *         var cacheIndex = 0
 *         for (i in start until end) {
 *             intArrayCache[cacheIndex] = startGlyph.getGlyphId(i)
 *             // The glyph positions are different from start to end since they are shaped
 *             // with different letter spacing. Use linear interpolation for positions
 *             // during animation.
 *             floatArrayCache[cacheIndex * 2] =
 *                     lerp(startGlyph.getGlyphX(i), endGlyph.getGlyphX(i), progress)
 *             floatArrayCache[cacheIndex * 2 + 1] =
 *                     lerp(startGlyph.getGlyphY(i), endGlyph.getGlyphY(i), progress)
 *             if (cacheIndex == CACHE_SIZE) {  // Cached int array is full. Flashing.
 *                 canvas.drawGlyphs(
 *                         intArrayCache, 0, // glyphID array and its starting offset
 *                         floatArrayCache, 0, // position array and its starting offset
 *                         cacheIndex, // glyph count
 *                         font,
 *                         paint
 *                 )
 *                 cacheIndex = 0
 *             }
 *             cacheIndex++
 *         }
 *         if (cacheIndex != 0) {
 *             canvas.drawGlyphs(
 *                     intArrayCache, 0, // glyphID array and its starting offset
 *                     floatArrayCache, 0, // position array and its starting offset
 *                     cacheIndex, // glyph count
 *                     font,
 *                     paint
 *             )
 *         }
 *     }
 *
 *     // Linear Interpolator
 *     private fun lerp(start: Float, end: Float, t: Float) = start * (1f - t) + end * t
 *     private fun lerp(start: Int, end: Int, t: Float) = (start * (1f - t) + end * t).toInt()
 *
 *     // The animation progress.
 *     var progress: Float = 0f
 *         set(value) {
 *             field = value
 *             invalidate()
 *         }
 *
 *     // working copy of paint.
 *     private val paint = Paint()
 *
 *     // Array cache for reducing allocation during drawing.
 *     private var intArrayCache = IntArray(CACHE_SIZE)
 *     private var floatArrayCache = FloatArray(CACHE_SIZE * 2)
 * }
 * </code>
 * </pre>
 * @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
 * @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
 * @see TextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint,
 * GlyphsConsumer)
 */
public class TextShaper {
    private TextShaper() {}

    /**
     * An consumer interface for accepting text shape result.
     */
    public interface GlyphsConsumer {
        /**
         * Accept text shape result.
         *
         * The implementation must not keep reference of paint since it will be mutated for the
         * subsequent styles. Also, for saving heap size, keep only necessary members in the
         * {@link TextPaint} instead of copying {@link TextPaint} object.
         *
         * @param start The start index of the shaped text.
         * @param count The length of the shaped text.
         * @param glyphs The shape result.
         * @param paint The paint to be used for drawing.
         */
        void accept(
                @IntRange(from = 0) int start,
                @IntRange(from = 0) int count,
                @NonNull PositionedGlyphs glyphs,
                @NonNull TextPaint paint);
    }

    /**
     * Shape multi-styled text.
     *
     * @param text a styled text.
     * @param start a start index of shaping target in the text.
     * @param count a length of shaping target in the text.
     * @param dir a text direction.
     * @param paint a paint
     * @param consumer a consumer of the shape result.
     */
    public static void shapeText(
            @NonNull CharSequence text, @IntRange(from = 0) int start,
            @IntRange(from = 0) int count, @NonNull TextDirectionHeuristic dir,
            @NonNull TextPaint paint, @NonNull GlyphsConsumer consumer) {
        MeasuredParagraph mp = MeasuredParagraph.buildForBidi(
                text, start, start + count, dir, null);
        TextLine tl = TextLine.obtain();
        try {
            tl.set(paint, text, start, start + count,
                    mp.getParagraphDir(),
                    mp.getDirections(start, start + count),
                    false /* tabstop is not supported */,
                    null,
                    -1, -1 // ellipsis is not supported.
            );
            tl.shape(consumer);
        } finally {
            TextLine.recycle(tl);
        }
    }

}
+5 −10
Original line number Diff line number Diff line
@@ -18,16 +18,12 @@ package android.text;

import static com.google.common.truth.Truth.assertThat;

import android.graphics.text.PositionedGlyphs;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.List;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class TextShaperTest {
@@ -36,11 +32,10 @@ public class TextShaperTest {
    public void testFontWithPath() {
        TextPaint p = new TextPaint();
        p.setFontFeatureSettings("'wght' 900");
        List<PositionedGlyphs> glyphs = StyledTextShaper.shapeText("a", 0, 1,
                TextDirectionHeuristics.LTR, p);
        assertThat(glyphs.size()).isEqualTo(1);
        TextShaper.shapeText("a", 0, 1, TextDirectionHeuristics.LTR, p,
                (start, end, glyphs, paint) -> {
                // This test only passes if the font of the Latin font is variable font.
        assertThat(glyphs.get(0).getFont(0).getFile()).isNotNull();

                assertThat(glyphs.getFont(0).getFile()).isNotNull();
            });
    }
}
Loading