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

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)
});

Related

Resolve image urls in SVG with 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

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.

Render Svg to Pdf using jspdf

I am having trouble in rendering svg element to pdf using jspdf . Iam using plugin https://github.com/CBiX/svgToPdf.js/ to do this.
Below is my code
// I recommend to keep the svg visible as a preview
var tmp = document.getElementById("chartContainer");
var svgDoc = tmp.getElementsByTagName("svg")[0];
var pdf = new jsPDF('p', 'pt', 'a4');
svgElementToPdf(svgDoc, pdf, {
scale: 72 / 96, // this is the ratio of px to pt units
removeInvalid: false // this removes elements that could not be translated to pdf from the source svg
});
pdf.output('datauri'); // use output() to get the jsPDF buffer
It is generarting blank pdf. Please help
You can do that using canvg.
Step1: Get "SVG" markup code from DOM
var svg = document.getElementById('svg-container').innerHTML;
if (svg)
svg = svg.replace(/\r?\n|\r/g, '').trim();
Step 2:
Use canvg to create canvas from svg.
var canvas = document.createElement('canvas');
canvg(canvas, svg);
Step 3:
Create image from canvas using .toDataURL()
var imgData = canvas.toDataURL('image/png');
// Generate PDF
var doc = new jsPDF('p', 'pt', 'a4');
doc.addImage(imgData, 'PNG', 40, 40, 75, 75);
doc.save('test.pdf');
Check the demo here http://jsfiddle.net/Purushoth/hvs91vpq/193/
Canvg Repo: https://github.com/gabelerner/canvg
I've tried both svg2pdf.js and addSvgAsImage using canvg internally.
Both didn't work well for me, the resulting images in the pdf where either incorrectly positioned or displayed.
I've ended up doing the following which works very well:
convert the SVG to PNG without any libraries, see my answer to "Convert SVG to image (JPEG, PNG, etc.) in the browser".
just add the result to the pdf using the normal addImage method
I think the current jspdf version (2.3.1) has an addSvgAsImage method, but it takes the svg as a string. I guess you could use an ajax call to retrieve the SVG content, but I just have the SVG in my code and pass it in that way.

yui How to make in tag image does not necessarily will been to specify its size?

Good day.
I use script Imagecropper
Script:
<img src="http://test.com/img/1362244329.jpg" id="yui_img" height="768" width="1024">
<script>
(function() {
var Dom = YAHOO.util.Dom,
Event = YAHOO.util.Event;
var crop = new YAHOO.widget.ImageCropper('yui_img');
})();
</script>
result:
But if i do not specify the image size, then i get next(see image):
<img src="http://test.com/img/1362244329.jpg" id="yui_img">
result:
And if i specify the wrong picture size, the window will increase the portion of the image:
<img src="http://test.com/img/1362244329.jpg" id="yui_img" height="333" width="500">
result:
How to make in tag image does not necessarily will been to specify its size?
First of all I'd like to point you to YUI 3 since YUI 2 is no longer supported. You shouldn't write new code using YUI 2. There's an ImageCropper component I wrote for YUI 3 that works just like the YUI 2 version in the YUI Gallery: http://yuilibrary.com/gallery/show/imagecropper. Since it copies what the YUI 2 ImageCropper did, it shares these issues with the older version.
What to do when the size of the image isn't specified
The reason why you're getting a small ImageCropper is that you're creating the widget before the image has been fetched and so the browser doesn't know its size yet. What you can do is wait for the image's onload event. You can listen to that event and create the ImageCropper after it fires:
(function() {
var Dom = YAHOO.util.Dom,
Event = YAHOO.util.Event;
var yui_img = Dom.get('yui_img');
Event.addListener(yui_img, 'load', function () {
var crop = new YAHOO.widget.ImageCropper(yui_img);
});
})();
Why the ImageCropper doesn't work with images with the wrong size
Neither the YUI 2 ImageCropper nor my YUI 3 version work with images when they don't have the right size. The reason is that both use the background: url() CSS style for showing the image inside the crop area (the non-darkened part of the widget). CSS backgrounds don't let you use a resized/zoomed image.
I plan on using another strategy at some point for the YUI 3 version that will fix the issue. However, you need to keep in mind that the ImageCropper component is designed so that you send the crop coordinates to the server for it to actually crop the image. That means that if you have the wrong size set to the image, the coordinates that the image cropper returns with its getCropCoords method wouldn't be the coordinates that match with the full sized image. Instead you'd also have to send the server the size of the image you've been using and do extra math to crop the image correctly.
In conclusion, you shouldn't use the image with the wrong size. You can fix the size of the image in two ways:
Use the HTML5 naturalWidth and naturalHeight attributes of the image. Those return the real size of the image even if it's resized. Unfortunately these attributes are not yet supported by all browsers.
Create a new image with JS, set it the same src as the image you're using, listen to its load event and get that image's size.
Something like this:
(function () {
var Dom = YAHOO.util.Dom;
var yui_img = Dom.get('yui_img'),
new_img = new Image();
new_img.onload = function () {
yui_img.width = new_img.width;
yui_img.height = new_img.height;
// create the ImageCropper
};
new_img.src = yui_img.src;
}());
A YUI3 version
You can easily do all this with YUI3:
YUI().use('gallery-imagecropper', function (Y) {
var img = Y.one('#yui_img');
img.on('load', function () {
var cropper = new Y.ImageCroper({
srcNode: img,
width: img.get('width'),
height: img.get('height')
});
cropper.render();
});
});
Typo in code. Should be
var cropper = new Y.ImageCropper({
You missed a letter "p".

How to import an image/raster in svg-edit using the embedded API

I determined using the core api that the method "svgCanvas.importImage(url) can import base64 encoded images.
But the embedded API does not expose this method which is private. It also seems i can not use any of the methods that method uses (svgFromgJson, etc).
Does anyone have advice on how I can load a base 64 string representing an image when the embedded svg-edit starts up? I am thinking to just wrap that string inside an < image> tag in a mock svg file and import that way..
Does SVG support embedding of bitmap images?
Modifying the existing code, based on the answer in my original question I modified their sample import, and it works just wonderfully.
Include the embedapi.js in your page
Define an Iframe to show svg-edit
Call the following code function in the iframe's onload event
// call onLoad for the iframe
var frame = document.getElementById('svgedit');
svgCanvas = new embedded_svg_edit(frame);
// Hide main button, as we will be controlling new/load/save etc from the host document
var doc;
doc = frame.contentDocument;
if (!doc)
{
doc = frame.contentWindow.document;
}
var mainButton = doc.getElementById('main_button');
mainButton.style.display = 'none';
var embeddedImage='<image xlink:href="data:image/png;base64,..OMITTED FOR BREVITY.." id="svg_2" height="128" width="128" y="0" x="0"/>';
var svgDef = '<svg width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><g><title>Layer 1</title>' + embeddedImage + '</g></svg>';
svgCanvas.setSvgString(svgDef);
}

Resources