PageRenderTime 213ms CodeModel.GetById 35ms RepoModel.GetById 41ms app.codeStats 0ms

/desktop/core/ext-py/lxml/doc/mklatex.py

https://github.com/jcrobak/hue
Python | 318 lines | 301 code | 10 blank | 7 comment | 2 complexity | 302f9b0af3b74cd755071097d714fcca MD5 | raw file
  1. # The script builds the LaTeX documentation.
  2. # Testing:
  3. # python mklatex.py latex .. 1.0
  4. from docstructure import SITE_STRUCTURE, BASENAME_MAP
  5. import os, shutil, re, sys, datetime
  6. try:
  7. set
  8. except NameError:
  9. # Python 2.3
  10. from sets import Set as set
  11. TARGET_FILE = "lxmldoc.tex"
  12. RST2LATEX_OPTIONS = " ".join([
  13. # "--no-toc-backlinks",
  14. "--strip-comments",
  15. "--language en",
  16. # "--date",
  17. "--use-latex-footnotes",
  18. "--use-latex-citations",
  19. "--use-latex-toc",
  20. "--font-encoding=T1",
  21. "--output-encoding=utf-8",
  22. "--input-encoding=utf-8",
  23. "--graphicx-option=pdftex",
  24. ])
  25. htmlnsmap = {"h" : "http://www.w3.org/1999/xhtml"}
  26. replace_invalid = re.compile(r'[-_/.\s\\]').sub
  27. replace_content = re.compile("\{[^\}]*\}").sub
  28. replace_epydoc_macros = re.compile(r'(,\s*amssymb|dvips\s*,\s*)').sub
  29. replace_rst_macros = re.compile(r'(\\usepackage\{color}|\\usepackage\[[^]]*]\{hyperref})').sub
  30. BASENAME_MAP = BASENAME_MAP.copy()
  31. BASENAME_MAP.update({'api' : 'lxmlapi'})
  32. # LaTeX snippets
  33. DOCUMENT_CLASS = r"""
  34. \documentclass[10pt,english]{report}
  35. \usepackage[a4paper]{geometry}
  36. \parindent0pt
  37. \parskip1ex
  38. """
  39. PYGMENTS_IMPORT = r"""
  40. \usepackage{fancyvrb}
  41. \input{_part_pygments.tex}
  42. """
  43. EPYDOC_IMPORT = r"""
  44. \input{_part_epydoc.tex}
  45. """
  46. def write_chapter(master, title, filename):
  47. filename = os.path.join(os.path.dirname(filename),
  48. "_part_%s" % os.path.basename(filename))
  49. master.write(r"""
  50. \chapter{%s}
  51. \label{%s}
  52. \input{%s}
  53. """ % (title, filename, filename))
  54. # the program ----
  55. def rest2latex(script, source_path, dest_path):
  56. command = ('%s %s %s %s > %s' %
  57. (sys.executable, script, RST2LATEX_OPTIONS,
  58. source_path, dest_path))
  59. os.system(command)
  60. def build_pygments_macros(filename):
  61. from pygments.formatters import LatexFormatter
  62. text = LatexFormatter().get_style_defs()
  63. f = file(filename, "w")
  64. f.write(text)
  65. f.write('\n')
  66. f.close()
  67. def copy_epydoc_macros(src, dest, existing_header_lines):
  68. doc = file(src, 'r')
  69. out = file(dest, "w")
  70. for line in doc:
  71. if line.startswith('%% generator') or line.startswith('% generated by '):
  72. break
  73. if line.startswith('%') or \
  74. r'\documentclass' in line or \
  75. r'\makeindex' in line or \
  76. r'{inputenc}' in line:
  77. continue
  78. if line.startswith(r'\usepackage'):
  79. if line in existing_header_lines:
  80. continue
  81. if '{hyperref}' in line:
  82. line = line.replace('black', 'blue')
  83. out.write( replace_epydoc_macros('', line) )
  84. out.close()
  85. doc.close()
  86. def noop(input):
  87. return input
  88. counter_no = 0
  89. def tex_postprocess(src, dest, want_header = False, process_line=noop):
  90. """
  91. Postprocessing of the LaTeX file generated from ReST.
  92. Reads file src and saves to dest only the true content
  93. (without the document header and final) - so it is suitable
  94. to be used as part of the longer document.
  95. Returns the title of document
  96. If want_header is set, returns also the document header (as
  97. the list of lines).
  98. """
  99. title = ''
  100. header = []
  101. add_header_line = header.append
  102. global counter_no
  103. counter_no = counter_no + 1
  104. counter_text = "listcnt%d" % counter_no
  105. search_title = re.compile(r'\\title{([^}]*)}').search
  106. skipping = re.compile(r'(\\end{document}|\\tableofcontents)').search
  107. src = file(src)
  108. dest = file(dest, "w")
  109. iter_lines = iter(src.readlines())
  110. for l in iter_lines:
  111. l = process_line(l)
  112. if not l:
  113. continue
  114. if want_header:
  115. add_header_line(replace_rst_macros('', l))
  116. m = search_title(l)
  117. if m:
  118. title = m.group(0)
  119. if l.startswith("\\maketitle"):
  120. break
  121. for l in iter_lines:
  122. l = process_line(l)
  123. if skipping(l):
  124. # To-Do minitoc instead of tableofcontents
  125. continue
  126. elif "\hypertarget{old-versions}" in l:
  127. break
  128. elif "listcnt0" in l:
  129. l = l.replace("listcnt0", counter_text)
  130. dest.write(l)
  131. if not title:
  132. raise Exception("Bueee, no title")
  133. return title, header
  134. def publish(dirname, lxml_path, release):
  135. if not os.path.exists(dirname):
  136. os.mkdir(dirname)
  137. book_title = "lxml %s" % release
  138. doc_dir = os.path.join(lxml_path, 'doc')
  139. script = os.path.join(doc_dir, 'rest2latex.py')
  140. pubkey = os.path.join(doc_dir, 'pubkey.asc')
  141. shutil.copy(pubkey, dirname)
  142. # build pygments macros
  143. build_pygments_macros(os.path.join(dirname, '_part_pygments.tex'))
  144. # Used in postprocessing of generated LaTeX files
  145. header = []
  146. titles = {}
  147. replace_interdoc_hyperrefs = re.compile(
  148. r'\\href\{([^/}]+)[.]([^./}]+)\}').sub
  149. replace_docinternal_hyperrefs = re.compile(
  150. r'\\href\{\\#([^}]+)\}').sub
  151. replace_image_paths = re.compile(
  152. r'^(\\includegraphics{)').sub
  153. def build_hyperref(match):
  154. basename, extension = match.groups()
  155. outname = BASENAME_MAP.get(basename, basename)
  156. if '#' in extension:
  157. anchor = extension.split('#')[-1]
  158. return r"\hyperref[%s]" % anchor
  159. elif extension != 'html':
  160. return r'\href{http://codespeak.net/lxml/%s.%s}' % (
  161. outname, extension)
  162. else:
  163. return r"\hyperref[_part_%s.tex]" % outname
  164. def fix_relative_hyperrefs(line):
  165. line = replace_image_paths(r'\1../html/', line)
  166. if r'\href' not in line:
  167. return line
  168. line = replace_interdoc_hyperrefs(build_hyperref, line)
  169. return replace_docinternal_hyperrefs(r'\hyperref[\1]', line)
  170. # Building pages
  171. have_epydoc_macros = False
  172. for section, text_files in SITE_STRUCTURE:
  173. for filename in text_files:
  174. if filename.startswith('@'):
  175. continue
  176. #page_title = filename[1:]
  177. #url = href_map[page_title]
  178. #build_menu_entry(page_title, url, section_head)
  179. basename = os.path.splitext(os.path.basename(filename))[0]
  180. basename = BASENAME_MAP.get(basename, basename)
  181. outname = basename + '.tex'
  182. outpath = os.path.join(dirname, outname)
  183. path = os.path.join(doc_dir, filename)
  184. print "Creating %s" % outname
  185. rest2latex(script, path, outpath)
  186. final_name = os.path.join(dirname, os.path.dirname(outname),
  187. "_part_%s" % os.path.basename(outname))
  188. title, hd = tex_postprocess(outpath, final_name,
  189. want_header = not header,
  190. process_line=fix_relative_hyperrefs)
  191. if not header:
  192. header = hd
  193. titles[outname] = title
  194. # integrate generated API docs
  195. print "Integrating API docs"
  196. apidocsname = 'api.tex'
  197. apipath = os.path.join(dirname, apidocsname)
  198. tex_postprocess(apipath, os.path.join(dirname, "_part_%s" % apidocsname),
  199. process_line=fix_relative_hyperrefs)
  200. copy_epydoc_macros(apipath, os.path.join(dirname, '_part_epydoc.tex'),
  201. set(header))
  202. # convert CHANGES.txt
  203. print "Integrating ChangeLog"
  204. find_version_title = re.compile(
  205. r'(.*\\section\{)([0-9][^\} ]*)\s+\(([^)]+)\)(\}.*)').search
  206. def fix_changelog(line):
  207. m = find_version_title(line)
  208. if m:
  209. line = "%sChanges in version %s, released %s%s" % m.groups()
  210. else:
  211. line = line.replace(r'\subsection{', r'\subsection*{')
  212. return line
  213. chgname = 'changes-%s.tex' % release
  214. chgpath = os.path.join(dirname, chgname)
  215. rest2latex(script,
  216. os.path.join(lxml_path, 'CHANGES.txt'),
  217. chgpath)
  218. tex_postprocess(chgpath, os.path.join(dirname, "_part_%s" % chgname),
  219. process_line=fix_changelog)
  220. # Writing a master file
  221. print "Building %s\n" % TARGET_FILE
  222. master = file( os.path.join(dirname, TARGET_FILE), "w")
  223. for hln in header:
  224. if hln.startswith(r"\documentclass"):
  225. #hln = hln.replace('article', 'book')
  226. hln = DOCUMENT_CLASS
  227. elif hln.startswith("%% generator ") or hln.startswith("% generated "):
  228. master.write(EPYDOC_IMPORT)
  229. elif hln.startswith(r"\begin{document}"):
  230. # pygments and epydoc support
  231. master.write(PYGMENTS_IMPORT)
  232. elif hln.startswith(r"\title{"):
  233. hln = replace_content(
  234. r'{%s\\\\\\vspace{1cm}\\includegraphics[width=2.5cm]{../html/tagpython-big.png}}' % book_title, hln)
  235. elif hln.startswith(r"\date{"):
  236. hln = replace_content(
  237. r'{%s}' % datetime.date.today().isoformat(), hln)
  238. elif hln.startswith("pdftitle"):
  239. hln = replace_content(
  240. r'{%s}' % book_title, hln)
  241. master.write(hln)
  242. master.write("\\setcounter{page}{2}\n")
  243. master.write("\\tableofcontents\n")
  244. for section, text_files in SITE_STRUCTURE:
  245. master.write("\n\n\\part{%s}\n" % section)
  246. for filename in text_files:
  247. if filename.startswith('@'):
  248. continue
  249. #print "Not yet implemented: %s" % filename[1:]
  250. #page_title = filename[1:]
  251. #url = href_map[page_title]
  252. #build_menu_entry(page_title, url, section_head)
  253. else:
  254. basename = os.path.splitext(os.path.basename(filename))[0]
  255. basename = BASENAME_MAP.get(basename, basename)
  256. outname = basename + '.tex'
  257. write_chapter(master, titles[outname], outname)
  258. master.write("\\appendix\n")
  259. master.write("\\begin{appendix}\n")
  260. write_chapter(master, "Changes", chgname)
  261. write_chapter(master, "Generated API documentation", apidocsname)
  262. master.write("\\end{appendix}\n")
  263. master.write("\\end{document}\n")
  264. if __name__ == '__main__':
  265. publish(sys.argv[1], sys.argv[2], sys.argv[3])