ActiveDocument.Printout issue - vba

I have two word documents, source and target. My target document is pulling values from a table inside source document and putting those concatenated values stored inside a variable named ReportHeader into it's page header. I have used two For-Next loops, the outer loops takes care of designated rows inside source document and this loop begins at 6th row till count of total rows inside that table.
My inner loop named "For xPages = 1 To numPages" takes care of number of parges required for each Annexure that it finds inside the table and loops through found number of pages for a particular Annexure reference.
Below is reference screenshot of my table. Please ignore the fact the few preceding lines of my tableare not being shown here. My business begins with 6th row that shows Annexure A.
Everything works fine as long as I test values in Debug.print or Msgbox but when I sent these values for printing using ActiveDocument.PrintOut, I notice an abnormal printing behaviour. First few rows starting at row 6 of table are ignored and later not all the pages are sent to printing.
Any feedback from you guys would be much appreciated.
Thanks
Syed
Sub PrintMyHeaders()
Dim r As Range
Dim sourceDoc, jobNumber, AnnexureRaw, Annexure, ReportHeader As String
Dim numPages As Integer
Application.ScreenUpdating = False
jobNumber = InputBox("Enter job number")
sourceDoc = ActiveDocument.Name
ActiveDocument.Tables(3).Range.ListFormat.RemoveNumbers
Set r = ActiveDocument.Tables(3).Range
For Each doc In Documents
If doc.Name = "Template.doc" Then Found = True
Next doc
If Found <> True Then
Documents.Open FileName:="C:\Users\smi\Documents\Template.doc"
Else
Documents("Template.doc").Activate
End If
Documents(sourceDoc).Activate
For i = 6 To r.Rows.Count
AnnexureRaw = Replace(r.Rows(i).Cells(2).Range.Text, "", "")
Annexure = Replace(AnnexureRaw, Chr(13), "")
numPages = Val(r.Rows(i).Cells(3).Range.Text)
For xPages = 1 To numPages
counter = counter + 1
ReportHeader = "PAGE " & xPages & " OF " & numPages & vbCrLf _
& "OUR REF: TKU-" & jobNumber & "/2018" & vbCrLf _
& "ANNEXURE : " & Chr(34) & Annexure & Chr(34)
Documents("Template.doc").Activate
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.Font.Name = "Arial"
Selection.Font.Size = 8
Selection.Font.Bold = True
Selection.Text = ReportHeader
Selection.ParagraphFormat.LineSpacingRule = wdLineSpaceExactly
Selection.ParagraphFormat.LineSpacing = 6
Selection.ParagraphFormat.Alignment = wdAlignParagraphRight
ActiveDocument.PrintOut
'Debug.Print ReportHeader & vbCrLf
Documents(sourceDoc).Activate
Next xPages
Next
Documents("template.doc").Activate
ActiveDocument.Close SaveChanges:=wdDoNotSaveChanges
Documents(sourceDoc).Activate
Application.ScreenUpdating = True
End Sub

When printing out documents in relatively quick succession, things can get "mixed up" if background printing is turned on. This is an option in Word:
File/Options/Advanced, section "Print"
In the object model, to turn it off programmatically you can use the following. If the user likes having it turned on, save the current setting, turn it off, then restore the setting at the end of the code:
Options.PrintBackground = False
Since speed of execution is a concern, your code can be optimized. The following suggestions can also help to make it more self-documenting. Note that I'm not testing, just writing from the top of my head, so I may make a typo or two...
Put Option Explicit at the top of your code modules. This will save you from frustrating error messages if you mistype a variable name.
Use declared objects throughout. Referring back to ActiveDocument each time is less efficient (VBA has to figure out which it is, every time. Also, the active document could change during code execution, which would mess things up.) Ditto for repeatedly requesting Tables(3).
If your concern when looping a collection is to ensure a certain member is present (a particular document, in your case) you can use Exit For to break off the loop before all members have been queried.
It's not necessary to activate a document in order to work with it in code. Once you have Document objects, that's all you need.
It's also not necessary to fiddle with the Views in order to work with a header or footer. I've deleted those lines.
I hope I've kept "everything straight"!
Option Explicit
Sub PrintMyHeaders()
Dim r As Range
Dim sourceDoc, jobNumber, AnnexureRaw, Annexure, ReportHeader As String
Dim numPages As Integer
Dim sourceTable as Word.Table, doc as Word.Document
Dim tDoc as Word.Document
Application.ScreenUpdating = False
jobNumber = InputBox("Enter job number")
Set sourceDoc = ActiveDocument
Set sourceTable = sourceDoc.Tables(3)
sourceTable.Range.ListFormat.RemoveNumbers
Set r = sourceTable.Range
For Each doc In Documents
If doc.Name = "Template.doc" Then
Found = True
Exit For
End If
Next doc
If Found <> True Then
Set tDoc = Documents.Open(FileName:="C:\Users\smi\Documents\Template.doc")
Else
Set tDoc = Documents("Template.doc")
End If
For i = 6 To r.Rows.Count
AnnexureRaw = Replace(r.Rows(i).Cells(2).Range.Text, "", "")
Annexure = Replace(AnnexureRaw, Chr(13), "")
numPages = Val(r.Rows(i).Cells(3).Range.Text)
For xPages = 1 To numPages
counter = counter + 1
ReportHeader = "PAGE " & xPages & " OF " & numPages & vbCrLf _
& "OUR REF: TKU-" & jobNumber & "/2018" & vbCrLf _
& "ANNEXURE : " & Chr(34) & Annexure & Chr(34)
Dim rngHeader as Word.Range
Set rngHeader = tDoc.Sections(1).Headers(wdHeaderFooterPrimary).Range
rngHeader.Font.Name = "Arial"
rngHeader.Font.Size = 8
rngHeader.Font.Bold = True
rngHeader.Text = ReportHeader
rngHeader.ParagraphFormat.LineSpacingRule = wdLineSpaceExactly
rngHeader.ParagraphFormat.LineSpacing = 6
rngHeader.ParagraphFormat.Alignment = wdAlignParagraphRight
tDoc.PrintOut
'Debug.Print ReportHeader & vbCrLf
Next xPages
Next
tDoc.Close SaveChanges:=wdDoNotSaveChanges
Application.ScreenUpdating = True
End Sub

Related

Find the column related to comments contained in a cell of a MS Word table

I have a Word document containing a table with two columns
column 1 contains numbers
column 2 contains text
Users introduce comments on the text in column 2 (see drawing).
I can create a table putting together all the comments with this code.
How do I access the number of the other column with reference to the texts commented?
The result so far is like this:
I need the number in the first column next to the text containing the comment.
I guess there is a method similar to:
oDoc.Comments(n).Scope.Information(wdActiveEndPageNumber)
but accessing the table cell - and then I could refer to the same row and first column to grab the content of the first column?
Following is code that produces the table above. Be aware the code does not take into account that the comments are made on text belonging to table cells, Which is what I am looking for.
Sub ExtractCommentsToNewDocument()
'=========================
'Macro created 2007 by Lene Fredborg, DocTools - www.thedoctools.com
'Revised October 2013 by Lene Fredborg: Date column added to extract
'THIS MACRO IS COPYRIGHT. YOU ARE WELCOME TO USE THE MACRO BUT YOU MUST KEEP THE LINE ABOVE.
'YOU ARE NOT ALLOWED TO PUBLISH THE MACRO AS YOUR OWN, IN WHOLE OR IN PART.
'=========================
'The macro creates a new document
'and extracts all comments from the active document
'incl. metadata
'Minor adjustments are made to the styles used
'You may need to change the style settings and table layout to fit your needs
'=========================
Dim oDoc As Document
Dim oNewDoc As Document
Dim oTable As Table
Dim nCount As Long
Dim n As Long
Dim Title As String
Title = "Extract All Comments to New Document"
Set oDoc = ActiveDocument
nCount = ActiveDocument.Comments.Count
If nCount = 0 Then
MsgBox "The active document contains no comments.", vbOKOnly, Title
GoTo ExitHere
Else
'Stop if user does not click Yes
If MsgBox("Do you want to extract all comments to a new document?", _
vbYesNo + vbQuestion, Title) <> vbYes Then
GoTo ExitHere
End If
End If
Application.ScreenUpdating = False
'Create a new document for the comments, base on Normal.dot
Set oNewDoc = Documents.Add
'Set to landscape
oNewDoc.PageSetup.Orientation = wdOrientLandscape
'Insert a 4-column table for the comments
With oNewDoc
.Content = ""
Set oTable = .Tables.Add _
(range:=Selection.range, _
NumRows:=nCount + 1, _
NumColumns:=5)
End With
'Insert info in header - change date format as you wish
oNewDoc.Sections(1).Headers(wdHeaderFooterPrimary).range.Text = _
"Comments extracted from: " & oDoc.FullName & vbCr & _
"Created by: " & Application.UserName & vbCr & _
"Creation date: " & Format(Date, "MMMM d, yyyy")
'Adjust the Normal style and Header style
With oNewDoc.Styles(wdStyleNormal)
.Font.Name = "Arial"
.Font.Size = 10
.ParagraphFormat.LeftIndent = 0
.ParagraphFormat.SpaceAfter = 6
End With
With oNewDoc.Styles(wdStyleHeader)
.Font.Size = 8
.ParagraphFormat.SpaceAfter = 0
End With
'Format the table appropriately
With oTable
.range.Style = wdStyleNormal
.AllowAutoFit = False
.PreferredWidthType = wdPreferredWidthPercent
.PreferredWidth = 100
.Columns.PreferredWidthType = wdPreferredWidthPercent
.Columns(1).PreferredWidth = 5
.Columns(2).PreferredWidth = 23
.Columns(3).PreferredWidth = 42
.Columns(4).PreferredWidth = 18
.Columns(5).PreferredWidth = 12
.Rows(1).HeadingFormat = True
End With
'Insert table headings
With oTable.Rows(1)
.range.Font.Bold = True
.Cells(1).range.Text = "Page"
.Cells(2).range.Text = "Code"
.Cells(3).range.Text = "Text"
.Cells(4).range.Text = "Interview"
.Cells(5).range.Text = "Date"
End With
'Get info from each comment from oDoc and insert in table
For n = 1 To nCount
With oTable.Rows(n + 1)
'Page number
.Cells(1).range.Text = _
oDoc.Comments(n).Scope.Information(wdActiveEndPageNumber)
'The comment itself
.Cells(2).range.Text = oDoc.Comments(n).range.Text
'The text marked by the comment
.Cells(3).range.Text = oDoc.Comments(n).Scope
'The comment author
.Cells(4).range.Text = oDoc.Comments(n).Author
'The comment date in format dd-MMM-yyyy
.Cells(5).range.Text = Format(oDoc.Comments(n).Date, "dd-MMM-yyyy")
End With
Next n
Application.ScreenUpdating = True
Application.ScreenRefresh
oNewDoc.Activate
MsgBox nCount & " comments found. Finished creating comments document.", vbOKOnly, Title
ExitHere:
Set oDoc = Nothing
Set oNewDoc = Nothing
Set oTable = Nothing
End Sub
After:
.Cells(3).Range.Text = oDoc.Comments(n).Scope
Insert:
If oDoc.Comments(n).Scope.Information(wdWithInTable) = True Then
If oDoc.Comments(n).Scope.Cells(1).ColumnIndex > 1 Then
.Cells(3).Range.InsertBefore Split(oDoc.Comments(n).Scope.Rows(1).Cells(1).Range.Text, vbCr)(0) & vbTab
End If
End If

There is a way to change the author of revisions in a Word document?

I would like to know if there is a way to change the author of revisions in a document, I've found the way to change the author of comments but no for the revisions part. I've tried to find a property/method in the Revisions.object documentation to change that but I find nothing.
As I said I've already tried to do this:
Sub ChangeCommentCreator()
Dim I As Long
Dim Novo As String
Dim Corto As String
If Selection.Comments.Count = 0 Then
MsgBox "No comments in your selection!", vbInformation, "Alerta"
Exit Sub
End If
Novo = InputBox("New author name?", "Alerta")
Corto = InputBox("New author initials?", "Alerta")
If Novo = "" Or Corto = "" Then
MsgBox "The author name/initials can’t be empty.", vbInformation, "Alerta"
Exit Sub
End If
With Selection
For I = 1 To .Comments.Count
.Comments(I).Author = Novo
.Comments(I).Initial = Corto
Next I
End With
End Sub
I'm going in the right way or there is just no way to change this?
Revisions automatically use the selected User Name in the UI. The language reference for the Revisions's Author property states:
Returns the name of the user who made the specified tracked change. Read-only String.
So there's no direct way using VBA and the object model to change that.
It is possible to change it by editing the underlying Word Open XML, as illustrated in the code that follows. I notice, however, that this appears to confuse Word - after running the macro no revisions are recognized in the document. Only after saving, closing and re-opening does Word "see" the revisions, again.
Sub ChangeAuthorName()
Dim sWOOXML As String
Dim findAuthor As String
Dim replaceAuthor As String
findAuthor = "w:author=" & Chr(34) & "Cindy Meister" & Chr(34)
replaceAuthor = "w:author=" & Chr(34) & "unknown" & Chr(34)
sWOOXML = ActiveDocument.content.WordOpenXML
sWOOXML = Replace(sWOOXML, findAuthor, replaceAuthor)
ActiveDocument.content.InsertXML sWOOXML
End Sub
Note that this will likely also change the author's name for comments. The more "elegant" way to do this would be to leverage an XML parser (such as MSXML) and work with the specific nodes. Or even use a package that works on the closed document and edits the Word Open XML. But this is the simplest way using straight Word VBA.
I picked up Cindy Meister's suggestion and cleared the Word confusion that he reported. All revision and comment authors, creators, and lastmodied will be changed by the name entered.
Sub ChangeCommentAndRevisionAuthor()
Dim j, jmax As Long
Dim Author(99) As String
Dim WXML, NewAuthor, FindAuthor, ReplaceAuthor As String
Dim BNew, StatusTrackRevision As Boolean
Dim Rev As Revision
Dim Cmt As Comment
' set some variables and put trackrevision to false
StatusTrackRevision = ActiveDocument.TrackRevisions
ActiveDocument.TrackRevisions = False
jmax = -1
' checks and input new author name
If ActiveDocument.Range.Revisions.count = 0 Then
MsgBox "No revisions in your document!", vbInformation, "Change comment and revision author"
Exit Sub
End If
NewAuthor = InputBox("New author name?", "Change comment and revision author")
If NewAuthor = "" Then
MsgBox "The author name can’t be empty.", vbInformation, "Change comment and revision author"
Exit Sub
End If
' Loop through all revisions and get all authors of revisions (maximum 100)
With ActiveDocument.Range
If .Revisions.count > 1000 Then
If MsgBox("The number of revisions it large. " & .Revisions.count & vbCr & " Do you want to continue?", vbOKCancel + vbQuestion + vbDefaultButton2, "Change comment and revision author") > 1 Then Exit Sub
End If
For Each Rev In .Revisions
BNew = True
For j = 0 To jmax
If Author(j) = Rev.Author Then
BNew = False
Exit For
End If
Next
If BNew Then
jmax = jmax + 1
If jmax > UBound(Author) Then jmax = UBound(Author)
Author(jmax) = Rev.Author
End If
Next
' change all comments
For Each Cmt In ActiveDocument.Comments
Cmt.Author = NewAuthor
Cmt.Initial = NewAuthor
Next
End With
' read XML and change all authors to the new author
WXML = ActiveDocument.Content.WordOpenXML
For j = 0 To jmax
WXML = Replace(WXML, "w:author=" & Chr(34) & Author(j) & Chr(34), "w:author=" & Chr(34) & NewAuthor & Chr(34))
Next
' change "last modified by" to new author
WXML = Replace(WXML, "<cp:lastModifiedBy>" & ActiveDocument.BuiltInDocumentProperties(7) & "</cp:lastModifiedBy>", "<cp:lastModifiedBy>" & NewAuthor & "</cp:lastModifiedBy>")
' save modified XML
ActiveDocument.Content.InsertXML WXML
' Change Creator of Document to new author
ActiveDocument.BuiltInDocumentProperties(3) = NewAuthor
' restore original status of track revision
ActiveDocument.TrackRevisions = StatusTrackRevision
End Sub

excel-vba: Turn text from cells with particular format into an object suitable for outlook e-mail body, while maintaining the same format properties

My problem is the following:
I want to define a range, including cells in my spreadsheet that contain formatted text (bold font), and turn it into any object that I can later use as the body for an outlook e-mail.
One of the ways I have tried so far is via the RangetoHTML function by Ron de Bruin (http://www.rondebruin.nl/win/s1/outlook/bmail2.htm). However, the function brings the text cells into another excel workbook which finally yields a table in the outlook e-mail. I want to keep the very same format that I start with in my excel cells. That is, it must be lines of ordinary text and not a table-like body in the mail.
That's my current code:
Sub Mail_Selection_Range_Outlook_Body()
Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
Set rng = Sheets("Preparation").Range("A90:A131")
With Selection
.Value = rng.Text
.Font.Bold = rng.Font.Bold
.Font.Color = rng.Font.Color
End With
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = ""
.CC = ""
.BCC = ""
.Subject = ""
.HTMLBody = RangetoHTML(rng)
.Display
End With
On Error GoTo 0
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Thanks in advance for your help
Ron de Bruin’s RangeToHtml shows how to use Excel’s PublishObjects to convert a worksheet range to Html that can be used as the body of an email. I am sure this has helped thousands of developers.
The difficulty that RdeB overcomes is that PublishObjects is designed to create and maintain webpages. His routine outputs to a file and then reads that file because that is the only way to get the Html string required for the email body.
The difficulty that RdeB cannot overcome is that PublishObjects create poor quality, proprietary CSS. By “poor quality”, I mean that there is a lot of unnecessary CSS and that row heights and column widths are defined in points to give sizes suitable for a PC. By “proprietary”, I mean it uses styles such as mso-ignore:padding and mso-number-format:General that only Microsoft browsers are guaranteed to understand. It appears the major browsers are able to cope but many people have found that some newer browsers cannot cope and display rubbish.
To demonstrate this and to test my code, I created a worksheet based on your image. Rows 16 to 18 are right-aligned because I have specified this. Rows 20 to 22 are right aligned because this is the Excel default for numeric, date and time values. Its appearance is:
You can use your real data.
Copy this code to your workbook:
Option Explicit
Sub Test1()
Dim PathCrnt As String
Dim PathFileCrnt As String
Dim RngStr As String
Dim WshtName As String
PathCrnt = ThisWorkbook.Path & "\" ' ## Output to the same folder as workbook holding the macro
PathFileCrnt = PathCrnt & "Test1.html" ' ## Change if you do not like my filename
WshtName = "Sheet1" ' ## Change to your worksheet
RngStr = "A1:A28" ' ## Change to your range
With ThisWorkbook
With .PublishObjects.Add(SourceType:=xlSourceRange, _
Filename:=PathFileCrnt, _
Sheet:=WshtName, _
Source:=RngStr, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
End With
End Sub
You will need to change some of the statements near the top marked with ##
Run this macro to output your range to the file.
On my laptop, Microsoft Edge, Microsoft Internet Explorer and Google Chrome all display the file and all look the same although IE and Chrome are slow to display. The column is down the centre of the window:
There are none of the background grey cells and wide, white border you showed. However, I have not tried to display it within Outlook.
Now look at the file with your favourite text editor. Notice how much CSS is repeated. Notice how many style start “mso-” indicating they are Microsoft extensions. Notice the heights and widths measured in “pt” (points). Some Html display engines can cope but some cannot.
I suspect that PublishObjects has not been maintained. It was available with Excel 2003 and perhaps earlier. Some of the old Microsoft CSS extensions now have standard CSS equivalents but PublishObjects has not been updated to use them.
I have my own RangeToHtml written entirely in VBA. It will handle all formatting except borders. My code is far too big to post on Stack Overflow so I have extracted the bits you need. You apparently need bold or not bold and left or right alignment. I do not know if you specify right alignment or if you have numeric fields which right align by default so I handle both.
My function ColToHtml(range) returns a complete Html file for the first column of a range. My code does not create a temporary workbook or a temporary file. It produces clean, crisp Html and Css. It produces a table because you cannot have right-alignment outside a table. However, with no borders, it is not obvious the output is a table. The only difference in appearance is that the table is left aligned. If you prefer a centred table, it would be an easy change.
This was my test routine:
Sub Test2()
Dim Rng As Range
With Worksheets("Sheet1")
Set Rng = .Range(.Cells(1, 1), .Cells(28, 1))
End With
Debug.Print ColumnToHtml(Rng)
End Sub
It outputs the Html string to the Immediate Window. I then copied it to a file. I could have used VBA to write to a file but this was easier. When I opened the file with Microsoft Edge, it looked the same. Have a look at this second file with your favourite text editor. Notice how much smaller it is. The PublishObjects version is 6,901 bytes while this second version is 1,681 bytes. Notice how only standard Css is used and that the minimum of Css is used. This allows the display engine to make its own decisions about how to display the file based on the type of output device.
My last test was:
Sub Test3()
' This will need a reference to Microsoft Outlook nn.0 Outlook library
' where nn is the number of the Outlook version you are using.
Dim Rng As Range
Dim OutApp As Outlook.Application
Dim MailItemNew As Outlook.MailItem
With Worksheets("Sheet1")
Set Rng = .Range(.Cells(1, 1), .Cells(28, 1))
End With
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set OutApp = CreateObject("Outlook.Application")
Set MailItemNew = OutApp.CreateItem(olMailItem)
With MailItemNew
.BodyFormat = olFormatHTML
.HTMLBody = ColumnToHtml(Rng)
.Display
End With
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set MailItemNew = Nothing
Set OutApp = Nothing
End Sub
This outputs the range to Outlook. I have used your code as a template but have referenced the Outlook library so I can use Outlook objects and constants. I had to reduce the font size to get it all on the screen at one time giving:
Again this has the same appearance except that the first letter of each line has been capitalized. I do not know how to stop the Outlook email editor doing this.
Incidentally, I selected the entire email and got the same appearance as in the image you posted.
The code for ColumnToHtml is below. Note that CellToHtml is the routine that actually creates the Html for a cell. It only handles bold and right alignment but it should be obvious that it would be easy to add other cell-level formats.
Function ColumnToHtml(ByRef RngCol As Range) As String
' Returns the first or only column of rng as a borderless table
' so it appears as a formatted list of rows.
Dim RngCell As Range
Dim RowCrnt As Long
Dim Table As String
' Build an Html table of the cells within the first column of RngCol
' ==================================================================
Table = Space(4) & "<table border=""0"">" & vbLf
For RowCrnt = RngCol.Row To RngCol.Row + RngCol.Rows.Count - 1
Set RngCell = RngCol.Worksheet.Cells(RowCrnt, RngCol.Column)
Table = Table & Space(6) & "<tr>" & CellToHtml(RngCell) & "</tr>" & vbLf
Next
Table = Table & Space(4) & "</table>"
' Build an Html file envelope around the table
' ============================================
ColumnToHtml = "<!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.0 Frameset//EN""" & _
"""http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"">" & vbLf & _
"<html xmlns=""http://www.w3.org/1999/xhtml"" xml:lang=""en"" lang=""en"">" & vbLf & _
" <head></head>" & vbLf & _
" <meta http-equiv=""Content-Type""content=""text/html; charset=utf-8""/>" & vbLf & _
" <style>" & vbLf & _
" td.bold {font-weight:bold;}" & vbLf & _
" td.hAlign-right {text-align:right;}" & vbLf & _
" </style>" & vbLf & _
" </head>" & vbLf & _
" <body>" & vbLf & Table & vbLf & _
" </body>" & vbLf & _
"</html>"
End Function
Function CellToHtml(ByRef RngCell As Range) As String
' Convert a single cell to Html.
' This code handles: value, bold or not-bold (default) and left )default) or
' right-alignment.
' Note RngCell.Value is the value perhaps "1234" or "42999".
' and RngCell.Text is the display text perhaps "1,234" or "21-Sep-17".
' This is particularly important with dates and time where the
' value is unlikely to be what is displayed.
' Dates are held as days since 1-Jan-1900 and times are held as
' seconds-since-midnight / seconds-in-a-day. It is the NumberFormat that
' determine what you see.
Dim BoldCell As Boolean
Dim RAlignedCell As Boolean
Dim Style As String
Dim StyleNeeded As Boolean
CellToHtml = "<td"
' Add interior formatting here if required
If RngCell.Value = "" Then
' Ignore font and alignment formatting of empty cell.
Else
' Test for formats
BoldCell = False
RAlignedCell = False
Style = ""
StyleNeeded = False
If RngCell.Font.Bold Then
BoldCell = True
StyleNeeded = True
End If
If RngCell.HorizontalAlignment = xlRight Or _
(RngCell.HorizontalAlignment = xlGeneral And _
(IsNumeric(RngCell.Value) Or IsDate(RngCell.Value))) Then
RAlignedCell = True
StyleNeeded = True
End If
If StyleNeeded Then
CellToHtml = CellToHtml & " class="""
If BoldCell Then
If Style <> "" Then
Style = Style & " "
End If
Style = Style & "bold"
End If
If RAlignedCell Then
If Style <> "" Then
Style = Style & " "
End If
Style = Style & "hAlign-right"
End If
CellToHtml = CellToHtml & Style & """"
End If
End If
CellToHtml = CellToHtml & ">" ' Terminate "<td"
If RngCell.Value = "" Then
' Blank rows are displayed narrow. Use Non-blank space so display at homral width
CellToHtml = CellToHtml & " "
Else
CellToHtml = CellToHtml & RngCell.Text
End If
CellToHtml = CellToHtml & "</td>"
End Function
One last comment. You have not selected anything so I do not see the purpose of this code:
With Selection
.Value = rng.Text
.Font.Bold = rng.Font.Bold
.Font.Color = rng.Font.Color
End With

VBA to insert reference page into MS word endnote

Book endnotes often forgo superscript numbers for page numbers. E.g., instead of
Abe Lincoln was assassinated with a pistol.^33
:
33. A single-shot derringer pistol.
books by several authors write
Abe Lincoln was assassinated with a pistol.
:
Page 297. Abe Lincoln was shot single-shot derringer pistol.
Word doesn't have this feature, so I believe it would have to be a Macro. I came up with simple code below that loops through all of the endnotes and adds
"Page ???. "
before each endnote, but what does "???" need to be to correctly insert the page number in my manuscript that the citation's located on?
Sub RedefineExistingEndNotes()
Dim fn As Endnote
For Each fn In ActiveDocument.Endnotes
fn.Range.Paragraphs(1).Range.Font.Reset
fn.Range.Paragraphs(1).Range.Characters(1).InsertBefore "Page" & "???" & " - "
Next fn
End Sub
Try the below VBA code:
Sub InsertPageNumberForEndnotes()
Dim endNoteCount As Integer
Dim curPageNumber As Integer
If ActiveDocument.Endnotes.Count > 0 Then
For endNoteCount = 1 To ActiveDocument.Endnotes.Count
Selection.GoTo What:=wdGoToEndnote, Which:=wdGoToAbsolute, Count:=endNoteCount
curPageNumber = Selection.Information(wdActiveEndPageNumber)
ActiveDocument.Endnotes(endNoteCount).Range.Select
ActiveDocument.Application.Selection.Collapse (WdCollapseDirection.wdCollapseStart)
ActiveDocument.Application.Selection.Paragraphs(1).Range.Characters(1).InsertBefore "Page " & CStr(curPageNumber) & " - "
Next
End If
End Sub
An alternative might be to use PAGEREF fields and hide the endnote references, e.g.
Sub modifyEndNotes()
Const bookmarkText As String = "endnote"
Dim en As Word.Endnote
Dim rng As Word.Range
For Each en In ActiveDocument.Endnotes
en.Reference.Bookmarks.Add bookmarkText & en.Index
en.Reference.Font.Hidden = True
Set rng = en.Range
rng.Paragraphs(1).Range.Font.Hidden = True
rng.Collapse WdCollapseDirection.wdCollapseStart
rng.Text = "Page . "
rng.SetRange rng.End - 2, rng.End - 2
rng.Fields.Add rng, WdFieldType.wdFieldEmpty, "PAGEREF " & bookmarkText & en.Index & " \h", False
'if necessary...
'rng.Fields.Update
en.Range.Font.Hidden = False
Next
Set rng = Nothing
End Sub
For a second run, you'd need to remove and re-insert the text and fields you had added.
Unfortunately, a further look suggests that it would be difficult, if not impossible, to hide the endnote references (in the endnotes themselves) without hiding the paragraph marker at the end of the first endnote para, which means that all the endnotes will end up looking like a single messy note. So I deleted this Answer.
However, the OP thought the approach could be modified in a useful way so I have undeleted. I can't re-research it right away but some possibilities might be to replace every endnote mark by a bullet (as suggested by the OP) or perhaps even something as simple as a space or a "-".
For example, something like this (which also hides the references using a different technique)...
Sub modifyEndNotes2()
' this version also formats the endnotes under page headings
Const bookmarkText As String = "endnote"
Dim en As Word.Endnote
Dim f As Word.Field
Dim i As Integer
Dim rng As Word.Range
Dim strSavedPage As String
strSavedPage = ""
For Each en In ActiveDocument.Endnotes
en.Reference.Bookmarks.Add bookmarkText & en.Index
Set rng = en.Range
rng.Collapse WdCollapseDirection.wdCollapseStart
If CStr(en.Reference.Information(wdActiveEndPageNumber)) <> strSavedPage Then
strSavedPage = CStr(en.Reference.Information(wdActiveEndPageNumber))
rng.Text = "Page :-" & vbCr & " - "
rng.SetRange rng.End - 6, rng.End - 6
rng.Fields.Add rng, WdFieldType.wdFieldEmpty, "PAGEREF " & bookmarkText & en.Index & " \h", False
rng.Collapse WdCollapseDirection.wdCollapseEnd
Else
rng.Text = "- "
End If
Next
If ActiveDocument.Endnotes.Count > 1 Then
ActiveDocument.Styles(wdStyleEndnoteReference).Font.Hidden = True
Else
ActiveDocument.Styles(wdStyleEndnoteReference).Font.Hidden = False
End If
Set rng = Nothing
End Sub
In the above case, notice that there is only one link to each page, that formatting might be needed to make it obvious that it is a link, and so on.

Using Multiple wildcard searches in Word 2007 with VBA

I have VBA code that runs through a document and identifies acronyms using wildcards and places them in a separate word document. Some of my writers don't always follow the proper style guides for acronyms so I'm running four different scripts to find all the possible acronyms. It's time consuming and I end up with multiple documents. Is there a method to run multiple searches from one script and have all the results placed in the separate document. Truth in Advertising: I found this script on the 'net, but I've been playing with it to attempt to make it do some other features. Adding current script:
Sub ExtractVariousValuesACRONYMSToNewDocument()
'The macro creates a new document,
'finds all words consisting of 2 or more uppercase letters
'in the active document and inserts the words
'in column 1 of a 3-column table in the new document
'Each acronym is added only once
'Use column 2 for definitions
'Page number of first occurrence is added by the macro in column 3
'Minor adjustments are made to the styles used
'You may need to change the style settings and table layout to fit your needs
'=========================
Dim oDoc_Source As Document
Dim oDoc_Target As Document
Dim strListSep As String
Dim strAcronym As String
Dim oTable As Table
Dim oRange As Range
Dim n As Long
Dim strAllFound As String
Dim Title As String
Dim Msg As String
Title = "Extract Acronyms to New Document"
'Show msg - stop if user does not click Yes
Msg = "This macro finds all words consisting of 2 or more " & _
"uppercase letters and extracts the words to a table " & _
"in a new document where you can add definitions." & vbCr & vbCr & _
"Do you want to continue?"
If MsgBox(Msg, vbYesNo + vbQuestion, Title) <> vbYes Then
Exit Sub
End If
Application.ScreenUpdating = False
'Find the list separator from international settings
'May be a comma or semicolon depending on the country
strListSep = Application.International(wdListSeparator)
'Start a string to be used for storing names of acronyms found
strAllFound = "#"
Set oDoc_Source = ActiveDocument
'Create new document for acronyms
Set oDoc_Target = Documents.Add
With oDoc_Target
'Make sure document is empty
.Range = ""
'Insert info in header - change date format as you wish
.PageSetup.TopMargin = CentimetersToPoints(3)
.Sections(1).Headers(wdHeaderFooterPrimary).Range.Text = _
"Acronyms extracted from: " & oDoc_Source.FullName & vbCr & _
"Created by: " & Application.UserName & vbCr & _
"Creation date: " & Format(Date, "MMMM d, yyyy")
'Adjust the Normal style and Header style
With .Styles(wdStyleNormal)
.Font.Name = "Arial"
.Font.Size = 10
.ParagraphFormat.LeftIndent = 0
.ParagraphFormat.SpaceAfter = 6
End With
With .Styles(wdStyleHeader)
.Font.Size = 8
.ParagraphFormat.SpaceAfter = 0
End With
'Insert a table with room for acronym and definition
Set oTable = .Tables.Add(Range:=.Range, NumRows:=2, NumColumns:=3)
With oTable
'Format the table a bit
'Insert headings
.Range.Style = wdStyleNormal
.AllowAutoFit = False
.Cell(1, 1).Range.Text = "Acronym"
.Cell(1, 2).Range.Text = "Definition"
.Cell(1, 3).Range.Text = "Page"
'Set row as heading row
.Rows(1).HeadingFormat = True
.Rows(1).Range.Font.Bold = True
.PreferredWidthType = wdPreferredWidthPercent
.Columns(1).PreferredWidth = 20
.Columns(2).PreferredWidth = 70
.Columns(3).PreferredWidth = 10
End With
End With
With oDoc_Source
Set oRange = .Range
n = 1 'used to count below
With oRange.Find
'Use wildcard search to find strings consisting of 2 or more uppercase letters
'Set the search conditions
'NOTE: If you want to find acronyms with e.g. 2 or more letters,
'change 3 to 2 in the line below
.Text = "<[A-Z]{2" & strListSep & "}>"
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = True
.MatchWildcards = True
'Perform the search
Do While .Execute
'Continue while found
strAcronym = oRange
'Insert in target doc
'If strAcronym is already in strAllFound, do not add again
If InStr(1, strAllFound, "#" & strAcronym & "#") = 0 Then
'Add new row in table from second acronym
If n > 1 Then oTable.Rows.Add
'Was not found before
strAllFound = strAllFound & strAcronym & "#"
'Insert in column 1 in oTable
'Compensate for heading row
With oTable
.Cell(n + 1, 1).Range.Text = strAcronym
'Insert page number in column 3
.Cell(n + 1, 3).Range.Text = oRange.Information(wdActiveEndPageNumber)
End With
n = n + 1
End If
Loop
End With
End With
'Sort the acronyms alphabetically - skip if only 1 found
If n > 2 Then
With Selection
.Sort ExcludeHeader:=True, FieldNumber:="Column 1", SortFieldType _
:=wdSortFieldAlphanumeric, SortOrder:=wdSortOrderAscending
'Go to start of document
.HomeKey (wdStory)
End With
End If
Application.ScreenUpdating = True
'If no acronyms found, show msg and close new document without saving
'Else keep open
If n = 1 Then
Msg = "No acronyms found."
oDoc_Target.Close savechanges:=wdDoNotSaveChanges
Else
Msg = "Finished extracting " & n - 1 & " acronymn(s) to a new document."
End If
MsgBox Msg, vbOKOnly, Title
'Clean up
Set oRange = Nothing
Set oDoc_Source = Nothing
Set oDoc_Target = Nothing
Set oTable = Nothing
End Sub
The best solution would be one searching pattern for all cases. Word hasn't full regular expressions, it is not always possible. Write all four patterns, maybe there is a way for join them into one super-pattern.
The second possibility is running multiple times the same algorithm in one macro, something like this:
Sub Example()
Dim patterns As String
Dim pts() As String
'list of patterns for each run delimited by a delimiter - comma in this example
patterns = "first pattern, second pattern, and so on"
pts = Split(patterns, ",") 'the second parameter is a delimiter
Dim i As Integer
For i = 0 To UBound(pts)
'do your subroutine for each searching pattern
Next i
'save document with result
End Sub
For better answer give us more details, please.