Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 181 lines (142 sloc) 5.59 KB
#!/usr/bin/env perl
=head1 NAME
s5pdf - Convert an S5 presentation to PDF
s5pdf [OPTION]... URI [FILE]
-s, --show show the presentation
-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
-S, --no-steps render only full slides (skip the steps)
-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)
s5pdf --zoom -2 s5-presentation.html
Convert and s5 presentation into a PDF.
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 {
my $do_steps = 1;
'v|verbose' => \my $verbose,
's|show' => \my $show,
'w|width=i' => \my $width,
'h|height=i' => \my $height,
'z|zoom=i' => \my $zoom,
'p|pause=i' => \my $timeout,
'steps!' => \$do_steps,
) or pod2usage(1);
my ($uri, $filename) = @ARGV or pod2usage(1);
$uri = "file://" . abs_path($uri) if -e $uri;
# The default file name is baed on there uri's filename
$filename ||= sprintf "%s.pdf", fileparse(URI->new($uri)->path, qr/\.[^.]*/) || 's5';
my $view = Gtk3::WebKit::WebView->new();
$view->set('zoom-level', 1 + ($zoom/10)) if $zoom;
# Introduce some JavaScript helper methods. This methods will communicate
# with the Perl script by writting data to the consolse.
function _is_end_of_slides () {
var last_slide = (snum == smax - 1) ? true : false;
var last_subslide = ( !incrementals[snum] || incpos >= incrementals[snum].length ) ? true : false;
var ret = (last_slide && last_subslide) ? true : false ;
// Let know Perl if we're done with the slides
console.log("s5-end-of-slides: " + ret);
return ret;
function _next_slide () {
if ($do_steps && incrementals[snum] && incpos < incrementals[snum].length) {
else {
# 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');
# We take a screenshot now
# 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 {
# 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
print "$message\n";
print "End of program caused by a JavaScript error\n";
my ($end) = ( $message =~ /^s5-end-of-slides: (true|false)$/) or return TRUE;
# See if we need to create a new PDF or a new page
if ($surface) {
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 {
print "Saving slide $count\n";
my $cr = Cairo::Context->create($surface);
# 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";
else {
# Go on with the slide
if ($timeout) {
Glib::Timeout->add($timeout, $grab_pdf);
else {
return TRUE;
my $window = $show ? Gtk3::Window->new('toplevel') : Gtk3::OffscreenWindow->new();
$window->set_default_size($width || 800, $height || 400);
return 0;
exit main() unless caller;