001/* 002 * $Id: ByteBuffer.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045import java.io.IOException; 046import java.io.OutputStream; 047import java.io.UnsupportedEncodingException; 048import java.text.DecimalFormat; 049import java.text.DecimalFormatSymbols; 050import java.util.Locale; 051import com.itextpdf.text.error_messages.MessageLocalization; 052 053import com.itextpdf.text.DocWriter; 054 055/** 056 * Acts like a <CODE>StringBuffer</CODE> but works with <CODE>byte</CODE> arrays. 057 * Floating point is converted to a format suitable to the PDF. 058 * @author Paulo Soares 059 */ 060 061public class ByteBuffer extends OutputStream { 062 /** The count of bytes in the buffer. */ 063 protected int count; 064 065 /** The buffer where the bytes are stored. */ 066 protected byte buf[]; 067 068 private static int byteCacheSize = 0; 069 070 private static byte[][] byteCache = new byte[byteCacheSize][]; 071 public static final byte ZERO = (byte)'0'; 072 private static final char[] chars = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; 073 private static final byte[] bytes = new byte[] {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102}; 074 /** 075 * If <CODE>true</CODE> always output floating point numbers with 6 decimal digits. 076 * If <CODE>false</CODE> uses the faster, although less precise, representation. 077 */ 078 public static boolean HIGH_PRECISION = false; 079 private static final DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US); 080 081 /** Creates new ByteBuffer with capacity 128 */ 082 public ByteBuffer() { 083 this(128); 084 } 085 086 /** 087 * Creates a byte buffer with a certain capacity. 088 * @param size the initial capacity 089 */ 090 public ByteBuffer(int size) { 091 if (size < 1) 092 size = 128; 093 buf = new byte[size]; 094 } 095 096 /** 097 * Sets the cache size. 098 * <P> 099 * This can only be used to increment the size. 100 * If the size that is passed through is smaller than the current size, nothing happens. 101 * 102 * @param size the size of the cache 103 */ 104 105 public static void setCacheSize(int size) { 106 if (size > 3276700) size = 3276700; 107 if (size <= byteCacheSize) return; 108 byte[][] tmpCache = new byte[size][]; 109 System.arraycopy(byteCache, 0, tmpCache, 0, byteCacheSize); 110 byteCache = tmpCache; 111 byteCacheSize = size; 112 } 113 114 /** 115 * You can fill the cache in advance if you want to. 116 * 117 * @param decimals 118 */ 119 120 public static void fillCache(int decimals) { 121 int step = 1; 122 switch(decimals) { 123 case 0: 124 step = 100; 125 break; 126 case 1: 127 step = 10; 128 break; 129 } 130 for (int i = 1; i < byteCacheSize; i += step) { 131 if (byteCache[i] != null) continue; 132 byteCache[i] = convertToBytes(i); 133 } 134 } 135 136 /** 137 * Converts an double (multiplied by 100 and cast to an int) into an array of bytes. 138 * 139 * @param i the int 140 * @return a byte array 141 */ 142 143 private static byte[] convertToBytes(int i) { 144 int size = (int)Math.floor(Math.log(i) / Math.log(10)); 145 if (i % 100 != 0) { 146 size += 2; 147 } 148 if (i % 10 != 0) { 149 size++; 150 } 151 if (i < 100) { 152 size++; 153 if (i < 10) { 154 size++; 155 } 156 } 157 size--; 158 byte[] cache = new byte[size]; 159 size --; 160 if (i < 100) { 161 cache[0] = (byte)'0'; 162 } 163 if (i % 10 != 0) { 164 cache[size--] = bytes[i % 10]; 165 } 166 if (i % 100 != 0) { 167 cache[size--] = bytes[(i / 10) % 10]; 168 cache[size--] = (byte)'.'; 169 } 170 size = (int)Math.floor(Math.log(i) / Math.log(10)) - 1; 171 int add = 0; 172 while (add < size) { 173 cache[add] = bytes[(i / (int)Math.pow(10, size - add + 1)) % 10]; 174 add++; 175 } 176 return cache; 177 } 178 179 /** 180 * Appends an <CODE>int</CODE>. The size of the array will grow by one. 181 * @param b the int to be appended 182 * @return a reference to this <CODE>ByteBuffer</CODE> object 183 */ 184 public ByteBuffer append_i(int b) { 185 int newcount = count + 1; 186 if (newcount > buf.length) { 187 byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)]; 188 System.arraycopy(buf, 0, newbuf, 0, count); 189 buf = newbuf; 190 } 191 buf[count] = (byte)b; 192 count = newcount; 193 return this; 194 } 195 196 /** 197 * Appends the subarray of the <CODE>byte</CODE> array. The buffer will grow by 198 * <CODE>len</CODE> bytes. 199 * @param b the array to be appended 200 * @param off the offset to the start of the array 201 * @param len the length of bytes to append 202 * @return a reference to this <CODE>ByteBuffer</CODE> object 203 */ 204 public ByteBuffer append(byte b[], int off, int len) { 205 if ((off < 0) || (off > b.length) || (len < 0) || 206 ((off + len) > b.length) || ((off + len) < 0) || len == 0) 207 return this; 208 int newcount = count + len; 209 if (newcount > buf.length) { 210 byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)]; 211 System.arraycopy(buf, 0, newbuf, 0, count); 212 buf = newbuf; 213 } 214 System.arraycopy(b, off, buf, count, len); 215 count = newcount; 216 return this; 217 } 218 219 /** 220 * Appends an array of bytes. 221 * @param b the array to be appended 222 * @return a reference to this <CODE>ByteBuffer</CODE> object 223 */ 224 public ByteBuffer append(byte b[]) { 225 return append(b, 0, b.length); 226 } 227 228 /** 229 * Appends a <CODE>String</CODE> to the buffer. The <CODE>String</CODE> is 230 * converted according to the encoding ISO-8859-1. 231 * @param str the <CODE>String</CODE> to be appended 232 * @return a reference to this <CODE>ByteBuffer</CODE> object 233 */ 234 public ByteBuffer append(String str) { 235 if (str != null) 236 return append(DocWriter.getISOBytes(str)); 237 return this; 238 } 239 240 /** 241 * Appends a <CODE>char</CODE> to the buffer. The <CODE>char</CODE> is 242 * converted according to the encoding ISO-8859-1. 243 * @param c the <CODE>char</CODE> to be appended 244 * @return a reference to this <CODE>ByteBuffer</CODE> object 245 */ 246 public ByteBuffer append(char c) { 247 return append_i(c); 248 } 249 250 /** 251 * Appends another <CODE>ByteBuffer</CODE> to this buffer. 252 * @param buf the <CODE>ByteBuffer</CODE> to be appended 253 * @return a reference to this <CODE>ByteBuffer</CODE> object 254 */ 255 public ByteBuffer append(ByteBuffer buf) { 256 return append(buf.buf, 0, buf.count); 257 } 258 259 /** 260 * Appends the string representation of an <CODE>int</CODE>. 261 * @param i the <CODE>int</CODE> to be appended 262 * @return a reference to this <CODE>ByteBuffer</CODE> object 263 */ 264 public ByteBuffer append(int i) { 265 return append((double)i); 266 } 267 268 public ByteBuffer append(byte b) { 269 return append_i(b); 270 } 271 272 public ByteBuffer appendHex(byte b) { 273 append(bytes[(b >> 4) & 0x0f]); 274 return append(bytes[b & 0x0f]); 275 } 276 277 /** 278 * Appends a string representation of a <CODE>float</CODE> according 279 * to the Pdf conventions. 280 * @param i the <CODE>float</CODE> to be appended 281 * @return a reference to this <CODE>ByteBuffer</CODE> object 282 */ 283 public ByteBuffer append(float i) { 284 return append((double)i); 285 } 286 287 /** 288 * Appends a string representation of a <CODE>double</CODE> according 289 * to the Pdf conventions. 290 * @param d the <CODE>double</CODE> to be appended 291 * @return a reference to this <CODE>ByteBuffer</CODE> object 292 */ 293 public ByteBuffer append(double d) { 294 append(formatDouble(d, this)); 295 return this; 296 } 297 298 /** 299 * Outputs a <CODE>double</CODE> into a format suitable for the PDF. 300 * @param d a double 301 * @return the <CODE>String</CODE> representation of the <CODE>double</CODE> 302 */ 303 public static String formatDouble(double d) { 304 return formatDouble(d, null); 305 } 306 307 /** 308 * Outputs a <CODE>double</CODE> into a format suitable for the PDF. 309 * @param d a double 310 * @param buf a ByteBuffer 311 * @return the <CODE>String</CODE> representation of the <CODE>double</CODE> if 312 * <CODE>buf</CODE> is <CODE>null</CODE>. If <CODE>buf</CODE> is <B>not</B> <CODE>null</CODE>, 313 * then the double is appended directly to the buffer and this methods returns <CODE>null</CODE>. 314 */ 315 public static String formatDouble(double d, ByteBuffer buf) { 316 if (HIGH_PRECISION) { 317 DecimalFormat dn = new DecimalFormat("0.######", dfs); 318 String sform = dn.format(d); 319 if (buf == null) 320 return sform; 321 else { 322 buf.append(sform); 323 return null; 324 } 325 } 326 boolean negative = false; 327 if (Math.abs(d) < 0.000015) { 328 if (buf != null) { 329 buf.append(ZERO); 330 return null; 331 } else { 332 return "0"; 333 } 334 } 335 if (d < 0) { 336 negative = true; 337 d = -d; 338 } 339 if (d < 1.0) { 340 d += 0.000005; 341 if (d >= 1) { 342 if (negative) { 343 if (buf != null) { 344 buf.append((byte)'-'); 345 buf.append((byte)'1'); 346 return null; 347 } else { 348 return "-1"; 349 } 350 } else { 351 if (buf != null) { 352 buf.append((byte)'1'); 353 return null; 354 } else { 355 return "1"; 356 } 357 } 358 } 359 if (buf != null) { 360 int v = (int) (d * 100000); 361 362 if (negative) buf.append((byte)'-'); 363 buf.append((byte)'0'); 364 buf.append((byte)'.'); 365 366 buf.append( (byte)(v / 10000 + ZERO) ); 367 if (v % 10000 != 0) { 368 buf.append( (byte)((v / 1000) % 10 + ZERO) ); 369 if (v % 1000 != 0) { 370 buf.append( (byte)((v / 100) % 10 + ZERO) ); 371 if (v % 100 != 0) { 372 buf.append((byte)((v / 10) % 10 + ZERO) ); 373 if (v % 10 != 0) { 374 buf.append((byte)((v) % 10 + ZERO) ); 375 } 376 } 377 } 378 } 379 return null; 380 } else { 381 int x = 100000; 382 int v = (int) (d * x); 383 384 StringBuffer res = new StringBuffer(); 385 if (negative) res.append('-'); 386 res.append("0."); 387 388 while( v < x/10 ) { 389 res.append('0'); 390 x /= 10; 391 } 392 res.append(v); 393 int cut = res.length() - 1; 394 while (res.charAt(cut) == '0') { 395 --cut; 396 } 397 res.setLength(cut + 1); 398 return res.toString(); 399 } 400 } else if (d <= 32767) { 401 d += 0.005; 402 int v = (int) (d * 100); 403 404 if (v < byteCacheSize && byteCache[v] != null) { 405 if (buf != null) { 406 if (negative) buf.append((byte)'-'); 407 buf.append(byteCache[v]); 408 return null; 409 } else { 410 String tmp = PdfEncodings.convertToString(byteCache[v], null); 411 if (negative) tmp = "-" + tmp; 412 return tmp; 413 } 414 } 415 if (buf != null) { 416 if (v < byteCacheSize) { 417 //create the cachebyte[] 418 byte[] cache; 419 int size = 0; 420 if (v >= 1000000) { 421 //the original number is >=10000, we need 5 more bytes 422 size += 5; 423 } else if (v >= 100000) { 424 //the original number is >=1000, we need 4 more bytes 425 size += 4; 426 } else if (v >= 10000) { 427 //the original number is >=100, we need 3 more bytes 428 size += 3; 429 } else if (v >= 1000) { 430 //the original number is >=10, we need 2 more bytes 431 size += 2; 432 } else if (v >= 100) { 433 //the original number is >=1, we need 1 more bytes 434 size += 1; 435 } 436 437 //now we must check if we have a decimal number 438 if (v % 100 != 0) { 439 //yes, do not forget the "." 440 size += 2; 441 } 442 if (v % 10 != 0) { 443 size++; 444 } 445 cache = new byte[size]; 446 int add = 0; 447 if (v >= 1000000) { 448 cache[add++] = bytes[(v / 1000000)]; 449 } 450 if (v >= 100000) { 451 cache[add++] = bytes[(v / 100000) % 10]; 452 } 453 if (v >= 10000) { 454 cache[add++] = bytes[(v / 10000) % 10]; 455 } 456 if (v >= 1000) { 457 cache[add++] = bytes[(v / 1000) % 10]; 458 } 459 if (v >= 100) { 460 cache[add++] = bytes[(v / 100) % 10]; 461 } 462 463 if (v % 100 != 0) { 464 cache[add++] = (byte)'.'; 465 cache[add++] = bytes[(v / 10) % 10]; 466 if (v % 10 != 0) { 467 cache[add++] = bytes[v % 10]; 468 } 469 } 470 byteCache[v] = cache; 471 } 472 473 if (negative) buf.append((byte)'-'); 474 if (v >= 1000000) { 475 buf.append( bytes[(v / 1000000)] ); 476 } 477 if (v >= 100000) { 478 buf.append( bytes[(v / 100000) % 10] ); 479 } 480 if (v >= 10000) { 481 buf.append( bytes[(v / 10000) % 10] ); 482 } 483 if (v >= 1000) { 484 buf.append( bytes[(v / 1000) % 10] ); 485 } 486 if (v >= 100) { 487 buf.append( bytes[(v / 100) % 10] ); 488 } 489 490 if (v % 100 != 0) { 491 buf.append((byte)'.'); 492 buf.append( bytes[(v / 10) % 10] ); 493 if (v % 10 != 0) { 494 buf.append( bytes[v % 10] ); 495 } 496 } 497 return null; 498 } else { 499 StringBuffer res = new StringBuffer(); 500 if (negative) res.append('-'); 501 if (v >= 1000000) { 502 res.append( chars[(v / 1000000)] ); 503 } 504 if (v >= 100000) { 505 res.append( chars[(v / 100000) % 10] ); 506 } 507 if (v >= 10000) { 508 res.append( chars[(v / 10000) % 10] ); 509 } 510 if (v >= 1000) { 511 res.append( chars[(v / 1000) % 10] ); 512 } 513 if (v >= 100) { 514 res.append( chars[(v / 100) % 10] ); 515 } 516 517 if (v % 100 != 0) { 518 res.append('.'); 519 res.append( chars[(v / 10) % 10] ); 520 if (v % 10 != 0) { 521 res.append( chars[v % 10] ); 522 } 523 } 524 return res.toString(); 525 } 526 } else { 527 StringBuffer res = new StringBuffer(); 528 if (negative) res.append('-'); 529 d += 0.5; 530 long v = (long) d; 531 return res.append(v).toString(); 532 } 533 } 534 535 /** 536 * Sets the size to zero. 537 */ 538 public void reset() { 539 count = 0; 540 } 541 542 /** 543 * Creates a newly allocated byte array. Its size is the current 544 * size of this output stream and the valid contents of the buffer 545 * have been copied into it. 546 * 547 * @return the current contents of this output stream, as a byte array. 548 */ 549 public byte[] toByteArray() { 550 byte newbuf[] = new byte[count]; 551 System.arraycopy(buf, 0, newbuf, 0, count); 552 return newbuf; 553 } 554 555 /** 556 * Returns the current size of the buffer. 557 * 558 * @return the value of the <code>count</code> field, which is the number of valid bytes in this byte buffer. 559 */ 560 public int size() { 561 return count; 562 } 563 564 public void setSize(int size) { 565 if (size > count || size < 0) 566 throw new IndexOutOfBoundsException(MessageLocalization.getComposedMessage("the.new.size.must.be.positive.and.lt.eq.of.the.current.size")); 567 count = size; 568 } 569 570 /** 571 * Converts the buffer's contents into a string, translating bytes into 572 * characters according to the platform's default character encoding. 573 * 574 * @return String translated from the buffer's contents. 575 */ 576 public String toString() { 577 return new String(buf, 0, count); 578 } 579 580 /** 581 * Converts the buffer's contents into a string, translating bytes into 582 * characters according to the specified character encoding. 583 * 584 * @param enc a character-encoding name. 585 * @return String translated from the buffer's contents. 586 * @throws UnsupportedEncodingException 587 * If the named encoding is not supported. 588 */ 589 public String toString(String enc) throws UnsupportedEncodingException { 590 return new String(buf, 0, count, enc); 591 } 592 593 /** 594 * Writes the complete contents of this byte buffer output to 595 * the specified output stream argument, as if by calling the output 596 * stream's write method using <code>out.write(buf, 0, count)</code>. 597 * 598 * @param out the output stream to which to write the data. 599 * @exception IOException if an I/O error occurs. 600 */ 601 public void writeTo(OutputStream out) throws IOException { 602 out.write(buf, 0, count); 603 } 604 605 public void write(int b) throws IOException { 606 append((byte)b); 607 } 608 609 public void write(byte[] b, int off, int len) { 610 append(b, off, len); 611 } 612 613 public byte[] getBuffer() { 614 return buf; 615 } 616}