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 167: 915 if (var5 < var3 - 1) 916 { 917 ++var5; 918 char var9 = par1Str.charAt(var5); 919 920 if (var9 != 108 && var9 != 76) 921 { 922 if (var9 == 114 || var9 == 82) 923 { 924 var7 = false; 925 } 926 } 927 else 928 { 929 var7 = true; 930 } 931 } 932 933 break; 934 case 32: 935 var6 = var5; 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 }