de.intarsys.pdf.writer.COSWriter Maven / Gradle / Ivy
/*
* Copyright (c) 2007, intarsys consulting GmbH
*
* 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 name of intarsys nor the names of its 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.
*/
package de.intarsys.pdf.writer;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import de.intarsys.pdf.content.CSContent;
import de.intarsys.pdf.content.CSOperation;
import de.intarsys.pdf.content.CSOperators;
import de.intarsys.pdf.cos.COSArray;
import de.intarsys.pdf.cos.COSBoolean;
import de.intarsys.pdf.cos.COSDictionary;
import de.intarsys.pdf.cos.COSDocumentElement;
import de.intarsys.pdf.cos.COSFixed;
import de.intarsys.pdf.cos.COSIndirectObject;
import de.intarsys.pdf.cos.COSInteger;
import de.intarsys.pdf.cos.COSName;
import de.intarsys.pdf.cos.COSNull;
import de.intarsys.pdf.cos.COSObject;
import de.intarsys.pdf.cos.COSObjectProxy;
import de.intarsys.pdf.cos.COSStream;
import de.intarsys.pdf.cos.COSString;
import de.intarsys.pdf.cos.COSVisitorException;
import de.intarsys.pdf.cos.ICOSObjectVisitor;
import de.intarsys.pdf.cos.ICOSProxyVisitor;
import de.intarsys.pdf.crypt.COSSecurityException;
import de.intarsys.pdf.crypt.ISystemSecurityHandler;
import de.intarsys.pdf.parser.PDFParser;
import de.intarsys.pdf.st.AbstractXRefWriter;
import de.intarsys.pdf.st.STDocument;
import de.intarsys.pdf.st.STXRefEntryOccupied;
import de.intarsys.pdf.st.STXRefSection;
import de.intarsys.tools.hex.HexTools;
import de.intarsys.tools.randomaccess.IRandomAccess;
import de.intarsys.tools.randomaccess.RandomAccessByteArray;
import de.intarsys.tools.string.StringTools;
/**
* A writer for PDF related data structures.
*/
public class COSWriter implements ICOSObjectVisitor, ICOSProxyVisitor {
public static final byte[] ARRAY_CLOSE = "]".getBytes(); //$NON-NLS-1$
public static final byte[] ARRAY_OPEN = "[".getBytes(); //$NON-NLS-1$
public static final byte[] COMMENT = "%".getBytes(); //$NON-NLS-1$
/*
* todo 1 @mit break up streams longer than allowed line (255 chars, pp67)
*/
/** To be used when 2 byte sequence is enforced. */
public static final byte[] CRLF = "\r\n".getBytes(); //$NON-NLS-1$
public static final byte[] DICT_CLOSE = ">>".getBytes(); //$NON-NLS-1$
public static final byte[] DICT_OPEN = "<<".getBytes(); //$NON-NLS-1$
/** a fast lookup for serializing digits */
protected static final char[] DIGITS = new char[] { '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '9' };
public static final byte[] ENDOBJ = "endobj".getBytes(); //$NON-NLS-1$
public static final byte[] ENDSTREAM = "endstream".getBytes(); //$NON-NLS-1$
public static final byte[] EOF = "%%EOF".getBytes(); //$NON-NLS-1$
/** standard line separator on this platform */
public static final byte[] EOL = System.getProperty("line.separator") //$NON-NLS-1$
.getBytes();
public static final byte[] FALSE = "false".getBytes(); //$NON-NLS-1$
public static final byte[] GARBAGE = "????".getBytes(); //$NON-NLS-1$
/** Line feed character. */
public static final byte[] LF = "\n".getBytes(); //$NON-NLS-1$
public static final byte[] LITERAL_ESCAPED_BS = "\\b".getBytes(); //$NON-NLS-1$
public static final byte[] LITERAL_ESCAPED_CR = "\\r".getBytes(); //$NON-NLS-1$
public static final byte[] LITERAL_ESCAPED_FF = "\\f".getBytes(); //$NON-NLS-1$
public static final byte[] LITERAL_ESCAPED_HT = "\\t".getBytes(); //$NON-NLS-1$
public static final byte[] LITERAL_ESCAPED_LF = "\\n".getBytes(); //$NON-NLS-1$
public static final byte[] NAME_ESCAPE = "#".getBytes(); //$NON-NLS-1$
public static final byte[] NAME_PREFIX = "/".getBytes(); //$NON-NLS-1$
public static final byte[] NULL = "null".getBytes(); //$NON-NLS-1$
public static final byte[] OBJ = "obj".getBytes(); //$NON-NLS-1$
public static final byte[] PDF_ESCAPE = "\\".getBytes(); //$NON-NLS-1$
public static final byte[] REFERENCE = "R".getBytes(); //$NON-NLS-1$
public static final byte[] SPACE = " ".getBytes(); //$NON-NLS-1$
public static final byte[] STREAM = "stream".getBytes(); //$NON-NLS-1$
public static final byte[] STRING_CLOSE = ")".getBytes(); //$NON-NLS-1$
public static final byte[] STRING_HEX_CLOSE = ">".getBytes(); //$NON-NLS-1$
public static final byte[] STRING_HEX_OPEN = "<".getBytes(); //$NON-NLS-1$
public static final byte[] STRING_OPEN = "(".getBytes(); //$NON-NLS-1$
public static final byte[] TRAILER = "trailer".getBytes(); //$NON-NLS-1$
public static final byte[] TRUE = "true".getBytes(); //$NON-NLS-1$
/*
* (non-Javadoc)
*
* @see de.intarsys.pdf.cos.COSObject#writeOn(java.io.OutputStream)
*/
public static void basicWriteFixed(IRandomAccess randomAccess, float value,
int precision) throws IOException {
NumberFormat format = NumberFormat.getIntegerInstance(Locale.US);
format.setMaximumFractionDigits(precision);
format.setGroupingUsed(false);
randomAccess.write(StringTools.toByteArray(format.format(value)));
}
/*
* (non-Javadoc)
*
* @see de.intarsys.pdf.cos.COSObject#writeOn(java.io.OutputStream)
*/
public static void basicWriteInteger(IRandomAccess randomAccess, int value)
throws IOException {
randomAccess.write(StringTools.toByteArray(Integer.toString(value)));
}
/**
* create the byte stream for the representation of a name
*
* @param randomAccess
* the randomAccessData to write to
* @param name
* the names byte stream
*
* @throws IOException
*/
public static void basicWriteName(IRandomAccess randomAccess, byte[] name)
throws IOException {
randomAccess.write(NAME_PREFIX);
for (int i = 0; i < name.length; i++) {
int current = name[i] & 0xff; // convert to unsigned byte
if ((current <= 32) || (current >= 127)
|| PDFParser.isDelimiter(current) || (current == 35)) {
randomAccess.write(NAME_ESCAPE);
randomAccess.write(HexTools.ByteToHex[current]);
} else {
randomAccess.write(current);
}
}
}
/**
* create a hex encoded byte stream representation of string
*
* @param randomAccess
* the randomAccessData to write to
* @param string
* the string to write
*
* @throws IOException
*/
public static void basicWriteStringHex(IRandomAccess randomAccess,
byte[] string) throws IOException {
randomAccess.write(STRING_HEX_OPEN);
for (int i = 0; i < string.length; i++) {
randomAccess.write(HexTools.ByteToHex[string[i] & 0xFF]);
}
randomAccess.write(STRING_HEX_CLOSE);
}
/**
* create the literal byte stream representation of string
*
* @param randomAccess
* the randomAccessData to write to
* @param string
* the string to write
*
* @throws IOException
*/
public static void basicWriteStringLiteral(IRandomAccess randomAccess,
byte[] string) throws IOException {
randomAccess.write(STRING_OPEN);
for (int i = 0; i < string.length; i++) {
int b = string[i];
if (b == '\n') {
randomAccess.write(LITERAL_ESCAPED_LF);
} else if (b == '\r') {
randomAccess.write(LITERAL_ESCAPED_CR);
} else if (b == '\t') {
randomAccess.write(LITERAL_ESCAPED_HT);
} else if (b == '\f') {
randomAccess.write(LITERAL_ESCAPED_FF);
} else if (b == '\b') {
randomAccess.write(LITERAL_ESCAPED_BS);
} else if ((b == '(') || (b == ')') || (b == '\\')) {
randomAccess.write(PDF_ESCAPE);
randomAccess.write(b);
} else {
randomAccess.write(b);
}
}
randomAccess.write(STRING_CLOSE);
}
/**
* Create a byte array representation from a COSObject.
*
* @param object
* The object to be serialized.
*
* @return A byte array representation from a COSObject.
*/
public static final byte[] toByteArray(COSObject object) {
RandomAccessByteArray tempRandom = new RandomAccessByteArray(null);
COSWriter writer = new COSWriter(tempRandom, null);
try {
writer.writeObject(object);
} catch (IOException e) {
return "*** not printable ***".getBytes(); //$NON-NLS-1$
}
return tempRandom.toByteArray();
}
private COSIndirectObject currentObject;
private ISystemSecurityHandler securityHandler;
private boolean incremental = true;
/** flag to prevent generating two newlines in sequence */
private boolean onNewLine = false;
private List proxies = new ArrayList();
/**
* The IRandomAccess we write to.
*/
private IRandomAccess randomAccess;
public COSWriter(IRandomAccess randomAccess,
ISystemSecurityHandler securityHandler) {
this.securityHandler = securityHandler;
this.randomAccess = randomAccess;
}
protected void basicWriteDocument(STDocument doc) throws IOException {
if (doc.isDirty()) {
doc.updateModificationDate();
doc.getTrailer().updateFileID();
}
if (doc.isNew()) {
// force complete write on new document
setIncremental(false);
}
if (doc.isClosed()) {
return;
}
if (!isIncremental()) {
doc.garbageCollect();
} else {
doc.incrementalGarbageCollect();
}
synchronized (doc.getAccessLock()) {
if (!isIncremental()) {
getRandomAccess().setLength(0);
writeHeader(doc);
}
Collection changes = doc.getChanges();
if (changes.size() > 0) {
seekToEnd();
STXRefSection xrefSection = doc.createNewXRefSection();
if (getSecurityHandler() != null) {
getSecurityHandler()
.updateTrailer(xrefSection.cosGetDict());
}
for (Iterator it = changes.iterator(); it.hasNext();) {
COSIndirectObject object = (COSIndirectObject) it.next();
writeEntry(xrefSection, object);
object.setDirty(false);
}
seekToEnd();
writeXRef(xrefSection);
writeEOF();
doc.setXRefSection(xrefSection);
doc.setDirty(false);
}
for (Iterator it = getProxies().iterator(); it.hasNext();) {
COSObjectProxy proxy = (COSObjectProxy) it.next();
proxy.ended(this);
}
getRandomAccess().flush();
}
}
protected void close(STDocument doc) throws IOException {
// todo 1 change dirty
}
protected byte[] encryptStream(COSDictionary dict, byte[] bytes)
throws IOException {
if (getSecurityHandler() != null && getCurrentObject() != null) {
try {
return getSecurityHandler().encryptStream(
getCurrentObject().getKey(), dict, bytes);
} catch (COSSecurityException e) {
IOException ioe = new IOException("error encrypting data"); //$NON-NLS-1$
ioe.initCause(e);
throw ioe;
}
}
return bytes;
}
protected byte[] encryptString(COSString obj) throws IOException {
byte[] bytes = obj.byteValue();
if (getSecurityHandler() != null && getCurrentObject() != null) {
try {
return getSecurityHandler().encryptString(
getCurrentObject().getKey(), bytes);
} catch (COSSecurityException e) {
IOException ioe = new IOException("error encrypting data"); //$NON-NLS-1$
ioe.initCause(e);
throw ioe;
}
}
return bytes;
}
protected COSIndirectObject getCurrentObject() {
return currentObject;
}
/**
* The collection of proxies to COSObjects visited by the writer.
*
* @return The collection of proxies to COSObjects visited by the writer.
*/
public List getProxies() {
return proxies;
}
public IRandomAccess getRandomAccess() {
return randomAccess;
}
protected ISystemSecurityHandler getSecurityHandler() {
return securityHandler;
}
public boolean isIncremental() {
return incremental;
}
/**
* This will tell if we are on a new line.
*
* @return true If we are on a new line.
*/
protected boolean isOnNewLine() {
return onNewLine;
}
protected void reset() {
onNewLine = false;
}
public void seekToEnd() throws IOException {
randomAccess.seek(randomAccess.getLength());
reset();
}
protected void setCurrentObject(COSIndirectObject currentObject) {
this.currentObject = currentObject;
}
public void setIncremental(boolean incremental) {
this.incremental = incremental;
}
/**
* visitFromArray.
*
* @param obj
* The object that is being visited.
*
* @return unused
*
* @throws COSVisitorException
* If there is an exception while visiting this object.
*/
public Object visitFromArray(COSArray obj) throws COSVisitorException {
try {
if (getSecurityHandler() != null) {
getSecurityHandler().pushContextObject(obj);
}
int count = 0;
write(ARRAY_OPEN);
for (Iterator i = obj.basicIterator(); i.hasNext();) {
COSDocumentElement current = (COSDocumentElement) i.next();
current.accept(this);
count++;
if (i.hasNext()) {
if ((count % 10) == 0) {
writeEOL();
} else {
write(SPACE);
}
}
}
write(ARRAY_CLOSE);
writeEOL();
} catch (IOException e) {
throw new COSVisitorException(e);
} finally {
if (getSecurityHandler() != null) {
getSecurityHandler().popContextObject();
}
}
return null;
}
/**
* visitFromBoolean.
*
* @param obj
* The object that is being visited.
*
* @return unused
*
* @throws COSVisitorException
* If there is an exception while visiting this object.
*/
public Object visitFromBoolean(COSBoolean obj) throws COSVisitorException {
try {
if (obj.booleanValue()) {
write(TRUE);
} else {
write(FALSE);
}
} catch (IOException e) {
throw new COSVisitorException(e);
}
return null;
}
/**
* visitFromDictionary.
*
* @param obj
* The object that is being visited.
*
* @return unused
*
* @throws COSVisitorException
* If there is an exception while visiting this object.
*/
public Object visitFromDictionary(COSDictionary obj)
throws COSVisitorException {
try {
if (getSecurityHandler() != null) {
getSecurityHandler().pushContextObject(obj);
}
write(DICT_OPEN);
writeEOL();
for (Iterator i = obj.basicEntryIterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
COSName name = (COSName) entry.getKey();
COSDocumentElement current = (COSDocumentElement) entry
.getValue();
if (current != null) {
// this is purely defensive, if entry is set to null instead
// of removed
basicWriteName(randomAccess, name.byteValue());
write(SPACE);
current.accept(this);
writeEOL();
}
}
write(DICT_CLOSE);
writeEOL();
} catch (IOException e) {
throw new COSVisitorException(e);
} finally {
if (getSecurityHandler() != null) {
getSecurityHandler().popContextObject();
}
}
return null;
}
/**
* visitFromFixed.
*
* @param obj
* The object that is being visited.
*
* @return unused
*
* @throws COSVisitorException
* If there is an exception while visiting this object.
*/
public Object visitFromFixed(COSFixed obj) throws COSVisitorException {
try {
basicWriteFixed(randomAccess, obj.floatValue(), obj.getPrecision());
onNewLine = false;
} catch (IOException e) {
throw new COSVisitorException(e);
}
return null;
}
/**
* This will write an indirect object reference
*
* @param obj
* The indirect object to write.
* @throws COSVisitorException
* If there is an exception while visiting this object.
*/
public Object visitFromIndirectObject(COSIndirectObject obj)
throws COSVisitorException {
reset();
try {
write(StringTools.toByteArray(Integer.toString(obj
.getObjectNumber())));
write(SPACE);
write(StringTools.toByteArray(Integer.toString(obj
.getGenerationNumber())));
write(SPACE);
write(REFERENCE);
} catch (IOException e) {
throw new COSVisitorException(e);
}
return null;
}
/**
* visitFromInteger.
*
* @param obj
* The object that is being visited.
*
* @return unused
*
* @throws COSVisitorException
* If there is an exception while visiting this object.
*/
public Object visitFromInteger(COSInteger obj) throws COSVisitorException {
try {
basicWriteInteger(randomAccess, obj.intValue());
onNewLine = false;
} catch (IOException e) {
throw new COSVisitorException(e);
}
return null;
}
/**
* visitFromName.
*
* @param obj
* The object that is being visited.
*
* @return unused
*
* @throws COSVisitorException
* If there is an exception while visiting this object.
*/
public Object visitFromName(COSName obj) throws COSVisitorException {
try {
basicWriteName(randomAccess, obj.byteValue());
onNewLine = false;
} catch (IOException e) {
throw new COSVisitorException(e);
}
return null;
}
/**
* visitFromNull.
*
* @param obj
* The object that is being visited.
*
* @return unused
*
* @throws COSVisitorException
* If there is an exception while visiting this object.
*/
public Object visitFromNull(COSNull obj) throws COSVisitorException {
try {
write(NULL);
} catch (IOException e) {
throw new COSVisitorException(e);
}
return null;
}
public Object visitFromProxy(COSObjectProxy obj) throws COSVisitorException {
try {
obj.setPosition(getRandomAccess().getOffset());
// move forward, don't initialize
randomAccess.seekBy(obj.getLength());
} catch (IOException e) {
throw new COSVisitorException(e);
}
proxies.add(obj);
return null;
}
/**
* visitFromStream.
*
* @param obj
* The object that is being visited.
*
* @return unused
*
* @throws COSVisitorException
* If there is an exception while visiting this object.
*/
public Object visitFromStream(COSStream obj) throws COSVisitorException {
try {
if (getSecurityHandler() != null) {
getSecurityHandler().pushContextObject(obj);
}
int length;
byte[] bytes = new byte[0];
if (!obj.isExternal()) {
// only standard (internal) streams have a writable byte content
bytes = obj.getEncodedBytes();
}
// MUST encrypt before dict is written - length may be changed
byte[] encrypted = encryptStream(obj.getDict(), bytes);
// when writing out stream, /Length will determine the number of
// bytes to be decrypted, use this value
length = encrypted == null ? 0 : encrypted.length;
obj.getDict().basicPutSilent(COSStream.DK_Length,
COSInteger.create(length));
// stream dictionaries are not indirect
obj.getDict().accept(this);
// revert to encoded bytes length available in object memory...
length = bytes == null ? 0 : bytes.length;
obj.getDict().basicPutSilent(COSStream.DK_Length,
COSInteger.create(length));
writeCRLF();
// write the stream content
writeStreamContent(encrypted);
} catch (IOException e) {
throw new COSVisitorException(e);
} finally {
if (getSecurityHandler() != null) {
getSecurityHandler().popContextObject();
}
}
return null;
}
/**
* visitFromString.
*
* @param obj
* The object that is being visited.
*
* @return unused
*
* @throws COSVisitorException
* If there is an exception while visiting this object.
*/
public Object visitFromString(COSString obj) throws COSVisitorException {
try {
if (obj.isHexMode()) {
writeStringHex(encryptString(obj));
} else {
writeStringLiteral(encryptString(obj));
}
onNewLine = false;
} catch (IOException e) {
throw new COSVisitorException(e);
}
return null;
}
/**
* This will write some byte to the stream.
*
* @param b
* The source byte array.
*
* @throws IOException
* If the underlying stream throws an exception.
*/
public void write(byte[] b) throws IOException {
onNewLine = false;
randomAccess.write(b);
}
/**
* This will write some byte to the stream.
*
* @param b
* The source byte array.
* @param off
* The offset into the array to start writing.
* @param len
* The number of bytes to write.
*
* @throws IOException
* If the underlying stream throws an exception.
*/
public void write(byte[] b, int off, int len) throws IOException {
onNewLine = false;
randomAccess.write(b, off, len);
}
/**
* This will write a single byte to the stream.
*
* @param b
* The byte to write to the stream.
*
* @throws IOException
* If there is an error writing to the underlying stream.
*/
public void write(int b) throws IOException {
onNewLine = false;
randomAccess.write(b);
}
public void writeContentStream(CSContent contentStream) throws IOException {
int len = contentStream.size();
for (int i = 0; i < len; i++) {
CSOperation operation = contentStream.getOperation(i);
try {
writeOperation(operation);
} catch (COSVisitorException e) {
IOException ioe = new IOException(e.getMessage());
ioe.initCause(e);
throw ioe;
}
write(' ');
}
}
/**
* This will write a CRLF to the stream
*
* @throws IOException
* If there is an error writing the data to the stream.
*/
protected void writeCRLF() throws IOException {
randomAccess.write(CRLF);
}
public void writeDocument(STDocument doc) throws IOException {
basicWriteDocument(doc);
close(doc);
}
protected void writeEntry(STXRefSection xrefSection,
COSIndirectObject object) throws IOException {
STXRefEntryOccupied entry = new STXRefEntryOccupied(object
.getObjectNumber(), object.getGenerationNumber(),
getRandomAccess().getOffset());
xrefSection.addEntry(entry);
writeIndirectObject(object);
}
protected void writeEOF() throws IOException {
write(COSWriter.EOF);
writeEOL();
}
/**
* This will write an EOL to the stream.
*
* @throws IOException
* If there is an error writing to the stream
*/
public void writeEOL() throws IOException {
if (!isOnNewLine()) {
randomAccess.write(EOL);
onNewLine = true;
}
}
/**
* This will write the header to the PDF document.
*
* @throws IOException
* If there is an error writing to the stream.
*/
protected void writeHeader(STDocument stdoc) throws IOException {
write(COMMENT);
write(stdoc.getVersion().getBytes());
writeEOL();
write(COMMENT);
write(GARBAGE);
writeEOL();
}
protected void writeImageData(COSObject imageData) throws IOException {
if (imageData instanceof COSString) {
randomAccess.write(((COSString) imageData).byteValue());
} else if (imageData instanceof COSStream) {
randomAccess.write(((COSStream) imageData).getDecodedBytes());
} else if (imageData instanceof COSName) {
randomAccess.write(((COSName) imageData).byteValue());
} else {
// ?
}
randomAccess.write('\n');
}
public void writeIndirectObject(COSIndirectObject obj) throws IOException {
setCurrentObject(obj);
reset();
write(StringTools.toByteArray(Integer.toString(obj.getObjectNumber())));
write(SPACE);
write(StringTools.toByteArray(Integer.toString(obj
.getGenerationNumber())));
write(SPACE);
write(OBJ);
writeEOL();
try {
obj.dereference().accept(this);
} catch (COSVisitorException e) {
Throwable cause = (e.getCause() == null) ? e : e.getCause();
IOException ioe = new IOException(cause.getMessage());
ioe.initCause(e);
throw ioe;
}
writeEOL();
write(ENDOBJ);
writeEOL();
setCurrentObject(null);
}
/**
* This will write a cos object to the stream
*
* @param object
* the object to write
*
* @throws IOException
* If an error occurs while generating the data.
*/
public void writeObject(COSObject object) throws IOException {
try {
object.accept(this);
} catch (COSVisitorException e) {
IOException ioe = new IOException(e.getMessage());
ioe.initCause(e);
throw ioe;
}
}
protected void writeOperation(CSOperation obj) throws COSVisitorException,
IOException {
if (obj.matchesOperator(CSOperators.CSO_EI) && obj.operandSize() == 1) {
writeImageData(obj.getOperand(0));
} else {
for (Iterator i = obj.getOperands(); i.hasNext();) {
COSObject operand = (COSObject) i.next();
operand.accept(this);
write(' ');
}
}
write(obj.getOperatorToken());
}
protected void writeStreamContent(byte[] bytes) throws IOException {
write(STREAM);
writeCRLF();
if (bytes != null) {
write(bytes);
}
writeCRLF();
write(ENDSTREAM);
writeEOL();
}
protected void writeStringHex(byte[] bytes) throws IOException {
basicWriteStringHex(randomAccess, bytes);
}
protected void writeStringLiteral(byte[] bytes) throws IOException {
basicWriteStringLiteral(randomAccess, bytes);
}
protected void writeXRef(STXRefSection xrefSection) throws IOException {
AbstractXRefWriter xrefWriter = xrefSection.getWriter(this);
xrefWriter.writeXRef(xrefSection);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy