diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/escapeTeXt |
Initial commit
Diffstat (limited to 'support/escapeTeXt')
-rw-r--r-- | support/escapeTeXt/README | 39 | ||||
-rw-r--r-- | support/escapeTeXt/escapeTeXt.py | 201 | ||||
-rw-r--r-- | support/escapeTeXt/escapeTeXt_test.py | 93 |
3 files changed, 333 insertions, 0 deletions
diff --git a/support/escapeTeXt/README b/support/escapeTeXt/README new file mode 100644 index 0000000000..6b4fa277b6 --- /dev/null +++ b/support/escapeTeXt/README @@ -0,0 +1,39 @@ +README for: escapeTeXt.py + +This Python program tries to turn plain text into something that can +get through LaTeX. The motivating application is to put text gathered +in a web application into a report generated in LaTeX. + +1) This program can be called from the command line. These commands +run Project Gutenberg's version of Jack London's _Call Of the Wild_. + ./escapeTeXt.py -l -ocallw10.ltx < callw10.txt + pdflatex callw10.ltx + acroread callw10.pdf +The result is not pretty; the program makes no attempt to recognize +sections, etc. (For this, there are programs on the web specialized +to do Gutenberg texts.) But the text makes it through. + +Here is the usage statement for the command line program: + convertTeXt.py: Convert plain text so it may make it through LaTeX + convertTeXt.py [options] + where the options are + -f filename (default None) file to read from; if None then stdin is used + -o filename (default None) file to write to; if None then stdout is used + -c Turn off the attempt to balance open and closed quotes + -r Turn off the refinements (em dashes, en dashes ..) + -l Add simple LaTeX header and footer + -v (default True) sets verbose output + --help or -? Give this usage statement + + +2) The routines can also be called from other Python programs. Thus, in +a makeReport.py program the code + import escapeTeXt + : + s=getUserResponseFromDb() + result=escapeTeXt.escapetext(s).output() +gets as "result" the text that will then be printed into the LaTeX file. + + +Author: Jim Hefferon, based on txt2latex by Tristan Miller +Date: 2006-Aug-27 diff --git a/support/escapeTeXt/escapeTeXt.py b/support/escapeTeXt/escapeTeXt.py new file mode 100644 index 0000000000..4f5a9e15c0 --- /dev/null +++ b/support/escapeTeXt/escapeTeXt.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python +# escapeTeXt.py +# Convert text to something LaTeX is less likely to choke on. +# 2006-Aug-24 Jim Hefferon Written. Based on stuff from Tristan Miller and +# Allin Cottrell + +# Like txt2latex but beyond being callable from the command line +# ./escapeTeXt.py < file1.txt > file1.ltx +# it is callable from a program as a function. +# import escapeTeXt +# result=escapeTeXt.escapetext(string).output() + +# TODO +# * at sign? +# * copyright, trademark? +# * allow conversion of "_Great Gatsby_" to "\textit{Great Gatsby}" +# * allow conversion of "I *really* do" to "I \emph{really} do" +# * add refinement of recognizing fractions "2/3" ==> "$2/3$"? + +import sys, re +from getopt import getopt, GetoptError + +DEBUG=False # currently unused + +class escapetextError(StandardError): # currently unused + pass + +class escapetext(object): + """Turn plain text into strings that have a chance of making it through + LaTeX. + """ + def __init__(self,txt='',countQuotes=True,refinements=False): + """Initialize instance. Build up the text in parts (say, as lines + from the input source) with this initializing, and using the feed() + routine. Then we dump it with output(). + txt='' Text to start with + countQuotes=True Should we try to match open with closed quotes? + refinements=False Should we try to get fancy? (Try to fix + en dashes, em dashes, the 'p. 2' construct.) + """ + self.countQuotes=countQuotes + self.refinements=refinements + self.singleQuoteCount=0 + self.doubleQuoteCount=0 + self.strings=[txt] # array of strings that we have been fed + + def feed(self,txt): + """Add more text to the pile. + txt Text to add + """ + self.strings.append(txt) + + pageNumberPattern="(\s)(p.)(\s)(\d)" # like 'p. 23' + pageNumberRE=re.compile(pageNumberPattern) + enDashPattern="(\d)-(\d)" # like '7-9' + enDashRE=re.compile(enDashPattern) + emDashPattern="((\D)-{1,2}(\D))" # like 'but-no' + emDashRE=re.compile(emDashPattern) + def texifyString(self,s): + """Convert a string to a form more acceptable to LaTeX. + s Plain text string. + """ + # Do simple substitutions + for (old,new) in [(u"\\",u"{\\textbackslash}"), # backslashes + (u"{",u"\\{"), # open curly braces + (u"}",u"\\}"), # close curly braces + (u"\\{\\textbackslash\\}",u"{\\textbackslash}"), # have to fix up the effect of the prior two on the first line's result + (u"$",u"\\$"), # dollar signs + (u"%",u"\\%"), # percent signs + (u"_",u"\\_"), # underscores + (u"&",u"\\&"), # ampersands + (u"#",u"\\#"), # sharp signs + (u"<",u"{\\textless}"), # less than + (u">",u"{\\textgreater}"), # greater than + ]: + s=s.replace(old,new) + if self.countQuotes: # make even-numbered instances open, odd closed + tmpS="" + priorC=None + for c in s: + if c=="'": + if (self.singleQuoteCount % 2)==0: + if priorC=='"': # open double quote followed by open single quote + tmpS+=u"\," # add a thinspace + tmpS+=u"`" # open single quote + else: + tmpS+=u"'" # close single quote + self.singleQuoteCount+=1 + elif c=='"': + if (self.doubleQuoteCount % 2)==0: + tmpS+=u"``" # open double quotes + else: + if priorC=="'": # single close quote followed by double close quote + tmpS+=u"\," # add a thinspace + tmpS+=u"''" # close double quotes + self.doubleQuoteCount+=1 + else: + tmpS+=c + priorC=c + s=tmpS + else: + s=s.replace(u'`',u"\verb!'!") # maybe u"\textquotesingle" if you \usepackage{textcomp}? + s=s.replace(u'"',u'\verb!"!') # maybe u"$^\\textquotestraightdblbase$" if you \usepackage{textcomp}? + if self.refinements: + s=escapetext.pageNumberRE.sub(r'\1\2\,\4',s) # replace " p. 2" with " p.\,2" + s=escapetext.enDashRE.sub(r'\1--\2',s) # replace "2-3" with "2--3" + s=escapetext.emDashRE.sub(r'\2---\3',s) # replace "but -- no" with "but --- no" + return s + + def output(self,restartCounts=False): + """Return a string that has the parts escaped. That clears the + internal buffer. + restartCounts=False Reset the counters for whether a single or + double quote is opening or closed + """ + r=map(self.texifyString,self.strings) + self.strings=[] + if restartCounts: + self.singleQuoteCount=0 + self.doubleQuoteCount=0 + return "".join(r) + +latexHead="""\\documentclass{article} +\\begin{document} +""" +latexFoot="""\\end{document}""" + +#............... script main body +if __name__=='__main__': + # parse args + inputFilename=None + outputFilename=None + countQuotes=True + refinements=True + latexWrap=False + verbose=False # currently unused + + usage="""%s: Convert plain text so it may make it through LaTeX + %s [options] +where the options are + -f filename (default %s) file to read from; if None then stdin is used + -o filename (default %s) file to write to; if None then stdout is used + -c Turn off the attempt to balance open and closed quotes + -r Turn off the refinements (em dashes, en dashes ..) + -l Add simple LaTeX header and footer + -v (default %s) sets verbose output + --help or -? Give this usage statement""" % (sys.argv[0],sys.argv[0],repr(inputFilename),repr(outputFilename),repr(verbose)) + + shortOptions='f:o:lcr?v' + longOptions=['help'] + try: + (opts,args_proper)=getopt(sys.argv[1:],shortOptions,longOptions) + except GetoptError, err: + print "ERROR: Unable to parse the command line arguments: %s" % (err,) + print usage + sys.exit(1) + for (option,parameter) in opts: + if option=='-f': + inputFilename=parameter + elif option=='-o': + outputFilename=parameter + elif option=='-c': + countQuotes=False + elif option=='-r': + refinements=False + elif option=='-l': + latexWrap=True + elif option=='-v': + verbose=True + elif (option=='-?' + or option=='--help'): + print usage + sys.exit(0) + else: + print "Unknown option: ",option + sys.exit(2) + # Done getting options; now the logic + if (inputFilename is None): + inFile=sys.stdin + else: + inFile=open(inputFilename,'r') + if (outputFilename is None): + outFile=sys.stdout + else: + outFile=open(outputFilename,'w') + + et=escapetext(countQuotes=countQuotes,refinements=refinements) + line=inFile.readline() + if latexWrap: + outFile.write(latexHead) + while line: + et.feed(line) + outFile.write(et.output()) + line=inFile.readline() + if latexWrap: + outFile.write(latexFoot) + inFile.close() + outFile.close() + + if verbose: + print "#s: done" % (sys.argv[0],) diff --git a/support/escapeTeXt/escapeTeXt_test.py b/support/escapeTeXt/escapeTeXt_test.py new file mode 100644 index 0000000000..e60953385a --- /dev/null +++ b/support/escapeTeXt/escapeTeXt_test.py @@ -0,0 +1,93 @@ +#! /usr/bin/python2.3 -u +# escapeTeXt_test.py +# Do unit testing of the escapeTeXt routines +# 2006-Aug-25 Jim Hefferon +import re + +import unittest + +from escapeTeXt import * + +class escapetext_test(unittest.TestCase): + """Test the escapetext class + """ + def test_generic(self): + # sanity check + s="Hello, World!" + et=escapetext(s) + self.failUnlessEqual(s, + et.output()) + # does it substitute at all? + s="abc $ def" + t="abc \\$ def" + self.failUnlessEqual(t, + escapetext(s).output()) + # bunch of routine substitutions + expected=[("abc { def","abc \\{ def"), + ("abc } def","abc \\} def"), + ("abc % def","abc \\% def"), + ("abc _ def","abc \\_ def"), + ("abc & def","abc \\& def"), + ("abc # def","abc \\# def"), + ("abc < def","abc {\\textless} def"), + ("abc > def","abc {\\textgreater} def"), + ] + for (s,t) in expected: + self.failUnlessEqual(t, + escapetext(s).output()) + # does it substitute twice on one row? + s="abc $ def % ghi" + t="abc \\$ def \\% ghi" + self.failUnlessEqual(t, + escapetext(s).output()) + # backslash + s="abc \ def" + t="abc {\\textbackslash} def" + self.failUnlessEqual(t, + escapetext(s).output()) + # single quotes + s="abc 'def' ghi" + t="abc `def' ghi" + self.failUnlessEqual(t, + escapetext(s).output()) + # single quote next to double quote + s="\"'abc'\" def" + t="``\\,`abc'\\,'' def" + self.failUnlessEqual(t, + escapetext(s).output()) + + def test_refinements(self): + # single quote next to double quote + s="look on p. 23." + t="look on p.\\,23." + self.failUnlessEqual(t, + escapetext(s,refinements=True).output()) + # en dash + s="pages 2-3" + t="pages 2--3" + self.failUnlessEqual(t, + escapetext(s,refinements=True).output()) + # em dash + s="but-no" + t="but---no" + self.failUnlessEqual(t, + escapetext(s,refinements=True).output()) + + def test_feed(self): + # single quote next to double quote + s1="abc $ def" + s2="ghi % jkl" + t1="abc \\$ defghi \\% jkl" + et=escapetext(s1) + et.feed(s2) + self.failUnlessEqual(t1, + et.output()) + s3='mno "yes" pqr' + t2="mno ``yes'' pqr" + et.feed(s3) + self.failUnlessEqual(t2, + et.output()) + + +if __name__=='__main__': + unittest.main() |