PageRenderTime 56ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/desktop/core/ext-py/Twisted/twisted/lore/latex.py

https://github.com/jcrobak/hue
Python | 457 lines | 435 code | 13 blank | 9 comment | 5 complexity | 547a1e3e4f1c3b492090ff46854b68b3 MD5 | raw file
  1. # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. #
  4. from twisted.web import microdom, domhelpers
  5. from twisted.python import text, procutils
  6. import os, os.path, re, string
  7. from cStringIO import StringIO
  8. import urlparse
  9. import tree
  10. escapingRE = re.compile(r'([\[\]#$%&_{}^~\\])')
  11. lowerUpperRE = re.compile(r'([a-z])([A-Z])')
  12. def _escapeMatch(match):
  13. c = match.group()
  14. if c == '\\':
  15. return '$\\backslash$'
  16. elif c == '~':
  17. return '\\~{}'
  18. elif c == '^':
  19. return '\\^{}'
  20. elif c in '[]':
  21. return '{'+c+'}'
  22. else:
  23. return '\\' + c
  24. def latexEscape(text):
  25. text = escapingRE.sub(_escapeMatch, text)
  26. return text.replace('\n', ' ')
  27. entities = {'amp': '\&', 'gt': '>', 'lt': '<', 'quot': '"',
  28. 'copy': '\\copyright', 'mdash': '---', 'rdquo': '``',
  29. 'ldquo': "''"}
  30. def realpath(path):
  31. # Normalise path
  32. cwd = os.getcwd()
  33. path = os.path.normpath(os.path.join(cwd, path))
  34. if path.startswith(cwd + '/'):
  35. path = path[len(cwd)+1:]
  36. return path.replace('\\', '/') # windows slashes make LaTeX blow up
  37. def getLatexText(node, writer, filter=lambda x:x, entities=entities):
  38. if hasattr(node, 'eref'):
  39. return writer(entities.get(node.eref, ''))
  40. if hasattr(node, 'data'):
  41. return writer(filter(node.data))
  42. for child in node.childNodes:
  43. getLatexText(child, writer, filter, entities)
  44. class BaseLatexSpitter:
  45. def __init__(self, writer, currDir='.', filename=''):
  46. self.writer = writer
  47. self.currDir = currDir
  48. self.filename = filename
  49. def visitNode(self, node):
  50. if isinstance(node, microdom.Comment):
  51. return
  52. if not hasattr(node, 'tagName'):
  53. self.writeNodeData(node)
  54. return
  55. getattr(self, 'visitNode_'+node.tagName, self.visitNodeDefault)(node)
  56. def visitNodeDefault(self, node):
  57. self.writer(getattr(self, 'start_'+node.tagName, ''))
  58. for child in node.childNodes:
  59. self.visitNode(child)
  60. self.writer(getattr(self, 'end_'+node.tagName, ''))
  61. def visitNode_a(self, node):
  62. if node.hasAttribute('class'):
  63. if node.getAttribute('class').endswith('listing'):
  64. return self.visitNode_a_listing(node)
  65. if node.hasAttribute('href'):
  66. return self.visitNode_a_href(node)
  67. if node.hasAttribute('name'):
  68. return self.visitNode_a_name(node)
  69. self.visitNodeDefault(node)
  70. def visitNode_span(self, node):
  71. if not node.hasAttribute('class'):
  72. return self.visitNodeDefault(node)
  73. node.tagName += '_'+node.getAttribute('class')
  74. self.visitNode(node)
  75. visitNode_div = visitNode_span
  76. def visitNode_h1(self, node):
  77. pass
  78. def visitNode_style(self, node):
  79. pass
  80. class LatexSpitter(BaseLatexSpitter):
  81. baseLevel = 0
  82. diaHack = bool(procutils.which("dia"))
  83. def writeNodeData(self, node):
  84. buf = StringIO()
  85. getLatexText(node, buf.write, latexEscape)
  86. self.writer(buf.getvalue().replace('<', '$<$').replace('>', '$>$'))
  87. def visitNode_head(self, node):
  88. authorNodes = domhelpers.findElementsWithAttribute(node, 'rel', 'author')
  89. authorNodes = [n for n in authorNodes if n.tagName == 'link']
  90. if authorNodes:
  91. self.writer('\\author{')
  92. authors = []
  93. for aNode in authorNodes:
  94. name = aNode.getAttribute('title', '')
  95. href = aNode.getAttribute('href', '')
  96. if href.startswith('mailto:'):
  97. href = href[7:]
  98. if href:
  99. if name:
  100. name += ' '
  101. name += '$<$' + href + '$>$'
  102. if name:
  103. authors.append(name)
  104. self.writer(' \\and '.join(authors))
  105. self.writer('}')
  106. self.visitNodeDefault(node)
  107. def visitNode_pre(self, node):
  108. self.writer('\\begin{verbatim}\n')
  109. buf = StringIO()
  110. getLatexText(node, buf.write)
  111. self.writer(text.removeLeadingTrailingBlanks(buf.getvalue()))
  112. self.writer('\\end{verbatim}\n')
  113. def visitNode_code(self, node):
  114. fout = StringIO()
  115. getLatexText(node, fout.write, latexEscape)
  116. data = lowerUpperRE.sub(r'\1\\linebreak[1]\2', fout.getvalue())
  117. data = data[:1] + data[1:].replace('.', '.\\linebreak[1]')
  118. self.writer('\\texttt{'+data+'}')
  119. def visitNode_img(self, node):
  120. fileName = os.path.join(self.currDir, node.getAttribute('src'))
  121. target, ext = os.path.splitext(fileName)
  122. if self.diaHack and os.access(target + '.dia', os.R_OK):
  123. ext = '.dia'
  124. fileName = target + ext
  125. f = getattr(self, 'convert_'+ext[1:], None)
  126. if not f:
  127. return
  128. target = os.path.join(self.currDir, os.path.basename(target)+'.eps')
  129. f(fileName, target)
  130. target = os.path.basename(target)
  131. self._write_img(target)
  132. def _write_img(self, target):
  133. """Write LaTeX for image."""
  134. self.writer('\\begin{center}\\includegraphics[%%\n'
  135. 'width=1.0\n'
  136. '\\textwidth,height=1.0\\textheight,\nkeepaspectratio]'
  137. '{%s}\\end{center}\n' % target)
  138. def convert_png(self, src, target):
  139. # XXX there's a *reason* Python comes with the pipes module -
  140. # someone fix this to use it.
  141. r = os.system('pngtopnm "%s" | pnmtops -noturn > "%s"' % (src, target))
  142. if r != 0:
  143. raise OSError(r)
  144. def convert_dia(self, src, target):
  145. # EVIL DISGUSTING HACK
  146. data = os.popen("gunzip -dc %s" % (src)).read()
  147. pre = '<dia:attribute name="scaling">\n <dia:real val="1"/>'
  148. post = '<dia:attribute name="scaling">\n <dia:real val="0.5"/>'
  149. f = open('%s_hacked.dia' % (src), 'wb')
  150. f.write(data.replace(pre, post))
  151. f.close()
  152. os.system('gzip %s_hacked.dia' % (src,))
  153. os.system('mv %s_hacked.dia.gz %s_hacked.dia' % (src,src))
  154. # Let's pretend we never saw that.
  155. # Silly dia needs an X server, even though it doesn't display anything.
  156. # If this is a problem for you, try using Xvfb.
  157. os.system("dia %s_hacked.dia -n -e %s" % (src, target))
  158. def visitNodeHeader(self, node):
  159. level = (int(node.tagName[1])-2)+self.baseLevel
  160. self.writer('\n\n\\'+level*'sub'+'section{')
  161. spitter = HeadingLatexSpitter(self.writer, self.currDir, self.filename)
  162. spitter.visitNodeDefault(node)
  163. self.writer('}\n')
  164. def visitNode_a_listing(self, node):
  165. fileName = os.path.join(self.currDir, node.getAttribute('href'))
  166. self.writer('\\begin{verbatim}\n')
  167. lines = map(string.rstrip, open(fileName).readlines())
  168. lines = lines[int(node.getAttribute('skipLines', 0)):]
  169. self.writer(text.removeLeadingTrailingBlanks('\n'.join(lines)))
  170. self.writer('\\end{verbatim}')
  171. # Write a caption for this source listing
  172. fileName = os.path.basename(fileName)
  173. caption = domhelpers.getNodeText(node)
  174. if caption == fileName:
  175. caption = 'Source listing'
  176. self.writer('\parbox[b]{\linewidth}{\\begin{center}%s --- '
  177. '\\begin{em}%s\\end{em}\\end{center}}'
  178. % (latexEscape(caption), latexEscape(fileName)))
  179. def visitNode_a_href(self, node):
  180. supported_schemes=['http', 'https', 'ftp', 'mailto']
  181. href = node.getAttribute('href')
  182. if urlparse.urlparse(href)[0] in supported_schemes:
  183. text = domhelpers.getNodeText(node)
  184. self.visitNodeDefault(node)
  185. if text != href:
  186. self.writer('\\footnote{%s}' % latexEscape(href))
  187. else:
  188. path, fragid = (href.split('#', 1) + [None])[:2]
  189. if path == '':
  190. path = self.filename
  191. else:
  192. path = os.path.join(os.path.dirname(self.filename), path)
  193. #if path == '':
  194. #path = os.path.basename(self.filename)
  195. #else:
  196. # # Hack for linking to man pages from howtos, i.e.
  197. # # ../doc/foo-man.html -> foo-man.html
  198. # path = os.path.basename(path)
  199. path = realpath(path)
  200. if fragid:
  201. ref = path + 'HASH' + fragid
  202. else:
  203. ref = path
  204. self.writer('\\textit{')
  205. self.visitNodeDefault(node)
  206. self.writer('}')
  207. self.writer('\\loreref{%s}' % ref)
  208. def visitNode_a_name(self, node):
  209. #self.writer('\\label{%sHASH%s}' % (os.path.basename(self.filename),
  210. # node.getAttribute('name')))
  211. self.writer('\\label{%sHASH%s}' % (realpath(self.filename),
  212. node.getAttribute('name')))
  213. self.visitNodeDefault(node)
  214. def visitNode_table(self, node):
  215. rows = [[col for col in row.childNodes
  216. if getattr(col, 'tagName', None) in ('th', 'td')]
  217. for row in node.childNodes if getattr(row, 'tagName', None)=='tr']
  218. numCols = 1+max([len(row) for row in rows])
  219. self.writer('\\begin{table}[ht]\\begin{center}')
  220. self.writer('\\begin{tabular}{@{}'+'l'*numCols+'@{}}')
  221. for row in rows:
  222. th = 0
  223. for col in row:
  224. self.visitNode(col)
  225. self.writer('&')
  226. if col.tagName == 'th':
  227. th = 1
  228. self.writer('\\\\\n') #\\ ends lines
  229. if th:
  230. self.writer('\\hline\n')
  231. self.writer('\\end{tabular}\n')
  232. if node.hasAttribute('title'):
  233. self.writer('\\caption{%s}'
  234. % latexEscape(node.getAttribute('title')))
  235. self.writer('\\end{center}\\end{table}\n')
  236. def visitNode_span_footnote(self, node):
  237. self.writer('\\footnote{')
  238. spitter = FootnoteLatexSpitter(self.writer, self.currDir, self.filename)
  239. spitter.visitNodeDefault(node)
  240. self.writer('}')
  241. def visitNode_span_index(self, node):
  242. self.writer('\\index{%s}\n' % node.getAttribute('value'))
  243. self.visitNodeDefault(node)
  244. visitNode_h2 = visitNode_h3 = visitNode_h4 = visitNodeHeader
  245. start_title = '\\title{'
  246. end_title = '}\n'
  247. start_sub = '$_{'
  248. end_sub = '}$'
  249. start_sup = '$^{'
  250. end_sup = '}$'
  251. start_html = '''\\documentclass{article}
  252. \\newcommand{\\loreref}[1]{%
  253. \\ifthenelse{\\value{page}=\\pageref{#1}}%
  254. { (this page)}%
  255. { (page \\pageref{#1})}%
  256. }'''
  257. start_body = '\\begin{document}\n\\maketitle\n'
  258. end_body = '\\end{document}'
  259. start_dl = '\\begin{description}\n'
  260. end_dl = '\\end{description}\n'
  261. start_ul = '\\begin{itemize}\n'
  262. end_ul = '\\end{itemize}\n'
  263. start_ol = '\\begin{enumerate}\n'
  264. end_ol = '\\end{enumerate}\n'
  265. start_li = '\\item '
  266. end_li = '\n'
  267. start_dt = '\\item['
  268. end_dt = ']'
  269. end_dd = '\n'
  270. start_p = '\n\n'
  271. start_strong = start_em = '\\begin{em}'
  272. end_strong = end_em = '\\end{em}'
  273. start_q = "``"
  274. end_q = "''"
  275. start_div_note = '\\begin{quotation}\\textbf{Note:}'
  276. end_div_note = '\\end{quotation}'
  277. start_th = '\\textbf{'
  278. end_th = '}'
  279. class SectionLatexSpitter(LatexSpitter):
  280. baseLevel = 1
  281. start_title = '\\section{'
  282. def visitNode_title(self, node):
  283. self.visitNodeDefault(node)
  284. #self.writer('\\label{%s}}\n' % os.path.basename(self.filename))
  285. self.writer('\\label{%s}}\n' % realpath(self.filename))
  286. end_title = end_body = start_body = start_html = ''
  287. class ChapterLatexSpitter(SectionLatexSpitter):
  288. baseLevel = 0
  289. start_title = '\\chapter{'
  290. class HeadingLatexSpitter(BaseLatexSpitter):
  291. start_q = "``"
  292. end_q = "''"
  293. writeNodeData = LatexSpitter.writeNodeData.im_func
  294. class FootnoteLatexSpitter(LatexSpitter):
  295. """For multi-paragraph footnotes, this avoids having an empty leading
  296. paragraph."""
  297. start_p = ''
  298. def visitNode_span_footnote(self, node):
  299. self.visitNodeDefault(node)
  300. def visitNode_p(self, node):
  301. self.visitNodeDefault(node)
  302. self.start_p = LatexSpitter.start_p
  303. class BookLatexSpitter(LatexSpitter):
  304. def visitNode_body(self, node):
  305. tocs=domhelpers.locateNodes([node], 'class', 'toc')
  306. domhelpers.clearNode(node)
  307. if len(tocs):
  308. toc=tocs[0]
  309. node.appendChild(toc)
  310. self.visitNodeDefault(node)
  311. def visitNode_link(self, node):
  312. if not node.hasAttribute('rel'):
  313. return self.visitNodeDefault(node)
  314. node.tagName += '_'+node.getAttribute('rel')
  315. self.visitNode(node)
  316. def visitNode_link_author(self, node):
  317. self.writer('\\author{%s}\n' % node.getAttribute('text'))
  318. def visitNode_link_stylesheet(self, node):
  319. if node.hasAttribute('type') and node.hasAttribute('href'):
  320. if node.getAttribute('type')=='application/x-latex':
  321. packagename=node.getAttribute('href')
  322. packagebase,ext=os.path.splitext(packagename)
  323. self.writer('\\usepackage{%s}\n' % packagebase)
  324. start_html = r'''\documentclass[oneside]{book}
  325. \usepackage{graphicx}
  326. \usepackage{times,mathptmx}
  327. '''
  328. start_body = r'''\begin{document}
  329. \maketitle
  330. \tableofcontents
  331. '''
  332. start_li=''
  333. end_li=''
  334. start_ul=''
  335. end_ul=''
  336. def visitNode_a(self, node):
  337. if node.hasAttribute('class'):
  338. a_class=node.getAttribute('class')
  339. if a_class.endswith('listing'):
  340. return self.visitNode_a_listing(node)
  341. else:
  342. return getattr(self, 'visitNode_a_%s' % a_class)(node)
  343. if node.hasAttribute('href'):
  344. return self.visitNode_a_href(node)
  345. if node.hasAttribute('name'):
  346. return self.visitNode_a_name(node)
  347. self.visitNodeDefault(node)
  348. def visitNode_a_chapter(self, node):
  349. self.writer('\\chapter{')
  350. self.visitNodeDefault(node)
  351. self.writer('}\n')
  352. def visitNode_a_sect(self, node):
  353. base,ext=os.path.splitext(node.getAttribute('href'))
  354. self.writer('\\input{%s}\n' % base)
  355. def processFile(spitter, fin):
  356. dom = microdom.parse(fin).documentElement
  357. spitter.visitNode(dom)
  358. def convertFile(filename, spitterClass):
  359. fout = open(os.path.splitext(filename)[0]+".tex", 'w')
  360. spitter = spitterClass(fout.write, os.path.dirname(filename), filename)
  361. fin = open(filename)
  362. processFile(spitter, fin)
  363. fin.close()
  364. fout.close()