]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Added initial support for PDF/X-3:2003 (ISO 15930-6:2003(E)).
authorJeremias Maerki <jeremias@apache.org>
Wed, 14 Jun 2006 14:34:29 +0000 (14:34 +0000)
committerJeremias Maerki <jeremias@apache.org>
Wed, 14 Jun 2006 14:34:29 +0000 (14:34 +0000)
Fixed bugs in PDF/A support: Transparency is now switched off and the CIDSet object is generated for CID fonts.
PDF/A and PDF/X are now "profiles" (see PDFProfile) which are used to control the allowed, required and forbidden features in PDF. PDF/A-1b and PDF/X-3:2003 can both be activated simultaneously as they are compatible.
For PDF/X, color handling has been improved a little (PDFColorSpace is now an interface, the old PDFColorSpace is now PDFDeviceColorSpace) and most importantly, the "DefaultRGB" color space is now mapped to the sRGB color space (for all colors in DeviceRGB) which ensures that all sRGB colors from XSL-FO are also sRGB colors in PDF which wasn't necessarily the case before.
For PDF/X, added support for a custom, non-sRGB output profile because PDF/X requires a "Output Device Profile" (which sRGB is not).
ICC profiles are now shared/cached among images.
Added support for fox:conversion-mode="bitmap" in the PDFRenderer to handle SVGs with transparency when transparency is forbidden (PDF/X and PDF/A). Only drawback: the image is not combined with the background. An opaque bitmap with a white background is generated.

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

29 files changed:
src/java/org/apache/fop/pdf/BitmapImage.java
src/java/org/apache/fop/pdf/PDFColor.java
src/java/org/apache/fop/pdf/PDFColorSpace.java
src/java/org/apache/fop/pdf/PDFDeviceColorSpace.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFDocument.java
src/java/org/apache/fop/pdf/PDFFactory.java
src/java/org/apache/fop/pdf/PDFFont.java
src/java/org/apache/fop/pdf/PDFFontDescriptor.java
src/java/org/apache/fop/pdf/PDFFontNonBase14.java
src/java/org/apache/fop/pdf/PDFICCBasedColorSpace.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFICCStream.java
src/java/org/apache/fop/pdf/PDFImage.java
src/java/org/apache/fop/pdf/PDFInfo.java
src/java/org/apache/fop/pdf/PDFLink.java
src/java/org/apache/fop/pdf/PDFMetadata.java
src/java/org/apache/fop/pdf/PDFPage.java
src/java/org/apache/fop/pdf/PDFPathPaint.java
src/java/org/apache/fop/pdf/PDFProfile.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFResources.java
src/java/org/apache/fop/pdf/PDFShading.java
src/java/org/apache/fop/pdf/PDFXMode.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFXObject.java
src/java/org/apache/fop/render/RendererContext.java
src/java/org/apache/fop/render/pdf/FopPDFImage.java
src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java
src/java/org/apache/fop/render/pdf/PDFRenderer.java
src/java/org/apache/fop/render/pdf/PDFSVGHandler.java
src/java/org/apache/fop/svg/PDFGraphics2D.java
status.xml

index 4e6664cff60802aee3e3309bf5ad487af146db1d..c9ecc6f971f2e15e9aed3e221c8c3f6baf6c518d 100644 (file)
@@ -31,7 +31,7 @@ public class BitmapImage implements PDFImage {
     private int height;
     private int width;
     private int bitsPerPixel;
-    private PDFColorSpace colorSpace;
+    private PDFDeviceColorSpace colorSpace;
     private byte[] bitmaps;
     private String maskRef;
     private PDFColor transparent = null;
@@ -54,7 +54,7 @@ public class BitmapImage implements PDFImage {
         this.height = height;
         this.width = width;
         this.bitsPerPixel = 8;
-        this.colorSpace = new PDFColorSpace(PDFColorSpace.DEVICE_RGB);
+        this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
         this.bitmaps = data;
         maskRef = mask;
     }
@@ -103,7 +103,7 @@ public class BitmapImage implements PDFImage {
      *
      * @param cs the pdf color space
      */
-    public void setColorSpace(PDFColorSpace cs) {
+    public void setColorSpace(PDFDeviceColorSpace cs) {
         colorSpace = cs;
     }
 
@@ -113,7 +113,7 @@ public class BitmapImage implements PDFImage {
      *
      * @return the pdf doclor space
      */
-    public PDFColorSpace getColorSpace() {
+    public PDFDeviceColorSpace getColorSpace() {
         return colorSpace;
     }
 
index 37e75dcfb1eaf3172015d53bbb39140fcec54e46..dfa096b5caa706ca065cf075176ed35c678d00d5 100644 (file)
@@ -47,7 +47,7 @@ public class PDFColor extends PDFPathPaint {
      */
     public PDFColor(double theRed, double theGreen, double theBlue) {
         // super(theNumber);
-        this.colorSpace = new PDFColorSpace(PDFColorSpace.DEVICE_RGB);
+        this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
 
         this.red = theRed;
         this.green = theGreen;
@@ -60,7 +60,7 @@ public class PDFColor extends PDFPathPaint {
      * @param col the sRGB color
      */
     public PDFColor(java.awt.Color col) {
-        this.colorSpace = new PDFColorSpace(PDFColorSpace.DEVICE_RGB);
+        this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
         float[] comps = new float[3];
         comps = col.getColorComponents(comps);
 
@@ -94,7 +94,7 @@ public class PDFColor extends PDFPathPaint {
                     double theBlack) {
         // super(theNumber);//?
 
-        this.colorSpace = new PDFColorSpace(PDFColorSpace.DEVICE_CMYK);
+        this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_CMYK);
 
         this.cyan = theCyan;
         this.magenta = theMagenta;
@@ -110,13 +110,13 @@ public class PDFColor extends PDFPathPaint {
      */
     public List getVector() {
         List theColorVector = new ArrayList();
-        if (this.colorSpace.getColorSpace() == PDFColorSpace.DEVICE_RGB) {
+        if (this.colorSpace.getColorSpace() == PDFDeviceColorSpace.DEVICE_RGB) {
             // RGB
             theColorVector.add(new Double(this.red));
             theColorVector.add(new Double(this.green));
             theColorVector.add(new Double(this.blue));
         } else if (this.colorSpace.getColorSpace()
-                   == PDFColorSpace.DEVICE_CMYK) {
+                   == PDFDeviceColorSpace.DEVICE_CMYK) {
             // CMYK
             theColorVector.add(new Double(this.cyan));
             theColorVector.add(new Double(this.magenta));
@@ -229,15 +229,15 @@ public class PDFColor extends PDFPathPaint {
     public void setColorSpace(int theColorSpace) {
         int theOldColorSpace = this.colorSpace.getColorSpace();
         if (theOldColorSpace != theColorSpace) {
-            if (theOldColorSpace == PDFColorSpace.DEVICE_RGB) {
-                if (theColorSpace == PDFColorSpace.DEVICE_CMYK) {
+            if (theOldColorSpace == PDFDeviceColorSpace.DEVICE_RGB) {
+                if (theColorSpace == PDFDeviceColorSpace.DEVICE_CMYK) {
                     this.convertRGBtoCMYK();
                 } else  {
                     // convert to Gray?
                     this.convertRGBtoGRAY();
                 }
-            } else if (theOldColorSpace == PDFColorSpace.DEVICE_CMYK) {
-                if (theColorSpace == PDFColorSpace.DEVICE_RGB) {
+            } else if (theOldColorSpace == PDFDeviceColorSpace.DEVICE_CMYK) {
+                if (theColorSpace == PDFDeviceColorSpace.DEVICE_RGB) {
                     this.convertCMYKtoRGB();
                 } else {
                     // convert to Gray?
@@ -245,7 +245,7 @@ public class PDFColor extends PDFPathPaint {
                 }
             } else {
                 // used to be Gray
-                if (theColorSpace == PDFColorSpace.DEVICE_RGB) {
+                if (theColorSpace == PDFDeviceColorSpace.DEVICE_RGB) {
                     this.convertGRAYtoRGB();
                 } else {
                     // convert to CMYK?
@@ -270,7 +270,7 @@ public class PDFColor extends PDFPathPaint {
         double tempDouble;
 
         if (this.colorSpace.getColorSpace()
-                == PDFColorSpace.DEVICE_RGB) {       // colorspace is RGB
+                == PDFDeviceColorSpace.DEVICE_RGB) {       // colorspace is RGB
             // according to pdfspec 12.1 p.399
             // if the colors are the same then just use the g or G operator
             boolean same = false;
@@ -300,7 +300,7 @@ public class PDFColor extends PDFPathPaint {
                 }
             }
         } else if (this.colorSpace.getColorSpace()
-                  == PDFColorSpace.DEVICE_CMYK) {
+                  == PDFDeviceColorSpace.DEVICE_CMYK) {
             // colorspace is CMYK
 
             if (fillNotStroke) {
index 780d9d9325d7270672c577da969ff9320376fd52..8ddda27a239f8fc0173a5df51d61c1f9afd9b7c5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 2006 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,146 +21,28 @@ package org.apache.fop.pdf;
 /**
  * PDF Color space.
  */
-public class PDFColorSpace {
-    private boolean hasICCProfile;
-    private byte[] iccProfile;
-    private int numComponents;
-
-    // Ok... so I had some grand purpose for this, but I can't recall.
-    // I'm just writing it
-
-    /**
-     * Unknown colorspace
-     */
-    public static final int DEVICE_UNKNOWN = -1;
-
-    /**
-     * Gray colorspace
-     */
-    public static final int DEVICE_GRAY = 1;
-
-    /**
-     * RGB colorspace
-     */
-    public static final int DEVICE_RGB = 2;
-
-    /**
-     * CMYK colorspace
-     */
-    public static final int DEVICE_CMYK = 3;
-
-    // Are there any others?
-
-    /**
-     * Current color space value.
-     */
-    protected int currentColorSpace = DEVICE_UNKNOWN;
-
-    /**
-     * Create a PDF colorspace object.
-     *
-     * @param theColorSpace the current colorspace
-     */
-    public PDFColorSpace(int theColorSpace) {
-        this.currentColorSpace = theColorSpace;
-        hasICCProfile = false;
-        numComponents = calculateNumComponents();
-    }
-
-    private int calculateNumComponents() {
-        if (currentColorSpace == DEVICE_GRAY) {
-            return 1;
-        } else if (currentColorSpace == DEVICE_RGB) {
-            return 3;
-        } else if (currentColorSpace == DEVICE_CMYK) {
-            return 4;
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Set the current colorspace.
-     *
-     * @param theColorSpace the new color space value
-     */
-    public void setColorSpace(int theColorSpace) {
-        this.currentColorSpace = theColorSpace;
-        numComponents = calculateNumComponents();
-    }
-
-    /**
-     * Check if this colorspace has an ICC profile.
-     *
-     * @return true if this has an ICC profile
-     */
-    public boolean hasICCProfile() {
-        return hasICCProfile;
-    }
-
-    /**
-     * Get the ICC profile for this colorspace
-     *
-     * @return the byte array containing the ICC profile data
-     */
-    public byte[] getICCProfile() {
-        if (hasICCProfile) {
-            return iccProfile;
-        } else {
-            return new byte[0];
-        }
-    }
-
-    /**
-     * Set the ICC profile for this colorspace.
-     *
-     * @param iccProfile the ICC profile data
-     */
-    public void setICCProfile(byte[] iccProfile) {
-        this.iccProfile = iccProfile;
-        hasICCProfile = true;
-    }
-
-    /**
-     * Get the colorspace value
-     *
-     * @return the colorspace value
-     */
-    public int getColorSpace() {
-        return (this.currentColorSpace);
-    }
-
+public interface PDFColorSpace {
+    
     /**
      * Get the number of color components for this colorspace
-     *
      * @return the number of components
      */
-    public int getNumComponents() {
-        return numComponents;
-    }
+    int getNumComponents();
 
+    /** @return the name of the color space */
+    String getName();
+    
     /**
-     * Get the PDF string for this colorspace.
-     *
-     * @return the PDF string for the colorspace
+     * @return true if the color space is a device-dependent color space (like DeviceRGB, 
+     *         DeviceCMYK and DeviceGray)
      */
-    public String getColorSpacePDFString() {
-        // shouldn't this be a select-case? I can never remember
-        // the syntax for that.
-        switch (currentColorSpace) {
-            case DEVICE_CMYK:
-                return "DeviceCMYK";
-            //break;
-            case DEVICE_GRAY:
-                return "DeviceGray";
-            //break;
-            case DEVICE_RGB:
-            default:
-                // unknown... Error. Tell them it's RGB and hope they
-                // don't notice.
-                return ("DeviceRGB");
-            //break;
-        }
-    }
-
+    boolean isDeviceColorSpace();
+    
+    /** @return true if the color space is an RGB color space */
+    boolean isRGBColorSpace();
+    /** @return true if the color space is an CMYK color space */
+    boolean isCMYKColorSpace();
+    /** @return true if the color space is an Gray color space */
+    boolean isGrayColorSpace();
+    
 }
diff --git a/src/java/org/apache/fop/pdf/PDFDeviceColorSpace.java b/src/java/org/apache/fop/pdf/PDFDeviceColorSpace.java
new file mode 100644 (file)
index 0000000..1cdec34
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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;
+
+/**
+ * Represents a device-specific color space. Used for mapping DeviceRGB, DeviceCMYK and DeviceGray.
+ */
+public class PDFDeviceColorSpace implements PDFColorSpace {
+    
+    private int numComponents;
+
+    /**
+     * Unknown colorspace
+     */
+    public static final int DEVICE_UNKNOWN = -1;
+
+    /**
+     * Gray colorspace
+     */
+    public static final int DEVICE_GRAY = 1;
+
+    /**
+     * RGB colorspace
+     */
+    public static final int DEVICE_RGB = 2;
+
+    /**
+     * CMYK colorspace
+     */
+    public static final int DEVICE_CMYK = 3;
+
+    // Are there any others?
+
+    /**
+     * Current color space value.
+     */
+    protected int currentColorSpace = DEVICE_UNKNOWN;
+
+    /**
+     * Create a PDF colorspace object.
+     *
+     * @param theColorSpace the current colorspace
+     */
+    public PDFDeviceColorSpace(int theColorSpace) {
+        this.currentColorSpace = theColorSpace;
+        numComponents = calculateNumComponents();
+    }
+
+    private int calculateNumComponents() {
+        if (currentColorSpace == DEVICE_GRAY) {
+            return 1;
+        } else if (currentColorSpace == DEVICE_RGB) {
+            return 3;
+        } else if (currentColorSpace == DEVICE_CMYK) {
+            return 4;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Set the current colorspace.
+     *
+     * @param theColorSpace the new color space value
+     */
+    public void setColorSpace(int theColorSpace) {
+        this.currentColorSpace = theColorSpace;
+        numComponents = calculateNumComponents();
+    }
+
+    /**
+     * Get the colorspace value
+     *
+     * @return the colorspace value
+     */
+    public int getColorSpace() {
+        return (this.currentColorSpace);
+    }
+
+    /**
+     * Get the number of color components for this colorspace
+     *
+     * @return the number of components
+     */
+    public int getNumComponents() {
+        return numComponents;
+    }
+
+    /** @return the name of the color space */
+    public String getName() {
+        switch (currentColorSpace) {
+        case DEVICE_CMYK:
+            return "DeviceCMYK";
+        case DEVICE_GRAY:
+            return "DeviceGray";
+        case DEVICE_RGB:
+            return "DeviceRGB";
+        default:
+            throw new IllegalStateException("Unsupported color space in use.");
+        }
+    }
+
+    /** @see org.apache.fop.pdf.PDFColorSpace#isDeviceColorSpace() */
+    public boolean isDeviceColorSpace() {
+        return true;
+    }
+
+    /** @see org.apache.fop.pdf.PDFColorSpace#isRGBColorSpace() */
+    public boolean isRGBColorSpace() {
+        return getColorSpace() == DEVICE_RGB;
+    }
+
+    /** @see org.apache.fop.pdf.PDFColorSpace#isCMYKColorSpace() */
+    public boolean isCMYKColorSpace() {
+        return getColorSpace() == DEVICE_CMYK;
+    }
+
+    /** @see org.apache.fop.pdf.PDFColorSpace#isGrayColorSpace() */
+    public boolean isGrayColorSpace() {
+        return getColorSpace() == DEVICE_GRAY;
+    }
+    
+}
index f97c498b67c2b32fec9f3076e3f80787fa421491..a55252924b60fb1e0e032592bb2414c4f4a99060 100644 (file)
@@ -108,10 +108,9 @@ public class PDFDocument {
     protected int pdfVersion = PDF_VERSION_1_4;
     
     /**
-     * Indicates the PDF/A-1 mode currently active. Defaults to "no restrictions", i.e. 
-     * PDF/A-1 not active.
+     * Indicates which PDF profiles are active (PDF/A, PDF/X etc.)
      */
-    protected PDFAMode pdfAMode = PDFAMode.DISABLED;
+    protected PDFProfile pdfProfile = new PDFProfile(this);
     
     /**
      * the /Root object
@@ -142,8 +141,8 @@ public class PDFDocument {
     /**
      * the colorspace (0=RGB, 1=CMYK)
      */
-    protected PDFColorSpace colorspace =
-        new PDFColorSpace(PDFColorSpace.DEVICE_RGB);
+    protected PDFDeviceColorSpace colorspace =
+        new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
 
     /**
      * the counter for Pattern name numbering (e.g. 'Pattern1')
@@ -268,26 +267,9 @@ public class PDFDocument {
         }
     }
 
-    /** @return the PDF/A mode currently active. */
-    public PDFAMode getPDFAMode() {
-        return this.pdfAMode;
-    }
-    
-    /**
-     * Sets the active PDF/A mode. This must be set immediately after calling the constructor so
-     * the checks will be activated.
-     * @param mode one of the PDFAMode constants
-     */
-    public void setPDFAMode(PDFAMode mode) {
-        if (mode == null) {
-            throw new NullPointerException("mode must not be null");
-        }
-        if (mode == PDFAMode.PDFA_1A) {
-            throw new UnsupportedOperationException("PDF/A-1a is not implemented, yet");
-        } else if (mode == PDFAMode.PDFA_1B) {
-            //you got the green light!
-        }
-        this.pdfAMode = mode;
+    /** @return the PDF profile currently active. */
+    public PDFProfile getProfile() {
+        return this.pdfProfile;
     }
     
     /**
@@ -514,9 +496,7 @@ public class PDFDocument {
      * @param params The encryption parameters for the pdf file
      */
     public void setEncryption(PDFEncryptionParams params) {
-        if (getPDFAMode().isPDFA1LevelB()) {
-            throw new PDFConformanceException("PDF/A-1 doesn't allow encrypted PDFs");
-        }
+        getProfile().verifyEncryptionAllowed();
         this.encryption = PDFEncryptionManager.newInstance(++this.objectcount, params);
         ((PDFObject)this.encryption).setDocument(this);
         if (encryption != null) {
@@ -658,7 +638,7 @@ public class PDFDocument {
      *
      * @return the color space
      */
-    public PDFColorSpace getPDFColorSpace() {
+    public PDFDeviceColorSpace getPDFColorSpace() {
         return this.colorspace;
     }
 
@@ -866,9 +846,7 @@ public class PDFDocument {
     public void outputHeader(OutputStream stream) throws IOException {
         this.position = 0;
 
-        if (getPDFAMode().isPDFA1LevelB() && getPDFVersion() != PDF_VERSION_1_4) {
-            throw new PDFConformanceException("PDF version must be 1.4 for " + getPDFAMode());
-        }
+        getProfile().verifyPDFVersion();
         
         byte[] pdf = ("%PDF-" + getPDFVersionString() + "\n").getBytes();
         stream.write(pdf);
@@ -899,10 +877,10 @@ public class PDFDocument {
             String s = PDFText.toHex(res);
             return "/ID [" + s + " " + s + "]";
         } catch (NoSuchAlgorithmException e) {
-            if (getPDFAMode().isPDFA1LevelB()) {
+            if (getProfile().isIDEntryRequired()) {
                 throw new UnsupportedOperationException("MD5 not available: " + e.getMessage());
             } else {
-                return ""; //Entry is optional if PDF/A is not active
+                return ""; //Entry is optional if PDF/A or PDF/X are not active
             }
         }
     }
index 432063f9f5bfc4916cb3430d687c27437afa0cfc..84eb6fd7e30cd6bc05a629838c1aa546b66084d1 100644 (file)
@@ -24,6 +24,8 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
+import java.util.BitSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import javax.xml.transform.Source;
@@ -34,6 +36,7 @@ import org.w3c.dom.Document;
 
 // Apache libs
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.output.ByteArrayOutputStream;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
@@ -435,7 +438,7 @@ public class PDFFactory {
      * @return the PDF shading that was created
      */
     public PDFShading makeShading(PDFResourceContext res, int theShadingType,
-                                  PDFColorSpace theColorSpace,
+                                  PDFDeviceColorSpace theColorSpace,
                                   List theBackground, List theBBox,
                                   boolean theAntiAlias, List theDomain,
                                   List theMatrix,
@@ -487,7 +490,7 @@ public class PDFFactory {
      * @return the PDF shading that was created
      */
     public PDFShading makeShading(PDFResourceContext res, int theShadingType,
-                                  PDFColorSpace theColorSpace,
+                                  PDFDeviceColorSpace theColorSpace,
                                   List theBackground, List theBBox,
                                   boolean theAntiAlias, List theCoords,
                                   List theDomain, PDFFunction theFunction,
@@ -540,7 +543,7 @@ public class PDFFactory {
      * @return the PDF shading that was created
      */
     public PDFShading makeShading(PDFResourceContext res, int theShadingType,
-                                  PDFColorSpace theColorSpace,
+                                  PDFDeviceColorSpace theColorSpace,
                                   List theBackground, List theBBox,
                                   boolean theAntiAlias,
                                   int theBitsPerCoordinate,
@@ -595,7 +598,7 @@ public class PDFFactory {
      * @return the PDF shading that was created
      */
     public PDFShading makeShading(PDFResourceContext res, int theShadingType,
-                                  PDFColorSpace theColorSpace,
+                                  PDFDeviceColorSpace theColorSpace,
                                   List theBackground, List theBBox,
                                   boolean theAntiAlias,
                                   int theBitsPerCoordinate,
@@ -718,7 +721,7 @@ public class PDFFactory {
      * @return the PDF pattern that was created
      */
     public PDFPattern makeGradient(PDFResourceContext res, boolean radial,
-                                   PDFColorSpace theColorspace,
+                                   PDFDeviceColorSpace theColorspace,
                                    List theColors, List theBounds,
                                    List theCoords, List theMatrix) {
         PDFShading myShad;
@@ -887,6 +890,7 @@ public class PDFFactory {
     }
 
     private String getGoToReference(String destination, float yoffset) {
+        getDocument().getProfile().verifyActionAllowed();
         String goToReference = null;
         PDFGoTo gt = new PDFGoTo(destination);
         gt.setYPosition(yoffset);
@@ -914,6 +918,7 @@ public class PDFFactory {
      * @return the pdf goto remote object
      */
     private PDFGoToRemote getGoToPDFAction(String file, String dest, int page) {
+        getDocument().getProfile().verifyActionAllowed();
         PDFFileSpec fileSpec = new PDFFileSpec(file);
         PDFFileSpec oldspec = getDocument().findFileSpec(fileSpec);
         if (oldspec == null) {
@@ -1122,10 +1127,47 @@ public class PDFFactory {
                 descriptor.setFontFile(desc.getFontType(), stream);
                 getDocument().registerObject(stream);
             }
+            CustomFont font = getCustomFont(desc);
+            if (font instanceof CIDFont) {
+                CIDFont cidFont = (CIDFont)font;
+                buildCIDSet(descriptor, cidFont);
+            }
         }
         return descriptor;
     }
 
+    private void buildCIDSet(PDFFontDescriptor descriptor, CIDFont cidFont) {
+        BitSet cidSubset = new BitSet();
+        Iterator iter = cidFont.usedGlyphs.keySet().iterator();
+        while (iter.hasNext()) {
+            Integer cid = (Integer)iter.next();
+            cidSubset.set(cid.intValue());
+        }
+        PDFStream cidSet = makeStream(null, true);
+        ByteArrayOutputStream baout = new ByteArrayOutputStream(cidSubset.length() / 8 + 1);
+        int value = 0;
+        for (int i = 0, c = cidSubset.length(); i < c; i++) {
+            int shift = i % 8;
+            boolean b = cidSubset.get(i); 
+            if (b) {
+                value |= 1 << 7 - shift; 
+            }
+            if (shift == 7) {
+                baout.write(value);
+                value = 0;
+            }
+        }
+        baout.write(value);
+        try {
+            cidSet.setData(baout.toByteArray());
+            descriptor.setCIDSet(cidSet);
+        } catch (IOException ioe) {
+            log.error(
+                    "Failed to write CIDSet [" + cidFont + "] "
+                    + cidFont.getFontName(), ioe);
+        }
+    }
+
     /**
      * Embeds a font.
      * @param desc FontDescriptor of the font.
@@ -1137,18 +1179,7 @@ public class PDFFactory {
                                                 + desc.getFontType());
         }
 
-        Typeface tempFont;
-        if (desc instanceof LazyFont) {
-            tempFont = ((LazyFont)desc).getRealFont();
-        } else {
-            tempFont = (Typeface)desc;
-        }
-        if (!(tempFont instanceof CustomFont)) {
-            throw new IllegalArgumentException(
-                      "FontDescriptor must be instance of CustomFont, but is a "
-                       + desc.getClass().getName());
-        }
-        CustomFont font = (CustomFont)tempFont;
+        CustomFont font = getCustomFont(desc);
 
         InputStream in = null;
         try {
@@ -1227,6 +1258,21 @@ public class PDFFactory {
         }
     }
 
+    private CustomFont getCustomFont(FontDescriptor desc) {
+        Typeface tempFont;
+        if (desc instanceof LazyFont) {
+            tempFont = ((LazyFont)desc).getRealFont();
+        } else {
+            tempFont = (Typeface)desc;
+        }
+        if (!(tempFont instanceof CustomFont)) {
+            throw new IllegalArgumentException(
+                      "FontDescriptor must be instance of CustomFont, but is a "
+                       + desc.getClass().getName());
+        }
+        return (CustomFont)tempFont;
+    }
+
 
     /* ========================= streams =================================== */
 
@@ -1258,7 +1304,7 @@ public class PDFFactory {
      * Create a PDFICCStream
      * @see PDFXObject
      * @see org.apache.fop.image.JpegImage
-     * @see org.apache.fop.pdf.PDFColorSpace
+     * @see org.apache.fop.pdf.PDFDeviceColorSpace
      * @return the new PDF ICC stream object
      */
     public PDFICCStream makePDFICCStream() {
@@ -1274,6 +1320,28 @@ public class PDFFactory {
 
     /* ========================= misc. objects ============================= */
 
+    /**
+     * Makes a new ICCBased color space and registers it in the resource context.
+     * @param res the PDF resource context to add the shading, may be null
+     * @param explicitName the explicit name for the color space, may be null
+     * @param iccStream the ICC stream to associate with this color space
+     * @return the newly instantiated color space
+     */
+    public PDFICCBasedColorSpace makeICCBasedColorSpace(PDFResourceContext res,
+            String explicitName, PDFICCStream iccStream) {
+        PDFICCBasedColorSpace cs = new PDFICCBasedColorSpace(explicitName, iccStream);
+        
+        getDocument().registerObject(cs);
+
+        if (res != null) {
+            res.getPDFResources().addColorSpace(cs);
+        } else {
+            getDocument().getResources().addColorSpace(cs);
+        }
+        
+        return cs;
+    }
+
     /**
      * make an Array object (ex. Widths array for a font)
      *
index b98736487add4dbde89ec1ab76cf73183cd210e5..9d297729899cc4c7308bb0e63138f16bd3ff38e1 100644 (file)
@@ -193,9 +193,10 @@ public class PDFFont extends PDFObject {
      * Validates the PDF object prior to serialization.
      */
     protected void validate() {
-        if (getDocumentSafely().getPDFAMode().isPDFA1LevelB()) {
+        if (getDocumentSafely().getProfile().isFontEmbeddingRequired()) {
             if (this.getClass() == PDFFont.class) {
-                throw new PDFConformanceException("For PDF/A-1, all fonts, even the base 14"
+                throw new PDFConformanceException("For " + getDocumentSafely().getProfile() 
+                    + ", all fonts, even the base 14"
                     + " fonts, have to be embedded! Offending font: " + this.basefont);
             }
         }
index 24e0243f5d38a29c610e9cbac0b7bab43bad9888..51883a01505192343289e3a2cbcb99ffe3cd9149 100644 (file)
@@ -44,6 +44,7 @@ public class PDFFontDescriptor extends PDFObject {
     private int maxWidth = 0;
     private int missingWidth = 0;
     private AbstractPDFStream fontfile;
+    private AbstractPDFStream cidSet;
     // private String charSet = null;
 
     private FontType subtype;
@@ -119,6 +120,19 @@ public class PDFFontDescriptor extends PDFObject {
         return this.fontfile;
     }
     
+    /**
+     * Sets the CIDSet stream for this font descriptor. (Optional)
+     * @param cidSet the CIDSet stream
+     */
+    public void setCIDSet(AbstractPDFStream cidSet) {
+        this.cidSet = cidSet;
+    }
+    
+    /** @return the CIDSet stream or null if not applicable */
+    public AbstractPDFStream getCIDSet() {
+        return this.cidSet;
+    }
+    
     // public void setCharSet(){}//for subset fonts
 
     /**
@@ -177,6 +191,10 @@ public class PDFFontDescriptor extends PDFObject {
             }
             p.append(fontfile.referencePDF());
         }
+        if (getCIDSet() != null) {
+            p.append("\n/CIDSet ");
+            p.append(getCIDSet().referencePDF());
+        }
         // charSet for subset fonts // not yet implemented
         // CID optional field
         fillInPDF(p);
index e32b11ce4d4516843ac2acd4611d6cf3c7bd4b42..cbdc9390f2fa475875fe0bf1071f4d1a749576cb 100644 (file)
@@ -95,9 +95,10 @@ public abstract class PDFFontNonBase14 extends PDFFont {
     
     /** @see org.apache.fop.pdf.PDFFont#validate() */
     protected void validate() {
-        if (getDocumentSafely().getPDFAMode().isPDFA1LevelB()) {
+        if (getDocumentSafely().getProfile().isFontEmbeddingRequired()) {
             if (this.getDescriptor().getFontFile() == null) {
-                throw new PDFConformanceException("For PDF/A-1, all fonts have to be embedded!");
+                throw new PDFConformanceException("For " + getDocumentSafely().getProfile() 
+                    + ", all fonts have to be embedded!");
             }
         }
     }
diff --git a/src/java/org/apache/fop/pdf/PDFICCBasedColorSpace.java b/src/java/org/apache/fop/pdf/PDFICCBasedColorSpace.java
new file mode 100644 (file)
index 0000000..1e2e258
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+/**
+ * Represents an ICCBased color space in PDF.
+ */
+public class PDFICCBasedColorSpace extends PDFObject implements PDFColorSpace {
+
+    private PDFICCStream iccStream;
+    private String explicitName;
+    
+    /**
+     * Constructs a the ICCBased color space with an explicit name (ex. "DefaultRGB").
+     * @param explicitName an explicit name or null if a name should be generated
+     * @param iccStream the ICC stream to associate with this color space
+     */
+    public PDFICCBasedColorSpace(String explicitName, PDFICCStream iccStream) {
+        this.explicitName = explicitName;
+        this.iccStream = iccStream;
+    }
+    
+    /**
+     * Constructs a the ICCBased color space.
+     * @param iccStream the ICC stream to associate with this color space
+     */
+    public PDFICCBasedColorSpace(PDFICCStream iccStream) {
+        this(null, iccStream);
+    }
+    
+    /** @return the ICC stream associated with this color space */
+    public PDFICCStream getICCStream() {
+        return this.iccStream;
+    }
+    
+    /** @see org.apache.fop.pdf.PDFColorSpace#getNumComponents() */
+    public int getNumComponents() {
+        return iccStream.getICCProfile().getNumComponents();
+    }
+
+    /** @see org.apache.fop.pdf.PDFColorSpace#getName() */
+    public String getName() {
+        if (explicitName != null) {
+            return explicitName;
+        } else {
+            return "ICC" + iccStream.getObjectNumber();
+        }
+    }
+
+    /** @see org.apache.fop.pdf.PDFColorSpace#isDeviceColorSpace() */
+    public boolean isDeviceColorSpace() {
+        return false;
+    }
+
+    /** @see org.apache.fop.pdf.PDFColorSpace#isRGBColorSpace() */
+    public boolean isRGBColorSpace() {
+        return getNumComponents() == 3;
+    }
+
+    /** @see org.apache.fop.pdf.PDFColorSpace#isCMYKColorSpace() */
+    public boolean isCMYKColorSpace() {
+        return getNumComponents() == 4;
+    }
+
+    /** @see org.apache.fop.pdf.PDFColorSpace#isGrayColorSpace() */
+    public boolean isGrayColorSpace() {
+        return getNumComponents() == 1;
+    }
+
+    /** @see org.apache.fop.pdf.PDFObject#toPDFString() */
+    protected String toPDFString() {
+        StringBuffer sb = new StringBuffer(64);
+        sb.append(getObjectID());
+        sb.append("[/ICCBased ").append(getICCStream().referencePDF()).append("]");
+        sb.append("\nendobj\n");
+        return sb.toString();
+    }
+
+}
index 1289cf2443f48035b5207a70b6f8c91217118d3a..2d7ca46741e5aa907ecba9de40f6528fa61f3792 100644 (file)
@@ -27,11 +27,8 @@ import java.io.OutputStream;
  */
 public class PDFICCStream extends PDFStream {
     
-    private int origLength;
-    private int len1, len3;
-
     private ICC_Profile cp;
-    private PDFColorSpace pdfColorSpace;
+    private PDFDeviceColorSpace pdfColorSpace;
 
     /**
      * @see org.apache.fop.pdf.PDFObject#PDFObject()
@@ -43,14 +40,19 @@ public class PDFICCStream extends PDFStream {
 
     /**
      * Sets the color space to encode in PDF.
-     * @param cp the ICC profile
+     * @param icc the ICC profile
      * @param alt the PDF color space
      */
-    public void setColorSpace(ICC_Profile cp, PDFColorSpace alt) {
-        this.cp = cp;
+    public void setColorSpace(ICC_Profile icc, PDFDeviceColorSpace alt) {
+        this.cp = icc;
         pdfColorSpace = alt;
     }
 
+    /** @return the ICC profile */
+    public ICC_Profile getICCProfile() {
+        return this.cp;
+    }
+    
     /**
      * overload the base object method so we don't have to copy
      * byte arrays around so much
@@ -81,7 +83,7 @@ public class PDFICCStream extends PDFStream {
         sb.append("/N " + cp.getNumComponents());
 
         if (pdfColorSpace != null) {
-            sb.append("\n/Alternate /" + pdfColorSpace.getColorSpacePDFString() + " ");
+            sb.append("\n/Alternate /" + pdfColorSpace.getName() + " ");
         }
 
         sb.append("\n/Length " + lengthEntry);
index 23114c65659a1d3acf589f3848785b306517d91c..7c81dc5122b7df49b015a1442162decee3c6a711 100644 (file)
@@ -65,7 +65,7 @@ public interface PDFImage {
      *
      * @return the color space
      */
-    PDFColorSpace getColorSpace();
+    PDFDeviceColorSpace getColorSpace();
 
     /**
      * Get the bits per pixel for this image.
index b12b27efdc1bab6cc834c7f494dce3fbdfc7002f..b7a4e0e30e480cc684901186ed635765405c2775 100644 (file)
@@ -146,14 +146,17 @@ public class PDFInfo extends PDFObject {
      * @see org.apache.fop.pdf.PDFObject#toPDF()
      */
     public byte[] toPDF() {
+        PDFProfile profile = getDocumentSafely().getProfile(); 
         ByteArrayOutputStream bout = new ByteArrayOutputStream(128);
         try {
             bout.write(encode(getObjectID()));
             bout.write(encode("<<\n"));
-            if (title != null) {
+            if (title != null && title.length() > 0) {
                 bout.write(encode("/Title "));
                 bout.write(encodeText(this.title));
                 bout.write(encode("\n"));
+            } else {
+                profile.verifyTitleAbsent();
             }
             if (author != null) {
                 bout.write(encode("/Author "));
@@ -187,7 +190,23 @@ public class PDFInfo extends PDFObject {
             }
             bout.write(encode("/CreationDate "));
             bout.write(encodeString(formatDateTime(creationDate)));
-            bout.write(encode("\n>>\nendobj\n"));
+            bout.write(encode("\n"));
+            
+            if (profile.isModDateRequired()) {
+                bout.write(encode("/ModDate "));
+                bout.write(encodeString(formatDateTime(creationDate)));
+                bout.write(encode("\n"));
+            }
+            if (profile.isPDFXActive()) {
+                bout.write(encode("/GTS_PDFXVersion "));
+                bout.write(encodeString(profile.getPDFXMode().getName()));
+                bout.write(encode("\n"));
+            }
+            if (profile.isTrappedEntryRequired()) {
+                bout.write(encode("/Trapped /False\n"));
+            }
+            
+            bout.write(encode(">>\nendobj\n"));
         } catch (IOException ioe) {
             log.error("Ignored I/O exception", ioe);
         }
index e0cd2a6617376758cab26956e164900e4329a1d2..377f4e59291ed483b95b31bad2d11a2320a8cb7d 100644 (file)
@@ -71,8 +71,9 @@ public class PDFLink extends PDFObject {
      * @see org.apache.fop.pdf.PDFObject#toPDFString()
      */
     public String toPDFString() {
+        getDocumentSafely().getProfile().verifyAnnotAllowed();
         String fFlag = "";
-        if (getDocumentSafely().getPDFAMode().isPDFA1LevelB()) {
+        if (getDocumentSafely().getProfile().getPDFAMode().isPDFA1LevelB()) {
             int f = 0;
             f |= 1 << (3 - 1); //Print, bit 3
             f |= 1 << (4 - 1); //NoZoom, bit 4
index 42aaed1b0ecf128dc90b625cd37c59da18b357b5..94e31372d92cdab58d5f96789136bfc00fc89e31 100644 (file)
@@ -133,7 +133,7 @@ public class PDFMetadata extends PDFStream {
     /** @see org.apache.fop.pdf.AbstractPDFStream#buildStreamDict(String) */
     protected String buildStreamDict(String lengthEntry) {
         final String filterEntry = getFilterList().buildFilterDictEntries();
-        if (getDocumentSafely().getPDFAMode().isPDFA1LevelB() 
+        if (getDocumentSafely().getProfile().getPDFAMode().isPDFA1LevelB() 
                 && filterEntry != null && filterEntry.length() > 0) {
             throw new PDFConformanceException(
                     "The Filter key is prohibited when PDF/A-1 is active");
@@ -248,6 +248,12 @@ public class PDFMetadata extends PDFStream {
         el = doc.createElementNS(XMPConstants.XMP_BASIC_NAMESPACE, "xmp:CreateDate");
         desc.appendChild(el);
         el.appendChild(doc.createTextNode(formatISO8601Date(info.getCreationDate())));
+        PDFProfile profile = pdfDoc.getProfile(); 
+        if (profile.isModDateRequired()) {
+            el = doc.createElementNS(XMPConstants.XMP_BASIC_NAMESPACE, "xmp:ModifyDate");
+            desc.appendChild(el);
+            el.appendChild(doc.createTextNode(formatISO8601Date(info.getCreationDate())));
+        }
         if (info.getCreator() != null) {
             el = doc.createElementNS(XMPConstants.XMP_BASIC_NAMESPACE, "xmp:CreatorTool");
             desc.appendChild(el);
@@ -274,7 +280,7 @@ public class PDFMetadata extends PDFStream {
         el.appendChild(doc.createTextNode(pdfDoc.getPDFVersionString()));
         
         //PDF/A identification
-        PDFAMode pdfaMode = pdfDoc.getPDFAMode(); 
+        PDFAMode pdfaMode = pdfDoc.getProfile().getPDFAMode(); 
         if (pdfaMode.isPDFA1LevelB()) {
             createPDFAIndentification(doc, rdf, 
                     XMPConstants.PDF_A_IDENTIFICATION, "pdfaid", pdfaMode);
index bd00cbd2d8a3b781638bf56126a94798ef39f4fd..6fdbc74adb933af37228924f31a80bcc8b2823b1 100644 (file)
@@ -157,11 +157,13 @@ public class PDFPage extends PDFResourceContext {
     public String toPDFString() {
         StringBuffer sb = new StringBuffer();
 
+        String box = "[ 0 0 " + getWidth() + " " + getHeight() + " ]";
         sb = sb.append(getObjectID()
                        + "<< /Type /Page\n" 
                        + "/Parent " + this.parentRef + "\n"
-                       + "/MediaBox [ 0 0 " + getWidth() + " "
-                       + getHeight() + " ]\n" 
+                       + "/MediaBox " + box + "\n" 
+                       + "/TrimBox " + box + "\n" //Needed for PDF/X
+                       + "/BleedBox " + box + "\n" //Recommended by PDF/X
                        + "/Resources " + this.resources.referencePDF() + "\n" 
                        + "/Contents " + this.contents.referencePDF() + "\n");
         if (this.annotList != null) {
index 5fd34fafb6024395f119c805fa370a63bcc8cfb5..f4134b81aac6c389593fb8f8e2922bdc5947b8a5 100644 (file)
@@ -27,7 +27,7 @@ public abstract class PDFPathPaint extends PDFObject {
     /**
      * The color space for this paint
      */
-    protected PDFColorSpace colorSpace;
+    protected PDFDeviceColorSpace colorSpace;
 
     /**
      * Get the PDF string for setting the path paint.
diff --git a/src/java/org/apache/fop/pdf/PDFProfile.java b/src/java/org/apache/fop/pdf/PDFProfile.java
new file mode 100644 (file)
index 0000000..30319d4
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+import java.text.MessageFormat;
+
+/**
+ * This class allows tracks the enabled PDF profiles (PDF/A and PDF/X) and provides methods to
+ * the libarary and its users to enable the generation of PDFs conforming to the enabled PDF
+ * profiles.
+ * <p>
+ * Some profile from PDF/X and PDF/A can be active simultaneously (example: PDF/A-1 and 
+ * PDF/X-3:2003).
+ */
+public class PDFProfile {
+
+    /**
+     * Indicates the PDF/A mode currently active. Defaults to "no restrictions", i.e. 
+     * PDF/A not active.
+     */
+    protected PDFAMode pdfAMode = PDFAMode.DISABLED;
+    
+    /**
+     * Indicates the PDF/X mode currently active. Defaults to "no restrictions", i.e. 
+     * PDF/X not active.
+     */
+    protected PDFXMode pdfXMode = PDFXMode.DISABLED;
+    
+    private PDFDocument doc;
+    
+    /**
+     * Main constructor
+     * @param doc the PDF document
+     */
+    public PDFProfile(PDFDocument doc) {
+        this.doc = doc;
+    }
+    
+    /**
+     * Validates if the requested profile combination is compatible.
+     */
+    protected void validateProfileCombination() {
+        if (pdfAMode != PDFAMode.DISABLED) {
+            if (pdfAMode == PDFAMode.PDFA_1A) {
+                throw new UnsupportedOperationException("PDF/A-1a is not implemented, yet");
+            }
+            if (pdfAMode == PDFAMode.PDFA_1B) {
+                if (pdfXMode != PDFXMode.DISABLED && pdfXMode != PDFXMode.PDFX_3_2003) {
+                    throw new PDFConformanceException(
+                            pdfAMode + " and " + pdfXMode + " are not compatible!");
+                }
+            }
+        }
+    }
+    
+    /** @return the PDFDocument this profile is attached to */
+    public PDFDocument getDocument() {
+        return this.doc;
+    }
+    
+    /** @return the PDF/A mode */
+    public PDFAMode getPDFAMode() {
+        return this.pdfAMode;
+    }
+    
+    /** @return true if any PDF/A mode is active */
+    public boolean isPDFAActive() {
+        return getPDFAMode() != PDFAMode.DISABLED;
+    }
+    
+    /**
+     * Sets the PDF/A mode
+     * @param mode the PDF/A mode
+     */
+    public void setPDFAMode(PDFAMode mode) {
+        if (mode == null) {
+            mode = PDFAMode.DISABLED;
+        }
+        this.pdfAMode = mode;
+        validateProfileCombination();
+    }
+    
+    /** @return the PDF/X mode */
+    public PDFXMode getPDFXMode() {
+        return this.pdfXMode;
+    }
+    
+    /** @return true if any PDF/X mode is active */
+    public boolean isPDFXActive() {
+        return getPDFXMode() != PDFXMode.DISABLED;
+    }
+    
+    /**
+     * Sets the PDF/X mode
+     * @param mode the PDF/X mode
+     */
+    public void setPDFXMode(PDFXMode mode) {
+        if (mode == null) {
+            mode = PDFXMode.DISABLED;
+        }
+        this.pdfXMode = mode;
+        validateProfileCombination();
+    }
+
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        if (isPDFAActive() && isPDFXActive()) {
+            sb.append("[").append(getPDFAMode()).append(",").append(getPDFXMode()).append("]");
+        } else if (isPDFAActive()) {
+            sb.append(getPDFAMode());
+        } else if (isPDFXActive()) {
+            sb.append(getPDFXMode());
+        } else {
+            sb.append(super.toString());
+        }
+        return sb.toString();
+    }
+    
+    //---------=== Info and validation methods ===---------
+    
+    private String format(String pattern, Object arg) {
+        return MessageFormat.format(pattern, new Object[] {arg});
+    }
+    
+    /** Checks if encryption is allowed. */
+    public void verifyEncryptionAllowed() {
+        final String err = "{0} doesn't allow encrypted PDFs";
+        if (isPDFAActive()) {
+            throw new PDFConformanceException(format(err, getPDFAMode()));
+        }
+        if (isPDFXActive()) {
+            throw new PDFConformanceException(format(err, getPDFXMode()));
+        }
+    }
+
+    /** Checks if PostScript XObjects are allowed. */
+    public void verifyPSXObjectsAllowed() {
+        final String err = "PostScript XObjects are prohibited when {0}"
+                + " is active. Convert EPS graphics to another format.";
+        if (isPDFAActive()) {
+            throw new PDFConformanceException(format(err, getPDFAMode()));
+        }
+        if (isPDFXActive()) {
+            throw new PDFConformanceException(format(err, getPDFXMode()));
+        }
+    }
+
+    /**
+     * Checks if the use of transparency is allowed.
+     * @param context Context information for the user to identify the problem spot
+     */
+    public void verifyTransparencyAllowed(String context) {
+        final String err = "{0} does not allow the use of transparency. ({1})";
+        if (isPDFAActive()) {
+            throw new PDFConformanceException(MessageFormat.format(err, 
+                    new Object[] {getPDFAMode(), context}));
+        }
+        if (isPDFXActive()) {
+            throw new PDFConformanceException(MessageFormat.format(err, 
+                    new Object[] {getPDFXMode(), context}));
+        }
+    }
+
+    /** Checks if the right PDF version is set. */
+    public void verifyPDFVersion() {
+        final String err = "PDF version must be 1.4 for {0}";
+        if (getPDFAMode().isPDFA1LevelB() 
+                && getDocument().getPDFVersion() != PDFDocument.PDF_VERSION_1_4) {
+            throw new PDFConformanceException(format(err, getPDFAMode()));
+        }
+        if (getPDFXMode() == PDFXMode.PDFX_3_2003 
+                && getDocument().getPDFVersion() != PDFDocument.PDF_VERSION_1_4) {
+            throw new PDFConformanceException(format(err, getPDFXMode()));
+        }
+    }
+    
+    /** @return true if the ID entry must be present in the trailer. */
+    public boolean isIDEntryRequired() {
+        return isPDFAActive() || isPDFXActive();
+    }
+
+    /** @return true if all fonts need to be embedded. */
+    public boolean isFontEmbeddingRequired() {
+        return isPDFAActive() || isPDFXActive();
+    }
+
+    /** Checks if a title may be absent. */
+    public void verifyTitleAbsent() {
+        if (isPDFXActive()) {
+            final String err = "{0} requires the title to be set.";
+            throw new PDFConformanceException(format(err, getPDFXMode()));
+        }
+    }
+
+    /** @return true if the ModDate Info entry must be present. */
+    public boolean isModDateRequired() {
+        return getPDFXMode() == PDFXMode.PDFX_3_2003;
+    }
+
+    /** @return true if the Trapped Info entry must be present. */
+    public boolean isTrappedEntryRequired() {
+        return getPDFXMode() == PDFXMode.PDFX_3_2003;
+    }
+
+    /** @return true if annotations are allowed */
+    public boolean isAnnotationAllowed() {
+        return !isPDFXActive();
+    }
+    
+    /** Checks if annotations are allowed. */
+    public void verifyAnnotAllowed() {
+        if (!isAnnotationAllowed()) {
+            final String err = "{0} does not allow annotations inside the printable area.";
+            //Note: this rule is simplified. Refer to the standard for details.
+            throw new PDFConformanceException(format(err, getPDFXMode()));
+        }
+    }
+
+    /** Checks if Actions are allowed. */
+    public void verifyActionAllowed() {
+        if (isPDFXActive()) {
+            final String err = "{0} does not allow Actions.";
+            throw new PDFConformanceException(format(err, getPDFXMode()));
+        }
+    }
+
+}
index 0945634dde366b2911f9ebf0968b7846444b5f4c..80147b0d738786932ff4225ac6678473247abea9 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.pdf;
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.fonts.Typeface;
 import org.apache.fop.fonts.FontDescriptor;
+import org.apache.fop.util.ColorProfileUtil;
 
 // Java
 import java.util.Iterator;
@@ -62,6 +63,12 @@ public class PDFResources extends PDFObject {
      */
     protected Set gstates = new HashSet();
 
+    /** Map of color spaces (key: color space name) */
+    protected Map colorSpaces = new HashMap();
+    
+    /** Map of ICC color spaces (key: ICC profile description) */
+    protected Map iccColorSpaces = new HashMap();
+    
     /**
      * create a /Resources object.
      *
@@ -139,6 +146,37 @@ public class PDFResources extends PDFObject {
         this.xObjects.add(xObject);
     }
 
+    /**
+     * Add a ColorSpace dictionary to the resources.
+     * @param colorSpace the color space 
+     */
+    public void addColorSpace(PDFICCBasedColorSpace colorSpace) {
+        this.colorSpaces.put(colorSpace.getName(), colorSpace);
+        String desc = ColorProfileUtil.getICCProfileDescription(
+                colorSpace.getICCStream().getICCProfile());
+        this.iccColorSpaces.put(desc, colorSpace);
+    }
+
+    /**
+     * Returns a ICCBased color space by profile name.
+     * @param desc the name of the color space
+     * @return the requested color space or null if it wasn't found
+     */
+    public PDFICCBasedColorSpace getICCColorSpaceByProfileName(String desc) {
+        PDFICCBasedColorSpace cs = (PDFICCBasedColorSpace)this.iccColorSpaces.get(desc);
+        return cs;
+    }
+
+    /**
+     * Returns a color space by name.
+     * @param name the name of the color space
+     * @return the requested color space or null if it wasn't found
+     */
+    public PDFICCBasedColorSpace getColorSpace(String name) {
+        PDFICCBasedColorSpace cs = (PDFICCBasedColorSpace)this.colorSpaces.get(name);
+        return cs;
+    }
+
     /**
      * represent the object in PDF
      * This adds the references to all the objects in the current
@@ -219,6 +257,17 @@ public class PDFResources extends PDFObject {
             p = p.append(">>\n");
         }
 
+        if (!this.colorSpaces.isEmpty()) {
+            p = p.append("/ColorSpace <<\n");
+            for (Iterator iter = colorSpaces.values().iterator(); iter.hasNext();) {
+                PDFICCBasedColorSpace colorSpace = (PDFICCBasedColorSpace)iter.next();
+                p = p.append("  /" + colorSpace.getName() + " "
+                             + colorSpace.referencePDF()
+                             + "\n");
+            }
+            p = p.append(">>\n");
+        }
+
         p = p.append(">>\nendobj\n");
 
         return p.toString();
index f6815222aa2df9950d63c809e8b072a719e6d486..62b4cc438d6ee2d4eba542e231424ae0dc4111b5 100644 (file)
@@ -47,7 +47,7 @@ public class PDFShading extends PDFObject {
     /**
      * A ColorSpace representing the colorspace. "DeviceRGB" is an example.
      */
-    protected PDFColorSpace colorSpace = null;
+    protected PDFDeviceColorSpace colorSpace = null;
 
     /**
      * The background color. Since shading is opaque,
@@ -163,7 +163,7 @@ public class PDFShading extends PDFObject {
      * It's optional, the default is the identity matrix
      * @param theFunction The PDF Function that maps an (x,y) location to a color
      */
-    public PDFShading(int theShadingType, PDFColorSpace theColorSpace,
+    public PDFShading(int theShadingType, PDFDeviceColorSpace theColorSpace,
                       List theBackground, List theBBox,
                       boolean theAntiAlias, List theDomain,
                       List theMatrix, PDFFunction theFunction) {
@@ -201,7 +201,7 @@ public class PDFShading extends PDFObject {
      *                  and end colors past the start and end points
      * The default is [false, false]
      */
-    public PDFShading(int theShadingType, PDFColorSpace theColorSpace,
+    public PDFShading(int theShadingType, PDFDeviceColorSpace theColorSpace,
                       List theBackground, List theBBox,
                       boolean theAntiAlias, List theCoords,
                       List theDomain, PDFFunction theFunction,
@@ -241,7 +241,7 @@ public class PDFShading extends PDFObject {
      * @param theDecode List of Doubles see PDF 1.3 spec pages 303 to 312.
      * @param theFunction the PDFFunction
      */
-    public PDFShading(int theShadingType, PDFColorSpace theColorSpace,
+    public PDFShading(int theShadingType, PDFDeviceColorSpace theColorSpace,
                       List theBackground, List theBBox,
                       boolean theAntiAlias, int theBitsPerCoordinate,
                       int theBitsPerComponent, int theBitsPerFlag,
@@ -280,7 +280,7 @@ public class PDFShading extends PDFObject {
      * @param theVerticesPerRow number of vertices in each "row" of the lattice.
      * @param theFunction The PDFFunction that's mapped on to this shape
      */
-    public PDFShading(int theShadingType, PDFColorSpace theColorSpace,
+    public PDFShading(int theShadingType, PDFDeviceColorSpace theColorSpace,
                       List theBackground, List theBBox,
                       boolean theAntiAlias, int theBitsPerCoordinate,
                       int theBitsPerComponent, List theDecode,
@@ -341,7 +341,7 @@ public class PDFShading extends PDFObject {
             + "<< \n/ShadingType " + this.shadingType + " \n");
         if (this.colorSpace != null) {
             p.append("/ColorSpace /"
-                     + this.colorSpace.getColorSpacePDFString() + " \n");
+                     + this.colorSpace.getName() + " \n");
         }
 
         if (this.background != null) {
diff --git a/src/java/org/apache/fop/pdf/PDFXMode.java b/src/java/org/apache/fop/pdf/PDFXMode.java
new file mode 100644 (file)
index 0000000..bfaa4aa
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+/** Enum class for PDF/X modes. */
+public final class PDFXMode {
+
+    /** PDF/X disabled */
+    public static final PDFXMode DISABLED = new PDFXMode("PDF/X disabled");
+    /** PDF/X-3:2003 enabled */
+    public static final PDFXMode PDFX_3_2003 = new PDFXMode("PDF/X-3:2003");
+    
+    private String name;
+
+    /**
+     * Constructor to add a new named item.
+     * @param name Name of the item.
+     */
+    private PDFXMode(String name) {
+        this.name = name;
+    }
+
+    /** @return the name of the enum */
+    public String getName() {
+        return this.name;
+    }
+    
+    /**
+     * Returns the mode enum object given a String.
+     * @param s the string
+     * @return the PDFAMode enum object (DISABLED will be returned if no match is found)
+     */
+    public static PDFXMode valueOf(String s) {
+        if (PDFX_3_2003.getName().equalsIgnoreCase(s)) {
+            return PDFX_3_2003;
+        } else {
+            return DISABLED;
+        }
+    }
+    
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        return name;
+    }
+    
+}
index 058628b792cf481c07ef6d78347e2d7c7367182c..66c6fecc5f8c59388c4dc0cb70e9eef8bf15c381 100644 (file)
@@ -94,10 +94,7 @@ public class PDFXObject extends AbstractPDFStream {
     
     private String buildDictionaryFromPS(String lengthEntry, 
                                          String dictEntries) {
-        if (getDocumentSafely().getPDFAMode().isPDFA1LevelB()) {
-            throw new PDFConformanceException("PostScript XObjects are prohibited when PDF/A"
-                    + " is active. Convert EPS graphics to another format.");
-        }
+        getDocumentSafely().getProfile().verifyPSXObjectsAllowed();
         StringBuffer sb = new StringBuffer(128);
         sb.append(getObjectID());
         sb.append("<</Type /XObject\n");
@@ -126,8 +123,8 @@ public class PDFXObject extends AbstractPDFStream {
             sb.append("/ColorSpace [/ICCBased "
                 + pdfICCStream.referencePDF() + "]\n");
         } else {
-            PDFColorSpace cs = pdfimage.getColorSpace();
-            sb.append("/ColorSpace /" + cs.getColorSpacePDFString()
+            PDFDeviceColorSpace cs = pdfimage.getColorSpace();
+            sb.append("/ColorSpace /" + cs.getName()
                   + "\n");
         }
 
@@ -136,11 +133,11 @@ public class PDFXObject extends AbstractPDFStream {
              * this will invert the values - too bad if it's not
              * a PhotoShop image...
              */
-            if (pdfimage.getColorSpace().getColorSpace() == PDFColorSpace.DEVICE_CMYK) {
+            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() == PDFColorSpace.DEVICE_RGB) {
+            } 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() == PDFColorSpace.DEVICE_GRAY) {
+            } else if (pdfimage.getColorSpace().getColorSpace() == PDFDeviceColorSpace.DEVICE_GRAY) {
                 sb.append("/Decode [ 1.0 0.0 ]\n");
             }
         }
index 0fdb74a1a6cd42aead0b221d1729503d144753b1..15e857675895a925a01f11fc6f39c02623216316 100644 (file)
@@ -130,6 +130,11 @@ public class RendererContext {
             this.context = context;
         }
         
+        /** @return the user agent */
+        public FOUserAgent getUserAgent() {
+            return context.getUserAgent();
+        }
+
         /** @return the currentXPosition */
         public int getCurrentXPosition() {
             return ((Integer)context.getProperty(RendererContextConstants.XPOS)).intValue();
index eed46a2181277b49f63a281093c3f3bfe108bef3..d5c1150e4f17b66e59ceab982ad3656aac6cf9ef 100644 (file)
@@ -21,6 +21,7 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.fop.pdf.PDFConformanceException;
 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;
@@ -28,7 +29,7 @@ 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.PDFColorSpace;
+import org.apache.fop.pdf.PDFDeviceColorSpace;
 import org.apache.fop.pdf.PDFXObject;
 import org.apache.fop.pdf.BitmapImage;
 import org.apache.fop.util.ColorProfileUtil;
@@ -118,23 +119,35 @@ public class FopPDFImage implements PDFImage {
             fopImage.load(FopImage.BITMAP);
         }
         ICC_Profile prof = fopImage.getICCProfile();
-        PDFColorSpace pdfCS = toPDFColorSpace(fopImage.getColorSpace());
+        PDFDeviceColorSpace pdfCS = toPDFColorSpace(fopImage.getColorSpace());
         if (prof != null) {
             boolean defaultsRGB = ColorProfileUtil.isDefaultsRGB(prof);
+            String desc = ColorProfileUtil.getICCProfileDescription(prof);
             if (log.isDebugEnabled()) {
-                String desc = ColorProfileUtil.getICCProfileDescription(prof);
                 log.debug("Image returns ICC profile: " + desc + ", default sRGB=" + defaultsRGB);
             }
-            //TODO Instead of skipping the ICC profile for sRGB, let's think about embedding our 
-            //own sRGB profile instead, but if possible only once per PDF (Need a new structure in 
-            //the PDF library for that).
+            PDFICCBasedColorSpace cs = doc.getResources().getICCColorSpaceByProfileName(desc);
             if (!defaultsRGB) {
-                pdfICCStream = doc.getFactory().makePDFICCStream();
-                pdfICCStream.setColorSpace(prof, pdfCS);
+                if (cs == null) {
+                    pdfICCStream = doc.getFactory().makePDFICCStream();
+                    pdfICCStream.setColorSpace(prof, pdfCS);
+                    cs = doc.getFactory().makeICCBasedColorSpace(null, null, pdfICCStream);
+                } else {
+                    pdfICCStream = cs.getICCStream();
+                }
+            } else {
+                if (cs == null && "sRGB".equals(desc)) {
+                    //It's the default sRGB profile which we mapped to DefaultRGB in PDFRenderer
+                    cs = doc.getResources().getColorSpace("DefaultRGB");
+                }
+                pdfICCStream = cs.getICCStream();
             }
         }
         //Handle transparency mask if applicable
         if (fopImage.hasSoftMask()) {
+            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) {
                 return;
@@ -142,19 +155,20 @@ public class FopPDFImage implements PDFImage {
             BitmapImage fopimg = new BitmapImage
                 ("Mask:" + key, fopImage.getWidth(), fopImage.getHeight(), 
                  softMask, null);
-            fopimg.setColorSpace(new PDFColorSpace(PDFColorSpace.DEVICE_GRAY));
+            fopimg.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
             PDFXObject xobj = doc.addImage(null, fopimg);
             softMaskRef = xobj.referencePDF();
         }
-        if (doc.getPDFAMode().isPDFA1LevelB()) {
+        if (doc.getProfile().getPDFAMode().isPDFA1LevelB()) {
             if (pdfCS != null
-                    && pdfCS.getColorSpace() != PDFColorSpace.DEVICE_RGB 
-                    && pdfCS.getColorSpace() != PDFColorSpace.DEVICE_GRAY
+                    && pdfCS.getColorSpace() != PDFDeviceColorSpace.DEVICE_RGB 
+                    && pdfCS.getColorSpace() != PDFDeviceColorSpace.DEVICE_GRAY
                     && prof == null) {
                 //See PDF/A-1, ISO 19005:1:2005(E), 6.2.3.3
                 //FOP is currently restricted to DeviceRGB if PDF/A-1 is active.
                 throw new PDFConformanceException(
-                        "PDF/A-1 does not allow mixing DeviceRGB and DeviceCMYK.");
+                        "PDF/A-1 does not allow mixing DeviceRGB and DeviceCMYK: " 
+                                + fopImage.getOriginalURI());
             }
         }
     }
@@ -176,7 +190,7 @@ public class FopPDFImage implements PDFImage {
     /**
      * @see org.apache.fop.pdf.PDFImage#getColorSpace()
      */
-    public PDFColorSpace getColorSpace() {
+    public PDFDeviceColorSpace getColorSpace() {
         // DeviceGray, DeviceRGB, or DeviceCMYK
         if (isCCF || isDCT || isPS) {
             return toPDFColorSpace(fopImage.getColorSpace());
@@ -320,21 +334,21 @@ public class FopPDFImage implements PDFImage {
      * @param cs ColorSpace instance
      * @return PDFColorSpace new converted object
      */
-    public static PDFColorSpace toPDFColorSpace(ColorSpace cs) {
+    public static PDFDeviceColorSpace toPDFColorSpace(ColorSpace cs) {
         if (cs == null) {
             return null;
         }
 
-        PDFColorSpace pdfCS = new PDFColorSpace(0);
+        PDFDeviceColorSpace pdfCS = new PDFDeviceColorSpace(0);
         switch(cs.getType()) {
             case ColorSpace.TYPE_CMYK:
-                pdfCS.setColorSpace(PDFColorSpace.DEVICE_CMYK);
+                pdfCS.setColorSpace(PDFDeviceColorSpace.DEVICE_CMYK);
             break;
             case ColorSpace.TYPE_RGB:
-                pdfCS.setColorSpace(PDFColorSpace.DEVICE_RGB);
+                pdfCS.setColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
             break;
             case ColorSpace.TYPE_GRAY:
-                pdfCS.setColorSpace(PDFColorSpace.DEVICE_GRAY);
+                pdfCS.setColorSpace(PDFDeviceColorSpace.DEVICE_GRAY);
             break;
         }
         return pdfCS;
index 0eb9bc5625f2a80d5144f404cc642b28b7ac3ff7..9598037b921ca0b81fd854cc26c6bddd7f7473db 100644 (file)
@@ -20,19 +20,23 @@ package org.apache.fop.render.pdf;
 \r
 import java.awt.Color;\r
 import java.awt.Dimension;\r
+import java.awt.Graphics2D;\r
+import java.awt.RenderingHints;\r
 import java.awt.geom.AffineTransform;\r
 import java.awt.geom.Rectangle2D;\r
+import java.awt.image.BufferedImage;\r
 import java.io.IOException;\r
 \r
-import org.apache.fop.render.Graphics2DAdapter;\r
+import org.apache.fop.render.AbstractGraphics2DAdapter;\r
 import org.apache.fop.render.Graphics2DImagePainter;\r
 import org.apache.fop.render.RendererContext;\r
+import org.apache.fop.render.RendererContext.RendererContextWrapper;\r
 import org.apache.fop.svg.PDFGraphics2D;\r
 \r
 /**\r
  * Graphics2DAdapter implementation for PDF.\r
  */\r
-public class PDFGraphics2DAdapter implements Graphics2DAdapter {\r
+public class PDFGraphics2DAdapter extends AbstractGraphics2DAdapter {\r
 \r
     private PDFRenderer renderer;\r
 \r
@@ -50,7 +54,6 @@ public class PDFGraphics2DAdapter implements Graphics2DAdapter {
             int x, int y, int width, int height) throws IOException {\r
         \r
         PDFSVGHandler.PDFInfo pdfInfo = PDFSVGHandler.getPDFInfo(context);\r
-        \r
         float fwidth = width / 1000f;\r
         float fheight = height / 1000f;\r
         float fx = x / 1000f;\r
@@ -78,6 +81,9 @@ public class PDFGraphics2DAdapter implements Graphics2DAdapter {
 \r
 \r
         final boolean textAsShapes = false;\r
+        if (pdfInfo.pdfContext == null) {\r
+            pdfInfo.pdfContext = pdfInfo.pdfPage;\r
+        }\r
         PDFGraphics2D graphics = new PDFGraphics2D(textAsShapes, \r
                 pdfInfo.fi, pdfInfo.pdfDoc,\r
                 pdfInfo.pdfContext, pdfInfo.pdfPage.referencePDF(),\r
@@ -91,13 +97,35 @@ public class PDFGraphics2DAdapter implements Graphics2DAdapter {
         graphics.setPDFState(pdfInfo.pdfState);\r
         graphics.setOutputStream(pdfInfo.outputStream);\r
 \r
-        Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, imw, imh);\r
-        painter.paint(graphics, area);\r
+        if (pdfInfo.paintAsBitmap) {\r
+            //Fallback solution: Paint to a BufferedImage\r
+            int resolution = (int)Math.round(context.getUserAgent().getTargetResolution());\r
+            RendererContextWrapper ctx = RendererContext.wrapRendererContext(context);\r
+            BufferedImage bi = paintToBufferedImage(painter, ctx, resolution, false, false);\r
+\r
+            float scale = PDFRenderer.NORMAL_PDF_RESOLUTION \r
+                            / context.getUserAgent().getTargetResolution();\r
+            graphics.drawImage(bi, new AffineTransform(scale, 0, 0, scale, 0, 0), null);\r
+        } else {\r
+            Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, imw, imh);\r
+            painter.paint(graphics, area);\r
+        }\r
 \r
         pdfInfo.currentStream.add(graphics.getString());\r
         renderer.restoreGraphicsState();\r
         pdfInfo.pdfState.pop();\r
-    \r
+    }\r
+\r
+    /**\r
+     * @see org.apache.fop.render.AbstractGraphics2DAdapter#setRenderingHintsForBufferedImage(\r
+     *              java.awt.Graphics2D)\r
+     */\r
+    protected void setRenderingHintsForBufferedImage(Graphics2D g2d) {\r
+        super.setRenderingHintsForBufferedImage(g2d);\r
+        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, \r
+                RenderingHints.VALUE_ANTIALIAS_ON);\r
+        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, \r
+                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);\r
     }\r
 \r
 }\r
index 86dbea0641de7d24036583c5df65a19cb29686e3..1888598aaf01c9a40efa9256d8034ed9f4cfee7c 100644 (file)
@@ -22,7 +22,7 @@ package org.apache.fop.render.pdf;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
+import java.net.URL;
 import java.awt.Color;
 import java.awt.color.ColorSpace;
 import java.awt.color.ICC_Profile;
@@ -32,6 +32,9 @@ import java.util.Iterator;
 import java.util.Map;
 import java.util.List;
 
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
 // XML
 import org.w3c.dom.Document;
 
@@ -68,10 +71,12 @@ import org.apache.fop.image.XMLImage;
 import org.apache.fop.pdf.PDFAMode;
 import org.apache.fop.pdf.PDFAnnotList;
 import org.apache.fop.pdf.PDFColor;
+import org.apache.fop.pdf.PDFConformanceException;
 import org.apache.fop.pdf.PDFDocument;
 import org.apache.fop.pdf.PDFEncryptionManager;
 import org.apache.fop.pdf.PDFEncryptionParams;
 import org.apache.fop.pdf.PDFFilterList;
+import org.apache.fop.pdf.PDFICCBasedColorSpace;
 import org.apache.fop.pdf.PDFICCStream;
 import org.apache.fop.pdf.PDFInfo;
 import org.apache.fop.pdf.PDFLink;
@@ -85,6 +90,7 @@ import org.apache.fop.pdf.PDFResources;
 import org.apache.fop.pdf.PDFState;
 import org.apache.fop.pdf.PDFStream;
 import org.apache.fop.pdf.PDFText;
+import org.apache.fop.pdf.PDFXMode;
 import org.apache.fop.pdf.PDFXObject;
 import org.apache.fop.render.AbstractPathOrientedRenderer;
 import org.apache.fop.render.Graphics2DAdapter;
@@ -118,6 +124,9 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
      */
     public static final String MIME_TYPE = MimeConstants.MIME_PDF;
 
+    /** Normal PDF resolution (72dpi) */
+    public static final int NORMAL_PDF_RESOLUTION = 72;
+    
     /** PDF encryption parameter: all parameters as object, datatype: PDFEncryptionParams */
     public static final String ENCRYPTION_PARAMS = "encryption-params";
     /** PDF encryption parameter: user password, datatype: String */
@@ -134,6 +143,10 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
     public static final String NO_ANNOTATIONS = "noannotations";
     /** Rendering Options key for the PDF/A mode. */
     public static final String PDF_A_MODE = "pdf-a-mode";
+    /** Rendering Options key for the PDF/X mode. */
+    public static final String PDF_X_MODE = "pdf-x-mode";
+    /** Rendering Options key for the ICC profile for the output intent. */
+    public static final String KEY_OUTPUT_PROFILE = "output-profile";
 
     /** Controls whether comments are written to the PDF stream. */
     protected static final boolean WRITE_COMMENTS = true;
@@ -146,6 +159,9 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
     /** the PDF/A mode (Default: disabled) */
     protected PDFAMode pdfAMode = PDFAMode.DISABLED;
     
+    /** the PDF/X mode (Default: disabled) */
+    protected PDFXMode pdfXMode = PDFXMode.DISABLED;
+    
     /**
      * Map of pages using the PageViewport as the key
      * this is used for prepared pages that cannot be immediately
@@ -190,6 +206,16 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
     
     /** the (optional) encryption parameters */
     protected PDFEncryptionParams encryptionParams;
+
+    /** the ICC stream used as output profile by this document for PDF/A and PDF/X functionality. */
+    protected PDFICCStream outputProfile;
+    /** the ICC stream for the sRGB color space. */
+    //protected PDFICCStream sRGBProfile;
+    /** the default sRGB color space. */
+    protected PDFICCBasedColorSpace sRGBColorSpace;
+    
+    /** Optional URI to an output profile to be used. */
+    protected String outputProfileURI; 
     
     /** The current Transform */
     protected AffineTransform currentBasicTransform;
@@ -267,6 +293,14 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
         if (s != null) {
             this.pdfAMode = PDFAMode.valueOf(s);
         }
+        s = cfg.getChild(PDF_X_MODE, true).getValue(null);
+        if (s != null) {
+            this.pdfXMode = PDFXMode.valueOf(s);
+        }
+        s = cfg.getChild(KEY_OUTPUT_PROFILE, true).getValue(null);
+        if (s != null) {
+            this.outputProfileURI = s;
+        }
     }
 
     private boolean booleanValueOf(Object obj) {
@@ -337,6 +371,14 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
         if (s != null) {
             this.pdfAMode = PDFAMode.valueOf(s);
         }
+        s = (String)agent.getRendererOptions().get(PDF_X_MODE);
+        if (s != null) {
+            this.pdfXMode = PDFXMode.valueOf(s);
+        }
+        s = (String)agent.getRendererOptions().get(KEY_OUTPUT_PROFILE);
+        if (s != null) {
+            this.outputProfileURI = s;
+        }
     }
 
     /**
@@ -349,7 +391,8 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
         ostream = stream;
         this.pdfDoc = new PDFDocument(
                 userAgent.getProducer() != null ? userAgent.getProducer() : "");
-        this.pdfDoc.setPDFAMode(this.pdfAMode);
+        this.pdfDoc.getProfile().setPDFAMode(this.pdfAMode);
+        this.pdfDoc.getProfile().setPDFXMode(this.pdfXMode);
         this.pdfDoc.setCreator(userAgent.getCreator());
         this.pdfDoc.setCreationDate(userAgent.getCreationDate());
         this.pdfDoc.getInfo().setAuthor(userAgent.getAuthor());
@@ -358,25 +401,32 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
         this.pdfDoc.setFilterMap(filterMap);
         this.pdfDoc.outputHeader(stream);
 
+        //Setup encryption if necessary
+        PDFEncryptionManager.setupPDFEncryption(encryptionParams, this.pdfDoc);
+
+        addsRGBColorSpace();
+        if (this.outputProfileURI != null) {
+            addDefaultOutputProfile();
+        }
+        if (pdfXMode != PDFXMode.DISABLED) {
+            log.debug(pdfXMode + " is active.");
+            log.warn("Note: " + pdfXMode 
+                    + " support is work-in-progress and not fully implemented, yet!");
+            addPDFXOutputIntent();
+        }
         if (pdfAMode.isPDFA1LevelB()) {
             log.debug("PDF/A is active. Conformance Level: " + pdfAMode);
             addPDFA1OutputIntent();
         }
         
-        //Setup encryption if necessary
-        PDFEncryptionManager.setupPDFEncryption(encryptionParams, this.pdfDoc);
     }
 
-    /**
-     * Adds an OutputIntent to the PDF as mandated by PDF/A-1 when uncalibrated color spaces
-     * are used (which is true if we use DeviceRGB to represent sRGB colors).
-     * @throws IOException in case of an I/O problem
-     */
-    private void addPDFA1OutputIntent() throws IOException {
-        PDFOutputIntent outputIntent = pdfDoc.getFactory().makeOutputIntent();
-        outputIntent.setSubtype(PDFOutputIntent.GTS_PDFA1);
-        PDFICCStream icc = pdfDoc.getFactory().makePDFICCStream();
+    private void addsRGBColorSpace() throws IOException {
+        if (this.sRGBColorSpace != null) {
+            return;
+        }
         ICC_Profile profile;
+        PDFICCStream sRGBProfile = pdfDoc.getFactory().makePDFICCStream();
         InputStream in = PDFDocument.class.getResourceAsStream("sRGB Color Space Profile.icm");
         if (in != null) {
             try {
@@ -388,10 +438,77 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
             //Fallback: Use the sRGB profile from the JRE (about 140KB)
             profile = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
         }
-        String desc = ColorProfileUtil.getICCProfileDescription(profile);
+        sRGBProfile.setColorSpace(profile, null);
+        
+        //Map sRGB as default RGB profile for DeviceRGB
+        this.sRGBColorSpace = pdfDoc.getFactory().makeICCBasedColorSpace(
+                null, "DefaultRGB", sRGBProfile);
+    }
+    
+    private void addDefaultOutputProfile() throws IOException {
+        if (this.outputProfile != null) {
+            return;
+        }
+        ICC_Profile profile;
+        InputStream in = null;
+        if (this.outputProfileURI != null) {
+            this.outputProfile = pdfDoc.getFactory().makePDFICCStream();
+            Source src = userAgent.resolveURI(this.outputProfileURI);
+            if (src == null) {
+                throw new IOException("Output profile not found: " + this.outputProfileURI);
+            }
+            if (src instanceof StreamSource) {
+                in = ((StreamSource)src).getInputStream();
+            } else {
+                in = new URL(src.getSystemId()).openStream();
+            }
+            try {
+                profile = ICC_Profile.getInstance(in);
+            } finally {
+                IOUtils.closeQuietly(in);
+            }
+            this.outputProfile.setColorSpace(profile, null);
+        } else {
+            //Fall back to sRGB profile
+            outputProfile = sRGBColorSpace.getICCStream();
+        }
+    }
+    
+    /**
+     * Adds an OutputIntent to the PDF as mandated by PDF/A-1 when uncalibrated color spaces
+     * are used (which is true if we use DeviceRGB to represent sRGB colors).
+     * @throws IOException in case of an I/O problem
+     */
+    private void addPDFA1OutputIntent() throws IOException {
+        addDefaultOutputProfile();
         
-        icc.setColorSpace(profile, null);
-        outputIntent.setDestOutputProfile(icc);
+        String desc = ColorProfileUtil.getICCProfileDescription(this.outputProfile.getICCProfile());
+        PDFOutputIntent outputIntent = pdfDoc.getFactory().makeOutputIntent();
+        outputIntent.setSubtype(PDFOutputIntent.GTS_PDFA1);
+        outputIntent.setDestOutputProfile(this.outputProfile);
+        outputIntent.setOutputConditionIdentifier(desc);
+        outputIntent.setInfo(outputIntent.getOutputConditionIdentifier());
+        pdfDoc.getRoot().addOutputIntent(outputIntent);
+    }
+
+    /**
+     * Adds an OutputIntent to the PDF as mandated by PDF/X when uncalibrated color spaces
+     * are used (which is true if we use DeviceRGB to represent sRGB colors).
+     * @throws IOException in case of an I/O problem
+     */
+    private void addPDFXOutputIntent() throws IOException {
+        addDefaultOutputProfile();
+        
+        String desc = ColorProfileUtil.getICCProfileDescription(this.outputProfile.getICCProfile());
+        int deviceClass = this.outputProfile.getICCProfile().getProfileClass();
+        if (deviceClass != ICC_Profile.CLASS_OUTPUT) {
+            throw new PDFConformanceException(pdfDoc.getProfile().getPDFXMode() + " requires that"
+                    + " the DestOutputProfile be an Output Device Profile. "
+                    + desc + " does not match that requirement.");
+        }
+        PDFOutputIntent outputIntent = pdfDoc.getFactory().makeOutputIntent();
+        outputIntent.setSubtype(PDFOutputIntent.GTS_PDFX);
+        outputIntent.setDestOutputProfile(this.outputProfile);
         outputIntent.setOutputConditionIdentifier(desc);
         outputIntent.setInfo(outputIntent.getOutputConditionIdentifier());
         pdfDoc.getRoot().addOutputIntent(outputIntent);
@@ -1015,36 +1132,40 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
         // render contents
         super.renderInlineParent(ip);
 
-        // place the link over the top
-        Object tr = ip.getTrait(Trait.INTERNAL_LINK);
-        boolean internal = false;
-        String dest = null;
-        float yoffset = 0;
-        if (tr == null) {
-            dest = (String)ip.getTrait(Trait.EXTERNAL_LINK);
-        } else {
-            String pvKey = (String)tr;
-            dest = (String)pageReferences.get(pvKey);
+        if (pdfDoc.getProfile().isAnnotationAllowed()) {
+            // place the link over the top
+            Object tr = ip.getTrait(Trait.INTERNAL_LINK);
+            boolean internal = false;
+            String dest = null;
+            float yoffset = 0;
+            if (tr == null) {
+                dest = (String)ip.getTrait(Trait.EXTERNAL_LINK);
+            } else {
+                String pvKey = (String)tr;
+                dest = (String)pageReferences.get(pvKey);
+                if (dest != null) {
+                    PageViewport pv = (PageViewport)pvReferences.get(pvKey);
+                    Rectangle2D bounds = pv.getViewArea();
+                    double h = bounds.getHeight();
+                    yoffset = (float)h / 1000f;
+                    internal = true;
+                }
+            }
             if (dest != null) {
-                PageViewport pv = (PageViewport)pvReferences.get(pvKey);
-                Rectangle2D bounds = pv.getViewArea();
-                double h = bounds.getHeight();
-                yoffset = (float)h / 1000f;
-                internal = true;
+                // add link to pdf document
+                Rectangle2D rect = new Rectangle2D.Float(start, top, width, height);
+                // transform rect to absolute coords
+                AffineTransform transform = currentState.getTransform();
+                rect = transform.createTransformedShape(rect).getBounds2D();
+                rect = currentBasicTransform.createTransformedShape(rect).getBounds2D();
+
+                int type = internal ? PDFLink.INTERNAL : PDFLink.EXTERNAL;
+                PDFLink pdflink = pdfDoc.getFactory().makeLink(
+                            rect, dest, type, yoffset);
+                currentPage.addAnnotation(pdflink);
             }
-        }
-        if (dest != null) {
-            // add link to pdf document
-            Rectangle2D rect = new Rectangle2D.Float(start, top, width, height);
-            // transform rect to absolute coords
-            AffineTransform transform = currentState.getTransform();
-            rect = transform.createTransformedShape(rect).getBounds2D();
-            rect = currentBasicTransform.createTransformedShape(rect).getBounds2D();
-
-            int type = internal ? PDFLink.INTERNAL : PDFLink.EXTERNAL;
-            PDFLink pdflink = pdfDoc.getFactory().makeLink(
-                        rect, dest, type, yoffset);
-            currentPage.addAnnotation(pdflink);
+        } else {
+            log.warn("Skipping annotation for a link due to PDF profile: " + pdfDoc.getProfile());
         }
     }
 
index 3aff8d94503eefbc71e4db108cbec0d2190f5f37..ad306a7ae0d7413cd57dbbe09ef58dfcd62e278e 100644 (file)
@@ -18,7 +18,9 @@
 
 package org.apache.fop.render.pdf;
 
+import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
 import java.awt.Color;
 import java.awt.geom.AffineTransform;
 
@@ -26,9 +28,10 @@ import org.w3c.dom.Document;
 import org.w3c.dom.svg.SVGDocument;
 import org.w3c.dom.svg.SVGSVGElement;
 
+import org.apache.fop.render.AbstractGenericSVGHandler;
 import org.apache.fop.render.Renderer;
-import org.apache.fop.render.XMLHandler;
 import org.apache.fop.render.RendererContext;
+import org.apache.fop.render.RendererContextConstants;
 import org.apache.fop.pdf.PDFDocument;
 import org.apache.fop.pdf.PDFNumber;
 import org.apache.fop.pdf.PDFPage;
@@ -38,6 +41,8 @@ import org.apache.fop.pdf.PDFResourceContext;
 import org.apache.fop.svg.PDFBridgeContext;
 import org.apache.fop.svg.PDFGraphics2D;
 import org.apache.fop.svg.SVGUserAgent;
+import org.apache.fop.util.QName;
+import org.apache.fop.fo.extensions.ExtensionElementMapping;
 import org.apache.fop.fonts.FontInfo;
 
 // Commons-Logging
@@ -49,7 +54,6 @@ import org.apache.avalon.framework.configuration.Configuration;
 import org.apache.batik.bridge.GVTBuilder;
 import org.apache.batik.bridge.BridgeContext;
 import org.apache.batik.bridge.ViewBox;
-import org.apache.batik.dom.svg.SVGDOMImplementation;
 import org.apache.batik.gvt.GraphicsNode;
 
 /**
@@ -58,29 +62,12 @@ import org.apache.batik.gvt.GraphicsNode;
  * It renders SVG to the PDF document using the PDFGraphics2D.
  * The properties from the PDF renderer are subject to change.
  */
-public class PDFSVGHandler implements XMLHandler, PDFRendererContextConstants {
+public class PDFSVGHandler extends AbstractGenericSVGHandler 
+            implements PDFRendererContextConstants {
 
     /** logging instance */
     private static Log log = LogFactory.getLog(PDFSVGHandler.class);
 
-
-    /**
-     * Create a new PDF XML handler for use by the PDF renderer.
-     */
-    public PDFSVGHandler() {
-    }
-
-    /** @see org.apache.fop.render.XMLHandler */
-    public void handleXML(RendererContext context, 
-                Document doc, String ns) throws Exception {
-        PDFInfo pdfi = getPDFInfo(context);
-
-        String svg = "http://www.w3.org/2000/svg";
-        if (svg.equals(ns)) {
-            renderSVGDocument(context, doc, pdfi);
-        }
-    }
-
     /**
      * Get the pdf information from the render context.
      *
@@ -103,6 +90,12 @@ public class PDFSVGHandler implements XMLHandler, PDFRendererContextConstants {
         pdfi.currentXPosition = ((Integer)context.getProperty(XPOS)).intValue();
         pdfi.currentYPosition = ((Integer)context.getProperty(YPOS)).intValue();
         pdfi.cfg = (Configuration)context.getProperty(HANDLER_CONFIGURATION);
+        Map foreign = (Map)context.getProperty(RendererContextConstants.FOREIGN_ATTRIBUTES);
+        QName qName = new QName(ExtensionElementMapping.URI, null, "conversion-mode");
+        if (foreign != null 
+                && "bitmap".equalsIgnoreCase((String)foreign.get(qName))) {
+            pdfi.paintAsBitmap = true;
+        }
         return pdfi;
     }
 
@@ -138,16 +131,26 @@ public class PDFSVGHandler implements XMLHandler, PDFRendererContextConstants {
         public int currentYPosition;
         /** see PDF_HANDLER_CONFIGURATION */
         public Configuration cfg;
+        /** true if SVG should be rendered as a bitmap instead of natively */
+        public boolean paintAsBitmap;
     }
 
     /**
-     * Render the svg document.
-     * @param context the renderer context
-     * @param doc the svg document
-     * @param pdfInfo the pdf information of the current context
+     * @see org.apache.fop.render.AbstractGenericSVGHandler#renderSVGDocument(
+     *          org.apache.fop.render.RendererContext, org.w3c.dom.Document)
      */
     protected void renderSVGDocument(RendererContext context,
-            Document doc, PDFInfo pdfInfo) {
+            Document doc) {
+        PDFInfo pdfInfo = getPDFInfo(context);
+        if (pdfInfo.paintAsBitmap) {
+            try {
+                super.renderSVGDocument(context, doc);
+            } catch (IOException ioe) {
+                log.error("I/O error while rendering SVG graphic: "
+                                       + ioe.getMessage(), ioe);
+            }
+            return;
+        }
         int xOffset = pdfInfo.currentXPosition;
         int yOffset = pdfInfo.currentYPosition;
 
@@ -262,9 +265,4 @@ public class PDFSVGHandler implements XMLHandler, PDFRendererContextConstants {
     public boolean supportsRenderer(Renderer renderer) {
         return (renderer instanceof PDFRenderer);
     }
-    /** @see org.apache.fop.render.XMLHandler#getNamespace() */
-    public String getNamespace() {
-        return SVGDOMImplementation.SVG_NAMESPACE_URI;
-    }
-    
 }
index a563449de47ea0c170bf8b81b744fe8ef8f6f7eb..f3bf3037c84ad8713478df81802a4d8114953255 100644 (file)
@@ -22,7 +22,7 @@ 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.PDFColorSpace;
+import org.apache.fop.pdf.PDFDeviceColorSpace;
 import org.apache.fop.pdf.PDFColor;
 import org.apache.fop.pdf.PDFState;
 import org.apache.fop.pdf.PDFNumber;
@@ -352,6 +352,9 @@ public class PDFGraphics2D extends AbstractGraphics2D {
      * @param linkType the type of link, internal or external
      */
     public void addLink(Rectangle2D bounds, AffineTransform trans, String dest, int linkType) {
+        if (!pdfDoc.getProfile().isAnnotationAllowed()) {
+            return;
+        }
         preparePainting();
         AffineTransform at = getTransform();
         Shape b = at.createTransformedShape(bounds);
@@ -563,7 +566,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
                 BitmapImage fopimg = new BitmapImage("TempImageMask:"
                                              + img.toString(), buf.getWidth(),
                                              buf.getHeight(), mask, null);
-                fopimg.setColorSpace(new PDFColorSpace(PDFColorSpace.DEVICE_GRAY));
+                fopimg.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
                 PDFXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
                 ref = xobj.referencePDF();
 
@@ -693,12 +696,12 @@ public class PDFGraphics2D extends AbstractGraphics2D {
         }
 
         if (c.getAlpha() != 255) {
+            checkTransparencyAllowed();
             Map vals = new java.util.HashMap();
             vals.put(PDFGState.GSTATE_ALPHA_STROKE, 
                     new Float(c.getAlpha() / 255f));
             PDFGState gstate = pdfDoc.getFactory().makeGState(
                     vals, graphicsState.getGState());
-            //gstate.setAlpha(c.getAlpha() / 255f, false);
             resourceContext.addGState(gstate);
             currentStream.write("/" + gstate.getName() + " gs\n");
         }
@@ -815,7 +818,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
             currentStream.write(currentColour.getColorSpaceOut(fill));
         } else if (c.getColorSpace().getType()
                    == ColorSpace.TYPE_CMYK) {
-            if (pdfDoc.getPDFAMode().isPDFA1LevelB()) {
+            if (pdfDoc.getProfile().getPDFAMode().isPDFA1LevelB()) {
                 //See PDF/A-1, ISO 19005:1:2005(E), 6.2.3.3
                 //FOP is currently restricted to DeviceRGB if PDF/A-1 is active.
                 throw new PDFConformanceException(
@@ -944,8 +947,8 @@ public class PDFGraphics2D extends AbstractGraphics2D {
                 }
             }
 
-            PDFColorSpace aColorSpace;
-            aColorSpace = new PDFColorSpace(PDFColorSpace.DEVICE_RGB);
+            PDFDeviceColorSpace aColorSpace;
+            aColorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
             PDFPattern myPat = pdfDoc.getFactory().makeGradient(
                     resourceContext, false, aColorSpace,
                     someColors, theBounds, theCoords, theMatrix);
@@ -1021,8 +1024,8 @@ public class PDFGraphics2D extends AbstractGraphics2D {
                 float offset = fractions[count];
                 theBounds.add(new Double(offset));
             }
-            PDFColorSpace colSpace;
-            colSpace = new PDFColorSpace(PDFColorSpace.DEVICE_RGB);
+            PDFDeviceColorSpace colSpace;
+            colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
 
             PDFPattern myPat = pdfDoc.getFactory().makeGradient
                 (resourceContext, true, colSpace,
@@ -1221,7 +1224,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
             if (mask != null) {
                 BitmapImage fopimg = new BitmapImage
                     ("TempImageMask:" + pctx.toString(), devW, devH, mask, null);
-                fopimg.setColorSpace(new PDFColorSpace(PDFColorSpace.DEVICE_GRAY));
+                fopimg.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
                 PDFXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
                 maskRef = xobj.referencePDF();
 
@@ -1439,6 +1442,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
         int salpha = c.getAlpha();
 
         if (salpha != 255) {
+            checkTransparencyAllowed();
             Map vals = new java.util.HashMap();
             vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(salpha / 255f));
             PDFGState gstate = pdfDoc.getFactory().makeGState(
@@ -1687,6 +1691,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
         }
 
         if (c.getAlpha() != 255) {
+            checkTransparencyAllowed();
             Map vals = new java.util.HashMap();
             vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, 
                     new Float(c.getAlpha() / 255f));
@@ -1730,6 +1735,11 @@ public class PDFGraphics2D extends AbstractGraphics2D {
         }
     }
 
+    /** Checks whether the use of transparency is allowed. */
+    protected void checkTransparencyAllowed() {
+        pdfDoc.getProfile().verifyTransparencyAllowed("Java2D graphics");
+    }
+
     /**
      * Processes a path iterator generating the necessary painting operations.
      * @param iter PathIterator to process
index ae46cceb1ce0c4039101293bcc2e0775b5d4a5f2..1dc0245fa3ca65b0783901f4d2ced5d953e58a77 100644 (file)
 
   <changes>
     <release version="FOP Trunk">
+      <action context="Code" dev="JM" type="add">
+        Added support for PDF/A-1b and PDF/X-3:2003. (Note: this may still be a bit
+        incomplete. Feedback is welcome!)
+      </action>
+      <action context="Code" dev="JM" type="add">
+        The CIDSet object is now generated for CID fonts which are embedded in PDFs.
+        (Required by PDF/A).
+      </action>
       <action context="Code" dev="JM" type="update">
         The Java2DRenderer (and therefore the print and bitmap renderers) is now
         offering the same quality as the PDF and PS renderers. Note: There can still