|
@@ -12,7 +12,135 @@
|
|
|
import os
|
|
|
import re
|
|
|
import sys
|
|
|
-import textwrap
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+NO_HYPHENATE=set("""
|
|
|
+pf-divert
|
|
|
+""".split())
|
|
|
+
|
|
|
+LASTLINE_UNDERFLOW_EXPONENT = 1
|
|
|
+LASTLINE_UNDERFLOW_PENALTY = 1
|
|
|
+
|
|
|
+UNDERFLOW_EXPONENT = 3
|
|
|
+UNDERFLOW_PENALTY = 1
|
|
|
+
|
|
|
+OVERFLOW_EXPONENT = 4
|
|
|
+OVERFLOW_PENALTY = 2000
|
|
|
+
|
|
|
+ORPHAN_PENALTY = 10000
|
|
|
+
|
|
|
+def generate_wrapping(words, divisions):
|
|
|
+ lines = []
|
|
|
+ last = 0
|
|
|
+ for i in divisions:
|
|
|
+ w = words[last:i]
|
|
|
+ last = i
|
|
|
+ line = " ".join(w).replace("\xff ","-").replace("\xff","-")
|
|
|
+ lines.append(line)
|
|
|
+ return lines
|
|
|
+
|
|
|
+def wrapping_quality(words, divisions, width1, width2):
|
|
|
+ total = 0.0
|
|
|
+
|
|
|
+ lines = generate_wrapping(words, divisions)
|
|
|
+ for line in lines:
|
|
|
+ length = len(line)
|
|
|
+ if line is lines[0]:
|
|
|
+ width = width1
|
|
|
+ else:
|
|
|
+ width = width2
|
|
|
+
|
|
|
+ if length > width:
|
|
|
+ total += OVERFLOW_PENALTY * (
|
|
|
+ (length - width) ** OVERFLOW_EXPONENT )
|
|
|
+ else:
|
|
|
+ if line is lines[-1]:
|
|
|
+ e,p = (LASTLINE_UNDERFLOW_EXPONENT, LASTLINE_UNDERFLOW_PENALTY)
|
|
|
+ if " " not in line:
|
|
|
+ total += ORPHAN_PENALTY
|
|
|
+ else:
|
|
|
+ e,p = (UNDERFLOW_EXPONENT, UNDERFLOW_PENALTY)
|
|
|
+
|
|
|
+ total += p * ((width - length) ** e)
|
|
|
+
|
|
|
+ return total
|
|
|
+
|
|
|
+def wrap_graf(words, prefix_len1=0, prefix_len2=0, width=72):
|
|
|
+ wrapping_after = [ (0,), ]
|
|
|
+
|
|
|
+ w1 = width - prefix_len1
|
|
|
+ w2 = width - prefix_len2
|
|
|
+
|
|
|
+ for i in range(1, len(words)+1):
|
|
|
+ best_so_far = None
|
|
|
+ best_score = 1e300
|
|
|
+ for j in range(i):
|
|
|
+ t = wrapping_after[j]
|
|
|
+ t1 = t[:-1] + (i,)
|
|
|
+ t2 = t + (i,)
|
|
|
+ wq1 = wrapping_quality(words, t1, w1, w2)
|
|
|
+ wq2 = wrapping_quality(words, t2, w1, w2)
|
|
|
+
|
|
|
+ if wq1 < best_score:
|
|
|
+ best_so_far = t1
|
|
|
+ best_score = wq1
|
|
|
+ if wq2 < best_score:
|
|
|
+ best_so_far = t2
|
|
|
+ best_score = wq2
|
|
|
+ wrapping_after.append( best_so_far )
|
|
|
+
|
|
|
+ lines = generate_wrapping(words, wrapping_after[-1])
|
|
|
+
|
|
|
+ return lines
|
|
|
+
|
|
|
+def hyphenateable(word):
|
|
|
+ if re.match(r'^[^\d\-].*-', word):
|
|
|
+ stripped = re.sub(r'^\W+','',word)
|
|
|
+ stripped = re.sub(r'\W+$','',word)
|
|
|
+ return stripped not in NO_HYPHENATE
|
|
|
+ else:
|
|
|
+ return False
|
|
|
+
|
|
|
+def split_paragraph(s):
|
|
|
+ "Split paragraph into words; tuned for Tor."
|
|
|
+
|
|
|
+ r = []
|
|
|
+ for word in s.split():
|
|
|
+ if hyphenateable(word):
|
|
|
+ while "-" in word:
|
|
|
+ a,word = word.split("-",1)
|
|
|
+ r.append(a+"\xff")
|
|
|
+ r.append(word)
|
|
|
+ return r
|
|
|
+
|
|
|
+def fill(text, width, initial_indent, subsequent_indent):
|
|
|
+ words = split_paragraph(text)
|
|
|
+ lines = wrap_graf(words, len(initial_indent), len(subsequent_indent),
|
|
|
+ width)
|
|
|
+ res = [ initial_indent, lines[0], "\n" ]
|
|
|
+ for line in lines[1:]:
|
|
|
+ res.append(subsequent_indent)
|
|
|
+ res.append(line)
|
|
|
+ res.append("\n")
|
|
|
+ return "".join(res)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
TP_MAINHEAD = 0
|
|
|
TP_HEADTEXT = 1
|
|
@@ -108,10 +236,11 @@ class ChangeLog(object):
|
|
|
if indent2 == -1:
|
|
|
indent2 = indent1
|
|
|
text = " ".join(re.sub(r'\s+', ' ', line.strip()) for line in par)
|
|
|
- print textwrap.fill(text, width=72,
|
|
|
- initial_indent=" "*indent1,
|
|
|
- subsequent_indent=" "*indent2,
|
|
|
- break_on_hyphens=False)
|
|
|
+
|
|
|
+ sys.stdout.write(fill(text,
|
|
|
+ width=72,
|
|
|
+ initial_indent=" "*indent1,
|
|
|
+ subsequent_indent=" "*indent2))
|
|
|
|
|
|
def dump(self):
|
|
|
print self.mainhead
|