Doxygen with :gh:`doxygen/doxygen#623` applied, otherwise the codes will be
present in the rendered output in their raw form.
+`Custom styling`_
+-----------------
+
+It's possible to insert custom m.css classes into the Doxygen output. Add the
+following to your ``Doxyfile-mcss``:
+
+.. code:: ini
+
+ ALIASES += \
+ "m_div{1}=@xmlonly<mcss:div xmlns:mcss=\"http://mcss.mosra.cz/doxygen/\" mcss:class=\"\1\">@endxmlonly" \
+ "m_enddiv=@xmlonly</mcss:div>@endxmlonly" \
+ "m_span{1}=@xmlonly<mcss:span xmlns:mcss=\"http://mcss.mosra.cz/doxygen/\" mcss:class=\"\1\">@endxmlonly" \
+ "m_endspan=@xmlonly</mcss:span>@endxmlonly" \
+ "m_class{1}=@xmlonly<mcss:class xmlns:mcss=\"http://mcss.mosra.cz/doxygen/\" mcss:class=\"\1\" />@endxmlonly"
+
+If you need backwards compatibility with stock Doxygen HTML output, just make
+the aliases empty in your original ``Doxyfile``. Note that you can rename the
+aliases however you want to fit your naming scheme.
+
+.. code:: ini
+
+ ALIASES += \
+ "m_div{1}=" \
+ "m_enddiv=" \
+ "m_span{1}=" \
+ "m_endspan=" \
+ "m_class{1}="
+
+With ``@m_div`` and ``@m_span`` it's possible to wrap individual paragraphs or
+inline text in :html:`<div>` / :html:`<span>` and add CSS classes to them.
+Example usage and corresponding rendered HTML output:
+
+.. code-figure::
+
+ .. code:: c++
+
+ /**
+ @div{m-note m-dim m-text-center} This paragraph is rendered in a dim
+ note, centered. @enddiv
+
+ This text contains a @span{m-text m-success} green @endspan word.
+ */
+
+ .. note-dim::
+ :class: m-text-center
+
+ This paragraph is rendered in a dim note, centered.
+
+ .. role:: success
+ :class: m-text m-success
+
+ This text contains a :success:`green` word.
+
+.. note-warning::
+
+ Note that due to Doxygen XML output limitations it's not possible to wrap
+ multiple paragraphs this way, attempt to do that will result in an invalid
+ XML file that can't be processed. Similarly, if you forget a closing
+ ``@enddiv`` / ``@endspan`` or misplace them, the result will be an invalid
+ XML file.
+
+With ``@m_class`` it's possible to add CSS classes to the immediately following
+paragraph, image, table, list or math formula block. When used inline, it
+affects the immediately following emphasis, strong text, link or inline math
+formula. Example usage:
+
+.. code-figure::
+
+ .. code:: c++
+
+ /** See the red @m_class{m-danger} @f$ \Sigma @f$ character. */
+
+ .. role:: math-danger(math)
+ :class: m-danger
+
+ See the red :math-danger:`\Sigma` character.
+
`Customizing the template`_
===========================
# Remove spacing inside <> and before & and *
return fix_type_spacing(out)
-def parse_desc_internal(state: State, element: ET.Element, immediate_parent: ET.Element = None, trim = True):
+def parse_desc_internal(state: State, element: ET.Element, immediate_parent: ET.Element = None, trim = True, add_css_class = None):
out = Empty()
out.section = None
out.templates = {}
out.params = {}
out.return_value = None
+ out.add_css_class = None
# DOXYGEN <PARA> PATCHING 1/4
#
if element.text:
out.parsed = html.escape(element.text.strip() if trim else element.text)
+ # There's some inline text at the start, *do not* add any CSS class to
+ # the first child element
+ add_css_class = None
+
# Needed later for deciding whether we can strip the surrounding <p> from
# the content
paragraph_count = 0
# kind), set only if there is no i.tail, reset in the next iteration.
previous_section = None
+ # A CSS class to be added inline (not propagated outside of the paragraph)
+ add_inline_css_class = None
+
i: ET.Element
for index, i in enumerate(element):
# State used later
# - <verbatim>
# - <variablelist>, <itemizedlist>, <orderedlist>
# - <image>, <table>
+ # - <mcss:div>
# - <formula> (if block)
# - <programlisting> (if block)
#
end_previous_paragraph = False
# Straightforward elements
- if i.tag in ['heading', 'blockquote', 'xrefsect', 'variablelist', 'verbatim', 'itemizedlist', 'orderedlist', 'image', 'table']:
+ if i.tag in ['heading', 'blockquote', 'xrefsect', 'variablelist', 'verbatim', 'itemizedlist', 'orderedlist', 'image', 'table', '{http://mcss.mosra.cz/doxygen/}div']:
end_previous_paragraph = True
# <simplesect> describing return type is cut out of text flow, so
# itself. Also, some paragraphs are actually block content and we
# might not want to write the start/closing tag.
#
- # Also, to make things even funnier, parameter and return value
- # description come from inside of some paragraph, so bubble them up
- # and assume they are not scattered all over the place (ugh).
- #
# There's also the patching of nested lists that results in the
# immediate_parent variable in the section 2/4 -- we pass the
# parent only if this is the first paragraph inside it.
- parsed = parse_desc_internal(state, i, element if paragraph_count == 1 and not has_block_elements else None, False)
+ parsed = parse_desc_internal(state, i,
+ immediate_parent=element if paragraph_count == 1 and not has_block_elements else None,
+ trim=False,
+ add_css_class=add_css_class)
parsed.parsed = parsed.parsed.strip()
if not parsed.is_reasonable_paragraph:
has_block_elements = True
if parsed.parsed:
- if parsed.write_paragraph_start_tag: out.parsed += '<p>'
+ if parsed.write_paragraph_start_tag:
+ # If there is some inline content at the beginning, assume
+ # the CSS class was meant to be added to the paragraph
+ # itself, not into a nested (block) element.
+ out.parsed += '<p{}>'.format(' class="{}"'.format(add_css_class) if add_css_class else '')
out.parsed += parsed.parsed
if parsed.write_paragraph_close_tag: out.parsed += '</p>'
+
+ # Also, to make things even funnier, parameter and return value
+ # description come from inside of some paragraph, so bubble them up
+ # and assume they are not scattered all over the place (ugh).
if parsed.templates:
assert not out.templates
out.templates = parsed.templates
assert not out.return_value
out.return_value = parsed.return_value
+ # The same is (of course) with bubbling up the <mcss:class>
+ # element. Reset the current value with the value coming from
+ # inside -- it's either reset back to None or scheduled to be used
+ # in the next iteration. In order to make this work, the resetting
+ # code at the end of the loop iteration resets it to None only if
+ # this is not a paragraph or the <mcss:class> element -- so we are
+ # resetting here explicitly.
+ add_css_class = parsed.add_css_class
+
# Assert we didn't miss anything important
assert not parsed.section
assert element.tag == 'para' # is inside a paragraph :/
has_block_elements = True
tag = 'ul' if i.tag == 'itemizedlist' else 'ol'
- out.parsed += '<{}>'.format(tag)
+ out.parsed += '<{}{}>'.format(tag,
+ ' class="{}"'.format(add_css_class) if add_css_class else '')
for li in i:
assert li.tag == 'listitem'
out.parsed += '<li>{}</li>'.format(parse_desc(state, li))
elif i.tag == 'table':
assert element.tag == 'para' # is inside a paragraph :/
has_block_elements = True
- out.parsed += '<table class="m-table">'
+ out.parsed += '<table class="m-table{}">'.format(
+ ' ' + add_css_class if add_css_class else '')
inside_tbody = False
row: ET.Element
caption = i.text
if caption:
- out.parsed += '<figure class="m-figure"><img src="{}" alt="Image" /><figcaption>{}</figcaption></figure>'.format(name, html.escape(caption))
+ out.parsed += '<figure class="m-figure{}"><img src="{}" alt="Image" /><figcaption>{}</figcaption></figure>'.format(
+ ' ' + add_css_class if add_css_class else '',
+ name, html.escape(caption))
else:
- out.parsed += '<img class="m-image" src="{}" alt="Image" />'.format(name)
+ out.parsed += '<img class="m-image{}" src="{}" alt="Image" />'.format(
+ ' ' + add_css_class if add_css_class else '', name)
+
+ # Custom <div> with CSS classes (for making dim notes etc)
+ elif i.tag == '{http://mcss.mosra.cz/doxygen/}div':
+ assert element.tag == 'para' # is inside a paragraph :/
+ has_block_elements = True
+
+ out.parsed += '<div class="{}">{}</div>'.format(i.attrib['{http://mcss.mosra.cz/doxygen/}class'], parse_desc(state, i))
+
+ # Adding a custom CSS class to the immediately following block/inline
+ # element
+ elif i.tag == '{http://mcss.mosra.cz/doxygen/}class':
+ assert element.tag == 'para' # is inside a paragraph :/
+
+ # Bubble up in case we are alone in a paragraph, as that's meant to
+ # affect the next paragraph content.
+ if len([listing for listing in element]) == 1:
+ out.add_css_class = i.attrib['{http://mcss.mosra.cz/doxygen/}class']
+
+ # Otherwise this is meant to only affect inline elements in this
+ # paragraph:
+ else:
+ add_inline_css_class = i.attrib['{http://mcss.mosra.cz/doxygen/}class']
# Either block or inline
elif i.tag == 'programlisting':
# Strip whitespace around if inline code, strip only trailing
# whitespace if a block
highlighted = highlighted.rstrip() if code_block else highlighted.strip()
- out.parsed += '<{0} class="{1}">{2}</{0}>'.format('pre' if code_block else 'code', class_, highlighted)
+ out.parsed += '<{0} class="{1}{2}">{3}</{0}>'.format(
+ 'pre' if code_block else 'code',
+ class_,
+ ' ' + add_css_class if code_block and add_css_class else '',
+ highlighted)
# Either block or inline
elif i.tag == 'formula':
if formula_block:
has_block_elements = True
rendered = latex2svg.latex2svg('$${}$$'.format(i.text[3:-3]), params=m.math.latex2svg_params)
- out.parsed += '<div class="m-math">{}</div>'.format(m.math._patch(i.text, rendered, ''))
+ out.parsed += '<div class="m-math{}">{}</div>'.format(
+ ' ' + add_css_class if add_css_class else '',
+ m.math._patch(i.text, rendered, ''))
else:
rendered = latex2svg.latex2svg('${}$'.format(i.text[2:-2]), params=m.math.latex2svg_params)
# CSS classes and styling for proper vertical alignment. Depth is relative
# to font size, describes how below the line the text is. Scaling it back
# to 12pt font, scaled by 125% as set above in the config.
- attribs = ' class="m-math" style="vertical-align: -{:.1f}pt;"'.format(rendered['depth']*12*1.25)
+ attribs = ' class="m-math{}" style="vertical-align: -{:.1f}pt;"'.format(
+ ' ' + add_inline_css_class if add_inline_css_class else '',
+ rendered['depth']*12*1.25)
out.parsed += m.math._patch(i.text, rendered, attribs)
# Inline elements
out.parsed += '<code>{}</code>'.format(parse_inline_desc(state, i))
elif i.tag == 'emphasis':
- out.parsed += '<em>{}</em>'.format(parse_inline_desc(state, i))
+ out.parsed += '<em{}>{}</em>'.format(
+ ' class="{}"'.format(add_inline_css_class) if add_inline_css_class else '',
+ parse_inline_desc(state, i))
elif i.tag == 'bold':
- out.parsed += '<strong>{}</strong>'.format(parse_inline_desc(state, i))
+ out.parsed += '<strong{}>{}</strong>'.format(
+ ' class="{}"'.format(add_inline_css_class) if add_inline_css_class else '',
+ parse_inline_desc(state, i))
elif i.tag == 'ref':
out.parsed += parse_ref(state, i)
elif i.tag == 'ulink':
- out.parsed += '<a href="{}">{}</a>'.format(html.escape(i.attrib['url']), add_wbr(parse_inline_desc(state, i)))
+ out.parsed += '<a href="{}"{}>{}</a>'.format(
+ html.escape(i.attrib['url']),
+ ' class="{}"'.format(add_inline_css_class) if add_inline_css_class else '',
+ add_wbr(parse_inline_desc(state, i)))
+
+ # <span> with custom CSS classes
+ elif i.tag == '{http://mcss.mosra.cz/doxygen/}span':
+ out.parsed += '<span class="{}">{}</span>'.format(i.attrib['{http://mcss.mosra.cz/doxygen/}class'], parse_inline_desc(state, i))
# WHAT THE HELL WHY IS THIS NOT AN XML ENTITY
elif i.tag == 'ndash': out.parsed += '–'
if i.tag != 'simplesect' and previous_section:
previous_section = None
+ # A custom inline CSS class was used (or was meant to be used) in this
+ # iteration, reset it so it's not added again in the next iteration. If
+ # this is a <mcss:class> element, it was added just now, don't reset
+ # it.
+ if i.tag != '{http://mcss.mosra.cz/doxygen/}class' and add_inline_css_class:
+ add_inline_css_class = None
+
+ # A custom block CSS class was used (or was meant to be used) in this
+ # iteration, reset it so it's not added again in the next iteration. If
+ # this is a paragraph, it might be added just now from within the
+ # nested content, don't reset it.
+ if i.tag != 'para' and add_css_class:
+ add_css_class = None
+
# DOXYGEN <PARA> PATCHING 4/4
#
# Besides putting notes and blockquotes and shit inside paragraphs,
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>Math | My Project</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+ <link rel="stylesheet" href="m-dark+doxygen.compiled.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta name="theme-color" content="#22272e" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
+ <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+ <div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
+ <div class="m-row">
+ <ol class="m-col-t-6 m-col-m-none">
+ <li><a href="pages.html" id="m-navbar-current">Pages</a></li>
+ <li><a href="namespaces.html">Namespaces</a></li>
+ </ol>
+ <ol class="m-col-t-6 m-col-m-none" start="3">
+ <li><a href="annotated.html">Classes</a></li>
+ <li><a href="files.html">Files</a></li>
+ </ol>
+ </div>
+ </div>
+ </div>
+ </div>
+</nav></header>
+<main><article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>
+ Math
+ </h1>
+<p>A green formula:</p><div class="m-math m-success"><svg height='12.5906pt' version='1.1' viewBox='188.371 -10.0725 11.3035 10.0725' width='14.1293pt'>
+<title>LaTeX Math</title>
+<desc>
+\[ \pi^2 \]
+</desc>
+<defs>
+<path d='M3.09639 -4.5071H4.44732C4.12453 -3.16812 3.9213 -2.29539 3.9213 -1.33898C3.9213 -1.17161 3.9213 0.119552 4.41146 0.119552C4.66252 0.119552 4.87771 -0.107597 4.87771 -0.310834C4.87771 -0.37061 4.87771 -0.394521 4.79402 -0.573848C4.47123 -1.39875 4.47123 -2.4269 4.47123 -2.51059C4.47123 -2.58232 4.47123 -3.43113 4.72229 -4.5071H6.06127C6.21669 -4.5071 6.61121 -4.5071 6.61121 -4.88966C6.61121 -5.15268 6.38406 -5.15268 6.16887 -5.15268H2.23562C1.96065 -5.15268 1.55417 -5.15268 1.00423 -4.56687C0.6934 -4.22017 0.310834 -3.58655 0.310834 -3.51482S0.37061 -3.41918 0.442341 -3.41918C0.526027 -3.41918 0.537983 -3.45504 0.597758 -3.52677C1.21943 -4.5071 1.8411 -4.5071 2.13998 -4.5071H2.82142C2.55841 -3.61046 2.25953 -2.57036 1.2792 -0.478207C1.18356 -0.286924 1.18356 -0.263014 1.18356 -0.191283C1.18356 0.0597758 1.39875 0.119552 1.50635 0.119552C1.85305 0.119552 1.94869 -0.191283 2.09215 -0.6934C2.28344 -1.30311 2.28344 -1.32702 2.40299 -1.80523L3.09639 -4.5071Z' id='eq1-g0-25'/>
+<path d='M2.24757 -1.6259C2.37509 -1.74545 2.70984 -2.00847 2.83736 -2.12005C3.33151 -2.57435 3.80174 -3.0127 3.80174 -3.73798C3.80174 -4.68643 3.00473 -5.30012 2.00847 -5.30012C1.05205 -5.30012 0.422416 -4.57484 0.422416 -3.8655C0.422416 -3.47497 0.73325 -3.41918 0.844832 -3.41918C1.0122 -3.41918 1.25928 -3.53873 1.25928 -3.84159C1.25928 -4.25604 0.860772 -4.25604 0.765131 -4.25604C0.996264 -4.83786 1.53026 -5.03711 1.9208 -5.03711C2.66202 -5.03711 3.04458 -4.40747 3.04458 -3.73798C3.04458 -2.90909 2.46276 -2.30336 1.52229 -1.33898L0.518057 -0.302864C0.422416 -0.215193 0.422416 -0.199253 0.422416 0H3.57061L3.80174 -1.42665H3.55467C3.53076 -1.26725 3.467 -0.868742 3.37136 -0.71731C3.32354 -0.653549 2.71781 -0.653549 2.59029 -0.653549H1.17161L2.24757 -1.6259Z' id='eq1-g1-50'/>
+</defs>
+<g id='eq1-page1'>
+<use x='188.371' xlink:href='#eq1-g0-25' y='0'/>
+<use x='195.44' xlink:href='#eq1-g1-50' y='-4.93619'/>
+</g>
+</svg></div><p>A yellow <svg class="m-math m-warning" style="vertical-align: -0.0pt;" height='10.2117pt' version='1.1' viewBox='0 -8.16937 8.45432 8.16937' width='10.5679pt'>
+<title>LaTeX Math</title>
+<desc>
+$ \Sigma $
+</desc>
+<defs>
+<path d='M4.23213 -3.88543C4.32777 -4.00498 4.35168 -4.02889 4.35168 -4.08867C4.35168 -4.11258 4.35168 -4.13649 4.27995 -4.23213L1.86501 -7.81868H4.68643C6.69489 -7.81868 7.30461 -7.38829 7.53176 -5.49938H7.79477L7.48394 -8.16538H0.944458C0.657534 -8.16538 0.645579 -8.16538 0.645579 -7.89041L3.55068 -3.58655L0.777086 -0.310834C0.681445 -0.203238 0.657534 -0.167372 0.657534 -0.119552C0.657534 0 0.753176 0 0.944458 0H7.48394L7.79477 -2.78555H7.53176C7.31656 -0.812951 6.56339 -0.466252 4.65056 -0.466252H1.33898L4.23213 -3.88543Z' id='eq2-g0-6'/>
+</defs>
+<g id='eq2-page1'>
+<use x='0' xlink:href='#eq2-g0-6' y='0'/>
+</g>
+</svg> inline formula.</p>
+ </div>
+ </div>
+ </div>
+</article></main>
+<footer><nav>
+ <div class="m-container">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <p>My Project. Created by <a href="http://doxygen.org/">Doxygen</a> and <a href="http://mcss.mosra.cz/">m.css</a>.</p>
+ </div>
+ </div>
+ </div>
+</nav></footer>
+</body>
+</html>
\ No newline at end of file