    001    package net.minecraft.client.gui;
    002
    003    import cpw.mods.fml.relauncher.Side;
    004    import cpw.mods.fml.relauncher.SideOnly;
    005    import java.awt.image.BufferedImage;
    006    import java.io.IOException;
    007    import java.io.InputStream;
    008    import java.text.Bidi;
    009    import java.util.Arrays;
    010    import java.util.Iterator;
    011    import java.util.List;
    012    import java.util.Random;
    013    import javax.imageio.ImageIO;
    014    import net.minecraft.client.renderer.RenderEngine;
    015    import net.minecraft.client.renderer.Tessellator;
    016    import net.minecraft.client.settings.GameSettings;
    017    import net.minecraft.util.ChatAllowedCharacters;
    018    import org.lwjgl.opengl.GL11;
    019
    020    @SideOnly(Side.CLIENT)
    021    public class FontRenderer
    022    {
    023        /** Array of width of all the characters in default.png */
    024        private int[] charWidth = new int[256];
    025        public int fontTextureName = 0;
    026
    027        /** the height in pixels of default text */
    028        public int FONT_HEIGHT = 9;
    029        public Random fontRandom = new Random();
    030
    031        /**
    032         * Array of the start/end column (in upper/lower nibble) for every glyph in the /font directory.
    033         */
    034        private byte[] glyphWidth = new byte[65536];
    035
    036        /**
    037         * Array of GL texture ids for loaded glyph_XX.png images. Indexed by Unicode block (group of 256 chars).
    038         */
    039        private final int[] glyphTextureName = new int[256];
    040
    041        /**
    042         * Array of RGB triplets defining the 16 standard chat colors followed by 16 darker version of the same colors for
    043         * drop shadows.
    044         */
    045        private int[] colorCode = new int[32];
    046
    047        /**
    048         * The currently bound GL texture ID. Avoids unnecessary glBindTexture() for the same texture if it's already bound.
    049         */
    050        private int boundTextureName;
    051
    052        /** The RenderEngine used to load and setup glyph textures. */
    053        private final RenderEngine renderEngine;
    054
    055        /** Current X coordinate at which to draw the next character. */
    056        private float posX;
    057
    058        /** Current Y coordinate at which to draw the next character. */
    059        private float posY;
    060
    061        /**
    062         * If true, strings should be rendered with Unicode fonts instead of the default.png font
    063         */
    064        private boolean unicodeFlag;
    065
    066        /**
    067         * If true, the Unicode Bidirectional Algorithm should be run before rendering any string.
    068         */
    069        private boolean bidiFlag;
    070
    071        /** Used to specify new red value for the current color. */
    072        private float red;
    073
    074        /** Used to specify new blue value for the current color. */
    075        private float blue;
    076
    077        /** Used to specify new green value for the current color. */
    078        private float green;
    079
    080        /** Used to speify new alpha value for the current color. */
    081        private float alpha;
    082
    083        /** Text color of the currently rendering string. */
    084        private int textColor;
    085
    086        /** Set if the "k" style (random) is active in currently rendering string */
    087        private boolean randomStyle = false;
    088
    089        /** Set if the "l" style (bold) is active in currently rendering string */
    090        private boolean boldStyle = false;
    091
    092        /** Set if the "o" style (italic) is active in currently rendering string */
    093        private boolean italicStyle = false;
    094
    095        /**
    096         * Set if the "n" style (underlined) is active in currently rendering string
    097         */
    098        private boolean underlineStyle = false;
    099
    100        /**
    101         * Set if the "m" style (strikethrough) is active in currently rendering string
    102         */
    103        private boolean strikethroughStyle = false;
    104
    105        FontRenderer()
    106        {
    107            this.renderEngine = null;
    108        }
    109
    110        public FontRenderer(GameSettings par1GameSettings, String par2Str, RenderEngine par3RenderEngine, boolean par4)
    111        {
    112            this.renderEngine = par3RenderEngine;
    113            this.unicodeFlag = par4;
    114            BufferedImage var5;
    115
    116            try
    117            {
    118                var5 = ImageIO.read(RenderEngine.class.getResourceAsStream(par2Str));
    119                InputStream var6 = RenderEngine.class.getResourceAsStream("/font/glyph_sizes.bin");
    120                var6.read(this.glyphWidth);
    121            }
    122            catch (IOException var18)
    123            {
    124                throw new RuntimeException(var18);
    125            }
    126
    127            int var19 = var5.getWidth();
    128            int var7 = var5.getHeight();
    129            int[] var8 = new int[var19 * var7];
    130            var5.getRGB(0, 0, var19, var7, var8, 0, var19);
    131            int var9 = 0;
    132            int var10;
    133            int var11;
    134            int var12;
    135            int var13;
    136            int var15;
    137            int var16;
    138
    139            while (var9 < 256)
    140            {
    141                var10 = var9 % 16;
    142                var11 = var9 / 16;
    143                var12 = 7;
    144
    145                while (true)
    146                {
    147                    if (var12 >= 0)
    148                    {
    149                        var13 = var10 * 8 + var12;
    150                        boolean var14 = true;
    151
    152                        for (var15 = 0; var15 < 8 && var14; ++var15)
    153                        {
    154                            var16 = (var11 * 8 + var15) * var19;
    155                            int var17 = var8[var13 + var16] & 255;
    156
    157                            if (var17 > 0)
    158                            {
    159                                var14 = false;
    160                            }
    161                        }
    162
    163                        if (var14)
    164                        {
    165                            --var12;
    166                            continue;
    167                        }
    168                    }
    169
    170                    if (var9 == 32)
    171                    {
    172                        var12 = 2;
    173                    }
    174
    175                    this.charWidth[var9] = var12 + 2;
    176                    ++var9;
    177                    break;
    178                }
    179            }
    180
    181            this.fontTextureName = par3RenderEngine.allocateAndSetupTexture(var5);
    182
    183            for (var9 = 0; var9 < 32; ++var9)
    184            {
    185                var10 = (var9 >> 3 & 1) * 85;
    186                var11 = (var9 >> 2 & 1) * 170 + var10;
    187                var12 = (var9 >> 1 & 1) * 170 + var10;
    188                var13 = (var9 >> 0 & 1) * 170 + var10;
    189
    190                if (var9 == 6)
    191                {
    192                    var11 += 85;
    193                }
    194
    195                if (par1GameSettings.anaglyph)
    196                {
    197                    int var20 = (var11 * 30 + var12 * 59 + var13 * 11) / 100;
    198                    var15 = (var11 * 30 + var12 * 70) / 100;
    199                    var16 = (var11 * 30 + var13 * 70) / 100;
    200                    var11 = var20;
    201                    var12 = var15;
    202                    var13 = var16;
    203                }
    204
    205                if (var9 >= 16)
    206                {
    207                    var11 /= 4;
    208                    var12 /= 4;
    209                    var13 /= 4;
    210                }
    211
    212                this.colorCode[var9] = (var11 & 255) << 16 | (var12 & 255) << 8 | var13 & 255;
    213            }
    214        }
    215
    216        /**
    217         * Pick how to render a single character and return the width used.
    218         */
    219        private float renderCharAtPos(int par1, char par2, boolean par3)
    220        {
    221            return par2 == 32 ? 4.0F : (par1 > 0 && !this.unicodeFlag ? this.renderDefaultChar(par1 + 32, par3) : this.renderUnicodeChar(par2, par3));
    222        }
    223
    224        /**
    225         * Render a single character with the default.png font at current (posX,posY) location...
    226         */
    227        private float renderDefaultChar(int par1, boolean par2)
    228        {
    229            float var3 = (float)(par1 % 16 * 8);
    230            float var4 = (float)(par1 / 16 * 8);
    231            float var5 = par2 ? 1.0F : 0.0F;
    232
    233            if (this.boundTextureName != this.fontTextureName)
    234            {
    235                GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.fontTextureName);
    236                this.boundTextureName = this.fontTextureName;
    237            }
    238
    239            float var6 = (float)this.charWidth[par1] - 0.01F;
    240            GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
    241            GL11.glTexCoord2f(var3 / 128.0F, var4 / 128.0F);
    242            GL11.glVertex3f(this.posX + var5, this.posY, 0.0F);
    243            GL11.glTexCoord2f(var3 / 128.0F, (var4 + 7.99F) / 128.0F);
    244            GL11.glVertex3f(this.posX - var5, this.posY + 7.99F, 0.0F);
    245            GL11.glTexCoord2f((var3 + var6) / 128.0F, var4 / 128.0F);
    246            GL11.glVertex3f(this.posX + var6 + var5, this.posY, 0.0F);
    247            GL11.glTexCoord2f((var3 + var6) / 128.0F, (var4 + 7.99F) / 128.0F);
    248            GL11.glVertex3f(this.posX + var6 - var5, this.posY + 7.99F, 0.0F);
    249            GL11.glEnd();
    250            return (float)this.charWidth[par1];
    251        }
    252
    253        /**
    254         * Load one of the /font/glyph_XX.png into a new GL texture and store the texture ID in glyphTextureName array.
    255         */
    256        private void loadGlyphTexture(int par1)
    257        {
    258            String var3 = String.format("/font/glyph_%02X.png", new Object[] {Integer.valueOf(par1)});
    259            BufferedImage var2;
    260
    261            try
    262            {
    263                var2 = ImageIO.read(RenderEngine.class.getResourceAsStream(var3));
    264            }
    265            catch (IOException var5)
    266            {
    267                throw new RuntimeException(var5);
    268            }
    269
    270            this.glyphTextureName[par1] = this.renderEngine.allocateAndSetupTexture(var2);
    271            this.boundTextureName = this.glyphTextureName[par1];
    272        }
    273
    274        /**
    275         * Render a single Unicode character at current (posX,posY) location using one of the /font/glyph_XX.png files...
    276         */
    277        private float renderUnicodeChar(char par1, boolean par2)
    278        {
    279            if (this.glyphWidth[par1] == 0)
    280            {
    281                return 0.0F;
    282            }
    283            else
    284            {
    285                int var3 = par1 / 256;
    286
    287                if (this.glyphTextureName[var3] == 0)
    288                {
    289                    this.loadGlyphTexture(var3);
    290                }
    291
    292                if (this.boundTextureName != this.glyphTextureName[var3])
    293                {
    294                    GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.glyphTextureName[var3]);
    295                    this.boundTextureName = this.glyphTextureName[var3];
    296                }
    297
    298                int var4 = this.glyphWidth[par1] >>> 4;
    299                int var5 = this.glyphWidth[par1] & 15;
    300                float var6 = (float)var4;
    301                float var7 = (float)(var5 + 1);
    302                float var8 = (float)(par1 % 16 * 16) + var6;
    303                float var9 = (float)((par1 & 255) / 16 * 16);
    304                float var10 = var7 - var6 - 0.02F;
    305                float var11 = par2 ? 1.0F : 0.0F;
    306                GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
    307                GL11.glTexCoord2f(var8 / 256.0F, var9 / 256.0F);
    308                GL11.glVertex3f(this.posX + var11, this.posY, 0.0F);
    309                GL11.glTexCoord2f(var8 / 256.0F, (var9 + 15.98F) / 256.0F);
    310                GL11.glVertex3f(this.posX - var11, this.posY + 7.99F, 0.0F);
    311                GL11.glTexCoord2f((var8 + var10) / 256.0F, var9 / 256.0F);
    312                GL11.glVertex3f(this.posX + var10 / 2.0F + var11, this.posY, 0.0F);
    313                GL11.glTexCoord2f((var8 + var10) / 256.0F, (var9 + 15.98F) / 256.0F);
    314                GL11.glVertex3f(this.posX + var10 / 2.0F - var11, this.posY + 7.99F, 0.0F);
    315                GL11.glEnd();
    316                return (var7 - var6) / 2.0F + 1.0F;
    317            }
    318        }
    319
    320        /**
    321         * Draws the specified string with a shadow.
    322         */
    323        public int drawStringWithShadow(String par1Str, int par2, int par3, int par4)
    324        {
    325            return this.drawString(par1Str, par2, par3, par4, true);
    326        }
    327
    328        /**
    329         * Draws the specified string.
    330         */
    331        public int drawString(String par1Str, int par2, int par3, int par4)
    332        {
    333            return this.drawString(par1Str, par2, par3, par4, false);
    334        }
    335
    336        /**
    337         * Draws the specified string. Args: string, x, y, color, dropShadow
    338         */
    339        public int drawString(String par1Str, int par2, int par3, int par4, boolean par5)
    340        {
    341            this.resetStyles();
    342
    343            if (this.bidiFlag)
    344            {
    345                par1Str = this.bidiReorder(par1Str);
    346            }
    347
    348            int var6;
    349
    350            if (par5)
    351            {
    352                var6 = this.renderString(par1Str, par2 + 1, par3 + 1, par4, true);
    353                var6 = Math.max(var6, this.renderString(par1Str, par2, par3, par4, false));
    354            }
    355            else
    356            {
    357                var6 = this.renderString(par1Str, par2, par3, par4, false);
    358            }
    359
    360            return var6;
    361        }
    362
    363        /**
    364         * Apply Unicode Bidirectional Algorithm to string and return a new possibly reordered string for visual rendering.
    365         */
    366        private String bidiReorder(String par1Str)
    367        {
    368            if (par1Str != null && Bidi.requiresBidi(par1Str.toCharArray(), 0, par1Str.length()))
    369            {
    370                Bidi var2 = new Bidi(par1Str, -2);
    371                byte[] var3 = new byte[var2.getRunCount()];
    372                String[] var4 = new String[var3.length];
    373                int var7;
    374
    375                for (int var5 = 0; var5 < var3.length; ++var5)
    376                {
    377                    int var6 = var2.getRunStart(var5);
    378                    var7 = var2.getRunLimit(var5);
    379                    int var8 = var2.getRunLevel(var5);
    380                    String var9 = par1Str.substring(var6, var7);
    381                    var3[var5] = (byte)var8;
    382                    var4[var5] = var9;
    383                }
    384
    385                String[] var11 = (String[])var4.clone();
    386                Bidi.reorderVisually(var3, 0, var4, 0, var3.length);
    387                StringBuilder var12 = new StringBuilder();
    388                var7 = 0;
    389
    390                while (var7 < var4.length)
    391                {
    392                    byte var13 = var3[var7];
    393                    int var14 = 0;
    394
    395                    while (true)
    396                    {
    397                        if (var14 < var11.length)
    398                        {
    399                            if (!var11[var14].equals(var4[var7]))
    400                            {
    401                                ++var14;
    402                                continue;
    403                            }
    404
    405                            var13 = var3[var14];
    406                        }
    407
    408                        if ((var13 & 1) == 0)
    409                        {
    410                            var12.append(var4[var7]);
    411                        }
    412                        else
    413                        {
    414                            for (var14 = var4[var7].length() - 1; var14 >= 0; --var14)
    415                            {
    416                                char var10 = var4[var7].charAt(var14);
    417
    418                                if (var10 == 40)
    419                                {
    420                                    var10 = 41;
    421                                }
    422                                else if (var10 == 41)
    423                                {
    424                                    var10 = 40;
    425                                }
    426
    427                                var12.append(var10);
    428                            }
    429                        }
    430
    431                        ++var7;
    432                        break;
    433                    }
    434                }
    435
    436                return var12.toString();
    437            }
    438            else
    439            {
    440                return par1Str;
    441            }
    442        }
    443
    444        /**
    445         * Reset all style flag fields in the class to false; called at the start of string rendering
    446         */
    447        private void resetStyles()
    448        {
    449            this.randomStyle = false;
    450            this.boldStyle = false;
    451            this.italicStyle = false;
    452            this.underlineStyle = false;
    453            this.strikethroughStyle = false;
    454        }
    455
    456        /**
    457         * Render a single line string at the current (posX,posY) and update posX
    458         */
    459        private void renderStringAtPos(String par1Str, boolean par2)
    460        {
    461            for (int var3 = 0; var3 < par1Str.length(); ++var3)
    462            {
    463                char var4 = par1Str.charAt(var3);
    464                int var5;
    465                int var6;
    466
    467                if (var4 == 167 && var3 + 1 < par1Str.length())
    468                {
    469                    var5 = "0123456789abcdefklmnor".indexOf(par1Str.toLowerCase().charAt(var3 + 1));
    470
    471                    if (var5 < 16)
    472                    {
    473                        this.randomStyle = false;
    474                        this.boldStyle = false;
    475                        this.strikethroughStyle = false;
    476                        this.underlineStyle = false;
    477                        this.italicStyle = false;
    478
    479                        if (var5 < 0 || var5 > 15)
    480                        {
    481                            var5 = 15;
    482                        }
    483
    484                        if (par2)
    485                        {
    486                            var5 += 16;
    487                        }
    488
    489                        var6 = this.colorCode[var5];
    490                        this.textColor = var6;
    491                        GL11.glColor4f((float)(var6 >> 16) / 255.0F, (float)(var6 >> 8 & 255) / 255.0F, (float)(var6 & 255) / 255.0F, this.alpha);
    492                    }
    493                    else if (var5 == 16)
    494                    {
    495                        this.randomStyle = true;
    496                    }
    497                    else if (var5 == 17)
    498                    {
    499                        this.boldStyle = true;
    500                    }
    501                    else if (var5 == 18)
    502                    {
    503                        this.strikethroughStyle = true;
    504                    }
    505                    else if (var5 == 19)
    506                    {
    507                        this.underlineStyle = true;
    508                    }
    509                    else if (var5 == 20)
    510                    {
    511                        this.italicStyle = true;
    512                    }
    513                    else if (var5 == 21)
    514                    {
    515                        this.randomStyle = false;
    516                        this.boldStyle = false;
    517                        this.strikethroughStyle = false;
    518                        this.underlineStyle = false;
    519                        this.italicStyle = false;
    520                        GL11.glColor4f(this.red, this.blue, this.green, this.alpha);
    521                    }
    522
    523                    ++var3;
    524                }
    525                else
    526                {
    527                    var5 = ChatAllowedCharacters.allowedCharacters.indexOf(var4);
    528
    529                    if (this.randomStyle && var5 > 0)
    530                    {
    531                        do
    532                        {
    533                            var6 = this.fontRandom.nextInt(ChatAllowedCharacters.allowedCharacters.length());
    534                        }
    535                        while (this.charWidth[var5 + 32] != this.charWidth[var6 + 32]);
    536
    537                        var5 = var6;
    538                    }
    539
    540                    float var9 = this.renderCharAtPos(var5, var4, this.italicStyle);
    541
    542                    if (this.boldStyle)
    543                    {
    544                        ++this.posX;
    545                        this.renderCharAtPos(var5, var4, this.italicStyle);
    546                        --this.posX;
    547                        ++var9;
    548                    }
    549
    550                    Tessellator var7;
    551
    552                    if (this.strikethroughStyle)
    553                    {
    554                        var7 = Tessellator.instance;
    555                        GL11.glDisable(GL11.GL_TEXTURE_2D);
    556                        var7.startDrawingQuads();
    557                        var7.addVertex((double)this.posX, (double)(this.posY + (float)(this.FONT_HEIGHT / 2)), 0.0D);
    558                        var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)(this.FONT_HEIGHT / 2)), 0.0D);
    559                        var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)(this.FONT_HEIGHT / 2) - 1.0F), 0.0D);
    560                        var7.addVertex((double)this.posX, (double)(this.posY + (float)(this.FONT_HEIGHT / 2) - 1.0F), 0.0D);
    561                        var7.draw();
    562                        GL11.glEnable(GL11.GL_TEXTURE_2D);
    563                    }
    564
    565                    if (this.underlineStyle)
    566                    {
    567                        var7 = Tessellator.instance;
    568                        GL11.glDisable(GL11.GL_TEXTURE_2D);
    569                        var7.startDrawingQuads();
    570                        int var8 = this.underlineStyle ? -1 : 0;
    571                        var7.addVertex((double)(this.posX + (float)var8), (double)(this.posY + (float)this.FONT_HEIGHT), 0.0D);
    572                        var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)this.FONT_HEIGHT), 0.0D);
    573                        var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)this.FONT_HEIGHT - 1.0F), 0.0D);
    574                        var7.addVertex((double)(this.posX + (float)var8), (double)(this.posY + (float)this.FONT_HEIGHT - 1.0F), 0.0D);
    575                        var7.draw();
    576                        GL11.glEnable(GL11.GL_TEXTURE_2D);
    577                    }
    578
    579                    this.posX += (float)((int)var9);
    580                }
    581            }
    582        }
    583
    584        /**
    585         * Render string either left or right aligned depending on bidiFlag
    586         */
    587        private int renderStringAligned(String par1Str, int par2, int par3, int par4, int par5, boolean par6)
    588        {
    589            if (this.bidiFlag)
    590            {
    591                par1Str = this.bidiReorder(par1Str);
    592                int var7 = this.getStringWidth(par1Str);
    593                par2 = par2 + par4 - var7;
    594            }
    595
    596            return this.renderString(par1Str, par2, par3, par5, par6);
    597        }
    598
    599        /**
    600         * Render single line string by setting GL color, current (posX,posY), and calling renderStringAtPos()
    601         */
    602        private int renderString(String par1Str, int par2, int par3, int par4, boolean par5)
    603        {
    604            if (par1Str == null)
    605            {
    606                return 0;
    607            }
    608            else
    609            {
    610                this.boundTextureName = 0;
    611
    612                if ((par4 & -67108864) == 0)
    613                {
    614                    par4 |= -16777216;
    615                }
    616
    617                if (par5)
    618                {
    619                    par4 = (par4 & 16579836) >> 2 | par4 & -16777216;
    620                }
    621
    622                this.red = (float)(par4 >> 16 & 255) / 255.0F;
    623                this.blue = (float)(par4 >> 8 & 255) / 255.0F;
    624                this.green = (float)(par4 & 255) / 255.0F;
    625                this.alpha = (float)(par4 >> 24 & 255) / 255.0F;
    626                GL11.glColor4f(this.red, this.blue, this.green, this.alpha);
    627                this.posX = (float)par2;
    628                this.posY = (float)par3;
    629                this.renderStringAtPos(par1Str, par5);
    630                return (int)this.posX;
    631            }
    632        }
    633
    634        /**
    635         * Returns the width of this string. Equivalent of FontMetrics.stringWidth(String s).
    636         */
    637        public int getStringWidth(String par1Str)
    638        {
    639            if (par1Str == null)
    640            {
    641                return 0;
    642            }
    643            else
    644            {
    645                int var2 = 0;
    646                boolean var3 = false;
    647
    648                for (int var4 = 0; var4 < par1Str.length(); ++var4)
    649                {
    650                    char var5 = par1Str.charAt(var4);
    651                    int var6 = this.getCharWidth(var5);
    652
    653                    if (var6 < 0 && var4 < par1Str.length() - 1)
    654                    {
    655                        ++var4;
    656                        var5 = par1Str.charAt(var4);
    657
    658                        if (var5 != 108 && var5 != 76)
    659                        {
    660                            if (var5 == 114 || var5 == 82)
    661                            {
    662                                var3 = false;
    663                            }
    664                        }
    665                        else
    666                        {
    667                            var3 = true;
    668                        }
    669
    670                        var6 = 0;
    671                    }
    672
    673                    var2 += var6;
    674
    675                    if (var3)
    676                    {
    677                        ++var2;
    678                    }
    679                }
    680
    681                return var2;
    682            }
    683        }
    684
    685        /**
    686         * Returns the width of this character as rendered.
    687         */
    688        public int getCharWidth(char par1)
    689        {
    690            if (par1 == 167)
    691            {
    692                return -1;
    693            }
    694            else if (par1 == 32)
    695            {
    696                return 4;
    697            }
    698            else
    699            {
    700                int var2 = ChatAllowedCharacters.allowedCharacters.indexOf(par1);
    701
    702                if (var2 >= 0 && !this.unicodeFlag)
    703                {
    704                    return this.charWidth[var2 + 32];
    705                }
    706                else if (this.glyphWidth[par1] != 0)
    707                {
    708                    int var3 = this.glyphWidth[par1] >>> 4;
    709                    int var4 = this.glyphWidth[par1] & 15;
    710
    711                    if (var4 > 7)
    712                    {
    713                        var4 = 15;
    714                        var3 = 0;
    715                    }
    716
    717                    ++var4;
    718                    return (var4 - var3) / 2 + 1;
    719                }
    720                else
    721                {
    722                    return 0;
    723                }
    724            }
    725        }
    726
    727        /**
    728         * Trims a string to fit a specified Width.
    729         */
    730        public String trimStringToWidth(String par1Str, int par2)
    731        {
    732            return this.trimStringToWidth(par1Str, par2, false);
    733        }
    734
    735        /**
    736         * Trims a string to a specified width, and will reverse it if par3 is set.
    737         */
    738        public String trimStringToWidth(String par1Str, int par2, boolean par3)
    739        {
    740            StringBuilder var4 = new StringBuilder();
    741            int var5 = 0;
    742            int var6 = par3 ? par1Str.length() - 1 : 0;
    743            int var7 = par3 ? -1 : 1;
    744            boolean var8 = false;
    745            boolean var9 = false;
    746
    747            for (int var10 = var6; var10 >= 0 && var10 < par1Str.length() && var5 < par2; var10 += var7)
    748            {
    749                char var11 = par1Str.charAt(var10);
    750                int var12 = this.getCharWidth(var11);
    751
    752                if (var8)
    753                {
    754                    var8 = false;
    755
    756                    if (var11 != 108 && var11 != 76)
    757                    {
    758                        if (var11 == 114 || var11 == 82)
    759                        {
    760                            var9 = false;
    761                        }
    762                    }
    763                    else
    764                    {
    765                        var9 = true;
    766                    }
    767                }
    768                else if (var12 < 0)
    769                {
    770                    var8 = true;
    771                }
    772                else
    773                {
    774                    var5 += var12;
    775
    776                    if (var9)
    777                    {
    778                        ++var5;
    779                    }
    780                }
    781
    782                if (var5 > par2)
    783                {
    784                    break;
    785                }
    786
    787                if (par3)
    788                {
    789                    var4.insert(0, var11);
    790                }
    791                else
    792                {
    793                    var4.append(var11);
    794                }
    795            }
    796
    797            return var4.toString();
    798        }
    799
    800        /**
    801         * Remove all newline characters from the end of the string
    802         */
    803        private String trimStringNewline(String par1Str)
    804        {
    805            while (par1Str != null && par1Str.endsWith("\n"))
    806            {
    807                par1Str = par1Str.substring(0, par1Str.length() - 1);
    808            }
    809
    810            return par1Str;
    811        }
    812
    813        /**
    814         * Splits and draws a String with wordwrap (maximum length is parameter k)
    815         */
    816        public void drawSplitString(String par1Str, int par2, int par3, int par4, int par5)
    817        {
    818            this.resetStyles();
    819            this.textColor = par5;
    820            par1Str = this.trimStringNewline(par1Str);
    821            this.renderSplitString(par1Str, par2, par3, par4, false);
    822        }
    823
    824        /**
    825         * Perform actual work of rendering a multi-line string with wordwrap and with darker drop shadow color if flag is
    826         * set
    827         */
    828        private void renderSplitString(String par1Str, int par2, int par3, int par4, boolean par5)
    829        {
    830            List var6 = this.listFormattedStringToWidth(par1Str, par4);
    831
    832            for (Iterator var7 = var6.iterator(); var7.hasNext(); par3 += this.FONT_HEIGHT)
    833            {
    834                String var8 = (String)var7.next();
    835                this.renderStringAligned(var8, par2, par3, par4, this.textColor, par5);
    836            }
    837        }
    838
    839        /**
    840         * Returns the width of the wordwrapped String (maximum length is parameter k)
    841         */
    842        public int splitStringWidth(String par1Str, int par2)
    843        {
    844            return this.FONT_HEIGHT * this.listFormattedStringToWidth(par1Str, par2).size();
    845        }
    846
    847        /**
    848         * Set unicodeFlag controlling whether strings should be rendered with Unicode fonts instead of the default.png
    849         * font.
    850         */
    851        public void setUnicodeFlag(boolean par1)
    852        {
    853            this.unicodeFlag = par1;
    854        }
    855
    856        /**
    857         * Get unicodeFlag controlling whether strings should be rendered with Unicode fonts instead of the default.png
    858         * font.
    859         */
    860        public boolean getUnicodeFlag()
    861        {
    862            return this.unicodeFlag;
    863        }
    864
    865        /**
    866         * Set bidiFlag to control if the Unicode Bidirectional Algorithm should be run before rendering any string.
    867         */
    868        public void setBidiFlag(boolean par1)
    869        {
    870            this.bidiFlag = par1;
    871        }
    872
    873        /**
    874         * Breaks a string into a list of pieces that will fit a specified width.
    875         */
    876        public List listFormattedStringToWidth(String par1Str, int par2)
    877        {
    878            return Arrays.asList(this.wrapFormattedStringToWidth(par1Str, par2).split("\n"));
    879        }
    880
    881        /**
    882         * Inserts newline and formatting into a string to wrap it within the specified width.
    883         */
    884        String wrapFormattedStringToWidth(String par1Str, int par2)
    885        {
    886            int var3 = this.sizeStringToWidth(par1Str, par2);
    887
    888            if (par1Str.length() <= var3)
    889            {
    890                return par1Str;
    891            }
    892            else
    893            {
    894                String var4 = par1Str.substring(0, var3);
    895                char var5 = par1Str.charAt(var3);
    896                boolean var6 = var5 == 32 || var5 == 10;
    897                String var7 = getFormatFromString(var4) + par1Str.substring(var3 + (var6 ? 1 : 0));
    898                return var4 + "\n" + this.wrapFormattedStringToWidth(var7, par2);
    899            }
    900        }
    901
    902        /**
    903         * Determines how many characters from the string will fit into the specified width.
    904         */
    905        private int sizeStringToWidth(String par1Str, int par2)
    906        {
    907            int var3 = par1Str.length();
    908            int var4 = 0;
    909            int var5 = 0;
    910            int var6 = -1;
    911
    912            for (boolean var7 = false; var5 < var3; ++var5)
    913            {
    914                char var8 = par1Str.charAt(var5);
    915
    916                switch (var8)
    917                {
    918                    case 10:
    919                        --var5;
    920                        break;
    921                    case 167:
    922                        if (var5 < var3 - 1)
    923                        {
    924                            ++var5;
    925                            char var9 = par1Str.charAt(var5);
    926
    927                            if (var9 != 108 && var9 != 76)
    928                            {
    929                                if (var9 == 114 || var9 == 82)
    930                                {
    931                                    var7 = false;
    932                                }
    933                            }
    934                            else
    935                            {
    936                                var7 = true;
    937                            }
    938                        }
    939
    940                        break;
    941                    case 32:
    942                        var6 = var5;
    943                    default:
    944                        var4 += this.getCharWidth(var8);
    945
    946                        if (var7)
    947                        {
    948                            ++var4;
    949                        }
    950                }
    951
    952                if (var8 == 10)
    953                {
    954                    ++var5;
    955                    var6 = var5;
    956                    break;
    957                }
    958
    959                if (var4 > par2)
    960                {
    961                    break;
    962                }
    963            }
    964
    965            return var5 != var3 && var6 != -1 && var6 < var5 ? var6 : var5;
    966        }
    967
    968        /**
    969         * Checks if the char code is a hexadecimal character, used to set colour.
    970         */
    971        private static boolean isFormatColor(char par0)
    972        {
    973            return par0 >= 48 && par0 <= 57 || par0 >= 97 && par0 <= 102 || par0 >= 65 && par0 <= 70;
    974        }
    975
    976        /**
    977         * Checks if the char code is O-K...lLrRk-o... used to set special formatting.
    978         */
    979        private static boolean isFormatSpecial(char par0)
    980        {
    981            return par0 >= 107 && par0 <= 111 || par0 >= 75 && par0 <= 79 || par0 == 114 || par0 == 82;
    982        }
    983
    984        /**
    985         * Digests a string for nonprinting formatting characters then returns a string containing only that formatting.
    986         */
    987        private static String getFormatFromString(String par0Str)
    988        {
    989            String var1 = "";
    990            int var2 = -1;
    991            int var3 = par0Str.length();
    992
    993            while ((var2 = par0Str.indexOf(167, var2 + 1)) != -1)
    994            {
    995                if (var2 < var3 - 1)
    996                {
    997                    char var4 = par0Str.charAt(var2 + 1);
    998
    999                    if (isFormatColor(var4))
    1000                    {
    1001                        var1 = "\u00a7" + var4;
    1002                    }
    1003                    else if (isFormatSpecial(var4))
    1004                    {
    1005                        var1 = var1 + "\u00a7" + var4;
    1006                    }
    1007                }
    1008            }
    1009
    1010            return var1;
    1011        }
    1012
    1013        /**
    1014         * Get bidiFlag that controls if the Unicode Bidirectional Algorithm should be run before rendering any string
    1015         */
    1016        public boolean getBidiFlag()
    1017        {
    1018            return this.bidiFlag;
    1019        }
    1020    }