Thursday, August 12, 2010

Printing HTML Documents Using Customised CSS and JavaScript

Recently, I was working on some Ruby on Rails project where users wanted to be able to print nice reports. I should admit that the gem solutions I found that time disappointed me because:
  • Most required a lot of time to understand than I had to make them work.
  • Some solutions required extra gems (and plugins) that I could not get because of usual gem error: ERROR: could not find gem XXX locally or in a repository!
One interesting fact was that the users had a defined report format which they wanted both screen layouts and printouts to be modelled from! An additional challenge was that the application was developed for use on a mouseless touchscreen computer that always open the browser in fullscreen mode. So, it would be difficult to manually select the Print command on the File menu of the browser. So after making a some Internet research, I got several solutions that I integrated to come up with mine that addresses the users' need. I present the solution here in a tutorial format so that it is easy to follow.
  1. Document Layout
Let us create a simple-and-easy-to-follow report layout using HTML tags as follows:

<html>
    <head>
    <title>The Application Name</title>
    <script src="/javascripts/report_printer.js" type="text/javascript"> </script>
    <link href="/stylesheets/report.css" media="screen" rel="stylesheet" type="text/css" />
    </head>
    <body>
    <div id="report">
        <div id="metaData" onclick="javascript:printContent('document');" style="float: right;">
            <a> Print Report</a>
        </div>
        <div id="document">
            <div id="reportHeader"> Organization Name </div>
            <div id="reportSubHeader"> Report Name and/or Description </div>
            <div id="dataTable">
                <!-- the data table is placed here -->
            </div>
        </div>
    </div>
</body>
</html>
Linked to this file is a standalone CSS file, report.css, that contains CSS definitions for each of the div ids and classes. In this example, the CSS file is placed in stylesheets sub-directory. The metaData div contains a hyper-linked button for printing the report. The metaData div customizes the hyper-link to a button. This div is significant to the whole printing process as we will see in a moment.
  1. Adding Printing Functionality using JavaScript
With the CSS, everything looks nice on the screen. However, there are two problems:
  1. The Print command on the File menu produces clumsy output.
  2. It would be difficult to manually select the Print command on the File menu of the browser since the application is developed for use on a mouseless touchscreen computer that always open the browser in fullscreen mode.
In order to print, we create a transitional pop-up window that opens upon clicking print button and closes after the printing action completes, whether successfully or not. All this pop-up window does is initiate an onload action via print_win() function. This is a simple Javascript function that uses the default DOM print()and close() functions to print and close respectively. Here is the print_win() function:
   function print_win(){
      window.print();
      window.close();
   }
Now we let Javascript create and destroy the pop-up window. This Javascript is invoked upon clicking the print button defined in metaData div. The pop-up window will get its data from the innerHTML of the element whose id is passed as argument to printContent()function. This implies we have to make sure that all data that we would like to be printed is placed in the right div, otherwise any data outside it will not be printed. In our example, the data is placed in the document div.
We should also remember that since print_win() function belongs to the pop-up window, it will be embedded within the outer Javascript that creates the pop-up window. We place our code in report_printer.js in the javascripts sub-directory. Here is the code that does it all:
     function printContent(id){
     var data = document.getElementById(id).innerHTML;
     var popupWindow = window.open('','printwin',
          'left=100,top=100,width=400,height=400');
     popupWindow.document.write('<HTML>\n<HEAD>\n');
     popupWindow.document.write('<TITLE></TITLE>\n');
     popupWindow.document.write('<URL></URL>\n');
     popupWindow.document.write('<script>\n');
     popupWindow.document.write('function print_win(){\n');
     popupWindow.document.write('\nwindow.print();\n');
     popupWindow.document.write('\nwindow.close();\n');
     popupWindow.document.write('}\n');
     popupWindow.document.write('<\/script>\n');
     popupWindow.document.write('</HEAD>\n');
     popupWindow.document.write('<BODY onload="print_win()">\n');
     popupWindow.document.write(data);
     popupWindow.document.write('</BODY>\n');
     popupWindow.document.write('</HTML>\n');
     popupWindow.document.close();
  }
Our application is ready for printing. Try printing your document using the print button.
  1. Improving the Output
Trying to print the document shows that the data is rightly captured but not well structured as required. Let us add report.css to the pop-up window in print mode using some Javascript code. We modify and add a simple statement in our Javascript code: popupWindow.document.write("<link href='/stylesheets/report.css' media='print' rel='stylesheet' type='text/css' />\n"); We also add a dummy line that formats the screen layout of document on the pop-up window so that it looks better. Notice that the line is similar to the previous one except that we specify the media as screen. Our function now looks like this:

     function printContent(id){
     var data = document.getElementById(id).innerHTML;
     var popupWindow = window.open('','printwin',
          'left=100,top=100,width=400,height=400');
     popupWindow.document.write('<HTML>\n<HEAD>\n');
     popupWindow.document.write('<TITLE></TITLE>\n');
     popupWindow.document.write('<URL></URL>\n');
     popupWindow.document.write("<link href='/stylesheets/report.css' media='print' rel='stylesheet' type='text/css' />\n");
    popupWindow.document.write("<link href='/stylesheets/report.css' media='screen' rel='stylesheet' type='text/css' />\n");
     popupWindow.document.write('<script>\n');
     popupWindow.document.write('function print_win(){\n');
     popupWindow.document.write('\nwindow.print();\n');
     popupWindow.document.write('\nwindow.close();\n');
     popupWindow.document.write('}\n');
     popupWindow.document.write('<\/script>\n');
     popupWindow.document.write('</HEAD>\n');
     popupWindow.document.write('<BODY onload="print_win()">\n');
     popupWindow.document.write(data);
     popupWindow.document.write('</BODY>\n');
     popupWindow.document.write('</HTML>\n');
     popupWindow.document.close();
  }

We have finished successfully. Your application should be able to produce documents that are as nice as your original screen versions. You can print directly or to files, whether PDF, PostScript, etc.

Happy coding!!

Tuesday, April 27, 2010

Javascript Functions for Prototypers

I have been working on a a web-based project for sometime now. Because of the diverse requirements on the system, I was required to have as many interactions with the users' data from a client-side point of view. I could not find inbuilt functions that could solve my problems. So I was compelled to extend the String object so as to suit my needs.

I should admit that they may not be all that robust to take pride in, but they are able to do what I wanted any way! I believe that there exists someone like me, looking for same (if not similar) functions and probably they are tired with googling. There might also be some who are working on some standardized library and may have been thinking of functions likes these. I guess this will be a starting point for both.

I have tried to make them very simple (simple in all senses) and straightforward, but if you have questions, keep them flowing. We are here to help each other. Constructive criticisms and suggestions are surely welcome!!

Programming hint: In each of these functions, You can improve the split functions to make comparisons using regular expression matching

  1. contains() function
  2. /* checks for the presence of a substring in a given string of
    * semi-colon separated substrings.
    * it returns 'true' if found, otherwise it returns 'false'
    *
    * for example :
    * 1. ["programming;in;javascript;is;cool"].contains("javascript") => true
    * 2. ["programming;in;javascript;is;cool"].contains("java") => false
    *
    * TO DO: ADD HANDLING OF 'SPACE' SEPARATED SUBSTRINGS
    *
    */
    String.prototype.contains = function (substring) {

    var array_of_strings = this.split(';');

    if (jQuery.inArray(substring, array_of_strings)>= 0) {
    return true;
    }
    else {
    return false;
    }
    }
  3. capitalize() function
  4. /* capitalizes a given string
    * Author: Edmond Kachale
    * for example :
    * "ProGraMming Is CooL".capitalize() => "Programming is cool"
    */
    String
    .prototype.capitalize = function(){
    var capitalized_string = new Array();

    if((this.length> 0)){
    capitalized_string.push(this[0].toUpperCase());
    capitalized_string.push(this.substring(1,this.length).toLowerCase());

    return
    capitalized_string.join("");
    }
    else{
    return
    this;
    }
    }

  5. titleize() function

  6. This function depends on capitalize() function above

    /* "titleizes" a given string
    * Author: Edmond Kachale
    * for example :
    * "Programming is cool".titleize() => "Programming Is Cool"
    */

    String.prototype.titleize = function(){
    var
    titleized_string = new Array();
    var
    sub_strings = this.split(" ");

    for(i = 0; i <this.length; i++)
    titleized_string.push(sub_strings[i].capitalize());

    return titleized_string.join(" ");
    }

Enjoy your programming!

NB: Forgive me for poor formatting. I didn't have enough time to create a custom css file!