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.
- Using Office Automation objects
- Using LibreOffice
- By making use of Aspose
- Using Spire.Presentation
- With the help of OpenOffice
- Using SharePoint PowerPoint Services
- 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.
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,
Outcome
No luck :(
Technique 2
I have configured the same MIME types in web.config file as below.
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.
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
- Click on "New Project".
- Expand Templates and select Web.
- Since we are going to create, select "ASP.Net Core Web Application (.NET Core)" template.
- Give the project name own your wish, let say "WebAppCoreConverter", then click on OK button.
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.)
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
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
-
- namespace WebAppCoreConverter.DocumentConverter
- {
- public interface IConverter
- {
- short convert(String sourcePath, String targetPath, ContentType sourceType);
- }
- public interface IDocumentConverterFactory
- {
- IConverter getConverter(ConversionType convType);
- }
- }
I am going to create a utility class (common.cs) where I am going to place the commonly used
common.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.IO;
-
- namespace WebAppCoreConverter.DocumentConverter
- {
-
- public enum ContentType
- {
- DOC,
- DOCX,
- XLS,
- XLSX,
- VSD,
- VDX,
- PPT,
- PPTX,
- XDW,
- PDF,
- XPS,
- JPEG,
- JPG,
- BMP,
- PNG,
- TIF,
- TIFF,
- GIF,
- SVG,
- TXT,
- RTF,
- XML,
- CSV,
- UNKNOWN = -1
- }
- public class Util
- {
- public static void releaseObject(object obj)
- {
- Console.WriteLine("MethodName: releaseObject of Class: Util started");
- try
- {
- System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
- obj = null;
- }
-
- catch (Exception exReleaseObject)
- {
- obj = null;
-
-
- }
- finally
- {
- GC.Collect();
- GC.WaitForPendingFinalizers();
- GC.Collect();
- GC.WaitForPendingFinalizers();
- }
- Console.WriteLine("MethodName: releaseObject of Class: Util ended");
- }
- public static string getTargetFileName(string contentPath, string contentName, ContentType contentType)
- {
- string getTargetFileNameStatus = string.Empty;
-
- try
- {
- string tempFileName = Path.GetFileNameWithoutExtension(contentName) + ".";
- tempFileName += contentType.ToString().ToLower();
- getTargetFileNameStatus = Path.Combine(contentPath, tempFileName);
- }
- catch (Exception exGetTargetFileName)
- {
- }
- return getTargetFileNameStatus;
- }
- public static short getFileExtension(string sourcePath, out ContentType fileExtn)
- {
- short getFileExtnStatus = 0;
- fileExtn = ContentType.UNKNOWN;
- try
- {
- string tempFileExtn = Path.GetExtension(sourcePath);
-
- tempFileExtn = tempFileExtn.Replace(".", string.Empty);
- fileExtn = (ContentType)Enum.Parse(typeof(ContentType), tempFileExtn, true);
- }
- catch (Exception exGetFileExtension)
- {
- } return getFileExtnStatus;
- }
- }
- public enum ConversionType
- {
- Doc2Pdf,
- Pdf2Img,
- Img2Img
- }
- }
I am going to create a factory class (DocumentConverterFactory.cs) which decides the conversion type.
DocumentConverterFactory.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
-
- namespace WebAppCoreConverter.DocumentConverter
- {
- public class DocumentConverterFactory : IDocumentConverterFactory
- {
- public IConverter getConverter(ConversionType convType)
- {
- IConverter converter = null;
- switch (convType)
- {
- case ConversionType.Doc2Pdf:
- converter = new ConvDoc2PdfWithMsOffice();
- break;
- case ConversionType.Pdf2Img:
- case ConversionType.Img2Img:
- default:
- break;
- }
- return converter;
- }
- }
- }
I am going to create a concrete class which inherits from "IConverter" interface to define the actual business logic.
ConvDoc2PdfWithMsOffice.cs
- using System;
- using Microsoft.Office.Core;
- using PowerPoint = Microsoft.Office.Interop.PowerPoint;
- using System.Reflection;
-
- namespace WebAppCoreConverter.DocumentConverter
- {
- public class ConvDoc2PdfWithMsOffice : IConverter
- {
-
-
-
-
-
-
-
- public short convert(String sourcePath, String targetPath, ContentType sourceType)
- {
- Console.WriteLine("Class: " + GetType() + " Method: " + MethodBase.GetCurrentMethod().Name + " Started ");
- short convDoc2PdfWithMsOfficeResult = 0;
- if (sourceType == ContentType.PPT || sourceType == ContentType.PPTX)
- {
- convDoc2PdfWithMsOfficeResult = powerPoint2Pdf((Object)sourcePath, (Object)targetPath);
- }
- else convDoc2PdfWithMsOfficeResult = -1;
- Console.WriteLine("Class: " + GetType() + " Method: " + MethodBase.GetCurrentMethod().Name + " Ended ");
- return convDoc2PdfWithMsOfficeResult;
- }
-
-
-
-
-
-
- public short powerPoint2Pdf(object originalPptPath, object pdfPath)
- {
- Console.WriteLine("Class: " + GetType() + " Method: " + MethodBase.GetCurrentMethod().Name + " Started ");
- short convertPowerPoint2PdfResult = -1;
- PowerPoint.Application pptApplication = null;
- PowerPoint.Presentation pptPresentation = null;
- object unknownType = Type.Missing;
- try
- {
-
- pptApplication = new PowerPoint.Application();
-
-
- pptPresentation = pptApplication.Presentations.Open((string)originalPptPath, MsoTriState.msoTrue, MsoTriState.msoTrue, MsoTriState.msoFalse);
-
-
- if (pptPresentation != null)
- {
- pptPresentation.ExportAsFixedFormat((string)pdfPath,
- PowerPoint.PpFixedFormatType.ppFixedFormatTypePDF,
- PowerPoint.PpFixedFormatIntent.ppFixedFormatIntentPrint,
- MsoTriState.msoFalse,
- PowerPoint.PpPrintHandoutOrder.ppPrintHandoutVerticalFirst,
- PowerPoint.PpPrintOutputType.ppPrintOutputSlides,
- MsoTriState.msoFalse, null,
- PowerPoint.PpPrintRangeType.ppPrintAll, string.Empty,
- true, true, true, true, false, unknownType);
- convertPowerPoint2PdfResult = 0;
- }
- else
- {
- Console.WriteLine("Error occured for conversion of office PowerPoint to PDF");
- convertPowerPoint2PdfResult = 504;
- }
- }
- catch (Exception exPowerPoint2Pdf)
- {
- Console.WriteLine("Error occured for conversion of office PowerPoint to PDF, Exception: ", exPowerPoint2Pdf);
- convertPowerPoint2PdfResult = 504;
- }
- finally
- {
-
- if (pptPresentation != null)
- {
- pptPresentation.Close();
- Util.releaseObject(pptPresentation);
- pptPresentation = null;
- }
-
-
- pptApplication.Quit();
- Util.releaseObject(pptApplication);
- pptApplication = null;
- }
- Console.WriteLine("Class: " + GetType() + " Method: " + MethodBase.GetCurrentMethod().Name + " Ended ");
- return convertPowerPoint2PdfResult;
- }
- }
- }
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).
- using System;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.Office.Interop.PowerPoint;
- using WebAppCoreConverter.DocumentConverter;
- using Microsoft.AspNetCore.Authorization;
-
- namespace WebAppCoreConverter.Controllers
- {
- [AllowAnonymous, Route("api/[controller]")]
- public class PPTXtoPDFController : Controller
- {
- [HttpGet, Route("GetConvertPPTXtoPDF")]
- public void GetConvertPPTXtoPDF()
- {
- Application ppApp = new Application();
- try
- {
- IConverter converter = new ConvDoc2PdfWithMsOffice();
- 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);
- }
- catch (Exception e)
- {
-
- }
- }
- }
- }
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.
Step 8
Publish the application.
It will generate the .EXE file with same name as project name.
Step 9
Click on .EXE file; then the server should run on default port number.
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.
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.
Service call using Fiddler tool,
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.
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:
- using System;
- using System.Threading.Tasks;
- using System.Net.Http;
-
- namespace ClientApp
- {
- class Program
- {
- static void Main(string[] args)
- {
- Parallel.For(1, 10, (i) =>
- {
- MyAPIToConvertPDF().Wait();
- Console.WriteLine(i);
- });
- }
-
- private static async Task MyAPIToConvertPDF()
- {
- try
- {
- using (var client = new HttpClient())
- {
- HttpResponseMessage response = await client.GetAsync("http://localhost:5000/api/PPTXtoPDF/GetConvertPPTXtoPDF");
- Console.WriteLine(response.IsSuccessStatusCode);
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.ToString());
- }
- }
- }
- }
Calling Web API service method using Web Application:
- <!DOCTYPE html>
- <html>
- <head>
- <title></title>
- <meta charset="utf-8" />
- <script src="Scripts/jquery-1.10.2.js"></script>
- <script src="Scripts/jquery-1.10.2.min.js"></script>
- <script>
- $(document).ready(function(){
- $.ajax({
- type: "GET",
- cache: false,
- url: "http://localhost:5000/api/PPTXtoPDF/GetConvertPPTXtoPDF",
- contentType: "application/json;charset=uft-8",
- async: true,
- processData: false,
- success: function (resp) {
- alert("Web API called successfully");
- },
- error: function (err) {
- alert("Web API call failed..");
- }
- })
- })
- </script>
- </head>
- <body>
- </body>
- </html>
Output
Screenshot 1
Screenshot 2
On every Web API service method request, you can see in the ASP.Net Core stand alone application output as below,
PPT/PPTX and PDF file source and destination location,
Source (PPTX) file,
Converted file (PDF),
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!!