001 package net.minecraft.client.gui; 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 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.func_85187_a(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.func_85187_a(par1Str, par2, par3, par4, false); 334 } 335 336 public int func_85187_a(String par1Str, int par2, int par3, int par4, boolean par5) 337 { 338 this.resetStyles(); 339 340 if (this.bidiFlag) 341 { 342 par1Str = this.bidiReorder(par1Str); 343 } 344 345 int var6; 346 347 if (par5) 348 { 349 var6 = this.renderString(par1Str, par2 + 1, par3 + 1, par4, true); 350 var6 = Math.max(var6, this.renderString(par1Str, par2, par3, par4, false)); 351 } 352 else 353 { 354 var6 = this.renderString(par1Str, par2, par3, par4, false); 355 } 356 357 return var6; 358 } 359 360 /** 361 * Apply Unicode Bidirectional Algorithm to string and return a new possibly reordered string for visual rendering. 362 */ 363 private String bidiReorder(String par1Str) 364 { 365 if (par1Str != null && Bidi.requiresBidi(par1Str.toCharArray(), 0, par1Str.length())) 366 { 367 Bidi var2 = new Bidi(par1Str, -2); 368 byte[] var3 = new byte[var2.getRunCount()]; 369 String[] var4 = new String[var3.length]; 370 int var7; 371 372 for (int var5 = 0; var5 < var3.length; ++var5) 373 { 374 int var6 = var2.getRunStart(var5); 375 var7 = var2.getRunLimit(var5); 376 int var8 = var2.getRunLevel(var5); 377 String var9 = par1Str.substring(var6, var7); 378 var3[var5] = (byte)var8; 379 var4[var5] = var9; 380 } 381 382 String[] var11 = (String[])var4.clone(); 383 Bidi.reorderVisually(var3, 0, var4, 0, var3.length); 384 StringBuilder var12 = new StringBuilder(); 385 var7 = 0; 386 387 while (var7 < var4.length) 388 { 389 byte var13 = var3[var7]; 390 int var14 = 0; 391 392 while (true) 393 { 394 if (var14 < var11.length) 395 { 396 if (!var11[var14].equals(var4[var7])) 397 { 398 ++var14; 399 continue; 400 } 401 402 var13 = var3[var14]; 403 } 404 405 if ((var13 & 1) == 0) 406 { 407 var12.append(var4[var7]); 408 } 409 else 410 { 411 for (var14 = var4[var7].length() - 1; var14 >= 0; --var14) 412 { 413 char var10 = var4[var7].charAt(var14); 414 415 if (var10 == 40) 416 { 417 var10 = 41; 418 } 419 else if (var10 == 41) 420 { 421 var10 = 40; 422 } 423 424 var12.append(var10); 425 } 426 } 427 428 ++var7; 429 break; 430 } 431 } 432 433 return var12.toString(); 434 } 435 else 436 { 437 return par1Str; 438 } 439 } 440 441 /** 442 * Reset all style flag fields in the class to false; called at the start of string rendering 443 */ 444 private void resetStyles() 445 { 446 this.randomStyle = false; 447 this.boldStyle = false; 448 this.italicStyle = false; 449 this.underlineStyle = false; 450 this.strikethroughStyle = false; 451 } 452 453 /** 454 * Render a single line string at the current (posX,posY) and update posX 455 */ 456 private void renderStringAtPos(String par1Str, boolean par2) 457 { 458 for (int var3 = 0; var3 < par1Str.length(); ++var3) 459 { 460 char var4 = par1Str.charAt(var3); 461 int var5; 462 int var6; 463 464 if (var4 == 167 && var3 + 1 < par1Str.length()) 465 { 466 var5 = "0123456789abcdefklmnor".indexOf(par1Str.toLowerCase().charAt(var3 + 1)); 467 468 if (var5 < 16) 469 { 470 this.randomStyle = false; 471 this.boldStyle = false; 472 this.strikethroughStyle = false; 473 this.underlineStyle = false; 474 this.italicStyle = false; 475 476 if (var5 < 0 || var5 > 15) 477 { 478 var5 = 15; 479 } 480 481 if (par2) 482 { 483 var5 += 16; 484 } 485 486 var6 = this.colorCode[var5]; 487 this.textColor = var6; 488 GL11.glColor4f((float)(var6 >> 16) / 255.0F, (float)(var6 >> 8 & 255) / 255.0F, (float)(var6 & 255) / 255.0F, this.alpha); 489 } 490 else if (var5 == 16) 491 { 492 this.randomStyle = true; 493 } 494 else if (var5 == 17) 495 { 496 this.boldStyle = true; 497 } 498 else if (var5 == 18) 499 { 500 this.strikethroughStyle = true; 501 } 502 else if (var5 == 19) 503 { 504 this.underlineStyle = true; 505 } 506 else if (var5 == 20) 507 { 508 this.italicStyle = true; 509 } 510 else if (var5 == 21) 511 { 512 this.randomStyle = false; 513 this.boldStyle = false; 514 this.strikethroughStyle = false; 515 this.underlineStyle = false; 516 this.italicStyle = false; 517 GL11.glColor4f(this.red, this.blue, this.green, this.alpha); 518 } 519 520 ++var3; 521 } 522 else 523 { 524 var5 = ChatAllowedCharacters.allowedCharacters.indexOf(var4); 525 526 if (this.randomStyle && var5 > 0) 527 { 528 do 529 { 530 var6 = this.fontRandom.nextInt(ChatAllowedCharacters.allowedCharacters.length()); 531 } 532 while (this.charWidth[var5 + 32] != this.charWidth[var6 + 32]); 533 534 var5 = var6; 535 } 536 537 float var9 = this.renderCharAtPos(var5, var4, this.italicStyle); 538 539 if (this.boldStyle) 540 { 541 ++this.posX; 542 this.renderCharAtPos(var5, var4, this.italicStyle); 543 --this.posX; 544 ++var9; 545 } 546 547 Tessellator var7; 548 549 if (this.strikethroughStyle) 550 { 551 var7 = Tessellator.instance; 552 GL11.glDisable(GL11.GL_TEXTURE_2D); 553 var7.startDrawingQuads(); 554 var7.addVertex((double)this.posX, (double)(this.posY + (float)(this.FONT_HEIGHT / 2)), 0.0D); 555 var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)(this.FONT_HEIGHT / 2)), 0.0D); 556 var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)(this.FONT_HEIGHT / 2) - 1.0F), 0.0D); 557 var7.addVertex((double)this.posX, (double)(this.posY + (float)(this.FONT_HEIGHT / 2) - 1.0F), 0.0D); 558 var7.draw(); 559 GL11.glEnable(GL11.GL_TEXTURE_2D); 560 } 561 562 if (this.underlineStyle) 563 { 564 var7 = Tessellator.instance; 565 GL11.glDisable(GL11.GL_TEXTURE_2D); 566 var7.startDrawingQuads(); 567 int var8 = this.underlineStyle ? -1 : 0; 568 var7.addVertex((double)(this.posX + (float)var8), (double)(this.posY + (float)this.FONT_HEIGHT), 0.0D); 569 var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)this.FONT_HEIGHT), 0.0D); 570 var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)this.FONT_HEIGHT - 1.0F), 0.0D); 571 var7.addVertex((double)(this.posX + (float)var8), (double)(this.posY + (float)this.FONT_HEIGHT - 1.0F), 0.0D); 572 var7.draw(); 573 GL11.glEnable(GL11.GL_TEXTURE_2D); 574 } 575 576 this.posX += (float)((int)var9); 577 } 578 } 579 } 580 581 /** 582 * Render string either left or right aligned depending on bidiFlag 583 */ 584 private int renderStringAligned(String par1Str, int par2, int par3, int par4, int par5, boolean par6) 585 { 586 if (this.bidiFlag) 587 { 588 par1Str = this.bidiReorder(par1Str); 589 int var7 = this.getStringWidth(par1Str); 590 par2 = par2 + par4 - var7; 591 } 592 593 return this.renderString(par1Str, par2, par3, par5, par6); 594 } 595 596 /** 597 * Render single line string by setting GL color, current (posX,posY), and calling renderStringAtPos() 598 */ 599 private int renderString(String par1Str, int par2, int par3, int par4, boolean par5) 600 { 601 if (par1Str == null) 602 { 603 return 0; 604 } 605 else 606 { 607 this.boundTextureName = 0; 608 609 if ((par4 & -67108864) == 0) 610 { 611 par4 |= -16777216; 612 } 613 614 if (par5) 615 { 616 par4 = (par4 & 16579836) >> 2 | par4 & -16777216; 617 } 618 619 this.red = (float)(par4 >> 16 & 255) / 255.0F; 620 this.blue = (float)(par4 >> 8 & 255) / 255.0F; 621 this.green = (float)(par4 & 255) / 255.0F; 622 this.alpha = (float)(par4 >> 24 & 255) / 255.0F; 623 GL11.glColor4f(this.red, this.blue, this.green, this.alpha); 624 this.posX = (float)par2; 625 this.posY = (float)par3; 626 this.renderStringAtPos(par1Str, par5); 627 return (int)this.posX; 628 } 629 } 630 631 /** 632 * Returns the width of this string. Equivalent of FontMetrics.stringWidth(String s). 633 */ 634 public int getStringWidth(String par1Str) 635 { 636 if (par1Str == null) 637 { 638 return 0; 639 } 640 else 641 { 642 int var2 = 0; 643 boolean var3 = false; 644 645 for (int var4 = 0; var4 < par1Str.length(); ++var4) 646 { 647 char var5 = par1Str.charAt(var4); 648 int var6 = this.getCharWidth(var5); 649 650 if (var6 < 0 && var4 < par1Str.length() - 1) 651 { 652 ++var4; 653 var5 = par1Str.charAt(var4); 654 655 if (var5 != 108 && var5 != 76) 656 { 657 if (var5 == 114 || var5 == 82) 658 { 659 var3 = false; 660 } 661 } 662 else 663 { 664 var3 = true; 665 } 666 667 var6 = 0; 668 } 669 670 var2 += var6; 671 672 if (var3) 673 { 674 ++var2; 675 } 676 } 677 678 return var2; 679 } 680 } 681 682 /** 683 * Returns the width of this character as rendered. 684 */ 685 public int getCharWidth(char par1) 686 { 687 if (par1 == 167) 688 { 689 return -1; 690 } 691 else if (par1 == 32) 692 { 693 return 4; 694 } 695 else 696 { 697 int var2 = ChatAllowedCharacters.allowedCharacters.indexOf(par1); 698 699 if (var2 >= 0 && !this.unicodeFlag) 700 { 701 return this.charWidth[var2 + 32]; 702 } 703 else if (this.glyphWidth[par1] != 0) 704 { 705 int var3 = this.glyphWidth[par1] >>> 4; 706 int var4 = this.glyphWidth[par1] & 15; 707 708 if (var4 > 7) 709 { 710 var4 = 15; 711 var3 = 0; 712 } 713 714 ++var4; 715 return (var4 - var3) / 2 + 1; 716 } 717 else 718 { 719 return 0; 720 } 721 } 722 } 723 724 /** 725 * Trims a string to fit a specified Width. 726 */ 727 public String trimStringToWidth(String par1Str, int par2) 728 { 729 return this.trimStringToWidth(par1Str, par2, false); 730 } 731 732 /** 733 * Trims a string to a specified width, and will reverse it if par3 is set. 734 */ 735 public String trimStringToWidth(String par1Str, int par2, boolean par3) 736 { 737 StringBuilder var4 = new StringBuilder(); 738 int var5 = 0; 739 int var6 = par3 ? par1Str.length() - 1 : 0; 740 int var7 = par3 ? -1 : 1; 741 boolean var8 = false; 742 boolean var9 = false; 743 744 for (int var10 = var6; var10 >= 0 && var10 < par1Str.length() && var5 < par2; var10 += var7) 745 { 746 char var11 = par1Str.charAt(var10); 747 int var12 = this.getCharWidth(var11); 748 749 if (var8) 750 { 751 var8 = false; 752 753 if (var11 != 108 && var11 != 76) 754 { 755 if (var11 == 114 || var11 == 82) 756 { 757 var9 = false; 758 } 759 } 760 else 761 { 762 var9 = true; 763 } 764 } 765 else if (var12 < 0) 766 { 767 var8 = true; 768 } 769 else 770 { 771 var5 += var12; 772 773 if (var9) 774 { 775 ++var5; 776 } 777 } 778 779 if (var5 > par2) 780 { 781 break; 782 } 783 784 if (par3) 785 { 786 var4.insert(0, var11); 787 } 788 else 789 { 790 var4.append(var11); 791 } 792 } 793 794 return var4.toString(); 795 } 796 797 /** 798 * Remove all newline characters from the end of the string 799 */ 800 private String trimStringNewline(String par1Str) 801 { 802 while (par1Str != null && par1Str.endsWith("\n")) 803 { 804 par1Str = par1Str.substring(0, par1Str.length() - 1); 805 } 806 807 return par1Str; 808 } 809 810 /** 811 * Splits and draws a String with wordwrap (maximum length is parameter k) 812 */ 813 public void drawSplitString(String par1Str, int par2, int par3, int par4, int par5) 814 { 815 this.resetStyles(); 816 this.textColor = par5; 817 par1Str = this.trimStringNewline(par1Str); 818 this.renderSplitString(par1Str, par2, par3, par4, false); 819 } 820 821 /** 822 * Perform actual work of rendering a multi-line string with wordwrap and with darker drop shadow color if flag is 823 * set 824 */ 825 private void renderSplitString(String par1Str, int par2, int par3, int par4, boolean par5) 826 { 827 List var6 = this.listFormattedStringToWidth(par1Str, par4); 828 829 for (Iterator var7 = var6.iterator(); var7.hasNext(); par3 += this.FONT_HEIGHT) 830 { 831 String var8 = (String)var7.next(); 832 this.renderStringAligned(var8, par2, par3, par4, this.textColor, par5); 833 } 834 } 835 836 /** 837 * Returns the width of the wordwrapped String (maximum length is parameter k) 838 */ 839 public int splitStringWidth(String par1Str, int par2) 840 { 841 return this.FONT_HEIGHT * this.listFormattedStringToWidth(par1Str, par2).size(); 842 } 843 844 /** 845 * Set unicodeFlag controlling whether strings should be rendered with Unicode fonts instead of the default.png 846 * font. 847 */ 848 public void setUnicodeFlag(boolean par1) 849 { 850 this.unicodeFlag = par1; 851 } 852 853 /** 854 * Get unicodeFlag controlling whether strings should be rendered with Unicode fonts instead of the default.png 855 * font. 856 */ 857 public boolean getUnicodeFlag() 858 { 859 return this.unicodeFlag; 860 } 861 862 /** 863 * Set bidiFlag to control if the Unicode Bidirectional Algorithm should be run before rendering any string. 864 */ 865 public void setBidiFlag(boolean par1) 866 { 867 this.bidiFlag = par1; 868 } 869 870 /** 871 * Breaks a string into a list of pieces that will fit a specified width. 872 */ 873 public List listFormattedStringToWidth(String par1Str, int par2) 874 { 875 return Arrays.asList(this.wrapFormattedStringToWidth(par1Str, par2).split("\n")); 876 } 877 878 /** 879 * Inserts newline and formatting into a string to wrap it within the specified width. 880 */ 881 String wrapFormattedStringToWidth(String par1Str, int par2) 882 { 883 int var3 = this.sizeStringToWidth(par1Str, par2); 884 885 if (par1Str.length() <= var3) 886 { 887 return par1Str; 888 } 889 else 890 { 891 String var4 = par1Str.substring(0, var3); 892 char var5 = par1Str.charAt(var3); 893 boolean var6 = var5 == 32 || var5 == 10; 894 String var7 = getFormatFromString(var4) + par1Str.substring(var3 + (var6 ? 1 : 0)); 895 return var4 + "\n" + this.wrapFormattedStringToWidth(var7, par2); 896 } 897 } 898 899 /** 900 * Determines how many characters from the string will fit into the specified width. 901 */ 902 private int sizeStringToWidth(String par1Str, int par2) 903 { 904 int var3 = par1Str.length(); 905 int var4 = 0; 906 int var5 = 0; 907 int var6 = -1; 908 909 for (boolean var7 = false; var5 < var3; ++var5) 910 { 911 char var8 = par1Str.charAt(var5); 912 913 switch (var8) 914 { 915 case 10: 916 --var5; 917 break; 918 case 167: 919 if (var5 < var3 - 1) 920 { 921 ++var5; 922 char var9 = par1Str.charAt(var5); 923 924 if (var9 != 108 && var9 != 76) 925 { 926 if (var9 == 114 || var9 == 82) 927 { 928 var7 = false; 929 } 930 } 931 else 932 { 933 var7 = true; 934 } 935 } 936 937 break; 938 case 32: 939 var6 = var5; 940 default: 941 var4 += this.getCharWidth(var8); 942 943 if (var7) 944 { 945 ++var4; 946 } 947 } 948 949 if (var8 == 10) 950 { 951 ++var5; 952 var6 = var5; 953 break; 954 } 955 956 if (var4 > par2) 957 { 958 break; 959 } 960 } 961 962 return var5 != var3 && var6 != -1 && var6 < var5 ? var6 : var5; 963 } 964 965 /** 966 * Checks if the char code is a hexadecimal character, used to set colour. 967 */ 968 private static boolean isFormatColor(char par0) 969 { 970 return par0 >= 48 && par0 <= 57 || par0 >= 97 && par0 <= 102 || par0 >= 65 && par0 <= 70; 971 } 972 973 /** 974 * Checks if the char code is O-K...lLrRk-o... used to set special formatting. 975 */ 976 private static boolean isFormatSpecial(char par0) 977 { 978 return par0 >= 107 && par0 <= 111 || par0 >= 75 && par0 <= 79 || par0 == 114 || par0 == 82; 979 } 980 981 /** 982 * Digests a string for nonprinting formatting characters then returns a string containing only that formatting. 983 */ 984 private static String getFormatFromString(String par0Str) 985 { 986 String var1 = ""; 987 int var2 = -1; 988 int var3 = par0Str.length(); 989 990 while ((var2 = par0Str.indexOf(167, var2 + 1)) != -1) 991 { 992 if (var2 < var3 - 1) 993 { 994 char var4 = par0Str.charAt(var2 + 1); 995 996 if (isFormatColor(var4)) 997 { 998 var1 = "\u00a7" + var4; 999 } 1000 else if (isFormatSpecial(var4)) 1001 { 1002 var1 = var1 + "\u00a7" + var4; 1003 } 1004 } 1005 } 1006 1007 return var1; 1008 } 1009 1010 /** 1011 * Get bidiFlag that controls if the Unicode Bidirectional Algorithm should be run before rendering any string 1012 */ 1013 public boolean getBidiFlag() 1014 { 1015 return this.bidiFlag; 1016 } 1017 }