Organizations often have numerous Word documents based on a specific template. Changes to this template, such as updating the company logo, are not uncommon. In such cases, you may need to update all existing documents to reflect these changes. This situation frequently arises during company acquisitions or when documents are migrated to a new system with a different template. I want to share my insights on how to do this efficiently.
You could manually update each document by following these steps:
- Open the source document in Word, select and copy all the content.
- Create a new document using the new template and paste the copied content.
- Save the document.
While this method works, it is time-consuming. Is there a way to automate this process?
Fortunately, there is. You can use PowerShell and the Word ComObject to automate the task. The concept is the same as the manual method, but the script includes a few additional steps. I have also added support for Word sections and page layout changes between sections.
Automating the Process with PowerShell
- Copying Content Between Word Documents:
- Iterate over sections of a source Word document ($srcDoc).
- For each section, calculate the start and end positions of the section’s range, excluding the last paragraph which contains header and footer information.
- If the range is not empty, select the range, copy it, and paste it into the destination Word document ($dstDoc) at the end of the document, using the destination styles for formatting.
- Saving the Destination Document:
- Disable warnings about markup in the document before saving.
- Save the destination document ($dstDoc) to a specified path ($dstDocPath) in the Word document format.
- Closing Documents and Word Application:
- Close the source document without saving changes.
- Close the destination document.
- Quit the Word application.
$srcDocPath = "C:\Temp\SourceDoc.docx"
$dstDocPath = "C:\Temp\DestinationDoc.docx"
$templatePath = "C:\Temp\DocTemplate.dotx"
# Load the required assembly (use Windows search to find this)
Add-Type -LiteralPath "C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.Word\15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Word.dll"
# Open Word
$word = New-Object -comobject word.application
$word.visible = $true # make word visible
$word.DisplayAlerts = [Microsoft.Office.Interop.Word.WdAlertLevel]::wdAlertsNone # surpress alerts. Script get's easily stuck to those
# open source document
$srcDoc = $word.documents.open($srcDocPath, $false, $true)
# create new document from template
$dstDoc = $word.Documents.add($templatePath)
# handle first section oritentation
if ($null -ne $srcDoc.Sections.Item(1).PageSetup.Orientation) {
$dstDoc.Sections.Item(1).PageSetup.Orientation = $srcDoc.Sections.Item(1).PageSetup.Orientation
$currentPageOrientation = $dstDoc.Sections.Item(1).PageSetup.Orientation
}
else {
$currentPageOrientation = $null
}
$dstSection = $dstDoc.Sections.Item(1)
# copy sections and content inside sections without the seaction header and footer
for ($sectionIndex = 1; $sectionIndex -le $srcDoc.Sections.Count; $sectionIndex++) {
$srcSection = $srcDoc.Sections.Item($sectionIndex)
# fix page orientation
if ($currentPageOrientation -ne $srcSection.PageSetup.Orientation) {
$dstSection = $dstDoc.Sections.Add()
if ($null -eq $srcSection.PageSetup.Orientation) {
$dstSection.PageSetup.Orientation = [Microsoft.Office.Interop.Word.WdOrientation]::wdOrienPortrait
}
else {
$dstSection.PageSetup.Orientation = $srcSection.PageSetup.Orientation
}
$currentPageOrientation = $srcSection.PageSetup.Orientation
}
# do not copy the last paragraph, because it icludes the header and footer information
$start = $srcSection.Range.Start
$end = $srcSection.Range.End - 1
# do not copy empty ranges
if ($start -ne $end) {
$srcDoc.Range($start, $end).Select()
$word.Selection.Copy()
$dstRange = $dstDoc.Range($dstSection.Range.End - 1, $dstSection.Range.End - 1)
$dstRange.Collapse($Word.WdCollapseDirection.wdCollapseEnd)
$dstRange.PasteAndFormat($Word.WdRecoveryType.wdUseDestinationStylesRecovery)
}
}
# Save the document
$word.Options.WarnBeforeSavingPrintingSendingMarkup = $false # surpress notification about markup in document
[ref]$saveFormat = "microsoft.office.interop.word.WdSaveFormat" -as [type]
$dstDoc.saveas([ref]$dstDocPath, [ref]$saveFormat::wdFormatDocument)
# close the documents
$srcDoc.Close([ref][Microsoft.Office.Interop.Word.WdSaveOptions]::wdDoNotSaveChanges)
$dstDoc.Close()
# close word
$word.Quit()
This method requires you to have all the documents saved locally or in a shared folder. In the case of a migration, you can use a custom PowerShell script or tools like ShareGate to dump all the files to a local folder. After the conversion, you can use your preferred method to migrate the documents to your new system.
Additional Tips
User input is often required when a document is protected or marked as final. To handle these situations seamlessly, you can incorporate the following logic into your script.
# Check if the document is protected
if ($srcDoc.ProtectionType -ne [Microsoft.Office.Interop.Word.WdProtectionType]::wdNoProtection) {
# Remove the protection
$srcDoc.Unprotect()
}
# Check if the document is marked as final
if ($srcDoc.Final) {
# Remove the "Mark as Final" status
$srcDoc.Final = $false
}
By following these steps, you can significantly reduce the time and effort required to update your Word documents. Good luck with your document conversions, hope you find this helpful.
Links