View | Details | Raw Unified | Return to bug 40676
Collapse All | Expand All

(-)src/java/META-INF/services/org.apache.fop.render.ImageHandler (+2 lines)
Lines 1-6 Link Here
1
org.apache.fop.render.pdf.PDFImageHandlerGraphics2D
1
org.apache.fop.render.pdf.PDFImageHandlerGraphics2D
2
org.apache.fop.render.pdf.PDFImageHandlerRenderedImage
2
org.apache.fop.render.pdf.PDFImageHandlerRenderedImage
3
org.apache.fop.render.pdf.PDFImageHandlerRawJPEG
3
org.apache.fop.render.pdf.PDFImageHandlerRawJPEG
4
org.apache.fop.render.pdf.PDFImageHandlerRawPNG
4
org.apache.fop.render.pdf.PDFImageHandlerRawCCITTFax
5
org.apache.fop.render.pdf.PDFImageHandlerRawCCITTFax
5
org.apache.fop.render.pdf.PDFImageHandlerSVG
6
org.apache.fop.render.pdf.PDFImageHandlerSVG
6
org.apache.fop.render.java2d.Java2DImageHandlerRenderedImage
7
org.apache.fop.render.java2d.Java2DImageHandlerRenderedImage
Lines 11-16 Link Here
11
org.apache.fop.render.ps.PSImageHandlerEPS
12
org.apache.fop.render.ps.PSImageHandlerEPS
12
org.apache.fop.render.ps.PSImageHandlerRawCCITTFax
13
org.apache.fop.render.ps.PSImageHandlerRawCCITTFax
13
org.apache.fop.render.ps.PSImageHandlerRawJPEG
14
org.apache.fop.render.ps.PSImageHandlerRawJPEG
15
org.apache.fop.render.ps.PSImageHandlerRawPNG
14
org.apache.fop.render.ps.PSImageHandlerGraphics2D
16
org.apache.fop.render.ps.PSImageHandlerGraphics2D
15
org.apache.fop.render.ps.PSImageHandlerSVG
17
org.apache.fop.render.ps.PSImageHandlerSVG
16
org.apache.fop.render.afp.AFPImageHandlerRenderedImage
18
org.apache.fop.render.afp.AFPImageHandlerRenderedImage
(-)src/java/org/apache/fop/render/pdf/PDFImageHandlerRawPNG.java (+65 lines)
Line 0 Link Here
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$ */
19
20
// Original author: Matthias Reichenbacher
21
22
package org.apache.fop.render.pdf;
23
24
import org.apache.xmlgraphics.image.loader.Image;
25
import org.apache.xmlgraphics.image.loader.ImageFlavor;
26
import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
27
28
import org.apache.fop.pdf.PDFImage;
29
import org.apache.fop.render.RenderingContext;
30
31
/**
32
 * Image handler implementation which handles CCITT encoded images (CCITT fax group 3/4)
33
 * for PDF output.
34
 */
35
public class PDFImageHandlerRawPNG extends AbstractPDFImageHandler {
36
37
    private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {ImageFlavor.RAW_PNG};
38
39
    @Override
40
    PDFImage createPDFImage(Image image, String xobjectKey) {
41
        return new ImageRawPNGAdapter((ImageRawPNG) image, xobjectKey);
42
    }
43
44
    /** {@inheritDoc} */
45
    public int getPriority() {
46
        return 100;
47
    }
48
49
    /** {@inheritDoc} */
50
    public Class<ImageRawPNG> getSupportedImageClass() {
51
        return ImageRawPNG.class;
52
    }
53
54
    /** {@inheritDoc} */
55
    public ImageFlavor[] getSupportedImageFlavors() {
56
        return FLAVORS;
57
    }
58
59
    /** {@inheritDoc} */
60
    public boolean isCompatible(RenderingContext targetContext, Image image) {
61
        return (image == null || image instanceof ImageRawPNG)
62
                && targetContext instanceof PDFRenderingContext;
63
    }
64
65
}
(-)src/java/org/apache/fop/render/pdf/ImageRawPNGAdapter.java (+258 lines)
Line 0 Link Here
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$ */
19
20
// Original author: Matthias Reichenbacher
21
22
package org.apache.fop.render.pdf;
23
24
import java.awt.image.ColorModel;
25
import java.awt.image.IndexColorModel;
26
import java.io.DataInputStream;
27
import java.io.IOException;
28
import java.io.InputStream;
29
import java.io.OutputStream;
30
import java.util.zip.Deflater;
31
import java.util.zip.DeflaterOutputStream;
32
import java.util.zip.Inflater;
33
import java.util.zip.InflaterInputStream;
34
35
import org.apache.commons.io.IOUtils;
36
import org.apache.commons.io.output.ByteArrayOutputStream;
37
import org.apache.commons.logging.Log;
38
import org.apache.commons.logging.LogFactory;
39
40
import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
41
import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;
42
43
import org.apache.fop.pdf.BitmapImage;
44
import org.apache.fop.pdf.FlateFilter;
45
import org.apache.fop.pdf.PDFColor;
46
import org.apache.fop.pdf.PDFDeviceColorSpace;
47
import org.apache.fop.pdf.PDFDictionary;
48
import org.apache.fop.pdf.PDFDocument;
49
import org.apache.fop.pdf.PDFFilter;
50
import org.apache.fop.pdf.PDFFilterException;
51
import org.apache.fop.pdf.PDFFilterList;
52
import org.apache.fop.pdf.PDFICCStream;
53
import org.apache.fop.pdf.PDFReference;
54
55
public class ImageRawPNGAdapter extends AbstractImageAdapter {
56
57
    /** logging instance */
58
    private static Log log = LogFactory.getLog(ImageRawPNGAdapter.class);
59
60
    private PDFICCStream pdfICCStream;
61
    private PDFFilter pdfFilter;
62
    private String maskRef;
63
    private PDFReference softMask;
64
    private int numberOfInterleavedComponents;
65
66
    /**
67
     * Creates a new PDFImage from an Image instance.
68
     * @param image the image
69
     * @param key XObject key
70
     */
71
    public ImageRawPNGAdapter(ImageRawPNG image, String key) {
72
        super(image, key);
73
    }
74
75
    /** {@inheritDoc} */
76
    public void setup(PDFDocument doc) {
77
        super.setup(doc);
78
        ColorModel cm = ((ImageRawPNG) this.image).getColorModel();
79
        if (cm instanceof IndexColorModel) {
80
            numberOfInterleavedComponents = 1;
81
        } else {
82
            // this can be 1 (gray), 2 (gray + alpha), 3 (rgb) or 4 (rgb + alpha)
83
            // numberOfInterleavedComponents = (cm.hasAlpha() ? 1 : 0) + cm.getNumColorComponents();
84
            numberOfInterleavedComponents = cm.getNumComponents();
85
        }
86
87
        // set up image compression for non-alpha channel
88
        FlateFilter flate;
89
        try {
90
            flate = new FlateFilter();
91
            flate.setApplied(true);
92
            flate.setPredictor(FlateFilter.PREDICTION_PNG_OPT);
93
            if (numberOfInterleavedComponents < 3) {
94
                // means palette (1) or gray (1) or gray + alpha (2)
95
                flate.setColors(1);
96
            } else {
97
                // means rgb (3) or rgb + alpha (4)
98
                flate.setColors(3);
99
            }
100
            flate.setColumns(image.getSize().getWidthPx());
101
            flate.setBitsPerComponent(this.getBitsPerComponent());
102
        } catch (PDFFilterException e) {
103
            throw new RuntimeException("FlateFilter configuration error", e);
104
        }
105
        this.pdfFilter = flate;
106
107
        // Handle transparency channel if applicable; note that for palette images the transparency is
108
        // not TRANSLUCENT
109
        if (cm.hasAlpha() && cm.getTransparency() == ColorModel.TRANSLUCENT) {
110
            doc.getProfile().verifyTransparencyAllowed(image.getInfo().getOriginalURI());
111
            // TODO: Implement code to combine image with background color if transparency is not allowed
112
            // here we need to inflate the PNG pixel data, which includes alpha, separate the alpha channel
113
            // and then deflate it back again
114
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
115
            DeflaterOutputStream dos = new DeflaterOutputStream(baos, new Deflater());
116
            InputStream in = ((ImageRawStream) image).createInputStream();
117
            try {
118
                InflaterInputStream infStream = new InflaterInputStream(in, new Inflater());
119
                DataInputStream dataStream = new DataInputStream(infStream);
120
                // offset is the byte offset of the alpha component
121
                int offset = numberOfInterleavedComponents - 1; // 1 for GA, 3 for RGBA
122
                int numColumns = image.getSize().getWidthPx();
123
                int bytesPerRow = numberOfInterleavedComponents * numColumns;
124
                int filter;
125
                // read line by line; the first byte holds the filter
126
                while ((filter = dataStream.read()) != -1) {
127
                    byte[] bytes = new byte[bytesPerRow];
128
                    dataStream.readFully(bytes, 0, bytesPerRow);
129
                    dos.write((byte) filter);
130
                    for (int j = 0; j < numColumns; j++) {
131
                        dos.write(bytes, offset, 1);
132
                        offset += numberOfInterleavedComponents;
133
                    }
134
                    offset = numberOfInterleavedComponents - 1;
135
                }
136
                dos.close();
137
            } catch (IOException e) {
138
                throw new RuntimeException("Error processing transparency channel:", e);
139
            } finally {
140
                IOUtils.closeQuietly(in);
141
            }
142
            // set up alpha channel compression
143
            FlateFilter transFlate;
144
            try {
145
                transFlate = new FlateFilter();
146
                transFlate.setApplied(true);
147
                transFlate.setPredictor(FlateFilter.PREDICTION_PNG_OPT);
148
                transFlate.setColors(1);
149
                transFlate.setColumns(image.getSize().getWidthPx());
150
                transFlate.setBitsPerComponent(this.getBitsPerComponent());
151
            } catch (PDFFilterException e) {
152
                throw new RuntimeException("FlateFilter configuration error", e);
153
            }
154
            BitmapImage alphaMask = new BitmapImage("Mask:" + this.getKey(), image.getSize().getWidthPx(),
155
                    image.getSize().getHeightPx(), baos.toByteArray(), null);
156
            alphaMask.setPDFFilter(transFlate);
157
            alphaMask.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
158
            softMask = doc.addImage(null, alphaMask).makeReference();
159
        }
160
    }
161
162
    /** {@inheritDoc} */
163
    public PDFDeviceColorSpace getColorSpace() {
164
        // DeviceGray, DeviceRGB, or DeviceCMYK
165
        return toPDFColorSpace(image.getColorSpace());
166
    }
167
168
    /** {@inheritDoc} */
169
    public int getBitsPerComponent() {
170
        return ((ImageRawPNG) this.image).getBitDepth();
171
    }
172
173
    /** {@inheritDoc} */
174
    public boolean isTransparent() {
175
        return ((ImageRawPNG) this.image).isTransparent();
176
    }
177
178
    /** {@inheritDoc} */
179
    public PDFColor getTransparentColor() {
180
        return new PDFColor(((ImageRawPNG) this.image).getTransparentColor());
181
    }
182
183
    /** {@inheritDoc} */
184
    public String getMask() {
185
        return maskRef;
186
    }
187
188
    /** {@inheritDoc} */
189
    public String getSoftMask() {
190
        return softMask.toString();
191
    }
192
193
    /** {@inheritDoc} */
194
    public PDFReference getSoftMaskReference() {
195
        return softMask;
196
    }
197
198
    /** {@inheritDoc} */
199
    public PDFFilter getPDFFilter() {
200
        return pdfFilter;
201
    }
202
203
    /** {@inheritDoc} */
204
    public void outputContents(OutputStream out) throws IOException {
205
        InputStream in = ((ImageRawStream) image).createInputStream();
206
207
        try {
208
            if (numberOfInterleavedComponents == 1 || numberOfInterleavedComponents == 3) {
209
                // means we have Gray, RGB, or Palette
210
                IOUtils.copy(in, out);
211
            } else {
212
                // means we have Gray + alpha or RGB + alpha
213
                // TODO: since we have alpha here do this when the alpha channel is extracted
214
                int numBytes = numberOfInterleavedComponents - 1; // 1 for Gray, 3 for RGB
215
                int numColumns = image.getSize().getWidthPx();
216
                InflaterInputStream infStream = new InflaterInputStream(in, new Inflater());
217
                DataInputStream dataStream = new DataInputStream(infStream);
218
                int offset = 0;
219
                int bytesPerRow = numberOfInterleavedComponents * numColumns;
220
                int filter;
221
                // here we need to inflate the PNG pixel data, which includes alpha, separate the alpha
222
                // channel and then deflate the RGB channels back again
223
                DeflaterOutputStream dos = new DeflaterOutputStream(out, new Deflater());
224
                while ((filter = dataStream.read()) != -1) {
225
                    byte[] bytes = new byte[bytesPerRow];
226
                    dataStream.readFully(bytes, 0, bytesPerRow);
227
                    dos.write((byte) filter);
228
                    for (int j = 0; j < numColumns; j++) {
229
                        dos.write(bytes, offset, numBytes);
230
                        offset += numberOfInterleavedComponents;
231
                    }
232
                    offset = 0;
233
                }
234
                dos.close();
235
            }
236
        } finally {
237
            IOUtils.closeQuietly(in);
238
        }
239
    }
240
241
    /** {@inheritDoc} */
242
    public PDFICCStream getICCStream() {
243
        return pdfICCStream;
244
    }
245
246
    /** {@inheritDoc} */
247
    public String getFilterHint() {
248
        return PDFFilterList.PRECOMPRESSED_FILTER;
249
    }
250
251
    public void populateXObjectDictionary(PDFDictionary dict) {
252
        ColorModel cm = ((ImageRawPNG) image).getColorModel();
253
        if (cm instanceof IndexColorModel) {
254
            IndexColorModel icm = (IndexColorModel) cm;
255
            super.populateXObjectDictionaryForIndexColorModel(dict, icm);
256
        }
257
    }
258
}
(-)src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java (-69 / +2 lines)
Lines 36-49 Link Here
36
import org.apache.xmlgraphics.ps.ImageEncodingHelper;
36
import org.apache.xmlgraphics.ps.ImageEncodingHelper;
37
37
38
import org.apache.fop.pdf.AlphaRasterImage;
38
import org.apache.fop.pdf.AlphaRasterImage;
39
import org.apache.fop.pdf.PDFArray;
40
import org.apache.fop.pdf.PDFColor;
39
import org.apache.fop.pdf.PDFColor;
41
import org.apache.fop.pdf.PDFDeviceColorSpace;
40
import org.apache.fop.pdf.PDFDeviceColorSpace;
42
import org.apache.fop.pdf.PDFDictionary;
41
import org.apache.fop.pdf.PDFDictionary;
43
import org.apache.fop.pdf.PDFDocument;
42
import org.apache.fop.pdf.PDFDocument;
44
import org.apache.fop.pdf.PDFFilter;
43
import org.apache.fop.pdf.PDFFilter;
45
import org.apache.fop.pdf.PDFFilterList;
44
import org.apache.fop.pdf.PDFFilterList;
46
import org.apache.fop.pdf.PDFName;
47
import org.apache.fop.pdf.PDFReference;
45
import org.apache.fop.pdf.PDFReference;
48
46
49
/**
47
/**
Lines 162-191 Link Here
162
        return (getImage().getTransparentColor() != null);
160
        return (getImage().getTransparentColor() != null);
163
    }
161
    }
164
162
165
    private static Integer getIndexOfFirstTransparentColorInPalette(RenderedImage image) {
166
        ColorModel cm = image.getColorModel();
167
        if (cm instanceof IndexColorModel) {
168
            IndexColorModel icm = (IndexColorModel)cm;
169
            //Identify the transparent color in the palette
170
            byte[] alphas = new byte[icm.getMapSize()];
171
            byte[] reds = new byte[icm.getMapSize()];
172
            byte[] greens = new byte[icm.getMapSize()];
173
            byte[] blues = new byte[icm.getMapSize()];
174
            icm.getAlphas(alphas);
175
            icm.getReds(reds);
176
            icm.getGreens(greens);
177
            icm.getBlues(blues);
178
            for (int i = 0;
179
                    i < ((IndexColorModel) cm).getMapSize();
180
                    i++) {
181
                if ((alphas[i] & 0xFF) == 0) {
182
                    return Integer.valueOf(i);
183
                }
184
            }
185
        }
186
        return null;
187
    }
188
189
    /** {@inheritDoc} */
163
    /** {@inheritDoc} */
190
    @Override
164
    @Override
191
    public PDFColor getTransparentColor() {
165
    public PDFColor getTransparentColor() {
Lines 230-283 Link Here
230
        }
204
        }
231
    }
205
    }
232
206
233
    private static final int MAX_HIVAL = 255;
234
235
    /** {@inheritDoc} */
207
    /** {@inheritDoc} */
236
    @Override
208
    @Override
237
    public void populateXObjectDictionary(PDFDictionary dict) {
209
    public void populateXObjectDictionary(PDFDictionary dict) {
238
        ColorModel cm = getEffectiveColorModel();
210
        ColorModel cm = getEffectiveColorModel();
239
        if (cm instanceof IndexColorModel) {
211
        if (cm instanceof IndexColorModel) {
240
            IndexColorModel icm = (IndexColorModel)cm;
212
            IndexColorModel icm = (IndexColorModel) cm;
241
            PDFArray indexed = new PDFArray(dict);
213
            super.populateXObjectDictionaryForIndexColorModel(dict, icm);
242
            indexed.add(new PDFName("Indexed"));
243
244
            if (icm.getColorSpace().getType() != ColorSpace.TYPE_RGB) {
245
                log.warn("Indexed color space is not using RGB as base color space."
246
                        + " The image may not be handled correctly."
247
                        + " Base color space: " + icm.getColorSpace()
248
                        + " Image: " + image.getInfo());
249
            }
250
            indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName()));
251
            int c = icm.getMapSize();
252
            int hival = c - 1;
253
            if (hival > MAX_HIVAL) {
254
                throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL);
255
            }
256
            indexed.add(Integer.valueOf(hival));
257
            int[] palette = new int[c];
258
            icm.getRGBs(palette);
259
            ByteArrayOutputStream baout = new ByteArrayOutputStream();
260
            for (int i = 0; i < c; i++) {
261
                //TODO Probably doesn't work for non RGB based color spaces
262
                //See log warning above
263
                int entry = palette[i];
264
                baout.write((entry & 0xFF0000) >> 16);
265
                baout.write((entry & 0xFF00) >> 8);
266
                baout.write(entry & 0xFF);
267
            }
268
            indexed.add(baout.toByteArray());
269
            IOUtils.closeQuietly(baout);
270
271
            dict.put("ColorSpace", indexed);
272
            dict.put("BitsPerComponent", icm.getPixelSize());
273
274
            Integer index = getIndexOfFirstTransparentColorInPalette(getImage().getRenderedImage());
275
            if (index != null) {
276
                PDFArray mask = new PDFArray(dict);
277
                mask.add(index);
278
                mask.add(index);
279
                dict.put("Mask", mask);
280
            }
281
        }
214
        }
282
    }
215
    }
283
216
(-)src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java (-1 / +68 lines)
Lines 20-32 Link Here
20
package org.apache.fop.render.pdf;
20
package org.apache.fop.render.pdf;
21
import java.awt.color.ColorSpace;
21
import java.awt.color.ColorSpace;
22
import java.awt.color.ICC_Profile;
22
import java.awt.color.ICC_Profile;
23
import java.awt.image.IndexColorModel;
23
24
25
import org.apache.commons.io.output.ByteArrayOutputStream;
24
import org.apache.commons.logging.Log;
26
import org.apache.commons.logging.Log;
25
import org.apache.commons.logging.LogFactory;
27
import org.apache.commons.logging.LogFactory;
26
28
27
import org.apache.xmlgraphics.image.loader.Image;
29
import org.apache.xmlgraphics.image.loader.Image;
28
import org.apache.xmlgraphics.java2d.color.profile.ColorProfileUtil;
30
import org.apache.xmlgraphics.java2d.color.profile.ColorProfileUtil;
29
31
32
import org.apache.fop.pdf.PDFArray;
30
import org.apache.fop.pdf.PDFColor;
33
import org.apache.fop.pdf.PDFColor;
31
import org.apache.fop.pdf.PDFConformanceException;
34
import org.apache.fop.pdf.PDFConformanceException;
32
import org.apache.fop.pdf.PDFDeviceColorSpace;
35
import org.apache.fop.pdf.PDFDeviceColorSpace;
Lines 50-56 Link Here
50
    /** the image */
53
    /** the image */
51
    protected Image image;
54
    protected Image image;
52
55
53
    private PDFICCStream pdfICCStream = null;
56
    private PDFICCStream pdfICCStream;
57
58
    private static final int MAX_HIVAL = 255;
54
59
55
    /**
60
    /**
56
     * Creates a new PDFImage from an Image instance.
61
     * Creates a new PDFImage from an Image instance.
Lines 203-208 Link Here
203
    }
208
    }
204
209
205
    /**
210
    /**
211
     * This is to be used by populateXObjectDictionary() when the image is palette based.
212
     * @param dict the dictionary to fill in
213
     * @param icm the image color model
214
     */
215
    protected void populateXObjectDictionaryForIndexColorModel(PDFDictionary dict, IndexColorModel icm) {
216
        PDFArray indexed = new PDFArray(dict);
217
        indexed.add(new PDFName("Indexed"));
218
        if (icm.getColorSpace().getType() != ColorSpace.TYPE_RGB) {
219
            log.warn("Indexed color space is not using RGB as base color space."
220
                    + " The image may not be handled correctly." + " Base color space: "
221
                    + icm.getColorSpace() + " Image: " + image.getInfo());
222
        }
223
        indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName()));
224
        int c = icm.getMapSize();
225
        int hival = c - 1;
226
        if (hival > MAX_HIVAL) {
227
            throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL);
228
        }
229
        indexed.add(new Integer(hival));
230
        int[] palette = new int[c];
231
        icm.getRGBs(palette);
232
        ByteArrayOutputStream baout = new ByteArrayOutputStream();
233
        for (int i = 0; i < c; i++) {
234
            // TODO Probably doesn't work for non RGB based color spaces
235
            // See log warning above
236
            int entry = palette[i];
237
            baout.write((entry & 0xFF0000) >> 16);
238
            baout.write((entry & 0xFF00) >> 8);
239
            baout.write(entry & 0xFF);
240
        }
241
        indexed.add(baout.toByteArray());
242
243
        dict.put("ColorSpace", indexed);
244
        dict.put("BitsPerComponent", icm.getPixelSize());
245
246
        Integer index = getIndexOfFirstTransparentColorInPalette(icm);
247
        if (index != null) {
248
            PDFArray mask = new PDFArray(dict);
249
            mask.add(index);
250
            mask.add(index);
251
            dict.put("Mask", mask);
252
        }
253
    }
254
255
    private static Integer getIndexOfFirstTransparentColorInPalette(IndexColorModel icm) {
256
        byte[] alphas = new byte[icm.getMapSize()];
257
        byte[] reds = new byte[icm.getMapSize()];
258
        byte[] greens = new byte[icm.getMapSize()];
259
        byte[] blues = new byte[icm.getMapSize()];
260
        icm.getAlphas(alphas);
261
        icm.getReds(reds);
262
        icm.getGreens(greens);
263
        icm.getBlues(blues);
264
        for (int i = 0; i < icm.getMapSize(); i++) {
265
            if ((alphas[i] & 0xFF) == 0) {
266
                return new Integer(i);
267
            }
268
        }
269
        return null;
270
    }
271
272
    /**
206
     * Converts a ColorSpace object to a PDFColorSpace object.
273
     * Converts a ColorSpace object to a PDFColorSpace object.
207
     * @param cs ColorSpace instance
274
     * @param cs ColorSpace instance
208
     * @return PDFColorSpace new converted object
275
     * @return PDFColorSpace new converted object
(-)src/java/org/apache/fop/render/ps/ImageEncoderPNG.java (+113 lines)
Line 0 Link Here
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$ */
19
20
package org.apache.fop.render.ps;
21
22
import java.awt.image.ColorModel;
23
import java.awt.image.IndexColorModel;
24
import java.io.ByteArrayInputStream;
25
import java.io.ByteArrayOutputStream;
26
import java.io.DataInputStream;
27
import java.io.IOException;
28
import java.io.InputStream;
29
import java.io.OutputStream;
30
import java.util.zip.Deflater;
31
import java.util.zip.DeflaterOutputStream;
32
import java.util.zip.Inflater;
33
import java.util.zip.InflaterInputStream;
34
35
import org.apache.commons.io.IOUtils;
36
37
import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
38
import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;
39
import org.apache.xmlgraphics.ps.ImageEncoder;
40
41
/**
42
 * ImageEncoder implementation for PNG images.
43
 */
44
public class ImageEncoderPNG implements ImageEncoder {
45
    private final ImageRawPNG image;
46
    private int numberOfInterleavedComponents;
47
48
    /**
49
     * Main constructor
50
     * @param image the PNG image
51
     */
52
    public ImageEncoderPNG(ImageRawPNG image) {
53
        this.image = image;
54
        ColorModel cm = ((ImageRawPNG) this.image).getColorModel();
55
        if (cm instanceof IndexColorModel) {
56
            numberOfInterleavedComponents = 1;
57
        } else {
58
            // this can be 1 (gray), 2 (gray + alpha), 3 (rgb) or 4 (rgb + alpha)
59
            // numberOfInterleavedComponents = (cm.hasAlpha() ? 1 : 0) + cm.getNumColorComponents();
60
            numberOfInterleavedComponents = cm.getNumComponents();
61
        }
62
    }
63
64
    /** {@inheritDoc} */
65
    public void writeTo(OutputStream out) throws IOException {
66
        // TODO: refactor this code with equivalent PDF code
67
        InputStream in = ((ImageRawStream) image).createInputStream();
68
        try {
69
            if (numberOfInterleavedComponents == 1 || numberOfInterleavedComponents == 3) {
70
                // means we have Gray, RGB, or Palette
71
                IOUtils.copy(in, out);
72
            } else {
73
                // means we have Gray + alpha or RGB + alpha
74
                int numBytes = numberOfInterleavedComponents - 1; // 1 for Gray, 3 for RGB
75
                int numColumns = image.getSize().getWidthPx();
76
                InflaterInputStream infStream = new InflaterInputStream(in, new Inflater());
77
                DataInputStream dataStream = new DataInputStream(infStream);
78
                int offset = 0;
79
                int bytesPerRow = numberOfInterleavedComponents * numColumns;
80
                int filter;
81
                // here we need to inflate the PNG pixel data, which includes alpha, separate the alpha
82
                // channel and then deflate the RGB channels back again
83
                // TODO: not using the baos below and using the original out instead (as happens in PDF)
84
                // would be preferable but that does not work with the rest of the postscript code; this
85
                // needs to be revisited
86
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
87
                DeflaterOutputStream dos = new DeflaterOutputStream(/* out */baos, new Deflater());
88
                while ((filter = dataStream.read()) != -1) {
89
                    byte[] bytes = new byte[bytesPerRow];
90
                    dataStream.readFully(bytes, 0, bytesPerRow);
91
                    dos.write((byte) filter);
92
                    for (int j = 0; j < numColumns; j++) {
93
                        dos.write(bytes, offset, numBytes);
94
                        offset += numberOfInterleavedComponents;
95
                    }
96
                    offset = 0;
97
                }
98
                dos.close();
99
                IOUtils.copy(new ByteArrayInputStream(baos.toByteArray()), out);
100
            }
101
        } finally {
102
            IOUtils.closeQuietly(in);
103
        }
104
    }
105
106
    /** {@inheritDoc} */
107
    public String getImplicitFilter() {
108
        String filter = "<< /Predictor 15 /Columns " + image.getSize().getWidthPx();
109
        filter += " /Colors " + (numberOfInterleavedComponents > 2 ? 3 : 1);
110
        filter += " /BitsPerComponent " + image.getBitDepth() + " >> /FlateDecode";
111
        return filter;
112
    }
113
}
(-)src/java/org/apache/fop/render/ps/PSImageHandlerRenderedImage.java (-7 / +19 lines)
Lines 19-25 Link Here
19
19
20
package org.apache.fop.render.ps;
20
package org.apache.fop.render.ps;
21
21
22
import java.awt.Dimension;
22
import java.awt.Rectangle;
23
import java.awt.Rectangle;
24
import java.awt.geom.Rectangle2D;
25
import java.awt.image.ColorModel;
23
import java.awt.image.RenderedImage;
26
import java.awt.image.RenderedImage;
24
import java.io.IOException;
27
import java.io.IOException;
25
28
Lines 28-33 Link Here
28
import org.apache.xmlgraphics.image.loader.ImageInfo;
31
import org.apache.xmlgraphics.image.loader.ImageInfo;
29
import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
32
import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
30
import org.apache.xmlgraphics.ps.FormGenerator;
33
import org.apache.xmlgraphics.ps.FormGenerator;
34
import org.apache.xmlgraphics.ps.ImageEncoder;
35
import org.apache.xmlgraphics.ps.ImageEncodingHelper;
31
import org.apache.xmlgraphics.ps.ImageFormGenerator;
36
import org.apache.xmlgraphics.ps.ImageFormGenerator;
32
import org.apache.xmlgraphics.ps.PSGenerator;
37
import org.apache.xmlgraphics.ps.PSGenerator;
33
import org.apache.xmlgraphics.ps.PSImageUtils;
38
import org.apache.xmlgraphics.ps.PSImageUtils;
Lines 47-63 Link Here
47
    /** {@inheritDoc} */
52
    /** {@inheritDoc} */
48
    public void handleImage(RenderingContext context, Image image, Rectangle pos)
53
    public void handleImage(RenderingContext context, Image image, Rectangle pos)
49
                throws IOException {
54
                throws IOException {
50
        PSRenderingContext psContext = (PSRenderingContext)context;
55
        PSRenderingContext psContext = (PSRenderingContext) context;
51
        PSGenerator gen = psContext.getGenerator();
56
        PSGenerator gen = psContext.getGenerator();
52
        ImageRendered imageRend = (ImageRendered)image;
57
        ImageRendered imageRend = (ImageRendered) image;
53
58
54
        float x = (float)pos.getX() / 1000f;
59
        float x = (float) pos.getX() / 1000f;
55
        float y = (float)pos.getY() / 1000f;
60
        float y = (float) pos.getY() / 1000f;
56
        float w = (float)pos.getWidth() / 1000f;
61
        float w = (float) pos.getWidth() / 1000f;
57
        float h = (float)pos.getHeight() / 1000f;
62
        float h = (float) pos.getHeight() / 1000f;
63
        Rectangle2D targetRect = new Rectangle2D.Double(x, y, w, h);
58
64
59
        RenderedImage ri = imageRend.getRenderedImage();
65
        RenderedImage ri = imageRend.getRenderedImage();
60
        PSImageUtils.renderBitmapImage(ri, x, y, w, h, gen);
66
        ImageEncoder encoder = ImageEncodingHelper.createRenderedImageEncoder(ri);
67
        Dimension imgDim = new Dimension(ri.getWidth(), ri.getHeight());
68
        String imgDescription = ri.getClass().getName();
69
        ImageEncodingHelper helper = new ImageEncodingHelper(ri);
70
        ColorModel cm = helper.getEncodedColorModel();
71
72
        PSImageUtils.writeImage(encoder, imgDim, imgDescription, targetRect, cm, gen);
61
    }
73
    }
62
74
63
    /** {@inheritDoc} */
75
    /** {@inheritDoc} */
(-)src/java/org/apache/fop/render/ps/PSImageHandlerRawPNG.java (+111 lines)
Line 0 Link Here
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$ */
19
20
package org.apache.fop.render.ps;
21
22
import java.awt.Dimension;
23
import java.awt.Rectangle;
24
import java.awt.geom.Rectangle2D;
25
import java.awt.image.ColorModel;
26
import java.io.IOException;
27
28
import org.apache.xmlgraphics.image.loader.Image;
29
import org.apache.xmlgraphics.image.loader.ImageFlavor;
30
import org.apache.xmlgraphics.image.loader.ImageInfo;
31
import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
32
import org.apache.xmlgraphics.ps.FormGenerator;
33
import org.apache.xmlgraphics.ps.ImageEncoder;
34
import org.apache.xmlgraphics.ps.ImageFormGenerator;
35
import org.apache.xmlgraphics.ps.PSGenerator;
36
import org.apache.xmlgraphics.ps.PSImageUtils;
37
38
import org.apache.fop.render.RenderingContext;
39
40
/**
41
 * Image handler implementation which handles raw (not decoded) PNG images for PostScript output.
42
 */
43
public class PSImageHandlerRawPNG implements PSImageHandler {
44
45
    private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {ImageFlavor.RAW_PNG};
46
47
    /** {@inheritDoc} */
48
    public void handleImage(RenderingContext context, Image image, Rectangle pos) throws IOException {
49
        PSRenderingContext psContext = (PSRenderingContext) context;
50
        PSGenerator gen = psContext.getGenerator();
51
        ImageRawPNG png = (ImageRawPNG) image;
52
53
        float x = (float) pos.getX() / 1000f;
54
        float y = (float) pos.getY() / 1000f;
55
        float w = (float) pos.getWidth() / 1000f;
56
        float h = (float) pos.getHeight() / 1000f;
57
        Rectangle2D targetRect = new Rectangle2D.Float(x, y, w, h);
58
59
        ImageEncoder encoder = new ImageEncoderPNG(png);
60
        ImageInfo info = image.getInfo();
61
        Dimension imgDim = info.getSize().getDimensionPx();
62
        String imgDescription = image.getClass().getName();
63
        ColorModel cm = png.getColorModel();
64
65
        PSImageUtils.writeImage(encoder, imgDim, imgDescription, targetRect, cm, gen);
66
    }
67
68
    /** {@inheritDoc} */
69
    public void generateForm(RenderingContext context, Image image, PSImageFormResource form)
70
            throws IOException {
71
        PSRenderingContext psContext = (PSRenderingContext) context;
72
        PSGenerator gen = psContext.getGenerator();
73
        ImageRawPNG png = (ImageRawPNG) image;
74
        ImageInfo info = image.getInfo();
75
        String imageDescription = info.getMimeType() + " " + info.getOriginalURI();
76
77
        ImageEncoder encoder = new ImageEncoderPNG(png);
78
        FormGenerator formGen = new ImageFormGenerator(form.getName(), imageDescription, info.getSize()
79
                .getDimensionPt(), info.getSize().getDimensionPx(), encoder, png.getColorSpace(),
80
                false);
81
        formGen.generate(gen);
82
    }
83
84
    /** {@inheritDoc} */
85
    public int getPriority() {
86
        return 200;
87
    }
88
89
    /** {@inheritDoc} */
90
    public Class<ImageRawPNG> getSupportedImageClass() {
91
        return ImageRawPNG.class;
92
    }
93
94
    /** {@inheritDoc} */
95
    public ImageFlavor[] getSupportedImageFlavors() {
96
        return FLAVORS;
97
    }
98
99
    /** {@inheritDoc} */
100
    public boolean isCompatible(RenderingContext targetContext, Image image) {
101
        if (targetContext instanceof PSRenderingContext) {
102
            PSRenderingContext psContext = (PSRenderingContext) targetContext;
103
            // The filters required for this implementation need PS level 2 or higher
104
            if (psContext.getGenerator().getPSLevel() >= 2) {
105
                return (image == null || image instanceof ImageRawPNG);
106
            }
107
        }
108
        return false;
109
    }
110
111
}
(-)src/java/org/apache/fop/pdf/BitmapImage.java (-1 / +5 lines)
Lines 37-42 Link Here
37
    private PDFColor transparent = null;
37
    private PDFColor transparent = null;
38
    private String key;
38
    private String key;
39
    private PDFDocument pdfDoc;
39
    private PDFDocument pdfDoc;
40
    private PDFFilter pdfFilter;
40
41
41
    /**
42
    /**
42
     * Create a bitmap image.
43
     * Create a bitmap image.
Lines 208-216 Link Here
208
     * {@inheritDoc}
209
     * {@inheritDoc}
209
     */
210
     */
210
    public PDFFilter getPDFFilter() {
211
    public PDFFilter getPDFFilter() {
211
        return null;
212
        return pdfFilter;
212
    }
213
    }
213
214
215
    public void setPDFFilter(PDFFilter pdfFilter) {
216
        this.pdfFilter = pdfFilter;
217
    }
214
}
218
}
215
219
216
220
(-)test/java/org/apache/fop/render/pdf/ImageRawPNGAdapterTestCase.java (+142 lines)
Line 0 Link Here
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$ */
19
20
package org.apache.fop.render.pdf;
21
22
import java.awt.image.ComponentColorModel;
23
import java.awt.image.IndexColorModel;
24
import java.io.ByteArrayInputStream;
25
import java.io.ByteArrayOutputStream;
26
import java.io.IOException;
27
import java.util.zip.Deflater;
28
import java.util.zip.DeflaterOutputStream;
29
30
import org.junit.Test;
31
32
import org.apache.xmlgraphics.image.loader.ImageSize;
33
import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
34
35
import org.apache.fop.pdf.FlateFilter;
36
import org.apache.fop.pdf.PDFAMode;
37
import org.apache.fop.pdf.PDFDocument;
38
import org.apache.fop.pdf.PDFProfile;
39
import org.apache.fop.render.RawPNGTestUtil;
40
41
import static org.junit.Assert.assertArrayEquals;
42
import static org.junit.Assert.assertEquals;
43
44
import static org.mockito.Mockito.mock;
45
import static org.mockito.Mockito.when;
46
47
public class ImageRawPNGAdapterTestCase {
48
49
    @Test
50
    public void testSetupWithIndexColorModel() {
51
        IndexColorModel cm = mock(IndexColorModel.class);
52
        ImageRawPNG irpng = mock(ImageRawPNG.class);
53
        PDFDocument doc = mock(PDFDocument.class);
54
        PDFProfile profile = mock(PDFProfile.class);
55
        ImageRawPNGAdapter irpnga = new ImageRawPNGAdapter(irpng, "mock");
56
        ImageSize is = RawPNGTestUtil.getImageSize();
57
58
        when(irpng.getColorModel()).thenReturn(cm);
59
        // when(cm.hasAlpha()).thenReturn(false);
60
        when(doc.getProfile()).thenReturn(profile);
61
        when(profile.getPDFAMode()).thenReturn(PDFAMode.PDFA_1A);
62
        when(irpng.getSize()).thenReturn(is);
63
        irpnga.setup(doc);
64
        FlateFilter filter = (FlateFilter) irpnga.getPDFFilter();
65
        assertEquals(1, filter.getColors());
66
    }
67
68
    @Test
69
    public void testSetupWithComponentColorModel() throws IOException {
70
        ComponentColorModel cm = mock(ComponentColorModel.class);
71
        ImageRawPNG irpng = mock(ImageRawPNG.class);
72
        PDFDocument doc = mock(PDFDocument.class);
73
        PDFProfile profile = mock(PDFProfile.class);
74
        ImageRawPNGAdapter irpnga = new ImageRawPNGAdapter(irpng, "mock");
75
        ImageSize is = RawPNGTestUtil.getImageSize();
76
77
        when(irpng.getColorModel()).thenReturn(cm);
78
        when(cm.getNumComponents()).thenReturn(3);
79
        // when(cm.hasAlpha()).thenReturn(false);
80
        when(doc.getProfile()).thenReturn(profile);
81
        when(profile.getPDFAMode()).thenReturn(PDFAMode.PDFA_1A);
82
        when(irpng.getSize()).thenReturn(is);
83
        irpnga.setup(doc);
84
        FlateFilter filter = (FlateFilter) irpnga.getPDFFilter();
85
        assertEquals(3, filter.getColors());
86
    }
87
88
    @Test
89
    public void testOutputContentsWithRGBPNG() throws IOException {
90
        testOutputContentsWithGRGBAPNG(-1, 128, 128, 128, -1);
91
    }
92
93
    @Test
94
    public void testOutputContentsWithRGBAPNG() throws IOException {
95
        testOutputContentsWithGRGBAPNG(-1, 128, 128, 128, 128);
96
    }
97
98
    @Test
99
    public void testOutputContentsWithGPNG() throws IOException {
100
        testOutputContentsWithGRGBAPNG(128, -1, -1, -1, -1);
101
    }
102
103
    @Test
104
    public void testOutputContentsWithGAPNG() throws IOException {
105
        testOutputContentsWithGRGBAPNG(128, -1, -1, -1, 128);
106
    }
107
108
    private void testOutputContentsWithGRGBAPNG(int gray, int red, int green, int blue, int alpha)
109
            throws IOException {
110
        int numColorComponents = gray > -1 ? 1 : 3;
111
        int numComponents = numColorComponents + (alpha > -1 ? 1 : 0);
112
        ComponentColorModel cm = mock(ComponentColorModel.class);
113
        ImageRawPNG irpng = mock(ImageRawPNG.class);
114
        PDFDocument doc = mock(PDFDocument.class);
115
        PDFProfile profile = mock(PDFProfile.class);
116
        ImageRawPNGAdapter irpnga = new ImageRawPNGAdapter(irpng, "mock");
117
        ImageSize is = RawPNGTestUtil.getImageSize();
118
119
        when(irpng.getColorModel()).thenReturn(cm);
120
        when(cm.getNumComponents()).thenReturn(numComponents);
121
        // when(cm.hasAlpha()).thenReturn(false);
122
        when(doc.getProfile()).thenReturn(profile);
123
        when(profile.getPDFAMode()).thenReturn(PDFAMode.PDFA_1A);
124
        when(irpng.getSize()).thenReturn(is);
125
        irpnga.setup(doc);
126
        FlateFilter filter = (FlateFilter) irpnga.getPDFFilter();
127
        assertEquals(numColorComponents, filter.getColors());
128
129
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
130
        byte[] data = RawPNGTestUtil.buildGRGBAData(gray, red, green, blue, alpha);
131
        ByteArrayInputStream bais = new ByteArrayInputStream(data);
132
        when(irpng.createInputStream()).thenReturn(bais);
133
        irpnga.outputContents(baos);
134
        if (alpha > -1) {
135
            byte[] expected = RawPNGTestUtil.buildGRGBAData(gray, red, green, blue, -1);
136
            assertArrayEquals(expected, baos.toByteArray());
137
        } else {
138
            assertArrayEquals(data, baos.toByteArray());
139
        }
140
    }
141
142
}
(-)test/java/org/apache/fop/render/ps/ImageEncoderPNGTestCase.java (+133 lines)
Line 0 Link Here
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$ */
19
20
package org.apache.fop.render.ps;
21
22
import java.awt.image.ComponentColorModel;
23
import java.awt.image.IndexColorModel;
24
import java.io.ByteArrayInputStream;
25
import java.io.ByteArrayOutputStream;
26
import java.io.IOException;
27
28
import org.junit.Test;
29
30
import org.apache.xmlgraphics.image.loader.ImageSize;
31
import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
32
33
import org.apache.fop.render.RawPNGTestUtil;
34
35
import static org.junit.Assert.assertArrayEquals;
36
import static org.junit.Assert.assertEquals;
37
38
import static org.mockito.Mockito.mock;
39
import static org.mockito.Mockito.when;
40
41
public class ImageEncoderPNGTestCase {
42
43
    @Test
44
    public void testWriteToWithRGBPNG() throws IOException {
45
        testWriteToWithGRGBAPNG(-1, 128, 128, 128, -1);
46
    }
47
48
    @Test
49
    public void testWriteToWithGPNG() throws IOException {
50
        testWriteToWithGRGBAPNG(128, -1, -1, -1, -1);
51
    }
52
53
    @Test
54
    public void testWriteToWithRGBAPNG() throws IOException {
55
        testWriteToWithGRGBAPNG(-1, 128, 128, 128, 128);
56
    }
57
58
    @Test
59
    public void testWriteToWithGAPNG() throws IOException {
60
        testWriteToWithGRGBAPNG(128, -1, -1, -1, 128);
61
    }
62
63
    private void testWriteToWithGRGBAPNG(int gray, int red, int green, int blue, int alpha)
64
            throws IOException {
65
        int numComponents = (gray > -1 ? 1 : 3) + (alpha > -1 ? 1 : 0);
66
        ImageSize is = RawPNGTestUtil.getImageSize();
67
        ComponentColorModel cm = mock(ComponentColorModel.class);
68
        when(cm.getNumComponents()).thenReturn(numComponents);
69
        ImageRawPNG irpng = mock(ImageRawPNG.class);
70
        when(irpng.getColorModel()).thenReturn(cm);
71
        when(irpng.getSize()).thenReturn(is);
72
        ImageEncoderPNG iepng = new ImageEncoderPNG(irpng);
73
74
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
75
        byte[] data = RawPNGTestUtil.buildGRGBAData(gray, red, green, blue, alpha);
76
        ByteArrayInputStream bais = new ByteArrayInputStream(data);
77
        when(irpng.createInputStream()).thenReturn(bais);
78
        iepng.writeTo(baos);
79
        if (alpha > -1) {
80
            byte[] expected = RawPNGTestUtil.buildGRGBAData(gray, red, green, blue, -1);
81
            assertArrayEquals(expected, baos.toByteArray());
82
        } else {
83
            assertArrayEquals(data, baos.toByteArray());
84
        }
85
    }
86
87
    @Test
88
    public void testWriteToWithPalettePNG() throws IOException {
89
        ImageSize is = RawPNGTestUtil.getImageSize();
90
        IndexColorModel cm = mock(IndexColorModel.class);
91
        ImageRawPNG irpng = mock(ImageRawPNG.class);
92
        when(irpng.getColorModel()).thenReturn(cm);
93
        when(irpng.getSize()).thenReturn(is);
94
        ImageEncoderPNG iepng = new ImageEncoderPNG(irpng);
95
96
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
97
        byte[] data = RawPNGTestUtil.buildGRGBAData(128, -1, -1, -1, -1);
98
        ByteArrayInputStream bais = new ByteArrayInputStream(data);
99
        when(irpng.createInputStream()).thenReturn(bais);
100
        iepng.writeTo(baos);
101
        assertArrayEquals(data, baos.toByteArray());
102
    }
103
104
    @Test
105
    public void testGetImplicitFilterWithIndexColorModel() {
106
        ImageSize is = RawPNGTestUtil.getImageSize();
107
        IndexColorModel cm = mock(IndexColorModel.class);
108
        ImageRawPNG irpng = mock(ImageRawPNG.class);
109
        when(irpng.getColorModel()).thenReturn(cm);
110
        when(irpng.getBitDepth()).thenReturn(8);
111
        when(irpng.getSize()).thenReturn(is);
112
        ImageEncoderPNG iepng = new ImageEncoderPNG(irpng);
113
114
        String expectedFilter = "<< /Predictor 15 /Columns 32 /Colors 1 /BitsPerComponent 8 >> /FlateDecode";
115
        assertEquals(expectedFilter, iepng.getImplicitFilter());
116
    }
117
118
    @Test
119
    public void testGetImplicitFilterWithComponentColorModel() {
120
        ImageSize is = RawPNGTestUtil.getImageSize();
121
        ComponentColorModel cm = mock(ComponentColorModel.class);
122
        when(cm.getNumComponents()).thenReturn(3);
123
        ImageRawPNG irpng = mock(ImageRawPNG.class);
124
        when(irpng.getColorModel()).thenReturn(cm);
125
        when(irpng.getBitDepth()).thenReturn(8);
126
        when(irpng.getSize()).thenReturn(is);
127
        ImageEncoderPNG iepng = new ImageEncoderPNG(irpng);
128
129
        String expectedFilter = "<< /Predictor 15 /Columns 32 /Colors 3 /BitsPerComponent 8 >> /FlateDecode";
130
        assertEquals(expectedFilter, iepng.getImplicitFilter());
131
    }
132
133
}
(-)test/java/org/apache/fop/render/RawPNGTestUtil.java (+92 lines)
Line 0 Link Here
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$ */
19
20
package org.apache.fop.render;
21
22
import java.io.ByteArrayOutputStream;
23
import java.io.IOException;
24
import java.util.zip.Deflater;
25
import java.util.zip.DeflaterOutputStream;
26
27
import org.apache.xmlgraphics.image.loader.ImageSize;
28
29
public final class RawPNGTestUtil {
30
31
    private static int NUM_ROWS = 32;
32
    private static int NUM_COLUMNS = 32;
33
    private static int DPI = 72;
34
35
    private RawPNGTestUtil() {
36
37
    }
38
39
    /**
40
     * Builds a PNG IDAT section for a square of a given color and alpha; the filter is fixed.
41
     * @param gray the gray color; set to -1 if using RGB
42
     * @param red the red color; ignored if gray > -1
43
     * @param green the green color; ignored if gray > -1
44
     * @param blue the blue color; ignored if gray > -1
45
     * @param alpha the alpha color; set to -1 if not present
46
     * @return the PNG IDAT byte array
47
     * @throws IOException
48
     */
49
    public static byte[] buildGRGBAData(int gray, int red, int green, int blue, int alpha) throws IOException {
50
        // build an image, 32x32, Gray or RGB, with or without alpha, and with filter
51
        int filter = 0;
52
        int numRows = NUM_ROWS;
53
        int numColumns = NUM_COLUMNS;
54
        int numComponents = (gray > -1 ? 1 : 3) + (alpha > -1 ? 1 : 0);
55
        int numBytesPerRow = numColumns * numComponents + 1; // 1 for filter
56
        int numBytes = numRows * numBytesPerRow;
57
        byte[] data = new byte[numBytes];
58
        for (int r = 0; r < numRows; r++) {
59
            data[r * numBytesPerRow] = (byte) filter;
60
            for (int c = 0; c < numColumns; c++) {
61
                if (numComponents == 1) {
62
                    data[r * numBytesPerRow + numComponents * c + 1] = (byte) gray;
63
                } else if (numComponents == 2) {
64
                    data[r * numBytesPerRow + numComponents * c + 1] = (byte) gray;
65
                    data[r * numBytesPerRow + numComponents * c + 2] = (byte) alpha;
66
                } else if (numComponents == 3) {
67
                    data[r * numBytesPerRow + numComponents * c + 1] = (byte) red;
68
                    data[r * numBytesPerRow + numComponents * c + 2] = (byte) green;
69
                    data[r * numBytesPerRow + numComponents * c + 3] = (byte) blue;
70
                } else if (numComponents == 4) {
71
                    data[r * numBytesPerRow + numComponents * c + 1] = (byte) red;
72
                    data[r * numBytesPerRow + numComponents * c + 2] = (byte) green;
73
                    data[r * numBytesPerRow + numComponents * c + 3] = (byte) blue;
74
                    data[r * numBytesPerRow + numComponents * c + 4] = (byte) alpha;
75
                }
76
            }
77
        }
78
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
79
        DeflaterOutputStream dos = new DeflaterOutputStream(baos, new Deflater());
80
        dos.write(data);
81
        dos.close();
82
        return baos.toByteArray();
83
    }
84
85
    /**
86
     * 
87
     * @return a default ImageSize
88
     */
89
    public static ImageSize getImageSize() {
90
        return new ImageSize(NUM_ROWS, NUM_COLUMNS, DPI);
91
    }
92
}

Return to bug 40676