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 || isFormatColor(var9)) 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 }