You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-users@xmlgraphics.apache.org by Ch...@daifukuamerica.com on 2004/07/22 19:22:24 UTC

using JSVGCanvas to draw somewhere else

Hello, fellow vector graphics pioneers.
First the questions:
1) Why does JSVGCanvas.getGraphicsNode() sometimes return null, at first. 
What event must I wait on to be sure that the data is available?
2) Why does GraphicsNode.getBounds() sometimes return really tiny 
rectangles? (Drawing many of the sample SVGs included with Batik renders 
them in a 1 or 2 pixel box, and I have to zoom the hell out of the 
transform in order to see anything. Still, other SVG samples display at 
the proper size.)
Now the details:
I am developing a graphical application which displays a Figure, where a 
Figure comprises a set of Doodles. Each Doodle is an independent object 
that can be programmatically added to or removed from the Figure. There 
are ShapeDoodles, TextDoodles, ImageDoodles, and various other sorts of 
Doodles, depending on what the Figure needs to display. Each Doodle 
implements the Doodle interface. The relevant part of this interface is 
shown below:
/**
 * Individual component in a figure.
 *
 * <p><b>Details:</b> A <code>Doodle</code> is a single, independent 
component
 * of a {@link Figure}.  Each doodle defines its boundaries, appearance, 
and
 * relative depth in the figure.</p>
*/
public interface Doodle
{

    /**
     * Renders doodle.
     *
     * <p><b>Details:</b> <code>draw</code> renders this doodle using the
     * supplied graphics environment.  When the host {@link FigureView} is
     * repainting itself, it will probably call this method, albeit 
indirectly,
     * to redraw its figure's components.  This method returns 
<code>true</code>
     * if drawing was successful, <code>false</code> otherwise.</p>
     *
     * @param ipG the graphics context
     * @return true iff drawing was successful
     */
    boolean draw(Graphics2D ipG);

    /**
     * Returns rectangular extent.
     *
     * <p><b>Details: </b> This method returns the rectangular extent of 
this
     * <code>Doodle</code>. The rectangle returned is in logical (i.e.
     * figure) units, not display units.</p>
     *
     * @return the extent
     */
    Rectangle2D getBounds();

}

As you may have guessed, I am developing an implementation of Doodle that 
renders SVG documents. A portion of my experimental implementation is 
shown below:
public class SvgDoodle implements Doodle
{

    private GraphicsNode mpGraphicsNode;

    public SvgDoodle(SVGDocument ipDocument)
    {
        JSVGCanvas vpCanvas = new JSVGCanvas();
        GvtTreeBuilderWaiter vpWaiter = new GvtTreeBuilderWaiter();
        vpCanvas.addGVTTreeBuilderListener(vpWaiter);
        vpCanvas.setDocument(ipDocument);
        vpWaiter.waitForResult();
        mpGraphicsNode = vpCanvas.getGraphicsNode();
        // What a hack!  This is horrible and needs to be fixed.
        while (mpGraphicsNode == null)
        {
            try
            {
                Thread.sleep(1000);
            }
            catch (InterruptedException ee)
            {
            }
            mpGraphicsNode = vpCanvas.getGraphicsNode();
        }
    }

    public boolean draw(Graphics2D ipG)
    {
        mpGraphicsNode.paint(ipG);
    }

    public Rectangle2D getBounds()
    {
        return mpGraphicsNode.getBounds();
    }

}

The class GvtTreeBuilderWaiter is implemented as follows:
/**
 * Waits for GVT tree builder's final state.
 *
 * <p><b>Details:</b> This class listens to a GVT tree builder and tracks 
the
 * tree's build state.  The utility of this class lies entirely in
 * {@link #waitForResult()}, which blocks and does not return until the 
tree
 * builder reaches its final state.</p>
*/
public class GvtTreeBuilderWaiter implements GVTTreeBuilderListener
{

    // Note: The values of the BUILD_* constants are not arbitrary.  Keep 
them
    // in the same numerical order.

    public static final int BUILD_STARTED = 1;

    public static final int BUILD_COMPLETED = 2;

    public static final int BUILD_CANCELLED = 3;

    public static final int BUILD_FAILED = 4;

    private int mnResult = 0;

    public void gvtBuildStarted(GVTTreeBuilderEvent ipIgnored)
    {
        mnResult = BUILD_STARTED;
    }

    public synchronized void gvtBuildCompleted(GVTTreeBuilderEvent 
ipIgnored)
    {
        mnResult = BUILD_COMPLETED;
        notifyAll();
    }

    public synchronized void gvtBuildCancelled(GVTTreeBuilderEvent 
ipIgnored)
    {
        mnResult = BUILD_CANCELLED;
        notifyAll();
    }

    public synchronized void gvtBuildFailed(GVTTreeBuilderEvent ipIgnored)
    {
        mnResult = BUILD_FAILED;
        notifyAll();
    }

    public synchronized boolean waitForResult()
    {
        while (mnResult < BUILD_COMPLETED)
            try
            {
                wait();
            }
            catch (InterruptedException ee)
            {
                return false;
            }
        return true;
    }

}

The bottom line is, I simply need a way to convert a URL into an object 
that has the following methods:
1.      a method that paints to any Graphics2D I supply
2.      a method that returns the document's rectangular bounds
The code shown above only works about half the time. Any ideas?

Re: using JSVGCanvas to draw somewhere else

Posted by Thomas DeWeese <Th...@Kodak.com>.
Tonny Kohar wrote:

>>2) Why does GraphicsNode.getBounds()sometimes return really tiny
>>rectangles? (Drawing many of the sample SVGs included with Batik
>>renders them in a 1 or 2 pixel box, and I have to zoom the hell out of
>>the transform in order to see anything. Still, other SVG samples
>>display at the proper size.)

    I suspect this is caused by loading the SVG before the JSVGCanvas
has a size.  If the canvas doesn't have a size (or size is zero) it
set's it's size to 1 for purposes of calculating the viewing
transform.

    This raises the other issue I was going to mention which is the
JSVGCanvas may not be what you want to be using here.  A better
match might be to use the batik.transcoder.SVGAbstractTranscoder.
This will build the GVT tree and set the viewing transform in a
bit lighter weight manner - and also probably easier to understand.

> It maybe because the viewbox or transform stuff. Try to calculate the
> bounds by applying the transform of graphicsNode.getGlobalTransform()
> eg: 
> Shape shape = (Shape)graphicsNode.getBounds();
> AffineTransform at = graphicsNode.getGlobalTransform()
> shape = at.createTransformedShape(shape);
> Rectangle bounds = shape.getBounds();
> 
> Regards
> Tonny Kohar


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


Re: using JSVGCanvas to draw somewhere else

Posted by Tonny Kohar <to...@kiyut.com>.
Hi,

> 1) Why does JSVGCanvas.getGraphicsNode()sometimes return null, at
> first. What event must I wait on to be sure that the data is
> available?

I think you can wait for GVT listener specially rendererComplete. Hope
it solve your problem.

> 2) Why does GraphicsNode.getBounds()sometimes return really tiny
> rectangles? (Drawing many of the sample SVGs included with Batik
> renders them in a 1 or 2 pixel box, and I have to zoom the hell out of
> the transform in order to see anything. Still, other SVG samples
> display at the proper size.)

It maybe because the viewbox or transform stuff. Try to calculate the
bounds by applying the transform of graphicsNode.getGlobalTransform()
eg: 
Shape shape = (Shape)graphicsNode.getBounds();
AffineTransform at = graphicsNode.getGlobalTransform()
shape = at.createTransformedShape(shape);
Rectangle bounds = shape.getBounds();

Regards
Tonny Kohar
-- 
Sketsa 
SVG Graphics Editor
http://www.kiyut.com



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