Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/master' into har
- Loading branch information
Showing
2 changed files
with
193 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
#!/usr/bin/env perl | ||
|
||
=head1 NAME | ||
deck2pdf - Convert an Deck JS presentation to PDF | ||
=head1 SYNOPSIS | ||
deck2pdf [OPTION]... URI [FILE] | ||
-v, --verbose enable verbose mode | ||
-w WIDTH, --width WIDHT the width of the slides in pixels | ||
-h HEIGHT, --height HEIGHT the height of the slides in pixels | ||
-z LEVEL, --zoom LEVEL zoom level (negative values zoom out, positive zoom in) | ||
-p MS, --pause MS number of milliseconds to wait before taking a screenshot | ||
-h, --help print this help message | ||
Simple usage: | ||
# Presentation with a zoom out of 2 levels (smaller fonts) | ||
deck2pdf --zoom -2 deck-presentation.html | ||
# Save the presentation through a frame buffer (no window visible) | ||
xvfb-run --server-args="-screen 0 1280x800x24" deck2pdf file://$HOME/Talks/WebKit-Perl/index.html --pause 1000 --width 1280 --height 800 webkit-perl.pdf | ||
=head1 DESCRIPTION | ||
Convert and s5 presentation into a PDF. | ||
=cut | ||
|
||
use strict; | ||
use warnings; | ||
|
||
use Data::Dumper; | ||
use Getopt::Long qw(:config auto_help); | ||
use Pod::Usage; | ||
use URI; | ||
use File::Basename qw(fileparse); | ||
use Cwd qw(abs_path); | ||
|
||
use Gtk3; | ||
use Glib::Object::Introspection; | ||
use Gtk3::WebKit; | ||
use Cairo::GObject; | ||
use Glib ':constants'; | ||
|
||
|
||
sub main { | ||
Gtk3::init(); | ||
|
||
GetOptions( | ||
'v|verbose' => \my $verbose, | ||
'w|width=i' => \my $width, | ||
'h|height=i' => \my $height, | ||
'z|zoom=i' => \my $zoom, | ||
'p|pause=i' => \my $timeout, | ||
) or pod2usage(1); | ||
my ($uri, $filename) = @ARGV or pod2usage(1); | ||
$uri = "file://" . abs_path($uri) if -e $uri; | ||
$width ||= 1024; | ||
$height ||= 768; | ||
|
||
# The default file name is baed on there uri's filename | ||
$filename ||= sprintf "%s.pdf", fileparse(URI->new($uri)->path, qr/\.[^.]*/) || 'deck'; | ||
|
||
my $view = Gtk3::WebKit::WebView->new(); | ||
$view->set('zoom-level', 1 + ($zoom/10)) if $zoom; | ||
|
||
# Start taking screenshot as soon as the document is loaded. Maybe we want | ||
# to add an onload callback and to log a message once we're ready. We want | ||
# to take a screenshot only when the page is done being rendered. | ||
$view->signal_connect('notify::load-status' => sub { | ||
return unless $view->get_uri and ($view->get_load_status eq 'finished'); | ||
Glib::Idle->add(\®ister_javascript, $view); | ||
}); | ||
|
||
|
||
# The JavaScripts communicates with Perl by writting into the console. This | ||
# is a hack but this is the best way that I could find so far. | ||
my $surface; | ||
my $count = 0; | ||
$view->signal_connect('console-message' => sub { | ||
my ($widget, $message, $line, $source_id) = @_; | ||
print "CONSOLE $message at $line $source_id\n" if $verbose; | ||
|
||
if ($message =~ /^ReferenceError: /) { | ||
# JavaScript error, we stop the program | ||
die "$message\n"; | ||
die "End of program caused by a JavaScript error\n"; | ||
Gtk3->main_quit(); | ||
return FALSE; | ||
} | ||
|
||
my ($end) = ( $message =~ /^deck-end-of-slides: (true|false)$/) or return TRUE; | ||
|
||
# See if we need to create a new PDF or a new page | ||
if ($surface) { | ||
$surface->show_page(); | ||
} | ||
else { | ||
my ($width, $height) = ($view->get_allocated_width, $view->get_allocated_height); | ||
$surface = Cairo::PdfSurface->create($filename, $width, $height); | ||
} | ||
|
||
# A new slide has been rendered on screen, we save it to the pdf | ||
my $grab_pdf = sub { | ||
++$count; | ||
print "Saving slide $count\n"; | ||
my $cr = Cairo::Context->create($surface); | ||
$view->draw($cr); | ||
|
||
# Go to the next slide or stop grabbing screenshots | ||
if ($end eq 'true') { | ||
# No more slides to grab | ||
my $s = $count > 1 ? 's' : ''; | ||
print "Presentation $filename has $count slide$s\n"; | ||
Gtk3->main_quit(); | ||
return FALSE; | ||
} | ||
else { | ||
# Go on with the slide | ||
$view->execute_script('_next_slide();'); | ||
} | ||
}; | ||
|
||
if ($timeout) { | ||
Glib::Timeout->add($timeout, $grab_pdf); | ||
} | ||
else { | ||
Glib::Idle->add($grab_pdf); | ||
} | ||
|
||
return TRUE; | ||
}); | ||
|
||
my $window = Gtk3::Window->new('toplevel'); | ||
$window->set_default_size($width, $height); | ||
|
||
# Without a scrolled window Deck JS makes this program go crazy. | ||
my $scrolls = Gtk3::ScrolledWindow->new(); | ||
$scrolls->add($view); | ||
$window->add($scrolls); | ||
|
||
$window->show_all(); | ||
|
||
$view->load_uri($uri); | ||
Gtk3->main(); | ||
return 0; | ||
} | ||
|
||
|
||
sub register_javascript { | ||
my ($view) = @_; | ||
|
||
# Introduce some JavaScript helper methods. This methods will communicate | ||
# with the Perl script by writting data to the consolse. | ||
$view->execute_script(qq{ | ||
var last_slide = jQuery.deck('getSlides').length - 1; | ||
var cur_slide = 0; | ||
function _is_end_of_slides() { | ||
var ret = false; | ||
if (cur_slide == last_slide) { | ||
// Let know Perl if we're done with the slides | ||
ret = true; | ||
} | ||
console.log("deck-end-of-slides: " + ret); | ||
return false; | ||
} | ||
function _next_slide () { | ||
jQuery.deck('go', ++cur_slide); | ||
_is_end_of_slides(); | ||
} | ||
}); | ||
|
||
# Sometimes the program dies with: | ||
# (<unknown>:19092): Gtk-CRITICAL **: gtk_widget_draw: assertion `!widget->priv->alloc_needed' failed | ||
# This seem to happend is there's a newtwork error and we can't download | ||
# external stuff (e.g. facebook iframe). This timeout seems to help a bit. | ||
Glib::Idle->add(sub { | ||
$view->execute_script('_is_end_of_slides();'); | ||
return FALSE; | ||
}); | ||
|
||
return FALSE; | ||
} | ||
|
||
|
||
exit main() unless caller; |