PDF Converter Services 3.5 – Adds watermarking and pluggable converters

Posted at: 10:23 on 28 June 2010 by Muhimbi


It is that time of the month again where we release new versions of our server based PDF Conversion products. Today’s release of the Muhimbi PDF Converter Services adds native support for the much requested Watermarking functionality as well as the ability to add custom converters using a new plug-in architecture.

A quick introduction for those not familiar with the product: The Muhimbi PDF Converter Services is a server based SDK that allows software developers to convert typical Office files to PDF format using a robust, scalable but friendly Web Services interface from Java and .NET based solutions. It supports a large number of file types including MS-Office and ODF file formats and is used by some of the largest organisations in the world for mission critical document conversions. A separate SharePoint specific version is available as well.

For details about the new functionality as well as a breaking change see the following posts:

ClassDiagram-WatermarkingNew Web Services classes related to watermarking. Click to enlarge

The main changes in version 3.5 are as follows:

865 New – Add support for watermarks, headers & footers using the Web Services interface.
837 New – Add support for custom converters using a plug-in architecture.
878 Improved – Allow server side Exception details to be (optionally) passed to WS Clients .
883 Improved – Include watermarking in the Java based sample code.
874 Improved – Include watermarking in the .NET based sample code.
876 Improved – Update documentation with details about watermarking and custom converters.

For more information check out the following resources:

As always, feel free to contact us using Twitter, our Blog, regular email or subscribe to our newsletter.


Download your free trial here (5MB).


Labels: , , ,

Breaking changes in the PDF Converter 3.5 Web Services interface

Posted at: 17:23 on 15 June 2010 by Muhimbi

Oh no! We broke somethingIn preparation of the upcoming 3.5 release of the Muhimbi PDF Converter Services we are making sure that all required documentation is readily available on our website (See previous posts on watermarking and custom converters).

Unfortunately, today I am documenting something I had hoped I would never need to document, a breaking change in our Web Services interface that may require our existing customers to refresh their web service proxies. Fortunately the change is isolated to a little used configuration interface, the main Convert method will continue to work fine….. pfew.



Our range of PDF Conversion products all share the same core engine, which runs as a Windows Service and can be accessed via an industry standard Web Services interface. However, this core engine originally started out as part of a SharePoint based product with a fixed number of hardcoded converters. The SharePoint user interface retrieves the list of converters via the GetConfiguration Web Service call and locally stores some settings against each converter. The same user interface uses the GetDiagnostics method on one or more converters to see if everything has been installed and configured correctly.

ClassDiagram-ConfigurationConfiguration / Diagnostics related classes


The change

Both the GetConfiguration and GetDiagnostics methods return objects that used to contain a ConverterType property, which holds an element of the following enumeration:

    private enum ConverterType

As it is now possible to add custom converters to the conversion engine, we can no longer rely on a fixed list of converters. As a result we have dropped the ConverterType property in favour of the new ConverterName property, which  holds a string that identifies the converter. This identifier is identical to the key field used by the config file to describe the converter.

In addition to this change we have also dropped the ConversionFidelity property from the DiagnosticRequestItem. This value is now read from the config file for the specified ConverterName.


We are fairly confident that very few people are using these specific interfaces. However, if you are and you need further information or assistance then please let us know.


Labels: , , , , ,

Using the PDF Watermarking features from Java based environments

Posted at: 11:20 on 11 June 2010 by Muhimbi

Java-Logo As an ever increasing number of customers are using our PDF Conversion Services from Java based environments we have decided to make our sample code and tutorials available in Java in addition to C# / .NET. Have a look at this post for an introduction to using the PDF Conversion Web Services from Java based environments.

The C# / .net version of this post ‘Using the awesome new watermarking features of the Muhimbi PDF Converter API and Server Platform’ provides a good introduction to the watermarking capabilities of our PDF Conversion products. I recommend reading it, even if you know nothing about C#, as the fundamental principles and object model are exactly the same.

The Muhimbi Document Conversion Service contains a very flexible system for applying watermarks to documents. Multiple watermarks can be applied to the same page and watermarks can be applied to page ranges or certain page types such as odd,even, portrait or landscape. In addition you can add field codes such as {PAGE} and {NUMPAGES} to dynamically add page numbers, headers and footers to PDF documents.

Watermarks are passed as part of the ConversionSettings object, a parameter of the Convert method. An overview of the watermarking related Web Services interface is provided below. For full details see the full User & Developer Guide.


The following Java based sample code is identical to a previous sample, with the exception that the Watermarks property in the ConversionSettings class is populated with a simple watermark that prints the word ‘Confidential’ on the front page in combination with the current date. Note that this sample code is automatically installed with the conversion service.

For details on how to setup your Java environment and generate the Web Service proxies see this post.

package com.muhimbi.app;

import com.muhimbi.ws.*;
import java.io.*;
import java.net.URL;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;

public class WsClient {


  public static void main(String[] args) {
    try {
      if (args.length != 1) {
        System.out.println("Please specify a single file name on the command line."); 
      } else {
        // ** Process command line parameters
        String sourceDocumentPath = args[0];
        File file = new File(sourceDocumentPath);
        String fileName = getFileName(file);
        String fileExt = getFileExtension(file);

        System.out.println("Converting file " + sourceDocumentPath);

        // ** Initialise Web Service
        DocumentConverterService_Service dcss = new DocumentConverterService_Service(
            new QName("http://tempuri.org/", "DocumentConverterService"));
        DocumentConverterService dcs = dcss.getBasicHttpBindingDocumentConverterService();

        // ** Only call conversion if file extension is supported
        if (isFileExtensionSupported(fileExt, dcs)) {
          // ** Read source file from disk
          byte[] fileContent = readFile(sourceDocumentPath);

          // ** Converting the file
          OpenOptions openOptions = getOpenOptions(fileName, fileExt);
          ConversionSettings conversionSettings = getConversionSettings();
          byte[] convertedFile = dcs.convert(fileContent, openOptions, conversionSettings);

          // ** Writing converted file to file system
          String destinationDocumentPath = getPDFDocumentPath(file);
          writeFile(convertedFile, destinationDocumentPath);
          System.out.println("File converted sucessfully to " + destinationDocumentPath);

        } else {
          System.out.println("The file extension is not supported.");

    } catch (IOException e) {
    } catch (DocumentConverterServiceGetConfigurationWebServiceFaultExceptionFaultFaultMessage e) {
    } catch (DocumentConverterServiceConvertWebServiceFaultExceptionFaultFaultMessage e) {

  public static OpenOptions getOpenOptions(String fileName, String fileExtension) {
    ObjectFactory objectFactory = new ObjectFactory();
    OpenOptions openOptions = new OpenOptions();
    // ** Set the minimum required open options. Additional options are available
    return openOptions;

  public static ConversionSettings getConversionSettings() {
    ConversionSettings conversionSettings = new ConversionSettings();
    // ** Set the minimum required conversion settings. Additional settings are available
    return conversionSettings;

  public static JAXBElement<ArrayOfWatermark> getWatermarks()
    ObjectFactory objectFactory = new ObjectFactory();
    ArrayOfWatermark watermarks = new ArrayOfWatermark();

    // ** Specify some of the default settings for properties
    Defaults wmDefaults = new Defaults();

    // ** 'Confidential' watermark for front page
    Watermark confidential = new Watermark();

    // ** Create a new Text element that goes inside the watermark
    Text cfText = new Text();
    cfText.setContent(objectFactory.createTextContent("Confidential - {DATE}"));

    // ** And add it to the list of watermark elements.
    ArrayOfElement cfElements = new ArrayOfElement();

    // ** And add the watermark to the list of watermarks

    return objectFactory.createConversionSettingsWatermarks(watermarks);
  public static String getFileName(File file) {
    String fileName = file.getName();
    return fileName.substring(0, fileName.lastIndexOf('.'));

  public static String getFileExtension(File file) {
    String fileName = file.getName();
    return fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length());

  public static String getPDFDocumentPath(File file) {
    String fileName = getFileName(file);
    String folder = file.getParent();
    if (folder == null) {
      folder = new File(file.getAbsolutePath()).getParent();
    return folder + File.separatorChar + fileName + '.' + OutputFormat.PDF.value();

  public static byte[] readFile(String filepath) throws IOException {
    File file = new File(filepath);
    InputStream is = new FileInputStream(file);
    long length = file.length();
    byte[] bytes = new byte[(int) length];

    int offset = 0;
    int numRead;
    while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
      offset += numRead;

    if (offset < bytes.length) {
      throw new IOException("Could not completely read file " + file.getName());
    return bytes;

  public static void writeFile(byte[] fileContent, String filepath) throws IOException {
    OutputStream os = new FileOutputStream(filepath);

  public static boolean isFileExtensionSupported(String extension, DocumentConverterService dcs)
    throws DocumentConverterServiceGetConfigurationWebServiceFaultExceptionFaultFaultMessage
      Configuration configuration = dcs.getConfiguration();
      final JAXBElement<ArrayOfConverterConfiguration> converters = configuration.getConverters();
      final ArrayOfConverterConfiguration ofConverterConfiguration = converters.getValue();
      final List<ConverterConfiguration> cList = ofConverterConfiguration.getConverterConfiguration();
      for (ConverterConfiguration cc : cList) {
        final List<String> supportedExtension = cc.getSupportedFileExtensions().getValue().getString();
        if (supportedExtension.contains(extension)) {
          return true;

    return false;

  public static void printException(WebServiceFaultException serviceFaultException) {
    JAXBElement<ArrayOfstring> element = serviceFaultException.getExceptionDetails();
    ArrayOfstring value = element.getValue();
    for (String msg : value.getString()) {


Labels: , , , , ,

Using the awesome new watermarking features of the Muhimbi PDF Converter Services

Posted at: 14:54 on 09 June 2010 by Muhimbi

confidential As I mentioned in a previous post about adding your own converters using our fancy new plug-in architecture, we prioritise new functionality based on the number of customers requesting a particular feature. The feature I will be discussing today is one that has been on our radar for a long time, Watermarking.

We have previously provided some sample code to watermark documents using our Workflow Power Pack and although that solution is working well for many of our customers, it was only an interim solution that doesn’t come close to providing the functionality in our new watermarking engine, which is totally awesome

Note that at this moment we have only added native support for watermarking to the back-end conversion engine. Although it can be accessed using the Workflow Power Pack, we have not yet released a dedicated Workflow Action to control watermarking from SharePoint, which will be released in the next version.

Right, so back to the topic at hand. Listed below is an overview of our new Watermarking architecture as well as some sample code. More information and a full description of the object model can be found in the PDF Converter API and Server Platform’s comprehensive User & Developer Guide. Note that you need version 3.5 or newer to make use of the new watermarking functionality.


The Muhimbi Document Conversion Service contains a very flexible system for applying watermarks to documents. Multiple watermarks can be applied to the same page and watermarks can be applied to page ranges or certain page types such as odd, even, portrait or landscape.

Watermarks are passed as part of the ConversionSettings object, a parameter of the Convert method. An overview of the watermarking related Web Services interface is provided below. For full details see the full User & Developer Guide.



Individual Element Types 

As all individual elements / shapes inherit from the Element class, they largely share the same properties.

The supported element types are as follows.

  • Line: Represents a single line. 
  • Rectangle: Represents a rectangle..
  • Ellipse: Represents an ellipse.
  • Rtf: Represents a piece of text encoded in RTF format.
  • Image: Represents an image. The following image types are supported: BMP, JPG, GIF, PNG, TIFF, WMF, EMF and EMF+.
  • Pdf: Represents an existing PDF file that is used as the watermark.
  • Text: Represents a text box that allows plain text to be specified with full control over horizontal and vertical alignment, font face and size as well as word wrapping. This field also allows field codes such as page number to be embedded (see below).


Embedding merge fields in the Text element

The Text element allows merge codes to be embedded such as the number of pages or the current date. This makes it very simple to use watermarks to automatically generate headers and footers on each page, while taking orientation and page interval (Odd / Even pages) into account.

The following merge fields are available for use:

  • {LONG_DATE}: The long representation of the current date.
  • {LONG_TIME}: The long representation of the current time.
  • {DATE}: The short representation of the current date.
  • {TIME}: The short representation of the current time.
  • {PAGE}: The number of the current page in the PDF file.
  • {NUMPAGES}: The total number of pages in the PDF file.

Date and time fields are formatted using the locale used by the account the Document Conversion Service is running under. More details about merge fields is available here.


Sample Code

As described previously, the PDF Conversion Service contains a powerful watermarking engine that can be used to add visible and invisible watermarks to pages as well as adding headers, footers and other recurring items.

The following C# example (Java sample available here) shows how to decorate a document with the following watermarks:

  1. Add the word ‘Confidential’ in the background of the cover page.
  2. Add page numbers in the right-hand side of the footer as well as a horizontal line to all even pages.
  3. Add page numbers in the left-hand side of the footer as well as a horizontal line to all odd pages.




The sample code expects the path of the PDF file on the command line. If the path is omitted then the first MS-Word file found in the current directory will be used.

Follow the steps described below to create the sample watermarking application. Please note that this sample code is also available via the Windows Start Menu.

  1. Create a new Visual Studio C# Console application named Watermarking.
  2. Add a Service Reference to the following URL and specify ConversionService as the namespace

  3. Paste the following code into Program.cs
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.ServiceModel;
    using Watermarking.ConversionService;
    namespace Watermarking
        class Program
         // ** The URL where the Web Service is located. Amend host name if needed.
         static string SERVICE_URL = "http://localhost:41734/Muhimbi.DocumentConverter.WebService/";
            static void Main(string[] args)
                DocumentConverterServiceClient client = null;
                    // ** Determine the source file and read it into a byte array.
                    string sourceFileName = null;
                    if (args.Length == 0)
                        string[] sourceFiles = Directory.GetFiles(
    Directory.GetCurrentDirectory(), "*.doc");
                        if (sourceFiles.Length > 0)
                            sourceFileName = sourceFiles[0];
                           Console.WriteLine("Please specify a document to convert and watermark.");
                        sourceFileName = args[0];
                    byte[] sourceFile = File.ReadAllBytes(sourceFileName);
                    // ** Open the service and configure the bindings
                    client = OpenService(SERVICE_URL);
                    //** Set the absolute minimum open options
                    OpenOptions openOptions = new OpenOptions();
                    openOptions.OriginalFileName = Path.GetFileName(sourceFileName);
                    openOptions.FileExtension = Path.GetExtension(sourceFileName);
                    // ** Set the absolute minimum conversion settings.
                    ConversionSettings conversionSettings = new ConversionSettings();
                    conversionSettings.Fidelity = ConversionFidelities.Full;
                    conversionSettings.Quality = ConversionQuality.OptimizeForPrint;
                    // ** Get the list of watermarks to apply.
                    conversionSettings.Watermarks = CreateWatermarks();
                    // ** Carry out the conversion.
                    Console.WriteLine("Converting file " + sourceFileName);
                    byte[] convFile = client.Convert(sourceFile, openOptions, conversionSettings);
                    // ** Write the converted file back to the file system with a PDF extension.
                    string destinationFileName = Path.GetDirectoryName(sourceFileName) + @"\" +
                                                 Path.GetFileNameWithoutExtension(sourceFileName) +
                                                 "." + conversionSettings.Format;
                    using (FileStream fs = File.Create(destinationFileName))
                        fs.Write(convFile, 0, convFile.Length);
                    Console.WriteLine("File converted to " + destinationFileName);
                    // ** Open the generated PDF file in a PDF Reader
                catch (FaultException<WebServiceFaultException> ex)
                    Console.WriteLine("FaultException occurred: ExceptionType: " +
                catch (Exception ex)
            /// <summary>
            /// Create the watermarks.
            /// </summary>
            /// <returns>An array of watermarks to apply</returns>
            public static Watermark[] CreateWatermarks()
                List<Watermark> watermarks = new List<Watermark>();
                // ** Specify the default settings for properties
                Defaults wmDefaults = new Defaults();
                wmDefaults.FillColor = "#000000";
                wmDefaults.LineColor = "#000000";
                wmDefaults.FontFamilyName = "Arial";
                wmDefaults.FontSize = "10";
                // **************** 'Confidential' Text ***************
                // ** 'Confidential' watermark for front page
                Watermark confidential = new Watermark();
                confidential.Defaults = wmDefaults;
                confidential.StartPage = 1;
                confidential.EndPage = 1;
                confidential.Rotation = "-45";
                confidential.Width = "500";
                confidential.Height = "500";
                confidential.HPosition = HPosition.Center;
                confidential.VPosition = VPosition.Middle;
                confidential.ZOrder = -1;
                // ** Create a new Text element that goes inside the watermark
                Text cfText = new Text();
                cfText.Content = "Confidential";
                cfText.FontSize = "40";
                cfText.FontStyle = FontStyle.Bold | FontStyle.Italic;
                cfText.Width = "500";
                cfText.Height = "500";
                cfText.Transparency = "0.10";
                // ** And add it to the list of watermark elements.
                confidential.Elements = new Element[] { cfText };
                // ** And add the watermark to the list of watermarks
                // **************** Watermark for Odd pages ***************
                Watermark oddPages = new Watermark();
                oddPages.Defaults = wmDefaults;
                oddPages.StartPage = 3;
                oddPages.PageInterval = 2;
                oddPages.Width = "600";
                oddPages.Height = "50";
                oddPages.HPosition = HPosition.Right;
                oddPages.VPosition = VPosition.Bottom;
                // ** Add a horizontal line
                Line line = new Line();
                line.X = "1";
                line.Y = "1";
                line.EndX = "600";
                line.EndY = "1";
                line.Width = "5";
                // ** Add a page counter
                Text oddText = new Text();
                oddText.Content = "Page: {PAGE} of {NUMPAGES}";
                oddText.Width = "100";
                oddText.Height = "20";
                oddText.X = "475";
                oddText.Y = "15";
                oddText.LineWidth = "-1";
                oddText.FontStyle = FontStyle.Regular;
                oddText.HAlign = HAlign.Right;
                // ** And add it to the list of watermark elements
                oddPages.Elements = new Element[] { line, oddText };
                // ** And add the watermark to the list of watermarks
                // **************** Watermark for Even pages ***************
                Watermark evenPages = new Watermark();
                evenPages.Defaults = wmDefaults;
                evenPages.StartPage = 2;
                evenPages.PageInterval = 2;
                evenPages.Width = "600";
                evenPages.Height = "50";
                evenPages.HPosition = HPosition.Left;
                evenPages.VPosition = VPosition.Bottom;
                // ** No need to create an additional line,re-use the previous one
                // ** Add a page counter
                Text evenText = new Text();
                evenText.Content = "Page: {PAGE} of {NUMPAGES}";
                evenText.Width = "100";
                evenText.Height = "20";
                evenText.X = "25";
                evenText.Y = "15";
                evenText.LineWidth = "-1";
                evenText.FontStyle = FontStyle.Regular;
                evenText.HAlign = HAlign.Left;
                // ** And add it to the list of watermark elements
                evenPages.Elements = new Element[] { line, evenText };
                // ** And add the watermark to the list of watermarks
                return watermarks.ToArray();
            /// <summary>
            /// Configure the Bindings, endpoints and open the service using the specified address.
            /// </summary>
            /// <returns>An instance of the Web Service.</returns>
            public static DocumentConverterServiceClient OpenService(string address)
                DocumentConverterServiceClient client = null;
                    BasicHttpBinding binding = new BasicHttpBinding();
                    // ** Use standard Windows Security.
                    binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
                    binding.Security.Transport.ClientCredentialType =
                    // ** Increase the client Timeout to deal with (very) long running requests.
                    binding.SendTimeout = TimeSpan.FromMinutes(30);
                    binding.ReceiveTimeout = TimeSpan.FromMinutes(30);
                    // ** Set the maximum document size to 50MB
                    binding.MaxReceivedMessageSize = 50 * 1024 * 1024;
                    binding.ReaderQuotas.MaxArrayLength = 50 * 1024 * 1024;
                    binding.ReaderQuotas.MaxStringContentLength = 50 * 1024 * 1024;
                    // ** Specify an identity (any identity) in order to get it past .net3.5 sp1
                    EndpointIdentity epi = EndpointIdentity.CreateUpnIdentity("unknown");
                    EndpointAddress epa = new EndpointAddress(new Uri(address), epi);
                    client = new DocumentConverterServiceClient(binding, epa);
                    return client;
                catch (Exception)
            /// <summary>
            /// Check if the client is open and then close it.
            /// </summary>
            /// <param name="client">The client to close</param>
            public static void CloseService(DocumentConverterServiceClient client)
                if (client != null && client.State == CommunicationState.Opened)
  4. Make sure the output folder contains an MS-Word file.
  5. Compile and execute the application.


As all this functionality is exposed via a Web Services interface, it works equally well from Java and other web services enabled environments.

This code is merely an example of what is possible, feel free to adapt it to you own needs, add more complex watermarks or your company logo to each page. The possibilities are endless.


Labels: , , , , ,

Subscribe to News feed