Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into har
Browse files Browse the repository at this point in the history
  • Loading branch information
potyl committed Dec 10, 2011
2 parents 97f4580 + 0cfe014 commit e0f7797
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 0 deletions.
1 change: 1 addition & 0 deletions README
Expand Up @@ -23,6 +23,7 @@ nanny.pl - very simple parental control
git.pl - display a web page using GIR
screenshot - save a screenshot as a PDF, PNG, SVG or PS
s5pdf - save a S5 presentation in PDF
deck2pdf - save a Deck JS presentation in PDF
css-rules-usage.p l - list css rules and their usage
dom-walker - walk the DOM tree
js - get the page's title through JavaScript
Expand Down
192 changes: 192 additions & 0 deletions deck2pdf
@@ -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(\&register_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;

0 comments on commit e0f7797

Please sign in to comment.