BidiRenderer.javaAPI DocAndroid 5.1 API12578Thu Mar 12 22:22:44 GMT


public class BidiRenderer extends Object
Render the text by breaking it into various scripts and using the right font for each script. Can be used to measure the text without actually drawing it.

Fields Summary
private final Graphics2D
private final Paint_Delegate
private char[]
private List
private RectF
private float
Constructors Summary
public BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text)

graphics May be null.
paint The Paint to use to get the fonts. Should not be null.
text Unidirectional text. Should not be null.

        assert (paint != null);
        mGraphics = graphics;
        mPaint = paint;
        mText = text;
        mFonts = new ArrayList<Font>(paint.getFonts().size());
        for (FontInfo fontInfo : paint.getFonts()) {
            if (fontInfo == null) {
        mBounds = new RectF();
Methods Summary
private static RectFawtRectToAndroidRect(java.awt.geom.Rectangle2D awtRec, float offsetX, float offsetY)

        float left = (float) awtRec.getX();
        float top = (float) awtRec.getY();
        float right = (float) (left + awtRec.getWidth());
        float bottom = (float) (top + awtRec.getHeight());
        RectF androidRect = new RectF(left, top, right, bottom);
        androidRect.offset(offsetX, offsetY);
        return androidRect;
private static intgetIcuFlags(int bidiFlag)

        switch (bidiFlag) {
            case Paint.BIDI_LTR:
            case Paint.BIDI_FORCE_LTR:
                return Bidi.DIRECTION_LEFT_TO_RIGHT;
            case Paint.BIDI_RTL:
            case Paint.BIDI_FORCE_RTL:
                return Bidi.DIRECTION_RIGHT_TO_LEFT;
            case Paint.BIDI_DEFAULT_LTR:
                return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
            case Paint.BIDI_DEFAULT_RTL:
                return Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT;
                assert false;
                return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
static java.util.ListgetScriptRuns(char[] text, int start, int limit, boolean isRtl, java.util.List fonts)

        LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>();

        int count = limit - start;
        UScriptRun uScriptRun = new UScriptRun(text, start, count);
        while ( {
            int scriptStart = uScriptRun.getScriptStart();
            int scriptLimit = uScriptRun.getScriptLimit();
            ScriptRun run = new ScriptRun(scriptStart, scriptLimit, isRtl);
            run.scriptCode = uScriptRun.getScriptCode();
            setScriptFont(text, run, fonts);

        return scriptRuns;
private static voidlogFontWarning()

                "Some fonts could not be loaded. The rendering may not be perfect. " +
                        "Try running the IDE with JRE 7.", null, null);
private voidrender(int start, int limit, java.awt.Font font, int flag, float[] advances, int advancesIndex, boolean draw)
Renders the text to the right of the bounds with the given font.

font The font to render the text with.

        FontRenderContext frc;
        if (mGraphics != null) {
            frc = mGraphics.getFontRenderContext();
        } else {
            frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
            // Metrics obtained this way don't have anti-aliasing set. So,
            // we create a new FontRenderContext with anti-aliasing set.
            frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), frc.usesFractionalMetrics());
        GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
        int ng = gv.getNumGlyphs();
        int[] ci = gv.getGlyphCharIndices(0, ng, null);
        if (advances != null) {
            for (int i = 0; i < ng; i++) {
                int adv_idx = advancesIndex + ci[i];
                advances[adv_idx] += gv.getGlyphMetrics(i).getAdvanceX();
        if (draw && mGraphics != null) {
            mGraphics.drawGlyphVector(gv, mBounds.right, mBaseline);

        // Update the bounds.
        Rectangle2D awtBounds = gv.getLogicalBounds();
        RectF bounds = awtRectToAndroidRect(awtBounds, mBounds.right, mBaseline);
        // If the width of the bounds is zero, no text had been drawn earlier. Hence, use the
        // coordinates from the bounds as an offset.
        if (Math.abs(mBounds.right - mBounds.left) == 0) {
            mBounds = bounds;
        } else {
private voidrenderScript(int start, int limit, java.awt.Font preferredFont, int flag, float[] advances, int advancesIndex, boolean draw)
Render a script run to the right of the bounds passed. Use the preferred font to render as much as possible. This also implements a fallback mechanism to render characters that cannot be drawn using the preferred font.

        if (mFonts.size() == 0 || preferredFont == null) {

        while (start < limit) {
            boolean foundFont = false;
            int canDisplayUpTo = preferredFont.canDisplayUpTo(mText, start, limit);
            if (canDisplayUpTo == -1) {
                // We can draw all characters in the text.
                render(start, limit, preferredFont, flag, advances, advancesIndex, draw);
            if (canDisplayUpTo > start) {
                // We can draw something.
                render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex, draw);
                advancesIndex += canDisplayUpTo - start;
                start = canDisplayUpTo;

            // The current character cannot be drawn with the preferred font. Cycle through all the
            // fonts to check which one can draw it.
            int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
            for (Font font : mFonts) {
                if (font == null) {
                canDisplayUpTo = font.canDisplayUpTo(mText, start, start + charCount);
                if (canDisplayUpTo == -1) {
                    render(start, start+charCount, font, flag, advances, advancesIndex, draw);
                    start += charCount;
                    advancesIndex += charCount;
                    foundFont = true;
            if (!foundFont) {
                // No font can display this char. Use the preferred font. The char will most
                // probably appear as a box or a blank space. We could, probably, use some
                // heuristics and break the character into the base character and diacritics and
                // then draw it, but it's probably not worth the effort.
                render(start, start + charCount, preferredFont, flag, advances, advancesIndex,
                start += charCount;
                advancesIndex += charCount;
public RectFrenderText(int start, int limit, int bidiFlags, float[] advances, int advancesIndex, boolean draw)
Perform Bidi Analysis on the text and then render it.

To skip the analysis and render unidirectional text, see {@link #renderText(int, int, boolean, float[], int, boolean)}

        Bidi bidi = new Bidi(mText, start, null, 0, limit - start, getIcuFlags(bidiFlags));
        for (int i = 0; i < bidi.countRuns(); i++) {
            BidiRun visualRun = bidi.getVisualRun(i);
            boolean isRtl = visualRun.getDirection() == Bidi.RTL;
            renderText(visualRun.getStart(), visualRun.getLimit(), isRtl, advances,
                    advancesIndex, draw);
        return mBounds;
public RectFrenderText(int start, int limit, boolean isRtl, float[] advances, int advancesIndex, boolean draw)
Render unidirectional text.

This method can also be used to measure the width of the text without actually drawing it.

start index of the first character
limit index of the first character that should not be rendered.
isRtl is the text right-to-left
advances If not null, then advances for each character to be rendered are returned here.
advancesIndex index into advances from where the advances need to be filled.
draw If true and {@code graphics} is not null, draw the rendered text on the graphics at the given co-ordinates
A rectangle specifying the bounds of the text drawn.

        // We break the text into scripts and then select font based on it and then render each of
        // the script runs.
        for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) {
            flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
            renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw);
            advancesIndex += run.limit - run.start;
        return mBounds;
public x, float y)

x The x-coordinate of the left edge of where the text should be drawn on the given graphics.
y The y-coordinate at which to draw the text on the given mGraphics.

        mBounds = new RectF(x, y, x, y);
        mBaseline = y;
        return this;
private static voidsetScriptFont(char[] text,$ScriptRun run, java.util.List fonts)

        for (Font font : fonts) {
            if (font == null) {
            if (font.canDisplayUpTo(text, run.start, run.limit) == -1) {
                run.font = font;
        run.font = fonts.get(0);