When I attempt to load a Google Chart in node.js, nothing happens.
I tried loading the first example from the line chart docs in both zombie.js and jsdom, but the chart never loads in either case.
The end goal is to retrieve the SVG data of the generated chart for export into an image or PDF. So if an alternate method (server side using node.js or PHP) to achieve this is possible, I'm open to suggestions.
NOTE: I have successfully generated a images of a few charts using gChartPhp, but the requirements of this project state that the embedded version be the interactive version provided by the current API and the exported version be visually IDENTICAL to the embedded one (without being interactive, obviously).
Edit: I tagged PhantomJS, since that is the solution with which I ultimately went.
Sorry for the lack of links, but the spam prevention mechanism will only allow me to post 2.
I'm 8 years late but I've just released an open-source project Google Charts Node that renders chart images in Puppeteer (somewhat of a successor to the original PhantomJS solution).
google-charts-node is available as an NPM library and can be used like so:
const GoogleChartsNode = require('google-charts-node');
function drawChart() {
// Set up your chart here, just like in the browser
// ...
const chart = new google.visualization.BarChart(container);
chart.draw(data, options);
}
// Render the chart to image
const image = await GoogleChartsNode.render(drawChart, {
width: 400,
height: 300,
});
Now you can save this image buffer as a file or return it as an HTTP response, etc.
It was pretty straightforward to create this. The main caveats were:
Not all charts support getImageURI, so I fall back to puppeteer to take a screenshot when this happens.
It's slow! But if you must use Google Charts as a requirement, you don't really have an alternative. This problem can be mitigated with enough cloud compute resources.
You can view the full source at the Github project, but here's the raw puppeteer flow if you want to do it yourself:
async function render() {
// Puppeteer setup
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Add the chart
await page.setContent(`...Insert your Google Charts code here...`);
// Use getImageURI if available (not all charts support)
const imageBase64 = await page.evaluate(() => {
if (!window.chart || typeof window.chart.getImageURI === 'undefined') {
return null;
}
return window.chart.getImageURI();
});
let buf;
if (imageBase64) {
buf = Buffer.from(imageBase64.slice('data:image/png;base64,'.length), 'base64');
} else {
// getImageURI was not available - take a screenshot using puppeteer
const elt = await page.$('#chart_div');
buf = await elt.screenshot();
}
await browser.close();
return buf;
}
It wasn't the ideal solution, but I found an alternative to node.js for accomplishing the same end goal in PhantomJS. Simply create an HTML file containing the chart (test.html) and like node.js, create a JS file containing your code (test.js). Then run your JS file with PhantomJS.
In your JS file, open your HTML file as a webpage, then render it, either saving the image buffer to a file:
var page = require('webpage').create();
page.open('test.html', function () {
page.render('test.png');
phantom.exit();
});
Then run it:
phantomjs test.js
To dynamically create a chart, create the following JS file (test2.js):
var system = require('system');
var page = require('webpage').create();
page.onCallback = function(data)
{
page.clipRect = data.clipRect;
page.render('test.png');
phantom.exit();
};
page.includeJs('http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js', function()
{
page.includeJs('https://www.google.com/jsapi', function()
{
page.evaluate(function(chartType, data_json, options_json)
{
var div = $('<div />').attr('id', 'chart').width(900).height(500).appendTo($('body'));
google.load("visualization", "1",
{
packages:[chartType == 'GeoChart' ? 'geochart' : 'corechart'],
callback: function()
{
data_arr = $.parseJSON(data_json);
data = google.visualization.arrayToDataTable(data_arr);
options = $.parseJSON(options_json);
chart = new google.visualization[chartType]($(div).get(0));
google.visualization.events.addListener(chart, 'ready', function()
{
window.callPhantom(
{
clipRect: $(div).get(0).getBoundingClientRect()
});
});
chart.draw(data, options);
}
});
}, system.args[1], system.args[2], system.args[3]);
});
});
Then run it:
phantomjs test2.js LineChart '[["Date","Steve","David","Other"],["Dec 31",8,5,3],["Jan 1",7,10,4],["Jan 2",9,4,3],["Jan 3",7,5,3]]' '{"hAxis.slantedText":true}'
phantomjs test2.js PieChart '[["Employee","Calls"],["Steve",31],["David",24],["Other",13]]' '{"is3D":true}'
phantomjs test2.js GeoChart '[["State","Calls"],["US-CA",7],["US-TX",5],["US-FL",4],["US-NY",8]]' '{"region":"US","resolution":"provinces"}'
To get the image data from an external script, make a copy of test2.js (test3.js) and change
page.render('test.png');
to
console.log(page.renderBase64('png'));
Then call it (from PHP, for example):
<?php
$data = array(
array("Employee", "Calls"),
array("Steve", 31),
array("David", 24),
array("Other", 13)
);
$options = array(
"is3D" => true
);
$command = "phantomjs test3.js PieChart '" . json_encode($data) . "' '" . json_encode($options) . "'";
unset($output);
$result = exec($command, $output);
$base64_image = implode("\n", $output);
$image = base64_decode($base64_image);
?>
NOTE: Looking back on this whole process, the problem I was having with node.js was possibly that I didn't setup callbacks or timeouts to wait until the charts were "ready".
Related
I want to PLay google drive videos in JW player in my website .I know it is possible because a lot of websites doing this e.g fmovies
Is there any API or piece of code through which i can achieve this URL for google drive video
https://redirector.googlevideo.com/videoplayback?id=4d0be5bc491e2dd7&itag=18&source=webdrive&requiressl=yes&ttl=transient&pl=21&mime=video/mp4&ipbits=0&sparams=api,expire,id,ip,ipbits,itag,mime,mm,mn,ms,mv,pl,requiressl,source,ttl&api=FsxSHTiLgbCln6EzpdilVgW6JQxNqam8I5YPHEFLs3E=&cms_redirect=yes&expire=1499015322&ip=37.187.148.42&mm=30&mn=sn-25ge7ney&ms=nxu&mt=1499000741&mv=m&signature=658276E5F00AAFD155876EC75453507DE10DDEDB.7FAD65986F21E14B5FAFB6E145252310F86A10B6&key=cms1&app=storage
I already tried juicy API but its not working for me
If you just want to stream a video from Google drive then this can be done - you can see a working example here:
https://sourcey.com/html5-video-streaming-from-google-drive/
You can see exactly how it is coded by right clicking and inspecting in your browser
<video controls="controls" style="margin-bottom:20px;width:590px">
<source src="https://drive.google.com/uc?export=download&id=0B0JMGMGgxp9WMEdWb1hyQUhlOWs" type="video/mp4">
</video>
To create a signed URL for Google cloud storage programatically you need to follow their instructions at the link below - I have not copied or summarised them here as, if the similar AWS mechanism is anything to go by, the approach can change often and it is best to always check the latest official documentation:
https://cloud.google.com/storage/docs/access-control/create-signed-urls-program
You can try this Perl script to get the direct download link. If you want to play it in JWPlayer, you'll have to run the script from the server to get the link, then insert that link into the HTML.
https://circulosmeos.wordpress.com/2014/04/12/google-drive-direct-download-of-big-files/
Files hosted at Google Drive service can be shared publicly. Nevertheless, if they’re too big, Google pages advice that “Google Drive can’t scan this file for viruses” and ask the user for confirmation in order to proceed with the download.
If for any reason you’re trying to do an unattended download of the file or downloading it on a resource-limited environment, this will prevent direct use of the link.
Here is the script
https://github.com/circulosmeos/gdown.pl
However, the script downloads the file to the filesystem, so you'll have to modify it so on the last URL re-direct, you save the final URL instead of downloading it.
if you have WordPress Webiste you can use GD Player WordPress Plugin works with Google Drive Mp4 etc.. it can also set subtitles in videos. I use it, and it for me works very well. I hope this helps.
I was trying to do this, I'm able to get the URL, but then there's the problem of streaming it from the server back to the client, basically use puppeteer to go to the URL, click the play button, wait for the video eleemnt to appear, get the "src" attribute, then (somehow over XMLHttpRequest) stream it back to the server, then have the server (nodeJS) stream it back to the client, then use that URL to embed the video (not complete yet see my [very humble] question on it for more):
var puppeteer = require("puppeteer-core");
var http=require("https");
var fs=require("fs");
var fetch=require("fetch-node");
(async() => {
var browser = await puppeteer.launch({
executablePath:"./cobchrome/chrome.exe"
});
console.log("Got browser", browser);
var page = await browser.newPage();
console.log(page,"got page");
await page.goto("https://docs.google.com/file/d/0B9aMNh1shw_4VUVVWkF0TjRHWTA/preview?enablejsapi=1&playerapiid=player4");
console.log("went to page..");
var newClickID = ".ndfHFb-c4YZDc";
var clicker = await page.waitForSelector(newClickID);
await page.click(newClickID);
var frame = await page.waitForSelector("iframe[id=drive-viewer-video-player-object-0]");
var cf = await frame.contentFrame();
await cf.waitFor(() => !!document.querySelector("video"))
var video = await cf.$("video");
var videoEl = await cf.evaluate(
v =>{
var result = {};
for(var k in v) {
result[k] = v[k];
}
return result;
},
video
);
var src = videoEl.src;
await page.goto(src);
console.log("went to video page ", src);
var file = fs.createWriteStream("output/therebbe.mp4");
var count = 0;
page.on("console", c => {
var txt = (c.text()),
buff = Buffer.from(txt,"base64");
// var pic = fs.createWriteStream("output/frame"+count+".png");
// pic.write(buff);
// pic.end();
console.log("Consoling ",count++, buff);
// file.write(buff);
});
await page.evaluate(() => {
function _arrayBufferToBase64( buffer ) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}
function str2ab(str) {
var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
var bufView = new Uint16Array(buf);
for (var i=0, strLen=str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
var x = new XMLHttpRequest();
var url = location.href;
x.onreadystatechange = function(){
if(x.readyState == 200) {
console.log("done");
} else {
console.log(
_arrayBufferToBase64(
str2ab(x.response)
)
);
}
}
// x.responseType="arraybuffer";
x.open("GET",url,true);
x.send("");
});
// await browser.close();
})();
Just an idea: You don't need JW player or PHP, I's all baked into browsers (HTML5) these days. Here's an example of how to do it (with Google's Material Design UI). Just plain old HTML/CSS/JS.
https://github.com/rhroyston/mdl-audio
In my Laravel 5 project, I am using Lavacharts (powered by Google Charts) for displaying charts and a dompdf wrapper, laravel-dompdf, for generating PDF files.
Is it possible to export such charts using dompdf, and if so, how can this be achieved?
Presumably I would have to first save the chart as an image, but this is not really an option since saving the image is done via Javascript and for generating the PDF all the work is done in the backend (PHP).
Dompdf is unable to process JavaScript-based content on a page. You have two options:
Preview the charts in a browser and send an image version to the server for rendering.
Use a headless browser (like PhantomJS) to render your HTML (with JavaScript) to PDF
I don't have much to say on the second option, but on the first...
The Google Charts API can generate a PNG version of your chart. There are plenty of posts on SO on how to get the PNG to your PHP code (search it).
Something like the following might work:
<script type="text/javascript">
google.charts.load("current", {packages:['corechart']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
['Element', 'Density', { role: 'style' }],
['Copper', 8.94, '#b87333', ],
['Silver', 10.49, 'silver'],
['Gold', 19.30, 'gold'],
['Platinum', 21.45, 'color: #e5e4e2' ]
]);
var options = {
title: "Density of Precious Metals, in g/cm^3",
bar: {groupWidth: '95%'},
legend: 'none',
};
var chart_div = document.getElementById('chart_div');
var chart_input = document.getElementById('chart_input');
var chart = new google.visualization.ColumnChart(chart_div);
// Wait for the chart to finish drawing before calling the getImageURI() method.
google.visualization.events.addListener(chart, 'ready', function () {
chart_div.innerHTML = '<img src="' + chart.getImageURI() + '">';
chart_input.value = chart.getImageURI();
});
chart.draw(data, options);
}
</script>
<div id='chart_div'></div>
<form method="post" action="print_chart.php">
<input type="hidden" name="chartImg" id="chartImg">
<button type="submit">print</button>
</form>
Google Charts renders the PNG using a data-uri, which dompdf supports. Once you have the PNG on the server side you can just insert it into an img tag:
$html = '<img src="' . $_POST['chart_input'] . '">';
$dompdf->load_html($html);
$dompdf->render();
$dompdf->stream('chart.pdf');
I want to build a web video chat application using ONLY javascript,HTML5,css.
Currently i tried to application that works in browser but little slow.
My basic logic is using java script writing a file to external file
From there data initializing to image so that flow of image look like video
writing to php file code
<script>
var onFailSoHard = function (e) {
console.log('Reeeejected!', e);
};
window.URL = window.URL || window.webkitURL;
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia || navigator.msGetUserMedia;
var video = document.querySelector('video');
if (navigator.getUserMedia) {
navigator.getUserMedia({
audio: true,
video: true
}, function (stream) {
video.src = window.URL.createObjectURL(stream);
}, onFailSoHard);
} else {
video.src = 'somevideo.webm'; // fallback.
}
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
setInterval(function () {
ctx.drawImage(video, 0, 0, video.width, video.height, 0, 0, canvas.width, canvas.height);
var imgData = canvas.toDataURL();
$.post('upload.php', {
data: (encodeURIComponent(imgData))
});
}, 0.01);
</script>
php code to write to external file
<?php
$data=rawurldecode ($_POST['data']);
file_put_contents('data.txt',$data);
?>
Reading from external file
<?php
header('Content-type:image/png');
readfile('data.txt');
?>
showing image image so that look like video
<script>
setInterval(function () {
$.get('streamData.php', function (data) {
$('#live').attr('src', data);
});
}, 1000 / 80);
</script>
This code is Working but problem this code is "It is too slow" and this is not working becaming more slow in Mobile devices please help me to work this properly??
Or suggest any other way of building slimier type of browser based app that should work in all devices (Devices that not use WEBRTC).
Instead of post/get use Websockets. It's possible with PHP (install eg. http://socketo.me/) but definitely not the way I'd go (I'd just use Node.js).
You should use websockets because GET/POST is heavy for the server and slower, like Miszy said you can use Node.js with socket.io. You can also create the server using Python or Go.
Firstly I appreciate my request is quite "ambitious", but any help is greatly appreciated as I'm not sure the best way to proceed.
On my site (built with PHP/MySQL) after a user has uploaded a PDF I would like to display the PDF inline on the page (I'm assuming in an iFrame). I then need them to be able to drag out a number of "boxes" on top of the PDF (I'm assuming with jQuery). I then need to record the co-ordinates of this box so then later I can re-create the PDF injecting new text into the defined "boxes".
Does this sound feasible? If not what else would you suggest? (please don't say imagemagick!)
I know how to recreate a PDF injecting new text, but my issue is with how to allow the user to record those coordinates.
You could use PDF.js to render the PDF on the page. PDF.js will display it as part of the page so you can attach events and interact with it in ways you could not if it was being displayed by the Acrobat plugin.
I couldn't find a preexisting library for getting the coordinates so I whipped up this code to implement it.
Live demo of selection code
$(function () {
"use strict";
var startX,
startY,
selectedBoxes = [],
$selectionMarquee = $('#selectionMarquee'),
positionBox = function ($box, coordinates) {
$box.css(
'top', coordinates.top
).css(
'left', coordinates.left
).css(
'height', coordinates.bottom - coordinates.top
).css(
'width', coordinates.right - coordinates.left
);
},
compareNumbers = function (a, b) {
return a - b;
},
getBoxCoordinates = function (startX, startY, endX, endY) {
var x = [startX, endX].sort(compareNumbers),
y = [startY, endY].sort(compareNumbers);
return {
top: y[0],
left: x[0],
right: x[1],
bottom: y[1]
};
},
trackMouse = function (event) {
var position = getBoxCoordinates(startX, startY,
event.pageX, event.pageY);
positionBox($selectionMarquee, position);
};
$(document).on('mousedown', function (event) {
startX = event.pageX;
startY = event.pageY;
positionBox($selectionMarquee,
getBoxCoordinates(startX, startY, startX, startY));
$selectionMarquee.show();
$(this).on('mousemove', trackMouse);
}).on('mouseup', function (event) {
var position,
$selectedBox;
$selectionMarquee.hide();
position = getBoxCoordinates(startX, startY,
event.pageX, event.pageY);
if (position.left !== position.right &&
position.top !== position.bottom) {
$selectedBox = $('<div class="selected-box"></div>');
$selectedBox.hide();
$('body').append($selectedBox);
positionBox($selectedBox, position);
$selectedBox.show();
selectedBoxes.push(position);
$(this).off('mousemove', trackMouse);
}
});
});
You will have to tweak it to get coordinates that are relative to the PDF once you display it, but this should get you on the right track.
So after hours of websearching, googling and overflowing i can't find the solution to my problem.
I got a linechart from Google charts. I want to convert it to PNG, save it on the server en insert it into a MySQL database.
Sounds simple, but i cant get it to work. The script from this website isnt working anymore (atleast not here) http://www.battlehorse.net/page/topics/charts/save_google_charts_as_image.html -> Not working.
Second option is the old option:
$imageData = file_get_contents('http://chart.apis.google.com/chart... etc');
I cant use that because its not supported anymore and cant get some decent quality out of it.
Is there anybody here that can give a good tutorial or help for my problem?
EDIT:
I used the code from Battlehorse combined with the code from EriC.
So now i got this working to show the chart as an image in a DIV i want to save this image on the server and update the mysql to use it in the future to use it in PDF files.
When you visit the site, paste this in the console (overwriting the malfunctioning function).
function getImgData(chartContainer) {
var chartArea = chartContainer.getElementsByTagName('svg')[0].parentNode;
var svg = chartArea.innerHTML;
var doc = chartContainer.ownerDocument;
var canvas = doc.createElement('canvas');
canvas.setAttribute('width', chartArea.offsetWidth);
canvas.setAttribute('height', chartArea.offsetHeight);
canvas.setAttribute(
'style',
'position: absolute; ' +
'top: ' + (-chartArea.offsetHeight * 2) + 'px;' +
'left: ' + (-chartArea.offsetWidth * 2) + 'px;');
doc.body.appendChild(canvas);
canvg(canvas, svg);
var imgData = canvas.toDataURL("image/png");
canvas.parentNode.removeChild(canvas);
return imgData;
}
In JS it was searching for an iframe bla bla to get the svg.
To automatically save the image, you can just let the method being invoked programmatically.
document.body.addEventListener("load", function() {
saveAsImg( document.getElementById("pie_div")); // or your ID
}, false );
For saving images serverside, this post could be helpful save a PNG image server-side
Update
Posting images to PHP (index.js)
function saveToPHP( imgdata ) {
var script = document.createElement("SCRIPT");
script.setAttribute( 'type', 'text/javascript' );
script.setAttribute( 'src', 'save.php?data=' + imgdata );
document.head.appendChild( script );
}
function save() {
var canvas = document.getElementById("canvas"), // Get your canvas
imgdata = canvas.toDataURL();
saveToPHP( imgdata );
}
function drawOnCanvas() {
var canvas = document.getElementById("canvas"), // Get your canvas
ctx = canvas.getContext("2d");
ctx.strokeStyle = "#000000";
ctx.fillStyle = "#FFFF00";
ctx.beginPath();
ctx.arc(100,99,50,0,Math.PI*2,true);
ctx.closePath();
ctx.stroke();
ctx.fill();
}
drawOnCanvas(); // Test
save();
save.php
<?php
// Get the request
$data = $_GET['data'];
// Save to your DB.
?>
You can use the grChartImg library. It's a cross browser solution and supports even old versions of IE (8 and earlier).It has many features such as download image,upload to the server, show the image in a dialog etc.
For more info look at http://www.chartstoimage.eu.
i hope help you.
This is not really an answer but might be one in the future and it is nescesary if you just want the feature back. The following URL shows all the current issues and feature requests for the visualization API.
https://code.google.com/p/google-visualization-api-issues/issues/list?can=2&q=&sort=-stars+id&colspec=ID%20Type%20Status%20Priority%20Milestone%20Owner%20Summary%20Stars
The more stars/votes this feature request gets, the higher the chance they will take a look at it.
I have the same issue - Save Google charts as image on server. None of answers here works for me. Finally I get solution but with some bugs(working only in Chrome browser). As base I used script from here https://gist.github.com/mpetherb/7085315 I made some changes for my project. I use jquery for importing generated graph image to to my server.
This is a graph that I want to convert to image and save google graph example id="ex0"
Script for converting to image and importing to server
<script>
function getImgData(chartContainer) {
var chartArea = chartContainer.getElementsByTagName('svg')[0].parentNode;
var svg = chartArea.innerHTML;
var doc = chartContainer.ownerDocument;
var canvas = doc.createElement('canvas');
canvas.setAttribute('width', chartArea.offsetWidth);
canvas.setAttribute('height', chartArea.offsetHeight);
canvas.setAttribute(
'style',
'position: absolute; ' +
'top: ' + (-chartArea.offsetHeight * 2) + 'px;' +
'left: ' + (-chartArea.offsetWidth * 2) + 'px;');
doc.body.appendChild(canvas);
canvg(canvas, svg);
var imgData = canvas.toDataURL("image/png");
canvas.parentNode.removeChild(canvas);
return imgData;
}
function toImg(chartContainer, imgContainer) {
var doc = chartContainer.ownerDocument;
var img = doc.createElement('img');
var myimg=img.src = getImgData(chartContainer);
//Here I am using jquery for importing decoded image to hidden.php on my server
$.ajax({
method: "POST",
url: "hidden.php",
data: { name: myimg } });
while (imgContainer.firstChild) {
imgContainer.removeChild(imgContainer.firstChild);
}
imgContainer.appendChild(img);
}
</script>
<button onclick="toImg(document.getElementById('ex0'), document.getElementById('ex0'));"
type="button" <Convert to image and upload on server></button>
// ex0 - div id of this type of google graph. If you using another type of google graph - you should change it
Don't forget include jquery to your code.
and php hidden script for receiving data from jquery method POST and saving it on server
hidden.php file
<?php
if(isset($_POST['name']))
{
$data = $_POST['name'];
list($type, $data) = explode(';', $data);
list(, $data) = explode(',', $data);
file_put_contents('graph_temp.png', base64_decode($data));
I will notice again - works only in Chrome browser. Firefox also create image file on server but without any content (looks like firefox not support base64 encoded data)