Fixed a NPE when a mask is null in BitmapImage.java Add support for properly encoding binary data as a hexadecimal string object (including encryption). Adjust palette generation for indexed bitmaps to work correctly with encryption. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_ImagePackageRedesign@611133 13f79535-47bb-0310-9956-ffa450edef68Temp_ImagePackageRedesign
this.bitsPerComponent = 8; | this.bitsPerComponent = 8; | ||||
this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); | this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); | ||||
this.bitmaps = data; | this.bitmaps = data; | ||||
maskRef = new PDFReference(mask); | |||||
if (mask != null) { | |||||
maskRef = new PDFReference(mask); | |||||
} | |||||
} | } | ||||
/** | /** |
*/ | */ | ||||
protected byte[] encodeString(String string) { | protected byte[] encodeString(String string) { | ||||
return encodeText(string); | return encodeText(string); | ||||
/* | |||||
final byte[] buf = encode(PDFText.escapeString(string)); | |||||
} | |||||
/** | |||||
* Encodes binary data as hexadecimal string object. | |||||
* @param data the binary data | |||||
* @param out the OutputStream to write the encoded object to | |||||
* @throws IOException if an I/O error occurs | |||||
*/ | |||||
protected void encodeBinaryToHexString(byte[] data, OutputStream out) throws IOException { | |||||
out.write('<'); | |||||
if (getDocumentSafely().isEncryptionActive()) { | if (getDocumentSafely().isEncryptionActive()) { | ||||
return PDFText.escapeByteArray( | |||||
getDocument().getEncryption().encrypt(buf, this)); | |||||
} else { | |||||
return buf; | |||||
}*/ | |||||
data = getDocument().getEncryption().encrypt(data, this); | |||||
} | |||||
String hex = PDFText.toHex(data, false); | |||||
byte[] encoded = hex.getBytes("US-ASCII"); | |||||
out.write(encoded); | |||||
out.write('>'); | |||||
} | } | ||||
/** | /** | ||||
} | } | ||||
} else if (obj instanceof Boolean) { | } else if (obj instanceof Boolean) { | ||||
writer.write(obj.toString()); | writer.write(obj.toString()); | ||||
} else if (obj instanceof byte[]) { | |||||
writer.flush(); | |||||
encodeBinaryToHexString((byte[])obj, out); | |||||
} else { | } else { | ||||
writer.flush(); | writer.flush(); | ||||
out.write(encodeText(obj.toString())); | out.write(encodeText(obj.toString())); |
* @param ref an object reference | * @param ref an object reference | ||||
*/ | */ | ||||
public PDFReference(String ref) { | public PDFReference(String ref) { | ||||
if (ref == null) { | |||||
throw new NullPointerException("ref must not be null"); | |||||
} | |||||
this.indirectReference = ref; | this.indirectReference = ref; | ||||
} | } | ||||
/** | /** | ||||
* This class represents a simple number object. It also contains contains some | * This class represents a simple number object. It also contains contains some | ||||
* utility methods for outputing numbers to PDF. | |||||
* utility methods for outputting numbers to PDF. | |||||
*/ | */ | ||||
public class PDFText extends PDFObject { | public class PDFText extends PDFObject { | ||||
private static final char[] DIGITS = | |||||
{'0', '1', '2', '3', '4', '5', '6', '7', | |||||
private static final char[] DIGITS | |||||
= {'0', '1', '2', '3', '4', '5', '6', '7', | |||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; | '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; | ||||
private String text; | private String text; | ||||
/** | /** | ||||
* Converts a byte array to a Hexadecimal String (3.2.3 in PDF 1.4 specs) | * Converts a byte array to a Hexadecimal String (3.2.3 in PDF 1.4 specs) | ||||
* @param data the data to encode | * @param data the data to encode | ||||
* @param brackets true if enclosing brackets should be included | |||||
* @return String the resulting string | * @return String the resulting string | ||||
*/ | */ | ||||
public static final String toHex(byte[] data) { | |||||
public static final String toHex(byte[] data, boolean brackets) { | |||||
final StringBuffer sb = new StringBuffer(data.length * 2); | final StringBuffer sb = new StringBuffer(data.length * 2); | ||||
sb.append("<"); | |||||
if (brackets) { | |||||
sb.append("<"); | |||||
} | |||||
for (int i = 0; i < data.length; i++) { | for (int i = 0; i < data.length; i++) { | ||||
sb.append(DIGITS[(data[i] >>> 4) & 0x0F]); | sb.append(DIGITS[(data[i] >>> 4) & 0x0F]); | ||||
sb.append(DIGITS[data[i] & 0x0F]); | sb.append(DIGITS[data[i] & 0x0F]); | ||||
} | } | ||||
sb.append(">"); | |||||
if (brackets) { | |||||
sb.append(">"); | |||||
} | |||||
return sb.toString(); | return sb.toString(); | ||||
} | } | ||||
/** | |||||
* Converts a byte array to a Hexadecimal String (3.2.3 in PDF 1.4 specs) | |||||
* @param data the data to encode | |||||
* @return String the resulting string | |||||
*/ | |||||
public static final String toHex(byte[] data) { | |||||
return toHex(data, true); | |||||
} | |||||
/** | /** | ||||
* Converts a String to UTF-16 (big endian). | * Converts a String to UTF-16 (big endian). | ||||
* @param text text to convert | * @param text text to convert |
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import org.apache.commons.io.output.ByteArrayOutputStream; | |||||
import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||
import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||
import org.apache.fop.pdf.PDFFilterList; | import org.apache.fop.pdf.PDFFilterList; | ||||
import org.apache.fop.pdf.PDFName; | import org.apache.fop.pdf.PDFName; | ||||
import org.apache.fop.pdf.PDFReference; | import org.apache.fop.pdf.PDFReference; | ||||
import org.apache.fop.pdf.PDFWritable; | |||||
/** | /** | ||||
* PDFImage implementation for the PDF renderer which handles RenderedImages. | * PDFImage implementation for the PDF renderer which handles RenderedImages. | ||||
return maskRef; | return maskRef; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
public String getSoftMask() { | |||||
return softMask.toInlinePDFString(); | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public PDFReference getSoftMaskReference() { | public PDFReference getSoftMaskReference() { | ||||
return softMask; | return softMask; | ||||
ColorModel cm = getEffectiveColorModel(); | ColorModel cm = getEffectiveColorModel(); | ||||
if (cm instanceof IndexColorModel) { | if (cm instanceof IndexColorModel) { | ||||
IndexColorModel icm = (IndexColorModel)cm; | IndexColorModel icm = (IndexColorModel)cm; | ||||
PDFArray indexed = new PDFArray(); | |||||
PDFArray indexed = new PDFArray(dict); | |||||
indexed.add(new PDFName("Indexed")); | indexed.add(new PDFName("Indexed")); | ||||
if (icm.getColorSpace().getType() != ColorSpace.TYPE_RGB) { | if (icm.getColorSpace().getType() != ColorSpace.TYPE_RGB) { | ||||
throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL); | throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL); | ||||
} | } | ||||
indexed.add(new Integer(hival)); | indexed.add(new Integer(hival)); | ||||
final StringBuffer sb = new StringBuffer("<"); | |||||
int[] palette = new int[c]; | int[] palette = new int[c]; | ||||
icm.getRGBs(palette); | icm.getRGBs(palette); | ||||
ByteArrayOutputStream baout = new ByteArrayOutputStream(); | |||||
for (int i = 0; i < c; i++) { | for (int i = 0; i < c; i++) { | ||||
if (i > 0) { | |||||
sb.append(" "); | |||||
} | |||||
//TODO Probably doesn't work for non RGB based color spaces | //TODO Probably doesn't work for non RGB based color spaces | ||||
rgb2Hex(palette[i], sb); | |||||
//See log warning above | |||||
int entry = palette[i]; | |||||
baout.write((entry & 0xFF0000) >> 16); | |||||
baout.write((entry & 0xFF00) >> 8); | |||||
baout.write(entry & 0xFF); | |||||
} | } | ||||
sb.append(">"); | |||||
indexed.add(new PDFWritable() { | |||||
public String toInlinePDFString() { | |||||
//Work-around String escaping. Maybe a little hacky. | |||||
return sb.toString(); | |||||
} | |||||
}); | |||||
indexed.add(baout.toByteArray()); | |||||
dict.put("ColorSpace", indexed); | dict.put("ColorSpace", indexed); | ||||
dict.put("BitsPerComponent", icm.getPixelSize()); | dict.put("BitsPerComponent", icm.getPixelSize()); | ||||
Integer index = getIndexOfFirstTransparentColorInPalette(getImage().getRenderedImage()); | Integer index = getIndexOfFirstTransparentColorInPalette(getImage().getRenderedImage()); | ||||
if (index != null) { | if (index != null) { | ||||
PDFArray mask = new PDFArray(); | |||||
PDFArray mask = new PDFArray(dict); | |||||
mask.add(index); | mask.add(index); | ||||
mask.add(index); | mask.add(index); | ||||
dict.put("Mask", mask); | dict.put("Mask", mask); | ||||
} | } | ||||
} | } | ||||
private static final char[] HEX = { | |||||
'0', '1', '2', '3', '4', '5', '6', '7', | |||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' | |||||
}; | |||||
private static void rgb2Hex(int rgb, StringBuffer sb) { | |||||
for (int i = 5; i >= 0; i--) { | |||||
int shift = i * 4; | |||||
int n = (rgb & (15 << shift)) >> shift; | |||||
sb.append(HEX[n % 16]); | |||||
} | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public String getFilterHint() { | public String getFilterHint() { | ||||
return PDFFilterList.IMAGE_FILTER; | return PDFFilterList.IMAGE_FILTER; |
import javax.xml.transform.Source; | import javax.xml.transform.Source; | ||||
import javax.xml.transform.stream.StreamSource; | import javax.xml.transform.stream.StreamSource; | ||||
import org.w3c.dom.Document; | |||||
import org.apache.commons.io.IOUtils; | import org.apache.commons.io.IOUtils; | ||||
import org.apache.commons.io.output.CountingOutputStream; | |||||
import org.apache.xmlgraphics.image.loader.ImageException; | |||||
import org.apache.xmlgraphics.image.loader.ImageInfo; | import org.apache.xmlgraphics.image.loader.ImageInfo; | ||||
import org.apache.xmlgraphics.image.loader.ImageManager; | import org.apache.xmlgraphics.image.loader.ImageManager; | ||||
import org.apache.xmlgraphics.image.loader.ImageSessionContext; | import org.apache.xmlgraphics.image.loader.ImageSessionContext; |
import javax.xml.transform.Source; | import javax.xml.transform.Source; | ||||
import org.w3c.dom.Document; | |||||
import org.apache.commons.io.IOUtils; | import org.apache.commons.io.IOUtils; | ||||
import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||
import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||
import org.apache.xmlgraphics.ps.PSState; | import org.apache.xmlgraphics.ps.PSState; | ||||
import org.apache.xmlgraphics.ps.dsc.DSCException; | import org.apache.xmlgraphics.ps.dsc.DSCException; | ||||
import org.apache.xmlgraphics.ps.dsc.ResourceTracker; | import org.apache.xmlgraphics.ps.dsc.ResourceTracker; | ||||
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentBoundingBox; | |||||
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentHiResBoundingBox; | |||||
import org.apache.fop.apps.FOPException; | import org.apache.fop.apps.FOPException; | ||||
import org.apache.fop.apps.FOUserAgent; | import org.apache.fop.apps.FOUserAgent; |