All:
Right now, I want to draw a svg and style it with external style. Everything goes well until I try to down load it:
function chartExporter(svg){
// svg is a D3 object
svg.attr("version", 1.1)
.attr("xmlns", "http://www.w3.org/2000/svg");
var svgDOM = svg.node();
var html = svgDOM.outerHTML;
var imgsrc = 'data:image/svg+xml;base64,'+ btoa(html);
imgobj.src = imgsrc;
imgobj.onload = function(){
var a = document.createElement("a");
a.download = "sample.svg";
a.href = imgsrc;
a.click();
}
}
The problem here is: If I use D3 to add inline style or attributes, the svg downloaded seems good, but if I use external css file to include style, those styles can not be applied to svg when converted( it make sense, cos they are not in that html string), I wonder how to bring those style into svg?
Thanks
Related
Trying to reproduce something similar to Openlayers with sidebar where once the sidebar is expanded the .ol-zoom margin-left is modified to change its position but I can't use jquery in my project, so looks for Vanilla JS or Angular based solution.
I saw that its quiet easy to change position of the openlayers Zoom buttons as answered here but I would like to change the position on some event trigger like a (sidebar-toggle button) button click.
Thanks
In JS you can dynamically add css to the application, for example
var css =
'.menuSeparator {' +
' border-bottom: solid 1px black;' +
'}';
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (style.styleSheet){
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
head.appendChild(style);
Here is my solution
const olZoom = document.getElementsByClassName("ol-zoom");
const sideBarElements = document.getElementsByClassName("sidebar-left");
if(!this.sidebarVisibility) {
(olZoom[0] as HTMLElement).style.marginLeft = "0px";
(olZoom[0] as HTMLElement).style.marginTop = "60px";
} else {
setTimeout(() => {
(olZoom[0] as HTMLElement).style.marginLeft = ((sideBarElements[0] as HTMLElement).offsetWidth - 10) + 'px';
(olZoom[0] as HTMLElement).style.marginTop = "0px";
}, 50);
}
major issue what I was facing was that I wasn't casting Element to HTMLElement in typescript as explained here which was blocking me to apply my margin style
Wirecloud uses iframes to render the html of widgets. This seems to create problems for some advanced svg features that require references to internal definitions (see also this discussion).
The js code producing the svg works fine in single page app or django view.
There are no error messages.
The entire code is too big to post here, but the key relevant elements are:
var canvas = document.getElementById("canvas");
var svgns = "http://www.w3.org/2000/svg";
var svg = document.createElementNS(svgns, 'svg');
then a typical definition (for example a gradient):
var defs = document.createElementNS(svgns, "defs");
var linearGradient = document.createElementNS(svgns, "linearGradient");
and finally the use of the definition
arc.setAttribute('style', "fill:url(about:srcdoc#linearGradient);");
It is the reference (here to #linearGradient) that is not properly linked to when the svg object is created (when inspecting the created graphics the fill attribute is null)
Normally in svg you just use url(#reference). Have tried "about:blank" and "about:srcdoc" as workarounds as suggested elsewhere on SO but somehow they don't seem to work (chrome / firefox)
This seems to be an issue with svg / iframe (not wirecloud specific) but I have not tried to render the svg in iframe outside wirecloud to confirm this.
If there is no workaround this would limit the type of svg graphics that can be rendered within a wirecloud widget programmatically using javascript. Maybe some of the other ways of embedding svg would work (fetching it from the server) but its not optimal design for interactive widgets, or maybe an iframe alternative :-)
All the architecture of WireCloud is based on iframes. Those elements are used for providing the isolation between each of the widgets and operators and, currently, cannot be disabled. Moreover, the proposed alternative (webcomponents) is not matured enough to be a real alternative :(.
Anyway, WireCloud doesn't make use of the srcdoc attribute, so your problem rendering SVG images inside widgets should not be related to the linked question. The problem seems to be related to the use of the <base> element (which is injected by WireCloud into the HTML of the widget). See this answer for more details. We have created a ticket for analysing if we can remove the <base> element, but for now, you will have to use absolute URLs. E.g.:
var baseUrl = window.location.origin + window.location.pathname + window.location.search;
arc.setAttribute("fill", "url(" + baseUrl + "#MyGradient)");
This is the full code of my "SVG example widget":
var baseUrl = window.location.origin + window.location.pathname + window.location.search;
var svgns = "http://www.w3.org/2000/svg";
var svg = document.createElementNS(svgns, 'svg');
document.body.appendChild(svg);
var defs = document.createElementNS(svgns, "defs");
var linearGradient = document.createElementNS(svgns, "linearGradient");
linearGradient.setAttribute("id", "MyGradient");
defs.appendChild(linearGradient);
svg.appendChild(defs);
var stop = document.createElementNS(svgns, "stop");
stop.setAttribute("offset", "5%");
stop.setAttribute("stop-color", "green");
linearGradient.appendChild(stop);
stop = document.createElementNS(svgns, "stop");
stop.setAttribute("offset", "95%");
stop.setAttribute("stop-color", "gold");
linearGradient.appendChild(stop);
var rect = document.createElementNS(svgns, "rect");
rect.setAttribute("x", "10");
rect.setAttribute("y", "10");
rect.setAttribute("width", "100");
rect.setAttribute("height", "100");
rect.setAttribute("fill", "url(" + baseUrl + "#MyGradient)");
We can use canvas for drawing custom shapes.
I need to draw my shape dynamically as a canvas item and place it for a div background item.
My pages generates run time and they aren't static html code so i can't use tricky methods.
What's your idea?
Regards
Looks like you searching for toDataURL().
UPD:
Here a usage exaple:
dataUrl = your_canvas.toDataURL();
your_div.style.background='url('+dataUrl+')'
Live demo on jsFiddle
Sounds like you need canvas2image: https://github.com/hongru/canvas2image
You can create a canvas and then get the contents as a png:
var canvas = document.createElement("canvas");
....do stuff here...
var img = Canvas2Image.convertToPNG(canvas, canvas.width, canvas.height);
Then all you need to do is use the png as a background image:
document.body.style.background = "url(" + img.src + ")";
Please correct me if any of this is wrong.
I'm interested in creating a PNG from SVG. I followed the code given in:
https://developer.mozilla.org/en-US/docs/HTML/Canvas/Drawing_DOM_objects_into_a_canvas
But the image does not come out right due to styling from CSS. I made a local CSS file and do an import into the SVG, as described in:
How to apply a style to an embedded SVG?
But it does not appear to be using the style sheet. Any ideas why I would have this error?
Thanks.
Have a look at PhantomJS - You need to install it then either write your own script or run something along these lines:
phantomjs rasterize.js http://ariya.github.com/svg/tiger.svg tiger.png
You can also save to PDF, set Zoom setting, etc.
You could use html2canvas to generate a canvas from any dom element (including svg elements).
However, style definitions for svg elements defined in stylesheets are not applied to the generated canvas. This can be patched by adding style definitions to the svg elements before calling html2canvas.
Inspired on this article, I've created this:
function generateStyleDefs(svgDomElement) {
var styleDefs = "";
var sheets = document.styleSheets;
for (var i = 0; i < sheets.length; i++) {
var rules = sheets[i].cssRules;
for (var j = 0; j < rules.length; j++) {
var rule = rules[j];
if (rule.style) {
var selectorText = rule.selectorText;
var elems = svgDomElement.querySelectorAll(selectorText);
if (elems.length) {
styleDefs += selectorText + " { " + rule.style.cssText + " }\n";
}
}
}
}
var s = document.createElement('style');
s.setAttribute('type', 'text/css');
s.innerHTML = "<![CDATA[\n" + styleDefs + "\n]]>";
//somehow cdata section doesn't always work; you could use this instead:
//s.innerHTML = styleDefs;
var defs = document.createElement('defs');
defs.appendChild(s);
svgDomElement.insertBefore(defs, svgDomElement.firstChild);
}
// generate style definitions on the svg element(s)
generateStyleDefs(document.getElementById('svgElementId'));
// after generating the style defintions, call html2canvas
html2canvas(document.getElementById('idOfElement')).then(function(canvas) {
document.body.appendChild(canvas);
});
The example at
"How to apply a style to an embedded SVG?" as you mentioned should work. You need to define youObjectElement in this line of code when you test it.
var svgDoc = yourObjectElement.contentDocument;
try again.
How can I change iframe background color on design time?
By design time I mean the following:
My class extends CompositeControl where i implement design-time support.
this is part of CreateChildControls():
editor = new HtmlGenericControl("iframe");
editor.ID = "editorID";
editor.Style["background-color"] = "Red";
editor.Style["color"] = "Black";
editor.Style["position"] = "absolute";
editor.Style["left"] ="0px";
editor.Style["top"] = "0px";
editor.Attributes["width"] = editorWidth.ToString() + "px";
editor.Attributes["height"] = editorHeight.ToString() + "px";
editor.Attributes["frameborder"] = "0";
editor.Attributes["scrolling"] = "auto";
You have to set the background of whatever page the iframe is pointed to (the src attribute) ;)
If the iframe is pointing to the same domain as you, then you could use Javascript to alter the background color or add an additional stylesheet - but this isn't at design time, either.
What is this iframe for?