diff --git a/cnxpublishing/models.py b/cnxpublishing/models.py index 32f89ac9..58aaa48e 100644 --- a/cnxpublishing/models.py +++ b/cnxpublishing/models.py @@ -11,6 +11,30 @@ from pyramid.threadlocal import get_current_registry +MATHML_NAMESPACE = "http://www.w3.org/1998/Math/MathML" +NSMAP = {'m': MATHML_NAMESPACE} + + +def _find_or_create_semantics_block(math_block): + """Finds or creates the MathML semantics tag.""" + try: + semantics_block = math_block.xpath( + '/m:samantics', + namespaces=NSMAP)[0] + except IndexError: + # Move all the contents of the math_block into a semantics wrapper. + children = [] + for child in math_block.getchildren(): + children.append(child) + math_block.remove(child) # why no pop? + semantics_block = etree.SubElement( + math_block, + '{{{}}}semantics'.format(MATHML_NAMESPACE)) + for child in children: + semantics_block.append(child) + return semantics_block + + def inject_mathml_svgs(content): """Inject MathML SVG annotations into HTML content.""" settings = get_current_registry().settings @@ -22,24 +46,23 @@ def inject_mathml_svgs(content): return content xml = etree.fromstring(content) - mathml_namespace = "http://www.w3.org/1998/Math/MathML" - mathml_blocks = xml.xpath( + math_blocks = xml.xpath( '//m:math[not(/m:annotation-xml[@encoding="image/svg+xml"])]', - namespaces={'m': mathml_namespace}) - for mathml_block in mathml_blocks: + namespaces=NSMAP) + for math_block in math_blocks: # Submit the MathML block to the SVG generation service. - payload = {'MathML': etree.tostring(mathml_block)} + payload = {'MathML': etree.tostring(math_block)} response = requests.post(url, data=payload) # Inject the SVG into the MathML as an annotation # only if the resposne was good, otherwise skip over it. - semantic_block = mathml_block.getchildren()[0] if response.status_code == 200: + semantics_wrapper = _find_or_create_semantics_block(math_block) svg = response.text content_type = response.headers['content-type'] # Insert the svg into the content annotation = etree.SubElement( - semantic_block, - '{{{}}}annotation-xml'.format(mathml_namespace)) + semantics_wrapper, + '{{{}}}annotation-xml'.format(MATHML_NAMESPACE)) annotation.set('encoding', content_type) annotation.append(etree.fromstring(svg)) modified_content = etree.tostring(xml) diff --git a/cnxpublishing/tests/test_models.py b/cnxpublishing/tests/test_models.py index 3faaa4f4..f6c32532 100644 --- a/cnxpublishing/tests/test_models.py +++ b/cnxpublishing/tests/test_models.py @@ -60,3 +60,20 @@ def test_mathml2svg(self): expected = """mocked""" self.assertEqual(etree.tostring(annotation), expected) + + def test_missing_semantics_wrapper(self): + content = """\ +
+ x = b ± b 2 4 a c 2 a +
""" + + # Call the target function. + result = self.target(content) + + elms = etree.fromstring(result) + annotation = elms.xpath( + '/div/m:math/m:semantics/m:annotation-xml[@encoding="image/svg+xml"]', + namespaces={'m': "http://www.w3.org/1998/Math/MathML"})[0] + expected = """mocked""" + + self.assertEqual(etree.tostring(annotation), expected)