VBA Word Shape location - top left corner - vba

I have written a small function, based on example provided by producer of Stroke Scribe ActiveX object. This is an plugin which allows us to create in Microsoft Word by VBA Macro an QR Code object.
The problem is with setting up a shape location
shp.Left = 0 + LeftMargin
shp.Top = 0 + TopMargin
I would like to put this shape (QR Code) on specific page on the most left top corner. But sometimes shape jumps to previous page (on bottom) or other location (vertical center).
Can you help me recognize a problem and fix it to locate Shape Object every time top left corner?
Code:
Sub QRCodeGenerator(SOP, BookmarkID, Page, TopMargin, LeftMargin)
Dim doc As Document
Set doc = Application.ActiveDocument
For Each sh In doc.Shapes
If sh.Type = msoOLEControlObject Then
If sh.OLEFormat.ProgID = "STROKESCRIBE.StrokeScribeCtrl.1" Then
sh.Delete
End If
End If
Next
With doc.PageSetup
usable_w = .PageWidth
usable_h = .PageHeight
End With
Dim pg As Range
Set pg = doc.GoTo(wdGoToPage, wdGoToRelative, Page)
Dim shp As Shape
Set shp = doc.Shapes.AddOLEControl(ClassType:="STROKESCRIBE.StrokeScribeCtrl.1", Anchor:=pg)
Dim sMyString As String
sMyString = ActiveDocument.Bookmarks(BookmarkID).Range.Text
sMyString = Replace(sMyString, "FORMTEXT ", "")
shp.LockAspectRatio = msoFalse
shp.Height = InchesToPoints(0.6)
shp.Width = shp.Height
shp.Left = 0 + LeftMargin
shp.Top = 0 + TopMargin ' // usable_h - shp.Height * 3 + TopMargin
Dim ss As StrokeScribe
Set ss = shp.OLEFormat.Object
ss.Alphabet = QRCODE 'StrokeScribe will draw a QR code picture
ss.Text = SOP & ";" & sMyString 'Any text you want to encode in the barcode
ss.QrECL = H 'Changes the default error correction level. This can be omitted
ss.QrMinVersion = 3 'Specifies the minimum barcode size. This can be omitted
ss.FontColor = RGB(0, 0, 0)
' ss.UTF8 = True 'Enable this, if you want to encode national characters for smartphones
If ss.Error Then
MsgBox ss.ErrorDescription
End If
End Sub

Word SHAPE objects must be anchored to a Range. The page location of that Range determines on which page the Shape will display. There's nothing you can do to "lock" a Shape to a particular page.
That said, it is possible to dictate that a Shape always appears in the same location on whichever page the anchoring Range lies.
This is always tricky if you add such a Shape before document editing is finished, because editing can move the anchoring paragraph to a different page. It can help to choose a paragraph as the anchor that's unlikely to move, for example perhaps the first paragraph on the page.
Something I did once, long ago, was write a macro that checks the page locations of Shapes before printing or saving. When inserting and positioning the Shape, I give it a Name that includes the page number. Before printing/saving the macro checks the page number in the Shape name with the page on which the Shape is located. If the two don't match, CUT the Shape and PASTE it to a paragraph on the correct page (it remembers its position settings).
The code sample below demonstrates how to name the Shape, lock the anchor to a specific paragraph and position the Shape flush to the top, left corner of the page.
Sub ShapePosTopLeft()
Dim doc As word.Document
Dim shp As word.Shape
Dim rng As word.Range
Set doc = ActiveDocument
Set rng = doc.GoTo(wdGoToPage, wdGoToRelative, Page)
Set rng = rng.Paragraphs(1).Range
Set shp = doc.Shapes.AddOLEControl(ClassType:="STROKESCRIBE.StrokeScribeCtrl.1", Anchor:=rng)
With shp
.Name = "Shape_Page" & Page
.LockAnchor = True
.RelativeHorizontalPosition = wdRelativeHorizontalPositionPage
.RelativeVerticalPosition = wdRelativeVerticalPositionPage
.Left = 0
.Top = 0
End With
End Sub

Related

Powerpoint VBA - adjust positions of chart and axis titles

I have the following code:
Sub StandardiseChart(ByVal control As IRibbonControl)
Dim activeShape As Shape
'Determine Which Shape is Active
If ActiveWindow.Selection.Type = ppSelectionShapes Then
'Loop in case multiples shapes selected
Dim shp As Shape
For Each shp In ActiveWindow.Selection.ShapeRange
Set activeShape = shp ' First shape selected
Exit For
Next
'Now, reformat the selected shape if it is a chart
With activeShape
If .HasChart Then
' Chart title
.Chart.HasTitle = True
With .Chart.ChartTitle
.Left = 0
.Top = 0
End With
' Y axis
With .Chart.Axes(xlValue, xlPrimary)
.HasTitle = True
.AxisTitle.Text = "Placeholder"
.AxisTitle.Left = 0
.AxisTitle.Top = 20
.AxisTitle.Orientation = 0
End With
' Plot Area
With .Chart.PlotArea
.Left = 10
.Top = 50
End With
End If
End With ' activeShape
End If
End Sub
What I'd like it to do is 3 things:
Pin the Chart Title to the top left corner of the entire object (this seems fine)
Set the y-axis title so that there is 20pt of space between it and the chart title (also seems fine)
Create a further 50pt of space between the plot area and the y-axis title (not fine).
No matter what I do (I've tried adjusting the number to 70 rather than 50, and even bigger), I can't seem to adjust the space to achieve (3). Specifically, the plot area doesn't move no matter what I do.
What am I doing wrong?
If you add a dot to the end of Chart.Plotarea, you can see the list of methods. In your case, you're looking for .InsideLeft and .InsideTop, since you want to adjust the inside distance from the chart area:
With .Chart.PlotArea
.InsideLeft = 70
.InsideTop = 70
End With

Wrong positioning of the shape in MS Word using vba

I'm writing a simple code to position my shapes (which are actually pictures) in the document. I want them to be positioned:
horizontally to exactly 0 mm. from the left side of the printable area
vertically to 7 mm. below the paragraph (to which the shape is anchored)
I wrote a simple code:
Selection.ShapeRange.RelativeHorizontalPosition = wdRelativeHorizontalPositionColumn
Selection.ShapeRange.Left = MillimetersToPoints(0)
Selection.ShapeRange.RelativeVerticalPosition = wdRelativeVerticalPositionParagraph
Selection.ShapeRange.Top = MillimetersToPoints(7)
Selection.ShapeRange.WrapFormat.Type = wdWrapTopBottom
For 1 shape on the page it works fine. But if there are more then 1 shape, it somehow "throws" the 2nd shape to the top of the page. It looks like Word anchors it to the 1st paragraph on the page. but it shouldn't. At the same time horizontal positioning is ok.
I would appreciate any help to fix this issue.
My possible solution for this issue will look as follows:
Sub PositShape_3()
Dim I As Integer
If Selection.InlineShapes.Count <> 0 Then
For I = Selection.InlineShapes.Count To 1 Step -1
Selection.InlineShapes(I).ConvertToShape
Next I
End If
Selection.ShapeRange.RelativeHorizontalPosition = wdRelativeHorizontalPositionColumn
Selection.ShapeRange.Left = MillimetersToPoints(0)
Selection.ShapeRange.RelativeVerticalPosition = wdRelativeVerticalPositionLine
Selection.ShapeRange.Top = MillimetersToPoints(7)
Selection.ShapeRange.WrapFormat.Type = wdWrapTopBottom
End Sub
In spite of the fact that the use of wdRelativeVerticalPositionLine solved the problem, it is still interesting why the use of wdRelativeVerticalPositionParagraph has such unexpected unwanted consequences.
Note the use of SELECTION in the code you show us. If you don't change the paragraph selection, then the shapes will always be anchored to the same paragraph. Working with a Selection in Word is tricky; it's much better to work with a more tangible object, such as a specific paragraph.
The following code sample illustrates using paragraph objects to anchor and position successively added Shapes.
Sub insertShapesProgressively()
Dim shp As word.Shape
Dim shpRng As word.ShapeRange
Dim rng As word.Range
Dim iParaCounter As Long
'We want to insert the Shape anchored to three different paragraphs
' on the same page
For i = 7 To 9
Set rng = ActiveDocument.Paragraphs(i).Range
Set shp = ActiveDocument.shapes.AddShape(msoShapeWave, 0, 0, 10, 10, rng)
Set shpRng = rng.ShapeRange
shpRng.RelativeHorizontalPosition = wdRelativeHorizontalPositionColumn
shpRng.Left = MillimetersToPoints(0)
shpRng.RelativeVerticalPosition = wdRelativeVerticalPositionParagraph
shpRng.Top = MillimetersToPoints(7)
shpRng.WrapFormat.Type = wdWrapTopBottom
Next
End Sub

Change Media Object (VBA PowerPoint)

I just want to change the music of a Media Object in PowerPoint using Macros. I have a music in a Slide, but I can't figure out how I can change it to different music. Or is it possible to replace it by a new one but with the same properties...?
I tried playing around with following code but I don't know the rest...
Slide3.Shapes("bg_music").MediaFormat. 'code that I don't know to change it's music/media
You're going to need to delete the existing shape and replace it with a new one, copying the properties as needed. This MSDN article enumerates some (all?) of the MediaFormat properties.
Option Explicit
Sub ReplaceMediaFormat()
Dim sld As Slide
Dim newShp As Shape
Dim shp As Shape
Dim mf As MediaFormat
Dim path As String
Set sld = ActivePresentation.Slides(1) '// Modify as needed
Set shp = sld.Shapes("bg_music")
Set mf = shp.MediaFormat
'// Modify the path for your new media file:
path = "C:\Users\david.zemens\Downloads\2540.mp3"
Set newShp = sld.Shapes.AddMediaObject2(path)
With newShp
.Top = shp.Top
.Left = shp.Left
.Width = shp.Width
.Height = shp.Height
' etc...
End With
' // copy the mediaformat properties as needed
With newShp.MediaFormat
.StartPoint = mf.StartPoint
.EndPoint = mf.EndPoint
.FadeInDuration = mf.FadeInDuration
.FadeOutDuration = mf.FadeOutDuration
.Muted = mf.Muted
.Volume = mf.Volume
' etc...
End With
'// remove the original
shp.Delete
Dim eff As Effect
'// Creates an effect in the timeline which triggers this audio to play when the slideshow begins
Set eff = sld.TimeLine.MainSequence.AddEffect(newShp, msoAnimEffectMediaPlay, trigger:=msoAnimTriggerWithPrevious)
With newShp.AnimationSettings.PlaySettings
.LoopUntilStopped = msoCTrue
.PauseAnimation = msoFalse
.PlayOnEntry = msoCTrue
.RewindMovie = msoCTrue
.StopAfterSlides = 999
.HideWhileNotPlaying = msoTrue
End With
With help from this article, I can get the audio to play automatically by creating an effect (see above Set eff = ...).

How to add picture into Word Header which contains already a table?

I am trying to add a picture (company logo) into a header by code.
This worked fine so far until there showed up some documents which contain a table in the header, which I want to keep there too.
Problem is: my code adds the picture into the first table cell. What i want is that the picture is positioned in the top right corner of the page (with some margin to the page) .. but outside the table.
How do I need to modify my code to do that? I guess the problem is the Range I use:
Set oSec = ActiveDocument.Sections(1)
Set oHeader = oSec.Headers(wdHeaderFooterFirstPage)
Set Rng = oHeader.Range '<<-- Problem here? What to do if there is a table in the header
Set sh = ActiveDocument.shapes.AddPicture(LogoFile, False, True, 0, 0, , , Rng)
With sh
.Height = LogoDimension
.Width = LogoDimension
.WrapFormat.Type = wdWrapTopBottom
.WrapFormat.Side = wdWrapTopBottom
.WrapFormat.DistanceBottom = MillimetersToPoints(10)
.RelativeHorizontalPosition = wdRelativeHorizontalPositionRightMarginArea
.RelativeVerticalPosition = wdRelativeVerticalPositionPage
.Left = MillimetersToPoints(0.5) - LogoDimension
.Top = MillimetersToPoints(11.5)
End With
Thanks for any hints!
I was able to test the scenario on my dev machine and could reproduce the problem. Shape management in Word's Headers/Footers is notorious for being "pliable" - this appears to be another one of those things.
What works is to insert the graphic in the paragraph below the table as an InlineShape object, use the ConvertToShape method, then immediately lock the anchor of the Shape so that moving it doesn't shift the anchor position to the nearest paragraph (table cell).
Sub InsertPicInHeaderOutsideTable()
Dim oSec As word.Section
Dim oHeader As word.HeaderFooter
Dim rng As word.Range
Dim sh As word.Shape, ils As word.InlineShape
Set oSec = ActiveDocument.Sections(1)
Set oHeader = oSec.Headers(wdHeaderFooterFirstPage)
Set rng = oHeader.Range
'**** Add the followign four lines to code in your question ****
rng.Collapse wdCollapseEnd
Set ils = rng.InlineShapes.AddPicture(LogoFile, False, True, rng)
Set sh = ils.ConvertToShape
sh.LockAnchor = True
With sh
'and so on...
after some more playing around with it I found the solution:
.LayoutInCell = False
adding this attribute to the shape will lead to positioning the picture as wanted and not affect the table anymore.
edit: this was not entirely correct it seems. The picture is still added to the table .. just not positioned in the cell anymore. If I delete the table for testing the picture gets deleted automatically with it. So this is not an ideal solution still.
I think still the range used is the problem

Access Shape By Name in Header

My first post so sorry for any errors.
I am trying to replace a image in the header of a document. This will be done a few thousand times.
This line of code:
Set shp = ActiveDocument.Sections(1).Headers.Shapes(strPic)
Causes:
Compile error: Method or data member not found
and
Set shp = ActiveDocument.Shapes("strPic")
Causes:
Run-time error '-2147024809(80070057): The item with the specified name wasn't found.
I have written in multiple ways and cannot get it to assign shp I feel like this is a very simple issue of how I am accessing the header or maybe this whole sub is just wrong. The strPic line before get the name of the shape fine.
Sub imagerepl()
With Selection
' \\ If we selected a InlineShape then convert to Shape
If .Type = wdSelectionInlineShape Then
.InlineShapes(1).ConvertToShape
End If
End With
strPic = Selection.ShapeRange.Name
Set shp = ActiveDocument.Sections(1).Headers.Shapes(strPic)
'Capture properties of exisitng picture such as location and size
With shp
t = .Top
l = .Left
h = .Height
w = .Width
End With
ActiveDocument.StoryRanges(wdPrimaryHeaderStory).ShapeRange(strPic).Delete
Set shp = ActiveDocument.Shapes.AddPicture("C:\Users\tk\Pictures\DFHlogo.png", msoFalse, msoTrue, l, t, w, h)
shp.Name = strPic
shp.ScaleHeight Factor:=1, RelativeToOriginalSize:=msoTrue
shp.ScaleWidth Factor:=1, RelativeToOriginalSize:=msoTrue
End Sub
You can access the header image through the Range.InlineShapes collection. For example, the following routine would delete the first image in the collection and then add new image to the header.
Dim sh As InlineShape
With ThisDocument.Sections(1).Headers(wdHeaderFooterPrimary).Range
' Get the first (possibly only, for you) image in the header...
Set sh = .InlineShapes(1)
' Delete it...
sh.Delete
' Add a new image to the header...
Set sh = .InlineShapes.AddPicture("c:\path\to\my.jpg")
' Set its properties...
sh.Width = 100
sh.Height = 100
...
End With