fmt.py :  » Mobile » Python-for-PalmOS » Python-1.5.2+reduced-1.0 » Demo » tkinter » www » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Mobile » Python for PalmOS 
Python for PalmOS » Python 1.5.2 reduced 1.0 » Demo » tkinter » www » fmt.py
# Text formatting abstractions


import string
import Para


# A formatter back-end object has one method that is called by the formatter:
# addpara(p), where p is a paragraph object.  For example:


# Formatter back-end to do nothing at all with the paragraphs
class NullBackEnd:
  #
  def __init__(self):
    pass
  #
  def addpara(self, p):
    pass
  #
  def bgn_anchor(self, id):
    pass
  #
  def end_anchor(self, id):
    pass


# Formatter back-end to collect the paragraphs in a list
class SavingBackEnd(NullBackEnd):
  #
  def __init__(self):
    self.paralist = []
  #
  def addpara(self, p):
    self.paralist.append(p)
  #
  def hitcheck(self, h, v):
    hits = []
    for p in self.paralist:
      if p.top <= v <= p.bottom:
        for id in p.hitcheck(h, v):
          if id not in hits:
            hits.append(id)
    return hits
  #
  def extract(self):
    text = ''
    for p in self.paralist:
      text = text + (p.extract())
    return text
  #
  def extractpart(self, long1, long2):
    if long1 > long2: long1, long2 = long2, long1
    para1, pos1 = long1
    para2, pos2 = long2
    text = ''
    while para1 < para2:
      ptext = self.paralist[para1].extract()
      text = text + ptext[pos1:]
      pos1 = 0
      para1 = para1 + 1
    ptext = self.paralist[para2].extract()
    return text + ptext[pos1:pos2]
  #
  def whereis(self, d, h, v):
    total = 0
    for i in range(len(self.paralist)):
      p = self.paralist[i]
      result = p.whereis(d, h, v)
      if result <> None:
        return i, result
    return None
  #
  def roundtowords(self, long1, long2):
    i, offset = long1
    text = self.paralist[i].extract()
    while offset > 0 and text[offset-1] <> ' ': offset = offset-1
    long1 = i, offset
    #
    i, offset = long2
    text = self.paralist[i].extract()
    n = len(text)
    while offset < n-1 and text[offset] <> ' ': offset = offset+1
    long2 = i, offset
    #
    return long1, long2
  #
  def roundtoparagraphs(self, long1, long2):
    long1 = long1[0], 0
    long2 = long2[0], len(self.paralist[long2[0]].extract())
    return long1, long2


# Formatter back-end to send the text directly to the drawing object
class WritingBackEnd(NullBackEnd):
  #
  def __init__(self, d, width):
    self.d = d
    self.width = width
    self.lineno = 0
  #
  def addpara(self, p):
    self.lineno = p.render(self.d, 0, self.lineno, self.width)


# A formatter receives a stream of formatting instructions and assembles
# these into a stream of paragraphs on to a back-end.  The assembly is
# parametrized by a text measurement object, which must match the output
# operations of the back-end.  The back-end is responsible for splitting
# paragraphs up in lines of a given maximum width.  (This is done because
# in a windowing environment, when the window size changes, there is no
# need to redo the assembly into paragraphs, but the splitting into lines
# must be done taking the new window size into account.)


# Formatter base class.  Initialize it with a text measurement object,
# which is used for text measurements, and a back-end object,
# which receives the completed paragraphs.  The formatting methods are:
# setfont(font)
# setleftindent(nspaces)
# setjust(type) where type is 'l', 'c', 'r', or 'lr'
# flush()
# vspace(nlines)
# needvspace(nlines)
# addword(word, nspaces)
class BaseFormatter:
  #
  def __init__(self, d, b):
    # Drawing object used for text measurements
    self.d = d
    #
    # BackEnd object receiving completed paragraphs
    self.b = b
    #
    # Parameters of the formatting model
    self.leftindent = 0
    self.just = 'l'
    self.font = None
    self.blanklines = 0
    #
    # Parameters derived from the current font
    self.space = d.textwidth(' ')
    self.line = d.lineheight()
    self.ascent = d.baseline()
    self.descent = self.line - self.ascent
    #
    # Parameter derived from the default font
    self.n_space = self.space
    #
    # Current paragraph being built
    self.para = None
    self.nospace = 1
    #
    # Font to set on the next word
    self.nextfont = None
  #
  def newpara(self):
    return Para.Para()
  #
  def setfont(self, font):
    if font == None: return
    self.font = self.nextfont = font
    d = self.d
    d.setfont(font)
    self.space = d.textwidth(' ')
    self.line = d.lineheight()
    self.ascent = d.baseline()
    self.descent = self.line - self.ascent
  #
  def setleftindent(self, nspaces):
    self.leftindent = int(self.n_space * nspaces)
    if self.para:
      hang = self.leftindent - self.para.indent_left
      if hang > 0 and self.para.getlength() <= hang:
        self.para.makehangingtag(hang)
        self.nospace = 1
      else:
        self.flush()
  #
  def setrightindent(self, nspaces):
    self.rightindent = int(self.n_space * nspaces)
    if self.para:
      self.para.indent_right = self.rightindent
      self.flush()
  #
  def setjust(self, just):
    self.just = just
    if self.para:
      self.para.just = self.just
  #
  def flush(self):
    if self.para:
      self.b.addpara(self.para)
      self.para = None
      if self.font <> None:
        self.d.setfont(self.font)
    self.nospace = 1
  #
  def vspace(self, nlines):
    self.flush()
    if nlines > 0:
      self.para = self.newpara()
      tuple = None, '', 0, 0, 0, int(nlines*self.line), 0
      self.para.words.append(tuple)
      self.flush()
      self.blanklines = self.blanklines + nlines
  #
  def needvspace(self, nlines):
    self.flush() # Just to be sure
    if nlines > self.blanklines:
      self.vspace(nlines - self.blanklines)
  #
  def addword(self, text, space):
    if self.nospace and not text:
      return
    self.nospace = 0
    self.blanklines = 0
    if not self.para:
      self.para = self.newpara()
      self.para.indent_left = self.leftindent
      self.para.just = self.just
      self.nextfont = self.font
    space = int(space * self.space)
    self.para.words.append(self.nextfont, text, \
      self.d.textwidth(text), space, space, \
      self.ascent, self.descent)
    self.nextfont = None
  #
  def bgn_anchor(self, id):
    if not self.para:
      self.nospace = 0
      self.addword('', 0)
    self.para.bgn_anchor(id)
  #
  def end_anchor(self, id):
    if not self.para:
      self.nospace = 0
      self.addword('', 0)
    self.para.end_anchor(id)
  #
  def hrule(self):
    # Typically need to override this for bit-mapped displays
    self.flush()
    self.addword('-'*60, 0)
    self.flush()


# Measuring object for measuring text as viewed on a tty
class NullMeasurer:
  #
  def __init__(self):
    pass
  #
  def setfont(self, font):
    pass
  #
  def textwidth(self, text):
    return len(text)
  #
  def lineheight(self):
    return 1
  #
  def baseline(self):
    return 0


# Drawing object for writing plain ASCII text to a file
class FileWriter:
  #
  def __init__(self, fp):
    self.fp = fp
    self.lineno, self.colno = 0, 0
  #
  def setfont(self, font):
    pass
  #
  def text(self, (h, v), str):
    if not str: return
    if '\n' in str:
      raise ValueError, 'can\'t write \\n'
    while self.lineno < v:
      self.fp.write('\n')
      self.colno, self.lineno = 0, self.lineno + 1
    while self.lineno > v:
      # XXX This should never happen...
      self.fp.write('\033[A') # ANSI up arrow
      self.lineno = self.lineno - 1
    if self.colno < h:
      self.fp.write(' ' * (h - self.colno))
    elif self.colno > h:
      self.fp.write('\b' * (self.colno - h))
    self.colno = h
    self.fp.write(str)
    self.colno = h + len(str)


# Formatting class to do nothing at all with the data
class NullFormatter(BaseFormatter):
  #
  def __init__(self):
    d = NullMeasurer()
    b = NullBackEnd()
    BaseFormatter.__init__(self, d, b)


# Formatting class to write directly to a file
class WritingFormatter(BaseFormatter):
  #
  def __init__(self, fp, width):
    dm = NullMeasurer()
    dw = FileWriter(fp)
    b = WritingBackEnd(dw, width)
    BaseFormatter.__init__(self, dm, b)
    self.blanklines = 1
  #
  # Suppress multiple blank lines
  def needvspace(self, nlines):
    BaseFormatter.needvspace(self, min(1, nlines))


# A "FunnyFormatter" writes ASCII text with a twist: *bold words*,
# _italic text_ and _underlined words_, and `quoted text'.
# It assumes that the fonts are 'r', 'i', 'b', 'u', 'q': (roman,
# italic, bold, underline, quote).
# Moreover, if the font is in upper case, the text is converted to
# UPPER CASE.
class FunnyFormatter(WritingFormatter):
  #
  def flush(self):
    if self.para: finalize(self.para)
    WritingFormatter.flush(self)


# Surrounds *bold words* and _italic text_ in a paragraph with
# appropriate markers, fixing the size (assuming these characters'
# width is 1).
openchar = \
    {'b':'*', 'i':'_', 'u':'_', 'q':'`', 'B':'*', 'I':'_', 'U':'_', 'Q':'`'}
closechar = \
    {'b':'*', 'i':'_', 'u':'_', 'q':'\'', 'B':'*', 'I':'_', 'U':'_', 'Q':'\''}
def finalize(para):
  oldfont = curfont = 'r'
  para.words.append('r', '', 0, 0, 0, 0) # temporary, deleted at end
  for i in range(len(para.words)):
    fo, te, wi = para.words[i][:3]
    if fo <> None: curfont = fo
    if curfont <> oldfont:
      if closechar.has_key(oldfont):
        c = closechar[oldfont]
        j = i-1
        while j > 0 and para.words[j][1] == '': j = j-1
        fo1, te1, wi1 = para.words[j][:3]
        te1 = te1 + c
        wi1 = wi1 + len(c)
        para.words[j] = (fo1, te1, wi1) + \
          para.words[j][3:]
      if openchar.has_key(curfont) and te:
        c = openchar[curfont]
        te = c + te
        wi = len(c) + wi
        para.words[i] = (fo, te, wi) + \
          para.words[i][3:]
      if te: oldfont = curfont
      else: oldfont = 'r'
    if curfont in string.uppercase:
      te = string.upper(te)
      para.words[i] = (fo, te, wi) + para.words[i][3:]
  del para.words[-1]


# Formatter back-end to draw the text in a window.
# This has an option to draw while the paragraphs are being added,
# to minimize the delay before the user sees anything.
# This manages the entire "document" of the window.
class StdwinBackEnd(SavingBackEnd):
  #
  def __init__(self, window, drawnow):
    self.window = window
    self.drawnow = drawnow
    self.width = window.getwinsize()[0]
    self.selection = None
    self.height = 0
    window.setorigin(0, 0)
    window.setdocsize(0, 0)
    self.d = window.begindrawing()
    SavingBackEnd.__init__(self)
  #
  def finish(self):
    self.d.close()
    self.d = None
    self.window.setdocsize(0, self.height)
  #
  def addpara(self, p):
    self.paralist.append(p)
    if self.drawnow:
      self.height = \
        p.render(self.d, 0, self.height, self.width)
    else:
      p.layout(self.width)
      p.left = 0
      p.top = self.height
      p.right = self.width
      p.bottom = self.height + p.height
      self.height = p.bottom
  #
  def resize(self):
    self.window.change((0, 0), (self.width, self.height))
    self.width = self.window.getwinsize()[0]
    self.height = 0
    for p in self.paralist:
      p.layout(self.width)
      p.left = 0
      p.top = self.height
      p.right = self.width
      p.bottom = self.height + p.height
      self.height = p.bottom
    self.window.change((0, 0), (self.width, self.height))
    self.window.setdocsize(0, self.height)
  #
  def redraw(self, area):
    d = self.window.begindrawing()
    (left, top), (right, bottom) = area
    d.erase(area)
    d.cliprect(area)
    for p in self.paralist:
      if top < p.bottom and p.top < bottom:
        v = p.render(d, p.left, p.top, p.right)
    if self.selection:
      self.invert(d, self.selection)
    d.close()
  #
  def setselection(self, new):
    if new:
      long1, long2 = new
      pos1 = long1[:3]
      pos2 = long2[:3]
      new = pos1, pos2
    if new <> self.selection:
      d = self.window.begindrawing()
      if self.selection:
        self.invert(d, self.selection)
      if new:
        self.invert(d, new)
      d.close()
      self.selection = new
  #
  def getselection(self):
    return self.selection
  #
  def extractselection(self):
    if self.selection:
      a, b = self.selection
      return self.extractpart(a, b)
    else:
      return None
  #
  def invert(self, d, region):
    long1, long2 = region
    if long1 > long2: long1, long2 = long2, long1
    para1, pos1 = long1
    para2, pos2 = long2
    while para1 < para2:
      self.paralist[para1].invert(d, pos1, None)
      pos1 = None
      para1 = para1 + 1
    self.paralist[para2].invert(d, pos1, pos2)
  #
  def search(self, prog):
    import regex, string
    if type(prog) == type(''):
      prog = regex.compile(string.lower(prog))
    if self.selection:
      iold = self.selection[0][0]
    else:
      iold = -1
    hit = None
    for i in range(len(self.paralist)):
      if i == iold or i < iold and hit:
        continue
      p = self.paralist[i]
      text = string.lower(p.extract())
      if prog.search(text) >= 0:
        a, b = prog.regs[0]
        long1 = i, a
        long2 = i, b
        hit = long1, long2
        if i > iold:
          break
    if hit:
      self.setselection(hit)
      i = hit[0][0]
      p = self.paralist[i]
      self.window.show((p.left, p.top), (p.right, p.bottom))
      return 1
    else:
      return 0
  #
  def showanchor(self, id):
    for i in range(len(self.paralist)):
      p = self.paralist[i]
      if p.hasanchor(id):
        long1 = i, 0
        long2 = i, len(p.extract())
        hit = long1, long2
        self.setselection(hit)
        self.window.show( \
          (p.left, p.top), (p.right, p.bottom))
        break


# GL extensions

class GLFontCache:
  #
  def __init__(self):
    self.reset()
    self.setfont('')
  #
  def reset(self):
    self.fontkey = None
    self.fonthandle = None
    self.fontinfo = None
    self.fontcache = {}
  #
  def close(self):
    self.reset()
  #
  def setfont(self, fontkey):
    if fontkey == '':
      fontkey = 'Times-Roman 12'
    elif ' ' not in fontkey:
      fontkey = fontkey + ' 12'
    if fontkey == self.fontkey:
      return
    if self.fontcache.has_key(fontkey):
      handle = self.fontcache[fontkey]
    else:
      import string
      i = string.index(fontkey, ' ')
      name, sizestr = fontkey[:i], fontkey[i:]
      size = eval(sizestr)
      key1 = name + ' 1'
      key = name + ' ' + `size`
      # NB key may differ from fontkey!
      if self.fontcache.has_key(key):
        handle = self.fontcache[key]
      else:
        if self.fontcache.has_key(key1):
          handle = self.fontcache[key1]
        else:
          import fm
          handle = fm.findfont(name)
          self.fontcache[key1] = handle
        handle = handle.scalefont(size)
        self.fontcache[fontkey] = \
          self.fontcache[key] = handle
    self.fontkey = fontkey
    if self.fonthandle <> handle:
      self.fonthandle = handle
      self.fontinfo = handle.getfontinfo()
      handle.setfont()


class GLMeasurer(GLFontCache):
  #
  def textwidth(self, text):
    return self.fonthandle.getstrwidth(text)
  #
  def baseline(self):
    return self.fontinfo[6] - self.fontinfo[3]
  #
  def lineheight(self):
    return self.fontinfo[6]


class GLWriter(GLFontCache):
  #
  # NOTES:
  # (1) Use gl.ortho2 to use X pixel coordinates!
  #
  def text(self, (h, v), text):
    import gl, fm
    gl.cmov2i(h, v + self.fontinfo[6] - self.fontinfo[3])
    fm.prstr(text)
  #
  def setfont(self, fontkey):
    oldhandle = self.fonthandle
    GLFontCache.setfont(fontkey)
    if self.fonthandle <> oldhandle:
      handle.setfont()


class GLMeasurerWriter(GLMeasurer, GLWriter):
  pass


class GLBackEnd(SavingBackEnd):
  #
  def __init__(self, wid):
    import gl
    gl.winset(wid)
    self.wid = wid
    self.width = gl.getsize()[1]
    self.height = 0
    self.d = GLMeasurerWriter()
    SavingBackEnd.__init__(self)
  #
  def finish(self):
    pass
  #
  def addpara(self, p):
    self.paralist.append(p)
    self.height = p.render(self.d, 0, self.height, self.width)
  #
  def redraw(self):
    import gl
    gl.winset(self.wid)
    width = gl.getsize()[1]
    if width <> self.width:
      setdocsize = 1
      self.width = width
      for p in self.paralist:
        p.top = p.bottom = None
    d = self.d
    v = 0
    for p in self.paralist:
      v = p.render(d, 0, v, width)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.