aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremias Maerki <jeremias@apache.org>2005-03-08 11:47:55 +0000
committerJeremias Maerki <jeremias@apache.org>2005-03-08 11:47:55 +0000
commitc230af051c6cab4460cee8b8d7d1b985fac93c27 (patch)
tree8e2aebc18fda280aacd1902264b1f1aec372acbe
parent2b60d0cdfdf3208e3d5650535b0d6a54129f1950 (diff)
downloadxmlgraphics-fop-c230af051c6cab4460cee8b8d7d1b985fac93c27.tar.gz
xmlgraphics-fop-c230af051c6cab4460cee8b8d7d1b985fac93c27.zip
Resurrected AWT/Java2D renderer
Submitted by: Renaud Richardet <renaud.richardet.at.gmail.com> Modifications to the patch: - correct copyright years - warning message when EPS files are used as images - removed/commented hard-coded path names, should be improved by loading the image from an InputStream obtained throug the user agent - Enabled loading images from the bitmap array coming from FopImage. There are hard-coded value which need to be fixed but they show the approach. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@198472 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--src/java/org/apache/fop/render/awt/AWTGraphicsState.java332
-rw-r--r--src/java/org/apache/fop/render/awt/AWTRenderer.java1121
-rw-r--r--src/java/org/apache/fop/render/awt/RendererState.java145
-rw-r--r--src/java/org/apache/fop/render/awt/viewer/PreviewDialog.java61
4 files changed, 1442 insertions, 217 deletions
diff --git a/src/java/org/apache/fop/render/awt/AWTGraphicsState.java b/src/java/org/apache/fop/render/awt/AWTGraphicsState.java
new file mode 100644
index 000000000..7dae50b78
--- /dev/null
+++ b/src/java/org/apache/fop/render/awt/AWTGraphicsState.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.awt;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.GeneralPath;
+import java.util.List;
+
+import org.apache.fop.datatypes.ColorType;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fonts.FontInfo;
+
+/**
+ * Keeps information about the current state of the Graphics2D currentGraphics.
+ * It is also used as a stack to hold a graphics context.
+ * <p>
+ * The graphics context is updated with the updateXXX() methods.
+ */
+public class AWTGraphicsState implements Constants, RendererState {
+
+ /** Holds the datas of the current state */
+ private Graphics2D currentGraphics;
+
+ private BasicStroke currentStroke;
+
+ private float currentStrokeWidth;
+
+ private int currentStrokeStyle;
+
+ private List stateStack = new java.util.ArrayList();
+
+ /** Font configuration, passed from AWTRenderer */
+ private FontInfo fontInfo;
+
+ /** State for storing graphics state. */
+ public AWTGraphicsState(Graphics2D graphics, FontInfo fontInfo) {
+ this.fontInfo = fontInfo;
+ this.currentGraphics = graphics;
+ }
+
+ /**
+ * @return the currently valid state
+ */
+ public Graphics2D getGraph() {
+ return currentGraphics;
+ }
+
+ /** @see org.apache.fop.render.awt.RendererState#push() */
+ public void push() {
+ Graphics2D tmpGraphics = (Graphics2D) currentGraphics.create();
+ stateStack.add(tmpGraphics);
+ }
+
+ /** @see org.apache.fop.render.awt.RendererState#pop() */
+ public Graphics2D pop() {
+ if (getStackLevel() > 0) {
+ Graphics2D popped = (Graphics2D) stateStack.remove(stateStack
+ .size() - 1);
+
+ currentGraphics = popped;
+ return popped;
+ } else {
+ return null;
+ }
+ }
+
+ /** @see org.apache.fop.render.awt.RendererState#getStackLevel() */
+ public int getStackLevel() {
+ return stateStack.size();
+ }
+
+ /**
+ * Restore the state to a particular level. this can be used to restore to a
+ * known level without making multiple pop calls.
+ *
+ * @param stack the level to restore to
+ */
+ /*
+ * public void restoreLevel(int stack) { int pos = stack; while
+ * (stateStack.size() > pos + 1) { stateStack.remove(stateStack.size() - 1); }
+ * if (stateStack.size() > pos) { pop(); } }
+ */
+
+ /**
+ * Set the current background color. Check if the background color will
+ * change and then set the new color.
+ *
+ * @param col the new color as a java.awt.Color
+ * @return true if the background color has changed
+ */
+ public boolean updateColor(Color col) {
+ if (!col.equals(getGraph().getColor())) {
+ getGraph().setColor(col);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Converts a ColorType to a java.awt.Color (sRGB).
+ *
+ * @param col the color as a org.apache.fop.datatypes.ColorType
+ * @return the converted color as a java.awt.Color
+ */
+ public Color toColor(ColorType col) {
+ return new Color(col.getRed(), col.getGreen(), col.getBlue());
+ }
+
+ /**
+ * @see org.apache.fop.render.awt.RendererState#updateColor(org.apache.fop.datatypes.ColorType,
+ * boolean, java.lang.StringBuffer)
+ */
+ public boolean updateColor(ColorType col, boolean fill, StringBuffer pdf) {
+ if (col == null) {
+ return false;
+ }
+ Color newCol = toColor(col);
+ return updateColor(newCol);
+ }
+
+ /**
+ * Update the current Color
+ * @param col the ColorType
+ */
+ public void updateColor(ColorType col) {
+ if (col == null) {
+ return;
+ }
+ Color newCol = toColor(col);
+ updateColor(newCol);
+ }
+
+ /**
+ * @return the current java.awt.Color
+ */
+ public java.awt.Color getColor() {
+ return currentGraphics.getColor();
+ }
+
+ /**
+ * @see org.apache.fop.render.awt.RendererState#updateFont(java.lang.String,
+ * int, java.lang.StringBuffer)
+ */
+ public boolean updateFont(String name, int size, StringBuffer pdf) {
+
+ boolean updateName = (!name.equals(getGraph().getFont().getFontName()));
+ boolean updateSize = (size != (getGraph().getFont().getSize()));
+
+ if (updateName || updateSize) {
+ // the font name and/or the font size have changed
+ FontMetricsMapper mapper = (FontMetricsMapper) fontInfo
+ .getMetricsFor(name);
+ java.awt.Font font = mapper.getFont(size);
+
+ currentGraphics.setFont(font);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @return the current java.awt.Font
+ */
+ public java.awt.Font getFont() {
+ return currentGraphics.getFont();
+ }
+
+ /**
+ * @see org.apache.fop.render.awt.RendererState#updateStroke(float, int)
+ */
+ public boolean updateStroke(float width, int style) {
+
+ boolean update = false;
+
+ // only update if necessary
+ if ((width != currentStrokeWidth) || (style != currentStrokeStyle)) {
+
+ update = true;
+
+ switch (style) {
+ case EN_DOTTED:
+
+ currentStroke = new BasicStroke(width, BasicStroke.CAP_BUTT,
+ BasicStroke.JOIN_BEVEL, 0f, new float[] { 2f }, 0f);
+ currentGraphics.setStroke(currentStroke);
+
+ currentStrokeWidth = width;
+ currentStrokeStyle = style;
+
+ break;
+
+ case EN_DASHED:
+
+ currentStroke = new BasicStroke(width, BasicStroke.CAP_BUTT,
+ BasicStroke.JOIN_BEVEL, 0f, new float[] { 8f, 2f }, 0f);
+ currentGraphics.setStroke(currentStroke);
+
+ currentStrokeWidth = width;
+ currentStrokeStyle = style;
+
+ break;
+
+ default: // EN_SOLID:
+
+ currentStroke = new BasicStroke(width);
+ currentGraphics.setStroke(currentStroke);
+
+ currentStrokeWidth = width;
+ currentStrokeStyle = style;
+
+ break;
+ }
+ }
+
+ return update;
+ }
+
+ public BasicStroke getStroke() {
+ return (BasicStroke) currentGraphics.getStroke();
+ }
+
+ /** @see org.apache.fop.render.awt.RendererState#updatePaint(java.awt.Paint) */
+ public boolean updatePaint(Paint p) {
+ if (getGraph().getPaint() == null) {
+ if (p != null) {
+ getGraph().setPaint(p);
+ return true;
+ }
+ } else if (p.equals(getGraph().getPaint())) {
+ getGraph().setPaint(p);
+ return true;
+ }
+ return false;
+ }
+
+ /** @see org.apache.fop.render.awt.RendererState#checkClip(java.awt.Shape) */
+ // TODO implement and test
+ public boolean checkClip(Shape cl) {
+ if (getGraph().getClip() == null) {
+ if (cl != null) {
+ return true;
+ }
+ } else if (cl.equals(getGraph().getClip())) {
+ return true;
+ }
+ // TODO check for clips that are larger than the current
+ return false;
+ }
+
+ /**
+ * @see org.apache.fop.render.awt.RendererState#updateClip(java.awt.Shape)
+ */
+ public boolean updateClip(Shape cl) {
+ if (getGraph().getClip() != null) {
+ Area newClip = new Area(getGraph().getClip());
+ newClip.intersect(new Area(cl));
+ getGraph().setClip(new GeneralPath(newClip));
+ } else {
+ getGraph().setClip(cl);
+ }
+ return true; // TODO only update if necessary
+ }
+
+ /**
+ * @see org.apache.fop.render.awt.RendererState#checkTransform(java.awt.geom.AffineTransform)
+ */
+ public boolean checkTransform(AffineTransform tf) {
+ return !tf.equals(getGraph().getTransform());
+ }
+
+ /**
+ * @see org.apache.fop.render.awt.RendererState#setTransform(java.awt.geom.AffineTransform)
+ */
+ public void setTransform(AffineTransform tf) {
+ getGraph().setTransform(tf);
+ }
+
+ /**
+ * @see org.apache.fop.render.awt.RendererState#transform(java.awt.geom.AffineTransform)
+ */
+ public void transform(AffineTransform tf) {
+ getGraph().transform(tf);
+ }
+
+ /**
+ * @see org.apache.fop.render.awt.RendererState#getTransform()
+ */
+ public AffineTransform getTransform() {
+ /*
+ * AffineTransform tf; AffineTransform at = new AffineTransform(); for
+ * (Iterator iter = stateStack.iterator(); iter.hasNext();) { Data d =
+ * (Data) iter.next(); tf = d.transform; at.concatenate(tf); }
+ * at.concatenate(getCurrentGraphics().transform);
+ *
+ * return at;
+ */
+ return getGraph().getTransform();
+ }
+
+ /** a verbose description of the current state */
+ public String toString() {
+ String s = "AWTGraphicsState " + currentGraphics.toString()
+ + ", Stroke (width: " + currentStrokeWidth + " style: "
+ + currentStrokeStyle + "), " + getTransform()
+ + ", StackLevel: " + getStackLevel();
+ return s;
+ }
+}
diff --git a/src/java/org/apache/fop/render/awt/AWTRenderer.java b/src/java/org/apache/fop/render/awt/AWTRenderer.java
index 3d87c43b8..ecc8e28cf 100644
--- a/src/java/org/apache/fop/render/awt/AWTRenderer.java
+++ b/src/java/org/apache/fop/render/awt/AWTRenderer.java
@@ -1,6 +1,6 @@
/*
- * Copyright 1999-2004 The Apache Software Foundation.
- *
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
* Licensed 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
@@ -27,78 +27,154 @@ package org.apache.fop.render.awt;
// Java
import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Toolkit;
+import java.awt.color.ColorSpace;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
+import java.awt.geom.Line2D;
+import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.MemoryImageSource;
+import java.awt.image.PixelInterleavedSampleModel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
import java.awt.print.PageFormat;
import java.awt.print.Pageable;
import java.awt.print.Printable;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Vector;
-import org.apache.fop.fonts.FontInfo;
+import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.GVTBuilder;
+import org.apache.batik.bridge.ViewBox;
+import org.apache.batik.gvt.GraphicsNode;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.area.Area;
+import org.apache.fop.area.Block;
+import org.apache.fop.area.BlockViewport;
+import org.apache.fop.area.CTM;
import org.apache.fop.area.Page;
import org.apache.fop.area.PageViewport;
-import org.apache.fop.area.RegionViewport;
import org.apache.fop.area.Trait;
+import org.apache.fop.area.inline.Character;
+import org.apache.fop.area.inline.ForeignObject;
+import org.apache.fop.area.inline.Image;
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.datatypes.ColorType;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.properties.ColorTypeProperty;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontMetrics;
import org.apache.fop.image.FopImage;
import org.apache.fop.image.ImageFactory;
+import org.apache.fop.image.XMLImage;
import org.apache.fop.render.AbstractRenderer;
-import org.apache.fop.traits.BorderProps;
-import org.apache.fop.render.awt.FontMetricsMapper;
+import org.apache.fop.render.RendererContext;
import org.apache.fop.render.awt.viewer.PreviewDialog;
import org.apache.fop.render.awt.viewer.Translator;
+import org.apache.fop.render.pdf.CTMHelper;
+import org.apache.fop.svg.SVGUserAgent;
+import org.apache.fop.traits.BorderProps;
+import org.w3c.dom.Document;
+import org.w3c.dom.svg.SVGDocument;
+import org.w3c.dom.svg.SVGSVGElement;
/**
- * This is FOP's AWT renderer.
+ * The <code>Java2DRenderer</code> class provides the abstract technical
+ * foundation for all rendering with the Java2D API. Renderers like
+ * <code>AWTRenderer</code> subclass it and provide the concrete output paths.
+ * <p>
+ * A lot of the logic is performed by <code>AbstractRenderer</code>. The
+ * class-variables <code>currentIPPosition</code> and
+ * <code>currentBPPosition</code> hold the position of the currently rendered
+ * area.
+ * <p>
+ * <code>AWTGraphicsState state</code> holds the <code>Graphics2D</code>,
+ * which is used along the whole rendering. <code>state</code> also acts as a
+ * stack (<code>state.push()</code> and <code>state.pop()</code>).
+ * <p>
+ * The rendering process is basically always the same:
+ * <p>
+ * <code>void renderXXXXX(Area area) {
+ * //calculate the currentPosition
+ * state.updateFont(name, size, null);
+ * state.updateColor(ct, false, null);
+ * state.getGraph.draw(new Shape(args));
+ * }</code>
+ *
*/
-public class AWTRenderer extends AbstractRenderer implements Printable, Pageable {
+public class AWTRenderer extends AbstractRenderer implements Printable,
+ Pageable {
- /** The MIME type for PostScript */
+ /** The MIME type for AWT-Rendering */
public static final String MIME_TYPE = "application/awt";
protected double scaleFactor = 100.0;
+
protected int pageNumber = 0;
+
private int pageWidth = 0;
+
private int pageHeight = 0;
+
private Vector pageViewportList = new java.util.Vector();
+
private Vector pageList = new java.util.Vector();
+
private Vector bufferedImageList = new java.util.Vector();
- private BufferedImage currentPageImage = null;
-
+
+ protected BufferedImage currentPageImage = null;
+
+ protected boolean antialiasing = true;
+
+ protected boolean qualityRendering = true;
+
+ /** The current state, holds a Graphics2D and its context */
+ protected AWTGraphicsState state;
+
+ /** a Line2D.Float used to draw text decorations and leaders */
+ protected Line2D.Float line = new Line2D.Float();
+
/** Font configuration */
protected FontInfo fontInfo;
- /**
- * The resource bundle used for AWT messages.
- */
+ /** The resource bundle used for AWT messages. */
protected Translator translator = null;
private Map fontNames = new java.util.Hashtable();
+
private Map fontStyles = new java.util.Hashtable();
- private Color saveColor = null;
/**
- * The preview dialog frame used for display of the documents.
- * Also used as the AWT Component for FontSetup in generating
- * valid font measures.
+ * The preview dialog frame used for display of the documents. Also used as
+ * the AWT Component for FontSetup in generating valid font measures.
*/
protected PreviewDialog frame;
+ /** Flag for visual-debugging */
+ public boolean debug = false;
+
public AWTRenderer() {
translator = new Translator();
}
@@ -124,11 +200,16 @@ public class AWTRenderer extends AbstractRenderer implements Printable, Pageable
return translator;
}
+ /** @see org.apache.fop.render.AbstractRenderer */
+ public String getMimeType() {
+ return MIME_TYPE;
+ }
+
public void setupFontInfo(FontInfo inFontInfo) {
// create a temp Image to test font metrics on
fontInfo = inFontInfo;
- BufferedImage fontImage =
- new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
+ BufferedImage fontImage = new BufferedImage(100, 100,
+ BufferedImage.TYPE_INT_RGB);
FontSetup.setup(fontInfo, fontImage.createGraphics());
}
@@ -148,19 +229,19 @@ public class AWTRenderer extends AbstractRenderer implements Printable, Pageable
return scaleFactor;
}
- public void startRenderer(OutputStream out)
- throws IOException {
+ public void startRenderer(OutputStream out) throws IOException {
// empty pageViewportList, in case of a reload from PreviewDialog
pageViewportList.removeAllElements();
pageList.removeAllElements();
bufferedImageList.removeAllElements();
- System.out.println("\nRegion Types: 0-Before/Top, 1-Start/Left, 2-Body, 3-End/Right, 4-After/Bottom");
+ System.out.println("\nRegion Types: 0-Before/Top, 1-Start/Left,"
+ + " 2-Body, 3-End/Right, 4-After/Bottom");
}
- public void stopRenderer()
- throws IOException {
+ public void stopRenderer() throws IOException {
frame.setStatus(translator.getString("Status.Show"));
frame.showPage();
+ // TODO set all vars to null for gc
}
// Printable Interface
@@ -188,7 +269,7 @@ public class AWTRenderer extends AbstractRenderer implements Printable, Pageable
}
});
- //Centers the window
+ // Centers the window
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = frame.getSize();
if (frameSize.height > screenSize.height) {
@@ -198,52 +279,68 @@ public class AWTRenderer extends AbstractRenderer implements Printable, Pageable
frameSize.width = screenSize.width;
}
frame.setLocation((screenSize.width - frameSize.width) / 2,
- (screenSize.height - frameSize.height) / 2);
+ (screenSize.height - frameSize.height) / 2);
frame.setVisible(true);
frame.setStatus(translator.getString("Status.Build.FO.tree"));
return frame;
}
- /** This method override only stores the PageViewport in a vector.
- * No actual rendering performed -- this is done by getPageImage(pageNum) instead.
- * @param pageViewport the <code>PageViewport</code> object supplied by the Area Tree
- * @see org.apache.fop.render.Renderer
- */
- public void renderPage(PageViewport pageViewport) throws IOException, FOPException {
+ /**
+ * This method override only stores the PageViewport in a vector. No actual
+ * rendering performed -- this is done by getPageImage(pageNum) instead.
+ *
+ * @param pageViewport the <code>PageViewport</code> object supplied by
+ * the Area Tree
+ * @see org.apache.fop.render.Renderer
+ */
+ public void renderPage(PageViewport pageViewport) throws IOException,
+ FOPException {
pageViewportList.add(pageViewport);
pageList.add(pageViewport.getPage().clone());
- bufferedImageList.add(getPageImage(pageViewport));
+ bufferedImageList
+ .add(getPageImage(pageViewport, pageViewport.getPage()));
}
public BufferedImage getBufferedPageImage(int pageNum) throws FOPException {
return (BufferedImage) bufferedImageList.get(pageNum);
}
- /** Generates a desired page from the renderer's page viewport vector.
+ /**
+ * Generates a desired page from the renderer's page viewport vector.
+ *
* @param pageNum the 0-based page number to generate
- * @return the <code>java.awt.image.BufferedImage</code> corresponding to the page
- * @throws FOPException in case of an out-of-range page number requested
- */
- public BufferedImage getPageImage(PageViewport pageViewport) throws FOPException {
- Page page = pageViewport.getPage();
+ * @return the <code>java.awt.image.BufferedImage</code> corresponding to
+ * the page
+ * @throws FOPException in case of an out-of-range page number requested
+ */
+ public BufferedImage getPageImage(PageViewport pageViewport, Page page)
+ throws FOPException {
Rectangle2D bounds = pageViewport.getViewArea();
- pageWidth = (int) Math.round(bounds.getWidth() / 1000f );
- pageHeight = (int) Math.round(bounds.getHeight() / 1000f );
-/*
- System.out.println("(Page) X, Y, Width, Height: " + bounds.getX()
- + " " + bounds.getY()
- + " " + bounds.getWidth()
- + " " + bounds.getHeight());
-*/
- currentPageImage =
- new BufferedImage((int)((pageWidth * (int)scaleFactor) / 100),
- (int)((pageHeight * (int)scaleFactor) / 100),
- BufferedImage.TYPE_INT_RGB);
+ pageWidth = (int) Math.round(bounds.getWidth() / 1000f);
+ pageHeight = (int) Math.round(bounds.getHeight() / 1000f);
+
+ getLogger().info(
+ "Rendering Page " + pageViewport.getPageNumberString()
+ + " (pageWidth " + pageWidth + ", pageHeight "
+ + pageHeight + ")");
+
+ currentPageImage = new BufferedImage(
+ (int) ((pageWidth * (int) scaleFactor) / 100),
+ (int) ((pageHeight * (int) scaleFactor) / 100),
+ BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = currentPageImage.createGraphics();
- graphics.setRenderingHint (RenderingHints.KEY_FRACTIONALMETRICS,
- RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+ graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
+ RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+ if (antialiasing) {
+ graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+ RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+ }
+ if (qualityRendering) {
+ graphics.setRenderingHint(RenderingHints.KEY_RENDERING,
+ RenderingHints.VALUE_RENDER_QUALITY);
+ }
// transform page based on scale factor supplied
AffineTransform at = graphics.getTransform();
@@ -260,98 +357,243 @@ public class AWTRenderer extends AbstractRenderer implements Printable, Pageable
graphics.drawLine(0, pageHeight + 2, pageWidth + 2, pageHeight + 2);
graphics.drawLine(1, pageHeight + 3, pageWidth + 3, pageHeight + 3);
+ state = new AWTGraphicsState(graphics, this.fontInfo);
+
+ // reset the current Positions
+ currentBPPosition = 0;
+ currentIPPosition = 0;
+
+ // this toggles the rendering of all areas
renderPageAreas(page);
return currentPageImage;
}
- /** Generates a desired page from the renderer's page viewport vector.
+ /**
+ * Generates a desired page from the renderer's page viewport vector.
+ *
* @param pageNum the 0-based page number to generate
- * @return the <code>java.awt.image.BufferedImage</code> corresponding to the page
- * @throws FOPException in case of an out-of-range page number requested
- */
+ * @return the <code>java.awt.image.BufferedImage</code> corresponding to
+ * the page
+ * @throws FOPException in case of an out-of-range page number requested
+ */
public BufferedImage getPageImage(int pageNum) throws FOPException {
if (pageNum < 0 || pageNum >= pageViewportList.size()) {
throw new FOPException("out-of-range page number (" + pageNum
- + ") requested; only " + pageViewportList.size()
- + " page(s) available.");
+ + ") requested; only " + pageViewportList.size()
+ + " page(s) available.");
}
- PageViewport pageViewport = (PageViewport) pageViewportList.get(pageNum);
+ PageViewport pageViewport = (PageViewport) pageViewportList
+ .get(pageNum);
Page page = (Page) pageList.get(pageNum);
+ return getPageImage(pageViewport, page);
+ }
- Rectangle2D bounds = pageViewport.getViewArea();
- pageWidth = (int) Math.round(bounds.getWidth() / 1000f );
- pageHeight = (int) Math.round(bounds.getHeight() / 1000f );
-/*
- System.out.println("(Page) X, Y, Width, Height: " + bounds.getX()
- + " " + bounds.getY()
- + " " + bounds.getWidth()
- + " " + bounds.getHeight());
-*/
- currentPageImage =
- new BufferedImage((int)((pageWidth * (int)scaleFactor) / 100),
- (int)((pageHeight * (int)scaleFactor) / 100),
- BufferedImage.TYPE_INT_RGB);
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM)
+ */
+ protected void startVParea(CTM ctm) {
- Graphics2D graphics = currentPageImage.createGraphics();
- graphics.setRenderingHint (RenderingHints.KEY_FRACTIONALMETRICS,
- RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+ // push (and save) the current graphics state
+ state.push();
- // transform page based on scale factor supplied
- AffineTransform at = graphics.getTransform();
- at.scale(scaleFactor / 100.0, scaleFactor / 100.0);
- graphics.setTransform(at);
+ // Set the given CTM in the graphics state
+ state.setTransform(new AffineTransform(CTMHelper.toPDFArray(ctm)));
- // draw page frame
- graphics.setColor(Color.white);
- graphics.fillRect(0, 0, pageWidth, pageHeight);
- graphics.setColor(Color.black);
- graphics.drawRect(-1, -1, pageWidth + 2, pageHeight + 2);
- graphics.drawLine(pageWidth + 2, 0, pageWidth + 2, pageHeight + 2);
- graphics.drawLine(pageWidth + 3, 1, pageWidth + 3, pageHeight + 3);
- graphics.drawLine(0, pageHeight + 2, pageWidth + 2, pageHeight + 2);
- graphics.drawLine(1, pageHeight + 3, pageWidth + 3, pageHeight + 3);
+ // TODO Set clip?
+ }
- renderPageAreas(page);
- return currentPageImage;
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#endVParea()
+ */
+ protected void endVParea() {
+ state.pop();
}
/**
- * Handle the traits for a region
- * This is used to draw the traits for the given page region.
- * (See Sect. 6.4.1.2 of XSL-FO spec.)
- * @param region the RegionViewport whose region is to be drawn
+ * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport,
+ * List)
*/
- protected void handleRegionTraits(RegionViewport region) {
- Rectangle2D viewArea = region.getViewArea();
-
- int startX = (int) Math.round((viewArea.getX() / 1000f)
- * (scaleFactor / 100f));
- int startY = (int) Math.round((viewArea.getY() / 1000f)
- * (scaleFactor / 100f));
- // for rounding to work correctly, need to take into account
- // fractional portion of X and Y.
- int width = (int) Math.round(((viewArea.getX() + viewArea.getWidth()) / 1000f)
- * (scaleFactor / 100f)) - startX;
- int height = (int) Math.round(((viewArea.getY() + viewArea.getHeight()) / 1000f)
- * (scaleFactor / 100f)) - startY;
-
- if (region.getRegion() != null) {
- System.out.print("\nRegion type = " + region.getRegion().getRegionClass());
+ protected void renderBlockViewport(BlockViewport bv, List children) {
+ // clip and position viewport if necessary
+
+ // save positions
+ int saveIP = currentIPPosition;
+ int saveBP = currentBPPosition;
+
+ CTM ctm = bv.getCTM();
+ int borderPaddingStart = bv.getBorderAndPaddingWidthStart();
+ int borderPaddingBefore = bv.getBorderAndPaddingWidthBefore();
+ float x, y;
+ x = (float) (bv.getXOffset() + containingIPPosition) / 1000f;
+ y = (float) (bv.getYOffset() + containingBPPosition) / 1000f;
+
+ if (bv.getPositioning() == Block.ABSOLUTE
+ || bv.getPositioning() == Block.FIXED) {
+ // TODO not tested yet
+ // For FIXED, we need to break out of the current viewports to the
+ // one established by the page. We save the state stack for
+ // restoration
+ // after the block-container has been painted. See below.
+ List breakOutList = null;
+ if (bv.getPositioning() == Block.FIXED) {
+ getLogger().debug("Block.FIXED --> break out");
+ breakOutList = new java.util.ArrayList();
+ Graphics2D graph;
+ while (true) {
+ graph = state.getGraph();
+ if (state.pop() == null) {
+ break;
+ }
+ breakOutList.add(0, graph); // Insert because of
+ // stack-popping
+ getLogger().debug("Adding to break out list: " + graph);
+ }
+ }
+
+ CTM tempctm = new CTM(containingIPPosition, containingBPPosition);
+ ctm = tempctm.multiply(ctm);
+
+ // This is the content-rect
+ float width = (float) bv.getIPD() / 1000f;
+ float height = (float) bv.getBPD() / 1000f;
+
+ // Adjust for spaces (from margin or indirectly by start-indent etc.
+ Integer spaceStart = (Integer) bv.getTrait(Trait.SPACE_START);
+ if (spaceStart != null) {
+ x += spaceStart.floatValue() / 1000;
+ }
+ Integer spaceBefore = (Integer) bv.getTrait(Trait.SPACE_BEFORE);
+ if (spaceBefore != null) {
+ y += spaceBefore.floatValue() / 1000;
+ }
+
+ float bpwidth = (borderPaddingStart + bv
+ .getBorderAndPaddingWidthEnd()) / 1000f;
+ float bpheight = (borderPaddingBefore + bv
+ .getBorderAndPaddingWidthAfter()) / 1000f;
+
+ drawBackAndBorders(bv, x, y, width + bpwidth, height + bpheight);
+
+ // Now adjust for border/padding
+ x += borderPaddingStart / 1000f;
+ y += borderPaddingBefore / 1000f;
+
+ if (bv.getClip()) {
+ // saves the graphics state in a stack
+ state.push();
+
+ clip(x, y, width, height);
+ }
+
+ startVParea(ctm);
+
+ renderBlocks(bv, children);
+ endVParea();
+
+ if (bv.getClip()) {
+ // restores the last graphics state from the stack
+ state.pop();
+ }
+
+ // clip if necessary
+
+ if (breakOutList != null) {
+ getLogger().debug(
+ "Block.FIXED --> restoring context after break-out");
+ Graphics2D graph;
+ Iterator i = breakOutList.iterator();
+ while (i.hasNext()) {
+ graph = (Graphics2D) i.next();
+ getLogger().debug("Restoring: " + graph);
+ state.push();
+ }
+ }
+
+ currentIPPosition = saveIP;
+ currentBPPosition = saveBP;
+
+ } else { // orientation = Block.STACK or RELATIVE
+
+ Integer spaceBefore = (Integer) bv.getTrait(Trait.SPACE_BEFORE);
+ if (spaceBefore != null) {
+ currentBPPosition += spaceBefore.intValue();
+ }
+
+ // borders and background in the old coordinate system
+ handleBlockTraits(bv);
+
+ CTM tempctm = new CTM(containingIPPosition, currentBPPosition
+ + containingBPPosition);
+ ctm = tempctm.multiply(ctm);
+
+ // Now adjust for border/padding
+ x += borderPaddingStart / 1000f;
+ y += borderPaddingBefore / 1000f;
+
+ // clip if necessary
+ if (bv.getClip()) {
+ // saves the graphics state in a stack
+ state.push();
+ float width = (float) bv.getIPD() / 1000f;
+ float height = (float) bv.getBPD() / 1000f;
+ clip(x, y, width, height);
+ }
+
+ if (ctm != null) {
+ startVParea(ctm);
+ }
+ renderBlocks(bv, children);
+ if (ctm != null) {
+ endVParea();
+ }
+
+ if (bv.getClip()) {
+ // restores the last graphics state from the stack
+ state.pop();
+ }
+
+ currentIPPosition = saveIP;
+ currentBPPosition = saveBP;
+
+ // Adjust BP position (alloc BPD + spaces)
+ if (spaceBefore != null) {
+ currentBPPosition += spaceBefore.intValue();
+ }
+ currentBPPosition += (int) (bv.getAllocBPD());
+ Integer spaceAfter = (Integer) bv.getTrait(Trait.SPACE_AFTER);
+ if (spaceAfter != null) {
+ currentBPPosition += spaceAfter.intValue();
+ }
}
+ }
- System.out.println(" X, Width, Y, Height: " + startX
- + " " + width
- + " " + startY
- + " " + height
- );
+ /**
+ * Clip an area.
+ */
+ protected void clip() {
+ // TODO via AWTGraphicsState.updateClip();
+ // currentStream.add("W\n");
+ // currentStream.add("n\n");
+ }
- drawBackAndBorders(region, startX, startY, width, height);
+ /**
+ * Clip an area. write a clipping operation given coordinates in the current
+ * transform.
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width of the area
+ * @param height the height of the area
+ */
+ protected void clip(float x, float y, float width, float height) {
+ // TODO via AWTGraphicsState.updateClip();
+ // currentStream.add(x + " " + y + " " + width + " " + height + "
+ // re ");
+ clip();
}
/**
- * Draw the background and borders.
- * This draws the background and border traits for an area given
- * the position.
+ * Draw the background and borders. This draws the background and border
+ * traits for an area given the position.
*
* @param block the area to get the traits from
* @param startx the start x position
@@ -359,112 +601,597 @@ public class AWTRenderer extends AbstractRenderer implements Printable, Pageable
* @param width the width of the area
* @param height the height of the area
*/
- protected void drawBackAndBorders(Area block,
- int startx, int starty,
- int width, int height) {
+ protected void drawBackAndBorders(Area area, float startx, float starty,
+ float width, float height) {
- // draw background then border
- Graphics2D graphics = currentPageImage.createGraphics();
+ if (debug) { // TODO implement visual-debugging as standalone
+ // Renderer
+ debugBackAndBorders(area, startx, starty, width, height);
+ }
+
+ BorderProps bpsBefore = (BorderProps) area
+ .getTrait(Trait.BORDER_BEFORE);
+ BorderProps bpsAfter = (BorderProps) area.getTrait(Trait.BORDER_AFTER);
+ BorderProps bpsStart = (BorderProps) area.getTrait(Trait.BORDER_START);
+ BorderProps bpsEnd = (BorderProps) area.getTrait(Trait.BORDER_END);
+
+ // draw background
Trait.Background back;
- back = (Trait.Background) block.getTrait(Trait.BACKGROUND);
+ back = (Trait.Background) area.getTrait(Trait.BACKGROUND);
if (back != null) {
+ // Calculate padding rectangle
+ float sx = startx;
+ float sy = starty;
+ float paddRectWidth = width;
+ float paddRectHeight = height;
+
+ if (bpsStart != null) {
+ sx += bpsStart.width / 1000f;
+ paddRectWidth -= bpsStart.width / 1000f;
+ }
+ if (bpsBefore != null) {
+ sy += bpsBefore.width / 1000f;
+ paddRectHeight -= bpsBefore.width / 1000f;
+ }
+ if (bpsEnd != null) {
+ paddRectWidth -= bpsEnd.width / 1000f;
+ }
+ if (bpsAfter != null) {
+ paddRectHeight -= bpsAfter.width / 1000f;
+ }
+
if (back.getColor() != null) {
- graphics.setColor(back.getColor().getAWTColor());
- graphics.fillRect(startx, starty, width, height);
+ drawBackground(back, sx, sy, paddRectWidth, paddRectHeight);
}
- if (back.getURL() != null) { // TODO: implement
- ImageFactory fact = ImageFactory.getInstance();
- FopImage fopimage = fact.getImage(back.getURL(), userAgent);
+
+ // background image
+ if (back.getFopImage() != null) {
+ FopImage fopimage = back.getFopImage();
if (fopimage != null && fopimage.load(FopImage.DIMENSIONS)) {
- if (back.getRepeat() == EN_REPEAT) {
- // create a pattern for the image
- } else {
- // place once
- Rectangle2D pos;
- pos = new Rectangle2D.Float((startx + back.getHoriz()) * 1000,
- (starty + back.getVertical()) * 1000,
- fopimage.getWidth() * 1000,
- fopimage.getHeight() * 1000);
-// putImage(back.getURL(), pos);
+ clip(sx, sy, paddRectWidth, paddRectHeight);
+ int horzCount = (int) ((paddRectWidth * 1000 / fopimage
+ .getIntrinsicWidth()) + 1.0f);
+ int vertCount = (int) ((paddRectHeight * 1000 / fopimage
+ .getIntrinsicHeight()) + 1.0f);
+ if (back.getRepeat() == EN_NOREPEAT) {
+ horzCount = 1;
+ vertCount = 1;
+ } else if (back.getRepeat() == EN_REPEATX) {
+ vertCount = 1;
+ } else if (back.getRepeat() == EN_REPEATY) {
+ horzCount = 1;
+ }
+ // change from points to millipoints
+ sx *= 1000;
+ sy *= 1000;
+ if (horzCount == 1) {
+ sx += back.getHoriz();
}
+ if (vertCount == 1) {
+ sy += back.getVertical();
+ }
+ for (int x = 0; x < horzCount; x++) {
+ for (int y = 0; y < vertCount; y++) {
+ // place once
+ Rectangle2D pos;
+ pos = new Rectangle2D.Float(sx
+ + (x * fopimage.getIntrinsicWidth()), sy
+ + (y * fopimage.getIntrinsicHeight()),
+ fopimage.getIntrinsicWidth(), fopimage
+ .getIntrinsicHeight());
+ putImage(back.getURL(), pos); // TODO test
+ }
+ }
+
+ } else {
+ getLogger().warn(
+ "Can't find background image: " + back.getURL());
}
}
}
- BorderProps bps = (BorderProps) block.getTrait(Trait.BORDER_BEFORE);
- if (bps != null) {
- int borderWidth = (int) Math.round((bps.width / 1000f) * (scaleFactor / 100f));
- graphics.setColor(bps.color.getAWTColor());
- graphics.fillRect(startx, starty, width, borderWidth);
+ // draw border
+ // BORDER_BEFORE
+ if (bpsBefore != null) {
+ int borderWidth = (int) Math.round((bpsBefore.width / 1000f)
+ * (scaleFactor / 100f));
+ state.updateColor(bpsBefore.color);
+ state.getGraph().fillRect((int) startx, (int) starty, (int) width,
+ borderWidth);
}
- bps = (BorderProps) block.getTrait(Trait.BORDER_AFTER);
- if (bps != null) {
- int borderWidth = (int) Math.round((bps.width / 1000f) * (scaleFactor / 100f));
- int sy = starty + height;
- graphics.setColor(bps.color.getAWTColor());
- graphics.fillRect(startx, starty + height - borderWidth,
- width, borderWidth);
+ // BORDER_AFTER
+ if (bpsAfter != null) {
+ int borderWidth = (int) Math.round((bpsAfter.width / 1000f)
+ * (scaleFactor / 100f));
+ float sy = starty + height;
+ state.updateColor(bpsAfter.color);
+ state.getGraph().fillRect((int) startx,
+ (int) (starty + height - borderWidth), (int) width,
+ borderWidth);
}
- bps = (BorderProps) block.getTrait(Trait.BORDER_START);
- if (bps != null) {
- int borderWidth = (int) Math.round((bps.width / 1000f) * (scaleFactor / 100f));
- graphics.setColor(bps.color.getAWTColor());
- graphics.fillRect(startx, starty, borderWidth, height);
+ // BORDER_START
+ if (bpsStart != null) {
+ int borderWidth = (int) Math.round((bpsStart.width / 1000f)
+ * (scaleFactor / 100f));
+ state.updateColor(bpsStart.color);
+ state.getGraph().fillRect((int) startx, (int) starty, borderWidth,
+ (int) height);
}
- bps = (BorderProps) block.getTrait(Trait.BORDER_END);
- if (bps != null) {
- int borderWidth = (int) Math.round((bps.width / 1000f) * (scaleFactor / 100f));
- int sx = startx + width;
- graphics.setColor(bps.color.getAWTColor());
- graphics.fillRect(startx + width - borderWidth, starty,
- borderWidth, height);
+ // BORDER_END
+ if (bpsEnd != null) {
+ int borderWidth = (int) Math.round((bpsEnd.width / 1000f)
+ * (scaleFactor / 100f));
+ float sx = startx + width;
+ state.updateColor(bpsEnd.color);
+ state.getGraph().fillRect((int) (startx + width - borderWidth),
+ (int) starty, borderWidth, (int) height);
}
-
+
+ }
+
+ /** Draws a thin border around every area to help debugging */
+ private void debugBackAndBorders(Area area, float startx, float starty,
+ float width, float height) {
+
+ // saves the graphics state in a stack
+ state.push();
+
+ ColorType ct = new ColorTypeProperty(0.7f, 0.7f, 0.7f);
+ state.updateColor(ct, true, null);
+ state.updateStroke(0.4f, EN_SOLID);
+ state.getGraph().draw(
+ new Rectangle2D.Float(startx, starty, width, height));
+
+ // restores the last graphics state from the stack
+ state.pop();
}
-
+
+ /**
+ * Draw the Background Rectangle of a given area.
+ *
+ * @param back the Trait.Background
+ * @param sx x coordinate of the rectangle to be filled.
+ * @param sy y the y coordinate of the rectangle to be filled.
+ * @param paddRectWidth the width of the rectangle to be filled.
+ * @param paddRectHeight the height of the rectangle to be filled.
+ */
+ protected void drawBackground(Trait.Background back, float sx, float sy,
+ float paddRectWidth, float paddRectHeight) {
+
+ state.updateColor(back.getColor());
+ state.getGraph().fillRect((int) sx, (int) sy, (int) paddRectWidth,
+ (int) paddRectHeight);
+ }
+
+ /**
+ * Handle block traits. The block could be any sort of block with any
+ * positioning so this should render the traits such as border and
+ * background in its position.
+ *
+ * @param block the block to render the traits
+ */
+ protected void handleBlockTraits(Block block) {
+ // copied from pdf
+ int borderPaddingStart = block.getBorderAndPaddingWidthStart();
+ int borderPaddingBefore = block.getBorderAndPaddingWidthBefore();
+
+ float startx = currentIPPosition / 1000f;
+ float starty = currentBPPosition / 1000f;
+ float width = block.getIPD() / 1000f;
+ float height = block.getBPD() / 1000f;
+
+ startx += block.getStartIndent() / 1000f;
+ startx -= block.getBorderAndPaddingWidthStart() / 1000f;
+ width += borderPaddingStart / 1000f;
+ width += block.getBorderAndPaddingWidthEnd() / 1000f;
+ height += borderPaddingBefore / 1000f;
+ height += block.getBorderAndPaddingWidthAfter() / 1000f;
+
+ drawBackAndBorders(block, startx, starty, width, height);
+ }
+
/**
* @see org.apache.fop.render.Renderer#renderText(TextArea)
*/
public void renderText(TextArea text) {
- System.out.println("In render text: " + text.getTextArea());
- Graphics2D graphics = currentPageImage.createGraphics();
- String fontName = (String) text.getTrait(Trait.FONT_NAME);
+ float x = currentIPPosition;
+ float y = currentBPPosition + text.getOffset(); // baseline
+
+ String name = (String) text.getTrait(Trait.FONT_NAME);
int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue();
-// Typeface f = (Typeface) fontInfo.getFonts().get(fontName);
+ state.updateFont(name, size, null);
+
ColorType ct = (ColorType) text.getTrait(Trait.COLOR);
+ state.updateColor(ct, false, null);
+
+ String s = text.getTextArea();
+ state.getGraph().drawString(s, x / 1000f, y / 1000f);
+
+ getLogger().debug(
+ "renderText(): \"" + s + "\", x: " + x + ", y: " + y + state);
+
+ // rendering text decorations
+ FontMetrics metrics = fontInfo.getMetricsFor(name);
+ Font fs = new Font(name, metrics, size);
+ renderTextDecoration(fs, text, y, x);
+
+ super.renderText(text);
+ }
- FontMetricsMapper mapper = (FontMetricsMapper)
- fontInfo.getMetricsFor(fontName);
- if (mapper == null) {
- mapper = new FontMetricsMapper("MonoSpaced", java.awt.Font.PLAIN,
- graphics);
+ /**
+ * @see org.apache.fop.render.Renderer#renderCharacter(Character)
+ */
+ public void renderCharacter(Character ch) {
+
+ float x = currentIPPosition;
+ float y = currentBPPosition + ch.getOffset(); // baseline
+
+ String name = (String) ch.getTrait(Trait.FONT_NAME);
+ int size = ((Integer) ch.getTrait(Trait.FONT_SIZE)).intValue();
+ state.updateFont(name, size, null);
+
+ ColorType ct = (ColorType) ch.getTrait(Trait.COLOR);
+ state.updateColor(ct, false, null);
+
+ String s = ch.getChar();
+ state.getGraph().drawString(s, x / 1000f, y / 1000f);
+
+ getLogger().debug(
+ "renderCharacter(): \"" + s + "\", x: " + x + ", y: " + y
+ + state);
+
+ // rendering text decorations
+ FontMetrics metrics = fontInfo.getMetricsFor(name);
+ Font fs = new Font(name, metrics, size);
+ renderTextDecoration(fs, ch, y, x);
+
+ super.renderCharacter(ch);
+ }
+
+ /**
+ * Paints the text decoration marks.
+ * @param fs Current font
+ * @param inline inline area to paint the marks for
+ * @param baseline position of the baseline
+ * @param startIPD start IPD
+ */
+ protected void renderTextDecoration(Font fs, InlineArea inline,
+ float baseline, float startIPD) {
+
+ boolean hasTextDeco = inline.hasUnderline() || inline.hasOverline()
+ || inline.hasLineThrough();
+
+ if (hasTextDeco) {
+ state.updateStroke((fs.getDescender() / (-8 * 1000f)),
+ Constants.EN_SOLID);
+ float endIPD = startIPD + inline.getIPD();
+ if (inline.hasUnderline()) {
+ ColorType ct = (ColorType) inline
+ .getTrait(Trait.UNDERLINE_COLOR);
+ state.updateColor(ct, false, null);
+ float y = baseline - fs.getDescender() / 2;
+ line.setLine(startIPD / 1000f, y / 1000f, endIPD / 1000f,
+ y / 1000f);
+ state.getGraph().draw(line);
+ }
+ if (inline.hasOverline()) {
+ ColorType ct = (ColorType) inline
+ .getTrait(Trait.OVERLINE_COLOR);
+ state.updateColor(ct, false, null);
+ float y = (float) (baseline - (1.1 * fs.getCapHeight()));
+ line.setLine(startIPD / 1000f, y / 1000f, endIPD / 1000f,
+ y / 1000f);
+ state.getGraph().draw(line);
+ }
+ if (inline.hasLineThrough()) {
+ ColorType ct = (ColorType) inline
+ .getTrait(Trait.LINETHROUGH_COLOR);
+ state.updateColor(ct, false, null);
+ float y = (float) (baseline - (0.45 * fs.getCapHeight()));
+ line.setLine(startIPD / 1000f, y / 1000f, endIPD / 1000f,
+ y / 1000f);
+ state.getGraph().draw(line);
+ }
}
+ }
-// graphics.setColor(ct.getAWTColor());
-// graphics.setFont(mapper.getFont(size));
- graphics.setColor(java.awt.Color.black);
- graphics.setFont(new java.awt.Font("monospaced", java.awt.Font.PLAIN,
- 10));
-
- int rx = currentIPPosition;
- int bl = currentBPPosition + text.getOffset();
-
- int newx = (int) (rx + 500) / 1000;
- int newy = (int) (pageHeight - (bl + 500) / 1000);
-
- String s = text.getTextArea();
-// graphics.drawString(s, newx, newy);
- graphics.drawString(s, 220, 200);
+ /**
+ * Render leader area. This renders a leader area which is an area with a
+ * rule.
+ *
+ * @param area the leader area to render
+ */
+ public void renderLeader(Leader area) {
+
+ // TODO leader-length: 25%, 50%, 75%, 100% not working yet
+ // TODO Colors do not work on Leaders yet
+
+ float startx = ((float) currentIPPosition) / 1000f;
+ float starty = ((currentBPPosition + area.getOffset()) / 1000f);
+ float endx = (currentIPPosition + area.getIPD()) / 1000f;
+
+ ColorType ct = (ColorType) area.getTrait(Trait.COLOR);
+ state.updateColor(ct, true, null);
+
+ line.setLine(startx, starty, endx, starty);
+ float thickness = area.getRuleThickness() / 1000f;
+
+ int style = area.getRuleStyle();
+ switch (style) {
+ case EN_SOLID:
+ case EN_DOTTED:
+ case EN_DASHED:
+ state.updateStroke(thickness, style);
+ state.getGraph().draw(line);
+ break;
+ case EN_DOUBLE:
+
+ state.updateStroke(thickness / 3f, EN_SOLID); // only a third
+
+ // upper Leader
+ line.setLine(startx, starty, endx, starty);
+ state.getGraph().draw(line);
+ // lower Leader
+ line.setLine(startx, starty + 2 * thickness, endx, starty + 2
+ * thickness);
+ state.getGraph().draw(line);
+
+ break;
+
+ case EN_GROOVE:
+ // The rule looks as though it were carved into the canvas.
+ // (Top/left half of the rule's thickness is the
+ // color specified; the other half is white.)
- // TODO: render text decorations
- currentIPPosition += text.getAllocIPD();
+ state.updateStroke(thickness / 2f, EN_SOLID); // only the half
+
+ // upper Leader
+ line.setLine(startx, starty, endx, starty);
+ state.getGraph().draw(line);
+ // lower Leader
+ line.setLine(startx, starty + thickness, endx, starty + thickness);
+ state.getGraph().setColor(Color.WHITE);
+ state.getGraph().draw(line);
+
+ // TODO the implementation could be nicer, f.eg. with triangles at
+ // the tip of the lines. See also RenderX's implementation (looks
+ // like a button)
+
+ break;
+
+ case EN_RIDGE:
+ // The opposite of "groove", the rule looks as though it were
+ // coming out of the canvas. (Bottom/right half of the rule's
+ // thickness is the color specified; the other half is white.)
+
+ state.updateStroke(thickness / 2f, EN_SOLID); // only the half
+
+ // lower Leader
+ line.setLine(startx, starty + thickness, endx, starty + thickness);
+ state.getGraph().draw(line);
+ // upperLeader
+ line.setLine(startx, starty, endx, starty);
+ state.getGraph().setColor(Color.WHITE);
+ state.getGraph().draw(line);
+
+ // TODO the implementation could be nicer, f.eg. with triangles at
+ // the tip of the lines. See also RenderX's implementation (looks
+ // like a button)
+
+ break;
+
+ case EN_NONE:
+ // No rule is drawn
+ break;
+
+ } // end switch
+
+ super.renderLeader(area);
}
- /** @see org.apache.fop.render.AbstractRenderer */
- public String getMimeType() {
- return MIME_TYPE;
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderImage(Image,
+ * Rectangle2D)
+ */
+ public void renderImage(Image image, Rectangle2D pos) {
+ // endTextObject();
+ String url = image.getURL();
+ putImage(url, pos);
+ }
+
+ /**
+ * draws an image
+ *
+ * @param url URL of the bitmap
+ * @param pos Position of the bitmap
+ */
+ protected void putImage(String pUrl, Rectangle2D pos) {
+
+ int x = currentIPPosition; // TODO + area.getXOffset();
+ int y = currentBPPosition;
+ String url = ImageFactory.getURL(pUrl);
+
+ ImageFactory fact = ImageFactory.getInstance();
+ FopImage fopimage = fact.getImage(url, userAgent);
+
+ if (fopimage == null) {
+ return;
+ }
+ if (!fopimage.load(FopImage.DIMENSIONS)) {
+ return;
+ }
+ int w = fopimage.getWidth();
+ int h = fopimage.getHeight();
+ String mime = fopimage.getMimeType();
+ if ("text/xml".equals(mime)) {
+ if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
+ return;
+ }
+ Document doc = ((XMLImage) fopimage).getDocument();
+ String ns = ((XMLImage) fopimage).getNameSpace();
+ renderDocument(doc, ns, pos);
+
+ } else if ("image/svg+xml".equals(mime)) {
+ if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
+ return;
+ }
+ Document doc = ((XMLImage) fopimage).getDocument();
+ renderSVGDocument(doc, pos); // TODO check if ok.
+
+ } else if ("image/eps".equals(mime)) {
+ getLogger().warn("EPS images are not supported by this renderer");
+ currentBPPosition += (h * 1000);
+ } else if ("image/jpeg".equals(mime)) {
+ if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
+ return;
+ }
+
+ // TODO Load JPEGs rather through fopimage.load(FopImage.BITMAP),
+ // but JpegImage will need to be extended for that
+
+ //url = url.substring(7);
+ //url = "C:/eclipse/myWorkbenches/fop4/xml-fop/examples/fo" + url;
+ java.awt.Image awtImage = new javax.swing.ImageIcon(url).getImage();
+
+ state.getGraph().drawImage(awtImage, (int) (x / 1000f),
+ (int) (y / 1000f), (int) w, h, null);
+ currentBPPosition += (h * 1000);
+ } else {
+ if (!fopimage.load(FopImage.BITMAP)) {
+ getLogger().warn("Loading of bitmap failed: " + url);
+ return;
+ }
+
+ byte[] raw = fopimage.getBitmaps();
+
+ //TODO Hardcoded color and sample models, FIX ME!
+ ColorModel cm = new ComponentColorModel(
+ ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB),
+ false, false, ColorModel.OPAQUE, DataBuffer.TYPE_BYTE);
+ SampleModel sampleModel = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
+ w, h, 3, w * 3, new int[] {0, 1, 2});
+ DataBuffer dbuf = new DataBufferByte(raw, w * h * 3);
+
+ WritableRaster raster = Raster.createWritableRaster(sampleModel, dbuf, null);
+
+ java.awt.Image awtImage;
+ // Combine the color model and raster into a buffered image
+ awtImage = new BufferedImage(cm, raster, false, null);
+
+ state.getGraph().drawImage(awtImage, (int) (x / 1000f),
+ (int) (y / 1000f), (int) w, h, null);
+ currentBPPosition += (h * 1000);
+ }
+ }
+
+ /**
+ * @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject,
+ * Rectangle2D)
+ */
+ public void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
+ Document doc = fo.getDocument();
+ String ns = fo.getNameSpace();
+ if (ns.equals("http://www.w3.org/2000/svg")) {
+ renderSVGDocument(doc, pos);
+ } else {
+ renderDocument(doc, ns, pos);
+ }
+ // this.currentXPosition += area.getContentWidth();
+ }
+
+ /**
+ * Renders an XML document (SVG for example).
+ *
+ * @param doc DOM document representing the XML document
+ * @param ns Namespace for the document
+ * @param pos Position on the page
+ */
+ public void renderDocument(Document doc, String ns, Rectangle2D pos) {
+ RendererContext context;
+ context = new RendererContext(MIME_TYPE);
+ context.setUserAgent(userAgent);
+ // TODO implement
+ /*
+ * context.setProperty(PDFXMLHandler.PDF_DOCUMENT, pdfDoc);
+ * context.setProperty(PDFXMLHandler.OUTPUT_STREAM, ostream);
+ * context.setProperty(PDFXMLHandler.PDF_STATE, currentState);
+ * context.setProperty(PDFXMLHandler.PDF_PAGE, currentPage);
+ * context.setProperty(PDFXMLHandler.PDF_CONTEXT, currentContext == null ?
+ * currentPage : currentContext);
+ * context.setProperty(PDFXMLHandler.PDF_CONTEXT, currentContext);
+ * context.setProperty(PDFXMLHandler.PDF_STREAM, currentStream);
+ * context.setProperty(PDFXMLHandler.PDF_XPOS, new
+ * Integer(currentIPPosition + (int) pos.getX()));
+ * context.setProperty(PDFXMLHandler.PDF_YPOS, new
+ * Integer(currentBPPosition + (int) pos.getY()));
+ * context.setProperty(PDFXMLHandler.PDF_FONT_INFO, fontInfo);
+ * context.setProperty(PDFXMLHandler.PDF_FONT_NAME, currentFontName);
+ * context.setProperty(PDFXMLHandler.PDF_FONT_SIZE, new
+ * Integer(currentFontSize));
+ * context.setProperty(PDFXMLHandler.PDF_WIDTH, new Integer((int)
+ * pos.getWidth())); context.setProperty(PDFXMLHandler.PDF_HEIGHT, new
+ * Integer((int) pos.getHeight())); renderXML(userAgent, context, doc,
+ * ns);
+ */
}
+ protected void renderSVGDocument(Document doc, Rectangle2D pos) {
+
+ int x = currentIPPosition; // TODO + area.getXOffset();
+ int y = currentBPPosition;
+
+ RendererContext context;
+ context = new RendererContext(MIME_TYPE);
+ context.setUserAgent(userAgent);
+
+ SVGUserAgent ua = new SVGUserAgent(context.getUserAgent()
+ .getPixelUnitToMillimeter(), new AffineTransform());
+
+ GVTBuilder builder = new GVTBuilder();
+ BridgeContext ctx = new BridgeContext(ua);
+
+ GraphicsNode root;
+ try {
+ root = builder.build(ctx, doc);
+ } catch (Exception e) {
+ getLogger().error(
+ "svg graphic could not be built: " + e.getMessage(), e);
+ return;
+ }
+ float w = (float) ctx.getDocumentSize().getWidth() * 1000f;
+ float h = (float) ctx.getDocumentSize().getHeight() * 1000f;
+
+ // correct integer roundoff
+ state.getGraph().translate(x / 1000, y / 1000);
+
+ SVGSVGElement svg = ((SVGDocument) doc).getRootElement();
+ AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg,
+ w / 1000f, h / 1000f);
+ AffineTransform inverse = null;
+ try {
+ inverse = at.createInverse();
+ } catch (NoninvertibleTransformException e) {
+ getLogger().warn(e);
+ }
+ if (!at.isIdentity()) {
+ state.getGraph().transform(at);
+ }
+
+ try {
+ root.paint(state.getGraph());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ if (inverse != null && !inverse.isIdentity()) {
+ state.getGraph().transform(inverse);
+ }
+ // correct integer roundoff
+ // currentState.getCurrentGraphics().translate(-x / 1000f, y / 1000f -
+ // pageHeight);
+ state.getGraph().translate(-(x + 500) / 1000,
+ (y + 500) / 1000 - pageHeight);
+ }
}
diff --git a/src/java/org/apache/fop/render/awt/RendererState.java b/src/java/org/apache/fop/render/awt/RendererState.java
new file mode 100644
index 000000000..3fdc8690a
--- /dev/null
+++ b/src/java/org/apache/fop/render/awt/RendererState.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.awt;
+
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+
+import org.apache.fop.datatypes.ColorType;
+
+/**
+ * An interface for the classes which hold the state of the current graphics context.
+ */
+public interface RendererState {
+
+ /**
+ * Push the current state onto the stack.
+ */
+ public abstract void push();
+
+ /**
+ * Pop the state from the stack and restore the graphics context.
+ * @return the restored state, null if the stack is empty.
+ */
+ public abstract Graphics2D pop();
+
+ /**
+ * Get the current stack level.
+ *
+ * @return the current stack level
+ */
+ public abstract int getStackLevel();
+
+ /**
+ * Establishes a new foreground or fill color.
+ * @param col the color to apply (null skips this operation)
+ * @param fill true to set the fill color, false for the foreground color
+ * @param pdf only used by the PDFRenderer, is set to null.
+ * @return true if the new Color changes the current Color
+ */
+ public abstract boolean updateColor(ColorType col, boolean fill, StringBuffer pdf);
+
+ /**
+ * Set the current font name. Check if the font name will change and then
+ * set the new name.
+ *
+ * @param name the new font name
+ * @param size
+ * @param pdf
+ * @return true if the new Font changes the current Font
+ */
+ public abstract boolean updateFont(String name, int size, StringBuffer pdf);
+
+ /**
+ * Sets the current Stroke. The line width should be set with
+ * updateLineWidth() before calling this method
+ *
+ * @param style the constant for the style of the line as an int
+ * @return true if the new Stroke changes the current Stroke
+ */
+ public abstract boolean updateStroke(float width, int style);
+
+ /**
+ * Set the current paint. This checks if the paint will change and then sets
+ * the current paint.
+ *
+ * @param p the new paint
+ * @return true if the new paint changes the current paint
+ */
+ public abstract boolean updatePaint(Paint p);
+
+ /**
+ * Check if the clip will change the current state. A clip is assumed to be
+ * used in a situation where it will add to any clip in the current or
+ * parent states. A clip cannot be cleared, this can only be achieved by
+ * going to a parent level with the correct clip. If the clip is different
+ * then it may start a new state so that it can return to the previous clip.
+ *
+ * @param cl the clip shape to check
+ * @return true if the clip will change the current clip.
+ */
+ // TODO test
+ public abstract boolean checkClip(Shape cl);
+
+ /**
+ * Set the current clip. This either sets a new clip or sets the clip to the
+ * intersect of the old clip and the new clip.
+ *
+ * @param cl the new clip in the current state
+ */
+ public abstract boolean updateClip(Shape cl);
+
+ /**
+ * Check the current transform. The transform for the current state is the
+ * combination of all transforms in the current state. The parameter is
+ * compared against this current transform.
+ *
+ * @param tf the transform to check against
+ * @return true if the new transform is different from the current transform
+ */
+ public abstract boolean checkTransform(AffineTransform tf);
+
+ /**
+ * Overwrites the Transform in the Graphics2D context. Use <code>transform()</code> if you
+ * wish to compose with the current Affinetransform instead.
+ * @see java.awt.Graphics2D.setTransform().
+ * @param tf the transform to concatonate to the current level transform
+ */
+ public abstract void setTransform(AffineTransform tf);
+
+ /**
+ * Composes an AffineTransform object with the Transform in this Graphics2D
+ * according to the rule last-specified-first-applied.
+ * @see java.awt.Graphics2D.transform().
+ *
+ * @param tf the transform to concatonate to the current level transform
+ */
+ public abstract void transform(AffineTransform tf);
+
+ /**
+ * Get the current transform. This gets the combination of all transforms in
+ * the current state.
+ *
+ * @return the calculate combined transform for the current state
+ */
+ public abstract AffineTransform getTransform();
+
+}
diff --git a/src/java/org/apache/fop/render/awt/viewer/PreviewDialog.java b/src/java/org/apache/fop/render/awt/viewer/PreviewDialog.java
index 21218d3e5..340eeec1d 100644
--- a/src/java/org/apache/fop/render/awt/viewer/PreviewDialog.java
+++ b/src/java/org/apache/fop/render/awt/viewer/PreviewDialog.java
@@ -1,6 +1,6 @@
/*
- * Copyright 1999-2004 The Apache Software Foundation.
- *
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
* Licensed 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
@@ -19,19 +19,6 @@
package org.apache.fop.render.awt.viewer;
//Java
-import javax.swing.BorderFactory;
-import javax.swing.ImageIcon;
-import javax.swing.JComboBox;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JMenu;
-import javax.swing.JMenuBar;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JToolBar;
-import javax.swing.SwingUtilities;
-
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
@@ -44,13 +31,26 @@ import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
-import java.awt.print.PrinterJob;
import java.awt.print.PrinterException;
+import java.awt.print.PrinterJob;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JToolBar;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
-//FOP
-import org.apache.fop.apps.Fop;
-import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.Fop;
import org.apache.fop.fo.Constants;
import org.apache.fop.render.awt.AWTRenderer;
@@ -123,6 +123,18 @@ public class PreviewDialog extends JFrame {
reload();
}
};
+ Command debugAction = new Command(" Debug") {
+ //TODO use Translator
+ public void doit() {
+ debug();
+ }
+ };
+
+ //set the system look&feel
+ try {
+ UIManager.setLookAndFeel(
+ UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception e) { }
setTitle("FOP: AWT-" + translator.getString("Title.Preview"));
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
@@ -171,8 +183,9 @@ public class PreviewDialog extends JFrame {
toolBar.add(lastPageAction);
toolBar.addSeparator();
toolBar.add(new JLabel(translator.getString("Menu.Zoom")));
- toolBar.addSeparator();
toolBar.add(scale);
+ toolBar.addSeparator();
+ toolBar.add(debugAction);
getContentPane().add(toolBar, BorderLayout.NORTH);
//Status bar
JPanel statusBar = new JPanel();
@@ -379,6 +392,14 @@ public class PreviewDialog extends JFrame {
}
/**
+ * Allows a (yet) simple visual debug of the document.
+ */
+ private void debug(){
+ renderer.debug = !renderer.debug;
+ showPage();
+ }
+
+ /**
* This class is used to reload document in
* a thread safe way.
*/