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