You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by je...@apache.org on 2003/03/11 09:42:24 UTC

cvs commit: xml-fop/src/org/apache/fop/render/ps PSTextPainter.java PSXMLHandler.java PSRenderer.java PSProcSets.java PSGraphics2D.java

jeremias    2003/03/11 00:42:24

  Modified:    src/org/apache/fop/render/ps PSXMLHandler.java
                        PSRenderer.java PSProcSets.java PSGraphics2D.java
  Added:       src/org/apache/fop/render/ps PSTextPainter.java
  Log:
  Port of the PDF TextPainter to PostScript.
  Support for SEG_QUADTO (curves).
  Some support for viewport traits (background and borders).
  Submitted by: Zhong Yi <yi...@yahoo.com>
  
  Revision  Changes    Path
  1.4       +16 -11    xml-fop/src/org/apache/fop/render/ps/PSXMLHandler.java
  
  Index: PSXMLHandler.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/org/apache/fop/render/ps/PSXMLHandler.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- PSXMLHandler.java	7 Mar 2003 09:46:30 -0000	1.3
  +++ PSXMLHandler.java	11 Mar 2003 08:42:24 -0000	1.4
  @@ -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 @@
               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);
  
  
  
  1.31      +197 -5    xml-fop/src/org/apache/fop/render/ps/PSRenderer.java
  
  Index: PSRenderer.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/org/apache/fop/render/ps/PSRenderer.java,v
  retrieving revision 1.30
  retrieving revision 1.31
  diff -u -r1.30 -r1.31
  --- PSRenderer.java	5 Mar 2003 20:38:26 -0000	1.30
  +++ PSRenderer.java	11 Mar 2003 08:42:24 -0000	1.31
  @@ -59,6 +59,9 @@
   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.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.
    * <br>
  @@ -416,14 +422,15 @@
                   {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 @@
           
           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 @@
           //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 @@
                               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);
  
  
  
  1.3       +14 -1     xml-fop/src/org/apache/fop/render/ps/PSProcSets.java
  
  Index: PSProcSets.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/org/apache/fop/render/ps/PSProcSets.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- PSProcSets.java	7 Mar 2003 09:46:30 -0000	1.2
  +++ PSProcSets.java	11 Mar 2003 08:42:24 -0000	1.3
  @@ -141,7 +141,20 @@
           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");
       }
   
  
  
  
  1.11      +96 -42    xml-fop/src/org/apache/fop/render/ps/PSGraphics2D.java
  
  Index: PSGraphics2D.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/org/apache/fop/render/ps/PSGraphics2D.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- PSGraphics2D.java	7 Mar 2003 09:46:30 -0000	1.10
  +++ PSGraphics2D.java	11 Mar 2003 08:42:24 -0000	1.11
  @@ -114,7 +114,10 @@
   
       /** Currently valid FontState */
       protected FontState fontState;
  -
  +    
  +    /** Overriding FontState */
  +    protected FontState overrideFontState = null;
  +    
       /**
        * the current (internal) font name
        */
  @@ -501,8 +504,9 @@
               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 @@
                                 + " 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 @@
                                 + " 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 @@
        * @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 + "]");
  -            
  -            gen.writeln("ET");
  -        } catch (IOException ioe) {
  -            handleIOException(ioe);
  +      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";
  +            
  +            //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 @@
               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 @@
                                 + " 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 @@
           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.
  
  
  
  1.1                  xml-fop/src/org/apache/fop/render/ps/PSTextPainter.java
  
  Index: PSTextPainter.java
  ===================================================================
  /*
   * $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 <jt...@jtauber.com>. For more information on the Apache
   * Software Foundation, please see <http://www.apache.org/>.
   */ 
  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 <tt>TextNode</tt>.
   * 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 <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
   * @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;
      }
  
  }
  
  
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: fop-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: fop-cvs-help@xml.apache.org