1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 /* $Id: PDFImageXObject.java 1758773 2016-09-01 13:02:29Z ssteiner $ */
19 
20 package org.apache.fop.pdf;
21 
22 // Java
23 import java.io.ByteArrayOutputStream;
24 import java.io.IOException;
25 import java.io.OutputStream;
26 import java.util.Set;
27 import java.util.UUID;
28 
29 /* modified by JKT to integrate with 0.12.0 */
30 /* modified by Eric SCHAEFFER to integrate with 0.13.0 */
31 
32 /**
33  * PDF XObject
34  *
35  * A derivative of the PDF Object, is a PDF Stream that has not only a
36  * dictionary but a stream of image data.
37  * The dictionary just provides information like the stream length.
38  * This outputs the image dictionary and the image data.
39  * This is used as a reference for inserting the same image in the
40  * document in another place.
41  */
42 public class PDFImageXObject extends PDFXObject {
43 
44     private PDFImage pdfimage;
45 
46     /**
47      * create an XObject with the given number and name and load the
48      * image in the object
49      *
50      * @param xnumber the pdf object X number
51      * @param img the pdf image that contains the image data
52      */
PDFImageXObject(int xnumber, PDFImage img)53     public PDFImageXObject(int xnumber, PDFImage img) {
54         super();
55         put("Name", new PDFName("Im" + xnumber));
56         pdfimage = img;
57     }
58 
59     /**
60      * Output the image as PDF.
61      * This sets up the image dictionary and adds the image data stream.
62      *
63      * @param stream the output stream to write the data
64      * @throws IOException if there is an error writing the data
65      * @return the length of the data written
66      */
output(OutputStream stream)67     public int output(OutputStream stream) throws IOException {
68         if (getDocument().getProfile().isPDFVTActive()) {
69             ByteArrayOutputStream baos = new ByteArrayOutputStream();
70             pdfimage.outputContents(baos);
71             put("GTS_XID", "uuid:" + UUID.nameUUIDFromBytes(baos.toByteArray()));
72         }
73         int length = super.output(stream);
74 
75         // let it gc
76         // this object is retained as a reference to inserting
77         // the same image but the image data is no longer needed
78         pdfimage = null;
79         return length;
80     }
81 
82     /** {@inheritDoc} */
populateStreamDict(Object lengthEntry)83     protected void populateStreamDict(Object lengthEntry) {
84         super.populateStreamDict(lengthEntry);
85         if (pdfimage.isPS()) {
86             populateDictionaryFromPS();
87         } else {
88             populateDictionaryFromImage();
89         }
90     }
91 
populateDictionaryFromPS()92     private void populateDictionaryFromPS() {
93         getDocumentSafely().getProfile().verifyPSXObjectsAllowed();
94         put("Subtype", new PDFName("PS"));
95     }
96 
populateDictionaryFromImage()97     private void populateDictionaryFromImage() {
98         put("Subtype", new PDFName("Image"));
99         put("Width", pdfimage.getWidth());
100         put("Height", pdfimage.getHeight());
101         put("BitsPerComponent", pdfimage.getBitsPerComponent());
102 
103         PDFICCStream pdfICCStream = pdfimage.getICCStream();
104         if (pdfICCStream != null) {
105             put("ColorSpace", new PDFArray(this,
106                     new Object[] {new PDFName("ICCBased"), pdfICCStream}));
107         } else {
108             PDFDeviceColorSpace cs = pdfimage.getColorSpace();
109             put("ColorSpace", new PDFName(cs.getName()));
110         }
111 
112         if (pdfimage.isInverted()) {
113             /* PhotoShop generates CMYK values that's inverse,
114              * this will invert the values - too bad if it's not
115              * a PhotoShop image...
116              */
117             final Float zero = 0.0f;
118             final Float one = 1.0f;
119             PDFArray decode = new PDFArray(this);
120             for (int i = 0, c = pdfimage.getColorSpace().getNumComponents(); i < c; i++) {
121                 decode.add(one);
122                 decode.add(zero);
123             }
124             put("Decode", decode);
125         }
126 
127         if (pdfimage.isTransparent()) {
128             PDFColor transp = pdfimage.getTransparentColor();
129             PDFArray mask = new PDFArray(this);
130             if (pdfimage.getColorSpace().isGrayColorSpace()) {
131                 mask.add(Integer.valueOf(transp.red255()));
132                 mask.add(Integer.valueOf(transp.red255()));
133             } else {
134                 mask.add(Integer.valueOf(transp.red255()));
135                 mask.add(Integer.valueOf(transp.red255()));
136                 mask.add(Integer.valueOf(transp.green255()));
137                 mask.add(Integer.valueOf(transp.green255()));
138                 mask.add(Integer.valueOf(transp.blue255()));
139                 mask.add(Integer.valueOf(transp.blue255()));
140             }
141             put("Mask", mask);
142         }
143         PDFReference ref = pdfimage.getSoftMaskReference();
144         if (ref != null) {
145             put("SMask", ref);
146         }
147         //Important: do this at the end so previous values can be overwritten.
148         pdfimage.populateXObjectDictionary(getDictionary());
149     }
150 
151     /** {@inheritDoc} */
outputRawStreamData(OutputStream out)152     protected void outputRawStreamData(OutputStream out) throws IOException {
153         pdfimage.outputContents(out);
154     }
155 
156     /** {@inheritDoc} */
getSizeHint()157     protected int getSizeHint() throws IOException {
158         return 0;
159     }
160 
161     /** {@inheritDoc} */
prepareImplicitFilters()162     protected void prepareImplicitFilters() {
163         PDFFilter pdfFilter = pdfimage.getPDFFilter();
164         if (pdfFilter != null) {
165             getFilterList().ensureFilterInPlace(pdfFilter);
166         }
167     }
168 
169     /**
170      * {@inheritDoc}
171      * This class uses the PDFImage instance to determine the default filter.
172      */
getDefaultFilterName()173     protected String getDefaultFilterName() {
174         return pdfimage.getFilterHint();
175     }
176 
177     /** {@inheritDoc} */
multipleFiltersAllowed()178     protected boolean multipleFiltersAllowed() {
179         return pdfimage.multipleFiltersAllowed();
180     }
181 
182     @Override
getChildren(Set<PDFObject> children)183     public void getChildren(Set<PDFObject> children) {
184         super.getChildren(children);
185         PDFICCStream pdfICCStream = pdfimage.getICCStream();
186         if (pdfICCStream != null) {
187             children.add(pdfICCStream);
188             pdfICCStream.getChildren(children);
189         }
190     }
191 
192 }
193