search and replace, save, convert to PDF, recursively
So, I have a bunch of nested folders full of Word files.
My goal here is to loop through all the folders, go to each Word file, do a search and replace operation, save the word file, then export a PDF version of that Word file, then move on.
To attempt this, I have cobbled together some answers that I got on this forum to previous questions. But when I run the code, it just hangs, and I have to 'end task' on Word (and it doesn't actually process any documents, as far as I can tell.) What am I doing wrong?
Code:
Option Explicit
Dim FSO As Object, oFolder As Object, StrFolds As String
Sub RecursiveDocuments()
' This recursively goes through a tree of folders
' and does something
Application.ScreenUpdating = False
Dim TopLevelFolder As String
Dim TheFolders As Variant, aFolder As Variant
Dim strFile As String, i As Long
TopLevelFolder = GetFolder
StrFolds = vbCr & TopLevelFolder
If TopLevelFolder = "" Then Exit Sub
If FSO Is Nothing Then
Set FSO = CreateObject("Scripting.FileSystemObject")
End If
'Get the sub-folder structure
Set TheFolders = FSO.GetFolder(TopLevelFolder).SubFolders
For Each aFolder In TheFolders
RecurseWriteFolderName (aFolder)
Next
'Process the documents in each folder
For i = 1 To UBound(Split(StrFolds, vbCr))
strFile = Dir(CStr(Split(StrFolds, vbCr)(i)) & "\*.doc", vbNormal)
Do While strFile <> ""
batchSpecsUpdate
Loop
Next
Application.ScreenUpdating = True
End Sub
Sub RecurseWriteFolderName(aFolder)
Dim SubFolders As Variant, SubFolder As Variant
Set SubFolders = FSO.GetFolder(aFolder).SubFolders
StrFolds = StrFolds & vbCr & CStr(aFolder)
On Error Resume Next
For Each SubFolder In SubFolders
RecurseWriteFolderName (SubFolder)
Next
End Sub
Function GetFolder() As String
Dim oFolder As Object
GetFolder = ""
Set oFolder = CreateObject("Shell.Application").BrowseForFolder(0, "Choose a folder", 0)
If (Not oFolder Is Nothing) Then GetFolder = oFolder.Items.Item.Path
Set oFolder = Nothing
End Function
Sub specsUpdate()
'
' specsUpdate Macro
'
'
If ActiveWindow.View.SplitSpecial <> wdPaneNone Then
ActiveWindow.Panes(2).Close
End If
If ActiveWindow.ActivePane.View.Type = wdNormalView Or ActiveWindow. _
ActivePane.View.Type = wdOutlineView Then
ActiveWindow.ActivePane.View.Type = wdPrintView
End If
ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "REVISION A"
.Replacement.Text = "REVISION B"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
With Selection.Find
.Text = "1 APRIL 1776"
.Replacement.Text = "31 DECEMBER 1492"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
End Sub
Sub batchSpecsUpdate()
'Written by Richard V. Michaels, Office Apps and Services MVP
'http://www.greatcirclelearning.com
'Browse for files to update
Dim i As Integer, selFiles() As String
Dim strFolderPath As String, Sep As String
Sep = Application.PathSeparator
Erase selFiles
#If Mac Then
Dim iPath As String, iScript As String, iFiles As String
Dim iSplit As Variant, N As Long, FileFormat As String
FileFormat = "{""org.openxmlformats.wordprocessingml.document"",""com.microsoft.word.doc""," & _
"""org.openxmlformats.wordprocessingml.document.macroenabled""}"
On Error Resume Next
iPath = MacScript("return (path to documents folder) as String")
If Application.Version < 15 Then
'Mac Office 2011
iScript = "set applescript's text item delimiters to {ASCII character 10} " & vbNewLine & _
"set theFiles to (choose file of type" & _
" " & FileFormat & " " & _
"with prompt ""Select the files to update"" default location alias """ & _
iPath & """ with multiple selections allowed) as string" & vbNewLine & _
"set applescript's text item delimiters to """" " & vbNewLine & _
"return theFiles"
Else
'Mac Office 2016
iScript = "set theFiles to (choose file of type" & _
" " & FileFormat & " " & _
"with prompt ""Select the files to update"" default location alias """ & _
iPath & """ with multiple selections allowed)" & vbNewLine & _
"set thePOSIXFiles to {}" & vbNewLine & _
"repeat with aFile in theFiles" & vbNewLine & _
"set end of thePOSIXFiles to POSIX path of aFile" & vbNewLine & _
"end repeat" & vbNewLine & _
"set {TID, text item delimiters} to {text item delimiters, ASCII character 10}" & vbNewLine & _
"set thePOSIXFiles to thePOSIXFiles as text" & vbNewLine & _
"set text item delimiters to TID" & vbNewLine & _
"return thePOSIXFiles"
End If
iFiles = MacScript(iScript)
On Error GoTo 0
If iFiles <> "" Then
iSplit = Split(iFiles, Chr(10))
ReDim Preserve selFiles(UBound(iSplit))
strFolderPath = Left(iSplit(0), InStrRev(iSplit(0), Sep))
For N = LBound(iSplit) To UBound(iSplit)
selFiles(N) = iSplit(N)
Next N
Else
Exit Sub
End If
#Else
'Windows Office 2016, 2013, 2010, 2007
With Application.FileDialog(msoFileDialogFilePicker)
.Title = "Select the files to update"
.InitialFileName = CurDir
.AllowMultiSelect = True
.Filters.Clear
.Filters.Add "All Word Files", "*.docx; *.docm; *.doc", 1
If .Show = 0 Then
Exit Sub
End If
ReDim Preserve selFiles(.SelectedItems.Count - 1)
strFolderPath = Left(.SelectedItems(1), InStrRev(.SelectedItems(1), Sep))
For i = 0 To .SelectedItems.Count - 1
selFiles(i) = .SelectedItems(i + 1)
Next
.Filters.Clear
End With
#End If
Dim doc As Word.Document, FirstTime As Boolean
FirstTime = True
On Error GoTo errHandler
For i = 0 To UBound(selFiles)
'The following is setup to perform a conditional first time check
'Depending on your exact requirements, you may not need it.
If FirstTime = True Then
Set doc = Documents.Open(FileName:=selFiles(i))
'This is where you will insert your code for applying the edits
'you want to perform
specsUpdate
Word_ExportPDF
doc.Save
DoEvents
FirstTime = False
Else
On Error GoTo 0
Set doc = Documents.Open(FileName:=selFiles(i))
'This is where you will insert your code for replicating the edits
'you made to the first document
specsUpdate
Word_ExportPDF
doc.Close Word.WdSaveOptions.wdSaveChanges
DoEvents
End If
Next
MsgBox "Update Complete. Your original document remains open.", vbInformation, "Success"
errHandler:
If Err.Number <> 0 Then
MsgBox Err.Description, vbExclamation, "Uh-Oh!"
End If
End Sub
Sub Word_ExportPDF()
'PURPOSE: Generate A PDF Document From Current Word Document
'NOTES: PDF Will Be Saved To Same Folder As Word Document File
'SOURCE: www.TheSpreadsheetGuru.com/the-code-vault
Dim CurrentFolder As String
Dim FileName As String
Dim myPath As String
Dim UniqueName As Boolean
UniqueName = False
'Store Information About Word File
myPath = ActiveDocument.FullName
CurrentFolder = ActiveDocument.Path & "\"
FileName = Mid(myPath, InStrRev(myPath, "\") + 1, _
InStrRev(myPath, ".") - InStrRev(myPath, "\") - 1)
'Does File Already Exist?
'If so, too bad
' Do While UniqueName = False
' DirFile = CurrentFolder & FileName & ".pdf"
' If Len(Dir(DirFile)) <> 0 Then
' UserAnswer = MsgBox("File Already Exists! Click " & _
' "[Yes] to override. Click [No] to Rename.", vbYesNoCancel)
' If UserAnswer = vbYes Then
UniqueName = True
' ElseIf UserAnswer = vbNo Then
' Do
' 'Retrieve New File Name
' FileName = InputBox("Provide New File Name " & _
' "(will ask again if you provide an invalid file name)", _
' "Enter File Name", FileName)
'Exit if User Wants To
' If FileName = "False" Or FileName = "" Then Exit Sub
' Loop While ValidFileName(FileName) = False
' Else
' Exit Sub 'Cancel
' End If
' Else
' UniqueName = True
' End If
' Loop
'Save As PDF Document
On Error GoTo ProblemSaving
ActiveDocument.ExportAsFixedFormat _
OutputFileName:=CurrentFolder & FileName & ".pdf", _
ExportFormat:=wdExportFormatPDF
On Error GoTo 0
'Confirm Save To User
With ActiveDocument
FolderName = Mid(.Path, InStrRev(.Path, "\") + 1, Len(.Path) - InStrRev(.Path, "\"))
End With
' MsgBox "PDF Saved in the Folder: " & FolderName
Exit Sub
'Error Handlers
ProblemSaving:
MsgBox "There was a problem saving your PDF. This is most commonly caused" & _
" by the original PDF file already being open."
Exit Sub
End Sub
Function ValidFileName(FileName As String) As Boolean
'PURPOSE: Determine If A Given Word Document File Name Is Valid
'SOURCE: www.TheSpreadsheetGuru.com/the-code-vault
Dim TempPath As String
Dim doc As Document
'Determine Folder Where Temporary Files Are Stored
TempPath = Environ("TEMP")
'Create a Temporary XLS file (XLS in case there are macros)
On Error GoTo InvalidFileName
Set doc = ActiveDocument.SaveAs2(ActiveDocument.TempPath & _
"\" & FileName & ".doc", wdFormatDocument)
On Error Resume Next
'Delete Temp File
Kill doc.FullName
'File Name is Valid
ValidFileName = True
Exit Function
'ERROR HANDLERS
InvalidFileName:
'File Name is Invalid
ValidFileName = False
End Function
Sub UpdateDocuments()
' This accepts changes on all files in a directory.
Application.ScreenUpdating = False
Dim strFolder As String, strFile As String, strDocNm As String, wdDoc As Document, Rng As Range
strDocNm = ActiveDocument.FullName
strFolder = GetFolder
If strFolder = "" Then Exit Sub
strFile = Dir(strFolder & "\*.doc", vbNormal)
While strFile <> ""
If strFolder & "\" & strFile <> strDocNm Then
Set wdDoc = Documents.Open(FileName:=strFolder & "\" & strFile, AddToRecentFiles:=False, Visible:=False)
With wdDoc
For Each Rng In .StoryRanges
Rng.Revisions.AcceptAll
Next
.Close SaveChanges:=True
End With
End If
strFile = Dir()
Wend
Set wdDoc = Nothing
Application.ScreenUpdating = True
End Sub
'Function GetFolder() As String
'Dim oFolder As Object
' GetFolder = ""
'Set oFolder = CreateObject("Shell.Application").BrowseForFolder(0, "Choose a folder", 0)
'If (Not oFolder Is Nothing) Then GetFolder = oFolder.Items.Item.Path
'Set oFolder = Nothing
'End Function
Sub batchWordToPDF()
'Written by Richard V. Michaels, Office Apps and Services MVP
'http://www.greatcirclelearning.com
'Browse for files to update
Dim i As Integer, selFiles() As String
Dim strFolderPath As String, Sep As String
Sep = Application.PathSeparator
Erase selFiles
#If Mac Then
Dim iPath As String, iScript As String, iFiles As String
Dim iSplit As Variant, N As Long, FileFormat As String
FileFormat = "{""org.openxmlformats.wordprocessingml.document"",""com.microsoft.word.doc""," & _
"""org.openxmlformats.wordprocessingml.document.macroenabled""}"
On Error Resume Next
iPath = MacScript("return (path to documents folder) as String")
If Application.Version < 15 Then
'Mac Office 2011
iScript = "set applescript's text item delimiters to {ASCII character 10} " & vbNewLine & _
"set theFiles to (choose file of type" & _
" " & FileFormat & " " & _
"with prompt ""Select the files to update"" default location alias """ & _
iPath & """ with multiple selections allowed) as string" & vbNewLine & _
"set applescript's text item delimiters to """" " & vbNewLine & _
"return theFiles"
Else
'Mac Office 2016
iScript = "set theFiles to (choose file of type" & _
" " & FileFormat & " " & _
"with prompt ""Select the files to update"" default location alias """ & _
iPath & """ with multiple selections allowed)" & vbNewLine & _
"set thePOSIXFiles to {}" & vbNewLine & _
"repeat with aFile in theFiles" & vbNewLine & _
"set end of thePOSIXFiles to POSIX path of aFile" & vbNewLine & _
"end repeat" & vbNewLine & _
"set {TID, text item delimiters} to {text item delimiters, ASCII character 10}" & vbNewLine & _
"set thePOSIXFiles to thePOSIXFiles as text" & vbNewLine & _
"set text item delimiters to TID" & vbNewLine & _
"return thePOSIXFiles"
End If
iFiles = MacScript(iScript)
On Error GoTo 0
If iFiles <> "" Then
iSplit = Split(iFiles, Chr(10))
ReDim Preserve selFiles(UBound(iSplit))
strFolderPath = Left(iSplit(0), InStrRev(iSplit(0), Sep))
For N = LBound(iSplit) To UBound(iSplit)
selFiles(N) = iSplit(N)
Next N
Else
Exit Sub
End If
#Else
'Windows Office 2016, 2013, 2010, 2007
With Application.FileDialog(msoFileDialogFilePicker)
.Title = "Select the files to update"
.InitialFileName = CurDir
.AllowMultiSelect = True
.Filters.Clear
.Filters.Add "All Word Files", "*.docx; *.docm; *.doc", 1
If .Show = 0 Then
Exit Sub
End If
ReDim Preserve selFiles(.SelectedItems.Count - 1)
strFolderPath = Left(.SelectedItems(1), InStrRev(.SelectedItems(1), Sep))
For i = 0 To .SelectedItems.Count - 1
selFiles(i) = .SelectedItems(i + 1)
Next
.Filters.Clear
End With
#End If
Dim doc As Word.Document, FirstTime As Boolean
FirstTime = True
On Error GoTo errHandler
For i = 0 To UBound(selFiles)
'The following is setup to perform a conditional first time check
'Depending on your exact requirements, you may not need it.
If FirstTime = True Then
Set doc = Documents.Open(FileName:=selFiles(i))
'This is where you will insert your code for applying the edits
'you want to perform
Word_ExportPDF
doc.Save
DoEvents
FirstTime = False
Else
On Error GoTo 0
Set doc = Documents.Open(FileName:=selFiles(i))
'This is where you will insert your code for replicating the edits
'you made to the first document
specsUpdate
Word_ExportPDF
doc.Close Word.WdSaveOptions.wdSaveChanges
DoEvents
End If
Next
MsgBox "Update Complete. Your original document remains open.", vbInformation, "Success"
errHandler:
If Err.Number <> 0 Then
MsgBox Err.Description, vbExclamation, "Uh-Oh!"
End If
End Sub
|