Skip to content

Commit

Permalink
Adds a /working_dir switch so we chose where to open files from.
Browse files Browse the repository at this point in the history
Changes how Word and PowerPoint are fired up to see if we can trap COM issues
  • Loading branch information
vittala committed Dec 16, 2017
1 parent 802efb8 commit 9f56476
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 18 deletions.
6 changes: 6 additions & 0 deletions OfficeToPDF.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{40A85399-B
LICENSE.md = LICENSE.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{81412F10-6C41-4627-AEFD-DE9E41157822}"
ProjectSection(SolutionItems) = preProject
LICENSE.md = LICENSE.md
README.md = README.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down
8 changes: 4 additions & 4 deletions OfficeToPDF/ExcelConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ class ExcelConverter: Converter
}

// Add in a delay to let Excel sort itself out
addExcelCOMDelay(options);
addCOMDelay(options);

// Unable to open workbook
if (workbook == null)
Expand Down Expand Up @@ -446,7 +446,7 @@ class ExcelConverter: Converter
{
return (int)ExitCode.UnknownError;
}
addExcelCOMDelay(options);
addCOMDelay(options);
}
else
{
Expand Down Expand Up @@ -559,7 +559,7 @@ protected static void setPageOptionsFromTemplate(Application app, Workbooks work
try
{
var template = workbooks.Open((string)options["template"]);
addExcelCOMDelay(options);
addCOMDelay(options);
if (template != null)
{
// Run macros from template if the /excel_template_macros option is given
Expand Down Expand Up @@ -610,7 +610,7 @@ protected static void setPageOptionsFromTemplate(Application app, Workbooks work
}

// Add in the required millisecond delay
private static void addExcelCOMDelay(Hashtable options)
private static void addCOMDelay(Hashtable options)
{
if ((int)options["excel_delay"] > 0)
{
Expand Down
4 changes: 2 additions & 2 deletions OfficeToPDF/OfficeToPDF.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,8 @@ goto :exit
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
<Error Condition="!Exists('..\packages\Fody.2.1.2\build\portable-net+sl+win+wpa+wp\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.2.1.2\build\portable-net+sl+win+wpa+wp\Fody.targets'))" />
<Error Condition="!Exists('..\packages\Costura.Fody.1.6.2\build\portable-net+sl+win+wpa+wp\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.1.6.2\build\portable-net+sl+win+wpa+wp\Costura.Fody.targets'))" />
<Error Condition="!Exists('..\packages\Fody.2.2.0\build\portable-net+sl+win+wpa+wp\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.2.2.0\build\portable-net+sl+win+wpa+wp\Fody.targets'))" />
</Target>
<UsingTask TaskName="CosturaCleanup" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" TaskFactory="CodeTaskFactory">
<ParameterGroup>
Expand Down Expand Up @@ -279,8 +279,8 @@ foreach (var item in filesToCleanup)
<CosturaCleanup Config="FodyWeavers.xml" Files="@(ReferenceCopyLocalPaths->'$(OutDir)%(DestinationSubDirectory)%(Filename)%(Extension)')" />
</Target>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Import Project="..\packages\Fody.2.1.2\build\portable-net+sl+win+wpa+wp\Fody.targets" Condition="Exists('..\packages\Fody.2.1.2\build\portable-net+sl+win+wpa+wp\Fody.targets')" />
<Import Project="..\packages\Costura.Fody.1.6.2\build\portable-net+sl+win+wpa+wp\Costura.Fody.targets" Condition="Exists('..\packages\Costura.Fody.1.6.2\build\portable-net+sl+win+wpa+wp\Costura.Fody.targets')" />
<Import Project="..\packages\Fody.2.2.0\build\portable-net+sl+win+wpa+wp\Fody.targets" Condition="Exists('..\packages\Fody.2.2.0\build\portable-net+sl+win+wpa+wp\Fody.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
Expand Down
24 changes: 24 additions & 0 deletions OfficeToPDF/PowerpointConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using MSCore = Microsoft.Office.Core;
using Microsoft.Office.Interop.PowerPoint;
Expand Down Expand Up @@ -63,8 +64,31 @@ class PowerpointConverter : Converter
}
catch (System.Exception)
{
int tries = 10;
app = new Microsoft.Office.Interop.PowerPoint.Application();
running = false;
while (tries > 0)
{
try
{
// Try to set a property on the object
app.DisplayAlerts = PpAlertLevel.ppAlertsNone;
}
catch (COMException)
{
// Decrement the number of tries and have a bit of a snooze
tries--;
Thread.Sleep(500);
continue;
}
// Looks ok, so bail out of the loop
break;
}
if (tries == 0)
{
Converter.releaseCOMObject(app);
return (int)ExitCode.ApplicationError;
}
}
MSCore.MsoTriState nowrite = (Boolean)options["readonly"] ? MSCore.MsoTriState.msoTrue : MSCore.MsoTriState.msoFalse;
bool pdfa = (Boolean)options["pdfa"] ? true : false;
Expand Down
99 changes: 96 additions & 3 deletions OfficeToPDF/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.AccessControl;
using System.Text;
using System.Text.RegularExpressions;
using PdfSharp.Pdf;
Expand Down Expand Up @@ -93,6 +94,8 @@ static void Main(string[] args)
options["merge"] = false;
options["template"] = "";
options["password"] = "";
options["working_dir"] = "";
options["has_working_dir"] = false;
options["excel_show_formulas"] = false;
options["excel_show_headings"] = false;
options["excel_auto_macros"] = false;
Expand Down Expand Up @@ -138,7 +141,7 @@ static void Main(string[] args)
{ "excel_delay", "Excel delay milliseconds" }
};

Regex switches = new Regex(@"^/(version|hidden|markup|readonly|bookmarks|merge|noquit|print|screen|pdfa|template|writepassword|password|help|verbose|exclude(props|tags)|excel_(delay|max_rows|show_formulas|show_headings|auto_macros|template_macros|active_sheet|worksheet|no_recalculate|no_link_update)|word_(header_dist|footer_dist|ref_fonts|no_field_update|field_quick_update(_safe)?|max_pages|keep_history)|pdf_(page_mode|append|prepend|layout|clean_meta|owner_pass|user_pass|restrict_(annotation|extraction|assembly|forms|modify|print|accessibility_extraction|full_quality))|\?)$", RegexOptions.IgnoreCase);
Regex switches = new Regex(@"^/(version|hidden|markup|readonly|bookmarks|merge|noquit|print|screen|pdfa|template|writepassword|password|help|verbose|exclude(props|tags)|excel_(delay|max_rows|show_formulas|show_headings|auto_macros|template_macros|active_sheet|worksheet|no_recalculate|no_link_update)|word_(header_dist|footer_dist|ref_fonts|no_field_update|field_quick_update(_safe)?|max_pages|keep_history)|pdf_(page_mode|append|prepend|layout|clean_meta|owner_pass|user_pass|restrict_(annotation|extraction|assembly|forms|modify|print|accessibility_extraction|full_quality))|working_dir|\?)$", RegexOptions.IgnoreCase);
for (int argIdx = 0; argIdx < args.Length; argIdx++)
{
string item = args[argIdx];
Expand Down Expand Up @@ -269,6 +272,66 @@ static void Main(string[] args)
argIdx++;
}
break;
case "working_dir":
// Allow for a local working directory where files are manipulated
if (argIdx + 2 < args.Length)
{
if (Directory.Exists(args[argIdx + 1]))
{
// Need to check the directory is writable
bool workingDirectoryWritable = false;
try
{
AuthorizationRuleCollection arc = Directory.GetAccessControl(args[argIdx + 1]).GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
foreach (FileSystemAccessRule rule in arc)
{
if (rule.AccessControlType == AccessControlType.Allow)
{
workingDirectoryWritable = true;
break;
}
}
}
catch (Exception)
{}
if (workingDirectoryWritable)
{
int maxTries = 20;
while (maxTries-- > 0)
{
options["working_dir"] = Path.Combine(args[argIdx + 1], Guid.NewGuid().ToString());
if (!Directory.Exists((string)options["working_dir"]))
{
DirectoryInfo di = Directory.CreateDirectory((string)options["working_dir"]);
if (di.Exists)
{
options["has_working_dir"] = true;
break;
}
}
}
if (maxTries <= 0)
{
Console.WriteLine("A working directory can not be created");
Environment.Exit((int)(ExitCode.Failed | ExitCode.InvalidArguments));
}
}
else
{
// The working directory must be writable
Console.WriteLine("The working directory {0} is not writable", args[argIdx + 1]);
Environment.Exit((int)(ExitCode.Failed | ExitCode.InvalidArguments));
}
}
else
{
// We need a real directory to work in, so there is an error here
Console.WriteLine("Unable to find working directory {0}", args[argIdx + 1]);
Environment.Exit((int)(ExitCode.Failed | ExitCode.InvalidArguments));
}
argIdx++;
}
break;
case "excel_max_rows":
case "excel_worksheet":
case "excel_delay":
Expand Down Expand Up @@ -475,6 +538,23 @@ static void Main(string[] args)
Environment.Exit((int)(ExitCode.Failed | ExitCode.DirectoryNotFound));
}

// We want the input and output files copied to a working area where
// we can manipulate them
if ((bool)options["has_working_dir"])
{
// Create a local temporary area and put the input and output in separate
// areas
string workingInput = Path.Combine((string)options["working_dir"], "input");
string workingOutput = Path.Combine((string)options["working_dir"], "output");
System.IO.Directory.CreateDirectory(workingInput);
System.IO.Directory.CreateDirectory(workingOutput);
string workingSource = Path.Combine(workingInput, Path.GetFileName(inputFile));
string workingDest = Path.Combine(workingOutput, Path.GetFileName(outputFile));
options["working_orig_dest"] = outputFile;
File.Copy(inputFile, workingSource);
inputFile = workingSource;
outputFile = workingDest;
}

// Now, do the cleverness of determining what the extension is, and so, which
// conversion class to pass it to
Expand Down Expand Up @@ -599,6 +679,18 @@ static void Main(string[] args)
break;
}
}

// Clear up the working directory and restore the expected output
if ((bool)options["has_working_dir"])
{
if (File.Exists(outputFile))
{
File.Copy(outputFile, (string)options["working_orig_dest"]);
outputFile = (string)options["working_orig_dest"];
}
Directory.Delete((string)options["working_dir"], true);
}

if (converted != (int)ExitCode.Success)
{
Console.WriteLine("Did not convert");
Expand Down Expand Up @@ -852,15 +944,15 @@ page settings from the first worksheet in the template
/excel_active_sheet - Only convert the active worksheet
/excel_auto_macros - Run Auto_Open macros in Excel files before conversion
/excel_show_formulas - Show formulas in the generated PDF
/excel_delay - Number of milliseconds to pause Excel for during file processing
/excel_delay <ms> - Number of milliseconds to pause Excel for during file processing
/excel_show_headings - Show row and column headings
/excel_max_rows <rows> - If any worksheet in a spreadsheet document has more
than this number of rows, do not attempt to convert
the file. Applies when converting with Excel.
/excel_no_link_update - Do not update links when opening Excel files.
/excel_no_recalculate - Skip automatic re-calculation of formulas in the workbook.
/excel_template_macros - Run Auto_Open macros in the /template document before conversion by Excel
/excel_worksheet <num> - Only convert worksheet <num> in the workbook. First sheet is 1.
/excel_worksheet <num> - Only convert worksheet <num> in the workbook. First sheet is 1
/word_header_dist <pts> - The distance (in points) from the header to the top of
the page.
/word_footer_dist <pts> - The distance (in points) from the footer to the bottom
Expand Down Expand Up @@ -905,6 +997,7 @@ this number of pages.
/pdf_restrict_modify - Prevent modification without the owner password.
/pdf_restrict_print - Prevent printing without the owner password.
/version - Print out the version of OfficeToPDF and exit.
/working_dir <path> - A path to copy the input file into temporarily when running the conversion.
input_file - The filename of the Office document to convert
output_file - The filename of the PDF to create. If not given, the input filename
Expand Down
4 changes: 2 additions & 2 deletions OfficeToPDF/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.8.3.0")]
[assembly: AssemblyFileVersion("1.8.3.0")]
[assembly: AssemblyVersion("1.8.4.0")]
[assembly: AssemblyFileVersion("1.8.4.0")]
29 changes: 27 additions & 2 deletions OfficeToPDF/WordConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Office.Interop.Word;


Expand Down Expand Up @@ -60,9 +61,33 @@ class WordConverter: Converter
}
catch (System.Exception)
{
word = new Microsoft.Office.Interop.Word.Application();
running = false;
int tries = 10;
word = new Microsoft.Office.Interop.Word.Application();
running = false;
while (tries > 0)
{
try
{
// Try to set a property on the object
word.ScreenUpdating = false;
}
catch (COMException)
{
// Decrement the number of tries and have a bit of a snooze
tries--;
Thread.Sleep(500);
continue;
}
// Looks ok, so bail out of the loop
break;
}
if (tries == 0)
{
Converter.releaseCOMObject(word);
return (int)ExitCode.ApplicationError;
}
}

word.DisplayAlerts = WdAlertLevel.wdAlertsNone;
// Issue #48 - we should allow control over whether the history is lost
if (!(Boolean)options["word_keep_history"])
Expand Down
8 changes: 4 additions & 4 deletions OfficeToPDF/app.config
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="PdfSharp-WPF" publicKeyToken="f94615aa0424f9eb" culture="neutral"/>
<bindingRedirect oldVersion="1.31.0.0-1.31.1789.0" newVersion="1.31.4604.0"/>
<assemblyIdentity name="PdfSharp-WPF" publicKeyToken="f94615aa0424f9eb" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.31.4604.0" newVersion="1.31.4604.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
</startup>
</configuration>
Loading

0 comments on commit 9f56476

Please sign in to comment.