summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi Abe <tabe@fixedpoint.jp>2016-11-13 21:06:55 +0900
committerTakeshi Abe <tabe@fixedpoint.jp>2016-11-15 00:27:30 +0000
commit074f0ab1d76f16fe92493868e2f2de75e67792ef (patch)
tree75e27850211fc1d12618b795eaa2d6e853f1f711
parentbb50b1609abe83265311613db4a18e992dc666c8 (diff)
tdf#76296 Import MathML's <mspace>
as possibly multiple "~" and/or "`" by honoring its width attribute. Change-Id: I17e361c3f8f5d061c856b72266332369497d16b9 Reviewed-on: https://gerrit.libreoffice.org/30809 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Takeshi Abe <tabe@fixedpoint.jp>
-rw-r--r--starmath/Library_sm.mk1
-rw-r--r--starmath/inc/node.hxx1
-rw-r--r--starmath/qa/extras/data/mspace.mml12
-rw-r--r--starmath/qa/extras/mmlexport-test.cxx10
-rw-r--r--starmath/qa/extras/mmlimport-test.cxx8
-rw-r--r--starmath/source/cursor.cxx5
-rw-r--r--starmath/source/mathmlattr.cxx147
-rw-r--r--starmath/source/mathmlattr.hxx52
-rw-r--r--starmath/source/mathmlimport.cxx87
-rw-r--r--starmath/source/mathmlimport.hxx6
-rw-r--r--starmath/source/node.cxx14
-rw-r--r--starmath/source/visitors.cxx11
12 files changed, 347 insertions, 7 deletions
diff --git a/starmath/Library_sm.mk b/starmath/Library_sm.mk
index 08a4b50d102a..d9c2d6d56da1 100644
--- a/starmath/Library_sm.mk
+++ b/starmath/Library_sm.mk
@@ -73,6 +73,7 @@ $(eval $(call gb_Library_add_exception_objects,sm,\
starmath/source/document \
starmath/source/edit \
starmath/source/format \
+ starmath/source/mathmlattr \
starmath/source/mathmlexport \
starmath/source/mathmlimport \
starmath/source/mathtype \
diff --git a/starmath/inc/node.hxx b/starmath/inc/node.hxx
index 68f3704883d8..c0e5de4ed2d5 100644
--- a/starmath/inc/node.hxx
+++ b/starmath/inc/node.hxx
@@ -1136,6 +1136,7 @@ public:
virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) override;
virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override;
void Accept(SmVisitor* pVisitor) override;
+ virtual void CreateTextFromNode(OUString &rText) override;
};
diff --git a/starmath/qa/extras/data/mspace.mml b/starmath/qa/extras/data/mspace.mml
new file mode 100644
index 000000000000..1906510940ea
--- /dev/null
+++ b/starmath/qa/extras/data/mspace.mml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<math xmlns="http://www.w3.org/1998/Math/MathML">
+ <mrow>
+ <mi>a</mi>
+ <mspace />
+ <mi>b</mi>
+ <mspace width="2em" />
+ <mi>c</mi>
+ <mspace width="5.5em" />
+ <mi>d</mi>
+ </mrow>
+</math>
diff --git a/starmath/qa/extras/mmlexport-test.cxx b/starmath/qa/extras/mmlexport-test.cxx
index 1266ca0ec3e5..b63b22f9b48d 100644
--- a/starmath/qa/extras/mmlexport-test.cxx
+++ b/starmath/qa/extras/mmlexport-test.cxx
@@ -35,10 +35,12 @@ public:
virtual void setUp() override;
virtual void tearDown() override;
+ void testBlank();
void testTdf97049();
void testTdf101022();
CPPUNIT_TEST_SUITE(MathMLExportTest);
+ CPPUNIT_TEST(testBlank);
CPPUNIT_TEST(testTdf97049);
CPPUNIT_TEST(testTdf101022);
CPPUNIT_TEST_SUITE_END();
@@ -87,6 +89,14 @@ xmlDocPtr MathMLExportTest::exportAndParse()
return pDoc;
}
+void MathMLExportTest::testBlank()
+{
+ mxDocShell->SetText("x`y~~z");
+ xmlDocPtr pDoc = exportAndParse();
+ assertXPath(pDoc, "/m:math/m:semantics/m:mrow/m:mspace[1]", "width", "0.5em");
+ assertXPath(pDoc, "/m:math/m:semantics/m:mrow/m:mspace[2]", "width", "4em");
+}
+
void MathMLExportTest::testTdf97049()
{
mxDocShell->SetText("intd {{1 over x} dx}");
diff --git a/starmath/qa/extras/mmlimport-test.cxx b/starmath/qa/extras/mmlimport-test.cxx
index 5ca5d70e8012..9bbc08ae39a9 100644
--- a/starmath/qa/extras/mmlimport-test.cxx
+++ b/starmath/qa/extras/mmlimport-test.cxx
@@ -32,12 +32,14 @@ public:
void testSimple();
void testNsPrefixMath();
void testMaction();
+ void testMspace();
void testtdf99556();
CPPUNIT_TEST_SUITE(Test);
CPPUNIT_TEST(testSimple);
CPPUNIT_TEST(testNsPrefixMath);
CPPUNIT_TEST(testMaction);
+ CPPUNIT_TEST(testMspace);
CPPUNIT_TEST(testtdf99556);
CPPUNIT_TEST_SUITE_END();
@@ -105,6 +107,12 @@ void Test::testMaction()
CPPUNIT_ASSERT_EQUAL_MESSAGE("loaded text", sExpected, mxDocShell->GetText());
}
+void Test::testMspace()
+{
+ loadURL(m_directories.getURLFromSrc("starmath/qa/extras/data/mspace.mml"));
+ CPPUNIT_ASSERT_EQUAL(OUString("{a b ~ c ~~``` d}"), mxDocShell->GetText());
+}
+
void Test::testtdf99556()
{
loadURL(m_directories.getURLFromSrc("starmath/qa/extras/data/tdf99556-1.mml"));
diff --git a/starmath/source/cursor.cxx b/starmath/source/cursor.cxx
index c96174167b2e..fadb103c8b0f 100644
--- a/starmath/source/cursor.cxx
+++ b/starmath/source/cursor.cxx
@@ -996,9 +996,12 @@ void SmCursor::InsertElement(SmFormulaElement element){
case BlankElement:
{
SmToken token;
+ token.eType = TBLANK;
token.nGroup = TG::Blank;
token.aText = "~";
- pNewNode = new SmBlankNode(token);
+ SmBlankNode* pBlankNode = new SmBlankNode(token);
+ pBlankNode->IncreaseBy(token);
+ pNewNode = pBlankNode;
}break;
case FactorialElement:
{
diff --git a/starmath/source/mathmlattr.cxx b/starmath/source/mathmlattr.cxx
new file mode 100644
index 000000000000..262609f1875d
--- /dev/null
+++ b/starmath/source/mathmlattr.cxx
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "mathmlattr.hxx"
+
+#include <cassert>
+
+namespace {
+
+sal_Int32 lcl_GetPowerOf10(sal_Int32 nPower)
+{
+ assert(nPower > 0);
+ sal_Int32 nResult = 1;
+ while (nPower--)
+ nResult *= 10;
+ return nResult;
+}
+
+}
+
+sal_Int32 ParseMathMLUnsignedNumber(const OUString &rStr, Fraction *pUN)
+{
+ assert(pUN);
+ auto nLen = rStr.getLength();
+ sal_Int32 nDecimalPoint = -1;
+ sal_Int32 nIdx;
+ for (nIdx = 0; nIdx < nLen; nIdx++)
+ {
+ auto cD = rStr[nIdx];
+ if (cD == sal_Unicode('.'))
+ {
+ if (nDecimalPoint >= 0)
+ return -1;
+ nDecimalPoint = nIdx;
+ continue;
+ }
+ if (cD < sal_Unicode('0') || sal_Unicode('9') < cD)
+ break;
+ }
+ if (nIdx == 0 || (nIdx == 1 && nDecimalPoint == 0))
+ return -1;
+ if (nDecimalPoint == -1)
+ {
+ assert(nIdx > 0);
+ *pUN = Fraction(rStr.copy(0, nIdx).toInt32(), 1);
+ return nIdx;
+ }
+ if (nDecimalPoint == 0)
+ {
+ assert(nIdx > 1);
+ *pUN = Fraction(rStr.copy(1, nIdx-1).toInt32(), lcl_GetPowerOf10(nIdx-1));
+ return nIdx;
+ }
+ assert(0 < nDecimalPoint);
+ assert(nDecimalPoint < nIdx);
+ *pUN = Fraction(rStr.copy(0, nDecimalPoint).toInt32(), 1);
+ if (++nDecimalPoint < nIdx)
+ *pUN += Fraction(rStr.copy(nDecimalPoint, nIdx-nDecimalPoint).toInt32(),
+ lcl_GetPowerOf10(nIdx-nDecimalPoint));
+ return nIdx;
+}
+
+sal_Int32 ParseMathMLNumber(const OUString &rStr, Fraction *pN)
+{
+ assert(pN);
+ if (rStr.isEmpty())
+ return -1;
+ bool bNegative = (rStr[0] == sal_Unicode('-'));
+ sal_Int32 nOffset = bNegative ? 1 : 0;
+ Fraction aF;
+ auto nIdx = ParseMathMLUnsignedNumber(rStr.copy(nOffset), &aF);
+ if (nIdx <= 0)
+ return -1;
+ if (bNegative)
+ *pN = Fraction(aF.GetNumerator(), aF.GetDenominator());
+ else
+ *pN = aF;
+ return nOffset + nIdx;
+}
+
+sal_Int32 ParseMathMLAttributeLengthValue(const OUString &rStr, MathMLAttributeLengthValue *pV)
+{
+ assert(pV);
+ auto nIdx = ParseMathMLNumber(rStr, &pV->aNumber);
+ if (nIdx <= 0)
+ return -1;
+ OUString sRest = rStr.copy(nIdx);
+ if (sRest.isEmpty())
+ {
+ pV->eUnit = MathMLLengthUnit::None;
+ return nIdx;
+ }
+ if (sRest.startsWith("em"))
+ {
+ pV->eUnit = MathMLLengthUnit::Em;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("ex"))
+ {
+ pV->eUnit = MathMLLengthUnit::Ex;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("px"))
+ {
+ pV->eUnit = MathMLLengthUnit::Px;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("in"))
+ {
+ pV->eUnit = MathMLLengthUnit::In;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("cm"))
+ {
+ pV->eUnit = MathMLLengthUnit::Cm;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("mm"))
+ {
+ pV->eUnit = MathMLLengthUnit::Mm;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("pt"))
+ {
+ pV->eUnit = MathMLLengthUnit::Pt;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("pc"))
+ {
+ pV->eUnit = MathMLLengthUnit::Pc;
+ return nIdx + 2;
+ }
+ if (sRest[0] == sal_Unicode('%'))
+ {
+ pV->eUnit = MathMLLengthUnit::Percent;
+ return nIdx + 2;
+ }
+ return nIdx;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/mathmlattr.hxx b/starmath/source/mathmlattr.hxx
new file mode 100644
index 000000000000..75dddb06d402
--- /dev/null
+++ b/starmath/source/mathmlattr.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_MATHMLATTR_HXX
+#define INCLUDED_STARMATH_SOURCE_MATHMLATTR_HXX
+
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <tools/fract.hxx>
+
+// MathML 3: 2.1.5.1 Syntax notation used in the MathML specification
+// <https://www.w3.org/TR/MathML/chapter2.html#id.2.1.5.1>
+// MathML 2: 2.4.4.2 Attributes with units
+// <https://www.w3.org/TR/MathML2/chapter2.html#fund.attval>
+
+sal_Int32 ParseMathMLUnsignedNumber(const OUString &rStr, Fraction *pUN);
+
+sal_Int32 ParseMathMLNumber(const OUString &rStr, Fraction *pN);
+
+// MathML 3: 2.1.5.2 Length Valued Attributes
+// <https://www.w3.org/TR/MathML/chapter2.html#fund.units>
+
+enum class MathMLLengthUnit {
+ None,
+ Em,
+ Ex,
+ Px,
+ In,
+ Cm,
+ Mm,
+ Pt,
+ Pc,
+ Percent
+};
+
+struct MathMLAttributeLengthValue
+{
+ Fraction aNumber;
+ MathMLLengthUnit eUnit;
+};
+
+sal_Int32 ParseMathMLAttributeLengthValue(const OUString &rStr, MathMLAttributeLengthValue *pV);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/mathmlimport.cxx b/starmath/source/mathmlimport.cxx
index b318406234c0..dc0d03212fa0 100644
--- a/starmath/source/mathmlimport.cxx
+++ b/starmath/source/mathmlimport.cxx
@@ -63,6 +63,7 @@ one go*/
#include <memory>
+#include "mathmlattr.hxx"
#include "mathmlimport.hxx"
#include "register.hxx"
#include <starmath.hrc>
@@ -1333,17 +1334,80 @@ public:
void StartElement(const uno::Reference< xml::sax::XAttributeList >& xAttrList ) override;
};
+namespace {
+
+bool lcl_CountBlanks(const MathMLAttributeLengthValue &rLV,
+ sal_Int32 *pWide, sal_Int32 *pNarrow)
+{
+ assert(pWide);
+ assert(pNarrow);
+ if (rLV.aNumber.GetNumerator() == 0)
+ {
+ *pWide = *pNarrow = 0;
+ return true;
+ }
+ // TODO: honor other units than em
+ if (rLV.eUnit != MathMLLengthUnit::Em)
+ return false;
+ if (rLV.aNumber.GetNumerator() < 0)
+ return false;
+ const Fraction aTwo(2, 1);
+ auto aWide = rLV.aNumber / aTwo;
+ auto nWide = static_cast<sal_Int32>(static_cast<long>(aWide));
+ if (nWide < 0)
+ return false;
+ const Fraction aPointFive(1, 2);
+ auto aNarrow = (rLV.aNumber - Fraction(nWide, 1) * aTwo) / aPointFive;
+ auto nNarrow = static_cast<sal_Int32>(static_cast<long>(aNarrow));
+ if (nNarrow < 0)
+ return false;
+ *pWide = nWide;
+ *pNarrow = nNarrow;
+ return true;
+}
+
+}
+
void SmXMLSpaceContext_Impl::StartElement(
- const uno::Reference<xml::sax::XAttributeList > & /*xAttrList*/ )
+ const uno::Reference<xml::sax::XAttributeList > & xAttrList )
{
- // There is not any syntax in Math to specify blank nodes of arbitrary
- // size. Hence we always interpret an <mspace> as a large gap "~".
+ // There is no syntax in Math to specify blank nodes of arbitrary size yet.
+ MathMLAttributeLengthValue aLV;
+ sal_Int32 nWide = 0, nNarrow = 0;
+
+ sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0;
+ for (sal_Int16 i=0;i<nAttrCount;i++)
+ {
+ OUString sAttrName = xAttrList->getNameByIndex(i);
+ OUString aLocalName;
+ sal_uInt16 nPrefix = GetImport().GetNamespaceMap().GetKeyByAttrName(sAttrName, &aLocalName);
+ OUString sValue = xAttrList->getValueByIndex(i);
+ const SvXMLTokenMap &rAttrTokenMap = GetSmImport().GetMspaceAttrTokenMap();
+ switch (rAttrTokenMap.Get(nPrefix, aLocalName))
+ {
+ case XML_TOK_WIDTH:
+ if ( ParseMathMLAttributeLengthValue(sValue.trim(), &aLV) <= 0 ||
+ !lcl_CountBlanks(aLV, &nWide, &nNarrow) )
+ SAL_WARN("starmath", "ignore mspace's width: " << sValue);
+ break;
+ default:
+ break;
+ }
+ }
SmToken aToken;
- aToken.cMathChar = '\0';
aToken.eType = TBLANK;
+ aToken.cMathChar = '\0';
+ aToken.nGroup = TG::Blank;
aToken.nLevel = 5;
std::unique_ptr<SmBlankNode> pBlank(new SmBlankNode(aToken));
- pBlank->IncreaseBy(aToken);
+ for (sal_Int32 i = 0; i < nWide; i++)
+ pBlank->IncreaseBy(aToken);
+ if (nNarrow > 0)
+ {
+ aToken.eType = TSBLANK;
+ for (sal_Int32 i = 0; i < nNarrow; i++)
+ pBlank->IncreaseBy(aToken);
+ }
GetSmImport().GetNodeStack().push_front(std::move(pBlank));
}
@@ -1898,6 +1962,12 @@ static const SvXMLTokenMapEntry aActionAttrTokenMap[] =
XML_TOKEN_MAP_END
};
+static const SvXMLTokenMapEntry aMspaceAttrTokenMap[] =
+{
+ { XML_NAMESPACE_MATH, XML_WIDTH, XML_TOK_WIDTH },
+ XML_TOKEN_MAP_END
+};
+
const SvXMLTokenMap& SmXMLImport::GetPresLayoutElemTokenMap()
{
@@ -1971,6 +2041,13 @@ const SvXMLTokenMap& SmXMLImport::GetActionAttrTokenMap()
return *pActionAttrTokenMap;
}
+const SvXMLTokenMap& SmXMLImport::GetMspaceAttrTokenMap()
+{
+ if (!pMspaceAttrTokenMap)
+ pMspaceAttrTokenMap.reset(new SvXMLTokenMap(aMspaceAttrTokenMap));
+ return *pMspaceAttrTokenMap;
+}
+
SvXMLImportContext *SmXMLDocContext_Impl::CreateChildContext(
sal_uInt16 nPrefix,
diff --git a/starmath/source/mathmlimport.hxx b/starmath/source/mathmlimport.hxx
index 04d49c82950d..626ee0bb0fb8 100644
--- a/starmath/source/mathmlimport.hxx
+++ b/starmath/source/mathmlimport.hxx
@@ -78,6 +78,7 @@ class SmXMLImport : public SvXMLImport
std::unique_ptr<SvXMLTokenMap> pPresTableElemTokenMap;
std::unique_ptr<SvXMLTokenMap> pColorTokenMap;
std::unique_ptr<SvXMLTokenMap> pActionAttrTokenMap;
+ std::unique_ptr<SvXMLTokenMap> pMspaceAttrTokenMap;
SmNodeStack aNodeStack;
bool bSuccess;
@@ -236,6 +237,7 @@ public:
const SvXMLTokenMap &GetPresTableElemTokenMap();
const SvXMLTokenMap &GetColorTokenMap();
const SvXMLTokenMap &GetActionAttrTokenMap();
+ const SvXMLTokenMap &GetMspaceAttrTokenMap();
SmNodeStack & GetNodeStack() { return aNodeStack; }
@@ -334,6 +336,10 @@ enum SmXMLActionAttrTokenMap
XML_TOK_SELECTION
};
+enum SmXMLMspaceAttrTokenMap
+{
+ XML_TOK_WIDTH
+};
#endif
diff --git a/starmath/source/node.cxx b/starmath/source/node.cxx
index febddc2f4785..dfbc8196e723 100644
--- a/starmath/source/node.cxx
+++ b/starmath/source/node.cxx
@@ -2774,6 +2774,20 @@ void SmBlankNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
SetWidth(nSpace);
}
+void SmBlankNode::CreateTextFromNode(OUString &rText)
+{
+ if (mnNum <= 0)
+ return;
+ sal_uInt16 nWide = mnNum / 4;
+ sal_uInt16 nNarrow = mnNum % 4;
+ for (sal_uInt16 i = 0; i < nWide; i++)
+ rText += "~";
+ for (sal_uInt16 i = 0; i < nNarrow; i++)
+ rText += "`";
+ rText += " ";
+}
+
+
/**************************************************************************/
//Implementation of all accept methods for SmVisitor
diff --git a/starmath/source/visitors.cxx b/starmath/source/visitors.cxx
index d9de2bed20a2..0397c4e8a1f5 100644
--- a/starmath/source/visitors.cxx
+++ b/starmath/source/visitors.cxx
@@ -2293,7 +2293,16 @@ void SmNodeToTextVisitor::Visit( SmMathSymbolNode* pNode )
void SmNodeToTextVisitor::Visit( SmBlankNode* pNode )
{
- Append( pNode->GetToken( ).aText );
+ sal_uInt16 nNum = pNode->GetBlankNum();
+ if (nNum <= 0)
+ return;
+ sal_uInt16 nWide = nNum / 4;
+ sal_uInt16 nNarrow = nNum % 4;
+ for (sal_uInt16 i = 0; i < nWide; i++)
+ Append( "~" );
+ for (sal_uInt16 i = 0; i < nNarrow; i++)
+ Append( "`" );
+ Append( " " );
}
void SmNodeToTextVisitor::Visit( SmErrorNode* )