Blog Post

Adding a dynamic watermark to a PDF file from a SharePoint Workflow

Clavin Fernandes
Illustration: Adding a dynamic watermark to a PDF file from a SharePoint Workflow

We have had a great response to our previous post about watermarking PDF files, however quite a few customers have told us that they want to use a dynamic watermark (e.g. a username, ip address etc) rather than a static one.

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.

Update: Please note that as of version 3.5 the PDF Converter Services also supports a native watermarking interface.

The solution presented below executes a workflow whenever a PDF file is added to a document library. It then applies security settings to prevent the watermark from being removed and then reads the watermark text from a SharePoint column. The workflow author can specify if the watermark is placed in the foreground or the background as well as the level of transparency. As the code is well documented it is easy to make further changes and customisations, e.g. the PDF file that needs to be watermarked may not be the file the workflow is acting on or the watermarked text may come from a workflow variable rather than a list column.

Create the workflow as follows:

  1. Download and install the Muhimbi Workflow Power Pack for SharePoint.

  2. Download and install the Muhimbi PDF Converter for SharePoint.

    Note that you need version 3.1.2.18 or newer, older versions do not allow watermarking.

  3. Download this article’s source code.

  4. 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.

  5. Make sure you have the appropriate privileges to create workflows on a site collection.

  6. Create a new column named Watermark in the Shared Documents library. The content of this column will be used as the text for the watermark.

  7. Create a new workflow using SharePoint Designer.

  8. On the Workflow definition screen associate the workflow with the Shared Documents library, tick the box next to ‘ Automatically start this workflow when a new item is created’ and proceed to the next screen. Please note that if you tick the ‘auto start on update’ box as well then the PDF file may be watermarked multiple times leading to unexpected results.

  9. 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).

  10. Click the Actions button and insert the Execute Custom Code action .

  11. Optionally click parameter 1 and enter a relative or absolute destination path. Leave the parameter empty to save the watermarked 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’.

  12. Insert the C# based code embedded in step #3’s download (also listed below) by clicking this code.

    /*********************************************************************************************
    
          Muhimbi PDF Converter - Dynamic Watermarking
    
       Copyright 2010, Muhimbi Ltd - www.muhimbi.com - All rights reserved
    
    The following code shows a simple way of adding a dynamic watermark to existing PDF Files.
    The watermark content is read from a SharePoint column and rendered on the center of the page.
    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 Muhimbi.SharePoint.DocumentConverter.PDF;
    using Syncfusion.Pdf.Security;
    SPFile spSourceDocument = MyWorkflow.Item.File;
    string destinationFileName = spSourceDocument.Name;
    string destinationFolderName = MyWorkflow.Parameter1 as string;
    // ** Read the text from the Column named 'Watermark', use a column name of your choice.
    string watermark = MyWorkflow.Item["Watermark"] as string;
    string pdfOwnerPassword = "some!Strong^Password";
    // ** z-order and transparency of the watermark
    bool watermarkInBackground = true;
    float watermarkTransparancy = 0.25f;
    // ** Method for creating a simple template, amend as needed
    WorkflowFunction<string, PdfTemplate> CreateWatermark = delegate(string[] parameters)
    {
        const float watermarkWidth = 250;
        const float watermarkHeight = 23;
        string watermarkText = parameters[0];
        if(watermarkText==null)
            watermarkText = "no watermark specified";
        // ** Create a new Template
        PdfTemplate template = new PdfTemplate(watermarkWidth, watermarkHeight);
        PdfGraphics tg = template.Graphics;
        // ** Rectangle
        PdfPen pen = new PdfPen(PdfBrushes.Black, 1);
        tg.DrawRectangle(pen, PdfBrushes.White, new RectangleF(0, 0, watermarkWidth                                                           watermarkHeight));
        // ** Evaluation message text
        using (Font f = new Font("Arial", 10f, FontStyle.Regular))
        {
            PdfFont font = new PdfTrueTypeFont(f, false);
            PdfStringFormat stringFormat = new PdfStringFormat();
            stringFormat.Alignment = PdfTextAlignment.Center;
            stringFormat.LineAlignment = PdfVerticalAlignment.Middle;
            stringFormat.WordWrap = PdfWordWrapType.WordOnly;
            stringFormat.ClipPath = true;
            tg.DrawString(watermarkText, font, PdfBrushes.Black, new RectangleF(5, 0,
                                    watermarkWidth - 10, watermarkHeight), stringFormat);
        }
        return template;
    };
    // ** Method for applying security settings, amend / uncomment as needed
    WorkflowMethod<PdfLoadedDocument> ApplySecurity = delegate(PdfLoadedDocument[] pdfDocuments)
    {
        PdfLoadedDocument pdfDocument = pdfDocuments[0];
        // ** Enable encryption of content
        pdfDocument.Security.KeySize = PdfEncryptionKeySize.Key128Bit;
        // ** Specify the password used for modifying permissions
        pdfDocument.Security.OwnerPassword = pdfOwnerPassword;
        // ** Apply the restrictions, if any. Note that by default nothing is
        // ** allowed and we need to individually enable permissions
        pdfDocument.Security.Permissions = PdfPermissionsFlags.Default;
        // ** Allow Commenting / annotations
        pdfDocument.Security.SetPermissions(PdfPermissionsFlags.EditAnnotations);
        // ** Allow Accessibility
        //pdfDocument.Security.SetPermissions(PdfPermissionsFlags.AccessibilityCopyContent);
        // ** Allow Copy content via clipboard etc.
        //pdfDocument.Security.SetPermissions(PdfPermissionsFlags.CopyContent);
        // ** Allow Assemble / modification of document
        //pdfDocument.Security.SetPermissions(PdfPermissionsFlags.AssembleDocument);
        // ** Allow Form fields to be filled out
        pdfDocument.Security.SetPermissions(PdfPermissionsFlags.FillFields);
        // ** Allow high resolution printing
        pdfDocument.Security.SetPermissions(PdfPermissionsFlags.FullQualityPrint);
        // ** Allow any kind of printing
        pdfDocument.Security.SetPermissions(PdfPermissionsFlags.Print);
    };
    // ** Load the document from SharePoint
    PdfLoadedDocument sourceDocument = new PdfLoadedDocument(spSourceDocument.OpenBinary());
    // ** Restrict the rights
    ApplySecurity(sourceDocument);
    // ** Create the watermark Template
    PdfTemplate watermarkTemplate = CreateWatermark(watermark);
    // ** Iterate over all pages and apply the watermark
    foreach (PdfPageBase page in sourceDocument.Pages)
    {
        // ** Position the watermark at the centre of the page
        float wmWidth = watermarkTemplate.Width;
        float wmHeight = watermarkTemplate.Height;
        float wmLeft = ((page.Size.Width - wmWidth) / 2);
        float wmTop = ((page.Size.Height - wmHeight) / 2);
        PointF wmPosition = new PointF(wmLeft, wmTop);
        // ** Place watermark behind or in front of text?
        if (watermarkInBackground == true)
        {
            PdfTemplate pageTemplate = page.CreateTemplate();
            page.Layers.Clear();
            PdfGraphics g = page.Graphics;
            g.SetTransparency(watermarkTransparancy);
            g.DrawPdfTemplate(watermarkTemplate, wmPosition);
            g.SetTransparency(1f);
            g.DrawPdfTemplate(pageTemplate, PointF.Empty, page.Size);
        }
        else
        {
            PdfGraphics g = page.Graphics;
            g.SetTransparency(watermarkTransparancy);
            g.DrawPdfTemplate(watermarkTemplate, wmPosition);
        }
    }
    // ** 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())
    {
        sourceDocument.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.");
    }
  13. Click the Actions button, select Log to History List, click this message and enter File watermarked.

  14. Close the Workflow Designer.

  15. Add a new PDF file to your library to trigger the workflow and apply the watermark. Make sure the content for the Watermark field is specified when the file is added as otherwise a generic watermark text will be inserted.

    WaterMarkScript

Naturally this is just a simple example. Feel free to play around with the code, change which parameters are passed into the workflow, modify where watermarked files are written to or use a different source for the watermark text. Leave a comment below if you are trying to do anything specific.

Update: If you get the message ‘no watermark specified’ then the workflow may have been triggered before you specified the contents of the Watermark field. For details see SharePoint Designer workflows trigger before properties are set .

.

Author
Clavin Fernandes Developer Relations and Support Services

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.

Explore related topics

Share post
Free trial Ready to get started?
Free trial