]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Merged https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_PDF_in_PDF...
authorJeremias Maerki <jeremias@apache.org>
Fri, 26 Oct 2007 07:48:14 +0000 (07:48 +0000)
committerJeremias Maerki <jeremias@apache.org>
Fri, 26 Oct 2007 07:48:14 +0000 (07:48 +0000)
Adds generic structures in PDF so it is possible to embed external PDF documents, for example to support PDF files in fo:external-graphic for PDF output. See also: http://mail-archives.apache.org/mod_mbox/xmlgraphics-fop-dev/200708.mbox/%3c20070806165027.5C59.DEV@jeremias-maerki.ch%3e
I tried to keep the changes on the API of the PDF library minimal but there could still be some incompatibilities for anyone who uses it separately.

This also adds support for dynamically registering an image handler. That's just a temporary solution until the image package redesign is done. But it works.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@588547 13f79535-47bb-0310-9956-ffa450edef68

39 files changed:
src/java/org/apache/fop/image/ImageFactory.java
src/java/org/apache/fop/image/RegisterableImageProvider.java [new file with mode: 0644]
src/java/org/apache/fop/image/analyser/ImageReaderFactory.java
src/java/org/apache/fop/pdf/ASCII85Filter.java
src/java/org/apache/fop/pdf/ASCIIHexFilter.java
src/java/org/apache/fop/pdf/AbstractPDFStream.java
src/java/org/apache/fop/pdf/BitmapImage.java
src/java/org/apache/fop/pdf/CCFFilter.java
src/java/org/apache/fop/pdf/DCTFilter.java
src/java/org/apache/fop/pdf/FlateFilter.java
src/java/org/apache/fop/pdf/NullFilter.java
src/java/org/apache/fop/pdf/PDFArray.java
src/java/org/apache/fop/pdf/PDFDictionary.java
src/java/org/apache/fop/pdf/PDFDocument.java
src/java/org/apache/fop/pdf/PDFEncryptionJCE.java
src/java/org/apache/fop/pdf/PDFFactory.java
src/java/org/apache/fop/pdf/PDFFilter.java
src/java/org/apache/fop/pdf/PDFFilterList.java
src/java/org/apache/fop/pdf/PDFFormXObject.java
src/java/org/apache/fop/pdf/PDFICCStream.java
src/java/org/apache/fop/pdf/PDFImage.java
src/java/org/apache/fop/pdf/PDFImageXObject.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFMetadata.java
src/java/org/apache/fop/pdf/PDFName.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFNull.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFNumber.java
src/java/org/apache/fop/pdf/PDFObject.java
src/java/org/apache/fop/pdf/PDFReference.java
src/java/org/apache/fop/pdf/PDFResources.java
src/java/org/apache/fop/pdf/PDFStream.java
src/java/org/apache/fop/pdf/PDFT1Stream.java
src/java/org/apache/fop/pdf/PDFTTFStream.java
src/java/org/apache/fop/pdf/PDFXObject.java
src/java/org/apache/fop/render/pdf/FopPDFImage.java
src/java/org/apache/fop/render/pdf/PDFImageHandler.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/PDFImageHandlerRegistry.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/PDFRenderer.java
src/java/org/apache/fop/svg/PDFGraphics2D.java
status.xml

index ee5b1ae75ebacbcdb326264724337862c555bba6..5c9f198e83c356f0b978a9d24397ec45ababca2e 100644 (file)
@@ -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 (file)
index 0000000..fd79ebd
--- /dev/null
@@ -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();
+
+}
index fce2d0b40f0559aeb4c839b946dbf36a7d06ed13..c07d68d3934d91f08833b448bee498726864616f 100644 (file)
@@ -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());
index ef50bae93143654a3159ca1ab5d6950b8ad932e1..2114ea540ae375407612545a434772892cc8f01f 100644 (file)
@@ -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);
+        }
     }
 
 }
index ba5f15882d914d6a94d01d4081478dad6930ec3e..41c42899895ec77d6b8f1aff41d1b2df57ab7270 100644 (file)
@@ -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);
+        }
     }
 
 }
index 364b42d66f878e4efa73a76e34e0c6a4563d03be..e208a39160a36df0a3b9762e3fa67aaed2b0dfcd 100644 (file)
@@ -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);
     }
 
     /**
index e855dc61a76127cab6fe90a7e92424392d388b70..461ec0a64b545a9aa662ddb4daa2d47059ed4a0e 100644 (file)
@@ -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;
     }
 
index 98d23d25e1c889efcfcefd7c112f52861a45172b..03561629c1a961515ba388c602fee7a44b43a6e0 100644 (file)
@@ -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;
     }
 
index 08eca97389c5978ca0cf0a90978549772a3653c6..92809d927a5fd8a19ffcda62dd513f72bf4ebec3 100644 (file)
@@ -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;
     }
 
index 83eb1ac08261a8351cf150eedcbd852c5856327e..d7dc81d1ff4139fcf8b92c122f6b5c61e7d941f3 100644 (file)
@@ -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);
+        }
     }
 
 }
index 7c7f54b68187e50ba71dbe5128e04a6ddf15a5d2..6f2e7f06933ebf990ead50db1543a4f6b5b0c84e 100644 (file)
@@ -38,7 +38,7 @@ public class NullFilter extends PDFFilter {
     /**
      * {@inheritDoc}
      */
-    public String getDecodeParms() {
+    public PDFObject getDecodeParms() {
         return null;
     }
 
index 1140363ecd27682c7cd94ab37eabef12bea2f748..2dd68ad8bca19b4645e5ca73cec00ab6f84aa3eb 100644 (file)
@@ -96,6 +96,15 @@ public class PDFArray extends PDFObject {
         this.values.set(index, obj);
     }
     
+    /**
+     * 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
@@ -113,6 +122,14 @@ public class PDFArray extends PDFObject {
         this.values.add(obj);
     }
     
+    /**
+     * Adds a new value to the array.
+     * @param value the value
+     */
+    public void add(double value) {
+        this.values.add(new Double(value));
+    }
+    
     /**
      * {@inheritDoc}
      */
index 73878bdc6dc27caa1d171eb4d0f69ec4a69320ad..71393cace64d671743635960492ec6062aaf96fc 100644 (file)
@@ -59,6 +59,18 @@ public class PDFDictionary extends PDFObject {
         this.entries.put(name, value);
     }
     
+    /**
+     * 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
@@ -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");
     }
 
 }
index 748c6b3a8fefae2222b87bc0c171ac0e43699523..045d521739458755796a762e5e51788a699d6eb8 100644 (file)
@@ -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;
     }
 
index 099948864f7868ba11b3f5e484b737ee96321040..203ca90e1ca5f4922d9a06cd76ac2cb6c01c6d25 100644 (file)
@@ -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;
         }
 
index d1e81647ac8a47403a3c4f0558077f2072cdba52..f2fe97139fd588d89847e6f6dbab6d6d0fdfedbc 100644 (file)
@@ -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
index 82efc4c4642bc3823e32845bd0d7b0a5be42941a..ae99ed31c56041a97043d593cab29d6474e8b554 100644 (file)
@@ -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.
index e0ee7eb4ba57bacbc51eacc518170df5b4bf53fb..8004e1e60e3b0f8b18aabad5fef7e9001386bdc7 100644 (file)
@@ -48,6 +48,8 @@ public class PDFFilterList {
     private List filters = new java.util.ArrayList();
 
     private boolean ignoreASCIIFilters = false;
+    
+    private boolean disableAllFilters = false;
         
     /**
      * Default constructor.
@@ -67,6 +69,22 @@ public class PDFFilterList {
         this.ignoreASCIIFilters = ignoreASCIIFilters;
     }
 
+    /**
+     * 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);
index 519bae47abf94d6e7c0a5427bbac4ba6e9feaaf1..456419f800b28ab9dd4bbe047780be642260e9ba 100644 (file)
@@ -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);
+    }
     
 }
 
index 6c08953cb14b208ac4a6ecd6996864ab21b02cb0..da1e8353c1401bebf6816a4b84ef42bb92400378 100644 (file)
@@ -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);
     }
 
 }
index db233d1d4094ed3c63f0ae0b64377d9fc4cfc5c7..dd24240b5fb7f301c3e3995011796c0b53e51c57 100644 (file)
@@ -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 (file)
index 0000000..4cc1741
--- /dev/null
@@ -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();
+    }
+    
+
+}
index 74ffb1580ac016d0bc472512f71bbb526e96b1b9..06750298658bdc881d29b4c91b918264846601be 100644 (file)
@@ -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 (file)
index 0000000..4c3c3d0
--- /dev/null
@@ -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 (file)
index 0000000..e3fb44a
--- /dev/null
@@ -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();
+    }
+    
+}
index 6da28f9be431db36593af8bfa3bd47d7f094cf22..2a8d6c47246cbdba4ebb52661171f1b01d489419 100644 (file)
@@ -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();
     }
 
index 544a30cc43979b897a0fc3c20bc0ddb64cb08eeb..8152fcd8336aed443fabea027291026ca050fcd1 100644 (file)
@@ -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(")");
index a1b776a818e07046396be4e6c9769398a309f8fd..ed1a8c01628019783101ac1702203e9197a6ca09 100644 (file)
@@ -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();
+    }
+    
 }
index 70e809aaaa4a08cb3055bcecb89dd35b9b22d69f..310f8d8d0661da1dec5f296e9d90d0938c9f13e4 100644 (file)
@@ -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");
             }
index ac025da87c59e60d7e5e9b7347b4055bc742c95c..59a8336c51e5b332afdf480819d60ebc11f4a8fd 100644 (file)
@@ -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
index aca7e4f60c58e6940c63e9495f497aacb3780c95..08a6260098f7a33b09d4e15d0ebc90811447e20d 100644 (file)
@@ -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()));
     }
 
     /**
index 5176fac66bf6de1175befbcfd0dba4c046179fe3..9f4c543ada29ac5afa0660a3bba156ccc0a21e94 100644 (file)
@@ -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
index 3e70e20c58aedd1dfacdddb7fecfb7fbe89ce33b..d0115fe66ffe975717aef8ed9ec240159f7abc43 100644 (file)
@@ -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();
-    }
-    
-
 }
index 6f16561bc0c5b5380d6de0b9d0c0de7562ac0cad..c9b0e3a7b036c6d1a36686b81e43be0a1bda53c8 100644 (file)
 /* $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 (file)
index 0000000..52043b3
--- /dev/null
@@ -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 (file)
index 0000000..36e4ea7
--- /dev/null
@@ -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);
+                }
+
+            }
+        }
+    }
+}
index cfe02e9359b781914a96f26414c7c196616e3165..a031796924e984110215c43b89f25ebb7dcfb78e 100644 (file)
 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();
     }
 
index 4132e17e877a60a5cd1b04eb50612799afb5c2b1..dc93c3371aa284f6e72f769501860c9339769007 100644 (file)
 
 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;
     }
 
index 975ffc2aa35b09c6064842887bab254e019f0f66..3b37daea4c79621b8c2bcbaec9b24da3e1c581f3 100644 (file)
 
   <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>