All the magic of BIG, now in PDF!

TMCW’s BIG is a fantastic little tool for knocking out presentations in little time. I’m often preparing something on short notice, and I dont need fancy transitions or animations, nor do I have the time to focus on building a fancy presentation. Maxxed-out text and quick-to-write slides is a dream come true. Until someone asks for a copy of the presentation…

Putting a BIG presentation online with gh-pages is dead simple but is isn’t really portable and distributable. In a chat the other day, someone said ‘someone should build an exporter for BIG…’ to which I replied ‘it shouldn’t be too hard to do that with PhantomJS’ having only briefly used Phantom, and not really knowing what I was talking about (as per usual.) I have had interest in PhantomJS for print services, like apollolm’s Phantasm, so I figured I might as well figure this out.

PhantomJS has dozens of great uses, from headless testing to scraping web content, but its ability to capture an image of a web page in a headless session is what I was interested in tapping into.

Capturing the pages

Since a BIG presentation is comprised of many full page views which are numerically tagged in the url, I thought the most straightforward way to export them would be to capture each page sequentially. I used CasperJS, which is an utility for PhantomJS which aids in navigation. Looping through the selected pages of the presentation, we use Casper to make an image of each page like so:

casper.start(
    url+'#'+start, function(){
      this.viewport(w, h);
    }
  ).then(function() {
    this.each(pageArray, function(casper,page) {
      this.thenOpen(url+'#'+page, function() {
        this.viewport(w, h);
        this.capture('outputs/'+(page < 10 ? '0' : '')+page+'.png',
    	{top: 0,left: 0,width: w,height: h});
      });
    });
});

casper.run();

Building the PDF

I had issues getting Phantom to create PDFs - the sizing was always off - so I used PDFKit to whip through the exported images and build a PDF document:

var PDFDocument = require('pdfkit');
var fs = require('fs');
var outDir = fs.readdirSync('outputs/');
var outputs = [];

for (var i=0; i < outDir.length; i++){
  if (outDir[i].split('.')[1] === 'png'){
    outputs.push(outDir[i]);
  }
}

var doc_options = {
  size: 'A4',
  layout: 'landscape',
  info: {
    Title: 'big.js presentation',
    Author: 'bigOut'
  }
}

var doc = new PDFDocument(doc_options);
var outDoc = fs.createWriteStream('bigOut.pdf');
doc.pipe(outDoc);

for (var i=0; i < outputs.length; i++){
  if (i > 0){
    doc.addPage();
  }
  doc.image('outputs/'+outputs[i], 0, 0, {width: 841, height: 595});
}

doc.end();

Keeping it off the system

Phantom and Casper are both system installs. If I were using daily, and knew that I didn’t need to change version, this would be fine, but for prototyping and playing, I didn’t want system installs (aside from node,js, which I do use daily,) and getting someone to try out a little project is easier if they don’t need system installs that may be different versions that what they are using. I use shelljs frequently for build scripts, so I used it here to call the node_modules version of PhantomJS:

// shelljs to call local phantomjs
var shell = require('shelljs');
var phjs = 'node_modules/phantomjs/bin/phantomjs';

shell.exec(phjs+' index.js '+url+' '+start+' '+end);

CasperJS is injected from node_modules into the phantom instance, removing that system reqirement as well:

phantom.casperPath = 'node_modules/casperjs';
phantom.injectJs(phantom.casperPath + '/bin/bootstrap.js');
var casper = require('casper').create();

The entire process is called from the terminal using node bigout.js http://abenrob.com/playingwithfire/ 0 38.

Check it out

Making bigOut.js was a fun little project, and a good excuse to learn somehting new! Feel free to check out the project and send me PRs if you want to make it better.

Updated: