# Using a VBA macro to position a graphic in a table cell



## sapod (Apr 5, 2012)

I have a VBA macro that creates a table, in each cell of which is a photo with text wrapped around it ('square' wrapping).

I would like to develop the macro to control where the photo sits within the cell. In normal manual use of WORD 2007 I would use Text Wrapping | More Layout Options | Picture Position.

However 'Record macro' won't follow this manual option, and I can't find instructions in Macro Help for doing this.

Can anyone point me in the right direction?

I'm using Vista and Office 2007.


----------



## macropod (Apr 11, 2008)

Hi sapod,

There are many things the macro recorder doen't record. That's one of them.

In any event, there are significant difficulties doing this with a macro if the photo's anchor isn't within the cell it overlays, which is quite common. Without that, a great deal of effort (both in terms of writing the code and in the code's execution) has to be expended trying to work out which shapes belong to which cells - all via getting the positions of both the horizontal & vertical cell borders and the picture tops & sides relative to the page - before any repositioning can be done.


----------



## sapod (Apr 5, 2012)

Thanks Paul. 

It starts off as a straightforward InlineShape located as the 'first character' within the table cell (see code below) and it is then converted into a wrappable shape, so I imagine the anchor is located at 'first character' position? 

Perhaps worth saying that my VBA knowledge is very patchy (I'm rather geriatric and I've never really made it from the good old procedural languages!) so I'm writing VBA by a mixture of 'Macro by example', useful Help examples, and a lot of trial and error, rather than really understanding the details of the code. 

However my macro already works pretty well - the image is always top left, and the text wraps well within the cell to its right and below it, but the position of the image varies slightly, it would be tidier if I could control it better, and if I set the borders manually I can get it to do what I want. But I can't see how to get the macro to do what the manual facilities do. 

The relevant code is as follows, and you will see that the image is an InlineShape located as the first 'character' in the cell right up to the last command in the code. The 'w' and 'h' bit in the middle is just scaling the image differently depending on whether it is portrait or landscape format:


'Set-up and fill a normal table cell with photo, text, etc.:

```
'Create 1-cell table with row height set to 12 cms.
Continue: ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=1, NumColumns:=1, _
DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:=wdAutoFitFixed
Selection.Tables(1).Select
With Selection.Rows
  .HeightRule = wdRowHeightAtLeast
  .Height = CentimetersToPoints(7)
  .AllowBreakAcrossPages = False
End With

With Selection.Tables(1)
  .TopPadding = CentimetersToPoints(0)
  .BottomPadding = CentimetersToPoints(0)
  .LeftPadding = CentimetersToPoints(0)
  .RightPadding = CentimetersToPoints(0)
  .Spacing = 0
End With

Selection.Cells.VerticalAlignment = wdCellAlignVerticalBottom

Selection.SelectCell

'Retrieve picture, size to maximum width or height whichever is greater, and convert inline mode to wrapped mode
Selection.Style = ActiveDocument.Styles("Normal") ' To ensure that picture is not a covert 'Heading'
Selection.InlineShapes.AddPicture FileName:=imageaddress, LinkToFile:=False, SaveWithDocument:=True
w = ActiveDocument.InlineShapes(1).Width
h = ActiveDocument.InlineShapes(1).Height
If w > h Then GoTo Landscape
Portrait: ActiveDocument.InlineShapes(1).Width = w * (250 / h)
ActiveDocument.InlineShapes(1).Height = 250
GoTo Convert
Landscape: ActiveDocument.InlineShapes(1).Height = h * (250 / w)
ActiveDocument.InlineShapes(1).Width = 250
Convert: ActiveDocument.InlineShapes(1).ConvertToShape.WrapFormat.Type = wdWrapSquare
```


----------



## macropod (Apr 11, 2008)

Your code appears intended to insert the picture at the bottom of the created table's cell. Given the possibility of the picture being as high as 250pt, a square picture or a picture in portrait orientation could exceed the minimum cell height by as much as 90pt. Even a picture in landscape orientation will do so if the H/W aspect ratio is more than 1/1.26. Perhaps that's what the issue is.

The following code does the same as your's more efficiently:

```
Dim oTbl As Table, oShp As Shape
... your other code
Continue:
With Selection
  'Create 1-cell table with row height set to 12 cms.
  Set oTbl = ActiveDocument.Tables.Add(Range:=.Range, NumRows:=1, NumColumns:=1, _
    DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:=wdAutoFitFixed)
  .Range.Style = "Normal" ' To ensure that picture is not a covert 'Heading'
  'Define the table's parameters
  With oTbl
    With .Rows
      .HeightRule = wdRowHeightAtLeast
      .Height = CentimetersToPoints(7)
      .AllowBreakAcrossPages = False
    End With
    .Range.Cells.VerticalAlignment = wdCellAlignVerticalBottom
    .TopPadding = CentimetersToPoints(0)
    .BottomPadding = CentimetersToPoints(0)
    .LeftPadding = CentimetersToPoints(0)
    .RightPadding = CentimetersToPoints(0)
    .Spacing = 0
  End With
  'Insert picture in wrapped mode
  Set oShp = ActiveDocument.Shapes.AddPicture(FileName:=ImageAddress, LinkToFile:=False)
  'Size picture to maximum width & height of 250 points, maintaining aspect ratio
  With oShp
    .LockAspectRatio = True
    .Height = 250
    If .Width > 250 Then .Width = 250
  End With
End With
```
PS: When posting code, please use code tags.


----------



## sapod (Apr 5, 2012)

Thanks, Paul - always lovely to see professionally written code rather than my ragged stuff!

However I still have a small problem. Your code seems to work beautifully, except that the photos (my code is embedded in a loop, so your code happens repeatedly) end up piled up at the start if the document, rather than in each table cell. 

It looks as if:


```
'Insert picture in wrapped mode
      Set oShp = ActiveDocument.Shapes.AddPicture(FileName:=ImageAddress, LinkToFile:=False)
```
needs something extra to get oShp into the cell you have just created. Any suggestions as to what is missing?

Many thanks!


----------



## macropod (Apr 11, 2008)

Hi sapod,

Try:

```
Dim oTbl As Table, oShp As Shape, SngTop As Single, SngLeft As Single, Rng As Range
Continue:
With Selection
  Set Rng = .Range
  SngTop = .Information(wdVerticalPositionRelativeToTextBoundary)
  SngLeft = .Information(wdHorizontalPositionRelativeToTextBoundary)
  'Create 1-cell table with row height set to 12 cms.
  Set oTbl = ActiveDocument.Tables.Add(Range:=.Range, NumRows:=1, NumColumns:=1, _
    DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:=wdAutoFitFixed)
  .Range.Style = "Normal" ' To ensure that picture is not a covert 'Heading'
  'Define the table's parameters
  With oTbl
    With .Rows
      .HeightRule = wdRowHeightAtLeast
      .Height = CentimetersToPoints(7)
      .AllowBreakAcrossPages = False
    End With
    .Range.Cells.VerticalAlignment = wdCellAlignVerticalBottom
    .TopPadding = CentimetersToPoints(0)
    .BottomPadding = CentimetersToPoints(0)
    .LeftPadding = CentimetersToPoints(0)
    .RightPadding = CentimetersToPoints(0)
    .Spacing = 0
  End With
  'Insert picture in wrapped mode
  Set oShp = ActiveDocument.Shapes.AddPicture(FileName:=ImageAddress, LinkToFile:=False)
  'Size picture to maximum width & height of 250 points, maintaining aspect ratio
  Rng.Collapse wdCollapseStart
  With oShp
    .LockAspectRatio = True
    .Height = 250
    If .Width > 250 Then .Width = 250
    'Reposition the shape and lock into position
    If Rng.Start - .Anchor.Start <> 0 Then
      .Anchor.Move wdCharacter, Rng.Start - .Anchor.Start
    End If
    .LockAnchor = True
    .Top = SngTop
    .Left = SngLeft
  End With
End With
```
You'll note that, except for the initial reference to a selection, which is needed for integration with the rest of your code, my code doesn't use selections. In all likelihood, neither should your's. Working with range objects is far more efficient.


----------



## sapod (Apr 5, 2012)

Thanks Paul. We are still not quite there - I see why you warned me about anchor problems!

I should say that the project is a photo-album, with photo and text in each cell, and new cells added one after another each time round the  loop, so we are dealing with a one-column table that grows ad lib over many pages. Usually you get 3 cells per A4 'page', but the page break is put in by Word, not by the macro. 

oShp is still ending up on pg 1 of the document rather than the start of the cell you have just created, but now we now get the first image of my series at the top of pg 1, and then three piles of images one under the other below it - I think that without the isolated top image, they would be in the correct vertical position on the page for the cell they are aiming at, but all on the first page of the document, rather than the page they should be on! We need some way to keep it 'relative to current page'. 

So it looks as if your latest chunk of code is setting the anchors relative to the start of the document rather than the start of the current page.

Any thoughts? I'm becoming very aware of using up a lot your time, so please feel free to say: 'enough' if you want me to carry on on my own! The help you have given so far is already wonderful!

sapod


----------



## macropod (Apr 11, 2008)

Hi sapod,

Without knowing what the rest of your code is doing, it's difficult to say what the issue is. Perhaps the safest approach at this stage is to simply insert the pic as an inlineshape first, then convert to a shape as you originally did. Try:

```
Dim oTbl As Table, oShp As Shape, SngTop As Single, SngLeft As Single, Rng As Range
Continue:
With Selection
  Set Rng = .Range
  SngTop = .Information(wdVerticalPositionRelativeToTextBoundary)
  SngLeft = .Information(wdHorizontalPositionRelativeToTextBoundary)
  'Create 1-cell table with row height set to 12 cms.
  Set oTbl = ActiveDocument.Tables.Add(Range:=.Range, NumRows:=1, NumColumns:=1, _
    DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:=wdAutoFitFixed)
  .Range.Style = "Normal" ' To ensure that picture is not a covert 'Heading'
  'Define the table's parameters
  With oTbl
    With .Rows
      .HeightRule = wdRowHeightAtLeast
      .Height = CentimetersToPoints(7)
      .AllowBreakAcrossPages = False
    End With
    .Range.Cells.VerticalAlignment = wdCellAlignVerticalBottom
    .TopPadding = CentimetersToPoints(0)
    .BottomPadding = CentimetersToPoints(0)
    .LeftPadding = CentimetersToPoints(0)
    .RightPadding = CentimetersToPoints(0)
    .Spacing = 0
  End With
  'Insert the picture
  ActiveDocument.InlineShapes.AddPicture FileName:=ImageAddress, LinkToFile:=False, Range:=Rng
  Rng.End = Rng.End + 1
  'Convert picture to wrapped mode
  Set oShp = Rng.InlineShapes(1).ConvertToShape
  'Size picture to maximum width & height of 250 points, maintaining aspect ratio
  With oShp
    .LockAspectRatio = True
    .Height = 250
    If .Width > 250 Then .Width = 250
    'Lock the shape into position
    .LockAnchor = True
    .Top = SngTop
    .Left = SngLeft
  End With
End With
```


----------



## sapod (Apr 5, 2012)

Thanks Paul - brain has had it for tonight, so I'll have a go at it tomorrow!

Once again, many thanks for your care and support!!!









sapod


----------

