sal_uInt32 ConvertMathToMathML(std::u16string_view rText, sal_Int32 nIndex = 0)
{ auto cRes = o3tl::iterateCodePoints(rText, &nIndex); if (IsInPrivateUseArea(cRes))
{
SAL_WARN("starmath", "Error: private use area characters should no longer be in use!");
cRes = u'@'; // just some character that should easily be notice as odd in the context
} return cRes;
}
}
// set progress range and start status indicator if (xStatusIndicator.is())
{
sal_Int32 nProgressRange = bFlat ? 1 : 3;
xStatusIndicator->start(SmResId(STR_STATSTR_WRITING), nProgressRange);
}
}
if (xStatusIndicator.is())
xStatusIndicator->end();
return bRet;
}
/// export through an XML exporter component (output stream version) bool SmXMLExportWrapper::WriteThroughComponent(const Reference<io::XOutputStream>& xOutputStream, const Reference<XComponent>& xComponent,
Reference<uno::XComponentContext> const& rxContext,
Reference<beans::XPropertySet> const& rPropSet, constchar* pComponentName)
{
OSL_ENSURE(xOutputStream.is(), "I really need an output stream!");
OSL_ENSURE(xComponent.is(), "Need component!");
OSL_ENSURE(nullptr != pComponentName, "Need component name!");
// get component
Reference<xml::sax::XWriter> xSaxWriter = xml::sax::Writer::create(rxContext);
// connect XML writer to output stream
xSaxWriter->setOutputStream(xOutputStream); if (m_bUseHTMLMLEntities)
xSaxWriter->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntitiesExport);
// prepare arguments (prepend doc handler to given arguments)
Sequence<Any> aArgs{ Any(xSaxWriter), Any(rPropSet) };
// get filter component
Reference<document::XExporter> xExporter(
rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
OUString::createFromAscii(pComponentName), aArgs, rxContext),
UNO_QUERY);
OSL_ENSURE(xExporter.is(), "can't instantiate export filter component"); if (!xExporter.is()) returnfalse;
// connect model and filter
xExporter->setSourceDocument(xComponent);
// all streams must be encrypted in encrypted document static constexpr OUStringLiteral sUseCommonStoragePasswordEncryption
= u"UseCommonStoragePasswordEncryption";
xSet->setPropertyValue(sUseCommonStoragePasswordEncryption, Any(true));
// set Base URL if (rPropSet.is())
{
rPropSet->setPropertyValue(u"StreamName"_ustr, Any(sStreamName));
}
// make use of a default namespace
ResetNamespaceMap(); // Math doesn't need namespaces from xmloff, since it now uses default namespaces (because that is common with current MathML usage in the web)
GetNamespaceMap_().Add(OUString(), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH);
if (pDocShell)
{ if (!pDocShell->GetFormat().IsTextmode())
{ // If the Math equation is not in text mode, we attach a display="block" // attribute on the <math> root. We don't do anything if it is in // text mode, the default display="inline" value will be used.
AddAttribute(XML_NAMESPACE_MATH, XML_DISPLAY, XML_BLOCK);
} if (pDocShell->GetFormat().IsRightToLeft())
{ // If the Math equation is set right-to-left, we attach a dir="rtl" // attribute on the <math> root. We don't do anything if it is set // left-to-right, the default dir="ltr" value will be used.
AddAttribute(XML_NAMESPACE_MATH, XML_DIR, XML_RTL);
}
}
// Unfold the binary tree structure as long as the nodes are SmBinHorNode // with the same nGroup. This will reduce the number of nested <mrow> // elements e.g. we only need three <mrow> levels to export
// #i115443: nodes of type expression always need to be grouped with mrow statement if (!bNoMrowContainer && (nSize > 1 || pNode->GetType() == SmNodeType::Expression))
pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true));
for (size_t i = 0; i < nSize; ++i)
{ if (const SmNode* pTemp = pNode->GetSubNode(i))
ExportNodes(pTemp, nLevel + 1);
}
}
void SmXMLExport::ExportBinaryVertical(const SmNode* pNode, int nLevel)
{
assert(pNode->GetNumSubNodes() == 3); const SmNode* pNum = pNode->GetSubNode(0); const SmNode* pDenom = pNode->GetSubNode(2); if (pNum->GetType() == SmNodeType::Align && pNum->GetToken().eType != TALIGNC)
{ // A left or right alignment is specified on the numerator: // attach the corresponding numalign attribute.
AddAttribute(XML_NAMESPACE_MATH, XML_NUMALIGN,
pNum->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
} if (pDenom->GetType() == SmNodeType::Align && pDenom->GetToken().eType != TALIGNC)
{ // A left or right alignment is specified on the denominator: // attach the corresponding denomalign attribute.
AddAttribute(XML_NAMESPACE_MATH, XML_DENOMALIGN,
pDenom->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
}
SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
ExportNodes(pNum, nLevel);
ExportNodes(pDenom, nLevel);
}
void SmXMLExport::ExportBinaryDiagonal(const SmNode* pNode, int nLevel)
{
assert(pNode->GetNumSubNodes() == 3);
if (pNode->GetToken().eType == TWIDESLASH)
{ // wideslash // export the node as <mfrac bevelled="true">
AddAttribute(XML_NAMESPACE_MATH, XML_BEVELLED, XML_TRUE);
SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
ExportNodes(pNode->GetSubNode(0), nLevel);
ExportNodes(pNode->GetSubNode(1), nLevel);
} else
{ // widebslash // We can not use <mfrac> to a backslash, so just use <mo>\</mo>
SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
void SmXMLExport::ExportTable(const SmNode* pNode, int nLevel)
{
std::unique_ptr<SvXMLElementExport> pTable;
size_t nSize = pNode->GetNumSubNodes();
//If the list ends in newline then the last entry has //no subnodes, the newline is superfluous so we just drop //the last node, inclusion would create a bad MathML //table if (nSize >= 1)
{ const SmNode* pLine = pNode->GetSubNode(nSize - 1); if (pLine->GetType() == SmNodeType::Line && pLine->GetNumSubNodes() == 1
&& pLine->GetSubNode(0) != nullptr
&& pLine->GetSubNode(0)->GetToken().eType == TNEWLINE)
--nSize;
}
// try to avoid creating a mtable element when the formula consists only // of a single output line if (nLevel || (nSize > 1))
pTable.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true));
for (size_t i = 0; i < nSize; ++i)
{ if (const SmNode* pTemp = pNode->GetSubNode(i))
{
std::unique_ptr<SvXMLElementExport> pRow;
std::unique_ptr<SvXMLElementExport> pCell; if (pTable)
{
pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTR, true, true));
SmTokenType eAlign = TALIGNC; if (pTemp->GetType() == SmNodeType::Align)
{ // For Binom() and Stack() constructions, the SmNodeType::Align nodes // are direct children. // binom{alignl ...}{alignr ...} and // stack{alignl ... ## alignr ... ## ...}
eAlign = pTemp->GetToken().eType;
} elseif (pTemp->GetType() == SmNodeType::Line && pTemp->GetNumSubNodes() == 1
&& pTemp->GetSubNode(0)
&& pTemp->GetSubNode(0)->GetType() == SmNodeType::Align)
{ // For the Table() construction, the SmNodeType::Align node is a child // of an SmNodeType::Line node. // alignl ... newline alignr ... newline ...
eAlign = pTemp->GetSubNode(0)->GetToken().eType;
} if (eAlign != TALIGNC)
{ // If a left or right alignment is specified on this line, // attach the corresponding columnalign attribute.
AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
eAlign == TALIGNL ? XML_LEFT : XML_RIGHT);
}
pCell.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTD, true, true));
}
ExportNodes(pTemp, nLevel + 1);
}
}
}
if (pNode->GetType() == SmNodeType::Math || pNode->GetType() == SmNodeType::GlyphSpecial)
{ // Export SmNodeType::Math and SmNodeType::GlyphSpecial symbols as <mo> elements
pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MO, true, false));
} elseif (pNode->GetType() == SmNodeType::Special)
{ bool bIsItalic = IsItalic(pNode->GetFont()); if (!bIsItalic)
AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
} else
{ // Export SmNodeType::MathIdent and SmNodeType::Place symbols as <mi> elements: // - These math symbols should not be drawn slanted. Hence we should // attach a mathvariant="normal" attribute to single-char <mi> elements // that are not mathematical alphanumeric symbol. For simplicity and to // work around browser limitations, we always attach such an attribute. // - The MathML specification suggests to use empty <mi> elements as // placeholders but they won't be visible in most MathML rendering // engines so let's use an empty square for SmNodeType::Place instead.
AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
} auto nArse = ConvertMathToMathML(pTemp->GetText());
OSL_ENSURE(nArse != 0xffff, "Non existent symbol");
GetDocHandler()->characters(OUString(&nArse, 1));
}
void SmXMLExport::ExportText(const SmNode* pNode)
{
std::unique_ptr<SvXMLElementExport> pText; const SmTextNode* pTemp = static_cast<const SmTextNode*>(pNode); switch (pNode->GetToken().eType)
{ default: case TIDENT:
{ //Note that we change the fontstyle to italic for strings that //are italic and longer than a single character. bool bIsItalic = IsItalic(pTemp->GetFont()); if ((pTemp->GetText().getLength() > 1) && bIsItalic)
AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_ITALIC); elseif ((pTemp->GetText().getLength() == 1) && !bIsItalic)
AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false)); break;
} case TNUMBER:
pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MN, true, false)); break; case TTEXT:
pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTEXT, true, false)); break;
}
GetDocHandler()->characters(pTemp->GetText());
}
void SmXMLExport::ExportBlank(const SmNode* pNode)
{ const SmBlankNode* pTemp = static_cast<const SmBlankNode*>(pNode); //!! exports an <mspace> element. Note that for example "~_~" is allowed in //!! Math (so it has no sense at all) but must not result in an empty //!! <msub> tag in MathML !!
if (pTemp->GetBlankNum() != 0)
{ // Attach a width attribute. We choose the (somewhat arbitrary) values // ".5em" for a small gap '`' and "2em" for a large gap '~'. // (see SmBlankNode::IncreaseBy for how pTemp->mnNum is set).
OUStringBuffer sStrBuf;
::sax::Converter::convertDouble(sStrBuf, pTemp->GetBlankNum() * .5);
sStrBuf.append("em");
AddAttribute(XML_NAMESPACE_MATH, XML_WIDTH, sStrBuf.makeStringAndClear());
}
//if we have prescripts at all then we must use the tensor notation
//This is one of those excellent locations where scope is vital to //arrange the construction and destruction of the element helper //classes correctly
pLSub = pNode->GetSubNode(LSUB + 1);
pLSup = pNode->GetSubNode(LSUP + 1); if (pLSub || pLSup)
{
SvXMLElementExport aMultiScripts(*this, XML_NAMESPACE_MATH, XML_MMULTISCRIPTS, true, true);
// This used to generate <mfenced> or <mrow>+<mo> elements according to // the stretchiness of fences. The MathML recommendation defines an // <mrow>+<mo> construction that is equivalent to the <mfenced> element: // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced // To simplify our code and avoid issues with mfenced implementations in // MathML rendering engines, we now always generate <mrow>+<mo> elements. // See #fdo 66282.
void SmXMLExport::ExportOperator(const SmNode* pNode, int nLevel)
{ /*we need to either use content or font and size attributes
*here*/
SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
ExportNodes(pNode->GetSubNode(0), nLevel + 1);
ExportNodes(pNode->GetSubNode(1), nLevel + 1);
}
void SmXMLExport::ExportAttributes(const SmNode* pNode, int nLevel)
{
std::unique_ptr<SvXMLElementExport> pElement;
void SmXMLExport::ExportFont(const SmNode* pNode, int nLevel)
{ // gather the mathvariant attribute relevant data from all // successively following SmFontNodes...
int nBold = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true; int nItalic = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true; int nSansSerifFixed = -1;
SmTokenType eNodeType = TUNKNOWN;
for (;;)
{
eNodeType = pNode->GetToken().eType; if (!lcl_HasEffectOnMathvariant(eNodeType)) break; switch (eNodeType)
{ case TBOLD:
nBold = 1; break; case TNBOLD:
nBold = 0; break; case TITALIC:
nItalic = 1; break; case TNITALIC:
nItalic = 0; break; case TSANS:
nSansSerifFixed = 0; break; case TSERIF:
nSansSerifFixed = 1; break; case TFIXED:
nSansSerifFixed = 2; break; default:
SAL_WARN("starmath", "unexpected case");
} // According to the parser every node that is to be evaluated here // has a single non-zero subnode at index 1!! Thus we only need to check // that single node for follow-up nodes that have an effect on the attribute. if (pNode->GetNumSubNodes() > 1 && pNode->GetSubNode(1)
&& lcl_HasEffectOnMathvariant(pNode->GetSubNode(1)->GetToken().eType))
{
pNode = pNode->GetSubNode(1);
} else break;
}
sal_uInt32 nc; switch (pNode->GetToken().eType)
{ case TPHANTOM: // No attribute needed. An <mphantom> element will be used below. break; case TMATHMLCOL:
{
nc = pNode->GetToken().cMathChar.toUInt32(16); const OUString& sssStr = starmathdatabase::Identify_Color_MATHML(nc).aIdent;
AddAttribute(XML_NAMESPACE_MATH, XML_MATHCOLOR, sssStr);
} break; case TRGB: case TRGBA: case THEX: case THTMLCOL: case TDVIPSNAMESCOL: case TICONICCOL:
{
nc = pNode->GetToken().cMathChar.toUInt32(16);
OUString ssStr("#" + Color(ColorTransparency, nc).AsRGBHEXString());
AddAttribute(XML_NAMESPACE_MATH, XML_MATHCOLOR, ssStr);
} break; case TSIZE:
{ const SmFontNode* pFontNode = static_cast<const SmFontNode*>(pNode); const Fraction& aFrac = pFontNode->GetSizeParameter();
OUStringBuffer sStrBuf; switch (pFontNode->GetSizeType())
{ case FontSizeType::MULTIPLY:
::sax::Converter::convertDouble(sStrBuf, static_cast<double>(aFrac * Fraction(100, 1)));
sStrBuf.append('%'); break; case FontSizeType::DIVIDE:
::sax::Converter::convertDouble(sStrBuf, static_cast<double>(Fraction(100, 1) / aFrac));
sStrBuf.append('%'); break; case FontSizeType::ABSOLUT:
::sax::Converter::convertDouble(sStrBuf, static_cast<double>(aFrac));
sStrBuf.append(GetXMLToken(XML_UNIT_PT)); break; default:
{ //The problem here is that the wheels fall off because //font size is stored in 100th's of a mm not pts, and //rounding errors take their toll on the original //value specified in points.
//Must fix StarMath to retain the original pt values double mytest
= o3tl::convert<double>(pFontNode->GetFont().GetFontSize().Height(),
SmO3tlLengthUnit(), o3tl::Length::pt);
switch (pNode->GetToken().eType)
{ case TOVERBRACE: default:
which = XML_MOVER; break; case TUNDERBRACE:
which = XML_MUNDER; break;
}
SvXMLElementExport aOver1(*this, XML_NAMESPACE_MATH, which, true, true);
{ //Scoping // using accents will draw the over-/underbraces too close to the base // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2 // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
SvXMLElementExport aOver2(*this, XML_NAMESPACE_MATH, which, true, true);
ExportNodes(pNode->Body(), nLevel);
AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
ExportNodes(pNode->Brace(), nLevel);
}
ExportNodes(pNode->Script(), nLevel);
}
void SmXMLExport::ExportMatrix(const SmNode* pNode, int nLevel)
{
SvXMLElementExport aTable(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true); const SmMatrixNode* pMatrix = static_cast<const SmMatrixNode*>(pNode);
size_t i = 0; for (sal_uInt16 y = 0; y < pMatrix->GetNumRows(); y++)
{
SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MTR, true, true); for (sal_uInt16 x = 0; x < pMatrix->GetNumCols(); x++)
{ if (const SmNode* pTemp = pNode->GetSubNode(i++))
{ if (pTemp->GetType() == SmNodeType::Align && pTemp->GetToken().eType != TALIGNC)
{ // A left or right alignment is specified on this cell, // attach the corresponding columnalign attribute.
AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
pTemp->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
}
SvXMLElementExport aCell(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
ExportNodes(pTemp, nLevel + 1);
}
}
}
}
void SmXMLExport::ExportNodes(const SmNode* pNode, int nLevel)
{ if (!pNode) return; switch (pNode->GetType())
{ case SmNodeType::Table:
ExportTable(pNode, nLevel); break; case SmNodeType::Align: case SmNodeType::Bracebody: case SmNodeType::Expression:
ExportExpression(pNode, nLevel); break; case SmNodeType::Line:
ExportLine(pNode, nLevel); break; case SmNodeType::Text:
ExportText(pNode); break; case SmNodeType::GlyphSpecial: case SmNodeType::Math:
{ const SmTextNode* pTemp = static_cast<const SmTextNode*>(pNode); if (pTemp->GetText().isEmpty())
{ // no conversion to MathML implemented -> export it as text // thus at least it will not vanish into nothing
ExportText(pNode);
} else
{ switch (pNode->GetToken().eType)
{ case TINTD:
AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE); break; default: break;
} //To fully handle generic MathML we need to implement the full //operator dictionary, we will generate MathML with explicit //stretchiness for now.
sal_Int16 nLength = GetAttrList().getLength(); bool bAddStretch = true; for (sal_Int16 i = 0; i < nLength; i++)
{
OUString sLocalName;
sal_uInt16 nPrefix = GetNamespaceMap().GetKeyByAttrName(
GetAttrList().getNameByIndex(i), &sLocalName);
if ((XML_NAMESPACE_MATH == nPrefix) && IsXMLToken(sLocalName, XML_STRETCHY))
{
bAddStretch = false; break;
}
} if (bAddStretch)
{
AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
}
ExportMath(pNode);
}
} break; case SmNodeType::
Special: //SmNodeType::Special requires some sort of Entity preservation in the XML engine. case SmNodeType::MathIdent: case SmNodeType::Place:
ExportMath(pNode); break; case SmNodeType::BinHor:
ExportBinaryHorizontal(pNode, nLevel); break; case SmNodeType::UnHor:
ExportUnaryHorizontal(pNode, nLevel); break; case SmNodeType::Brace:
ExportBrace(pNode, nLevel); break; case SmNodeType::BinVer:
ExportBinaryVertical(pNode, nLevel); break; case SmNodeType::BinDiagonal:
ExportBinaryDiagonal(pNode, nLevel); break; case SmNodeType::SubSup:
ExportSubSupScript(pNode, nLevel); break; case SmNodeType::Root:
ExportRoot(pNode, nLevel); break; case SmNodeType::Oper:
ExportOperator(pNode, nLevel); break; case SmNodeType::Attribute:
ExportAttributes(pNode, nLevel); break; case SmNodeType::Font:
ExportFont(pNode, nLevel); break; case SmNodeType::VerticalBrace:
ExportVerticalBrace(static_cast<const SmVerticalBraceNode*>(pNode), nLevel); break; case SmNodeType::Matrix:
ExportMatrix(pNode, nLevel); break; case SmNodeType::Blank:
ExportBlank(pNode); break; default:
SAL_WARN("starmath", "Warning: failed to export a node?"); break;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.