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