diff options
author | Guido van Rossum <guido@python.org> | 1995-08-07 20:07:36 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 1995-08-07 20:07:36 (GMT) |
commit | a0eab1d3670897e8bc4407e8706fdec315a7daf9 (patch) | |
tree | a357102b3d97c92d27dbc237b140d78fc81a298f | |
parent | 0a2eaac86757a8cbcbb2401923ff91e36aaf50f4 (diff) | |
download | cpython-a0eab1d3670897e8bc4407e8706fdec315a7daf9.zip cpython-a0eab1d3670897e8bc4407e8706fdec315a7daf9.tar.gz cpython-a0eab1d3670897e8bc4407e8706fdec315a7daf9.tar.bz2 |
Initial revision
-rw-r--r-- | Lib/formatter.py | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/Lib/formatter.py b/Lib/formatter.py new file mode 100644 index 0000000..67d2b16 --- /dev/null +++ b/Lib/formatter.py @@ -0,0 +1,276 @@ +import regex +import regsub +import string +import sys + + +AS_IS = None + +whitespace = '[' + string.whitespace + ']+' + + +class AbstractFormatter: + + def __init__(self, writer): + self.writer = writer # Output device + self.font_stack = [] # Font state + self.margin_stack = [] # Margin state + self.spacing = None # Vertical spacing state + self.style_stack = [] # Other state, e.g. color + self.nospace = 1 # Should leading space be suppressed + self.softspace = 0 # Should a space be inserted + + def end_paragraph(self, blankline): + if not self.nospace: + self.writer.send_paragraph(blankline) + self.nospace = 1 + self.softspace = 0 + + def add_line_break(self): + self.writer.send_line_break() + self.nospace = 1 + self.softspace = 0 + + def add_hor_rule(self): + self.writer.send_hor_rule() + self.nospace = 1 + self.softspace = 0 + + def add_label_data(self, format, counter): + data = self.format_counter(format, counter) + self.writer.send_label_data(data) + + def format_counter(self, format, counter): + if counter <= 0: + return format + label = '' + for c in format: + try: + if c == '1': + c = '%d' % counter + elif c in 'aA': + c = self.format_letter(c, counter) + elif c in 'iI': + c = self.format_roman(c, counter) + except: + pass + label = label + c + return label + + def format_letter(self, case, counter): + label = '' + while counter > 0: + counter, x = divmod(counter-1, 26) + s = chr(ord(case) + x) + label = s + label + return label + + def format_roman(self, case, counter): + ones = ['i', 'x', 'c', 'm'] + fives = ['v', 'l', 'd'] + label = '' + index = 0 + # This will die of IndexError when counter is too big + while counter > 0: + counter, x = divmod(counter, 10) + if x == 9: + s = ones[index] + ones[index+1] + elif x == 4: + s = ones[index] + fives[index] + else: + if x >= 5: + s = fives[index] + x = x-5 + else: + s = '' + s = s + ones[index]*x + label = s + label + index = index + 1 + if case == 'I': label = string.upper(label) + return label + + def add_flowing_data(self, data): + if not data: return + data = regsub.gsub(whitespace, ' ', data) + if self.nospace and data[0] == ' ': + data = data[1:] + if not data: return + elif self.softspace and data[0] != ' ': + data = ' ' + data + self.nospace = self.softspace = 0 + if data[-1] == ' ': + data = data[:-1] + self.softspace = 1 + self.writer.send_flowing_data(data) + + def add_literal_data(self, data): + if self.softspace and data[:1] != '\n': + data = ' ' + data + self.nospace = self.softspace = 0 + self.writer.send_literal_data(data) + + def push_font(self, (size, i, b, tt)): + if self.font_stack: + csize, ci, cb, ctt = self.font_stack[-1] + if size is AS_IS: size = csize + if i is AS_IS: i = ci + if b is AS_IS: b = cb + if tt is AS_IS: tt = ctt + font = (size, i, b, tt) + self.font_stack.append(font) + self.writer.new_font(font) + + def pop_font(self): + if self.font_stack: + del self.font_stack[-1] + if self.font_stack: + font = self.font_stack[-1] + else: + font = None + self.writer.new_font(font) + + def push_margin(self, margin): + self.margin_stack.append(margin) + self.writer.new_margin(margin, len(self.margin_stack)) + + def pop_margin(self): + if self.margin_stack: + del self.margin_stack[-1] + if self.margin_stack: + margin = self.margin_stack[-1] + else: + margin = None + self.writer.new_margin(margin, len(self.margin_stack)) + + def set_spacing(self, spacing): + self.spacing = spacing + self.writer.new_spacing(spacing) + + def push_style(self, style): + self.style_stack.append(style) + self.writer.new_styles(tuple(self.style_stack)) + + def pop_style(self): + if self.style_stack: + del self.style_stack[-1] + self.writer.new_styles(tuple(self.style_stack)) + + +class AbstractWriter: + + def __init__(self): + pass + + def new_font(self, font): + print "new_font(%s)" % `font` + + def new_margin(self, margin, level): + print "new_margin(%s, %d)" % (`margin`, level) + + def new_spacing(self, spacing): + print "new_spacing(%s)" % `spacing` + + def new_styles(self, styles): + print "new_styles(%s)" % `styles` + + def send_paragraph(self, blankline): + print "send_paragraph(%s)" % `blankline` + + def send_line_break(self): + print "send_line_break()" + + def send_hor_rule(self): + print "send_hor_rule()" + + def send_label_data(self, data): + print "send_label_data(%s)" % `data` + + def send_flowing_data(self, data): + print "send_flowing_data(%s)" % `data` + + def send_literal_data(self, data): + print "send_literal_data(%s)" % `data` + + +class DumbWriter(AbstractWriter): + + def __init__(self, file=None, maxcol=72): + self.file = file or sys.stdout + self.maxcol = maxcol + AbstractWriter.__init__(self) + self.reset() + + def reset(self): + self.col = 0 + self.atbreak = 0 + + def send_paragraph(self, blankline): + self.file.write('\n' + '\n'*blankline) + self.col = 0 + self.atbreak = 0 + + def send_line_break(self): + self.file.write('\n') + self.col = 0 + self.atbreak = 0 + + def send_hor_rule(self): + self.file.write('\n') + self.file.write('-'*self.maxcol) + self.file.write('\n') + self.col = 0 + self.atbreak = 0 + + def send_literal_data(self, data): + self.file.write(data) + i = string.rfind(data, '\n') + if i >= 0: + self.col = 0 + data = data[i+1:] + data = string.expandtabs(data) + self.col = self.col + len(data) + self.atbreak = 0 + + def send_flowing_data(self, data): + if not data: return + atbreak = self.atbreak or data[0] in string.whitespace + col = self.col + maxcol = self.maxcol + write = self.file.write + for word in string.split(data): + if atbreak: + if col + len(word) >= maxcol: + write('\n') + col = 0 + else: + write(' ') + col = col + 1 + write(word) + col = col + len(word) + atbreak = 1 + self.col = col + self.atbreak = data[-1] in string.whitespace + + +def test(file = None): + w = DumbWriter() + f = AbstractFormatter(w) + if file: + fp = open(file) + elif sys.argv[1:]: + fp = open(sys.argv[1]) + else: + fp = sys.stdin + while 1: + line = fp.readline() + if not line: + break + if line == '\n': + f.end_paragraph(1) + else: + f.add_flowing_data(line) + f.end_paragraph(0) + + +if __name__ == '__main__': + test() |