using System; using System.IO; using System.Text; using System.Globalization; /* * $Id: ByteBuffer.cs,v 1.7 2008/05/13 11:25:17 psoares33 Exp $ * * * Copyright 2000, 2001, 2002 by Paulo Soares. * * The contents of this file are subject to the Mozilla Public License Version 1.1 * (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the License. * * The Original Code is 'iText, a free JAVA-PDF library'. * * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. * All Rights Reserved. * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. * * Contributor(s): all the names of the contributors are added in the source code * where applicable. * * Alternatively, the contents of this file may be used under the terms of the * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the * provisions of LGPL are applicable instead of those above. If you wish to * allow use of your version of this file only under the terms of the LGPL * License and not to allow others to use your version of this file under * the MPL, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the LGPL. * If you do not delete the provisions above, a recipient may use your version * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. * * This library is free software; you can redistribute it and/or modify it * under the terms of the MPL as stated above or under the terms of the GNU * Library General Public License as published by the Free Software Foundation; * either version 2 of the License, or any later version. * * This library 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 Library general Public License for more * details. * * If you didn't download this code from the following link, you should check if * you aren't using an obsolete version: * http://www.lowagie.com/iText/ */ namespace iTextSharp.text.pdf { /** * Acts like a StringBuilder but works with byte arrays. * floating point is converted to a format suitable to the PDF. * @author Paulo Soares (psoares@consiste.pt) */ public class ByteBuffer : Stream { /** 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 const byte ZERO = (byte)'0'; private static char[] chars = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; private static 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 bool HIGH_PRECISION = false; /** 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.Array.Copy(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 bytearray */ 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)]; Array.Copy(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)]; Array.Copy(buf, 0, newbuf, 0, count); buf = newbuf; } Array.Copy(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); } 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) { String sform = d.ToString("0.######", CultureInfo.InvariantCulture); if (buf == null) return sform; else { buf.Append(sform); return null; } } bool 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[cut] == '0') { --cut; } res.Length = 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 { StringBuilder res = new StringBuilder(); if (negative) res.Append('-'); d += 0.5; long v = (long) d; return res.Append(v).ToString(); } } /** * 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]; Array.Copy(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 { get { return count; } set { if (value > count || value < 0) throw new ArgumentOutOfRangeException("The new size must be positive and <= of the current size"); count = value; } } /** * 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. */ public override string ToString() { char[] tmp = this.ConvertToChar(buf); return new String(tmp, 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(System.Text.Encoding enc) { // return new String(ref 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(Stream str) { str.Write(buf, 0, count); } private char[] ConvertToChar(byte[] buf) { char[] retVal = new char[count + 1]; for (int i = 0; i <= count; i++) { retVal[i] = (char)buf[i]; } return retVal; } public byte[] Buffer { get { return buf; } } public override bool CanRead { get { return false; } } public override bool CanSeek { get { return false; } } public override bool CanWrite { get { return true; } } public override long Length { get { return count; } } public override long Position { get { return count; } set { } } public override void Flush() { } public override int Read(byte[] buffer, int offset, int count) { return 0; } public override long Seek(long offset, SeekOrigin origin) { return 0; } public override void SetLength(long value) { } public override void Write(byte[] buffer, int offset, int count) { Append(buffer, offset, count); } public override void WriteByte(byte value) { Append(value); } } }