Dyota's blog

PowerShell: Retemplating a Word document

Old templates, new templates

A colleague of mine asked me to re-template a tranche of Microsoft Word documents. These documents had got left behind in a wave of rebranding, and the header banner on the front page needed to be changed to the new style of banner.

Sure, I thought, with PowerShell, I can do anything!

It wasn't quite like that, and though it wasn't perfect, I pulled it off in the end.

Different approaches

Change the image

I thought that the objective was to change the banner. If there was a way to target the image object in every page and tell it to "be something different", that would do the job.

I come from working in HTML in modern browsers, so I am used to cracking open Dev Tools, hovering over a web page, and having the browser tell me exactly what a particular element is called, and where it is in the code.

MS Word has no such facility, and though I did find a way to find out what an image object was called in a document, I couldn't seem to figure out how to target it using the COM.

Copy the text over

I had to try something else.

My colleague had set up a blank template with the new style of header, and no text.

My second approach was to target the source document, "copy" all of the text and images in there, paste it in an instance of the template document, and "Save As" a new document.

This was the approach that worked in the end. The function that does it below. I had to loop it over a series of targets to make it perform in bulk.

Things I learned

Code

using namespace Microsoft.Office.Interop.Word

function Retemplate ([string] $source, [string] $targetFilename, [string] $destination) {
    
    ### set up Word

        # Create new object
        $word = New-Object -ComObject Word.application

    ###

    ### source document

        # Open document
        $document = $word.Documents.Open("$source\$targetFilename.docx")

        $range = $document.Range()

        # select top to bottom
            [void] $word.Selection.GoTo([WdGoToItem]::wdGoToPercent,[WdGoToDirection]::wdGoToFirst)
            $range.Start = $word.Selection.Start

            [void] $word.Selection.GoTo([WdGoToItem]::wdGoToPercent,[WdGoToDirection]::wdGoToLast)
            $range.End = $word.Selection.End
        #

        $range.Copy()
    ###

    ### new document

        $newDoc = $word.Documents.Open($templateFilename)

        # put the filename in the header
            $section = $newDoc.Sections.Item(1)
            $header = $section.Headers.Item(1)
            $header.Range.Text = $targetFilename

        $word.Selection.Paste()
    
        $newDoc.SaveAs(
            [ref] "$destination\$targetFilename.pdf",
            [ref] [WdSaveFormat]::wdFormatPDF
        )
    ###

    ### clean up

        # Close object
        $newDoc.Close()
        $document.Close()
        $word.Quit()
    

        # Clean up Com object
        $null =[System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject] $word)
        Remove-Variable word

    ###

}

#powershell #word