All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itextpdf.text.pdf.ByteBuffer Maven / Gradle / Ivy

/*
 * $Id: 0e30bceaec800dd928fff8e7de5a4a96fa91c334 $
 *
 * This file is part of the iText (R) project.
 * Copyright (c) 1998-2016 iText Group NV
 * Authors: Bruno Lowagie, Paulo Soares, et al.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
 * ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
 * OF THIRD PARTY RIGHTS
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
 * http://itextpdf.com/terms-of-use/
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License,
 * a covered work must retain the producer line in every PDF that is created
 * or manipulated using iText.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the iText software without
 * disclosing the source code of your own applications.
 * These activities include: offering paid services to customers as an ASP,
 * serving PDFs on the fly in a web application, shipping iText with a closed
 * source product.
 *
 * For more information, please contact iText Software Corp. at this
 * address: [email protected]
 */
package com.itextpdf.text.pdf;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import com.itextpdf.text.error_messages.MessageLocalization;

import com.itextpdf.text.DocWriter;

/**
 * Acts like a StringBuffer but works with byte arrays.
 * Floating point is converted to a format suitable to the PDF.
 * @author Paulo Soares
 */

public class ByteBuffer extends OutputStream {
    /** The count of bytes in the buffer. */
    protected int count;
    
    /** The buffer where the bytes are stored. */
    protected byte buf[];
    
    private static int byteCacheSize = 0;
    
    private static byte[][] byteCache = new byte[byteCacheSize][];
    public static final byte ZERO = (byte)'0';
    private static final char[] chars = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    private static final byte[] bytes = new byte[] {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102};
    /**
     * If true always output floating point numbers with 6 decimal digits.
     * If false uses the faster, although less precise, representation.
     */    
    public static boolean HIGH_PRECISION = false;
    private static final DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US);
    
    /** Creates new ByteBuffer with capacity 128 */
    public ByteBuffer() {
        this(128);
    }
    
    /**
     * Creates a byte buffer with a certain capacity.
     * @param size the initial capacity
     */
    public ByteBuffer(int size) {
        if (size < 1)
            size = 128;
        buf = new byte[size];
    }
    
    /**
     * Sets the cache size.
     * 

* This can only be used to increment the size. * If the size that is passed through is smaller than the current size, nothing happens. * * @param size the size of the cache */ public static void setCacheSize(int size) { if (size > 3276700) size = 3276700; if (size <= byteCacheSize) return; byte[][] tmpCache = new byte[size][]; System.arraycopy(byteCache, 0, tmpCache, 0, byteCacheSize); byteCache = tmpCache; byteCacheSize = size; } /** * You can fill the cache in advance if you want to. * * @param decimals */ public static void fillCache(int decimals) { int step = 1; switch(decimals) { case 0: step = 100; break; case 1: step = 10; break; } for (int i = 1; i < byteCacheSize; i += step) { if (byteCache[i] != null) continue; byteCache[i] = convertToBytes(i); } } /** * Converts an double (multiplied by 100 and cast to an int) into an array of bytes. * * @param i the int * @return a byte array */ private static byte[] convertToBytes(int i) { int size = (int)Math.floor(Math.log(i) / Math.log(10)); if (i % 100 != 0) { size += 2; } if (i % 10 != 0) { size++; } if (i < 100) { size++; if (i < 10) { size++; } } size--; byte[] cache = new byte[size]; size --; if (i < 100) { cache[0] = (byte)'0'; } if (i % 10 != 0) { cache[size--] = bytes[i % 10]; } if (i % 100 != 0) { cache[size--] = bytes[(i / 10) % 10]; cache[size--] = (byte)'.'; } size = (int)Math.floor(Math.log(i) / Math.log(10)) - 1; int add = 0; while (add < size) { cache[add] = bytes[(i / (int)Math.pow(10, size - add + 1)) % 10]; add++; } return cache; } /** * Appends an int. The size of the array will grow by one. * @param b the int to be appended * @return a reference to this ByteBuffer object */ public ByteBuffer append_i(int b) { int newcount = count + 1; if (newcount > buf.length) { byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)]; System.arraycopy(buf, 0, newbuf, 0, count); buf = newbuf; } buf[count] = (byte)b; count = newcount; return this; } /** * Appends the subarray of the byte array. The buffer will grow by * len bytes. * @param b the array to be appended * @param off the offset to the start of the array * @param len the length of bytes to append * @return a reference to this ByteBuffer object */ public ByteBuffer append(byte b[], int off, int len) { if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || len == 0) return this; int newcount = count + len; if (newcount > buf.length) { byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)]; System.arraycopy(buf, 0, newbuf, 0, count); buf = newbuf; } System.arraycopy(b, off, buf, count, len); count = newcount; return this; } /** * Appends an array of bytes. * @param b the array to be appended * @return a reference to this ByteBuffer object */ public ByteBuffer append(byte b[]) { return append(b, 0, b.length); } /** * Appends a String to the buffer. The String is * converted according to the encoding ISO-8859-1. * @param str the String to be appended * @return a reference to this ByteBuffer object */ public ByteBuffer append(String str) { if (str != null) return append(DocWriter.getISOBytes(str)); return this; } /** * Appends a char to the buffer. The char is * converted according to the encoding ISO-8859-1. * @param c the char to be appended * @return a reference to this ByteBuffer object */ public ByteBuffer append(char c) { return append_i(c); } /** * Appends another ByteBuffer to this buffer. * @param buf the ByteBuffer to be appended * @return a reference to this ByteBuffer object */ public ByteBuffer append(ByteBuffer buf) { return append(buf.buf, 0, buf.count); } /** * Appends the string representation of an int. * @param i the int to be appended * @return a reference to this ByteBuffer object */ public ByteBuffer append(int i) { return append((double)i); } /** * Appends the string representation of a long. * @param i the long to be appended * @return a reference to this ByteBuffer object */ public ByteBuffer append(long i) { return append(Long.toString(i)); } public ByteBuffer append(byte b) { return append_i(b); } public ByteBuffer appendHex(byte b) { append(bytes[(b >> 4) & 0x0f]); return append(bytes[b & 0x0f]); } /** * Appends a string representation of a float according * to the Pdf conventions. * @param i the float to be appended * @return a reference to this ByteBuffer object */ public ByteBuffer append(float i) { return append((double)i); } /** * Appends a string representation of a double according * to the Pdf conventions. * @param d the double to be appended * @return a reference to this ByteBuffer object */ public ByteBuffer append(double d) { append(formatDouble(d, this)); return this; } /** * Outputs a double into a format suitable for the PDF. * @param d a double * @return the String representation of the double */ public static String formatDouble(double d) { return formatDouble(d, null); } /** * Outputs a double into a format suitable for the PDF. * @param d a double * @param buf a ByteBuffer * @return the String representation of the double if * buf is null. If buf is not null, * then the double is appended directly to the buffer and this methods returns null. */ public static String formatDouble(double d, ByteBuffer buf) { if (HIGH_PRECISION) { DecimalFormat dn = new DecimalFormat("0.######", dfs); String sform = dn.format(d); if (buf == null) return sform; else { buf.append(sform); return null; } } boolean negative = false; if (Math.abs(d) < 0.000015) { if (buf != null) { buf.append(ZERO); return null; } else { return "0"; } } if (d < 0) { negative = true; d = -d; } if (d < 1.0) { d += 0.000005; if (d >= 1) { if (negative) { if (buf != null) { buf.append((byte)'-'); buf.append((byte)'1'); return null; } else { return "-1"; } } else { if (buf != null) { buf.append((byte)'1'); return null; } else { return "1"; } } } if (buf != null) { int v = (int) (d * 100000); if (negative) buf.append((byte)'-'); buf.append((byte)'0'); buf.append((byte)'.'); buf.append( (byte)(v / 10000 + ZERO) ); if (v % 10000 != 0) { buf.append( (byte)((v / 1000) % 10 + ZERO) ); if (v % 1000 != 0) { buf.append( (byte)((v / 100) % 10 + ZERO) ); if (v % 100 != 0) { buf.append((byte)((v / 10) % 10 + ZERO) ); if (v % 10 != 0) { buf.append((byte)((v) % 10 + ZERO) ); } } } } return null; } else { int x = 100000; int v = (int) (d * x); StringBuilder res = new StringBuilder(); if (negative) res.append('-'); res.append("0."); while( v < x/10 ) { res.append('0'); x /= 10; } res.append(v); int cut = res.length() - 1; while (res.charAt(cut) == '0') { --cut; } res.setLength(cut + 1); return res.toString(); } } else if (d <= 32767) { d += 0.005; int v = (int) (d * 100); if (v < byteCacheSize && byteCache[v] != null) { if (buf != null) { if (negative) buf.append((byte)'-'); buf.append(byteCache[v]); return null; } else { String tmp = PdfEncodings.convertToString(byteCache[v], null); if (negative) tmp = "-" + tmp; return tmp; } } if (buf != null) { if (v < byteCacheSize) { //create the cachebyte[] byte[] cache; int size = 0; if (v >= 1000000) { //the original number is >=10000, we need 5 more bytes size += 5; } else if (v >= 100000) { //the original number is >=1000, we need 4 more bytes size += 4; } else if (v >= 10000) { //the original number is >=100, we need 3 more bytes size += 3; } else if (v >= 1000) { //the original number is >=10, we need 2 more bytes size += 2; } else if (v >= 100) { //the original number is >=1, we need 1 more bytes size += 1; } //now we must check if we have a decimal number if (v % 100 != 0) { //yes, do not forget the "." size += 2; } if (v % 10 != 0) { size++; } cache = new byte[size]; int add = 0; if (v >= 1000000) { cache[add++] = bytes[(v / 1000000)]; } if (v >= 100000) { cache[add++] = bytes[(v / 100000) % 10]; } if (v >= 10000) { cache[add++] = bytes[(v / 10000) % 10]; } if (v >= 1000) { cache[add++] = bytes[(v / 1000) % 10]; } if (v >= 100) { cache[add++] = bytes[(v / 100) % 10]; } if (v % 100 != 0) { cache[add++] = (byte)'.'; cache[add++] = bytes[(v / 10) % 10]; if (v % 10 != 0) { cache[add++] = bytes[v % 10]; } } byteCache[v] = cache; } if (negative) buf.append((byte)'-'); if (v >= 1000000) { buf.append( bytes[(v / 1000000)] ); } if (v >= 100000) { buf.append( bytes[(v / 100000) % 10] ); } if (v >= 10000) { buf.append( bytes[(v / 10000) % 10] ); } if (v >= 1000) { buf.append( bytes[(v / 1000) % 10] ); } if (v >= 100) { buf.append( bytes[(v / 100) % 10] ); } if (v % 100 != 0) { buf.append((byte)'.'); buf.append( bytes[(v / 10) % 10] ); if (v % 10 != 0) { buf.append( bytes[v % 10] ); } } return null; } else { StringBuilder res = new StringBuilder(); if (negative) res.append('-'); if (v >= 1000000) { res.append( chars[(v / 1000000)] ); } if (v >= 100000) { res.append( chars[(v / 100000) % 10] ); } if (v >= 10000) { res.append( chars[(v / 10000) % 10] ); } if (v >= 1000) { res.append( chars[(v / 1000) % 10] ); } if (v >= 100) { res.append( chars[(v / 100) % 10] ); } if (v % 100 != 0) { res.append('.'); res.append( chars[(v / 10) % 10] ); if (v % 10 != 0) { res.append( chars[v % 10] ); } } return res.toString(); } } else { d += 0.5; long v = (long) d; if (negative) return "-" + Long.toString(v); else return Long.toString(v); } } /** * Sets the size to zero. */ public void reset() { count = 0; } /** * Creates a newly allocated byte array. Its size is the current * size of this output stream and the valid contents of the buffer * have been copied into it. * * @return the current contents of this output stream, as a byte array. */ public byte[] toByteArray() { byte newbuf[] = new byte[count]; System.arraycopy(buf, 0, newbuf, 0, count); return newbuf; } /** * Returns the current size of the buffer. * * @return the value of the count field, which is the number of valid bytes in this byte buffer. */ public int size() { return count; } public void setSize(int size) { if (size > count || size < 0) throw new IndexOutOfBoundsException(MessageLocalization.getComposedMessage("the.new.size.must.be.positive.and.lt.eq.of.the.current.size")); count = size; } /** * Converts the buffer's contents into a string, translating bytes into * characters according to the platform's default character encoding. * * @return String translated from the buffer's contents. */ @Override public String toString() { return new String(buf, 0, count); } /** * Converts the buffer's contents into a string, translating bytes into * characters according to the specified character encoding. * * @param enc a character-encoding name. * @return String translated from the buffer's contents. * @throws UnsupportedEncodingException * If the named encoding is not supported. */ public String toString(String enc) throws UnsupportedEncodingException { return new String(buf, 0, count, enc); } /** * Writes the complete contents of this byte buffer output to * the specified output stream argument, as if by calling the output * stream's write method using out.write(buf, 0, count). * * @param out the output stream to which to write the data. * @exception IOException if an I/O error occurs. */ public void writeTo(OutputStream out) throws IOException { out.write(buf, 0, count); } public void write(int b) throws IOException { append((byte)b); } @Override public void write(byte[] b, int off, int len) { append(b, off, len); } public byte[] getBuffer() { return buf; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy