13257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel"""Generic output formatting.
23257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
33257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielFormatter objects transform an abstract flow of formatting events into
43257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielspecific output events on writer objects. Formatters manage several stack
53257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielstructures to allow various properties of a writer object to be changed and
63257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielrestored; writers need not be able to handle relative changes nor any sort
73257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielof ``change back'' operation. Specific writer properties which may be
83257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielcontrolled via formatter objects are horizontal alignment, font, and left
93257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielmargin indentations. A mechanism is provided which supports providing
103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielarbitrary, non-exclusive style settings to a writer as well. Additional
113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielinterfaces facilitate formatting events which are not reversible, such as
123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielparagraph separation.
133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielWriter objects encapsulate device interfaces. Abstract devices, such as
153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielfile formats, are supported as well as physical devices. The provided
163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielimplementations all work with abstract devices. The interface makes
173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielavailable mechanisms for setting the properties which formatter objects
183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielmanage and inserting data into the output.
193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel"""
203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielimport sys
223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielAS_IS = None
253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielclass NullFormatter:
283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """A formatter which does nothing.
293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    If the writer parameter is omitted, a NullWriter instance is created.
313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    No methods of the writer are called by NullFormatter instances.
323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    Implementations should inherit from this class if implementing a writer
343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    interface but don't need to inherit any implementation.
353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """
373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def __init__(self, writer=None):
393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if writer is None:
403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            writer = NullWriter()
413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.writer = writer
423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def end_paragraph(self, blankline): pass
433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def add_line_break(self): pass
443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def add_hor_rule(self, *args, **kw): pass
453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def add_label_data(self, format, counter, blankline=None): pass
463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def add_flowing_data(self, data): pass
473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def add_literal_data(self, data): pass
483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def flush_softspace(self): pass
493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def push_alignment(self, align): pass
503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def pop_alignment(self): pass
513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def push_font(self, x): pass
523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def pop_font(self): pass
533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def push_margin(self, margin): pass
543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def pop_margin(self): pass
553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def set_spacing(self, spacing): pass
563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def push_style(self, *styles): pass
573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def pop_style(self, n=1): pass
583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def assert_line_data(self, flag=1): pass
593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielclass AbstractFormatter:
623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """The standard formatter.
633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    This implementation has demonstrated wide applicability to many writers,
653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    and may be used directly in most circumstances.  It has been used to
663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    implement a full-featured World Wide Web browser.
673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """
693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    #  Space handling policy:  blank spaces at the boundary between elements
713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    #  are handled by the outermost context.  "Literal" data is not checked
723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    #  to determine context, so spaces in literal data are handled directly
733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    #  in all circumstances.
743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def __init__(self, writer):
763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.writer = writer            # Output device
773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.align = None               # Current alignment
783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.align_stack = []           # Alignment stack
793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.font_stack = []            # Font state
803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.margin_stack = []          # Margin state
813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.spacing = None             # Vertical spacing state
823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.style_stack = []           # Other state, e.g. color
833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.nospace = 1                # Should leading space be suppressed
843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.softspace = 0              # Should a space be inserted
853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.para_end = 1               # Just ended a paragraph
863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.parskip = 0                # Skipped space between paragraphs?
873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.hard_break = 1             # Have a hard break
883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.have_label = 0
893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def end_paragraph(self, blankline):
913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not self.hard_break:
923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.send_line_break()
933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.have_label = 0
943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.parskip < blankline and not self.have_label:
953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.send_paragraph(blankline - self.parskip)
963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.parskip = blankline
973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.have_label = 0
983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.hard_break = self.nospace = self.para_end = 1
993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.softspace = 0
1003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def add_line_break(self):
1023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not (self.hard_break or self.para_end):
1033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.send_line_break()
1043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.have_label = self.parskip = 0
1053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.hard_break = self.nospace = 1
1063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.softspace = 0
1073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def add_hor_rule(self, *args, **kw):
1093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not self.hard_break:
1103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.send_line_break()
1113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.writer.send_hor_rule(*args, **kw)
1123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.hard_break = self.nospace = 1
1133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.have_label = self.para_end = self.softspace = self.parskip = 0
1143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def add_label_data(self, format, counter, blankline = None):
1163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.have_label or not self.hard_break:
1173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.send_line_break()
1183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not self.para_end:
1193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.send_paragraph((blankline and 1) or 0)
1203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if isinstance(format, str):
1213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.send_label_data(self.format_counter(format, counter))
1223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
1233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.send_label_data(format)
1243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.nospace = self.have_label = self.hard_break = self.para_end = 1
1253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.softspace = self.parskip = 0
1263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def format_counter(self, format, counter):
1283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        label = ''
1293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        for c in format:
1303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if c == '1':
1313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                label = label + ('%d' % counter)
1323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            elif c in 'aA':
1333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                if counter > 0:
1343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    label = label + self.format_letter(c, counter)
1353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            elif c in 'iI':
1363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                if counter > 0:
1373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    label = label + self.format_roman(c, counter)
1383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            else:
1393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                label = label + c
1403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return label
1413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def format_letter(self, case, counter):
1433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        label = ''
1443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        while counter > 0:
1453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            counter, x = divmod(counter-1, 26)
1463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # This makes a strong assumption that lowercase letters
1473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # and uppercase letters form two contiguous blocks, with
1483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            # letters in order!
1493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            s = chr(ord(case) + x)
1503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            label = s + label
1513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return label
1523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def format_roman(self, case, counter):
1543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        ones = ['i', 'x', 'c', 'm']
1553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        fives = ['v', 'l', 'd']
1563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        label, index = '', 0
1573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        # This will die of IndexError when counter is too big
1583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        while counter > 0:
1593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            counter, x = divmod(counter, 10)
1603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if x == 9:
1613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                label = ones[index] + ones[index+1] + label
1623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            elif x == 4:
1633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                label = ones[index] + fives[index] + label
1643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            else:
1653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                if x >= 5:
1663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    s = fives[index]
1673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    x = x-5
1683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                else:
1693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    s = ''
1703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                s = s + ones[index]*x
1713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                label = s + label
1723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            index = index + 1
1733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if case == 'I':
1743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return label.upper()
1753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return label
1763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def add_flowing_data(self, data):
1783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not data: return
1793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        prespace = data[:1].isspace()
1803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        postspace = data[-1:].isspace()
1813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        data = " ".join(data.split())
1823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.nospace and not data:
1833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return
1843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        elif prespace or self.softspace:
1853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if not data:
1863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                if not self.nospace:
1873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    self.softspace = 1
1883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    self.parskip = 0
1893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                return
1903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if not self.nospace:
1913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                data = ' ' + data
1923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.hard_break = self.nospace = self.para_end = \
1933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                          self.parskip = self.have_label = 0
1943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.softspace = postspace
1953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.writer.send_flowing_data(data)
1963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def add_literal_data(self, data):
1983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not data: return
1993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.softspace:
2003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.send_flowing_data(" ")
2013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.hard_break = data[-1:] == '\n'
2023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.nospace = self.para_end = self.softspace = \
2033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                       self.parskip = self.have_label = 0
2043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.writer.send_literal_data(data)
2053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def flush_softspace(self):
2073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.softspace:
2083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.hard_break = self.para_end = self.parskip = \
2093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                              self.have_label = self.softspace = 0
2103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.nospace = 1
2113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.send_flowing_data(' ')
2123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def push_alignment(self, align):
2143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if align and align != self.align:
2153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.new_alignment(align)
2163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.align = align
2173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.align_stack.append(align)
2183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
2193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.align_stack.append(self.align)
2203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def pop_alignment(self):
2223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.align_stack:
2233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            del self.align_stack[-1]
2243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.align_stack:
2253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.align = align = self.align_stack[-1]
2263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.new_alignment(align)
2273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
2283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.align = None
2293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.new_alignment(None)
2303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def push_font(self, font):
2323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        size, i, b, tt = font
2333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.softspace:
2343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.hard_break = self.para_end = self.softspace = 0
2353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.nospace = 1
2363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.send_flowing_data(' ')
2373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.font_stack:
2383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            csize, ci, cb, ctt = self.font_stack[-1]
2393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if size is AS_IS: size = csize
2403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if i is AS_IS: i = ci
2413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if b is AS_IS: b = cb
2423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if tt is AS_IS: tt = ctt
2433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        font = (size, i, b, tt)
2443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.font_stack.append(font)
2453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.writer.new_font(font)
2463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def pop_font(self):
2483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.font_stack:
2493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            del self.font_stack[-1]
2503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.font_stack:
2513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            font = self.font_stack[-1]
2523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
2533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            font = None
2543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.writer.new_font(font)
2553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def push_margin(self, margin):
2573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.margin_stack.append(margin)
2583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        fstack = filter(None, self.margin_stack)
2593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not margin and fstack:
2603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            margin = fstack[-1]
2613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.writer.new_margin(margin, len(fstack))
2623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def pop_margin(self):
2643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.margin_stack:
2653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            del self.margin_stack[-1]
2663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        fstack = filter(None, self.margin_stack)
2673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if fstack:
2683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            margin = fstack[-1]
2693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
2703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            margin = None
2713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.writer.new_margin(margin, len(fstack))
2723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def set_spacing(self, spacing):
2743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.spacing = spacing
2753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.writer.new_spacing(spacing)
2763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def push_style(self, *styles):
2783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.softspace:
2793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.hard_break = self.para_end = self.softspace = 0
2803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.nospace = 1
2813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.writer.send_flowing_data(' ')
2823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        for style in styles:
2833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.style_stack.append(style)
2843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.writer.new_styles(tuple(self.style_stack))
2853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def pop_style(self, n=1):
2873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        del self.style_stack[-n:]
2883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.writer.new_styles(tuple(self.style_stack))
2893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def assert_line_data(self, flag=1):
2913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.nospace = self.hard_break = not flag
2923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.para_end = self.parskip = self.have_label = 0
2933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielclass NullWriter:
2963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """Minimal writer interface to use in testing & inheritance.
2973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    A writer which only provides the interface definition; no actions are
2993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    taken on any methods.  This should be the base class for all writers
3003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    which do not need to inherit any implementation methods.
3013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """
3033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def __init__(self): pass
3043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def flush(self): pass
3053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def new_alignment(self, align): pass
3063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def new_font(self, font): pass
3073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def new_margin(self, margin, level): pass
3083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def new_spacing(self, spacing): pass
3093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def new_styles(self, styles): pass
3103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_paragraph(self, blankline): pass
3113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_line_break(self): pass
3123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_hor_rule(self, *args, **kw): pass
3133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_label_data(self, data): pass
3143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_flowing_data(self, data): pass
3153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_literal_data(self, data): pass
3163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielclass AbstractWriter(NullWriter):
3193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """A writer which can be used in debugging formatters, but not much else.
3203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    Each method simply announces itself by printing its name and
3223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    arguments on standard output.
3233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """
3253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def new_alignment(self, align):
3273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        print "new_alignment(%r)" % (align,)
3283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def new_font(self, font):
3303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        print "new_font(%r)" % (font,)
3313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def new_margin(self, margin, level):
3333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        print "new_margin(%r, %d)" % (margin, level)
3343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def new_spacing(self, spacing):
3363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        print "new_spacing(%r)" % (spacing,)
3373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def new_styles(self, styles):
3393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        print "new_styles(%r)" % (styles,)
3403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_paragraph(self, blankline):
3423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        print "send_paragraph(%r)" % (blankline,)
3433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_line_break(self):
3453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        print "send_line_break()"
3463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_hor_rule(self, *args, **kw):
3483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        print "send_hor_rule()"
3493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_label_data(self, data):
3513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        print "send_label_data(%r)" % (data,)
3523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_flowing_data(self, data):
3543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        print "send_flowing_data(%r)" % (data,)
3553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_literal_data(self, data):
3573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        print "send_literal_data(%r)" % (data,)
3583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielclass DumbWriter(NullWriter):
3613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """Simple writer class which writes output on the file object passed in
3623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    as the file parameter or, if file is omitted, on standard output.  The
3633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    output is simply word-wrapped to the number of columns specified by
3643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    the maxcol parameter.  This class is suitable for reflowing a sequence
3653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    of paragraphs.
3663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """
3683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def __init__(self, file=None, maxcol=72):
3703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.file = file or sys.stdout
3713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.maxcol = maxcol
3723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        NullWriter.__init__(self)
3733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.reset()
3743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def reset(self):
3763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.col = 0
3773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.atbreak = 0
3783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_paragraph(self, blankline):
3803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.file.write('\n'*blankline)
3813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.col = 0
3823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.atbreak = 0
3833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_line_break(self):
3853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.file.write('\n')
3863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.col = 0
3873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.atbreak = 0
3883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_hor_rule(self, *args, **kw):
3903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.file.write('\n')
3913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.file.write('-'*self.maxcol)
3923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.file.write('\n')
3933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.col = 0
3943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.atbreak = 0
3953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_literal_data(self, data):
3973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.file.write(data)
3983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        i = data.rfind('\n')
3993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if i >= 0:
4003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.col = 0
4013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            data = data[i+1:]
4023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        data = data.expandtabs()
4033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.col = self.col + len(data)
4043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.atbreak = 0
4053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def send_flowing_data(self, data):
4073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not data: return
4083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        atbreak = self.atbreak or data[0].isspace()
4093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        col = self.col
4103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        maxcol = self.maxcol
4113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        write = self.file.write
4123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        for word in data.split():
4133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if atbreak:
4143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                if col + len(word) >= maxcol:
4153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    write('\n')
4163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    col = 0
4173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                else:
4183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    write(' ')
4193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                    col = col + 1
4203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            write(word)
4213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            col = col + len(word)
4223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            atbreak = 1
4233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.col = col
4243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.atbreak = data[-1].isspace()
4253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef test(file = None):
4283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    w = DumbWriter()
4293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    f = AbstractFormatter(w)
4303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if file is not None:
4313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        fp = open(file)
4323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    elif sys.argv[1:]:
4333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        fp = open(sys.argv[1])
4343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    else:
4353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        fp = sys.stdin
4363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    for line in fp:
4373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if line == '\n':
4383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            f.end_paragraph(1)
4393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
4403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            f.add_flowing_data(line)
4413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    f.end_paragraph(0)
4423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
4443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielif __name__ == '__main__':
4453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    test()
446