From 9641f2a32dca4bd7078f1fdbc2871292823ee423 Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Tue, 2 May 2006 14:39:32 +0000 Subject: [PATCH] Java2D Renderer: Symbol and ZapfDingbats now work due to a little change in FontSetup.java Made the class a subclass of AbstractPathOrientedRenderer. This enables to remove some redundant code. Border painting on a Graphics2D is now available to other renderers. PCL Renderer: Improved Graphics2D implementation. PCLGraphic2D throws an UnsupportedOperationException if it encounters a feature it cannot properly handle. The Graphics2DAdapter can then fall back to painting a graphic in-memory and then as a bitmap. Added border painting. PCLRenderer supports to modes "quality" and "speed". The user can configure the mode depending on his needs. In "speed" mode borders are painted as shaded rectangles only. In "quality" mode it uses border rendering of the Java2DRenderer. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@398945 13f79535-47bb-0310-9956-ffa450edef68 --- .../render/AbstractPathOrientedRenderer.java | 52 +- .../apache/fop/render/AbstractRenderer.java | 23 + .../org/apache/fop/render/PrintRenderer.java | 2 +- .../apache/fop/render/awt/AWTRenderer.java | 10 +- .../fop/render/awt/viewer/PreviewPanel.java | 2 +- .../render/bitmap/PNGRenderer_onthefly.java | 7 +- .../fop/render/java2d/FontMetricsMapper.java | 25 +- .../apache/fop/render/java2d/FontSetup.java | 5 +- .../fop/render/java2d/Java2DFontMetrics.java | 17 +- .../render/java2d/Java2DGraphicsState.java | 26 +- .../fop/render/java2d/Java2DRenderer.java | 921 +++++++----------- .../fop/render/java2d/RendererState.java | 9 - .../apache/fop/render/xml/XMLRenderer.java | 8 + .../apache/fop/render/pcl/PCLGenerator.java | 59 +- .../apache/fop/render/pcl/PCLGraphics2D.java | 340 +++++-- .../fop/render/pcl/PCLGraphics2DAdapter.java | 128 +-- .../apache/fop/render/pcl/PCLRenderer.java | 758 ++++++++++---- .../apache/fop/render/pcl/PCLSVGHandler.java | 50 +- .../apache/fop/render/svg/SVGRenderer.java | 7 +- 19 files changed, 1412 insertions(+), 1037 deletions(-) diff --git a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java index 5821c96a6..e7ad1c307 100644 --- a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java +++ b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2005 The Apache Software Foundation. + * Copyright 2005-2006 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. @@ -18,7 +18,6 @@ package org.apache.fop.render; -import java.awt.Color; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; import java.util.List; @@ -30,9 +29,6 @@ import org.apache.fop.area.CTM; import org.apache.fop.area.RegionViewport; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.InlineArea; -import org.apache.fop.area.inline.InlineBlockParent; -import org.apache.fop.area.inline.InlineParent; -import org.apache.fop.area.inline.Space; import org.apache.fop.area.inline.Viewport; import org.apache.fop.datatypes.ColorType; import org.apache.fop.fo.Constants; @@ -46,15 +42,6 @@ import org.apache.fop.traits.BorderProps; */ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { - /** - * Converts a ColorType to a java.awt.Color (sRGB). - * @param col the color - * @return the converted color - */ - protected Color toColor(ColorType col) { - return new Color(col.getRed(), col.getGreen(), col.getBlue()); - } - /** * Handle block traits. * The block could be any sort of block with any positioning @@ -208,6 +195,24 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { } } + Rectangle2D.Float borderRect = new Rectangle2D.Float(startx, starty, width, height); + drawBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); + } + + /** + * Draws borders. + * @param borderRect the border rectangle + * @param bpsBefore the border specification on the before side + * @param bpsAfter the border specification on the after side + * @param bpsStart the border specification on the start side + * @param bpsEnd the border specification on the end side + */ + protected void drawBorders(Rectangle2D.Float borderRect, + BorderProps bpsBefore, BorderProps bpsAfter, BorderProps bpsStart, BorderProps bpsEnd) { + float startx = borderRect.x; + float starty = borderRect.y; + float width = borderRect.width; + float height = borderRect.height; boolean[] b = new boolean[] { (bpsBefore != null), (bpsEnd != null), (bpsAfter != null), (bpsStart != null)}; @@ -394,25 +399,6 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer { } - /** @see org.apache.fop.render.AbstractRenderer#renderInlineSpace(Space) */ - protected void renderInlineSpace(Space space) { - space.setBPD(0); - renderInlineAreaBackAndBorders(space); - super.renderInlineSpace(space); - } - - /** @see org.apache.fop.render.AbstractRenderer#renderInlineParent(InlineParent) */ - protected void renderInlineParent(InlineParent ip) { - renderInlineAreaBackAndBorders(ip); - super.renderInlineParent(ip); - } - - /** @see org.apache.fop.render.AbstractRenderer */ - protected void renderInlineBlockParent(InlineBlockParent ibp) { - renderInlineAreaBackAndBorders(ibp); - super.renderInlineBlockParent(ibp); - } - /** * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport, List) */ diff --git a/src/java/org/apache/fop/render/AbstractRenderer.java b/src/java/org/apache/fop/render/AbstractRenderer.java index d6c2c734f..57d15ba8b 100644 --- a/src/java/org/apache/fop/render/AbstractRenderer.java +++ b/src/java/org/apache/fop/render/AbstractRenderer.java @@ -19,6 +19,7 @@ package org.apache.fop.render; // Java +import java.awt.Color; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; import java.io.IOException; @@ -63,6 +64,7 @@ import org.apache.fop.area.inline.TextArea; import org.apache.fop.area.inline.WordArea; import org.apache.fop.area.inline.SpaceArea; import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.datatypes.ColorType; import org.apache.fop.fo.Constants; import org.apache.fop.fonts.FontInfo; import org.apache.commons.logging.Log; @@ -646,11 +648,21 @@ public abstract class AbstractRenderer currentIPPosition += ch.getAllocIPD(); } + /** + * Common method to render the background and borders for any inline area. + * The all borders and padding are drawn outside the specified area. + * @param area the inline area for which the background, border and padding is to be + * rendered + */ + protected abstract void renderInlineAreaBackAndBorders(InlineArea area); + /** * Render the given Space. * @param space the space to render */ protected void renderInlineSpace(Space space) { + space.setBPD(0); + renderInlineAreaBackAndBorders(space); // an inline space moves the inline progression position // for the current block by the width or height of the space // it may also have styling (only on this object) that needs @@ -701,6 +713,7 @@ public abstract class AbstractRenderer * @param ip the inline parent to render */ protected void renderInlineParent(InlineParent ip) { + renderInlineAreaBackAndBorders(ip); int saveIP = currentIPPosition; int saveBP = currentBPPosition; currentIPPosition += ip.getBorderAndPaddingWidthStart(); @@ -718,6 +731,7 @@ public abstract class AbstractRenderer * @param ibp the inline block parent to render */ protected void renderInlineBlockParent(InlineBlockParent ibp) { + renderInlineAreaBackAndBorders(ibp); currentIPPosition += ibp.getBorderAndPaddingWidthStart(); // For inline content the BP position is updated by the enclosing line area int saveBP = currentBPPosition; @@ -867,5 +881,14 @@ public abstract class AbstractRenderer public String getMimeType() { return null; } + + /** + * Converts a ColorType to a java.awt.Color (sRGB). + * @param col the color + * @return the converted color + */ + protected Color toColor(ColorType col) { + return new Color(col.getRed(), col.getGreen(), col.getBlue()); + } } diff --git a/src/java/org/apache/fop/render/PrintRenderer.java b/src/java/org/apache/fop/render/PrintRenderer.java index 873879fe1..dab571d47 100644 --- a/src/java/org/apache/fop/render/PrintRenderer.java +++ b/src/java/org/apache/fop/render/PrintRenderer.java @@ -79,7 +79,7 @@ public abstract class PrintRenderer extends AbstractRenderer { * @param factor factor by which to lighten up (negative values darken the color) * @return the modified color */ - protected Color lightenColor(Color col, float factor) { + public static Color lightenColor(Color col, float factor) { float[] cols = new float[3]; cols = col.getColorComponents(cols); if (factor > 0) { diff --git a/src/java/org/apache/fop/render/awt/AWTRenderer.java b/src/java/org/apache/fop/render/awt/AWTRenderer.java index 62d5f8c47..4e4e785d4 100644 --- a/src/java/org/apache/fop/render/awt/AWTRenderer.java +++ b/src/java/org/apache/fop/render/awt/AWTRenderer.java @@ -113,9 +113,10 @@ public class AWTRenderer extends Java2DRenderer implements Pageable { dialogDisplay = show; } - /** @see org.apache.fop.render.Renderer#renderPage(org.apache.fop.area.PageViewport) */ - public void renderPage(PageViewport pageViewport) - throws IOException, FOPException { + /** + * @see org.apache.fop.render.Renderer#renderPage(org.apache.fop.area.PageViewport) + */ + public void renderPage(PageViewport pageViewport) throws IOException { super.renderPage(pageViewport); if (frame != null) { @@ -263,7 +264,7 @@ public class AWTRenderer extends Java2DRenderer implements Pageable { state.push(); ColorType ct = new ColorTypeProperty(0.7f, 0.7f, 0.7f); - state.updateColor(ct, true, null); + state.updateColor(ct); state.updateStroke(0.4f, EN_SOLID); state.getGraph().draw( new Rectangle2D.Float(startx, starty, width, height)); @@ -271,4 +272,5 @@ public class AWTRenderer extends Java2DRenderer implements Pageable { // restores the last graphics state from the stack state.pop(); } + } diff --git a/src/java/org/apache/fop/render/awt/viewer/PreviewPanel.java b/src/java/org/apache/fop/render/awt/viewer/PreviewPanel.java index aedcf801f..abd053811 100644 --- a/src/java/org/apache/fop/render/awt/viewer/PreviewPanel.java +++ b/src/java/org/apache/fop/render/awt/viewer/PreviewPanel.java @@ -290,7 +290,7 @@ public class PreviewPanel extends JPanel { private class Reloader extends Thread { public void run() { - if (!renderer.renderingDone) { + if (!renderer.isRenderingDone()) { // do not allow the reloading while FOP is still rendering JOptionPane.showMessageDialog(previewArea, "Cannot perform the requested operation until " diff --git a/src/java/org/apache/fop/render/bitmap/PNGRenderer_onthefly.java b/src/java/org/apache/fop/render/bitmap/PNGRenderer_onthefly.java index cfef97430..1426d0cbe 100644 --- a/src/java/org/apache/fop/render/bitmap/PNGRenderer_onthefly.java +++ b/src/java/org/apache/fop/render/bitmap/PNGRenderer_onthefly.java @@ -95,9 +95,10 @@ public class PNGRenderer_onthefly extends Java2DRenderer { fileSyntax = s.substring(0, i); } - /** @see org.apache.fop.render.Renderer#renderPage(org.apache.fop.area.PageViewport) */ - public void renderPage(PageViewport pageViewport) throws IOException, - FOPException { + /** + * @see org.apache.fop.render.Renderer#renderPage(org.apache.fop.area.PageViewport) + */ + public void renderPage(PageViewport pageViewport) throws IOException { // Do the rendering: get the image for this page RenderedImage image = (RenderedImage) getPageImage(pageViewport); diff --git a/src/java/org/apache/fop/render/java2d/FontMetricsMapper.java b/src/java/org/apache/fop/render/java2d/FontMetricsMapper.java index fb27bdee6..e49ee8967 100644 --- a/src/java/org/apache/fop/render/java2d/FontMetricsMapper.java +++ b/src/java/org/apache/fop/render/java2d/FontMetricsMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -25,6 +25,7 @@ import java.util.Map; // FOP import org.apache.fop.fonts.FontMetrics; import org.apache.fop.fonts.FontType; +import org.apache.fop.fonts.Typeface; /** @@ -35,13 +36,7 @@ import org.apache.fop.fonts.FontType; * style as member varibles */ -public class FontMetricsMapper implements FontMetrics { - - /** - * The first and last non space-character - */ - private static final int FIRST_CHAR = 32; - private static final int LAST_CHAR = 255; +public class FontMetricsMapper extends Typeface implements FontMetrics { /** * This is a Java2DFontMetrics that does the real calculation. @@ -157,6 +152,20 @@ public class FontMetricsMapper implements FontMetrics { return false; } + /** @see org.apache.fop.fonts.Typeface#getEncoding() */ + public String getEncoding() { + return null; //Not applicable to Java2D rendering + } + + /** @see org.apache.fop.fonts.Typeface#mapChar(char) */ + public char mapChar(char c) { + return c; + } + + /** @see org.apache.fop.fonts.Typeface#hasChar(char) */ + public boolean hasChar(char c) { + return metric.hasChar(family, style, Java2DFontMetrics.FONT_SIZE, c); + } } diff --git a/src/java/org/apache/fop/render/java2d/FontSetup.java b/src/java/org/apache/fop/render/java2d/FontSetup.java index 7adbfb1ac..85e61ce65 100644 --- a/src/java/org/apache/fop/render/java2d/FontSetup.java +++ b/src/java/org/apache/fop/render/java2d/FontSetup.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -98,7 +98,8 @@ public class FontSetup { // --> goes to F12 fontInfo.addMetrics("F12", metric); - metric = new FontMetricsMapper("Symbol", bolditalic, graphics); + metric = new FontMetricsMapper("Serif", normal, graphics); + //"Symbol" doesn't seem to work here, but "Serif" does the job just fine. *shrug* // --> goes to F13 and F14 fontInfo.addMetrics("F13", metric); fontInfo.addMetrics("F14", metric); diff --git a/src/java/org/apache/fop/render/java2d/Java2DFontMetrics.java b/src/java/org/apache/fop/render/java2d/Java2DFontMetrics.java index 2ae7b8ce3..724fda81d 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DFontMetrics.java +++ b/src/java/org/apache/fop/render/java2d/Java2DFontMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -53,7 +53,7 @@ public class Java2DFontMetrics { /** * The width of all 256 character, if requested */ - private int width[] = null; + private int[] width = null; /** * The typical height of a small cap latter @@ -291,6 +291,19 @@ public class Java2DFontMetrics { */ } + /** + * Indicates whether the font contains a particular character/glyph. + * @param family font family (jave name) to use + * @param style font style (jave def.) to use + * @param size font size + * @param c the glyph to check + * @return true if the character is supported + */ + public boolean hasChar(String family, int style, int size, char c) { + setFont(family, style, size); + return f1.canDisplay(c); + } + } diff --git a/src/java/org/apache/fop/render/java2d/Java2DGraphicsState.java b/src/java/org/apache/fop/render/java2d/Java2DGraphicsState.java index 22596ea53..4e605f33a 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DGraphicsState.java +++ b/src/java/org/apache/fop/render/java2d/Java2DGraphicsState.java @@ -140,18 +140,6 @@ public class Java2DGraphicsState implements Constants, RendererState { return new Color(col.getRed(), col.getGreen(), col.getBlue()); } - /** - * @see org.apache.fop.render.java2d.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 @@ -177,13 +165,13 @@ public class Java2DGraphicsState implements Constants, RendererState { */ public boolean updateFont(String name, int size, StringBuffer pdf) { - boolean updateName = (!name.equals(getGraph().getFont().getFontName())); - boolean updateSize = (size != (getGraph().getFont().getSize())); + FontMetricsMapper mapper = (FontMetricsMapper)fontInfo.getMetricsFor(name); + boolean updateName = (!mapper.getFontName().equals( + getGraph().getFont().getFontName())); + boolean updateSize = (size != (getGraph().getFont().getSize() * 1000)); 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); @@ -215,8 +203,8 @@ public class Java2DGraphicsState implements Constants, RendererState { switch (style) { case EN_DOTTED: - currentStroke = new BasicStroke(width, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_BEVEL, 0f, new float[] { 2f }, 0f); + currentStroke = new BasicStroke(width, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_BEVEL, 0f, new float[] {0, 2 * width}, width); currentGraphics.setStroke(currentStroke); currentStrokeWidth = width; @@ -227,7 +215,7 @@ public class Java2DGraphicsState implements Constants, RendererState { case EN_DASHED: currentStroke = new BasicStroke(width, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_BEVEL, 0f, new float[] { 8f, 2f }, 0f); + BasicStroke.JOIN_BEVEL, 0f, new float[] {8f, 2f}, 0f); currentGraphics.setStroke(currentStroke); currentStrokeWidth = width; diff --git a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java index 31e0a2bc4..5f9144a01 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java +++ b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java @@ -19,13 +19,14 @@ package org.apache.fop.render.java2d; // Java +import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; -import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; @@ -44,40 +45,31 @@ import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; import java.util.List; -import java.util.Map; + +import org.w3c.dom.Document; 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.PageViewport; -import org.apache.fop.area.RegionViewport; import org.apache.fop.area.Trait; 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.InlineBlockParent; -import org.apache.fop.area.inline.InlineParent; import org.apache.fop.area.inline.Leader; -import org.apache.fop.area.inline.Space; import org.apache.fop.area.inline.TextArea; -import org.apache.fop.area.inline.Viewport; import org.apache.fop.datatypes.ColorType; import org.apache.fop.fo.Constants; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; -import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.fonts.Typeface; 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.render.AbstractPathOrientedRenderer; import org.apache.fop.render.Graphics2DAdapter; import org.apache.fop.render.RendererContext; import org.apache.fop.render.pdf.CTMHelper; -import org.apache.fop.traits.BorderProps; -import org.w3c.dom.Document; /** * The Java2DRenderer class provides the abstract technical @@ -103,7 +95,7 @@ import org.w3c.dom.Document; * } * */ -public abstract class Java2DRenderer extends AbstractRenderer implements Printable { +public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implements Printable { /** The scale factor for the image size, values: ]0 ; 1] */ protected double scaleFactor = 1; @@ -132,19 +124,11 @@ public abstract class Java2DRenderer extends AbstractRenderer implements Printab /** The current state, holds a Graphics2D and its context */ protected Java2DGraphicsState state; - /** a Line2D.Float used to draw text decorations and leaders */ - protected Line2D.Float line = new Line2D.Float(); - - /** Font configuration */ - protected FontInfo fontInfo; - - protected Map fontNames = new java.util.Hashtable(); - - protected Map fontStyles = new java.util.Hashtable(); - /** true if the renderer has finished rendering all the pages */ - public boolean renderingDone; + private boolean renderingDone; + private GeneralPath currentPath = null; + /** Default constructor */ public Java2DRenderer() { } @@ -166,6 +150,7 @@ public abstract class Java2DRenderer extends AbstractRenderer implements Printab * @see org.apache.fop.render.Renderer#setupFontInfo(org.apache.fop.fonts.FontInfo) */ public void setupFontInfo(FontInfo inFontInfo) { + //Don't call super.setupFontInfo() here! Java2D needs a special font setup // create a temp Image to test font metrics on fontInfo = inFontInfo; BufferedImage fontImage = new BufferedImage(100, 100, @@ -186,14 +171,17 @@ public abstract class Java2DRenderer extends AbstractRenderer implements Printab scaleFactor = newScaleFactor; } + /** @return the scale factor */ public double getScaleFactor() { return scaleFactor; } + /** @see org.apache.fop.render.Renderer#startRenderer(java.io.OutputStream) */ public void startRenderer(OutputStream out) throws IOException { // do nothing by default } + /** @see org.apache.fop.render.Renderer#stopRenderer() */ public void stopRenderer() throws IOException { log.debug("Java2DRenderer stopped"); renderingDone = true; @@ -204,6 +192,11 @@ public abstract class Java2DRenderer extends AbstractRenderer implements Printab } } + /** @return true if the renderer is not currently processing */ + public boolean isRenderingDone() { + return this.renderingDone; + } + /** * @return The 0-based current page number */ @@ -243,11 +236,11 @@ public abstract class Java2DRenderer extends AbstractRenderer implements Printab * * @param pageViewport the PageViewport object supplied by * the Area Tree + * @throws IOException In case of an I/O error * @see org.apache.fop.render.Renderer */ - public void renderPage(PageViewport pageViewport) - throws IOException, FOPException { - // TODO clone + public void renderPage(PageViewport pageViewport) throws IOException { + // TODO clone? pageViewportList.add(pageViewport.clone()); currentPageNumber++; } @@ -331,8 +324,9 @@ public abstract class Java2DRenderer extends AbstractRenderer implements Printab /** - * Returns the page viewport + * Returns a page viewport. * @param pageNum the page number + * @return the requested PageViewport instance * @exception FOPException If the page is out of range. */ public PageViewport getPageViewport(int pageNum) throws FOPException { @@ -350,67 +344,12 @@ public abstract class Java2DRenderer extends AbstractRenderer implements Printab * @param pageNum the 0-based page number to generate * @return the java.awt.image.BufferedImage corresponding to * the page or null if the page doesn't exist. - * @throws FOPException + * @throws FOPException If there's a problem preparing the page image */ public BufferedImage getPageImage(int pageNum) throws FOPException { return getPageImage(getPageViewport(pageNum)); } - /** - * 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 - * @TODO This is a copy from AbstractPathOrientedRenderer. Put this method in AbstractRenderer - */ - protected void handleRegionTraits(RegionViewport region) { - Rectangle2D viewArea = region.getViewArea(); - float startx = (float)(viewArea.getX() / 1000f); - float starty = (float)(viewArea.getY() / 1000f); - float width = (float)(viewArea.getWidth() / 1000f); - float height = (float)(viewArea.getHeight() / 1000f); - - if (region.getRegionReference().getRegionClass() == FO_REGION_BODY) { - currentBPPosition = region.getBorderAndPaddingWidthBefore(); - currentIPPosition = region.getBorderAndPaddingWidthStart(); - } - drawBackAndBorders(region, startx, starty, width, height); - } - - /** - * Render an inline viewport. - * This renders an inline viewport by clipping if necessary. - * @param viewport the viewport to handle - * @TODO This is a copy from AbstractPathOrientedRenderer. Put this method in AbstractRenderer - */ - public void renderViewport(Viewport viewport) { - - float x = currentIPPosition / 1000f; - float y = (currentBPPosition + viewport.getOffset()) / 1000f; - float width = viewport.getIPD() / 1000f; - float height = viewport.getBPD() / 1000f; - // TODO: Calculate the border rect correctly. - float borderPaddingStart = viewport.getBorderAndPaddingWidthStart() / 1000f; - float borderPaddingBefore = viewport.getBorderAndPaddingWidthBefore() / 1000f; - float bpwidth = borderPaddingStart - + (viewport.getBorderAndPaddingWidthEnd() / 1000f); - float bpheight = borderPaddingBefore - + (viewport.getBorderAndPaddingWidthAfter() / 1000f); - - drawBackAndBorders(viewport, x, y, width + bpwidth, height + bpheight); - - if (viewport.getClip()) { - saveGraphicsState(); - - clipRect(x + borderPaddingStart, y + borderPaddingBefore, width, height); - } - super.renderViewport(viewport); - - if (viewport.getClip()) { - restoreGraphicsState(); - } - } - /** Saves the graphics state of the rendering engine. */ protected void saveGraphicsState() { // push (and save) the current graphics state @@ -449,467 +388,298 @@ public abstract class Java2DRenderer extends AbstractRenderer implements Printab } /** - * @see org.apache.fop.render.AbstractRenderer - * @TODO This is a copy from AbstractPathOrientedRenderer. Put this method in AbstractRenderer + * @see org.apache.fop.render.AbstractPathOrientedRenderer#restoreStateStackAfterBreakOut( + * java.util.List) */ - protected void renderInlineSpace(Space space) { - space.setBPD(0); - renderInlineAreaBackAndBorders(space); - super.renderInlineSpace(space); + protected void restoreStateStackAfterBreakOut(List breakOutList) { + log.debug( + "Block.FIXED --> restoring context after break-out"); + Graphics2D graph; + Iterator i = breakOutList.iterator(); + while (i.hasNext()) { + graph = (Graphics2D) i.next(); + log.debug("Restoring: " + graph); + state.push(); + } } - + /** - * @see org.apache.fop.render.AbstractRenderer - * @TODO This is a copy from AbstractPathOrientedRenderer. Put this method in AbstractRenderer + * @see org.apache.fop.render.AbstractPathOrientedRenderer#breakOutOfStateStack() */ - protected void renderInlineParent(InlineParent ip) { - renderInlineAreaBackAndBorders(ip); - super.renderInlineParent(ip); + protected List breakOutOfStateStack() { + List breakOutList; + log.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 + log.debug("Adding to break out list: " + graph); + } + return breakOutList; } /** - * @see org.apache.fop.render.AbstractRenderer - * @TODO This is a copy from AbstractPathOrientedRenderer. Put this method in AbstractRenderer + * @see org.apache.fop.render.AbstractPathOrientedRenderer#updateColor( + * org.apache.fop.datatypes.ColorType, boolean) */ - protected void renderInlineBlockParent(InlineBlockParent ibp) { - renderInlineAreaBackAndBorders(ibp); - super.renderInlineBlockParent(ibp); + protected void updateColor(ColorType col, boolean fill) { + state.updateColor(toColor(col)); } - + /** - * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport, - * List) + * @see org.apache.fop.render.AbstractPathOrientedRenderer#clip() */ - 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; - // This is the content-rect - float width = (float) bv.getIPD() / 1000f; - float height = (float) bv.getBPD() / 1000f; - - - if (bv.getPositioning() == Block.ABSOLUTE - || bv.getPositioning() == Block.FIXED) { - - currentIPPosition = bv.getXOffset(); - currentBPPosition = bv.getYOffset(); - - // 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) { - log.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 - log.debug("Adding to break out list: " + graph); - } - } - - CTM tempctm = new CTM(containingIPPosition, containingBPPosition); - ctm = tempctm.multiply(ctm); - - // Adjust for spaces (from margin or indirectly by start-indent etc. - x += bv.getSpaceStart() / 1000f; - currentIPPosition += bv.getSpaceStart(); - - y += bv.getSpaceBefore() / 1000f; - currentBPPosition += bv.getSpaceBefore(); - - 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 - currentIPPosition += borderPaddingStart; - currentBPPosition += borderPaddingBefore; - - Rectangle2D clippingRect = null; - if (bv.getClip()) { - clippingRect = new Rectangle(currentIPPosition, currentBPPosition, - bv.getIPD(), bv.getBPD()); - } - - startVParea(ctm, clippingRect); - currentIPPosition = 0; - currentBPPosition = 0; - renderBlocks(bv, children); - endVParea(); - - if (breakOutList != null) { - log.debug( - "Block.FIXED --> restoring context after break-out"); - Graphics2D graph; - Iterator i = breakOutList.iterator(); - while (i.hasNext()) { - graph = (Graphics2D) i.next(); - log.debug("Restoring: " + graph); - state.push(); - } - } - - currentIPPosition = saveIP; - currentBPPosition = saveBP; - - } else { // orientation = Block.STACK or RELATIVE - - currentBPPosition += bv.getSpaceBefore(); - - // borders and background in the old coordinate system - handleBlockTraits(bv); - - //Advance to start of content area - currentIPPosition += bv.getStartIndent(); - - CTM tempctm = new CTM(containingIPPosition, currentBPPosition - + containingBPPosition); - ctm = tempctm.multiply(ctm); - - // Now adjust for border/padding - x += borderPaddingStart / 1000f; - y += borderPaddingBefore / 1000f; - - Rectangle2D clippingRect = null; - if (bv.getClip()) { - clippingRect = new Rectangle(currentIPPosition, currentBPPosition, - bv.getIPD(), bv.getBPD()); - } - - startVParea(ctm, clippingRect); - currentIPPosition = 0; - currentBPPosition = 0; - renderBlocks(bv, children); - endVParea(); - - currentIPPosition = saveIP; - currentBPPosition = saveBP; - - currentBPPosition += (int)(bv.getAllocBPD()); + protected void clip() { + if (currentPath == null) { + throw new IllegalStateException("No current path available!"); } + state.updateClip(currentPath); + currentPath = null; } /** - * Clip an area. write a clipping operation given coordinates in the current - * transform. Coordinates are in points. - * - * @param x the x coordinate - * @param y the y coordinate - * @param width the width of the area - * @param height the height of the area + * @see org.apache.fop.render.AbstractPathOrientedRenderer#closePath() */ - protected void clipRect(float x, float y, float width, float height) { - Rectangle2D rect = new Rectangle2D.Float(x, y, width, height); - state.updateClip(rect); + protected void closePath() { + currentPath.closePath(); } /** - * Draw the background and borders. This draws the background and border - * traits for an area given the position. - * - * @param area the area whose traits are used - * @param startx the start x position - * @param starty the start y position - * @param width the width of the area - * @param height the height of the area + * @see org.apache.fop.render.AbstractPathOrientedRenderer#lineTo(float, float) */ - protected void drawBackAndBorders(Area area, float startx, float starty, - float width, float 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) 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) { - drawBackground(back, sx, sy, paddRectWidth, paddRectHeight); - } - - // background image - if (back.getFopImage() != null) { - FopImage fopimage = back.getFopImage(); - if (fopimage != null && fopimage.load(FopImage.DIMENSIONS)) { - saveGraphicsState(); - clipRect(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 - } - } - restoreGraphicsState(); - } else { - log.warn( - "Can't find background image: " + back.getURL()); - } - } - } - - // draw border - // BORDER_BEFORE - if (bpsBefore != null) { - int borderWidth = (int) Math.round((bpsBefore.width / 1000f)); - state.updateColor(bpsBefore.color); - state.getGraph().fillRect((int) startx, (int) starty, (int) width, - borderWidth); - } - // BORDER_AFTER - if (bpsAfter != null) { - int borderWidth = (int) Math.round((bpsAfter.width / 1000f)); - float sy = starty + height; - state.updateColor(bpsAfter.color); - state.getGraph().fillRect((int) startx, - (int) (starty + height - borderWidth), (int) width, - borderWidth); - } - // BORDER_START - if (bpsStart != null) { - int borderWidth = (int) Math.round((bpsStart.width / 1000f)); - state.updateColor(bpsStart.color); - state.getGraph().fillRect((int) startx, (int) starty, borderWidth, - (int) height); - } - // BORDER_END - if (bpsEnd != null) { - int borderWidth = (int) Math.round((bpsEnd.width / 1000f)); - float sx = startx + width; - state.updateColor(bpsEnd.color); - state.getGraph().fillRect((int) (startx + width - borderWidth), - (int) starty, borderWidth, (int) height); + protected void lineTo(float x, float y) { + if (currentPath == null) { + currentPath = new GeneralPath(); } + currentPath.lineTo(x, y); } /** - * 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. + * @see org.apache.fop.render.AbstractPathOrientedRenderer#moveTo(float, float) */ - 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); - } - - /** - * Common method to render the background and borders for any inline area. - * The all borders and padding are drawn outside the specified area. - * @param area the inline area for which the background, border and padding is to be - * rendered - * @TODO This is a copy from AbstractPathOrientedRenderer. Put this method in AbstractRenderer - */ - protected void renderInlineAreaBackAndBorders(InlineArea area) { - float x = currentIPPosition / 1000f; - float y = (currentBPPosition + area.getOffset()) / 1000f; - float width = area.getIPD() / 1000f; - float height = area.getBPD() / 1000f; - float borderPaddingStart = area.getBorderAndPaddingWidthStart() / 1000f; - float borderPaddingBefore = area.getBorderAndPaddingWidthBefore() / 1000f; - float bpwidth = borderPaddingStart - + (area.getBorderAndPaddingWidthEnd() / 1000f); - float bpheight = borderPaddingBefore - + (area.getBorderAndPaddingWidthAfter() / 1000f); - - if (height != 0.0f || bpheight != 0.0f && bpwidth != 0.0f) { - drawBackAndBorders(area, x, y - borderPaddingBefore - , width + bpwidth - , height + bpheight); + protected void moveTo(float x, float y) { + if (currentPath == null) { + currentPath = new GeneralPath(); } - + currentPath.moveTo(x, y); } /** - * 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 + * @see org.apache.fop.render.AbstractPathOrientedRenderer#clipRect(float, float, float, float) */ - 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); + protected void clipRect(float x, float y, float width, float height) { + state.updateClip(new Rectangle2D.Float(x, y, width, height)); } /** - * Returns a Font object constructed based on the font traits in an area - * @param area the area from which to retrieve the font triplet information - * @return the requested Font instance or null if not found + * @see org.apache.fop.render.AbstractPathOrientedRenderer#fillRect(float, float, float, float) */ - protected Font getFontFromArea(Area area) { - FontTriplet triplet = (FontTriplet)area.getTrait(Trait.FONT); - int size = ((Integer)area.getTrait(Trait.FONT_SIZE)).intValue(); - return fontInfo.getFontInstance(triplet, size); + protected void fillRect(float x, float y, float width, float height) { + state.getGraph().fill(new Rectangle2D.Float(x, y, width, height)); } + /** + * @see org.apache.fop.render.AbstractPathOrientedRenderer#drawBorderLine( + * float, float, float, float, boolean, boolean, int, + * org.apache.fop.datatypes.ColorType) + */ + protected void drawBorderLine(float x1, float y1, float x2, float y2, + boolean horz, boolean startOrBefore, int style, ColorType col) { + Graphics2D g2d = state.getGraph(); + drawBorderLine(new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1), + horz, startOrBefore, style, toColor(col), g2d); + } + + /** + * Draw a border segment of an XSL-FO style border. + * @param lineRect the line defined by its bounding rectangle + * @param horz true for horizontal border segments, false for vertical border segments + * @param startOrBefore true for border segments on the start or before edge, + * false for end or after. + * @param style the border style (one of Constants.EN_DASHED etc.) + * @param col the color for the border segment + * @param g2d the Graphics2D instance to paint to + */ + public static void drawBorderLine(Rectangle2D.Float lineRect, + boolean horz, boolean startOrBefore, int style, Color col, Graphics2D g2d) { + float x1 = lineRect.x; + float y1 = lineRect.y; + float x2 = x1 + lineRect.width; + float y2 = y1 + lineRect.height; + float w = lineRect.width; + float h = lineRect.height; + if ((w < 0) || (h < 0)) { + log.error("Negative extent received. Border won't be painted."); + return; + } + switch (style) { + case Constants.EN_DASHED: + g2d.setColor(col); + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int)(w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + float ym = y1 + (h / 2); + BasicStroke s = new BasicStroke(h, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, 10.0f, new float[] {unit}, 0); + g2d.setStroke(s); + g2d.draw(new Line2D.Float(x1, ym, x2, ym)); + } else { + float unit = Math.abs(2 * w); + int rep = (int)(h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + float xm = x1 + (w / 2); + BasicStroke s = new BasicStroke(w, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, 10.0f, new float[] {unit}, 0); + g2d.setStroke(s); + g2d.draw(new Line2D.Float(xm, y1, xm, y2)); + } + break; + case Constants.EN_DOTTED: + g2d.setColor(col); + if (horz) { + float unit = Math.abs(2 * h); + int rep = (int)(w / unit); + if (rep % 2 == 0) { + rep++; + } + unit = w / rep; + float ym = y1 + (h / 2); + BasicStroke s = new BasicStroke(h, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_MITER, 10.0f, new float[] {0, unit}, 0); + g2d.setStroke(s); + g2d.draw(new Line2D.Float(x1, ym, x2, ym)); + } else { + float unit = Math.abs(2 * w); + int rep = (int)(h / unit); + if (rep % 2 == 0) { + rep++; + } + unit = h / rep; + float xm = x1 + (w / 2); + BasicStroke s = new BasicStroke(w, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_MITER, 10.0f, new float[] {0, unit}, 0); + g2d.setStroke(s); + g2d.draw(new Line2D.Float(xm, y1, xm, y2)); + } + break; + case Constants.EN_DOUBLE: + g2d.setColor(col); + if (horz) { + float h3 = h / 3; + float ym1 = y1 + (h3 / 2); + float ym2 = ym1 + h3 + h3; + BasicStroke s = new BasicStroke(h3); + g2d.setStroke(s); + g2d.draw(new Line2D.Float(x1, ym1, x2, ym1)); + g2d.draw(new Line2D.Float(x1, ym2, x2, ym2)); + } else { + float w3 = w / 3; + float xm1 = x1 + (w3 / 2); + float xm2 = xm1 + w3 + w3; + BasicStroke s = new BasicStroke(w3); + g2d.setStroke(s); + g2d.draw(new Line2D.Float(xm1, y1, xm1, y2)); + g2d.draw(new Line2D.Float(xm2, y1, xm2, y2)); + } + break; + case Constants.EN_GROOVE: + case Constants.EN_RIDGE: + float colFactor = (style == EN_GROOVE ? 0.4f : -0.4f); + if (horz) { + Color uppercol = lightenColor(col, -colFactor); + Color lowercol = lightenColor(col, colFactor); + float h3 = h / 3; + float ym1 = y1 + (h3 / 2); + g2d.setStroke(new BasicStroke(h3)); + g2d.setColor(uppercol); + g2d.draw(new Line2D.Float(x1, ym1, x2, ym1)); + g2d.setColor(col); + g2d.draw(new Line2D.Float(x1, ym1 + h3, x2, ym1 + h3)); + g2d.setColor(lowercol); + g2d.draw(new Line2D.Float(x1, ym1 + h3 + h3, x2, ym1 + h3 + h3)); + } else { + Color leftcol = lightenColor(col, -colFactor); + Color rightcol = lightenColor(col, colFactor); + float w3 = w / 3; + float xm1 = x1 + (w3 / 2); + g2d.setStroke(new BasicStroke(w3)); + g2d.setColor(leftcol); + g2d.draw(new Line2D.Float(xm1, y1, xm1, y2)); + g2d.setColor(col); + g2d.draw(new Line2D.Float(xm1 + w3, y1, xm1 + w3, y2)); + g2d.setColor(rightcol); + g2d.draw(new Line2D.Float(xm1 + w3 + w3, y1, xm1 + w3 + w3, y2)); + } + break; + case Constants.EN_INSET: + case Constants.EN_OUTSET: + colFactor = (style == EN_OUTSET ? 0.4f : -0.4f); + if (horz) { + col = lightenColor(col, (startOrBefore ? 1 : -1) * colFactor); + g2d.setStroke(new BasicStroke(h)); + float ym1 = y1 + (h / 2); + g2d.setColor(col); + g2d.draw(new Line2D.Float(x1, ym1, x2, ym1)); + } else { + col = lightenColor(col, (startOrBefore ? 1 : -1) * colFactor); + float xm1 = x1 + (w / 2); + g2d.setStroke(new BasicStroke(w)); + g2d.setColor(col); + g2d.draw(new Line2D.Float(xm1, y1, xm1, y2)); + } + break; + case Constants.EN_HIDDEN: + break; + default: + g2d.setColor(col); + if (horz) { + float ym = y1 + (h / 2); + g2d.setStroke(new BasicStroke(h)); + g2d.draw(new Line2D.Float(x1, ym, x2, ym)); + } else { + float xm = x1 + (w / 2); + g2d.setStroke(new BasicStroke(w)); + g2d.draw(new Line2D.Float(xm, y1, xm, y2)); + } + } + } + /** * @see org.apache.fop.render.AbstractRenderer#renderText(TextArea) */ public void renderText(TextArea text) { renderInlineAreaBackAndBorders(text); - float x = currentIPPosition + text.getBorderAndPaddingWidthStart(); - float y = currentBPPosition + text.getOffset() + text.getBaselineOffset(); // baseline + int rx = currentIPPosition + text.getBorderAndPaddingWidthStart(); + int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset(); Font font = getFontFromArea(text); state.updateFont(font.getFontName(), font.getFontSize(), null); ColorType ct = (ColorType) text.getTrait(Trait.COLOR); - state.updateColor(ct, false, null); + state.updateColor(ct); String s = text.getText(); - state.getGraph().drawString(s, x / 1000f, y / 1000f); - - // getLogger().debug("renderText(): \"" + s + "\", x: " - // + x + ", y: " + y + state); - - // rendering text decorations + state.getGraph().drawString(s, rx / 1000f, bl / 1000f); super.renderText(text); - renderTextDecoration(font, text, y, x); - } - - /** - * 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); - } - } + // rendering text decorations + Typeface tf = (Typeface) fontInfo.getFonts().get(font.getFontName()); + int fontsize = text.getTraitAsInteger(Trait.FONT_SIZE); + renderTextDecoration(tf, fontsize, text, bl, rx); } /** @@ -929,80 +699,62 @@ public abstract class Java2DRenderer extends AbstractRenderer implements Printab float endx = (currentIPPosition + area.getBorderAndPaddingWidthStart() + area.getIPD()) / 1000f; - ColorType ct = (ColorType) area.getTrait(Trait.COLOR); - state.updateColor(ct, true, null); + ColorType col = (ColorType) area.getTrait(Trait.COLOR); + state.updateColor(col); + Line2D line = new Line2D.Float(); line.setLine(startx, starty, endx, starty); - float thickness = area.getRuleThickness() / 1000f; + float ruleThickness = 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); - + drawBorderLine(startx, starty, endx, starty + ruleThickness, + true, true, style, col); 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.) - - 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); + case EN_DOTTED: + //TODO Dots should be shifted to the left by ruleThickness / 2 + state.updateStroke(ruleThickness, style); + float rt2 = ruleThickness / 2f; + line.setLine(line.getX1(), line.getY1() + rt2, line.getX2(), line.getY2() + rt2); 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_GROOVE: 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; + float half = area.getRuleThickness() / 2000f; + + state.updateColor(lightenColor(toColor(col), 0.6f)); + moveTo(startx, starty); + lineTo(endx, starty); + lineTo(endx, starty + 2 * half); + lineTo(startx, starty + 2 * half); + closePath(); + state.getGraph().fill(currentPath); + currentPath = null; + state.updateColor(toColor(col)); + if (style == EN_GROOVE) { + moveTo(startx, starty); + lineTo(endx, starty); + lineTo(endx, starty + half); + lineTo(startx + half, starty + half); + lineTo(startx, starty + 2 * half); + } else { + moveTo(endx, starty); + lineTo(endx, starty + 2 * half); + lineTo(startx, starty + 2 * half); + lineTo(startx, starty + half); + lineTo(endx - half, starty + half); + } + closePath(); + state.getGraph().fill(currentPath); + currentPath = null; case EN_NONE: // No rule is drawn break; - + default: } // end switch super.renderLeader(area); @@ -1015,20 +767,18 @@ public abstract class Java2DRenderer extends AbstractRenderer implements Printab public void renderImage(Image image, Rectangle2D pos) { // endTextObject(); String url = image.getURL(); - putImage(url, pos); + drawImage(url, pos); } /** - * Draws an image - * - * @param pUrl URL of the bitmap - * @param pos Position of the bitmap + * @see org.apache.fop.render.AbstractPathOrientedRenderer#drawImage( + * java.lang.String, java.awt.geom.Rectangle2D) */ - protected void putImage(String pUrl, Rectangle2D pos) { + protected void drawImage(String url, Rectangle2D pos) { - int x = currentIPPosition; // TODO + area.getXOffset(); - int y = currentBPPosition; - String url = ImageFactory.getURL(pUrl); + int x = currentIPPosition + (int)Math.round(pos.getX()); + int y = currentBPPosition + (int)Math.round(pos.getY()); + url = ImageFactory.getURL(url); ImageFactory fact = userAgent.getFactory().getImageFactory(); FopImage fopimage = fact.getImage(url, userAgent); @@ -1060,21 +810,6 @@ public abstract class Java2DRenderer extends AbstractRenderer implements Printab renderDocument(doc, ns, pos); } else if ("image/eps".equals(mime)) { log.warn("EPS images are not supported by this renderer"); - } 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)(pos.getWidth() / 1000f), (int)(pos.getHeight() / 1000f), null); } else { if (!fopimage.load(FopImage.BITMAP)) { log.warn("Loading of bitmap failed: " + url); @@ -1090,7 +825,7 @@ public abstract class Java2DRenderer extends AbstractRenderer implements Printab 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.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, @@ -1155,23 +890,33 @@ public abstract class Java2DRenderer extends AbstractRenderer implements Printab Graphics2D graphics = (Graphics2D) g; Java2DGraphicsState oldState = state; - BufferedImage image; try { - PageViewport viewport = getPageViewport(pageIndex); - AffineTransform at = graphics.getTransform(); - state = new Java2DGraphicsState(graphics, this.fontInfo, at); + PageViewport viewport = getPageViewport(pageIndex); + AffineTransform at = graphics.getTransform(); + state = new Java2DGraphicsState(graphics, this.fontInfo, at); - // reset the current Positions - currentBPPosition = 0; - currentIPPosition = 0; + // reset the current Positions + currentBPPosition = 0; + currentIPPosition = 0; - renderPageAreas(viewport.getPage()); - return PAGE_EXISTS; + renderPageAreas(viewport.getPage()); + return PAGE_EXISTS; } catch (FOPException e) { log.error(e); return NO_SUCH_PAGE; } finally { - oldState = state; + state = oldState; } } + + /** @see org.apache.fop.render.AbstractPathOrientedRenderer#beginTextObject() */ + protected void beginTextObject() { + //not necessary in Java2D + } + + /** @see org.apache.fop.render.AbstractPathOrientedRenderer#endTextObject() */ + protected void endTextObject() { + //not necessary in Java2D + } + } diff --git a/src/java/org/apache/fop/render/java2d/RendererState.java b/src/java/org/apache/fop/render/java2d/RendererState.java index 3253294f5..ff7933f34 100644 --- a/src/java/org/apache/fop/render/java2d/RendererState.java +++ b/src/java/org/apache/fop/render/java2d/RendererState.java @@ -48,15 +48,6 @@ public interface RendererState { */ 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. diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java index e97068968..3847e14ee 100644 --- a/src/java/org/apache/fop/render/xml/XMLRenderer.java +++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java @@ -604,6 +604,14 @@ public class XMLRenderer extends PrintRenderer { //only necessary for graphical output } + /** + * @see org.apache.fop.render.AbstractRenderer#renderInlineAreaBackAndBorders( + * org.apache.fop.area.inline.InlineArea) + */ + protected void renderInlineAreaBackAndBorders(InlineArea area) { + //only necessary for graphical output + } + /** * @see org.apache.fop.render.AbstractRenderer#renderBeforeFloat(BeforeFloat) */ diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLGenerator.java b/src/sandbox/org/apache/fop/render/pcl/PCLGenerator.java index e0d3f9305..4e647a977 100644 --- a/src/sandbox/org/apache/fop/render/pcl/PCLGenerator.java +++ b/src/sandbox/org/apache/fop/render/pcl/PCLGenerator.java @@ -160,10 +160,45 @@ public class PCLGenerator { * @param y the Y coordinate (in millipoints) * @throws IOException In case of an I/O error */ - public void setCursorPos(int x, int y) throws IOException { - writeCommand("*p" + (x / 100) + "h" + (y / 100) + "V"); + public void setCursorPos(double x, double y) throws IOException { + if (x < 0) { + //A negative x value will result in a relative movement so go to "0" first. + //But this will most probably have no effect anyway since you can't paint to the left + //of the logical page + writeCommand("&a0h" + formatDouble2(x / 100) + "h" + formatDouble2(y / 100) + "V"); + } else { + writeCommand("&a" + formatDouble2(x / 100) + "h" + formatDouble2(y / 100) + "V"); + } } + /** + * Enters the HP GL/2 mode. + * @param restorePreviousHPGL2Cursor true if the previous HP GL/2 pen position should be + * restored, false if the current position is maintained + * @throws IOException In case of an I/O error + */ + public void enterHPGL2Mode(boolean restorePreviousHPGL2Cursor) throws IOException { + if (restorePreviousHPGL2Cursor) { + writeCommand("%0B"); + } else { + writeCommand("%1B"); + } + } + + /** + * Enters the PCL mode. + * @param restorePreviousPCLCursor true if the previous PCL cursor position should be restored, + * false if the current position is maintained + * @throws IOException In case of an I/O error + */ + public void enterPCLMode(boolean restorePreviousPCLCursor) throws IOException { + if (restorePreviousPCLCursor) { + writeCommand("%0A"); + } else { + writeCommand("%1A"); + } + } + /** * Generate a filled rectangle * @@ -428,24 +463,6 @@ public class PCLGenerator { int lastcount = -1; byte lastbyte = 0; int rlewidth = 0; - /* - int xres = (iw * 72000) / w; - int yres = (ih * 72000) / h; - int resolution = xres; - if (yres > xres) - resolution = yres; - - if (resolution > 300) - resolution = 600; - else if (resolution > 150) - resolution = 300; - else if (resolution > 100) - resolution = 150; - else if (resolution > 75) - resolution = 100; - else - resolution = 75; - */ // Transfer graphics data for (y = 0; y < imgh; y++) { @@ -498,5 +515,5 @@ public class PCLGenerator { // End raster graphics writeCommand("*rB"); } - + } diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2D.java b/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2D.java index 5e976505f..fe2d6a12a 100644 --- a/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2D.java +++ b/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2D.java @@ -20,6 +20,7 @@ package org.apache.fop.render.pcl; import java.awt.BasicStroke; import java.awt.Color; +import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; @@ -28,6 +29,8 @@ import java.awt.Image; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; @@ -50,6 +53,9 @@ public class PCLGraphics2D extends AbstractGraphics2D { /** The PCL generator */ protected PCLGenerator gen; + private boolean failOnUnsupportedFeature = true; + private boolean clippingDisabled = false; + /** * Create a new PCLGraphics2D. * @param gen the PCL Generator to paint with @@ -70,7 +76,9 @@ public class PCLGraphics2D extends AbstractGraphics2D { /** @see java.awt.Graphics#create() */ public Graphics create() { - return new PCLGraphics2D(this); + PCLGraphics2D copy = new PCLGraphics2D(this); + copy.setGraphicContext((GraphicContext)getGraphicContext().clone()); + return copy; } /** @see java.awt.Graphics#dispose() */ @@ -95,6 +103,18 @@ public class PCLGraphics2D extends AbstractGraphics2D { ioe.printStackTrace(); } + /** + * Raises an UnsupportedOperationException if this instance is configured to do so and an + * unsupported feature has been requested. Clients can make use of this to fall back to + * a more compatible way of painting a PCL graphic. + * @param msg the error message to be displayed + */ + protected void handleUnsupportedFeature(String msg) { + if (this.failOnUnsupportedFeature) { + throw new UnsupportedOperationException(msg); + } + } + /** @see java.awt.Graphics2D#getDeviceConfiguration() */ public GraphicsConfiguration getDeviceConfiguration() { return GraphicsEnvironment.getLocalGraphicsEnvironment(). @@ -114,18 +134,35 @@ public class PCLGraphics2D extends AbstractGraphics2D { if (da != null) { gen.writeText("UL1,"); - for (int idx = 0, len = Math.min(20, da.length); idx < len; idx++) { - gen.writeText(gen.formatDouble4(da[idx])); + int len = Math.min(20, da.length); + float patternLen = 0.0f; + for (int idx = 0; idx < len; idx++) { + patternLen += da[idx]; + } + if (len == 1) { + patternLen *= 2; + } + for (int idx = 0; idx < len; idx++) { + float perc = da[idx] * 100 / patternLen; + gen.writeText(gen.formatDouble2(perc)); if (idx < da.length - 1) { gen.writeText(","); } } + if (len == 1) { + gen.writeText("," + gen.formatDouble2(da[0] * 100 / patternLen )); + + } gen.writeText(";"); /* TODO Dash phase NYI float offset = bs.getDashPhase(); gen.writeln(gen.formatDouble4(offset) + " setdash"); */ - gen.writeText("LT1;"); + Point2D ptLen = new Point2D.Double(patternLen, 0); + //interpret as absolute length + getTransform().deltaTransform(ptLen, ptLen); + double transLen = UnitConv.pt2mm(ptLen.distance(0, 0)); + gen.writeText("LT1," + gen.formatDouble4(transLen) + ",1;"); } else { gen.writeText("LT;"); } @@ -166,13 +203,13 @@ public class PCLGraphics2D extends AbstractGraphics2D { float lw = bs.getLineWidth(); Point2D ptSrc = new Point2D.Double(lw, 0); //Pen widths are set as absolute metric values (WU0;) - Point2D ptDest = getTransform().transform(ptSrc, null); + Point2D ptDest = getTransform().deltaTransform(ptSrc, null); double transDist = UnitConv.pt2mm(ptDest.distance(0, 0)); //System.out.println("--" + ptDest.distance(0, 0) + " " + transDist); gen.writeText(";PW" + gen.formatDouble4(transDist) + ";"); } else { - System.err.println("Unsupported Stroke: " + stroke.getClass().getName()); + handleUnsupportedFeature("Unsupported Stroke: " + stroke.getClass().getName()); } } @@ -187,7 +224,31 @@ public class PCLGraphics2D extends AbstractGraphics2D { int shade = gen.convertToPCLShade(col); gen.writeText("TR0;FT10," + shade + ";"); } else { - System.err.println("Unsupported Paint: " + paint.getClass().getName()); + handleUnsupportedFeature("Unsupported Paint: " + paint.getClass().getName()); + } + } + + private void writeClip(Shape imclip) throws IOException { + if (clippingDisabled) { + return; + } + if (imclip == null) { + //gen.writeText("IW;"); + } else { + handleUnsupportedFeature("Clipping is not supported. Shape: " + imclip); + /* This is an attempt to clip using the "InputWindow" (IW) but this only allows to + * clip a rectangular area. Force falling back to bitmap mode for now. + Rectangle2D bounds = imclip.getBounds2D(); + Point2D p1 = new Point2D.Double(bounds.getX(), bounds.getY()); + Point2D p2 = new Point2D.Double( + bounds.getX() + bounds.getWidth(), bounds.getY() + bounds.getHeight()); + getTransform().transform(p1, p1); + getTransform().transform(p2, p2); + gen.writeText("IW" + gen.formatDouble4(p1.getX()) + + "," + gen.formatDouble4(p2.getY()) + + "," + gen.formatDouble4(p2.getX()) + + "," + gen.formatDouble4(p1.getY()) + ";"); + */ } } @@ -197,16 +258,17 @@ public class PCLGraphics2D extends AbstractGraphics2D { AffineTransform trans = getTransform(); Shape imclip = getClip(); - //writeClip(imclip); - //establishColor(getColor()); + writeClip(imclip); - applyPaint(getPaint()); + if (!Color.black.equals(getColor())) { + //TODO PCL 5 doesn't support colored pens, PCL5c has a pen color (PC) command + handleUnsupportedFeature("Only black is supported as stroke color: " + getColor()); + } applyStroke(getStroke()); - //gen.writeln("newpath"); PathIterator iter = s.getPathIterator(trans); - processPathIterator(iter); - gen.writeText("EP;"); + processPathIteratorStroke(iter); + writeClip(null); } catch (IOException ioe) { handleIOException(ioe); } @@ -217,16 +279,13 @@ public class PCLGraphics2D extends AbstractGraphics2D { try { AffineTransform trans = getTransform(); Shape imclip = getClip(); - //writeClip(imclip); + writeClip(imclip); - //establishColor(getColor()); - applyPaint(getPaint()); PathIterator iter = s.getPathIterator(trans); - processPathIterator(iter); - int fillMethod = (iter.getWindingRule() == PathIterator.WIND_EVEN_ODD ? 0 : 1); - gen.writeText("FP" + fillMethod + ";"); + processPathIteratorFill(iter); + writeClip(null); } catch (IOException ioe) { handleIOException(ioe); } @@ -237,97 +296,163 @@ public class PCLGraphics2D extends AbstractGraphics2D { * @param iter PathIterator to process * @throws IOException In case of an I/O problem. */ - public void processPathIterator(PathIterator iter) throws IOException { + public void processPathIteratorStroke(PathIterator iter) throws IOException { + gen.writeText("\n"); double[] vals = new double[6]; boolean penDown = false; - boolean hasFirst = false; - double x = 0, firstX = 0; - double y = 0, firstY = 0; - boolean pendingPM0 = true; - penUp(); + double x = 0; + double y = 0; + StringBuffer sb = new StringBuffer(256); + penUp(sb); while (!iter.isDone()) { int type = iter.currentSegment(vals); if (type == PathIterator.SEG_CLOSE) { - hasFirst = false; - /* - if (firstX != x && firstY != y) { - plotAbsolute(firstX, firstY); - }*/ - //penUp(); - gen.writeText("PM1;"); + gen.writeText("PM;"); + gen.writeText(sb.toString()); + gen.writeText("PM2;EP;"); + sb.setLength(0); iter.next(); continue; - } - if (type == PathIterator.SEG_MOVETO) { + } else if (type == PathIterator.SEG_MOVETO) { + gen.writeText(sb.toString()); + sb.setLength(0); if (penDown) { - penUp(); + penUp(sb); penDown = false; } } else { if (!penDown) { - penDown(); + penDown(sb); penDown = true; } } switch (type) { + case PathIterator.SEG_CLOSE: + break; + case PathIterator.SEG_MOVETO: + x = vals[0]; + y = vals[1]; + plotAbsolute(x, y, sb); + gen.writeText(sb.toString()); + sb.setLength(0); + break; + case PathIterator.SEG_LINETO: + x = vals[0]; + y = vals[1]; + plotAbsolute(x, y, sb); + break; case PathIterator.SEG_CUBICTO: x = vals[4]; y = vals[5]; - bezierAbsolute(vals[0], vals[1], vals[2], vals[3], x, y); + bezierAbsolute(vals[0], vals[1], vals[2], vals[3], x, y, sb); break; - case PathIterator.SEG_LINETO: + case PathIterator.SEG_QUADTO: + double originX = x; + double originY = y; + x = vals[2]; + y = vals[3]; + quadraticBezierAbsolute(originX, originY, vals[0], vals[1], x, y, sb); + break; + default: + break; + } + iter.next(); + } + sb.append("\n"); + gen.writeText(sb.toString()); + } + + /** + * Processes a path iterator generating the nexessary painting operations. + * @param iter PathIterator to process + * @throws IOException In case of an I/O problem. + */ + public void processPathIteratorFill(PathIterator iter) throws IOException { + gen.writeText("\n"); + double[] vals = new double[6]; + boolean penDown = false; + double x = 0; + double y = 0; + boolean pendingPM0 = true; + StringBuffer sb = new StringBuffer(256); + penUp(sb); + while (!iter.isDone()) { + int type = iter.currentSegment(vals); + if (type == PathIterator.SEG_CLOSE) { + sb.append("PM1;"); + iter.next(); + continue; + } else if (type == PathIterator.SEG_MOVETO) { + if (penDown) { + penUp(sb); + penDown = false; + } + } else { + if (!penDown) { + penDown(sb); + penDown = true; + } + } + switch (type) { + case PathIterator.SEG_MOVETO: x = vals[0]; y = vals[1]; - plotAbsolute(x, y); + plotAbsolute(x, y, sb); break; - case PathIterator.SEG_MOVETO: + case PathIterator.SEG_LINETO: x = vals[0]; y = vals[1]; - plotAbsolute(x, y); + plotAbsolute(x, y, sb); + break; + case PathIterator.SEG_CUBICTO: + x = vals[4]; + y = vals[5]; + bezierAbsolute(vals[0], vals[1], vals[2], vals[3], x, y, sb); break; case PathIterator.SEG_QUADTO: double originX = x; double originY = y; x = vals[2]; y = vals[3]; - quadraticBezierAbsolute(originX, originY, vals[0], vals[1], x, y); - break; - case PathIterator.SEG_CLOSE: + quadraticBezierAbsolute(originX, originY, vals[0], vals[1], x, y, sb); break; default: - break; + throw new IllegalStateException("Must not get here"); } if (pendingPM0) { pendingPM0 = false; - gen.writeText("PM;"); - } - if (!hasFirst) { - firstX = x; - firstY = y; + sb.append("PM;"); } iter.next(); } - gen.writeText("PM2;"); + sb.append("PM2;"); + fillPolygon(iter.getWindingRule(), sb); + sb.append("\n"); + gen.writeText(sb.toString()); + } + + private void fillPolygon(int windingRule, StringBuffer sb) { + int fillMethod = (windingRule == PathIterator.WIND_EVEN_ODD ? 0 : 1); + sb.append("FP").append(fillMethod).append(";"); } - private void plotAbsolute(double x, double y) throws IOException { - gen.writeText("PA" + gen.formatDouble4(x) + "," - + gen.formatDouble4(y) + ";"); + private void plotAbsolute(double x, double y, StringBuffer sb) { + sb.append("PA").append(gen.formatDouble4(x)); + sb.append(",").append(gen.formatDouble4(y)).append(";"); } - private void bezierAbsolute(double x1, double y1, double x2, double y2, double x3, double y3) - throws IOException { - gen.writeText("BZ" + gen.formatDouble4(x1) + "," - + gen.formatDouble4(y1) + "," - + gen.formatDouble4(x2) + "," - + gen.formatDouble4(y2) + "," - + gen.formatDouble4(x3) + "," - + gen.formatDouble4(y3) + ";"); + private void bezierAbsolute(double x1, double y1, double x2, double y2, double x3, double y3, + StringBuffer sb) { + sb.append("BZ").append(gen.formatDouble4(x1)); + sb.append(",").append(gen.formatDouble4(y1)); + sb.append(",").append(gen.formatDouble4(x2)); + sb.append(",").append(gen.formatDouble4(y2)); + sb.append(",").append(gen.formatDouble4(x3)); + sb.append(",").append(gen.formatDouble4(y3)).append(";"); } private void quadraticBezierAbsolute(double originX, double originY, - double x1, double y1, double x2, double y2) - throws IOException { + double x1, double y1, double x2, double y2, StringBuffer sb) { //Quadratic Bezier curve can be mapped to a normal bezier curve //See http://pfaedit.sourceforge.net/bezier.html double nx1 = originX + (2.0 / 3.0) * (x1 - originX); @@ -336,28 +461,31 @@ public class PCLGraphics2D extends AbstractGraphics2D { double nx2 = nx1 + (1.0 / 3.0) * (x2 - originX); double ny2 = ny1 + (1.0 / 3.0) * (y2 - originY); - bezierAbsolute(nx1, ny1, nx2, ny2, x2, y2); + bezierAbsolute(nx1, ny1, nx2, ny2, x2, y2, sb); } - private void penDown() throws IOException { - gen.writeText("PD;"); + private void penDown(StringBuffer sb) { + sb.append("PD;"); } - private void penUp() throws IOException { - gen.writeText("PU;"); + private void penUp(StringBuffer sb) { + sb.append("PU;"); } /** @see java.awt.Graphics2D#drawString(java.lang.String, float, float) */ public void drawString(String s, float x, float y) { - // TODO Auto-generated method stub - System.err.println("drawString NYI"); + java.awt.Font awtFont = getFont(); + FontRenderContext frc = getFontRenderContext(); + GlyphVector gv = awtFont.createGlyphVector(frc, s); + Shape glyphOutline = gv.getOutline(x, y); + fill(glyphOutline); } /** @see java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float) */ public void drawString(AttributedCharacterIterator iterator, float x, float y) { // TODO Auto-generated method stub - System.err.println("drawString NYI"); + handleUnsupportedFeature("drawString NYI"); } /** @@ -365,8 +493,7 @@ public class PCLGraphics2D extends AbstractGraphics2D { * java.awt.geom.AffineTransform) */ public void drawRenderedImage(RenderedImage img, AffineTransform xform) { - // TODO Auto-generated method stub - System.err.println("drawRenderedImage NYI"); + handleUnsupportedFeature("Bitmap images are not supported"); } /** @@ -374,8 +501,7 @@ public class PCLGraphics2D extends AbstractGraphics2D { * java.awt.geom.AffineTransform) */ public void drawRenderableImage(RenderableImage img, AffineTransform xform) { - // TODO Auto-generated method stub - System.err.println("drawRenderedImage NYI"); + handleUnsupportedFeature("Bitmap images are not supported"); } /** @@ -384,8 +510,7 @@ public class PCLGraphics2D extends AbstractGraphics2D { */ public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { - // TODO Auto-generated method stub - System.err.println("drawImage NYI"); + handleUnsupportedFeature("Bitmap images are not supported"); return false; } @@ -393,21 +518,62 @@ public class PCLGraphics2D extends AbstractGraphics2D { * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, java.awt.image.ImageObserver) */ public boolean drawImage(Image img, int x, int y, ImageObserver observer) { - // TODO Auto-generated method stub - System.err.println("drawImage NYI"); + handleUnsupportedFeature("Bitmap images are not supported"); return false; + /* + * First attempt disabled. + * Reasons: Lack of transparency control, positioning and rotation issues + final int width = img.getWidth(observer); + final int height = img.getHeight(observer); + if (width == -1 || height == -1) { + return false; + } + + Dimension size = new Dimension(width, height); + BufferedImage buf = buildBufferedImage(size); + + java.awt.Graphics2D g = buf.createGraphics(); + try { + g.setComposite(AlphaComposite.SrcOver); + g.setBackground(new Color(255, 255, 255)); + g.setPaint(new Color(255, 255, 255)); + g.fillRect(0, 0, width, height); + g.clip(new Rectangle(0, 0, buf.getWidth(), buf.getHeight())); + + if (!g.drawImage(img, 0, 0, observer)) { + return false; + } + } finally { + g.dispose(); + } + + try { + AffineTransform at = getTransform(); + gen.enterPCLMode(false); + //Shape imclip = getClip(); Clipping is not available in PCL + Point2D p1 = new Point2D.Double(x, y); + at.transform(p1, p1); + pclContext.getTransform().transform(p1, p1); + gen.setCursorPos(p1.getX(), p1.getY()); + gen.paintBitmap(buf, 72); + gen.enterHPGL2Mode(false); + } catch (IOException ioe) { + handleIOException(ioe); + } + + return true;*/ } /** @see java.awt.Graphics#copyArea(int, int, int, int, int, int) */ public void copyArea(int x, int y, int width, int height, int dx, int dy) { // TODO Auto-generated method stub - System.err.println("copyArea NYI"); + handleUnsupportedFeature("copyArea NYI"); } /** @see java.awt.Graphics#setXORMode(java.awt.Color) */ public void setXORMode(Color c1) { // TODO Auto-generated method stub - System.err.println("setXORMode NYI"); + handleUnsupportedFeature("setXORMode NYI"); } /** @@ -422,6 +588,16 @@ public class PCLGraphics2D extends AbstractGraphics2D { fmg = bi.createGraphics(); } + /** + * Creates a buffered image. + * @param size dimensions of the image to be created + * @return the buffered image + */ + protected BufferedImage buildBufferedImage(Dimension size) { + return new BufferedImage(size.width, size.height, + BufferedImage.TYPE_BYTE_GRAY); + } + /** @see java.awt.Graphics#getFontMetrics(java.awt.Font) */ public java.awt.FontMetrics getFontMetrics(java.awt.Font f) { return fmg.getFontMetrics(f); diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java b/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java index 268cc24ec..7f2c9d6cf 100644 --- a/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java +++ b/src/sandbox/org/apache/fop/render/pcl/PCLGraphics2DAdapter.java @@ -27,6 +27,9 @@ import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; +import org.apache.commons.io.output.ByteArrayOutputStream; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.fop.render.Graphics2DAdapter; import org.apache.fop.render.Graphics2DImagePainter; import org.apache.fop.render.RendererContext; @@ -38,6 +41,9 @@ import org.apache.xmlgraphics.java2d.GraphicContext; */ public class PCLGraphics2DAdapter implements Graphics2DAdapter { + /** logging instance */ + private static Log log = LogFactory.getLog(PCLGraphics2DAdapter.class); + /** * Main constructor */ @@ -57,80 +63,84 @@ public class PCLGraphics2DAdapter implements Graphics2DAdapter { float imw = (float)dim.getWidth(); float imh = (float)dim.getHeight(); + boolean painted = false; boolean paintAsBitmap = pclContext.paintAsBitmap(); - if (paintAsBitmap) { + if (!paintAsBitmap) { + ByteArrayOutputStream baout = new ByteArrayOutputStream(); + PCLGenerator tempGen = new PCLGenerator(baout); + try { + GraphicContext ctx = (GraphicContext)pcl.getGraphicContext().clone(); + + AffineTransform prepareHPGL2 = new AffineTransform(); + prepareHPGL2.scale(0.001, 0.001); + ctx.setTransform(prepareHPGL2); + + PCLGraphics2D graphics = new PCLGraphics2D(tempGen); + graphics.setGraphicContext(ctx); + Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, imw, imh); + painter.paint(graphics, area); + + //If we arrive here, the graphic is natively paintable, so write the graphic + pcl.saveGraphicsState(); + pcl.setCursorPos(x, y); + gen.writeCommand("*c" + gen.formatDouble4(width / 100f) + "x" + + gen.formatDouble4(height / 100f) + "Y"); + gen.writeCommand("*c0T"); + gen.enterHPGL2Mode(false); + gen.writeText("\nIN;"); + gen.writeText("SP1;"); + //One Plotter unit is 0.025mm! + double scale = imw / UnitConv.mm2pt(imw * 0.025); + gen.writeText("SC0," + gen.formatDouble4(scale) + + ",0,-" + gen.formatDouble4(scale) + ",2;"); + gen.writeText("IR0,100,0,100;"); + gen.writeText("PU;PA0,0;\n"); + baout.writeTo(gen.getOutputStream()); //Buffer is written to output stream + gen.writeText("\n"); + + gen.enterPCLMode(false); + pcl.restoreGraphicsState(); + painted = true; + } catch (UnsupportedOperationException uoe) { + log.debug( + "Cannot paint graphic natively. Falling back to bitmap painting. Reason: " + + uoe.getMessage()); + } + } + + if (!painted) { int resolution = 300; //TODO not hard-coded, please! int bmw = UnitConv.mpt2px(pclContext.getWidth(), resolution); int bmh = UnitConv.mpt2px(pclContext.getHeight(), resolution); BufferedImage bi = new BufferedImage( bmw, bmh, - BufferedImage.TYPE_INT_RGB); + BufferedImage.TYPE_BYTE_GRAY); Graphics2D g2d = bi.createGraphics(); try { g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_OFF); - g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); - g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, - RenderingHints.VALUE_FRACTIONALMETRICS_OFF); - g2d.setRenderingHint(RenderingHints.KEY_RENDERING, - RenderingHints.VALUE_RENDER_QUALITY); - g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, - RenderingHints.VALUE_COLOR_RENDER_QUALITY); - g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); - g2d.setRenderingHint(RenderingHints.KEY_DITHERING, - RenderingHints.VALUE_DITHER_ENABLE); - g2d.setBackground(Color.white); - g2d.setColor(Color.black); - g2d.clearRect(0, 0, bmw, bmh); - double sx = (double)bmw / pclContext.getWidth() * 1000; - double sy = (double)bmh / pclContext.getHeight() * 1000; - g2d.scale(sx, sy); + RenderingHints.VALUE_ANTIALIAS_OFF); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + + g2d.setBackground(Color.white); + g2d.setColor(Color.black); + g2d.clearRect(0, 0, bmw, bmh); + double sx = (double)bmw / pclContext.getWidth(); + double sy = (double)bmh / pclContext.getHeight(); + g2d.scale(sx, sy); - //Paint the SVG on the BufferedImage - Rectangle2D area = new Rectangle2D.Double( - 0.0, 0.0, pclContext.getWidth(), pclContext.getHeight()); - painter.paint(g2d, area); + //Paint the SVG on the BufferedImage + Rectangle2D area = new Rectangle2D.Double( + 0.0, 0.0, pclContext.getWidth(), pclContext.getHeight()); + painter.paint(g2d, area); } finally { g2d.dispose(); } - pcl.moveTo(x, y); + pcl.setCursorPos(x, y); gen.paintBitmap(bi, resolution); - } else { - pcl.saveGraphicsState(); - GraphicContext ctx = (GraphicContext)pcl.getGraphicContext().clone(); - - // Clip to the image area. - //gen.writeln("newpath"); - //gen.defineRect(fx, fy, fwidth, fheight); - //gen.writeln("clip"); - - AffineTransform prepareHPGL2 = new AffineTransform(); - //prepareHPGL2.scale(1, 1); - ctx.setTransform(prepareHPGL2); - - pcl.moveTo(x, y); - gen.writeCommand("*c" + gen.formatDouble4(width / 100f) + "x" - + gen.formatDouble4(height / 100f) + "Y"); - gen.writeCommand("*c0T"); - gen.writeCommand("%0B"); - gen.writeText("IN;"); - gen.writeText("SP1;"); - //One Plotter unit is 0.025mm! - double scale = imw / UnitConv.mm2pt(imw * 0.025); - gen.writeText("SC0," + gen.formatDouble4(scale) - + ",0,-" + gen.formatDouble4(scale) + ",2;"); - gen.writeText("IR0,100,0,100;"); - gen.writeText("PU;PA0,0;"); - PCLGraphics2D graphics = new PCLGraphics2D(gen); - graphics.setGraphicContext(ctx); - Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, imw, imh); - painter.paint(graphics, area); - - gen.writeCommand("%0A"); - pcl.restoreGraphicsState(); } } diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLRenderer.java b/src/sandbox/org/apache/fop/render/pcl/PCLRenderer.java index 04e619822..3cab4d63f 100644 --- a/src/sandbox/org/apache/fop/render/pcl/PCLRenderer.java +++ b/src/sandbox/org/apache/fop/render/pcl/PCLRenderer.java @@ -18,7 +18,38 @@ package org.apache.fop.render.pcl; +//Java +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.color.ColorSpace; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +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.PixelInterleavedSampleModel; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +import org.w3c.dom.Document; + +import org.apache.xmlgraphics.java2d.GraphicContext; + // FOP +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.MimeConstants; import org.apache.fop.area.Area; @@ -31,44 +62,25 @@ import org.apache.fop.area.Trait.Color; import org.apache.fop.area.inline.AbstractTextArea; 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.SpaceArea; import org.apache.fop.area.inline.TextArea; -import org.apache.fop.area.inline.Viewport; import org.apache.fop.area.inline.WordArea; +import org.apache.fop.fo.extensions.ExtensionElementMapping; import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontSetup; import org.apache.fop.image.EPSImage; import org.apache.fop.image.FopImage; import org.apache.fop.image.ImageFactory; import org.apache.fop.image.XMLImage; import org.apache.fop.render.Graphics2DAdapter; +import org.apache.fop.render.Graphics2DImagePainter; import org.apache.fop.render.PrintRenderer; import org.apache.fop.render.RendererContext; import org.apache.fop.render.RendererContextConstants; +import org.apache.fop.render.java2d.Java2DRenderer; import org.apache.fop.traits.BorderProps; -import org.apache.xmlgraphics.java2d.GraphicContext; -import org.w3c.dom.Document; - -// Java -import java.awt.Rectangle; -import java.awt.color.ColorSpace; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -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.PixelInterleavedSampleModel; -import java.awt.image.Raster; -import java.awt.image.RenderedImage; -import java.awt.image.SampleModel; -import java.awt.image.WritableRaster; -import java.io.IOException; -import java.io.OutputStream; -import java.util.List; -import java.util.Map; -import java.util.Stack; +import org.apache.fop.util.QName; /** * Renderer for the PCL 5 printer language. It also uses HP GL/2 for certain graphic elements. @@ -87,6 +99,15 @@ public class PCLRenderer extends PrintRenderer { private Stack graphicContextStack = new Stack(); private GraphicContext graphicContext = new GraphicContext(); + + private GeneralPath currentPath = null; + private java.awt.Color currentFillColor = null; + + /** + * Controls whether appearance is more important than speed. False can cause some FO feature + * to be ignored (like the advanced borders). + */ + private boolean qualityBeforeSpeed = false; /** * Create the PCL renderer @@ -94,6 +115,23 @@ public class PCLRenderer extends PrintRenderer { public PCLRenderer() { } + /** + * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration) + */ + public void configure(Configuration cfg) throws ConfigurationException { + super.configure(cfg); + String rendering = cfg.getChild("rendering").getValue(null); + if ("quality".equalsIgnoreCase(rendering)) { + this.qualityBeforeSpeed = true; + } else if ("speed".equalsIgnoreCase(rendering)) { + this.qualityBeforeSpeed = false; + } else if (rendering != null) { + throw new ConfigurationException( + "Valid values for 'rendering' are 'quality' and 'speed'. Value found: " + + rendering); + } + } + /** * Central exception handler for I/O exceptions. * @param ioe IOException to handle @@ -229,6 +267,8 @@ public class PCLRenderer extends PrintRenderer { this.gen = new PCLGenerator(out); gen.universalEndOfLanguage(); + gen.writeText("@PJL JOB NAME = \"" + userAgent.getTitle() + "\"\n"); + gen.writeText("@PJL ENTER LANGUAGE = PCL\n"); gen.resetPrinter(); } @@ -253,19 +293,6 @@ public class PCLRenderer extends PrintRenderer { final long pageheight = Math.round(page.getViewArea().getHeight()); selectPageFormat(pagewidth, pageheight); - if (false) { //TODO DEBUG CODE! Remove me. - //gen.fillRect(0, 0, (int)pagewidth, (int)pageheight, java.awt.Color.yellow); - //gen.fillRect(5000, 5000, (int)pagewidth - 10000, (int)pageheight - 10000, java.awt.Color.yellow); - //gen.fillRect(10000, 10000, (int)pagewidth / 4 - 20000, (int)pageheight / 4 - 20000, java.awt.Color.red); - for (int i = 0; i < 29; i++) { - if (i % 2 == 0) { - int w = (int)(10 * 2.835 * 1000); - Point2D p = transformedPoint(i * w, 0); - gen.fillRect((int)p.getX(), (int)p.getY(), w, w, java.awt.Color.yellow); - } - } - } - super.renderPage(page); gen.formFeed(); restoreGraphicsState(); @@ -389,7 +416,7 @@ public class PCLRenderer extends PrintRenderer { saveGraphicsState(); updatePrintDirection(); graphicContext.translate(rx, bl); - moveTo(0, 0); + setCursorPos(0, 0); super.renderText(area); //Updates IPD @@ -400,10 +427,83 @@ public class PCLRenderer extends PrintRenderer { } } - void moveTo(int x, int y) throws IOException { - Point2D transPoint = transformedPoint(x, y); - gen.writeCommand("&a" + gen.formatDouble2(transPoint.getX() / 100) + "h" - + gen.formatDouble2(transPoint.getY() / 100) + "V"); + /** + * Sets the current cursor position. The coordinates are transformed to the absolute position + * on the logical PCL page and then passed on to the PCLGenerator. + * @param x the x coordinate (in millipoints) + * @param y the y coordinate (in millipoints) + */ + void setCursorPos(float x, float y) { + try { + Point2D transPoint = transformedPoint(x, y); + gen.setCursorPos(transPoint.getX(), transPoint.getY()); + } catch (IOException ioe) { + handleIOTrouble(ioe); + } + } + + /** + * @see org.apache.fop.render.AbstractPathOrientedRenderer#clip() + */ + protected void clip() { + if (currentPath == null) { + throw new IllegalStateException("No current path available!"); + } + //TODO Find a good way to do clipping + currentPath = null; + } + + /** + * @see org.apache.fop.render.AbstractPathOrientedRenderer#closePath() + */ + protected void closePath() { + currentPath.closePath(); + } + + /** + * @see org.apache.fop.render.AbstractPathOrientedRenderer#lineTo(float, float) + */ + protected void lineTo(float x, float y) { + if (currentPath == null) { + currentPath = new GeneralPath(); + } + currentPath.lineTo(x, y); + } + + /** + * @see org.apache.fop.render.AbstractPathOrientedRenderer#moveTo(float, float) + */ + protected void moveTo(float x, float y) { + if (currentPath == null) { + currentPath = new GeneralPath(); + } + currentPath.moveTo(x, y); + } + + /** + * Fill a rectangular area. + * @param x the x coordinate (in pt) + * @param y the y coordinate (in pt) + * @param width the width of the rectangle + * @param height the height of the rectangle + */ + protected void fillRect(float x, float y, float width, float height) { + try { + Point2D p = transformedPoint(x * 1000, y * 1000); + gen.fillRect((int)p.getX(), (int)p.getY(), + (int)width * 1000, (int)height * 1000, + this.currentFillColor); + } catch (IOException ioe) { + handleIOTrouble(ioe); + } + } + + /** + * Sets the new current fill color. + * @param color the color + */ + protected void updateFillColor(java.awt.Color color) { + this.currentFillColor = color; } private void updatePrintDirection() throws IOException { @@ -481,37 +581,6 @@ public class PCLRenderer extends PrintRenderer { super.renderSpace(space); } - /** - * @see org.apache.fop.render.AbstractRenderer#renderViewport(org.apache.fop.area.inline.Viewport) - */ - public void renderViewport(Viewport viewport) { - - float x = currentIPPosition / 1000f; - float y = (currentBPPosition + viewport.getOffset()) / 1000f; - float width = viewport.getIPD() / 1000f; - float height = viewport.getBPD() / 1000f; - // TODO: Calculate the border rect correctly. - float borderPaddingStart = viewport.getBorderAndPaddingWidthStart() / 1000f; - float borderPaddingBefore = viewport.getBorderAndPaddingWidthBefore() / 1000f; - float bpwidth = borderPaddingStart - + (viewport.getBorderAndPaddingWidthEnd() / 1000f); - float bpheight = borderPaddingBefore - + (viewport.getBorderAndPaddingWidthAfter() / 1000f); - - drawBackAndBorders(viewport, x, y, width + bpwidth, height + bpheight); - - if (viewport.getClip()) { - saveGraphicsState(); - - clipRect(x + borderPaddingStart, y + borderPaddingBefore, width, height); - } - super.renderViewport(viewport); - - if (viewport.getClip()) { - restoreGraphicsState(); - } - } - /** * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport, List) */ @@ -625,7 +694,17 @@ public class PCLRenderer extends PrintRenderer { * @see org.apache.fop.render.AbstractRenderer#renderImage(Image, Rectangle2D) */ public void renderImage(Image image, Rectangle2D pos) { - String url = ImageFactory.getURL(image.getURL()); + drawImage(image.getURL(), pos, image.getForeignAttributes()); + } + + /** + * Draw an image at the indicated location. + * @param url the URI/URL of the image + * @param pos the position of the image + * @param foreignAttributes an optional Map with foreign attributes, may be null + */ + protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes) { + url = ImageFactory.getURL(url); ImageFactory fact = userAgent.getFactory().getImageFactory(); FopImage fopimage = fact.getImage(url, userAgent); if (fopimage == null) { @@ -642,7 +721,7 @@ public class PCLRenderer extends PrintRenderer { Document doc = ((XMLImage) fopimage).getDocument(); String ns = ((XMLImage) fopimage).getNameSpace(); - renderDocument(doc, ns, pos, image.getForeignAttributes()); + renderDocument(doc, ns, pos, foreignAttributes); } else if ("image/svg+xml".equals(mime)) { if (!fopimage.load(FopImage.ORIGINAL_DATA)) { return; @@ -650,7 +729,7 @@ public class PCLRenderer extends PrintRenderer { Document doc = ((XMLImage) fopimage).getDocument(); String ns = ((XMLImage) fopimage).getNameSpace(); - renderDocument(doc, ns, pos, image.getForeignAttributes()); + renderDocument(doc, ns, pos, foreignAttributes); } else if (fopimage instanceof EPSImage) { log.warn("EPS images are not supported by this renderer"); } else { @@ -678,7 +757,7 @@ public class PCLRenderer extends PrintRenderer { RenderedImage img = new BufferedImage(cm, raster, false, null); try { - moveTo(this.currentIPPosition + (int)pos.getX(), + setCursorPos(this.currentIPPosition + (int)pos.getX(), this.currentBPPosition + (int)pos.getY()); int resolution = (int)Math.round(Math.max(fopimage.getHorizontalResolution(), fopimage.getVerticalResolution())); @@ -706,27 +785,72 @@ public class PCLRenderer extends PrintRenderer { * @param foreignAttributes the foreign attributes containing rendering hints, or null */ public void renderDocument(Document doc, String ns, Rectangle2D pos, Map foreignAttributes) { + int x = currentIPPosition + (int) pos.getX(); + int y = currentBPPosition + (int) pos.getY(); + int width = (int)pos.getWidth(); + int height = (int)pos.getHeight(); + RendererContext context = createRendererContext(x, y, width, height, foreignAttributes); + + renderXML(context, doc, ns); + } + + /** + * Creates a RendererContext for an image. + * @param x the x coordinate (in millipoints) + * @param y the y coordinate (in millipoints) + * @param width the width of the image (in millipoints) + * @param height the height of the image (in millipoints) + * @param foreignAttributes a Map or foreign attributes, may be null + * @return the RendererContext + */ + protected RendererContext createRendererContext(int x, int y, int width, int height, + Map foreignAttributes) { RendererContext context; context = new RendererContext(this, MIME_TYPE); context.setUserAgent(userAgent); context.setProperty(RendererContextConstants.WIDTH, - new Integer((int) pos.getWidth())); + new Integer(width)); context.setProperty(RendererContextConstants.HEIGHT, - new Integer((int) pos.getHeight())); + new Integer(height)); context.setProperty(RendererContextConstants.XPOS, - new Integer(currentIPPosition + (int) pos.getX())); + new Integer(x)); context.setProperty(RendererContextConstants.YPOS, - new Integer(currentBPPosition + (int) pos.getY())); + new Integer(y)); context.setProperty(RendererContextConstants.PAGE_VIEWPORT, getCurrentPageViewport()); if (foreignAttributes != null) { context.setProperty(RendererContextConstants.FOREIGN_ATTRIBUTES, foreignAttributes); } - - renderXML(context, doc, ns); + return context; } + /** + * Common method to render the background and borders for any inline area. + * The all borders and padding are drawn outside the specified area. + * @param area the inline area for which the background, border and padding is to be + * rendered + * @todo Copied from AbstractPathOrientedRenderer + */ + protected void renderInlineAreaBackAndBorders(InlineArea area) { + float x = currentIPPosition / 1000f; + float y = (currentBPPosition + area.getOffset()) / 1000f; + float width = area.getIPD() / 1000f; + float height = area.getBPD() / 1000f; + float borderPaddingStart = area.getBorderAndPaddingWidthStart() / 1000f; + float borderPaddingBefore = area.getBorderAndPaddingWidthBefore() / 1000f; + float bpwidth = borderPaddingStart + + (area.getBorderAndPaddingWidthEnd() / 1000f); + float bpheight = borderPaddingBefore + + (area.getBorderAndPaddingWidthAfter() / 1000f); + + if (height != 0.0f || bpheight != 0.0f && bpwidth != 0.0f) { + drawBackAndBorders(area, x, y - borderPaddingBefore + , width + bpwidth + , height + bpheight); + } + } + /** * Draw the background and borders. This draws the background and border * traits for an area given the position. @@ -742,127 +866,401 @@ public class PCLRenderer extends PrintRenderer { try { updatePrintDirection(); 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) 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) { - Point2D p = transformedPoint(sx * 1000, sy * 1000); - gen.fillRect((int)p.getX(), (int)p.getY(), - (int)paddRectWidth * 1000, (int)paddRectHeight * 1000, - back.getColor().getAWTColor()); - } - - // background image - if (back.getFopImage() != null) { - FopImage fopimage = back.getFopImage(); - if (fopimage != null && fopimage.load(FopImage.DIMENSIONS)) { - saveGraphicsState(); - clipRect(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 + 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) 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) { + updateFillColor(back.getColor().getAWTColor()); + fillRect(sx, sy, paddRectWidth, paddRectHeight); + } + + // background image + if (back.getFopImage() != null) { + FopImage fopimage = back.getFopImage(); + if (fopimage != null && fopimage.load(FopImage.DIMENSIONS)) { + saveGraphicsState(); + clipRect(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; + // Image positions are relative to the currentIP/BP + pos = new Rectangle2D.Float( + sx - currentIPPosition + + (x * fopimage.getIntrinsicWidth()), + sy - currentBPPosition + + (y * fopimage.getIntrinsicHeight()), + fopimage.getIntrinsicWidth(), + fopimage.getIntrinsicHeight()); + drawImage(back.getURL(), pos, null); + } } + restoreGraphicsState(); + } else { + log.warn( + "Can't find background image: " + back.getURL()); } - restoreGraphicsState(); - } else { - log.warn( - "Can't find background image: " + back.getURL()); } } + + Rectangle2D.Float borderRect = new Rectangle2D.Float(startx, starty, width, height); + drawBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); + + } catch (IOException ioe) { + handleIOTrouble(ioe); } -/* - // draw border - // BORDER_BEFORE + } + + /** + * Draws borders. + * @param borderRect the border rectangle + * @param bpsBefore the border specification on the before side + * @param bpsAfter the border specification on the after side + * @param bpsStart the border specification on the start side + * @param bpsEnd the border specification on the end side + */ + protected void drawBorders(Rectangle2D.Float borderRect, + final BorderProps bpsBefore, final BorderProps bpsAfter, + final BorderProps bpsStart, final BorderProps bpsEnd) { + if (bpsBefore == null && bpsAfter == null && bpsStart == null && bpsEnd == null) { + return; //no borders to paint + } + if (qualityBeforeSpeed) { + drawQualityBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); + } else { + drawFastBorders(borderRect, bpsBefore, bpsAfter, bpsStart, bpsEnd); + } + } + + /** + * Draws borders. Borders are drawn as shaded rectangles with no clipping. + * @param borderRect the border rectangle + * @param bpsBefore the border specification on the before side + * @param bpsAfter the border specification on the after side + * @param bpsStart the border specification on the start side + * @param bpsEnd the border specification on the end side + */ + protected void drawFastBorders(Rectangle2D.Float borderRect, + final BorderProps bpsBefore, final BorderProps bpsAfter, + final BorderProps bpsStart, final BorderProps bpsEnd) { + float startx = borderRect.x; + float starty = borderRect.y; + float width = borderRect.width; + float height = borderRect.height; if (bpsBefore != null) { int borderWidth = (int) Math.round((bpsBefore.width / 1000f)); - state.updateColor(bpsBefore.color); - state.getGraph().fillRect((int) startx, (int) starty, (int) width, + updateFillColor(bpsBefore.color.getAWTColor()); + fillRect((int) startx, (int) starty, (int) width, borderWidth); } - // BORDER_AFTER if (bpsAfter != null) { int borderWidth = (int) Math.round((bpsAfter.width / 1000f)); - float sy = starty + height; - state.updateColor(bpsAfter.color); - state.getGraph().fillRect((int) startx, + updateFillColor(bpsAfter.color.getAWTColor()); + fillRect((int) startx, (int) (starty + height - borderWidth), (int) width, borderWidth); } - // BORDER_START if (bpsStart != null) { int borderWidth = (int) Math.round((bpsStart.width / 1000f)); - state.updateColor(bpsStart.color); - state.getGraph().fillRect((int) startx, (int) starty, borderWidth, + updateFillColor(bpsStart.color.getAWTColor()); + fillRect((int) startx, (int) starty, borderWidth, (int) height); } - // BORDER_END if (bpsEnd != null) { int borderWidth = (int) Math.round((bpsEnd.width / 1000f)); - float sx = startx + width; - state.updateColor(bpsEnd.color); - state.getGraph().fillRect((int) (startx + width - borderWidth), + updateFillColor(bpsEnd.color.getAWTColor()); + fillRect((int) (startx + width - borderWidth), (int) starty, borderWidth, (int) height); } - */ + } + + /** + * Draws borders. Borders are drawn in-memory and painted as a bitmap. + * @param borderRect the border rectangle + * @param bpsBefore the border specification on the before side + * @param bpsAfter the border specification on the after side + * @param bpsStart the border specification on the start side + * @param bpsEnd the border specification on the end side + */ + protected void drawQualityBorders(Rectangle2D.Float borderRect, + final BorderProps bpsBefore, final BorderProps bpsAfter, + final BorderProps bpsStart, final BorderProps bpsEnd) { + Graphics2DAdapter g2a = getGraphics2DAdapter(); + final Rectangle.Float effBorderRect = new Rectangle2D.Float( + borderRect.x - (currentIPPosition / 1000f), + borderRect.y - (currentBPPosition / 1000f), + borderRect.width, borderRect.height); + final Rectangle paintRect = new Rectangle( + (int)Math.round(borderRect.x * 1000), + (int)Math.round(borderRect.y * 1000), + (int)Math.floor(borderRect.width * 1000) + 1, + (int)Math.floor(borderRect.height * 1000) + 1); + int xoffset = (bpsStart != null ? bpsStart.width : 0); + paintRect.x += xoffset; + paintRect.width += xoffset; + paintRect.width += (bpsEnd != null ? bpsEnd.width : 0); + + RendererContext rc = createRendererContext(paintRect.x, paintRect.y, + paintRect.width, paintRect.height, null); + if (false) { + Map atts = new java.util.HashMap(); + atts.put(new QName(ExtensionElementMapping.URI, null, "conversion-mode"), "bitmap"); + rc.setProperty(RendererContextConstants.FOREIGN_ATTRIBUTES, atts); + } + + Graphics2DImagePainter painter = new Graphics2DImagePainter() { + + public void paint(Graphics2D g2d, Rectangle2D area) { + g2d.translate((bpsStart != null ? bpsStart.width : 0), 0); + g2d.scale(1000, 1000); + float startx = effBorderRect.x; + float starty = effBorderRect.y; + float width = effBorderRect.width; + float height = effBorderRect.height; + boolean[] b = new boolean[] { + (bpsBefore != null), (bpsEnd != null), + (bpsAfter != null), (bpsStart != null)}; + if (!b[0] && !b[1] && !b[2] && !b[3]) { + return; + } + float[] bw = new float[] { + (b[0] ? bpsBefore.width / 1000f : 0.0f), + (b[1] ? bpsEnd.width / 1000f : 0.0f), + (b[2] ? bpsAfter.width / 1000f : 0.0f), + (b[3] ? bpsStart.width / 1000f : 0.0f)}; + float[] clipw = new float[] { + BorderProps.getClippedWidth(bpsBefore) / 1000f, + BorderProps.getClippedWidth(bpsEnd) / 1000f, + BorderProps.getClippedWidth(bpsAfter) / 1000f, + BorderProps.getClippedWidth(bpsStart) / 1000f}; + starty += clipw[0]; + height -= clipw[0]; + height -= clipw[2]; + startx += clipw[3]; + width -= clipw[3]; + width -= clipw[1]; + + boolean[] slant = new boolean[] { + (b[3] && b[0]), (b[0] && b[1]), (b[1] && b[2]), (b[2] && b[3])}; + if (bpsBefore != null) { + //endTextObject(); + + float sx1 = startx; + float sx2 = (slant[0] ? sx1 + bw[3] - clipw[3] : sx1); + float ex1 = startx + width; + float ex2 = (slant[1] ? ex1 - bw[1] + clipw[1] : ex1); + float outery = starty - clipw[0]; + float clipy = outery + clipw[0]; + float innery = outery + bw[0]; + + //saveGraphicsState(); + Graphics2D g = (Graphics2D)g2d.create(); + moveTo(sx1, clipy); + float sx1a = sx1; + float ex1a = ex1; + if (bpsBefore.mode == BorderProps.COLLAPSE_OUTER) { + if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) { + sx1a -= clipw[3]; + } + if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { + ex1a += clipw[1]; + } + lineTo(sx1a, outery); + lineTo(ex1a, outery); + } + lineTo(ex1, clipy); + lineTo(ex2, innery); + lineTo(sx2, innery); + closePath(); + //clip(); + g.clip(currentPath); + currentPath = null; + Rectangle2D.Float lineRect = new Rectangle2D.Float( + sx1a, outery, ex1a - sx1a, innery - outery); + Java2DRenderer.drawBorderLine(lineRect, true, true, + bpsBefore.style, toColor(bpsBefore.color), g); + //restoreGraphicsState(); + } + if (bpsEnd != null) { + //endTextObject(); + + float sy1 = starty; + float sy2 = (slant[1] ? sy1 + bw[0] - clipw[0] : sy1); + float ey1 = starty + height; + float ey2 = (slant[2] ? ey1 - bw[2] + clipw[2] : ey1); + float outerx = startx + width + clipw[1]; + float clipx = outerx - clipw[1]; + float innerx = outerx - bw[1]; + + //saveGraphicsState(); + Graphics2D g = (Graphics2D)g2d.create(); + moveTo(clipx, sy1); + float sy1a = sy1; + float ey1a = ey1; + if (bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { + if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) { + sy1a -= clipw[0]; + } + if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) { + ey1a += clipw[2]; + } + lineTo(outerx, sy1a); + lineTo(outerx, ey1a); + } + lineTo(clipx, ey1); + lineTo(innerx, ey2); + lineTo(innerx, sy2); + closePath(); + //clip(); + g.setClip(currentPath); + currentPath = null; + Rectangle2D.Float lineRect = new Rectangle2D.Float( + innerx, sy1a, outerx - innerx, ey1a - sy1a); + Java2DRenderer.drawBorderLine(lineRect, false, false, + bpsEnd.style, toColor(bpsEnd.color), g); + //restoreGraphicsState(); + } + if (bpsAfter != null) { + //endTextObject(); + + float sx1 = startx; + float sx2 = (slant[3] ? sx1 + bw[3] - clipw[3] : sx1); + float ex1 = startx + width; + float ex2 = (slant[2] ? ex1 - bw[1] + clipw[1] : ex1); + float outery = starty + height + clipw[2]; + float clipy = outery - clipw[2]; + float innery = outery - bw[2]; + + //saveGraphicsState(); + Graphics2D g = (Graphics2D)g2d.create(); + moveTo(ex1, clipy); + float sx1a = sx1; + float ex1a = ex1; + if (bpsAfter.mode == BorderProps.COLLAPSE_OUTER) { + if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) { + sx1a -= clipw[3]; + } + if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) { + ex1a += clipw[1]; + } + lineTo(ex1a, outery); + lineTo(sx1a, outery); + } + lineTo(sx1, clipy); + lineTo(sx2, innery); + lineTo(ex2, innery); + closePath(); + //clip(); + g.setClip(currentPath); + currentPath = null; + Rectangle2D.Float lineRect = new Rectangle2D.Float( + sx1a, innery, ex1a - sx1a, outery - innery); + Java2DRenderer.drawBorderLine(lineRect, true, false, + bpsAfter.style, toColor(bpsAfter.color), g); + //restoreGraphicsState(); + } + if (bpsStart != null) { + //endTextObject(); + + float sy1 = starty; + float sy2 = (slant[0] ? sy1 + bw[0] - clipw[0] : sy1); + float ey1 = sy1 + height; + float ey2 = (slant[3] ? ey1 - bw[2] + clipw[2] : ey1); + float outerx = startx - clipw[3]; + float clipx = outerx + clipw[3]; + float innerx = outerx + bw[3]; + + //saveGraphicsState(); + Graphics2D g = (Graphics2D)g2d.create(); + moveTo(clipx, ey1); + float sy1a = sy1; + float ey1a = ey1; + if (bpsStart.mode == BorderProps.COLLAPSE_OUTER) { + if (bpsBefore != null && bpsBefore.mode == BorderProps.COLLAPSE_OUTER) { + sy1a -= clipw[0]; + } + if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) { + ey1a += clipw[2]; + } + lineTo(outerx, ey1a); + lineTo(outerx, sy1a); + } + lineTo(clipx, sy1); + lineTo(innerx, sy2); + lineTo(innerx, ey2); + closePath(); + //clip(); + g.setClip(currentPath); + currentPath = null; + Rectangle2D.Float lineRect = new Rectangle2D.Float( + outerx, sy1a, innerx - outerx, ey1a - sy1a); + Java2DRenderer.drawBorderLine(lineRect, false, false, + bpsStart.style, toColor(bpsStart.color), g); + //restoreGraphicsState(); + } + } + + public Dimension getImageSize() { + return paintRect.getSize(); + } + + }; + try { + g2a.paintImage(painter, rc, + paintRect.x - xoffset, paintRect.y, paintRect.width, paintRect.height); } catch (IOException ioe) { handleIOTrouble(ioe); } } - + + } diff --git a/src/sandbox/org/apache/fop/render/pcl/PCLSVGHandler.java b/src/sandbox/org/apache/fop/render/pcl/PCLSVGHandler.java index 2e187d482..7eec5169d 100644 --- a/src/sandbox/org/apache/fop/render/pcl/PCLSVGHandler.java +++ b/src/sandbox/org/apache/fop/render/pcl/PCLSVGHandler.java @@ -86,33 +86,35 @@ public class PCLSVGHandler implements XMLHandler, RendererContextConstants { int x = pclContext.getCurrentXPosition(); int y = pclContext.getCurrentYPosition(); + SVGUserAgent ua = new SVGUserAgent( + context.getUserAgent().getSourcePixelUnitToMillimeter(), + new AffineTransform()); + GVTBuilder builder = new GVTBuilder(); + final BridgeContext ctx = new BridgeContext(ua); + + final GraphicsNode root; + try { + root = builder.build(ctx, doc); + + } catch (Exception e) { + log.error("SVG graphic could not be built: " + + e.getMessage(), e); + return; + } + Graphics2DImagePainter painter = new Graphics2DImagePainter() { public void paint(Graphics2D g2d, Rectangle2D area) { - SVGUserAgent ua = new SVGUserAgent( - context.getUserAgent().getSourcePixelUnitToMillimeter(), - new AffineTransform()); - GVTBuilder builder = new GVTBuilder(); - BridgeContext ctx = new BridgeContext(ua); - - GraphicsNode root; - try { - root = builder.build(ctx, doc); - - // If no viewbox is defined in the svg file, a viewbox of 100x100 is - // assumed, as defined in SVGUserAgent.getViewportSize() - float iw = (float) ctx.getDocumentSize().getWidth() * 1000f; - float ih = (float) ctx.getDocumentSize().getHeight() * 1000f; - float w = (float) area.getWidth(); - float h = (float) area.getHeight(); - g2d.scale(w / iw, h / ih); - - root.paint(g2d); - } catch (Exception e) { - log.error("SVG graphic could not be built: " - + e.getMessage(), e); - return; - } + + // If no viewbox is defined in the svg file, a viewbox of 100x100 is + // assumed, as defined in SVGUserAgent.getViewportSize() + float iw = (float) ctx.getDocumentSize().getWidth(); + float ih = (float) ctx.getDocumentSize().getHeight(); + float w = (float) area.getWidth(); + float h = (float) area.getHeight(); + g2d.scale(w / iw, h / ih); + + root.paint(g2d); } public Dimension getImageSize() { diff --git a/src/sandbox/org/apache/fop/render/svg/SVGRenderer.java b/src/sandbox/org/apache/fop/render/svg/SVGRenderer.java index 769e7e259..ac05532df 100644 --- a/src/sandbox/org/apache/fop/render/svg/SVGRenderer.java +++ b/src/sandbox/org/apache/fop/render/svg/SVGRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -23,6 +23,7 @@ import org.apache.fop.area.CTM; import org.apache.fop.area.PageViewport; import org.apache.fop.area.LineArea; import org.apache.fop.area.inline.ForeignObject; +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.svg.SVGUtilities; @@ -433,5 +434,9 @@ public class SVGRenderer extends AbstractRenderer { // TODO Auto-generated method stub } + protected void renderInlineAreaBackAndBorders(InlineArea area) { + // TODO Auto-generated method stub + } + } -- 2.39.5