_text.tcl

Go to the documentation of this file.
00001 /*  -*- tcl -*-*/
00002 /* */
00003 /*  _text.tcl -- Core support for text engines.*/
00004 
00005 
00006 /* */
00007 
00008 if {0} {
00009     catch {rename ret  proc__} msg ; puts_stderr >>$msg
00010     proc__ proc (type cmd , type argl , type body) {
00011     puts_stderr "proc $cmd $argl ..."
00012     uplevel [list proc__ $cmd $argl $body]
00013     }
00014 }
00015 
00016 dt_package textutil::string ; /*  for adjust*/
00017 dt_package textutil::repeat
00018 dt_package textutil::adjust
00019 
00020 if {0} {
00021     puts_stderr ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00022     rename ret  {}
00023     rename proc__ proc
00024     puts_stderr ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00025 }
00026 
00027 
00028 ################################################################
00029 # Formatting constants ... Might be engine variables in the future.
00030 
00031 global lmarginIncrement ; set lmarginIncrement 4
00032 global rmarginThreshold ; set rmarginThreshold 20
00033 global bulleting        ; set bulleting        (* - # @ ~ %)
00034 global enumeration      ; set enumeration      {[%] (%) <%>}
00035 
00036 ret  Bullet (type ivar) {
00037     global bulleting ; upvar $ivar i
00038     set res [lindex $bulleting $i]
00039     set i [expr {($i + 1) % [llength $bulleting]}]
00040     return $res
00041 }
00042 
00043 ret  EnumBullet (type ivar) {
00044     global enumeration ; upvar $ivar i
00045     set res [lindex $enumeration $i]
00046     set i [expr {($i + 1) % [llength $enumeration]}]
00047     return $res
00048 }
00049 
00050 /* */
00051 
00052 /* */
00053 /*  The engine maintains several data structures per document and pass.*/
00054 /*  Most important is an internal representation of the text better*/
00055 /*  suited to perform the final layouting, the display list. Elements of*/
00056 /*  the display list are lists containing 2 elements, an operation, and*/
00057 /*  its arguments, in this order. The arguments are a list again, its*/
00058 /*  contents are specific to the operation.*/
00059 /* */
00060 /*  The operations are:*/
00061 /* */
00062 /*  - SECT  Section.    Title.*/
00063 /*  - SUBSECT     Subsection. Title.*/
00064 /*  - PARA  Paragraph.  Environment reference and text.*/
00065 /* */
00066 /*  The PARA operation is the workhorse of the engine, dooing all the*/
00067 /*  formatting, using the information in an "environment" as the guide*/
00068 /*  for doing so. The environments themselves are generated during the*/
00069 /*  second pass through the contents. They contain the information about*/
00070 /*  nesting (i.e. indentation), bulleting and the like.*/
00071 /* */
00072 
00073 global cmds ;  cmds =  [list]   ; /*  Display list*/
00074 global pEnv ; array  pEnv =  {} ; /*  Defined paragraph environments (bulleting, indentation, other).*/
00075 global para ;  para =  ""       ; /*  Text buffer for paragraphs.*/
00076 
00077 global nextId     ;        nextId =      0      ; /*  Counter for environment generation.*/
00078 global currentId  ;        currentId =   {}     ; /*  Id of current environment in 'pEnv'*/
00079 global currentEnv ; array  currentEnv =  {}     ; /*  Current environment, expanded form.*/
00080 global contexts   ;        contexts =    [list] ; /*  Stack of saved environments.*/
00081 global off        ;  off =    1                 ; /*  Supression of plain text in some places.*/
00082 
00083 /* */
00084 /*  Management of the current context.*/
00085 
00086 ret  Text  (type text)    {global para ; append para $text ; return}
00087 ret  Store (type op , type args) {global cmds ; lappend cmds [list $op $args] ; return}
00088 ret  Off   ()        {global off ; set off 1 ; return}
00089 ret  On    ()        {global off para ; set off 0 ; set para "" ; return}
00090 ret  IsOff ()        {global off ; return [expr {$off == 1}]}
00091 
00092 /*  Debugging ...*/
00093 /* proc Text  {text}    {puts_stderr "TXT \{$text\}"; global para; append para $text ; return}*/
00094 /* proc Store {op args} {puts_stderr "STO $op $args"; global cmds; lappend cmds [list $op $args]; return}*/
00095 /* proc Off   {}        {puts_stderr OFF ; global off ; set off 1 ; return}*/
00096 /* proc On    {}        {puts_stderr ON_ ; global off para ; set off 0 ; set para "" ; return}*/
00097 
00098 
00099 ret  NewEnv (type name , type script) {
00100     global currentId  nextId currentEnv
00101 
00102     #puts_stderr "NewEnv ($name)"
00103 
00104     set    parentId  $currentId
00105     set    currentId $nextId
00106     incr              nextId
00107 
00108     append currentEnv(NAME) -$parentId-$name
00109     set currentEnv(parent) $parentId
00110     set currentEnv(id)     $currentId
00111 
00112     # Always squash a verbatim environment inherited from the previous
00113     # environment ...
00114     catch {unset currentEnv(verbenv)}
00115 
00116     uplevel $script
00117     SaveEnv
00118     return $currentId
00119 }
00120 
00121 /* */
00122 
00123 ret  TextInitialize () {
00124     global off  ; set off 1
00125     global cmds ; set cmds [list]   ; # Display list
00126     global pEnv ; array set pEnv {} ; # Defined paragraph environments (bulleting, indentation, other).
00127     global para ; set para ""       ; # Text buffer for paragraphs.
00128 
00129     global nextId     ; set       nextId     0      ; # Counter for environment generation.
00130     global currentId  ; set       currentId  {}     ; # Id of current environment in 'pEnv'
00131     global currentEnv ; array set currentEnv {}     ; # Current environment, expanded form.
00132     global contexts   ; set       contexts   [list] ; # Stack of saved environments.
00133 
00134     # lmargin  = location of left margin for text.
00135     # prefix   = prefix string to use for all lines.
00136     # wspfx    = whitespace prefix for all but the first line
00137     # listtype = type of list, if any
00138     # bullet   = bullet to use for unordered, bullet template for ordered.
00139     # verbatim = flag if verbatim formatting requested.
00140     # next     = if present the environment to use after closing the paragraph using this one.
00141 
00142     NewEnv Base {
00143     array set currentEnv {
00144         lmargin     0
00145         prefix      {}
00146         wspfx       {}
00147         listtype    {}
00148         bullet      {}
00149         verbatim    0
00150         bulleting   0
00151         enumeration 0
00152     }
00153     }
00154     return
00155 }
00156 
00157 /* */
00158 
00159 ret  Section    (type name) {Store SECT    $name ; return}
00160 ret  Subsection (type name) {Store SUBSECT $name ; return}
00161 
00162 ret  CloseParagraph (optional id ={)} {
00163     global para currentId
00164     if {$para != {}} {
00165     if {$id == {}} { id =  $currentId}
00166     Store PARA $id $para
00167     /* puts_stderr "CloseParagraph $id"*/
00168     }
00169      para =  ""
00170     return
00171 } 
00172 
00173 ret  SaveContext () {
00174     global  contexts  currentId
00175     lappend contexts $currentId
00176 
00177     #global currentEnv ; puts_stderr "Save>> $currentId ($currentEnv(NAME))"
00178     return
00179 }
00180 
00181 ret  RestoreContext () {
00182     global                contexts
00183     SetContext   [lindex $contexts end]
00184     set contexts [lrange $contexts 0 end-1]
00185 
00186     #global currentId currentEnv ; puts_stderr "<<Restored $currentId ($currentEnv(NAME))"
00187     return
00188 }
00189 
00190 ret  SetContext (type id) {
00191     global    currentId currentEnv pEnv
00192     set       currentId $id
00193 
00194     # Ensure that array is clean before setting hte new block of
00195     # information.
00196     unset     currentEnv
00197     array set currentEnv $pEnv($currentId)
00198 
00199     #puts_stderr "--Set $currentId ($currentEnv(NAME))"
00200     return
00201 }
00202 
00203 ret  SaveEnv () {
00204     global pEnv  currentId             currentEnv
00205     set    pEnv($currentId) [array get currentEnv]
00206     return
00207 }
00208 
00209 /* */
00210 
00211 ret  NewVerbatim () {
00212     global currentEnv
00213     return [NewEnv Verbatim {set currentEnv(verbatim) 1}]
00214 }
00215 
00216 ret  Verbatim () {
00217     global currentEnv
00218     if {![info exists currentEnv(verbenv)]} {
00219     SaveContext
00220     set verb [NewVerbatim]
00221     RestoreContext
00222 
00223     # Remember verbatim mode in the base environment
00224     set currentEnv(verbenv) $verb
00225     SaveEnv
00226     }
00227     return $currentEnv(verbenv)
00228 }
00229 
00230 /* */
00231 
00232 ret  text_plain_text (type text) {
00233     #puts_stderr "<<text_plain_text>>"
00234 
00235     if  {[IsOff]} {return}
00236 
00237     # Note: Whenever we get plain text it is possible that a macro for
00238     # visual markup actually generated output before the expander got
00239     # to the current text. This output was captured by the expander in
00240     # its current context. Given the current organization of the
00241     # engine we have to retrieve this formatted text from the expander
00242     # or it will be lost. This is the purpose of the 'ctopandclear',
00243     # which retrieves the data and also clears the capture buffer. The
00244     # latter to prevent us from retrieving it again later, after the
00245     # next macro added more data.
00246 
00247     set text [ex_ctopandclear]$text
00248 
00249     # ... TODO ... Handling of example => verbatim
00250 
00251     if {[string length [string trim $text]] == 0} return
00252 
00253     Text $text
00254     return
00255 }
00256 
00257 /* */
00258 
00259 ret  text_postprocess (type text) {
00260 
00261     #puts_stderr XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
00262     #puts_stderr <<$text>>
00263     #puts_stderr XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
00264 
00265     global cmds
00266     # The argument is not relevant. Access the display list, perform
00267     # the final layouting and return its result.
00268 
00269     set linebuffer [list]
00270     array set state {lmargin 0 rmargin 0}
00271     foreach cmd $cmds {
00272     foreach {op arguments} $cmd break
00273     $op $arguments
00274     }
00275 
00276     #puts_stderr XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
00277 
00278     return [join $linebuffer \n]
00279 }
00280 
00281 
00282 ret  SECT (type text) {
00283     upvar linebuffer linebuffer
00284 
00285     # text is actually the list of arguments, having one element, the text.
00286     set text [lindex $text 0]
00287     #puts_stderr "SECT $text"
00288     #puts_stderr ""
00289 
00290     # Write section title, underline it
00291 
00292     lappend linebuffer ""
00293     lappend linebuffer $text
00294     lappend linebuffer [textutil::repeat::strRepeat = [string length $text]]
00295     return
00296 }
00297 
00298 ret  SUBSECT (type text) {
00299     upvar linebuffer linebuffer
00300 
00301     # text is actually the list of arguments, having one element, the text.
00302     set text [lindex $text 0]
00303     #puts_stderr "SUBSECT $text"
00304     #puts_stderr ""
00305 
00306     # Write subsection title, underline it (with less emphasis)
00307 
00308     lappend linebuffer ""
00309     lappend linebuffer $text
00310     lappend linebuffer [textutil::repeat::strRepeat - [string length $text]]
00311     return
00312 }
00313 
00314 ret  PARA (type arguments) {
00315     global pEnv
00316     upvar linebuffer linebuffer
00317 
00318     foreach {env text} $arguments break
00319     array set para $pEnv($env)
00320 
00321     #puts_stderr "PARA $env"
00322     #parray_stderr para
00323     #puts_stderr "     \{$text\}"
00324     #puts_stderr ""
00325 
00326     # Use the information in the referenced environment to format the paragraph.
00327 
00328     if {$para(verbatim)} {
00329     set text [textutil::adjust::undent $text]
00330     } else {
00331     # The size is determined through the set left and right margins
00332     # right margin is fixed at 80, left margin is variable. Size
00333     # is at least 20. I.e. when left margin > 60 right margin is
00334     # shifted out to the right.
00335 
00336     set size [expr {80 - $para(lmargin)}]
00337     if {$size < 20} {set size 20}
00338 
00339     set text [textutil::adjust::adjust $text -length $size]
00340     }
00341 
00342     # Now apply prefixes, (ws prefixes bulleting), at last indentation.
00343 
00344     if {[string length $para(prefix)] > 0} {
00345     set text [textutil::adjust::indent $text $para(prefix)]
00346     }
00347 
00348     if {$para(listtype) != {}} {
00349     switch -exact $para(listtype) {
00350         bullet {
00351         # Indent for bullet, but not the first line. This is
00352         # prefixed by the bullet itself.
00353 
00354         set thebullet $para(bullet)
00355         }
00356         enum {
00357         # Handling the enumeration counter. Special case: An
00358         # example as first paragraph in an item has to use the
00359         # counter in environment it is derived from to prevent
00360         # miscounting.
00361 
00362         if {[info exists para(example)]} {
00363             set parent $para(parent)
00364             array set __ $pEnv($parent)
00365             if {![info exists __(counter)]} {
00366             set __(counter) 1
00367             } else {
00368             incr __(counter)
00369             }
00370             set pEnv($parent) [array get __] ; # Save context change ...
00371             set n $__(counter)
00372         } else {
00373             if {![info exists para(counter)]} {
00374             set para(counter) 1
00375             } else {
00376             incr para(counter)
00377             }
00378             set pEnv($env) [array get para] ; # Save context change ...
00379             set n $para(counter)
00380         }
00381 
00382         set thebullet [string map [list % $n] $para(bullet)]
00383         }
00384     }
00385 
00386     set blen [string length $thebullet]
00387     if {$blen >= [string length $para(wspfx)]} {
00388         set text    "$thebullet\n[textutil::adjust::indent $text $para(wspfx)]"
00389     } else {
00390         set fprefix $thebullet[string range $para(wspfx) $blen end]
00391         set text    "${fprefix}[textutil::adjust::indent $text $para(wspfx) 1]"
00392     }
00393     }
00394 
00395     if {$para(lmargin) > 0} {
00396     set text [textutil::adjust::indent $text \
00397               [textutil::repeat::strRepeat " " $para(lmargin)]]
00398     }
00399 
00400     lappend linebuffer ""
00401     lappend linebuffer $text
00402     return
00403 }
00404 
00405 /* */
00406 
00407 ret  strong      (type text) {return *${text}*}
00408 ret  em          (type text) {return _${text}_}
00409 
00410 /* */
00411 
00412 ret  parray_stderr (type a , optional pattern =*) {
00413     upvar 1 $a array
00414     if {![array exists array]} {
00415         error "\"$a\" isn't an array"
00416     }
00417     set maxl 0
00418     foreach name [lsort [array names array $pattern]] {
00419         if {[string length $name] > $maxl} {
00420             set maxl [string length $name]
00421         }
00422     }
00423     set maxl [expr {$maxl + [string length $a] + 2}]
00424     foreach name [lsort [array names array $pattern]] {
00425         set nameString [format %s(%s) $a $name]
00426         puts_stderr "    [format "%-*s = {%s}" $maxl $nameString $array($name)]"
00427     }
00428 }
00429 
00430 /* */
00431 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1