Resolve image urls in SVG with node js - node.js

I have a few <image> elements in my SVG file:
<image id="image1" width="36" height="36" href="https://IMAGE_URL" preserveAspectRatio="xMidYMid slice" />
My final goal is to convert my SVG to PNG or JPEG but I can't do that if I have remote urls in my image elements. I tried multiple libraries (like npm sharp) and the images elements in the PNG result were empty.
One solution I thought about was to resolve all the image urls (href) in my SVG so it contains all the images data.
For example:
<image href="data:image/jpeg;base64,ALL_IMAGE_DATA" />
But I couldn't find a good way to do that in node js.
Any ideas of how to do that? Or any other solutions to convert SVG to other image format while resolving all svg image elements?

The following asynchronous function takes an image URL and resolves to its data-URL:
function href2base64(url) {
return new Promise(function(resolve, reject) {
https.get(url).on("response", function(r) {
var buffers = [];
r.on("data", function(data) {
buffers.push(data);
}).on("end", function() {
resolve(`data:${r.headers["content-type"]};base64,${Buffer.concat(buffers).toString("base64")}`);
});
});
});
}

svg-to-png
Try using this module

Related

How to draw a polygon and save it to image using Node.js

I'm trying to create an image, draw polygon shapes on it and save it to a file in Node.js, but I'm having a hard time finding a library that can achieve that.
In addition, I would prefer a library that doesn't require the installations of third party programs.
Example of an image I'm trying to create:
Without having to use any library you can just write the SVG image.
const fs = require('fs')
const result = `
<svg
width="541"
height="271"
>
<rect
width="541"
height="271"
x="0"
y="0"
fill="#3d5ea1"
/>
<polygon
fill="white"
points="100,50 400,100 320,200 80,230"
/>
</svg>
`
fs.writeFile("./file.svg", result, (err) => {
if (err) {return console.err(err)}
console.log("The file was saved!")
});
This code will create a similar image like the one on your example In case you need a png or jpeg you can use any external library to transform the svg.

Why is svg generated by MathJax.js different than the svg generated by MathJax-node

I am writing an web app that saves html to onenote. In order to save math formulas, I plan to convert math formulas to svg by MathJax.js and then convert svg to png, because the html/css supported in onenote api is limited.
But it seems the svg generated by MathJax.js in browser is not a valid svg. I tested it with a simple math formula: $$a^2 + b^2 = c^2$$ (demo code) and copy the svg to jsfiddle and it displays nothing.
Then I tried to write a MathJax-node demo and copy the svg to jsfiddle again, it looks good. Here is my demo code, it's almost the same as the GitHub repo demo:
// a simple TeX-input example
const fs = require('fs')
var mjAPI = require("mathjax-node");
mjAPI.config({
MathJax: {
// traditional MathJax configuration
}
});
mjAPI.start();
var yourMath = String.raw`a^2 + b^2 = c^2`
mjAPI.typeset({
math: yourMath,
format: "TeX", // or "inline-TeX", "MathML"
svg: true, // or svg:true, or html:true
}, function (data) {
if (!data.errors) {console.log(data.svg)}
// will produce:
// <math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
// <mi>E</mi>
// <mo>=</mo>
// <mi>m</mi>
// <msup>
// <mi>c</mi>
// <mn>2</mn>
// </msup>
// </math>
fs.writeFile('math.txt', data.svg, (error) => {
console.log(error)
})
});
I also tested two svg with cloudconvert, it's the same result. Why are the two svg different? Do I miss something?
The difference is due to a specific setting: useGlobalCache
By default, MathJax (docs) sets this to true while mathjax-node (docs) sets this to false.
On the server MathJax-node does not have any document context and produces self-contained SVGs. On the client, MathJax has a full document context and thus can re-use the SVG paths across equations.

How can I use an SVG image as a map marker in OpenLayers-3?

I am trying to create map "pin-drops" (ie. map markers) in OpenLayers-3 (OL3) using SVG images.
Currently, I am using PNG images as the pindrops that reference the ol.style.Icon source (“src”) property attribute just fine. However, this fails using an SVG image. Is there some other way to use an SVG in the same manner? Maybe by using a reference besides ol.style.Icon even? There is already a lot of built-in SVG in Open Layers so this should be possible, but I haven't found a way to get this working in OL3. Is there some other way to do this in OL3 that I should consider?
Please note: we already tried using an ol.Vector layer, however when the user zooms in/out, the size of the SVG image grows/shrinks which is an inadequate workaround.
OL3 (fails):
var createMapMarkerImage = function() {
return function(feature, resolution) {
var iconStyle = new ol.style.Style({
image: new ol.style.Icon( ({
src: 'img/map_pindrop.svg' // OL3 doesn’t like this, but accepts a .PNG just fine
}))
});
return [iconStyle];
};
};
Very similar functionality, is the below example I found online, is almost perfect if it weren’t for the fact that the example uses OpenLayers-2 (OL2) functionality which calls openlayers.js library (instead of OL3’s ol.js library). Sadly, swapping these javascript files out fails.
OL2 (works -but is the old OL library):
http://dev.openlayers.org/sandbox/camptocamp/tipi/examples/vector-symbols.html
Searching online for a solution to this seems to produce only other confused people searching for a solution.
Please help,
FreeBeer
Based on #ahocevar answer, you can use data URIs for SVG:
new ol.style.Style({
image: new ol.style.Icon({
anchor: [0, 0],
src: 'data:image/svg+xml;utf8,<svg>/* SVG DATA */</svg>'
})
});
Convert the SVG to Base 64 . This (Link) helped me.
copied the base 64 and used it as a string in javascript .
Eg : var svg = "convertedBase64";
Then
var icon = new ol.style.Icon({
src:'data:image/svg+xml;base64,'+svg ,
other props
});
And you are done, may be a few Kbs more than SVG but this did work perfect for me .
SVG icons work fine as long as the content-type of your SVG image file is image/svg+xml. Also note that no external references are supported inside the SVG. OpenLayers 3 simply uses the drawImage function of the 2d context. You can find more details on the requirements of SVG content here: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_DOM_objects_into_a_canvas.
I had the same issue, but not even serving the image with the proper mime type helped.
It boiled down to the SVG not defining width and height properly.
I added the width and height attributes to the <svg> tag, like:
<svg width="100px" height="100px" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0.75 0 30 45" xml:space="preserve">
After that I was able to use my svg just like any other image.
I also had issues to show the icon image, ahocevar answer helped me to
solve my problem but I had also to search for the php header, for SVG
In case you are or others who see this answer are using php to generate the SVG you have to use header function to identify the content-type
header('Content-type: image/svg+xml'); /* this line will do the magic */
echo '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>
</svg>';
Building upon SoonDead's answer, I had to come up with a way to add the width and height to the svg without touching the source. Using angular, this is sort of what I did:
$http.get('path/to/image.svg').then(function (response) {
// create element
var svgEl = angular.element(response.data);
// set width and height
svgEl.attr('width', '50px');
svgEl.attr('height', '50px');
// base64 encode
var base64Svg = btoa(unescape(encodeURIComponent(svgEl[0].outerHTML)));
// create the style
var style = new ol.style.Style({
image: new ol.style.Icon({
src: 'data:image/svg+xml;base64,'+base64Svg,
imgSize: [50, 50],
size: [50, 50],
})
});
// apply the style
feature.setStyle(style);
});
It's a little verbose, but it seems to do the job.

How do I fill/stroke an SVG file on my website?

I Googled this issue for about 30 minutes and was surprised nobody has asked so maybe it's not possible.
I'm using this line to embed the SVG file that I made in AI (note that when I saved the SVG, I had no fill OR stroke on the paths):
<object type="image/svg+xml" data="example.svg" height=600px width=600px>Your browser does not support SVG</object>
This obviously comes up with no image because the SVG file has no fill or stroke.
I tried adding in
...fill=yellow>
or
...style="fill:yellow;">
but I'm not having any luck. Thanks!
Have a nice trick: Embed it as <img> and use javascript to convert it into inline <svg> with this code (that came from SO I think). Now you can manipulate this SVG object
CODE::
/*
* Replace all SVG images with inline SVG
*/
jQuery('img.svg').each(function(){
var $img = jQuery(this);
var imgID = $img.attr('id');
var imgClass = $img.attr('class');
var imgURL = $img.attr('src');
jQuery.get(imgURL, function(data) {
// Get the SVG tag, ignore the rest
var $svg = jQuery(data).find('svg');
// Add replaced image's ID to the new SVG
if(typeof imgID !== 'undefined') {
$svg = $svg.attr('id', imgID);
}
// Add replaced image's classes to the new SVG
if(typeof imgClass !== 'undefined') {
$svg = $svg.attr('class', imgClass+' replaced-svg');
}
// Remove any invalid XML tags as per http://validator.w3.org
$svg = $svg.removeAttr('xmlns:a');
// Replace image with new SVG
$img.replaceWith($svg);
});
});
Are you trying to add the fill or style on the object element? If so, that's not going to work. Those properties are not supported on the object element. You're going to have to add it into the SVG in the SVG file.

How do I convert SVG with image links to PNG/JPEG?

I'm using fabric.js to add and manipulate images on canvas element. I'd like to export the final result to an image file (JPEG/PNG), and have tried several approaches, without much luck.
The first was using the HTML5 canvas.toDataURL callback - didn't work (apparently this cannot be done with cross-origin images).
I then tried to export the canvas to SVG and use convert utility:
$ convert file.svg file.png
The SVG looks a bit like this:
<svg>
...
<image xlink:href="http://example.com/images/image.png"
style="stroke: none; stroke-width: 1; stroke-dasharray: ; fill: rgb(0,0,0); opacity: 1;"
transform="translate(-160 -247.5)" width="320" height="495">
</image>
...
</svg>
That also didn't work, presumably because of the image links. I then tried to download the linked image and store it locally, replacing the http://...image.png link with /path/to/image.png. No luck.
Is there a way to make this happen? thanks.
UPDATE
I've since solved this by using fabric's node module (npm install fabric) and simply using fabric's JSON representation of the canvas (canvas.toJSON() in fabric) to generate an image (also uses canvas module - npm install canvas).
Here's a simple example on how to do this:
// import the fabric module
var fabric = require('fabric').fabric,
fs = require('fs');
// read the fabric json data as string
// (from file, db, etc)
var jsonString = readJSON(filepath);
// create canvas with specified dimensions
// (change to match your needs)
canvas = fabric.createCanvasForNode(100, 100);
// create output file to hold the png image
output = fs.createWriteStream("/tmp/image.png")
// load the json to the canvas and
// pipe the output to the png file
canvas.loadFromJSON(jsonString, function() {
var stream = canvas.createPNGStream();
stream.pipe(output)
});

Resources