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