I recently used Protovis to generate some matrix diagrams for use in a
research paper. The vector output looked great, but saving as a PDF
through the browser became an annoying manual process.
On the the mailing list, there are plenty of hits for similar
requests:
* Issue 41: Allow one to save the generated image
http://code.google.com/p/protovis-js/issues/detail?id=41
Method there uses {jquery, rsvg, php}
* server side engine - Options? [Feb 15, 2010, Pedro Alves]
http://groups.google.com/group/protovis/browse_thread/thread/ee5737796b4235eb
Nice discussion of various options and tradeoffs, with Jamie Love's
demo "rhinotest.zip" at the bottom.
* Vector Export [Aug 17, 2009, toomim]:
http://groups.google.com/group/protovis/browse_thread/thread/3169031c3b2aae52/eb1c5d6c4747f0aa
* Export protovis SVG to image in python/django [Nov 5 2010, Jan van
Gemert]
http://groups.google.com/group/protovis/browse_thread/thread/529e2cb095e3ef75?pli=1
I had trouble getting the rsvg approach to work on a Mac via MacPorts,
which wouldn't compile, though it was easier on Ubuntu. Ideally, you
wouldn't need to build anything.
The approach of using the Rhino Java-based JavaScript engine along w/
envJS to get a headless browser, then extracting the protovis-
generated SVG via innerHTML, seemed to work fine. Once you have SVG
you can use Batik to turn it into a PDF. This uses a pre-built
Java .jar so there's nothing to install.
I put together a Python script to generate PDF output in one line,
based on Jamie Love's code post. It's usable, though there's a bug
with text elements that appears to be issue with the envjs CSS
implementation as well as more complicated stuff (like the Matrix
example) running out of memory. I've put up the work-in-progress
here:
https://github.com/brandonheller/pv_export
It would be great if a CSS/SVG expert could take a quick look and see
if there's a workaround to get text style changes working, or if this
requires more envjs implementation; if we could get past that bug, it
should be much easier to generate Protovis PDFs offline.
Thanks,
Brandon Heller
Issues:
(1) Running the Area Chart example w/protovis-d3.2:
http://vis.stanford.edu/protovis/ex/area.html
...yields this error:
js: "protovis-d3.2.js", line 5057: uncaught JavaScript runtime
exception: TypeError: Cannot call method "removeProperty" of undefined
I can effectively comment out the error to get past it, and the output
loooks OK.
diff --git a/protovis-d3.2.js b/protovis-d3.2.js
index bfe5a02..63f4996 100644
--- a/protovis-d3.2.js
+++ b/protovis-d3.2.js
@@ -5054,7 +5054,7 @@ pv.SvgScene.expect = function(e, type,
attributes, style)
for (var name in style) {
var value = style[name];
if (value == this.implicit.css[name]) value = null;
- if (value == null) e.style.removeProperty(name);
+ if (value == null) 1; //e.style.removeProperty(name);
else e.style[name] = value;
}
return e;
The same error happens when using the PV master branch.
Adding some prints, I get this:
e: text
e.style: undefined
type: text
attributes: ({'pointer-events':"none", cursor:null, x:"-3", y:0,
dy:".35em", transform:"translate(0,200)", fill:"rgb(0,0,0)", 'fill-
opacity':1, 'text-anchor':"end"})
style: ({font:"10px sans-serif", 'text-shadow':null, 'text-
decoration':null})
name: font
Here's the function getting called, with the error marked:
/**
* Expects the element <i>e</i> to be the specified type. If the
element does
* not exist, a new one is created. If the element does exist but is
the wrong
* type, it is replaced with the specified element.
*
* @param e the current SVG element.
* @param type {string} an SVG element type, such as "rect".
* @param attributes an optional attribute map.
* @param style an optional style map.
* @returns a new SVG element.
*/
pv.SvgScene.expect = function(e, type, attributes, style) {
if (e) {
if (e.tagName == "a") e = e.firstChild;
if (e.tagName != type) {
var n = this.create(type);
e.parentNode.replaceChild(n, e);
e = n;
}
} else {
e = this.create(type);
}
for (var name in attributes) {
var value = attributes[name];
if (value == this.implicit.svg[name]) value = null;
if (value == null) e.removeAttribute(name);
else e.setAttribute(name, value);
}
for (var name in style) {
var value = style[name];
if (value == this.implicit.css[name]) value = null;
if (value == null) e.style.removeProperty(name) // ERROR
else {e.style[name] = value};
}
return e;
};
I'm trying to trace the bug here, and the first confusion is what
this.implicit.css is. 'implicit' doesn't turn up outside of this
function. It's evaluating to the same thing as value - "10px sans-
serif" - which for some reason sets value to null. Then on the next
line the code tries to remove a property from a newly created element
that has no properties! This seems to be a difference in behavior w/
envJS: when I run in Firefox, and print the created e w/Firebug, it's
been initialized:
e: <text pointer-events="none" x="-3" dy="0.35em"
transform="translate(0, 200)" fill="rgb(0,0,0)" text-anchor="end">
... and e.style is:
CSSStyleDeclaration { length=0 }
(2) (probably due to the hack for #1, or same root cause):
I can't change text properties, and get an error when trying to do
something like this to on a label:
.font("12px sans-serif")
(3) Rhino can't handle the full matrix example - causes heap space
errors. Tried adjusting this by starting the JVM with -Xss,
increasing to 128m, but always seemed to crash after a minute or so.