Error when inserting image using variable for Selection.InlineShapes.AddPicture - vba

I am trying to go through a word document, and replace existing image path strings with the actual image. When I enter the address hard coded, it works. But if I put the same code in a variable I get an error
Error:
Run-time error '5152':
This is not a valid file name.
Try one or more of the following:
* Check the path to make sure it was typed correctly,
* Select a file from the list of files and folders.
Code:
Sub InsertJPGs()
For Each singleLine In ActiveDocument.Paragraphs
Dim Value As Variant
Dim imageName As String
Options.DefaultFilePath(wdDocumentsPath) = "d:\Downloads\ReportImages\"
originalLineText = singleLine.Range.Text
lineText = singleLine.Range.Text
If InStr(lineText, ".jpg") <> 0 Then
singleLine.Range.Select
rangeText = singleLine.Range.Text
imageName = rangeText
imageName = "D:\Downloads\ReportImages\" & rangeText
'imageName = "D:\Downloads\ReportImages\PictureImportTest_ATTICSkylight#1#_img2.jpg"
Selection.InlineShapes.AddPicture FileName:= _
imageName, LinkToFile:=True, SaveWithDocument:=True
End If
If InStr(lineText, "[[[IMAGE LIST]]]") <> 0 Then
Exit For
End If
Next singleLine
End Sub

Try:
Sub Demo()
Application.ScreenUpdating = False
Dim FlNm As String: Const StrPath As String = "d:\Downloads\ReportImages\"
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "<[! ^t^l^13]#.[Jj][Pp][Gg]>"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
FlNm = .Duplicate.Text
.Text = vbNullString
.InlineShapes.AddPicture StrPath & FlNm, False, True, .Duplicate
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
End Sub
If you want to retain the filenames in the document, delete or comment out:
.Text = vbNullString

Related

How to find table column, then move down and replace the cell's content IF it is "N/A"

I have almost 1,800 Word documents that have about 8 pages with unique data in tables. We were just informed that the data we were given for some of those tables is inaccurate and needs to be changed from "N/A" to "0.0%". As "N/A" is used a lot in the document, I unfortunately cannot just find/replace that text.
Using this thread (Macro to find in Word table for specific string in a cell and move x cell left, check isnumeric then set typography on down x cell in the same column) I was able to adjust the code below to find the column header (On-Time Completion Rate) and move to the adjacent cells to update them. However, since this column is for percentages, the IsNumeric code is changing any data it finds due to the percentage symbol.
Is there a way to do the same but instead of using IsNumeric (since it does not work for percentages) check the value in the cell and if it finds "N/A" change it to "0.0%"? This would then need to be repeated for two more tables, with one table have four rows to look through.
Thank you in advance for any help you can offer!
Screenshot of table
Sub Demo()
Application.ScreenUpdating = False
Dim r As Long, c As Long
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "On-time Completion Rate" 'Column Header'
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
If .Information(wdWithInTable) = True Then
r = .Cells(1).RowIndex
c = .Cells(1).ColumnIndex
With .Tables(1)
If Not IsNumeric(Split(.Cell(r + 1, c).Range.Text, vbCr)(0)) Then .Cell(r + 1, c).Range.Text = "0.0%"
If Not IsNumeric(Split(.Cell(r + 2, c).Range.Text, vbCr)(0)) Then .Cell(r + 2, c).Range.Text = "0.0%"
End With
End If
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
End Sub
Try this:
Sub Demo()
Application.ScreenUpdating = False
Dim r As Long, c As Long
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "On-time Completion Rate" 'Column Header'
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
If .Information(wdWithInTable) = True Then
r = .Cells(1).RowIndex
c = .Cells(1).ColumnIndex
With .Tables(1)
If Split(.Cell(r + 1, c).Range.Text, vbCr)(0) = "N/A" Then .Cell(r + 1, c).Range.Text = "0.0%"
If Split(.Cell(r + 2, c).Range.Text, vbCr)(0) = "N/A" Then .Cell(r + 2, c).Range.Text = "0.0%"
End With
End If
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
End Sub
If all instances of N/A in the tables are to be replaced, the following would be more efficient:
Sub Demo()
Application.ScreenUpdating = False
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "On-time Completion Rate"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
.Duplicate.Tables(1).Range.Find.Execute FindText:="N/A", ReplaceWith:="0.0%", Wrap:=wdFindStop, Replace:=wdReplaceAll
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
End Sub
Extending this to process a whole folder of documents, you could use code like:
Sub UpdateDocuments()
Application.ScreenUpdating = False
Dim strFolder As String, strFile As String, strDocNm As String, wdDoc As Document
strDocNm = ActiveDocument.FullName: strFolder = GetFolder
If strFolder = "" Then Exit Sub
strFile = Dir(strFolder & "\*.docx", vbNormal)
While strFile <> ""
If strFolder & "\" & strFile <> strDocNm Then
Set wdDoc = Documents.Open(FileName:=strFolder & "\" & strFile, AddToRecentFiles:=False, Visible:=False)
With wdDoc
With .Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "On-time Completion Rate"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
.Duplicate.Tables(1).Range.Find.Execute FindText:="N/A", ReplaceWith:="0.0%", Wrap:=wdFindStop, Replace:=wdReplaceAll
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
.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
To extend the code even further to process documents in sub-folders, see: https://www.msofficeforums.com/47785-post14.html
To save the updated documents as PDFs, insert:
.SaveAs FileName:=Split(.FullName, ".doc")(0) & ".pdf", FileFormat:=wdFormatPDF, AddToRecentFiles:=False
before:
.Close SaveChanges:=True

Trying to use VBA to Automate Document Splitting in Word

I am trying to VBA my way into automating a process that my team and myself currently do manually-- taking a Word document and splitting it into multiple documents based on H1 sections (by which I mean, if a doc has 6 H1s, then we wind up with 6 documents).
I have found some code that works well enough, but there are a couple pieces that I can't quite puzzle out.
Getting the footers from my original document to show up in the subdocuments, and
adding a sequential number at the start of each file name.
The former requirement is pretty simple-- my original doc has a footer on it, and I'd like the documents that the code spits out to have the same footer. Right now, the resulting files have blank footers. The latter requirement is that I ultimately would like the new files to have file names with the format "XX - [HeadingText].docx". The code I'm using gets me the heading text just fine, but I can't seem to plug in the sequential numbering.
Here's the code I'm using; any help would be appreciated!
Sub SeparateHeadings()
'
' SeparateHeadings Macro
'
'
Application.ScreenUpdating = False
Dim StrTmplt As String, StrPath As String, StrFlNm As String, Rng As Range, Doc As Document, i As Long
Dim iTemp As Integer
With ActiveDocument
StrTmplt = .AttachedTemplate.FullName
StrPath = .Path & "\"
With .Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Style = "Heading 1"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
Do While .Find.Found
Set Rng = .Paragraphs(1).Range.Duplicate
With Rng
StrFlNm = Replace(.Text, vbCr, "")
For i = 1 To 255
Select Case i
Case 1 To 31, 33, 34, 37, 42, 44, 46, 47, 58 - 63, 91 - 93, 96, 124, 147, 148
StrFlNm = Replace(StrFlNm, Chr(i), "")
End Select
Next
iTemp = iTemp + 1
Do
If .Paragraphs.Last.Range.End = ActiveDocument.Range.End Then Exit Do
Select Case .Paragraphs.Last.Next.Style
Case "Heading 1"
Exit Do
Case Else
.MoveEnd wdParagraph, 1
End Select
Loop
End With
Set Doc = Documents.Add(Template:=StrTmplt, Visible:=False)
With Doc
.Range.FormattedText = Rng.FormattedText
.SaveAs2 FileName:=StrPath & StrFlNm, Fileformat:=wdFormatXMLDocument, AddToRecentFiles:=False
.Close False
End With
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
End With
Set Doc = Nothing: Set Rng = Nothing
Application.ScreenUpdating = True
End Sub
Try:
Sub SplitDocByHeading1()
Application.ScreenUpdating = False
Dim StrTmplt As String, StrPath As String, StrFlNm As String
Dim Rng As Range, i As Long, j As Long, Doc As Document
Const StrNoChr As String = """*./\:?|"
With ActiveDocument
StrTmplt = .FullName
StrPath = .Path & "\"
'Convert auto numbering to static numbering
.ConvertNumbersToText (wdNumberAllNumbers)
With .Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Style = wdStyleHeading1
.Format = True
.Forward = True
.Wrap = wdFindStop
.Execute
End With
Do While .Find.Found
Set Rng = .Duplicate: i = i + 1
StrFlNm = Split(Rng.Paragraphs(1).Range.Text, vbCr)(0)
For j = 1 To Len(StrNoChr)
StrFlNm = Replace(StrFlNm, Mid(StrNoChr, j, 1), "_")
Next
StrFlNm = Format(i, "00") & "_" & StrFlNm & ".docx"
Set Rng = Rng.GoTo(What:=wdGoToBookmark, Name:="\HeadingLevel")
Set Doc = Documents.Add(Template:=StrTmplt, Visible:=False)
With Doc
.Range.FormattedText = Rng.FormattedText
.SaveAs2 FileName:=StrPath & StrFlNm, Fileformat:=wdFormatXMLDocument, AddToRecentFiles:=False
.Close False
End With
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
End With
Set Doc = Nothing: Set Rng = Nothing
Application.ScreenUpdating = True
End Sub

Need Help Combining 2 VBA codes

I need to combine these two VBA Codes the first is finding and replacing multi items in one document and the second find and replaces a word throughout an entire folder. As you can imagine I need to find and replace multiple words in each document in the folder with 1 button.
CODE 1:
Sub FindAndReplaceMultiItems()
Dim strFindText As String
Dim strReplaceText As String
Dim nSplitItem As Long
Application.ScreenUpdating = False
' Enter items to be replaces and new ones.
strFindText = InputBox("Enter items to be found here,seperated by comma: ", "Items to be found")
strReplaceText = InputBox("Enter new items here, seperated by comma: ", "New items")
nSplitItem = UBound(Split(strFindText, ","))
' Find each item and replace it with new one respectively.
For nSplitItem = 0 To nSplitItem
With Selection
.HomeKey Unit:=wdStory
With Selection.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = Split(strFindText, ",")(nSplitItem)
.Replacement.Text = Split(strReplaceText, ",")(nSplitItem)
.Format = False
.MatchWholeWord = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
End With
Next nSplitItem
Application.ScreenUpdating = True
End Sub
CODE 2:
Sub FindAndReplaceInFolder()
Dim objDoc As Document
Dim strFile As String
Dim strFolder As String
Dim strFindText As String
Dim strReplaceText As String
' Pop up input boxes for user to enter folder path, the finding and replacing texts.
strFolder = InputBox("C:\Users\freil\AppData\Local\Packages\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\TempState\Downloads\Agreements Folder:")
strFile = Dir(strFolder & "\" & "*.docx", vbNormal)
strFindText = InputBox("Find:")
strReplaceText = InputBox("Replace:")
' Open each file in the folder to search and replace texts. Save and close the file after the action.
While strFile <> ""
Set objDoc = Documents.Open(FileName:=strFolder & "\" & strFile)
With objDoc
With Selection
.HomeKey Unit:=wdStory
With Selection.Find
.Text = strFindText
.Replacement.Text = strReplaceText
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
End With
objDoc.Save
objDoc.Close
strFile = Dir()
End With
Wend
End Sub
Welcome to SO. You just need to encompass While strFile <> "" loop (and related variables etc) from Code 2 around Code 1 For loop. However there are other issues with code. May try
Sub FindAndReplaceMultiItems()
Dim strFindText As String
Dim strReplaceText As String
Dim nSplitItem As Long, i As Long
Dim strFolder As String, StrFile As String
Dim objDoc As Document
'Application.ScreenUpdating = False
With Application.FileDialog(msoFileDialogFolderPicker)
.AllowMultiSelect = False
If .Show = -1 Then
strFolder = .SelectedItems(1)
End If
End With
If Len(strFolder) = 0 Then
MsgBox " No folder Selected"
Exit Sub
End If
strFindText = InputBox("Enter items to be found here,seperated by comma: ", "Items to be found", "asdf,qwert,zxc")
If Len(strFindText) = 0 Then
MsgBox " No Find Text Entered"
Exit Sub
End If
strReplaceText = InputBox("Enter new items here, seperated by comma: ", "New items", "0000000000,1111111111,222222222222")
If Len(strReplaceText) = 0 Then
MsgBox " No Replace Text Entered"
Exit Sub
End If
nSplitItem = UBound(Split(strFindText, ","))
If nSplitItem <> UBound(Split(strReplaceText, ",")) Then
MsgBox " Unequal Numbers of Find & Replacement Text"
Exit Sub
End If
StrFile = Dir(strFolder & "\" & "*.docx", vbNormal)
'Open each file in the folder to search and replace texts. Save and close the file after the action.
While StrFile <> ""
Set objDoc = Documents.Open(FileName:=strFolder & "\" & StrFile)
objDoc.Select
' Find each item and replace it with new one respectively.
For i = 0 To nSplitItem
With Selection
.HomeKey Unit:=wdStory
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = Split(strFindText, ",")(i)
.Replacement.Text = Split(strReplaceText, ",")(i)
.Format = False
.MatchWholeWord = False
.Execute Replace:=wdReplaceAll
End With
End With
Next i
'objDoc.Save
objDoc.Close True
StrFile = Dir()
Wend
'Application.ScreenUpdating = True
End Sub
Try something along the lines of:
Sub BulkFindReplace()
Application.ScreenUpdating = False
Dim Doc As Document, strFolder As String, strFile As String, i As Long
Const FList As String = "One,Two,Three"
Const RList As String = "Four,Five,Six"
StrFolder = GetFolder
If strFolder = "" Then Exit Sub
strFile = Dir(strFolder & "\*.doc", vbNormal)
'Loop through all documents in the chosen folder
While strFile <> ""
Set Doc = Documents.Open(Filename:=strFolder & "\" & strFile, AddToRecentFiles:=False, Visible:=False)
With Doc
With .Range.Find
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
'Process each word from the Find/Replace Lists
For i = 0 To UBound(Split(FList, ","))
.Text = Split(FList, ",")(i)
.Replacement.Text = Split(RList, ",")(i)
.Execute Replace:=wdReplaceAll
Next
End With
.Close SaveChanges:=True
End With
strFile = Dir()
Wend
Set Doc = 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

Select certain page on condition in vba

I am writing a script that extract tables from Word file as copies it to a worksheet in Excel. However, the Word files I received do not have the same format and the tables I need are not always on the same page. Hence I cannot use the regular table index.
Each table is on a different page and only on that page there somewhere is a text string (may or may not be in the table itself) like 'material/material list'. What I'd like to do is scan each page of the Word document for a certain textstring and only if that string is present, use the corresponding table on that page. Is this possible and how would I go about this?
A complication of the inconsistent formatting is that on some pages, the data is not even in a table so for those files I'd like an alert if the trigger word is found on a page but no table is there.
Edited:
I have tried to redefine the range considered. My hope is that this is the easiest method; see where the keyword occurs and then use the first table after that. However this does not seem to work.
With ActiveDocument.Content.Find
.Text = "Equipment"
.Forward = True
.Execute
If .Found = True Then Set aRange = ActiveDocument.Range(Start:=0, End:=0)
End With
Edit:
I tried to combine the code from macropod with a vba in Excel that copies the table to the worksheet.
Sub LookForWordDocs()
Dim FolderName As String
With Application.FileDialog(msoFileDialogFolderPicker)
.AllowMultiSelect = False
.Show
On Error Resume Next
FolderName = .SelectedItems(1)
Err.Clear
On Error GoTo 0
End With
Dim sFoldPath As String: sFoldPath = FolderName ' Change the path. Ensure that your have "\" at the end of your path
Dim oFSO As New FileSystemObject ' Requires "Microsoft Scripting Runtime" reference
Dim oFile As File
' Loop to go through all files in specified folder
For Each oFile In oFSO.GetFolder(sFoldPath).Files
' Check if file is a word document. (Also added a check to ensure that we don't pick up a temp Word file)
If ((InStr(1, LCase(oFSO.GetExtensionName(oFile.Path)), "doc", vbTextCompare) > 0) Or _
(InStr(1, LCase(oFSO.GetExtensionName(oFile.Path)), "docx", vbTextCompare) > 0)) And _
(InStr(1, oFile.Name, "~$") = 0) And _
((InStr(1, oFile.Name, "k") = 1) Or (InStr(1, oFile.Name, "K") = 1)) Then
' Call the UDF to copy from word document
ImpTable oFile
End If
Next
End Sub
Sub ImpTable(ByVal oFile As File)
Dim oWdApp As New Word.Application
Dim oWdDoc As Word.Document
Dim oWdTable As Word.Table
Dim oWS As Excel.Worksheet
Dim lLastRow$, lLastColumn$
Dim s As String
s = "No correct table found"
With Excel.ThisWorkbook
Set oWS = Excel.Worksheets.Add
On Error Resume Next
oWS.Name = oFile.Name
On Error GoTo 0
Set sht = oWS.Range("A1")
Set oWdDoc = oWdApp.Documents.Open(oFile.Path)
oWdDoc.Activate
'Application.ScreenUpdating = False
Dim StrFnd As String, Rng As Word.Range, i As Long, j As Long
j = 0
StrFnd = "equipment"
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = StrFnd
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
Do While .Find.Found
i = .Information(wdActiveEndAdjustedPageNumber)
Set Rng = Word.ActiveDocument.Goto(What:=wdGoToPage, Name:=i)
Set Rng = Rng.Goto(What:=wdGoToBookmark, Name:="\page")
If Rng.Tables.Count > 0 Then
With Rng.Tables(1)
Set oWdTable = Rng.Tables(1)
oWdTable.Range.Copy
sht.PasteSpecial xlPasteValues, xlPasteSpecialOperationNone
j = 1
End With
End If
.Start = Rng.End
.Find.Execute
Loop
End With
If j = 0 Then sht.Value = s
'Application.ScreenUpdating = True
oWdDoc.Close savechanges:=False
oWdApp.Quit
End With
Set oWS = Nothing
Set sht = Nothing
Set oWdDoc = Nothing
Set oWdTable = Nothing
Set Rng = Nothing
End Sub
For the first file, the code works fine. However on the second run I get a run-time error "The remote Server Machine does not Exist or is unavailable" on line
"Word.ActiveDocument.Range". I added a couple of qualifications for elements but this still did not solve the problem. Am I missing another line?
BTW When I place "Word" before ActiveDocument.Range the code does not work any more.
Since you've changed the text from 'material/material list' to 'Equipment', it's a bit hard to know quite what you want. Try something along the lines of:
Sub Demo()
Application.ScreenUpdating = False
Dim StrFnd As String, Rng As Range, i As Long
StrFnd = InputBox("What is the Text to Find")
If Trim(StrFnd) = "" Then Exit Sub
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = StrFnd
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = True
.MatchWholeWord = True
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
Do While .Find.Found
i = .Information(wdActiveEndAdjustedPageNumber)
Set Rng = ActiveDocument.GoTo(What:=wdGoToPage, Name:=i)
Set Rng = Rng.GoTo(What:=wdGoToBookmark, Name:="\page")
If Rng.Tables.Count > 0 Then
MsgBox Chr(34) & StrFnd & Chr(34) & " and table found on page " & i & "."
With Rng.Tables(1)
'process this table
End With
Else
MsgBox Chr(34) & StrFnd & Chr(34) & " found on page " & i & " but no table."
End If
.Start = Rng.End
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
End Sub
Note: the above code will test all pages on which the Find text is found.

Word VBA - Inserting Inline Picture from filepath in document

I have a Word Document that includes as text the complete filepaths to multiple images (e.g. C:\Users\Name\Documents\Test Logos\alphatest.png). I am trying to create a macro to replace each text filepath with the image it refers to as inline shapes. The script also resizes the images. I am having trouble assigning a valid reference to the inline shape object variable using the Set statement.
((Right now, I am locating the filepaths in the Word document by manually putting "QQQ" before and after the text in the Word Document and then having the script search for text that is flanked by "QQQ." So, in the Word Document, each filepath looks like this: "QQQC:\Users\Name\Documents\Test Logos\alphatest.pngQQQ". This is a temporary kludge and does not seem to be the source of the error.))
Sub InsertAndResizeLogos()
'
' InsertAndResizeLogos Macro
' Insert logos at the correct place in the document, resize to less than 1 inch tall and 2 inches wide.
'
Selection.HomeKey Unit:=wdStory
With Selection.Find
.ClearFormatting
.Text = "QQQ*QQQ"
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
Do While .Execute
While Selection.Find.Found
Dim imagePath As String
Debug.Print Replace(Selection.Text, "QQQ", "")
imagePath = Replace(Selection.Text, "QQQ", "")
imagePath = Replace(imagePath, "\", "//")
imagePath = Replace(imagePath, vbCr, "")
Debug.Print imagePath
Dim SHP As InlineShape
Set SHP = Selection.InlineShapes.AddPicture(FileName:=imagePath, LinkToFile:=False, SaveWithDocument:=True)
SHP.LockAspectRatio = True
SHP.Height = InchesToPoints(1)
If SHP.Width > InchesToPoints(2) Then
SHP.Width = InchesToPoints(2)
End If
Wend
Loop
End With
End Sub
If I don't convert the filepath string to VBA's preferred format (i.e., removing this line from the script:)
imagePath = Replace(imagePath, "\", "//")
then the script successfully combs through the Word Document, finds the first filepath, and replaces it with the correct image. But then it throws a "Runtime Error 5152: This is not a valid file name." on the "Set" line and breaks.
If I do convert the filepath string to VBA format by replacing the \'s with //'s, then it does not successfully insert the image and throws a "Runtime Error 91: Object variable or With block variable not set" on the SHP.LockAspectRation=True line and breaks.
It seems like if I feed the filepath into the Set statement with //'s, it can no longer find the image. Is this something I could fix with error handling, or am I making a more fundamental mistake?
((If I set the filepath within the script, (i.e. imagePath = C:\Users\Name\Documents\Test Logos\alphatest.png), the script will successfully iterate through the entire document and replace all text with the QQQ's with that image.))
SOLUTION
Here is the final code that worked correctly:
Sub InsertAndResizeLogos()
'
' InsertAndResizeLogos Macro
' Insert logos at the correct place in the document, resize to less than 1 inch tall and 2 inches wide.
'
Application.ScreenUpdating = False
Dim i As Long, j As Long, StrNm As String, StrErr As String, iShp As InlineShape
With Selection 'ActiveDocument.Range
With .Find
.ClearFormatting
.Text = "*.[A-Za-z]{3}>"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
StrNm = .Text
If Dir(StrNm) = "" Then
j = j + 1: StrErr = StrErr & vbCr & StrNm
Else
i = i + 1
Set iShp = .InlineShapes.AddPicture(FileName:=StrNm, LinkToFile:=False, SaveWithDocument:=True)
With iShp
.LockAspectRatio = True
.Height = InchesToPoints(1)
If .Width > InchesToPoints(2) Then .Width = InchesToPoints(2)
End With
End If
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
MsgBox i & " images added." & vbCr & j & " image files not found:" & StrErr
End Sub
The problem seems to have been related to pulling the filepath from Selection.Text rather than from .Find.Found.Text
This mostly uses the approach suggested below by Macropod, although applied to Selection rather than to Document.Range to maintain the "replace the text with the image" functionality. For some reason, Find.Execute's ReplaceWith parameter and Find's Replacement property refused to work no matter where in the process I called them.
You don't need all the QQQ circumlocution. You also don't need:
imagePath = Replace(imagePath, "\", "//")
But you should add error-checking to the code in case one or more image files is missing. Try:
Sub Demo()
Application.ScreenUpdating = False
Dim i As Long, j As Long, StrNm As String, StrErr As String, iShp As InlineShape
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "C:\\Users\\*.[A-Za-z]{3}>"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
StrNm = .Text
If Dir(StrNm) = "" Then
j = j + 1: StrErr = StrErr & vbCr & StrNm
Else
i = i + 1: .Text = vbNullString
Set iShp = .InlineShapes.AddPicture(FileName:=StrNm, LinkToFile:=False, SaveWithDocument:=True, Range:=.Duplicate)
With iShp
.LockAspectRatio = True
.Height = InchesToPoints(1)
If .Width > InchesToPoints(2) Then .Width = InchesToPoints(2)
End With
End If
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
MsgBox i & " images added." & vbCr & j & " image files not found:" & StrErr
End Sub
The following works for me.
I am using *png to identify the strings that end with .png.
I am then using
Right$(imagePath, Len(imagePath) - InStr(1,imagePath,":\") + 2)
to extract the string that holds the filepath on the assumption your filepaths are along the lines of C:\ etc. You could evolve this logic to suit your purposes.
I have removed the other loop and simply allowed the .Execute to continue until False.
Sub Test
Selection.HomeKey Unit:=wdStory
With Selection.Find
.ClearFormatting
.Text = "*png"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
Do While .Execute
Dim imagePath As String
imagePath = Selection.Range.Text
imagePath = Right$(imagePath, Len(imagePath) - InStr(1,imagePath,":\") + 2)
Dim SHP As InlineShape
Set SHP = Selection.InlineShapes.AddPicture(FileName:=imagePath, LinkToFile:=False, SaveWithDocument:=True)
SHP.LockAspectRatio = True
SHP.Height = InchesToPoints(1)
If SHP.Width > InchesToPoints(2) Then
SHP.Width = InchesToPoints(2)
End If
Loop
End With
End Sub
Reference:
https://superuser.com/questions/1009085/find-all-instances-of-a-text-and-make-it-a-hyperlink-with-a-macro