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
Related
I have a Word Document with mixed styles and need to add HTML tags if the line matches h1, h2 or h3.
I'm trying to save the new string into an array and save as .html file
But have no idea how to write the if condition in .vba, so please help.
Public Sub Word2HTML()
Dim NewArray() As String
Dim TextFile As Integer
Dim FilePath As String
FilePath = "C:\Temp\final.html"
TextFile = FreeFile
'Open the text file
Open FilePath For Output As TextFile
For i = 1 To ActiveDocument.Paragraphs.Count
' error on the next line
If ActiveDocument.Paragraphs(i).Style.Text = "Heading 1" Then
ReDim Preserve NewArray(i-1)
NewArray(i-1) = "<h1>" + ActiveDocument.Paragraphs(i) + "</h1>"
Elseif ActiveDocument.Paragraphs(i).Style.Text = "Heading 2" Then
ReDim Preserve NewArray(i-1)
NewArray(i-1) = "<h2>" + ActiveDocument.Paragraphs(i) + "</h2>"
Elseif ActiveDocument.Paragraphs(i).Style.Text = "Heading 3" Then
ReDim Preserve NewArray(i-1)
NewArray(i-1) = "<h3>" + ActiveDocument.Paragraphs(i) + "</h3>"
Else
End If
Next i
For Each headline In NewArray
Print #TextFile, headline
Next
Close TextFile
End Sub
Here is the error message I got from MS Word VB
The following captures all headings and saves them to an HTML file with the same name as the active document:
Sub HeadingsToHTML()
Application.ScreenUpdating = False
Dim Rng As Range, h As Long
With ActiveDocument
With .Range.Find
.ClearFormatting
.Replacement.ClearFormatting
.Forward = True
.Font.Bold = True
.Wrap = wdFindContinue
.Text = ""
.Replacement.Text = "<strong>^&</strong>"
.Execute Replace:=wdReplaceAll
.Text = "^p"
.Replacement.Text = "</strong>^&<strong>"
.Execute Replace:=wdReplaceAll
End With
Set Rng = .Range(0, 0)
Set Rng = .TablesOfContents.Add(Range:=Rng, UseHeadingStyles:=True, _
UpperHeadingLevel:=1, LowerHeadingLevel:=9, IncludePageNumbers:=False).Range
Rng.Fields.Unlink
Rng.Collapse wdCollapseEnd
Rng.End = .Range.End
Rng.Delete
With .Range.Find
.ClearFormatting
.Replacement.ClearFormatting
.Forward = True
.Wrap = wdFindContinue
.MatchWildcards = True
For h = 1 To 9
.Style = "TOC " & h
.Text = "([!^13]{1,})"
.Replacement.Text = "<h" & h & ">\1</h" & h & ">"
.Execute Replace:=wdReplaceAll
Next
End With
.SaveAs2 FileName:=Split(.FullName, ".doc")(0) & ".html", FileFormat:=wdFormatText, AddToRecentFiles:=False
End With
Application.ScreenUpdating = True
End Sub
Way faster than looping through all paragraphs, too.
If your headings have level #s and you don't want those in the output, replace:
.Text = "([!^13]{1,})"
with:
.Text = "[!^13]#^t([!^13]{1,})"
Styles do not have a .Text property. You probably are trying to do this:
If ActiveDocument.Paragraphs(i).Style = "Heading 1" Then
' do something here
End If
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
I am using this code which is a batch find and replace macro. It finds and replaces the words in the document by reading the replacement words from another document (text.docx). This works absolutely fine when there are a handful of changes (i.e. less than 1 page). However, I hope to use this macro on documents that are 10-20 pages. When I use it, the word document just immediately crashes (starts not responding) and has to be forced to quit.
Does anyone have any tips on what can be done to prevent it from crashing? How can I modify the code to batch edit thousands of words? Code is below.
Thanks in advance!
Sub ReplaceFromTableList()
Dim oChanges As Document, oDoc As Document
Dim oTable As Table
Dim oRng As Range
Dim rFindText As Range, rReplacement As Range
Dim i As Long
Dim y As Integer
Dim sFname As String
Dim sAsk As String
sFname = "/Users/user/Desktop/test.docx"
Set oDoc = ActiveDocument
Set oChanges = Documents.Open(FileName:=sFname, Visible:=False)
Set oTable = oChanges.Tables(1)
y = 0
For i = 1 To oTable.Rows.Count
Set oRng = oDoc.Range
Set rFindText = oTable.Cell(i, 1).Range
rFindText.End = rFindText.End - 1
Set rReplacement = oTable.Cell(i, 2).Range
rReplacement.End = rReplacement.End - 1
With oRng.Find
.ClearFormatting
.Replacement.ClearFormatting
Do While .Execute(findText:=rFindText, _
MatchWholeWord:=True, _
MatchWildcards:=False, _
Forward:=True, _
Wrap:=wdFindStop) = True
oRng.Select
oRng.FormattedText = rReplacement.FormattedText
y = y + 1
Loop
End With
Next i
oChanges.Close wdDoNotSaveChanges
MsgBox (y & " errors fixed")
End Sub
Your use of the FormattedText method to reproduce the formatting necessitates a time-consuming loop for each expression. The more the find expression occurs in the target document, the longer the process will take. Your unnecessary use of oRng.Select (which you don't then do anything with) makes it even slower - especially since you don't disable ScreenUpdating. The following macro avoids the need for the FormattedText looping:
Sub BulkFindReplace()
Application.ScreenUpdating = False
Dim ThisDoc As Document, FRDoc As Document, Rng As Range, i As Long, j As Long, StrRep As String, StrCount As String
Set ThisDoc = ActiveDocument
Set FRDoc = Documents.Open("C:\Users\" & Environ("Username") & "\Downloads\FindReplaceTable.docx", _
ReadOnly:=True, AddToRecentFiles:=False, Visible:=False)
With ThisDoc.Range.Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = False
.Forward = True
.Wrap = wdFindContinue
'Process each word from the F/R Table
For i = 1 To FRDoc.Tables(1).Rows.Count
Set Rng = FRDoc.Tables(1).Rows(i).Cells(1).Range
Rng.End = Rng.End - 1
.Text = Rng
StrCount = StrCount & vbCr & Rng.Text & ":" & vbTab & _
(Len(ThisDoc.Range.Text) - Len(Replace(ThisDoc.Range, Rng.Text, ""))) / Len(Rng.Text)
Set Rng = FRDoc.Tables(1).Rows(i).Cells(2).Range
Rng.End = Rng.End - 1
With Rng
If Len(.Text) > 0 Then
.Copy
StrRep = "^c"
Else
StrRep = ""
End If
End With
.Replacement.Text = StrRep
.Execute Replace:=wdReplaceAll
If i Mod 20 = 0 Then DoEvents
Next
End With
FRDoc.Close False
MsgBox "The following strings were replaced:" & StrCount
Set Rng = Nothing: Set FRDoc = Nothing: Set ThisDoc = Nothing
Application.ScreenUpdating = True
End Sub
Try this:
Sub FindReplaceAll()
Dim MyDialog As FileDialog, GetStr(1 To 100) As String
'100 files is the maximum applying this code
On Error Resume Next
Set MyDialog = Application.FileDialog(msoFileDialogFilePicker)
With MyDialog
.Filters.Clear
.AllowMultiSelect = True
i = 1
If .Show = -1 Then
For Each stiSelectedItem In .SelectedItems
GetStr(i) = stiSelectedItem
i = i + 1
Next
i = i - 1
End If
Application.ScreenUpdating = False
For j = 1 To i Step 1
Set Doc = Documents.Open(FileName:=GetStr(j), Visible:=True)
Windows(GetStr(j)).Activate
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "Marriott International" 'Find What
.Replacement.Text = "Marriott" 'Replace With
.Forward = True
.Wrap = wdFindAsk
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchByte = True
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
Application.Run macroname:="NEWMACROS"
ActiveDocument.Save
ActiveWindow.Close
Next
Application.ScreenUpdating = True
End With
MsgBox "operation end, please view", vbInformation
End Sub
The idea comes from here:
https://www.extendoffice.com/documents/word/1002-word-replace-multiple-files.html
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
I am trying to use a Word Macro to select all text with the style "Number_List" and call the .ConvertNumbersToText function on it. I am having trouble only finding the list or that specific style.
Dim selBkUp As Range
Set selBkUp = ActiveDocument.Range(ActiveDocument.Range.Start, ActiveDocument.Range.End)
With ActiveDocument.Range.Find
.Style = ActiveDocument.Styles("Number_List")
.Forward = True
.Wrap = wdFindContinue
Dim SearchSuccessful As Boolean
SearchSuccessful = .Execute
If SearchSuccessful Then
selBkUp.Select
Selection.Range.ListFormat.ConvertNumbersToText
Else
' code
End If
End With
I select the entire document and covert all of the lists numbers to text, but I am trying to only select ones with that specific style or avoid the other 5 styles that may or may not be present. Any help would be appreciated!
The following code will search for one style and, if found, will convert to another style. Below this code is another subroutine that will list all styles found in a document.
' From http://forums.codeguru.com/showthread.php?448185-Macro-to-Change-Styles-in-Word
' This code will search for a specified Style and convert that to another Style
Sub FindReplaceStyle()
Selection.HomeKey wdStory
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Style = "Normal" ' Look for 'Normal'
'.Text = ""
.Replacement.Style = "Heading 1" ' Change to 'Heading 1'
' .Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Do While Selection.Find.Execute()
If Selection.Start = Selection.Paragraphs.First.Range.Start Then
Selection.Style = "Heading 1"
End If
Selection.Collapse wdCollapseEnd
Loop
End Sub
The code below will produce a list of all styles found in a document.
Also, I get an error trying to use your "Number_List"
' Following code from: http://www.vbaexpress.com/forum/showthread.php?41125-How-to-get-all-the-applied-Paragraph-Styles-of-a-document
Sub GetActiveStyles()
Application.ScreenUpdating = False
Dim RngStory As Range, oSty As Style, StrType As String, StrStyles As String
With ActiveDocument
For Each oSty In .Styles
For Each RngStory In .StoryRanges
With RngStory.Find
.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Style = oSty.NameLocal
.Forward = True
.Wrap = wdFindStop
.Format = True
.Execute
If .Found Then
Select Case oSty.Type
Case wdStyleTypeCharacter: StrType = "Character"
Case wdStyleTypeList: StrType = "list"
Case wdStyleTypeParagraph: StrType = "Paragraph"
Case wdStyleTypeTable: StrType = "Table"
End Select
StrStyles = StrStyles & oSty.NameLocal & " (" & StrType & ")" & vbCr
Exit For
End If
End With
Next RngStory
Next oSty
End With
Debug.Print StrStyles
'MsgBox StrStyles
Application.ScreenUpdating = True
End Sub