Introduction

As a developer, generating downloadable PDF files is very common requirement in SharePoint 2013. While developing SharePoint apps, a developer is faced with couple of questions like, “Do I generate the PDF document client side?”, and “Is it possible to generate PDF documents client side?” The answer is definitely yes, and it’s not that much hard at all. I used the JSPDF library, which has a comprehensive JavaScript API for generating PDF documents.

Solution

Here’s a quick and easy example for generating proposal documents in PDF format.

First of all, create a custom list, and add required columns in that list to store proposal related details (i.e. Title, Project ID, Project Name, Proposed By, Date of Proposal, Description, Start Date, End Date, Price, and Place)

Next, download the JSPDF library files, and copy those files into your SharePoint site. I put them into the Style Library/Custom/PDF/. Then, be sure to include file references inside of your main text file.

<script src="/Style%20Library/Custom/PDF/jquery.min.js"></script>
<script src<br>

<br>="/Style%20Library/Custom/PDF/jspdf.min.js"></script>

<script src="/Style%20Library/Custom/PDF/FileSaver.js"></script>

Now, let’s generate a PDF document. You can try below code snippet in CEWP on display form of list that will add a button in ribbon to generate a very simple PDF document and save it to the local file system.

$(document).ready(function() {
 // To add print button in ribbon
 
 var linkButton = '<span class="ms-cui-ctl-largeIconContainer" unselectable="on"><span class=" ms-cui-img-32by32 ms-cui-img-cont-float ms-cui-imageDisabled" unselectable="on"><img class="" style="top: -409px;left: -239px;" unselectable="on" src="/_layouts/15/1033/images/formatmap32x32.png?rev=40"></span></span><span class="ms-cui-ctl-largelabel" unselectable="on">Print<br>Item</span>'
 var editLink = document.getElementById("Ribbon.ListForm.Display.Manage.EditItem-Large");
 var node = document.createElement("A");
 node.text = "Print Item";
 node.className = "ms-cui-ctl-large";
 node.innerHTML = linkButton;
 node.onclick = function() {
 printTable(this);
 return false;
 };
 editLink.parentNode.appendChild(node);
});
 
var objSender;
var fileName;
 
function printTable(sender) {
 // To generate pdf document using list item data 
 
 objSender = sender;
 var promise = $.ajax({
 url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Proposal Details')/items('" + getParameterByName('ID') + "')",
 method: "GET",
 headers: {
 "ACCEPT": "application/json;odata=verbose"
 }
 });
 
 promise.done(successFunction);
 promise.fail(errorFunction);
}
 
function successFunction(data) {
 l = {
 orientation: 'p',
 unit: 'px',
 format: 'a4',
 compress: true,
 fontSize: 28,
 lineHeight: 0,
 autoSize: true,
 printHeaders: true
 };
 
 
 var imgLogo = 'data:image/png;base64,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'
 
 var varHeight = 100; // Starting point from the top
 
 var doc = new jsPDF(l, 'px', 'a4', true),
 sizes = [12],
 fonts = [
 ['Helvetica', '']
 ],
 font, size, lines, margin = 0.5,
 verticalOffset = margin,
 loremipsum = data.d.Description;
 
 doc.addImage(imgLogo, 'JPEG', 38, 35, 53, 40)
 
 doc.setFontSize(12);
 doc.setTextColor(0, 114, 198);
 doc.setFontType("bold");
 doc.text(40, varHeight + 2, data.d.Title);
 
 doc.setFontSize(9);
 doc.setTextColor(0, 0, 0);
 
 // first table
 
 doc.line(40, varHeight + 20, 410, varHeight + 20);
 doc.line(40, varHeight + 32, 410, varHeight + 32);
 doc.line(40, varHeight + 44, 410, varHeight + 44);
 doc.line(40, varHeight + 20, 40, varHeight + 44);
 doc.line(110, varHeight + 20, 110, varHeight + 44);
 doc.line(250, varHeight + 20, 250, varHeight + 44);
 doc.line(330, varHeight + 20, 330, varHeight + 44);
 doc.line(410, varHeight + 20, 410, varHeight + 44);
 
 // first table data
 doc.text(50, varHeight + 29, 'Project name');
 doc.text(260, varHeight + 29, 'Project ID');
 doc.text(50, varHeight + 41, 'Proposed by');
 doc.text(260, varHeight + 41, 'Date of proposal');
 
 doc.setFontType("normal");
 doc.text(120, varHeight + 29, data.d.Project_x0020_Name);
 doc.text(340, varHeight + 29, data.d.Project_x0020_ID);
 doc.text(120, varHeight + 41, data.d.Proposed_x0020_By);
 doc.text(340, varHeight + 41, data.d.Date_x0020_Of_x0020_Proposal);
 
 doc.setFontSize(12);
 doc.setTextColor(0, 114, 198);
 doc.setFontType("bold");
 doc.text(40, varHeight + 80, 'GENERAL DESCRIPTION');
 
 doc.setFontSize(9);
 doc.setTextColor(0, 0, 0);
 
 font = fonts[0];
 size = sizes[0];
 doc.setFontType("normal");
 lines = doc.setFont().setFontSize(size).splitTextToSize(loremipsum, 375);
 doc.text(40, varHeight + 95 + verticalOffset + size / 72, lines);
 verticalOffset += (lines.length + 0.5) * size / 72;
 
 varHeight += lines.length * 8 + 10;
 doc.setFontSize(12);
 doc.setTextColor(0, 114, 198);
 doc.setFontType("bold");
 doc.text(40, varHeight + 125, 'PRICING AND SCHEDULE');
 
 doc.setFontSize(9);
 doc.setTextColor(0, 0, 0);
  
 // second table
  
 doc.line(40, varHeight + 140, 410, varHeight + 140);
 doc.line(40, varHeight + 152, 410, varHeight + 152);
 doc.line(40, varHeight + 164, 410, varHeight + 164);
 doc.line(40, varHeight + 140, 40, varHeight + 164);
 doc.line(163, varHeight + 140, 163, varHeight + 164);
 doc.line(286, varHeight + 140, 286, varHeight + 164);
 doc.line(410, varHeight + 140, 410, varHeight + 164);
 
 // second table data
  
 doc.text(50, varHeight + 149, 'Start Date');
 doc.text(173, varHeight + 149, 'End Date');
 doc.text(296, varHeight + 149, 'Payment in USD');
 
 doc.setFontType("normal");
 doc.text(50, varHeight + 161, data.d.Start_x0020_Date);
 doc.text(173, varHeight + 161, data.d.End_x0020_Date);
 doc.text(296, varHeight + 161, data.d.Price);
 
 // footer part
  
 doc.text(40, varHeight + 200, 'Place and date');
 doc.text(40, varHeight + 210, 'In ' + data.d.Place + '  ' + convertDate(new Date()));
 doc.text(40, varHeight + 220, data.d.Client);
  
 // save document to the local file system.
 
 doc.save("Test.pdf");
}
 
function errorFunction(error) {
 var errText = $.parseJSON(error.responseText);
 alert(errText.error.message.value);
}
 
function getParameterByName(name) {
 name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
 var regexS = "[\\?&]" + name + "=([^&#]*)";
 var regex = new RegExp(regexS);
 var results = regex.exec(window.location.search);
 if (results == null)
 return "";
 else
 return decodeURIComponent(results[1].replace(/\+/g, " "));
}
 
function convertDate(inputFormat) {
 function pad(s) {
 return (s < 10) ? '0' + s : s;
 }
 var d = new Date(inputFormat);
 return [pad(d.getDate()), pad(d.getMonth() + 1), d.getFullYear()].join('/');
}

Above script gives us very grateful output something like below.

Be sure to check out the JSPDF online examples for more details.

Code Download

You can download the code snippet here that is used to generate the PDF file.