I am trying to use VBA to add an embedded OLE (Empty MSWord document, Icon:=True) from an open MSWord document without opening the User Interface for the newly created Empty MSWord document). The following VBA replaces the second paragraph in the current document with the MSWord OLE Icon, But it opens the MSWord Icon document in its own window.
Sub InsertOLEobject()
ActiveDocument.InlineShapes.AddOLEObject _
ClassType:="Word.Document", DisplayAsIcon:=True, _
Range:=ActiveDocument.Paragraphs(2).Range 'Replaces the second paragraph with the MSWord Icon
End Sub
Can anyone give me some advice?
The following is the latest VBA code to do what I wanted to do. The InlineShapes.AddOLEObject will not open the Word User Interface if you use an existing file. I was just hoping for a more elegant way without creating a file that needs to be deleted.
Sub InsertOLEobject()
Dim docNewBlank As Document
Set docNewBlank = Documents.Add
Set DocA = ActiveDocument
With docNewBlank
'Centering the paragraph within the blank document
.Paragraphs(1).Alignment = wdAlignParagraphCenter
'Saved the file to a temporary scratch location
.SaveAs FileName:="c:\temp\Blank.doc"
.Close 'Close the word document
End With
'Add the embedded MSWord OLE Icon
ActiveDocument.InlineShapes.AddOLEObject _
ClassType:="Word.Document", _
FileName:="c:\Temp\Blank.doc", _
DisplayAsIcon:=True, _
IconLabel:="Blank.doc", _
IconFileName:="WINWORD.EXE", _
Range:=ActiveDocument.Paragraphs(2).Range
'Set the filename of your choice
'Set the Range to the destination of the ole icon
'Set the icon to the path of your WINWORD.EXE
End Sub
FWIW I'm not sure I have followed exactly what you are trying to do, but I wonder whether this will do what you need (except the document isn't quite empty and will be a .docx rather than a .doc):
Sub embedNearlyEmptyWordDoc()
Dim appFullName As String
' Windows only, perhaps there is a better place to get this string
appFullName = Application.Path & Application.PathSeparator & "winword.exe"
With ActiveDocument
' insert a space somewhere "safe" (not necessarily where I have put it
.Range(0, 0).Text = " "
.Range(0, 1).Copy
.Range(0, 0).Text = ""
End With
' Alter this to put the doc where you want
ActiveDocument.Paragraphs(2).Range.PasteSpecial IconIndex:=0, Link:=False, Placement:=wdInLine, DisplayAsIcon:=True, DataType:=wdPasteOLEObject, IconFileName:=appFullName, IconLabel:="Blank.docx"
End Sub
The ultimate solution was to add "ActiveWindow.Close" after inserting the Word OLE Icon with a blank name; this closes the window just opened by the "ActiveDocument.InlineShapes.AddOLEObject". So I did not have to create a blank.doc first; so I deleted that VBA code.
I was asked what my goal was going into this project; my goal was to cut Tables and Images within a document and paste them in place into Word OLE objects icons. The resulting word file with all images and tables encapsulated into Word OLE object icons that can be used as an input to the Rational DOORS (DOORS) export Addin. I could have exported the images and tables in DOORS directly so they are viewable within DOORS but if you have ever double clicked on an image or table in DOORS you would find out that the OLE object opens within the cell and is very hard to edit, resize, or position. I separated the table function from the inlineshapes sub; my VBA code is as follows:
'================================================================================
' Cut all Tables and paste them into Word OLE Icons
'================================================================================
Sub IconizeInlineTables()
'================================================================================
Dim iTable As Table
Dim RngA As Range
Dim RngB As Range
Dim RngC As Range
Dim DocA As Document
Dim DocC As Document
Set DocA = ActiveDocument
For Each iTable In DocA.Tables
Set RngA = iTable.Range 'Go to the start of the table
RngA.Expand Unit:=wdTable
Set RngB = RngA.Duplicate 'Duplicate the range to keep track of table location
RngB.Collapse Direction:=wdCollapseEnd 'Collapse the Duplicate range to the point after the Table
RngA.Select
'MsgBox "Tables Left to Process =" & DocA.Tables.Count
Selection.Cut 'Cut the Table and put it in the paste buffer
'Insert the MSWord.doc OLE icon
ActiveDocument.InlineShapes.AddOLEObject _
ClassType:="Word.Document", _
FileName:="", _
LinkToFile:=False, _
DisplayAsIcon:=True, _
IconFileName:="WINWORD.EXE", _
IconLabel:="Table-" & iTableCount & ".doc", _
Range:=RngB
ActiveWindow.Close
'Expand the range to include the OLE icon then select the new range
With RngB
.Expand Unit:=wdParagraph
.Paragraphs(1).Style = wdStyleBodyText 'Apply the Body Text style
.Paragraphs(1).Format.Alignment = wdAlignParagraphCenter 'Center the paragraph
.InlineShapes(1).Select 'Select the inlineshape containing the OLE
End With
'Make sure that the InlineShape is the OLE icon
If InStr(1, RngB.InlineShapes(1).OLEFormat.ProgID, "Word.Document.", vbTextCompare) Then
Set DocC = RngB.InlineShapes(1).OLEFormat.Object 'Select the OLE Object
Set RngC = DocC.Paragraphs(1).Range 'Select the first Paragraph within the OLE Object
With RngC
.Paragraphs(1).Style = wdStyleBodyText 'Apply the Body Text style
.Paragraphs(1).Format.Alignment = wdAlignParagraphCenter 'Center the paragraph
.Collapse Direction:=wdCollapseEnd 'Collapse to just beyond end of paragraph
.MoveEnd Unit:=wdCharacter, Count:=-1 'Back the RngC up one character to not include the paragraph marker
.Select
.Paste 'Paste the Table cut with RngA.cut here
End With
Else 'Something I did not expect happened
MsgBox "Error: " & Selection.InlineShapes(1).OLEFormat.ProgID & "Not Expected"
End If
Next
End Sub
'================================================================================
' Cut all Figures and paste them into Word OLE Icons
'================================================================================
Sub IconizeInlineShapes()
'================================================================================
Dim iShape As InlineShape
Dim RngA As Range
Dim RngB As Range
Dim RngC As Range
Dim DocA As Document
Dim DocC As Document
Set DocA = ActiveDocument
Selection.GoTo What:=wdGoToSection, Which:=wdGoToFirst 'Start at the beginning of the document
For Each iShape In DocA.InlineShapes
If iShape.Type = wdInlineShapePicture Then
Set RngA = iShape.Range 'Set the range to the image
Set RngB = RngA.Duplicate 'Duplicate the range to keep track of location
RngB.Collapse Direction:=wdCollapseEnd 'Collapses to the end of the selection
RngA.Select
Selection.Cut 'Cut the image and put it in the paste buffer
'Insert the MSWord OLE icon
ActiveDocument.InlineShapes.AddOLEObject _
ClassType:="Word.Document", _
FileName:="", _
LinkToFile:=False, _
DisplayAsIcon:=True, _
IconFileName:="WINWORD.EXE", _
IconLabel:="Figure-" & DocA.InlineShapes.Count & ".doc", _
Range:=RngB
ActiveWindow.Close
'The inserted OLE is the next InlineShape to be found so you don't have to select it just loop around to next iShape
ElseIf InStr(1, iShape.OLEFormat.ProgID, "Word.Document.", vbTextCompare) Then 'Make sure iShape OLE object was next
Set DocC = iShape.OLEFormat.Object 'Select the OLE object
Set RngC = DocC.Paragraphs(1).Range 'Select the first Paragraph within the OLE Object
RngC.Paragraphs(1).Style = wdStyleBodyText 'Apply the Body Text style
RngC.Paragraphs(1).Format.Alignment = wdAlignParagraphCenter 'Center the paragraph
RngC.Collapse Direction:=wdCollapseEnd 'Collapse to just beyond end of paragraph
RngC.MoveEnd Unit:=wdCharacter, Count:=-1 'Back the RngC up one character to not include the paragraph marker
RngC.Select
RngC.Paste 'Paste the image cut with RngA.cut here
Else 'Something I did not expect happened
MsgBox "Warning: " & iShape.OLEFormat.ProgID & "Not handled by this software"
End If
Next
End Sub
Note the "IconFileName:="WINWORD.EXE" is generic but may have to be modified for your particular installation.
Related
I have a macro that runs to make a single page doc into a 5 page doc (NCR Duplicates) for all files in a folder.
I am using a set of nested IF fields in my footer, which changes the footer based on page number. The field looks like this
Text here {If{PAGE}="1""Original"{If{PAGE}="2""Copy 1"
{If{PAGE}="3""Copy 2"{If{PAGE}="4""Copy 3"{If{PAGE}="5""Copy 4"}}}}}
Other Text
I am trying to figure out how to add this footer to all the documents in a folder. It doesn't need to use field, if there is a way simply based on page number.
I have bashed my head against the wall, searched like crazy, and now come hat in hand.
The macro to make the duplicate copies is:
Sub Make5CopiesNCR()
vDirectory = BrowseForFolder
vFile = Dir(vDirectory & "\" & "*.*")
Do While vFile <> ""
Documents.Open FileName:=vDirectory & "\" & vFile
MakeCopies
vFile = Dir
Loop
End Sub
End Sub
Private Sub MakeCopies()
Dim i As Integer
Selection.WholeStory
Selection.Copy
For i = 1 To 6
Selection.PasteAndFormat wdFormatOriginalFormatting
Next
With ActiveDocument
.GoTo What:=wdGoToPage, Which:=wdGoToAbsolute, Name:=6 'Page number
.Bookmarks("\Page").Select
With Selection
.Delete
ActiveDocument.Close SaveChanges:=wdSaveChanges, OriginalFormat:=wdWordDocument
End With
End With
End Sub
The problem with using a mailmerge with your field construction is that it gets converted to the result. Try a field coded as:
{={PAGE}-1 \# "'Copy {={PAGE}-1}';;'Original'"}
Now, if you create the required 5 pages in your mailmerge main document, all the outputs will likewise be in multiples of 5 pages, with the correct page numbering.
Even if you use a mailmerge main document with only a single page, the outputs will have the field coding required to produce the correct numbering for however many more pages you want to add to the outputs.
As for replicating this in your existing files, simply create a document with the required footer content, then use a macro like:
Sub ReplicateFooter()
Application.ScreenUpdating = False
Dim DocSrc As Document, DocTgt As Document, Rng As Range
Dim StrPth As String, StrNm As String, StrSrc As String
Set DocSrc = ActiveDocument
Set Rng = DocSrc.Sections.First.Footers(wdHeaderFooterPrimary).Range
StrPth = DocSrc.Path & "\": StrSrc = DocSrc.FullName
StrNm = Dir(StrPth & "*.doc", vbNormal)
While StrNm <> ""
If StrPth & StrNm <> StrSrc Then
Set DocTgt = Documents.Open(FileName:=StrPth & StrNm, AddToRecentFiles:=False, Visible:=False)
With DocTgt
With .Sections.First.Footers(wdHeaderFooterPrimary).Range
.FormattedText = Rng.FormattedText
.Characters.Last.Text = vbNullString
End With
.Close True
End With
End If
StrNm = Dir()
Wend
Set Rng = Nothing: Set DocTgt = Nothing: Set DocSrc = Nothing
Application.ScreenUpdating = True
End Sub
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
When I select logo object from footer, Selection.Copy creates an object in Clipboard, which - tested in Break Mode - after pasting anywhere appears as 2 next line separators: Chr(13) chr(10) Chr(13) Chr(10).
If after selecting logo object with code, I manually navigate to template document and use CTRL + C, it is properly stored in Clipboard.
I tried tempDoc.Activate before Selection.Copy, but it doesn't bring template file to front (I have openedDoc open as well at the same time).
Sub changeHeadersFooters(openedDoc As Object, tempDoc As Object)
Dim shp As Object, logo As Object
Dim HdFt As Object, hdrOpened As Object
'create logo object in template doc
For Each HdFt In tempDoc.Sections(1).footers
If HdFt.Exists Then
For Each shp In HdFt.Shapes
If InStr(shp.Name, "image") <> 0 Then Set logo = shp: Exit For
Next shp
End If
Next HdFt
Set hdrOpened = openedDoc.Sections(1).footers(1)
logo.Select
Selection.Copy
With hdrOpened.Range
.Collapse Direction:=1 'wdCollapseStart=1, wdCollapseEnd = 0
.Paste
End With
End Sub
I have an Excel worksheet from which I am trying to paste Information into a wordfile "Template" (just a word-document in the layout I want), which contains bookmarks. What I would like to do is:
Copy everything in the word document (including bookmarks)
Replace the bookmarks with the data in my sheet
Go to the bottom of the page, insert a page break and paste the copied Text, including bookmarks
Loop through points 2 & 3 for all the rows in my excel file
I have patched together some code, but I'm unable to get the bookmark to paste the text with the bookmarks still intact. Can any of you help me get there?
Sub ReplaceBookmarks
'Select template
PickFolder = "C:\Users\Folder"
Set fdn = Application.FileDialog(msoFileDialogFilePicker)
With fdn
.AllowMultiSelect = False
.Title = "Please select the file containing the Template"
.Filters.Clear
.InitialFileName = PickFolder
If .Show = True Then
Temp = fdn.SelectedItems(1)
End If
End With
'open the word document
Set wdApp = CreateObject("Word.Application")
Set wdDoc = wdApp.Documents.Open(Temp)
'show the word document - put outside of loop for speed later
wdApp.Visible = True
'Copy everything in word document
wdDoc.Application.Selection.Wholestory
wdDoc.Application.Selection.Copy
LastRow2 = 110 ' In real code this is counted on the sheet
For i = 2 To LastRow2
'Data that will replace bookmarks in ws2 (defined somewhere in real code)
Rf1 = ws2.Cells(i, 4).Value
Rf2 = ws2.Cells(i, 2).Value
Rf3 = ws2.Cells(i, 3).Value
'replace the bookmarks with the variables - references sub "Fillbookmark"
FillBookmark wdDoc, Rf1, "Rf1"
FillBookmark wdDoc, Rf2, "Rf2"
FillBookmark wdDoc, Rf3, "Rf3"
' Jump to bottom of document, add page break and paste
With wdDoc
.Application.Selection.EndKey Unit:=wdStory
.Application.Selection.InsertBreak Type:=wdPageBreak
.Application.Selection.PasteAndFormat (wdFormatOriginalFormatting)
End With
Next i
End Sub
Sub FillBookmark(ByRef wdDoc As Object, _
ByVal vValue As Variant, _
ByVal sBmName As String, _
Optional sFormat As String)
Dim wdRng As Object
'store the bookmarks range
Set wdRng = wdDoc.Bookmarks(sBmName).Range
'if the optional format wasn’t supplied
If Len(sFormat) = 0 Then
'replace the bookmark text
wdRng.Text = vValue
Else
'replace the bookmark text with formatted text
wdRng.Text = Format(vValue, sFormat)
End If
End Sub
First try, instead of Copy/Paste, using WordOpenXml. This is much more reliable than copy/paste. Now remember that a Bookmark is a named location, when you copy a section of the document and put it back on another location when the original bookmark is still in place, the new section won't get the copied Bookmark.
I'll provide a little bit of code to show this to you:
Sub Test()
ActiveDocument.Bookmarks.Add Name:="BM1", Range:=ActiveDocument.Paragraphs(1).Range
ActiveDocument.Application.Selection.WholeStory
Dim openxml As String
openxml = ActiveDocument.Application.Selection.wordopenxml
ActiveDocument.Bookmarks(1).Delete
With ActiveDocument
.Application.Selection.EndKey Unit:=wdStory
.Application.Selection.InsertBreak Type:=wdPageBreak
.Application.Selection.InsertXML xml:=openxml
End With
' ActiveDocument.Bookmarks(1).Delete
With ActiveDocument
.Application.Selection.EndKey Unit:=wdStory
.Application.Selection.InsertBreak Type:=wdPageBreak
.Application.Selection.InsertXML xml:=openxml
End With
End Sub
Now open a new document enter some text by entering =Rand() as text in the document and hit enter
Next run the code from the Test macro.
You'll see that because you delete the bookmark using ActiveDocument.Bookmarks(1).Delete from the original part the first inserted text now contains the bookmark, the second does not.
If you uncomment the ' ActiveDocument.Bookmarks(1).Delete line you will see that the bookmark ends up in the second added text part because there is no duplicate bookmark anymore when creating the second section.
So in short, copying a bookmark will not duplicate the bookmark when pasting it, so you need to make sure you either delete the original bookmark or rename the bookmarks to make them unique again. Duplicates is a no go.
I am trying to write a macro to find and copy all the graphs/images inline in a word document and paste them into individual slides in a new powerpoint. However when I run into multiple runtime errors. Here's the entire code.
Sub wordtoppt()
'This macro copies all pictures out of a word document of your choice and into a new powerpoint presentation.
'Two reference libraries need to be open - Word and Powerpoint. Go Tools > References, and tick the relevant box.
Dim wdApp As Word.Application 'Set up word and powerpoint objects
Dim wdDoc As Word.Document
Dim pptApp As PowerPoint.Application
Dim pptShw As PowerPoint.Presentation
Dim pptChart As PowerPoint.Shape
Dim pptSld As PowerPoint.Slide
On Error GoTo 0
Dim wcount As Integer 'Number of open word documents
Dim doclist() As String 'Collects the names of open word documents
Dim desc As String 'inputbox text
Dim chosendoc As Integer 'stores the index number of your selected word document
Dim ccount As Integer 'number of shapes in the word document
Dim wellpasted As Integer 'Counts the number of shapes that have successfully been pasted into powerpoint.
Application.ScreenUpdating = False
'Establishes link with word.
On Error Resume Next
Set wdApp = GetObject(, "Word.Application")
On Error GoTo 0
If wdApp Is Nothing Then 'Error message if Word is not open
MsgBox "Error: Word is not open." & Chr(10) & Chr(10) & "Is word actually open? This is a bug."
Exit Sub
End If
'Counts the number of word documents open
wcount = CInt(wdApp.Documents.Count)
ReDim doclist(wcount) 'resizes string array of word documents
If wcount = 0 Then 'Error message if Word is open, but there are no documents open
MsgBox "There are no word documents open!" & Chr(10) & "Open a word document and try again"
Exit Sub
End If
'text for input box
desc = "Which document would you like to extract the graphs from?" & Chr(10) & Chr(10) & "Type the number in the box (one number only)." & Chr(10) & Chr(10)
'input boxes for selection of word document
If wcount = 1 Then 'if only one document open
myinput = MsgBox("Do you want to paste graphs from " & wdApp.Documents(1).Name & "?", vbYesNo, "From Release Note to Powerpoint")
If myinput = vbYes Then
chosendoc = 1
Else
Exit Sub
End If
Else
For i = 1 To wcount 'multiple documents open
doclist(i) = wdApp.Documents(i).Name
desc = desc & i & ": " & doclist(i) & Chr(10)
Next
myinput = InputBox(desc, "From Release Note to Powerpoint")
If IsNumeric(myinput) And myinput <= wcount Then 'Error handling - if cancel is clicked, or anything other than a number is typed into the input box.
chosendoc = CInt(myinput)
Else
If myinput = "" Then 'clicking cancel, or leaving input box blank
MsgBox "You didn't enter anything!"
Exit Sub
Else 'if you type a short novel
MsgBox "You didn't enter a valid number!" & Chr(10) & "(Your response was " & myinput & ")"
Exit Sub
End If
End If
End If
'Error handling, for chart-free word documents.
If wdApp.Documents(chosendoc).InlineShapes.Count = 0 Then
MsgBox "There are no charts in this Word Document!"
Exit Sub
End If
'Opens a new powerpoint presentation
Set pptApp = CreateObject("PowerPoint.Application")
Set pptShw = pptApp.Presentations.Add
'PowerPoint.Application
'Sets up slide dimensions
Dim sldwidth As Integer
Dim sldheight As Integer
sldwidth = pptShw.PageSetup.SlideWidth
sldheight = pptShw.PageSetup.SlideHeight
wellpasted = 0
Dim shapecount As Integer 'Number of shapes in the word document
shapecount = wdApp.Documents(chosendoc).InlineShapes.Count
For j = 1 To shapecount 'Adds in the correct number of slides into the powerpoint presentation
Set pptSld = pptShw.Slides.Add(pptShw.Slides.Count + 1, ppLayoutBlank)
Next
For j = 1 To shapecount 'loops through all shapes in the document
On Error GoTo Skiptheloop 'sometimes some objects don't paste. This is a way to skip over them.
'Application.Wait Now + (1 / 86400)
wdApp.Documents(chosendoc).InlineShapes(j).Range.Copy 'copies chart
Set pptSld = pptShw.Slides(j)
pptSld.Shapes.Paste 'pastes chart
'Application.CutCopyMode = False
With pptSld.Shapes(1) 'resizes and aligns shapes
.LockAspectRatio = msoTrue 'Currently sets charts to the height of the slide. Alternatively can scale to 100%
.Height = sldheight
.Left = (sldwidth / 2) - (.Width / 2)
.Top = (sldheight / 2) - (.Height / 2)
End With
wellpasted = wellpasted + 1 'if the chart was pasted successfully, increment by 1.
Skiptheloop:
Next
On Error GoTo 0
If (shapecount - wellpasted) <> 0 Then 'produces a message box if some shapes did not paste successfully.
MsgBox CStr(shapecount - wellpasted) & " (of " & CStr(shapecount) & ") shapes were not pasted. Best that you check all the graphs are in."
End If
Application.ScreenUpdating = True
pptApp.Activate 'brings powerpoint to the front of the screen
Exit Sub
End Sub
On the line pptSld.shapes.paste I get the error clipboard empty or cannot paste.
Any ideas?
I am using Simple solution for my job devided in two pars
1) Extract all images from word file
This can be done in two ways.
a. save as html which will create the folder filenam_files which will hold all the images in .png formate. There may be duplicate images in diff formate but .png will be unique.
b. change filename of word from file.docx to file.docx.zip
You can get the images at file.docx\word\media
There will be no duplicate images in this method.
2) Import all images in powerpoint.
1)
As you have already opened the document manually you can do one more step manually or record macro which will look like this.
Sub exportimages()
ChangeFileOpenDirectory "D:\temp\"
ActiveDocument.SaveAs2 FileName:="data.html", FileFormat:=wdFormatHTML, _
LockComments:=False, passWord:="", AddToRecentFiles:=True, WritePassword _
:="", ReadOnlyRecommended:=False, EmbedTrueTypeFonts:=False, _
SaveNativePictureFormat:=False, SaveFormsData:=False, SaveAsAOCELetter:= _
False, CompatibilityMode:=0
End Sub
2)
Close the word document.
Open Power point and paste this
Sub ImportABunch()
Dim strTemp As String
Dim strPath As String
Dim strFileSpec As String
Dim oSld As Slide
Dim oPic As Shape
strPath = "D:\temp\data_files\"
strFileSpec = "*.png" 'if you are using mehtod **a.** to extract the images.
'strFileSpec = "*.*" 'if you are using mehtod **b.** to extract the images.
strTemp = Dir(strPath & strFileSpec)
Do While strTemp <> ""
Set oSld = ActivePresentation.Slides.Add(ActivePresentation.Slides.Count + 1, ppLayoutBlank)
Set oPic = oSld.Shapes.AddPicture(FileName:=strPath & strTemp, _
LinkToFile:=msoFalse, _
SaveWithDocument:=msoTrue, _
Left:=0, _
Top:=0, _
Width:=-1, _
Height:=-1)
strTemp = Dir
Loop
End Sub
You can write vbscript to combine this two steps together. I have no idea how to do that. You can google it.