Math Latex宏在reStructuredText和Sphinx中进行替换



使用Sphinx和reStructuredText,是否可以定义数学宏来在数学Latex公式中进行替换?

基本上,我想要这样的东西

.. |bnabla| replace:: :math:`boldsymbol{nabla}`
.. |vv| replace:: :math:`textbf{v}`
.. math:: rho mbox{D}_t |vv| = - |bnabla| p + rho textbf{g} + eta |bnabla|^2 |vv|,
where |vv| is the velocity and |bnabla| is the nabla operator.
Then follow many other equations with |vv| and |bnabla|...

但它根本不起作用。首先,标志在数学模式中不被替换,其次,即使它们被替换,:math:语句在.. math::块中也不起作用。在狮身人面像中改变这两种行为是个好主意吗?

另一个解决方案是使用latex宏,如本问题所示。在Sphinx中创建latex数学宏,但我认为最终代码将更容易阅读,如我的示例中使用本地rst替换。我希望有清晰的公式,也可以在文本模式下阅读。

此外,我正在使用MathJax扩展,因此无法使用变量pngmath_latex_preamble。我可以将此解决方案用于MathJaxhttps://stackoverflow.com/a/19268549/1779806但这似乎相当复杂,而且通过"局部"的rst替换,代码会更加清晰。

编辑:

我意识到,对于许多人来说,直接在reStructuredText中(即在Python包docutils中)实现mathmacro替换指令将非常方便和有用。新指令应该是这样的:

定义:

.. |bnabla| mathmacro:: boldsymbol{nabla}
.. |vv| mathmacro:: textbf{v}

这些数学宏可以像一样直接包含在文本中

|bnabla| is the nabla operator,

它应该产生一个像这样的内联方程

:math:`boldsymbol{nabla}` is the nabla operator.

这些宏也可以包含在内联方程式中:

This is an inline equation :math:`vv = bnabla f`, 

应该相当于

:math:`textbf{v} = boldsymbol{nabla}f`

它们也可以包含在块方程中

.. math:: vv = bnabla f

应该相当于

.. math:: textbf{v} = boldsymbol{nabla}f.

然而,我真的不熟悉docutils内部是如何工作的。我特别注意到,MathBlock指令(在docutils/parsers/rst/directives/body.py中定义)不调用任何对其输入的解析,因此在数学模式下没有替换。

我不知道是否有可能改变替换指令的行为,使替换成为clever,并适应文本、内联数学或块数学中调用它的上下文。

有人能给我一些关于如何实现这个有用的新功能的线索吗?

因为我真的需要一个好的解决方案来满足这个需求,所以我自己解决了。。。它让我受益了一段时间,解决方案可能并不完美,但至少效果很好。我提出这个结果是因为它对其他人有用。

我还将该解决方案改编为狮身人面像的扩展,可以在这里找到。

我必须定义一个新的替代指令,并重新定义数学指令和角色。所有这些都是在一个文件mathmmacro.py:中完成的

"""
'Proof of concept' for a new reStructuredText directive *mathmacro*.
Use for example with::
  python mathmacro.py example.rst example.html
"""
import re
from docutils.parsers.rst.directives.misc import Replace
from docutils.parsers.rst.directives.body import MathBlock
from docutils.parsers.rst.roles import math_role
def multiple_replacer(replace_dict):
    """Return a function replacing doing multiple replacements.
    The produced function replace `replace_dict.keys()` by
    `replace_dict.values`, respectively.
    """
    def replacement_function(match):
        s = match.group(0)
        end = s[-1]
        if re.match(r'[W_]', end):
            return replace_dict[s[:-1]]+end
        else:
            return replace_dict[s]
    pattern = re.compile("|".join([re.escape(k)+r'[W_Z]|'+re.escape(k)+r'Z'
                                   for k in replace_dict.keys()]), 
                         re.M)
    return lambda string: pattern.sub(replacement_function, string)

class MathMacro(Replace):
    """Directive defining a math macro."""
    def run(self):
        if not hasattr(self.state.document, 'math_macros'):
            self.state.document.math_macros = {}
        latex_key = '\'+self.state.parent.rawsource.split('|')[1]
        self.state.document.math_macros[latex_key] = ''.join(self.content)
        self.state.document.math_macros_replace = 
            multiple_replacer(self.state.document.math_macros)
        self.content[0] = ':math:`'+self.content[0]
        self.content[-1] = self.content[-1]+'`'
        return super(MathMacro, self).run()

class NewMathBlock(MathBlock):
    """New math block directive parsing the latex code."""
    def run(self):
        try:
            multiple_replace = self.state.document.math_macros_replace
        except AttributeError:
            pass
        else:
            if self.state.document.math_macros:
                for i, c in enumerate(self.content):
                    self.content[i] = multiple_replace(c)
        return super(NewMathBlock, self).run()

def new_math_role(role, rawtext, text, lineno, inliner, 
                  options={}, content=[]):
    """New math role parsing the latex code."""
    try:
        multiple_replace = inliner.document.math_macros_replace
    except AttributeError:
        pass
    else:
        if inliner.document.math_macros:
            rawtext = multiple_replace(rawtext)
    return math_role(role, rawtext, text, lineno, inliner,
                     options=options, content=content)

if __name__ == '__main__':
    from docutils.parsers.rst.directives import register_directive
    from docutils.parsers.rst.roles import register_canonical_role
    register_directive('mathmacro', MathMacro)
    register_directive('math', NewMathBlock)
    register_canonical_role('math', new_math_role)

    from docutils.core import publish_cmdline, default_description
    description = ('Generates (X)HTML documents '
                   'from standalone reStructuredText sources. '
                   +default_description)
    publish_cmdline(writer_name='html', description=description)

文件的内容示例第一:

Here, I show how to use a new mathmacro substitution directive in
reStructuredText. I think even this small example demonstrates that it
is useful.

First some math without math macros.  Let's take the example of the
incompressible Navier-Stokes equation:
.. math:: mbox{D}_t textbf{v} = 
   -boldsymbol{nabla} p + nu boldsymbol{nabla} ^2 textbf{v}.
where :math:`mbox{D}_t` is the convective derivative,
:math:`textbf{v}` the velocity, :math:`boldsymbol{nabla}` the
nabla operator, :math:`nu` the viscosity and
:math:`boldsymbol{nabla}^2` the Laplacian operator.

.. |Dt| mathmacro:: mbox{D}_t
.. |bnabla| mathmacro:: boldsymbol{nabla}
.. |vv| mathmacro:: textbf{v}
Now, let's use some math macros and try to get the same result...  The
Navier-Stokes equation can now be written like this:
.. math:: Dt vv = - bnabla p + nu bnabla^2 vv
where |Dt| is the convective derivative, |vv| the velocity, |bnabla|
the nabla operator, :math:`nu` the viscosity and :math:`bnabla^2`
the Laplacian operator.

最新更新