Trivial Stringwidth question

Basically I need to draw a string (with DrawString) in a canvas and fills the whole width by changing the font size.

So for example the string is “This text fills the entire canvas width” and the canvas width is 500. How to calculate the correct font size to make this happen?
Seems easy, but isn’t. :slight_smile:

This seems to work but I highly doubt this a clean way. Surely there is a much better (and faster) way ? Not?

[code]
Dim width, height As integer
Dim fontSize as integer = 3 // start with a font size of 3 should be good
Dim tekst as string = “This text fills the entire canvas width”
Dim canvasW as integer = 500

for a as integer = 0 to 1000 // 1000 cycles should be enough to determine a correct width
Dim p As New Picture(1, 1)
p.Graphics.TextFont = “Arial”
p.Graphics.TextSize = fontSize
width = p.Graphics.StringWidth(tekst)
height = p.Graphics.StringHeight(tekst,0)
if width > canvasW then exit // exit the for/next loop when width > canvasW
fontSize = fontSize + 1 // increase font by one
next a

dim pic as new Picture(width,height)
pic.Graphics.TextFont = “Arial”
pic.Graphics.TextSize = fontSize
pic.Graphics.DrawString tekst,0,0[/code]

make a loop for fontsize from 1 to nn
set the fontsize for the canvas
use Graphics.StringWidth to have the real width of the string
go on until too wide for the canvas and go back 1 fontsize.

that would work, but slow…
if you need more speed, use a binary search to narrow the test sample.
for a sample from 0 to 1024, you would only need to check 10 or 11 sizes max, not all 1024

Thats how I do it now (see above). But surely not a ‘nice’ way.

[quote=287408:@Dave S]that would work, but slow…
if you need more speed, use a binary search to narrow the test sample.
for a sample from 0 to 1024, you would only need to check 10 or 11 sizes max, not all 1024[/quote]

Ok Dave, you lost me here. :slight_smile: Any simple example?

not “real” code, but an illustration of how a binary search works

low=0
hi =1024
do
   size=(low+hi)/2
   if size is ok then exit do
  if size is too big then 
       hi=size
  else 
      low=size
loop

I would expect the string width to pretty much vary linearly with the font size. So calculate the width at, say, font size 20, then compute the scaled font size.

[code]g.TextSize = 20

dim initialWidth As Double = g.StringWidth(s)

g.TextSize = 20 * g.Width / initialWidth

[/code]

untested. hoping my math is correct :slight_smile:

edit: TextSize is a Single so the above should work. But if the OS or something is restricting font size to only Integers then maybe add a Floor

g.TextSize = Floor( 20 * g.Width / initialWidth )
and/or test 1 size less and 1 size more. At worst that’s 4 string measures. Hmm, and if the TextSize is Integer only, especially if StringWidth is returning Integer, then use a large initial TextSize so you’re more likely to be scaling down.

Tried this but there is always a small error in the width. Apparently fonts are not always sizing linearly.

It should be ‘pretty much’ linear. It’ll get you close then test the neighborhood.

What OS are you on? I ran this on a mac and it fits pretty tight

[code]Sub Paint(g As Graphics, areas() As REALbasic.Rect)
g.ForeColor = &cFFFFFF
g.FillRect 0, 0, g.Width, g.Height

dim s As String = “ladytron velocifero”

g.TextFont = “Monaco”
g.TextSize = 20

dim initialWidth As Double = g.StringWidth( s )

g.TextSize = 20 * g.Width / initialWidth

g.ForeColor = &c000000
g.DrawString( s, 0, g.Height / 2 )

End Sub[/code]

They should if the screen was not made out of pixels. There are rounding errors because each character is intrinsically made of integers (pixels), and the imager uses the best optical rendition rather than precise extrapolation. The smaller the width, the more difficult it gets.