/*
* $Id$
* Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
* For details on use and redistribution please refer to the
* LICENSE file included with these sources.
*/
package org.apache.fop.render.ps;
// FOP
import org.apache.fop.svg.SVGArea;
import org.apache.fop.render.AbstractRenderer;
import org.apache.fop.render.Renderer;
import org.apache.fop.image.ImageArea;
import org.apache.fop.image.FopImage;
import org.apache.fop.image.FopImageException;
import org.apache.fop.layout.*;
import org.apache.fop.layout.inline.*;
import org.apache.fop.datatypes.*;
import org.apache.fop.fo.properties.*;
import org.apache.fop.render.pdf.Font;
import org.apache.fop.image.*;
import org.apache.batik.bridge.*;
import org.apache.batik.swing.svg.*;
import org.apache.batik.swing.gvt.*;
import org.apache.batik.gvt.*;
import org.apache.batik.gvt.renderer.*;
import org.apache.batik.gvt.filter.*;
import org.apache.batik.gvt.event.*;
import org.apache.log.Logger;
// SVG
import org.w3c.dom.svg.SVGSVGElement;
import org.w3c.dom.svg.SVGDocument;
import org.w3c.dom.*;
import org.w3c.dom.svg.*;
// Java
import java.io.*;
import java.util.*;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Hashtable;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.Dimension;
/**
* Renderer that renders to PostScript.
*
* This class currently generates PostScript Level 2 code. The only exception
* is the FlateEncode filter which is a Level 3 feature. The filters in use
* are hardcoded at the moment.
*
* This class follows the Document Structuring Conventions (DSC) version 3.0
* (If I did everything right). If anyone modifies this renderer please make
* sure to also follow the DSC to make it simpler to programmatically modify
* the generated Postscript files (ex. extract pages etc.).
*
* TODO: Character size/spacing, SVG Transcoder for Batik, configuration, move
* to PrintRenderer, maybe improve filters (I'm not very proud of them), add a
* RunLengthEncode filter (useful for Level 2 Postscript), Improve
* DocumentProcessColors stuff (probably needs to be configurable, then maybe
* add a color to grayscale conversion for bitmaps to make output smaller (See
* PCLRenderer), font embedding, support different character encodings, try to
* implement image transparency, positioning of images is wrong etc.
*
* Modified by Mark Lillywhite mark-fop@inomial.com, to use the new
* Renderer interface. This PostScript renderer appears to be the
* most efficient at producing output.
*
* @author Jeremias Märki
*/
public class PSRenderer extends AbstractRenderer {
/**
* the application producing the PostScript
*/
protected String producer;
int imagecount = 0; // DEBUG
private boolean enableComments = true;
/**
* the stream used to output the PostScript
*/
protected PSStream out;
private boolean ioTrouble = false;
private String currentFontName;
private int currentFontSize;
private int pageHeight;
private int pageWidth;
private int currentXPosition = 0;
private int currentYPosition = 0;
private int currentAreaContainerXPosition = 0;
private float currRed;
private float currGreen;
private float currBlue;
private FontInfo fontInfo;
protected Hashtable options;
/**
* set the document's producer
*
* @param producer string indicating application producing the PostScript
*/
public void setProducer(String producer) {
this.producer = producer;
}
/**
* set up renderer options
*/
public void setOptions(Hashtable options) {
this.options = options;
}
/**
* write out a command
*/
protected void write(String cmd) {
try {
out.write(cmd);
} catch (IOException e) {
if (!ioTrouble)
e.printStackTrace();
ioTrouble = true;
}
}
/**
* write out a comment
*/
protected void comment(String comment) {
if (this.enableComments)
write(comment);
}
protected void writeFontDict(FontInfo fontInfo) {
write("%%BeginResource: procset FOPFonts");
write("%%Title: Font setup (shortcuts) for this file");
write("/FOPFonts 100 dict dup begin");
write("/bd{bind def}bind def");
write("/ld{load def}bd");
write("/M/moveto ld");
write("/RM/rmoveto ld");
write("/t/show ld");
write("/ux 0.0 def");
write("/uy 0.0 def");
// write("/cf /Helvetica def");
// write("/cs 12000 def");
// F
write("/F {");
write(" /Tp exch def");
// write(" currentdict exch get");
write(" /Tf exch def");
write(" Tf findfont Tp scalefont setfont");
write(" /cf Tf def /cs Tp def /cw ( ) stringwidth pop def");
write("} bd");
write("/ULS {currentpoint /uy exch def /ux exch def} bd");
write("/ULE {");
write(" /Tcx currentpoint pop def");
write(" gsave");
write(" newpath");
write(" cf findfont cs scalefont dup");
write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
write(" /UnderlinePosition get Ts mul /To exch def");
write(" /UnderlineThickness get Ts mul /Tt exch def");
write(" ux uy To add moveto Tcx uy To add lineto");
write(" Tt setlinewidth stroke");
write(" grestore");
write("} bd");
write("/OLE {");
write(" /Tcx currentpoint pop def");
write(" gsave");
write(" newpath");
write(" cf findfont cs scalefont dup");
write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
write(" /UnderlinePosition get Ts mul /To exch def");
write(" /UnderlineThickness get Ts mul /Tt exch def");
write(" ux uy To add cs add moveto Tcx uy To add cs add lineto");
write(" Tt setlinewidth stroke");
write(" grestore");
write("} bd");
write("/SOE {");
write(" /Tcx currentpoint pop def");
write(" gsave");
write(" newpath");
write(" cf findfont cs scalefont dup");
write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
write(" /UnderlinePosition get Ts mul /To exch def");
write(" /UnderlineThickness get Ts mul /Tt exch def");
write(" ux uy To add cs 10 mul 26 idiv add moveto Tcx uy To add cs 10 mul 26 idiv add lineto");
write(" Tt setlinewidth stroke");
write(" grestore");
write("} bd");
// write("/gfF1{/Helvetica findfont} bd");
// write("/gfF3{/Helvetica-Bold findfont} bd");
Hashtable fonts = fontInfo.getFonts();
Enumeration enum = fonts.keys();
while (enum.hasMoreElements()) {
String key = (String)enum.nextElement();
Font fm = (Font)fonts.get(key);
write("/" + key + " /" + fm.fontName() + " def");
}
write("end def");
write("%%EndResource");
enum = fonts.keys();
while (enum.hasMoreElements()) {
String key = (String)enum.nextElement();
Font fm = (Font)fonts.get(key);
write("/" + fm.fontName() + " findfont");
write("dup length dict begin");
write(" {1 index /FID ne {def} {pop pop} ifelse} forall");
write(" /Encoding ISOLatin1Encoding def");
write(" currentdict");
write("end");
write("/" + fm.fontName() + " exch definefont pop");
}
}
protected void movetoCurrPosition() {
write(this.currentXPosition + " " + this.currentYPosition + " M");
}
/**
* set up the font info
*
* @param fontInfo the font info object to set up
*/
public void setupFontInfo(FontInfo fontInfo) {
/* use PDF's font setup to get PDF metrics */
org.apache.fop.render.pdf.FontSetup.setup(fontInfo);
this.fontInfo = fontInfo;
}
/**
* render an area container to PostScript
*
* @param area the area container to render
*/
public void renderAreaContainer(AreaContainer area) {
int saveY = this.currentYPosition;
int saveX = this.currentAreaContainerXPosition;
if (area.getPosition() == Position.ABSOLUTE) {
// Y position is computed assuming positive Y axis, adjust for negative postscript one
this.currentYPosition = area.getYPosition()
- 2 * area.getPaddingTop()
- 2 * area.getBorderTopWidth();
this.currentAreaContainerXPosition = area.getXPosition();
} else if (area.getPosition() == Position.RELATIVE) {
this.currentYPosition -= area.getYPosition();
this.currentAreaContainerXPosition += area.getXPosition();
} else if (area.getPosition() == Position.STATIC) {
this.currentYPosition -= area.getPaddingTop()
+ area.getBorderTopWidth();
this.currentAreaContainerXPosition += area.getPaddingLeft()
+ area.getBorderLeftWidth();
}
this.currentXPosition = this.currentAreaContainerXPosition;
// comment("% --- AreaContainer begin");
doFrame(area);
Enumeration e = area.getChildren().elements();
while (e.hasMoreElements()) {
Box b = (Box)e.nextElement();
b.render(this);
}
// comment("% --- AreaContainer end");
if (area.getPosition() != Position.STATIC) {
this.currentYPosition = saveY;
this.currentAreaContainerXPosition = saveX;
} else {
this.currentYPosition -= area.getHeight();
}
}
/**
* render a body area container to PostScript
*
* @param area the body area container to render
*/
public void renderBodyAreaContainer(BodyAreaContainer area) {
int saveY = this.currentYPosition;
int saveX = this.currentAreaContainerXPosition;
if (area.getPosition() == Position.ABSOLUTE) {
// Y position is computed assuming positive Y axis, adjust for negative postscript one
this.currentYPosition = area.getYPosition();
this.currentAreaContainerXPosition = area.getXPosition();
} else if (area.getPosition() == Position.RELATIVE) {
this.currentYPosition -= area.getYPosition();
this.currentAreaContainerXPosition += area.getXPosition();
}
this.currentXPosition = this.currentAreaContainerXPosition;
int w, h;
int rx = this.currentAreaContainerXPosition;
w = area.getContentWidth();
h = area.getContentHeight();
int ry = this.currentYPosition;
// comment("% --- BodyAreaContainer begin");
doFrame(area);
// movetoCurrPosition();
Enumeration e = area.getChildren().elements();
while (e.hasMoreElements()) {
Box b = (Box)e.nextElement();
b.render(this);
}
// comment("% --- BodyAreaContainer end");
if (area.getPosition() != Position.STATIC) {
this.currentYPosition = saveY;
this.currentAreaContainerXPosition = saveX;
} else {
this.currentYPosition -= area.getHeight();
}
}
/**
* render a span area to PostScript
*
* @param area the span area to render
*/
public void renderSpanArea(SpanArea area) {
// comment("% --- SpanArea begin");
Enumeration e = area.getChildren().elements();
while (e.hasMoreElements()) {
Box b = (Box)e.nextElement();
b.render(this);
}
// comment("% --- SpanArea end");
}
/**
* render a block area to PostScript
*
* @param area the block area to render
*/
public void renderBlockArea(BlockArea area) {
// comment("% --- BlockArea begin");
doFrame(area);
Enumeration e = area.getChildren().elements();
while (e.hasMoreElements()) {
Box b = (Box)e.nextElement();
b.render(this);
}
// comment("% --- BlockArea end");
}
/**
* render a display space to PostScript
*
* @param space the space to render
*/
public void renderDisplaySpace(DisplaySpace space) {
// write("% --- DisplaySpace size="+space.getSize());
this.currentYPosition -= space.getSize();
movetoCurrPosition();
}
/**
* render a foreign object area
*/
public void renderForeignObjectArea(ForeignObjectArea area) {
// if necessary need to scale and align the content
area.getObject().render(this);
}
/**
* render an SVG area to PostScript
*
* @param area the area to render
*/
public void renderSVGArea(SVGArea area) {
int x = this.currentXPosition;
int y = this.currentYPosition;
Document doc = area.getSVGDocument();
SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
int w = (int)(svg.getWidth().getBaseVal().getValue() * 1000);
int h = (int)(svg.getHeight().getBaseVal().getValue() * 1000);
float sx = 1, sy = -1;
int xOffset = x, yOffset = y;
/*
* Clip to the svg area.
* Note: To have the svg overlay (under) a text area then use
* an fo:block-container
*/
comment("% --- SVG Area");
write("gsave");
if (w != 0 && h != 0) {
write("newpath");
write(x / 1000f + " " + y / 1000f + " M");
write((x + w) / 1000f + " " + y / 1000f + " rlineto");
write((x + w) / 1000f + " " + (y - h) / 1000f + " rlineto");
write(x / 1000f + " " + (y - h) / 1000f + " rlineto");
write("closepath");
write("clippath");
}
// 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.
write(xOffset + " " + yOffset + " translate");
write(sx + " " + sy + " " + " scale");
UserAgent userAgent = new MUserAgent(new AffineTransform(), log);
GVTBuilder builder = new GVTBuilder();
GraphicsNodeRenderContext rc = getRenderContext();
BridgeContext ctx = new BridgeContext(userAgent, rc);
GraphicsNode root;
PSGraphics2D graphics = new PSGraphics2D(false, area.getFontState(),
this, currentFontName,
currentFontSize,
currentXPosition,
currentYPosition);
graphics.setGraphicContext(new org.apache.batik.ext.awt.g2d.GraphicContext());
graphics.setRenderingHints(rc.getRenderingHints());
try {
root = builder.build(ctx, doc);
root.paint(graphics, rc);
} catch (Exception e) {
log.error("svg graphic could not be rendered: "
+ e.getMessage(), e);
}
write("grestore");
comment("% --- SVG Area end");
movetoCurrPosition();
}
public GraphicsNodeRenderContext getRenderContext() {
GraphicsNodeRenderContext nodeRenderContext = null;
if (nodeRenderContext == null) {
RenderingHints hints = new RenderingHints(null);
hints.put(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
FontRenderContext fontRenderContext =
new FontRenderContext(new AffineTransform(), true, true);
TextPainter textPainter = new StrokingTextPainter();
// TextPainter textPainter = new PDFTextPainter();
GraphicsNodeRableFactory gnrFactory =
new ConcreteGraphicsNodeRableFactory();
nodeRenderContext =
new GraphicsNodeRenderContext(new AffineTransform(), null,
hints, fontRenderContext,
textPainter, gnrFactory);
nodeRenderContext.setTextPainter(textPainter);
}
return nodeRenderContext;
}
public void renderBitmap(FopImage img, int x, int y, int w, int h) {
try {
boolean iscolor = img.getColorSpace().getColorSpace()
!= ColorSpace.DEVICE_GRAY;
byte[] imgmap = img.getBitmaps();
write("gsave");
write("/DeviceRGB setcolorspace");
write(x + " " + (y - h) + " translate");
write(w + " " + h + " scale");
write("<<");
write(" /ImageType 1");
write(" /Width " + img.getWidth());
write(" /Height " + img.getHeight());
write(" /BitsPerComponent 8");
if (iscolor) {
write(" /Decode [0 1 0 1 0 1]");
} else {
write(" /Decode [0 1]");
}
// Setup scanning for left-to-right and top-to-bottom
write(" /ImageMatrix [" + img.getWidth() + " 0 0 -"
+ img.getHeight() + " 0 " + img.getHeight() + "]");
write(" /DataSource currentfile /ASCII85Decode filter /FlateDecode filter");
// write(" /DataSource currentfile /ASCIIHexDecode filter /FlateDecode filter");
// write(" /DataSource currentfile /ASCII85Decode filter /RunLengthDecode filter");
// write(" /DataSource currentfile /ASCIIHexDecode filter /RunLengthDecode filter");
// write(" /DataSource currentfile /ASCIIHexDecode filter");
// write(" /DataSource currentfile /ASCII85Decode filter");
// write(" /DataSource currentfile /RunLengthDecode filter");
write(">>");
write("image");
/*
* for (int y=0; y 127) {
sb = sb.append("\\" + Integer.toOctalString(mch));
} else {
String escape = "\\()[]{}";
if (escape.indexOf(mch) >= 0) {
sb.append("\\");
}
sb = sb.append(mch);
}
}
// System.out.println("["+s+"] --> ["+sb.toString()+"]");
// comment("% --- InlineArea font-weight="+fontWeight+": " + sb.toString());
useFont(fs.getFontName(), fs.getFontSize());
useColor(area.getRed(), area.getGreen(), area.getBlue());
if (area.getUnderlined() || area.getLineThrough()
|| area.getOverlined())
write("ULS");
write("(" + sb.toString() + ") t");
if (area.getUnderlined())
write("ULE");
if (area.getLineThrough())
write("SOE");
if (area.getOverlined())
write("OLE");
this.currentXPosition += area.getContentWidth();
}
public void useFont(String name, int size) {
if ((currentFontName != name) || (currentFontSize != size)) {
write(name + " " + size + " F");
currentFontName = name;
currentFontSize = size;
}
}
/**
* render an inline space to PostScript
*
* @param space the space to render
*/
public void renderInlineSpace(InlineSpace space) {
// write("% --- InlineSpace size="+space.getSize());
this.currentXPosition += space.getSize();
if (space.getUnderlined() || space.getLineThrough()
|| space.getOverlined())
write("ULS");
write(space.getSize() + " 0 RM");
if (space.getUnderlined())
write("ULE");
if (space.getLineThrough())
write("SOE");
if (space.getOverlined())
write("OLE");
}
/**
* render a line area to PostScript
*
* @param area the area to render
*/
public void renderLineArea(LineArea area) {
int rx = this.currentAreaContainerXPosition + area.getStartIndent();
int ry = this.currentYPosition;
int w = area.getContentWidth();
int h = area.getHeight();
this.currentYPosition -= area.getPlacementOffset();
this.currentXPosition = rx;
int bl = this.currentYPosition;
movetoCurrPosition();
String fontWeight = area.getFontState().getFontWeight();
// comment("% --- LineArea begin font-weight="+fontWeight);
Enumeration e = area.getChildren().elements();
while (e.hasMoreElements()) {
Box b = (Box)e.nextElement();
this.currentYPosition = ry - area.getPlacementOffset();
b.render(this);
}
// comment("% --- LineArea end");
this.currentYPosition = ry - h;
this.currentXPosition = rx;
}
/**
* render a page to PostScript
*
* @param page the page to render
*/
public void renderPage(Page page) {
BodyAreaContainer body;
AreaContainer before, after;
write("%%Page: " + page.getNumber() + " " + page.getNumber());
write("%%BeginPageSetup");
write("FOPFonts begin");
write("0.001 0.001 scale");
write("%%EndPageSetup");
body = page.getBody();
before = page.getBefore();
after = page.getAfter();
if (before != null) {
renderAreaContainer(before);
}
renderBodyAreaContainer(body);
if (after != null) {
renderAreaContainer(after);
}
write("showpage");
write("%%PageTrailer");
write("%%EndPage");
}
/**
* render a leader area to PostScript
*
* @param area the area to render
*/
public void renderLeaderArea(LeaderArea area) {
int rx = this.currentXPosition;
int ry = this.currentYPosition;
int w = area.getContentWidth();
int th = area.getRuleThickness();
int th2 = th / 2;
int th3 = th / 3;
int th4 = th / 4;
switch (area.getLeaderPattern()) {
case LeaderPattern.SPACE:
// NOP
break;
case LeaderPattern.RULE:
if (area.getRuleStyle() == RuleStyle.NONE)
break;
useColor(area.getRed(), area.getGreen(), area.getBlue());
write("gsave");
write("0 setlinecap");
switch (area.getRuleStyle()) {
case RuleStyle.DOTTED:
write("newpath");
write("[1000 3000] 0 setdash");
write(th + " setlinewidth");
write(rx + " " + ry + " M");
write(w + " 0 rlineto");
useColor(area.getRed(), area.getGreen(), area.getBlue());
write("stroke");
break;
case RuleStyle.DASHED:
write("newpath");
write("[3000 3000] 0 setdash");
write(th + " setlinewidth");
write(rx + " " + ry + " M");
write(w + " 0 rlineto");
useColor(area.getRed(), area.getGreen(), area.getBlue());
write("stroke");
break;
case RuleStyle.SOLID:
write("newpath");
write(th + " setlinewidth");
write(rx + " " + ry + " M");
write(w + " 0 rlineto");
useColor(area.getRed(), area.getGreen(), area.getBlue());
write("stroke");
break;
case RuleStyle.DOUBLE:
write("newpath");
write(th3 + " setlinewidth");
write(rx + " " + (ry - th3) + " M");
write(w + " 0 rlineto");
write(rx + " " + (ry + th3) + " M");
write(w + " 0 rlineto");
useColor(area.getRed(), area.getGreen(), area.getBlue());
write("stroke");
break;
case RuleStyle.GROOVE:
write(th2 + " setlinewidth");
write("newpath");
write(rx + " " + (ry - th4) + " M");
write(w + " 0 rlineto");
useColor(area.getRed(), area.getGreen(), area.getBlue());
write("stroke");
write("newpath");
write(rx + " " + (ry + th4) + " M");
write(w + " 0 rlineto");
useColor(1, 1, 1); // white
write("stroke");
break;
case RuleStyle.RIDGE:
write(th2 + " setlinewidth");
write("newpath");
write(rx + " " + (ry - th4) + " M");
write(w + " 0 rlineto");
useColor(1, 1, 1); // white
write("stroke");
write("newpath");
write(rx + " " + (ry + th4) + " M");
write(w + " 0 rlineto");
useColor(area.getRed(), area.getGreen(), area.getBlue());
write("stroke");
break;
}
write("grestore");
break;
case LeaderPattern.DOTS:
comment("% --- Leader dots NYI");
log.error("Leader dots: Not yet implemented");
break;
case LeaderPattern.USECONTENT:
comment("% --- Leader use-content NYI");
log.error("Leader use-content: Not yet implemented");
break;
}
this.currentXPosition += area.getContentWidth();
write(area.getContentWidth() + " 0 RM");
}
private void doFrame(Area area) {
int w, h;
int rx = this.currentAreaContainerXPosition;
w = area.getContentWidth();
BorderAndPadding bap = area.getBorderAndPadding();
if (area instanceof BlockArea)
rx += ((BlockArea)area).getStartIndent();
h = area.getContentHeight();
int ry = this.currentYPosition;
rx = rx - area.getPaddingLeft();
ry = ry + area.getPaddingTop();
w = w + area.getPaddingLeft() + area.getPaddingRight();
h = h + area.getPaddingTop() + area.getPaddingBottom();
rx = rx - area.getBorderLeftWidth();
ry = ry + area.getBorderTopWidth();
w = w + area.getBorderLeftWidth() + area.getBorderRightWidth();
h = h + area.getBorderTopWidth() + area.getBorderBottomWidth();
// Create a textrect with these dimensions.
// The y co-ordinate is measured +ve downwards so subtract page-height
ColorType bg = area.getBackgroundColor();
if ((bg != null) && (bg.alpha() == 0)) {
write("newpath");
write(rx + " " + ry + " M");
write(w + " 0 rlineto");
write("0 " + (-h) + " rlineto");
write((-w) + " 0 rlineto");
write("0 " + h + " rlineto");
write("closepath");
useColor(bg);
write("fill");
}
if (area.getBorderTopWidth() != 0) {
write("newpath");
write(rx + " " + ry + " M");
write(w + " 0 rlineto");
write(area.getBorderTopWidth() + " setlinewidth");
write("0 setlinecap");
useColor(bap.getBorderColor(BorderAndPadding.TOP));
write("stroke");
}
if (area.getBorderLeftWidth() != 0) {
write("newpath");
write(rx + " " + ry + " M");
write("0 " + (-h) + " rlineto");
write(area.getBorderLeftWidth() + " setlinewidth");
write("0 setlinecap");
useColor(bap.getBorderColor(BorderAndPadding.LEFT));
write("stroke");
}
if (area.getBorderRightWidth() != 0) {
write("newpath");
write((rx + w) + " " + ry + " M");
write("0 " + (-h) + " rlineto");
write(area.getBorderRightWidth() + " setlinewidth");
write("0 setlinecap");
useColor(bap.getBorderColor(BorderAndPadding.RIGHT));
write("stroke");
}
if (area.getBorderBottomWidth() != 0) {
write("newpath");
write(rx + " " + (ry - h) + " M");
write(w + " 0 rlineto");
write(area.getBorderBottomWidth() + " setlinewidth");
write("0 setlinecap");
useColor(bap.getBorderColor(BorderAndPadding.BOTTOM));
write("stroke");
}
}
private void useColor(ColorType col) {
useColor(col.red(), col.green(), col.blue());
}
private void useColor(float red, float green, float blue) {
if ((red != currRed) || (green != currGreen) || (blue != currBlue)) {
write(red + " " + green + " " + blue + " setrgbcolor");
currRed = red;
currGreen = green;
currBlue = blue;
}
}
protected class MUserAgent implements UserAgent {
AffineTransform currentTransform = null;
Logger log;
/**
* Creates a new SVGUserAgent.
*/
protected MUserAgent(AffineTransform at, Logger logger) {
currentTransform = at;
log = logger;
}
/**
* Displays an error message.
*/
public void displayError(String message) {
log.error(message);
}
/**
* Displays an error resulting from the specified Exception.
*/
public void displayError(Exception ex) {
log.error("SVG Error" + ex.getMessage(), ex);
}
/**
* Displays a message in the User Agent interface.
* The given message is typically displayed in a status bar.
*/
public void displayMessage(String message) {
log.info(message);
}
/**
* Returns a customized the pixel to mm factor.
*/
public float getPixelToMM() {
// this is set to 72dpi as the values in fo are 72dpi
return 0.3527777777777777778f; // 72 dpi
// return 0.26458333333333333333333333333333f; // 96dpi
}
/**
* Returns the language settings.
*/
public String getLanguages() {
return "en"; // userLanguages;
}
/**
* Returns the user stylesheet uri.
* @return null if no user style sheet was specified.
*/
public String getUserStyleSheetURI() {
return null; // userStyleSheetURI;
}
/**
* Returns the class name of the XML parser.
*/
public String getXMLParserClassName() {
return org.apache.fop.apps.Driver.getParserClassName();
}
/**
* Opens a link in a new component.
* @param doc The current document.
* @param uri The document URI.
*/
public void openLink(SVGAElement elt) {
// application.openLink(uri);
}
public Point getClientAreaLocationOnScreen() {
return new Point(0, 0);
}
public void setSVGCursor(java.awt.Cursor cursor) {}
public AffineTransform getTransform() {
return currentTransform;
}
public Dimension2D getViewportSize() {
return new Dimension(100, 100);
}
public EventDispatcher getEventDispatcher() {
return null;
}
public boolean supportExtension(String str) {
return false;
}
public boolean hasFeature(String str) {
return false;
}
public void registerExtension(BridgeExtension be) {}
public void handleElement(Element elt, Object data) {}
}
/**
Default start renderer method. This would
normally be overridden. (mark-fop@inomial.com).
*/
public void startRenderer(OutputStream outputStream)
throws IOException {
log.debug("rendering areas to PostScript");
this.out = new PSStream(outputStream);
write("%!PS-Adobe-3.0");
write("%%Creator: "+this.producer);
write("%%DocumentProcessColors: Black");
write("%%DocumentSuppliedResources: procset FOPFonts");
write("%%EndComments");
write("%%BeginDefaults");
write("%%EndDefaults");
write("%%BeginProlog");
write("%%EndProlog");
write("%%BeginSetup");
writeFontDict(fontInfo);
write("%%EndSetup");
write("FOPFonts begin");
}
/**
Default stop renderer method. This would
normally be overridden. (mark-fop@inomial.com).
*/
public void stopRenderer(OutputStream outputStream)
throws IOException {
write("%%Trailer");
write("%%EOF");
this.out.flush();
log.debug("written out PostScript");
}
public void render(Page page, OutputStream outputStream) {
this.renderPage(page);
}
}