mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			281 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			281 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 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 flush_softspace(self):
 | |
| 	if self.softspace:
 | |
| 	    self.nospace = self.softspace = 0
 | |
| 	    self.writer.send_flowing_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()
 | 
