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