001: /**
002: * Copyright (c) 2003, www.pdfbox.org
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * 1. Redistributions of source code must retain the above copyright notice,
009: * this list of conditions and the following disclaimer.
010: * 2. Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: * 3. Neither the name of pdfbox; nor the names of its
014: * contributors may be used to endorse or promote products derived from this
015: * software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
021: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: *
028: * http://www.pdfbox.org
029: *
030: */package org.pdfbox.io;
031:
032: import java.io.FilterOutputStream;
033: import java.io.IOException;
034: import java.io.OutputStream;
035:
036: /**
037: * This class represents an ASCII85 output stream.
038: *
039: * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
040: * @version $Revision: 1.7 $
041: */
042: public class ASCII85OutputStream extends FilterOutputStream {
043:
044: private int lineBreak;
045: private int count;
046:
047: private byte[] indata;
048: private byte[] outdata;
049:
050: /**
051: * Function produces five ASCII printing characters from
052: * four bytes of binary data.
053: */
054: private int maxline;
055: private boolean flushed;
056: private char terminator;
057:
058: /**
059: * Constructor.
060: *
061: * @param out The output stream to write to.
062: */
063: public ASCII85OutputStream(OutputStream out) {
064: super (out);
065: lineBreak = 36 * 2;
066: maxline = 36 * 2;
067: count = 0;
068: indata = new byte[4];
069: outdata = new byte[5];
070: flushed = true;
071: terminator = '~';
072: }
073:
074: /**
075: * This will set the terminating character.
076: *
077: * @param term The terminating character.
078: */
079: public void setTerminator(char term) {
080: if (term < 118 || term > 126 || term == 'z') {
081: throw new IllegalArgumentException(
082: "Terminator must be 118-126 excluding z");
083: }
084: terminator = term;
085: }
086:
087: /**
088: * This will get the terminating character.
089: *
090: * @return The terminating character.
091: */
092: public char getTerminator() {
093: return terminator;
094: }
095:
096: /**
097: * This will set the line length that will be used.
098: *
099: * @param l The length of the line to use.
100: */
101: public void setLineLength(int l) {
102: if (lineBreak > l) {
103: lineBreak = l;
104: }
105: maxline = l;
106: }
107:
108: /**
109: * This will get the length of the line.
110: *
111: * @return The line length attribute.
112: */
113: public int getLineLength() {
114: return maxline;
115: }
116:
117: /**
118: * This will transform the next four ascii bytes.
119: */
120: private final void transformASCII85() {
121: long word;
122: word = ((((indata[0] << 8) | (indata[1] & 0xFF)) << 16)
123: | ((indata[2] & 0xFF) << 8) | (indata[3] & 0xFF)) & 0xFFFFFFFFL;
124: // System.out.println("word=0x"+Long.toString(word,16)+" "+word);
125:
126: if (word == 0) {
127: outdata[0] = (byte) 'z';
128: outdata[1] = 0;
129: return;
130: }
131: long x;
132: x = word / (85L * 85L * 85L * 85L);
133: // System.out.println("x0="+x);
134: outdata[0] = (byte) (x + '!');
135: word -= x * 85L * 85L * 85L * 85L;
136:
137: x = word / (85L * 85L * 85L);
138: // System.out.println("x1="+x);
139: outdata[1] = (byte) (x + '!');
140: word -= x * 85L * 85L * 85L;
141:
142: x = word / (85L * 85L);
143: // System.out.println("x2="+x);
144: outdata[2] = (byte) (x + '!');
145: word -= x * 85L * 85L;
146:
147: x = word / 85L;
148: // System.out.println("x3="+x);
149: outdata[3] = (byte) (x + '!');
150:
151: // word-=x*85L;
152:
153: // System.out.println("x4="+(word % 85L));
154: outdata[4] = (byte) ((word % 85L) + '!');
155: }
156:
157: /**
158: * This will write a single byte.
159: *
160: * @param b The byte to write.
161: *
162: * @throws IOException If there is an error writing to the stream.
163: */
164: public final void write(int b) throws IOException {
165: flushed = false;
166: indata[count++] = (byte) b;
167: if (count < 4) {
168: return;
169: }
170: transformASCII85();
171: for (int i = 0; i < 5; i++) {
172: if (outdata[i] == 0) {
173: break;
174: }
175: out.write(outdata[i]);
176: if (--lineBreak == 0) {
177: out.write('\n');
178: lineBreak = maxline;
179: }
180: }
181: count = 0;
182: }
183:
184: /**
185: * This will write a chunk of data to the stream.
186: *
187: * @param b The byte buffer to read from.
188: * @param off The offset into the buffer.
189: * @param sz The number of bytes to read from the buffer.
190: *
191: * @throws IOException If there is an error writing to the underlying stream.
192: */
193: public final void write(byte[] b, int off, int sz)
194: throws IOException {
195: for (int i = 0; i < sz; i++) {
196: if (count < 3) {
197: indata[count++] = b[off + i];
198: } else {
199: write(b[off + i]);
200: }
201: }
202: }
203:
204: /**
205: * This will flush the data to the stream.
206: *
207: * @throws IOException If there is an error writing the data to the stream.
208: */
209: public final void flush() throws IOException {
210: if (flushed) {
211: return;
212: }
213: if (count > 0) {
214: for (int i = count; i < 4; i++) {
215: indata[i] = 0;
216: }
217: transformASCII85();
218: if (outdata[0] == 'z') {
219: for (int i = 0; i < 5; i++) // expand 'z',
220: {
221: outdata[i] = (byte) '!';
222: }
223: }
224: for (int i = 0; i < count + 1; i++) {
225: out.write(outdata[i]);
226: if (--lineBreak == 0) {
227: out.write('\n');
228: lineBreak = maxline;
229: }
230: }
231: }
232: if (--lineBreak == 0) {
233: out.write('\n');
234: }
235: out.write(terminator);
236: out.write('\n');
237: count = 0;
238: lineBreak = maxline;
239: flushed = true;
240: super .flush();
241: }
242:
243: /**
244: * This will close the stream.
245: *
246: * @throws IOException If there is an error closing the wrapped stream.
247: */
248: public void close() throws IOException {
249: try {
250: super .close();
251: } finally {
252: indata = outdata = null;
253: }
254: }
255:
256: /**
257: * This will flush the stream.
258: *
259: * @throws Throwable If there is an error.
260: */
261: protected void finalize() throws Throwable {
262: try {
263: flush();
264: } finally {
265: super.finalize();
266: }
267: }
268: }
|