Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating HTML Canvas image data server-side?

Tags:

The title of this question may be slightly misleading, but I'm not sure what the best title would be (since I can't guess at a solution yet).

Basically the system I am developing relies heavily on canvas graphs. These graphs are generated through javascript, and are made using data pulled via ajax from an API server.

The tricky part is, I'd like to be able to email these graphs to users of this system, without them actually having to go to the web page at all. So while I'm aware that it is possible to get the Base64 value of an image generated with javascript in a browser, what about if no one is there to run that javascript?

I'd like to keep the graphs generated in javascript/canvas, rather than making them in a common server-side graphics library (GD, ImageMagick). The Canvas graphs are dynamic, and allow for interaction via javascript. Though I don't want that functionality in the email notification, I do want them to be identical otherwise (at least in appearance).

So the question is, how can I get these graphs into an email?

At this point, my only guess is that I'd need to literally make a website that does AJAX requests for "graphs to render", renders these graphs, and sends the results to the server. Then I'd need a "server" that just sits there on that web page and churns out graphs. Is that the only solution here?

like image 373
GoldenNewby Avatar asked Feb 23 '12 07:02

GoldenNewby


People also ask

How do I get image data from canvas?

The getImageData() method returns an ImageData object that copies the pixel data for the specified rectangle on a canvas. Note: The ImageData object is not a picture, it specifies a part (rectangle) on the canvas, and holds information of every pixel inside that rectangle.

Is HTML5 canvas still used?

The HTML5 canvas has the potential to become a staple of the web, enjoying ubiquitous browser and platform support in addition to widespread webpage support, as nearly 90% of websites have ported to HTML5.

Which HTML element is used for canvas graphics?

The HTML <canvas> element is used to draw graphics, on the fly, via JavaScript. The <canvas> element is only a container for graphics.


1 Answers

I used phantomJs (like node.js but different) serverside to run exactly the same code as client side, and get the same result. all you need is one single exe-file (like a webkit stand alone web brower)

The following program (in Perl, but should be feasible to translate to you favourite language) takes some data, inserts into a web-page (could be ajax'ed) and either sends that web page to the client, or stores it as a temporary file, and starts PhantomJs on the same page. Then ask PhantomJs to generate a jpg, that is then picked up (and in this case sendt to the client).

#!/usr/bin/perl  use strict; use File::Temp; $|=1; #this script returns a graph, either as html +js web page to render client side, #or renders the same page server side, and returns the jpg image.  #files needed: #.\phantom_srv_client.pl  #this script #.\phantomjs.exe          #the webkit runtime stand alone file, from http://phantomjs.org/ #.\Scripts\excanvas.min.js #canvas simulator for IE8- #.\Scripts\jquery.min.js   #jQuery as we know it #.\Scripts\jquery.jqplot.min.js #graph library on top of jQuery from http://www.jqplot.com/ (Flot or any can be used)   #do we want client side rendering (html + js), or server side rendering (jpg) #jpg seems to render nicer than png on some pages? use CGI; my $show_as_jpg = CGI::param("jpg");  #path to javascript libraries (jQuery etc).  #Must be absolute file location for server rendering, relative for web use FindBin; my $script_path = $show_as_jpg      ? $FindBin::Bin."/Scripts"      : './Scripts';   #data to send to graph (two sets) my $data = [[2,5,4], [6,4,5]];  #use json to get this as a javascript text my $json_data; eval {require JSON; $json_data=JSON::to_json($data)}; #in case JSON is not installed, get the json/javascript data manually (just for demo) $json_data ||= "[[2,5,4], [6,4,9]]"; #here 9 at the end to see a difference  #The following is the web page that renders the graph, client or server side  #(links to scripts must be abolute to work serverside, as temp-files may go anywhere, $script_path keeps track of that) #$json_data is the Perl data structure converted to JSON (aka javascript, but not) my $graph_html =qq| <!DOCTYPE html> <html> <head>     <!--[if lt IE 9]><script language="javascript" type="text/javascript" src="$script_path/excanvas.min.js"></script><![endif]-->     <script class="include" type="text/javascript" src="$script_path/jquery.min.js"></script>     <script class="include" type="text/javascript" src="$script_path/jquery.jqplot.min.js"></script>      <script class="code" type="text/javascript" language="javascript">         jQuery(document).ready(function(){             /*data from perl (\$json_data) inserted here */             var data = $json_data;             jQuery.jqplot("chart1", data );         });     </script>     </head> <body>     <div id="chart1" style="width:600px; height:400px;"></div>     <a href='?jpg=1'>View as jpg</a> </body> </html> |;   #this is the javascript that tells PhantomJs what to do (ie open a doc and render it to bitmap) my $phantom_doc_js =qq|     var system = require('system');     //read from commandline which files to open, and write to     var open_doc = system.args[1];     var return_doc = system.args[2];     var page = require('webpage').create();     page.open(open_doc, function () {         page.render(return_doc);         phantom.exit();     }); |;  #see if we shall render this page serverside if ($show_as_jpg) {     #get temporary filenames with related file handlers     #where to put phantomjs script (generic so could be a static file)     my ($phantom_doc_filehandler, $phantom_doc_filename) = File::Temp::tempfile(  SUFFIX => '.js', TMPDIR => 1);     #where to put web page with data to render and ref to javascripts etc     my ($phantom_graph_filehandler, $phantom_graph_filename) = File::Temp::tempfile(SUFFIX => '.html', TMPDIR => 1);     #also get a filename with no handler, so phantomjs can return the jpg file. Extention must be .jpg!     my (undef, $image_filename) = File::Temp::tempfile( SUFFIX => '.jpg',TMPDIR => 1, OPEN => 0);      #store file content and close files     print $phantom_doc_filehandler $phantom_doc_js; close $phantom_doc_filehandler;     print $phantom_graph_filehandler $graph_html;   close $phantom_graph_filehandler;      #now call PhantomJs with filenames to read from and write to.     #Next version should support piping, which would simplify a lot      #use absolute path to phantomjs.exe in case web-server does not use current path     system($FindBin::Bin.'\\phantomjs', $phantom_doc_filename, $phantom_graph_filename, $image_filename) == 0          or die "system failed: $?";      #read the entire image file     my $img = slurp_file($image_filename);     print "Content-Type: image/jpeg\nPragma: no-cache\n\n".$img;      #The temp files are no more needed     unlink $phantom_doc_filename, $phantom_graph_filename, $image_filename;  } else { # just render client side     print "Content-Type: text/html\nPragma: no-cache\n\n".$graph_html; }  #slurp is not always std perl    sub slurp_file{   my $filename = shift;   my $string;   local $/ = undef;   open FILE, $filename or die "Couldn't open file: $!";   binmode FILE;   $string = <FILE>;   close FILE;   return $string; } 
like image 166
FtLie Avatar answered Sep 21 '22 17:09

FtLie