Converting PowerPoint Presentations (PPT/PPTX) To PDF Using ASP.Net Core with Office Interop C#

In this article, I am going to share you one of my biggest experiences on converting PPT/PPTX to PDF file.

There are multiple approaches to convert a PPT/PPTX to a PDF file, as listed below.
  1. Using Office Automation objects
  2. Using LibreOffice
  3. By making use of Aspose
  4. Using Spire.Presentation
  5. With the help of OpenOffice
  6. Using SharePoint PowerPoint Services
  7. Using ASP.NET Core Web API based solution
Using Office Automation objects

Here, I have used Microsoft.Office.Core and Microsoft.Office.Interop.PowerPoint libraries to convert PPT/PPTX file to PDF file. By using these libraries, I am able to convert PPT/PPTX file to PDF file when I run on IIS Express. If I host the same code snippet on IIS Server, it is throwing an error as below.

C#

The following official article from Microsoft summarizes issues with using Office Automation objects on Server-side (unattended execution, parallel execution, etc.).

https://support.microsoft.com/en-in/help/257757/considerations-for-server-side-automation-of-office

Please go through the below link; Microsoft has clearly specified as below:

"Microsoft does not currently recommend, and does not support, Automation of Microsoft Office applications from any unattended, non-interactive client application or component (including ASP, ASP.NET, DCOM, and NT Services), because Office may exhibit unstable behavior and/or deadlock when Office is run in this environment."

But still, I wanted to try with a few other techniques.

Technique 1

By setting the MIME type in IIS Server with below steps.

Step 1

Press Windows key + R.

Step 2

Type "inetmgr" and press Enter key.

Step 3

Click on "MIME Types".

Step 4

Click on "Add" button and set the below MIME types,

C#

Outcome

No luck :(

Technique 2

I have configured the same MIME types in web.config file as below.

C#

Outcome

No luck :(

Technique 3

I thought this might be a COM permissions problem. I tried the below steps -

Step 1

Start > Run > dcomcnfg and then add the Component Services snap-in manually if you can’t find the app under step 3)

Step 2

Navigate to Component Services > Computers > My Computer > DCOM Config

Step 3

Locate the MS application giving you trouble (eg: “Microsoft Excel Application” for Excel or “Microsoft Word 97 – 2003 Document” for Word)

Step 4

Right click > Properties.

Step 5

In the Security tab, select Customize under Launch and Activation Permissions and click Edit…

Step 6

Add the account under which the site is running (eg: Network Service) and assign Local Launch & Local Activation permissions.

Outcome

No luck :(

Technique 4

I set web application permission in Internet Information Services (IIS) with full permission for the below users.

  • CREATOR OWNER
  • SYSTEM
  • Users
  • Administrators
  • IUSR
  • IIS_IUSRS

Outcome

No luck :(

Technique 5

I have created a Windows Service for the same code snippet and self hosting at "services.msc". See the below screenshot for reference.

C#

Outcome

No luck :(

In the same way, I have tried with some other techniques but none of them worked.

Using LibreOffice

LibreOffice is a free and an open source library. I have tried with this library and experienced a few rendering issues as below.

  • While converting WordArts especially 3D Text.
  • And for some kind of background graphics and animations.
Using Aspose

First of all, make a note, Aspose is a paid one. I have tried with trial version and found few observations as below.

There are rendering issues

  • While converting WordArt especially 3D Text.
  • For some kind of background graphics and animations.

May be the rendering issues happen due to trial version but I am not sure.

Using Presentation

This library also is a paid one. I have tried with this library and experienced the same issues which I faced with Aspose library. Maybe the rendering issues are due to the trial version but I am not sure.

Using OpenOffice

OpenOffice is a free and open source library. I have tried with this library and experienced with the same issues which I faced with LibreOffice library.

Using SharePoint PowerPoint Services

SharePoint cost could be much higher than just going for other direct commercial libraries (like Aspose, etc.,). And if you buy a SharePoint server, then it costs around 8 to 10 lakhs. There is a SharePoint Server 2013 available on AWS with 30 days free trial as well. And it costs USD $3.5 per hour. For more details, have a look at link (https://aws.amazon.com/marketplace/pp/B01BXJ8FSK).

We don't know whether the sample code snippet works as expected or not in SharePoint Server. It requires that the original PPT/PPTX document is stored on SharePoint server and the resultant PDF is also stored on SharePoint server. And there are two ways of achieve converting PPT/PPTX to PDF file, one by creating a program that runs on SharePoint server box, other using a Web Services API of SharePoint, where the program can run on box other than SharePoint. And we do not know which one works and which one is the best solution.

Note

Since we are not sure with the sample code snippet whether that will work or not on SharePoint Server, that is why it is very difficult to convince the Client and it is always better to go with some other alternatives.

Using .Net Core Web API based solution

Finally, I was able to achieve my requirement (converting PPT/PPTX file to PDF file) using ASP.NET Core with Office Automation objects as a standalone application.

ASP.NET Core Web API based solution, where the Web API server will run in interactive user mode, will accept HTTP calls for conversion. This will in turn use MS Office automation objects.

 Now, it's time to show you step by step in practical.

Step 1 Create a ASP.Net Core application 

  1. Click on "New Project".
  2. Expand Templates and select Web.
  3. Since we are going to create, select "ASP.Net Core Web Application (.NET Core)" template.
  4. Give the project name own your wish, let say "WebAppCoreConverter", then click on OK button.

    C#

Step 2 Select the project template

Here, I have selected "Web API" template because I want to create as a service method and will expose to outside world, so that everyone can be able to consume my created service method irrespective of programming languages (.NET, JAVA, AS400, SharePoint etc.)

C#

Step 3 Create business components in the project

Create a folder and name it, as per your wish. I have named it as "DocumentConverter". In this folder, we are going to create the business components where the actual logic resides. We are going to write a code in such a way that, it can be easily Extendable and Maintainable. To understand the code structure, you should be good in minimum basic Design Patterns and SOLID principles. Here, I am not going to discuss on Design Patterns and SOLID principles (if you don't know, then you can search online and learn) because our focus in this article is different.

I am going to create an interface (IConverter.cs) with method with 3 input parameters.

  • Source location
  • Target location
  • Input file type

Here, the intention is, the method should support for multiple input file types (PPT, PPTX, Word, Excel etc.) into PDF file based on the supplied source and destination file locations.

IConverter.cs

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5.   
  6. namespace WebAppCoreConverter.DocumentConverter  
  7. {  
  8.     public interface IConverter  
  9.     {  
  10.         short convert(String sourcePath, String targetPath, ContentType sourceType);                
  11.     }  
  12.     public interface IDocumentConverterFactory  
  13.     {  
  14.          IConverter getConverter(ConversionType convType);  
  15.     }  
  16. }  

I am going to create a utility class (common.cs) where I am going to place the commonly used

  • Methods
  • Enumerations etc.

common.cs

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.IO;  
  6.   
  7. namespace WebAppCoreConverter.DocumentConverter  
  8. {  
  9.   
  10.     public enum ContentType  
  11.     {  
  12.         DOC,  
  13.         DOCX,  
  14.         XLS,  
  15.         XLSX,  
  16.         VSD,  
  17.         VDX,  
  18.         PPT,  
  19.         PPTX,  
  20.         XDW,  
  21.         PDF,  
  22.         XPS,  
  23.         JPEG,  
  24.         JPG,  
  25.         BMP,  
  26.         PNG,  
  27.         TIF,  
  28.         TIFF,  
  29.         GIF,  
  30.         SVG,  
  31.         TXT,  
  32.         RTF,  
  33.         XML,  
  34.         CSV,  
  35.         UNKNOWN = -1  
  36.     }  
  37.     public class Util  
  38.     {  
  39.         public static void releaseObject(object obj)  
  40.         {  
  41.             Console.WriteLine("MethodName: releaseObject of Class: Util started");  
  42.             try  
  43.             {  
  44.                 System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);  
  45.                 obj = null;  
  46.             }  
  47.   
  48.             catch (Exception exReleaseObject)  
  49.             {  
  50.                 obj = null;  
  51.                 //   Console.WriteLine(CMSResourceFile.REALESE_FAILED+ exReleaseObject);  
  52.   
  53.             }  
  54.             finally  
  55.             {  
  56.                 GC.Collect();  
  57.                 GC.WaitForPendingFinalizers();  
  58.                 GC.Collect();  
  59.                 GC.WaitForPendingFinalizers();  
  60.             }  
  61.             Console.WriteLine("MethodName: releaseObject of Class: Util ended");  
  62.         }  
  63.         public static string getTargetFileName(string contentPath, string contentName, ContentType contentType)  
  64.         {  
  65.             string getTargetFileNameStatus = string.Empty;  
  66.   
  67.             try  
  68.             {  
  69.                 string tempFileName = Path.GetFileNameWithoutExtension(contentName) + ".";  
  70.                 tempFileName += contentType.ToString().ToLower();  
  71.                 getTargetFileNameStatus =  Path.Combine(contentPath, tempFileName);  
  72.             }  
  73.             catch (Exception exGetTargetFileName)  
  74.             {  
  75.             }  
  76.             return getTargetFileNameStatus;  
  77.         }  
  78.         public static short getFileExtension(string sourcePath, out ContentType fileExtn)  
  79.         {  
  80.             short getFileExtnStatus = 0;  
  81.             fileExtn = ContentType.UNKNOWN;  
  82.             try  
  83.             {  
  84.                 string tempFileExtn = Path.GetExtension(sourcePath);  
  85.   
  86.                 tempFileExtn = tempFileExtn.Replace(".", string.Empty);  
  87.                 fileExtn = (ContentType)Enum.Parse(typeof(ContentType), tempFileExtn, true);  
  88.             }  
  89.             catch (Exception exGetFileExtension)  
  90.             {  
  91.             } return getFileExtnStatus;  
  92.         }  
  93.     }  
  94.     public enum ConversionType  
  95.     {  
  96.         Doc2Pdf,  
  97.         Pdf2Img,  
  98.         Img2Img  
  99.     }  
  100. }  

I am going to create a factory class (DocumentConverterFactory.cs) which decides the conversion type.

DocumentConverterFactory.cs

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5.   
  6. namespace WebAppCoreConverter.DocumentConverter  
  7. {  
  8.     public class DocumentConverterFactory : IDocumentConverterFactory  
  9.     {  
  10.         public IConverter getConverter(ConversionType convType)  
  11.         {  
  12.             IConverter converter = null;  
  13.             switch (convType)  
  14.             {  
  15.                 case ConversionType.Doc2Pdf:  
  16.                     converter = new ConvDoc2PdfWithMsOffice();  
  17.                     break;  
  18.                 case ConversionType.Pdf2Img:  
  19.                 case ConversionType.Img2Img:  
  20.                 default:  
  21.                     break;  
  22.             }  
  23.             return converter;  
  24.         }  
  25.     }  
  26. }  

I am going to create a concrete class which inherits from "IConverter" interface to define the actual business logic.

ConvDoc2PdfWithMsOffice.cs

  1. using System;  
  2. using Microsoft.Office.Core;  
  3. using PowerPoint = Microsoft.Office.Interop.PowerPoint;  
  4. using System.Reflection;  
  5.   
  6. namespace WebAppCoreConverter.DocumentConverter  
  7. {  
  8.     public class ConvDoc2PdfWithMsOffice : IConverter  
  9.     {  
  10.         /// <summary>  
  11.         /// Convert MSOffice file to PDF by calling required method  
  12.         /// </summary>  
  13.         /// <param name="sourcePath">MSOffice file path</param>  
  14.         /// <param name="targetPath">Target PDF path</param>  
  15.         /// <param name="sourceType">MSOffice file type</param>  
  16.         /// <returns>error code : 0(sucess)/ -1 or errorcode (unknown error or failure)</returns>  
  17.         public short convert(String sourcePath, String targetPath, ContentType sourceType)  
  18.         {  
  19.             Console.WriteLine("Class: " + GetType() + " Method: " + MethodBase.GetCurrentMethod().Name + " Started ");  
  20.             short convDoc2PdfWithMsOfficeResult = 0;  
  21.             if (sourceType == ContentType.PPT || sourceType == ContentType.PPTX)  
  22.             {  
  23.                 convDoc2PdfWithMsOfficeResult = powerPoint2Pdf((Object)sourcePath, (Object)targetPath);  
  24.             }              
  25.             else convDoc2PdfWithMsOfficeResult = -1;  
  26.             Console.WriteLine("Class: " + GetType() + " Method: " + MethodBase.GetCurrentMethod().Name + " Ended ");  
  27.             return convDoc2PdfWithMsOfficeResult;  
  28.         }  
  29.         /// <summary>  
  30.         ///  Convert  powerPoint file to PDF by calling required method  
  31.         /// </summary>  
  32.         /// <param name="originalPptPath">file path</param>  
  33.         /// <param name="pdfPath">Target PDF path</param>  
  34.         /// <returns>error code : 0(sucess)/ -1 or errorcode (unknown error or failure)</returns>  
  35.         public short powerPoint2Pdf(object originalPptPath, object pdfPath)  
  36.         {  
  37.             Console.WriteLine("Class: " + GetType() + " Method: " + MethodBase.GetCurrentMethod().Name + " Started ");  
  38.             short convertPowerPoint2PdfResult = -1;  
  39.             PowerPoint.Application pptApplication = null;  
  40.             PowerPoint.Presentation pptPresentation = null;  
  41.             object unknownType = Type.Missing;  
  42.             try  
  43.             {  
  44.                 //start power point   
  45.                 pptApplication = new PowerPoint.Application();  
  46.   
  47.                 //open powerpoint document  
  48.                 pptPresentation = pptApplication.Presentations.Open((string)originalPptPath, MsoTriState.msoTrue, MsoTriState.msoTrue, MsoTriState.msoFalse);  
  49.   
  50.                 //export PDF from PPT  
  51.                 if (pptPresentation != null)  
  52.                 {  
  53.                     pptPresentation.ExportAsFixedFormat((string)pdfPath,  
  54.                                                          PowerPoint.PpFixedFormatType.ppFixedFormatTypePDF,  
  55.                                                          PowerPoint.PpFixedFormatIntent.ppFixedFormatIntentPrint,  
  56.                                                          MsoTriState.msoFalse,  
  57.                                                          PowerPoint.PpPrintHandoutOrder.ppPrintHandoutVerticalFirst,  
  58.                                                          PowerPoint.PpPrintOutputType.ppPrintOutputSlides,  
  59.                                                          MsoTriState.msoFalse, null,  
  60.                                                          PowerPoint.PpPrintRangeType.ppPrintAll, string.Empty,  
  61.                                                          truetruetruetruefalse, unknownType);  
  62.                     convertPowerPoint2PdfResult = 0;  
  63.                 }  
  64.                 else  
  65.                 {  
  66.                     Console.WriteLine("Error occured for conversion of office PowerPoint to PDF");  
  67.                     convertPowerPoint2PdfResult = 504;  
  68.                 }  
  69.             }  
  70.             catch (Exception exPowerPoint2Pdf)  
  71.             {  
  72.                 Console.WriteLine("Error occured for conversion of office PowerPoint to PDF, Exception: ", exPowerPoint2Pdf);  
  73.                 convertPowerPoint2PdfResult = 504;  
  74.             }  
  75.             finally  
  76.             {  
  77.                 // Close and release the Document object.  
  78.                 if (pptPresentation != null)  
  79.                 {  
  80.                     pptPresentation.Close();  
  81.                     Util.releaseObject(pptPresentation);  
  82.                     pptPresentation = null;  
  83.                 }  
  84.   
  85.                 // Quit Word and release the ApplicationClass object.  
  86.                 pptApplication.Quit();  
  87.                 Util.releaseObject(pptApplication);  
  88.                 pptApplication = null;  
  89.             }  
  90.             Console.WriteLine("Class: " + GetType() + " Method: " + MethodBase.GetCurrentMethod().Name + " Ended ");  
  91.             return convertPowerPoint2PdfResult;  
  92.         }  
  93.     }  
  94. }  

Step 4 Create a Web API controller

The Web API controller is to create the service method which calls the "convert" method internally by supplying the actual inputs (source location, target location and file type).

  1. using System;  
  2. using Microsoft.AspNetCore.Mvc;  
  3. using Microsoft.Office.Interop.PowerPoint;  
  4. using WebAppCoreConverter.DocumentConverter;  
  5. using Microsoft.AspNetCore.Authorization;  
  6.   
  7. namespace WebAppCoreConverter.Controllers  
  8. {  
  9.     [AllowAnonymous, Route("api/[controller]")]  
  10.     public class PPTXtoPDFController : Controller  
  11.     {  
  12.         [HttpGet, Route("GetConvertPPTXtoPDF")]  
  13.         public void GetConvertPPTXtoPDF()  
  14.         {  
  15.             Application ppApp = new Application();  
  16.             try  
  17.             {  
  18.                 IConverter converter = new ConvDoc2PdfWithMsOffice();  
  19.                 short conversionResult = converter.convert(@"E:\PPTX TO PDF ASP.Net Core\Output\ng2Intro.pptx", @"E:\PPTX TO PDF ASP.Net Core\Output\ng2Intro" + DateTime.Now.Ticks + ".pdf",  ContentType.PPTX);  
  20.             }  
  21.             catch (Exception e)  
  22.             {  
  23.                 //"";  
  24.             }  
  25.         }    
  26.     }  
  27. }  

Step 5

If you want, you can change the framework version then please go through the below link,

http://www.c-sharpcorner.com/blogs/configuring-asp-net-framework-version-and-customize-default-port-number-in-asp-net-core-application

Step 6

If you want to set the application settings or if you want to change the default host address in the ASP.Net Core application then please go through the below link,

http://www.c-sharpcorner.com/blogs/configuring-and-reading-application-settings-in-asp-net-core-application

Step 7

Build the solution.

C#

Step 8

Publish the application.

It will generate the .EXE file with same name as project name.

C#

C#

Step 9

Click on .EXE file; then the server should run on default port number.

C#

C#

Step 10

If the Server (above black screen) is not running on double clicking on .EXE file and you are receiving the below error, then:

The connection to 'localhost' failed. <br />Error: ConnectionRefused (0x274d). <br />System.Net.Sockets.SocketException No connection could be made because the target machine actively refused it xxx.xxx.x.xx:5000

If you are getting such an error, you might be replacing localhost with your system IP address or may be with other host address.

Ex -

In my case, I was getting the above mentioned error because for some reason I was replacing localhost with my system IP address in the host file. For reference, screenshot is given below.

C#

To run the ASP.NET Core stand alone application as expected we have to remove line number 20 from the above screenshot.

If the ASP.NET Core stand alone application runs as expected, then we can test the created Web API service method through Postman or Fiddler tools.

Service call using Postman tool.

C#

Service call using Fiddler tool,

C#

Consuming service method from Client end

Make a note that usually we host the created service methods in one Global place that may be a Cloud or some other Servers, so that everyone can access them. So, to run the requested service methods, the relevant Framework need to be installed in the location where the service methods are hosted. So in our case, I have written this service method under "net461" framework. This framework should be installed in the service method's hosted location.

C#

Now we write client side code to call the Web API service methods. Here I have used Parallel.For loop to make sure that my service method handles for parallel requests as well.

Calling Web API service method using Console Application:

  1. using System;  
  2. using System.Threading.Tasks;  
  3. using System.Net.Http;  
  4.   
  5. namespace ClientApp  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             Parallel.For(1, 10, (i) =>  
  12.               {  
  13.                   MyAPIToConvertPDF().Wait();  
  14.                   Console.WriteLine(i);  
  15.               });  
  16.         }   
  17.           
  18.         private static async Task MyAPIToConvertPDF()  
  19.         {  
  20.             try  
  21.             {  
  22.                 using (var client = new HttpClient())  
  23.                 {  
  24.                     HttpResponseMessage response = await client.GetAsync("http://localhost:5000/api/PPTXtoPDF/GetConvertPPTXtoPDF");  
  25.                     Console.WriteLine(response.IsSuccessStatusCode);  
  26.                 }  
  27.             }  
  28.             catch (Exception ex)  
  29.             {  
  30.                 Console.WriteLine(ex.ToString());  
  31.             }  
  32.         }  
  33.     }  
  34. }  

Calling Web API service method using Web Application:

  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <title></title>  
  5.     <meta charset="utf-8" />  
  6.     <script src="Scripts/jquery-1.10.2.js"></script>  
  7.     <script src="Scripts/jquery-1.10.2.min.js"></script>  
  8.     <script>  
  9.         $(document).ready(function(){  
  10.             $.ajax({  
  11.                 type: "GET",  
  12.                 cache: false,  
  13.                 url: "http://localhost:5000/api/PPTXtoPDF/GetConvertPPTXtoPDF",  
  14.                 contentType: "application/json;charset=uft-8",  
  15.                 async: true,  
  16.                 processData: false,  
  17.                 success: function (resp) {  
  18.                     alert("Web API called successfully");  
  19.                 },  
  20.                 error: function (err) {        
  21.                     alert("Web API call failed..");               
  22.                 }  
  23.             })  
  24.         })  
  25.     </script>  
  26. </head>  
  27. <body>  
  28. </body>  
  29. </html>  

Output

Screenshot 1

C#

Screenshot 2

C#
On every Web API service method request, you can see in the ASP.Net Core stand alone application output as below,

C#

PPT/PPTX and PDF file source and destination location,

C#

Source (PPTX) file,

C#

Converted file (PDF),

C#

Conclusion

The code is written in such a way that the convert method decides the input file based on supplied input file type. On top of this code, we can easily extend by adding a new code snippet to achieve new functionality which results in code maintainability.

In continuation to this article, I am going to publish a few more articles. Your valuable feedback and suggestions will be highly appreciated.

Happy coding!!