Text Box Rotation Issue - vba

I am trying to write a macro to insert a custom watermark in my Word document.
The code works perfectly for the first two pages of the document but thereafter the the Textbox does not rotate to -45 as mentioned in the code
What am I doing wrong?
Sub CustomWatermark()
Dim activeDoc As Document
Dim rngDoc As Range
Dim shpTextBox As Shape
Dim lngPages As Long
Dim i As Long
Dim strWatermark As String
Set activeDoc = ActiveDocument
lngPages = activeDoc.Range.Information(wdNumberOfPagesInDocument)
strWatermark = InputBox("Enter Watermark")
With activeDoc
For i = 1 To lngPages
Set rngDoc = .GoTo(What:=wdGoToPage, Name:=i)
rngDoc.Collapse wdCollapseStart
Set shpTextBox = .Shapes.AddTextbox(Orientation:=msoTextOrientationHorizontal, _
Left:=InchesToPoints(1), _
Top:=InchesToPoints(4), _
Width:=InchesToPoints(6), _
Height:=InchesToPoints(2), _
Anchor:=rngDoc)
With shpTextBox
.Line.Visible = msoFalse
.Rotation = -45
.WrapFormat.Type = wdWrapBehind
.TextFrame.HorizontalAnchor = msoAnchorCenter
.TextFrame.VerticalAnchor = msoAnchorMiddle
With .TextFrame.TextRange
.Font.AllCaps = True
.Font.Size = "60"
.Font.ColorIndex = wdGray25
.ParagraphFormat.Alignment = wdAlignParagraphCenter
.Text = strWatermark
End With
End With
Next
End With
End Sub

It looks that, selecting all their range and doing rotation at once, works...
Sub CustomWatermarkBis()
Dim activeDoc As Document, rngDoc As Range, shpTextBox As Shape
Dim lngPages As Long, i As Long, strWatermark As String, shR As ShapeRange
Dim arrRot As Variant, k As Long
Set activeDoc = ActiveDocument
lngPages = activeDoc.Range.Information(wdNumberOfPagesInDocument)
ReDim arrRot(0 To lngPages - 1)
strWatermark = InputBox("Enter Watermark Text")
With activeDoc
For i = 1 To lngPages
Set rngDoc = .GoTo(What:=wdGoToPage, Name:=i)
rngDoc.Collapse wdCollapseStart
Set shpTextBox = .Shapes.AddTextbox(Orientation:=msoTextOrientationHorizontal, _
Left:=InchesToPoints(1), _
Top:=InchesToPoints(4), _
Width:=InchesToPoints(6), _
Height:=InchesToPoints(2), _
Anchor:=rngDoc)
With shpTextBox.TextFrame.TextRange
.Font.AllCaps = True
.Font.Size = "60"
.Font.ColorIndex = wdGray25
.ParagraphFormat.Alignment = wdAlignParagraphCenter
.Text = strWatermark
End With
shpTextBox.Name = "T" & i
arrRot(k) = shpTextBox.Name: k = k + 1
Next
Set shR = .Shapes.Range(arrRot)
End With
With shR
.Select
.Line.Visible = msoFalse
.Rotation = -45
.WrapFormat.Type = wdWrapBehind
.TextFrame.HorizontalAnchor = msoAnchorCenter
.TextFrame.VerticalAnchor = msoAnchorMiddle
End With
Selection.Collapse
End Sub

Re-ordering your code to add the text before changing the rotation should solve the issue. It certainly does for me in Word 365.
EDIT: That approach worked for me exactly twice and now I can't repeat it. Adding the text box to the header does work reliably though, and results in a much tidier document.
Sub CustomWatermarkInHeader()
Dim activeDoc As Document
Dim rngDoc As Range
Dim strWatermark As String
Dim docSection As Section
Dim shpTextBox As Shape
Set activeDoc = ActiveDocument
strWatermark = InputBox("Enter Watermark")
With activeDoc
For Each docSection In .Sections
Set rngDoc = docSection.Headers(wdHeaderFooterPrimary).Range
rngDoc.Collapse wdCollapseStart
Set shpTextBox = .Shapes.AddTextbox(Orientation:=msoTextOrientationHorizontal, _
Left:=InchesToPoints(1), _
Top:=InchesToPoints(4), _
Width:=InchesToPoints(6), _
Height:=InchesToPoints(2), _
Anchor:=rngDoc)
With shpTextBox
With .TextFrame
.HorizontalAnchor = msoAnchorCenter
.VerticalAnchor = msoAnchorMiddle
With .TextRange
.Font.AllCaps = True
.Font.Size = "60"
.Font.ColorIndex = wdGray25
.ParagraphFormat.Alignment = wdAlignParagraphCenter
.Text = strWatermark
End With
End With
.Line.Visible = msoFalse
.Rotation = -45
.WrapFormat.Type = wdWrapBehind
End With
Next
End With
End Sub
However, you may want to consider:
adding the text box to the header instead of cluttering your document with a text box on every page. You will achieve the same result with fewer text boxes.
Using the built-in functionality to add a custom watermark. You can find this on the Design tab of the ribbon.

Instead of:
.Rotation = -45
Try:
.ThreeD.IncrementRotationZ -45
https://learn.microsoft.com/en-us/office/vba/api/word.threedformat

Related

How to insert an image on all pages using Word VBA?

I want to insert an image on every page.
I know that the command is Next in a For loop.
Sub InsertImage()
Dim oILS As InlineShape, oShp As Shape
Set oILS = Selection.InlineShapes.AddPicture(FileName:= _
"C:\Users\" & LCase(Environ("UserName")) & "\Desktop\SubEscritorio3\Ejercicios Matemáticas\Barra.png", LinkToFile:=False, _
SaveWithDocument:=True)
Set oShp = oILS.ConvertToShape
With oShp
.WrapFormat.Type = wdWrapBehind
.Left = -55
.Top = 471.1
.Height = 21.5
.Width = 522
End With
End Sub
Well, I found the way to do it on all pages. If it helps anyone, here it is:
Sub Demo()
Dim Rng As Range, i As Long, Shp As Shape, ImageName As String
ImageName = "C:\Users\" & LCase(Environ("UserName")) & "\Desktop\SubEscritorio3\Ejercicios Matemáticas\Barra.png"
With ActiveDocument
Set Rng = .Range(0, 0)
For i = 1 To .ComputeStatistics(wdStatisticPages)
Set Rng = Rng.GoTo(What:=wdGoToPage, Name:=i)
Set Rng = Rng.GoTo(What:=wdGoToBookmark, Name:="\page")
Rng.Collapse wdCollapseStart
Set Shp = .InlineShapes.AddPicture(FileName:=ImageName, SaveWithDocument:=True, Range:=Rng).ConvertToShape
With Shp
.Left = -55
.Top = 471.1
.Width = 522
.Height = 21.5
.WrapFormat.Type = wdWrapBehind
End With
Next
End With
End Sub

Macro update for VBA PPT to fit contents of a slide to a specific predefined workarea

I have a macro for VBA PPT to fit contents of a slide to a specific predefined workarea, now I select the required shapes to be fit into workarea and run this tool slide by slide. can anybody suggest how can I select multiple slides and get all the shapes (except placeholders) in those slides fit to the same work area
Sub FitContents()
Dim shp, grid, ZenSmartGroup, ZenWorkGrid As Shape
Dim SelectShapes As Variant
Dim targetSlides As SlideRange
Dim thisSlide, oSld As Slide
Dim theseShapes As ShapeRange
Set thisSlide = ActivePresentation.Slides(1)
Dim GridTop, GridLeft, GridHeight, GridWidth As Single
If ActiveWindow.Selection.Type = ppSelectionSlides Then
Set targetSlides = ActiveWindow.Selection.SlideRange
End If
For Each oSld In targetSlides
For Each shp In oSld.Shapes
If Not ActivePresentation.Slides(1).Tags("Font Size") = "" Then
If shp.HasTextFrame Then
If shp.TextFrame.HasText Then
shp.TextFrame.TextRange.Font.Size = ActivePresentation.Slides(1).Tags("Font Size")
End If
End If
End If
Next
If ActivePresentation.Slides(1).Tags("Grid Height") = "" Then
MsgBox "Please set grid size in Prezent Admin > Settings", vbInformation, "Set Grid Size"
End
End If
GridTop = ActivePresentation.Slides(1).Tags("Grid Top")
GridLeft = ActivePresentation.Slides(1).Tags("Grid Left")
GridHeight = ActivePresentation.Slides(1).Tags("Grid Height")
GridWidth = ActivePresentation.Slides(1).Tags("Grid Width")
oSld.Select
ActiveWindow.ViewType = ppViewSlide
ActiveWindow.Selection.ShapeRange.Group.Select
With ActiveWindow.Selection.ShapeRange(1)
.Top = GridTop
.Left = GridLeft
.LockAspectRatio = frmFitToGrid.chkAspectRatio
.Width = GridWidth
.Height = GridHeight
If frmFitToGrid.optHeight = True Then
.Height = GridHeight
End If
'If .Width > GridWidth Then
If frmFitToGrid.optWidth = True Then
.Width = GridWidth
End If
.Tags.Add "Type", "ZenSmartGroup"
.Name = "ZenSmartGroup"
End With
Set grid = oSld.Shapes.AddShape(msoShapeRectangle, GridLeft, GridTop, GridWidth, GridHeight)
grid.Fill.Visible = msoFalse
grid.Line.Visible = msoTrue
grid.Line.ForeColor.RGB = RGB(0, 255, 0)
grid.Line.Weight = 2.25
'grid.Select
grid.Name = "ZenWorkGrid"
SelectShapes = Array("ZenSmartGroup", "ZenWorkGrid")
'Set theseShapes = thisSlide.Shapes.Range(SelectShapes)
'theseShapes.Align msoAlignMiddles, msoFalse
'theseShapes.Align msoAlignCenters, msoFalse
Set ZenSmartGroup = oSld.Shapes("ZenSmartGroup")
Set ZenWorkGrid = oSld.Shapes("ZenWorkGrid")
'Align Middle (Horizontal Center)
If Not (frmFitToGrid.chkAlignLeft) Then
ZenSmartGroup.Top = ZenWorkGrid.Top + ((ZenWorkGrid.Height - ZenSmartGroup.Height) / 2)
End If
'Align Center (Vertical Center)
If Not (frmFitToGrid.chkAlignTop) Then
ZenSmartGroup.Left = ZenWorkGrid.Left + ((ZenWorkGrid.Width - ZenSmartGroup.Width) / 2)
End If
grid.Delete
'ActiveWindow.Selection.ShapeRange.Align msoAlignMiddles, msoFalse
'ActiveWindow.Selection.ShapeRange(1).Delete
'ActiveWindow.Selection.ShapeRange.Align msoAlignCenters, msoTrue
oSld.Shapes.Range.Ungroup
Next
End Sub
NOTE: code below serves as an example, as it cannot be tested given the information in your post. Please adapt it to your situation as needed.
I've included several (hopefully) helpful additions to your code in order to improve readability and maintainability. These include:
Error Checking - make sure the user has provided all the values required for the macro to effectively execute, and...
... declare your variables as close as possible to their first use.
Note the use of targetSlides as the focus object for all the selected slides. This way you avoid to continually reference ActivePresentation.Slides(1). (Note this was an assumption on my part, adjust the code as necessary)
'--- make sure the user has selected at least two slides
Dim targetSlides As SlideRange
If ActiveWindow.Selection.Type = ppSelectionSlides Then
Set targetSlides = ActiveWindow.Selection.SlideRange
Else
MsgBox "Please select two or more slides in the left-hand slide overview panel.", _
vbCritical + vbInformation + vbOKOnly, "Select Slides for Grids"
Exit Sub
End If
'--- make sure the grid values are set
If targetSlides(1).Tags("Grid Height") = vbNullString Then
MsgBox "Please set grid size in Prezent Admin > Settings", _
vbCritical + vbInformation + vbOKOnly, "Set Grid Size"
End
End If
'--- assumes ONLY the first slide in the target slides has the Grid tags
Dim gridTop As Long
Dim gridLeft As Long
Dim gridHeight As Long
Dim gridWidth As Long
Dim fontSize As Double
With targetSlides(1)
gridTop = .Tags("GRID TOP")
gridLeft = .Tags("GRID LEFT")
gridHeight = .Tags("GRID HEIGHT")
gridWidth = .Tags("GRID WIDTH")
fontSize = IIf(.Tags("FONT SIZE") <> vbNullString, .Tags("FONT SIZE"), 0#)
End With
Break code into separate subs or functions to increase the readability of the logic.
It's easy to get lose the overall point of the solution when you have to mentally summarize large blocks of code. In my example, the main logic loop is:
Dim sld As Slide
For Each sld In targetSlides
ResetTextSize fontSize, sld
Dim slideShapes As ShapeRange
Set slideShapes = SelectAllShapes(sld)
CreateShapeGrid sld, slideShapes, _
gridTop, gridLeft, gridHeight, gridWidth
Next
Before looking at the full solution below, look at some of the supporting subs and functions. Most especially, note the function IsPlaceholder which checks a Shape on any slide to see if it's part of the layout (and shouldn't be selected) or not.
Full code module:
Option Explicit
Sub FitContents()
'--- make sure the user has selected at least two slides
Dim targetSlides As SlideRange
If ActiveWindow.Selection.Type = ppSelectionSlides Then
Set targetSlides = ActiveWindow.Selection.SlideRange
Else
MsgBox "Please select two or more slides in the left-hand slide overview panel.", _
vbCritical + vbInformation + vbOKOnly, "Select Slides for Grids"
Exit Sub
End If
'--- make sure the grid values are set
If targetSlides(1).Tags("Grid Height") = vbNullString Then
MsgBox "Please set grid size in Prezent Admin > Settings", _
vbCritical + vbInformation + vbOKOnly, "Set Grid Size"
End
End If
'--- assumes ONLY the first slide in the target slides has the Grid tags
Dim gridTop As Long
Dim gridLeft As Long
Dim gridHeight As Long
Dim gridWidth As Long
Dim fontSize As Double
With targetSlides(1)
gridTop = .Tags("GRID TOP")
gridLeft = .Tags("GRID LEFT")
gridHeight = .Tags("GRID HEIGHT")
gridWidth = .Tags("GRID WIDTH")
fontSize = IIf(.Tags("FONT SIZE") <> vbNullString, .Tags("FONT SIZE"), 0#)
End With
Dim sld As Slide
For Each sld In targetSlides
ResetTextSize fontSize, sld
Dim slideShapes As ShapeRange
Set slideShapes = SelectAllShapes(sld)
CreateShapeGrid sld, slideShapes, _
gridTop, gridLeft, gridHeight, gridWidth
Next
End Sub
Sub ResetTextSize(ByVal fontSize As Double, ByRef sld As Slide)
'--- (re)set the font sizes in all shapes with text, as long
' as it's not a placeholder shape on the current slide
If fontSize > 0 Then
Dim shp As Shape
For Each shp In sld.Shapes
If Not IsPlaceholder(sld, shp) Then
If shp.HasTextFrame Then
If shp.TextFrame.HasText Then
shp.TextFrame.TextRange.Font.Size = fontSize
End If
End If
End If
Next
End If
End Sub
Function IsPlaceholder(ByRef sld As Slide, ByRef shp As Shape) As Boolean
With sld.Shapes.Placeholders
IsPlaceholder = False
If .Count > 0 Then
Dim i As Long
For i = 1 To .Count
If .Item(i).Name = shp.Name Then
IsPlaceholder = True
Exit Function
End If
Next i
End If
End With
End Function
Function CollectionToArray(ByRef c As Collection) As Variant()
Dim a() As Variant: ReDim a(0 To c.Count - 1)
Dim i As Integer
For i = 1 To c.Count
a(i - 1) = c.Item(i)
Next
CollectionToArray = a
End Function
Function SelectAllShapes(ByRef sld As Slide) As ShapeRange
'--- creates a Collection of all the non-placeholder shape names, then
' convert the names to an array to create a ShapeRange object
Dim shp As Shape
Dim shps As Collection
Set shps = New Collection
For Each shp In sld.Shapes
If Not IsPlaceholder(sld, shp) Then
shps.Add shp.Name
End If
Next shp
If shps.Count > 0 Then
Dim shpsArray() As Variant
shpsArray = CollectionToArray(shps)
Set SelectAllShapes = sld.Shapes.Range(shpsArray)
Else
Set SelectAllShapes = Nothing
End If
End Function
Sub CreateShapeGrid(ByRef sld As Slide, ByRef slideShapes As ShapeRange, _
ByVal gridTop As Long, ByVal gridLeft As Long, _
ByVal gridHeight As Long, ByVal gridWidth As Long)
'--- position the group of shapes
With slideShapes.Group
.top = gridTop
.left = gridLeft
.LockAspectRatio = frmFitToGrid.chkAspectRatio
.width = gridWidth
.height = gridHeight
If frmFitToGrid.optHeight = True Then
.height = gridHeight
End If
'If .Width > GridWidth Then
If frmFitToGrid.optWidth = True Then
.width = gridWidth
End If
.Tags.Add "Type", "ZenSmartGroup"
.Name = "ZenSmartGroup"
End With
'--- now create a grid over the shapes
Dim grid As Shape
Set grid = sld.Shapes.AddShape(msoShapeRectangle, gridLeft, gridTop, gridWidth, gridHeight)
grid.Fill.Visible = msoFalse
grid.Line.Visible = msoTrue
grid.Line.ForeColor.RGB = RGB(0, 255, 0)
grid.Line.Weight = 2.25
'grid.Select
grid.Name = "ZenWorkGrid"
SelectShapes = Array("ZenSmartGroup", "ZenWorkGrid")
'Set theseShapes = thisSlide.Shapes.Range(SelectShapes)
'theseShapes.Align msoAlignMiddles, msoFalse
'theseShapes.Align msoAlignCenters, msoFalse
Set ZenSmartGroup = sld.Shapes("ZenSmartGroup")
Set ZenWorkGrid = sld.Shapes("ZenWorkGrid")
'Align Middle (Horizontal Center)
' If Not (frmFitToGrid.chkAlignLeft) Then
' ZenSmartGroup.Top = ZenWorkGrid.Top + ((ZenWorkGrid.Height - ZenSmartGroup.Height) / 2)
' End If
'
' 'Align Center (Vertical Center)
' If Not (frmFitToGrid.chkAlignTop) Then
' ZenSmartGroup.Left = ZenWorkGrid.Left + ((ZenWorkGrid.Width - ZenSmartGroup.Width) / 2)
' End If
grid.Delete
'ActiveWindow.Selection.ShapeRange.Align msoAlignMiddles, msoFalse
'ActiveWindow.Selection.ShapeRange(1).Delete
'ActiveWindow.Selection.ShapeRange.Align msoAlignCenters, msoTrue
slideShapes.Ungroup
End Sub

How get i get an AddedPicture center on a page word

To get around the fact that the manual way to insert a pdf in a word don't give really good quality result.
I'm trying to insert pictures that have been nicely converted from a pdf to png with Imagemagick in a word with a macro.
The moment where i struggle is when i want the picture to get in a middle of each page and don't overlap each over.
I come up with this but i don't understand why it seems that AllowOverlap and wdShapeCenter do nothing while wdWrapTopBottom work properly. The picture get stuck to the top-left corner'
Sub Test()
Dim objShape As Shape
strPath = "Some.png"
'insert the image
Set objShape = ActiveDocument.Shapes.AddPicture( _
FileName:=strPath, LinkToFile:=False, _
SaveWithDocument:=True)
objShape.WrapFormat.AllowOverlap = False
objShape.Top = WdShapePosition.wdShapeCenter
objShape.WrapFormat.Type = wdWrapTopBottom
End Sub
I tried to use Selection.InlineShapes.AddPicture to resolve the overlap problem but i can't get the picture move from the top-left corner neither.
Thanks for your help
For example:
Sub Demo()
Application.ScreenUpdating = False
Dim Shp As Shape
With Dialogs(wdDialogInsertPicture)
.Display
If .Name <> "" Then
Set Shp = ActiveDocument.Shapes.AddPicture(FileName:=.Name, _
LinkToFile:=False, SaveWithDocument:=True, Anchor:=Selection.Range)
With Shp
.LockAspectRatio = True
.Height = InchesToPoints(2)
.RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin
.Left = wdShapeCenter
.RelativeVerticalPosition = wdRelativeVerticalPositionMargin
.Top = wdShapeCenter
.WrapFormat.AllowOverlap = False
End With
End If
End With
Application.ScreenUpdating = True
End Sub
With the above code, the inserted pic will be positioned in the center of the page. If there's already one centered there, the existing pic will be pushed down.
In light of your additional information, you should use something like:
Sub Demo()
Application.ScreenUpdating = False
Dim iShp As InlineShape, sWdth As Single, sHght As Single
With Dialogs(wdDialogInsertPicture)
.Display
If .Name <> "" Then
Set Shp = .InlineShapes.AddPicture(FileName:=.Name, _
LinkToFile:=False, SaveWithDocument:=True, Range:=Selection.Range)
With ActiveDocument.PageSetup
sWdth = .PageWidth - .LeftMargin - .RightMargin
sHght = .PageHeight - .TopMargin - .LeftMargin
End With
With iShp
.LockAspectRatio = True
.Width = sWdth
If .Height > sHght Then .Height = sHght
End With
End If
End With
Application.ScreenUpdating = True
End Sub
Finaly with your help I come up with this.
It center and place on it's own page pictures from a Folder.
Sub Folder_Picture_To_Word()
Dim shp As Shape
Dim intResult As Integer
Dim strPath As String
Dim strFolderPath As String
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
'the dialog is displayed to the user
intResult = Application.FileDialog(msoFileDialogFolderPicker).Show
'checks if user has cancled the dialog
If intResult <> 0 Then
'dispaly message box
strFolderPath = Application.FileDialog(msoFileDialogFolderPicker _
).SelectedItems(1)
'Create an instance of the FileSystemObject
Set objFSO = CreateObject("Scripting.FileSystemObject")
'Get the folder object
Set objFolder = objFSO.GetFolder(strFolderPath)
'loops through each file in the directory and prints their names and path
For Each objFile In objFolder.Files
'get file path
strPath = objFile.Path
Selection.InsertAfter ChrW(12)
'insert the image
Set shp = ActiveDocument.Shapes.AddPicture(FileName:=strPath, _
LinkToFile:=False, SaveWithDocument:=True, Anchor:=Selection.Range)
With shp
'.LockAspectRatio = True
'.Height = InchesToPoints(8)
.RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin
.Left = wdShapeCenter
.RelativeVerticalPosition = wdRelativeVerticalPositionMargin
.Top = wdShapeCenter
.WrapFormat.Type = wdWrapTopBottom
.WrapFormat.AllowOverlap = False
End With
'Go to next Page to get ready for a new picture
Selection.GoTo What:=wdGoToPage, Which:=wdGoToNext
Next objFile
Selection.GoTo What:=wdGoToPage, Which:=wdGoToPrevious 'Go to second last page
'To delete the extra jump page made in the loop
Selection.Delete
End If
End Sub

textbox moves to the top of last page in word document vba macro

I am writing a vba macro for a word document. I use vba macro to generate textbox and text to the word document. The issue is that the textbox moves to the top of last page instead of staying on the first page.
I don't know what i am doing wrong. All i need is for that textbox to remain on the first page. I really need to include the textbox.
below is my code and the output image
Dim wrdDoc As Object
Dim tmpDoc As Object
Dim WDoc As String
Dim myDoc As String
myDoc = "myTest"
WDoc = ThisDocument.Path & "\mydocument.docx"
On Error Resume Next
Set wdApp = GetObject(, "Word.Application")
If wdApp Is Nothing Then
' no current word application
Set wdApp = CreateObject("Word.application")
Set wrdDoc = wdApp.Documents.Open(WDoc)
wdApp.Visible = True
Else
' word app running
For Each tmpDoc In wdApp.Documents
If StrComp(tmpDoc.FullName, WDoc, vbTextCompare) = 0 Then
' this is your doc
Set wrdDoc = tmpDoc
Exit For
End If
Next
If wrdDoc Is Nothing Then
' not open
Set wrdDoc = wdApp.Documents.Open(WDoc)
End If
End If
ActiveDocument.Content.Select
Selection.Delete
With wdApp
.Visible = True
.Activate
With .Selection
Dim objShape As Word.Shape
Set objShape2 = ActiveDocument.Shapes.addTextbox _
(Orientation:=msoTextOrientationHorizontal, _
Left:=400, Top:=100, Width:=250, Height:=60)
With objShape2
.RelativeHorizontalPosition = wdRelativeHorizontalPositionColumn
.RelativeVerticalPosition = wdRelativeVerticalPositionMargin
.Left = wdShapeRight
.Top = wdShapeTop
.TextFrame.TextRange = "This is nice and shine" & vbCrLf & "222"
.TextFrame.TextRange.ParagraphFormat.Alignment = wdAlignParagraphLeft
End With
End With
With .Selection
.TypeParagraph
.TypeParagraph
.TypeParagraph
.TypeParagraph
.TypeParagraph
.TypeParagraph
.TypeParagraph
For i = 1 To 40
.TypeText i
.TypeParagraph
Next i
End With
End With
Word Shape objects must be anchored to a character position in the Word document. They will always appear on the page where the anchor character is and, if the anchor formatting is not to the page, they will move relatively on the page with the anchor character.
A special case ensues when a document is "empty" (a lone paragraph), so it helps to make sure the document has more than one character in it. In the code sample below an additional paragraph is inserted before adding the TextBox - to the first paragraph.
I've made some other adjustments to the code:
Added On Error GoTo 0 so that error messages will appear. Otherwise, debugging becomes impossible.
Removed the With for the Word application since it's not necessary when using Word objects
Declared and use a Word Range object for inserting content. As with Excel, it's better to not work with Selection whenever possible.
Used the wrdDoc object you declare and instantiate instead of ActiveDocument.
This code worked fine in my test, but I cannot, of course, repro your entire environment.
Dim wrdDoc As Object
Dim tmpDoc As Object
Dim WDoc As String
Dim myDoc As String
myDoc = "myTest"
WDoc = ThisDocument.Path & "\mydocument.docx"
On Error Resume Next
Set wdApp = GetObject(, "Word.Application")
On Error GoTo 0
If wdApp Is Nothing Then
' no current word application
Set wdApp = CreateObject("Word.application")
Set wrdDoc = wdApp.Documents.Open(WDoc)
wdApp.Visible = True
Else
' word app running
For Each tmpDoc In wdApp.Documents
If StrComp(tmpDoc.FullName, WDoc, vbTextCompare) = 0 Then
' this is your doc
Set wrdDoc = tmpDoc
Exit For
End If
Next
If wrdDoc Is Nothing Then
' not open
Set wrdDoc = wdApp.Documents.Open(WDoc)
End If
End If
wdApp.Visible = True
wrdApp.Activate
Dim i As Long
Dim objShape2 As Word.Shape
Dim rng As Word.Range
Set rng = wrdDoc.Content
rng.Delete
With rng
.InsertAfter vbCr
.Collapse wdCollapseStart
Set objShape2 = ActiveDocument.Shapes.AddTextbox _
(Orientation:=msoTextOrientationHorizontal, _
Left:=400, Top:=100, Width:=250, Height:=60, Anchor:=rng)
With objShape2
.RelativeHorizontalPosition = wdRelativeHorizontalPositionColumn
.RelativeVerticalPosition = wdRelativeVerticalPositionMargin
.Left = wdShapeRight
.Top = wdShapeTop
.TextFrame.TextRange = "This is nice and shine" & vbCrLf & "222"
.TextFrame.TextRange.ParagraphFormat.Alignment = wdAlignParagraphLeft
End With
rng.Start = ActiveDocument.Content.End
For i = 1 To 40
.Text = i & vbCr
.Collapse wdCollapseEnd
Next i
End With
Another solution for you to look at.
'12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
'========1=========2=========3=========4=========5=========6=========7=========8=========9=========A=========B=========C
Option Explicit
Sub textboxtest()
Const my_doc_name As String = "mydocument.docx"
Dim my_fso As Scripting.FileSystemObject
Dim my_doc As Word.Document
Dim my_range As Word.Range
Dim counter As Long
Dim my_text_box As Word.Shape
Dim my_shape_range As Word.ShapeRange
' There is no need to test for the Word app existing
' if this macro is in a Word template or Document
' because to run the macro Word MUST be loaded
Set my_fso = New Scripting.FileSystemObject
If my_fso.FileExists(ThisDocument.Path & "\" & my_doc_name) Then
Set my_doc = Documents.Open(ThisDocument.Path & "\" & my_doc_name)
Else
Set my_doc = Documents.Add
my_doc.SaveAs2 ThisDocument.Path & "\" & my_doc_name
End If
my_doc.Activate ' Although it should already be visible
my_doc.content.Delete
Set my_text_box = my_doc.Shapes.AddTextbox( _
Orientation:=msoTextOrientationHorizontal, _
left:=400, _
top:=100, _
Width:=250, _
Height:=60)
With my_text_box
.Name = "TextBox1"
.RelativeHorizontalPosition = wdRelativeHorizontalPositionColumn
.RelativeVerticalPosition = wdRelativeVerticalPositionMargin
.left = wdShapeRight
.top = wdShapeTop
With .TextFrame
.TextRange = "This is nice and shine" & vbCrLf & "222"
.TextRange.ParagraphFormat.Alignment = wdAlignParagraphLeft
End With
End With
Set my_range = my_text_box.Parent.Paragraphs(1).Range
'FROM
'
' https://learn.microsoft.com/en-us/office/vba/api/word.shape'
' Every Shape object is anchored to a range of text. A shape is anchored
' to the beginning of the first paragraph that contains the anchoring
' range. The shape will always remain on the same page as its anchor.
my_range.Collapse Direction:=wdCollapseEnd
With my_range
For counter = 1 To 90
.Text = counter
.InsertParagraphAfter
.Collapse Direction:=wdCollapseEnd
Next
End With
End Sub

Tables overwritten when exporting multiple tables from excel to word

I am trying to use VBA to create a Word document with multiple tables each on a new page (using a loop) compiled with cell information from Excel.
So far everything works fantastically except after inserting the first table it is replaced by the second table, then the third table replaces the second, and so on. What I am left with is only the last created table.
I'm not sure how to cause a new table to be created instead of replacing the previously created table.
Screen shot of Excel table
Sub Export_to_Word()
'(1) Word objects.
Dim wdApp As Word.Application
Dim wdDoc As Word.Document
Dim wdCell As Word.Cell
Dim wdTabl As Word.Table
Dim wdRange As Word.Range
'(2) Excel objects
Dim wbBook As Workbook
Dim wsSheet As Worksheet
Dim strValue As String
Dim i As Integer
Dim x As Integer
'For assiging integer value to calculate number of table rows
Dim ARows As Integer
Dim BRows As Integer
Dim CRows As Integer
Dim DRows As Integer
'For copying question part as a value in the excel sheet
Dim QueNum As Variant
Dim PartA As Variant
Dim PartB As Variant
Dim PartC As Variant
Dim PartD As Variant
'For copying the question in the excel sheet
Dim QueA As Variant
Dim QueB As Variant
Dim QueC As Variant
Dim QueD As Variant
'For copying question part as a value in the excel sheet
Dim MarkA As Variant
Dim MarkB As Variant
Dim MarkC As Variant
Dim MarkD As Variant
'For copying the answers in the excel sheet
Dim AnsA As Variant
Dim AnsB As Variant
Dim AnsC As Variant
Dim AnsD As Variant
'For copying the header values in the excel sheet
Dim CandCode As Variant
Dim AnPath As Variant
Dim Logo As Variant
Dim EngNam As Variant
Dim EngTex As Variant
Dim FreNam As Variant
Dim FreTex As Variant
'(4) Initialize the Excel objects
Set wbBook = ThisWorkbook
Set wsSheet = wbBook.Worksheets("Sheet1")
'(5)Create table in excel before copying to word
'Create Word file.
Set wdApp = New Word.Application
wdApp.Visible = True
Set wdDoc = wdApp.Documents.Add
'(5a)Enter excel values into header
With wdDoc.Sections(1)
.Headers(wdHeaderFooterPrimary).Range.Text = CandCode & vbCr & vbCr & AnPath
.Headers(wdHeaderFooterPrimary).Range.Font.Name = "Arial"
.Headers(wdHeaderFooterPrimary).Range.Font.Size = 7
.Headers(wdHeaderFooterPrimary).Range.ParagraphFormat.Alignment = wdAlignParagraphCenter
End With
'(5b)Start of new cycle for loop
For i = 4 To 6
'(5c) Equate cell values to the the variables defined under Excel objects (Part 2). N.B in equation "Cells(3,i) 3= row number and i=column number
ARows = wsSheet.Cells(3, i).Value
BRows = wsSheet.Cells(7, i).Value
CRows = wsSheet.Cells(11, i).Value
DRows = wsSheet.Cells(15, i).Value
QueNum = wsSheet.Cells(1, i).Value
PartA = wsSheet.Range("A2").Value
PartB = wsSheet.Range("A6").Value
PartC = wsSheet.Range("A10").Value
PartD = wsSheet.Range("A14").Value
QueA = wsSheet.Cells(2, i).Value
QueB = wsSheet.Cells(6, i).Value
QueC = wsSheet.Cells(10, i).Value
QueD = wsSheet.Cells(14, i).Value
MarkA = wsSheet.Cells(4, i).Value
MarkB = wsSheet.Cells(8, i).Value
MarkC = wsSheet.Cells(12, i).Value
MarkD = wsSheet.Cells(16, i).Value
AnsA = wsSheet.Cells(5, i).Value
AnsB = wsSheet.Cells(9, i).Value
AnsC = wsSheet.Cells(13, i).Value
AnsD = wsSheet.Cells(17, i).Value
CandCode = wsSheet.Range("V24").Value
AnPath = wsSheet.Range("V25").Value
Logo = wsSheet.Range("V26").Value
EngNam = wsSheet.Range("V27").Value
EngTex = wsSheet.Range("V28").Value
FreNam = wsSheet.Range("V29").Value
FreTex = wsSheet.Range("V30").Value
'(5d)Creates variables that identifes location of each of the rows with the question part
TotRows = ARows + BRows + CRows + DRows + 5
QuesA_row = 2
QuesB_row = ARows + 3
QuesC_row = ARows + BRows + 4
QuesD_row = ARows + BRows + CRows + 5
'(5e)Create Word table
Set wdRange = wdDoc.Range
wdDoc.Tables.Add wdRange, NumRows:=(TotRows), NumColumns:=5, DefaultTableBehavior:=wdWord8TableBehavior, AutoFitBehavior:=wdAutoFitWindow
Set wdTabl = wdDoc.Tables(1)
'(5f)Edit Table
With wdTabl
.ApplyStyleHeadingRows = False
.ApplyStyleLastRow = False
.ApplyStyleFirstColumn = False
.ApplyStyleLastColumn = True
.ApplyStyleRowBands = False
.ApplyStyleColumnBands = False
'Changes font of table
.Range.Font.Name = "Arial"
.Range.Font.Size = "10"
'Changes spacing of lines in table to single
.Range.ParagraphFormat.SpaceBeforeAuto = False
.Range.ParagraphFormat.SpaceBefore = 8
.Range.ParagraphFormat.SpaceAfterAuto = False
.Range.ParagraphFormat.SpaceAfter = 0
.Range.ParagraphFormat.LineSpacingRule = wdLineSpaceSingle
.Range.ParagraphFormat.PageBreakBefore = False
'Adjust column widths
.Columns(1).SetWidth ColumnWidth:=20, RulerStyle:=wdAdjustNone
.Columns(2).SetWidth ColumnWidth:=23, RulerStyle:=wdAdjustNone
.Columns(3).SetWidth ColumnWidth:=400, RulerStyle:=wdAdjustNone
.Columns(4).SetWidth ColumnWidth:=11, RulerStyle:=wdAdjustNone
.Columns(5).SetWidth ColumnWidth:=40, RulerStyle:=wdAdjustNone
'Shading for marks column & borders
.Borders.Enable = False
.Columns(5).Shading.BackgroundPatternColor = wdColorGray20
.Columns(5).Borders(wdBorderTop).Color = wdColorBlack
.Columns(5).Borders(wdBorderTop).LineStyle = Options.DefaultBorderLineStyle
.Columns(5).Borders(wdBorderTop).LineWidth = Options.DefaultBorderLineWidth
.Columns(5).Borders(wdBorderLeft).Color = wdColorBlack
.Columns(5).Borders(wdBorderLeft).LineStyle = Options.DefaultBorderLineStyle
.Columns(5).Borders(wdBorderLeft).LineWidth = Options.DefaultBorderLineWidth
.Columns(5).Borders(wdBorderRight).Color = wdColorBlack
.Columns(5).Borders(wdBorderRight).LineStyle = Options.DefaultBorderLineStyle
.Columns(5).Borders(wdBorderRight).LineWidth = Options.DefaultBorderLineWidth
.Columns(5).Borders(wdBorderBottom).Color = wdColorBlack
.Columns(5).Borders(wdBorderBottom).LineStyle = Options.DefaultBorderLineStyle
.Columns(5).Borders(wdBorderBottom).LineWidth = Options.DefaultBorderLineWidth
.Columns(5).Cells(1).Borders(wdBorderBottom).Color = wdColorBlack
.Columns(5).Cells(1).Borders(wdBorderBottom).LineStyle = Options.DefaultBorderLineStyle
.Columns(5).Cells(1).Borders(wdBorderBottom).LineWidth = Options.DefaultBorderLineWidth
'Underlines for questions
.Columns(3).Cells.Borders.InsideLineStyle = wdLineStyleSingle 'Adds bottom border to all cells in column 3
.Columns(3).Cells(1).Borders(wdBorderBottom).Color = wdColorWhite 'Removes bottom border
.Columns(3).Cells(1).Borders(wdBorderBottom).LineStyle = Options.DefaultBorderLineStyle
.Columns(3).Cells(1).Borders(wdBorderBottom).LineWidth = Options.DefaultBorderLineWidth
.Columns(3).Cells(QuesA_row).Borders(wdBorderBottom).Color = wdColorWhite 'Removes bottom border
.Columns(3).Cells(QuesA_row).Borders(wdBorderBottom).LineStyle = Options.DefaultBorderLineStyle
.Columns(3).Cells(QuesA_row).Borders(wdBorderBottom).LineWidth = Options.DefaultBorderLineWidth
.Columns(3).Cells(QuesB_row).Borders(wdBorderBottom).Color = wdColorWhite 'Removes bottom border
.Columns(3).Cells(QuesB_row).Borders(wdBorderBottom).LineStyle = Options.DefaultBorderLineStyle
.Columns(3).Cells(QuesB_row).Borders(wdBorderBottom).LineWidth = Options.DefaultBorderLineWidth
.Columns(3).Cells(QuesC_row).Borders(wdBorderBottom).Color = wdColorWhite 'Removes bottom border
.Columns(3).Cells(QuesC_row).Borders(wdBorderBottom).LineStyle = Options.DefaultBorderLineStyle
.Columns(3).Cells(QuesC_row).Borders(wdBorderBottom).LineWidth = Options.DefaultBorderLineWidth
.Columns(3).Cells(QuesD_row).Borders(wdBorderBottom).Color = wdColorWhite 'Removes bottom border
.Columns(3).Cells(QuesD_row).Borders(wdBorderBottom).LineStyle = Options.DefaultBorderLineStyle
.Columns(3).Cells(QuesD_row).Borders(wdBorderBottom).LineWidth = Options.DefaultBorderLineWidth
.Columns(3).Cells(TotRows).Borders(wdBorderBottom).Color = wdColorBlack 'Adds border to bottom row of column
.Columns(3).Cells(TotRows).Borders(wdBorderBottom).LineStyle = Options.DefaultBorderLineStyle
.Columns(3).Cells(TotRows).Borders(wdBorderBottom).LineWidth = Options.DefaultBorderLineWidth
'Enter Data into table
.Columns(1).Cells(2).Range.Text = QueNum & "."
.Columns(2).Cells(QuesA_row).Range.Text = PartA
.Columns(2).Cells(QuesB_row).Range.Text = PartB
.Columns(2).Cells(QuesC_row).Range.Text = PartC
.Columns(2).Cells(QuesD_row).Range.Text = PartD
.Columns(3).Cells(QuesA_row).Range.Text = QueA
.Columns(3).Cells(QuesB_row).Range.Text = QueB
.Columns(3).Cells(QuesC_row).Range.Text = QueC
.Columns(3).Cells(QuesD_row).Range.Text = QueD
.Columns(5).Cells(1).Range.Text = "Marks"
.Columns(5).Cells(QuesA_row).Range.Text = MarkA
.Columns(5).Cells(QuesB_row).Range.Text = MarkB
.Columns(5).Cells(QuesC_row).Range.Text = MarkC
.Columns(5).Cells(QuesD_row).Range.Text = MarkD
'Modifying marks column
.Columns(5).Cells(1).Range.Font.Bold = True 'Modifys "marks" cell
.Columns(5).Cells(1).Range.ParagraphFormat.Alignment = wdAlignParagraphCenter
.Columns(5).Cells(1).Range.Cells.VerticalAlignment = wdCellAlignVerticalBottom
.Columns(5).Cells(QuesA_row).Range.Font.Bold = True
.Columns(5).Cells(QuesA_row).Range.ParagraphFormat.Alignment = wdAlignParagraphCenter
.Columns(5).Cells(QuesA_row).Range.Cells.VerticalAlignment = wdCellAlignVerticalTop
.Columns(5).Cells(QuesB_row).Range.Font.Bold = True
.Columns(5).Cells(QuesB_row).Range.ParagraphFormat.Alignment = wdAlignParagraphCenter
.Columns(5).Cells(QuesB_row).Range.Cells.VerticalAlignment = wdCellAlignVerticalTop
.Columns(5).Borders(wdBorderTop).Color = wdColorBlack
.Columns(5).Cells(QuesB_row).Borders(wdBorderTop).LineStyle = Options.DefaultBorderLineStyle
.Columns(5).Cells(QuesB_row).Borders(wdBorderTop).LineWidth = Options.DefaultBorderLineWidth
.Columns(5).Cells(QuesC_row).Range.Font.Bold = True
.Columns(5).Cells(QuesC_row).Range.ParagraphFormat.Alignment = wdAlignParagraphCenter
.Columns(5).Cells(QuesC_row).Range.Cells.VerticalAlignment = wdCellAlignVerticalTop
.Columns(5).Borders(wdBorderTop).Color = wdColorBlack
.Columns(5).Cells(QuesC_row).Borders(wdBorderTop).LineStyle = Options.DefaultBorderLineStyle
.Columns(5).Cells(QuesC_row).Borders(wdBorderTop).LineWidth = Options.DefaultBorderLineWidth
.Columns(5).Cells(QuesD_row).Range.Font.Bold = True
.Columns(5).Cells(QuesD_row).Range.ParagraphFormat.Alignment = wdAlignParagraphCenter
.Columns(5).Cells(QuesD_row).Range.Cells.VerticalAlignment = wdCellAlignVerticalTop
.Columns(5).Borders(wdBorderTop).Color = wdColorBlack
.Columns(5).Cells(QuesD_row).Borders(wdBorderTop).LineStyle = Options.DefaultBorderLineStyle
.Columns(5).Cells(QuesD_row).Borders(wdBorderTop).LineWidth = Options.DefaultBorderLineWidth
'Adjusts text alignment in question column
.Columns(3).Cells.VerticalAlignment = wdCellAlignVerticalBottom
' Exit table and insert page break so next table starts at beginning of page
With wdRange
.Collapse Direction:=wdCollapseEnd
.InsertParagraphAfter
.InsertBreak Type:=wdPageBreak
.Collapse Direction:=wdCollapseEnd
End With
End With
Next i
'(7)Identifies all numbered words and replaces them with all caps bold
Dim A(10) As String
A(1) = "one"
A(2) = "two"
A(3) = "three"
A(4) = "four"
A(5) = "five"
A(6) = "six"
A(7) = "seven"
A(8) = "eight"
A(9) = "nine"
A(10) = "ten"
Set wdRange = ActiveDocument.Content
With wdRange
For x = 1 To 10
.Find.ClearFormatting
.Find.Replacement.ClearFormatting
.Find.Replacement.Font.Bold = True
With .Find
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = True
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Replacement.Font.Bold = True
.Replacement.Font.Allcaps = True
wdRange.Find.Execute FindText:=A(x), ReplaceWith:=A(x), Format:=True, _
Replace:=wdReplaceAll
End With
Next x
End With
'(8)Null out the variables.
Set wdCell = Nothing
Set wdDoc = Nothing
Set wdApp = Nothing
Set wdRange = Nothing
Set wdTabl = Nothing
'(9) Adds message box to show complete
MsgBox "Success! The exam questions are complete!", vbInformation
End Sub
This stripped-down version worked for me:
Sub Export_to_Word()
Dim wdApp As Word.Application, i As Long, wdDoc As Word.Document
Dim wdCell As Word.Cell, wdTabl As Word.Table, wdRange As Word.Range
Dim wbBook As Workbook, wsSheet As Worksheet
Set wbBook = ThisWorkbook
Set wsSheet = wbBook.Worksheets("Sheet1")
Set wdApp = New Word.Application
wdApp.Visible = True
Set wdDoc = wdApp.Documents.Add
For i = 1 To 5
wdDoc.Paragraphs.Add
Set wdRange = ActiveDocument.Paragraphs.Last.Range
Set wdTabl = wdDoc.Tables.Add(wdRange, NumRows:=5, NumColumns:=5, _
DefaultTableBehavior:=wdWord8TableBehavior, _
AutoFitBehavior:=wdAutoFitWindow)
With wdTabl
.Borders.Enable = True
.Columns(1).Cells(1).Range.Text = "First"
.Columns(5).Cells(5).Range.Text = "Last"
End With
Next i
End Sub
You set up only one table.
'(5e)Create Word table
Set wdRange = wdDoc.Range
wdDoc.Tables.Add wdRange, NumRows:=(TotRows), NumColumns:=5, DefaultTableBehavior:=wdWord8TableBehavior, AutoFitBehavior:=wdAutoFitWindow
Set wdTabl = wdDoc.Tables(1)
Change code.
'(5e)Create Word table
Set wdRange = wdDoc.Range
Set wdTabl = wdDoc.Tables.Add(wdRange, NumRows:=(TotRows), NumColumns:=5, DefaultTableBehavior:=wdWord8TableBehavior, AutoFitBehavior:=wdAutoFitWindow)
'Set wdTabl = wdDoc.Tables(1)