From ac8d584dfdcaf99a278202e90e1576173508471b Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Tue, 11 Mar 2003 08:42:24 +0000 Subject: [PATCH] Port of the PDF TextPainter to PostScript. Support for SEG_QUADTO (curves). Some support for viewport traits (background and borders). Submitted by: Zhong Yi git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@196058 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/fop/render/ps/PSGraphics2D.java | 136 ++++-- src/org/apache/fop/render/ps/PSProcSets.java | 15 +- src/org/apache/fop/render/ps/PSRenderer.java | 202 +++++++- .../apache/fop/render/ps/PSTextPainter.java | 435 ++++++++++++++++++ .../apache/fop/render/ps/PSXMLHandler.java | 27 +- 5 files changed, 757 insertions(+), 58 deletions(-) create mode 100644 src/org/apache/fop/render/ps/PSTextPainter.java diff --git a/src/org/apache/fop/render/ps/PSGraphics2D.java b/src/org/apache/fop/render/ps/PSGraphics2D.java index 294060e20..5a27c0fb0 100644 --- a/src/org/apache/fop/render/ps/PSGraphics2D.java +++ b/src/org/apache/fop/render/ps/PSGraphics2D.java @@ -114,7 +114,10 @@ public class PSGraphics2D extends AbstractGraphics2D { /** Currently valid FontState */ protected FontState fontState; - + + /** Overriding FontState */ + protected FontState overrideFontState = null; + /** * the current (internal) font name */ @@ -501,8 +504,9 @@ public class PSGraphics2D extends AbstractGraphics2D { Shape imclip = getClip(); writeClip(imclip); Color c = getColor(); - gen.writeln(c.getRed() + " " + c.getGreen() + " " + c.getBlue() - + " setrgbcolor"); + gen.writeln(gen.formatDouble(c.getRed() / 255.0) + " " + + gen.formatDouble(c.getGreen() / 255.0) + " " + + gen.formatDouble(c.getBlue() / 255.0) + " setrgbcolor"); applyPaint(getPaint(), false); applyStroke(getStroke()); @@ -533,10 +537,10 @@ public class PSGraphics2D extends AbstractGraphics2D { + " M"); break; case PathIterator.SEG_QUADTO: - // psRenderer.write((1000 * PDFNumber.doubleOut(vals[0])) + - // " " + (1000 * PDFNumber.doubleOut(vals[1])) + " " + - // (1000 * PDFNumber.doubleOut(vals[2])) + " " + - // (1000 * PDFNumber.doubleOut(vals[3])) + " y\n"); + gen.writeln(gen.formatDouble(1000 * vals[0]) + " " + + gen.formatDouble(1000 * vals[1]) + " " + + gen.formatDouble(1000 * vals[2]) + " " + + gen.formatDouble(1000 * vals[3]) + " QUADTO "); break; case PathIterator.SEG_CLOSE: gen.writeln("closepath"); @@ -585,10 +589,10 @@ public class PSGraphics2D extends AbstractGraphics2D { + " M"); break; case PathIterator.SEG_QUADTO: - // psRenderer.write(1000 * PDFNumber.doubleOut(vals[0]) + - // " " + 1000 * PDFNumber.doubleOut(vals[1]) + " " + - // 1000 * PDFNumber.doubleOut(vals[2]) + " " + - // 1000 * PDFNumber.doubleOut(vals[3]) + " y\n"); + gen.writeln(gen.formatDouble(1000 * vals[0]) + " " + + gen.formatDouble(1000 * vals[1]) + " " + + gen.formatDouble(1000 * vals[2]) + " " + + gen.formatDouble(1000 * vals[3]) + " QUADTO "); break; case PathIterator.SEG_CLOSE: gen.writeln("closepath"); @@ -803,32 +807,73 @@ public class PSGraphics2D extends AbstractGraphics2D { * @see #setClip */ public void drawString(String s, float x, float y) { - try { - System.out.println("drawString(String)"); - gen.writeln("BT"); - Shape imclip = getClip(); - writeClip(imclip); - Color c = getColor(); - gen.writeln(c.getRed() + " " + c.getGreen() + " " + c.getBlue() - + " setrgbcolor"); - - AffineTransform trans = getTransform(); - trans.translate(x, y); - double[] vals = new double[6]; - trans.getMatrix(vals); - - gen.writeln(gen.formatDouble(vals[0]) + " " - + gen.formatDouble(vals[1]) + " " - + gen.formatDouble(vals[2]) + " " - + gen.formatDouble(vals[3]) + " " - + gen.formatDouble(vals[4]) + " " - + gen.formatDouble(vals[5]) + " " - + gen.formatDouble(vals[6]) + " Tm [" + s + "]"); + try { + if (overrideFontState == null) { + Font gFont = getFont(); + String n = gFont.getFamily(); + if (n.equals("sanserif")) { + n = "sans-serif"; + } + int siz = gFont.getSize(); + String style = gFont.isItalic() ? "italic" : "normal"; + String weight = gFont.isBold() ? "bold" : "normal"; - gen.writeln("ET"); - } catch (IOException ioe) { - handleIOException(ioe); + //try { + //fontState = new FontState(n, fontState.getFontMetrics(),siz); + //} catch (org.apache.fop.apps.FOPException fope) { + //fope.printStackTrace(); + //} + } else { + fontState = overrideFontState; + overrideFontState = null; + } + Shape imclip = getClip(); + writeClip(imclip); + Color c = getColor(); + gen.writeln(c.getRed() / 255.0 + " " + + c.getGreen() / 255.0 + " " + + c.getBlue() / 255.0 + " setrgbcolor"); + + AffineTransform trans = getTransform(); + trans.translate(x, y); + double[] vals = new double[6]; + trans.getMatrix(vals); + gen.writeln(gen.formatDouble(1000 * vals[4]) + " " + + gen.formatDouble(1000 * vals[5]) + " moveto "); + //String fontWeight = fontState.getFontWeight(); + StringBuffer sb = new StringBuffer(); + + int l = s.length(); + + if ((currentFontName != fontState.getFontName()) + || (currentFontSize != fontState.getFontSize())) { + gen.writeln(fontState.getFontName() + " " + fontState.getFontSize() + " F"); + currentFontName = fontState.getFontName(); + currentFontSize = fontState.getFontSize(); } + for (int i = 0; i < l; i++) { + char ch = s.charAt(i); + char mch = fontState.mapChar(ch); + if (mch > 127) { + sb = sb.append("\\" + Integer.toOctalString(mch)); + } else { + String escape = "\\()[]{}"; + if (escape.indexOf(mch) >= 0) { + sb.append("\\"); + } + sb = sb.append(mch); + } + } + + String psString = null; + psString = " (" + sb.toString() + ") " + " t "; + + gen.writeln(" 1.0 -1.0 scale"); + gen.writeln(psString); + gen.writeln(" 1.0 -1.0 scale"); + } catch (IOException ioe) { + handleIOException(ioe); + } } /** @@ -917,8 +962,9 @@ public class PSGraphics2D extends AbstractGraphics2D { Shape imclip = getClip(); writeClip(imclip); Color c = getColor(); - gen.writeln(c.getRed() + " " + c.getGreen() + " " + c.getBlue() - + " setrgbcolor"); + gen.writeln(gen.formatDouble(c.getRed() / 255.0) + " " + + gen.formatDouble(c.getGreen() / 255.0) + " " + + gen.formatDouble(c.getBlue() / 255.0) + " setrgbcolor"); applyPaint(getPaint(), true); @@ -948,10 +994,10 @@ public class PSGraphics2D extends AbstractGraphics2D { + " M"); break; case PathIterator.SEG_QUADTO: - // psRenderer.write(1000 * PDFNumber.doubleOut(vals[0]) + - // " " + 1000 * PDFNumber.doubleOut(vals[1]) + " " + - // 1000 * PDFNumber.doubleOut(vals[2]) + " " + - // 1000 * PDFNumber.doubleOut(vals[3]) + " y\n"); + gen.writeln(gen.formatDouble(1000 * vals[0]) + " " + + gen.formatDouble(1000 * vals[1]) + " " + + gen.formatDouble(1000 * vals[2]) + " " + + gen.formatDouble(1000 * vals[3]) + " QUADTO "); break; case PathIterator.SEG_CLOSE: gen.writeln("closepath"); @@ -1021,6 +1067,14 @@ public class PSGraphics2D extends AbstractGraphics2D { fmg = bi.createGraphics(); } + /** + * Sets the overrideing font state. + * @param infont FontState to set + */ + public void setOverrideFontState(FontState infont) { + overrideFontState = infont; + } + /** * Gets the font metrics for the specified font. * @return the font metrics for the specified font. diff --git a/src/org/apache/fop/render/ps/PSProcSets.java b/src/org/apache/fop/render/ps/PSProcSets.java index 3068ec9f2..7508e7b9b 100644 --- a/src/org/apache/fop/render/ps/PSProcSets.java +++ b/src/org/apache/fop/render/ps/PSProcSets.java @@ -141,7 +141,20 @@ public final class PSProcSets { gen.writeln(" Tt setlinewidth stroke"); gen.writeln(" grestore"); gen.writeln("} bd"); - + + gen.writeln("/QUADTO {"); + gen.writeln("/Y22 exch store"); + gen.writeln("/X22 exch store"); + gen.writeln("/Y21 exch store"); + gen.writeln("/X21 exch store"); + gen.writeln("currentpoint"); + gen.writeln("/Y21 load 2 mul add 3 div exch"); + gen.writeln("/X21 load 2 mul add 3 div exch"); + gen.writeln("/X21 load 2 mul /X22 load add 3 div"); + gen.writeln("/Y21 load 2 mul /Y22 load add 3 div"); + gen.writeln("/X22 load /Y22 load curveto"); + gen.writeln("} bd"); + gen.writeln("%%EndResource"); } diff --git a/src/org/apache/fop/render/ps/PSRenderer.java b/src/org/apache/fop/render/ps/PSRenderer.java index 6a5ce3f6f..5b950745c 100644 --- a/src/org/apache/fop/render/ps/PSRenderer.java +++ b/src/org/apache/fop/render/ps/PSRenderer.java @@ -59,6 +59,9 @@ import java.util.List; import java.util.Map; // FOP +import org.apache.fop.fo.properties.BackgroundRepeat; +import org.apache.fop.area.Area; +import org.apache.fop.area.RegionViewport; import org.apache.fop.apps.FOPException; import org.apache.fop.area.Block; import org.apache.fop.area.BlockViewport; @@ -73,9 +76,12 @@ import org.apache.fop.fonts.Font; import org.apache.fop.layout.FontInfo; import org.apache.fop.render.AbstractRenderer; import org.apache.fop.render.RendererContext; -import org.w3c.dom.Document; +import org.apache.fop.image.FopImage; +import org.apache.fop.image.ImageFactory; +import org.apache.fop.traits.BorderProps; +import org.w3c.dom.Document; /** * Renderer that renders to PostScript. *
@@ -416,14 +422,15 @@ public class PSRenderer extends AbstractRenderer { {page.getPageNumber(), new Integer(this.currentPageNumber)}); final Integer zero = new Integer(0); - final Long pagewidth = new Long(Math.round(page.getViewArea().getWidth() / 1000f)); - final Long pageheight = new Long(Math.round(page.getViewArea().getHeight() / 1000f)); + final Long pagewidth = new Long(Math.round(page.getViewArea().getWidth())); + final Long pageheight = new Long(Math.round(page.getViewArea().getHeight())); gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[] {zero, zero, pagewidth, pageheight}); gen.writeDSCComment(DSCConstants.BEGIN_PAGE_SETUP); gen.writeln("FOPFonts begin"); - concatMatrix(1, 0, 0, -1, 0, pageheight.doubleValue()); gen.writeln("0.001 0.001 scale"); + concatMatrix(1, 0, 0, -1, 0, pageheight.doubleValue()); + gen.writeDSCComment(DSCConstants.END_PAGE_SETUP); //Process page @@ -629,7 +636,7 @@ public class PSRenderer extends AbstractRenderer { saveGraphicsState(); // multiply with current CTM - //currentStream.add(CTMHelper.toPDFString(ctm) + " cm\n"); + //writeln(CTMHelper.toPDFString(ctm) + " cm\n"); final double matrix[] = ctm.toArray(); concatMatrix(matrix); @@ -646,7 +653,190 @@ public class PSRenderer extends AbstractRenderer { //currentState.pop(); } + /** + * Handle the viewport traits. + * This is used to draw the traits for a viewport. + * + * @param region the viewport region to handle + */ + protected void handleViewportTraits(RegionViewport region) { + currentFontName = ""; + float startx = 0; + float starty = 0; + Rectangle2D viewArea = region.getViewArea(); + float width = (float)(viewArea.getWidth()); + float height = (float)(viewArea.getHeight()); + /* + Trait.Background back; + back = (Trait.Background)region.getTrait(Trait.BACKGROUND); + */ + drawBackAndBorders(region, startx, starty, width, height); + } + + /** + * Handle block traits. + * The block could be any sort of block with any positioning + * so this should render the traits such as border and background + * in its position. + * + * @param block the block to render the traits + */ + protected void handleBlockTraits(Block block) { + float startx = currentIPPosition; + float starty = currentBPPosition; + drawBackAndBorders(block, startx, starty, + block.getWidth(), block.getHeight()); + } + + /** + * Draw the background and borders. + * This draws the background and border traits for an area given + * the position. + * + * @param block the area to get the traits from + * @param startx the start x position + * @param starty the start y position + * @param width the width of the area + * @param height the height of the area + */ + protected void drawBackAndBorders(Area block, + float startx, float starty, + float width, float height) { + // draw background then border + + boolean started = false; + Trait.Background back; + back = (Trait.Background)block.getTrait(Trait.BACKGROUND); + if (back != null) { + started = true; +// closeText(); + endTextObject(); + //saveGraphicsState(); + + if (back.getColor() != null) { + updateColor(back.getColor(), true, null); + writeln(startx + " " + starty + " " + + width + " " + height + " rectfill"); + } + if (back.getURL() != null) { + ImageFactory fact = ImageFactory.getInstance(); + FopImage fopimage = fact.getImage(back.getURL(), userAgent); + if (fopimage != null && fopimage.load(FopImage.DIMENSIONS, userAgent)) { + if (back.getRepeat() == BackgroundRepeat.REPEAT) { + // create a pattern for the image + } else { + // place once + Rectangle2D pos; + pos = new Rectangle2D.Float((startx + back.getHoriz()) * 1000, + (starty + back.getVertical()) * 1000, + fopimage.getWidth() * 1000, + fopimage.getHeight() * 1000); + // putImage(back.url, pos); + } + } + } + } + + BorderProps bps = (BorderProps)block.getTrait(Trait.BORDER_BEFORE); + if (bps != null) { + float endx = startx + width; + + if (!started) { + started = true; +// closeText(); + endTextObject(); + //saveGraphicsState(); + } + + float bwidth = bps.width ; + updateColor(bps.color, false, null); + writeln(bwidth + " setlinewidth"); + + drawLine(startx, starty + bwidth / 2, endx, starty + bwidth / 2); + } + bps = (BorderProps)block.getTrait(Trait.BORDER_START); + if (bps != null) { + float endy = starty + height; + + if (!started) { + started = true; +// closeText(); + endTextObject(); + //saveGraphicsState(); + } + + float bwidth = bps.width ; + updateColor(bps.color, false, null); + writeln(bwidth + " setlinewidth"); + + drawLine(startx + bwidth / 2, starty, startx + bwidth / 2, endy); + } + bps = (BorderProps)block.getTrait(Trait.BORDER_AFTER); + if (bps != null) { + float sy = starty + height; + float endx = startx + width; + + if (!started) { + started = true; +// closeText(); + endTextObject(); + //saveGraphicsState(); + } + + float bwidth = bps.width ; + updateColor(bps.color, false, null); + writeln(bwidth + " setlinewidth"); + + drawLine(startx, sy - bwidth / 2, endx, sy - bwidth / 2); + } + bps = (BorderProps)block.getTrait(Trait.BORDER_END); + if (bps != null) { + float sx = startx + width; + float endy = starty + height; + + if (!started) { + started = true; + // closeText(); + endTextObject(); + //saveGraphicsState(); + } + + float bwidth = bps.width ; + updateColor(bps.color, false, null); + writeln(bwidth + " setlinewidth"); + drawLine(sx - bwidth / 2, starty, sx - bwidth / 2, endy); + } + if (started) { + //restoreGraphicsState(); + beginTextObject(); + // font last set out of scope in text section + currentFontName = ""; + } + } + + /** + * Draw a line. + * + * @param startx the start x position + * @param starty the start y position + * @param endx the x end position + * @param endy the y end position + */ + private void drawLine(float startx, float starty, float endx, float endy) { + writeln(startx + " " + starty + " M "); + writeln(endx + " " + endy + " lineto"); + } + private void updateColor(ColorType col, boolean fill, StringBuffer pdf) { + writeln(gen.formatDouble(col.getRed()) + " " + + gen.formatDouble(col.getGreen()) + " " + + gen.formatDouble(col.getBlue()) + " setrgbcolor"); + } + + private void updateFont(String name, int size, StringBuffer pdf) { + + } + /** * @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject, Rectangle2D) */ @@ -677,6 +867,8 @@ public class PSRenderer extends AbstractRenderer { new Integer(currentBlockIPPosition + (int) pos.getX())); context.setProperty(PSXMLHandler.PS_YPOS, new Integer(currentBPPosition + (int) pos.getY())); + //context.setProperty("strokeSVGText", options.get("strokeSVGText")); + /* context.setProperty(PDFXMLHandler.PDF_DOCUMENT, pdfDoc); context.setProperty(PDFXMLHandler.OUTPUT_STREAM, ostream); diff --git a/src/org/apache/fop/render/ps/PSTextPainter.java b/src/org/apache/fop/render/ps/PSTextPainter.java new file mode 100644 index 000000000..6f2aab65a --- /dev/null +++ b/src/org/apache/fop/render/ps/PSTextPainter.java @@ -0,0 +1,435 @@ +/* + * $Id$ + * ============================================================================ + * The Apache Software License, Version 1.1 + * ============================================================================ + * + * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: "This product includes software + * developed by the Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "FOP" and "Apache Software Foundation" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", nor may + * "Apache" appear in their name, without prior written permission of the + * Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + * + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation and was originally created by + * James Tauber . For more information on the Apache + * Software Foundation, please see . + */ +package org.apache.fop.render.ps; + +import java.awt.Graphics2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.Font; + +import java.text.AttributedCharacterIterator; +import java.awt.font.TextAttribute; +import java.awt.Shape; +import java.awt.Paint; +import java.awt.Stroke; +import java.awt.Color; +import java.util.List; +import java.util.Iterator; + +import org.apache.batik.gvt.text.Mark; +import org.apache.batik.gvt.TextPainter; +import org.apache.batik.gvt.TextNode; +import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; +import org.apache.batik.gvt.font.GVTFontFamily; +import org.apache.batik.bridge.SVGFontFamily; +import org.apache.batik.gvt.renderer.StrokingTextPainter; + +import org.apache.fop.fonts.FontMetrics; +import org.apache.fop.layout.FontState; +import org.apache.fop.layout.FontInfo; + +/** + * Renders the attributed character iterator of a TextNode. + * This class draws the text directly into the PSGraphics2D so that + * the text is not drawn using shapes which makes the PS files larger. + * If the text is simple enough to draw then it sets the font and calls + * drawString. If the text is complex or the cannot be translated + * into a simple drawString the StrokingTextPainter is used instead. + * + * @todo handle underline, overline and strikethrough + * @todo use drawString(AttributedCharacterIterator iterator...) for some + * + * @author Keiron Liddle + * @version $Id: PSTextPainter.java,v 1.15 2003/01/08 14:03:55 jeremias Exp $ + */ +public class PSTextPainter implements TextPainter { + private FontInfo fontInfo; + + /** + * Use the stroking text painter to get the bounds and shape. + * Also used as a fallback to draw the string with strokes. + */ + protected static final TextPainter PROXY_PAINTER = + StrokingTextPainter.getInstance(); + + /** + * Create a new PS text painter with the given font information. + * @param fi the fint info + */ + public PSTextPainter(FontInfo fi) { + fontInfo = fi; + } + + /** + * Paints the specified attributed character iterator using the + * specified Graphics2D and context and font context. + * @param node the TextNode to paint + * @param g2d the Graphics2D to use + */ + public void paint(TextNode node, Graphics2D g2d) { + // System.out.println("PSText paint"); + String txt = node.getText(); + Point2D loc = node.getLocation(); + + AttributedCharacterIterator aci = + node.getAttributedCharacterIterator(); + // reset position to start of char iterator + if (aci.getBeginIndex() == aci.getEndIndex()) { + return; + } + char ch = aci.first(); + if (ch == AttributedCharacterIterator.DONE) { + return; + } + TextNode.Anchor anchor; + anchor = (TextNode.Anchor) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE); + + List gvtFonts; + gvtFonts = (List) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); + Paint forg = (Paint) aci.getAttribute(TextAttribute.FOREGROUND); + Paint strokePaint; + strokePaint = (Paint) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.STROKE_PAINT); + Float size = (Float) aci.getAttribute(TextAttribute.SIZE); + if (size == null) { + return; + } + Stroke stroke = (Stroke) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.STROKE); + /* + Float xpos = (Float) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.X); + Float ypos = (Float) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.Y); + */ + + Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE); + Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT); + + boolean useStrokePainter = false; + + if (forg instanceof Color) { + Color col = (Color) forg; + if (col.getAlpha() != 255) { + useStrokePainter = true; + } + g2d.setColor(col); + } + g2d.setPaint(forg); + g2d.setStroke(stroke); + + if (strokePaint != null) { + // need to draw using AttributedCharacterIterator + useStrokePainter = true; + } + + if (hasUnsupportedAttributes(aci)) { + useStrokePainter = true; + } + + // text contains unsupported information + if (useStrokePainter) { + PROXY_PAINTER.paint(node, g2d); + return; + } + + String style = ((posture != null) && (posture.floatValue() > 0.0)) + ? "italic" : "normal"; + int weight = ((taWeight != null) + && (taWeight.floatValue() > 1.0)) ? FontInfo.BOLD + : FontInfo.NORMAL; + + FontState fontState = null; + FontInfo fi = fontInfo; + boolean found = false; + String fontFamily = null; + if (gvtFonts != null) { + Iterator i = gvtFonts.iterator(); + while (i.hasNext()) { + GVTFontFamily fam = (GVTFontFamily) i.next(); + if (fam instanceof SVGFontFamily) { + PROXY_PAINTER.paint(node, g2d); + return; + } + fontFamily = fam.getFamilyName(); + if (fi.hasFont(fontFamily, style, weight)) { + String fname = fontInfo.fontLookup(fontFamily, style, + weight); + FontMetrics metrics = fontInfo.getMetricsFor(fname); + int fsize = (int)(size.floatValue() * 1000); + fontState = new FontState(fname, metrics, fsize); + found = true; + break; + } + } + } + if (!found) { + String fname = + fontInfo.fontLookup("any", style, FontInfo.NORMAL); + FontMetrics metrics = fontInfo.getMetricsFor(fname); + int fsize = (int)(size.floatValue() * 1000); + fontState = new FontState(fname, metrics, fsize); + } else { + if (g2d instanceof PSGraphics2D) { + ((PSGraphics2D) g2d).setOverrideFontState(fontState); + } + } + int fStyle = Font.PLAIN; + if (weight == FontInfo.BOLD) { + if (style.equals("italic")) { + fStyle = Font.BOLD | Font.ITALIC; + } else { + fStyle = Font.BOLD; + } + } else { + if (style.equals("italic")) { + fStyle = Font.ITALIC; + } else { + fStyle = Font.PLAIN; + } + } + Font font = new Font(fontFamily, fStyle, + (int)(fontState.getFontSize() / 1000)); + + g2d.setFont(font); + + float advance = getStringWidth(txt, fontState); + float tx = 0; + if (anchor != null) { + switch (anchor.getType()) { + case TextNode.Anchor.ANCHOR_MIDDLE: + tx = -advance / 2; + break; + case TextNode.Anchor.ANCHOR_END: + tx = -advance; + } + } + g2d.drawString(txt, (float)(loc.getX() + tx), (float)(loc.getY())); + } + + private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) { + boolean hasunsupported = false; + Object letSpace = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING); + if (letSpace != null) { + hasunsupported = true; + } + + Object wordSpace = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING); + if (wordSpace != null) { + hasunsupported = true; + } + + AttributedCharacterIterator.Attribute key; + key = GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE; + Object writeMod = aci.getAttribute(key); + if (!GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals( + writeMod)) { + hasunsupported = true; + } + + Object vertOr = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION); + if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals( + vertOr)) { + hasunsupported = true; + } + return hasunsupported; + } + + private float getStringWidth(String str, FontState fontState) { + float wordWidth = 0; + float whitespaceWidth = fontState.getWidth(fontState.mapChar(' ')); + + for (int i = 0; i < str.length(); i++) { + float charWidth; + char c = str.charAt(i); + if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) { + charWidth = fontState.getWidth(fontState.mapChar(c)); + if (charWidth <= 0) { + charWidth = whitespaceWidth; + } + } else { + charWidth = whitespaceWidth; + } + wordWidth += charWidth; + } + return wordWidth / 1000f; + } + + /** + * Get the outline shape of the text characters. + * This uses the StrokingTextPainter to get the outline + * shape since in theory it should be the same. + * + * @param node the text node + * @return the outline shape of the text characters + */ + public Shape getOutline(TextNode node) { + return PROXY_PAINTER.getOutline(node); + } + + /** + * Get the bounds. + * This uses the StrokingTextPainter to get the bounds + * since in theory it should be the same. + * + * @param node the text node + * @return the bounds of the text + */ + public Rectangle2D getBounds2D(TextNode node) { + return PROXY_PAINTER.getBounds2D(node); + } + + /** + * Get the geometry bounds. + * This uses the StrokingTextPainter to get the bounds + * since in theory it should be the same. + * @param node the text node + * @return the bounds of the text + */ + public Rectangle2D getGeometryBounds(TextNode node) { + return PROXY_PAINTER.getGeometryBounds(node); + } + + // Methods that have no purpose for PS + + /** + * Get the mark. + * This does nothing since the output is pdf and not interactive. + * @param node the text node + * @param pos the position + * @param all select all + * @return null + */ + public Mark getMark(TextNode node, int pos, boolean all) { + System.out.println("PSText getMark"); + return null; + } + + /** + * Select at. + * This does nothing since the output is pdf and not interactive. + * @param x the x position + * @param y the y position + * @param node the text node + * @return null + */ + public Mark selectAt(double x, double y, TextNode node) { + System.out.println("PSText selectAt"); + return null; + } + + /** + * Select to. + * This does nothing since the output is pdf and not interactive. + * @param x the x position + * @param y the y position + * @param beginMark the start mark + * @return null + */ + public Mark selectTo(double x, double y, Mark beginMark) { + System.out.println("PSText selectTo"); + return null; + } + + /** + * Selec first. + * This does nothing since the output is pdf and not interactive. + * @param node the text node + * @return null + */ + public Mark selectFirst(TextNode node) { + System.out.println("PSText selectFirst"); + return null; + } + + /** + * Select last. + * This does nothing since the output is pdf and not interactive. + * @param node the text node + * @return null + */ + public Mark selectLast(TextNode node) { + System.out.println("PSText selectLast"); + return null; + } + + /** + * Get selected. + * This does nothing since the output is pdf and not interactive. + * @param start the start mark + * @param finish the finish mark + * @return null + */ + public int[] getSelected(Mark start, Mark finish) { + System.out.println("PSText getSelected"); + return null; + } + + /** + * Get the highlighted shape. + * This does nothing since the output is pdf and not interactive. + * @param beginMark the start mark + * @param endMark the end mark + * @return null + */ + public Shape getHighlightShape(Mark beginMark, Mark endMark) { + System.out.println("PSText getHighlightShape"); + return null; + } + +} + + diff --git a/src/org/apache/fop/render/ps/PSXMLHandler.java b/src/org/apache/fop/render/ps/PSXMLHandler.java index 626e7839f..d1053fb30 100644 --- a/src/org/apache/fop/render/ps/PSXMLHandler.java +++ b/src/org/apache/fop/render/ps/PSXMLHandler.java @@ -50,24 +50,27 @@ */ package org.apache.fop.render.ps; -import org.apache.fop.render.XMLHandler; -import org.apache.fop.render.RendererContext; -import org.apache.fop.svg.SVGUserAgent; -import org.apache.fop.layout.FontInfo; +// Java +import java.awt.geom.AffineTransform; +import java.io.IOException; +// DOM import org.w3c.dom.Document; +import org.w3c.dom.svg.SVGDocument; +import org.w3c.dom.svg.SVGSVGElement; +// Batik import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.ViewBox; - import org.apache.batik.gvt.GraphicsNode; +import org.apache.batik.gvt.TextPainter; -import org.w3c.dom.svg.SVGDocument; -import org.w3c.dom.svg.SVGSVGElement; - -import java.awt.geom.AffineTransform; -import java.io.IOException; +// FOP +import org.apache.fop.render.XMLHandler; +import org.apache.fop.render.RendererContext; +import org.apache.fop.svg.SVGUserAgent; +import org.apache.fop.layout.FontInfo; /** * PostScript XML handler. @@ -300,7 +303,9 @@ public class PSXMLHandler implements XMLHandler { transform.translate(xOffset / 1000f, yOffset / 1000f); //aBridge.setCurrentTransform(transform); //ctx.putBridge(aBridge); - + + TextPainter textPainter = new PSTextPainter(psInfo.getFontInfo()); + ctx.setTextPainter(textPainter); GraphicsNode root; try { root = builder.build(ctx, doc); -- 2.39.5