rahulkapoor90/VITacademics-Enhancement-Suite

View on GitHub
Extension for Firefox/scripts/timeTable/jspdf.js

Summary

Maintainability
F
2 wks
Test Coverage
/**
 * jsPDF
 * (c) 2009 James Hall
 * 
 * Some parts based on FPDF.
 */

var jsPDF = function(){
    
    // Private properties
    var version = '20090504';
    var buffer = '';
    
    var pdfVersion = '1.3'; // PDF Version
    var defaultPageFormat = 'a4';
    var pageFormats = { // Size in mm of various paper formats
        'a3': [841.89, 1190.55],
        'a4': [595.28, 841.89],
        'a5': [420.94, 595.28],
        'letter': [612, 792],
        'legal': [612, 1008]
    };
    var textColor = '0 g';
    var page = 0;
    var objectNumber = 2; // 'n' Current object number
    var state = 0; // Current document state
    var pages = new Array();
    var offsets = new Array(); // List of offsets
    var lineWidth = 0.200025; // 2mm
    var pageHeight;
    var k; // Scale factor
    var unit = 'mm'; // Default to mm for units
    var fontNumber; // TODO: This is temp, replace with real font handling
    var documentProperties = {};
    var fontSize = 16; // Default font size
    var pageFontSize = 16;

    // Initilisation 
    if (unit == 'pt') {
        k = 1;
    } else if(unit == 'mm') {
        k = 72/25.4;
    } else if(unit == 'cm') {
        k = 72/2.54;
    } else if(unit == 'in') {
        k = 72;
    }
    
    // Private functions
    var newObject = function() {
        //Begin a new object
        objectNumber ++;
        offsets[objectNumber] = buffer.length;
        out(objectNumber + ' 0 obj');        
    }
    
    
    var putHeader = function() {
        out('%PDF-' + pdfVersion);
    }
    
    var putPages = function() {
        
        // TODO: Fix, hardcoded to a4 portrait
        var wPt = pageWidth * k;
        var hPt = pageHeight * k;

        for(n=1; n <= page; n++) {
            newObject();
            out('<</Type /Page');
            out('/Parent 1 0 R');    
            out('/Resources 2 0 R');
            out('/Contents ' + (objectNumber + 1) + ' 0 R>>');
            out('endobj');
            
            //Page content
            p = pages[n];
            newObject();
            out('<</Length ' + p.length  + '>>');
            putStream(p);
            out('endobj');                    
        }
        offsets[1] = buffer.length;
        out('1 0 obj');
        out('<</Type /Pages');
        var kids='/Kids [';
        for (i = 0; i < page; i++) {
            kids += (3 + 2 * i) + ' 0 R ';
        }
        out(kids + ']');
        out('/Count ' + page);
        out(sprintf('/MediaBox [0 0 %.2f %.2f]', wPt, hPt));
        out('>>');
        out('endobj');        
    }
    
    var putStream = function(str) {
        out('stream');
        out(str);
        out('endstream');
    }
    
    var putResources = function() {
        putFonts();
        putImages();
        
        //Resource dictionary
        offsets[2] = buffer.length;
        out('2 0 obj');
        out('<<');
        putResourceDictionary();
        out('>>');
        out('endobj');
    }    
    
    var putFonts = function() {
        // TODO: Only supports core font hardcoded to Helvetica
        newObject();
        fontNumber = objectNumber;
        name = 'Helvetica';
        out('<</Type /Font');
        out('/BaseFont /' + name);
        out('/Subtype /Type1');
        out('/Encoding /WinAnsiEncoding');
        out('>>');
        out('endobj');
    }
    
    var putImages = function() {
        // TODO
    }
    
    var putResourceDictionary = function() {
        out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
        out('/Font <<');
        // Do this for each font, the '1' bit is the index of the font
        // fontNumber is currently the object number related to 'putFonts'
        out('/F1 ' + fontNumber + ' 0 R');
        out('>>');
        out('/XObject <<');
        putXobjectDict();
        out('>>');
    }
    
    var putXobjectDict = function() {
        // TODO
        // Loop through images
    }
    
    
    var putInfo = function() {
        out('/Producer (jsPDF ' + version + ')');
        if(documentProperties.title != undefined) {
            out('/Title (' + pdfEscape(documentProperties.title) + ')');
        }
        if(documentProperties.subject != undefined) {
            out('/Subject (' + pdfEscape(documentProperties.subject) + ')');
        }
        if(documentProperties.author != undefined) {
            out('/Author (' + pdfEscape(documentProperties.author) + ')');
        }
        if(documentProperties.keywords != undefined) {
            out('/Keywords (' + pdfEscape(documentProperties.keywords) + ')');
        }
        if(documentProperties.creator != undefined) {
            out('/Creator (' + pdfEscape(documentProperties.creator) + ')');
        }        
        var created = new Date();
        var year = created.getFullYear();
        var month = (created.getMonth() + 1);
        var day = created.getDate();
        var hour = created.getHours();
        var minute = created.getMinutes();
        var second = created.getSeconds();
        out('/CreationDate (D:' + sprintf('%02d%02d%02d%02d%02d%02d', year, month, day, hour, minute, second) + ')');
    }
    
    var putCatalog = function () {
        out('/Type /Catalog');
        out('/Pages 1 0 R');
        // TODO: Add zoom and layout modes
        out('/OpenAction [3 0 R /FitH null]');
        out('/PageLayout /OneColumn');
    }    
    
    function putTrailer() {
        out('/Size ' + (objectNumber + 1));
        out('/Root ' + objectNumber + ' 0 R');
        out('/Info ' + (objectNumber - 1) + ' 0 R');
    }    
    
    var endDocument = function() {
        state = 1;
        putHeader();
        putPages();
        
        putResources();
        //Info
        newObject();
        out('<<');
        putInfo();
        out('>>');
        out('endobj');
        
        //Catalog
        newObject();
        out('<<');
        putCatalog();
        out('>>');
        out('endobj');
        
        //Cross-ref
        var o = buffer.length;
        out('xref');
        out('0 ' + (objectNumber + 1));
        out('0000000000 65535 f ');
        for (var i=1; i <= objectNumber; i++) {
            out(sprintf('%010d 00000 n ', offsets[i]));
        }
        //Trailer
        out('trailer');
        out('<<');
        putTrailer();
        out('>>');
        out('startxref');
        out(o);
        out('%%EOF');
        state = 3;        
    }
    
    var beginPage = function() {
        page ++;
        // Do dimension stuff
        state = 2;
        pages[page] = '';
        
        // TODO: Hardcoded at A4 and portrait
        pageHeight = pageFormats['a4'][1] / k;
        pageWidth = pageFormats['a4'][0] / k;
    }
    
    var out = function(string) {
        if(state == 2) {
            pages[page] += string + '\n';
        } else {
            buffer += string + '\n';
        }
    }
    
    var _addPage = function() {
        beginPage();
        // Set line width
        out(sprintf('%.2f w', (lineWidth * k)));
        
        // Set font - TODO
        // 16 is the font size
        pageFontSize = fontSize;
        out('BT /F1 ' + parseInt(fontSize) + '.00 Tf ET');         
    }
    
    // Add the first page automatically
    _addPage();    

    // Escape text
    var pdfEscape = function(text) {
        return text.replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
    }
    
    return {
        addPage: function() {
            _addPage();
        },
        text: function(x, y, text) {
            // need page height
            if(pageFontSize != fontSize) {
                out('BT /F1 ' + parseInt(fontSize) + '.00 Tf ET');
                pageFontSize = fontSize;
            }
            var str = sprintf('BT %.2f %.2f Td (%s) Tj ET', x * k, (pageHeight - y) * k, pdfEscape(text));
            out(str);
        },
        setProperties: function(properties) {
            documentProperties = properties;
        },
        addImage: function(imageData, format, x, y, w, h) {
        
        },
        output: function(type, options) {
            endDocument();
            if(type == undefined) {
                return buffer;
            }
            if(type == 'datauri') {
                document.location.href = 'data:application/pdf;base64,' + Base64.encode(buffer);
            }
            // @TODO: Add different output options
        },
        setFontSize: function(size) {
            fontSize = size;
        }
    }

};