Save pictures of Excel cells into a Word document - excel

My code pastes a picture of the cells.
It should then paste the other cells which don't fit on the first page.
The picture of the 'second page' does not paste.
Dim wdApp As Object
Dim wd As Object
Dim sFil As String
On Error Resume Next
Set wdApp = GetObject(, "Word.Application")
If Err.Number <> 0 Then Set wdApp = CreateObject("Word.Application")
On Error GoTo 0
Set wd = wdApp.Documents.Add ' create a new document
wdApp.Visible = True
'change sheet and range below
ActiveSheet.Range("a1:z43").CopyPicture xlPrinter
wd.Range.Paste
wdApp.ActiveDocument.Range(0, 0).InsertBreak Type:=wdSectionBreakNextPage
wdApp.ActiveDocument.Sections (2)
ActiveSheet.Range("a43:z76").CopyPicture xlPicture
wd.Range.Paste
'wd.SaveAs Filename:="I:\'Files\RKG-COMMS\" & URNa & " - " & URNb & ".doc"
'wd.Close
'wd.Quit
Here is the result - only the first page
This is the second page which should be pasted

It's difficult for me to test what you're doing, but logically I believe the following should work, although there may be one problem (keep reading)...
Notice how this code uses a Word Range object as the target for new content. After pasting it's uncertain whether docRange will be before the pasted content, or contain the pasted content. If it contains the pasted content, the code should work. If it's before the pasted content, then next picture will come in before the first one. In that case, repeat the line Set docRange = wd.Content immediately after docRange.Paste.
Set wd = wdApp.Documents.Add ' create a new document
Dim docRange as Object 'Word.Range
Set docRange = wd.Content
wdApp.Visible = True
'change sheet and range below
ActiveSheet.Range("a1:z43").CopyPicture xlPrinter
docRange.Paste
'Go to the end
docRange.Collapse 0 'wdCollapseEnd
docRange.InsertBreak Type:=wdSectionBreakNextPage
ActiveSheet.Range("a43:z76").CopyPicture xlPicture
docRange.Paste
In response to an inquiry in comments about how the code works in detail:
Dim docRange as Object 'Word.Range
Declares an object variable to hold the part of the document the code works with. Because the code in the question, running from Excel, apparently uses late-binding (no reference to the Word object library) it's declared as an Object.
If early-binding were being used (the code project has a reference to the Word libraray) then declaring it As Word.Range would be preferable. I include the Word object data type (Word.Range) for the sake of completeness and to make it possible to research the object and its properties in the Help.
Set docRange = wd.Content
This instantiates the Word.Range object to contain the entire main body of the document (wd being instantiated in the code in the question to the target Word document).
After pasting, docRange still refers to the entire content of the document, including what was pasted. If the section break and second paste action were to be executed immediately, this would delete the content of the range. (Think of it like selecting text in Word, then typing: what was selected is replaced by what is typed. In order to avoid this, one presses the right arrow key before typing so that the new content follows what was selected.)
For this reason, the range is "collapsed" (like pressing the arrow key). Then the section break is inserted and the next paste action is executed.

Related

How to clear bookmark data using macro in MS word

I am trying to update data in ms word from excel using macro. To simplify the process, I am using bookmark feature. The issue is when I ran the code, the data in bookmark gets duplicated and I see the same data points from excel in ms word multiple times. I tried to find out a solution on how to clear the data stored in bookmarks before pasting new data but the problem seems to be unresolved.
------------------CODE----------------------------
Option Explicit
Const FilePath As String = "C:\Users\Prablee.Singh\Desktop\"
Dim wd As New Word.Application
Sub ExporttoWord()
Dim doc As Word.Document
wd.Visible = True
Dim Model_Name As String
Dim Model_Description As String
Dim Model_Status As String
Model_Name = ThisWorkbook.Sheets(2).Range("A2").Value 'value from sheet1
Model_Description = ThisWorkbook.Sheets(2).Range("B2").Value
Model_Status = ThisWorkbook.Sheets(2).Range("C2").Value
Set doc = wd.Documents.Open(FilePath & "VBA Code Doc.docx")
Copy2word "Project1", Model_Name
Copy2word "Project1Description", Model_Description
Copy2word "Project1Status", Model_Status
doc.Close
wd.Quit
End Sub
Sub Copy2word(BookMarkName As String, Text2Type As String)
wd.Selection.GoTo What:=wdGoToBookmark, Name:=BookMarkName
wd.Selection.TypeText Text2Type
End Sub
Correct me if I'm wrong. You want to insert text to replace old bookmark? Is your bookmarks marked as range or it's only location? You can replace whole bookmark text using this sample:
Dim BookmarkRange As Range
Set BookmarkRange = ActiveDocument.Bookmarks("myBookmarkRange").Range
BookmarkRange.Text = "this text will replace the old one."
This problem is likely caused by the bookmark simply marking a location in the document, as shown in the top example of the screenshot below, rather than enclosing text.
To see the bookmarks go to File | Options | Advanced | Show Document Content and tick the "Show bookmarks" option.
When inserting text into an enclosing bookmark the bookmark gets deleted and needs to be replaced. This can be done by using a routine such as the one below which also checks that the bookmark exists to avoid errors.
Public Sub InsertIntoBookmark(bookmarkName As String, text As String, _
Optional targetDoc As Document = Nothing)
Dim bookmarkRange As Range
If targetDoc Is Nothing Then Set targetDoc = ActiveDocument
If targetDoc.Bookmarks.Exists(bookmarkName) Then
Set bookmarkRange = targetDoc.Bookmarks(bookmarkName).Range
bookmarkRange.text = text
targetDoc.Bookmarks.Add bookmarkName, bookmarkRange
End If
End Sub
Since the text, as you've said is duplicating that indicates your bookmark is a single point in the document and not a spanned range. The resultant behavor is then to add the text behind the bookmark and leave the bookmark in place. If your bookmark was a spanned range of existing text then Word would replace the text but it would also remove the bookmark.
Either way, your code must reestablish a bookmark ... that is unless you want to remove the bookmark after you have placed the new data into the document.
Try this code ...
Dim rng As Word.Range
Set rng = doc.Bookmarks("BookMarkName").Range
rng.Text = "Project Name"
doc.Bookmarks.Add "BookMarkName", rng
As I've tried to explain above, if your Word document had in the BookMarkName a placeholder of "Temp Name" the code above would replace it with "Project Name" and then reestablish the BookMareName surrounding "Project Name". If you didn't what it to reestablish BookMarkName, then just omit the last line.
If in your existing document BookMarkName is a single address point and not a span of text, the code above will still add the new text, but it will also relocate the BookMarkName range to the new span of text. And the difference here is if you don't want the BookMarkName to remain after inserting the new text, you will have to replace the last code line with ...
doc.Bookmarks(BookMarkName).Delete

Use Word Content Control Values for chart object in same Word doc

Using MS Word (in my case 2010 version), I have constructed a form with Content Control elements to be filled out by the user. Now I want certain entries (that I already gave titles to) be shown in a chart inside the same Word document (not in a separate Excel document).
This should be an automated process, so that if the user changes one of the Content Control entries, the chart updates itself automatically; I would also be OK if the user had to press a button in order to update the chart (but the user shouldn't have to click around a lot, since I must assume the user to have little skills.)
So I inserted an Excel chart object in my Word form document. I also wrote some VBA code inside this Excel object to read the Content Control values from the Word document as source for the chart. But I think what I really need is the VBA code to be in my Word document itself (for example to be executed upon click on a button by the user), yet I don't know how to address the Excel chart object and the cells within.
My VBA code inside the Excel object is:
Sub ChartDataAcquirer()
Dim wdApp As Object
Dim wdDoc As Object
Dim DocName As String
Dim ccX As String
Dim ccY As String
Dim datapairs As Integer
'''''''''' Variables '''''''''
DocName = "wordform.docm"
ccX = "titleX"
ccY = "titleY"
datapairs = 5
''''''''''''''''''''''''''''''
Set wdApp = GetObject(, "Word.Application")
Set wdDoc = wdApp.Documents(DocName)
Dim i As Integer
For i = 1 To datapairs
With ActiveSheet.Cells(i + 1, 1) ' The first row contains headline, therefore i+1
.Value = wdDoc.SelectContentControlsByTitle(ccX & i).Item(1).Range.Text ' The CC objects containing the x values have titles "titleX1", "titleX2" ..., therefore "ccX & i"
On Error Resume Next
.Value = CSng(wdDoc.SelectContentControlsByTitle(ccX & i).Item(1).Range.Text) ' To transform text into numbers, if user filled the CC object with numbers (which he should do)
End With
With ActiveSheet.Cells(i + 1, 2)
.Value = wdDoc.SelectContentControlsByTitle(ccY & i).Item(1).Range.Text
On Error Resume Next
.Value = CSng(wdDoc.SelectContentControlsByTitle(ccY & i).Item(1).Range.Text)
End With
Next
End Sub
I guess I need a similar code that is placed in and operates from the Word form document itself, but that is where I am stuck...
The following is demo code that shows how to access an embedded Excel chart.
Note that the Name (Shapes([indexValue])) of your chart Shape is probably different than in this code. You'll need to check and change that assignment. Also, your chart may be an InlineShape rather than a Shape, so you may need to adjust that bit, as well.
This code checks whether the Shape is actually a chart. If it is, the Chart object is accessed as well as its data sheet. Via that, it's possible to get the actual workbook, the worksheets, even the Excel application if you should need it.
Sub EditChartData()
Dim doc As Word.Document
Dim shp As Word.Shape
Dim cht As Word.Chart
Dim wb As Excel.Workbook, ws As Excel.Worksheet, xlApp As Excel.Application
Set doc = ActiveDocument
Set shp = doc.Shapes("MyChart")
If shp.HasChart Then
Set cht = shp.Chart
cht.ChartData.Activate
Set wb = cht.ChartData.Workbook
Set xlApp = wb.Application
Set ws = wb.ActiveSheet
Debug.Print ws.Cells(1, 2).Value2
End If
Set ws = Nothing
Set wb = Nothing
Set cht = Nothing
Set xlApp = Nothing
End Sub

VBA Using Excel 2016 to Populate Word 2016 Content Controls

I'm attempting to populate a Word.dotm document that has content control text boxes using Excel.
In Excel, I create the word objects and open the file, but I can't figure out how to actually access the controls.
In the Word doc's VBA, I can access them via
.SelectContentControlsByTitle("control").Item(1).Range.Text = "..."
but that doesn't work using an Object in Excel..
Here's the code I've tried in Excel:
Dim objWord As Object
Dim objDoc As Object
Dim objSelection As Object
Dim ctrl As Word.ContentControl
Set objWord = CreateObject("Word.Application")
Set objDoc = objWord.Documents.Add(ThisWorkbook.Path & "\MyDoc.dotm")
objWord.Visible = True
Set objSelection = objWord.Selection
'Doesn't work
objDoc.SelectContentControlsByTitle("control").Item(1).Range.Text = "..."
'Doesn't work either, but tried nonetheless
For Each ctrl In objDoc.contentcontrols
If ctrl.Title = "control" Then
ctrl.Range.Text = "!"
Exit For
End If
Next ctrl
UPDATE:
I didn't have the MS Word 16.0 Object Library referenced... the early binding and the tip to copy the template file rather than altering it directly mentioned by Timothy Rylatt now works and John Korchok's For Each loop works as well. Their additions have been added to the code above. Thank you!
Although you have declared ctrl as a ContentControl it is not a Word content control, which is why the For Each loop won't work.
If you want to use early binding you need to specify the library that the object type belongs to in the variable declaration. Some libraries have objects with the same name but which are completely different.
Dim ctrl As Word.ContentControl
NB: You should also create a document from the Word template rather than edit the template directly:
Set objDoc = objWord.Documents.Add(ThisWorkbook.Path & "\MyDoc.dotm")
In any macro, but especially in cross-program macros, avoid using the Selection object and use Range object instead. Sometimes using the range object requires a slightly less direct route to accessing the control, such as having to poll all content controls to find which one has the correct title:
Sub SetCCText()
Dim CC As ContentControl
For Each CC In ActiveDocument.ContentControls
If CC.Title = "control" Then
CC.Range.Text = "Text"
End If
Next CC
End Sub

How do I copy the contents of one word document to the end of another using vba?

Goal for my project:
I want to be able to copy the contents of one document and append that selection to the end of another document.
What it does... (this is just background info so you understand why I am trying to do this):
I am trying to dynamically produce a document which quotes a variety of information regarding different parts and materials involved for a product.
The document itself has a consistent format which I have broken down and separated into two documents. The first contains a bunch of data that needs to be entered manually, and is where I want to append all additional content. The second contains roughly a dozen custom fields which are updated from an excel spreadsheet in VBA. For a single part and as a single doc this works as I want it (my base case). However my issue is when there are multiple parts for a project.
The Problem:
For multiple parts I have to store information in an array which changes in size dynamically as each additional part is added. When someone has added all the necessary parts they can select a button called "Create Quote".
Create quote runs a procedure which creates/opens separate copies of the two template documents mentioned above (saved on my computer). It then iterates through the array of parts and updates all the custom field in the 2nd document (no problems). Now I just need the contents of the 2nd document appended to the end of the first which is my problem.
What I want:
Ideally, my procedure will continue to iterate through every part in the array - updating custom fields, copy then paste the updated text, repeat... Until every part is included in the newly generated quote.
What I Tried - this code can be found in my generate quote procedure
I have tried many of the examples and suggestions provided by people who had similar question, but I don't know if its because I am operating from an excel doc, but many of their solution have not worked for me.
This is my most recent attempt and occurs after each iteration of the for loop
wrdDoc2.Fields.Update 'Update all the fields in the format document
wrdDoc2.Activate
Selection.WholeStory ' I want to select the entire document
Selection.Copy ' Copy the doc
wrdDoc1.Activate ' Set focus to the target document
Selection.EndKey wdStory ' I want the selection to be pasted to the end of the document
Selection.PasteAndFormat wdPasteDefault
QUOTE PROCEDURE - I am only including a handful of the fields I am updating because its not necessary to show them all
Private Sub quote_button_Click()
On Error GoTo RunError
Dim wrdApp1, wrdApp2 As Word.Application
Dim wrdDoc1, wrdDoc2 As Word.Document
Set wrdApp1 = CreateObject("Word.Application")
Set wrdApp2 = CreateObject("Word.Application")
wrdApp1.Visible = True
wrdApp2.Visible = True
Set wrdDoc1 = wrdApp1.Documents.Add(Template:="C:\MWES\AQT_v1.1(start).docm", NewTemplate:=False, DocumentType:=0)
Set wrdDoc2 = wrdApp2.Documents.Add(Template:="C:\MWES\AQT_v2.1(format).docm", NewTemplate:=False, DocumentType:=0)
Dim propName As String
For i = LBound(part_array, 1) To UBound(part_array, 1)
For Each prop In wrdDoc2.CustomDocumentProperties
propName = prop.name
' Looks for and sets the property name to custom values of select properties
With wrdDoc2.CustomDocumentProperties(propName)
Select Case propName
Case "EST_Quantity"
.value = part_array(i, 0) ' "QTY" ' Sheet1.Cells(1, 3) 'NA
Case "EST_Metal_Number"
.value = part_array(i, 1) ' "METAL_#" ' Sheet1.Cells(2, 3) 'NA"
Case "EST_Metal_Name"
.value = part_array(i, 2) ' "METAL_N" ' Sheet1.Cells(5, 2)
End Select
End With
Next prop ' Iterates until all the custom properties are set
wrdDoc2.Fields.Update 'Update all the fields in the format document
wrdDoc2.Activate
Selection.WholeStory ' I want to select the entire document
Selection.Copy ' Copy the doc
wrdDoc1.Activate ' Set focus to the target document
Selection.EndKey wdStory ' I want the selection to be pasted to the end of the document
Selection.PasteAndFormat wdPasteDefault
Next i ' update the document for the next part
RunError: ' Reportd any errors that might occur in the system
If Err.Number = 0 Then
Debug.Print "IGNORE ERROR 0!"
Else
Dim strError As String
strError = "ERROR: " & Err.Number & vbCrLf & Err.Description & vbCrLf & Erl
MsgBox strError
Debug.Print strError & " LINE: " & Erl
End If
End Sub
I apologize this was so long winded. Let me know if there is anything confusing or you may want clarified. I think I included everything though.
I think you're close, so here are a couple of comments and an example.
First of all, you're opening two separate MS Word Application objects. You only need one. In fact, it's possible that the copy/paste is failing because you're trying to copy from one Word app to a document opened in the other. (Trust me, I've seen weird things like this.) My example below shows how to do this by only opening a single application instance.
Dim mswApp As Word.Application
Set mswApp = AttachToMSWordApplication() 'more on this function below...
Dim doc1 As Word.Document
Dim doc2 As Word.Document
Set doc1 = mswApp.Documents.Open("C:\Temp\combined.docx")
Set doc2 = mswApp.Documents.Open("C:\Temp\control.docx")
While I don't often write code for Word, I've found that there are so many different ways to get at the same content using different objects or properties. This is always a source of confusion.
Based on this answer, which has worked well for me in the past, I then set up the source and destination ranges to perform the "copy":
Dim destination As Word.Range
Dim source As Word.Range
Set source = doc1.Content
Set destination = doc2.Content
destination.Collapse Direction:=Word.wdCollapseEnd
destination.FormattedText = source
Here is the whole module for reference:
Option Explicit
Sub AddDocs()
Dim wordWasRunning As Boolean
wordWasRunning = IsMSWordRunning()
Dim mswApp As Word.Application
Set mswApp = AttachToMSWordApplication()
Dim doc1 As Word.Document
Dim doc2 As Word.Document
Set doc1 = mswApp.Documents.Open("C:\Temp\combined.docx")
Set doc2 = mswApp.Documents.Open("C:\Temp\control.docx")
Dim destination As Word.Range
Dim source As Word.Range
Set source = doc1.Content
Set destination = doc2.Content
destination.Collapse Direction:=Word.wdCollapseEnd
destination.FormattedText = source
doc2.Close SaveChanges:=True
doc1.Close
If Not wordWasRunning Then
mswApp.Quit
End If
End Sub
Here's the promised note on a couple functions I use in the sample. I've built up a set of library functions, several of which help me access other Office applications. I save these modules as .bas files (by using the Export function in the VBA Editor) and import them as needed. So if you'd like to use it, just save the code below in using a plain text editor (NOT in the VBA Editor!), then import that file into your project.
Suggested filename is Lib_MSWordSupport.bas:
Attribute VB_Name = "Lib_MSWordSupport"
Attribute VB_Description = "Variety of support functions operating on MS Word"
Option Explicit
Public Function IsMSWordRunning() As Boolean
Attribute IsMSWordRunning.VB_Description = "quick check to see if an instance of MS Word is running"
'--- quick check to see if an instance of MS Word is running
Dim msApp As Object
On Error Resume Next
Set msApp = GetObject(, "Word.Application")
If Err > 0 Then
'--- not running
IsMSWordRunning = False
Else
'--- running
IsMSWordRunning = True
End If
End Function
Public Function AttachToMSWordApplication() As Word.Application
Attribute AttachToMSWordApplication.VB_Description = "finds an existing and running instance of MS Word, or starts the application if one is not already running"
'--- finds an existing and running instance of MS Word, or starts
' the application if one is not already running
Dim msApp As Word.Application
On Error Resume Next
Set msApp = GetObject(, "Word.Application")
If Err > 0 Then
'--- we have to start one
' an exception will be raised if the application is not installed
Set msApp = CreateObject("Word.Application")
End If
Set AttachToMSWordApplication = msApp
End Function

Use Excel-VBA to create and Insert String/Text AND Image to Word Document table-cell

I tried since more days to create a Word Document with Excel-VBA
Step by Step:
first: create Word-Document and add a Table (Mailing-Label)
second: fill sometext into some cells. Works great!
Now my Problem:
at last, i want to append an Picture in the cell.
My Problem is, the Image RANGE clear the old text.
And i don't know, how to set the Image and the text at the end of the Loop.
My code
oDoc.Tables(1).Cell(zeile, spalte).Range.Text = "some string"
oDoc.Tables(1).Cell(zeile, spalte).Range.InlineShapes.AddPicture path_to_image
The way to understand what's happening is to think about how this would work if you were doing this manually, working with a selection. When you assign text to a Range that's like typing it in, as you'd expect. The second line of code, inserting the image, is like selecting the entire cell (in this case) then inserting the image: it replaces what's in the Range. When working manually, if you had selected the entire cell, you'd press Right Arrow or click at the end to put the focus after what had been typed.
The same principle applies when using a Range object: it needs to collapse in order to add something to it.
The following code example demonstrates this. It also highlights how the code can be made more efficient by assigning the table and the target range to objects.
Dim tbl As Word.Table 'or As Object if using late-binding
Dim rng As Word.Range 'or As Object if using late-binding
Dim chrCount As Long
Set tbl = oDoc.Tables(1)
Set rng = tbl.Cell(zeile, spalte).Range
rng.Text = "test"
chrCount = rng.Characters.Count
'Get the end of the cell content
Set rng = rng.Characters(chrCount - 1)
rng.Collapse wdCollapseEnd
rng.InlineShapes.AddPicture path_to_image
May be something like
Sub Test()
Dim Wrd As Word.Application
Dim oDoc As Word.Document
Set Wrd = CreateObject("Word.Application")
Wrd.Visible = True
Set oDoc = Wrd.Documents.Add
oDoc.Tables.Add oDoc.Range, 3, 3
zeile = 2
spalte = 2
path_to_image = "C:\Users\user\Desktop\Pull2.jpg"
oDoc.Tables(1).Cell(zeile, spalte).Range.Select
With Wrd.Selection
.TypeText Text:="some string"
.InlineShapes.AddPicture path_to_image
End With
End Sub

Resources