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 Alan Deikman <Al...@znyx.com> on 2009/03/28 01:35:15 UTC
Merging one document with another in a JSVGCanvas
A few weeks ago I was painting one or more JSVGCanvas objects on top of
another to build a display with multiple document roots. This works
pretty well by using the JContainer.add() method, then you can use Swing
methods to size and position the child JSVGCanvas. However it has some
drawbacks -- the deal breaker for me was reconciling the coordinate
systems and CTMs of both the parent and child objects so that the child
showed up where I wanted it to in the right scale. Further, the
kbd/mouse interactors for both objects remain completely separate.
So I learned how to do the following:
SVGDocument ourDoc = getSVGDocument();
SVGSVGElement root = ourDoc.getRootElement();
// now get another document and import it
SVGDocument doc = otherCanvas.getSVGDocument();
SVGSVGElement panelRoot = doc.getRootElement();
SVGOMSVGElement node = (SVGOMSVGElement)
ourDoc.importNode(panelRoot, true);
// now add it to the display
root.appendChild(node);
So far so good. The next step is what I need help with, which is to
scale, translate, and possibly rotate the sub-document in "node" to a
fixed place in "root." I have the target position as follows:
SVGGraphicsElement position = (SVGGraphicsElement)
ourDoc.getElementByID("xyzzy");
SVGRect targetRect = position.getBBox();
So what is the most efficient way to get "node" to appear exactly on top
of "targetRect?" I have gotten hopelessly confused over the various
viewports, viewboxes, and transforms involved.
I may also need to rotate node by 90 degrees. Since node is an SVG
element, it won't take a transform, so do I need to encapsulate it
inside a G element?
Much thanks for any help on this.
--
Alan Deikman
ZNYX Networks
---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org
Re: Merging one document with another in a JSVGCanvas
Posted by Helder Magalhães <he...@gmail.com>.
Hi Alan,
> However it has some drawbacks --
> the deal breaker for me was reconciling the coordinate systems and CTMs of
> both the parent and child objects so that the child showed up where I wanted
> it to in the right scale. Further, the kbd/mouse interactors for both
> objects remain completely separate.
Yes, I recall a few related mailing list threads recently, with
complex math and quite a bit of code to make it work all together...
;-)
> So I learned how to do the following:
[code snippet]
So "the following" seems to be embedding a document within the other.
Generally it seems a good idea. :-)
> So what is the most efficient way to get "node" to appear exactly on top of
> "targetRect?" I have gotten hopelessly confused over the various
> viewports, viewboxes, and transforms involved.
> I may also need to rotate node by 90 degrees. Since node is an SVG element,
> it won't take a transform, so do I need to encapsulate it inside a G
> element?
Well, for a start I'd propose a slightly different approach, intending
to be more general:
* Create a parent document to hold all the imported documents ("container");
* Create a svg container [1] for each document being imported;
* Adjust the dimension and aspect ratio [2] of the child documents.
The proposal would result in something like (untested code snippet):
<svg xmlns="http://www.w3.org/2000/svg" id="container">
<svg preserveAspectRatio="xMaxYMax" width="$width" height="$height"
id="document1">
<!-- document 1 contents -->
</svg>
<svg preserveAspectRatio="xMaxYMax" width="$width" height="$height"
id="document2">
<!-- document 2 contents -->
</svg>
<!-- ... document N ... -->
</svg>
Note that "$width" and "$height" are variables to be replaced with
actual values which should be coherent with the target dimension.
Thing is, generally, the overlay approach may have a caveat: if the
proportions of the documents are not the same, the final look can be
weird. In the sample above, the documents are stretch to the target
dimension (aspect ratio isn't preserved), although you may try other
approaches (see the specification [2] for a nice sample).
> Much thanks for any help on this.
Hope this helps,
Helder Magalhães
[1] http://www.w3.org/TR/SVG11/intro.html#TermContainerElement
[2] http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org
Re: Merging one document with another in a JSVGCanvas
Posted by Alan Deikman <Al...@znyx.com>.
thomas.deweese@kodak.com wrote:
>
> Right. The easiest way to map things is to get the bbox of
> 'node' (same as position above).
Tom,
I was trying to avoid this, because at the time that 'node' is imported,
the GVT tree isn't computed so node.getBBox() returns null. I did
figure out how to get it computed as follows:
UserAgentAdapter agent = new UserAgentAdapter();
BridgeContext ctx = new BridgeContext(agent);
GVTBuilder builder = new GVTBuilder();
GraphicsNode rootGN = builder.build(ctx, ourDoc);
And then all the bboxes are available. Question: does invoking a
GVTBuilder() at this point have any bad side effects or is there another
way of doing this? Remember I am doing this inside the UpdateManager's
thread.
So far I have discovered that I can get the translation done by just
setting the viewport on the SVG element as follows:
node.setAttribute("x", Double.toString(position.getX()));
node.setAttribute("y", Double.toString(position.getY()));
node.setAttribute("width", Double.toString(position.getWidth()));
node.setAttribute("height",
Double.toString(position.getHeight()));
This way I don't need the bbox of 'node'. However, once I need
rotation it all gets screwed up both in terms of position and scale.
I'm still working on that but I wanted to follow up on your helpful post
with what I had so far.
--
Alan Deikman
Re: Merging one document with another in a JSVGCanvas -- one solution
Posted by Alan Deikman <Al...@znyx.com>.
Helder Magalhães wrote:
> I'm not particularly interested, but yes: sharing a/the solution (or a
> possible workaround, etc.), even if just a summary of it, is generally
> welcome [1]... ;-)
>
OK. I do have an outstanding question about my own code, but it is an
SVG question and not anything to do with Batik. The question is what is
the role of the viewBox attribute in the imported node? I found that if
I do not set it to the viewport parameters this doesn't work.
SVGDocument ourDoc = getSVGDocument();
SVGSVGElement root = ourDoc.getRootElement();
SVGDocument doc = /* the document to import */ ;
// import the root SVG element
SVGSVGElement panelRoot = doc.getRootElement();
SVGOMSVGElement node = (SVGOMSVGElement)
ourDoc.importNode(panelRoot, true);
// make sure we are using the right namespace
String ns = root.getNamespaceURI();
// discover the rect where we want the new graphic to go
SVGRect position = /* where we want node to appear */;
// the original panel width is used to calculate the scale
// factor. We assume that the aspect ratio of node and root are
the same
float originalPanelWidth = node.getWidth().getBaseVal().getValue();
// set the viewport
node.setAttribute("x", Double.toString(position.getX()));
node.setAttribute("y", Double.toString(position.getY()));
node.setAttribute("width", Double.toString(position.getWidth()));
node.setAttribute("height", Double.toString(position.getHeight()));
// set the viewbox to the same as the view port
node.setAttribute("viewBox", String.format("%f %f %f %f",
position.getX(), position.getY(),
position.getWidth(), position.getHeight()));
// create a g element and transfer all of the svg element's
// elements to it, then make the g element the only child
// of the original g element
SVGOMGElement g = (SVGOMGElement)
ourDoc.createElementNS(ns, "g");
Node child;
while ((child = node.getFirstElementChild()) != null)
g.appendChild(child);
node.appendChild(g);
// move and scale to the position without rotation
if (!isRotationNeeded()) {
g.setAttribute("transform", String.format(
"translate(%f, %f) scale(%f)",
position.getX(), position.getY(),
position.getWidth() / originalPanelWidth));
}
// or rotate 90 degrees if needed
else {
g.setAttribute("transform", String.format(
"rotate(90) translate(%f, %f) scale(%f)",
0.0,
0 - position.getX() - position.getWidth(),
position.getHeight() / originalPanelWidth));
}
// attach to the new SVG element to our root and we are done
root.appendChild(node);
I hope someone finds this useful even though there are probably more
elegant ways of getting this done.
--
Alan Deikman
---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org
Re: Merging one document with another in a JSVGCanvas
Posted by Helder Magalhães <he...@gmail.com>.
Hi Alan,
> If anyone is interested in the solution I
> can post it here.
I'm not particularly interested, but yes: sharing a/the solution (or a
possible workaround, etc.), even if just a summary of it, is generally
welcome [1]... ;-)
Regards,
Helder
[1] http://www.catb.org/~esr/faqs/smart-questions.html#followup
---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org
Re: Merging one document with another in a JSVGCanvas
Posted by Alan Deikman <Al...@znyx.com>.
thomas.deweese@kodak.com wrote:
>
> Right. The easiest way to map things is to get the bbox of
> 'node' (same as position above). Then you can map the center of
> the node bbox to 0,0:
I managed to solve this problem a few weekends back but haven't posted
about it because it turns out to be much more an SVG exercise than
anything to do with Batik specifically. It works without having to
compute the bounding box of the imported node first. If anyone is
interested in the solution I can post it here. Thanks to everyone for
the help.
--
Alan Deikman
Re: Merging one document with another in a JSVGCanvas
Posted by th...@kodak.com.
Hi Alan,
Alan Deikman <Al...@znyx.com> wrote on 03/27/2009 08:35:15 PM:
> So far so good. The next step is what I need help with, which is to
> scale, translate, and possibly rotate the sub-document in "node" to a
> fixed place in "root." I have the target position as follows:
>
> SVGGraphicsElement position = (SVGGraphicsElement)
> ourDoc.getElementByID("xyzzy");
> SVGRect targetRect = position.getBBox();
>
> So what is the most efficient way to get "node" to appear exactly on top
> of "targetRect?" I have gotten hopelessly confused over the various
> viewports, viewboxes, and transforms involved.
> I may also need to rotate node by 90 degrees. Since node is an SVG
> element, it won't take a transform, so do I need to encapsulate it
> inside a G element?
Right. The easiest way to map things is to get the bbox of
'node' (same as position above). Then you can map the center of
the node bbox to 0,0:
translate(-nodeRect.X+nodeRect.Width/2, -nodeRect.Y+nodeRect.Height/2)
Then scale the width and height of 'node' to match
targetRect (take into account rotation if needed):
scale(targetRect.Width/nodeRect.Width,
targetRect.Height/nodeRect.Height)
Then translate it back to the center of the targetRect.
translate(targetRect.X+targetRect.Width/2,
targetRect.Y+targetRect.Height/2)
They need to appear in 'reverse order' (IIRC) so:
<g transform="translate(targetRect.X+targetRect.Width/2,
targetRect.Y+targetRect.Height/2);
scale(targetRect.Width/nodeRect.Width,
targetRect.Height/nodeRect.Height);
translate(-nodeRect.X+nodeRect.Width/2,
-nodeRect.Y+nodeRect.Height/2);">