[Libreoffice-commits] core.git: starmath/inc starmath/Library_sm.mk starmath/qa starmath/source

Takeshi Abe tabe at fixedpoint.jp
Tue Nov 15 00:27:56 UTC 2016


 starmath/Library_sm.mk                |    1 
 starmath/inc/node.hxx                 |    1 
 starmath/qa/extras/data/mspace.mml    |   12 ++
 starmath/qa/extras/mmlexport-test.cxx |   10 ++
 starmath/qa/extras/mmlimport-test.cxx |    8 +
 starmath/source/cursor.cxx            |    5 -
 starmath/source/mathmlattr.cxx        |  147 ++++++++++++++++++++++++++++++++++
 starmath/source/mathmlattr.hxx        |   52 ++++++++++++
 starmath/source/mathmlimport.cxx      |   87 ++++++++++++++++++--
 starmath/source/mathmlimport.hxx      |    6 +
 starmath/source/node.cxx              |   14 +++
 starmath/source/visitors.cxx          |   11 ++
 12 files changed, 347 insertions(+), 7 deletions(-)

New commits:
commit 074f0ab1d76f16fe92493868e2f2de75e67792ef
Author: Takeshi Abe <tabe at fixedpoint.jp>
Date:   Sun Nov 13 21:06:55 2016 +0900

    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 at libreoffice.org>
    Reviewed-by: Takeshi Abe <tabe at fixedpoint.jp>

diff --git a/starmath/Library_sm.mk b/starmath/Library_sm.mk
index 08a4b50..d9c2d6d 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 68f3704..c0e5de4e 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 0000000..1906510
--- /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 1266ca0..b63b22f 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 5ca5d70..9bbc08a 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 c961741..fadb103 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 0000000..262609f
--- /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 0000000..75dddb0
--- /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 b318406..dc0d032 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 04d49c8..626ee0b 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 febddc2..dfbc819 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 d9de2be..0397c4e 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* )


More information about the Libreoffice-commits mailing list