Centering text lines top and bottom in a canvas

  1. 6 months ago
    Edited 6 months ago

    I can vertically center 1 or 2 lines of text in a canvas pretty accurately, but when it gets to 3 or 4 (or 5?) I can't seem to get the right recipe to vertically center it.
    Here is what I'm using. The 3rd line is in the ballpark but not quite right. 4 tends to run off the top of the canvas.

      if ubound(numLines) = 0 then   // there is only 1 line
        stringHeight = (g.height + g.TextAscent)/2
      elseif ubound(numLines) = 1  then    // there is 2 lines
        stringHeight = (g.height + g.TextAscent)/2 -  g.TextAscent
      elseif ubound(numLines) = 2  then   // there is 3 lines
        stringHeight =  (g.height + g.TextAscent)/2 - 1.5 * g.TextAscent
      end if
      
  2. Tim P

    Jan 8 Pre-Release Testers Rochester, NY

    TextAscent isn't the correct property, it measures from the baseline to the top of the tallest character. StringHeight measures the whole height of the string. Also consider that each line might be a different height.

    Perhaps this might be closer? (I haven't tested it, post-editor code)

    dim dHeight as Double
    
    for i as Integer = 0 to numLines.Ubound
      dim sThis as String = numLines(i)
      dHeight = dHeight + g.StringHeight(sThis, g.Width)
    
    next
    
    // dHeight is now the height of all the lines.
  3. jim m

    Jan 8 Pre-Release Testers, Xojo Pro piDog.com

    You could join and then measure the whole string to find the height and then draw based on the top+textascent...

    Dim lines() As String=Array("line1","LINE2","----","line3")
    
    Dim joined As String=Join(lines,Chr(13))
    
    Dim firstlineY As Double=g.Height/2-g.StringHeight(joined,g.Width)/2+g.TextAscent
    
    g.DrawString(joined,g.Width/2-g.StringWidth(joined)/2,firstlineY)
    

    and if you also wanted to center each line horizontally...

    For i As Integer=0 To lines.Ubound
      g.DrawString(lines(i) , g.Width/2-g.StringWidth(lines(i))/2 ,  firstlineY+g.TextHeight*i)
    next
  4. Tim,

    forgot to note that "numLines" is a string array populated by splitting "myText" by a marker | where the lines are to break.
    so I use numLines = numLines = split(myText,"|") just to figure out how many lines there are. So I really need to join the lines first. they're already joined in myText.
    Would it be dHeight = dHeight + g.StringHeight(myText, g.width) and that would get me the proper line height? Once I do get the proper line height, how do I properly calculate by the number of lines in myText? Is it just a straight dHeight * numLines ?

    Jim,

    That code might come in handy when I eventually allow each line it to be centered as well. Thanks!
    Was also wondering if it were possible to auto-wrap text when painting into a canvas.

  5. Dave S

    Jan 8 San Diego, California USA
    Edited 6 months ago

    If all the text is the same font and font size then

    dHeight=g.TextHeight * numLines

    and if the is the case then to center should be something like this (off the top of my head)

    dHeight=g.TextHeight * (eachLine.ubound+1) // assume you split the text
    yTop = yCenter - (dHeight/2) + g.textAscent // where YCenter is the point you want to center the text on Vertically
    for i=0 to eachLine.ubound // assume you split the text
    g.drawstring eachLine(i),x,yTop
    yTop=yTop+g.textHeight
    next i
  6. thanks. I'll play around with that. getting centered vertically will be the key.

  7. Tim P

    Jan 8 Pre-Release Testers Rochester, NY

    I'm going to break out your questions one at a time just so it's easier to answer.

    @Patrick Besong Would it be dHeight = dHeight + g.StringHeight(myText, g.width) and that would get me the proper line height?

    The original calculation I offered adds the individual line heights (they can be different depending on the characters in the line!) so that at the end of the for loop dHeight is the total correct height for the block of myText.

    @Patrick Besong Once I do get the proper line height, how do I properly calculate by the number of lines in myText?

    The example I wrote calculates the whole block of text height. I had written my example assuming numLines was your array of strings to write. If you want each individual line, draw the string after the dHeight is calculated for that line within the loop.

    @Patrick Besong Is it just a straight dHeight * numLines ?

    No, you can't assume that each line height is the same. A line's height is affected by the characters in the line. Letters like y and j extend below the baseline which is why you can't use TextAscent for this calculation. Letters like h and i are taller than n, m, c, v. I'm not a languages expert, but I wouldn't trust that a line would always have letters that are tall.
    ( @Michel B is probably dying inside reading my butchering of this).

    I would recommend calculating and drawing each line individually. This will make it easier for you to implement horizontal alignment later down the road. Insert a DrawString underneath the dHeight calculation in my example loop to get started down that path :)

  8. Dave S

    Jan 8 San Diego, California USA

    @Tim P No, you can't assume that each line height is the same.

    if the TextFont is the same the Height is the same.... TextHeight is from top to bottom (below baseline), TextAscent is from top to baseline.... but the metrics of HEIGHT are constant which is why there is a TextHeight property. Width however is a totally different story depending on the font

  9. Tim P

    Jan 8 Pre-Release Testers Rochester, NY

    Ah, I stand corrected! Thanks for the update :)

or Sign Up to reply!