Having formatting issue in creating a word document from a copy of another word document - vb.net

The application I support is creating an amalgamted Word document by copying couple of Word documents in one document right after each other.
The problem is the format of the some of the fields of the document that gets appended is changed in amalgamated document while the amalgamated document is the copy of AppendDocument (imagine if we have one document to copy in the amalgamated document)
The first and second line of of the Word document looks like:
From: Mr x To: Gary Y
Address: NorkYork Date: 2010/05/01
From:, To:, Address: and Date: are bold and size 10 in the AppendDocument while in amalgamated document they are bold but their size change to 12!
I confused I am not sure why the size for these 4 items are changed even their actual values have the same size!
Please see the below code which 2 documents path are passed. The first one is the BaseDocuemnt or amalgamated document and the second one is the document which is appended.
Private Sub DocumentAppend(ByVal strBaseDocument As String, ByVal strAppendDocument As String)
Dim FirstDocument As Boolean
Dim fleBaseDocument As File
Dim wrdRange As Word.Range
Dim wrdAppendDocument As Word.DocumentClass
wrdAppendDocument = New Word.DocumentClass()
Dim AmalgamatedDocument As Word.DocumentClass
AmalgamatedDocument = New Word.DocumentClass()
Dim wrdApp As Word.ApplicationClass
wrdApp = AmalgamatedDocument.Application
Dim AmalgamatedDocumentRange As Word.Range
Try
wrdApp.Visible = True
If fleBaseDocument.Exists(strBaseDocument) Then
FirstDocument = False
AmalgamatedDocument = wrdApp.Documents.Open(strBaseDocument)
Else
FirstDocument = True
AmalgamatedDocument = wrdApp.Documents.Add()
End If
AmalgamatedDocumentRange = AmalgamatedDocument.Content
AmalgamatedDocumentRange.Collapse(Word.WdCollapseDirection.wdCollapseEnd)
If Not FirstDocument Then
AmalgamatedDocumentRange.InsertBreak (Word.WdBreakType.wdSectionBreakNextPage)
End If
''# get the document to be appended
wrdAppendDocument = wrdApp.Documents.Open(strAppendDocument)
wrdAppendDocument.Activate()
wrdAppendDocument.Select()
''# +++++++++++++++++++++++
wrdApp.Selection.Copy()
wrdApp.Selection.CopyFormat()
AmalgamatedDocument.Activate()
wrdRange = AmalgamatedDocument.Content
wrdRange.Collapse(Word.WdCollapseDirection.wdCollapseEnd)
wrdRange.Paste()
''# New
wrdApp.Selection.PasteFormat()
''# +++++++++++++++++++++++
wrdAppendDocument.Close()
''# save the new document
AmalgamatedDocument.SaveAs(FileName:=strBaseDocument)
AmalgamatedDocument.Close()
End Sub
Any advice would be greatly appreciated!

Well, first, I'd start by trying to avoid use of the SELECTION object when you're not actually wanting to manipulate the active onscreen document in Word.
I'd also suggest looking into the Range.InsertFile method. Basically, you open or create your "target" document, then obtain a range object of the CONTENT, collapse it to the end, and finally invoke the INSERTFILE to insert the file at that point.
Something like this
dim rngend = Doc.Content
rngend.Collapse(WdCollapseDirection.wdCollapseEnd)
rngend.InsertFile(File, ConfirmConversions:=False, Link:=False, Attachment:=False)
That will usually preserve formatting faithfully, though I've run into some off situations where it's not quite 100%

Related

How can i change every occurence of a specific font ind a Word document?

i have following problem. Im currently creating a Macro that gets every font thats been used in a Word document. Afterwards it checks, if this font is even installed and changes the font into predefined fonts. (As the Microsoft auto-font-change in Word is pretty bad and changes my fonts into Comic Sans (no joke ...).
Everything works as intended except for one thing.
This here is the code i am using to exchange every occurence of the found
font in the document:
For i = 0 To UBound(missingFont)
For Each oCharacter In ActiveDocument.Range.Characters
If oCharacter.Font.name = missingFont(i) Then
oCharacter.Font.name = fontToUse
If InStr(missingFont(i), "bold") Then
oCharacter.Font.Bold = True
End If
If InStr(missingFont(i), "italic") Then
oCharacter.Font.Italic = True
End If
End If
Next oCharacter
Next i
So basically im checking every Character in my document and change it if needed. Now this only works for Characters that are not inside of textfields, the header or footer. How can i check every, EVERY, character inside of the Document?
First i've tried to use ActiveDocument.Range.Paragraphs instead of ActiveDocument.Range.Characters. I've also tried using the macro given here: http://www.vbaexpress.com/forum/showthread.php?55726-find-replace-fonts-macro but couldnt get this to work at all.
It's not clear what is meant by "textfield" as that could be any of five or six different things in Word...
But there is a way to access almost everything (excluding ActiveX controls) in a Word document by looping all StoryRanges. A StoryRange includes the main body of the document, headers, footers, footnotes, text ranges in Shapes, etc.
The following code sample demonstrates how to loop all the "Stories" in a document. I've put the code provided in the question in a separate procedure that's called from the "Stories" loop. (Note that I am not able to test, not having access to either the documents or relevant portions of code used in the question.)
Sub ProcessAllStories()
Dim doc as Word.Document
Dim missingFont as Variant
Dim myStoryRange as Word.StoryRange
'Define missingFont
Set doc = ActiveDocument
For Each myStoryRange In doc.StoryRanges
CheckFonts myStoryRange, missingFont
Do While Not (myStoryRange.NextStoryRange Is Nothing)
Set myStoryRange = myStoryRange.NextStoryRange
CheckFonts myStoryRange, missingFont
Loop
Next myStoryRange
End Sub
Sub CheckFonts(rng as Word.Range, missingFont as Variant)
Dim oCharacter as Word.Range
For i = 0 To UBound(missingFont)
For Each oCharacter In rng.Characters
If oCharacter.Font.name = missingFont(i) Then
oCharacter.Font.name = fontToUse
If InStr(missingFont(i), "bold") Then
oCharacter.Font.Bold = True
End If
If InStr(missingFont(i), "italic") Then
oCharacter.Font.Italic = True
End If
End If
Next oCharacter
Next i
End Sub

Insert RichText (From RichTextBox, RTF File, OR Clipboard) into Word Document (Bookmarks or Find/Replace)

To summarize what I'm attempting to do, I work for a non-profit organization that sends out acknowledgement letters when someone donates money to us (a thank you, basically). We have multiple different letters that are written every month and sent to IS to "process". I would like to make this as efficient and use as little time as possible for IS, so I've created a program in VB.NET that takes content and pastes it into a template using Word bookmarks, updates a table in SQL so that the letter can be tested with live data, and sends an e-mail to the Production department letting them know to test the letter. It works fully, except...
I cannot for the life of me figure out how to retain RTF (RichText) when I insert the content into the letter template.
I've tried saving the content of the RichTextBox as an RTF file, but I can't figure out how to insert the RTF file contents into my document template and replace the bookmark.
I've tried using the Clipboard.SetText, odoc......Paste method, but it's unreliable as I can't accurately state where I'd like the text to paste. The find/replace function isn't very helpful because all of the bookmarks I'm trying to replace are within text boxes.
I'd show some code, but most of it has been deleted out of frustration for not working. Either way, here's some code I've been working with:
Private Sub testing()
strTemplateLocation = "\\SERVER\AcknowledgementLetters\TEST\TEMPLATE.dot"
Dim Selection As Word.Selection
Dim goWord As Word.Application
Dim odoc As Word.Document
goWord = CreateObject("Word.Application")
goWord.Visible = True
odoc = goWord.Documents.Add(strTemplateLocation)
Clipboard.Clear()
Clipboard.SetText(txtPreD.Rtf, TextDataFormat.Rtf)
odoc.Content.Find.Execute(FindText:="<fp>", ReplaceWith:=My.Computer.Clipboard.GetText)
'Code for looping through all MS Word Textboxes, but didn't produce desired results
For Each oCtl As Shape In odoc.Shapes
If oCtl.Type = Microsoft.Office.Core.MsoShapeType.msoTextBox Then
oCtl.TextFrame.TextRange.Text.Replace("<fp>", "Test")
goWord.Selection.Paste()
End If
Next
'Clipboard.Clear()
'Clipboard.SetText(txtPostD.Rtf, TextDataFormat.Rtf)
'odoc.Content.Find.Execute(FindText:="<bp>", ReplaceWith:="")
'goWord.Selection.Paste()
MsgBox("Click Ok when finished checking.")
odoc.SaveAs2("\\SERVER\AcknowledgementLetters\TEST\TEST.docx")
odoc = Nothing
goWord.Quit(False)
odoc = Nothing
goWord = Nothing
End Sub
...and here is the default code for setting bookmarks. This works perfectly as long as formatting is not required:
Private Sub SetBookmark(odoc As Object, strBookmark As String, strValue As String)
Dim bookMarkRange As Object
If odoc.Bookmarks.Exists(strBookmark) = False Then
Exit Sub
End If
bookMarkRange = odoc.Bookmarks(strBookmark).Range
If ((Err.Number = 0) And (Not (bookMarkRange Is Nothing))) Then
bookMarkRange.text = strValue
odoc.Bookmarks.Add(strBookmark, bookMarkRange)
bookMarkRange = Nothing
End If
End Sub
TL;DR - Need formatted text (Example: "TEST") to be inserted into a Word document either as a bookmark or as a replacement text.
Expected results: Replace "fp" (front page) bookmark with "TEST" including bold formatting.
Actual results: "fp" is not replaced (when using clipboard and find/replace method), or is replaced as "TEST" with no formatting.
I figured it out! I had to do it a weird way, but it works.
The following code saves the RichTextBox as an .rtf file:
RichTextBoxName.SaveFile("temp .rtf file location")
I then used the following code to insert the .rtf file into the bookmark:
goWord.ActiveDocument.Bookmarks("BookmarkName").Select()
goWord.Selection.InsertFile(FileName:="temp .rtf file location")
I then deleted the temp files:
If My.Computer.FileSystem.FileExists("temp .rtf file location") Then
My.Computer.FileSystem.DeleteFile("\temp .rtf file location")
End If

Is there a way to list broken internal hyperlinks with VBA in MS Word? (Hyperlink Subaddress)

In MS Word, you can create hyperlinks to a "Place in this document" so that a link takes you someplace else in the same Word file. However, if you change headers or move things around these links will sometimes break. I want to write some VBA to check for broken links.
With VBA, you can list each hyperlink subaddress using the code below:
Sub CheckLinks()
Set doc = ActiveDocument
Dim i
For i = 1 To doc.Hyperlinks.Count
Debug.Print doc.Hyperlinks(i).SubAddress
Next
End Sub
The output from the code above also matches what is shown in the field codes for the hyperlink.
However, I'm not really clear on how to verify if the SubAddress is correct. For example, an excerpt from the program output shows this:
_Find_a_Staff_1
_Edit_Organization_Settings_2
_Set_the_Staff
_Find_a_Staff_1
But there's no obvious way to tell what the "correct" suffix should be for a given heading. Any thoughts on how to check if these are valid?
Is there a way to get the list of all valid subaddresses for the headings in the document?
The code below will list the hyperlinks where the corresponding bookmark does not exist in the document. (Note that it only detects missing links, not links that go to the wrong place.)
Sub CheckLinks()
Dim doc As Document
Set doc = ActiveDocument
Dim i, j
Dim found As Boolean
For i = 1 To doc.Hyperlinks.Count
found = False
For j = 1 To doc.Bookmarks.Count
If doc.Range.Bookmarks(j).Name = doc.Hyperlinks(i).SubAddress Then
found = True
End If
Next
If found = False Then
Debug.Print doc.Hyperlinks(i).SubAddress
End If
Next
End Sub

VBA Macro to Replace Endnotes in Word

I am hoping to use a macro to replace the endnotes in a word document. Here is my situtation:
I have two word docs. Both documents have the exact same number of endnotes. One document is full of the correct body content, but has placeholders for the end notes. The other document has outdated content, but has the correct endnotes to to fill the placeholders in the first document.
I have setup a macro below that can loop through all of the endnotes in the correct file, and then opens the other document called "old.docx" below. I just dont know how to go about replacing the endnotes in old.docx with the value of ftstr (please see below).
Any help would be great!
Sub endnoteReplacer()
Dim ft As Endnote
Dim wrdApp As Object
Dim wrdDoc As Object
Dim r1 As Range, ftstr As String, mark
Set wrdApp = CreateObject("Word.Application")
wrdApp.Visible = False
Set wrdDoc = wrdApp.Documents.Open("C:\Desktop\old.docx")
For Each ft In Word.ActiveDocument.Endnotes
ftstr = ft.Range.Text
wrdDoc.Endnotes(ft.Index).Range.Text = ftstr
Next ft
End Sub
If I get you right you need this simple solution to add within your loop:
For Each ft In Word.ActiveDocument.Endnotes
ftstr = ft.Range.Text
'change value of corresponding footnote in old.docx to value of ftstr
'!! new line !!
wrdDoc.Endnotes(ft.Index).Range.Text = ftstr
Next ft
But I assumed that you need to change endnotes(1) to endnotes(1), 2 to 2, etc...

Add a content control after an existing content control in word 2010 using vba

A little more detail:
I am inserting (lots of) documents with content controls into a single document.
One of the content controls in each doc is a title control (linked to document property), which naturally receives the same value as the destination document's title on insert.
Renaming the control's title and or tag, using word or vba does not fix the problem (weird!)
My proposed solution is to create a new control with a different name, copy across the .range.text from the original title control and then delete the title control.
I have a loop which goes through all the files that need changing which works fine. However, whatever I seem to do, any new controls that I create appear at the beginning of the document and not in the correct place (there is a control with a code for the document before it).
Ideas? As an aside is there any logical reason why changing the control names doesn't work?
Current code:
Sub FieldChanger()
Dim docCur As Document
Dim strCurPath As String
Dim strCurFile As String
Dim rngTitle As Range
Dim strTitle As String
Dim ccName As ContentControl
strCurPath = "C:\Users\User\Desktop\BGS\Final\"
strCurFile = Dir(strCurPath & "*.docx")
Do While strCurrentFile <> ""
Set docCur = Application.Documents.Open(strCurPath & strCurFile)
With docCur.ContentControls
.Item(1).LockContents = False //Unlock outer content control
Set rngTitle = .Item(3).Range
strTitle = rngTitle.Text
rngTitle = rngTitle.Move(wdCharacter, 1)
ccName = rngTitle.ContentControls.Add(wdContentControlRichText) //This line throws a 4198 error
ccName.Title = "ccName"
ccName.Tag = "ccName"
ccName.Range = strTitle
ccName.LockContentControl = True
.Item(3).LockContentControl = False
.Item(3).Delete
.Item(1).LockContents = True //Lock outer content control
End With
docCur.Save
docCur.Close
strCurFile = Dir
Loop
End Sub
As an aside is there any logical reason why changing the control names doesn't work?
The Content Control (CC) name is just a name. Renaming the CC from "Title" doesn't change where Word gets the content from. Nor would naming a CC as "Title" cause Word to put the document's title string in the CC. If you create an empty document, insert the Title document property (as a CC) and look at the value of
activedocument.ContentControls(1).XMLMapping.XPath
you will probably see the value
/ns1:coreProperties[1]/ns0:title[1]
This is what tells Word that it needs to put the value of the Title builtin document property in the CC, and where to go to get it. You can link your own plain text CCs to builtin properties using the same mechanism, or you can link them to nodes in "Custom XML parts" of your own. But they don't have to be linked to anything.
As for the code, how about something more like this (NB, I have also changed "strCurrentFile" to strCurFile). I wondered whether you really need to re-insert the CC value as a new CC (i.e. why not just remove the CC and leave its existing value there) but have assumed that you need the CC there.
NB, as a general rule in VBA you need to use the Set keyword when setting the value of objects such as range variables and CCs. In theory you should also set objects to Nothing (e.g. Set rngTitle = Nothing) when you have finished with them. I haven't added that stuff here. In VB.NET you don't need to do either of those things.
Dim docCur As Document
Dim strCurPath As String
Dim strCurFile As String
Dim rngTitle As Range
Dim strTitle As String
Dim ccName As ContentControl
strCurPath = "C:\a\test\"
strCurFile = Dir(strCurPath & "*.docx")
Do While strCurFile <> ""
Set docCur = Application.Documents.Open(strCurPath & strCurFile)
With docCur.ContentControls
.Item(1).LockContents = False 'Unlock outer content control
Set rngTitle = .Item(3).Range
strTitle = rngTitle.Text
' we need the following line to ensure that deleting the range
' does not remove the CC prematurely
.Item(3).Temporary = False
rngTitle.Delete
rngTitle.Collapse wdCollapseStart
' Delete the control here instead of later
.Item(3).LockContentControl = False
.Item(3).Delete
Set ccName = rngTitle.ContentControls.Add(wdContentControlRichText)
ccName.Title = "ccName"
ccName.Tag = "ccName"
ccName.Range = strTitle
ccName.LockContentControl = True
.Item(1).LockContents = True 'Lock outer content control
End With
docCur.Save
docCur.Close
strCurFile = Dir
Loop
Comment consolidation...
There are addins that may help, e.g. the databinding toolkit at cctw.codeplex.com (not checked that link recently)