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: PSImageHandlerGraphics2D.java 1812122 2017-10-13 12:12:52Z ssteiner $ */
19 
20 package org.apache.fop.render.ps;
21 
22 import java.awt.Dimension;
23 import java.awt.Rectangle;
24 import java.awt.geom.AffineTransform;
25 import java.awt.geom.Dimension2D;
26 import java.awt.geom.Rectangle2D;
27 import java.io.IOException;
28 
29 import org.apache.xmlgraphics.image.loader.Image;
30 import org.apache.xmlgraphics.image.loader.ImageFlavor;
31 import org.apache.xmlgraphics.image.loader.ImageInfo;
32 import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
33 import org.apache.xmlgraphics.java2d.GeneralGraphics2DImagePainter;
34 import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
35 import org.apache.xmlgraphics.java2d.ps.PSGraphics2D;
36 import org.apache.xmlgraphics.ps.FormGenerator;
37 import org.apache.xmlgraphics.ps.PSGenerator;
38 import org.apache.xmlgraphics.ps.PSProcSets;
39 
40 import org.apache.fop.fonts.FontInfo;
41 import org.apache.fop.render.RenderingContext;
42 
43 /**
44  * Image handler implementation which handles vector graphics (Java2D) for PostScript output.
45  */
46 public class PSImageHandlerGraphics2D implements PSImageHandler {
47 
48     private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {
49         ImageFlavor.GRAPHICS2D
50     };
51 
52     /** {@inheritDoc} */
handleImage(RenderingContext context, Image image, Rectangle pos)53     public void handleImage(RenderingContext context, Image image, Rectangle pos)
54                 throws IOException {
55         PSRenderingContext psContext = (PSRenderingContext)context;
56         PSGenerator gen = psContext.getGenerator();
57         ImageGraphics2D imageG2D = (ImageGraphics2D)image;
58         Graphics2DImagePainter painter = imageG2D.getGraphics2DImagePainter();
59 
60         float fx = (float)pos.getX() / 1000f;
61         float fy = (float)pos.getY() / 1000f;
62         float fwidth = (float)pos.getWidth() / 1000f;
63         float fheight = (float)pos.getHeight() / 1000f;
64 
65         // get the 'width' and 'height' attributes of the SVG document
66         Dimension dim = painter.getImageSize();
67         float imw = (float)dim.getWidth() / 1000f;
68         float imh = (float)dim.getHeight() / 1000f;
69 
70         float sx = fwidth / imw;
71         float sy = fheight / imh;
72 
73         gen.commentln("%FOPBeginGraphics2D");
74         gen.saveGraphicsState();
75         final boolean clip = false;
76         if (clip) {
77             // Clip to the image area.
78             gen.writeln("newpath");
79             gen.defineRect(fx, fy, fwidth, fheight);
80             gen.writeln("clip");
81         }
82 
83         // transform so that the coordinates (0,0) is from the top left
84         // and positive is down and to the right. (0,0) is where the
85         // viewBox puts it.
86         gen.concatMatrix(sx, 0, 0, sy, fx, fy);
87 
88         final boolean textAsShapes = false;
89         PSGraphics2D graphics = (painter instanceof GeneralGraphics2DImagePainter)
90                 ? (PSGraphics2D) ((GeneralGraphics2DImagePainter) painter).getGraphics(textAsShapes, gen)
91                 : new PSGraphics2D(textAsShapes, gen);
92         graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
93         AffineTransform transform = new AffineTransform();
94         // scale to viewbox
95         transform.translate(fx, fy);
96         gen.getCurrentState().concatMatrix(transform);
97         Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, imw, imh);
98         if (painter instanceof GeneralGraphics2DImagePainter) {
99             PSFontUtils.addFallbackFonts(psContext.getFontInfo(), (GeneralGraphics2DImagePainter) painter);
100         }
101         painter.paint(graphics, area);
102         gen.restoreGraphicsState();
103         gen.commentln("%FOPEndGraphics2D");
104     }
105 
106     /** {@inheritDoc} */
generateForm(RenderingContext context, Image image, final PSImageFormResource form)107     public void generateForm(RenderingContext context, Image image, final PSImageFormResource form)
108             throws IOException {
109         PSRenderingContext psContext = (PSRenderingContext)context;
110         PSGenerator gen = psContext.getGenerator();
111         final ImageGraphics2D imageG2D = (ImageGraphics2D)image;
112         ImageInfo info = image.getInfo();
113 
114         FormGenerator formGen = buildFormGenerator(gen.getPSLevel(), form, info, imageG2D, psContext.getFontInfo());
115         formGen.generate(gen);
116     }
117     /** {@inheritDoc} */
getPriority()118     public int getPriority() {
119         return 200;
120     }
121 
122     /** {@inheritDoc} */
getSupportedImageClass()123     public Class getSupportedImageClass() {
124         return ImageGraphics2D.class;
125     }
126 
127     /** {@inheritDoc} */
getSupportedImageFlavors()128     public ImageFlavor[] getSupportedImageFlavors() {
129         return FLAVORS;
130     }
131 
132     /** {@inheritDoc} */
isCompatible(RenderingContext targetContext, Image image)133     public boolean isCompatible(RenderingContext targetContext, Image image) {
134         if (targetContext instanceof PSRenderingContext) {
135             return (image == null || image instanceof ImageGraphics2D);
136         }
137         return false;
138     }
139 
buildFormGenerator(int psLanguageLevel, final PSImageFormResource form, final ImageInfo info, final ImageGraphics2D imageG2D, final FontInfo fontInfo)140     private FormGenerator buildFormGenerator(int psLanguageLevel, final PSImageFormResource form, final ImageInfo info,
141                                              final ImageGraphics2D imageG2D, final FontInfo fontInfo) {
142         String imageDescription = info.getMimeType() + " " + info.getOriginalURI();
143         final Dimension2D dimensionsPt = info.getSize().getDimensionPt();
144         final Dimension2D dimensionsMpt = info.getSize().getDimensionMpt();
145         FormGenerator formGen;
146 
147         if (psLanguageLevel <= 2) {
148             formGen = new EPSFormGenerator(form.getName(), imageDescription, dimensionsPt) {
149 
150                 @Override
151                 void doGeneratePaintProc(PSGenerator gen) throws IOException {
152                     paintImageG2D(imageG2D, dimensionsMpt, gen, fontInfo);
153                 }
154             };
155         } else {
156             formGen = new EPSFormGenerator(form.getName(), imageDescription, dimensionsPt) {
157 
158                 @Override
159                 protected void generateAdditionalDataStream(PSGenerator gen) throws IOException {
160                     gen.writeln("/" + form.getName() + ":Data currentfile <<");
161                     gen.writeln("  /Filter /SubFileDecode");
162                     gen.writeln("  /DecodeParms << /EODCount 0 /EODString (%FOPEndOfData) >>");
163                     gen.writeln(">> /ReusableStreamDecode filter");
164                     paintImageG2D(imageG2D, dimensionsMpt, gen, fontInfo);
165                     gen.writeln("%FOPEndOfData");
166                     gen.writeln("def");
167                 }
168 
169                 @Override
170                 void doGeneratePaintProc(PSGenerator gen) throws IOException {
171                     gen.writeln(form.getName() + ":Data 0 setfileposition");
172                     gen.writeln(form.getName() + ":Data cvx exec");
173                 }
174             };
175         }
176         return formGen;
177     }
178 
179     private abstract static class EPSFormGenerator extends FormGenerator {
180 
EPSFormGenerator(String formName, String title, Dimension2D dimensions)181         EPSFormGenerator(String formName, String title, Dimension2D dimensions) {
182             super(formName, title, dimensions);
183         }
184 
paintImageG2D(final ImageGraphics2D imageG2D, Dimension2D dimensionsMpt, PSGenerator gen, FontInfo fontInfo)185         protected void paintImageG2D(final ImageGraphics2D imageG2D, Dimension2D dimensionsMpt,
186                 PSGenerator gen, FontInfo fontInfo) throws IOException {
187             PSGraphics2DAdapter adapter = new PSGraphics2DAdapter(gen, false, fontInfo);
188             adapter.paintImage(imageG2D.getGraphics2DImagePainter(),
189                         null,
190                         0, 0,
191                         (int) Math.round(dimensionsMpt.getWidth()),
192                         (int) Math.round(dimensionsMpt.getHeight()));
193         }
194 
195         @Override
generatePaintProc(PSGenerator gen)196         protected final void generatePaintProc(PSGenerator gen) throws IOException {
197             gen.getResourceTracker().notifyResourceUsageOnPage(
198                     PSProcSets.EPS_PROCSET);
199             gen.writeln("BeginEPSF");
200             doGeneratePaintProc(gen);
201             gen.writeln("EndEPSF");
202         }
203 
doGeneratePaintProc(PSGenerator gen)204         abstract void doGeneratePaintProc(PSGenerator gen) throws IOException;
205     }
206 }
207