I have a set of documents (lots) where the header in each has a table in the header with hardcoded address entries. I need to update all these documents to replace these hard-coded address with mergefields.
The code is in an excel spreadsheet where the user selects the folder containing the documents to update. Below is an extract of where the updating is done e.g. trying to replace hardcoded value of 1 Maple Road with {MERGEFIELD Address_Line1}. Not sure where i'm going wrong but message is usually wrong number of arguments or does not work at all
Thanks
Dim doc As Word.Document
Dim hf As Word.HeaderFooter
Dim lr As ListRow
Dim updated As Boolean
Dim tableCount As Integer
Dim t As Integer
Dim c As Cell
Set wd = New Word.Application
Set doc = wd.Documents.Open(Filename:="c:/......./example.docx", ReadOnly:=False)
For Each hf In doc.Sections(1).Headers()
tableCount = hf.Range.Tables.Count
For t = 1 To tableCount
For Each c In hf.Range.Tables(t).Range.Cells
If InStr(1, c.Range.Text, "1 Maple Road") > 0 Then
c.Range.Text = ""
c.Range.Select
doc.Fields.Add Range:=Selection.Range, Type:=wdFieldEmpty, PreserveFormatting:=True, Text:="MERGEFIELD Address_line1"
End If
Next c
Next t
Next hf
doc.Close False
wd.Quit False
Or tried
Dim doc As Word.Document
Dim hf As Word.HeaderFooter
Dim lr As ListRow
Dim updated As Boolean
Dim tableCount As Integer
Dim t As Integer
Dim c As Cell
Set wd = New Word.Application
Set doc = wd.Documents.Open(Filename:="c:/......./example.docx", ReadOnly:=False)
For Each hf In doc.Sections(1).Headers()
tableCount = hf.Range.Tables.Count
For t = 1 To tableCount
For Each c In hf.Range.Tables(t).Range.Cells
If InStr(1, c.Range.Text, "1 Maple Road") > 0 Then
c.Range.Text = ""
c.Range.Select
Selection.Fields.Add Range:=Selection.Range, Type:=wdFieldEmpty, PreserveFormatting:=True
Selection.TypeText Text:="MERGEFIELD Address_Line1"
End If
Next c
Next t
Next hf
doc.Close False
wd.Quit False
Instr is not reliable where tables, fields, and so on are involved. Furthermore, in your code, Selection.Range is referring to an Excel selection! To refer to a Word selection you'd need wd.Selection.Range. In any event there is no need to select anything. Try:
For Each hf In doc.Sections(1).Headers
With hf.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "1 Maple Road"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
If .Find.Found = True Then
.Fields.Add .Range, wdFieldEmpty, "MERGEFIELD Address_line1", False
End If
End With
Next
Apologies making it clearer. In the document header there is a table (on right hand side) with 3 cells. The second on has hardcoded addresses e.g.
1 Maple Road
SomeTown
SomeCity SomePostCode
I need to replace the contents of this cell with mergefields e.g.
MERGEFIELD Address_Line1
MERGEFIELD Address_Line2
MERGEFIELD Address_City MERGEFIELD Address_PostCode
(so long as the hardcoded entries match the specified Road,Town, City and PostCode)
The is a batch job done in excel VBA that targets one folder at a time containing a number of documents to update.
The formatting also needs to be retained
Thanks
Related
I am trying to obtain the numbered locations of my bookmarks (paragraph number without context) in a Word document (a lengthy legal document template) and. I am currently using the following code to pull the bookmarked text values from the Word document into an Excel workbook I've built out to grab other data from other sources, but I haven't been able to figure out how to manipulate the code to grab the bookmark's paragraph numbers (I searched high and low for this one too, and am a VBA newbie. I know just enough to be dangerous, but not enough to be helpful lol). Please Help!
Sub SectionLocationImportTESTING()
Dim intDocCount As Integer
Dim wdApp As Word.Application, wdDoc As Word.Document, xlWb As Excel.Workbook, xlWs As Excel.Worksheet
Dim BookmarkText As String
Set wdApp = GetObject(, "Word.Application")
On Error GoTo 0
If wdApp Is Nothing Then
MsgBox "There are no MS Word Documents open.", vbInformation, "No Word Documents open"
Exit Sub
End If
Set xlWb = ThisWorkbook
Set xlWs = ActiveWorkbook.Sheets("Data Input")
intDocCount = wdApp.Documents.Count
If intDocCount > 1 Then
MsgBox "There are " & intDocCount & " Word Documents open." & vbNewLine & vbNewLine & _
"Please close the additional MS Word Documents", vbCritical, "Too many Word Documents open!"
Set wdApp = Nothing
Exit Sub
End If
With wdApp
Set wdDoc = wdApp.ActiveDocument
wdDoc.Activate
'This is very abbreviated, I have about 300 bookmarks that transfer
If wdDoc.Bookmarks.Exists("Section_Rent") = True Then
BookmarkText = wdDoc.Bookmarks("Section_Rent").Range.Text
xlWs.Cells(202, 22) = ("Section_Rent")
xlWs.Cells(202, 23) = BookmarkText
End If
End With
ActiveWorkbook.RefreshAll
ActiveSheet.PivotTables("Data_Input_Table").PivotFields("Trimmed Data"). _
PivotFilters.Add2 Type:=xlCaptionIsGreaterThan, Value1:="0"
Columns("D:D").EntireColumn.AutoFit
Range("A1").Select
MsgBox "Transfer is complete."
End Sub
I don't think there's a straight-forward way of doing that.
You could do this for example:
Sub Tester()
Debug.Print ParagraphNumber(Selection.Range)
End Sub
Function ParagraphNumber(rng As Range)
ParagraphNumber = rng.Document.Range(0, rng.End).Paragraphs.Count
End Function
...but it will also count "empty" paragraphs.
If you have a lot of bookmarks, you could consider listing the names in your Excel sheet and then looping over that range to run the text extraction. If you hard-code all those names into your VBA that's going to be very hard to maintain.
E.g.
'...
Dim c As Range, bm As String, rngBM As Word.Range
'...
'...
Set wdDoc = wdApp.ActiveDocument
wdDoc.Activate
'range with your bookmark names
Set rngBM = ThisWorkbook.Sheets("Bookmarks").Range("A2:A300")
For Each c In rngBM.Cells
bm = c.Value 'bookmark name
If wdDoc.Bookmarks.Exists(bm) Then
Set rngBM = wdDoc.Bookmarks(bm).Range
'for demo purposes just putting info next to the bookmark name...
c.Offset(0, 1).Value = rngBM.Text
c.Offset(0, 2).Value = ParagraphNumber(rngBM)
End If
Next c
There's 2 ways to get the paragraph number, depending on what you want:
Option 1
This will get the exact string of the auto-numbering that you see in the paragraph itself:
E.g. the below paragraph will get you 1.
This is a test paragraph.
If wdDoc.Bookmarks.Exists("Section_Rent") Then
Dim BookmarkText As String
BookmarkText = wdDoc.Bookmarks("Section_Rent").Range.Text
xlWs.Cells(202, 22) = "Section_Rent"
xlWs.Cells(202, 23) = BookmarkText
Dim BookmarkParaNum As String
BookmarkParaNum = wdDoc.Bookmarks("Section_Rent").Range.ListFormat.ListString
xlWs.Cells(202, 24) = BookmarkParaNum
End If
Option 2
This will get the string that you see if you insert a cross reference to the paragraph:
Using the below code for the same paragraph in Option 1 will give you just 1, the same as what inserting it as cross reference will get you.
wdDoc.Paragraphs.Last.Range.InsertParagraphAfter 'A temporary paragraph for inserting field later
Dim fieldRng As Range
Set fieldRng = wdDoc.Paragraphs.Last.Range.Duplicate
If wdDoc.Bookmarks.Exists("Section_Rent") Then
Dim BookmarkText As String
BookmarkText = wdDoc.Bookmarks("Section_Rent").Range.Text
xlWs.Cells(202, 22) = "Section_Rent"
xlWs.Cells(202, 23) = BookmarkText
fieldRng.InsertCrossReference ReferenceType:="Bookmark", ReferenceKind:=wdNumberNoContext, ReferenceItem:="Section_Term", InsertAsHyperlink:=True, IncludePosition:=False, SeparateNumbers:=False, SeparatorString:=" "
Dim tempField As Field
Set tempField = fieldRng.Fields(1)
Dim BookmarkParaNum As String
BookmarkParaNum = tempField.Result
xlWs.Cells(202, 24) = BookmarkParaNum
tempField.Delete
End If
fieldRng.Delete 'Delete the temporary paragraph
I am new to VBA and trying to extract some string values that contain a certain key word from a Word document to Excel. For example, there are country codes such as USA.001.01.033592 and I want to extract all the string values look like the country code from the word doc and collect them into a Excel spreadsheet.
The country codes I'm looking at as a format of
USA.xxx.xx.xxxxxx
JPA.xxx.xx.xxxxxx
FRA.xxx.xx.xxxxxx
The Xs represent numbers and the problem is theses codes are in the main body paragraphs, tables within the paragraphs, and the footnotes. Also, when I retrieve the codes, I also want to extract the page number.
Is there any way I can extract the data I want from the main paragraphs, tables, and footnotes at once with their page number?
I have a rough draft of my code but it's not working at all. Could anyone please help?
Here's my code:
Option Explicit
Sub Footnotes()
Dim appExcel As Object
Dim objSheet As Object
Dim aRange As Range
Dim intRowCount As Integer
intRowCount = 1
Set aRange = ActiveDocument.Range
With aRange.Find
Do
.Text = "USA." or "JPA." or "FRA."
.Execute
If .Found Then
aRange.Expand Unit:=wdSentence
aRange.Copy
aRange.Collapse wdCollapseEnd
If objSheet Is Nothing Then
Set appExcel = CreateObject("Excel.Application")
Set objSheet = appExcel.workbooks.Open("C:\Users\Footnotes.xlsx").Sheets("Sheet1")
intRowCount = 1
End If
objSheet.Cells(intRowCount, 1).Select
objSheet.Paste
intRowCount = intRowCount + 1
End If
Loop While .Found
End With
If Not objSheet Is Nothing Then
appExcel.workbooks(1).Close True
appExcel.Quit
Set objSheet = Nothing
Set appExcel = Nothing
End If
Set aRange = Nothing
End Sub
currently, I'm trying to use VBA from the word document but if it's better to start from the excel file please let me know.
As you have a distinct pattern to search for you can use Word's Find with wildcards. There is a good reference for that at the Word MVP website. This will enable Find to return the entire string that you are looking for without needing to extend the found range.
Once you have the found range you can then retrieve the text to pass to Excel and obtain the page number using the Information property.
A Word document consists of a number of parts referred to as StoryRanges. Whilst tables are just part of the range that contains them, footnotes are contained in a separate StoryRange. The code below loops through the StoryRanges and checks which type the current one is. I have done it this way so that you can add other types if you need to.
It wasn't stated in your question what you wanted to do with the page number so you'll need to modify the code below for that.
Sub Footnotes()
Dim appExcel As Excel.Application
Dim objSheet As Excel.Worksheet
Dim findRange As Range
Dim intRowCount As Integer
Dim pageNum As Long
If objSheet Is Nothing Then
Set appExcel = CreateObject("Excel.Application")
Set objSheet = appExcel.workbooks.Open("C:\Users\Footnotes.xlsx").Sheets("Sheet1")
End If
intRowCount = 2
'Set findRange = ActiveDocument.Range
For Each findRange In ActiveDocument.StoryRanges
With findRange.Find
.Text = "[UJF][PRS]A.[0-9]{3}.[0-9]{2}.[0-9]{6}"
.MatchWildcards = True
Do While .Execute = True
pageNum = CLng(findRange.Information(wdActiveEndPageNumber))
objSheet.Cells(intRowCount, 1).Value = findRange.Text
objSheet.Cells(intRowCount, 2).Value = pageNum
intRowCount = intRowCount + 1
findRange.Collapse wdCollapseEnd
Loop
End With
Next findRange
If Not objSheet Is Nothing Then
appExcel.workbooks(1).Close True
appExcel.Quit
Set objSheet = Nothing
Set appExcel = Nothing
End If
Set findRange = Nothing
End Sub
EDIT:
The code above only finds the country codes listed in the question. To find any country code change Find.Text to "[A-Z]{3}.[0-9]{3}.[0-9]{2}.[0-9]{6}"
I want to set up a word template, which has a linked excel table via a field with following code:
{LINK Excel.SheetMacroEnabled.12 D:\calendar.xlsm calendar!namedR \f 4 \h \* MERGEFORMAT}
The table resides in excel and is updated daily. The excel has several columns and one of them has a set of hyperlinks to various websites in rows. When I try to break the link to the table, in order to create rest of the document and archive it via the following code, I always get all of the links (including hyperlinks to websites) in the linked table broken.
The funny thing is that when I do it manually and select the table in Word it breaks only the link to the excel table, leaving the hyperlinks in a table column intact. This is the desired programmatic outcome. I tried 2 following macros and none of them works. Is there a way to achieve it via VBA without breaking all the links in a linked table?
Sub ConvertTableLink()
Dim myField As Field
For Each myField In ActiveDocument.Fields
If myField.Type = wdFieldLink Then
myField.Unlink
End If
Next
End Sub
Sub ConvertTableLinkTake2()
ActiveDocument.Fields(1).Update
ActiveDocument.Fields(1).Unlink
End Sub
It's not obvious why your links are remaining when you unlink the LINK field manually but not with VBA. Generally speaking, I see the same behavior whichever way I do it here - the links remain blue so they still look like links, but they aren't.
You can do your own "unlink" that should preserve the hyperlinks using this, assuming the LINK is the first LINK field in the document:
Sub replaceLinkByResult1()
Dim fld As Word.Field
Dim rng As Word.Range
For Each fld In ActiveDocument.Fields
If fld.Type = wdFieldLink Then
Set rng = fld.Result.Tables(1).Range
rng.Copy
fld.Delete
rng.Paste
Exit For
End If
Next
End Sub
If Copy/Paste doesn't work or you prefer to avoid it, the following should also work:
Sub replaceLinkByResult2()
Dim fld As Word.Field
Dim rng1 As Word.Range
Dim rng2 As Word.Range
For Each fld In ActiveDocument.Fields
If fld.Type = wdFieldLink Then
Set rng1 = fld.Result.Tables(1).Range
Set rng2 = rng1.Duplicate
rng2.Collapse wdCollapseEnd
rng2.Start = rng2.Start + 1
rng2.InsertParagraph
rng2.Start = rng2.Start + 1
rng2.FormattedText = rng1.FormattedText
fld.Delete
Set rng2 = Nothing
Set rng1 = Nothing
Exit For
End If
Next
End Sub
For example, assuming the linked object is the first field in the document:
Sub Demo()
Dim Rng As Range, Hlnk As Hyperlink, i As Long
Dim StrDisp As String, StrLink As String, StrFont As String, StrSize As String
With ActiveDocument.Fields(1)
Set Rng = .Result
With Rng
For Each Hlnk In .Hyperlinks
StrDisp = StrDisp & "|" & Hlnk.TextToDisplay
StrLink = StrLink & "|" & Hlnk.Address
StrFont = StrFont & "|" & Hlnk.Range.Font.Name
StrSize = StrSize & "|" & Hlnk.Range.Font.Size
Next
End With
.Unlink
With Rng
For i = 1 To UBound(Split(StrDisp, "|"))
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Forward = True
.Format = False
.Text = Split(StrDisp, "|")(i)
.Replacement.Text = ""
.Wrap = wdFindStop
.Execute
End With
Set Hlnk = .Hyperlinks.Add(Anchor:=.Duplicate, Address:=Split(StrLink, "|")(i), TextToDisplay:=Split(StrDisp, "|")(i))
Hlnk.Range.Font.Name = Split(StrFont, "|")(i)
Hlnk.Range.Font.Size = Split(StrSize, "|")(i)
Next
End With
End With
End Sub
Since nobody was able to help me with the problem I posted here before (link is below), I am now trying to solve the task through VBA.
Finding a heading in word file and copying entire paragraph thereafter to new word file with python
To briefly recap, I have a large amount of word files, which I would like to reduce to a more readable size each. In each file, there is one heading that appears several times, always formated as a 'Heading 2'. I look for this specific heading which occurs several times in a document and I want to copy all the text parts in just these chapters with the respective heading to a new word document.
I decided to create an excel file in which I list the files and the respective heading of the chapters that I want to copy (see picture below).
To do this now I have written the following code:
Sub SelectData()
Application.ScreenUpdating = False
Dim WdApp As Word.Application
Set WdApp = CreateObject("Word.Application")
Dim Doc As Word.Document
Dim NewDoc As Word.Document
Dim HeadingToFind As String
Dim ChapterToFind As String
Dim StartRange As Long
Dim EndRange As Long
Dim WkSht As Worksheet
Dim LRow As Long
Dim i As Long
Set WkSht = ThisWorkbook.Sheets("Sheet1")
LRow = WkSht.UsedRange.Cells.SpecialCells(xlCellTypeLastCell).Row
With WkSht
For i = 1 To LRow
If Dir(.Cells(i, 1).Text, vbNormal) = "" Then
.Cells(i, 3).Value = "Please check File Location"
Else
Set Doc = WdApp.Documents.Open(Filename:=.Cells(i, 1).Text, _
AddToRecentFiles:=False, Visible:=False, ReadOnly:=False)
Set NewDoc = Documents.Add
ChapterToFind = LCase(.Cells(i, 2).Text)
With Doc
Selection.HomeKey Unit:=wdStory
With Selection
With .Find
.ClearFormatting
.Text = ChapterToFind
.MatchWildcards = False
.MatchCase = True
.Execute
End With
If .Find.Found Then
.Collapse wdCollapseStart
With .Find
.Text = ""
.Style = "Heading 2"
.Forward = False
.Execute
End With
.MoveDown Count:=1
.HomeKey Unit:=wdLine
StartRange = .Start
.Find.Forward = True
.Find.Execute
.Collapse wdCollapseStart
.MoveUp Count:=1
.EndKey Unit:=wdLine
EndRange = .End
Doc.Range(StartRange, EndRange).Copy
NewDoc.Content.Paste
NewDoc.SaveAs2 Doc.Path & "Clean" & ".docx", wdFormatFlatXML
Else
WkSht.Cells(i, 4).Value = "Error Chapter Not Found"
End If
End With
End With
WdApp.Quit
Set Doc = Nothing: Set NewDoc = Nothing: Set WdApp = Nothing: Set WkSht = Nothing
Application.ScreenUpdating = True
End If
Next
End With
End Sub
However I am really struggling. It seems to not work as I constantly get an error with the command (RunTimeError 438):
Selection.HomeKey Unit:=wdStory
I am aware that I have to activate the Microsoft Word 15.0 Object Library in the references to be able to get word commands. Nevertheless it is not working.
I would greatly appreciate any help, I am also open to other suggestions of course.
The word files look something like in the picture below, however the chapter that I want to extract can occur several times within one word document. As a result my code would probably need a loop or something, I was not able to get this done.
Also I have considered the following links to the topic:
Use VBA with Powerpoint to Search titles in a Word Doc and Copy Text into another Word Document
VBA: open word from excel
word vba: select text between headings
Did I understand this correctly? The following code is the core of what I think you're trying to do. It finds the first Heading 2, then finds all the paragraphs after it until it finds another header of any type or the end of the document. startCopyRange and endCopyRange is the range of those paragraphs. You'll have to piece this into your Excel routine.
A few notes. Always save the active document to a variable and work from that; the user is then free to change active documents while this routine is running. Never use Selection, always use ranges. Never use relative movements like Move, always use API calls.
Sub SelectData()
Dim Doc As Word.Document
Set Doc = ActiveDocument
Dim findRange As Range
Set findRange = Doc.Range
ChapterToFind = "My Chapter"
findRange.Find.Text = ChapterToFind
findRange.Find.Style = "Heading 2"
findRange.Find.MatchCase = True
Dim startCopyRange As Long
Dim endCopyRange As Long
Do While findRange.Find.Execute() = True
startCopyRange = findRange.End + 1
endCopyRange = -1
'findRange.Select
Dim myParagraph As Paragraph
Set myParagraph = findRange.Paragraphs(1).Next
Do While Not myParagraph Is Nothing
myParagraph.Range.Select 'Debug only
If InStr(myParagraph.Style, "Heading") > 0 Then
endCopyRange = myParagraph.Range.Start - 0
End If
If myParagraph.Next Is Nothing Then
endCopyRange = myParagraph.Range.End - 0
End If
If endCopyRange <> -1 Then
Doc.Range(startCopyRange, endCopyRange).Select 'Debug only
DoEvents
Exit Do
End If
Set myParagraph = myParagraph.Next
DoEvents
Loop
Loop
End Sub
Thank you for taking the time to read my request. I have tried using a few answers on this site and I am not getting what I want. (I tried this: Word VBA how to select text between two substrings and assign to variable?)
I am trying to select a number that is ALWAYS between the two same words. It is between "Account No.:" and "IMPORTANT" (yes in all caps, unsure if caps/ no-caps matters for denoting it).
I am creating a macro where I open a word document with say 200 pages. I want to open and save EACH PAGE as it's own pdf with a specific name. I have gotten the code to run where I open and save as PDF. What I want to do, is with in that code, have something that finds the text between "Account No.:" and "IMPORTANT", selects it and copies it. This text is an account number.
Then, when I go to save the file, I want it to paste the account number as the file name. Or have a reference that when it finds the account number it assigns it to a variable. I am new to VBA, so if you can please be descriptive, and put instructions in laymans terms. THANK YOU!
My macro:
Sub CutePDFWriter()
Dim FName, FPath, username, LoanNo As String
Dim wordapp As Word.Application
Dim wordDoc As Word.Document
Dim i As Integer
Dim rngParagraphs As Range
'open doc and export as a pdf
Set wordapp = CreateObject("word.Application")
Set wordDoc = wordapp.Documents.Open("G:\test.doc")
For i = 1 To wordDoc.BuiltinDocumentProperties("Number of Pages")
**Here is where I want to add the “Find and Select” code**
'set variable strings
FPath = "G:\Excel Doc Tests\"
FName = "___**Here is where I want the acct nbr to go_______"** & i & ""
wordDoc.ExportAsFixedFormat FPath & FName & "-escrtax", ExportFormat:=wdExportFormatPDF, Range:=wdExportFromTo, From:=i, To:=i
Next i
'empty word doc objects
wordDoc.Close (False)
wordapp.Quit
End Sub
I added a comment to the question at that link which makes his code work. But I spent time on this: (tested with "blah blah Account No.:123-456IMPORTANT blah blah"):
Option Explicit
Sub Sub1()
Dim i&, FName$ ' I presume
Dim i1&, i2&, s1$, rngDoc As Range
Selection.HomeKey wdStory ' ?
i1 = getPoint("Account No.:", 1) ' get start
i2 = getPoint("IMPORTANT", 2) ' get end
Set rngDoc = ActiveDocument.Range(i1, i2)
s1 = rngDoc.Text
FName = "Prefix" & s1 & "Postfix" & Str$(i)
Stop ' and hover over FName
End Sub
Function getPoint&(sText$, iStart&) ' 1 for start, 2 for end
With Selection.Find
.Text = sText
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = True
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = False
.Execute
End With
If iStart = 1 Then
getPoint = Selection.End
Else
getPoint = Selection.Start
End If
End Function