/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pdfbox.io;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* This class represents an ASCII85 output stream.
*
* @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
* @version $Revision: 1.7 $
*/
public class ASCII85OutputStream extends FilterOutputStream
{
private int lineBreak;
private int count;
private byte[] indata;
private byte[] outdata;
/**
* Function produces five ASCII printing characters from
* four bytes of binary data.
*/
private int maxline;
private boolean flushed;
private char terminator;
/**
* Constructor.
*
* @param out The output stream to write to.
*/
public ASCII85OutputStream( OutputStream out )
{
super( out );
lineBreak = 36*2;
maxline = 36*2;
count=0;
indata=new byte[4];
outdata=new byte[5];
flushed=true;
terminator='~';
}
/**
* This will set the terminating character.
*
* @param term The terminating character.
*/
public void setTerminator(char term)
{
if(term<118 || term>126 || term=='z')
{
throw new IllegalArgumentException("Terminator must be 118-126 excluding z");
}
terminator=term;
}
/**
* This will get the terminating character.
*
* @return The terminating character.
*/
public char getTerminator()
{
return terminator;
}
/**
* This will set the line length that will be used.
*
* @param l The length of the line to use.
*/
public void setLineLength(int l)
{
if( lineBreak > l )
{
lineBreak = l;
}
maxline=l;
}
/**
* This will get the length of the line.
*
* @return The line length attribute.
*/
public int getLineLength()
{
return maxline;
}
/**
* This will transform the next four ascii bytes.
*/
private final void transformASCII85()
{
long word;
word=( (((indata[0] << 8) | (indata[1] &0xFF)) << 16) |
( (indata[2] & 0xFF) << 8) | (indata[3] & 0xFF)
) & 0xFFFFFFFFL;
// System.out.println("word=0x"+Long.toString(word,16)+" "+word);
if (word == 0 )
{
outdata[0]=(byte)'z';
outdata[1]=0;
return;
}
long x;
x=word/(85L*85L*85L*85L);
// System.out.println("x0="+x);
outdata[0]=(byte)(x+'!');
word-=x*85L*85L*85L*85L;
x=word/(85L*85L*85L);
// System.out.println("x1="+x);
outdata[1]=(byte)(x+'!');
word-=x*85L*85L*85L;
x=word/(85L*85L);
// System.out.println("x2="+x);
outdata[2]=(byte)(x+'!');
word-=x*85L*85L;
x=word/85L;
// System.out.println("x3="+x);
outdata[3]=(byte)(x+'!');
// word-=x*85L;
// System.out.println("x4="+(word % 85L));
outdata[4]=(byte)((word%85L)+'!');
}
/**
* This will write a single byte.
*
* @param b The byte to write.
*
* @throws IOException If there is an error writing to the stream.
*/
public final void write(int b) throws IOException
{
flushed=false;
indata[count++]=(byte)b;
if(count < 4 )
{
return;
}
transformASCII85();
for(int i=0;i<5;i++)
{
if(outdata[i]==0)
{
break;
}
out.write(outdata[i]);
if(--lineBreak==0)
{
out.write('\n');
lineBreak=maxline;
}
}
count = 0;
}
/**
* This will write a chunk of data to the stream.
*
* @param b The byte buffer to read from.
* @param off The offset into the buffer.
* @param sz The number of bytes to read from the buffer.
*
* @throws IOException If there is an error writing to the underlying stream.
*/
public final void write(byte[] b,int off, int sz) throws IOException
{
for(int i=0;i<sz;i++)
{
if(count < 3)
{
indata[count++]=b[off+i];
}
else
{
write(b[off+i]);
}
}
}
/**
* This will flush the data to the stream.
*
* @throws IOException If there is an error writing the data to the stream.
*/
public final void flush() throws IOException
{
if(flushed)
{
return;
}
if(count > 0 )
{
for( int i=count; i<4; i++ )
{
indata[i]=0;
}
transformASCII85();
if(outdata[0]=='z')
{
for(int i=0;i<5;i++) // expand 'z',
{
outdata[i]=(byte)'!';
}
}
for(int i=0;i<count+1;i++)
{
out.write(outdata[i]);
if(--lineBreak==0)
{
out.write('\n');
lineBreak=maxline;
}
}
}
if(--lineBreak==0)
{
out.write('\n');
}
out.write(terminator);
out.write('\n');
count = 0;
lineBreak=maxline;
flushed=true;
super.flush();
}
/**
* This will close the stream.
*
* @throws IOException If there is an error closing the wrapped stream.
*/
public void close() throws IOException
{
try
{
super.close();
}
finally
{
indata=outdata=null;
}
}
/**
* This will flush the stream.
*
* @throws Throwable If there is an error.
*/
protected void finalize() throws Throwable
{
try
{
flush();
}
finally
{
super.finalize();
}
}
}