0

i'm working on a Word VSTO Add-In to automate some Corporate Identity Stuff for the Company i work at.

I want to replace certain Images both before and after a call to PrintOut

This is the Method i'm currently using

public static void PrintDocumentAsPDF(this WordRibbon _, string printerName = "Microsoft Print to PDF")
{

    try
    {
        // Un-Protected Document
        Globals.ThisAddIn.Application.ActiveDocument.Unprotect("");
    }
    catch (COMException) { }

    // Replace Images in Document
    Extensions.ReplaceImage(Constants.ImageType.DesignElement, Constants.ImageVariant.FullResolution);
    Extensions.ReplaceImage(Constants.ImageType.Logo, Constants.ImageVariant.FullResolution);

    // Get last Active Printer
    string lastPrinter = Globals.ThisAddIn.Application.ActivePrinter;
    try
    {
        // Set temporary Active Printer
        Globals.ThisAddIn.Application.ActivePrinter = printerName;
    }
    catch (Exception)
    {
        MessageBox.Show("Drucker konnte nicht konfiguriert werden!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }

    try
    {
        // Print Document
        Globals.ThisAddIn.Application.PrintOut(
            Constants.FalseObject, // Background
            Constants.Missing, // Append
            Constants.Missing, // Range
            Constants.Missing, // Output File Name
            Constants.Missing, // From
            Constants.Missing, // To
            Constants.Missing, // Item
            Constants.Missing, // Copies
            Constants.Missing, // Pages
            Constants.Missing, // PageType
            Constants.Missing, // PrintToFile
            Constants.Missing, // Collate
            Constants.Missing, // ActivePrinterMacGX
            Constants.Missing, // ManualDuplexPrint
            Constants.Missing, // PrintZoomColumn
            Constants.Missing, // PrintZoomRow
            Constants.Missing, // PrintZoomPaperWidth
            Constants.Missing  // PrintZoomPaperHeight
        );
    }
    catch (Exception)
    {
        MessageBox.Show("Dokument konnte nicht gedruckt werden!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    
    // Restore Active Printer
    try
    {
        Globals.ThisAddIn.Application.ActivePrinter = lastPrinter;
    }
    catch (Exception)
    {
        MessageBox.Show("Ursprüngliche Druckereinstellungen konnte nicht wiederhergestellt werden!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }

    // Replace Images with low-resolution Versions
    Extensions.ReplaceImage(Constants.ImageType.DesignElement, Constants.ImageVariant.LowResolution);
    Extensions.ReplaceImage(Constants.ImageType.Logo, Constants.ImageVariant.LowResolution);

    try
    {
        // Protect Document
        Globals.ThisAddIn.Application.ActiveDocument.Protect(WdProtectionType.wdAllowOnlyFormFields, Constants.FalseObject, "");
    }
    catch (COMException) { }
}

This is the Code for Extensions.ReplaceImage(...)

public static void ReplaceImage(ImageType image, ImageVariant imageVariant)
{

    try
    {
        foreach (Range storyRange in Globals.ThisAddIn.Application.ActiveDocument.StoryRanges)
        {
            foreach (Shape shape in storyRange.ShapeRange)
            {
                if (shape.AlternativeText == ImageRepository[image][imageVariant].Name)
                {
                    object anchor = shape.Anchor;
                    Shape newShape = Globals.ThisAddIn.Application.ActiveDocument.Shapes.AddPicture(
                        ImageRepository[image][imageVariant].Path /* Image Path */,
                        // ref Constants.Missing,
                        // ref Constants.Missing,
                        // ref Constants.Missing,
                        // ref Constants.Missing,
                        // ref Constants.Missing,
                        // ref Constants.Missing,
                        Anchor: ref anchor);
                    newShape.Top = shape.Top;
                    newShape.Left = shape.Left;
                    newShape.Width = shape.Width;
                    newShape.Height = shape.Height;
                    newShape.AlternativeText = shape.AlternativeText;
                    shape.Delete();
                }
            }
            foreach (InlineShape inlineShape in storyRange.InlineShapes)
            {
                if (inlineShape.AlternativeText == ImageRepository[image][imageVariant].Name)
                {
                    InlineShape newInlineShape = Globals.ThisAddIn.Application.ActiveDocument.InlineShapes.AddPicture(
                        ImageRepository[image][imageVariant].Path,
                        ref Missing,
                        ref Missing,
                        inlineShape.Range
                    );
                    newInlineShape.Width = inlineShape.Width;
                    newInlineShape.Height = inlineShape.Height;
                    newInlineShape.AlternativeText = inlineShape.AlternativeText;
                    inlineShape.Delete();
                }
            }
        }
    }
    catch (COMException comException)
    {
        MessageBox.Show(comException.Message, "COM Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

Definition for Constants.FalseObject: public static object FalseObject = false; Definition for Constants.Missing: public static object Missing = System.Reflection.Missing.Value;

And these are the definitions for ImageType and ImageVariant

public enum ImageType
{
    DesignElement,
    Logo
}

public enum ImageVariant
{
    LowResolution,
    FullResolution
}

public class ImageElement
{
    public string Path { get; }
    public string Name { get; }

    public ImageElement(string path, string name)
    {
        Path = path;
        Name = name;
    }
}

public static Dictionary<ImageType, Dictionary<ImageVariant, ImageElement>> ImageRepository = new Dictionary<ImageType, Dictionary<ImageVariant, ImageElement>>()
{
    [ImageType.DesignElement] = new Dictionary<ImageVariant, ImageElement>()
    {
        [ImageVariant.FullResolution] = new ImageElement(Path.Combine(LocalResourceBasePath, "Images", "DocumentTitle.png"), "Designelement"),
        [ImageVariant.LowResolution] = new ImageElement(Path.Combine(LocalResourceBasePath, "Images", "DocumentTitle_lowres.png"), "Designelement")
    },
    [ImageType.Logo] = new Dictionary<ImageVariant, ImageElement>()
    {
        [ImageVariant.FullResolution] = new ImageElement(Path.Combine(LocalResourceBasePath, "Images", "Logo_rgb.png"), "Logo"),
        [ImageVariant.LowResolution] = new ImageElement(Path.Combine(LocalResourceBasePath, "Images", "Logo_rgb_lowres.png"), "Logo")
    },
};

The issue is, that the second call(s) to ReplaceImage after the PrintOut()-Call are done before the First Replacement and Print is done.

I saw that there was a Event called DocumentBeforePrint that gets invoked before anything is being printed. Is something similar like DocumentAfterPrint ?

I also attempted to use this Post to fix it, but this wouldn't work either.

Can anyone help ? ( if my code is complete garbage - which would not surprise me - i'm also open to suggestions on how to do this better )

Oliver Karger
  • 105
  • 1
  • 11
  • Is this [Is there a DocumentAfterPrint event in MS Word?](https://stackoverflow.com/questions/1155970/is-there-a-documentafterprint-event-in-ms-word) helpful? And how about [this](https://learn.microsoft.com/en-us/dotnet/api/microsoft.office.interop.word._application.backgroundprintingstatus?view=word-pia) [Application.BackgroundPrintingStatus](https://learn.microsoft.com/en-us/office/vba/api/word.application.backgroundprintingstatus)? – Oscar Sun Jul 20 '23 at 14:49
  • [how to check if word document has printed already](https://stackoverflow.com/questions/11510315/how-to-check-if-word-document-has-printed-already). [When using a virtual printer how can I check that print is finished?](https://stackoverflow.com/questions/3914880/when-using-a-virtual-printer-how-can-i-check-that-print-is-finished?rq=3) – Oscar Sun Jul 20 '23 at 15:00

1 Answers1

0

The issue is, that the second call(s) to ReplaceImage after the PrintOut()-Call are done before the First Replacement and Print is done.

OK, I use BackgroundPrintingStatus property to wait the method PrintOut() done. It works. This is my test code:

       void Cs_VSTO_Replace_Images_in_Document_before_and_after_PrintOut()
        {
            string f = @"X:\t.docx";
            string fPrint = @"X:\t.pdf";//just for test, because I don't have a printer to test
            string pic = @"X:\2.png";


            if (File.Exists(fPrint)) File.Delete(fPrint);

            int howLongtoWaitInSeconds = 50;
            TimeSpan sp = new TimeSpan();
            DateTime t = DateTime.Now;

            int backgroundPrintingCount = Globals.ThisAddIn.Application.BackgroundPrintingStatus;
            Microsoft.Office.Interop.Word.Document d = Globals.ThisAddIn.Application.Documents.Open(f);
            d.PrintOut(Background: false, OutputFileName: fPrint, PrintToFile: true);
            while (Globals.ThisAddIn.Application.BackgroundPrintingStatus > backgroundPrintingCount)
            {
                //Just in case.
                if (DateTime.Now.Subtract(t).TotalSeconds > howLongtoWaitInSeconds) break;
            }
            //if you also print to file then use Fils.Exsted to check
            /*
            while(!File.Exists(fPrint)) {
                //Just in case.
                if (DateTime.Now.Subtract(t).TotalSeconds > howLongtoWaitInSeconds) break;
            }
            */

            //just for test
            //d.Range().Find.Execute(FindText: "易", Replace: Microsoft.Office.Interop.Word.WdReplace.wdReplaceAll, ReplaceWith: "●");
            System.Media.SystemSounds.Exclamation.Play();
            foreach (Microsoft.Office.Interop.Word.InlineShape shp in d.InlineShapes)
            {
                d.Range().InlineShapes.AddPicture(FileName:pic,Range:shp.Range);
                shp.Delete();
            }

            d.Close(SaveChanges: Microsoft.Office.Interop.Word.WdSaveOptions.wdSaveChanges);
            Globals.ThisAddIn.Application.Documents.Open(f);
            System.Media.SystemSounds.Asterisk.Play();
            Process.Start(fPrint);
        }

So if in your code, it may be like:

        public static void PrintDocumentAsPDF(this WordRibbon _, string printerName = "Microsoft Print to PDF")
        {

            try
            {
                // Un-Protected Document
                Globals.ThisAddIn.Application.ActiveDocument.Unprotect("");
            }
            catch (COMException) { }

            // Replace Images in Document
            Extensions.ReplaceImage(Constants.ImageType.DesignElement, Constants.ImageVariant.FullResolution);
            Extensions.ReplaceImage(Constants.ImageType.Logo, Constants.ImageVariant.FullResolution);

            // Get last Active Printer
            string lastPrinter = Globals.ThisAddIn.Application.ActivePrinter;
            try
            {
                // Set temporary Active Printer
                Globals.ThisAddIn.Application.ActivePrinter = printerName;
            }
            catch (Exception)
            {
                MessageBox.Show("Drucker konnte nicht konfiguriert werden!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }


            int howLongtoWaitInSeconds = 50;
            TimeSpan sp = new TimeSpan();
            DateTime t = DateTime.Now;

            int backgroundPrintingCount = Globals.ThisAddIn.Application.BackgroundPrintingStatus;            
            

            try
            {
                // Print Document
                Globals.ThisAddIn.Application.PrintOut(
                    Constants.FalseObject, // Background
                    Constants.Missing, // Append
                    Constants.Missing, // Range
                    Constants.Missing, // Output File Name
                    Constants.Missing, // From
                    Constants.Missing, // To
                    Constants.Missing, // Item
                    Constants.Missing, // Copies
                    Constants.Missing, // Pages
                    Constants.Missing, // PageType
                    Constants.Missing, // PrintToFile
                    Constants.Missing, // Collate
                    Constants.Missing, // ActivePrinterMacGX
                    Constants.Missing, // ManualDuplexPrint
                    Constants.Missing, // PrintZoomColumn
                    Constants.Missing, // PrintZoomRow
                    Constants.Missing, // PrintZoomPaperWidth
                    Constants.Missing  // PrintZoomPaperHeight
                );
            }
            catch (Exception)
            {
                MessageBox.Show("Dokument konnte nicht gedruckt werden!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            // Restore Active Printer
            try
            {
                Globals.ThisAddIn.Application.ActivePrinter = lastPrinter;
            }
            catch (Exception)
            {
                MessageBox.Show("Ursprüngliche Druckereinstellungen konnte nicht wiederhergestellt werden!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }


            
            while (Globals.ThisAddIn.Application.BackgroundPrintingStatus > backgroundPrintingCount)
            {
                //Just in case.
                if (DateTime.Now.Subtract(t).TotalSeconds > howLongtoWaitInSeconds) break;
            }



            // Replace Images with low-resolution Versions
            Extensions.ReplaceImage(Constants.ImageType.DesignElement, Constants.ImageVariant.LowResolution);
            Extensions.ReplaceImage(Constants.ImageType.Logo, Constants.ImageVariant.LowResolution);

            try
            {
                // Protect Document
                Globals.ThisAddIn.Application.ActiveDocument.Protect(WdProtectionType.wdAllowOnlyFormFields, Constants.FalseObject, "");
            }
            catch (COMException) { }
        }
  • If you are still not sure then how about using the Task object Wait method to double insurance:
        void Cs_VSTO_Replace_Images_in_Document_before_and_after_PrintOut()
        {
            string f = @"X:\t.docx";
            string fPrint = @"X:\t.pdf";//just for test, because I don't have a printer to test
            string pic = @"X:\2.png";


            if (File.Exists(fPrint)) File.Delete(fPrint);

            Microsoft.Office.Interop.Word.Document d = Globals.ThisAddIn.Application.Documents.Open(f);

            System.Threading.Tasks.Task wait =
                System.Threading.Tasks.Task.Run(() =>
                    {
                        int howLongtoWaitInSeconds = 50;
                        TimeSpan sp = new TimeSpan();
                        DateTime t = DateTime.Now;

                        int backgroundPrintingCount = Globals.ThisAddIn.Application.BackgroundPrintingStatus;

                        d.PrintOut(Background: false, OutputFileName: fPrint, PrintToFile: true);
                        while (Globals.ThisAddIn.Application.BackgroundPrintingStatus > backgroundPrintingCount)
                        {
                            //Just in case.
                            if (DateTime.Now.Subtract(t).TotalSeconds > howLongtoWaitInSeconds) break;
                        }
                        //if you also print to file then use Fils.Exsted to check
                        /*
                        while(!File.Exists(fPrint)) {
                            //Just in case.
                            if (DateTime.Now.Subtract(t).TotalSeconds > howLongtoWaitInSeconds) break;
                        }
                        */
                    }
                    );

            wait.Wait();

            //just for test
            //d.Range().Find.Execute(FindText: "易", Replace: Microsoft.Office.Interop.Word.WdReplace.wdReplaceAll, ReplaceWith: "●");
            System.Media.SystemSounds.Exclamation.Play();
            foreach (Microsoft.Office.Interop.Word.InlineShape shp in d.InlineShapes)
            {
                d.Range().InlineShapes.AddPicture(FileName: pic, Range: shp.Range);
                shp.Delete();
            }

            d.Close(SaveChanges: Microsoft.Office.Interop.Word.WdSaveOptions.wdSaveChanges);
            Globals.ThisAddIn.Application.Documents.Open(f);
            System.Media.SystemSounds.Asterisk.Play();
            Process.Start(fPrint);
        }

And you code will be:

        public static void PrintDocumentAsPDF(this WordRibbon _, string printerName = "Microsoft Print to PDF")
        {

            System.Threading.Tasks.Task wait = System.Threading.Tasks.Task.Run(()=>{

                try
                {
                    // Un-Protected Document
                    Globals.ThisAddIn.Application.ActiveDocument.Unprotect("");
                }
                catch (COMException) { }

                // Replace Images in Document
                Extensions.ReplaceImage(Constants.ImageType.DesignElement, Constants.ImageVariant.FullResolution);
                Extensions.ReplaceImage(Constants.ImageType.Logo, Constants.ImageVariant.FullResolution);

                // Get last Active Printer
                string lastPrinter = Globals.ThisAddIn.Application.ActivePrinter;
                try
                {
                    // Set temporary Active Printer
                    Globals.ThisAddIn.Application.ActivePrinter = printerName;
                }
                catch (Exception)
                {
                    MessageBox.Show("Drucker konnte nicht konfiguriert werden!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }


                int howLongtoWaitInSeconds = 50;
                TimeSpan sp = new TimeSpan();
                DateTime t = DateTime.Now;

                int backgroundPrintingCount = Globals.ThisAddIn.Application.BackgroundPrintingStatus;


                try
                {
                    // Print Document
                    Globals.ThisAddIn.Application.PrintOut(
                        Constants.FalseObject, // Background
                        Constants.Missing, // Append
                        Constants.Missing, // Range
                        Constants.Missing, // Output File Name
                        Constants.Missing, // From
                        Constants.Missing, // To
                        Constants.Missing, // Item
                        Constants.Missing, // Copies
                        Constants.Missing, // Pages
                        Constants.Missing, // PageType
                        Constants.Missing, // PrintToFile
                        Constants.Missing, // Collate
                        Constants.Missing, // ActivePrinterMacGX
                        Constants.Missing, // ManualDuplexPrint
                        Constants.Missing, // PrintZoomColumn
                        Constants.Missing, // PrintZoomRow
                        Constants.Missing, // PrintZoomPaperWidth
                        Constants.Missing  // PrintZoomPaperHeight
                    );
                }
                catch (Exception)
                {
                    MessageBox.Show("Dokument konnte nicht gedruckt werden!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

                // Restore Active Printer
                try
                {
                    Globals.ThisAddIn.Application.ActivePrinter = lastPrinter;
                }
                catch (Exception)
                {
                    MessageBox.Show("Ursprüngliche Druckereinstellungen konnte nicht wiederhergestellt werden!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }



                while (Globals.ThisAddIn.Application.BackgroundPrintingStatus > backgroundPrintingCount)
                {
                    //Just in case.
                    if (DateTime.Now.Subtract(t).TotalSeconds > howLongtoWaitInSeconds) break;
                }

            });

            wait.Wait();

            // Replace Images with low-resolution Versions
            Extensions.ReplaceImage(Constants.ImageType.DesignElement, Constants.ImageVariant.LowResolution);
            Extensions.ReplaceImage(Constants.ImageType.Logo, Constants.ImageVariant.LowResolution);

            try
            {
                // Protect Document
                Globals.ThisAddIn.Application.ActiveDocument.Protect(WdProtectionType.wdAllowOnlyFormFields, Constants.FalseObject, "");
            }
            catch (COMException) { }
        }

Would you like to take a chance?

However, your function is named PrintDocumentAsPDF, but the arguments PrintToFile and OutputFileName both are Constants.Missing in your code. How can yours print to file as PDF without going along like mine?

Oscar Sun
  • 1,427
  • 2
  • 8
  • 13