1 /*
2 * $Id: ByteBuffer.java 3373 2008-05-12 16:21:24Z xlv $
3 *
4 * Copyright 2000, 2001, 2002 by Paulo Soares.
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * (the "License"); you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the License.
13 *
14 * The Original Code is 'iText, a free JAVA-PDF library'.
15 *
16 * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
17 * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
18 * All Rights Reserved.
19 * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
20 * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
21 *
22 * Contributor(s): all the names of the contributors are added in the source code
23 * where applicable.
24 *
25 * Alternatively, the contents of this file may be used under the terms of the
26 * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
27 * provisions of LGPL are applicable instead of those above. If you wish to
28 * allow use of your version of this file only under the terms of the LGPL
29 * License and not to allow others to use your version of this file under
30 * the MPL, indicate your decision by deleting the provisions above and
31 * replace them with the notice and other provisions required by the LGPL.
32 * If you do not delete the provisions above, a recipient may use your version
33 * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
34 *
35 * This library is free software; you can redistribute it and/or modify it
36 * under the terms of the MPL as stated above or under the terms of the GNU
37 * Library General Public License as published by the Free Software Foundation;
38 * either version 2 of the License, or any later version.
39 *
40 * This library is distributed in the hope that it will be useful, but WITHOUT
41 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42 * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
43 * details.
44 *
45 * If you didn't download this code from the following link, you should check if
46 * you aren't using an obsolete version:
47 * http://www.lowagie.com/iText/
48 */
49
50 package com.lowagie.text.pdf;
51 import java.io.IOException;
52 import java.io.OutputStream;
53 import java.io.UnsupportedEncodingException;
54 import java.text.DecimalFormat;
55 import java.text.DecimalFormatSymbols;
56 import java.util.Locale;
57
58 import com.lowagie.text.DocWriter;
59
60 /**
61 * Acts like a <CODE>StringBuffer</CODE> but works with <CODE>byte</CODE> arrays.
62 * Floating point is converted to a format suitable to the PDF.
63 * @author Paulo Soares (psoares@consiste.pt)
64 */
65
66 public class ByteBuffer extends OutputStream {
67 /** The count of bytes in the buffer. */
68 protected int count;
69
70 /** The buffer where the bytes are stored. */
71 protected byte buf[];
72
73 private static int byteCacheSize = 0;
74
75 private static byte[][] byteCache = new byte[byteCacheSize][];
76 public static final byte ZERO = (byte)'0';
77 private static final char[] chars = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
78 private static final byte[] bytes = new byte[] {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102};
79 /**
80 * If <CODE>true</CODE> always output floating point numbers with 6 decimal digits.
81 * If <CODE>false</CODE> uses the faster, although less precise, representation.
82 */
83 public static boolean HIGH_PRECISION = false;
84 private static final DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US);
85
86 /** Creates new ByteBuffer with capacity 128 */
87 public ByteBuffer() {
88 this(128);
89 }
90
91 /**
92 * Creates a byte buffer with a certain capacity.
93 * @param size the initial capacity
94 */
95 public ByteBuffer(int size) {
96 if (size < 1)
97 size = 128;
98 buf = new byte[size];
99 }
100
101 /**
102 * Sets the cache size.
103 * <P>
104 * This can only be used to increment the size.
105 * If the size that is passed through is smaller than the current size, nothing happens.
106 *
107 * @param size the size of the cache
108 */
109
110 public static void setCacheSize(int size) {
111 if (size > 3276700) size = 3276700;
112 if (size <= byteCacheSize) return;
113 byte[][] tmpCache = new byte[size][];
114 System.arraycopy(byteCache, 0, tmpCache, 0, byteCacheSize);
115 byteCache = tmpCache;
116 byteCacheSize = size;
117 }
118
119 /**
120 * You can fill the cache in advance if you want to.
121 *
122 * @param decimals
123 */
124
125 public static void fillCache(int decimals) {
126 int step = 1;
127 switch(decimals) {
128 case 0:
129 step = 100;
130 break;
131 case 1:
132 step = 10;
133 break;
134 }
135 for (int i = 1; i < byteCacheSize; i += step) {
136 if (byteCache[i] != null) continue;
137 byteCache[i] = convertToBytes(i);
138 }
139 }
140
141 /**
142 * Converts an double (multiplied by 100 and cast to an int) into an array of bytes.
143 *
144 * @param i the int
145 * @return a byte array
146 */
147
148 private static byte[] convertToBytes(int i) {
149 int size = (int)Math.floor(Math.log(i) / Math.log(10));
150 if (i % 100 != 0) {
151 size += 2;
152 }
153 if (i % 10 != 0) {
154 size++;
155 }
156 if (i < 100) {
157 size++;
158 if (i < 10) {
159 size++;
160 }
161 }
162 size--;
163 byte[] cache = new byte[size];
164 size --;
165 if (i < 100) {
166 cache[0] = (byte)'0';
167 }
168 if (i % 10 != 0) {
169 cache[size--] = bytes[i % 10];
170 }
171 if (i % 100 != 0) {
172 cache[size--] = bytes[(i / 10) % 10];
173 cache[size--] = (byte)'.';
174 }
175 size = (int)Math.floor(Math.log(i) / Math.log(10)) - 1;
176 int add = 0;
177 while (add < size) {
178 cache[add] = bytes[(i / (int)Math.pow(10, size - add + 1)) % 10];
179 add++;
180 }
181 return cache;
182 }
183
184 /**
185 * Appends an <CODE>int</CODE>. The size of the array will grow by one.
186 * @param b the int to be appended
187 * @return a reference to this <CODE>ByteBuffer</CODE> object
188 */
189 public ByteBuffer append_i(int b) {
190 int newcount = count + 1;
191 if (newcount > buf.length) {
192 byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
193 System.arraycopy(buf, 0, newbuf, 0, count);
194 buf = newbuf;
195 }
196 buf[count] = (byte)b;
197 count = newcount;
198 return this;
199 }
200
201 /**
202 * Appends the subarray of the <CODE>byte</CODE> array. The buffer will grow by
203 * <CODE>len</CODE> bytes.
204 * @param b the array to be appended
205 * @param off the offset to the start of the array
206 * @param len the length of bytes to append
207 * @return a reference to this <CODE>ByteBuffer</CODE> object
208 */
209 public ByteBuffer append(byte b[], int off, int len) {
210 if ((off < 0) || (off > b.length) || (len < 0) ||
211 ((off + len) > b.length) || ((off + len) < 0) || len == 0)
212 return this;
213 int newcount = count + len;
214 if (newcount > buf.length) {
215 byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
216 System.arraycopy(buf, 0, newbuf, 0, count);
217 buf = newbuf;
218 }
219 System.arraycopy(b, off, buf, count, len);
220 count = newcount;
221 return this;
222 }
223
224 /**
225 * Appends an array of bytes.
226 * @param b the array to be appended
227 * @return a reference to this <CODE>ByteBuffer</CODE> object
228 */
229 public ByteBuffer append(byte b[]) {
230 return append(b, 0, b.length);
231 }
232
233 /**
234 * Appends a <CODE>String</CODE> to the buffer. The <CODE>String</CODE> is
235 * converted according to the encoding ISO-8859-1.
236 * @param str the <CODE>String</CODE> to be appended
237 * @return a reference to this <CODE>ByteBuffer</CODE> object
238 */
239 public ByteBuffer append(String str) {
240 if (str != null)
241 return append(DocWriter.getISOBytes(str));
242 return this;
243 }
244
245 /**
246 * Appends a <CODE>char</CODE> to the buffer. The <CODE>char</CODE> is
247 * converted according to the encoding ISO-8859-1.
248 * @param c the <CODE>char</CODE> to be appended
249 * @return a reference to this <CODE>ByteBuffer</CODE> object
250 */
251 public ByteBuffer append(char c) {
252 return append_i(c);
253 }
254
255 /**
256 * Appends another <CODE>ByteBuffer</CODE> to this buffer.
257 * @param buf the <CODE>ByteBuffer</CODE> to be appended
258 * @return a reference to this <CODE>ByteBuffer</CODE> object
259 */
260 public ByteBuffer append(ByteBuffer buf) {
261 return append(buf.buf, 0, buf.count);
262 }
263
264 /**
265 * Appends the string representation of an <CODE>int</CODE>.
266 * @param i the <CODE>int</CODE> to be appended
267 * @return a reference to this <CODE>ByteBuffer</CODE> object
268 */
269 public ByteBuffer append(int i) {
270 return append((double)i);
271 }
272
273 public ByteBuffer append(byte b) {
274 return append_i(b);
275 }
276
277 public ByteBuffer appendHex(byte b) {
278 append(bytes[(b >> 4) & 0x0f]);
279 return append(bytes[b & 0x0f]);
280 }
281
282 /**
283 * Appends a string representation of a <CODE>float</CODE> according
284 * to the Pdf conventions.
285 * @param i the <CODE>float</CODE> to be appended
286 * @return a reference to this <CODE>ByteBuffer</CODE> object
287 */
288 public ByteBuffer append(float i) {
289 return append((double)i);
290 }
291
292 /**
293 * Appends a string representation of a <CODE>double</CODE> according
294 * to the Pdf conventions.
295 * @param d the <CODE>double</CODE> to be appended
296 * @return a reference to this <CODE>ByteBuffer</CODE> object
297 */
298 public ByteBuffer append(double d) {
299 append(formatDouble(d, this));
300 return this;
301 }
302
303 /**
304 * Outputs a <CODE>double</CODE> into a format suitable for the PDF.
305 * @param d a double
306 * @return the <CODE>String</CODE> representation of the <CODE>double</CODE>
307 */
308 public static String formatDouble(double d) {
309 return formatDouble(d, null);
310 }
311
312 /**
313 * Outputs a <CODE>double</CODE> into a format suitable for the PDF.
314 * @param d a double
315 * @param buf a ByteBuffer
316 * @return the <CODE>String</CODE> representation of the <CODE>double</CODE> if
317 * <CODE>buf</CODE> is <CODE>null</CODE>. If <CODE>buf</CODE> is <B>not</B> <CODE>null</CODE>,
318 * then the double is appended directly to the buffer and this methods returns <CODE>null</CODE>.
319 */
320 public static String formatDouble(double d, ByteBuffer buf) {
321 if (HIGH_PRECISION) {
322 DecimalFormat dn = new DecimalFormat("0.######", dfs);
323 String sform = dn.format(d);
324 if (buf == null)
325 return sform;
326 else {
327 buf.append(sform);
328 return null;
329 }
330 }
331 boolean negative = false;
332 if (Math.abs(d) < 0.000015) {
333 if (buf != null) {
334 buf.append(ZERO);
335 return null;
336 } else {
337 return "0";
338 }
339 }
340 if (d < 0) {
341 negative = true;
342 d = -d;
343 }
344 if (d < 1.0) {
345 d += 0.000005;
346 if (d >= 1) {
347 if (negative) {
348 if (buf != null) {
349 buf.append((byte)'-');
350 buf.append((byte)'1');
351 return null;
352 } else {
353 return "-1";
354 }
355 } else {
356 if (buf != null) {
357 buf.append((byte)'1');
358 return null;
359 } else {
360 return "1";
361 }
362 }
363 }
364 if (buf != null) {
365 int v = (int) (d * 100000);
366
367 if (negative) buf.append((byte)'-');
368 buf.append((byte)'0');
369 buf.append((byte)'.');
370
371 buf.append( (byte)(v / 10000 + ZERO) );
372 if (v % 10000 != 0) {
373 buf.append( (byte)((v / 1000) % 10 + ZERO) );
374 if (v % 1000 != 0) {
375 buf.append( (byte)((v / 100) % 10 + ZERO) );
376 if (v % 100 != 0) {
377 buf.append((byte)((v / 10) % 10 + ZERO) );
378 if (v % 10 != 0) {
379 buf.append((byte)((v) % 10 + ZERO) );
380 }
381 }
382 }
383 }
384 return null;
385 } else {
386 int x = 100000;
387 int v = (int) (d * x);
388
389 StringBuffer res = new StringBuffer();
390 if (negative) res.append('-');
391 res.append("0.");
392
393 while( v < x/10 ) {
394 res.append('0');
395 x /= 10;
396 }
397 res.append(v);
398 int cut = res.length() - 1;
399 while (res.charAt(cut) == '0') {
400 --cut;
401 }
402 res.setLength(cut + 1);
403 return res.toString();
404 }
405 } else if (d <= 32767) {
406 d += 0.005;
407 int v = (int) (d * 100);
408
409 if (v < byteCacheSize && byteCache[v] != null) {
410 if (buf != null) {
411 if (negative) buf.append((byte)'-');
412 buf.append(byteCache[v]);
413 return null;
414 } else {
415 String tmp = PdfEncodings.convertToString(byteCache[v], null);
416 if (negative) tmp = "-" + tmp;
417 return tmp;
418 }
419 }
420 if (buf != null) {
421 if (v < byteCacheSize) {
422 //create the cachebyte[]
423 byte[] cache;
424 int size = 0;
425 if (v >= 1000000) {
426 //the original number is >=10000, we need 5 more bytes
427 size += 5;
428 } else if (v >= 100000) {
429 //the original number is >=1000, we need 4 more bytes
430 size += 4;
431 } else if (v >= 10000) {
432 //the original number is >=100, we need 3 more bytes
433 size += 3;
434 } else if (v >= 1000) {
435 //the original number is >=10, we need 2 more bytes
436 size += 2;
437 } else if (v >= 100) {
438 //the original number is >=1, we need 1 more bytes
439 size += 1;
440 }
441
442 //now we must check if we have a decimal number
443 if (v % 100 != 0) {
444 //yes, do not forget the "."
445 size += 2;
446 }
447 if (v % 10 != 0) {
448 size++;
449 }
450 cache = new byte[size];
451 int add = 0;
452 if (v >= 1000000) {
453 cache[add++] = bytes[(v / 1000000)];
454 }
455 if (v >= 100000) {
456 cache[add++] = bytes[(v / 100000) % 10];
457 }
458 if (v >= 10000) {
459 cache[add++] = bytes[(v / 10000) % 10];
460 }
461 if (v >= 1000) {
462 cache[add++] = bytes[(v / 1000) % 10];
463 }
464 if (v >= 100) {
465 cache[add++] = bytes[(v / 100) % 10];
466 }
467
468 if (v % 100 != 0) {
469 cache[add++] = (byte)'.';
470 cache[add++] = bytes[(v / 10) % 10];
471 if (v % 10 != 0) {
472 cache[add++] = bytes[v % 10];
473 }
474 }
475 byteCache[v] = cache;
476 }
477
478 if (negative) buf.append((byte)'-');
479 if (v >= 1000000) {
480 buf.append( bytes[(v / 1000000)] );
481 }
482 if (v >= 100000) {
483 buf.append( bytes[(v / 100000) % 10] );
484 }
485 if (v >= 10000) {
486 buf.append( bytes[(v / 10000) % 10] );
487 }
488 if (v >= 1000) {
489 buf.append( bytes[(v / 1000) % 10] );
490 }
491 if (v >= 100) {
492 buf.append( bytes[(v / 100) % 10] );
493 }
494
495 if (v % 100 != 0) {
496 buf.append((byte)'.');
497 buf.append( bytes[(v / 10) % 10] );
498 if (v % 10 != 0) {
499 buf.append( bytes[v % 10] );
500 }
501 }
502 return null;
503 } else {
504 StringBuffer res = new StringBuffer();
505 if (negative) res.append('-');
506 if (v >= 1000000) {
507 res.append( chars[(v / 1000000)] );
508 }
509 if (v >= 100000) {
510 res.append( chars[(v / 100000) % 10] );
511 }
512 if (v >= 10000) {
513 res.append( chars[(v / 10000) % 10] );
514 }
515 if (v >= 1000) {
516 res.append( chars[(v / 1000) % 10] );
517 }
518 if (v >= 100) {
519 res.append( chars[(v / 100) % 10] );
520 }
521
522 if (v % 100 != 0) {
523 res.append('.');
524 res.append( chars[(v / 10) % 10] );
525 if (v % 10 != 0) {
526 res.append( chars[v % 10] );
527 }
528 }
529 return res.toString();
530 }
531 } else {
532 StringBuffer res = new StringBuffer();
533 if (negative) res.append('-');
534 d += 0.5;
535 long v = (long) d;
536 return res.append(v).toString();
537 }
538 }
539
540 /**
541 * Sets the size to zero.
542 */
543 public void reset() {
544 count = 0;
545 }
546
547 /**
548 * Creates a newly allocated byte array. Its size is the current
549 * size of this output stream and the valid contents of the buffer
550 * have been copied into it.
551 *
552 * @return the current contents of this output stream, as a byte array.
553 */
554 public byte[] toByteArray() {
555 byte newbuf[] = new byte[count];
556 System.arraycopy(buf, 0, newbuf, 0, count);
557 return newbuf;
558 }
559
560 /**
561 * Returns the current size of the buffer.
562 *
563 * @return the value of the <code>count</code> field, which is the number of valid bytes in this byte buffer.
564 */
565 public int size() {
566 return count;
567 }
568
569 public void setSize(int size) {
570 if (size > count || size < 0)
571 throw new IndexOutOfBoundsException("The new size must be positive and <= of the current size");
572 count = size;
573 }
574
575 /**
576 * Converts the buffer's contents into a string, translating bytes into
577 * characters according to the platform's default character encoding.
578 *
579 * @return String translated from the buffer's contents.
580 */
581 public String toString() {
582 return new String(buf, 0, count);
583 }
584
585 /**
586 * Converts the buffer's contents into a string, translating bytes into
587 * characters according to the specified character encoding.
588 *
589 * @param enc a character-encoding name.
590 * @return String translated from the buffer's contents.
591 * @throws UnsupportedEncodingException
592 * If the named encoding is not supported.
593 */
594 public String toString(String enc) throws UnsupportedEncodingException {
595 return new String(buf, 0, count, enc);
596 }
597
598 /**
599 * Writes the complete contents of this byte buffer output to
600 * the specified output stream argument, as if by calling the output
601 * stream's write method using <code>out.write(buf, 0, count)</code>.
602 *
603 * @param out the output stream to which to write the data.
604 * @exception IOException if an I/O error occurs.
605 */
606 public void writeTo(OutputStream out) throws IOException {
607 out.write(buf, 0, count);
608 }
609
610 public void write(int b) throws IOException {
611 append((byte)b);
612 }
613
614 public void write(byte[] b, int off, int len) {
615 append(b, off, len);
616 }
617
618 public byte[] getBuffer() {
619 return buf;
620 }
621 }