/* * 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.ps; // Java import java.awt.geom.AffineTransform; import java.io.IOException; import java.util.Map; import org.w3c.dom.Document; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.gvt.GraphicsNode; import org.apache.xmlgraphics.java2d.ps.PSGraphics2D; import org.apache.xmlgraphics.ps.PSGenerator; import org.apache.fop.configuration.Configuration; import org.apache.fop.fonts.FontInfo; import org.apache.fop.image.loader.batik.BatikUtil; import org.apache.fop.render.AbstractGenericSVGHandler; import org.apache.fop.render.ImageHandlerUtil; import org.apache.fop.render.Renderer; import org.apache.fop.render.RendererContext; import org.apache.fop.render.RendererContextConstants; import org.apache.fop.svg.SVGEventProducer; import org.apache.fop.svg.SVGUserAgent; /** * PostScript XML handler for SVG. Uses Apache Batik for SVG processing. * This handler handles XML for foreign objects when rendering to PostScript. * It renders SVG to the PostScript document using the PSGraphics2D. * The properties from the PostScript renderer are subject to change. * * @version $Id$ */ public class PSSVGHandler extends AbstractGenericSVGHandler implements PSRendererContextConstants { /** * Create a new PostScript XML handler for use by the PostScript renderer. */ public PSSVGHandler() { } /** * Get the pdf information from the render context. * * @param context the renderer context * @return the pdf information retrieved from the context */ public static PSInfo getPSInfo(RendererContext context) { PSInfo psi = new PSInfo(); psi.psGenerator = (PSGenerator)context.getProperty(PS_GENERATOR); psi.fontInfo = (org.apache.fop.fonts.FontInfo) context.getProperty(PS_FONT_INFO); psi.width = (Integer) context.getProperty(WIDTH); psi.height = (Integer) context.getProperty(HEIGHT); psi.currentXPosition = (Integer) context.getProperty(XPOS); psi.currentYPosition = (Integer) context.getProperty(YPOS); psi.cfg = (Configuration)context.getProperty(HANDLER_CONFIGURATION); return psi; } /** * PostScript information structure for drawing the XML document. */ public static class PSInfo { /** see PS_GENERATOR */ private PSGenerator psGenerator; /** see PS_FONT_INFO */ private org.apache.fop.fonts.FontInfo fontInfo; /** see WIDTH */ private int width; /** see HEIGHT */ private int height; /** see XPOS */ private int currentXPosition; /** see YPOS */ private int currentYPosition; /** see HANDLER_CONFIGURATION */ private Configuration cfg; /** * Returns the PSGenerator. * @return PSGenerator */ public PSGenerator getPSGenerator() { return psGenerator; } /** * Sets the PSGenerator. * @param psGenerator The PSGenerator to set */ public void setPsGenerator(PSGenerator psGenerator) { this.psGenerator = psGenerator; } /** * Returns the fontInfo. * @return FontInfo */ public FontInfo getFontInfo() { return fontInfo; } /** * Sets the fontInfo. * @param fontInfo The fontInfo to set */ public void setFontInfo(FontInfo fontInfo) { this.fontInfo = fontInfo; } /** * Returns the currentXPosition. * @return int */ public int getCurrentXPosition() { return currentXPosition; } /** * Sets the currentXPosition. * @param currentXPosition The currentXPosition to set */ public void setCurrentXPosition(int currentXPosition) { this.currentXPosition = currentXPosition; } /** * Returns the currentYPosition. * @return int */ public int getCurrentYPosition() { return currentYPosition; } /** * Sets the currentYPosition. * @param currentYPosition The currentYPosition to set */ public void setCurrentYPosition(int currentYPosition) { this.currentYPosition = currentYPosition; } /** * Returns the width. * @return int */ public int getWidth() { return width; } /** * Sets the width. * @param width The pageWidth to set */ public void setWidth(int width) { this.width = width; } /** * Returns the height. * @return int */ public int getHeight() { return height; } /** * Sets the height. * @param height The height to set */ public void setHeight(int height) { this.height = height; } /** * Returns the height. * @return int */ public Configuration getHandlerConfiguration() { return this.cfg; } /** * Sets the handler configuration. * @param cfg the configuration object */ public void setHandlerConfiguration(Configuration cfg) { this.cfg = cfg; } } /** * Render the svg document. * @param context the renderer context * @param doc the svg document */ protected void renderSVGDocument(RendererContext context, Document doc) { assert context != null; PSInfo psInfo = getPSInfo(context); int xOffset = psInfo.currentXPosition; int yOffset = psInfo.currentYPosition; PSGenerator gen = psInfo.psGenerator; boolean paintAsBitmap = false; Map foreign = (Map)context.getProperty(RendererContextConstants.FOREIGN_ATTRIBUTES); paintAsBitmap = ImageHandlerUtil.isConversionModeBitmap(foreign); if (paintAsBitmap) { try { super.renderSVGDocument(context, doc); } catch (IOException ioe) { SVGEventProducer eventProducer = SVGEventProducer.Provider.get( context.getUserAgent().getEventBroadcaster()); eventProducer.svgRenderingError(this, ioe, getDocumentURI(doc)); } return; } //Controls whether text painted by Batik is generated using text or path operations boolean strokeText = false; Configuration cfg = psInfo.getHandlerConfiguration(); if (cfg != null) { strokeText = cfg.getChild("stroke-text", true).getValueAsBoolean(strokeText); } SVGUserAgent ua = new SVGUserAgent(context.getUserAgent(), null /* TODO */, new AffineTransform()); PSGraphics2D graphics = new PSGraphics2D(strokeText, gen); graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); BridgeContext ctx = new PSBridgeContext(ua, (strokeText ? null : psInfo.fontInfo), context.getUserAgent().getImageManager(), context.getUserAgent().getImageSessionContext()); //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine) //to it. Document clonedDoc = BatikUtil.cloneSVGDocument(doc); GraphicsNode root; try { GVTBuilder builder = new GVTBuilder(); root = builder.build(ctx, clonedDoc); } catch (Exception e) { SVGEventProducer eventProducer = SVGEventProducer.Provider.get( context.getUserAgent().getEventBroadcaster()); eventProducer.svgNotBuilt(this, e, getDocumentURI(doc)); return; } // get the 'width' and 'height' attributes of the SVG document float w = (float)ctx.getDocumentSize().getWidth() * 1000f; float h = (float)ctx.getDocumentSize().getHeight() * 1000f; float sx = psInfo.getWidth() / w; float sy = psInfo.getHeight() / h; try { gen.commentln("%FOPBeginSVG"); gen.saveGraphicsState(); /* * Clip to the svg area. * Note: To have the svg overlay (under) a text area then use * an fo:block-container */ gen.writeln("newpath"); gen.defineRect(xOffset / 1000f, yOffset / 1000f, psInfo.getWidth() / 1000f, psInfo.getHeight() / 1000f); gen.writeln("clip"); // transform so that the coordinates (0,0) is from the top left // and positive is down and to the right. (0,0) is where the // viewBox puts it. gen.concatMatrix(sx, 0, 0, sy, xOffset / 1000f, yOffset / 1000f); /* SVGSVGElement svg = ((SVGDocument)doc).getRootElement(); AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg, psInfo.getWidth() / 1000f, psInfo.getHeight() / 1000f, ctx); if (!at.isIdentity()) { double[] vals = new double[6]; at.getMatrix(vals); gen.concatMatrix(vals); }*/ AffineTransform transform = new AffineTransform(); // scale to viewbox transform.translate(xOffset, yOffset); gen.getCurrentState().concatMatrix(transform); try { root.paint(graphics); } catch (Exception e) { SVGEventProducer eventProducer = SVGEventProducer.Provider.get( context.getUserAgent().getEventBroadcaster()); eventProducer.svgRenderingError(this, e, getDocumentURI(doc)); } gen.restoreGraphicsState(); gen.commentln("%FOPEndSVG"); } catch (IOException ioe) { SVGEventProducer eventProducer = SVGEventProducer.Provider.get( context.getUserAgent().getEventBroadcaster()); eventProducer.svgRenderingError(this, ioe, getDocumentURI(doc)); } } /** {@inheritDoc} */ public boolean supportsRenderer(Renderer renderer) { return false; } }