aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/java/org/apache/fop/image/ImageFactory.java20
-rw-r--r--src/java/org/apache/fop/image/RegisterableImageProvider.java49
-rw-r--r--src/java/org/apache/fop/image/analyser/ImageReaderFactory.java15
-rw-r--r--src/java/org/apache/fop/pdf/ASCII85Filter.java8
-rw-r--r--src/java/org/apache/fop/pdf/ASCIIHexFilter.java8
-rw-r--r--src/java/org/apache/fop/pdf/AbstractPDFStream.java45
-rw-r--r--src/java/org/apache/fop/pdf/BitmapImage.java12
-rw-r--r--src/java/org/apache/fop/pdf/CCFFilter.java6
-rw-r--r--src/java/org/apache/fop/pdf/DCTFilter.java2
-rw-r--r--src/java/org/apache/fop/pdf/FlateFilter.java26
-rw-r--r--src/java/org/apache/fop/pdf/NullFilter.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFArray.java17
-rw-r--r--src/java/org/apache/fop/pdf/PDFDictionary.java40
-rw-r--r--src/java/org/apache/fop/pdf/PDFDocument.java41
-rw-r--r--src/java/org/apache/fop/pdf/PDFEncryptionJCE.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFFactory.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFFilter.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFFilterList.java114
-rw-r--r--src/java/org/apache/fop/pdf/PDFFormXObject.java178
-rw-r--r--src/java/org/apache/fop/pdf/PDFICCStream.java21
-rw-r--r--src/java/org/apache/fop/pdf/PDFImage.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFImageXObject.java168
-rw-r--r--src/java/org/apache/fop/pdf/PDFMetadata.java16
-rw-r--r--src/java/org/apache/fop/pdf/PDFName.java79
-rw-r--r--src/java/org/apache/fop/pdf/PDFNull.java47
-rw-r--r--src/java/org/apache/fop/pdf/PDFNumber.java8
-rw-r--r--src/java/org/apache/fop/pdf/PDFObject.java10
-rw-r--r--src/java/org/apache/fop/pdf/PDFReference.java13
-rw-r--r--src/java/org/apache/fop/pdf/PDFResources.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFStream.java12
-rw-r--r--src/java/org/apache/fop/pdf/PDFT1Stream.java18
-rw-r--r--src/java/org/apache/fop/pdf/PDFTTFStream.java16
-rw-r--r--src/java/org/apache/fop/pdf/PDFXObject.java168
-rw-r--r--src/java/org/apache/fop/render/pdf/FopPDFImage.java64
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandler.java50
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandlerRegistry.java119
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderer.java86
-rw-r--r--src/java/org/apache/fop/svg/PDFGraphics2D.java129
-rw-r--r--status.xml4
39 files changed, 1134 insertions, 487 deletions
diff --git a/src/java/org/apache/fop/image/ImageFactory.java b/src/java/org/apache/fop/image/ImageFactory.java
index ee5b1ae75..5c9f198e8 100644
--- a/src/java/org/apache/fop/image/ImageFactory.java
+++ b/src/java/org/apache/fop/image/ImageFactory.java
@@ -26,12 +26,12 @@ import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
-import java.util.Map;
-import java.util.Set;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.Map.Entry;
import javax.xml.transform.Source;
@@ -39,11 +39,10 @@ import javax.xml.transform.stream.StreamSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
-// FOP
-import org.apache.fop.image.analyser.ImageReaderFactory;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.URISpecification;
+import org.apache.fop.image.analyser.ImageReaderFactory;
+import org.apache.xmlgraphics.util.Service;
/**
* Create FopImage objects (with a configuration file - not yet implemented).
@@ -131,6 +130,14 @@ public final class ImageFactory {
imt = new ImageMimeType("image/emf");
imageMimeTypes.put(imt.getMimeType(), imt);
imt.addProvider(emfImage);
+
+ Iterator iter = Service.providers(RegisterableImageProvider.class, true);
+ while (iter.hasNext()) {
+ RegisterableImageProvider impl = (RegisterableImageProvider)iter.next();
+ imt = new ImageMimeType(impl.getSupportedMimeType());
+ imageMimeTypes.put(imt.getMimeType(), imt);
+ imt.addProvider(new ImageProvider(impl.getName(), impl.getClassName()));
+ }
}
/**
@@ -698,5 +705,4 @@ class ImageMimeType {
providers.add(provider);
}
}
-}
-
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/image/RegisterableImageProvider.java b/src/java/org/apache/fop/image/RegisterableImageProvider.java
new file mode 100644
index 000000000..fd79ebd91
--- /dev/null
+++ b/src/java/org/apache/fop/image/RegisterableImageProvider.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.image;
+
+/**
+ * This interface is used to dynamically register FopImage implementations.
+ * <p>
+ * NOTE: Please don't rely on this interface too much. It is a temporary measure
+ * until the whole image package can be redesigned. The redesign will likely
+ * provide a different mechanism to dynamically register new implementations.
+ */
+public interface RegisterableImageProvider {
+
+ /**
+ * Returns the MIME type the implementation supports.
+ * @return the MIME type
+ */
+ String getSupportedMimeType();
+
+ /**
+ * Returns the name of the implementation.
+ * @return the name
+ */
+ String getName();
+
+ /**
+ * Returns the fully qualified class name for the implementing class.
+ * @return the class name
+ */
+ String getClassName();
+
+}
diff --git a/src/java/org/apache/fop/image/analyser/ImageReaderFactory.java b/src/java/org/apache/fop/image/analyser/ImageReaderFactory.java
index fce2d0b40..c07d68d39 100644
--- a/src/java/org/apache/fop/image/analyser/ImageReaderFactory.java
+++ b/src/java/org/apache/fop/image/analyser/ImageReaderFactory.java
@@ -22,13 +22,14 @@ package org.apache.fop.image.analyser;
// Java
import java.io.IOException;
import java.io.InputStream;
-import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.image.FopImage;
-
+import org.apache.xmlgraphics.util.Service;
/**
* Factory for ImageReader objects.
@@ -38,8 +39,9 @@ import org.apache.fop.image.FopImage;
*/
public class ImageReaderFactory {
- private static ArrayList formats = new ArrayList();
+ private static List formats = new java.util.ArrayList();
+ /** logger */
protected static Log log = LogFactory.getLog(ImageReaderFactory.class);
static {
@@ -50,6 +52,13 @@ public class ImageReaderFactory {
registerFormat(new TIFFReader());
registerFormat(new EPSReader());
registerFormat(new EMFReader());
+
+ //Dynamic registration of ImageReaders
+ Iterator iter = Service.providers(ImageReader.class, true);
+ while (iter.hasNext()) {
+ registerFormat((ImageReader)iter.next());
+ }
+
// the xml parser through batik closes the stream when finished
// so there is a workaround in the SVGReader
registerFormat(new SVGReader());
diff --git a/src/java/org/apache/fop/pdf/ASCII85Filter.java b/src/java/org/apache/fop/pdf/ASCII85Filter.java
index ef50bae93..2114ea540 100644
--- a/src/java/org/apache/fop/pdf/ASCII85Filter.java
+++ b/src/java/org/apache/fop/pdf/ASCII85Filter.java
@@ -52,7 +52,7 @@ public class ASCII85Filter extends PDFFilter {
*
* @return always null
*/
- public String getDecodeParms() {
+ public PDFObject getDecodeParms() {
return null;
}
@@ -60,7 +60,11 @@ public class ASCII85Filter extends PDFFilter {
* {@inheritDoc}
*/
public OutputStream applyFilter(OutputStream out) throws IOException {
- return new ASCII85OutputStream(out);
+ if (isApplied()) {
+ return out;
+ } else {
+ return new ASCII85OutputStream(out);
+ }
}
}
diff --git a/src/java/org/apache/fop/pdf/ASCIIHexFilter.java b/src/java/org/apache/fop/pdf/ASCIIHexFilter.java
index ba5f15882..41c428998 100644
--- a/src/java/org/apache/fop/pdf/ASCIIHexFilter.java
+++ b/src/java/org/apache/fop/pdf/ASCIIHexFilter.java
@@ -51,7 +51,7 @@ public class ASCIIHexFilter extends PDFFilter {
*
* @return always null
*/
- public String getDecodeParms() {
+ public PDFObject getDecodeParms() {
return null;
}
@@ -59,7 +59,11 @@ public class ASCIIHexFilter extends PDFFilter {
* {@inheritDoc}
*/
public OutputStream applyFilter(OutputStream out) throws IOException {
- return new ASCIIHexOutputStream(out);
+ if (isApplied()) {
+ return out;
+ } else {
+ return new ASCIIHexOutputStream(out);
+ }
}
}
diff --git a/src/java/org/apache/fop/pdf/AbstractPDFStream.java b/src/java/org/apache/fop/pdf/AbstractPDFStream.java
index 364b42d66..e208a3916 100644
--- a/src/java/org/apache/fop/pdf/AbstractPDFStream.java
+++ b/src/java/org/apache/fop/pdf/AbstractPDFStream.java
@@ -28,7 +28,7 @@ import org.apache.fop.util.CloseBlockerOutputStream;
/**
* This is an abstract base class for PDF streams.
*/
-public abstract class AbstractPDFStream extends PDFObject {
+public abstract class AbstractPDFStream extends PDFDictionary {
/** The filters that should be applied */
private PDFFilterList filters;
@@ -64,7 +64,10 @@ public abstract class AbstractPDFStream extends PDFObject {
this.filters = new PDFFilterList();
} else {
this.filters = new PDFFilterList(getDocument().isEncryptionActive());
- //this.filters = new PDFFilterList(false);
+ }
+ boolean hasFilterEntries = (get("Filter") != null);
+ if (hasFilterEntries) {
+ this.filters.setDisableAllFilters(true);
}
}
return this.filters;
@@ -118,8 +121,8 @@ public abstract class AbstractPDFStream extends PDFObject {
//Allocate a temporary buffer to find out the size of the encoded stream
final StreamCache encodedStream = StreamCacheFactory.getInstance().
createStreamCache(getSizeHint());
- OutputStream filteredOutput =
- getFilterList().applyFilters(encodedStream.getOutputStream());
+ OutputStream filteredOutput
+ = getFilterList().applyFilters(encodedStream.getOutputStream());
outputRawStreamData(filteredOutput);
filteredOutput.flush();
filteredOutput.close();
@@ -146,8 +149,7 @@ public abstract class AbstractPDFStream extends PDFObject {
//Stream contents
CloseBlockerOutputStream cbout = new CloseBlockerOutputStream(out);
CountingOutputStream cout = new CountingOutputStream(cbout);
- OutputStream filteredOutput =
- getFilterList().applyFilters(cout);
+ OutputStream filteredOutput = getFilterList().applyFilters(cout);
outputRawStreamData(filteredOutput);
filteredOutput.close();
refLength.setNumber(new Integer(cout.getCount()));
@@ -172,19 +174,17 @@ public abstract class AbstractPDFStream extends PDFObject {
StreamCache encodedStream = null;
PDFNumber refLength = null;
- final String lengthEntry;
+ final Object lengthEntry;
if (getDocument().isEncodingOnTheFly()) {
refLength = new PDFNumber();
getDocumentSafely().registerObject(refLength);
- lengthEntry = refLength.referencePDF();
+ lengthEntry = refLength;
} else {
encodedStream = encodeStream();
- lengthEntry = Integer.toString(encodedStream.getSize() + 1);
+ lengthEntry = new Integer(encodedStream.getSize() + 1);
}
- String filterEntry = getFilterList().buildFilterDictEntries();
byte[] p = encode(buildStreamDict(lengthEntry));
-
stream.write(p);
length += p.length;
@@ -209,12 +209,23 @@ public abstract class AbstractPDFStream extends PDFObject {
* @param lengthEntry value for the /Length entry
* @return the newly constructed dictionary
*/
- protected String buildStreamDict(String lengthEntry) {
- final String filterEntry = getFilterList().buildFilterDictEntries();
- return (getObjectID()
- + "<< /Length " + lengthEntry + "\n"
- + filterEntry
- + "\n>>\n");
+ protected String buildStreamDict(Object lengthEntry) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getObjectID());
+ populateStreamDict(lengthEntry);
+
+ writeDictionary(sb);
+ return sb.toString();
+ }
+
+ /**
+ * Populates the dictionary with all necessary entries for the stream.
+ * Override this method if you need additional entries.
+ * @param lengthEntry value for the /Length entry
+ */
+ protected void populateStreamDict(Object lengthEntry) {
+ put("Length", lengthEntry);
+ getFilterList().putFilterDictEntries(this);
}
/**
diff --git a/src/java/org/apache/fop/pdf/BitmapImage.java b/src/java/org/apache/fop/pdf/BitmapImage.java
index e855dc61a..461ec0a64 100644
--- a/src/java/org/apache/fop/pdf/BitmapImage.java
+++ b/src/java/org/apache/fop/pdf/BitmapImage.java
@@ -21,7 +21,6 @@ package org.apache.fop.pdf;
import java.io.IOException;
import java.io.OutputStream;
-//import java.util.Map;
/**
* Bitmap image.
@@ -34,7 +33,7 @@ public class BitmapImage implements PDFImage {
private int bitsPerPixel;
private PDFDeviceColorSpace colorSpace;
private byte[] bitmaps;
- private String maskRef;
+ private PDFReference maskRef;
private PDFColor transparent = null;
private String key;
private PDFDocument pdfDoc;
@@ -47,7 +46,7 @@ public class BitmapImage implements PDFImage {
* @param width the width of the image
* @param height the height of the image
* @param data the bitmap data
- * @param mask the transparancy mask reference if any
+ * @param mask the transparency mask reference if any
*/
public BitmapImage(String k, int width, int height, byte[] data,
String mask) {
@@ -57,7 +56,7 @@ public class BitmapImage implements PDFImage {
this.bitsPerPixel = 8;
this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
this.bitmaps = data;
- maskRef = mask;
+ maskRef = new PDFReference(mask);
}
/**
@@ -170,6 +169,11 @@ public class BitmapImage implements PDFImage {
* @return the soft mask reference if any
*/
public String getSoftMask() {
+ return maskRef.toInlinePDFString();
+ }
+
+ /** {@inheritDoc} */
+ public PDFReference getSoftMaskReference() {
return maskRef;
}
diff --git a/src/java/org/apache/fop/pdf/CCFFilter.java b/src/java/org/apache/fop/pdf/CCFFilter.java
index 98d23d25e..03561629c 100644
--- a/src/java/org/apache/fop/pdf/CCFFilter.java
+++ b/src/java/org/apache/fop/pdf/CCFFilter.java
@@ -28,7 +28,7 @@ package org.apache.fop.pdf;
*/
public class CCFFilter extends NullFilter {
- private String decodeParms;
+ private PDFObject decodeParms;
/**
* {@inheritDoc}
@@ -40,7 +40,7 @@ public class CCFFilter extends NullFilter {
/**
* {@inheritDoc}
*/
- public String getDecodeParms() {
+ public PDFObject getDecodeParms() {
return this.decodeParms;
}
@@ -48,7 +48,7 @@ public class CCFFilter extends NullFilter {
* Sets the CCF decoding parameters
* @param decodeParms The decoding parameters
*/
- public void setDecodeParms(String decodeParms) {
+ public void setDecodeParms(PDFObject decodeParms) {
this.decodeParms = decodeParms;
}
diff --git a/src/java/org/apache/fop/pdf/DCTFilter.java b/src/java/org/apache/fop/pdf/DCTFilter.java
index 08eca9738..92809d927 100644
--- a/src/java/org/apache/fop/pdf/DCTFilter.java
+++ b/src/java/org/apache/fop/pdf/DCTFilter.java
@@ -41,7 +41,7 @@ public class DCTFilter extends NullFilter {
* Get the decode params for this filter.
* @return the DCT filter has no decode params
*/
- public String getDecodeParms() {
+ public PDFObject getDecodeParms() {
return null;
}
diff --git a/src/java/org/apache/fop/pdf/FlateFilter.java b/src/java/org/apache/fop/pdf/FlateFilter.java
index 83eb1ac08..d7dc81d1f 100644
--- a/src/java/org/apache/fop/pdf/FlateFilter.java
+++ b/src/java/org/apache/fop/pdf/FlateFilter.java
@@ -94,22 +94,20 @@ public class FlateFilter extends PDFFilter {
*
* @return a string containing the decode params for this filter
*/
- public String getDecodeParms() {
+ public PDFObject getDecodeParms() {
if (predictor > PREDICTION_NONE) {
- StringBuffer sb = new StringBuffer();
- sb.append("<< /Predictor ");
- sb.append(predictor);
+ PDFDictionary dict = new PDFDictionary();
+ dict.put("Predictor", predictor);
if (colors > 0) {
- sb.append(" /Colors " + colors);
+ dict.put("Colors", colors);
}
if (bitsPerComponent > 0) {
- sb.append(" /BitsPerComponent " + bitsPerComponent);
+ dict.put("BitsPerComponent", bitsPerComponent);
}
if (columns > 0) {
- sb.append(" /Columns " + columns);
+ dict.put("Columns", columns);
}
- sb.append(" >> ");
- return sb.toString();
+ return dict;
}
return null;
}
@@ -210,11 +208,13 @@ public class FlateFilter extends PDFFilter {
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public OutputStream applyFilter(OutputStream out) throws IOException {
- return new FlateEncodeOutputStream(out);
+ if (isApplied()) {
+ return out;
+ } else {
+ return new FlateEncodeOutputStream(out);
+ }
}
}
diff --git a/src/java/org/apache/fop/pdf/NullFilter.java b/src/java/org/apache/fop/pdf/NullFilter.java
index 7c7f54b68..6f2e7f069 100644
--- a/src/java/org/apache/fop/pdf/NullFilter.java
+++ b/src/java/org/apache/fop/pdf/NullFilter.java
@@ -38,7 +38,7 @@ public class NullFilter extends PDFFilter {
/**
* {@inheritDoc}
*/
- public String getDecodeParms() {
+ public PDFObject getDecodeParms() {
return null;
}
diff --git a/src/java/org/apache/fop/pdf/PDFArray.java b/src/java/org/apache/fop/pdf/PDFArray.java
index 1140363ec..2dd68ad8b 100644
--- a/src/java/org/apache/fop/pdf/PDFArray.java
+++ b/src/java/org/apache/fop/pdf/PDFArray.java
@@ -97,6 +97,15 @@ public class PDFArray extends PDFObject {
}
/**
+ * Sets an entry at a given location.
+ * @param index the index of the value to set
+ * @param value the new value
+ */
+ public void set(int index, double value) {
+ this.values.set(index, new Double(value));
+ }
+
+ /**
* Gets an entry at a given location.
* @param index the index of the value to set
* @return the requested value
@@ -114,6 +123,14 @@ public class PDFArray extends PDFObject {
}
/**
+ * Adds a new value to the array.
+ * @param value the value
+ */
+ public void add(double value) {
+ this.values.add(new Double(value));
+ }
+
+ /**
* {@inheritDoc}
*/
public String toPDFString() {
diff --git a/src/java/org/apache/fop/pdf/PDFDictionary.java b/src/java/org/apache/fop/pdf/PDFDictionary.java
index 73878bdc6..71393cace 100644
--- a/src/java/org/apache/fop/pdf/PDFDictionary.java
+++ b/src/java/org/apache/fop/pdf/PDFDictionary.java
@@ -60,6 +60,18 @@ public class PDFDictionary extends PDFObject {
}
/**
+ * Puts a new name/value pair.
+ * @param name the name
+ * @param value the value
+ */
+ public void put(String name, int value) {
+ if (!entries.containsKey(name)) {
+ this.order.add(name);
+ }
+ this.entries.put(name, new Integer(value));
+ }
+
+ /**
* Returns the value given a name.
* @param name the name of the value
* @return the value or null, if there's no value with the given name.
@@ -76,21 +88,29 @@ public class PDFDictionary extends PDFObject {
if (hasObjectNumber()) {
p.append(getObjectID());
}
- p.append("<<");
+ writeDictionary(p);
+ if (hasObjectNumber()) {
+ p.append("endobj\n");
+ }
+ return p.toString();
+ }
+
+ /**
+ * Writes the contents of the dictionary to a StringBuffer.
+ * @param sb the target StringBuffer
+ */
+ protected void writeDictionary(StringBuffer sb) {
+ sb.append("<<");
Iterator iter = this.order.iterator();
while (iter.hasNext()) {
String key = (String)iter.next();
- p.append("\n /");
- p.append(key);
- p.append(" ");
+ sb.append("\n /");
+ sb.append(key);
+ sb.append(" ");
Object obj = this.entries.get(key);
- formatObject(obj, p);
+ formatObject(obj, sb);
}
- p.append("\n>>\n");
- if (hasObjectNumber()) {
- p.append("endobj\n");
- }
- return p.toString();
+ sb.append("\n>>\n");
}
}
diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java
index 748c6b3a8..045d52173 100644
--- a/src/java/org/apache/fop/pdf/PDFDocument.java
+++ b/src/java/org/apache/fop/pdf/PDFDocument.java
@@ -721,8 +721,20 @@ public class PDFDocument {
*
* @param key the image key to look for
* @return the image or PDFXObject for the key if found
+ * @deprecated Use getXObject instead (so forms are treated in the same way)
*/
- public PDFXObject getImage(String key) {
+ public PDFImageXObject getImage(String key) {
+ PDFImageXObject xObject = (PDFImageXObject)xObjectsMap.get(key);
+ return xObject;
+ }
+
+ /**
+ * Get an XObject from the image map.
+ *
+ * @param key the XObject key to look for
+ * @return the PDFXObject for the key if found
+ */
+ public PDFXObject getXObject(String key) {
PDFXObject xObject = (PDFXObject)xObjectsMap.get(key);
return xObject;
}
@@ -779,10 +791,10 @@ public class PDFDocument {
* @param img the PDF image to add
* @return the PDF XObject that references the PDF image data
*/
- public PDFXObject addImage(PDFResourceContext res, PDFImage img) {
+ public PDFImageXObject addImage(PDFResourceContext res, PDFImage img) {
// check if already created
String key = img.getKey();
- PDFXObject xObject = (PDFXObject)xObjectsMap.get(key);
+ PDFImageXObject xObject = (PDFImageXObject)xObjectsMap.get(key);
if (xObject != null) {
if (res != null) {
res.getPDFResources().addXObject(xObject);
@@ -793,7 +805,7 @@ public class PDFDocument {
// setup image
img.setup(this);
// create a new XObject
- xObject = new PDFXObject(++this.xObjectCount, img);
+ xObject = new PDFImageXObject(++this.xObjectCount, img);
registerObject(xObject);
this.resources.addXObject(xObject);
if (res != null) {
@@ -811,26 +823,35 @@ public class PDFDocument {
*
* @param res the PDF resource context to add to, may be null
* @param cont the PDF Stream contents of the Form XObject
- * @param formres the PDF Resources for the Form XObject data
+ * @param formres a reference to the PDF Resources for the Form XObject data
* @param key the key for the object
* @return the PDF Form XObject that references the PDF data
*/
public PDFFormXObject addFormXObject(
PDFResourceContext res,
PDFStream cont,
- PDFResources formres,
+ PDFReference formres,
String key) {
- PDFFormXObject xObject;
- xObject =
- new PDFFormXObject(
+
+ // check if already created
+ PDFFormXObject xObject = (PDFFormXObject)xObjectsMap.get(key);
+ if (xObject != null) {
+ if (res != null) {
+ res.getPDFResources().addXObject(xObject);
+ }
+ return xObject;
+ }
+
+ xObject = new PDFFormXObject(
++this.xObjectCount,
cont,
- formres.referencePDF());
+ formres);
registerObject(xObject);
this.resources.addXObject(xObject);
if (res != null) {
res.getPDFResources().addXObject(xObject);
}
+ this.xObjectsMap.put(key, xObject);
return xObject;
}
diff --git a/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java b/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java
index 099948864..203ca90e1 100644
--- a/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java
+++ b/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java
@@ -75,7 +75,7 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
* Return a parameter dictionary for this filter, or null
* @return The parameter dictionary. In this case, null.
*/
- public String getDecodeParms() {
+ public PDFObject getDecodeParms() {
return null;
}
diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java
index d1e81647a..f2fe97139 100644
--- a/src/java/org/apache/fop/pdf/PDFFactory.java
+++ b/src/java/org/apache/fop/pdf/PDFFactory.java
@@ -1471,7 +1471,7 @@ public class PDFFactory {
/**
* Create a PDFICCStream
- * @see PDFXObject
+ * @see PDFImageXObject
* @see org.apache.fop.image.JpegImage
* @see org.apache.fop.pdf.PDFDeviceColorSpace
* @return the new PDF ICC stream object
diff --git a/src/java/org/apache/fop/pdf/PDFFilter.java b/src/java/org/apache/fop/pdf/PDFFilter.java
index 82efc4c46..ae99ed31c 100644
--- a/src/java/org/apache/fop/pdf/PDFFilter.java
+++ b/src/java/org/apache/fop/pdf/PDFFilter.java
@@ -92,7 +92,7 @@ public abstract class PDFFilter {
*
* @return the decode params for the filter
*/
- public abstract String getDecodeParms();
+ public abstract PDFObject getDecodeParms();
/**
* Applies a filter to an OutputStream.
diff --git a/src/java/org/apache/fop/pdf/PDFFilterList.java b/src/java/org/apache/fop/pdf/PDFFilterList.java
index e0ee7eb4b..8004e1e60 100644
--- a/src/java/org/apache/fop/pdf/PDFFilterList.java
+++ b/src/java/org/apache/fop/pdf/PDFFilterList.java
@@ -48,6 +48,8 @@ public class PDFFilterList {
private List filters = new java.util.ArrayList();
private boolean ignoreASCIIFilters = false;
+
+ private boolean disableAllFilters = false;
/**
* Default constructor.
@@ -68,6 +70,22 @@ public class PDFFilterList {
}
/**
+ * Used to disable all filters.
+ * @param value true if all filters shall be disabled
+ */
+ public void setDisableAllFilters(boolean value) {
+ this.disableAllFilters = value;
+ }
+
+ /**
+ * Returns true if all filters are disabled.
+ * @return true if all filters are disabled
+ */
+ public boolean isDisableAllFilters() {
+ return this.disableAllFilters;
+ }
+
+ /**
* Indicates whether the filter list is already initialized.
* @return true if more there are filters present
*/
@@ -185,21 +203,7 @@ public class PDFFilterList {
List names = new java.util.ArrayList();
List parms = new java.util.ArrayList();
- // run the filters
- int nonNullParams = 0;
- for (int count = 0; count < filters.size(); count++) {
- PDFFilter filter = (PDFFilter)filters.get(count);
- // place the names in our local vector in reverse order
- if (filter.getName().length() > 0) {
- names.add(0, filter.getName());
- if (filter.getDecodeParms() != null) {
- parms.add(0, filter.getDecodeParms());
- nonNullParams++;
- } else {
- parms.add(0, null);
- }
- }
- }
+ int nonNullParams = populateNamesAndParms(names, parms);
// now build up the filter entries for the dictionary
return buildFilterEntries(names)
@@ -209,6 +213,47 @@ public class PDFFilterList {
}
+ /**
+ * Apply the filters to the data
+ * in the order given and add the /Filter and /DecodeParms
+ * entries to the stream dictionary. If the filters have already
+ * been applied to the data (either externally, or internally)
+ * then the dictionary entries added.
+ * @param dict the PDFDictionary to set the entries on
+ */
+ protected void putFilterDictEntries(PDFDictionary dict) {
+ if (filters != null && filters.size() > 0) {
+ List names = new java.util.ArrayList();
+ List parms = new java.util.ArrayList();
+
+ populateNamesAndParms(names, parms);
+
+ // now build up the filter entries for the dictionary
+ putFilterEntries(dict, names);
+ putDecodeParams(dict, parms);
+ }
+ }
+
+ private int populateNamesAndParms(List names, List parms) {
+ // run the filters
+ int nonNullParams = 0;
+ for (int count = 0; count < filters.size(); count++) {
+ PDFFilter filter = (PDFFilter)filters.get(count);
+ // place the names in our local vector in reverse order
+ if (filter.getName().length() > 0) {
+ names.add(0, filter.getName());
+ PDFObject param = filter.getDecodeParms();
+ if (param != null) {
+ parms.add(0, param);
+ nonNullParams++;
+ } else {
+ parms.add(0, null);
+ }
+ }
+ }
+ return nonNullParams;
+ }
+
private String buildFilterEntries(List names) {
int filterCount = 0;
StringBuffer sb = new StringBuffer(64);
@@ -231,6 +276,23 @@ public class PDFFilterList {
}
}
+ private void putFilterEntries(PDFDictionary dict, List names) {
+ PDFArray array = new PDFArray();
+ for (int i = 0, c = names.size(); i < c; i++) {
+ final String name = (String)names.get(i);
+ if (name.length() > 0) {
+ array.add(new PDFName(name));
+ }
+ }
+ if (array.length() > 0) {
+ if (array.length() > 1) {
+ dict.put("Filter", array);
+ } else {
+ dict.put("Filter", array.get(0));
+ }
+ }
+ }
+
private String buildDecodeParms(List parms) {
StringBuffer sb = new StringBuffer();
boolean needParmsEntry = false;
@@ -259,6 +321,26 @@ public class PDFFilterList {
}
}
+ private void putDecodeParams(PDFDictionary dict, List parms) {
+ boolean needParmsEntry = false;
+ PDFArray array = new PDFArray();
+ for (int i = 0, c = parms.size(); i < c; i++) {
+ Object obj = parms.get(i);
+ if (obj != null) {
+ array.add(obj);
+ needParmsEntry = true;
+ } else {
+ array.add(null);
+ }
+ }
+ if (array.length() > 0 & needParmsEntry) {
+ if (array.length() > 1) {
+ dict.put("DecodeParams", array);
+ } else {
+ dict.put("DecodeParams", array.get(0));
+ }
+ }
+ }
/**
* Applies all registered filters as necessary. The method returns an
@@ -269,7 +351,7 @@ public class PDFFilterList {
*/
public OutputStream applyFilters(OutputStream stream) throws IOException {
OutputStream out = stream;
- if (filters != null) {
+ if (filters != null && !isDisableAllFilters()) {
for (int count = filters.size() - 1; count >= 0; count--) {
PDFFilter filter = (PDFFilter)filters.get(count);
out = filter.applyFilter(out);
diff --git a/src/java/org/apache/fop/pdf/PDFFormXObject.java b/src/java/org/apache/fop/pdf/PDFFormXObject.java
index 519bae47a..456419f80 100644
--- a/src/java/org/apache/fop/pdf/PDFFormXObject.java
+++ b/src/java/org/apache/fop/pdf/PDFFormXObject.java
@@ -20,6 +20,8 @@
package org.apache.fop.pdf;
// Java
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.OutputStream;
@@ -30,79 +32,149 @@ import java.io.OutputStream;
* dictionary but a stream of image data.
*/
public class PDFFormXObject extends PDFXObject {
+
private PDFStream contents;
- private String resRef;
+ private PDFReference resRef;
/**
* create a FormXObject with the given number and name and load the
* image in the object
*
* @param xnumber the pdf object X number
- * @param cont the pdf stream contents
- * @param ref the resource PDF reference
+ * @param contents the form's contents
+ * @param resources the resource PDF reference
*/
- public PDFFormXObject(int xnumber, PDFStream cont, String ref) {
- super(xnumber, null);
- contents = cont;
- resRef = ref;
+ public PDFFormXObject(int xnumber, PDFStream contents, PDFReference resources) {
+ super();
+ put("Name", new PDFName("Form" + xnumber));
+ this.resRef = resources;
+ this.contents = contents;
+
+ put("Type", new PDFName("XObject"));
+ put("Subtype", new PDFName("Form"));
+ put("FormType", new Integer(1));
+ setMatrix(new AffineTransform());
}
/**
- * Output the form stream as PDF.
- * This sets up the form XObject dictionary and adds the content
- * data stream.
- *
- * @param stream the output stream to write the data
- * @throws IOException if there is an error writing the data
- * @return the length of the data written
+ * Sets the bounding box of the Form XObject.
+ * @param bbox the bounding box
*/
- protected int output(OutputStream stream) throws IOException {
- int length = 0;
-
- String dictEntries = getFilterList().buildFilterDictEntries();
-
- final StreamCache encodedStream = encodeStream();
-
- StringBuffer sb = new StringBuffer(128);
- sb.append(getObjectID());
- sb.append("<</Type /XObject\n");
- sb.append("/Subtype /Form\n");
- sb.append("/FormType 1\n");
- sb.append("/BBox [0 0 1000 1000]\n");
- sb.append("/Matrix [1 0 0 1 0 0]\n");
- sb.append("/Resources " + resRef + "\n");
- sb.append("/Length " + (encodedStream.getSize() + 1) + "\n");
-
- sb.append(dictEntries);
- sb.append(">>\n");
-
- // push the pdf dictionary on the writer
- byte[] pdfBytes = encode(sb.toString());
- stream.write(pdfBytes);
- length += pdfBytes.length;
-
- //Send encoded stream to target OutputStream
- length += outputStreamData(encodedStream, stream);
- encodedStream.clear(); //Encoded stream can now be discarded
-
- pdfBytes = encode("endobj\n");
- stream.write(pdfBytes);
- length += pdfBytes.length;
-
- // let it gc
- // this object is retained as a reference to inserting
- // the same image but the image data is no longer needed
- contents = null;
- return length;
+ public void setBBox(Rectangle2D bbox) {
+ PDFArray array = (PDFArray)get("BBox");
+ if (array == null) {
+ array = new PDFArray();
+ array.add(bbox.getX());
+ array.add(bbox.getY());
+ array.add(bbox.getWidth());
+ array.add(bbox.getHeight());
+ put("BBox", array);
+ } else {
+ array.set(0, bbox.getX());
+ array.set(1, bbox.getY());
+ array.set(2, bbox.getWidth());
+ array.set(3, bbox.getHeight());
+ }
+ }
+
+ /**
+ * Returns the bounding box.
+ * @return the BBox value
+ */
+ public Rectangle2D getBBox() {
+ PDFArray array = (PDFArray)get("BBox");
+ if (array != null) {
+ Rectangle2D rect = new Rectangle2D.Double();
+ double x = ((Number)array.get(0)).doubleValue();
+ double y = ((Number)array.get(1)).doubleValue();
+ double w = ((Number)array.get(2)).doubleValue();
+ double h = ((Number)array.get(3)).doubleValue();
+ rect.setFrame(x, y, w, h);
+ return rect;
+ } else {
+ return null;
+ }
}
/**
- * {@inheritDoc}
+ * Sets the Matrix value
+ * @param at the AffineTransform defining the transformation matrix
*/
+ public void setMatrix(AffineTransform at) {
+ PDFArray array = (PDFArray)get("Matrix");
+ double[] m = new double[6];
+ at.getMatrix(m);
+ if (array == null) {
+ array = new PDFArray();
+ array.add(m[0]);
+ array.add(m[1]);
+ array.add(m[2]);
+ array.add(m[3]);
+ array.add(m[4]);
+ array.add(m[5]);
+ put("Matrix", array);
+ } else {
+ array.set(0, m[0]);
+ array.set(1, m[1]);
+ array.set(2, m[2]);
+ array.set(3, m[3]);
+ array.set(4, m[4]);
+ array.set(5, m[5]);
+ }
+ }
+
+ /**
+ * Returns the Matrix value.
+ * @return the Matrix
+ */
+ public AffineTransform getMatrix() {
+ PDFArray array = (PDFArray)get("Matrix");
+ if (array != null) {
+ AffineTransform at = new AffineTransform();
+ double m00 = ((Number)array.get(0)).doubleValue();
+ double m10 = ((Number)array.get(1)).doubleValue();
+ double m01 = ((Number)array.get(2)).doubleValue();
+ double m11 = ((Number)array.get(3)).doubleValue();
+ double m02 = ((Number)array.get(4)).doubleValue();
+ double m12 = ((Number)array.get(5)).doubleValue();
+ at.setTransform(m00, m10, m01, m11, m02, m12);
+ return at;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Used to set the contents of the PDF stream.
+ * @param data the contents as a byte array
+ * @throws IOException in case of an I/O problem
+ */
+ public void setData(byte[] data) throws IOException {
+ this.contents.setData(data);
+ }
+
+ /** {@inheritDoc} */
protected void outputRawStreamData(OutputStream out) throws IOException {
contents.outputRawStreamData(out);
}
+ /** {@inheritDoc} */
+ protected int output(OutputStream stream) throws IOException {
+ final int len = super.output(stream);
+
+ //Now that the data has been written, it can be discarded.
+ this.contents = null;
+ return len;
+ }
+
+ /** {@inheritDoc} */
+ protected void populateStreamDict(Object lengthEntry) {
+ if (get("Matrix") == null) {
+ put("Matrix", new PDFArray(new int[] {1, 0, 0, 1, 0, 0}));
+ }
+ put("Resources", resRef);
+ super.populateStreamDict(lengthEntry);
+ }
}
diff --git a/src/java/org/apache/fop/pdf/PDFICCStream.java b/src/java/org/apache/fop/pdf/PDFICCStream.java
index 6c08953cb..da1e8353c 100644
--- a/src/java/org/apache/fop/pdf/PDFICCStream.java
+++ b/src/java/org/apache/fop/pdf/PDFICCStream.java
@@ -73,24 +73,13 @@ public class PDFICCStream extends PDFStream {
cp.write(out);
}
- /**
- * {@inheritDoc}
- */
- protected String buildStreamDict(String lengthEntry) {
- final String filterEntry = getFilterList().buildFilterDictEntries();
- final StringBuffer sb = new StringBuffer(128);
- sb.append(getObjectID());
- sb.append("<< ");
- sb.append("/N " + cp.getNumComponents());
-
+ /** {@inheritDoc} */
+ protected void populateStreamDict(Object lengthEntry) {
+ put("N", cp.getNumComponents());
if (pdfColorSpace != null) {
- sb.append("\n/Alternate /" + pdfColorSpace.getName() + " ");
+ put("Alternate", new PDFName(pdfColorSpace.getName()));
}
-
- sb.append("\n/Length " + lengthEntry);
- sb.append("\n" + filterEntry);
- sb.append("\n>>\n");
- return sb.toString();
+ super.populateStreamDict(lengthEntry);
}
}
diff --git a/src/java/org/apache/fop/pdf/PDFImage.java b/src/java/org/apache/fop/pdf/PDFImage.java
index db233d1d4..dd24240b5 100644
--- a/src/java/org/apache/fop/pdf/PDFImage.java
+++ b/src/java/org/apache/fop/pdf/PDFImage.java
@@ -109,6 +109,8 @@ public interface PDFImage {
* @return the PDF reference for a soft mask image
*/
String getSoftMask();
+
+ PDFReference getSoftMaskReference();
/** @return true for CMYK images generated by Adobe Photoshop */
boolean isInverted();
diff --git a/src/java/org/apache/fop/pdf/PDFImageXObject.java b/src/java/org/apache/fop/pdf/PDFImageXObject.java
new file mode 100644
index 000000000..4cc174100
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFImageXObject.java
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+// Java
+import java.io.IOException;
+import java.io.OutputStream;
+
+/* modified by JKT to integrate with 0.12.0 */
+/* modified by Eric SCHAEFFER to integrate with 0.13.0 */
+
+/**
+ * PDF XObject
+ *
+ * A derivative of the PDF Object, is a PDF Stream that has not only a
+ * dictionary but a stream of image data.
+ * The dictionary just provides information like the stream length.
+ * This outputs the image dictionary and the image data.
+ * This is used as a reference for inserting the same image in the
+ * document in another place.
+ */
+public class PDFImageXObject extends PDFXObject {
+
+ private PDFImage pdfimage;
+
+ /**
+ * create an XObject with the given number and name and load the
+ * image in the object
+ *
+ * @param xnumber the pdf object X number
+ * @param img the pdf image that contains the image data
+ */
+ public PDFImageXObject(int xnumber, PDFImage img) {
+ super();
+ put("Name", new PDFName("Im" + xnumber));
+ pdfimage = img;
+ }
+
+ /**
+ * Output the image as PDF.
+ * This sets up the image dictionary and adds the image data stream.
+ *
+ * @param stream the output stream to write the data
+ * @throws IOException if there is an error writing the data
+ * @return the length of the data written
+ */
+ protected int output(OutputStream stream) throws IOException {
+ int length = super.output(stream);
+
+ // let it gc
+ // this object is retained as a reference to inserting
+ // the same image but the image data is no longer needed
+ pdfimage = null;
+ return length;
+ }
+
+ /** {@inheritDoc} */
+ protected void populateStreamDict(Object lengthEntry) {
+ super.populateStreamDict(lengthEntry);
+ if (pdfimage.isPS()) {
+ populateDictionaryFromPS();
+ } else {
+ populateDictionaryFromImage();
+ }
+ }
+
+ private void populateDictionaryFromPS() {
+ getDocumentSafely().getProfile().verifyPSXObjectsAllowed();
+ put("Subtype", new PDFName("PS"));
+ }
+
+ private void populateDictionaryFromImage() {
+ put("Subtype", new PDFName("Image"));
+ put("Width", new Integer(pdfimage.getWidth()));
+ put("Height", new Integer(pdfimage.getHeight()));
+ put("BitsPerComponent", new Integer(pdfimage.getBitsPerPixel()));
+
+ PDFICCStream pdfICCStream = pdfimage.getICCStream();
+ if (pdfICCStream != null) {
+ put("ColorSpace", new PDFArray(new Object[] {new PDFName("ICCBased"), pdfICCStream}));
+ } else {
+ PDFDeviceColorSpace cs = pdfimage.getColorSpace();
+ put("ColorSpace", new PDFName(cs.getName()));
+ }
+
+ if (pdfimage.isInverted()) {
+ /* PhotoShop generates CMYK values that's inverse,
+ * this will invert the values - too bad if it's not
+ * a PhotoShop image...
+ */
+ final Float zero = new Float(0.0f);
+ final Float one = new Float(1.0f);
+ PDFArray decode = new PDFArray();
+ for (int i = 0, c = pdfimage.getColorSpace().getNumComponents(); i < c; i++) {
+ decode.add(one);
+ decode.add(zero);
+ }
+ put("Decode", decode);
+ }
+
+ if (pdfimage.isTransparent()) {
+ PDFColor transp = pdfimage.getTransparentColor();
+ PDFArray mask = new PDFArray();
+ mask.add(new Integer(transp.red255()));
+ mask.add(new Integer(transp.red255()));
+ mask.add(new Integer(transp.green255()));
+ mask.add(new Integer(transp.green255()));
+ mask.add(new Integer(transp.blue255()));
+ mask.add(new Integer(transp.blue255()));
+ put("Mask", mask);
+ }
+ PDFReference ref = pdfimage.getSoftMaskReference();
+ if (ref != null) {
+ put("SMask", ref);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected void outputRawStreamData(OutputStream out) throws IOException {
+ pdfimage.outputContents(out);
+ }
+
+ /** {@inheritDoc} */
+ protected int getSizeHint() throws IOException {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ protected void prepareImplicitFilters() {
+ PDFFilter pdfFilter = pdfimage.getPDFFilter();
+ if (pdfFilter != null) {
+ getFilterList().ensureFilterInPlace(pdfFilter);
+ }
+ }
+
+ /**
+ * This sets up the default filters for XObjects. It uses the PDFImage
+ * instance to determine what default filters to apply.
+ * {@inheritDoc}
+ */
+ protected void setupFilterList() {
+ if (!getFilterList().isInitialized()) {
+ getFilterList().addDefaultFilters(
+ getDocumentSafely().getFilterMap(),
+ pdfimage.getFilterHint());
+ }
+ super.setupFilterList();
+ }
+
+
+}
diff --git a/src/java/org/apache/fop/pdf/PDFMetadata.java b/src/java/org/apache/fop/pdf/PDFMetadata.java
index 74ffb1580..067502986 100644
--- a/src/java/org/apache/fop/pdf/PDFMetadata.java
+++ b/src/java/org/apache/fop/pdf/PDFMetadata.java
@@ -101,24 +101,18 @@ public class PDFMetadata extends PDFStream {
}
/** {@inheritDoc} */
- protected String buildStreamDict(String lengthEntry) {
+ protected void populateStreamDict(Object lengthEntry) {
final String filterEntry = getFilterList().buildFilterDictEntries();
if (getDocumentSafely().getProfile().getPDFAMode().isPDFA1LevelB()
&& filterEntry != null && filterEntry.length() > 0) {
throw new PDFConformanceException(
"The Filter key is prohibited when PDF/A-1 is active");
}
- final StringBuffer sb = new StringBuffer(128);
- sb.append(getObjectID());
- sb.append("<< ");
- sb.append("/Type /Metadata");
- sb.append("\n/Subtype /XML");
- sb.append("\n/Length " + lengthEntry);
- sb.append("\n" + filterEntry);
- sb.append("\n>>\n");
- return sb.toString();
+ put("Type", new PDFName("Metadata"));
+ put("Subtype", new PDFName("XML"));
+ super.populateStreamDict(lengthEntry);
}
-
+
/**
* Creates an XMP document based on the settings on the PDF Document.
* @param pdfDoc the PDF Document
diff --git a/src/java/org/apache/fop/pdf/PDFName.java b/src/java/org/apache/fop/pdf/PDFName.java
new file mode 100644
index 000000000..4c3c3d003
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFName.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+/**
+ * Class representing a PDF name object.
+ */
+public class PDFName implements PDFWritable {
+
+ private String name;
+
+ /**
+ * Creates a new PDF name object.
+ * @param name the name value
+ */
+ public PDFName(String name) {
+ this.name = escapeName(name);
+ }
+
+
+ private static String escapeName(String name) {
+ StringBuffer sb = new StringBuffer(Math.min(16, name.length() + 4));
+ if (!name.startsWith("/")) {
+ sb.append('/');
+ }
+ for (int i = 0, c = name.length(); i < c; i++) {
+ char ch = name.charAt(i);
+ if (ch >= 33 && ch <= 126) {
+ sb.append(ch);
+ } else {
+ sb.append('#');
+ toHex(ch, sb);
+ }
+ }
+ return sb.toString();
+ }
+
+ private static final char[] DIGITS
+ = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ private static void toHex(char ch, StringBuffer sb) {
+ if (ch >= 256) {
+ throw new IllegalArgumentException(
+ "Only 8-bit characters allowed by this implementation");
+ }
+ sb.append(DIGITS[ch >>> 4 & 0x0F]);
+ sb.append(DIGITS[ch & 0x0F]);
+ }
+
+
+ /** {@inheritDoc} */
+ public String toInlinePDFString() {
+ return this.name;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return toInlinePDFString();
+ }
+
+}
diff --git a/src/java/org/apache/fop/pdf/PDFNull.java b/src/java/org/apache/fop/pdf/PDFNull.java
new file mode 100644
index 000000000..e3fb44a8d
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFNull.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+/**
+ * Class representing a PDF name object.
+ */
+public final class PDFNull implements PDFWritable {
+
+ /** Instance for the "null" object. */
+ public static final PDFNull INSTANCE = new PDFNull();
+
+ /**
+ * Creates a new PDF name object.
+ * @param name the name value
+ */
+ private PDFNull() {
+ }
+
+ /** {@inheritDoc} */
+ public String toInlinePDFString() {
+ return "null";
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return toInlinePDFString();
+ }
+
+}
diff --git a/src/java/org/apache/fop/pdf/PDFNumber.java b/src/java/org/apache/fop/pdf/PDFNumber.java
index 6da28f9be..2a8d6c472 100644
--- a/src/java/org/apache/fop/pdf/PDFNumber.java
+++ b/src/java/org/apache/fop/pdf/PDFNumber.java
@@ -111,9 +111,13 @@ public class PDFNumber extends PDFObject {
"The number of this PDFNumber must not be empty");
}
StringBuffer sb = new StringBuffer(64);
- sb.append(getObjectID());
+ if (hasObjectNumber()) {
+ sb.append(getObjectID());
+ }
sb.append(getNumber().toString());
- sb.append("\nendobj\n");
+ if (hasObjectNumber()) {
+ sb.append("\nendobj\n");
+ }
return sb.toString();
}
diff --git a/src/java/org/apache/fop/pdf/PDFObject.java b/src/java/org/apache/fop/pdf/PDFObject.java
index 544a30cc4..8152fcd83 100644
--- a/src/java/org/apache/fop/pdf/PDFObject.java
+++ b/src/java/org/apache/fop/pdf/PDFObject.java
@@ -251,9 +251,17 @@ public abstract class PDFObject implements PDFWritable {
* @param sb the StringBuffer to write to
*/
protected void formatObject(Object obj, StringBuffer sb) {
- if (obj instanceof PDFWritable) {
+ if (obj == null) {
+ sb.append("null");
+ } else if (obj instanceof PDFWritable) {
sb.append(((PDFWritable)obj).toInlinePDFString());
} else if (obj instanceof Number) {
+ if (obj instanceof Double || obj instanceof Float) {
+ sb.append(PDFNumber.doubleOut(((Number)obj).doubleValue()));
+ } else {
+ sb.append(obj);
+ }
+ } else if (obj instanceof Boolean) {
sb.append(obj);
} else {
sb.append("(").append(obj).append(")");
diff --git a/src/java/org/apache/fop/pdf/PDFReference.java b/src/java/org/apache/fop/pdf/PDFReference.java
index a1b776a81..ed1a8c016 100644
--- a/src/java/org/apache/fop/pdf/PDFReference.java
+++ b/src/java/org/apache/fop/pdf/PDFReference.java
@@ -42,6 +42,14 @@ public class PDFReference implements PDFWritable {
this.indirectReference = obj.referencePDF();
this.objReference = new SoftReference(obj);
}
+
+ /**
+ * Creates a new PDF reference, but without a reference to the original object.
+ * @param ref an object reference
+ */
+ public PDFReference(String ref) {
+ this.indirectReference = ref;
+ }
/**
* Returns the PDF object
@@ -64,4 +72,9 @@ public class PDFReference implements PDFWritable {
return this.indirectReference;
}
+ /** {@inheritDoc} */
+ public String toString() {
+ return toInlinePDFString();
+ }
+
}
diff --git a/src/java/org/apache/fop/pdf/PDFResources.java b/src/java/org/apache/fop/pdf/PDFResources.java
index 70e809aaa..310f8d8d0 100644
--- a/src/java/org/apache/fop/pdf/PDFResources.java
+++ b/src/java/org/apache/fop/pdf/PDFResources.java
@@ -240,7 +240,7 @@ public class PDFResources extends PDFObject {
p = p.append("/XObject <<\n");
for (Iterator iter = xObjects.iterator(); iter.hasNext();) {
PDFXObject xobj = (PDFXObject)iter.next();
- p = p.append(" /Im" + xobj.getXNumber() + " "
+ p = p.append(" " + xobj.getName() + " "
+ xobj.referencePDF()
+ "\n");
}
diff --git a/src/java/org/apache/fop/pdf/PDFStream.java b/src/java/org/apache/fop/pdf/PDFStream.java
index ac025da87..59a8336c5 100644
--- a/src/java/org/apache/fop/pdf/PDFStream.java
+++ b/src/java/org/apache/fop/pdf/PDFStream.java
@@ -64,7 +64,17 @@ public class PDFStream extends AbstractPDFStream {
}
}
-
+
+ /**
+ * Returns an OutputStream that can be used to write to the buffer which is used
+ * to build up the PDF stream.
+ * @return the OutputStream
+ * @throws IOException In case of an I/O problem
+ */
+ public OutputStream getBufferOutputStream() throws IOException {
+ return this.data.getOutputStream();
+ }
+
/**
* Used to set the contents of the PDF stream.
* @param data the contents as a byte array
diff --git a/src/java/org/apache/fop/pdf/PDFT1Stream.java b/src/java/org/apache/fop/pdf/PDFT1Stream.java
index aca7e4f60..08a626009 100644
--- a/src/java/org/apache/fop/pdf/PDFT1Stream.java
+++ b/src/java/org/apache/fop/pdf/PDFT1Stream.java
@@ -61,18 +61,12 @@ public class PDFT1Stream extends AbstractPDFStream {
return length;
}
- /**
- * {@inheritDoc}
- */
- protected String buildStreamDict(String lengthEntry) {
- final String filterEntry = getFilterList().buildFilterDictEntries();
- return (getObjectID()
- + "<< /Length " + lengthEntry
- + " /Length1 " + pfb.getLength1()
- + " /Length2 " + pfb.getLength2()
- + " /Length3 " + pfb.getLength3()
- + "\n" + filterEntry
- + "\n>>\n");
+ /** {@inheritDoc} */
+ protected void populateStreamDict(Object lengthEntry) {
+ super.populateStreamDict(lengthEntry);
+ put("Length1", new Integer(pfb.getLength1()));
+ put("Length2", new Integer(pfb.getLength2()));
+ put("Length3", new Integer(pfb.getLength3()));
}
/**
diff --git a/src/java/org/apache/fop/pdf/PDFTTFStream.java b/src/java/org/apache/fop/pdf/PDFTTFStream.java
index 5176fac66..9f4c543ad 100644
--- a/src/java/org/apache/fop/pdf/PDFTTFStream.java
+++ b/src/java/org/apache/fop/pdf/PDFTTFStream.java
@@ -51,18 +51,12 @@ public class PDFTTFStream extends PDFStream {
return length;
}
- /**
- * {@inheritDoc}
- */
- protected String buildStreamDict(String lengthEntry) {
- final String filterEntry = getFilterList().buildFilterDictEntries();
- return (getObjectID()
- + "<< /Length " + lengthEntry
- + " /Length1 " + origLength
- + "\n" + filterEntry
- + "\n>>\n");
+ /** {@inheritDoc} */
+ protected void populateStreamDict(Object lengthEntry) {
+ put("Length1", origLength);
+ super.populateStreamDict(lengthEntry);
}
-
+
/**
* Sets the TrueType font data.
* @param data the font payload
diff --git a/src/java/org/apache/fop/pdf/PDFXObject.java b/src/java/org/apache/fop/pdf/PDFXObject.java
index 3e70e20c5..d0115fe66 100644
--- a/src/java/org/apache/fop/pdf/PDFXObject.java
+++ b/src/java/org/apache/fop/pdf/PDFXObject.java
@@ -21,13 +21,9 @@ package org.apache.fop.pdf;
// Java
import java.io.IOException;
-import java.io.OutputStream;
-
-/* modified by JKT to integrate with 0.12.0 */
-/* modified by Eric SCHAEFFER to integrate with 0.13.0 */
/**
- * PDF XObject
+ * Abstract base class of PDF XObjects.
*
* A derivative of the PDF Object, is a PDF Stream that has not only a
* dictionary but a stream of image data.
@@ -36,170 +32,32 @@ import java.io.OutputStream;
* This is used as a reference for inserting the same image in the
* document in another place.
*/
-public class PDFXObject extends AbstractPDFStream {
+public abstract class PDFXObject extends AbstractPDFStream {
- private PDFImage pdfimage;
- private int xnum;
-
/**
- * create an XObject with the given number and name and load the
- * image in the object
- *
- * @param xnumber the pdf object X number
- * @param img the pdf image that contains the image data
+ * Create an XObject with the given number.
*/
- public PDFXObject(int xnumber, PDFImage img) {
+ public PDFXObject() {
super();
- this.xnum = xnumber;
- pdfimage = img;
- }
-
- /**
- * Get the xnumber for this pdf object.
- *
- * @return the PDF XObject number
- */
- public int getXNumber() {
- return this.xnum;
- }
-
- /**
- * Output the image as PDF.
- * This sets up the image dictionary and adds the image data stream.
- *
- * @param stream the output stream to write the data
- * @throws IOException if there is an error writing the data
- * @return the length of the data written
- */
- protected int output(OutputStream stream) throws IOException {
- int length = super.output(stream);
-
- // let it gc
- // this object is retained as a reference to inserting
- // the same image but the image data is no longer needed
- pdfimage = null;
- return length;
}
/**
- * {@inheritDoc}
+ * Returns the XObject's name.
+ * @return the name of the XObject
*/
- protected String buildStreamDict(String lengthEntry) {
- String dictEntries = getFilterList().buildFilterDictEntries();
- if (pdfimage.isPS()) {
- return buildDictionaryFromPS(lengthEntry, dictEntries);
- } else {
- return buildDictionaryFromImage(lengthEntry, dictEntries);
- }
+ public PDFName getName() {
+ return (PDFName)get("Name");
}
- private String buildDictionaryFromPS(String lengthEntry,
- String dictEntries) {
- getDocumentSafely().getProfile().verifyPSXObjectsAllowed();
- StringBuffer sb = new StringBuffer(128);
- sb.append(getObjectID());
- sb.append("<</Type /XObject\n");
- sb.append("/Subtype /PS\n");
- sb.append("/Length " + lengthEntry);
-
- sb.append(dictEntries);
- sb.append("\n>>\n");
- return sb.toString();
- }
-
- private String buildDictionaryFromImage(String lengthEntry,
- String dictEntries) {
- StringBuffer sb = new StringBuffer(128);
- sb.append(getObjectID());
- sb.append("<</Type /XObject\n");
- sb.append("/Subtype /Image\n");
- sb.append("/Name /Im" + xnum + "\n");
- sb.append("/Length " + lengthEntry + "\n");
- sb.append("/Width " + pdfimage.getWidth() + "\n");
- sb.append("/Height " + pdfimage.getHeight() + "\n");
- sb.append("/BitsPerComponent " + pdfimage.getBitsPerPixel() + "\n");
-
- PDFICCStream pdfICCStream = pdfimage.getICCStream();
- if (pdfICCStream != null) {
- sb.append("/ColorSpace [/ICCBased "
- + pdfICCStream.referencePDF() + "]\n");
- } else {
- PDFDeviceColorSpace cs = pdfimage.getColorSpace();
- sb.append("/ColorSpace /" + cs.getName()
- + "\n");
- }
-
- if (pdfimage.isInverted()) {
- /* PhotoShop generates CMYK values that's inverse,
- * this will invert the values - too bad if it's not
- * a PhotoShop image...
- */
- if (pdfimage.getColorSpace().getColorSpace() == PDFDeviceColorSpace.DEVICE_CMYK) {
- sb.append("/Decode [ 1.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0 ]\n");
- } else if (pdfimage.getColorSpace().getColorSpace() == PDFDeviceColorSpace.DEVICE_RGB) {
- sb.append("/Decode [ 1.0 0.0 1.0 0.0 1.0 0.0 ]\n");
- } else if (pdfimage.getColorSpace().getColorSpace() == PDFDeviceColorSpace.DEVICE_GRAY) {
- sb.append("/Decode [ 1.0 0.0 ]\n");
- }
- }
-
- if (pdfimage.isTransparent()) {
- PDFColor transp = pdfimage.getTransparentColor();
- sb.append("/Mask ["
- + transp.red255() + " "
- + transp.red255() + " "
- + transp.green255() + " "
- + transp.green255() + " "
- + transp.blue255() + " "
- + transp.blue255() + "]\n");
- }
- String ref = pdfimage.getSoftMask();
- if (ref != null) {
- sb.append("/SMask " + ref + "\n");
- }
-
- sb.append(dictEntries);
- sb.append("\n>>\n");
- return sb.toString();
+ /** {@inheritDoc} */
+ protected void populateStreamDict(Object lengthEntry) {
+ put("Type", new PDFName("XObject"));
+ super.populateStreamDict(lengthEntry);
}
- /**
- * {@inheritDoc}
- */
- protected void outputRawStreamData(OutputStream out) throws IOException {
- pdfimage.outputContents(out);
- }
-
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected int getSizeHint() throws IOException {
return 0;
}
- /**
- * {@inheritDoc}
- */
- protected void prepareImplicitFilters() {
- PDFFilter pdfFilter = pdfimage.getPDFFilter();
- if (pdfFilter != null) {
- getFilterList().ensureFilterInPlace(pdfFilter);
- }
- }
-
- /**
- * This sets up the default filters for XObjects. It uses the PDFImage
- * instance to determine what default filters to apply.
- * {@inheritDoc}
- */
- protected void setupFilterList() {
- if (!getFilterList().isInitialized()) {
- getFilterList().addDefaultFilters(
- getDocumentSafely().getFilterMap(),
- pdfimage.getFilterHint());
- }
- super.setupFilterList();
- }
-
-
}
diff --git a/src/java/org/apache/fop/render/pdf/FopPDFImage.java b/src/java/org/apache/fop/render/pdf/FopPDFImage.java
index 6f16561bc..c9b0e3a7b 100644
--- a/src/java/org/apache/fop/render/pdf/FopPDFImage.java
+++ b/src/java/org/apache/fop/render/pdf/FopPDFImage.java
@@ -18,32 +18,32 @@
/* $Id$ */
package org.apache.fop.render.pdf;
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_Profile;
+import java.io.IOException;
+import java.io.OutputStream;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.fop.image.EPSImage;
+import org.apache.fop.image.FopImage;
+import org.apache.fop.image.TIFFImage;
+import org.apache.fop.pdf.BitmapImage;
+import org.apache.fop.pdf.CCFFilter;
+import org.apache.fop.pdf.DCTFilter;
+import org.apache.fop.pdf.PDFColor;
import org.apache.fop.pdf.PDFConformanceException;
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.pdf.PDFDictionary;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFFilter;
import org.apache.fop.pdf.PDFFilterList;
import org.apache.fop.pdf.PDFICCBasedColorSpace;
-import org.apache.fop.pdf.PDFImage;
-import org.apache.fop.pdf.PDFFilter;
import org.apache.fop.pdf.PDFICCStream;
-import org.apache.fop.pdf.PDFColor;
-import org.apache.fop.pdf.PDFDocument;
-import org.apache.fop.pdf.DCTFilter;
-import org.apache.fop.pdf.CCFFilter;
-import org.apache.fop.pdf.PDFDeviceColorSpace;
-import org.apache.fop.pdf.PDFXObject;
-import org.apache.fop.pdf.BitmapImage;
+import org.apache.fop.pdf.PDFImage;
+import org.apache.fop.pdf.PDFReference;
import org.apache.fop.util.ColorProfileUtil;
-import org.apache.fop.image.FopImage;
-import org.apache.fop.image.EPSImage;
-import org.apache.fop.image.TIFFImage;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.awt.color.ColorSpace;
-import java.awt.color.ICC_Profile;
-
/**
* PDFImage implementation for the PDF renderer.
*/
@@ -56,7 +56,7 @@ public class FopPDFImage implements PDFImage {
private PDFICCStream pdfICCStream = null;
private PDFFilter pdfFilter = null;
private String maskRef;
- private String softMaskRef;
+ private PDFReference softMask;
private boolean isPS = false;
private boolean isCCF = false;
private boolean isDCT = false;
@@ -104,8 +104,10 @@ public class FopPDFImage implements PDFImage {
} else if (comp == 4) {
pdfFilter = new CCFFilter();
pdfFilter.setApplied(true);
- ((CCFFilter)pdfFilter).setDecodeParms("<< /K -1 /Columns "
- + tiffImage.getWidth() + " >>");
+ PDFDictionary dict = new PDFDictionary();
+ dict.put("K", -1);
+ dict.put("Columns", tiffImage.getWidth());
+ ((CCFFilter)pdfFilter).setDecodeParms(dict);
isCCF = true;
} else if (comp == 6) {
pdfFilter = new DCTFilter();
@@ -149,16 +151,15 @@ public class FopPDFImage implements PDFImage {
doc.getProfile().verifyTransparencyAllowed(fopImage.getOriginalURI());
//TODO Implement code to combine image with background color if transparency is not
//allowed (need BufferedImage support for that)
- byte [] softMask = fopImage.getSoftMask();
- if (softMask == null) {
+ byte [] softMaskBitmap = fopImage.getSoftMask();
+ if (softMaskBitmap == null) {
return;
}
BitmapImage fopimg = new BitmapImage
("Mask:" + key, fopImage.getWidth(), fopImage.getHeight(),
- softMask, null);
+ softMaskBitmap, null);
fopimg.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
- PDFXObject xobj = doc.addImage(null, fopimg);
- softMaskRef = xobj.referencePDF();
+ softMask = doc.addImage(null, fopimg).makeReference();
}
if (doc.getProfile().getPDFAMode().isPDFA1LevelB()) {
if (pdfCS != null
@@ -236,13 +237,16 @@ public class FopPDFImage implements PDFImage {
return maskRef;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public String getSoftMask() {
- return softMaskRef;
+ return softMask.toInlinePDFString();
}
+ /** {@inheritDoc} */
+ public PDFReference getSoftMaskReference() {
+ return softMask;
+ }
+
/** @return true for CMYK images generated by Adobe Photoshop */
public boolean isInverted() {
return fopImage.isInverted();
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandler.java b/src/java/org/apache/fop/render/pdf/PDFImageHandler.java
new file mode 100644
index 000000000..52043b38e
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandler.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+
+import java.io.IOException;
+
+import org.apache.fop.image.FopImage;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFXObject;
+
+/**
+ * This interface is used for handling all sorts of image type for PDF output.
+ */
+public interface PDFImageHandler {
+
+ /**
+ * Returns the MIME type supported by this instance.
+ * @return the MIME type
+ */
+ String getSupportedMimeType();
+
+ /**
+ * Generates the PDF objects for the given FopImage instance and returns
+ * the resulting XObject.
+ * @param image the image to be handled
+ * @param uri the URI of the image
+ * @param pdfDoc the target PDF document
+ * @return the generated XObject
+ * @throws IOException if an I/O error occurs
+ */
+ PDFXObject generateImage(FopImage image, String uri, PDFDocument pdfDoc) throws IOException;
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRegistry.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRegistry.java
new file mode 100644
index 000000000..36e4ea7e1
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRegistry.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.util.Service;
+
+/**
+ * This class holds references to various image handlers used by the PDF renderer. It also
+ * supports automatic discovery of additional handlers available through
+ * the class path.
+ */
+public class PDFImageHandlerRegistry {
+
+ /** the logger */
+ private static Log log = LogFactory.getLog(PDFImageHandlerRegistry.class);
+
+ /** Map containing PDF image handlers for various MIME types */
+ private Map handlers = new java.util.HashMap();
+
+ /**
+ * Default constructor.
+ */
+ public PDFImageHandlerRegistry() {
+ discoverHandlers();
+ }
+
+ /**
+ * Add an PDFImageHandler. The handler itself is inspected to find out what it supports.
+ * @param classname the fully qualified class name
+ */
+ public void addHandler(String classname) {
+ try {
+ PDFImageHandler handlerInstance
+ = (PDFImageHandler)Class.forName(classname).newInstance();
+ addHandler(handlerInstance);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("Could not find "
+ + classname);
+ } catch (InstantiationException e) {
+ throw new IllegalArgumentException("Could not instantiate "
+ + classname);
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException("Could not access "
+ + classname);
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException(classname
+ + " is not an "
+ + PDFImageHandler.class.getName());
+ }
+ }
+
+ /**
+ * Add an image handler. The handler itself is inspected to find out what it supports.
+ * @param handler the PDFImageHandler instance
+ */
+ public void addHandler(PDFImageHandler handler) {
+ String mime = handler.getSupportedMimeType();
+ handlers.put(mime, handler);
+ }
+
+ /**
+ * Returns an PDFImageHandler which handles an specific image type given the MIME type
+ * of the image.
+ * @param mime the requested MIME type
+ * @return the PDFImageHandler responsible for handling the image or null if none is available
+ */
+ public PDFImageHandler getHandler(String mime) {
+ PDFImageHandler handler;
+
+ handler = (PDFImageHandler)handlers.get(mime);
+ return handler;
+ }
+
+ /**
+ * Discovers PDFImageHandler implementations through the classpath and dynamically
+ * registers them.
+ */
+ private void discoverHandlers() {
+ // add mappings from available services
+ Iterator providers = Service.providers(PDFImageHandler.class);
+ if (providers != null) {
+ while (providers.hasNext()) {
+ PDFImageHandler handler = (PDFImageHandler)providers.next();
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("Dynamically adding PDFImageHandler: "
+ + handler.getClass().getName());
+ }
+ addHandler(handler);
+ } catch (IllegalArgumentException e) {
+ log.error("Error while adding PDFImageHandler", e);
+ }
+
+ }
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
index cfe02e935..a03179692 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
@@ -20,58 +20,56 @@
package org.apache.fop.render.pdf;
// Java
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URL;
-import java.awt.geom.Point2D;
import java.awt.Color;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
-import java.awt.geom.Rectangle2D;
import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
import java.util.Iterator;
-import java.util.Map;
import java.util.List;
+import java.util.Map;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
-// XML
-import org.w3c.dom.Document;
-
-// Avalon
import org.apache.commons.io.IOUtils;
-
-// FOP
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
+import org.apache.fop.area.BookmarkData;
import org.apache.fop.area.CTM;
+import org.apache.fop.area.DestinationData;
import org.apache.fop.area.LineArea;
import org.apache.fop.area.OffDocumentExtensionAttachment;
+import org.apache.fop.area.OffDocumentItem;
import org.apache.fop.area.PageViewport;
import org.apache.fop.area.RegionViewport;
import org.apache.fop.area.Trait;
-import org.apache.fop.area.OffDocumentItem;
-import org.apache.fop.area.BookmarkData;
import org.apache.fop.area.inline.AbstractTextArea;
-import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.Image;
-import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.InlineParent;
-import org.apache.fop.area.inline.WordArea;
+import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.SpaceArea;
-import org.apache.fop.fonts.Typeface;
+import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.area.inline.WordArea;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.apache.fop.fo.extensions.xmp.XMPMetadata;
import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.Typeface;
import org.apache.fop.image.FopImage;
import org.apache.fop.image.ImageFactory;
import org.apache.fop.image.XMLImage;
-import org.apache.fop.pdf.PDFAction;
import org.apache.fop.pdf.PDFAMode;
+import org.apache.fop.pdf.PDFAction;
import org.apache.fop.pdf.PDFAnnotList;
import org.apache.fop.pdf.PDFColor;
import org.apache.fop.pdf.PDFConformanceException;
@@ -102,14 +100,10 @@ import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.RendererContext;
import org.apache.fop.util.CharUtilities;
import org.apache.fop.util.ColorProfileUtil;
-import org.apache.fop.fo.Constants;
-import org.apache.fop.fo.extensions.ExtensionAttachment;
-import org.apache.fop.fo.extensions.xmp.XMPMetadata;
import org.apache.xmlgraphics.xmp.Metadata;
import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter;
import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;
-
-import org.apache.fop.area.DestinationData;
+import org.w3c.dom.Document;
/**
* Renderer that renders areas to PDF.
@@ -256,6 +250,9 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
*/
protected boolean inTextMode = false;
+ /** Image handler registry */
+ private PDFImageHandlerRegistry imageHandlerRegistry = new PDFImageHandlerRegistry();
+
/**
* create the PDF renderer
*/
@@ -1659,21 +1656,21 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
}
/**
- * Adds a PDF XObject (a bitmap) to the PDF that will later be referenced.
+ * Adds a PDF XObject (a bitmap or form) to the PDF that will later be referenced.
* @param url URL of the bitmap
* @param pos Position of the bitmap
*/
protected void putImage(String url, Rectangle2D pos) {
- PDFXObject xobject = pdfDoc.getImage(url);
+ url = ImageFactory.getURL(url);
+ PDFXObject xobject = pdfDoc.getXObject(url);
if (xobject != null) {
float w = (float) pos.getWidth() / 1000f;
float h = (float) pos.getHeight() / 1000f;
placeImage((float)pos.getX() / 1000f,
- (float)pos.getY() / 1000f, w, h, xobject.getXNumber());
+ (float)pos.getY() / 1000f, w, h, xobject);
return;
}
- url = ImageFactory.getURL(url);
ImageFactory fact = userAgent.getFactory().getImageFactory();
FopImage fopimage = fact.getImage(url, userAgent);
if (fopimage == null) {
@@ -1683,7 +1680,24 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
return;
}
String mime = fopimage.getMimeType();
- if ("text/xml".equals(mime)) {
+
+ //First check for a dynamically registered handler
+ PDFImageHandler handler = imageHandlerRegistry.getHandler(mime);
+ if (handler != null) {
+ PDFXObject xobj;
+ try {
+ xobj = handler.generateImage(fopimage, url, pdfDoc);
+ } catch (IOException ioe) {
+ log.error("I/O error while handling " + mime + " image", ioe);
+ return;
+ }
+ fact.releaseImage(url, userAgent);
+
+ float w = (float)pos.getWidth() / 1000f;
+ float h = (float)pos.getHeight() / 1000f;
+ placeImage((float) pos.getX() / 1000,
+ (float) pos.getY() / 1000, w, h, xobj);
+ } else if ("text/xml".equals(mime)) {
if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
return;
}
@@ -1701,7 +1715,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
renderDocument(doc, ns, pos, null);
} else if ("image/eps".equals(mime)) {
FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
- int xobj = pdfDoc.addImage(currentContext, pdfimage).getXNumber();
+ PDFXObject xobj = pdfDoc.addImage(currentContext, pdfimage);
fact.releaseImage(url, userAgent);
float w = (float)pos.getWidth() / 1000f;
@@ -1710,7 +1724,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
(float) pos.getY() / 1000, w, h, xobj);
} else if ("image/jpeg".equals(mime) || "image/tiff".equals(mime)) {
FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
- int xobj = pdfDoc.addImage(currentContext, pdfimage).getXNumber();
+ PDFXObject xobj = pdfDoc.addImage(currentContext, pdfimage);
fact.releaseImage(url, userAgent);
float w = (float)pos.getWidth() / 1000f;
@@ -1722,7 +1736,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
return;
}
FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
- int xobj = pdfDoc.addImage(currentContext, pdfimage).getXNumber();
+ PDFXObject xobj = pdfDoc.addImage(currentContext, pdfimage);
fact.releaseImage(url, userAgent);
float w = (float) pos.getWidth() / 1000f;
@@ -1745,15 +1759,15 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
* @param y Y coordinate
* @param w width for image
* @param h height for image
- * @param xobj object number of the referenced image
+ * @param xobj the image XObject
*/
- protected void placeImage(float x, float y, float w, float h, int xobj) {
+ protected void placeImage(float x, float y, float w, float h, PDFXObject xobj) {
saveGraphicsState();
currentStream.add(format(w) + " 0 0 "
+ format(-h) + " "
+ format(currentIPPosition / 1000f + x) + " "
+ format(currentBPPosition / 1000f + h + y)
- + " cm\n" + "/Im" + xobj + " Do\n");
+ + " cm\n" + xobj.getName() + " Do\n");
restoreGraphicsState();
}
diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java
index 4132e17e8..dc93c3371 100644
--- a/src/java/org/apache/fop/svg/PDFGraphics2D.java
+++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java
@@ -19,80 +19,77 @@
package org.apache.fop.svg;
-import org.apache.fop.pdf.PDFConformanceException;
-import org.apache.fop.pdf.PDFResourceContext;
-import org.apache.fop.pdf.PDFResources;
-import org.apache.fop.pdf.PDFGState;
-import org.apache.fop.pdf.PDFDeviceColorSpace;
-import org.apache.fop.pdf.PDFColor;
-import org.apache.fop.pdf.PDFState;
-import org.apache.fop.pdf.PDFNumber;
-import org.apache.fop.pdf.PDFText;
-import org.apache.fop.pdf.PDFXObject;
-import org.apache.fop.pdf.PDFPattern;
-import org.apache.fop.pdf.PDFDocument;
-import org.apache.fop.pdf.PDFLink;
-import org.apache.fop.pdf.PDFAnnotList;
-import org.apache.fop.pdf.BitmapImage;
-import org.apache.fop.fonts.FontInfo;
-import org.apache.fop.fonts.Font;
-import org.apache.fop.fonts.FontSetup;
-import org.apache.fop.fonts.FontTriplet;
-import org.apache.fop.fonts.LazyFont;
-import org.apache.fop.image.JpegImage;
-import org.apache.fop.fonts.CIDFont;
-import org.apache.fop.render.pdf.FopPDFImage;
-import org.apache.fop.util.ColorExt;
-
-import org.apache.xmlgraphics.java2d.AbstractGraphics2D;
-import org.apache.xmlgraphics.java2d.GraphicContext;
-
-import org.apache.batik.ext.awt.RadialGradientPaint;
-import org.apache.batik.ext.awt.LinearGradientPaint;
-import org.apache.batik.ext.awt.MultipleGradientPaint;
-import org.apache.batik.ext.awt.RenderingHintsKeyExt;
-import org.apache.batik.gvt.PatternPaint;
-import org.apache.batik.gvt.GraphicsNode;
-
-import java.text.AttributedCharacterIterator;
-import java.text.CharacterIterator;
+import java.awt.AlphaComposite;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
-import java.awt.Color;
import java.awt.GraphicsConfiguration;
-/* java.awt.Font is not imported to avoid confusion with
- org.apache.fop.fonts.Font */
-import java.awt.GradientPaint;
import java.awt.Image;
-import java.awt.Shape;
-import java.awt.Stroke;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Rectangle;
-import java.awt.Dimension;
-import java.awt.BasicStroke;
-import java.awt.AlphaComposite;
-import java.awt.geom.AffineTransform;
+import java.awt.Shape;
+import java.awt.Stroke;
import java.awt.color.ColorSpace;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
-import java.awt.image.DirectColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
+import java.awt.image.DirectColorModel;
import java.awt.image.ImageObserver;
-import java.awt.image.RenderedImage;
import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.awt.image.renderable.RenderableImage;
-import java.awt.geom.PathIterator;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-import java.io.StringWriter;
import java.io.IOException;
import java.io.OutputStream;
-
-import java.util.Map;
+import java.io.StringWriter;
+import java.text.AttributedCharacterIterator;
+import java.text.CharacterIterator;
import java.util.List;
+import java.util.Map;
+
+import org.apache.batik.ext.awt.LinearGradientPaint;
+import org.apache.batik.ext.awt.MultipleGradientPaint;
+import org.apache.batik.ext.awt.RadialGradientPaint;
+import org.apache.batik.ext.awt.RenderingHintsKeyExt;
+import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.gvt.PatternPaint;
+import org.apache.fop.fonts.CIDFont;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontSetup;
+import org.apache.fop.fonts.FontTriplet;
+import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.image.JpegImage;
+import org.apache.fop.pdf.BitmapImage;
+import org.apache.fop.pdf.PDFAnnotList;
+import org.apache.fop.pdf.PDFColor;
+import org.apache.fop.pdf.PDFConformanceException;
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFGState;
+import org.apache.fop.pdf.PDFImageXObject;
+import org.apache.fop.pdf.PDFLink;
+import org.apache.fop.pdf.PDFName;
+import org.apache.fop.pdf.PDFNumber;
+import org.apache.fop.pdf.PDFPattern;
+import org.apache.fop.pdf.PDFResourceContext;
+import org.apache.fop.pdf.PDFResources;
+import org.apache.fop.pdf.PDFState;
+import org.apache.fop.pdf.PDFText;
+import org.apache.fop.pdf.PDFXObject;
+import org.apache.fop.render.pdf.FopPDFImage;
+import org.apache.fop.util.ColorExt;
+import org.apache.xmlgraphics.java2d.AbstractGraphics2D;
+import org.apache.xmlgraphics.java2d.GraphicContext;
/**
* PDF Graphics 2D.
@@ -406,8 +403,8 @@ public class PDFGraphics2D extends AbstractGraphics2D {
String key = "__AddJPEG_" + hashCode() + "_" + jpegCount[0];
jpegCount[0]++;
FopPDFImage fopimage = new FopPDFImage(jpeg, key);
- int xObjectNum = this.pdfDoc.addImage(resourceContext,
- fopimage).getXNumber();
+ PDFName imageName = this.pdfDoc.addImage(resourceContext,
+ fopimage).getName();
AffineTransform at = getTransform();
double[] matrix = new double[6];
at.getMatrix(matrix);
@@ -421,8 +418,8 @@ public class PDFGraphics2D extends AbstractGraphics2D {
currentStream.write("" + width + " 0 0 "
+ (-height) + " "
+ x + " "
- + (y + height) + " cm\n" + "/Im"
- + xObjectNum + " Do\nQ\n");
+ + (y + height) + " cm\n"
+ + imageName + " Do\nQ\n");
if (outputStream != null) {
try {
@@ -518,7 +515,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
// the pdf document. If so, we just reuse the reference;
// otherwise we have to build a FopImage and add it to the pdf
// document
- PDFXObject imageInfo = pdfDoc.getImage("TempImage:" + img.toString());
+ PDFXObject imageInfo = pdfDoc.getXObject("TempImage:" + img.toString());
if (imageInfo == null) {
// OK, have to build and add a PDF image
@@ -579,7 +576,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
+ img.toString(), buf.getWidth(),
buf.getHeight(), mask, null);
fopimg.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
- PDFXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
+ PDFImageXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
ref = xobj.referencePDF();
if (outputStream != null) {
@@ -623,7 +620,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
writeClip(imclip);
currentStream.write("" + width + " 0 0 " + (-height) + " " + x
+ " " + (y + height) + " cm\n" + "/Im"
- + imageInfo.getXNumber() + " Do\nQ\n");
+ + imageInfo.getName() + " Do\nQ\n");
return true;
}
@@ -1189,7 +1186,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
PaintContext pctx = paint.createContext(rgbCM, devBounds, usrBounds,
at, getRenderingHints());
- PDFXObject imageInfo = pdfDoc.getImage
+ PDFXObject imageInfo = pdfDoc.getXObject
("TempImage:" + pctx.toString());
if (imageInfo != null) {
resourceContext.getPDFResources().addXObject(imageInfo);
@@ -1237,7 +1234,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
BitmapImage fopimg = new BitmapImage
("TempImageMask:" + pctx.toString(), devW, devH, mask, null);
fopimg.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
- PDFXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
+ PDFImageXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
maskRef = xobj.referencePDF();
if (outputStream != null) {
@@ -1265,8 +1262,8 @@ public class PDFGraphics2D extends AbstractGraphics2D {
currentStream.write("q\n");
writeClip(shape);
currentStream.write("" + usrW + " 0 0 " + (-usrH) + " " + usrX
- + " " + (usrY + usrH) + " cm\n" + "/Im"
- + imageInfo.getXNumber() + " Do\nQ\n");
+ + " " + (usrY + usrH) + " cm\n"
+ + imageInfo.getName() + " Do\nQ\n");
return true;
}
diff --git a/status.xml b/status.xml
index 975ffc2aa..3b37daea4 100644
--- a/status.xml
+++ b/status.xml
@@ -28,6 +28,10 @@
<changes>
<release version="FOP Trunk">
+ <action context="Code" dev="JM" type="add">
+ Added generic structures to the PDF library in order to support PDF
+ file in fo:external-graphic later.
+ </action>
<action context="Code" dev="JM" type="add" fixes-bug="43597" due-to="Max Berger">
Added support for SVGZ.
</action>