Rough Book

random musings of just another computer nerd

Category: Javascript

Doing front-end development IS such a pain

This is so true that it’s hilarious. And sad. Any time I try to do something a little nontrivial for the front-end, it goes downhill so quickly. It’s like you are at this bizarre Home Depot with a million tools and you aren’t quite sure what they do because the most of the instruction manuals are missing pages or are just completely absent. There are very few that are complete.

You went there trying to get some nails and a hammer to hang a picture at home. But the nails only come in package that includes a lava lamp, a sledgehammer that weighs 50 lbs, and 217 blocks of assorted shapes, sizes, and colors. You get a hammer that seems to be the most popular but then you read a blog post from a carpenter ninja rockstar who has come out with a new hammer-design (and a cool name: “Hamm.ür”) that everyone is raving about and is a huge improvement over the new one and has started a new company that is already building and selling it. Home Depot also just happens to have it. You decide to get the new one; the old one wasn’t that actively supported by the manufacturer anymore anyway. In fact, it was just made by one guy in his garage and no one had seen him in a year and he rarely answered his phone or responded to emails. The new hammer does also come in a package with other stuff but at this point you really just want to hang that picture because that thing has been sitting against the wall for months.

You get home and finally start to hammer in the nail but end up burning your house down because the hammer replaced the lava lamp’s power adapter with a bare copper wire that set your curtains on fire. When you complain that using bare copper is unsafe and that they shouldn’t come with hammers or replace power adapters in a lava lamp that you didn’t even want in the first place, because seriously why the hell does anyone sell lava lamps with nails, you get told “Stfu noob! Copper is like the second-best fucking conductor and it is cheaper than silver and bare copper is so much lighter than a whole stupid adapter; seriously weren’t you just complaining about not wanting extra shit with what you buy?”

JavaOne

I am at JavaOne! The last time I was here was in ’08, when it was run by Sun. Of course, it’s run by Oracle now. The first day has been pretty good. I attended sessions on Garbage Collection, the Nashorn JavaScript engine, writing DSLs, and about parallelization options offered by JDK 7 and 8 to leverage multicore processors. Pretty good first day!

Nashorn looks pretty interesting and I will be giving it a closer look when I come back home. They are also looking for people to help out so I am going to see if I can contribute anything useful.

Today I will be checking out a talk on Lambdas by Brian Goetz and will also be going to a talk on Big Data. Finally I also have some BOF (birds of a feather) sessions on evolutionary algorithms and writing parsers in Scala. Pretty interesting day and I’m looking forward to it!

Rendering a PDF with text-selection, using pdf.js

I have been working a project for the last few days, that deals with rendering PDF’s in-browser. Initially, I was going to parse the PDF and extract the text content, but then I ran into pdf.js, which is a library developed by Mozilla for rendering PDF’s in-browser via JavaScript. The project I am working on has a requirement that users should be able to select text within the PDF. This is possible using pdf.js. Unfortunately, the example code only shows you how to render a PDF, but not how to enable text-selection. I wasn’t able to find any API access to enable text-selection either. I finally ended up on the #pdfjs IRC channel and the friendly folks there gave me some direction. The logic for enabling text-selection was buried inside the code for Mozilla’s PDF viewer, and was heavily intertwined with the viewer code as well. I spent a few days playing around with the viewer and tracing through the code. I was stumped many times since the code was complex and I know jack about parsing PDF’s. But eventually I was able to focus on the part of the code that actually took care of enabling text-selection.

pdf.js’ approach to enabling text-selection is actually quite clever. The library overlays divs over the PDF, and these divs contain text that matches the PDF text that they are floating over. So when you select the text, you are actually selecting the text inside the overlaid divs. This was fine and dandy, but I was still stuck as far as getting this to work on my project. What I needed was a minimal example that I could adapt for my uses. After a day or two of tracing code, experimenting, debugging, and staring at the screen in frustration, I was eventually able to come up with a minimal example! To accomplish this, I extracted code that was relevant to creating the overlays out of the viewer code, into its own independent file. I also removed a lot of code that was dependent on the viewer itself. Keep in mind that this example doesn’t have functionality like text finding or matching, and that code is also heavily intertwined with the viewer code. All this example does is render a PDF with text-selection enabled. However, I think this is a good start!

If you are interested, you can check out the code on github and a working example on this fiddle.

The pertintent code is as follows (keep in mind you still require additional resources; all of that information is available on github):

window.onload = function () {
    var pdfBase64 = "..."; //base64 representing the PDF

    var scale = 1.5; //Set this to whatever you want. This is basically the "zoom" factor for the PDF.

    /**
     * Converts a base64 string into a Uint8Array
     */
    function base64ToUint8Array(base64) {
        var raw = atob(base64); //This is a native function that decodes a base64-encoded string.
        var uint8Array = new Uint8Array(new ArrayBuffer(raw.length));
        for (var i = 0; i < raw.length; i++) {
            uint8Array&#91;i&#93; = raw.charCodeAt(i);
        }

        return uint8Array;
    }

    function loadPdf(pdfData) {
        PDFJS.disableWorker = true; //Not using web workers. Not disabling results in an error. This line is
        //missing in the example code for rendering a pdf.

        var pdf = PDFJS.getDocument(pdfData);
        pdf.then(renderPdf);
    }

    function renderPdf(pdf) {
        pdf.getPage(1).then(renderPage);
    }

    function renderPage(page) {
        var viewport = page.getViewport(scale);
        var $canvas = jQuery("<canvas></canvas>");

        //Set the canvas height and width to the height and width of the viewport
        var canvas = $canvas.get(0);
        var context = canvas.getContext("2d");
        canvas.height = viewport.height;
        canvas.width = viewport.width;

        //Append the canvas to the pdf container div
        var $pdfContainer = jQuery("#pdfContainer");
        $pdfContainer.css("height", canvas.height + "px").css("width", canvas.width + "px");
        $pdfContainer.append($canvas);

        //The following few lines of code set up scaling on the context if we are on a HiDPI display
        var outputScale = getOutputScale();
        if (outputScale.scaled) {
            var cssScale = 'scale(' + (1 / outputScale.sx) + ', ' +
                (1 / outputScale.sy) + ')';
            CustomStyle.setProp('transform', canvas, cssScale);
            CustomStyle.setProp('transformOrigin', canvas, '0% 0%');

            if ($textLayerDiv.get(0)) {
                CustomStyle.setProp('transform', $textLayerDiv.get(0), cssScale);
                CustomStyle.setProp('transformOrigin', $textLayerDiv.get(0), '0% 0%');
            }
        }

        context._scaleX = outputScale.sx;
        context._scaleY = outputScale.sy;
        if (outputScale.scaled) {
            context.scale(outputScale.sx, outputScale.sy);
        }

        var canvasOffset = $canvas.offset();
        var $textLayerDiv = jQuery("<div />")
            .addClass("textLayer")
            .css("height", viewport.height + "px")
            .css("width", viewport.width + "px")
            .offset({
                top: canvasOffset.top,
                left: canvasOffset.left
            });

        $pdfContainer.append($textLayerDiv);

        page.getTextContent().then(function (textContent) {
            var textLayer = new TextLayerBuilder($textLayerDiv.get(0), 0); //The second zero is an index identifying
            //the page. It is set to page.number - 1.
            textLayer.setTextContent(textContent);

            var renderContext = {
                canvasContext: context,
                viewport: viewport,
                textLayer: textLayer
            };

            page.render(renderContext);
        });
    }

    var pdfData = base64ToUint8Array(pdfBase64);
    loadPdf(pdfData);
};

Anonymous self-invoked recursive-function in Javascript

I needed to do an n-ary tree traversal recently with some Javascript code that I’m working on and I initially wrote an iterative n-ary tree-traversal algorithm (using a node stack with a while loop). I wanted to keep track of some extra (depth-dependent) data during the traversal and I didn’t like the way I was forced to do it with the iterative algorithm. I also didn’t feel like adding a new function to the namespace. Then I realized that you can create function-literals in Javascript and self-invoke them. So I ended up with the an anonymous self-invoked recursive function. Here’s the basic skeleton for an n-ary tree traversal (this one is pre-order):

(function(node){
for(var i = 0; i < node.children.length; i++) { console.log(node.data); arguments.callee(node.children[i]); } }(root)); [/sourcecode] You can also do a binary tree traversal (this one is also pre-order): [sourcecode="javascript"] (function(node){ if(node != null) { console.log(node.data); arguments.callee(node.left); arguments.callee(node.right); } }(root)); [/sourcecode] Seems very elegant and also very Lisp-like to me (must be the parentheses!)

All original content on these pages is fingerprinted and certified by Digiprove
%d bloggers like this: