Browse Source

Added initial support for PDF/X-3:2003 (ISO 15930-6:2003(E)).

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
pull/25/head
Jeremias Maerki 18 years ago
parent
commit
cbc3059c39
29 changed files with 1097 additions and 349 deletions
  1. 4
    4
      src/java/org/apache/fop/pdf/BitmapImage.java
  2. 12
    12
      src/java/org/apache/fop/pdf/PDFColor.java
  3. 18
    136
      src/java/org/apache/fop/pdf/PDFColorSpace.java
  4. 139
    0
      src/java/org/apache/fop/pdf/PDFDeviceColorSpace.java
  5. 12
    34
      src/java/org/apache/fop/pdf/PDFDocument.java
  6. 86
    18
      src/java/org/apache/fop/pdf/PDFFactory.java
  7. 3
    2
      src/java/org/apache/fop/pdf/PDFFont.java
  8. 18
    0
      src/java/org/apache/fop/pdf/PDFFontDescriptor.java
  9. 3
    2
      src/java/org/apache/fop/pdf/PDFFontNonBase14.java
  10. 95
    0
      src/java/org/apache/fop/pdf/PDFICCBasedColorSpace.java
  11. 10
    8
      src/java/org/apache/fop/pdf/PDFICCStream.java
  12. 1
    1
      src/java/org/apache/fop/pdf/PDFImage.java
  13. 21
    2
      src/java/org/apache/fop/pdf/PDFInfo.java
  14. 2
    1
      src/java/org/apache/fop/pdf/PDFLink.java
  15. 8
    2
      src/java/org/apache/fop/pdf/PDFMetadata.java
  16. 4
    2
      src/java/org/apache/fop/pdf/PDFPage.java
  17. 1
    1
      src/java/org/apache/fop/pdf/PDFPathPaint.java
  18. 244
    0
      src/java/org/apache/fop/pdf/PDFProfile.java
  19. 49
    0
      src/java/org/apache/fop/pdf/PDFResources.java
  20. 6
    6
      src/java/org/apache/fop/pdf/PDFShading.java
  21. 62
    0
      src/java/org/apache/fop/pdf/PDFXMode.java
  22. 6
    9
      src/java/org/apache/fop/pdf/PDFXObject.java
  23. 5
    0
      src/java/org/apache/fop/render/RendererContext.java
  24. 33
    19
      src/java/org/apache/fop/render/pdf/FopPDFImage.java
  25. 34
    6
      src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java
  26. 165
    44
      src/java/org/apache/fop/render/pdf/PDFRenderer.java
  27. 29
    31
      src/java/org/apache/fop/render/pdf/PDFSVGHandler.java
  28. 19
    9
      src/java/org/apache/fop/svg/PDFGraphics2D.java
  29. 8
    0
      status.xml

+ 4
- 4
src/java/org/apache/fop/pdf/BitmapImage.java View 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;
}


+ 12
- 12
src/java/org/apache/fop/pdf/PDFColor.java View 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) {

+ 18
- 136
src/java/org/apache/fop/pdf/PDFColorSpace.java View 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();
}

+ 139
- 0
src/java/org/apache/fop/pdf/PDFDeviceColorSpace.java View File

@@ -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;
}
}

+ 12
- 34
src/java/org/apache/fop/pdf/PDFDocument.java View 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
}
}
}

+ 86
- 18
src/java/org/apache/fop/pdf/PDFFactory.java View 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)
*

+ 3
- 2
src/java/org/apache/fop/pdf/PDFFont.java View 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);
}
}

+ 18
- 0
src/java/org/apache/fop/pdf/PDFFontDescriptor.java View 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);

+ 3
- 2
src/java/org/apache/fop/pdf/PDFFontNonBase14.java View 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!");
}
}
}

+ 95
- 0
src/java/org/apache/fop/pdf/PDFICCBasedColorSpace.java View File

@@ -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();
}

}

+ 10
- 8
src/java/org/apache/fop/pdf/PDFICCStream.java View 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);

+ 1
- 1
src/java/org/apache/fop/pdf/PDFImage.java View File

@@ -65,7 +65,7 @@ public interface PDFImage {
*
* @return the color space
*/
PDFColorSpace getColorSpace();
PDFDeviceColorSpace getColorSpace();

/**
* Get the bits per pixel for this image.

+ 21
- 2
src/java/org/apache/fop/pdf/PDFInfo.java View 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);
}

+ 2
- 1
src/java/org/apache/fop/pdf/PDFLink.java View 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

+ 8
- 2
src/java/org/apache/fop/pdf/PDFMetadata.java View 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);

+ 4
- 2
src/java/org/apache/fop/pdf/PDFPage.java View 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) {

+ 1
- 1
src/java/org/apache/fop/pdf/PDFPathPaint.java View 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.

+ 244
- 0
src/java/org/apache/fop/pdf/PDFProfile.java View File

@@ -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()));
}
}

}

+ 49
- 0
src/java/org/apache/fop/pdf/PDFResources.java View 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();

+ 6
- 6
src/java/org/apache/fop/pdf/PDFShading.java View 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) {

+ 62
- 0
src/java/org/apache/fop/pdf/PDFXMode.java View File

@@ -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;
}
}

+ 6
- 9
src/java/org/apache/fop/pdf/PDFXObject.java View 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");
}
}

+ 5
- 0
src/java/org/apache/fop/render/RendererContext.java View 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();

+ 33
- 19
src/java/org/apache/fop/render/pdf/FopPDFImage.java View 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;

+ 34
- 6
src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java View File

@@ -20,19 +20,23 @@ package org.apache.fop.render.pdf;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.AbstractGraphics2DAdapter;
import org.apache.fop.render.Graphics2DImagePainter;
import org.apache.fop.render.RendererContext;
import org.apache.fop.render.RendererContext.RendererContextWrapper;
import org.apache.fop.svg.PDFGraphics2D;
/**
* Graphics2DAdapter implementation for PDF.
*/
public class PDFGraphics2DAdapter implements Graphics2DAdapter {
public class PDFGraphics2DAdapter extends AbstractGraphics2DAdapter {
private PDFRenderer renderer;
@@ -50,7 +54,6 @@ public class PDFGraphics2DAdapter implements Graphics2DAdapter {
int x, int y, int width, int height) throws IOException {
PDFSVGHandler.PDFInfo pdfInfo = PDFSVGHandler.getPDFInfo(context);
float fwidth = width / 1000f;
float fheight = height / 1000f;
float fx = x / 1000f;
@@ -78,6 +81,9 @@ public class PDFGraphics2DAdapter implements Graphics2DAdapter {
final boolean textAsShapes = false;
if (pdfInfo.pdfContext == null) {
pdfInfo.pdfContext = pdfInfo.pdfPage;
}
PDFGraphics2D graphics = new PDFGraphics2D(textAsShapes,
pdfInfo.fi, pdfInfo.pdfDoc,
pdfInfo.pdfContext, pdfInfo.pdfPage.referencePDF(),
@@ -91,13 +97,35 @@ public class PDFGraphics2DAdapter implements Graphics2DAdapter {
graphics.setPDFState(pdfInfo.pdfState);
graphics.setOutputStream(pdfInfo.outputStream);
Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, imw, imh);
painter.paint(graphics, area);
if (pdfInfo.paintAsBitmap) {
//Fallback solution: Paint to a BufferedImage
int resolution = (int)Math.round(context.getUserAgent().getTargetResolution());
RendererContextWrapper ctx = RendererContext.wrapRendererContext(context);
BufferedImage bi = paintToBufferedImage(painter, ctx, resolution, false, false);
float scale = PDFRenderer.NORMAL_PDF_RESOLUTION
/ context.getUserAgent().getTargetResolution();
graphics.drawImage(bi, new AffineTransform(scale, 0, 0, scale, 0, 0), null);
} else {
Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, imw, imh);
painter.paint(graphics, area);
}
pdfInfo.currentStream.add(graphics.getString());
renderer.restoreGraphicsState();
pdfInfo.pdfState.pop();
}
/**
* @see org.apache.fop.render.AbstractGraphics2DAdapter#setRenderingHintsForBufferedImage(
* java.awt.Graphics2D)
*/
protected void setRenderingHintsForBufferedImage(Graphics2D g2d) {
super.setRenderingHintsForBufferedImage(g2d);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
}

+ 165
- 44
src/java/org/apache/fop/render/pdf/PDFRenderer.java View 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());
}
}


+ 29
- 31
src/java/org/apache/fop/render/pdf/PDFSVGHandler.java View 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;
}
}

+ 19
- 9
src/java/org/apache/fop/svg/PDFGraphics2D.java View 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

+ 8
- 0
status.xml View File

@@ -27,6 +27,14 @@

<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

Loading…
Cancel
Save