PDA

View Full Version : [SLEEPER:] Getting position of top and bottom of text



neilt17
08-02-2019, 01:48 AM
I have an add-in which inserts images between lines of text. To make space for the images I increase the line spacing in the text. The problem is I haven't found a way which can consistently and accurately work out where the top and bottom of the text ends up after changing the line spacing.

For example, if you increase the space using SpaceWithin (or manually), space is actually added above and below the lines of text (more above than below), except for the last line where space is only added above. Any BoundTop and BoundHeight values that I can find refer to the line and not to the characters within it.

How can I find the top and height measurements for characters within the lines?

This demonstrates the issue:


Public Sub PositionTest()
Dim shTextBox As Shape
Dim i As Long
With ActivePresentation.Slides(1)
Set shTextBox = .Shapes.AddTextbox(Orientation:=msoTextOrientationHorizontal, Left:=300, Top:=100, Width:=300, Height:=100)
End With
With shTextBox.TextFrame.TextRange
.Text = "Line number: 1" & vbCrLf & "Line number: 2" & vbCrLf & "Line number: 3"
End With
With shTextBox.TextFrame.TextRange
For i = 1 To .Lines.Count
With .Lines(i, 1)
Debug.Print .Text & " Top: " & .BoundTop & " Height: " & .BoundHeight
End With
Next i
End With
Debug.Print "New spacing"
With shTextBox.TextFrame.TextRange
.Lines.ParagraphFormat.LineRuleWithin = msoTrue
.Lines.ParagraphFormat.SpaceWithin = 3
End With
With shTextBox.TextFrame.TextRange
For i = 1 To .Lines.Count
With .Lines(i, 1)
Debug.Print .Text & " Top: " & .BoundTop & " Height: " & .BoundHeight
End With
Next i
End With
End Sub

And this is the output that that produces:


Line number: 1
Top: 103.6 Height: 21.6
Line number: 2
Top: 125.2 Height: 21.6
Line number: 3 Top: 146.8 Height: 21.6
New spacing
Line number: 1
Top: 103.6 Height: 64.8
Line number: 2
Top: 168.4 Height: 64.8
Line number: 3 Top: 233.2 Height: 53.35

If I try to refer to any individual characters - e.g. .Characters(n).BoundTop - the output is the same as for the line in which that character is in.

neilt17
08-05-2019, 08:27 AM
The solution I have come up with involves creating a temporary test textbox for each line in the input textbox, and then inspecting the heights of this new textbox before and after applying the paragraph formatting used in the input textbox:



Option Explicit

' lngLine - the index of the line to compute TextTop and TextHeight for.
' shTextBox - the text box containing the line.
' dblTextTop - returned value for the top of the text.
' dblTextHeight - returned value for the height of the text.

Public Sub TextPosition(ByVal lngLine As Long, _
ByRef shTextBox As Shape, _
ByRef dblTextTop As Double, _
ByRef dblTextHeight As Double)
Dim shTestTextBox As Shape
Dim dblMaxCharHeight As Double
Dim trLine As TextRange
Set trLine = shTextBox.TextFrame.TextRange.Lines(lngLine)
' The height of the derived textbox before manipulation:
Dim dblBaseBoundHeight As Double
dblMaxCharHeight = MaxCharHeight(trLine)
Dim lngSlideIndex As Long
lngSlideIndex = shTextBox.Parent.SlideIndex
With shTextBox.Parent.Parent.Slides(lngSlideIndex)
' Create a one line high text box from the original text box.
' The height of this before and after paragraph formatting is used to compute where the text will be in relation to the bounding box of the line.
Set shTestTextBox = .Shapes.AddTextbox(Orientation:=msoTextOrientationHorizontal, _
Left:=shTextBox.Left, _
Top:=trLine.BoundTop, _
Width:=shTextBox.Width, _
Height:=trLine.BoundHeight)
End With
shTestTextBox.TextFrame.WordWrap = msoFalse
With shTestTextBox.TextFrame.TextRange
' Any returns at the end of lines (i.e. new paragraphs) must be removed to ensure that the test textbox contains only one line.
.Text = Replace(trLine.Text, Chr(13), "")
.Characters(1, .Characters.Count).Font.Size = dblMaxCharHeight
' Record the bound height of the text box before copying over the paragraph formatting.
dblBaseBoundHeight = .BoundHeight
.ParagraphFormat.LineRuleAfter = trLine.ParagraphFormat.LineRuleAfter
.ParagraphFormat.SpaceAfter = trLine.ParagraphFormat.SpaceAfter
.ParagraphFormat.LineRuleBefore = trLine.ParagraphFormat.LineRuleBefore
.ParagraphFormat.SpaceBefore = trLine.ParagraphFormat.SpaceBefore
.ParagraphFormat.LineRuleWithin = trLine.ParagraphFormat.LineRuleWithin
.ParagraphFormat.SpaceWithin = trLine.ParagraphFormat.SpaceWithin
End With
dblTextHeight = dblBaseBoundHeight
If lngLine = shTextBox.TextFrame.TextRange.Lines.Count Then
' Space is never added after the last line in a textbox, so the top of the text is the bottom of the original bound box less the _
bound height of the (one line high) test textbox.
dblTextTop = (trLine.BoundTop + trLine.BoundHeight) - dblBaseBoundHeight
Else
' For the other lines, also allow for the extra space that is inserted below a line with paragraph formatting.
dblTextTop = (trLine.BoundTop + trLine.BoundHeight) - _
dblBaseBoundHeight - (trLine.BoundHeight - shTestTextBox.TextFrame.TextRange.BoundHeight)
End If
shTestTextBox.Delete
End Sub

Public Function MaxCharHeight(ByRef trLine As TextRange) As Double
Dim i As Long
Dim dblMaxHeight As Double
For i = 1 To trLine.Characters.Count
If trLine.Characters(i, 1).Font.Size > dblMaxHeight Then
dblMaxHeight = trLine.Characters(i, 1).Font.Size
End If
Next i
MaxCharHeight = dblMaxHeight
End Function

neilt17
08-10-2019, 12:21 AM
I've posted an updated version of this question on StackOverflow - https://stackoverflow.com/questions/57397272/getting-the-top-and-height-of-text-within-formatted-paragraphs