home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyth_os2.zip / python-1.0.2 / Lib / stdwin / formatter.py < prev    next >
Text File  |  1993-12-17  |  6KB  |  208 lines

  1. # A class to help applications that do fancy text formatting.
  2. # You create an instance each time you must redraw the window.
  3. # Set the initial left, top and right coordinates;
  4. # then feed it words, font changes and vertical movements.
  5. #
  6. # This class should eventually be extended to support much fancier
  7. # formatting, along the lines of TeX; for now, a very simple model
  8. # is sufficient.
  9. #
  10. class formatter:
  11.     #
  12.     # Initialize a formatter instance.
  13.     # Pass the window's drawing object, and left, top, right
  14.     # coordinates of the drawing space as arguments.
  15.     #
  16.     def __init__(self, d, left, top, right):
  17.         self.d = d        # Drawing object
  18.         self.left = left    # Left margin
  19.         self.right = right    # Right margin
  20.         self.v = top        # Top of current line
  21.         self.center = 0
  22.         self.justify = 1
  23.         self.setfont('')    # Default font
  24.         self._reset()        # Prepare for new line
  25.     #
  26.     # Reset for start of fresh line.
  27.     #
  28.     def _reset(self):
  29.         self.boxes = []        # Boxes and glue still to be output
  30.         self.sum_width = 0    # Total width of boxes
  31.         self.sum_space = 0    # Total space between boxes
  32.         self.sum_stretch = 0    # Total stretch for space between boxes
  33.         self.max_ascent = 0    # Max ascent of current line
  34.         self.max_descent = 0    # Max descent of current line
  35.         self.avail_width = self.right - self.left
  36.         self.hang_indent = 0
  37.     #
  38.     # Set the current font, and compute some values from it.
  39.     #
  40.     def setfont(self, font):
  41.         self.font = font
  42.         self.d.setfont(font)
  43.         self.font_space = self.d.textwidth(' ')
  44.         self.font_ascent = self.d.baseline()
  45.         self.font_descent = self.d.lineheight() - self.font_ascent
  46.     #
  47.     # Add a word to the list of boxes; first flush if line is full.
  48.     # Space and stretch factors are expressed in fractions
  49.     # of the current font's space width.
  50.     # (Two variations: one without, one with explicit stretch factor.)
  51.     #
  52.     def addword(self, word, spacefactor):
  53.         self.addwordstretch(word, spacefactor, spacefactor)
  54.     #
  55.     def addwordstretch(self, word, spacefactor, stretchfactor):
  56.         width = self.d.textwidth(word)
  57.         if width > self.avail_width:
  58.             self._flush(1)
  59.         space = int(float(self.font_space) * float(spacefactor))
  60.         stretch = int(float(self.font_space) * float(stretchfactor))
  61.         box = (self.font, word, width, space, stretch)
  62.         self.boxes.append(box)
  63.         self.sum_width = self.sum_width + width
  64.         self.sum_space = self.sum_space + space
  65.         self.sum_stretch = self.sum_stretch + stretch
  66.         self.max_ascent = max(self.font_ascent, self.max_ascent)
  67.         self.max_descent = max(self.font_descent, self.max_descent)
  68.         self.avail_width = self.avail_width - width - space
  69.     #
  70.     # Flush current line and start a new one.
  71.     # Flushing twice is harmless (i.e. does not introduce a blank line).
  72.     # (Two versions: the internal one has a parameter for justification.)
  73.     #
  74.     def flush(self):
  75.         self._flush(0)
  76.     #
  77.     def _flush(self, justify):
  78.         if not self.boxes:
  79.             return
  80.         #
  81.         # Compute amount of stretch needed.
  82.         #
  83.         if justify and self.justify or self.center:
  84.             #
  85.             # Compute extra space to fill;
  86.             # this is avail_width plus glue from last box.
  87.             # Also compute available stretch.
  88.             #
  89.             last_box = self.boxes[len(self.boxes)-1]
  90.             font, word, width, space, stretch = last_box
  91.             tot_extra = self.avail_width + space
  92.             tot_stretch = self.sum_stretch - stretch
  93.         else:
  94.             tot_extra = tot_stretch = 0
  95.         #
  96.         # Output the boxes.
  97.         #
  98.         baseline = self.v + self.max_ascent
  99.         h = self.left + self.hang_indent
  100.         if self.center:
  101.             h = h + tot_extra / 2
  102.             tot_extra = tot_stretch = 0
  103.         for font, word, width, space, stretch in self.boxes:
  104.             self.d.setfont(font)
  105.             v = baseline - self.d.baseline()
  106.             self.d.text((h, v), word)
  107.             h = h + width + space
  108.             if tot_extra > 0 and tot_stretch > 0:
  109.                 extra = stretch * tot_extra / tot_stretch
  110.                 h = h + extra
  111.                 tot_extra = tot_extra - extra
  112.                 tot_stretch = tot_stretch - stretch
  113.         #
  114.         # Prepare for next line.
  115.         #
  116.         self.v = baseline + self.max_descent
  117.         self.d.setfont(self.font)
  118.         self._reset()
  119.     #
  120.     # Add vertical space; first flush.
  121.     # Vertical space is expressed in fractions of the current
  122.     # font's line height.
  123.     #
  124.     def vspace(self, lines):
  125.         self.vspacepixels(int(lines * self.d.lineheight()))
  126.     #
  127.     # Add vertical space given in pixels.
  128.     #
  129.     def vspacepixels(self, dv):
  130.         self.flush()
  131.         self.v = self.v + dv
  132.     #
  133.     # Set temporary (hanging) indent, for paragraph start.
  134.     # First flush.
  135.     #
  136.     def tempindent(self, space):
  137.         self.flush()
  138.         hang = int(float(self.font_space) * float(space))
  139.         self.hang_indent = hang
  140.         self.avail_width = self.avail_width - hang
  141.     #
  142.     # Add (permanent) left indentation.  First flush.
  143.     #
  144.     def addleftindent(self, space):
  145.         self.flush()
  146.         self.left = self.left \
  147.             + int(float(self.font_space) * float(space))
  148.         self._reset()
  149.     #
  150.  
  151.  
  152. # Test procedure
  153. #
  154. def test():
  155.     import stdwin, stdwinq
  156.     from stdwinevents import *
  157.     try:
  158.         import mac
  159.         # Mac font assignments:
  160.         font1 = 'times', '', 12
  161.         font2 = 'times', 'b', 14
  162.     except ImportError:
  163.         # X11R4 font assignments
  164.         font1 = '*times-medium-r-*-120-*'
  165.         font2 = '*times-bold-r-*-140-*'
  166.     words = \
  167.         ['The','quick','brown','fox','jumps','over','the','lazy','dog.']
  168.     words = words * 2
  169.     stage = 0
  170.     stages = [(0,0,'ragged'), (1,0,'justified'), (0,1,'centered')]
  171.     justify, center, title = stages[stage]
  172.     stdwin.setdefwinsize(300,200)
  173.     w = stdwin.open(title)
  174.     winsize = w.getwinsize()
  175.     while 1:
  176.         type, window, detail = stdwinq.getevent()
  177.         if type == WE_CLOSE:
  178.             break
  179.         elif type == WE_SIZE:
  180.             newsize = w.getwinsize()
  181.             if newsize <> winsize:
  182.                 w.change((0,0), winsize)
  183.                 winsize = newsize
  184.                 w.change((0,0), winsize)
  185.         elif type == WE_MOUSE_DOWN:
  186.             stage = (stage + 1) % len(stages)
  187.             justify, center, title = stages[stage]
  188.             w.settitle(title)
  189.             w.change((0, 0), (1000, 1000))
  190.         elif type == WE_DRAW:
  191.             width, height = winsize
  192.             f = formatter(w.begindrawing(), 0, 0, width)
  193.             f.center = center
  194.             f.justify = justify
  195.             if not center:
  196.                 f.tempindent(5)
  197.             for font in font1, font2, font1:
  198.                 f.setfont(font)
  199.                 for word in words:
  200.                     space = 1 + (word[-1:] == '.')
  201.                     f.addword(word, space)
  202.                     if center and space > 1:
  203.                         f.flush()
  204.             f.flush()
  205.             height = f.v
  206.             del f
  207.             w.setdocsize(0, height)
  208.