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] != nullcontinue;
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 }