14adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao"""Generic output formatting. 24adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 34adfde8bc82dd39f59e0445588c3e599ada477dJosh GaoFormatter objects transform an abstract flow of formatting events into 44adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaospecific output events on writer objects. Formatters manage several stack 54adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaostructures to allow various properties of a writer object to be changed and 64adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaorestored; writers need not be able to handle relative changes nor any sort 74adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoof ``change back'' operation. Specific writer properties which may be 84adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaocontrolled via formatter objects are horizontal alignment, font, and left 94adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaomargin indentations. A mechanism is provided which supports providing 104adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoarbitrary, non-exclusive style settings to a writer as well. Additional 114adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaointerfaces facilitate formatting events which are not reversible, such as 124adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoparagraph separation. 134adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 144adfde8bc82dd39f59e0445588c3e599ada477dJosh GaoWriter objects encapsulate device interfaces. Abstract devices, such as 154adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaofile formats, are supported as well as physical devices. The provided 164adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimplementations all work with abstract devices. The interface makes 174adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoavailable mechanisms for setting the properties which formatter objects 184adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaomanage and inserting data into the output. 194adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao""" 204adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 214adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimport sys 224adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 234adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 244adfde8bc82dd39f59e0445588c3e599ada477dJosh GaoAS_IS = None 254adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 264adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 274adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoclass NullFormatter: 284adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """A formatter which does nothing. 294adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 304adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao If the writer parameter is omitted, a NullWriter instance is created. 314adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao No methods of the writer are called by NullFormatter instances. 324adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 334adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao Implementations should inherit from this class if implementing a writer 344adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao interface but don't need to inherit any implementation. 354adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 364adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """ 374adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 384adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def __init__(self, writer=None): 394adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if writer is None: 404adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao writer = NullWriter() 414adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer = writer 424adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def end_paragraph(self, blankline): pass 434adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def add_line_break(self): pass 444adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def add_hor_rule(self, *args, **kw): pass 454adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def add_label_data(self, format, counter, blankline=None): pass 464adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def add_flowing_data(self, data): pass 474adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def add_literal_data(self, data): pass 484adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def flush_softspace(self): pass 494adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def push_alignment(self, align): pass 504adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def pop_alignment(self): pass 514adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def push_font(self, x): pass 524adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def pop_font(self): pass 534adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def push_margin(self, margin): pass 544adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def pop_margin(self): pass 554adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def set_spacing(self, spacing): pass 564adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def push_style(self, *styles): pass 574adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def pop_style(self, n=1): pass 584adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def assert_line_data(self, flag=1): pass 594adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 604adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 614adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoclass AbstractFormatter: 624adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """The standard formatter. 634adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 644adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao This implementation has demonstrated wide applicability to many writers, 654adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao and may be used directly in most circumstances. It has been used to 664adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao implement a full-featured World Wide Web browser. 674adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 684adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """ 694adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 704adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # Space handling policy: blank spaces at the boundary between elements 714adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # are handled by the outermost context. "Literal" data is not checked 724adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # to determine context, so spaces in literal data are handled directly 734adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # in all circumstances. 744adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 754adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def __init__(self, writer): 764adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer = writer # Output device 774adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.align = None # Current alignment 784adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.align_stack = [] # Alignment stack 794adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.font_stack = [] # Font state 804adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.margin_stack = [] # Margin state 814adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.spacing = None # Vertical spacing state 824adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.style_stack = [] # Other state, e.g. color 834adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.nospace = 1 # Should leading space be suppressed 844adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.softspace = 0 # Should a space be inserted 854adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.para_end = 1 # Just ended a paragraph 864adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.parskip = 0 # Skipped space between paragraphs? 874adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.hard_break = 1 # Have a hard break 884adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.have_label = 0 894adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 904adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def end_paragraph(self, blankline): 914adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if not self.hard_break: 924adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_line_break() 934adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.have_label = 0 944adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self.parskip < blankline and not self.have_label: 954adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_paragraph(blankline - self.parskip) 964adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.parskip = blankline 974adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.have_label = 0 984adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.hard_break = self.nospace = self.para_end = 1 994adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.softspace = 0 1004adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1014adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def add_line_break(self): 1024adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if not (self.hard_break or self.para_end): 1034adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_line_break() 1044adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.have_label = self.parskip = 0 1054adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.hard_break = self.nospace = 1 1064adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.softspace = 0 1074adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1084adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def add_hor_rule(self, *args, **kw): 1094adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if not self.hard_break: 1104adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_line_break() 1114adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_hor_rule(*args, **kw) 1124adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.hard_break = self.nospace = 1 1134adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.have_label = self.para_end = self.softspace = self.parskip = 0 1144adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1154adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def add_label_data(self, format, counter, blankline = None): 1164adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self.have_label or not self.hard_break: 1174adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_line_break() 1184adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if not self.para_end: 1194adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_paragraph((blankline and 1) or 0) 1204adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if isinstance(format, str): 1214adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_label_data(self.format_counter(format, counter)) 1224adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao else: 1234adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_label_data(format) 1244adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.nospace = self.have_label = self.hard_break = self.para_end = 1 1254adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.softspace = self.parskip = 0 1264adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1274adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def format_counter(self, format, counter): 1284adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao label = '' 1294adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao for c in format: 1304adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if c == '1': 1314adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao label = label + ('%d' % counter) 1324adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao elif c in 'aA': 1334adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if counter > 0: 1344adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao label = label + self.format_letter(c, counter) 1354adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao elif c in 'iI': 1364adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if counter > 0: 1374adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao label = label + self.format_roman(c, counter) 1384adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao else: 1394adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao label = label + c 1404adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return label 1414adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1424adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def format_letter(self, case, counter): 1434adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao label = '' 1444adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao while counter > 0: 1454adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao counter, x = divmod(counter-1, 26) 1464adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # This makes a strong assumption that lowercase letters 1474adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # and uppercase letters form two contiguous blocks, with 1484adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # letters in order! 1494adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao s = chr(ord(case) + x) 1504adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao label = s + label 1514adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return label 1524adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1534adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def format_roman(self, case, counter): 1544adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao ones = ['i', 'x', 'c', 'm'] 1554adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao fives = ['v', 'l', 'd'] 1564adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao label, index = '', 0 1574adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # This will die of IndexError when counter is too big 1584adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao while counter > 0: 1594adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao counter, x = divmod(counter, 10) 1604adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if x == 9: 1614adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao label = ones[index] + ones[index+1] + label 1624adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao elif x == 4: 1634adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao label = ones[index] + fives[index] + label 1644adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao else: 1654adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if x >= 5: 1664adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao s = fives[index] 1674adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao x = x-5 1684adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao else: 1694adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao s = '' 1704adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao s = s + ones[index]*x 1714adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao label = s + label 1724adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao index = index + 1 1734adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if case == 'I': 1744adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return label.upper() 1754adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return label 1764adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1774adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def add_flowing_data(self, data): 1784adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if not data: return 1794adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao prespace = data[:1].isspace() 1804adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao postspace = data[-1:].isspace() 1814adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao data = " ".join(data.split()) 1824adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self.nospace and not data: 1834adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return 1844adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao elif prespace or self.softspace: 1854adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if not data: 1864adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if not self.nospace: 1874adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.softspace = 1 1884adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.parskip = 0 1894adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return 1904adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if not self.nospace: 1914adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao data = ' ' + data 1924adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.hard_break = self.nospace = self.para_end = \ 1934adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.parskip = self.have_label = 0 1944adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.softspace = postspace 1954adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_flowing_data(data) 1964adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1974adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def add_literal_data(self, data): 1984adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if not data: return 1994adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self.softspace: 2004adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_flowing_data(" ") 2014adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.hard_break = data[-1:] == '\n' 2024adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.nospace = self.para_end = self.softspace = \ 2034adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.parskip = self.have_label = 0 2044adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_literal_data(data) 2054adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2064adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def flush_softspace(self): 2074adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self.softspace: 2084adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.hard_break = self.para_end = self.parskip = \ 2094adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.have_label = self.softspace = 0 2104adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.nospace = 1 2114adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_flowing_data(' ') 2124adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2134adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def push_alignment(self, align): 2144adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if align and align != self.align: 2154adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.new_alignment(align) 2164adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.align = align 2174adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.align_stack.append(align) 2184adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao else: 2194adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.align_stack.append(self.align) 2204adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2214adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def pop_alignment(self): 2224adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self.align_stack: 2234adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao del self.align_stack[-1] 2244adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self.align_stack: 2254adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.align = align = self.align_stack[-1] 2264adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.new_alignment(align) 2274adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao else: 2284adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.align = None 2294adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.new_alignment(None) 2304adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2314adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def push_font(self, font): 2324adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao size, i, b, tt = font 2334adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self.softspace: 2344adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.hard_break = self.para_end = self.softspace = 0 2354adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.nospace = 1 2364adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_flowing_data(' ') 2374adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self.font_stack: 2384adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao csize, ci, cb, ctt = self.font_stack[-1] 2394adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if size is AS_IS: size = csize 2404adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if i is AS_IS: i = ci 2414adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if b is AS_IS: b = cb 2424adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if tt is AS_IS: tt = ctt 2434adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao font = (size, i, b, tt) 2444adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.font_stack.append(font) 2454adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.new_font(font) 2464adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2474adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def pop_font(self): 2484adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self.font_stack: 2494adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao del self.font_stack[-1] 2504adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self.font_stack: 2514adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao font = self.font_stack[-1] 2524adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao else: 2534adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao font = None 2544adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.new_font(font) 2554adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2564adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def push_margin(self, margin): 2574adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.margin_stack.append(margin) 2584adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao fstack = filter(None, self.margin_stack) 2594adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if not margin and fstack: 2604adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao margin = fstack[-1] 2614adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.new_margin(margin, len(fstack)) 2624adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2634adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def pop_margin(self): 2644adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self.margin_stack: 2654adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao del self.margin_stack[-1] 2664adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao fstack = filter(None, self.margin_stack) 2674adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if fstack: 2684adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao margin = fstack[-1] 2694adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao else: 2704adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao margin = None 2714adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.new_margin(margin, len(fstack)) 2724adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2734adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def set_spacing(self, spacing): 2744adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.spacing = spacing 2754adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.new_spacing(spacing) 2764adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2774adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def push_style(self, *styles): 2784adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self.softspace: 2794adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.hard_break = self.para_end = self.softspace = 0 2804adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.nospace = 1 2814adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.send_flowing_data(' ') 2824adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao for style in styles: 2834adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.style_stack.append(style) 2844adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.new_styles(tuple(self.style_stack)) 2854adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2864adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def pop_style(self, n=1): 2874adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao del self.style_stack[-n:] 2884adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.writer.new_styles(tuple(self.style_stack)) 2894adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2904adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def assert_line_data(self, flag=1): 2914adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.nospace = self.hard_break = not flag 2924adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.para_end = self.parskip = self.have_label = 0 2934adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2944adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2954adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoclass NullWriter: 2964adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """Minimal writer interface to use in testing & inheritance. 2974adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 2984adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao A writer which only provides the interface definition; no actions are 2994adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao taken on any methods. This should be the base class for all writers 3004adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao which do not need to inherit any implementation methods. 3014adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3024adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """ 3034adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def __init__(self): pass 3044adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def flush(self): pass 3054adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def new_alignment(self, align): pass 3064adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def new_font(self, font): pass 3074adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def new_margin(self, margin, level): pass 3084adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def new_spacing(self, spacing): pass 3094adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def new_styles(self, styles): pass 3104adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_paragraph(self, blankline): pass 3114adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_line_break(self): pass 3124adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_hor_rule(self, *args, **kw): pass 3134adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_label_data(self, data): pass 3144adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_flowing_data(self, data): pass 3154adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_literal_data(self, data): pass 3164adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3174adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3184adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoclass AbstractWriter(NullWriter): 3194adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """A writer which can be used in debugging formatters, but not much else. 3204adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3214adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao Each method simply announces itself by printing its name and 3224adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao arguments on standard output. 3234adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3244adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """ 3254adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3264adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def new_alignment(self, align): 3274adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao print "new_alignment(%r)" % (align,) 3284adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3294adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def new_font(self, font): 3304adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao print "new_font(%r)" % (font,) 3314adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3324adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def new_margin(self, margin, level): 3334adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao print "new_margin(%r, %d)" % (margin, level) 3344adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3354adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def new_spacing(self, spacing): 3364adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao print "new_spacing(%r)" % (spacing,) 3374adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3384adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def new_styles(self, styles): 3394adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao print "new_styles(%r)" % (styles,) 3404adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3414adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_paragraph(self, blankline): 3424adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao print "send_paragraph(%r)" % (blankline,) 3434adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3444adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_line_break(self): 3454adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao print "send_line_break()" 3464adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3474adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_hor_rule(self, *args, **kw): 3484adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao print "send_hor_rule()" 3494adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3504adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_label_data(self, data): 3514adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao print "send_label_data(%r)" % (data,) 3524adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3534adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_flowing_data(self, data): 3544adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao print "send_flowing_data(%r)" % (data,) 3554adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3564adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_literal_data(self, data): 3574adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao print "send_literal_data(%r)" % (data,) 3584adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3594adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3604adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoclass DumbWriter(NullWriter): 3614adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """Simple writer class which writes output on the file object passed in 3624adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao as the file parameter or, if file is omitted, on standard output. The 3634adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao output is simply word-wrapped to the number of columns specified by 3644adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao the maxcol parameter. This class is suitable for reflowing a sequence 3654adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao of paragraphs. 3664adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3674adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """ 3684adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3694adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def __init__(self, file=None, maxcol=72): 3704adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.file = file or sys.stdout 3714adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.maxcol = maxcol 3724adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao NullWriter.__init__(self) 3734adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.reset() 3744adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3754adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def reset(self): 3764adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.col = 0 3774adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.atbreak = 0 3784adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3794adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_paragraph(self, blankline): 3804adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.file.write('\n'*blankline) 3814adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.col = 0 3824adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.atbreak = 0 3834adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3844adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_line_break(self): 3854adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.file.write('\n') 3864adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.col = 0 3874adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.atbreak = 0 3884adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3894adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_hor_rule(self, *args, **kw): 3904adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.file.write('\n') 3914adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.file.write('-'*self.maxcol) 3924adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.file.write('\n') 3934adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.col = 0 3944adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.atbreak = 0 3954adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 3964adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_literal_data(self, data): 3974adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.file.write(data) 3984adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao i = data.rfind('\n') 3994adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if i >= 0: 4004adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.col = 0 4014adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao data = data[i+1:] 4024adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao data = data.expandtabs() 4034adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.col = self.col + len(data) 4044adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.atbreak = 0 4054adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 4064adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def send_flowing_data(self, data): 4074adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if not data: return 4084adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao atbreak = self.atbreak or data[0].isspace() 4094adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao col = self.col 4104adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao maxcol = self.maxcol 4114adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao write = self.file.write 4124adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao for word in data.split(): 4134adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if atbreak: 4144adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if col + len(word) >= maxcol: 4154adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao write('\n') 4164adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao col = 0 4174adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao else: 4184adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao write(' ') 4194adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao col = col + 1 4204adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao write(word) 4214adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao col = col + len(word) 4224adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao atbreak = 1 4234adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.col = col 4244adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.atbreak = data[-1].isspace() 4254adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 4264adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 4274adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef test(file = None): 4284adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao w = DumbWriter() 4294adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao f = AbstractFormatter(w) 4304adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if file is not None: 4314adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao fp = open(file) 4324adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao elif sys.argv[1:]: 4334adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao fp = open(sys.argv[1]) 4344adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao else: 4354adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao fp = sys.stdin 4364adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao for line in fp: 4374adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if line == '\n': 4384adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao f.end_paragraph(1) 4394adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao else: 4404adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao f.add_flowing_data(line) 4414adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao f.end_paragraph(0) 4424adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 4434adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 4444adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoif __name__ == '__main__': 4454adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao test() 446