public int Convert(string inputFile, ref string outputFile) { if (!_initialized) { throw new Exception("ConverService is not initialized"); } // if no output is provided, use the source file and change to a PDF extension if (string.IsNullOrEmpty(outputFile)) { outputFile = Path.ChangeExtension(inputFile, "pdf"); } else { // if the outputFile spec is a directory, put the file in the directory with same name and new extension if (Directory.Exists(outputFile)) { outputFile = Path.Combine(outputFile, Path.GetFileNameWithoutExtension(inputFile) + ".pdf"); } } // confirm that the input file exists and convert to component path elements in options FileInfo inputInfo; try { inputInfo = new FileInfo(inputFile); } catch { inputInfo = null; } if (inputInfo == null || !inputInfo.Exists) { throw new Exception("Input file not found"); } inputFile = inputInfo.FullName; _options["original_filename"] = inputInfo.Name; _options["original_basename"] = inputInfo.Name.Substring(0, inputInfo.Name.Length - inputInfo.Extension.Length); // handle the output file existing or path existing FileInfo outputInfo = new FileInfo(outputFile); // Remove the destination unless we're doing a PDF merge if (outputInfo != null) { outputFile = outputInfo.FullName; if (outputInfo.Exists) { if ((MergeMode)_options["pdf_merge"] == MergeMode.None) { // We are not merging, so delete the final destination System.IO.File.Delete(outputInfo.FullName); } else { // We are merging, so make a temporary file outputFile = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".pdf"; } } else { // If there is no current output, no need to merge _options["pdf_merge"] = MergeMode.None; } } else { throw new Exception("Unable to determine outputFile location"); } if (!System.IO.Directory.Exists(outputInfo.DirectoryName)) { throw new Exception("Output directory does not exist"); } // actually do the conversion, finally int converted = WordConverter.Convert(inputFile, outputFile, _options); return(converted); }
/// <summary> /// Convert a Word file to PDF /// </summary> /// <param name="inputFile">Full path of the input Word file</param> /// <param name="outputFile">Full path of the output PDF</param> /// <returns></returns> public static new int Convert(String inputFile, String outputFile, Hashtable options) { Boolean running = (Boolean)options["noquit"]; Application word = null; object oMissing = System.Reflection.Missing.Value; Template tmpl; String temporaryStorageDir = null; float wordVersion = 0; List <AppOption> wordOptionList = new List <AppOption>(); try { String filename = (String)inputFile; Boolean hasSignatures = WordConverter.HasDigitalSignatures(filename); Boolean visible = !(Boolean)options["hidden"]; Boolean openAndRepair = !(Boolean)options["word_no_repair"]; Boolean nowrite = (Boolean)options["readonly"]; Boolean includeProps = !(Boolean)options["excludeprops"]; Boolean includeTags = !(Boolean)options["excludetags"]; Boolean bitmapMissingFonts = !(Boolean)options["word_ref_fonts"]; Boolean autosave = options.ContainsKey("IsTempWord") && (Boolean)options["IsTempWord"]; bool pdfa = (Boolean)options["pdfa"] ? true : false; String writePassword = ""; String readPassword = ""; int maxPages = 0; WdExportOptimizeFor quality = WdExportOptimizeFor.wdExportOptimizeForPrint; WdExportItem showMarkup = WdExportItem.wdExportDocumentContent; WdExportCreateBookmarks bookmarks = (Boolean)options["bookmarks"] ? WdExportCreateBookmarks.wdExportCreateHeadingBookmarks : WdExportCreateBookmarks.wdExportCreateNoBookmarks; Options wdOptions = null; Documents documents = null; Template normalTemplate = null; tmpl = null; try { word = (Microsoft.Office.Interop.Word.Application)Marshal.GetActiveObject("Word.Application"); } catch (System.Exception) { 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) { ReleaseCOMObject(word); return((int)ExitCode.ApplicationError); } } wdOptions = word.Options; word.DisplayAlerts = WdAlertLevel.wdAlertsNone; // Issue #48 - we should allow control over whether the history is lost if (!(Boolean)options["word_keep_history"]) { word.DisplayRecentFiles = false; } word.DisplayDocumentInformationPanel = false; word.FeatureInstall = Microsoft.Office.Core.MsoFeatureInstall.msoFeatureInstallNone; wordVersion = (float)System.Convert.ToDecimal(word.Version, new CultureInfo("en-US")); // Set the Word options in a way that allows us to reset the options when we finish try { wordOptionList.Add(new AppOption("AlertIfNotDefault", false, ref wdOptions)); wordOptionList.Add(new AppOption("AllowReadingMode", false, ref wdOptions)); wordOptionList.Add(new AppOption("PrecisePositioning", true, ref wdOptions)); wordOptionList.Add(new AppOption("UpdateFieldsAtPrint", false, ref wdOptions)); wordOptionList.Add(new AppOption("UpdateLinksAtPrint", false, ref wdOptions)); wordOptionList.Add(new AppOption("UpdateLinksAtOpen", false, ref wdOptions)); wordOptionList.Add(new AppOption("UpdateFieldsWithTrackedChangesAtPrint", false, ref wdOptions)); wordOptionList.Add(new AppOption("WarnBeforeSavingPrintingSendingMarkup", false, ref wdOptions)); wordOptionList.Add(new AppOption("BackgroundSave", true, ref wdOptions)); wordOptionList.Add(new AppOption("SavePropertiesPrompt", false, ref wdOptions)); wordOptionList.Add(new AppOption("DoNotPromptForConvert", true, ref wdOptions)); wordOptionList.Add(new AppOption("PromptUpdateStyle", false, ref wdOptions)); wordOptionList.Add(new AppOption("ConfirmConversions", false, ref wdOptions)); wordOptionList.Add(new AppOption("CheckGrammarAsYouType", false, ref wdOptions)); wordOptionList.Add(new AppOption("CheckGrammarWithSpelling", false, ref wdOptions)); wordOptionList.Add(new AppOption("CheckSpellingAsYouType", false, ref wdOptions)); wordOptionList.Add(new AppOption("DisplaySmartTagButtons", false, ref wdOptions)); wordOptionList.Add(new AppOption("EnableLivePreview", false, ref wdOptions)); wordOptionList.Add(new AppOption("ShowReadabilityStatistics", false, ref wdOptions)); wordOptionList.Add(new AppOption("SuggestSpellingCorrections", false, ref wdOptions)); wordOptionList.Add(new AppOption("AllowDragAndDrop", false, ref wdOptions)); wordOptionList.Add(new AppOption("EnableMisusedWordsDictionary", false, ref wdOptions)); wordOptionList.Add(new AppOption("ShowFormatError", false, ref wdOptions)); wordOptionList.Add(new AppOption("StoreRSIDOnSave", false, ref wdOptions)); wordOptionList.Add(new AppOption("SaveNormalPrompt", false, ref wdOptions)); wordOptionList.Add(new AppOption("AllowFastSave", false, ref wdOptions)); wordOptionList.Add(new AppOption("BackgroundOpen", false, ref wdOptions)); wordOptionList.Add(new AppOption("ShowMarkupOpenSave", false, ref wdOptions)); wordOptionList.Add(new AppOption("SaveInterval", 0, ref wdOptions)); wordOptionList.Add(new AppOption("PrintHiddenText", (Boolean)options["word_show_hidden"], ref wdOptions)); } catch (SystemException) { } // Set up the PDF output quality if ((Boolean)options["print"]) { quality = WdExportOptimizeFor.wdExportOptimizeForPrint; } if ((Boolean)options["screen"]) { quality = WdExportOptimizeFor.wdExportOptimizeForOnScreen; } if ((Boolean)options["markup"]) { showMarkup = WdExportItem.wdExportDocumentWithMarkup; } if (!String.IsNullOrEmpty((String)options["password"])) { readPassword = (String)options["password"]; } if (!String.IsNullOrEmpty((String)options["writepassword"])) { writePassword = (String)options["writepassword"]; } // Large Word files may simply not print reliably - if the word_max_pages // configuration option is set, then we must close up and forget about // converting the file. maxPages = (int)options[@"word_max_pages"]; documents = word.Documents; normalTemplate = word.NormalTemplate; // Check for password protection and no password if (IsPasswordProtected(inputFile) && String.IsNullOrEmpty(readPassword)) { normalTemplate.Saved = true; throw new Exception("Unable to open password protected file"); } // If we are opening a document with a write password and no read password, and // we are not in read only mode, we should follow the document properties and // enforce a read only open. If we do not, Word pops up a dialog if (!nowrite && String.IsNullOrEmpty(writePassword) && IsReadOnlyEnforced(inputFile)) { nowrite = true; } // Having signatures means we should open the document very carefully if (hasSignatures) { nowrite = true; autosave = false; openAndRepair = false; } Document doc = null; try { if ((bool)options["merge"] && !String.IsNullOrEmpty((string)options["template"]) && File.Exists((string)options["template"]) && System.Text.RegularExpressions.Regex.IsMatch((string)options["template"], @"^.*\.dot[mx]?$", System.Text.RegularExpressions.RegexOptions.IgnoreCase)) { // Create a new document based on a template doc = documents.Add((string)options["template"]); Object rStart = 0; Object rEnd = 0; Range range = doc.Range(rStart, rEnd); range.InsertFile(inputFile); ReleaseCOMObject(range); // Make sure we save the file with the original filename so // filename fields update correctly temporaryStorageDir = Path.GetTempFileName(); File.Delete(temporaryStorageDir); Directory.CreateDirectory(temporaryStorageDir); doc.SaveAs(Path.Combine(temporaryStorageDir, Path.GetFileName(inputFile))); } else { // Open the source document doc = documents.OpenNoRepairDialog(FileName: filename, ReadOnly: nowrite, PasswordDocument: readPassword, WritePasswordDocument: writePassword, Visible: visible, OpenAndRepair: openAndRepair); } } catch (COMException) { throw new Exception("Unable to open file"); } // Check if there are signatures in the document which changes how we do things if (hasSignatures) { // Add in a delay to allow signatures to load Thread.Sleep(500); } else { Window docWin = null; View docWinView = null; doc.Activate(); // Check if there are too many pages if (maxPages > 0) { var pageCount = doc.ComputeStatistics(WdStatistic.wdStatisticPages, false); doc.Saved = true; if (pageCount > maxPages) { throw new Exception(String.Format("Too many pages to process ({0}). More than {1}", pageCount, maxPages)); } } // Prevent "property not available" errors, see http://blogs.msmvps.com/wordmeister/2013/02/22/word2013bug-not-available-for-reading/ docWin = doc.ActiveWindow; docWinView = docWin.View; if (wordVersion >= 15) { docWinView.ReadingLayout = false; } // Sometimes the print view will not be available (e.g. for a blog post) // Try and switch view try { docWinView.Type = WdViewType.wdPrintPreview; } catch (Exception) { } // Handle markup if ((Boolean)options["word_show_all_markup"]) { options["word_show_comments"] = true; options["word_show_revs_comments"] = true; options["word_show_format_changes"] = true; options["word_show_ink_annot"] = true; options["word_show_ins_del"] = true; } if ((Boolean)options["word_show_comments"] || (Boolean)options["word_show_revs_comments"] || (Boolean)options["word_show_format_changes"] || (Boolean)options["word_show_ink_annot"] || (Boolean)options["word_show_ins_del"] || showMarkup == WdExportItem.wdExportDocumentWithMarkup) { docWinView.MarkupMode = (Boolean)options["word_markup_balloon"] ? WdRevisionsMode.wdBalloonRevisions : WdRevisionsMode.wdInLineRevisions; } word.PrintPreview = false; docWinView.RevisionsView = WdRevisionsView.wdRevisionsViewFinal; docWinView.ShowRevisionsAndComments = (Boolean)options["word_show_revs_comments"]; docWinView.ShowComments = (Boolean)options["word_show_comments"]; docWinView.ShowFormatChanges = (Boolean)options["word_show_format_changes"]; docWinView.ShowInkAnnotations = (Boolean)options["word_show_ink_annot"]; docWinView.ShowInsertionsAndDeletions = (Boolean)options["word_show_ins_del"]; // Try to avoid Word thinking any changes are happening to the document doc.SpellingChecked = true; doc.GrammarChecked = true; // Changing these properties may be disallowed if the document is protected // and is not signed if (doc.ProtectionType == WdProtectionType.wdNoProtection && !hasSignatures) { if (autosave) { doc.Save(); doc.Saved = true; } doc.TrackMoves = false; doc.TrackRevisions = false; doc.TrackFormatting = false; if ((Boolean)options["word_fix_table_columns"]) { FixWordTableColumnWidths(doc); } } normalTemplate.Saved = true; // Hide the document window if need be if ((Boolean)options["hidden"]) { word.Visible = false; var activeWin = word.ActiveWindow; activeWin.Visible = false; activeWin.WindowState = WdWindowState.wdWindowStateMinimize; ReleaseCOMObject(activeWin); } // Check if we have a template file to apply to this document // The template must be a file and must end in .dot, .dotx or .dotm if (!String.IsNullOrEmpty((String)options["template"]) && !(bool)options["merge"]) { string template = (string)options["template"]; if (File.Exists(template) && System.Text.RegularExpressions.Regex.IsMatch(template, @"^.*\.dot[mx]?$")) { doc.set_AttachedTemplate(template); doc.UpdateStyles(); tmpl = doc.get_AttachedTemplate(); } else { // Console.WriteLine("Invalid template '{0}'", template); } } // See if we have to update fields if (!(Boolean)options["word_no_field_update"]) { UpdateDocumentFields(doc, word, inputFile, options); } var pageSetup = doc.PageSetup; if ((float)options["word_header_dist"] >= 0) { pageSetup.HeaderDistance = (float)options["word_header_dist"]; } if ((float)options["word_footer_dist"] >= 0) { pageSetup.FooterDistance = (float)options["word_footer_dist"]; } ReleaseCOMObject(pageSetup); try { // Make sure we are not in a header footer view docWinView.SeekView = WdSeekView.wdSeekPrimaryHeader; docWinView.SeekView = WdSeekView.wdSeekPrimaryFooter; docWinView.SeekView = WdSeekView.wdSeekMainDocument; } catch (Exception) { // We might fail when switching views } normalTemplate.Saved = true; if (autosave) { doc.Save(); } doc.Saved = true; ReleaseCOMObject(docWinView); ReleaseCOMObject(docWin); } // Set up a delegate function if we're using a printer PrintDocument printFunc = delegate(string destination, string printerName) { word.ActivePrinter = printerName; doc.PrintOut(Background: false, OutputFileName: destination); }; // Enable screen updating before exporting to ensure that Word // renders borders correctly word.ScreenUpdating = true; if (String.IsNullOrEmpty((string)options["printer"])) { // No printer given, so export try { doc.ExportAsFixedFormat(outputFile, WdExportFormat.wdExportFormatPDF, false, quality, WdExportRange.wdExportAllDocument, 1, 1, showMarkup, includeProps, true, bookmarks, includeTags, bitmapMissingFonts, pdfa); } catch (Exception) { // Couldn't export, so see if there is a fallback printer if (!String.IsNullOrEmpty((string)options["fallback_printer"])) { PrintToGhostscript((string)options["fallback_printer"], outputFile, printFunc); } else { throw; } } } else { PrintToGhostscript((string)options["printer"], outputFile, printFunc); } if (tmpl != null) { tmpl.Saved = true; } object saveChanges = autosave? WdSaveOptions.wdSaveChanges : WdSaveOptions.wdDoNotSaveChanges; if (nowrite) { doc.Saved = true; } normalTemplate.Saved = true; ((_Document)doc).Close(ref saveChanges, ref oMissing, ref oMissing); // Reset options foreach (AppOption opt in wordOptionList) { opt.ResetValue(ref wdOptions); } ReleaseCOMObject(wdOptions); ReleaseCOMObject(documents); ReleaseCOMObject(doc); ReleaseCOMObject(tmpl); ReleaseCOMObject(normalTemplate); return((int)ExitCode.Success); } catch (Exception e) { throw new Exception(e.Message); } finally { if (temporaryStorageDir != null && Directory.Exists(temporaryStorageDir)) { try { if (File.Exists(Path.Combine(temporaryStorageDir, Path.GetFileName(inputFile)))) { File.Delete(Path.Combine(temporaryStorageDir, Path.GetFileName(inputFile))); } Directory.Delete(temporaryStorageDir); } catch (Exception) { } } if (word != null && !running) { CloseWordApplication(word); } ReleaseCOMObject(word); } }