From 8475611c548c0172e2c4a4d52c274cd1a0f9528d Mon Sep 17 00:00:00 2001 From: lacatoire Date: Thu, 5 Feb 2026 10:30:44 +0100 Subject: [PATCH] Support MathML (mml:*) elements for XHTML rendering Closes #172 --- phpdotnet/phd/Package/Generic/XHTML.php | 73 +++++++++++++++++++++ phpdotnet/phd/Reader.php | 1 + tests/package/php/data/mathml_rendering.xml | 26 ++++++++ tests/package/php/mathml_rendering.phpt | 46 +++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 tests/package/php/data/mathml_rendering.xml create mode 100644 tests/package/php/mathml_rendering.phpt diff --git a/phpdotnet/phd/Package/Generic/XHTML.php b/phpdotnet/phd/Package/Generic/XHTML.php index 156c5d03..52433334 100644 --- a/phpdotnet/phd/Package/Generic/XHTML.php +++ b/phpdotnet/phd/Package/Generic/XHTML.php @@ -392,6 +392,48 @@ abstract class Package_Generic_XHTML extends Format_Abstract_XHTML { //phd 'phd:toc' => 'format_phd_toc', + // MathML (namespace http://www.w3.org/1998/Math/MathML) + 'mml:math' => 'format_mml_element', + // Token + 'mml:mi' => 'format_mml_element', + 'mml:mn' => 'format_mml_element', + 'mml:mo' => 'format_mml_element', + 'mml:mtext' => 'format_mml_element', + 'mml:mspace' => 'format_mml_element', + 'mml:ms' => 'format_mml_element', + // Layout + 'mml:mrow' => 'format_mml_element', + 'mml:mfrac' => 'format_mml_element', + 'mml:msqrt' => 'format_mml_element', + 'mml:mroot' => 'format_mml_element', + 'mml:mstyle' => 'format_mml_element', + 'mml:merror' => 'format_mml_element', + 'mml:mpadded' => 'format_mml_element', + 'mml:mphantom' => 'format_mml_element', + 'mml:mfenced' => 'format_mml_element', + 'mml:menclose' => 'format_mml_element', + // Scripts and limits + 'mml:msub' => 'format_mml_element', + 'mml:msup' => 'format_mml_element', + 'mml:msubsup' => 'format_mml_element', + 'mml:munder' => 'format_mml_element', + 'mml:mover' => 'format_mml_element', + 'mml:munderover' => 'format_mml_element', + 'mml:mmultiscripts' => 'format_mml_element', + 'mml:mprescripts' => 'format_mml_element', + 'mml:none' => 'format_mml_element', + // Tables + 'mml:mtable' => 'format_mml_element', + 'mml:mtr' => 'format_mml_element', + 'mml:mtd' => 'format_mml_element', + 'mml:mlabeledtr' => 'format_mml_element', + // Semantics + 'mml:semantics' => 'format_mml_element', + 'mml:annotation' => 'format_mml_element', + 'mml:annotation-xml' => 'format_mml_element', + // Actions + 'mml:maction' => 'format_mml_element', + ); /* }}} */ private $mytextmap = array( @@ -631,6 +673,37 @@ function format_phd_toc($open, $name, $attrs, $props) { ) . "\n"; } + /** + * Handle MathML elements (mml:* namespace). + * Strips the "mml:" prefix and outputs the HTML5 local name. + */ + public function format_mml_element($open, $name, $attrs, $props) { + $localName = substr($name, 4); + + if ($open) { + $attrStr = ''; + + // Add xmlns on the root element for XHTML compatibility + if ($localName === 'math') { + $attrStr .= ' xmlns="' . Reader::XMLNS_MATHML . '"'; + } + + // Preserve MathML attributes (stored under XMLNS_DOCBOOK as they have no namespace) + foreach ($attrs[Reader::XMLNS_DOCBOOK] as $attr => $val) { + $attrStr .= ' ' . $attr . '="' . $this->TEXT($val) . '"'; + } + + // Preserve xml:id as id + if (isset($attrs[Reader::XMLNS_XML]["id"])) { + $attrStr .= ' id="' . $attrs[Reader::XMLNS_XML]["id"] . '"'; + } + + return '<' . $localName . $attrStr . ($props["empty"] ? '/>' : '>'); + } + + return ''; + } + public function createLink($for, &$desc = null, $type = Format::SDESC) { $retval = null; if (isset($this->indexes[$for])) { diff --git a/phpdotnet/phd/Reader.php b/phpdotnet/phd/Reader.php index 796a2d71..47240f14 100644 --- a/phpdotnet/phd/Reader.php +++ b/phpdotnet/phd/Reader.php @@ -7,6 +7,7 @@ class Reader extends \XMLReader const XMLNS_XLINK = "http://www.w3.org/1999/xlink"; const XMLNS_PHD = "http://www.php.net/ns/phd"; const XMLNS_DOCBOOK = "http://docbook.org/ns/docbook"; + const XMLNS_MATHML = "http://www.w3.org/1998/Math/MathML"; protected OutputHandler $outputHandler; diff --git a/tests/package/php/data/mathml_rendering.xml b/tests/package/php/data/mathml_rendering.xml new file mode 100644 index 00000000..c32c7dfe --- /dev/null +++ b/tests/package/php/data/mathml_rendering.xml @@ -0,0 +1,26 @@ + + + +
+ 1. Inline MathML equation + + The quadratic formula is + x=-b±b2-4ac2a. + +
+ +
+ 2. Self-closing mspace element + + ab + +
+ +
+ 3. Element with mathvariant attribute + + x + +
+ +
diff --git a/tests/package/php/mathml_rendering.phpt b/tests/package/php/mathml_rendering.phpt new file mode 100644 index 00000000..f755da39 --- /dev/null +++ b/tests/package/php/mathml_rendering.phpt @@ -0,0 +1,46 @@ +--TEST-- +MathML rendering +--FILE-- +xmlFile = $xmlFile; + +$format = new TestPHPChunkedXHTML($config, $outputHandler); + +$render = new TestRender(new Reader($outputHandler), $config, $format); + +$render->run(); +?> +--EXPECT-- +Filename: mathml_rendering.html +Content: +
+ +
+

1. Inline MathML equation

+

+ The quadratic formula is + x=-b±b2-4ac2a. +

+
+ +
+

2. Self-closing mspace element

+

+ ab +

+
+ +
+

3. Element with mathvariant attribute

+

+ x +

+
+ +