diff --git a/doc/dox/whatsnew.dox b/doc/dox/whatsnew.dox index afd3759644..c9ef022bd9 100644 --- a/doc/dox/whatsnew.dox +++ b/doc/dox/whatsnew.dox @@ -42,6 +42,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
  • IMPROVED: rendering and size calculation of decorations
  • IMPROVED/breaking: refactored symbol node JKQTMathTextSymbolNode and changed font-lookup!
  • IMPROVED/NEW/breaking: refactored whitespace-processing node JKQTMathTextWhitespaceNode, now all major LaTeX whitespace commands are supported properly
  • +
  • IMPROVED/NEW/breaking: refactored LaTeX parser in JKQTMathText
  • REMOVED/breaking: \v[a-zA-Z] and shorthand for \vec{a-zA-Z} was removed, implementation of \bbR,\bbC,... changed
  • NEW: now supports new decoration instructions: \cancel, \xcancel, \bcancel, \sout, \ocirc, \widetilde, \widehat, \breve
  • NEW: reworked drawing of decorations: improved appearance and positioning!
  • @@ -54,6 +55,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
  • NEW: \limits and \nolimits works as in LaTeX now (before it was simply removed and the functionality implemented for a fixed list of symbols)
  • NEW: added top-corner (\ulcorner/\urcorner) and bottom-corner brackets (\llcorner/\lrcorner)
  • NEW: added \overbracket and \underbracket
  • +
  • NEW: added \shaded{color}{...}
  • NEW: added functions to set the font-size in pixels (as alternative to the existing functions that set them in points), implements request #76 from user:igormironchik
  • diff --git a/doc/images/jkqtmathtext/jkqtmathtext_boxed.png b/doc/images/jkqtmathtext/jkqtmathtext_boxed.png index db2f7009d2..0b6f51a5e5 100644 Binary files a/doc/images/jkqtmathtext/jkqtmathtext_boxed.png and b/doc/images/jkqtmathtext/jkqtmathtext_boxed.png differ diff --git a/doc/images/jkqtmathtext/jkqtmathtext_colorbox.png b/doc/images/jkqtmathtext/jkqtmathtext_colorbox.png index c4eaa57e45..46a73b97e8 100644 Binary files a/doc/images/jkqtmathtext/jkqtmathtext_colorbox.png and b/doc/images/jkqtmathtext/jkqtmathtext_colorbox.png differ diff --git a/doc/images/jkqtmathtext/jkqtmathtext_doublebox.png b/doc/images/jkqtmathtext/jkqtmathtext_doublebox.png new file mode 100644 index 0000000000..c980bffe5d Binary files /dev/null and b/doc/images/jkqtmathtext/jkqtmathtext_doublebox.png differ diff --git a/doc/images/jkqtmathtext/jkqtmathtext_doubleovalbox.png b/doc/images/jkqtmathtext/jkqtmathtext_doubleovalbox.png new file mode 100644 index 0000000000..e2724146ab Binary files /dev/null and b/doc/images/jkqtmathtext/jkqtmathtext_doubleovalbox.png differ diff --git a/doc/images/jkqtmathtext/jkqtmathtext_fcolorbox.png b/doc/images/jkqtmathtext/jkqtmathtext_fcolorbox.png new file mode 100644 index 0000000000..b658a701f5 Binary files /dev/null and b/doc/images/jkqtmathtext/jkqtmathtext_fcolorbox.png differ diff --git a/doc/images/jkqtmathtext/jkqtmathtext_oovalbox.png b/doc/images/jkqtmathtext/jkqtmathtext_oovalbox.png new file mode 100644 index 0000000000..80e75768c7 Binary files /dev/null and b/doc/images/jkqtmathtext/jkqtmathtext_oovalbox.png differ diff --git a/doc/images/jkqtmathtext/jkqtmathtext_ovalboxed.png b/doc/images/jkqtmathtext/jkqtmathtext_ovalboxed.png new file mode 100644 index 0000000000..11b7956f4f Binary files /dev/null and b/doc/images/jkqtmathtext/jkqtmathtext_ovalboxed.png differ diff --git a/doc/images/jkqtmathtext/jkqtmathtext_shaded.png b/doc/images/jkqtmathtext/jkqtmathtext_shaded.png new file mode 100644 index 0000000000..0186d2608b Binary files /dev/null and b/doc/images/jkqtmathtext/jkqtmathtext_shaded.png differ diff --git a/examples/jkqtmathtext_test/testform.cpp b/examples/jkqtmathtext_test/testform.cpp index ae8ebb9427..99c29df114 100644 --- a/examples/jkqtmathtext_test/testform.cpp +++ b/examples/jkqtmathtext_test/testform.cpp @@ -191,7 +191,7 @@ TestForm::TestForm(QWidget *parent) : ui->cmbTestset->addItem("math: operator test (textmode)", "x=0\\ \\ y>0\\ \\ x+y\\ \\ -1\\ \\ x-2\\ \\ x\\cdot y\\ \\ x\\geq 4\\ \\ x~4"); ui->cmbTestset->addItem("math: operator test (mathmode)", "$x=0\\ \\ y>0\\ \\ x+y\\ \\ -1\\ \\ x-2\\ \\ x\\cdot y\\ \\ x\\geq 4\\ \\ x~4$"); ui->cmbTestset->addItem("text: color test", "\\textcolor{red}{RED}\\textcolor{blue}{BLUE}"); - ui->cmbTestset->addItem("text: boxed test", "test: \\boxed{boxed text} in the middle"); + ui->cmbTestset->addItem("text: boxed test", "test: {\\backslash}boxed: \\boxed{boxed text} {\\backslash}doublebox: \\doublebox{boxed text} {\\backslash}ovalbox: \\ovalbox{boxed text} {\\backslash}Ovalbox: \\Ovalbox{boxed text} {\\backslash}ovaldoublebox: \\ovaldoublebox{boxed text} {\\backslash}colorbox\\{red\\}: \\colorbox{red}{boxed text} {\\backslash}shaded\\{AliceBlue\\}: \\shaded{AliceBlue}{boxed text} {\\backslash}fcolorbox\\{red\\}\\{AliceBlue\\}: \\fcolorbox{red}{AliceBlue}{boxed text} in the middle"); ui->cmbTestset->addItem("mathboxed test", "$\\fbox{2^{2^{\\colorbox{red}{2^{x}}}}}$"); ui->cmbTestset->addItem("axiom of power test", "$\\forall A \\, \\exists P \\, \\forall B \\, [B \\in P \\iff \\forall C \\, (C \\in B \\Rightarrow C \\in A)]$"); ui->cmbTestset->addItem("math: De Morgan's law", "$\\neg(P\\land Q)\\iff(\\neg P)\\lor(\\neg Q)$ or $\\overline{\\bigcap_{i \\in I} A_{i}}\\equiv\\bigcup_{i \\in I} \\overline{A_{i}}$ or $\\overline{A \\cup B}\\equiv\\overline{A} \\cap \\overline{B}$"); @@ -417,7 +417,8 @@ QTreeWidgetItem *TestForm::createTree(JKQTMathTextNode *node, QTreeWidgetItem* p JKQTMathTextWhitespaceNode* spN=dynamic_cast(node); JKQTMathTextSymbolNode* symN=dynamic_cast(node); JKQTMathTextListNode* lstN=dynamic_cast(node); - JKQTMathTextInstruction1Node* inst1N=dynamic_cast(node); + JKQTMathTextModifiedTextPropsInstructionNode* inst1N=dynamic_cast(node); + JKQTMathTextBoxInstructionNode* inst1B=dynamic_cast(node); JKQTMathTextSubscriptNode* subN=dynamic_cast(node); JKQTMathTextSuperscriptNode* superN=dynamic_cast(node); JKQTMathTextBraceNode* braceN=dynamic_cast(node); @@ -465,8 +466,11 @@ QTreeWidgetItem *TestForm::createTree(JKQTMathTextNode *node, QTreeWidgetItem* p name=QString("MTsubscriptNode"); if (subN->getChild()) ti->addChild(createTree(subN->getChild(), ti)); } else if (inst1N) { - name=QString("MTinstruction1Node: \'%1\' (subsuper=%2").arg(inst1N->getName()).arg(inst1N->isSubSuperscriptAboveBelowNode()); + name=QString("ModTxtPropsInstructionNode: \'%1\' (subsuper=%2, params=%3)").arg(inst1N->getInstructionName()).arg(inst1N->isSubSuperscriptAboveBelowNode()).arg(inst1N->getParameters().join("/")); if (inst1N->getChild()) ti->addChild(createTree(inst1N->getChild(), ti)); + } else if (inst1B) { + name=QString("BoxInstructionNode: \'%1\' (subsuper=%2, params=%3)").arg(inst1B->getInstructionName()).arg(inst1B->isSubSuperscriptAboveBelowNode()).arg(inst1B->getParameters().join("/")); + if (inst1B->getChild()) ti->addChild(createTree(inst1B->getChild(), ti)); } else if (lstN) { name=QString("MTlistNode"); QList list=lstN->getChildren(); diff --git a/lib/jkqtmathtext/jkqtmathtext.cpp b/lib/jkqtmathtext/jkqtmathtext.cpp index 1fee1e03d9..7ac5d97ad6 100644 --- a/lib/jkqtmathtext/jkqtmathtext.cpp +++ b/lib/jkqtmathtext/jkqtmathtext.cpp @@ -1154,232 +1154,147 @@ JKQTMathTextNode* JKQTMathText::parseLatexString(bool get, JKQTMathTextBraceType } else { nl->addChild(new JKQTMathTextTextNode(this, text, addWhite, parsingMathEnvironment)); } + } else if (currentToken==MTTwhitespace) { + if (!parsingMathEnvironment) nl->addChild(new JKQTMathTextWhitespaceNode(this)); } else if (currentToken==MTTinstruction) { const QString currentInstructionName=currentTokenName; if (currentInstructionName=="\\") break; // break on linebrak character - if (JKQTMathTextWhitespaceNode::supportsInstructionName(currentInstructionName)) { - nl->addChild(new JKQTMathTextWhitespaceNode(currentInstructionName, this)); - } else if (JKQTMathTextSymbolNode::hasSymbol(currentInstructionName)) { - nl->addChild(new JKQTMathTextSymbolNode(this, currentInstructionName)); - if (JKQTMathTextSymbolNode::isSubSuperscriptBelowAboveSymbol(currentInstructionName) && parsingMathEnvironment) { - nl->getLastChild()->setSubSuperscriptAboveBelowNode(true); - } - } else if (currentTokenName=="limits") { + if (currentInstructionName=="limits") { if (nl->hasChildren()) nl->getLastChild()->setSubSuperscriptAboveBelowNode(true); - } else if (currentTokenName=="nolimits") { + } else if (currentInstructionName=="nolimits") { if (nl->hasChildren()) nl->getLastChild()->setSubSuperscriptAboveBelowNode(false); - } else { - getToken(); // look at next token - if (currentToken==MTTopenbrace) { - //std::cout<<"found '{' after '"<addChild(new JKQTMathTextSqrtNode(this, parseLatexString(true))); - } else if (currentInstructionName=="cbrt") { - nl->addChild(new JKQTMathTextSqrtNode(this, parseLatexString(true), new JKQTMathTextTextNode(this, "3", false))); - } else if (currentInstructionName=="verb") { - QString text=""; - currentTokenID++; - if (currentTokenID<=parseString.size()-1) { - QChar c=parseString[currentTokenID]; - while (c!='}' && (currentTokenIDaddChild(new JKQTMathTextTextNode(this, text, false)); - } - } else if (JKQTMathTextFracNode::supportsInstructionName(currentInstructionName)) { - JKQTMathTextNode* n1=parseLatexString(true); - JKQTMathTextNode* n2=nullptr; - if (getToken()==MTTopenbrace) n2=parseLatexString(true); - if (n1 && n2) nl->addChild(new JKQTMathTextFracNode(this, n1, n2, JKQTMathTextFracNode::InstructionName2FracType(currentInstructionName))); - else error_list.append(tr("error @ ch. %1: expected two arguments in '{' braces after '%2' command").arg(currentTokenID).arg(currentInstructionName)); - } else if (currentInstructionName=="binom") { - JKQTMathTextNode* n1=parseLatexString(true); - JKQTMathTextNode* n2=nullptr; - if (getToken()==MTTopenbrace) n2=parseLatexString(true); - if (n1 && n2) nl->addChild(new JKQTMathTextBraceNode(this, MTBTParenthesis, MTBTParenthesis, new JKQTMathTextFracNode(this, n1, n2, JKQTMathTextFracNode::MTFMstackrel))); - else error_list.append(tr("error @ ch. %1: expected two arguments in '{' braces after '%2' command").arg(currentTokenID).arg(currentInstructionName)); - } else if (currentInstructionName=="begin") { - if (getToken()==MTTtext) { - QString envname=currentTokenName; - while (currentToken!=MTTclosebrace) getToken(); // find closing brace '}' after '\\begin{name' - if (envname=="matrix" || envname=="array" || envname=="aligned" || envname=="align" || envname=="cases" || envname=="pmatrix"|| envname=="bmatrix"|| envname=="Bmatrix"|| envname=="vmatrix"|| envname=="Vmatrix") { - QVector< QVector > items; - //int lines=0; - //int cols=0; - bool first=true; - QVector line; - //std::cout<<"found \\begin{matrix}\n"; - while (first || currentToken==MTTampersand || (currentToken==MTTinstruction && currentTokenName=="\\")) { - JKQTMathTextNode* it=parseLatexString(true, MTBTAny, envname); - if (currentToken==MTTampersand) { - //std::cout<<" appending item\n"; - line.append(it); - } else { - line.append(it); - //std::cout<<" appending item and line with "<addChild(new JKQTMathTextBraceNode(this, MTBTParenthesis, MTBTParenthesis, new JKQTMathTextMatrixNode(this, items))); - else if (envname=="cases") nl->addChild(new JKQTMathTextBraceNode(this, MTBTCurlyBracket, MTBTNone, new JKQTMathTextMatrixNode(this, items))); - else if (envname=="bmatrix") nl->addChild(new JKQTMathTextBraceNode(this, MTBTSquareBracket, MTBTSquareBracket, new JKQTMathTextMatrixNode(this, items))); - else if (envname=="Bmatrix") nl->addChild(new JKQTMathTextBraceNode(this, MTBTCurlyBracket, MTBTCurlyBracket, new JKQTMathTextMatrixNode(this, items))); - else if (envname=="vmatrix") nl->addChild(new JKQTMathTextBraceNode(this, MTBTSingleLine, MTBTSingleLine, new JKQTMathTextMatrixNode(this, items))); - else if (envname=="Vmatrix") nl->addChild(new JKQTMathTextBraceNode(this, MTBTDoubleLine, MTBTDoubleLine, new JKQTMathTextMatrixNode(this, items))); - else nl->addChild(new JKQTMathTextMatrixNode(this, items)); - //std::cout<<" creating matrix-node ... done!\n"; + } else if (currentInstructionName=="begin") { + if (getToken()==MTTopenbrace && getToken()==MTTtext) { + QString envname=currentTokenName; + while (currentToken!=MTTclosebrace) getToken(); // find closing brace '}' after '\\begin{name' + if (envname=="matrix" || envname=="array" || envname=="aligned" || envname=="align" || envname=="cases" || envname=="pmatrix"|| envname=="bmatrix"|| envname=="Bmatrix"|| envname=="vmatrix"|| envname=="Vmatrix") { + QVector< QVector > items; + //int lines=0; + //int cols=0; + bool first=true; + QVector line; + //std::cout<<"found \\begin{matrix}\n"; + while (first || currentToken==MTTampersand || (currentToken==MTTinstruction && currentTokenName=="\\")) { + JKQTMathTextNode* it=parseLatexString(true, MTBTAny, envname); + if (currentToken==MTTampersand) { + //std::cout<<" appending item\n"; + line.append(it); } else { - error_list.append(tr("error @ ch. %1: unknown environment '%2'").arg(currentTokenID).arg(envname)); + line.append(it); + //std::cout<<" appending item and line with "<addChild(new JKQTMathTextDecoratedNode(this, JKQTMathTextDecoratedNode::InstructionName2DecorationType(currentInstructionName), parseLatexString(true))); + //std::cout<<" creating matrix-node with "<addChild(new JKQTMathTextBraceNode(this, MTBTParenthesis, MTBTParenthesis, new JKQTMathTextMatrixNode(this, items))); + else if (envname=="cases") nl->addChild(new JKQTMathTextBraceNode(this, MTBTCurlyBracket, MTBTNone, new JKQTMathTextMatrixNode(this, items))); + else if (envname=="bmatrix") nl->addChild(new JKQTMathTextBraceNode(this, MTBTSquareBracket, MTBTSquareBracket, new JKQTMathTextMatrixNode(this, items))); + else if (envname=="Bmatrix") nl->addChild(new JKQTMathTextBraceNode(this, MTBTCurlyBracket, MTBTCurlyBracket, new JKQTMathTextMatrixNode(this, items))); + else if (envname=="vmatrix") nl->addChild(new JKQTMathTextBraceNode(this, MTBTSingleLine, MTBTSingleLine, new JKQTMathTextMatrixNode(this, items))); + else if (envname=="Vmatrix") nl->addChild(new JKQTMathTextBraceNode(this, MTBTDoubleLine, MTBTDoubleLine, new JKQTMathTextMatrixNode(this, items))); + else nl->addChild(new JKQTMathTextMatrixNode(this, items)); + //std::cout<<" creating matrix-node ... done!\n"; } else { - if (currentInstructionName=="textcolor" || currentInstructionName=="mathcolor" || currentInstructionName=="color" || currentInstructionName=="colorbox") { - bool foundError=true; - QString col=""; - if (getToken()==MTTtext) { - col=currentTokenName; - if (getToken()==MTTclosebrace) { - if (getToken()==MTTopenbrace) { - foundError=false; - } - } - } - if (foundError) error_list.append(tr("error @ ch. %1: expected two arguments in '{' braces after '%2' command").arg(currentTokenID).arg(currentInstructionName)); - else nl->addChild(new JKQTMathTextInstruction1Node(this, currentInstructionName, parseLatexString(true), QStringList(col))); - - - } else { - nl->addChild(new JKQTMathTextInstruction1Node(this, currentInstructionName, parseLatexString(true))); - } + error_list.append(tr("error @ ch. %1: unknown environment '%2'").arg(currentTokenID).arg(envname)); } - } else if (currentToken==MTTopenbracket && currentInstructionName!="left") { - //std::cout<<"found '[' after '"<addChild(new JKQTMathTextSqrtNode(this, n2, n1)); - else error_list.append(tr("error @ ch. %1: expected two arguments in '{' braces after '%2' command").arg(currentTokenID).arg(currentInstructionName)); + } else { // find next '}' + error_list.append(tr("error @ ch. %1: text after '\\begin{' expected!").arg(currentTokenID)); + while (currentToken!=MTTclosebrace) getToken(); + getNew=true; + } + } else if (currentInstructionName=="end") { + if (getToken()==MTTopenbrace && getToken()==MTTtext) { + QString envname=currentTokenName; + while (currentToken!=MTTclosebrace) getToken(); // find closing brace '}' after '\\begin{name' + if (envname==quitOnEnvironmentEnd) { + break; } else { - nl->addChild(new JKQTMathTextTextNode(this, "[", false)); + error_list.append(tr("error @ ch. %1: '\\end{%2}' widthout preceding '\\begin{%3}'").arg(currentTokenID).arg(envname).arg(envname)); } - } else { - //std::cout<<"did not find '{' after '"<0) { - bool tokenWasNoBrace=false; - const QString firstTokenChar(currentTokenName[0]); - if (TokenNameMatchesJKQTMathTextBraceType(firstTokenChar, quitOnClosingBrace, true, &tokenWasNoBrace)) { - lastRightBraceType=TokenName2JKQTMathTextBraceType(firstTokenChar); - if (quitOnClosingBrace!=MTBTAny) currentTokenName=currentTokenName.right(currentTokenName.size()-1); - break; - } else { - getNew=false; - } - } - } else if (currentToken==MTTinstruction) { - if (InstructionNameMatchesJKQTMathTextBraceType(currentTokenName, quitOnClosingBrace, true)) { - lastRightBraceType=InstructionName2OpeningJKQTMathTextBraceType(currentTokenName); - break; - } - } else if (currentToken==MTTclosebracket) { - if (quitOnClosingBrace==MTBTSquareBracket || quitOnClosingBrace==MTBTAny) { - lastRightBraceType=MTBTSquareBracket; - break; - } + } else { // find next '}' + error_list.append(tr("error @ ch. %1: text after '\\begin{' expected!").arg(currentTokenID)); + while (currentToken!=MTTclosebrace) getToken(); + getNew=true; + } + } else if (currentInstructionName=="right") { + getToken(); + if (currentToken==MTTtext) { + if (currentTokenName.size()>0) { + bool tokenWasNoBrace=false; + const QString firstTokenChar(currentTokenName[0]); + if (TokenNameMatchesJKQTMathTextBraceType(firstTokenChar, quitOnClosingBrace, true, &tokenWasNoBrace)) { + lastRightBraceType=TokenName2JKQTMathTextBraceType(firstTokenChar); + if (quitOnClosingBrace!=MTBTAny) currentTokenName=currentTokenName.right(currentTokenName.size()-1); + break; } else { getNew=false; } - } else if (currentInstructionName=="left") { - if (currentToken==MTTtext) { - if (currentTokenName.size()>0) { - const QString firstTokenChar(currentTokenName[0]); - const JKQTMathTextBraceType bracetype=TokenName2JKQTMathTextBraceType(firstTokenChar); - if (bracetype==MTBTNone) { - currentTokenName=currentTokenName.right(currentTokenName.size()-1); - JKQTMathTextNode* cn=parseLatexString(currentTokenName.size()<=0, MTBTAny); - nl->addChild(new JKQTMathTextBraceNode(this, MTBTNone, bracetype, cn)); - } else if (isPrintableJKQTMathTextBraceType(bracetype)) { - currentTokenName=currentTokenName.right(currentTokenName.size()-1); // we already used the first character from the text token! - JKQTMathTextNode* c=parseLatexString(currentTokenName.size()<=0, bracetype); - nl->addChild(new JKQTMathTextBraceNode(this, bracetype, lastRightBraceType, c)); - } else { - getNew=false; - } - } - } else if (currentToken==MTTinstruction) { - const JKQTMathTextBraceType bracetypeopening=InstructionName2OpeningJKQTMathTextBraceType(currentTokenName); - if (bracetypeopening!=MTBTUnknown) { - JKQTMathTextNode* c=parseLatexString(true, bracetypeopening); - nl->addChild(new JKQTMathTextBraceNode(this, bracetypeopening, lastRightBraceType, c)); - } else if (currentToken==MTTinstruction && TokenNameMatchesJKQTMathTextBraceType(currentTokenName, quitOnClosingBrace, true)) { - break; - } - } else if (currentToken==MTTopenbracket) { - JKQTMathTextNode* c=parseLatexString(true, MTBTSquareBracket); - nl->addChild(new JKQTMathTextBraceNode(this, MTBTSquareBracket, lastRightBraceType, c)); + } + } else if (currentToken==MTTinstruction) { + if (InstructionNameMatchesJKQTMathTextBraceType(currentTokenName, quitOnClosingBrace, true)) { + lastRightBraceType=InstructionName2JKQTMathTextBraceType(currentTokenName); + break; + } + } else if (currentToken==MTTclosebracket) { + if (quitOnClosingBrace==MTBTSquareBracket || quitOnClosingBrace==MTBTAny) { + lastRightBraceType=MTBTSquareBracket; + break; + } + } else { + error_list.append(tr("error @ ch. %1: unexpected token after \\left").arg(currentTokenID)); + } + } else if (currentInstructionName=="left") { + getToken(); + if (currentToken==MTTtext) { + if (currentTokenName.size()>0) { + const QString firstTokenChar(currentTokenName[0]); + const JKQTMathTextBraceType bracetype=TokenName2JKQTMathTextBraceType(firstTokenChar); + if (bracetype==MTBTNone) { + currentTokenName=currentTokenName.right(currentTokenName.size()-1); + JKQTMathTextNode* cn=parseLatexString(currentTokenName.size()<=0, MTBTAny); + nl->addChild(new JKQTMathTextBraceNode(this, MTBTNone, bracetype, cn)); + } else if (isPrintableJKQTMathTextBraceType(bracetype)) { + currentTokenName=currentTokenName.right(currentTokenName.size()-1); // we already used the first character from the text token! + JKQTMathTextNode* c=parseLatexString(currentTokenName.size()<=0, bracetype); + nl->addChild(new JKQTMathTextBraceNode(this, bracetype, lastRightBraceType, c)); } else { - error_list.append(tr("error @ ch. %1: unexpected token after \\left").arg(currentTokenID)); + getNew=false; } - + } + } else if (currentToken==MTTinstruction) { + const JKQTMathTextBraceType bracetypeopening=InstructionName2OpeningJKQTMathTextBraceType(currentTokenName); + if (bracetypeopening!=MTBTUnknown) { + JKQTMathTextNode* c=parseLatexString(true, bracetypeopening); + nl->addChild(new JKQTMathTextBraceNode(this, bracetypeopening, lastRightBraceType, c)); + } else if (currentToken==MTTinstruction && TokenNameMatchesJKQTMathTextBraceType(currentTokenName, quitOnClosingBrace, true)) { + break; + } + } else if (currentToken==MTTopenbracket) { + JKQTMathTextNode* c=parseLatexString(true, MTBTSquareBracket); + nl->addChild(new JKQTMathTextBraceNode(this, MTBTSquareBracket, lastRightBraceType, c)); + } else { + error_list.append(tr("error @ ch. %1: unexpected token after \\left").arg(currentTokenID)); + } + } else { + bool foundError=false; + JKQTMathTextNode* node=parseInstruction(&foundError, &getNew); + if (node) { + if (foundError) { + delete node; + node=nullptr; } else { - getNew=false; - //error_list.append(tr("error @ ch. %1: unknown instruction \\%2").arg(currentTokenID).arg(currentInstructionName)); + nl->addChild(node); } } } - } else if (currentToken==MTTwhitespace) { - if (!parsingMathEnvironment) nl->addChild(new JKQTMathTextWhitespaceNode(this)); } else if (currentToken==MTTunderscore) { getToken(); JKQTMathTextNode* child=nullptr; JKQTMathTextNode* child2=nullptr; if (currentToken==MTTinstruction) { - const QString currentInstructionName=currentTokenName; - getToken(); // look at next token - if (currentToken==MTTopenbrace) { - child=new JKQTMathTextInstruction1Node(this, currentInstructionName, parseLatexString(true)); - } else if (JKQTMathTextWhitespaceNode::supportsInstructionName(currentInstructionName)) { - getNew=false; - child=new JKQTMathTextWhitespaceNode(currentInstructionName, this); - } else if (JKQTMathTextSymbolNode::hasSymbol(currentInstructionName)) { - getNew=false; - child=new JKQTMathTextSymbolNode(this, currentInstructionName); - if (JKQTMathTextSymbolNode::isSubSuperscriptBelowAboveSymbol(currentInstructionName) && parsingMathEnvironment) { - child->setSubSuperscriptAboveBelowNode(true); - } - } else { - error_list.append(tr("error @ ch. %1: unknown instruction \\%2").arg(currentTokenID).arg(currentInstructionName)); - } + child=parseInstruction(nullptr, &getNew); } else if (currentToken==MTTopenbrace) { child=parseLatexString(true); } else if (currentToken==MTTtext) { @@ -1399,24 +1314,7 @@ JKQTMathTextNode* JKQTMathText::parseLatexString(bool get, JKQTMathTextBraceType JKQTMathTextNode* child=nullptr; JKQTMathTextNode* child2=nullptr; if (currentToken==MTTinstruction) { - const QString currentInstructionName=currentTokenName; - if (JKQTMathTextWhitespaceNode::supportsInstructionName(currentInstructionName)) { - getNew=true; - nl->addChild(new JKQTMathTextWhitespaceNode(currentInstructionName, this)); - } else if (JKQTMathTextSymbolNode::hasSymbol(currentInstructionName)){ - getNew=true; - child=new JKQTMathTextSymbolNode(this, currentInstructionName); - if (JKQTMathTextSymbolNode::isSubSuperscriptBelowAboveSymbol(currentInstructionName) && parsingMathEnvironment) { - child->setSubSuperscriptAboveBelowNode(true); - } - } else { - getToken(); // look at next token - if (currentToken==MTTopenbrace) { - child=new JKQTMathTextInstruction1Node(this, currentInstructionName, parseLatexString(true)); - } else { - error_list.append(tr("error @ ch. %1: unknown instruction \\%2").arg(currentTokenID).arg(currentInstructionName)); - } - } + child=parseInstruction(nullptr, &getNew); } else if (currentToken==MTTopenbrace) { child=parseLatexString(true); } else if (currentToken==MTTtext) { @@ -1448,7 +1346,7 @@ JKQTMathTextNode* JKQTMathText::parseLatexString(bool get, JKQTMathTextBraceType break; } else { // starting math environment parsingMathEnvironment=true; - nl->addChild(new JKQTMathTextInstruction1Node(this, "equation", parseLatexString(true))); + nl->addChild(new JKQTMathTextModifiedTextPropsInstructionNode(this, "equation", parseLatexString(true))); } } if (getNew) getToken(); @@ -1457,6 +1355,177 @@ JKQTMathTextNode* JKQTMathText::parseLatexString(bool get, JKQTMathTextBraceType return simplifyJKQTMathTextNode(nl); } +JKQTMathTextNode* JKQTMathText::parseInstruction(bool *_foundError, bool* getNew) { + if (currentToken!=MTTinstruction) { + if (_foundError) *_foundError=true; + if (getNew) *getNew=false; + error_list.append(tr("error @ ch. %1: expected instruction token").arg(currentTokenID)); + return nullptr; + } + bool foundError=false; + const QString currentInstructionName=currentTokenName; + JKQTMathTextNode* child=nullptr; + if (getNew) *getNew=true; + if (JKQTMathTextWhitespaceNode::supportsInstructionName(currentInstructionName)) { + if (getNew) *getNew=true; + child= new JKQTMathTextWhitespaceNode(currentInstructionName, this); + } else if (JKQTMathTextSymbolNode::hasSymbol(currentInstructionName)) { + child=new JKQTMathTextSymbolNode(this, currentInstructionName); + if (JKQTMathTextSymbolNode::isSubSuperscriptBelowAboveSymbol(currentInstructionName) && parsingMathEnvironment) { + child->setSubSuperscriptAboveBelowNode(true); + } + if (getNew) *getNew=true; + } else if (JKQTMathTextModifiedTextPropsInstructionNode::supportsInstructionName(currentInstructionName)) { + const size_t Nparams=JKQTMathTextModifiedTextPropsInstructionNode::countParametersOfInstruction(currentInstructionName); + bool foundError=false; + const QStringList params=parseStringParams(true, Nparams, &foundError); + if (!foundError) { + if (getToken()==MTTopenbrace) { + const bool oldParseMath=parsingMathEnvironment; + auto __finalpaint=JKQTPFinally(std::bind([&oldParseMath](bool& parsingMathEnvironment) { parsingMathEnvironment=oldParseMath; }, std::ref(parsingMathEnvironment))); + JKQTMathTextModifiedTextPropsInstructionNode::modifyInMathEnvironment(currentInstructionName, parsingMathEnvironment, params); + child=new JKQTMathTextModifiedTextPropsInstructionNode(this, currentInstructionName, parseLatexString(true), params); + + } else { + foundError=true; + } + } + if (foundError){ + error_list.append(tr("error @ ch. %1: expected %3 arguments in '{...}' braces after '%2' command").arg(currentTokenID).arg(currentInstructionName).arg(Nparams+1)); + } + } else if (JKQTMathTextBoxInstructionNode::supportsInstructionName(currentInstructionName)) { + const size_t Nparams=JKQTMathTextBoxInstructionNode::countParametersOfInstruction(currentInstructionName); + bool foundError=false; + const QStringList params=parseStringParams(true, Nparams, &foundError); + if (!foundError) { + if (getNew) *getNew=true; + if (getToken()==MTTopenbrace) { + const bool oldParseMath=parsingMathEnvironment; + auto __finalpaint=JKQTPFinally(std::bind([&oldParseMath](bool& parsingMathEnvironment) { parsingMathEnvironment=oldParseMath; }, std::ref(parsingMathEnvironment))); + JKQTMathTextBoxInstructionNode::modifyInMathEnvironment(currentInstructionName, parsingMathEnvironment, params); + child=new JKQTMathTextBoxInstructionNode(this, currentInstructionName, parseLatexString(true), params); + } else { + foundError=true; + } + } + if (foundError){ + error_list.append(tr("error @ ch. %1: expected %3 arguments in '{...}' braces after '%2' command").arg(currentTokenID).arg(currentInstructionName).arg(Nparams+1)); + } + } else if (currentInstructionName=="sqrt") { + getToken(); + if (currentToken==MTTopenbrace) { + child=new JKQTMathTextSqrtNode(this, parseLatexString(true)); + } else if (currentToken==MTTopenbracket) { + JKQTMathTextNode* n1=parseLatexString(true, MTBTAny, "", true); + JKQTMathTextNode* n2=nullptr; + if (getToken()==MTTopenbrace) n2=parseLatexString(true); + else error_list.append(tr("error @ ch. %1: expected one argument in '{' braces after '%2' command with an optional argument in []").arg(currentTokenID).arg(currentInstructionName)); + + if (n1 && n2) { + child=new JKQTMathTextSqrtNode(this, n2, n1); + } else { + if (n1) delete n1; + if (n2) delete n2; + error_list.append(tr("error @ ch. %1: expected two arguments in '{' braces after '%2' command").arg(currentTokenID).arg(currentInstructionName)); + } + } else { + error_list.append(tr("error @ ch. %1: expected %3 arguments in '{...}' braces after '%2' command").arg(currentTokenID).arg(currentInstructionName).arg(1)); + } + } else if (currentInstructionName=="cbrt") { + if (getToken()==MTTopenbrace) { + child=new JKQTMathTextSqrtNode(this, parseLatexString(true), new JKQTMathTextTextNode(this, "3", false)); + } else { + error_list.append(tr("error @ ch. %1: expected %3 arguments in '{...}' braces after '%2' command").arg(currentTokenID).arg(currentInstructionName).arg(1)); + } + } else if (currentInstructionName=="verb") { + if (getToken()==MTTopenbrace) { + QString text=""; + currentTokenID++; + if (currentTokenID<=parseString.size()-1) { + QChar c=parseString[currentTokenID]; + while (c!='}' && (currentTokenID0 || (n==0 && get)) getToken(); + if (currentToken==MTTopenbrace) { + getToken(); + if (currentToken==MTTtext) { + params.append(currentTokenName); + if (getToken()!=MTTclosebrace) { + if (*foundError) *foundError=true; + return params; + } + } else if (currentToken==MTTwhitespace) { + params.append(" "); + if (getToken()!=MTTclosebrace) { + if (*foundError) *foundError=true; + return params; + } + } else if (currentToken==MTTclosebrace) { + params.append(""); + } else { + if (*foundError) *foundError=true; + return params; + } + } else { + if (*foundError) *foundError=true; + return params; + } + } + return params; + } +} + JKQTMathTextNode *JKQTMathText::getParsedNode() const { return this->parsedNode; diff --git a/lib/jkqtmathtext/jkqtmathtext.h b/lib/jkqtmathtext/jkqtmathtext.h index fef9ceca4e..630584b2e7 100644 --- a/lib/jkqtmathtext/jkqtmathtext.h +++ b/lib/jkqtmathtext/jkqtmathtext.h @@ -123,6 +123,13 @@ class JKQTMathTextNode; // forward - \c \\tt{...} \c \\texttt{...} \c \\mathtt{...} : draw text in typewriter font \image html jkqtmathtext/jkqtmathtext_fonts.png - \c \\textcolor{color}{...} \c \\color{color} \c \\mathcolor{color}{...} : draw colored text \image html jkqtmathtext/jkqtmathtext_colored.png - \c \\boxed{...} : draw text with a box around it \image html jkqtmathtext/jkqtmathtext_boxed.png + - \c \\doublebox{...} : draw text with a rounded box around it \image html jkqtmathtext/jkqtmathtext_doublebox.png + - \c \\ovalbox{...} : draw text with a rounded box around it \image html jkqtmathtext/jkqtmathtext_ovalbox.png + - \c \\Ovalbox{...} : draw a thick oval box \image html jkqtmathtext/jkqtmathtext_oovalbox.png + - \c \\ovaldoublebox{...} : draw a double oval box \image html jkqtmathtext/jkqtmathtext_ovaldoublebox.png + - \c \\colorbox{bordercolor}{...} : draw a colored box \image html jkqtmathtext/jkqtmathtext_colorbox.png + - \c \\shaded{backgroundcolor}{...} : draw a filled box \image html jkqtmathtext/jkqtmathtext_shaded.png + - \c \\fcolorbox{bordercolor}{backgroundcolor}{...} : draw a colored, filled box \image html jkqtmathtext/jkqtmathtext_fcolorbox.png - \c \\colorbox{color}{...} : draw a colored box around text \image html jkqtmathtext/jkqtmathtext_colorbox.png - \c \\alpha ... : display the according greek letter \image html jkqtmathtext/jkqtmathtext_greek.png - \c ^{...} \c _{...} : display the contents of braces in superscript/subscript \image html jkqtmathtext/jkqtmathtext_supersub.png @@ -813,6 +820,22 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { * \param quitOnClosingBracket if \c true, quits on encountering a MTTclosebracket token */ JKQTMathTextNode* parseLatexString(bool get, JKQTMathTextBraceType quitOnClosingBrace=JKQTMathTextBraceType::MTBTAny, const QString& quitOnEnvironmentEnd=QString(""), bool quitOnClosingBracket=false); + /** \brief parses a list of string-arguments, i.e. \c {p1}{p2}{...} + * + * \param get call getToken() at the start, otherwise it is expected that currentToken==MTTopenbrace + * \param Nparams the number of parameters to expect + * \param[out] foundError will be set to \c true if an error occured (unexpected token) or \c false otherwise + * \return the list of parameter strings with Nparam entries or an empty or partial list on error + */ + QStringList parseStringParams(bool get, size_t Nparams, bool *foundError=nullptr); + /** \brief parses a single instruction (including it's parameters) + * + * \param[out] _foundError will be set to \c true if an error occured (unexpected token) or \c false otherwise + * \param[out] getNew returns \c true if the parser has to call getToken() to go on + * \return the instruction node or \c nullptr on error (then also \a _foundError is set \c true ) + * \note This method expects the current token currentToken to be MTTinstruction + */ + JKQTMathTextNode* parseInstruction(bool *_foundError=nullptr, bool* getNew=nullptr); /** \brief parse a LaTeX math environment */ JKQTMathTextNode* parseMath(bool get); diff --git a/lib/jkqtmathtext/nodes/jkqtmathtextinstructionnode.cpp b/lib/jkqtmathtext/nodes/jkqtmathtextinstructionnode.cpp index a5c8b8da50..f8965b5688 100644 --- a/lib/jkqtmathtext/nodes/jkqtmathtextinstructionnode.cpp +++ b/lib/jkqtmathtext/nodes/jkqtmathtextinstructionnode.cpp @@ -36,117 +36,534 @@ -JKQTMathTextInstruction1Node::JKQTMathTextInstruction1Node(JKQTMathText* _parent, const QString& name, JKQTMathTextNode* child, const QStringList& parameters): - JKQTMathTextSingleChildNode(child, _parent) +JKQTMathTextInstruction1Node::JKQTMathTextInstruction1Node(JKQTMathText* _parent, const QString& _instructionName, JKQTMathTextNode* child, const QStringList& _parameters): + JKQTMathTextSingleChildNode(child, _parent), + instructionName(_instructionName), + parameters(_parameters) { - this->name=name; - this->parameters=parameters; - - JKQTMathTextEnvironment ev; - if (!setupMTenvironment(ev)) { - parentMathText->addToErrorList(QObject::tr("unknown instruction '%1' found!").arg(name)); - } } JKQTMathTextInstruction1Node::~JKQTMathTextInstruction1Node() { } -QString JKQTMathTextInstruction1Node::getTypeName() const -{ - return QLatin1String("MTinstruction1Node(")+name+")"; +const QString& JKQTMathTextInstruction1Node::getInstructionName() const { + return this->instructionName; } -void JKQTMathTextInstruction1Node::getSizeInternal(QPainter& painter, JKQTMathTextEnvironment currentEv, double& width, double& baselineHeight, double& overallHeight, double& strikeoutPos, const JKQTMathTextNodeSize* /*prevNodeSize*/) { +const QStringList &JKQTMathTextInstruction1Node::getParameters() const { + return this->parameters; +} + + + + + +JKQTMathTextModifiedTextPropsInstructionNode::JKQTMathTextModifiedTextPropsInstructionNode(JKQTMathText* _parent, const QString& name, JKQTMathTextNode* child, const QStringList& parameters): + JKQTMathTextInstruction1Node(_parent, name, child, parameters) +{ +} + +JKQTMathTextModifiedTextPropsInstructionNode::~JKQTMathTextModifiedTextPropsInstructionNode() { +} + + +QString JKQTMathTextModifiedTextPropsInstructionNode::getTypeName() const +{ + return QLatin1String("JKQTMathTextModifiedTextPropsInstructionNode(")+instructionName+")"; +} + +void JKQTMathTextModifiedTextPropsInstructionNode::getSizeInternal(QPainter& painter, JKQTMathTextEnvironment currentEv, double& width, double& baselineHeight, double& overallHeight, double& strikeoutPos, const JKQTMathTextNodeSize* /*prevNodeSize*/) { JKQTMathTextEnvironment ev=currentEv; - setupMTenvironment(ev); + executeInstruction(ev); getChild()->getSize(painter, ev, width, baselineHeight, overallHeight, strikeoutPos); - if (name=="colorbox" || name=="fbox" || name=="boxed") { - QFontMetricsF fm(ev.getFont(parentMathText)); - double xw=fm.boundingRect("x").width(); - width+=xw; - overallHeight+=xw; - baselineHeight+=xw/2.0; - } } -double JKQTMathTextInstruction1Node::draw(QPainter& painter, double x, double y, JKQTMathTextEnvironment currentEv, const JKQTMathTextNodeSize* /*prevNodeSize*/) { +double JKQTMathTextModifiedTextPropsInstructionNode::draw(QPainter& painter, double x, double y, JKQTMathTextEnvironment currentEv, const JKQTMathTextNodeSize* /*prevNodeSize*/) { doDrawBoxes(painter, x, y, currentEv); JKQTMathTextEnvironment ev=currentEv; - setupMTenvironment(ev); + executeInstruction(ev); - QPen oldPen=painter.pen(); - double shiftX=0; - if (name=="colorbox" || name=="fbox" || name=="boxed") { - QColor fcol=currentEv.color; - if (name=="colorbox") fcol=QColor(parameters.value(0, ev.color.name())); - //qDebug()<<"COLOR="<getSize(painter, currentEv, width, baselineHeight, overallHeight, strikeoutPos); - QPen p=painter.pen(); - QFontMetricsF fm(currentEv.getFont(parentMathText)); - double xw=fm.boundingRect("x").width(); - p.setColor(fcol); - painter.setPen(p); - painter.drawRect(QRectF(x,y-baselineHeight-xw/2,width+xw,overallHeight+xw)); - shiftX=xw/2.0; - } - - double xnew= getChild()->draw(painter, x+shiftX, y, ev); - painter.setPen(oldPen); - return xnew; + return getChild()->draw(painter, x, y, ev); } -bool JKQTMathTextInstruction1Node::toHtml(QString &html, JKQTMathTextEnvironment currentEv, JKQTMathTextEnvironment defaultEv) { +bool JKQTMathTextModifiedTextPropsInstructionNode::toHtml(QString &html, JKQTMathTextEnvironment currentEv, JKQTMathTextEnvironment defaultEv) { JKQTMathTextEnvironment ev=currentEv; - - setupMTenvironment(ev); + fillInstructions(); + executeInstruction(ev); return getChild()->toHtml(html, ev, defaultEv); } -QString JKQTMathTextInstruction1Node::getName() const { - return this->name; -} - -QStringList JKQTMathTextInstruction1Node::getParameters() const { - return this->parameters; -} - -bool JKQTMathTextInstruction1Node::setupMTenvironment(JKQTMathTextEnvironment &ev) const +bool JKQTMathTextModifiedTextPropsInstructionNode::supportsInstructionName(const QString &instructionName) { - if (name=="bf" || name=="textbf" || name=="mathbf") ev.bold=true; - else if (name=="em") ev.italic=!ev.italic; - else if (name=="it" || name=="textit" || name=="mathit") ev.italic=true; - else if (name=="textcolor" || name=="mathcolor" || name=="color") ev.color=QColor(parameters.value(0, ev.color.name())); - else if (name=="ensuremath" || name=="equation") { ev.italic=true; ev.insideMath=true; } - else if (name=="sc" || name=="textsc" || name=="mathsc") ev.smallCaps=true; - else if (name=="ul" || name=="underline" || name=="underlined") ev.underlined=true; - else if (name=="ol" || name=="overline" || name=="overlined") ev.overline=true; - else if (name=="strike") ev.strike=true; - else if (name=="rm" || name=="textrm") { ev.font=JKQTMathTextEnvironmentFont::MTEroman; ev.italic=false; } - else if (name=="mathrm" || name=="unit" || name=="operatorname") { ev.font=JKQTMathTextEnvironmentFont::MTEroman; ev.italic=false; } - else if (name=="mathbfit" || name=="bfit" || name=="textbfit") { ev.bold=true; ev.italic=true; } - else if (name=="text" || name=="mbox" || name=="ensuretext") { ev.insideMath=false; ev.font=JKQTMathTextEnvironmentFont::MTEroman; ev.italic=false; } - else if (name=="mat") { ev.font=JKQTMathTextEnvironmentFont::MTEroman; ev.italic=false; ev.bold=true; } - else if (name=="cal" || name=="textcal" || name=="mathcal") { ev.font=JKQTMathTextEnvironmentFont::MTEcaligraphic; ev.italic=false; } - else if (name=="fcal" || name=="textfcal" || name=="mathfcal") { ev.font=JKQTMathTextEnvironmentFont::MTEcaligraphic; ev.bold=true; } - else if (name=="frak" || name=="textfrak" || name=="mathfrak") { ev.font=JKQTMathTextEnvironmentFont::MTEfraktur; ev.italic=false; } - else if (name=="ffrak" || name=="textffrak" || name=="mathffrak") { ev.font=JKQTMathTextEnvironmentFont::MTEfraktur; ev.bold=true; } - else if (name=="bb" || name=="textbb" || name=="mathbb") { ev.font=JKQTMathTextEnvironmentFont::MTEblackboard; ev.italic=false; } - else if (name=="tt" || name=="texttt" || name=="mathtt") { ev.font=JKQTMathTextEnvironmentFont::MTEtypewriter; ev.italic=false; } - else if (name=="sf" || name=="textsf" || name=="mathsf") { ev.font=JKQTMathTextEnvironmentFont::MTEsans; ev.italic=false; } - else if (name=="sfit" || name=="textsfit" || name=="mathsfit") { ev.font=JKQTMathTextEnvironmentFont::MTEsans; ev.italic=true; } - else if (name=="script" || name=="scr" || name=="textscript" || name=="textscr" || name=="mathscript" || name=="mathscr") { ev.font=JKQTMathTextEnvironmentFont::MTEscript; ev.italic=false; } - else if (name=="fscript" || name=="fscr" || name=="textfscript" || name=="textfscr" || name=="mathfscript" || name=="mathfscr") { ev.font=JKQTMathTextEnvironmentFont::MTEscript; ev.bold=true; ev.italic=false; } - else if (name=="displaystyle") { ev.fontSize=ev.fontSize/0.8; } - else if (name=="scriptstyle") { ev.fontSize=ev.fontSize*0.8; } - else if (name=="scriptscriptstyle") { ev.fontSize=ev.fontSize*0.8*0.8; } - else { - return false; + fillInstructions(); + return instructions.contains(instructionName); +} + +size_t JKQTMathTextModifiedTextPropsInstructionNode::countParametersOfInstruction(const QString &instructionName) +{ + fillInstructions(); + if (instructions.contains(instructionName)) return instructions[instructionName].NParams; + return 0; +} + +void JKQTMathTextModifiedTextPropsInstructionNode::modifyInMathEnvironment(const QString &instructionName, bool &insideMath, const QStringList& params) +{ + fillInstructions(); + if (instructions.contains(instructionName)) { + JKQTMathTextEnvironment ev; + ev.insideMath=insideMath; + instructions[instructionName].modifier(ev, params); + insideMath=ev.insideMath; + } +} + + +void JKQTMathTextModifiedTextPropsInstructionNode::executeInstruction(JKQTMathTextEnvironment &ev) const +{ + instructions.value(getInstructionName(), InstructionProperties()).modifier(ev, getParameters()); +} + +QHash JKQTMathTextModifiedTextPropsInstructionNode::instructions; + +void JKQTMathTextModifiedTextPropsInstructionNode::fillInstructions() +{ + + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) {ev.bold=true; }, 0); + instructions["bf"] = i; + instructions["textbf"] = i; + instructions["mathbf"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) {ev.italic=!ev.italic; }, 0); + instructions["em"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) {ev.italic=true; }, 0); + instructions["it"] = i; + instructions["textit"] = i; + instructions["mathit"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) {ev.color=jkqtp_String2QColor(parameters.value(0, ev.color.name())); }, 1); + instructions["textcolor"] = i; + instructions["mathcolor"] = i; + instructions["color"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.italic=true; ev.insideMath=true; }, 0); + instructions["ensuremath"] = i; + instructions["equation"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) {ev.smallCaps=true; }, 0); + instructions["sc"] = i; + instructions["textsc"] = i; + instructions["mathsc"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) {ev.underlined=true; }, 0); + instructions["ul"] = i; + instructions["underline"] = i; + instructions["underlined"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) {ev.overline=true; }, 0); + instructions["ol"] = i; + instructions["overline"] = i; + instructions["overlined"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) {ev.strike=true; }, 0); + instructions["strike"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.font=JKQTMathTextEnvironmentFont::MTEroman; ev.italic=false; }, 0); + instructions["rm"] = i; + instructions["textrm"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.font=JKQTMathTextEnvironmentFont::MTEroman; ev.italic=false; }, 0); + instructions["mathrm"] = i; + instructions["unit"] = i; + instructions["operatorname"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.bold=true; ev.italic=true; }, 0); + instructions["mathbfit"] = i; + instructions["bfit"] = i; + instructions["textbfit"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.insideMath=false; ev.font=JKQTMathTextEnvironmentFont::MTEroman; ev.italic=false; }, 0); + instructions["text"] = i; + instructions["mbox"] = i; + instructions["ensuretext"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.font=JKQTMathTextEnvironmentFont::MTEroman; ev.italic=false; ev.bold=true; }, 0); + instructions["mat"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.font=JKQTMathTextEnvironmentFont::MTEcaligraphic; ev.italic=false; }, 0); + instructions["cal"] = i; + instructions["textcal"] = i; + instructions["mathcal"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.font=JKQTMathTextEnvironmentFont::MTEcaligraphic; ev.bold=true; }, 0); + instructions["fcal"] = i; + instructions["textfcal"] = i; + instructions["mathfcal"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.font=JKQTMathTextEnvironmentFont::MTEfraktur; ev.italic=false; }, 0); + instructions["frak"] = i; + instructions["textfrak"] = i; + instructions["mathfrak"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.font=JKQTMathTextEnvironmentFont::MTEfraktur; ev.bold=true; }, 0); + instructions["ffrak"] = i; + instructions["textffrak"] = i; + instructions["mathffrak"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.font=JKQTMathTextEnvironmentFont::MTEblackboard; ev.italic=false; }, 0); + instructions["bb"] = i; + instructions["textbb"] = i; + instructions["mathbb"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.font=JKQTMathTextEnvironmentFont::MTEtypewriter; ev.italic=false; }, 0); + instructions["tt"] = i; + instructions["texttt"] = i; + instructions["mathtt"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.font=JKQTMathTextEnvironmentFont::MTEsans; ev.italic=false; }, 0); + instructions["sf"] = i; + instructions["textsf"] = i; + instructions["mathsf"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.font=JKQTMathTextEnvironmentFont::MTEsans; ev.italic=true; }, 0); + instructions["sfit"] = i; + instructions["textsfit"] = i; + instructions["mathsfit"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.font=JKQTMathTextEnvironmentFont::MTEscript; ev.italic=false; }, 0); + instructions["script"] = i; + instructions["scr"] = i; + instructions["textscript"] = i; + instructions["textscr"] = i; + instructions["mathscript"] = i; + instructions["mathscr"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.font=JKQTMathTextEnvironmentFont::MTEscript; ev.bold=true; ev.italic=false; }, 0); + instructions["fscript"] = i; + instructions["fscr"] = i; + instructions["textfscript"] = i; + instructions["textfscr"] = i; + instructions["mathfscript"] = i; + instructions["mathfscr"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.fontSize=ev.fontSize/0.8; }, 0); + instructions["displaystyle"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.fontSize=ev.fontSize*0.8; }, 0); + instructions["scriptstyle"]= i; + } + { + InstructionProperties i([](JKQTMathTextEnvironment& ev, const QStringList& parameters) { ev.fontSize=ev.fontSize*0.8*0.8; }, 0); + instructions["scriptscriptstyle"]= i; + } +} + +JKQTMathTextModifiedTextPropsInstructionNode::InstructionProperties::InstructionProperties(): + NParams(0), + modifier([](JKQTMathTextEnvironment&, const QStringList&) {}) +{ + +} + +JKQTMathTextModifiedTextPropsInstructionNode::InstructionProperties::InstructionProperties(const ModifyEnvironmentFunctor &_modifier, size_t _NParams): + modifier(_modifier), + NParams(_NParams) +{ + +} + + + + + + + + + + + + + + +JKQTMathTextBoxInstructionNode::JKQTMathTextBoxInstructionNode(JKQTMathText* _parent, const QString& name, JKQTMathTextNode* child, const QStringList& parameters): + JKQTMathTextInstruction1Node(_parent, name, child, parameters) +{ +} + +JKQTMathTextBoxInstructionNode::~JKQTMathTextBoxInstructionNode() { +} + + +QString JKQTMathTextBoxInstructionNode::getTypeName() const +{ + return QLatin1String("JKQTMathTextBoxInstructionNode(")+instructionName+")"; +} + +void JKQTMathTextBoxInstructionNode::getSizeInternal(QPainter& painter, JKQTMathTextEnvironment currentEv, double& width, double& baselineHeight, double& overallHeight, double& strikeoutPos, const JKQTMathTextNodeSize* /*prevNodeSize*/) { + JKQTMathTextEnvironment ev=currentEv; + + const auto& inst=instructions.value(getInstructionName()); + inst.modifier(ev, getParameters()); + const QPen p=inst.pen(ev, getParameters(), parentMathText); + const QBrush b=inst.brush(ev, getParameters(), parentMathText); + const QFontMetricsF fmNonItalic(JKQTMathTextGetNonItalic(currentEv.getFont(parentMathText))); + const double lw=p.widthF(); + const double padding=inst.paddingFactor*fmNonItalic.tightBoundingRect("x").width(); + + getChild()->getSize(painter, ev, width, baselineHeight, overallHeight, strikeoutPos); + width=width+2.0*(padding+lw/2.0); + baselineHeight=baselineHeight+padding+lw/2.0; + overallHeight=overallHeight+2.0*(padding+lw/2.0); +} + +double JKQTMathTextBoxInstructionNode::draw(QPainter& painter, double x, double y, JKQTMathTextEnvironment currentEv, const JKQTMathTextNodeSize* /*prevNodeSize*/) { + doDrawBoxes(painter, x, y, currentEv); + JKQTMathTextEnvironment ev=currentEv; + + const auto& inst=instructions.value(getInstructionName()); + inst.modifier(ev, getParameters()); + const QPen p=inst.pen(ev, getParameters(), parentMathText); + const QBrush b=inst.brush(ev, getParameters(), parentMathText); + const QFontMetricsF fmNonItalic(JKQTMathTextGetNonItalic(currentEv.getFont(parentMathText))); + const double lw=p.widthF(); + const double padding=inst.paddingFactor*fmNonItalic.tightBoundingRect("x").width(); + const double rr=inst.roundingFactor*fmNonItalic.tightBoundingRect("x").width(); + double width=0, baselineHeight=0, overallHeight=0, strikeoutPos=0; + getChild()->getSize(painter, ev, width, baselineHeight, overallHeight, strikeoutPos); + + { + painter.save(); + JKQTPFinalAct __finalpaint([&painter]() { painter.restore(); }); + painter.setBrush(b); + painter.setPen(p); + const QRectF rect(x+lw/2.0, y-baselineHeight-padding-lw/2.0, width+2.0*padding, overallHeight+2.0*padding); + if (rr>0) painter.drawRoundedRect(rect, rr, rr, Qt::AbsoluteSize); + else painter.drawRect(rect); + if (inst.doubleLine) { + painter.setBrush(Qt::NoBrush); + QPen p2=p; + p2.setWidthF(p.widthF()*0.6); + const QRectF recti(x+lw*2.5, y-baselineHeight-lw/2.0-padding+2.0*lw, width+2.0*padding-4.0*lw, overallHeight+2.0*padding-4.0*lw); + if (rr>0) painter.drawRoundedRect(recti, rr, rr, Qt::AbsoluteSize); + else painter.drawRect(recti); + } } - return true; + double xnew = getChild()->draw(painter, x+padding+lw/2.0, y, ev); + + return xnew+padding+lw/2.0; +} + +bool JKQTMathTextBoxInstructionNode::toHtml(QString &html, JKQTMathTextEnvironment currentEv, JKQTMathTextEnvironment defaultEv) { + JKQTMathTextEnvironment ev=currentEv; + fillInstructions(); + const auto& inst=instructions.value(getInstructionName()); + inst.modifier(ev, getParameters()); + const QPen p=inst.pen(ev, getParameters(), parentMathText); + const QBrush b=inst.brush(ev, getParameters(), parentMathText); + const QFontMetricsF fmNonItalic(JKQTMathTextGetNonItalic(currentEv.getFont(parentMathText))); + const double lw=p.widthF(); + const double padding=inst.paddingFactor*fmNonItalic.tightBoundingRect("x").width(); + const double rr=inst.roundingFactor*fmNonItalic.tightBoundingRect("x").width(); + QString s=QString("padding: %1px").arg(padding); + if (p!=Qt::NoPen) { + if (s.size()>0 && s.right(2)!="; ") s=s+"; "; + if (p.widthF()>0 && p.color()!=Qt::transparent) s=s+"border-color: "+p.color().name(); + if (s.size()>0 && s.right(2)!="; ") s=s+"; "; + if (p.widthF()>0 && p.color()!=Qt::transparent) s=s+QString("border-width: %1px").arg(p.width()); + } + if (b!=Qt::NoBrush) { + if (s.size()>0 && s.right(2)!="; ") s=s+"; "; + if (b.color()!=Qt::transparent) s=s+"background-color: "+b.color().name(); + } + + if (s.size()>0) html=html+ QString("").arg(s); + bool ok=getChild()->toHtml(html, ev, defaultEv); + if (s.size()>0) html=html+""; + return ok; +} + +bool JKQTMathTextBoxInstructionNode::supportsInstructionName(const QString &instructionName) +{ + fillInstructions(); + return instructions.contains(instructionName); +} + +size_t JKQTMathTextBoxInstructionNode::countParametersOfInstruction(const QString &instructionName) +{ + fillInstructions(); + if (instructions.contains(instructionName)) return instructions[instructionName].NParams; + return 0; +} + +void JKQTMathTextBoxInstructionNode::modifyInMathEnvironment(const QString &instructionName, bool &insideMath, const QStringList& params) +{ + fillInstructions(); + if (instructions.contains(instructionName)) { + JKQTMathTextEnvironment ev; + ev.insideMath=insideMath; + instructions[instructionName].modifier(ev, params); + insideMath=ev.insideMath; + } +} + + +QHash JKQTMathTextBoxInstructionNode::instructions; + +void JKQTMathTextBoxInstructionNode::fillInstructions() +{ + + { + InstructionProperties i(InstructionProperties::NoModification, + InstructionProperties::DefaultPen, + InstructionProperties::NoBrush, + InstructionProperties::DefaultPadding, + /*Nparams=*/0); + instructions["fbox"] = i; + instructions["framebox"] = i; + instructions["boxed"] = i; + instructions["framed"] = i; + } + { + InstructionProperties i(InstructionProperties::NoModification, + InstructionProperties::DefaultPen, + InstructionProperties::NoBrush, + InstructionProperties::DefaultPadding, + /*Nparams=*/0); + i.doubleLine=true; + instructions["doublebox"] = i; + } + { + InstructionProperties i(InstructionProperties::NoModification, + InstructionProperties::DefaultPen, + InstructionProperties::NoBrush, + InstructionProperties::DefaultPadding, + /*Nparams=*/0); + i.roundingFactor=0.7; + instructions["ovalbox"] = i; + } + { + InstructionProperties i(InstructionProperties::NoModification, + [](JKQTMathTextEnvironment& ev, const QStringList& parameters, JKQTMathText* parent){ + QPen p=InstructionProperties::DefaultPen(ev, parameters, parent); + p.setWidthF(p.widthF()*1.5); + return p; + }, + InstructionProperties::NoBrush, + InstructionProperties::DefaultPadding, + /*Nparams=*/0); + i.roundingFactor=0.8; + instructions["Ovalbox"] = i; + } + { + InstructionProperties i(InstructionProperties::NoModification, + InstructionProperties::DefaultPen, + InstructionProperties::NoBrush, + InstructionProperties::DefaultPadding, + /*Nparams=*/0); + i.roundingFactor=0.7; + i.doubleLine=true; + instructions["ovaldoublebox"] = i; + } + { + InstructionProperties i(InstructionProperties::NoModification, + [](JKQTMathTextEnvironment& ev, const QStringList& parameters, JKQTMathText* parent){ + QPen p=InstructionProperties::DefaultPen(ev, parameters, parent); + p.setColor(jkqtp_String2QColor(parameters.value(0, p.color().name()))); + return p; + }, + InstructionProperties::NoBrush, + InstructionProperties::DefaultPadding, + /*Nparams=*/1); + instructions["colorbox"] = i; + } + { + InstructionProperties i(InstructionProperties::NoModification, + InstructionProperties::NoPen, + [](JKQTMathTextEnvironment& ev, const QStringList& parameters, JKQTMathText* parent){ + return QBrush(jkqtp_String2QColor(parameters.value(0, QColor(Qt::transparent).name())), Qt::SolidPattern); + }, + InstructionProperties::DefaultPadding, + /*Nparams=*/1); + instructions["shaded"] = i; + } + { + InstructionProperties i(InstructionProperties::NoModification, + [](JKQTMathTextEnvironment& ev, const QStringList& parameters, JKQTMathText* parent){ + QPen p=InstructionProperties::DefaultPen(ev, parameters, parent); + p.setColor(jkqtp_String2QColor(parameters.value(0, p.color().name()))); + return p; + }, + [](JKQTMathTextEnvironment& ev, const QStringList& parameters, JKQTMathText* parent){ + return QBrush(jkqtp_String2QColor(parameters.value(1, QColor(Qt::transparent).name())), Qt::SolidPattern); + }, + InstructionProperties::DefaultPadding, + /*Nparams=*/2); + instructions["fcolorbox"] = i; + } +} + +JKQTMathTextBoxInstructionNode::InstructionProperties::ModifyEnvironmentFunctor JKQTMathTextBoxInstructionNode::InstructionProperties::NoModification= + [](JKQTMathTextEnvironment& ev, const QStringList& parameters){}; +JKQTMathTextBoxInstructionNode::InstructionProperties::GetBoxPenFunctor JKQTMathTextBoxInstructionNode::InstructionProperties::DefaultPen= + [](JKQTMathTextEnvironment& ev, const QStringList& parameters, JKQTMathText* parent){ return QPen(ev.color, QFontMetricsF(ev.getFont(parent)).lineWidth(), Qt::SolidLine); }; +JKQTMathTextBoxInstructionNode::InstructionProperties::GetBoxPenFunctor JKQTMathTextBoxInstructionNode::InstructionProperties::NoPen= + [](JKQTMathTextEnvironment& ev, const QStringList& parameters, JKQTMathText* parent){ return Qt::NoPen; }; +JKQTMathTextBoxInstructionNode::InstructionProperties::GetBoxBrushFunctor JKQTMathTextBoxInstructionNode::InstructionProperties::NoBrush= + [](JKQTMathTextEnvironment& ev, const QStringList& parameters, JKQTMathText* parent){ return Qt::NoBrush; }; +double JKQTMathTextBoxInstructionNode::InstructionProperties::DefaultPadding=0.5; + +JKQTMathTextBoxInstructionNode::InstructionProperties::InstructionProperties(): + InstructionProperties(NoModification, DefaultPen, NoBrush, DefaultPadding, 0) +{ + +} + +JKQTMathTextBoxInstructionNode::InstructionProperties::InstructionProperties(const GetBoxPenFunctor &_pen, const GetBoxBrushFunctor &_brush, double _paddingFactor, size_t _NParams): + InstructionProperties(NoModification, _pen, _brush, _paddingFactor, _NParams) + +{ + +} + +JKQTMathTextBoxInstructionNode::InstructionProperties::InstructionProperties(const ModifyEnvironmentFunctor &_modifier, const GetBoxPenFunctor &_pen, const GetBoxBrushFunctor &_brush, double _paddingFactor, size_t _NParams): + modifier(_modifier), + pen(_pen), + brush(_brush), + NParams(_NParams), + paddingFactor(_paddingFactor), + doubleLine(false), + roundingFactor(0.0) +{ + } diff --git a/lib/jkqtmathtext/nodes/jkqtmathtextinstructionnode.h b/lib/jkqtmathtext/nodes/jkqtmathtextinstructionnode.h index 39a8d00b38..aa8e8ab156 100644 --- a/lib/jkqtmathtext/nodes/jkqtmathtextinstructionnode.h +++ b/lib/jkqtmathtext/nodes/jkqtmathtextinstructionnode.h @@ -27,17 +27,46 @@ #include "jkqtmathtext/jkqtmathtexttools.h" #include "jkqtmathtext/nodes/jkqtmathtextnode.h" #include +#include class JKQTMathText; // forward // JKQTMATHTEXT_LIB_EXPORT -/** \brief subclass representing an instruction node with exactly one argument in the syntax tree +/** \brief subclass representing an instruction node with exactly one argument and possibly additional parameters in the syntax tree + * This is a base-class without concrete implementations ... Implementations can be found in derived classes! * \ingroup jkqtmathtext_items + * + * The parameters are sinple strings (e.g. directly for instructions like \c \\textcolor{color}{coloredLatexText}, + * but they might also be translated into numbers or other properties. + * + * Typically derived nodes will provide static methods to determine the number of instructions + * and whether an instruction name is supported. */ class JKQTMATHTEXT_LIB_EXPORT JKQTMathTextInstruction1Node: public JKQTMathTextSingleChildNode { public: explicit JKQTMathTextInstruction1Node(JKQTMathText* parent, const QString& name, JKQTMathTextNode* child, const QStringList& parameters=QStringList()); virtual ~JKQTMathTextInstruction1Node() override; + /** \copydoc name */ + const QString& getInstructionName() const; + /** \copydoc parameters */ + const QStringList& getParameters() const; + protected: + /** \brief instruction name */ + QString instructionName; + /** \brief additional string-parameters */ + QStringList parameters; +}; + + + +/** \brief subclass representing an instruction node which modifies the current font (as defined in JKQTMathTextEnvironment), + * i.e. it represents instructions like \c \\textbf{...}, \c \\ul{underlinedText}, ... + * \ingroup jkqtmathtext_items + */ +class JKQTMATHTEXT_LIB_EXPORT JKQTMathTextModifiedTextPropsInstructionNode: public JKQTMathTextInstruction1Node { + public: + explicit JKQTMathTextModifiedTextPropsInstructionNode(JKQTMathText* parent, const QString& name, JKQTMathTextNode* child, const QStringList& parameters=QStringList()); + virtual ~JKQTMathTextModifiedTextPropsInstructionNode() override; /** \copydoc JKQTMathTextNode::getTypeName() */ virtual QString getTypeName() const override; /** \copydoc JKQTMathTextNode::draw() */ @@ -45,19 +74,133 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathTextInstruction1Node: public JKQTMathTextS /** \brief convert node to HTML and returns \c true on success */ /** \copydoc JKQTMathTextNode::toHtml() */ virtual bool toHtml(QString& html, JKQTMathTextEnvironment currentEv, JKQTMathTextEnvironment defaultEv) override; - /** \copydoc name */ - QString getName() const; - /** \copydoc parameters */ - QStringList getParameters() const; + + /** \brief returns true, if the given \a instructionName can be represented by this node + * \see instructions + */ + static bool supportsInstructionName(const QString& instructionName); + /** \brief returns the number of additional string parameters, required for the given \a instructionName + * \see instructions + */ + static size_t countParametersOfInstruction(const QString& instructionName); + /** \brief sets \a insideMath to \c true if inside the node is to be parsed in math mode and \c false else + * \see instructions + */ + static void modifyInMathEnvironment(const QString& instructionName, bool& insideMath, const QStringList ¶ms=QStringList()); + + protected: + /** \copydoc JKQTMathTextNode::getSizeInternal() */ + virtual void getSizeInternal(QPainter& painter, JKQTMathTextEnvironment currentEv, double& width, double& baselineHeight, double& overallHeight, double& strikeoutPos, const JKQTMathTextNodeSize* prevNodeSize=nullptr) override; + /** \brief defines the implementation of an instruction represented by JKQTMathTextModifiedTextPropsInstructionNode */ + struct InstructionProperties { + /** \brief this functor implements the instruction */ + typedef std::function ModifyEnvironmentFunctor; + /** \brief default constructor, creates a NOP-instruction that does nothing */ + InstructionProperties(); + /** \brief constructor which gets a functor \a _modifier and a number of required parameters \a _NParams */ + InstructionProperties(const ModifyEnvironmentFunctor& _modifier, size_t _NParams=0); + /** \brief number of parameters for this node */ + size_t NParams; + /** \brief functor that modifies a JKQTMathTextEnvironment */ + ModifyEnvironmentFunctor modifier; + }; + + /** \brief fills instructions + * + * \note this is the customization point for new instructions! + */ + static void fillInstructions(); + /** \brief defines all implemented instructions in this node */ + static QHash instructions; + /** \brief executes the instruction on \a ev */ + void executeInstruction(JKQTMathTextEnvironment& ev) const; +}; + + +/** \brief subclass representing an instruction node which draws a (possibly colored) box around it's contents + * i.e. it represents instructions like \c \\fbox{...}, \c \\colorbox{color}{...}, ... + * \ingroup jkqtmathtext_items + */ +class JKQTMATHTEXT_LIB_EXPORT JKQTMathTextBoxInstructionNode: public JKQTMathTextInstruction1Node { + public: + explicit JKQTMathTextBoxInstructionNode(JKQTMathText* parent, const QString& name, JKQTMathTextNode* child, const QStringList& parameters=QStringList()); + virtual ~JKQTMathTextBoxInstructionNode() override; + /** \copydoc JKQTMathTextNode::getTypeName() */ + virtual QString getTypeName() const override; + /** \copydoc JKQTMathTextNode::draw() */ + virtual double draw(QPainter& painter, double x, double y, JKQTMathTextEnvironment currentEv, const JKQTMathTextNodeSize* prevNodeSize=nullptr) override; + /** \brief convert node to HTML and returns \c true on success */ + /** \copydoc JKQTMathTextNode::toHtml() */ + virtual bool toHtml(QString& html, JKQTMathTextEnvironment currentEv, JKQTMathTextEnvironment defaultEv) override; + + /** \brief returns true, if the given \a instructionName can be represented by this node + * \see instructions + */ + static bool supportsInstructionName(const QString& instructionName); + /** \brief returns true, if the given \a instructionName can be represented by this node + * \see instructions + */ + static void modifyInMathEnvironment(const QString& instructionName, bool& insideMath); + /** \brief returns the number of additional string parameters, required for the given \a instructionName + * \see instructions + */ + static size_t countParametersOfInstruction(const QString& instructionName); + /** \brief sets \a insideMath to \c true if inside the node is to be parsed in math mode and \c false else + * \see instructions + */ + static void modifyInMathEnvironment(const QString& instructionName, bool& insideMath, const QStringList ¶ms=QStringList()); protected: /** \copydoc JKQTMathTextNode::getSizeInternal() */ virtual void getSizeInternal(QPainter& painter, JKQTMathTextEnvironment currentEv, double& width, double& baselineHeight, double& overallHeight, double& strikeoutPos, const JKQTMathTextNodeSize* prevNodeSize=nullptr) override; /** \brief set all properties in \a ev, as appropriate for the represented instruction */ bool setupMTenvironment(JKQTMathTextEnvironment &ev) const; - /** \brief instruction name */ - QString name; - /** \brief additional string-parameters */ - QStringList parameters; + + /** \brief defines the implementation of an instruction represented by JKQTMathTextModifiedTextPropsInstructionNode */ + struct InstructionProperties { + /** \brief this functor implements the instruction */ + typedef std::function ModifyEnvironmentFunctor; + static ModifyEnvironmentFunctor NoModification; + /** \brief this functor returns the QPen to use for the box outline */ + typedef std::function GetBoxPenFunctor; + /** \bbrief generates a QPen with the lineWidth associated with the QFont of the environment (using QFontMetricsF::lineWidth() ) */ + static GetBoxPenFunctor DefaultPen; + /** \bbrief generates an invisible pen with 0 width */ + static GetBoxPenFunctor NoPen; + /** \brief this functor returns the QBrush to use for the box fill */ + typedef std::function GetBoxBrushFunctor; + /** \brief generates an invisible QBrush */ + static GetBoxBrushFunctor NoBrush; + /** \brief default padding factor 0.5 xWidth */ + static double DefaultPadding; + /** \brief default constructor, creates a NOP-instruction that does nothing */ + InstructionProperties(); + /** \brief constructor */ + InstructionProperties(const ModifyEnvironmentFunctor& _modifier, const GetBoxPenFunctor& _pen, const GetBoxBrushFunctor& _brush, double _paddingFactor=DefaultPadding, size_t _NParams=0); + /** \brief constructor */ + InstructionProperties(const GetBoxPenFunctor& _pen, const GetBoxBrushFunctor& _brush, double _paddingFactor=DefaultPadding, size_t _NParams=0); + /** \brief number of parameters for this node */ + size_t NParams; + /** \brief functor that modifies a JKQTMathTextEnvironment */ + ModifyEnvironmentFunctor modifier; + /** \brief functor that creates a QPen for the box outline */ + GetBoxPenFunctor pen; + /** \brief functor that creates a QBrush for the box fill */ + GetBoxBrushFunctor brush; + /** \brief padding of the box, as a factor to xWidth of the current font */ + double paddingFactor; + /** \brief draw a double-line */ + bool doubleLine; + /** \brief rounding radius (rx=ry) of the box, as a factor to xWidth of the current font */ + double roundingFactor; + }; + + /** \brief fills instructions + * + * \note this is the customization point for new instructions! + */ + static void fillInstructions(); + /** \brief defines all implemented instructions in this node */ + static QHash instructions; }; diff --git a/lib/jkqtmathtext/nodes/jkqtmathtextsymbolnode.cpp b/lib/jkqtmathtext/nodes/jkqtmathtextsymbolnode.cpp index db645875d6..6822470630 100644 --- a/lib/jkqtmathtext/nodes/jkqtmathtextsymbolnode.cpp +++ b/lib/jkqtmathtext/nodes/jkqtmathtextsymbolnode.cpp @@ -726,7 +726,7 @@ void JKQTMathTextSymbolNode::fillSymbolTables() symbols["searrow"]=UprightSymbolUnicode(QChar(0x2198)).addUprightHtml("↘"); symbols["setminus"]=MathOperatorSymbolUnicode(QChar(0x2216)).addMathOperatorHtml("∖"); { auto s=MathOperatorSymbolUnicode(QChar(0x223C)).addMathOperatorHtml("˜").addMathOperatorStd("~"); - symbols["~"]=s; symbols["sim"]=s; symbols["tilde"]=s; } + symbols["~"]=s; symbols["sim"]=s; } symbols["simeq"]=MathOperatorSymbolUnicode(QChar(0x2243)).addMathOperatorHtml("≃"); symbols["sqcap"]=MathOperatorSymbolUnicode(QChar(0x2293)).addMathOperatorHtml("⊓"); symbols["sqcup"]=MathOperatorSymbolUnicode(QChar(0x2294)).addMathOperatorHtml("⊔");