Version 7 of string geometry

Updated 2013-11-07 21:21:56 by RFox

Richard Suchenwirth 2013-11-07 - Everything is a string, and every string has its string length. But if we want to display a string in a text widget with tight fitting (i.e. just the right size, not too big, not too small), we need to specify the "geometry" of a string, i.e. its width and height.

If we disregard word wrapping, things are very easy:

 set height [regexp -all \n $str\n]

regexp normally stops at the first match, and returns 1 then (or 0 if there were none). With -all, it returns the number of matches, so we can use it as a character counter.

I have appended an extra newline at the end, because many strings don't have (and in fact don't need) it. Perfectionists may first do string trimright before that. And here's how to calculate the width of a string:

 proc string_width str {
     set w 0
     foreach line [split $str \n] {
        set length [string length $line]
        if {$length > $w} {set w $length}
     }
     return $w
 }

Now for the less trivial, and hence more interesting case of word wrapping. The original author of the string doesn't have to bother for layout, we will.

If the string is N characters long, a simple approximation for height and width would be ceil(sqrt($N)). As characters are typically about double as high than wide, and we want an aspect ratio of about 4:3(in pixels) or 8:3 (in character units), here's a first shot that happens to match the initial requirements:

 set sample "String geometry\n\nThis is a long string meant to display in a\
  Tk text widget. Some lines are very long, and then again there are paragraph\
  breaks.\n\nThe text widget shall wrap words, but be neither too big nor too\
  small, and have a pleasing aspect ratio (say, in pixels, 4:3 or 16:9)."

 set length [string length $sample]
 set w0 [expr round(8./3.*ceil(sqrt($length)))]
 set h0 [expr round(3./8.*ceil(sqrt($length)))+[regexp -all \n $sample]]
 pack [text .t -wrap word -width $w0 -height $h0]
 .t insert end $sample

Should it not match the requirements, increase the height until the bbox of the last character in the string is no longer {}...


RFox - 2013-11-07 21:21:56

Why not get the both in one shot? The proc below returns a list of the width/height of a string without resorting to regexps an appending newlines to anything (at least I think it does ...untested but pretty simple change).

 proc string_geometry str {
     set w 0
     set h 0
     foreach line [split $str \n] {
        incr h
        set length [string length $line]
        if {$length > $w} {set w $length}
     }
     return [list $w $h]
  }