Description edit
Richard Suchenwirth 2002-07-28: Microjustification is a typesetting technique where small gaps are inserted between words to make lines fully justified (flush left and right). Donald E. Knuth has done extensive research on the topic (which I didn't have handy over weekend ;-), so here's only my Tk experiments.The text widget does not support full justification, so I use a canvas instead - it can display text at pixel-precise positions, and also produce Postscript. I could not use its text editing features, because then "a space is a space" of font-dependent constant width.In this design, a sort of "mega-item" mjtext is created on the canvas, which first just is a rectangle. The returned handle however accepts text input with $mj insert end.. like a text widget. The words are rendered as separate text items and wrap around, except at newlines. For the resulting "raw lines", the number of missing pixels to flush-right is determined, and the rightmost word is shifted right by that amount, then the others by decreasing amounts to make them approximately evenly distributed.That's all - still a far cry from real DTP (rather like 1970's IBM Composer); but it let me cross another horizon of what's all possible with Tcl/Tk - hyphenation might be the next target..set docu(mjtext) {
insert a good-size chunk of text, here
}
proc mjtext {c x0 y0 x1 y1 args} {
array set opt {-bg white -font {Times 11}}
array set opt $args
set _self mj[$c create rect $x0 $y0 $x1 $y1 \
-fill $opt(-bg) -outline $opt(-bg)]
upvar #0 $_self self
array set self [list x $x0 x0 $x0 y $y0 x1 $x1 y1 $y1 c $c]
set self(-font) $opt(-font)
set self(dy) [font metrics $opt(-font) -linespace]
interp alias {} $_self {} mjtext'do $_self
}
proc mjtext'do {_self cmd cmd2 args} {
upvar #0 $_self self
if {$cmd=="insert" && $cmd2=="end"} {
foreach {text tag} $args {
foreach line [split $text \n] {
set ids {}
foreach word [split $line] {
if {$word==""} continue
set id [$self(c) create text $self(x) $self(y) \
-anchor nw -text $word -font $self(-font)]
foreach {x0 y0 x1 y1} [$self(c) bbox $id] break
if {$x1 > $self(x1)} {
set dx [expr {$self(x0) - $x0}]
$self(c) move $id $dx $self(dy)
foreach {x0 y0 x1 y1} [$self(c) bbox $id] break
mjtext'justify $self(c) $ids $self(x1)
set ids {}
} else {lappend ids $id}
set self(x) [expr {$x1 + 1}]
set self(y) $y0
}
set self(x) $self(x0)
set self(y) [expr {$self(y) + 2 * $self(dy)}]
}
}
} else {error "usage: $self insert end text"}
}
proc mjtext'justify {c ids x1} {
set last [lindex $ids end]
set diff [expr {$x1 - [lindex [$c bbox $last] 2]}]
set step [expr {double($diff)/([llength $ids]-1)}]
for {set i [llength $ids]} {$i>1} {} {
$c move [lindex $ids [incr i -1]] $diff 0
set diff [expr {$diff-$step}]
}
}
#----------- Test:
proc nl2flowtext s {
# turn multiline text into flowtext, \n only on empty lines
regsub -all {\n *\n} $s \x81 s ;# dummy char
string map {\n " " \x81 \n} $s
}
pack [canvas .c -width 400 -height 410] -expand 1
set mj [mjtext .c 5 5 390 400]
$mj insert end [nl2flowtext $docu(mjtext)]Disclaimer: Due to differing font metrics, Postscript output is produced, but not exactly flush right - mildly ragged instead. Hm - to reinvent TeX in a few hours is not that simple ;-)
Donal Fellows has an implementation
for text widgets which uses blank images as spacers:
