package totalcross.util.pdf;
// Copyright (c) 2005, Luc Maisonobe
// All rights reserved.
//
// Redistribution and use in source and binary forms, with
// or without modification, are permitted provided that
// the following conditions are met:
//
// Redistributions of source code must retain the
// above copyright notice, this list of conditions and
// the following disclaimer.
// Redistributions in binary form must reproduce the
// above copyright notice, this list of conditions and
// the following disclaimer in the documentation
// and/or other materials provided with the
// distribution.
// Neither the names of spaceroots.org, spaceroots.com
// nor the names of their contributors may be used to
// endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
/**
* This class encodes a binary stream into a text stream.
* <p>
* The ASCII85encoding is suitable when binary data needs to be transmitted or stored as text. It has been defined by
* Adobe for the PostScript and PDF formats (see PDF Reference, section 3.3 Details of Filtered Streams).
* </p>
* <p>
* The encoded stream is about 25% larger than the corresponding binary stream (32 binary bits are converted into 40
* encoded bits, and there may be start/end of line markers).
* </p>
*
* @author Luc Maisonobe
*/
public class ASCII85Encoder
{
totalcross.io.Stream out;
/**
* Create an encoder wrapping a sink of binary data.
*
* @param out sink of binary data to filter
*/
public ASCII85Encoder(totalcross.io.Stream out)
{
this.out = out;
lineLength = -1;
c1 = -1;
phase = 4;
}
/**
* Create an encoder wrapping a sink of binary data.
* <p>
* The additional arguments allow to specify some text formatting
* </p>
* <p>
* Note that specifying a negative number for <code>lineLength</code> is really equivalent to calling the one
* argument.
* </p>
* <p>
* If non-null start/end of line are used, they must contain only whitespace characters as other characters would
* otherwise interfere with the decoding process on the other side of the channel. For safety, it is recommended to
* stick to space (' ', 0x32) and horizontal tabulation ('\t', 0x9) characters for the start of line marker, and to
* line feed ('\n', 0xa) and carriage return ('\r', 0xd) characters according to the platform convention for the end
* of line marker.
* </p>
*
* @param out sink of binary data to filter
* @param lineLength maximal length of a ligne (counting <code>sol</code> but not counting <code>eol</code>), if
* negative lines will not be split
* @param sol start of line marker to use (mainly for indentation purposes), may be null
* @param eol end of line marker to use, may be null only if <code>lineLength</code> is negative
*/
public ASCII85Encoder(totalcross.io.Stream out, int lineLength, byte[] sol, byte[] eol)
{
this.out = out;
this.lineLength = lineLength;
this.sol = sol;
this.eol = eol;
this.c1 = -1;
this.phase = 4;
}
/**
* Closes this output stream and releases any system resources associated with the stream.
*/
public void close() throws totalcross.io.IOException
{
if (c1 >= 0)
{
c4 += c5 / 85;
c3 += c4 / 85;
c2 += c3 / 85;
c1 += c2 / 85;
// output only the required number of bytes
putByte(33 + c1);
putByte(33 + (c2 % 85));
if (phase > 1)
{
putByte(33 + (c3 % 85));
if (phase > 2)
{
putByte(33 + (c4 % 85));
if (phase > 3)
{
putByte(33 + (c5 % 85));
}
}
}
// output the end marker
putByte('~');
putByte('>');
}
// end the last line properly
if (length != 0)
{
out.writeBytes(eol, 0, eol.length);
}
// close the underlying stream
out.close();
}
/**
* Writes the specified byte to this output stream.
*
* @param b byte to write (only the 8 low order bits are used)
*/
public void write(int b) throws totalcross.io.IOException
{
b = b & 0xff;
switch (phase)
{
case 1:
c3 += 9 * b;
c4 += 6 * b;
c5 += b;
phase = 2;
break;
case 2:
c4 += 3 * b;
c5 += b;
phase = 3;
break;
case 3:
c5 += b;
phase = 4;
break;
default:
if (c1 >= 0)
{
// there was a preceding quantum, we now know it was not the last
if ((c1 == 0) && (c2 == 0) && (c3 == 0) && (c4 == 0) && (c5 == 0))
{
putByte('z');
}
else
{
c4 += c5 / 85;
c3 += c4 / 85;
c2 += c3 / 85;
c1 += c2 / 85;
putByte(33 + c1);
putByte(33 + (c2 % 85));
putByte(33 + (c3 % 85));
putByte(33 + (c4 % 85));
putByte(33 + (c5 % 85));
}
}
c1 = 0;
c2 = 27 * b;
c3 = c2;
c4 = 9 * b;
c5 = b;
phase = 1;
}
}
private byte[] one = new byte[1];
/**
* Put a byte in the underlying stream, inserting line breaks as needed.
*
* @param b byte to put in the underlying stream (only the 8 low order bits are used)
* @exception IOException if the underlying stream throws one
*/
private void putByte(int b) throws totalcross.io.IOException
{
if (lineLength >= 0)
{
// split encoded lines if needed
if ((length == 0) && (sol != null))
{
out.writeBytes(sol, 0, sol.length);
length = sol.length;
}
one[0] = (byte)b;
out.writeBytes(one,0,1);
if (++length >= lineLength)
{
out.writeBytes(eol, 0, eol.length);
length = 0;
}
}
else
{
one[0] = (byte)b;
out.writeBytes(one,0,1);
}
}
/** Line length (not counting eol). */
private int lineLength;
/** Start of line marker (indentation). */
private byte[] sol;
/** End Of Line marker. */
private byte[] eol;
/** Coefficients of the 32-bits quantum in base 85. */
private int c1;
private int c2;
private int c3;
private int c4;
private int c5;
/** Phase (between 1 and 4) of raw bytes. */
private int phase;
/** Current length of the line being written. */
private int length;
}