tdf#103430 Apply mathvariant attribute to <mi> and <mo>
by emulating it with SmFontNode. In case of <mo>, current implementation supports only the one named with an alphabet. Change-Id: I827a7e80f5aa94e243098a6e50eb758cf915c282 Reviewed-on: https://gerrit.libreoffice.org/31240 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Takeshi Abe <tabe@fixedpoint.jp>
This commit is contained in:
parent
9abc547ef2
commit
2bad9f1cd8
15
starmath/qa/extras/data/tdf103430.mml
Normal file
15
starmath/qa/extras/data/tdf103430.mml
Normal file
@ -0,0 +1,15 @@
|
||||
<math xmlns="http://www.w3.org/1998/Math/MathML">
|
||||
<mfrac>
|
||||
<mrow>
|
||||
<msup>
|
||||
<mo form="prefix" rspace="0">d</mo>
|
||||
<mn>2</mn>
|
||||
</msup>
|
||||
<mi mathvariant="normal" mathcolor="blue">y</mi>
|
||||
</mrow>
|
||||
<mrow>
|
||||
<mo fontstyle="italic" fontweight="bold" mathvariant="normal" form="prefix" rspace="0">d</mo>
|
||||
<mi fontfamily="serif" mathvariant="sans-serif-bold-italic" mathcolor="red">x</mi>
|
||||
</mrow>
|
||||
</mfrac>
|
||||
</math>
|
@ -34,6 +34,7 @@ public:
|
||||
void testMaction();
|
||||
void testMspace();
|
||||
void testtdf99556();
|
||||
void testTdf103430();
|
||||
void testTdf103500();
|
||||
|
||||
CPPUNIT_TEST_SUITE(Test);
|
||||
@ -42,6 +43,7 @@ public:
|
||||
CPPUNIT_TEST(testMaction);
|
||||
CPPUNIT_TEST(testMspace);
|
||||
CPPUNIT_TEST(testtdf99556);
|
||||
CPPUNIT_TEST(testTdf103430);
|
||||
CPPUNIT_TEST(testTdf103500);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
@ -122,10 +124,17 @@ void Test::testtdf99556()
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("loaded text", sExpected, mxDocShell->GetText());
|
||||
}
|
||||
|
||||
void Test::testTdf103430()
|
||||
{
|
||||
loadURL(m_directories.getURLFromSrc("starmath/qa/extras/data/tdf103430.mml"));
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("{{nitalic d}^2 {nitalic {color blue y}}} over {{nitalic d} {font sans {bold {italic {color red x}}}}}"),
|
||||
mxDocShell->GetText());
|
||||
}
|
||||
|
||||
void Test::testTdf103500()
|
||||
{
|
||||
loadURL(m_directories.getURLFromSrc("starmath/qa/extras/data/tdf103500.mml"));
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("{{ int csub a csup b {1 over x ` d x}} = {intd csub a csup b {1 over y ` d y}}}"),
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("{{ int csub a csup b {1 over x ` {nitalic d} x}} = {intd csub a csup b {1 over y ` {nitalic d} y}}}"),
|
||||
mxDocShell->GetText());
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "mathmlattr.hxx"
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace {
|
||||
|
||||
@ -144,4 +145,38 @@ sal_Int32 ParseMathMLAttributeLengthValue(const OUString &rStr, MathMLAttributeL
|
||||
return nIdx;
|
||||
}
|
||||
|
||||
|
||||
bool GetMathMLMathvariantValue(const OUString &rStr, MathMLMathvariantValue *pV)
|
||||
{
|
||||
static const std::unordered_map<OUString, MathMLMathvariantValue, OUStringHash> aMap{
|
||||
{"normal", MathMLMathvariantValue::Normal},
|
||||
{"bold", MathMLMathvariantValue::Bold},
|
||||
{"italic", MathMLMathvariantValue::Italic},
|
||||
{"bold-italic", MathMLMathvariantValue::BoldItalic},
|
||||
{"double-struck", MathMLMathvariantValue::DoubleStruck},
|
||||
{"bold-fraktur", MathMLMathvariantValue::BoldFraktur},
|
||||
{"script", MathMLMathvariantValue::Script},
|
||||
{"bold-script", MathMLMathvariantValue::BoldScript},
|
||||
{"fraktur", MathMLMathvariantValue::Fraktur},
|
||||
{"sans-serif", MathMLMathvariantValue::SansSerif},
|
||||
{"bold-sans-serif", MathMLMathvariantValue::BoldSansSerif},
|
||||
{"sans-serif-italic", MathMLMathvariantValue::SansSerifItalic},
|
||||
{"sans-serif-bold-italic", MathMLMathvariantValue::SansSerifBoldItalic},
|
||||
{"monospace", MathMLMathvariantValue::Monospace},
|
||||
{"initial", MathMLMathvariantValue::Initial},
|
||||
{"tailed", MathMLMathvariantValue::Tailed},
|
||||
{"looped", MathMLMathvariantValue::Looped},
|
||||
{"stretched", MathMLMathvariantValue::Stretched}
|
||||
};
|
||||
|
||||
assert(pV);
|
||||
auto it = aMap.find(rStr);
|
||||
if (it != aMap.end())
|
||||
{
|
||||
*pV = it->second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|
||||
|
@ -47,6 +47,33 @@ struct MathMLAttributeLengthValue
|
||||
|
||||
sal_Int32 ParseMathMLAttributeLengthValue(const OUString &rStr, MathMLAttributeLengthValue *pV);
|
||||
|
||||
|
||||
// MathML 3: 3.2.2 Mathematics style attributes common to token elements
|
||||
// <https://www.w3.org/TR/MathML3/chapter3.html#presm.commatt>
|
||||
|
||||
enum class MathMLMathvariantValue {
|
||||
Normal,
|
||||
Bold,
|
||||
Italic,
|
||||
BoldItalic,
|
||||
DoubleStruck,
|
||||
BoldFraktur,
|
||||
Script,
|
||||
BoldScript,
|
||||
Fraktur,
|
||||
SansSerif,
|
||||
BoldSansSerif,
|
||||
SansSerifItalic,
|
||||
SansSerifBoldItalic,
|
||||
Monospace,
|
||||
Initial,
|
||||
Tailed,
|
||||
Looped,
|
||||
Stretched
|
||||
};
|
||||
|
||||
bool GetMathMLMathvariantValue(const OUString &rStr, MathMLMathvariantValue *pV);
|
||||
|
||||
#endif
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|
||||
|
@ -43,6 +43,7 @@ one go*/
|
||||
#include <comphelper/servicehelper.hxx>
|
||||
#include <comphelper/string.hxx>
|
||||
#include <o3tl/make_unique.hxx>
|
||||
#include <rtl/character.hxx>
|
||||
#include <rtl/math.hxx>
|
||||
#include <sfx2/frame.hxx>
|
||||
#include <sfx2/docfile.hxx>
|
||||
@ -573,7 +574,6 @@ struct SmXMLContext_Helper
|
||||
sal_Int8 nIsBold;
|
||||
sal_Int8 nIsItalic;
|
||||
double nFontSize;
|
||||
bool bFontNodeNeeded;
|
||||
OUString sFontFamily;
|
||||
OUString sColor;
|
||||
|
||||
@ -583,22 +583,28 @@ struct SmXMLContext_Helper
|
||||
: nIsBold( -1 )
|
||||
, nIsItalic( -1 )
|
||||
, nFontSize( 0.0 )
|
||||
, bFontNodeNeeded( false )
|
||||
, rContext( rImport )
|
||||
{}
|
||||
|
||||
bool IsFontNodeNeeded() const;
|
||||
void RetrieveAttrs(const uno::Reference< xml::sax::XAttributeList > &xAttrList );
|
||||
void ApplyAttrs();
|
||||
};
|
||||
|
||||
bool SmXMLContext_Helper::IsFontNodeNeeded() const
|
||||
{
|
||||
return nIsBold != -1 ||
|
||||
nIsItalic != -1 ||
|
||||
nFontSize != 0.0 ||
|
||||
!sFontFamily.isEmpty() ||
|
||||
!sColor.isEmpty();
|
||||
}
|
||||
|
||||
void SmXMLContext_Helper::RetrieveAttrs(const uno::Reference<
|
||||
xml::sax::XAttributeList > & xAttrList )
|
||||
{
|
||||
sal_Int8 nOldIsBold=nIsBold;
|
||||
sal_Int8 nOldIsItalic=nIsItalic;
|
||||
double nOldFontSize=nFontSize;
|
||||
bool bMvFound = false;
|
||||
sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0;
|
||||
OUString sOldFontFamily = sFontFamily;
|
||||
for (sal_Int16 i=0;i<nAttrCount;i++)
|
||||
{
|
||||
OUString sAttrName = xAttrList->getNameByIndex(i);
|
||||
@ -640,24 +646,30 @@ void SmXMLContext_Helper::RetrieveAttrs(const uno::Reference<
|
||||
case XML_TOK_MATHCOLOR:
|
||||
sColor = sValue;
|
||||
break;
|
||||
case XML_TOK_MATHVARIANT:
|
||||
bMvFound = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((nOldIsBold!=nIsBold) || (nOldIsItalic!=nIsItalic) ||
|
||||
(nOldFontSize!=nFontSize) || (sOldFontFamily!=sFontFamily)
|
||||
|| !sColor.isEmpty())
|
||||
bFontNodeNeeded=true;
|
||||
else
|
||||
bFontNodeNeeded=false;
|
||||
if (bMvFound)
|
||||
{
|
||||
// Ignore deprecated attributes fontfamily, fontweight, and fontstyle
|
||||
// in favor of mathvariant, as specified in
|
||||
// <https://www.w3.org/TR/MathML3/chapter3.html#presm.deprecatt>.
|
||||
sFontFamily.clear();
|
||||
nIsBold = -1;
|
||||
nIsItalic = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void SmXMLContext_Helper::ApplyAttrs()
|
||||
{
|
||||
SmNodeStack &rNodeStack = rContext.GetSmImport().GetNodeStack();
|
||||
|
||||
if (bFontNodeNeeded)
|
||||
if (IsFontNodeNeeded())
|
||||
{
|
||||
SmToken aToken;
|
||||
aToken.cMathChar = '\0';
|
||||
@ -741,6 +753,131 @@ void SmXMLContext_Helper::ApplyAttrs()
|
||||
}
|
||||
|
||||
|
||||
class SmXMLTokenAttrHelper
|
||||
{
|
||||
SmXMLImportContext& mrContext;
|
||||
MathMLMathvariantValue meMv;
|
||||
bool mbMvFound;
|
||||
|
||||
public:
|
||||
SmXMLTokenAttrHelper(SmXMLImportContext& rContext)
|
||||
: mrContext(rContext)
|
||||
, meMv(MathMLMathvariantValue::Normal)
|
||||
, mbMvFound(false)
|
||||
{}
|
||||
|
||||
void RetrieveAttrs(const uno::Reference<xml::sax::XAttributeList>& xAttrList);
|
||||
void ApplyAttrs(MathMLMathvariantValue eDefaultMv);
|
||||
};
|
||||
|
||||
void SmXMLTokenAttrHelper::RetrieveAttrs(const uno::Reference<xml::sax::XAttributeList>& xAttrList)
|
||||
{
|
||||
if (!xAttrList.is())
|
||||
return;
|
||||
sal_Int16 nAttrCount = xAttrList->getLength();
|
||||
for (sal_Int16 i=0;i<nAttrCount;i++)
|
||||
{
|
||||
OUString sAttrName = xAttrList->getNameByIndex(i);
|
||||
OUString aLocalName;
|
||||
sal_uInt16 nPrefix = mrContext.GetSmImport().GetNamespaceMap().
|
||||
GetKeyByAttrName(sAttrName, &aLocalName);
|
||||
OUString sValue = xAttrList->getValueByIndex(i);
|
||||
const SvXMLTokenMap &rAttrTokenMap =
|
||||
mrContext.GetSmImport().GetPresLayoutAttrTokenMap();
|
||||
switch(rAttrTokenMap.Get(nPrefix, aLocalName))
|
||||
{
|
||||
case XML_TOK_MATHVARIANT:
|
||||
if (!GetMathMLMathvariantValue(sValue, &meMv))
|
||||
SAL_WARN("starmath", "failed to recognize mathvariant: " << sValue);
|
||||
mbMvFound = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SmXMLTokenAttrHelper::ApplyAttrs(MathMLMathvariantValue eDefaultMv)
|
||||
{
|
||||
assert( eDefaultMv == MathMLMathvariantValue::Normal ||
|
||||
eDefaultMv == MathMLMathvariantValue::Italic );
|
||||
|
||||
std::vector<SmTokenType> vVariant;
|
||||
MathMLMathvariantValue eMv = mbMvFound ? meMv : eDefaultMv;
|
||||
switch(eMv)
|
||||
{
|
||||
case MathMLMathvariantValue::Normal:
|
||||
vVariant.push_back(TNITALIC);
|
||||
break;
|
||||
case MathMLMathvariantValue::Bold:
|
||||
vVariant.push_back(TBOLD);
|
||||
break;
|
||||
case MathMLMathvariantValue::Italic:
|
||||
// nothing to do
|
||||
break;
|
||||
case MathMLMathvariantValue::BoldItalic:
|
||||
vVariant.push_back(TITALIC);
|
||||
vVariant.push_back(TBOLD);
|
||||
break;
|
||||
case MathMLMathvariantValue::DoubleStruck:
|
||||
// TODO
|
||||
break;
|
||||
case MathMLMathvariantValue::BoldFraktur:
|
||||
// TODO: Fraktur
|
||||
vVariant.push_back(TBOLD);
|
||||
break;
|
||||
case MathMLMathvariantValue::Script:
|
||||
// TODO
|
||||
break;
|
||||
case MathMLMathvariantValue::BoldScript:
|
||||
// TODO: Script
|
||||
vVariant.push_back(TBOLD);
|
||||
break;
|
||||
case MathMLMathvariantValue::Fraktur:
|
||||
// TODO
|
||||
break;
|
||||
case MathMLMathvariantValue::SansSerif:
|
||||
vVariant.push_back(TSANS);
|
||||
break;
|
||||
case MathMLMathvariantValue::BoldSansSerif:
|
||||
vVariant.push_back(TSANS);
|
||||
vVariant.push_back(TBOLD);
|
||||
break;
|
||||
case MathMLMathvariantValue::SansSerifItalic:
|
||||
vVariant.push_back(TITALIC);
|
||||
vVariant.push_back(TSANS);
|
||||
break;
|
||||
case MathMLMathvariantValue::SansSerifBoldItalic:
|
||||
vVariant.push_back(TITALIC);
|
||||
vVariant.push_back(TBOLD);
|
||||
vVariant.push_back(TSANS);
|
||||
break;
|
||||
case MathMLMathvariantValue::Monospace:
|
||||
vVariant.push_back(TFIXED);
|
||||
break;
|
||||
case MathMLMathvariantValue::Initial:
|
||||
case MathMLMathvariantValue::Tailed:
|
||||
case MathMLMathvariantValue::Looped:
|
||||
case MathMLMathvariantValue::Stretched:
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
if (vVariant.empty())
|
||||
return;
|
||||
SmNodeStack &rNodeStack = mrContext.GetSmImport().GetNodeStack();
|
||||
for (auto eType : vVariant)
|
||||
{
|
||||
SmToken aToken;
|
||||
aToken.eType = eType;
|
||||
aToken.cMathChar = '\0';
|
||||
aToken.nLevel = 5;
|
||||
std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
|
||||
pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
|
||||
rNodeStack.push_front(std::move(pFontNode));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SmXMLDocContext_Impl : public SmXMLImportContext
|
||||
{
|
||||
public:
|
||||
@ -1198,14 +1335,16 @@ void SmXMLStringContext_Impl::EndElement()
|
||||
|
||||
class SmXMLIdentifierContext_Impl : public SmXMLImportContext
|
||||
{
|
||||
protected:
|
||||
SmXMLTokenAttrHelper maTokenAttrHelper;
|
||||
SmXMLContext_Helper aStyleHelper;
|
||||
SmToken aToken;
|
||||
|
||||
public:
|
||||
SmXMLIdentifierContext_Impl(SmXMLImport &rImport,sal_uInt16 nPrefix,
|
||||
const OUString& rLName)
|
||||
: SmXMLImportContext(rImport,nPrefix,rLName),aStyleHelper(*this)
|
||||
: SmXMLImportContext(rImport,nPrefix,rLName)
|
||||
, maTokenAttrHelper(*this)
|
||||
, aStyleHelper(*this)
|
||||
{
|
||||
aToken.cMathChar = '\0';
|
||||
aToken.nLevel = 5;
|
||||
@ -1215,6 +1354,7 @@ public:
|
||||
void TCharacters(const OUString &rChars) override;
|
||||
void StartElement(const uno::Reference< xml::sax::XAttributeList > & xAttrList ) override
|
||||
{
|
||||
maTokenAttrHelper.RetrieveAttrs(xAttrList);
|
||||
aStyleHelper.RetrieveAttrs(xAttrList);
|
||||
};
|
||||
void EndElement() override;
|
||||
@ -1234,23 +1374,20 @@ void SmXMLIdentifierContext_Impl::EndElement()
|
||||
}
|
||||
else
|
||||
pNode.reset(new SmTextNode(aToken,FNT_VARIABLE));
|
||||
if (aStyleHelper.bFontNodeNeeded && aStyleHelper.nIsItalic != -1)
|
||||
if (aStyleHelper.nIsItalic != -1)
|
||||
{
|
||||
if (aStyleHelper.nIsItalic)
|
||||
pNode->GetFont().SetItalic(ITALIC_NORMAL);
|
||||
else
|
||||
pNode->GetFont().SetItalic(ITALIC_NONE);
|
||||
aStyleHelper.nIsItalic = -1;
|
||||
}
|
||||
|
||||
if ((-1!=aStyleHelper.nIsBold) || (0.0!=aStyleHelper.nFontSize) ||
|
||||
(!aStyleHelper.sFontFamily.isEmpty()) ||
|
||||
!aStyleHelper.sColor.isEmpty())
|
||||
aStyleHelper.bFontNodeNeeded=true;
|
||||
else
|
||||
aStyleHelper.bFontNodeNeeded=false;
|
||||
if (aStyleHelper.bFontNodeNeeded)
|
||||
aStyleHelper.ApplyAttrs();
|
||||
GetSmImport().GetNodeStack().push_front(std::move(pNode));
|
||||
aStyleHelper.ApplyAttrs();
|
||||
|
||||
maTokenAttrHelper.ApplyAttrs( (aToken.aText.getLength() == 1)
|
||||
? MathMLMathvariantValue::Italic
|
||||
: MathMLMathvariantValue::Normal );
|
||||
}
|
||||
|
||||
void SmXMLIdentifierContext_Impl::TCharacters(const OUString &rChars)
|
||||
@ -1261,15 +1398,16 @@ void SmXMLIdentifierContext_Impl::TCharacters(const OUString &rChars)
|
||||
|
||||
class SmXMLOperatorContext_Impl : public SmXMLImportContext
|
||||
{
|
||||
SmXMLTokenAttrHelper maTokenAttrHelper;
|
||||
bool bIsStretchy;
|
||||
|
||||
protected:
|
||||
SmToken aToken;
|
||||
|
||||
public:
|
||||
SmXMLOperatorContext_Impl(SmXMLImport &rImport,sal_uInt16 nPrefix,
|
||||
const OUString& rLName)
|
||||
: SmXMLImportContext(rImport,nPrefix,rLName), bIsStretchy(false)
|
||||
: SmXMLImportContext(rImport,nPrefix,rLName)
|
||||
, maTokenAttrHelper(*this)
|
||||
, bIsStretchy(false)
|
||||
{
|
||||
aToken.eType = TSPECIAL;
|
||||
aToken.nLevel = 5;
|
||||
@ -1294,12 +1432,18 @@ void SmXMLOperatorContext_Impl::EndElement()
|
||||
if (bIsStretchy)
|
||||
pNode->SetScaleMode(SCALE_HEIGHT);
|
||||
GetSmImport().GetNodeStack().push_front(std::move(pNode));
|
||||
|
||||
// TODO: apply to non-alphabetic characters too
|
||||
if (rtl::isAsciiAlpha(aToken.cMathChar))
|
||||
maTokenAttrHelper.ApplyAttrs(MathMLMathvariantValue::Normal);
|
||||
}
|
||||
|
||||
|
||||
void SmXMLOperatorContext_Impl::StartElement(const uno::Reference<
|
||||
xml::sax::XAttributeList > & xAttrList )
|
||||
{
|
||||
maTokenAttrHelper.RetrieveAttrs(xAttrList);
|
||||
|
||||
sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0;
|
||||
for (sal_Int16 i=0;i<nAttrCount;i++)
|
||||
{
|
||||
@ -1885,6 +2029,7 @@ static const SvXMLTokenMapEntry aPresLayoutAttrTokenMap[] =
|
||||
{ XML_NAMESPACE_MATH, XML_FONTFAMILY, XML_TOK_FONTFAMILY },
|
||||
{ XML_NAMESPACE_MATH, XML_COLOR, XML_TOK_COLOR },
|
||||
{ XML_NAMESPACE_MATH, XML_MATHCOLOR, XML_TOK_MATHCOLOR },
|
||||
{ XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_TOK_MATHVARIANT },
|
||||
XML_TOKEN_MAP_END
|
||||
};
|
||||
|
||||
|
@ -286,7 +286,8 @@ enum SmXMLPresLayoutAttrTokenMap
|
||||
XML_TOK_FONTSIZE,
|
||||
XML_TOK_FONTFAMILY,
|
||||
XML_TOK_COLOR,
|
||||
XML_TOK_MATHCOLOR
|
||||
XML_TOK_MATHCOLOR,
|
||||
XML_TOK_MATHVARIANT
|
||||
};
|
||||
|
||||
|
||||
|
@ -1710,6 +1710,8 @@ void SmAttributNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
|
||||
|
||||
void SmFontNode::CreateTextFromNode(OUString &rText)
|
||||
{
|
||||
rText += "{";
|
||||
|
||||
switch (GetToken().eType)
|
||||
{
|
||||
case TBOLD:
|
||||
@ -1823,6 +1825,9 @@ void SmFontNode::CreateTextFromNode(OUString &rText)
|
||||
}
|
||||
if(GetNumSubNodes() > 1)
|
||||
GetSubNode(1)->CreateTextFromNode(rText);
|
||||
|
||||
rText = comphelper::string::stripEnd(rText, ' ');
|
||||
rText += "} ";
|
||||
}
|
||||
|
||||
void SmFontNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
|
||||
|
Loading…
x
Reference in New Issue
Block a user