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}