1 //                  ROFF for Multics
  2 //
  3 //  Last modified on 08/29/74 at 19:23:28 by R F Mabee.
  4 //
  5 //  This file contains the following routines for printing text:
  6 //        Text                process a line of text
  7 //      * Fill                make a complete line if possible
  8 //      * TrytoHyphenate      break word if possible
  9 //      * Adjust              print a line, right-adjusted
 10 //        Width               return the width of a character
 11 //        Break               break text, emptying buffers
 12 //        Spacing             prepare to upspace paper
 13 //        Eject               finish printing a page
 14 //      * Center              print a line, centered
 15 //      * Equation            print an equation line
 16 //  Routines marked * are not external.
 17 
 18 //  Copyright (c) 1974 by Massachusetts Institute of Technology and Honeywell Information Systems, Inc.
 19 
 20 //  General permission is granted to copy and use this program, but not to sell it, provided that the above
 21 //  copyright statement is given.  Contact Information Processing Services, MIT, for further information.
 22 //  Please contact R F Mabee at MIT for information on this program and versions for other machines.
 23 
 24 get "runoff_head"   //  Declarations for ROFF.
 25 
 26 external
 27      $(   HyphenateWord = "hyphenate_word_"  $)
 28 
 29 //  Text is called after a line of text has been read into
 30 //        Rawchar[1] ... Rawchar[Nr]
 31 //  It arranges to print the text, as required.
 32 
 33 let Text () be
 34      {    if Nr = 0 do { Break (); Spacing (); Newline (1); return }  //  Blank line produces break and blank output line.
 35           if Rawchar[1] = '*s' do Break ()        //  Line indented is automatic break.
 36           if Eq > 0 do { Equation (); return }    //  Print an equation.
 37           for i = 1 to Nr do Char[Nc + i] := Rawchar[i]     //  Move text to output buffer.
 38           Nc := Nc + Nr
 39           if Ce>0 do { Center (); return }        //  Center a line.
 40           unless Fi do { Break (); return }       //  Fill.
 41 
 42 //  Nothing special is happening, so do the work.
 43 
 44           Fill ()                                 //  Loops while enough text to fill line.
 45 
 46 //  Not enough to fill line, prepare for next to be concatenated.
 47           while Nc > 0 & Char!Nc = '*s' do Nc := Nc - 1
 48           if Nc > 0 do
 49                $(   let x, y = Char!(Nc - 1), Char!Nc
 50                     if y = '.' | y = ':' | y = ';' | y = '!' | y = '?'
 51                               | (y = '"' | y = ')') & (x = '.' | x = '!' | x = '?') do
 52                                    $(   Nc := Nc + 1                  //  Two spaces after period, etc.
 53                                         Char!Nc := '*s'
 54                                    $)
 55                     Nc := Nc + 1                            //  And anyway one.
 56                     Char!Nc := '*s'
 57                $)
 58      }
 59 
 60 and Fill () be                //  Routine to print out as much as will fit on one line.
 61      $(   if Nc < Ll - Un return        //  Not enough to possibly fill line.
 62 
 63           let Ne = 0                    //  Elements (character positions) so far.
 64           let Nc1, Ne1 = 0, 0           //  Characters and elements at previous gap.
 65           let Nco = 0                   //  Characters in undented part.
 66 
 67           let Usable = Ll - Un          //  Remaining elements on line.
 68           and Undent = In - Un          //  Elements at left exempt from adjusting.
 69 
 70           Char!(Nc + 1) := '*s'
 71           for i = 1 to Nc + 1 do
 72                $(   test Char!i = '*s'
 73                     then $(   if Ne le Usable | Ne1 = 0 do Nc1, Ne1 := i, Ne
 74                               if Ne ge Usable do
 75                                    $(   unless Hyphenating & Ne > Usable break
 76                                         let x = TryToHyphenate (Nc1, i, Usable - Ne1)
 77                                         while Nc1 < x do
 78                                              $(   Ne1 := Ne1 + Width (Char!Nc1)
 79                                                   Nc1 := Nc1 + 1
 80                                              $)
 81                                         break
 82                                    $)
 83                               while i le Nc do
 84                                    $(   Ne := Ne + 1                  //  Width ('*s')
 85                                         unless Char!(i + 1) = '*s' break
 86                                         i := i + 1
 87                                    $)
 88                          $)
 89                     or Ne := Ne + Width (Char!i)
 90 
 91                     if Nco = 0 & Ne ge Undent do Nco := i + 1
 92                $)
 93           if Ne < Usable return
 94 
 95 //  Now print a line.
 96           Spacing ()
 97           if Print | Ft do
 98                $(   PrinterIndent ()
 99                     Blank (Un)          //  Leading blanks for indent.
100                     test Ad & Ne1 < Usable
101                     then      test Undent = 0
102                               then Adjust (1, Nc1 - 1, Usable - Ne1)
103                               or   $(   PadLeft := false
104                                         if Nco = 0 | Nco > Nc1 do Nco := Nc1
105                                         for i = 1 to Nco - 1 do WriteChar (Char!i)
106                                         Adjust (Nco, Nc1 - 1, Usable - Ne1)
107                                    $)
108                     or for i = 1 to Nc1 - 1 do WriteChar (Char!i)
109                $)
110           Newline (1)
111 
112 //  Now move unprinted stuff in Char to the left.
113           while Char!(Nc1 + 1) = '*s' & Nc1 < Nc do Nc1 := Nc1 + 1
114           Nc := Nc - Nc1
115           if Nc < 0 do Nc := 0
116           for i = 1 to Nc do Char!i := Char!(Nc1 + i)
117           Un := In
118           Fill ()             //  Print more if possible.
119      $)
120 
121 
122 //  This routine attempts to break a word across lines.
123 //  It calls a user-supplied hyphenation procedure to determine
124 //  where and whether the word can be broken.
125 //  It moves text around in Char to insert a hyphen,
126 //  and returns the offset to the new breaking place.
127 
128 and TryToHyphenate (Begin, End, Space) = valof
129      $(   while Char!Begin = '*s' & Begin le Nc do Begin, Space := Begin + 1, Space - 1
130           let Len = End - Begin
131           if Space < 3 | Len < 4 resultis 0
132           let v, w = vec Maxline, vec Maxline / 4
133           for i = 1 to Len do v!i := Char!(Begin + i - 1)
134           v!0 := Len
135           Packstring (v, w)
136           let h = 0
137           call HyphenateWord (w string, lv Space, lv h)
138           if h le 0 | h ge Len resultis 0
139           for i = Nc + 1 to Begin + h by -1 do Char!(i + 2) := Char!i
140           Char!(Begin + h), Char!(Begin + h + 1) := '-', '*s'
141           Nc := Nc + 2
142           resultis Begin + h + 1
143      $)
144 
145 
146 //  This routine prints a line, with right-adjustment.  It alternates
147 //  between putting extra blanks on the right and on the left.
148 //  It operates on the text between Begin and End in Char.
149 //  Pad is the number of spaces which must be inserted into the line.
150 
151 and Adjust (Begin, End, Pad) be
152      $(   let Gaps = 0
153 //  Find out how many gaps there are with which to stretch line.
154           for i = Begin to End if Char!i = '*s' do
155                $(   if i > Begin do Gaps := Gaps + 1
156                     while Char!(i + 1) = '*s' & i < End do i := i + 1
157                $)
158           let s, k = 0, -1
159           unless Gaps = 0 do
160                $(   s := Pad / Gaps
161                     k := Pad - s * Gaps
162                $)
163 
164           for i = Begin to End do
165                {    WriteChar ( Char[i] )         //  Print next character.
166                     if Char[i + 1] = '*s' & Char[i] ne '*s' & i < End do        //  We have just encountered a gap.
167                          {    Blank (s)           //  Most of the space required.
168                               test PadLeft        //  Where to put the extra space?
169                               then if k > 0 do { WriteChar ('*s'); k := k - 1 }
170                               else test k < Gaps then k := k + 1 or WriteChar ('*s')
171                          }
172                }
173           PadLeft := not PadLeft
174      $)
175 
176 
177 and Width (Char) =  //  How many print positions does Char take?
178           $8040 le Conv!Char le $8176 -* 1,       //  Most characters take 1.
179           Conv!Char = '*b' -* -1,       //  Backspace takes -1.
180           0         //  Anything else takes 0.
181 
182 
183 //  Break in the text, so print out anything already read.
184 
185 and Break () be
186      {    if Fi do Fill ()
187           while Nc > 0 & Char!Nc = '*s' do Nc := Nc - 1
188           if Nc > 0 do
189                $(   Spacing ()          //  Do we need some upspacing?
190                     if Print | Ft do
191                          $(   PrinterIndent ()
192                               Blank (Un)          //  Leading blanks, for indented lines.
193                               for i = 1 to Nc do WriteChar ( Char[i] )          //  Print the line.
194                          $)
195                     Newline (1)         //  and upspace.
196                     Un := In  //  Back to the usual indenting.
197                $)
198           Nc := 0   //  Nothing more to print.
199      }
200 
201 
202 //  This routine is called just before any up-spacing.  It does
203 //  two things for us:
204 //        1. It takes care of double spacing.
205 //        2. If we are about to complete a page, it does head
206 //           and foot printing, and such.
207 
208 and Spacing () be
209      {    if Nl > 0 do        //  Have we printed yet on this page?
210                $(   Newline (MinI (Ms - 1, LinesLeft + Fl))
211                     if LinesLeft ge Ms logor Ft logor Flp do
212                          $(   Flp := false                  //  Clear switch indicating footnote reference.
213                               return
214                          $)
215                     Eject ()            //  Close to bottom, so to work...
216                $)
217 
218           if Ft return
219 
220           if Waitsw & Print do $( Wait (); Waitsw := Stopsw $)
221 
222           OddPage := Np rem 2 ne 0
223 
224           //  Skip lines above the header.
225           if Printersw do Nl := Nl + 3                      //  Defect in printer DIM - can't use first three lines on page.
226           Newline (Ma1 - Nl)
227           for i = 1 to Maxheads do Title ((OddPage -> Oh, Eh)!i)
228 
229           Newline (Ma2)       //  Space below the header.
230           if LinesLeft + Fl le 0 do LinesLeft := 1 - Fl     //  Make sure at least some text appears on page.
231 
232           if Pi = 0 return    //  No lines waiting for picture.
233           //  Now print space required for pictures to be drawn.
234           if Pi ge LinesLeft do
235                $(   Newline (LinesLeft + Fl)
236                     if Pi > Pl * 10 do Pi := Pi rem (Pl * 10)
237                     Pi := Pi - Pl
238                     Flp := false
239                     Spacing ()
240                     return
241                $)
242           Newline (Pi)
243           Pi := 0
244      }
245 
246 and Eject () be     //  Eject paper, first printing footer and footnotes.
247      {    if Ft return                            //  Can happen.
248           unless Fl = 0 do PrintFootnotes ()      //  If there are footnotes to print do it now.
249           if Nl = 0 return    //  Page empty, don't print footers or count page.
250           Newline (NoPaging -> Ma3, Pl - Nl - Ma4 - (OddPage -> Of, Ef)!0)      //  Skip down to footer.
251           for i = Maxheads to 1 by -1 do Title ((OddPage -> Of, Ef)!i)
252           test Printersw & ^ NoPaging
253           then for i = 1 to MultiplePagecount do WriteChar ('*f')
254           or Newline (NoPaging -> Ma4, Pl - Nl)
255           Nl := 0
256           LinesLeft := Pl               //  Soon reset by Spacing.
257           if Fr do Foot := 1            //  Reset footnote counter if required.
258           Np := NNp; NNp := NNp + 1
259           Print := Fp le Np le Lp & Passes le 1
260      }
261 
262 and Center () be              //  Print current line, centered.
263      {    let Ne = 0
264           for i = 1 to Nc do Ne := Ne+Width (Char[i])
265           Spacing ()
266           PrinterIndent ()
267           Blank ( (Ll-In-Ne)/2 + In)
268           for i = 1 to Nc do WriteChar (Char[i])
269           Newline (1)
270           Nc := 0
271           Ce := Ce - 1
272      }
273 
274 //  The next routine prints a line as an equation.  An
275 //  equation is like a title, and is of the form
276 //                  'aaa'bbb'ccc'
277 //  (where ' is the first non-blank character on the line).
278 //  aaa is printed at the left margin, ccc at the right and bbb centered.
279 
280 and Equation () be
281      {    Spacing ()
282           Nrx := 1
283           let v = vec Maxline
284           Title (Readhead (v))
285           Eq := Eq - 1
286      }