I have a <div> that contains an inline svg. I would like a function that opens that svg in a new tab/window. I only want to use the front-end js without having to save the svg anywhere.
If I use window.open(), it puts the svg inside an <html> tag which I'm trying to avoid.
I'm basically trying to change this answer but then only have the svg code left:
https://stackoverflow.com/a/21667339/1083923
//---print button---
var printSVG = function()
{
var popUpAndPrint = function()
{
var container = $('#svgDiv');
var width = parseFloat(container.getAttribute("width"))
var height = parseFloat(container.getAttribute("height"))
var printWindow = window.open('', 'PrintMap',
'width=' + width + ',height=' + height);
printWindow.document.writeln($(container).html());
printWindow.document.close();
printWindow.print();
printWindow.close();
};
setTimeout(popUpAndPrint, 500);
};
You can stringify your element into a valid SVG document and store it as a Blob, then point your new Window to that Blob using a blob:// URI:
// we need to handle a user gesture to use 'open'
document.getElementById("btn").onclick = (evt) => {
// grab your svg element
const svg = document.querySelector("svg");
// convert to a valid XML source
const as_text = new XMLSerializer().serializeToString(svg);
// store in a Blob
const blob = new Blob([as_text], { type: "image/svg+xml" });
// create an URI pointing to that blob
const url = URL.createObjectURL(blob);
const win = open(url);
// so the Garbage Collector can collect the blob
win.onload = (evt) => URL.revokeObjectURL(url);
};
JS-fiddle demo since StackSnippet's sand-boxed iframes don't allow opening new windows.
Related
I am building a site that allows users to create customized graphics and then download them directly from the page.
I am doing this via the user customizing an svg via a javascript powered form which is then made into a PNG before downloading.
However, now I want to store a copy of the png that the user created so that I can display it on the site's home page. What is the best way to store a copy of the dynamically created graphic? AJAX? HTTP Request? Both are very foreign topics to me so I am unsure how to go about it from here.
For reference here is the code that builds the PNG from the svg and then triggers the download for the user.
var svg = document.getElementById("bg2");
var canvas = document.querySelector("canvas");
function triggerDownload (imgURI) {
var evt = new MouseEvent('click', {
view: window,
bubbles: false,
cancelable: true
});
if(document.getElementById("name").value=="null" || document.getElementById("name").value=="") {
var un = "MakeAGraphic";
} else {
var enteredName = document.getElementById("name").value.replace(/[^a-zA-Z ]/g, "");
var un = `${enteredName}_MakeAGraphic`
}
var a = document.createElement('a');
a.setAttribute('download', `${un}.png`);
a.setAttribute('href', imgURI);
a.setAttribute('target', '_blank');
a.dispatchEvent(evt);
}
btn.addEventListener('click', function () {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var data = (new XMLSerializer()).serializeToString(svg);
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svgBlob = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
var url = DOMURL.createObjectURL(svgBlob);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
var imgURI = canvas
.toDataURL('image/png')
.replace('image/png', 'image/octet-stream');
triggerDownload(imgURI);
};
img.src = url;
});
agree to that answer mentioned by #showdev .
Call
canvas.toBlob(callback, mimeType, qualityArgument);
then call
formData.append("an-image", blob);
then upload formData using the AJAX or fetch API.
see the documents at
https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
https://developer.mozilla.org/en-US/docs/Web/API/FormData/append
https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
Hi im developing activex control to manage webcam in IE, everything works but i need to refresh the image when a new frame is captures, up to now i display images on click, but i want to listen "Captured frame event" in JS to refresh my html view.
I have seen a lot of samples but all of these are using Object tag in html
<object id="Control" classid="CLSID:id"/>
<script for="Control" event="myEvent()">
alert("hello");
</script>
is there a way to listen activex events using pure JS? using :
Ob = new ActivexObject('PROG.ID');
No, it's not working in purly JavaScript you can only use JScript.
JScript only works in Internet Explorer and there it only works, if the user confirms to execute the scripts, ussualy you use it in an .hta Hyper Text (Markup Language) Application
Example:
<script language="JScript">
"use strict";
function openFile(strFileName) {
var fso = new ActiveXObject("Scripting.FileSystemObject");
var f = fso.OpenTextFile(strFileName, 1);
if (f.AtEndOfStream) {
return;
}
var file = f.ReadAll();
f.close();
return file;
}
function writeFile(filename, content) {
var fso = new ActiveXObject("Scripting.FileSystemObject");
var f = fso.OpenTextFile(filename, 2, true);
f.Write(content);
f.Close();
}
</script>
<script language="javascript">
"use strict";
var native_db;
function init() {
var file = openFile("./Natives.json");
native_db = JSON.parse(file);
displayNamespaces();
};
I also would have as alternate an example how it works in modern Browsers, if it helps you out there:
<video id="webcam"></video>
<input type="button" value="activate webcam" onclick="test()">
<script>
function test() {
navigator.mediaDevices.getUserMedia({video: true, audio: true}).then((stream) => { // get access to webcam
const videoElement = document.getElementById('webcam');
videoElement.srcObject = stream; // set our stream, to the video element
videoElement.play();
// This creates a Screenshot on click, as you mentioned
videoElement.addEventListener('click', function(event) {
const canvas = document.createElement('canvas');
canvas.width = videoElement.videoWidth;
canvas.height = videoElement.videoHeight;
const context = canvas.getContext('2d');
context.drawImage(videoElement, 0, 0, videoElement.videoWidth, videoElement.videoHeight);
// you could just do this, to display the canvas:
// document.body.appendChild(canvas);
canvas.toBlob(function (blob) { // we automatlicly download the screenshot
const a = document.createElement('a');
a.href = URL.createObjectURL(blob); // creates a Datastream Handle Link in your Browser
a.download = 'screenshot.png'; // the name we have to define here now, since it will not use the url name
document.body.appendChild(a); // some Browsers don't like operating with Events of Objects which aren't on DOM
setTimeout(function() { // so we timeout a ms
a.click(); // trigger the download
URL.revokeObjectURL(a.href); // release the Handle
document.body.removeChild(a); // remove the element again
}, 1);
});
});
}).catch(console.error);
}
</script>
I ended up using timer, when i click enable button in the view i call the method startRecord, in this method i read Activex value every 70 ms, I would rather not use timer but it works.
private async startRecord() {
while (this.banCapturing) {
await new Promise(res => setTimeout(res, 70)); //14 ~ 15 fps
this.foto = this.Ob.imageBase64; //Read Activex Value, DLL is updating imageBase64 on camera event.
}
}
I currently have a website using D3 and I'd like the user to have the option to save the SVG as an SVG file. I'm using crowbar.js to do this, but it only works on chrome. Nothing happens of safari and IE gives an access denied on the click() method used in crowbar.js to download the file.
var e = document.createElement('script');
if (window.location.protocol === 'https:') {
e.setAttribute('src', 'https://raw.github.com/NYTimes/svg-crowbar/gh-pages/svg-crowbar.js');
} else {
e.setAttribute('src', 'http://nytimes.github.com/svg-crowbar/svg-crowbar.js');
}
e.setAttribute('class', 'svg-crowbar');
document.body.appendChild(e);
How do I download an SVG file based on the SVG element on my website in safari, IE and chrome?
There are 5 steps. I often use this method to output inline svg.
get inline svg element to output.
get svg source by XMLSerializer.
add name spaces of svg and xlink.
construct url data scheme of svg by encodeURIComponent method.
set this url to href attribute of some "a" element, and right click this link to download svg file.
//get svg element.
var svg = document.getElementById("svg");
//get svg source.
var serializer = new XMLSerializer();
var source = serializer.serializeToString(svg);
//add name spaces.
if(!source.match(/^<svg[^>]+xmlns="http\:\/\/www\.w3\.org\/2000\/svg"/)){
source = source.replace(/^<svg/, '<svg xmlns="http://www.w3.org/2000/svg"');
}
if(!source.match(/^<svg[^>]+"http\:\/\/www\.w3\.org\/1999\/xlink"/)){
source = source.replace(/^<svg/, '<svg xmlns:xlink="http://www.w3.org/1999/xlink"');
}
//add xml declaration
source = '<?xml version="1.0" standalone="no"?>\r\n' + source;
//convert svg source to URI data scheme.
var url = "data:image/svg+xml;charset=utf-8,"+encodeURIComponent(source);
//set url value to a element's href attribute.
document.getElementById("link").href = url;
//you can download svg file by right click menu.
I know this has already been answered, and that answer works well most of the time. However I found that it failed on Chrome (but not Firefox) if the svg image was large-ish (about 1MB). It works if you go back to using a Blob construct, as described here and here. The only difference is the type argument. In my code I wanted a single button press to download the svg for the user, which I accomplished with:
var svgData = $("#figureSvg")[0].outerHTML;
var svgBlob = new Blob([svgData], {type:"image/svg+xml;charset=utf-8"});
var svgUrl = URL.createObjectURL(svgBlob);
var downloadLink = document.createElement("a");
downloadLink.href = svgUrl;
downloadLink.download = "newesttree.svg";
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
October 2019 edit:
Comments have indicated that this code will work even without appending downloadLink to document.body and subsequently removing it after click(). I believe that used to work on Firefox, but as of now it no longer does (Firefox requires that you append and then remove downloadLink). The code works on Chrome either way.
Combining Dave's and defghi1977 answers. Here is a reusable function:
function saveSvg(svgEl, name) {
svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
var svgData = svgEl.outerHTML;
var preface = '<?xml version="1.0" standalone="no"?>\r\n';
var svgBlob = new Blob([preface, svgData], {type:"image/svg+xml;charset=utf-8"});
var svgUrl = URL.createObjectURL(svgBlob);
var downloadLink = document.createElement("a");
downloadLink.href = svgUrl;
downloadLink.download = name;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
Invocation example:
saveSvg(svg, 'test.svg')
For this snippet to work you need FileSaver.js.
function save_as_svg(){
var svg_data = document.getElementById("svg").innerHTML //put id of your svg element here
var head = '<svg title="graph" version="1.1" xmlns="http://www.w3.org/2000/svg">'
//if you have some additional styling like graph edges put them inside <style> tag
var style = '<style>circle {cursor: pointer;stroke-width: 1.5px;}text {font: 10px arial;}path {stroke: DimGrey;stroke-width: 1.5px;}</style>'
var full_svg = head + style + svg_data + "</svg>"
var blob = new Blob([full_svg], {type: "image/svg+xml"});
saveAs(blob, "graph.svg");
};
I tryed every solution here and nothing worked for me. My picture was always smaller than my d3.js canvas.
I had to set the canvas width, height then do a clearRect on the context to make it works. Here is my working version
Export function:
var svgHtml = document.getElementById("d3-canvas"),
svgData = new XMLSerializer().serializeToString(svgHtml),
svgBlob = new Blob([svgData], {type:"image/svg+xml;charset=utf-8"}),
bounding = svgHtml.getBoundingClientRect(),
width = bounding.width * 2,
height = bounding.height * 2,
canvas = document.createElement("canvas"),
context = canvas.getContext("2d"),
exportFileName = 'd3-graph-image.png';
//Set the canvas width and height before loading the new Image
canvas.width = width;
canvas.height = height;
var image = new Image();
image.onload = function() {
//Clear the context
context.clearRect(0, 0, width, height);
context.drawImage(image, 0, 0, width, height);
//Create blob and save if with FileSaver.js
canvas.toBlob(function(blob) {
saveAs(blob, exportFileName);
});
};
var svgUrl = URL.createObjectURL(svgBlob);
image.src = svgUrl;
It use FileSaver.js to save the file.
This is my canvas creation, note that i solve the namespace issue here
d3.js canvas creation:
var canvas = d3.select("body")
.insert("svg")
.attr('id', 'd3-canvas')
//Solve the namespace issue (xmlns and xlink)
.attr("xmlns", "http://www.w3.org/2000/svg")
.attr("xmlns:xlink", "http://www.w3.org/1999/xlink")
.attr("width", width)
.attr("height", height);
While this question has been answered, I created a small library called SaveSVG which can help save D3.js generated SVG which use external stylesheets or external definition files (using <use> and def) tags.
Based on #vasyl-vaskivskyi 's answer.
<script src="../../assets/FileSaver.js"></script>
<script>
function save_as_svg(){
fetch('path/../assets/chart.css')
.then(response => response.text())
.then(text => {
var svg_data = document.getElementById("svg").innerHTML
var head = '<svg title="graph" version="1.1" xmlns="http://www.w3.org/2000/svg">'
var style = "<style>" + text + "</style>"
var full_svg = head + style + svg_data + "</svg>"
var blob = new Blob([full_svg], {type: "image/svg+xml"});
saveAs(blob, "graph.svg");
})
};
save_as_svg();
</script>
The above code read your chart.css and then embed the css code to your svg file.
I try this and worked for me.
function downloadSvg() {
var svg = document.getElementById("svg");
var serializer = new XMLSerializer();
var source = serializer.serializeToString(svg);
source = source.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
source = source.replace(/ns\d+:href/g, 'xlink:href'); // Safari NS namespace fix.
if (!source.match(/^<svg[^>]+xmlns="http\:\/\/www\.w3\.org\/2000\/svg"/)) {
source = source.replace(/^<svg/, '<svg xmlns="http://www.w3.org/2000/svg"');
}
if (!source.match(/^<svg[^>]+"http\:\/\/www\.w3\.org\/1999\/xlink"/)) {
source = source.replace(/^<svg/, '<svg xmlns:xlink="http://www.w3.org/1999/xlink"');
}
var preface = '<?xml version="1.0" standalone="no"?>\r\n';
var svgBlob = new Blob([preface, source], { type: "image/svg+xml;charset=utf-8" });
var svgUrl = URL.createObjectURL(svgBlob);
var downloadLink = document.createElement("a");
downloadLink.href = svgUrl;
downloadLink.download = name;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
In my scenario, I had to use some svg images created using D3.js in other projects. So I opened dev tools and inspected those svg and copied their content. Then created a new blank svg file and pasted the copied content there. Then I used those new svg files in other areas.
And if you want to do it programmatically, then we can use document.getElementById('svgId')
I know this is a basic approach but in case anyone find it useful.
I found and edited below code to save my html5 canvas as a .png-image. The canvas is generated by Three.js/Potree.
This code works perfectly. But, I want a higher resolution image as an output. Usually, the THREEjs renderer is sized according to the clients browser size. I have already manually enlarged it to 3000x2000. This edit will give a higher-resolution image, but when increasing more no effects are visible anymore.
Is there a simple (I'm not that into javascript..) workaround for downloading a high-resolution output image?
At first, the images are just for me. No user needs to download it or needs to pass it onto the server. Although, if its a neat solution, I'd like to give the user the possibility to save their image as well.
function saveAsImage() {
var strDownloadMime = "image/octet-stream";
//alert("function saveimage")
var imgData, imgNode;
try {
var strMime = "image/png";
imgData = viewer.renderer.domElement.toDataURL(strMime);
saveFile(imgData.replace(strMime, strDownloadMime), "test.png");
} catch (e) {
console.log(e);
return;
}
}
var saveFile = function (strData, filename) {
//alert("savefilefunction");
var link = document.createElement('a');
if (typeof link.download === 'string') {
document.body.appendChild(link); //Firefox requires the link to be in the body
link.download = filename;
link.href = strData;
link.click();
document.body.removeChild(link); //remove the link when done
} else {
location.replace(uri);
}
}
THREE Renderer settings:
let width = 3000;
let height= 2000;
this.renderer = new THREE.WebGLRenderer({premultipliedAlpha: false, preserveDrawingBuffer: true});
this.renderer.setSize(width, height);
Basically, what I am doing is generating a PDF file on the server and showing it in the browser via javascript like this:
file = new window.Blob([data], { type: 'application/pdf' });
var fileUrl = URL.createObjectURL(file);
var wnd = window.open(fileUrl, "_blank", "location=no, fullscreen=yes, scrollbars=auto, width=" + screen.width + ",height=" + screen.height);
All this works fine but every browser is showing an ugly subtitle (something like this): blob:2da57927-311e-4b3d-a261-d2679074802c
Is there any way to get rid of this subtitle or to replace it with something meaningful?
Edited:
Here is a screen capture of the improved code (after applying VisioN's suggestion):
As I mentioned in the comments, one possible way is to make an <iframe> in the popup window, that displays the current Blob data, and to style the popup as you wish:
var win = open('', 'name', 'height=300, width=300'),
iframe = document.createElement('iframe'),
title = document.createElement('title'),
file = new Blob([data], { type: 'application/pdf' }),
fileUrl = URL.createObjectURL(file);
title.appendChild(document.createTextNode('Nice title :)'));
iframe.src = fileUrl;
iframe.width = '100%';
iframe.height = '100%';
iframe.style.border = 'none';
win.document.head.appendChild(title);
win.document.body.appendChild(iframe);
win.document.body.style.margin = 0;
DEMO: http://jsfiddle.net/MeY9e/
You can simply add a settimeout to change the page title after the blob has been loaded in the new tab like this -
newTab.location.href = URL.createObjectURL(blob);
setTimeout(function() {
newTab.document.title = blob.name;
}, 10);