I want to center a logo image in the footer of a Word 2016 document.
Based on code from Word footer image alignment in VBA, I can get the logo into the footer but cannot get it centered.
Sub Main()
'Based on https://stackoverflow.com/questions/58257052/word-footer-image-alignment-in-vba
Dim FIRMADOC As String
Dim SHP As InlineShape
Dim rng As Word.Range
FIRMADOC = "C:\Users\" & Environ("username") & "\Documents\Invoice\footer.png"
Set rng = ActiveDocument.Sections(1).Footers(wdHeaderFooterPrimary).Range
rng.Collapse wdCollapseStart
rng.Text = vbTab & vbTab 'position at second, right-aligned tab in the footer)
‘(Note: I have tried removing the tabs to no useful effect)
Set SHP = rng.InlineShapes.AddPicture(FileName:=FIRMADOC, LinkToFile:=False, SaveWithDocument:=True, Range:=rng)
'Based on Record Macro
ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageFooter
Application.Templates( _
"C:\Users\Brian\AppData\Roaming\Microsoft\Document Building Blocks\1033\16\Built-In Building Blocks.dotx" _
).BuildingBlockEntries("Bold Numbers 3").Insert Where:=Selection.Range, _
RichText:=True
End Sub
The image ('FIRMADOC') ends up left justified. Manually changing alignment from Home Tab works. Recording that manual change comes up empty.
For example:
Dim Shp As Shape
With ActiveDocument
Set Shp = .Shapes.AddPicture(FileName:="C:\Users\" & Environ("username") & "\Documents\Invoice\footer.png", _
LinkToFile:=False, SaveWithDocument:=True, Anchor:=.Sections(1).Footers(wdHeaderFooterPrimary).Range)
Shp.RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin
Shp.Left = wdShapeCenter
End With
or, if you want the logo formatted as in-line:
With ActiveDocument.Sections(1).Footers(wdHeaderFooterPrimary).Range
.InlineShapes.AddPicture FileName:="C:\Users\" & Environ("username") & "\Documents\Invoice\footer.png", _
LinkToFile:=False, SaveWithDocument:=True, Range:=.Characters.Last
.ParagraphFormat.Alignment = wdAlignParagraphCenter
End With
Related
I have multiple slide decks with pictures that are too big (in pixels / resolution). I can use PowerPoint's "Compress Pictures" function to reduce the resolution, but there is not one single resolution that would suit all images (e.g. photos could go with 96 ppi E-mail resolution, while screenshots would require 220 ppi Print resolution). For that reason, I cannot simply apply one resolution to all pictures (by deselecting the "Apply only to this picture" checkbox).
So I would fancy a macro that steps through all pictures in the slide deck, and for each picture offers the user to select the resolution for compression (with a default set to 150 ppi Web, which suits most cases).
I was thinking of a code like this:
Sub Compress_Pictures_one_by_one()
Dim shp As Shape
Dim sld As Slide
'Loop through each slide in ActivePresentation:
For Each sld In ActivePresentation.Slides
'Loop through each shape on the slide:
For Each shp In sld.Shapes
If shp.Type = msoPicture Then
shp.Select
'Show the Compress Pictures" dialog:
Application.CommandBars.ExecuteMso "PicturesCompress"
'Preselect Web resolution:
SendKeys "%W", True
End If
Next shp
Next sld
End Sub
However, this does not wait for the user to complete the dialog (with OK or Cancel) before moving on to the next picture.
Any idea how to solve? Or got any alternatives?
The code search for the dialog and wait for the dialog to be closed to continue
Declare PtrSafe Function FindWindow Lib "user32" Alias _
"FindWindowA" (ByVal wClassName As Any, ByVal _
wWindowName As Any) As LongPtr
Sub Compress_Pictures_one_by_one()
Dim shp As Shape
Dim sld As Slide
'Loop through each slide in ActivePresentation:
For Each sld In ActivePresentation.Slides
'Loop through each shape on the slide:
For Each shp In sld.Shapes
If shp.Type = msoPicture Then
shp.Select
'Show the Compress Pictures" dialog:
Application.CommandBars.ExecuteMso "PicturesCompress"
'Preselect Web resolution:
SendKeys "%W", True
While testDialogOpen
DoEvents
Wend
End If
Next shp
Next sld
End Sub
Function testDialogOpen()
Dim wHandle As LongPtr
Dim wName As String
wName = "Compress Pictures"
wHandle = FindWindow(0&, wName)
If wHandle = 0 Then
testDialogOpen = False
Else
testDialogOpen = True
End If
End Function
In my final code, I have added some user information as well as the option for the user to verify the compression result and then choose to move on or to apply a different compression setting. Wanted to share in case anyone needs it:
Declare PtrSafe Function FindWindow Lib "user32" Alias _
"FindWindowA" (ByVal wClassName As Any, ByVal _
wWindowName As Any) As LongPtr
Sub Compress_Pictures_one_by_one()
Dim shp As Shape
Dim sld As Slide
Dim intCounter As Integer
Dim blnNext As Boolean
'Intro:
If MsgBox("This procedure will loop through all pictures in you slide deck. " _
& "For each picture it will offer you to select a compression. " _
& "If you do not want to change the compression of a picture, hit the Cancel button in the Compress Pictures dialog. " & vbCr _
& "After each compression setting you'll be asked whether to keep the compression setting and move on to the next picture, " _
& "or to re-choose a compression for the current picture, or to stop processing. ", _
vbInformation + vbOKCancel, "Introduction") = vbCancel Then Exit Sub
intCounter = 0
'Loop through each slide in ActivePresentation:
For Each sld In ActivePresentation.Slides
'Loop through each shape on the slide:
For Each shp In sld.Shapes
If shp.Type = msoPicture Then
intCounter = intCounter + 1
ActiveWindow.View.GotoSlide sld.SlideIndex
shp.Select
'MsgBox "Picture format: " & shp.PictureFormat
Do
'Show the Compress Pictures" dialog:
Application.CommandBars.ExecuteMso "PicturesCompress"
'Preselect Web resolution:
SendKeys "%W", True
While testDialogOpen
DoEvents
Wend
'Have user verify the compression result and choose how to proceed:
Select Case MsgBox("Move to the next picture?" & vbCr _
& "Yes: Continue with next picture." & vbCr _
& "No: Re-choose a setting for the current picture." & vbCr _
& "Cancel: Stop processing any further pictures.", _
vbYesNoCancel + vbQuestion, _
"Continue?")
Case vbYes
blnNext = True
Case vbNo
blnNext = False
Case vbCancel
If MsgBox("You are about to cancel the task. " & vbCr _
& intCounter & " picture" & IIf(intCounter = 1, " has", "s have") & " been touched." & vbCr _
& "No further pictures will be processed." & vbCr _
& "Are you sure you want to stop?", _
vbCritical + vbYesNo, "Cancel?") _
= vbYes Then Exit Sub
End Select
Loop Until blnNext
End If
Next shp
Next sld
'Finish:
If intCounter = 0 Then
MsgBox "No pictures have been detected in your slides.", vbInformation + vbOKOnly
Else
MsgBox "Task completed. " & vbCr & intCounter & " picture" & IIf(intCounter = 1, " has", "s have") & " been touched.", vbInformation, "Compress Pictures"
End If
End Sub
Function testDialogOpen()
Dim wHandle As LongPtr
Dim wName As String
wName = "Compress Pictures"
wHandle = FindWindow(0&, wName)
If wHandle = 0 Then
testDialogOpen = False
Else
testDialogOpen = True
End If
End Function
Thanks again for your help, wrbp!
I using a VBA code to batch find and replace highlighted text. The macro finds and replaces the words in the document. It works well with a few number of highlighted text on a small document (1-2 pages). However, when I use this macro on a large documents which has over a 100 pages, Microsoft word crashed and becomes unresponsive so I have to forced to quit.
The code is to help make it easy to redact information. I am replacing the highlight text which occur also in tables with XXXXX and highlighted black.
Does anyone have any tips to make the code more efficient?
Here is the code
Sub FindandReplaceHighlight()
Dim strFindColor As String
Dim strReplaceColor As String
Dim strText As String
Dim objDoc As Document
Dim objRange As Range
Application.ScreenUpdating = False
Set objDoc = ActiveDocument
strFindColor = InputBox("Specify a color (enter the value):", "Specify Highlight Color")
strReplaceColor = InputBox("Specify a new color (enter the value):", "New Highlight Color")
strText = InputBox("Specify a new text (enter the value):", "New Text")
With Selection
.HomeKey Unit:=wdStory
With Selection.Find
.Highlight = True
Do While .Execute
If Selection.Range.HighlightColorIndex = strFindColor Then
Set objRange = Selection.Range
objRange.HighlightColorIndex = strReplaceColor
objRange.Text = strText
objRange.Font.ColorIndex = wdBlack
Selection.Collapse wdCollapseEnd
End If
Loop
End With
End With
Application.ScreenUpdating = True
End Sub
Try:
Sub FindandReplaceHighlight()
Application.ScreenUpdating = False
Dim ClrFnd As Long, ClrRep As Long, strTxt As String
Const StrColors As String = vbCr & _
" 1 Black" & vbCr & _
" 2 Blue" & vbCr & _
" 3 Turquoise" & vbCr & _
" 4 Bright Green" & vbCr & _
" 5 Pink" & vbCr & _
" 6 Red" & vbCr & _
" 7 Yellow" & vbCr & _
" 8 White" & vbCr & _
" 9 Dark Blue" & vbCr & _
"10 Teal" & vbCr & _
"11 Green" & vbCr & _
"12 Violet" & vbCr & _
"13 Dark Red" & vbCr & _
"14 Dark Yellow" & vbCr & _
"15 Gray 50" & vbCr & _
"16 Gray 25%"
ClrFnd = InputBox("Specify the old color (enter the value):" & StrColors, "Specify Highlight Color")
ClrRep = InputBox("Specify the new color (enter the value):" & StrColors, "New Highlight Color")
strTxt = InputBox("Specify the new text (enter the value):", "New Text")
With ActiveDocument
With .Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Highlight = True
.Forward = True
.Wrap = wdFindStop
End With
Do While .Find.Execute
If .HighlightColorIndex = ClrFnd Then
.HighlightColorIndex = ClrRep
.Text = strTxt
.Font.ColorIndex = wdBlack
.Collapse wdCollapseEnd
End If
Loop
End With
End With
Application.ScreenUpdating = True
End Sub
Edit: 17-07-2018 found a solution to retrieve image dimensions in Excel.
I've created a code to retrieve image files in Excel and it's working fine, but once I resize the image it doesn't automatically update its value I need to shift between images before and then go back to the resized imaged to get the value.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim mypic As Picture
If Target.Address = "$A$4" Then
Me.Pictures.Visible = False
With Range("e2")
For Each mypic In Me.Pictures
If mypic.Name = .Text Then
mypic.Visible = True
mypic.Top = .Top
mypic.Left = .Left
Exit For
End If
Next mypic
End With
With ActiveSheet
s = Round(.Shapes(.Range("e2").Value).Height / 72 * 2.54, 2) & "cm"
y = Round(.Shapes(.Range("e2").Value).Width / 72 * 2.54, 2) & "cm"
MsgBox "Picture dimensions are " & vbLf & vbLf & _
"Height: " & s & vbLf & vbLf & _
"Width: " & y
.Range("Q5") = s
.Range("Q6") = y
End With
End If
End Sub
The code as above is there a way to automatically update the values without closing the Excel file or shifting between images.
Thank you in advanced!
I have thoroughly read the above answer you posted in the link below.
Programmatically Update Linked Named Range of excel object in MS Word (2007)
I am facing issue while updating a shared drive path using the same above steps. My excel file is in a shared drive folder and I have tried putting in OLE objects manually, which I succeeded. While using a similar logic :
ActiveDocument.Bookmarks("R1").Range.InlineShapes.AddOLEObject filename:=filename _
& "!Range1", LinkToFile:=True"
it gives me the below error:
Word is unable to create a link to the object you specified. Please
insert the object directly into your file without creating a link
I am not able to figure out why this error comes up. Any help in this regard will be appreciated.
Note: I am updating a new range as well as the file location and file name. I have verified range in excel file is valid.
Thanks
Since the link already exists, you shouldn't be using .AddOLEObject. You should instead be editing the filepath. For example:
Dim iShp As InlineShape
Const strPath As String = "New Path"
For Each iShp In ActiveDocument.InlineShapes
With iShp
If Not .LinkFormat Is Nothing Then
With .LinkFormat
.SourceFullName = Replace(.SourceFullName, .SourcePath, strPath)
End With
With .Field
.Code.Text = Replace(.Code.Text, "5 - EW_RA!R2C17", "6 - EW_RA!R2C17")
.Update
End With
End If
End With
Next
For wrapped objects, with early binding:
Dim wdApp As New Word.Application, wdDoc As Word.Document
Dim wdShp As Word.Shape, wdRng As Word.Range, i As Long, Fmt As Long, StrID As String, StrNm As String
Dim vRel As Long, vPos As Single, hRel As Long, hPos As Single, Hght As Single, Wdth As Single
Const strPath As String = "New Path"
With wdApp
.Visible = True
Set wdDoc = .Documents.Open(Filename:="C:\Users\" & Environ("Username") & "\Documents\Target Document.docx", _
AddToRecentFiles:=False, Visible:=True)
With wdDoc
For i = .Shapes.Count To 1 Step -1
With .Shapes(i)
If Not .LinkFormat Is Nothing Then
Set wdRng = .Anchor: StrID = .OLEFormat.progID: StrNm = "\" & .LinkFormat.SourceName
Fmt = .WrapFormat.Type: Hght = .Height: Wdth = .Width
vRel = .RelativeVerticalPosition: vPos = .Top
hRel = .RelativeHorizontalPosition: hPos = .Left
.Delete
With wdRng
.Fields.Add Range:=.Duplicate, Type:=wdFieldEmpty, PreserveFormatting:=False, _
Text:="LINK " & StrID & " " & Chr(34) & Replace(strPath & StrNm, "\", "\\") & Chr(34) & " " & _
"6 - EW_RA!R2C17" & " \p"
.End = .End + 1
Set wdShp = .Fields(1).InlineShape.ConvertToShape
End With
With wdShp
.WrapFormat.Type = Fmt: .Height = Hght: .Width = Wdth
.RelativeVerticalPosition = vRel: .Top = vPos
.RelativeHorizontalPosition = hRel: .Left = hPos
End With
End If
End With
Next
.Close True
End With
.Quit
End With
I wrote this macro to generate a textbox with more than one line:
Sub multipleLineTextBox()
Dim Box As Shape
Set Box = ActiveDocument.Shapes.AddTextbox( _
Orientation:=msoTextOrientationHorizontal, _
Left:=50, Top:=50, Width:=200, Height:=200)
Box.Line.Style = msoLineThinThin
Box.Line.Weight = 6
Box.TextFrame.TextRange.Text = "first line" & vbCrLf & "second line"
Box.TextFrame.TextRange.Font.Size = 20
End Sub
The last line edits all the text in the textbox to be size 20.
How can I edit each line separately?
TextRange has a Paragraphs collection. You can loop that or work with each item individually. For example
Dim bxRange As Word.Range
Set bxRange = Bix.TextFrame.TextRange
bxRange.Paragraphs(1).Range.Font.Size = 12
bxRange.Paragraphs(2).Range.Font.Size = 10
Use this:
Sub multipleLineTextBox()
Dim Box As Shape
Set Box = ActiveDocument.Shapes.AddTextbox( _
Orientation:=msoTextOrientationHorizontal, _
Left:=50, Top:=50, Width:=200, Height:=200)
With Box
.Line.Style = msoLineThinThin
.Line.Weight = 6
.TextFrame.TextRange.Text = "first line" & vbCrLf & "second line"
.TextFrame.TextRange.Paragraphs(2).Range.Font.Size = 20
End with
End Sub