Use SharePoint Workflows to Inject JavaScript into PDFs and Print the Open Date
Ah, those pesky customers of ours, always looking for some niche functionality that is impossible to include in a generic product. However, using the Workflow Power Pack for SharePoint we can achieve almost anything we can think of…..almost.
Previously I described how to configure PDF Security settings from a SharePoint workflow and how to automatically watermark PDF files from a workflow. This time I’ll show how to add JavaScript to any PDF file to automatically print the current date (the date the PDF was opened) on every page. In essence this adds a print date without modifying the PDF file every day to include the current date.
A quick introduction for those not familiar with the product: The Muhimbi Workflow Power Pack for SharePoint allows custom C# or VB.NET code to be embedded in SharePoint Designer Workflows without the need to resort to complex Visual Studio based workflows, the development of bespoke Workflow Activities or long development cycles.
The solution presented below executes a workflow whenever a PDF file is added or updated. It iterates over all pages and inserts a form field on each page. Some client side JavaScript is then added to the PDF file that iterates over all newly added fields to insert the current date every time the PDF file is opened.
As the code is well documented it is easy to make further changes and customisations, e.g. change the formatting of the date or position of the label. Note that this has only been tested with a recent version of Adobe Acrobat reader. If you use a different PDF viewer your mileage may vary.
Create the workflow as follows:
-
Download and install the Muhimbi Workflow Power Pack for SharePoint.
-
Download and install the Muhimbi PDF Converter for SharePoint.
Note that you need version 3.2.0.20 or newer, older versions do not allow JavaScript to be inserted.
-
We need to be able to access functionality in the Muhimbi.SharePoint.DocumentConverter.PDF and System.Drawing assemblies. Add these references to the relevant Web Application using the Workflow Power Pack Central Administration screens as described in the Administration Guide. Make sure to place each reference on a new line.
-
Make sure you have the appropriate privileges to create workflows on a site collection.
-
Create a new workflow using SharePoint Designer.
-
On the Workflow definition screen associate the workflow with the Shared Documents library, tick the boxes next to both ‘ Automatically start….’ options and proceed to the next screen.
-
We only want to act on files of type PDF. Although we could have put this validation in the code, in this example we use a workflow condition for it so add a Compare Any Data Source condition and:
a. Click on the first value followed by the display data binding (fx) button.
b. Select Current Item as the Source and select File Type in the Field. Click the OK button to continue.
d. Click on the second value and enter pdf. (Use lower case as the compare option is case sensitive).
-
Click the Actions button and insert the Execute Custom Code action .
-
Optionally click parameter 1 and enter a relative or absolute destination path. Leave the parameter empty to save the modified file on top of the existing PDF file. For details about how paths are handled, see this post and search for the words ‘this url’.
-
Insert the C# based code embedded in step #3’s download (also listed below) by clicking this code.
/********************************************************************************************* Muhimbi PDF Converter - JavaScript Watermarking Copyright 2010, Muhimbi Ltd - www.muhimbi.com - All rights reserved The following code shows a simple way of adding JavaScript to existing PDF Files. It adds the current date to each page in the document in order to simulate a 'print date' that is always up to date without the need to modify the PDF file. The code is automatically executed when the document is opened in the Adobe Acrobat Viewer. Error and permission checking as well as other minor features have been omitted for the sake of brevity and clarity. Ideally PDF Conversion, applying security and watermarking is executed in the same step, see /blog/configure-pdf-security-from-a-sharepoint-workflow/ This code requires Muhimbi’s PDF Converter and Workflow Power Pack to be installed. *********************************************************************************************/ using System.Drawing; using System.IO; using Syncfusion.Pdf; using Syncfusion.Pdf.Parsing; using Syncfusion.Pdf.Graphics; using Syncfusion.Pdf.Interactive; using Muhimbi.SharePoint.DocumentConverter.PDF; SPFile spSourceDocument = MyWorkflow.Item.File; string destinationFileName = spSourceDocument.Name; string destinationFolderName = MyWorkflow.Parameter1 as string; // ** Load the document PdfLoadedDocument sourceDocument = new PdfLoadedDocument(spSourceDocument.OpenBinary()); PdfDocument destinationDocument = new PdfDocument(); // ** Copy all pages from the source document into the destination document // ** so we can add JavaScript actions. destinationDocument.ImportPageRange(sourceDocument, 0, sourceDocument.Pages.Count - 1); sourceDocument.Dispose(); // ** Iterate over all pages and add a form element for (int i = 0; i < destinationDocument.Pages.Count; i++) { PdfPage destinationPage = destinationDocument.Pages[i]; // ** Create a new field using a unique name PdfTextBoxField field = new PdfTextBoxField(destinationPage, "_M_PrintDateField_" + i); // ** Center the field const int BOX_WIDTH = 200; int boxLeft = (int)((destinationPage.Size.Width - BOX_WIDTH) / 2); field.Bounds = new RectangleF(boxLeft, 20, BOX_WIDTH, 20); // ** Format the field PdfFont font = new PdfStandardFont(PdfFontFamily.Helvetica, 12f); field.Font = font; field.BorderColor = new PdfColor(Color.White); field.BackColor = new PdfColor(Color.White); field.ReadOnly = true; field.TextAlignment = PdfTextAlignment.Center; destinationDocument.Form.Fields.Add(field); } // ** Create a client side script that iterates over all fields and populates the date string jscript = @" var pages = " + destinationDocument.Pages.Count + @"; var today = util.printd('dd-mm-yyyy', new Date()); for(var i=0; i<pages; i++) { var field = this.getField('_M_PrintDateField_' + i); field.value = 'Today is: ' + today; } "; // ** Attach the script to the Document Open event. PdfJavaScriptAction jsAction = new PdfJavaScriptAction(jscript); destinationDocument.Actions.AfterOpen = jsAction; // ** Construct the path and file to write the watermarked PDF file to. if (string.IsNullOrEmpty(destinationFolderName) == true) destinationFolderName = spSourceDocument.ParentFolder.Url; SPFolder destinationFolder = Utility.GetSPFolder(destinationFolderName, MyWorkflow.Web); string destinationFilePath = string.Format("{0}/{1}", destinationFolder.Url, destinationFileName); SPWeb destinationWeb = destinationFolder.ParentWeb; SPFile spDestinationFile = destinationWeb.GetFile(destinationFilePath); // ** If a document library requires manual checkout and the file is not checked out, then // ** check the file out before uploading. if (spDestinationFile.Exists && spDestinationFile.Item.ParentList.ForceCheckout && spDestinationFile.CheckOutStatus == SPFile.SPCheckOutStatus.None) { spDestinationFile.CheckOut(); } // ** Add the file to the site including the meta data using (MemoryStream watermarkedFile = new MemoryStream()) { destinationDocument.Save(watermarkedFile); spDestinationFile = destinationWeb.Files.Add(destinationFilePath, watermarkedFile, spSourceDocument.Item.Properties, true); } // ** Check the file back in if this script was responsible for checking it out. if (spDestinationFile.Item.ParentList.ForceCheckout == true) { spDestinationFile.CheckIn("Auto check-in after PDF watermarking."); }
-
Click the Actions button, select Log to History List, click this message and enter File watermarked.
-
Close the Workflow Designer.
-
Update an existing PDF or add a new PDF file to your library to trigger the workflow and apply the JavaScript.
Naturally this is just a simple example. Feel free to play around with the code, change which parameters are passed into the workflow, or add different JavaScript. Note that you may want to add a check to the code to check if the JavaScript / fields have previously been added, otherwise duplicate form fields may be added every time the PDF is updated.
This example uses the destinationDocument.Actions.AfterOpen event to execute the JavaScript when the document is opened. If you want to only display this content when printing the PDF then use the destinationDocument.Actions.
BeforePrint event.
Adobe’s JavaScript for Acrobat reference can be found here.
Clavin is a Microsoft Business Applications MVP who supports 1,000+ high-level enterprise customers with challenges related to PDF conversion in combination with SharePoint on-premises Office 365, Azure, Nintex, K2, and Power Platform mostly no-code solutions.