Creating PDFs from Word and Excel Documents on IIS. For Free.

pdf.pngConverting a Microsoft Word or Excel file to a PDF is usually an expensive task. Commercial Services exist, but there are no free options. As well, you may not be able to use a service as your documents may be confidential and can’t be sent over national borders.

There is a simple solution that is free, secure and easy. Really.

Note: You will need full control over your IIS server to use this toolset

Summary: How we do it

Your web site will save the Word or Excel file onto the server’s file system. A Windows Service will watch that folder then when a document appears will send it off to the open source Libre Office and have it create the PDF via a headless call. Your web site will then pick up the PDF from the same folder and send it along.

Let’s do it!


Step 1: IIS Server Setup. You’ll need total control of the server.

  1. Create a file folder on the server that your web site’s IIS Application pool has read-write access to. For example c:\FileDrop
  2. Install LibreOffice on the server. Default settings.

Step 2: Web Site

Your web site will need to deposit the Word or Excel file into c:\FileDrop. You’ll need to handle that code, but it’s fairly simple. If you need help, Google it.


Step 3: The Windows Service

Create a new Console App. This will be your Windows Service.

Change Program.cs to this:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.ServiceProcess;
 
namespace PdfService
{
    internal class Program : ServiceBase
    {
        private static void Main(string[] args)
        {
            ServiceBase.Run(new Program());
        }
 
        public Program()
        {
            this.ServiceName = "DataSystem PDF";
        }
 
        private FileSystemWatcher _watch;
 
        protected override void OnStart(string[] args)
        {
            base.OnStart(args);
            try
            {
                var path = System.Configuration.ConfigurationManager.AppSettings["PdfPath"];
                
                Trace.WriteLine("PDF: Using Path=" + path);
                if (!Directory.Exists(path))
                {
                    Trace.WriteLine("PDF: Creating " + path);
                    Directory.CreateDirectory(path);
                }
 
                _watch = new FileSystemWatcher(System.Configuration.ConfigurationManager.AppSettings["PdfPath"]);
                Trace.WriteLine("PDF: Started File Watcher");
                _watch.Created += watch_Created;
                _watch.EnableRaisingEvents = true;
 
            }
            catch (Exception e)
            {
                Trace.WriteLine("PDF: Error " + e.Message); 
            }
        }
 
        private void watch_Created(object sender, FileSystemEventArgs e)
        {
            Trace.WriteLine("PDF: WATCH: " + e.FullPath);
            if (Path.GetExtension(e.FullPath) == ".xlsx" || Path.GetExtension(e.FullPath) == ".docx")
            {
 
                Trace.WriteLine("PDF: Processing " + e.FullPath);
                string exePdf = System.Configuration.ConfigurationManager.AppSettings["PdfExe"];
                string param = System.Configuration.ConfigurationManager.AppSettings["PdfParameters"];
 
                var pdfProcess = new Process();
                pdfProcess.StartInfo.FileName = exePdf;
                pdfProcess.StartInfo.Arguments = param + \"" + e.FullPath + "\"";
                pdfProcess.StartInfo.WorkingDirectory = Path.GetDirectoryName(e.FullPath); //!!!!This is really important!!!!!
                pdfProcess.Start();
                pdfProcess.WaitForExit(25000);
                if (!pdfProcess.HasExited)
                    pdfProcess.Kill();
            }
        }
 
        protected override void OnStop()
        {
            base.OnStop();
 
            _watch = null;
        }
    }
}

Add a new class called PDFServiceInstaller.cs and change the code to this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration.Install;
using System.ComponentModel;
using System.ServiceProcess;
 
namespace PdfService
{
  [RunInstaller(true)]
  public class PDFServiceInstaller : Installer
  {
      private ServiceInstaller serviceInstaller;
 
      public PDFServiceInstaller()
    {
      var processInstaller = new ServiceProcessInstaller();
        serviceInstaller = new ServiceInstaller();
 
      //set the privileges
      processInstaller.Account = ServiceAccount.LocalSystem;
 
      serviceInstaller.DisplayName = "DataSystem PDF";
      serviceInstaller.StartType = ServiceStartMode.Automatic;
 
      //must be the same as what was set in Program's constructor
      serviceInstaller.ServiceName = "DataSystem PDF";
 
      this.AfterInstall += new InstallEventHandler(ServiceInstaller_AfterInstall);
 
      this.Installers.Add(processInstaller);
      this.Installers.Add(serviceInstaller);
    }
 
      void ServiceInstaller_AfterInstall(object sender, InstallEventArgs e)
      {
          using (ServiceController sc = new ServiceController(serviceInstaller.ServiceName))
          {
              sc.Start();
          }
      }
  }
}

Edit the App.Config and change it to this. Make any changes to paths you need.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="PdfExe" value="C:\Program Files (x86)\LibreOffice 6\program\soffice.exe"/>
    <add key="PdfParameters" value=" -norestore -nofirststartwizard -nologo -headless -convert-to pdf "/>
    <add key="PdfPath" value="c:\FileDrop"/>
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
  </startup>
</configuration>

Step 4: Installing the Service

Compile the PdfService and place it on your IIS server, in our example c:\PdfService. Here’s how you install it.

  1. Run the command prompt as ADMIN
  2. cd to -> C:\Windows\Microsoft.NET\Framework64\v4.0.30319
  3. Run InstallUtil.exe C:\PdfService\PdfService.exe

To uninstall
InstallUtil.exe -u C:\PdfService\PDFService.exe

To watch DEBUG info, Run DebugViewer from Sys Internals and enable Global Win32 watching in DebugViewer


Step 5: Start the service via the management tool Services.msc. Simple right click “DataSystem PDF”


Conclusion

Now that the service is running, you can test it by just dropping a Word or Excel file into the folder. A PDF should appear in 10-20 seconds depending on size and your server’s power.

 

 

Advertisements