How to reuse a SVG generated from highcharts as background - svg

I am generating a grid, which is a SVG generated using highcharts and I want to reuse it as background of couple of pictures (<img src=https://xxx.xx.xxx.xx/pic_1.svg> & pic_2.svg) how to get this done?
I think might be possible to export the grid SVG to a file first then use it as a file. However, is there anyway simply save a Highchart.SVGRender or Highchart.SVGElement and use it, instead of exporting to a file, which I think will be slow I/O?
thank you.

You can parse a new string by concatenation of data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg">, generated SVG outerHTML value, and then setting yourimage.src by generated string. Here is how to achieve it 'step by step':
Create new SVG element using SVGRenderer from Highcharts:
var dot = chart.renderer.circle(10, 10, 10)
.attr({
fill: 'red',
zIndex: 99
})
.add()
Then, let's create new empty <img> element.
<img id="dot" src='' alt="">
And finally, set <img> src attribute by new generated value:
var svgString = dot.element.outerHTML
var dotImg = document.getElementById('dot')
var string = 'data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20">'+svgString+'</svg>'
dotImg.src = string
Live example: https://jsfiddle.net/864s9cwz/

Related

Blazor - Add element to SVG object

I want to manipulate an SVG object with blazor, is it possible to do this via C# client side, or do I need to use javascript.
For example, draw a line programatically based on clicks in the SVG area.
Any pointers would be greatly appreciated. I found a lot on adding SVG component, but nothing on adding elements to the already existing svg component
You can use SVG file contents as the markup in a Blazor component, and then do any of the Blazor-y things you'd normally do.
Put a variable in the svg markup, and build it as a string.
Here's a highly-simplified excerpt:
(MySvgComponent.blazor)
<svg blah blah blah>
<polyline fill="none" stroke="#0074d9"
stroke-width="2" points="#PointString" />
</svg>
#code {
public string #PointString {get;set;}
public void AddPoint (int X, int Y){
#PointString += " " + X + "," + Y;
}
You'll have to add your own code to figure out where you want to add the points. You could make a List<Point> parameter to pass in from a parent or something, and then call AddPoint in a foreach loop in OnInitialized(). You could also very easily replace the stroke color or anything else by replacing literals like "0074d9" with variables "#myColorString."
Handling mouse-click locations will require some fancy JS work. Try using Javascript Interop with something like the following:
How to get the click coordinates relative to SVG element holding the onclick listener?

getting text width in SVG prior to rendering

I want to put a rectangle around a text in SVG.
The height of the text is known to me (the font-size attribute of the text element). But the width is dependent on the actual content. Using getBBox() or getComputedTextLength() should work. But this only works after rendering.
Is there a way to specify that in an other way? For example defining the x and width attributes relative to other values? I didn't find anything like that in the SVG Spec.
Figuring where text ends presumably requires roughly the same underlying code path as the rendering itself implements - going through the width of each character based on the font and style, etc... As I am not aware the SVG standards define a method for directly getting this information without doing the actual full rendering, till such methods emerge or are reported here by others, the approach should be to render invisibly before doing the actual rendering.
You can do that in a hidden layer (z-index, opacity and stuff) or outside the visible viewport, whichever works best in experimentation. You just need to get the browser do the rendering to find out, so you render invisibly for that sake, then use getComputedTextLength()
I know this is old, but a few ideas:
If you can choose a mono-spaced font, you know your width by a simple constant multiplication with glyph count
If you are bound to proportional fonts, you can find an average glyph size, do the math as with mono-space, and leave enough padding. Alternatively you can fill the padding with text element textLength attribute. If the constant is chosen carefully, the results are not very displeasing.
EDIT: As matanster found it to be hacky
Predetermine glyph widths with getComputedTextLength() and build a lookup table. Downside is that it does not account for kerning, but if your cache size is not a problem, you can append glyph-pair widths to this lookup.
Going beyond that is to find some way to do server side rendering: Is there a way to perform server side rendering of SVG graphics using React?
It is possible using canvas with measureText():
// Get text width before rendering
const getTextWidth = (text, font) => {
const element = document.createElement('canvas');
const context = element.getContext('2d');
context.font = font;
return context.measureText(text).width;
}
// Demo
const font = '16px serif';
const text = 'My svg text';
const textWidth = getTextWidth(text, font);
document.body.innerHTML = `
<svg>
<text x="0" y="20" font="${font}">${text}</text>
<rect x="0" y="30" width="${textWidth}" height="4" fill="red" />
</svg>
`;
Adapted from https://stackoverflow.com/a/31305410/1657101

Raphael and preserveAspectRatio

With RaphaelJS, this command inserts an image:-
var myImg = paper.image('image.svg', 100, 100, 150,150);
and the SVG output is:-
<image x="100" y="100" width="150" height="150" preserveAspectRatio="none" href="image.svg"/>
Question: How do I directly access preserveAspectRatio attribute and change it to xMidYMid meet - if you examine myImg.attr(), it doesnt show this attribute.
The roundabout way is navigate the SVG DOM tree, and execute svgImg.setAttributeNS(null,"preserveAspectRatio" , "xMidYMid meet" );
Note: Only some images require none while the rest needs the xMidYMid meet tag. Hence I can't set this attribute on parent <svg>
Note2: Chrome doesn't support preserveAspectRatio with SVG images. Use FF or IE to test.
At the source code level, preserveAspectRatio is hardcoded to none
Answer The quickest way to change this:-;
myImg[0].preserveAspectRatio.baseVal.align = 6 (1 = off, 6 = xMidYMid)
myImg[0].preserveAspectRatio.baseVal.meetOrSlice = 1 (1 = meet, 2 = slice)
Update:- jQuery style:-
jQuery(myImg.node).prop('preserveAspectRatio').baseVal.align = 6 ;
jQuery(myImg.node).prop('preserveAspectRatio').baseVal.meetOrSlice = 1 ;
Raphael's docs for Element.node "Gives you a reference to the DOM object, so you can assign event handlers or just mess around. Note: Don’t mess with it."
You can call these parameters on the Raphael canvas as a whole.
First create SVG:
var paper = Raphael('content',xSize,ySize);
Place image in it:
paper.image('image.svg', 100, 100, 150,150);
Then change attributes of svg:
paper.canvas.setAttribute('preserveAspectRatio', 'xMidYMid meet');

raphael icons extract path

I am trying to create some login buttons based on the RaphaelJs icons, but on the example page there is only the Twitter paths that are available!
So I am looking to understand how to extract the SVG paths from Inkscape and update the example on http://jsfiddle.net/aqoon/LN23r/5/ for Google using http://upload.wikimedia.org/wikipedia/commons/2/28/Google_free_icon.svg file
var facebookBtn = "",
googleBtn = "M47.446122,148.46699L47.463496,149.0968L47.430196,149.09969C47.363594,148.8854247.278172,148.7259147.173az931,148.62119C47.069686,148.5164646.943725,148.464146.796048,148.4641C46.681186,148.464146.582493,148.4875146.499968,148.53432C46.417441,148.5811346.340465,148.6571446.269039,148.76235C46.124256,148.9775946.051865,149.2237246.051865,149.50074C46.051865,149.6329846.069239,149.7577346.103987,149.875C46.138734,149.9922846.189408,150.095846.256009,150.18556C46.385347,150.359346.54316,150.4461746.729448,150.44617C46.802803,150.4461746.871816,150.4307346.936487,150.39984C47.001155,150.3689547.054242,150.3264847.095748,150.27243C47.152694,150.1971547.181168,150.1078647.18117,150.00458C47.181168,149.9090347.155349,149.8400247.103711,149.79755C47.05207,149.7550846.96689,149.7338446.84817,149.73384L46.84817,149.70054L47.771883,149.70054L47.771883,149.73384C47.686942,149.7415647.62734,149.7533947.593076,149.76931C47.558809,149.7852447.533472,149.8125147.517065,149.85111C47.498724,149.8935947.489555,149.9780447.489557,150.10448C47.489555,150.1392347.491485,150.1932847.495348,150.26664L47.498244,150.29994C47.461564,150.2864347.434538,150.2796747.417165,150.27967C47.395929,150.2796747.368903,150.2907747.336087,150.31297C47.252112,150.3708847.154625,150.4152847.043626,150.44617C46.932625,150.4770646.815351,150.492546.691804,150.4925C46.521925,150.492546.368697,150.4635546.23212,150.40563C46.095541,150.3477245.984782,150.2647145.899844,150.15661C45.836139,150.0755345.787154,149.9790145.752889,149.86704C45.718624,149.7550845.701491,149.6363545.701491,149.51088C45.701491,149.338145.732378,149.1773945.794152,149.02875C45.855926,148.8801145.942795,148.7575346.054761,148.661C46.146456,148.5828246.252147,148.5224946.371835,148.48002C46.491521,148.4375646.617964,148.4163246.751165,148.41632C46.828382,148.4163246.900049,148.4235646.966168,148.43804C47.032284,148.4525247.102503,148.4761647.176826,148.50898L47.262248,148.54807C47.283481,148.5567647.302785,148.561147.320161,148.5611C47.358768,148.561147.388207,148.5297347.408479,148.46699L47.446122,148.46699z",
twitterBtn = "M14.605,13.11c0.913-2.851,2.029-4.698,3.313-6.038c0.959-1,1.453-1.316,0.891-0.216c0.25-0.199,0.606-0.464,0.885-0.605c1.555-0.733,1.442-0.119,0.373,0.54c2.923-1.045,2.82,0.286-0.271,0.949c2.527,0.047,5.214,1.656,5.987,5.077c0.105,0.474-0.021,0.428,0.464,0.514c1.047,0.186,2.03,0.174,2.991-0.13c-0.104,0.708-1.039,1.167-2.497,1.471c-0.541,0.112-0.651,0.083-0.005,0.229c0.799,0.179,1.69,0.226,2.634,0.182c-0.734,0.846-1.905,1.278-3.354,1.296c-0.904,3.309-2.976,5.678-5.596,7.164c-6.152,3.492-15.108,2.984-19.599-3.359c2.947,2.312,7.312,2.821,10.555-0.401c-2.125,0-2.674-1.591-0.99-2.449c-1.595-0.017-2.608-0.521-3.203-1.434c-0.226-0.347-0.229-0.374,0.14-0.64c0.405-0.293,0.958-0.423,1.528-0.467c-1.651-0.473-2.66-1.335-3.009-2.491c-0.116-0.382-0.134-0.363,0.256-0.462c0.38-0.097,0.87-0.148,1.309-0.17C6.11,10.88,5.336,9.917,5.139,8.852c-0.186-1.006,0.005-0.748,0.758-0.46C9.263,9.68,12.619,11.062,14.605,13.11L14.605,13.11z",
yahooBtn = "";
$('.twitterBtn').each(function(i) {
paper = Raphael($(this)[0], 40, 40)
paper.path(twitterBtn).attr({
"fill": "#333"
})
})
$('.googleBtn').each(function(i) {
paper = Raphael($(this)[0], 40, 40)
paper.path(googleBtn).attr({
"fill": "#333"
})
})​
I tried to strip the SVG file so that I just have one layer and only the 'G' but on the http://jsfiddle.net/aqoon/LN23r/5/ is not being displayed, what am i missing?
Also how do i add extra layers to the var googleBtn, as when I open the http://upload.wikimedia.org/wikipedia/commons/2/28/Google_free_icon.svg in Inkscape there are many layers and paths?
Raphael does not support ( SVG files in Raphael, can they be used?… ) loading whole SVG images and using those as paths, so the only reasonable option here is to extract separate paths and store them in some sort of datastructure, like in the example with the tiger ( http://raphaeljs.com/tiger.html ) -- check the source code there.
The SVG files can also have rather strange internal coordinate system, so it pays to adjust the view box after loading one, like this
var path = paper.path(googleBtn).attr({
"fill": "#333"
});
var bbox = path.getBBox();
paper.setViewBox(bbox.x, bbox.y, bbox.width, bbox.height);
To group different elements, one can use Raphael.Set http://jsfiddle.net/LN23r/40/
var shadow = paper.path(googleBtn).attr({"fill": "#0F0", "stroke":"none"}).translate(0.08,0.08);
var path = paper.path(googleBtn).attr({"fill": "#333", "stroke":"none"});
var set = paper.set(shadow, path);
var bbox = set.getBBox();
paper.setViewBox(bbox.x, bbox.y, bbox.width, bbox.height);
When grouping elements, note that they have transformation matrices associated with them in the SVG file (e.g. transform="matrix(204.67566,0,0,204.67566,-9225.9642,-30242.949)"), which affects the respective position and scale of the elements.
On the whole, the process of porting paths from SVG is not entirely trivial, but manageable. There is also a plugin that may help you with this, see https://github.com/wout/raphael-svg-import

Calculating viewBox parameters based on path elements in SVG

I get an XML or JSON with paths only, and I need to recreate the SVG image.
I create an empty
<svg xmlns='http://www.w3.org/2000/svg' version='1.1'></svg>,
I add a <g transform="scale(1 -1)" fill='#aaa' stroke='black' stroke-width='5' ></g> in it, and then in this element I add all of the paths in it (e.g. <path d= ... />).
In the end I get a SVG image, but because I haven't set the viewBox attribute in the SVG element the image isn't properly displayed - when I open it in browser, a part of it is displayed full size.
Can the viewBox be calculated from the values from the paths?
Thank you!
Similar to Martin Spa's answer, but a better way to do get the max viewport area is using the getBBox function:
var clientrect = path.getBBox();
var viewBox = clientrect.x+' '+clientrect.y+' '+clientrect.width+' '+clientrect.height;
You can then set the viewbox to these co-ordinates.
n.b. i think you can change the viewbox of an svg after it's rendered so you may have to re-render the svg.
OK so I solved it the following way:
removed all letters from the paths string and made an array out of it with
var values = pathValue.split('L').join(' ').split('M').join(' ').split('z').join('').split(' ');
found max and min from those values:
var max = Math.max.apply( Math, values );
var min = Math.min.apply( Math, values );
set the viewBox:
viewBox = max min max max
This worked in my case excellent. Hope that it will be helpful to someone else too.

Resources