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