mathmpl.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import hashlib
  2. import os
  3. import sys
  4. from docutils import nodes
  5. from docutils.parsers.rst import Directive, directives
  6. import sphinx
  7. from matplotlib import rcParams
  8. from matplotlib import cbook
  9. from matplotlib.mathtext import MathTextParser
  10. rcParams['mathtext.fontset'] = 'cm'
  11. mathtext_parser = MathTextParser("Bitmap")
  12. # Define LaTeX math node:
  13. class latex_math(nodes.General, nodes.Element):
  14. pass
  15. def fontset_choice(arg):
  16. return directives.choice(arg, ['cm', 'stix', 'stixsans'])
  17. def math_role(role, rawtext, text, lineno, inliner,
  18. options={}, content=[]):
  19. i = rawtext.find('`')
  20. latex = rawtext[i+1:-1]
  21. node = latex_math(rawtext)
  22. node['latex'] = latex
  23. node['fontset'] = options.get('fontset', 'cm')
  24. return [node], []
  25. math_role.options = {'fontset': fontset_choice}
  26. @cbook.deprecated("3.1", alternative="MathDirective")
  27. def math_directive(name, arguments, options, content, lineno,
  28. content_offset, block_text, state, state_machine):
  29. latex = ''.join(content)
  30. node = latex_math(block_text)
  31. node['latex'] = latex
  32. node['fontset'] = options.get('fontset', 'cm')
  33. return [node]
  34. class MathDirective(Directive):
  35. has_content = True
  36. required_arguments = 0
  37. optional_arguments = 0
  38. final_argument_whitespace = False
  39. option_spec = {'fontset': fontset_choice}
  40. def run(self):
  41. latex = ''.join(self.content)
  42. node = latex_math(self.block_text)
  43. node['latex'] = latex
  44. node['fontset'] = self.options.get('fontset', 'cm')
  45. return [node]
  46. # This uses mathtext to render the expression
  47. def latex2png(latex, filename, fontset='cm'):
  48. latex = "$%s$" % latex
  49. orig_fontset = rcParams['mathtext.fontset']
  50. rcParams['mathtext.fontset'] = fontset
  51. if os.path.exists(filename):
  52. depth = mathtext_parser.get_depth(latex, dpi=100)
  53. else:
  54. try:
  55. depth = mathtext_parser.to_png(filename, latex, dpi=100)
  56. except Exception:
  57. cbook._warn_external("Could not render math expression %s" % latex,
  58. Warning)
  59. depth = 0
  60. rcParams['mathtext.fontset'] = orig_fontset
  61. sys.stdout.write("#")
  62. sys.stdout.flush()
  63. return depth
  64. # LaTeX to HTML translation stuff:
  65. def latex2html(node, source):
  66. inline = isinstance(node.parent, nodes.TextElement)
  67. latex = node['latex']
  68. name = 'math-%s' % hashlib.md5(latex.encode()).hexdigest()[-10:]
  69. destdir = os.path.join(setup.app.builder.outdir, '_images', 'mathmpl')
  70. if not os.path.exists(destdir):
  71. os.makedirs(destdir)
  72. dest = os.path.join(destdir, '%s.png' % name)
  73. path = '/'.join((setup.app.builder.imgpath, 'mathmpl'))
  74. depth = latex2png(latex, dest, node['fontset'])
  75. if inline:
  76. cls = ''
  77. else:
  78. cls = 'class="center" '
  79. if inline and depth != 0:
  80. style = 'style="position: relative; bottom: -%dpx"' % (depth + 1)
  81. else:
  82. style = ''
  83. return '<img src="%s/%s.png" %s%s/>' % (path, name, cls, style)
  84. def setup(app):
  85. setup.app = app
  86. # Add visit/depart methods to HTML-Translator:
  87. def visit_latex_math_html(self, node):
  88. source = self.document.attributes['source']
  89. self.body.append(latex2html(node, source))
  90. def depart_latex_math_html(self, node):
  91. pass
  92. # Add visit/depart methods to LaTeX-Translator:
  93. def visit_latex_math_latex(self, node):
  94. inline = isinstance(node.parent, nodes.TextElement)
  95. if inline:
  96. self.body.append('$%s$' % node['latex'])
  97. else:
  98. self.body.extend(['\\begin{equation}',
  99. node['latex'],
  100. '\\end{equation}'])
  101. def depart_latex_math_latex(self, node):
  102. pass
  103. app.add_node(latex_math,
  104. html=(visit_latex_math_html, depart_latex_math_html),
  105. latex=(visit_latex_math_latex, depart_latex_math_latex))
  106. app.add_role('mathmpl', math_role)
  107. app.add_directive('mathmpl', MathDirective)
  108. if sphinx.version_info < (1, 8):
  109. app.add_role('math', math_role)
  110. app.add_directive('math', MathDirective)
  111. metadata = {'parallel_read_safe': True, 'parallel_write_safe': True}
  112. return metadata