Using PowerShell, I need to write a script which would remove all hidden text of a Word Document.
Here is what I have so far :
$WordDocument = Get-Item "C:\MyWordDocument.docx"
$word_app = New-Object -ComObject Word.Application
$word_app.Visible = $false
$document = $word_app.Documents.Open($WordDocument.FullName)
$objSelection = $word_app.Selection
$objSelection.Font.Hidden = $True
$FindText = "" # search on formatting only (according to MS doc)
$wdFindContinue = 1
$ReplaceAll = 2
$MatchCase = $False
$MatchWholeWord = $False
$MatchWildcards = $False
$MatchSoundsLike = $False
$MatchAllWordForms = $False
$Forward = $True
$Wrap = $wdFindContinue
$Format = $True # ?
$ReplaceWith = ""
$a = $objSelection.Find.Execute($FindText,$MatchCase,$MatchWholeWord, `
$MatchWildcards,$MatchSoundsLike,$MatchAllWordForms,$Forward,`
$Wrap,$Format,$ReplaceWith,$ReplaceAll)
$document.Save()
$document.Close()
$word_app.Quit()
It does not work, and I cannot figure out why.
Any idea ?
The mistake is where you set the search filter to find hidden text. Instead of $objSelection.Font.Hidden = $True (this actually hides the currently selected text) you need to set the property on the $objSelection.Find object:
$objSelection.Find.Font.Hidden = $True
Related
I'm trying to replace multiple strings in a word document using PowerShell, but only one string is replaced when running the code below:
#Includes
Add-Type -AssemblyName System.Windows.Forms
#Functions
#Function to find and replace in a word document
function FindAndReplace($objSelection, $findText,$replaceWith){
$matchCase = $true
$matchWholeWord = $true
$matchWildcards = $false
$matchSoundsLike = $false
$matchAllWordForms = $false
$forward = $true
$wrap = [Microsoft.Office.Interop.Word.WdFindWrap]::wdReplaceAll
$format = $false
$replace = [Microsoft.Office.Interop.Word.WdFindWrap]::wdFindContinue
$objSelection.Find.Execute($findText,$matchCase,$matchWholeWord,$matchWildcards,$matchSoundsLike,$matchAllWordForms,$forward,$wrap,$format,$replaceWith, $replace) > $null
}
$item1 = "Should"
$item2 = "this"
$item3 = "work"
$item4 = "?"
$fileName = "NewFile"
#Opens a file browsers to select a word document
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property #{
InitialDirectory = [Environment]::GetFolderPath('Desktop')
Filter = 'Documents (*.docx)|*.docx'
}
Write-Host "Select word template file"
$FileBrowser.ShowDialog()
$templateFile = $FileBrowser.FileName
$word = New-Object -comobject Word.Application
$word.Visible = $false
$template = $word.Documents.Open($templateFile)
$selection = $template.ActiveWindow.Selection
FindAndReplace $selection '#ITEM1#' $item1
FindAndReplace $selection '#ITEM2#' $item2
FindAndReplace $selection '#ITEM3#' $item3
FindAndReplace $selection '#ITEM4#' $item4
$fileName = $fileName
$template.SaveAs($fileName)
$word.Quit()
If I comment out FindAndReplace the first one that runs works, but subsequent calls do not.
For example running this as is results in:
Input Output
#ITEM1# Should
#ITEM2# #ITEM2#
#ITEM3# #ITEM3#
#ITEM4# #ITEM4#
I'm not sure what I'm missing, any help would be appreciated
As was suggested it appears that the cursor was not returning to the beginning of the document. I added the following code:
Set-Variable -Name wdGoToLine -Value 3 -Option Constant
Set-Variable -Name wdGoToAbsolute -Value 1 -Option Constant
To the beginning of my script and:
$objSelection.GoTo($wdGoToLine, $wdGoToAbsolute, 1) > $null
as the first line in my FindAndReplace function, and now it works as expected.
There may be a more elegant solution, but this works for me
I would like to extract formatting information from Word documents with PowerShell. Using Word you can search formatted pieces of texts. This way Word highlights the parts satisfying the criterion (e.g. green underlined text). With this one I can find italic text in PowerShell as well:
$objWord = New-Object -Com Word.Application
$myWordFile = 'C:\My\Word\File.docx'
$objDocument = $objWord.Documents.Open($myWordFile)
$objDocument.Paragraphs[0].Range.Find.Font.Italic = $true
$objDocument.Paragraphs[0].Range.Find.Execute()
However, I'm curious about the italic text itself, a similar thing as the content of the $matches for -match.
Here is an example of what you are trying to do …
This is find and replace, so, ignore that replace part if that is not your end goal - it is also find words and then applying italics, but the same approach can be used to just find all italicized words.
$application = New-Object -comobject word.application
$application.visible = $true
$document = $application.documents.open("C:fsoTest.docx")
$selection = $application.Selection
$words = "exchange","sql"
$matchCase = $false
$matchWholeWord = $true
$matchWildCards = $false
$matchSoundsLike = $false
$matchAllWordForms = $false
$forward = $true
$wrap = 1
$format = $true
$replace = 2
Foreach ($word in $words)
{
$findText = $word
$replaceWith = $word
$selection.find.replacement.font.italic = $true
$exeRTN = $selection.find.execute($findText,$matchCase,
$matchWholeWord,$matchWIldCards,$matchSoundsLike,
$matchAllWordForms,$forward,$wrap,$format,$replaceWith,
$replace)
}
… as documented here:
Hey, Scripting Guy! How Can I Italicize Specific Words in a Microsoft Word Document?
i want to replace a string with an hyperlink
i try with something like this
Update:
$FindText = "[E-mail]"
$email ="asdadasd#asdada.com"
$a=$objSelection.Find.Execute($FindText)
$newaddress = $objSelection.Hyperlinks.Add($objSelection.Range,$email) )
but this insert the email at beginnig of file word don't replace the string "[E-mail]"
Add-Type -AssemblyName "Microsoft.Office.Interop.Word"
$wdunits = "Microsoft.Office.Interop.Word.wdunits" -as [type]
$objWord = New-Object -ComObject Word.Application
$objWord.Visible = $false
$findText = "[E-mail]"
$emailAddress = "someemail#example.com"
$mailTo = "mailto:"+$emailAddress
$objDoc = $objWord.Documents.Open("Path\to\input.docx")
$saveAs = "Path\to\output.docx")
$range = $objDoc.Content
$null = $range.movestart($wdunits::wdword,$range.start)
$objSelection = $objWord.Selection
$matchCase = $false
$matchWholeWord = $true
$matchWildcards = $false
$matchSoundsLike = $false
$matchAllWordForms = $false
$forward = $true
$wrap = 1
$format = $False
$wdReplaceNone = 0
$wdFindContinue = 1
$wdReplaceAll = 2
$wordFound = $range.find.execute($findText,$matchCase,$matchWholeWord,$matchWildCards,$matchSoundsLike,$matchAllWordForms,$forward,$wrap)
if($wordFound)
{
if ($range.style.namelocal -eq "normal")
{
$null = $objDoc.Hyperlinks.Add($range,$mailTo,$null,$null,$emailAddress)
}
}
$objDoc.SaveAs($saveAs)
$objDoc.Close()
$objWord.Quit()
Remove-Variable -Name objWord
[gc]::Collect()
[gc]::WaitForPendingFinalizers()
Kinda ugly, but this script will do what you need. It loads the .docx specified with $objDoc, finds all instances of $findText, and replaces it with a mailto link for $emailAddress and then saves the changes to $saveAs.
Most of this based on a "Hey, Scripting Guy" Article
I have a question about my code.
I realize a form in powershell and when i click on the button, The code add in the datagridview the variable $Test, if checkbox is checked and $test = YES. After the first action on the button, I change the variable $test to "NO".
But when I click again on the button, the variable $Test is again to "YES" ... and I don't understand why the variable is reset to YES and not saved with the new value "NO"
Example :
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
$Test = "YES"
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$MyForm = New-Object System.Windows.Forms.Form
$MyForm.Text="test"
$MyForm.Size = New-Object System.Drawing.Size(1000,350)
$mButton1 = New-Object System.Windows.Forms.Button
$mButton1.Text="test"
$mButton1.Top="155"
$mButton1.Left="30"
$mButton1.Anchor="Left,Top"
$mButton1.Size = New-Object System.Drawing.Size(100,23)
$MyForm.Controls.Add($mButton1)
$mButton1.Add_Click({
If($mCheckBox4.Checked -eq $True -and $Test -eq "YES"){
write-host "IN IF"$Test
$DataGridView.Rows.Add($Test)
}
$Test = "NO"
write-host "Out IF"$Test
})
$mCheckBox4 = New-Object System.Windows.Forms.CheckBox
$mCheckBox4.Text="test"
$mCheckBox4.Top="80"
$mCheckBox4.Left="10"
$mCheckBox4.Anchor="Left,Top"
$mCheckBox4.Size = New-Object System.Drawing.Size(100,23)
$MyForm.Controls.Add($mCheckBox4)
$dataGridView = New-Object System.Windows.Forms.DataGridView
$dataGridView.RowTemplate.DefaultCellStyle.ForeColor = [System.Drawing.Color]::FromArgb(255,0,128,0)
$dataGridView.Name = 'dataGridView'
$dataGridView.DataBindings.DefaultDataSourceUpdateMode = 0
$dataGridView.ReadOnly = $True
$dataGridView.Top="5"
$dataGridView.Left="500"
$dataGridView.Size = New-Object System.Drawing.Size(425,185)
$dataGridView.AllowUserToDeleteRows = $False
$dataGridView.RowHeadersVisible = $False
#$dataGridView.TabIndex = 8
$dataGridView.SelectionMode = 'FullRowSelect'
#$dataGridView.Anchor = 15
$dataGridView.AutoSizeColumnsMode = 16
$dataGridView.AllowUserToAddRows = $False
#$dataGridView.ColumnHeadersHeightSizeMode = 2
#$dataGridView.AllowUserToOrderColumns = $True
#$dataGridView.AllowUserToResizeRows = $False
$dataGridView.ColumnCount = 1
$dataGridView.ColumnHeadersVisible = $true
$dataGridView.Columns[0].Name = "Trigger"
$MyForm.Controls.Add($dataGridView)
$MyForm.ShowDialog()
https://www.hostingpics.net/viewer.php?id=8107302017081612h0222.png
In this pictures, i add a write-host to see the value of my variable $Test, and he switch into "YES/NO" "YES/NO" ...
Thanks for your help
Regards
I've managed to find& edit per one word file. With this code:
$objWord = New-Object -comobject Word.Application
$objWord.Visible = $false
$objDoc = $objWord.Documents.Open("C:\users\stefan\test\New Microsoft Word Document.docx")
$objSelection = $objWord.Selection
$FindText = "that"
$MatchCase = $False
$MatchWholeWord = $true
$MatchWildcards = $False
$MatchSoundsLike = $False
$MatchAllWordForms = $False
$Forward = $True
$Wrap = $wdFindContinue
$Format = $False
$wdReplaceNone = 0
$ReplaceWith = "this"
$wdFindContinue = 1
$a = $objSelection.Find.Execute($FindText,$MatchCase,$MatchWholeWord, `
$MatchWildcards,$MatchSoundsLike,$MatchAllWordForms,$Forward,`
$Wrap,$Format,$ReplaceWith)
$objDoc.Save()
$objWord.Quit()
But I want to do it for whole folder. I've tried to insert something like this:
$objWord = New-Object -comobject Word.Application
$objWord.Visible = $false
$list = Get-ChildItem "c:\users\stefan\test\*.*" -Include *.doc*
foreach($item in $list){
$objDoc = $objWord.Documents.Open($list.FullName,$true)
$objSelection = $objWord.Selection
$FindText = "Sara"
$MatchCase = $False
$MatchWholeWord = $true
$MatchWildcards = $False
$MatchSoundsLike = $False
$MatchAllWordForms = $False
$Forward = $True
$Wrap = $wdFindContinue
$Format = $False
$wdReplaceNone = 0
$ReplaceWith = "AJMOO"
$wdFindContinue = 1
$a = $objSelection.Find.Execute($FindText,$MatchCase,$MatchWholeWord, `
$MatchWildcards,$MatchSoundsLike,$MatchAllWordForms,$Forward,`
$Wrap,$Format,$ReplaceWith)
$objDoc.Save()
$objWord.Quit()
}
Also, it changes only one item that is found, but I want all items in the file.
Thanks.
Multiple Replacements
Also, it changes only one item that is found, but I want all items in the file
This is because you have not set the scope of the replacement to be all items. The is from the next argument you did not specify in your Execute method call. Set a variable called $wdReplaceAll and set it to 2. Then you adjust your call to add that variable.
$a = $objSelection.Find.Execute($FindText,$MatchCase,$MatchWholeWord, `
$MatchWildcards,$MatchSoundsLike,$MatchAllWordForms,$Forward,`
$Wrap,$Format,$ReplaceWith,$wdReplaceAll)
That fixes that issue when run against one file.
Multiple Files
But I want to do it for whole folder
The partial issue with that is your are not properly querying the folder for files. -Include is finicky and work when partnered with -Recurse however you are treating it like a -Filter anyway so adjust as such.
$list = Get-ChildItem "c:\users\stefan\test\" -filter "*.doc*"
Next, when you are looping you are not using the current iteration but the whole collection when calling .open()
$objDoc = $objWord.Documents.Open($list.FullName,$true)
Should instead be
$objDoc = $objWord.Documents.Open($item.FullName,$true)
as per your loop definition.
Now you need to be sure you close each document before you quit the application. Right now you are quitting word inside the loop.
foreach($item in $list){
#.... Stuff and things happens here.
$objDoc.Save()
$objDoc.Close()
}
$objWord.Quit()
Variable declaration and calls
Right now you set $wrap to the value of the variable $wdFindContinue. When that is first called $wdFindContinue is null as it is not set to a few lines later in the code.
$Wrap = $wdFindContinue
#...
$wdFindContinue = 1
Switch the order of these lines or just set $wrap directly to 1. I am unsure of the implications of this being incorrect.
Thankfully to #Matt I've solved my code.
Here is a correct version which works:
$objWord = New-Object -comobject Word.Application
$objWord.Visible = $false
$list = Get-ChildItem "c:\users\stefan\test\*.*" -Include *.doc*
foreach($item in $list){
$objDoc = $objWord.Documents.Open($item.FullName,$true)
$objSelection = $objWord.Selection
$wdFindContinue = 1
$FindText = "Sara"
$MatchCase = $False
$MatchWholeWord = $true
$MatchWildcards = $False
$MatchSoundsLike = $False
$MatchAllWordForms = $False
$Forward = $True
$Wrap = $wdFindContinue
$Format = $False
$wdReplaceNone = 0
$ReplaceWith = "AJMOO"
$wdFindContinue = 1
$ReplaceAll = 2
$a = $objSelection.Find.Execute($FindText,$MatchCase,$MatchWholeWord, `
$MatchWildcards,$MatchSoundsLike,$MatchAllWordForms,$Forward,`
$Wrap,$Format,$ReplaceWith,$ReplaceAll)
$objDoc.Save()
$objDoc.Close()
}
$objWord.Quit()