Browse Source

added svg renderer


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@194489 13f79535-47bb-0310-9956-ffa450edef68
pull/33/head
Keiron Liddle 22 years ago
parent
commit
36f1c831ce

+ 17
- 0
src/org/apache/fop/apps/CommandLineOptions.java View File

@@ -48,6 +48,8 @@ public class CommandLineOptions {
private static final int PS_OUTPUT = 6;
/* output: text file */
private static final int TXT_OUTPUT = 7;
/* output: svg file */
private static final int SVG_OUTPUT = 8;

/* System buffers */
private static final int BUFFER_FILE = 8;
@@ -247,6 +249,14 @@ public class CommandLineOptions {
outfile = new File(args[i + 1]);
i++;
}
} else if (args[i].equals("-svg")) {
setOutputMode(SVG_OUTPUT);
if ((i + 1 == args.length)
|| (args[i + 1].charAt(0) == '-')) {
throw new FOPException("you must specify the svg output file"); } else {
outfile = new File(args[i + 1]);
i++;
}
} else if (args[i].charAt(0) != '-') {
if (inputmode == NOT_SET) {
inputmode = FO_INPUT;
@@ -376,6 +386,8 @@ public class CommandLineOptions {
return Driver.RENDER_PS;
case TXT_OUTPUT:
return Driver.RENDER_TXT;
case SVG_OUTPUT:
return Driver.RENDER_SVG;
case AREA_OUTPUT:
rendererOptions.put("fineDetail", isCoarseAreaXml());
return Driver.RENDER_XML;
@@ -532,6 +544,7 @@ public class CommandLineOptions {
+ " -pcl outfile input will be rendered as pcl file (outfile req'd) \n"
+ " -ps outfile input will be rendered as PostScript file (outfile req'd) \n"
+ " -txt outfile input will be rendered as text file (outfile req'd) \n"
+ " -svg outfile input will be rendered as an svg slides file (outfile req'd) \n"
+ " -at outfile representation of area tree as XML (outfile req'd) \n"
+ " -print input file will be rendered and sent to the printer \n"
+ " see options with \"-print help\" \n\n"
@@ -614,6 +627,10 @@ public class CommandLineOptions {
log.debug("txt");
log.debug("output file: " + outfile.toString());
break;
case SVG_OUTPUT:
log.debug("svg");
log.debug("output file: " + outfile.toString());
break;
default:
log.debug("unknown input type");
}

+ 9
- 0
src/org/apache/fop/apps/Driver.java View File

@@ -131,6 +131,11 @@ public class Driver implements Loggable {
*/
public static final int RENDER_TXT = 8;

/**
* Render to SVG. OutputStream must be set
*/
public static final int RENDER_SVG = 9;

/**
* the FO tree builder
*/
@@ -308,6 +313,7 @@ public class Driver implements Loggable {
* <li>RENDER_PCL
* <li>RENDER_PS
* <li>RENDER_TXT
* <li>RENDER_SVG
* </ul>
* @param renderer the type of renderer to use
*/
@@ -335,6 +341,9 @@ public class Driver implements Loggable {
case RENDER_XML:
setRenderer(new org.apache.fop.render.xml.XMLRenderer());
break;
case RENDER_SVG:
setRenderer(new org.apache.fop.render.svg.SVGRenderer());
break;
default:
throw new IllegalArgumentException("Unknown renderer type");
}

+ 640
- 0
src/org/apache/fop/render/svg/SVGRenderer.java View File

@@ -0,0 +1,640 @@
/*
* $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.svg;

import org.apache.fop.layout.*;
import org.apache.fop.layout.inline.*;
import org.apache.fop.datatypes.*;
import org.apache.fop.image.*;
import org.apache.fop.svg.*;
import org.apache.fop.render.pdf.*;
import org.apache.fop.render.awt.*;
import org.apache.fop.viewer.*;
import org.apache.fop.apps.*;
import org.apache.fop.svg.SVGUtilities;

import org.w3c.dom.*;
import org.w3c.dom.svg.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.DOMImplementation;

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.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.dom.svg.SVGOMElement;
import org.apache.batik.dom.util.XMLSupport;
import org.apache.batik.transcoder.svg2svg.SVGTranscoder;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.TranscoderException;

import java.awt.*;
import java.awt.Image;
import java.awt.image.*;
import java.awt.geom.*;
import java.awt.font.*;
import java.util.*;
import java.net.URL;
import java.net.MalformedURLException;
import java.io.*;
import java.beans.*;
import javax.swing.*;
import java.awt.print.*;
import java.awt.image.BufferedImage;
import java.text.*;

import org.apache.fop.render.AbstractRenderer;

import org.apache.batik.util.SVGConstants;
import org.apache.batik.svggen.SVGGraphics2D;
import org.apache.batik.dom.svg.ExtensibleSVGDOMImplementation;

public class SVGRenderer extends AbstractRenderer {
static final String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI;
Document svgDocument;
Element svgRoot;
Element currentPageG = null;
Element lastLink = null;

float totalWidth = 0;
float totalHeight = 0;

protected int pageWidth = 0;
protected int pageHeight = 0;
protected int pageNumber = 0;

protected Hashtable fontNames = new Hashtable();
protected Hashtable fontStyles = new Hashtable();
protected Color saveColor = null;

protected IDReferences idReferences = null;

/**
* The current (internal) font name
*/
protected String currentFontName;

/**
* The current font size in millipoints
*/
protected int currentFontSize;

/**
* The current colour's red, green and blue component
*/
protected float currentRed = 0;
protected float currentGreen = 0;
protected float currentBlue = 0;

/**
* The parent component, used to set up the font.
* This is needed as FontSetup needs a live AWT component
* in order to generate valid font measures.
*/
protected Component parent;

/**
* options
*/
protected Hashtable options;

/**
* set up renderer options
*/
public void setOptions(Hashtable options) {
this.options = options;
}

public SVGRenderer() {
}

/**
* add a line to the current stream
*
* @param x1 the start x location in millipoints
* @param y1 the start y location in millipoints
* @param x2 the end x location in millipoints
* @param y2 the end y location in millipoints
* @param th the thickness in millipoints
* @param r the red component
* @param g the green component
* @param b the blue component
*/
protected void addLine(int x1, int y1, int x2, int y2, int th, float r,
float g, float b) {
Element line = SVGUtilities.createLine(svgDocument, x1 / 1000f, pageHeight - (y1 / 1000f), x2 / 1000f, pageHeight - (y2 / 1000f));
line.setAttributeNS(null, "style", "stroke-width:" + (Math.abs(th) / 1000f)
+ ";stroke:rgb(" + ((int)(255 * r)) + "," + ((int)(255 * g)) + "," + ((int)(255 * b)) + ")");
currentPageG.appendChild(line);
}

/**
* draw a rectangle
*
* @param x the x position of left edge in millipoints
* @param y the y position of top edge in millipoints
* @param w the width in millipoints
* @param h the height in millipoints
* @param r the red component
* @param g the green component
* @param b the blue component
*/
protected void addRect(int x, int y, int w, int h, float r, float g,
float b) {
Element rect = SVGUtilities.createRect(svgDocument, x / 1000f, pageHeight - (y / 1000f), w / 1000f, h / 1000f);
rect.setAttributeNS(null, "style", "stroke:rgb(" + ((int)(255 * r)) + "," + ((int)(255 * g)) + "," + ((int)(255 * b)) + ")");
currentPageG.appendChild(rect);
}

/**
* draw a filled rectangle
*
* @param x the x position of left edge in millipoints
* @param y the y position of top edge in millipoints
* @param w the width in millipoints
* @param h the height in millipoints
* @param r the red component of edges
* @param g the green component of edges
* @param b the blue component of edges
* @param fr the red component of the fill
* @param fg the green component of the fill
* @param fb the blue component of the fill
*/
protected void addRect(int x, int y, int w, int h, float r, float g,
float b, float fr, float fg, float fb) {
Element rect = SVGUtilities.createRect(svgDocument, x / 1000f, pageHeight - (y / 1000f), w / 1000f, h / 1000f);
rect.setAttributeNS(null, "style", "stroke:rgb(" + ((int)(255 * r)) + "," + ((int)(255 * g)) + "," + ((int)(255 * b)) + ");fill:rgb(" + ((int)(255 * fr)) + "," + ((int)(255 * fg)) + "," + ((int)(255 * fb)) + ")");
currentPageG.appendChild(rect);
}

/**
* draw a filled rectangle in the current color
*
* @param x the x position of left edge in millipoints
* @param y the y position of top edge in millipoints
* @param w the width in millipoints
* @param h the height in millipoints
* @param drawAsOutline true for draw, false for fill
*/
protected void addRect(int x, int y, int w, int h,
boolean drawAsOutline) {
int startx = (x + 500) / 1000;
int starty = pageHeight - ((y + 500) / 1000);
int endx = (x + w + 500) / 1000;
int endy = pageHeight - ((y + h + 500) / 1000);
if (drawAsOutline) {
Element rect = SVGUtilities.createRect(svgDocument, startx, starty, endx - startx, endy - starty);
rect.setAttributeNS(null, "style", "fill:none");
currentPageG.appendChild(rect);
} else {
Element rect = SVGUtilities.createRect(svgDocument, startx, starty, endx - startx, starty - endy);
rect.setAttributeNS(null, "style", "stroke:none");
currentPageG.appendChild(rect);
}
}

protected void addFilledRect(int x, int y, int w, int h,
ColorType col) {
float r = col.red();
float g = col.green();
float b = col.blue();
addRect(x, y, w, h, r, g, b, r, g, b);
}

protected void drawFrame() {
int width = pageWidth;
int height = pageHeight;
Element rect = SVGUtilities.createRect(svgDocument, 0, 0, width, height);
rect.setAttributeNS(null, "style", "fill:none;stroke:black");
currentPageG.appendChild(rect);
}

public void render(Page page, OutputStream stream)
throws IOException {
pageNumber++;
this.render(page);
}

public void render(Page page)
throws IOException {
idReferences = page.getIDReferences();

int lastWidth = pageWidth;
int lastHeight = pageHeight;

pageWidth = (int)((float)page.getWidth() / 1000f + .5);
pageHeight = (int)((float)page.getHeight() / 1000f + .5);

if(lastLink != null && currentPageG != null) {
lastLink.setAttributeNS(null, "xlink:href", "#svgView(viewBox(0, "+ (totalHeight) + ", " + pageWidth + ", " + pageHeight + "))");
currentPageG.appendChild(lastLink);
}

totalHeight += pageHeight;
if(totalWidth < pageWidth) {
totalWidth = pageWidth;
}

currentPageG = SVGUtilities.createG(svgDocument);
currentPageG.setAttributeNS(null, "id", /*title + */"Page-" + pageNumber);
currentPageG.setAttributeNS(null, "style", "font-family:sanserif;font-size:12");
svgRoot.appendChild(currentPageG);

drawFrame();

renderPage(page);

currentPageG.setAttributeNS(null, "transform", "translate(0," + (totalHeight - pageHeight) + ")");

Element lastPageLink = svgDocument.createElementNS(svgNS, "a");
if(lastLink != null) {
lastPageLink.setAttributeNS(null, "xlink:href", "#svgView(viewBox(0, " + (totalHeight - pageHeight - lastHeight) + ", " + lastWidth + ", " + lastHeight + "))");
} else {
lastPageLink.setAttributeNS(null, "xlink:href", "#svgView(viewBox(0, " + (totalHeight - pageHeight) + ", " + pageWidth + ", " + pageHeight + "))");
}
currentPageG.appendChild(lastPageLink);
Element rect = SVGUtilities.createRect(svgDocument, 0, 0, pageWidth / 2, pageHeight);
rect.setAttributeNS(null, "style", "fill:blue;visibility:hidden");
lastPageLink.appendChild(rect);

lastLink = svgDocument.createElementNS(svgNS, "a");
rect = SVGUtilities.createRect(svgDocument, pageWidth / 2, 0, pageWidth / 2, pageHeight);
rect.setAttributeNS(null, "style", "fill:blue;visibility:hidden");
lastLink.appendChild(rect);

/*
* if (page.hasLinks()) {
* ....
* }
*/

}

public void renderPage(Page page) {
BodyAreaContainer body;
AreaContainer before, after;

body = page.getBody();
before = page.getBefore();
after = page.getAfter();

this.currentFontName = "";
this.currentFontSize = 0;

renderBodyAreaContainer(body);

if (before != null) {
renderAreaContainer(before);
}

if (after != null) {
renderAreaContainer(after);
}
}

protected void doFrame(org.apache.fop.layout.Area area) {
int w, h;
int rx = this.currentAreaContainerXPosition;
w = area.getContentWidth();

if (area instanceof BlockArea) {
rx += ((BlockArea)area).getStartIndent();
}

h = area.getContentHeight();
int ry = this.currentYPosition;
ColorType bg = area.getBackgroundColor();

rx = rx - area.getPaddingLeft();
ry = ry + area.getPaddingTop();
w = w + area.getPaddingLeft() + area.getPaddingRight();
h = h + area.getPaddingTop() + area.getPaddingBottom();

// I'm not sure I should have to check for bg being null
// but I do
if ((bg != null) && (bg.alpha() == 0)) {
this.addRect(rx, ry, w, h, bg.red(), bg.green(), bg.blue(),
bg.red(), bg.green(), bg.blue());
}

rx = rx - area.getBorderLeftWidth();
ry = ry + area.getBorderTopWidth();
w = w + area.getBorderLeftWidth() + area.getBorderRightWidth();
h = h + area.getBorderTopWidth() + area.getBorderBottomWidth();

BorderAndPadding bp = area.getBorderAndPadding();
ColorType borderColor;

if (area.getBorderTopWidth() != 0) {
borderColor = bp.getBorderColor(BorderAndPadding.TOP);
addLine(rx, ry, rx + w, ry, area.getBorderTopWidth(),
borderColor.red(), borderColor.green(),
borderColor.blue());
}

if (area.getBorderLeftWidth() != 0) {
borderColor = bp.getBorderColor(BorderAndPadding.LEFT);
addLine(rx, ry, rx, ry - h, area.getBorderLeftWidth(),
borderColor.red(), borderColor.green(),
borderColor.blue());
}

if (area.getBorderRightWidth() != 0) {
borderColor = bp.getBorderColor(BorderAndPadding.RIGHT);
addLine(rx + w, ry, rx + w, ry - h,
area.getBorderRightWidth(), borderColor.red(),
borderColor.green(),
borderColor.blue());
}

if (area.getBorderBottomWidth() != 0) {
borderColor = bp.getBorderColor(BorderAndPadding.BOTTOM);
addLine(rx, ry - h, rx + w, ry - h, area.getBorderBottomWidth(),
borderColor.red(), borderColor.green(),
borderColor.blue());
}
}

protected Rectangle2D getBounds(org.apache.fop.layout.Area a) {
return new Rectangle2D.Double(currentAreaContainerXPosition,
currentYPosition,
a.getAllocationWidth(), a.getHeight());
}

public void setupFontInfo(FontInfo fontInfo) {
// create a temp Image to test font metrics on
BufferedImage fontImage =
new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
org.apache.fop.render.awt.FontSetup.setup(fontInfo, fontImage.createGraphics());
}

public void renderDisplaySpace(DisplaySpace space) {
int d = space.getSize();
this.currentYPosition -= d;
}

public void renderImageArea(ImageArea area) {

int x = currentXPosition + area.getXOffset();
int y = currentYPosition;
int w = area.getContentWidth();
int h = area.getHeight();
this.currentYPosition -= h;

FopImage img = area.getImage();

if (img == null) {
log.error("Error while loading image : area.getImage() is null");

addRect(x, y, w, h, true); // use helper function

} else {
if (img instanceof SVGImage) {
try {
SVGDocument svg = ((SVGImage)img).getSVGDocument();
renderSVGDocument(svg, x / 1000f, pageHeight - y / 1000f);
} catch (FopImageException e) {}

} else {

String urlString = img.getURL();
try {
URL url = new URL(urlString);

ImageIcon icon = new ImageIcon(url);
Image image = icon.getImage();

int startx = (x + 500) / 1000;
int starty = pageHeight - ((y + 500) / 1000);
int endx = (x + w + 500) / 1000;
int endy = pageHeight - ((y + h + 500) / 1000);

// reverse start and end y because h is positive
//graphics.drawImage(image, startx, starty, endx - startx,
// starty - endy, null);

} catch (MalformedURLException mue) {
// cannot normally occur because, if URL is wrong, constructing FopImage
// will already have failed earlier on
}
}
}

this.currentXPosition += area.getContentWidth();
}

public void renderWordArea(WordArea area) {
char ch;
StringBuffer pdf = new StringBuffer();

String name = area.getFontState().getFontFamily();
int size = area.getFontState().getFontSize();
boolean underlined = area.getUnderlined();

float red = area.getRed();
float green = area.getGreen();
float blue = area.getBlue();

if ((!name.equals(this.currentFontName))
|| (size != this.currentFontSize)) {
this.currentFontName = name;
this.currentFontSize = size;
}

if ((red != this.currentRed) || (green != this.currentGreen)
|| (blue != this.currentBlue)) {
this.currentRed = red;
this.currentGreen = green;
this.currentBlue = blue;
}

int rx = this.currentXPosition;
int bl = this.currentYPosition;

String s; // = area.getText();
if (area.getPageNumberID()
!= null) { // this text is a page number, so resolve it
s = idReferences.getPageNumber(area.getPageNumberID());
if (s == null) {
s = "";
}
} else {
s = area.getText();
}

if (saveColor != null) {
if (saveColor.getRed() != red || saveColor.getGreen() != green
|| saveColor.getBlue() != blue) {
saveColor = new Color(red, green, blue);
}
} else {
saveColor = new Color(red, green, blue);
}

Element text = SVGUtilities.createText(svgDocument, rx / 1000f, pageHeight - bl / 1000f, s);
String st = null;
if(!"sans-serif".equals(this.currentFontName)) {
st = "font-family:" + this.currentFontName;
}
if(this.currentFontSize != 12000) {
if(st == null) {
st = "";
} else {
st += ";";
}
st += "font-size:" + (this.currentFontSize / 1000f);
}
if(red != 0 || green != 0 || blue != 0) {
if(st == null) {
st = "";
} else {
st += ";";
}
st += "fill:rgb(" + ((int)(255 * red)) + "," + ((int)(255 * green)) + "," + ((int)(255 * blue)) + ")";
}
String fweight = area.getFontState().getFontWeight();
if(!"normal".equals(fweight)) {
if(st == null) {
st = "";
} else {
st += ";";
}
st += "font-weight:" + fweight;
}
String fstyle = area.getFontState().getFontStyle();
if(!"normal".equals(fstyle)) {
if(st == null) {
st = "";
} else {
st += ";";
}
st += "font-style:" + fstyle;
}

if(st != null) {
text.setAttributeNS(null, "style", st);
}
currentPageG.appendChild(text);

this.currentXPosition += area.getContentWidth();
}

public void renderInlineSpace(InlineSpace space) {
this.currentXPosition += space.getSize();
}

/**
*
* @param area area to render
*/
public void renderLeaderArea(LeaderArea area) {

int rx = this.currentXPosition;
int ry = this.currentYPosition;
int w = area.getLeaderLength();
int h = area.getHeight();
int th = area.getRuleThickness();
int st = area.getRuleStyle(); // not used at the moment
float r = area.getRed();
float g = area.getGreen();
float b = area.getBlue();

//graphics.setColor(new Color(r, g, b));

addRect(rx, ry, w, th, false);

this.currentXPosition += area.getContentWidth();
}

public void renderSVGArea(SVGArea area) {

float x = this.currentXPosition / 1000f;
float y = pageHeight - this.currentYPosition / 1000f;
int w = area.getContentWidth();
int h = area.getHeight();

Document doc = area.getSVGDocument();
renderSVGDocument(doc, x, y);
this.currentXPosition += area.getContentWidth();
}

protected void renderSVGDocument(Document doc, float x, float y) {
SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
Element view = svgDocument.createElementNS(svgNS, "svg");
Node newsvg = svgDocument.importNode(svg, true);
//view.setAttributeNS(null, "viewBox", "0 0 ");
view.setAttributeNS(null, "x", "" + x);
view.setAttributeNS(null, "y", "" + y);

// this fixes a problem where the xmlns is repeated sometimes
Element ele = (Element)newsvg;
ele.setAttributeNS(XMLSupport.XMLNS_NAMESPACE_URI, "xmlns", svgNS);
if(ele.hasAttributeNS(null, "xmlns")) {
ele.removeAttributeNS(null, "xmlns");
}

view.appendChild(newsvg);
currentPageG.appendChild(view);
}

public void setProducer(String producer) {
// defined in Renderer Interface
}

public static Color colorType2Color(ColorType ct) {
if (ct == null) {
return null;
}
return new Color(ct.red(), ct.green(), ct.blue());
}

public void renderForeignObjectArea(ForeignObjectArea area) {
area.getObject().render(this);
}

public void startRenderer(OutputStream outputStream)
throws IOException {
DOMImplementation impl = SVGDOMImplementation.getDOMImplementation();
svgDocument = impl.createDocument(svgNS, "svg", null);

svgRoot = svgDocument.getDocumentElement();
}

public void stopRenderer(OutputStream outputStream)
throws IOException {
svgRoot.setAttributeNS(null, "width", "" + totalWidth);
svgRoot.setAttributeNS(null, "height", "" + totalHeight);
//svgRoot.setAttributeNS(null, "viewBox", "0 0 " + pageWidth + " " + pageHeight);
SVGTranscoder svgT = new SVGTranscoder();
TranscoderInput input = new TranscoderInput(svgDocument);
TranscoderOutput output = new TranscoderOutput(new OutputStreamWriter(outputStream));
try {
svgT.transcode(input, output);
} catch(TranscoderException e) {
log.error("could not write svg file :" + e.getMessage(), e);
}
outputStream.flush();

svgDocument = null;
svgRoot = null;
currentPageG = null;
lastLink = null;

totalWidth = 0;
totalHeight = 0;

pageNumber = 0;
}

}

Loading…
Cancel
Save