diff --git a/doc/dox/whatsnew.dox b/doc/dox/whatsnew.dox index c686ded028..9740fa3a24 100644 --- a/doc/dox/whatsnew.dox +++ b/doc/dox/whatsnew.dox @@ -18,6 +18,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
  • fixed issue #70: Typo in jkqtplotter/CMakeLists.txt, thanks to user:tedlinlab
  • fixed: styling was not properly applied to coordinate axes of colorbars outside the plot
  • fixed: JKQTMathText added a little whitespace before and after the LaTeX-string. This was removed as it disturbed the layout of text in plots
  • +
  • fixes/improvements: JKQTMathText renders several LaTeX strings better (simple braces in math mode, +-*... as symbols with proper sizes in math mode, added some missing instruction aliases, improved size of \vec and \hat, corrrected fonts usage for mathrm
  • improved: high-dpr-support in JKQTMathText
  • improved: QT6-compatibility by removing deprecated warnings
  • NEW: JKQTPFilledCurveXGraph and JKQTPFilledCurveYGraph can now plot wiggle plots with different fill styles above and below the baseline (feature request #68 Wiggle Plots from user:xichaoqiang
  • diff --git a/examples/jkqtmathtext_test/testform.cpp b/examples/jkqtmathtext_test/testform.cpp index 9aff919fbf..2388dbdb1e 100644 --- a/examples/jkqtmathtext_test/testform.cpp +++ b/examples/jkqtmathtext_test/testform.cpp @@ -17,7 +17,7 @@ TestForm::TestForm(QWidget *parent) : ui->cmbTestset->addItem("named symbols 3", "leftharpoonup: $\\leftharpoonup$\\ rightharpoonup: $\\rightharpoonup$\\ upharpoonleft: $\\upharpoonleft$\\ downharpoonleft: $\\downharpoonleft$\\ leftrightharpoon: $\\leftrightharpoon$\\ rightleftharpoon: $\\rightleftharpoon$"); ui->cmbTestset->addItem("named symbols 4", "coprod: $\\coprod$\\ leftharpoondown: $\\leftharpoondown$\\ rightharpoondown: $\\rightharpoondown$\\ upharpoonright: $\\upharpoonright$\\ downharpoonright: $\\downharpoonright$\\ nwarrow: $\\nwarrow$\\ nearrow: $\\nearrow$\\ "); ui->cmbTestset->addItem("named symbols 5", "searrow: $\\searrow$\\ swarrow: $\\swarrow$\\ mapsto: $\\mapsto$\\ div: $\\div$\\ multimap: $\\multimap$\\ maporiginal: $\\maporiginal$\\ mapimage: $\\mapimage$\\ "); - ui->cmbTestset->addItem("named symbols 6", "times: $\\times$\\ propto: $\\propto$\\ bullet: $\\bullet$\\ neq: $\\neq$\\ ne: $\\ne$\\ equiv: $\\equiv$\\ approx: $\\approx$\\ otimes: $\\otimes$\\ oplus: $\\oplus$"); + ui->cmbTestset->addItem("named symbols 6", "times: $\\times$\\ ast: $\\ast$\\ star: $\\star$\\ propto: $\\propto$\\ bullet: $\\bullet$\\ neq: $\\neq$\\ ne: $\\ne$\\ equiv: $\\equiv$\\ approx: $\\approx$\\ otimes: $\\otimes$\\ oplus: $\\oplus$"); ui->cmbTestset->addItem("named symbols 7", "oslash: $\\oslash$\\ cap: $\\cap$\\ land: $\\land$\\ cup: $\\cup$\\ lor: $\\lor$\\ supset: $\\supset$\\ supseteq: $\\supseteq$\\ supsetnot: $\\supsetnot$\\ subset: $\\subset$"); ui->cmbTestset->addItem("named symbols 8", "subseteq: $\\subseteq$\\ in: $\\in$\\ notin: $\\notin$\\ cdot: $\\cdot$\\ wedge: $\\wedge$\\ vee: $\\vee$\\ cong: $\\cong$\\ bot: $\\bot$"); ui->cmbTestset->addItem("symbols", "$\\ll\\gg\\leq\\geq\\leftrightarrow\\leftarrow\\rightarrow\\to\\uparrow\\downarrow\\updownarrow\\Leftrightarrow\\iff\\Leftarrow\\Rightarrow\\Uparrow\\Downarrow\\Updownarrow\\pm\\mp\\nexists\\ni\\notni\\circ\\sim\\emptyset\\odot\\ominus\\subsetnot\\bot\\leftharpoonup\\rightharpoonup\\upharpoonleft\\downharpoonleft\\leftrightharpoon\\rightleftharpoon\\coprod\\leftharpoondown\\rightharpoondown\\upharpoonright\\downharpoonright\\nwarrow\\nearrow\\searrow\\swarrow\\mapsto\\div\\multimap\\maporiginal\\mapimage\\times\\propto\\bullet\\neq\\ne\\equiv\\approx\\otimes\\oplus\\oslash\\cap\\land\\cup\\lor\\supset\\supseteq\\supsetnot\\subset\\subseteq\\in\\notin\\cdot\\wedge\\vee\\cong\\bot$"); @@ -94,6 +94,11 @@ TestForm::TestForm(QWidget *parent) : ui->cmbTestset->addItem("brace10 test", "\\left\\{\\left[\\left( r^{123}\\right)\\right]\\right\\} -- $\\left\\{\\left[\\left( r^{123}\\right)\\right]\\right\\}$"); ui->cmbTestset->addItem("brace11 test: floor", "\\left\\lfloor\\left\\lfloor\\left\\lfloor r^{123}\\right\\rfloor\\right\\rfloor\\right\\rfloor -- $\\left\\lfloor\\left\\lfloor\\left\\lfloor r^{123}\\right\\rfloor\\right\\rfloor\\right\\rfloor$"); ui->cmbTestset->addItem("brace12 test: ceil", "\\left\\lceil\\left\\lceil\\left\\lceil r^{123}\\right\\rceil\\right\\rceil\\right\\rceil -- $\\left\\lceil\\left\\lceil\\left\\lceil r^{123}\\right\\rceil\\right\\rceil\\right\\rceil$"); + ui->cmbTestset->addItem("brace13 test: non-left/right ( )", "(((r^{123}))) -- $(((r^{123})))$"); + ui->cmbTestset->addItem("brace14 test: non-left/right [ ]", "[[[r^{123}]]] -- $[[[r^{123}]]]$"); + ui->cmbTestset->addItem("brace15 test: non-left/right { }", "\\{\\{\\{r^{123}\\}\\}\\} -- $\\{\\{\\{ r^{123}\\}\\}\\}$"); + ui->cmbTestset->addItem("brace16 test: non-left/right | |", "|||r^{123}||| -- $|||r^{123}|||$"); + ui->cmbTestset->addItem("brace17 test: non-left/right { | }", "\\{r^{123}|r\\equiv 5\\} -- $\\{r^{123}|r\\equiv 5\\}$"); ui->cmbTestset->addItem("sub-, superscript test", "r^{1234}_{321} r_{321}^{1234} -- $r^{1234}_{321} r_{321}^{1234} -- \\kappa^2 -- \\kappa_2 -- \\kappa_2^2$"); ui->cmbTestset->addItem("super-, subscript test", "r^{123}_{4321} r_{4321}^{123} -- $r^{123}_{4321} r_{4321}^{123} -- \\kappa^2 -- \\kappa_2 -- \\kappa_2^2$"); //ui->cmbTestset->addItem("", ""); @@ -111,7 +116,7 @@ TestForm::TestForm(QWidget *parent) : ui->cmbTestset->addItem("math 12 (under/overbrace)", "$\\underbrace{\\overbrace{x+x+...+x}{k\\ \\mathrm{times}} \\overbrace{x+x+...+x}{k\\ \\mathrm{times}}}{2k\\ \\mathrm{times}}$"); ui->cmbTestset->addItem("math 13", "$y_1''\\ \\ \\ y_2'''$"); ui->cmbTestset->addItem("math 14", "$f(x)=\\begin{cases} 1/3 & \\mathrm{if}\\ 0\\leq x\\leq1 \\\\ 2/3 & \\mathrm{if}\\ 3\\leq x\\leq4 \\\\0 & \\mathrm{elsewhere} \\end{cases}$"); - ui->cmbTestset->addItem("math 15", "$\\Re{z} =\\frac{n\\pi \\dfrac{\\theta +\\psi}{2}}{\\left(\\dfrac{\\theta +\\psi}{2}\\right)^2 + \\left( \\dfrac{1}{2}\\log \\left\\lvert\\dfrac{B}{A}\\right\\rvert\\right)^2}.$"); + ui->cmbTestset->addItem("math 15", "$\\Re(z) =\\frac{n\\pi \\dfrac{\\theta +\\psi}{2}}{\\left(\\dfrac{\\theta +\\psi}{2}\\right)^2 + \\left( \\dfrac{1}{2}\\log \\left\\lvert\\dfrac{B}{A}\\right\\rvert\\right)^2}.$"); ui->cmbTestset->addItem("math 16", "$\\sum_{m=1}^\\infty\\sum_{n=1}^\\infty\\frac{m^2\\,n}{3^m\\left(m\\,3^n+n\\,3^m\\right)}$"); ui->cmbTestset->addItem("math 17", "$\\phi_n(\\kappa) =\\frac{1}{4\\pi^2\\kappa^2} \\int_0^\\infty\\frac{\\sin(\\kappa R)}{\\kappa R}\\frac{\\partial}{\\partial R}\\left[R^2\\frac{\\partial D_n(R)}{\\partial R}\\right]\\,dR$"); ui->cmbTestset->addItem("math 18", "${}_pF_q(a_1,\\dots,a_p;c_1,\\dots,c_q;z)= \\sum_{n=0}^\\infty\\frac{(a_1)_n\\cdots(a_p)_n}{(c_1)_n\\cdots(c_q)_n}\\frac{z^n}{n!}$"); diff --git a/lib/jkqtmathtext/jkqtmathtext.cpp b/lib/jkqtmathtext/jkqtmathtext.cpp index effdce4047..e72df19f30 100644 --- a/lib/jkqtmathtext/jkqtmathtext.cpp +++ b/lib/jkqtmathtext/jkqtmathtext.cpp @@ -415,11 +415,12 @@ QString JKQTMathText::MTtextNode::textTransform(const QString &text, JKQTMathTex switch(c.unicode()) { case '-': txt+=QString(QString(" ")+QChar(0x2212)); break; case '+': txt+=QString(QString(" +")); break; - case '/': txt+=QString(QString(" / ")); break; - case '<': txt+=QString(QString(" < ")); break; - case '>': txt+=QString(QString(" > ")); break; - case '=': txt+=QString(QString(" = ")); break; + case '/': txt+=QString(QString(" /")); break; + case '<': txt+=QString(QString(" <")); break; + case '>': txt+=QString(QString(" >")); break; + case '=': txt+=QString(QString(" =")); break; case ';': txt+=QString(QString("; ")); break; + case ',': txt+=QString(QString(", ")); break; default: txt+=c; break; } } @@ -539,7 +540,7 @@ bool JKQTMathText::MTinstruction1Node::setupMTenvironment(JKQTMathText::MTenviro 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=JKQTMathText::MTEroman; ev.italic=false; } - else if (name=="mathrm" || name=="operatorname") { ev.font=JKQTMathText::MTEroman; ev.italic=false; } + else if (name=="mathrm" || name=="unit" || name=="operatorname") { ev.font=JKQTMathText::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=JKQTMathText::MTEroman; ev.italic=false; } else if (name=="mat") { ev.font=JKQTMathText::MTEroman; ev.italic=false; ev.bold=true; } @@ -1185,7 +1186,7 @@ double JKQTMathText::MTdecoratedNode::draw(QPainter& painter, double x, double y if (decoration==MTDvec) { painter.setPen(p); QPolygonF poly; - poly<") { props.symbol = ">"; props.bold = 0; props.italic = 0; } + else if (n == "<") { props.symbol = "<"; props.bold = 0; props.italic = 0; } else if (n == "$") { props.symbol = "$"; props.bold = 0; props.italic = 0; } else if (n == "%") { props.symbol = "%"; props.bold = 0; props.italic = 0; } else if (n == "&") { props.symbol = "&"; props.bold = 0; props.italic = 0; } else if (n == "#") { props.symbol = "#"; props.bold = 0; props.italic = 0; } else if (n == "ast") { props.symbol = "*"; props.bold = 0; props.italic = 0; } + else if (n == "asterisk") { props.symbol = "*"; props.bold = 0; props.italic = 0; } else if (n == "glq") { props.symbol = "'"; props.bold = 0; props.italic = 0; } else if (n == "grq") { props.symbol = "'"; props.bold = 0; props.italic = 0; } else if (n == "glqq") { props.symbol = "\""; props.bold = 0; props.italic = 0; } @@ -2354,9 +2368,21 @@ bool JKQTMathText::MTsymbolNode::getStandardTextSymbolProp(JKQTMathText::MTsymbo //qDebug()<<" +--- getStandardTextSymbolProp("<") { props.symbol = ">"; props.bold = 0; } + else if (n == "|") { props.symbol = "|"; props.bold = 0; } else if (n == "}") { props.symbol = "}"; } else if (n == "{") { props.symbol = "{"; } - else if (n == "backslash") { props.symbol = "\\"; props.bold = 0; props.italic = 0; } + else if (n == "]") { props.symbol = "]"; } + else if (n == "[") { props.symbol = "["; } + else if (n == "(") { props.symbol = "("; } + else if (n == ")") { props.symbol = ")"; } + else if (n == "|") { props.symbol = "|"; } else if (n == "$") { props.symbol = "$"; } else if (n == "%") { props.symbol = "%"; } else if (n == "&") { props.symbol = "&"; } @@ -2381,6 +2407,10 @@ bool JKQTMathText::MTsymbolNode::getUnicodeBaseSymbolProp(JKQTMathText::MTsymbol unicodeBaseSymbol.insert("infty", QChar(0x221E)); unicodeBaseSymbol.insert("partial", QChar(0x2202)); unicodeBaseSymbol.insert("times", QChar(0x2A2F)); + unicodeBaseSymbol.insert("*", QChar(0x2217)); + unicodeBaseSymbol.insert("ast", QChar(0x2217)); + unicodeBaseSymbol.insert("asterisk", QChar(0x2217)); + unicodeBaseSymbol.insert("star", QChar(0x22C6)); unicodeBaseSymbol.insert("bullet", QChar(0x2219)); unicodeBaseSymbol.insert("copyright", QChar(0x00A9)); unicodeBaseSymbol.insert("registered", QChar(0x00AE)); @@ -2413,6 +2443,9 @@ bool JKQTMathText::MTsymbolNode::getUnicodeBaseSymbolProp(JKQTMathText::MTsymbol unicodeBaseSymbol.insert("cdots", QString(QChar(0x00B7)) + QString(QChar(0x00B7)) + QString(QChar(0x00B7))); unicodeBaseSymbol.insert("approx", QChar(0x2248)); unicodeBaseSymbol.insert("Angstroem", QChar(0x212B)); + unicodeBaseSymbol.insert("-", QChar(0x2212)); + unicodeBaseSymbol.insert("dots", QChar(0x2026)); + unicodeBaseSymbol.insert("ldots", QChar(0x2026)); } QHash::iterator itbasesymbol = unicodeBaseSymbol.find(n); @@ -2738,7 +2771,7 @@ JKQTMathText::MTsymbolNode::SymbolProps JKQTMathText::MTsymbolNode::getSymbolPro <<"subsetnot"<<"bot"<<"leftharpoonup"<<"rightharpoonup"<<"upharpoonleft"<<"downharpoonleft"<<"leftrightharpoon"<<"rightleftharpoon"<<"coprod"<<"leftharpoondown" <<"rightharpoondown"<<"upharpoonright"<<"downharpoonright"<<"nwarrow"<<"nearrow"<<"searrow"<<"swarrow"<<"mapsto"<<"div"<<"multimap"<<"maporiginal"<<"mapimage" <<"times"<<"propto"<<"bullet"<<"neq"<<"ne"<<"equiv"<<"approx"<<"otimes"<<"oplus"<<"oslash"<<"cap"<<"land"<<"cup"<<"lor"<<"supset"<<"supseteq"<<"supsetnot" - <<"subset"<<"subseteq"<<"in"<<"notin"<<"cdot"<<"wedge"<<"vee"<<"cong"<<"bot"<<"mid"; + <<"subset"<<"subseteq"<<"in"<<"notin"<<"cdot"<<"wedge"<<"vee"<<"cong"<<"bot"<<"mid"<<"+"<<"-"<<"|"<<"*"<<"/"<<"<"<<">"; } if (extendWInMM.contains(n)) { @@ -2820,23 +2853,34 @@ JKQTMathText::MTsymbolNode::SymbolProps JKQTMathText::MTsymbolNode::getSymbolPro if (itsimplehia != simpleTranslations_heightIsAscent.end()) { props.symbol = itsimplehia.value(); props.heightIsAscent = true; - } else { - props.font=fnt.first; - if (!getSymbolProp(props, n, currentEv, mathFontFactor)) { - errorExplanation="didn't find symbol given font def:"+fnt.first+"["+encoding2String(fnt.second)+"] / sym:"+fntSym.first+"["+encoding2String(fntSym.second)+"] / grk:"+fntGreek.first+"["+encoding2String(fntGreek.second)+"]"; - } + } else { + props.font=fnt.first; + if (!getSymbolProp(props, n, currentEv, mathFontFactor)) { + errorExplanation="didn't find symbol given font def:"+fnt.first+"["+encoding2String(fnt.second)+"] / sym:"+fntSym.first+"["+encoding2String(fntSym.second)+"] / grk:"+fntGreek.first+"["+encoding2String(fntGreek.second)+"]"; } + } } if (addWhitespace) props.symbol+=" "; static QSet extraSymbolName = { "infty", - "|", " ", "quad", "qquad", "space", ";", ":", ",", "!", + "|", " ", "quad", "qquad", "space", ";", ":", ",", "!","%" "longleftarrow", "longrightarrow", "Longleftarrow", "Longrightarrow", "longleftrightarrow", "Longleftrightarrow" }; + + static QSet extraSymbolNonItalic = { + "+", "-", "*", "/", "<", ">", "=", "|", "{", "(", "[", "]", ")", "}", "\\" + }; + + if (props.symbol.simplified().isEmpty() && extraSymbolNonItalic.contains(n)) { + props.symbol=n; + if (n=="=") props.symbol="= "; + props.italic=-1; + } + if (props.symbol.simplified().isEmpty() && !extraSymbolName.contains(n)) { parent->error_list.append(tr("unknown symbol '%1' found (%2)!").arg(n).arg(errorExplanation)); } @@ -3129,6 +3173,9 @@ bool JKQTMathText::MTsymbolNode::toHtml(QString &html, JKQTMathText::MTenvironme entitylut.insert("propto", "∝"); entitylut.insert("partial", "∂"); entitylut.insert("bullet", "•"); + entitylut.insert("star", "⋆"); + entitylut.insert("ast", "∗"); + entitylut.insert("asterisk", "∗"); entitylut.insert("neq", "≠"); entitylut.insert("ne", "≠"); entitylut.insert("equiv", "≡"); @@ -3194,6 +3241,7 @@ bool JKQTMathText::MTsymbolNode::toHtml(QString &html, JKQTMathText::MTenvironme entitylut.insert("DC", "="); entitylut.insert("cdots", "⋅⋅⋅"); entitylut.insert("dots", "..."); + entitylut.insert("ldots", "..."); entitylut.insert("cent", "¢"); @@ -3585,8 +3633,12 @@ QPair JKQTMathText::getReplacementFont(con return res; } -QPair JKQTMathText::getFontData(JKQTMathText::MTenvironmentFont font, bool /*in_math_environment*/, FontSubclass subclass) const +QPair JKQTMathText::getFontData(JKQTMathText::MTenvironmentFont font, bool in_math_environment, FontSubclass subclass) const { + if (in_math_environment) { + if (font==MTEroman) font=MTEmathRoman; + if (font==MTEsans) font=MTEmathSans; + } const auto fd=fontDefinitions.value(font); if (subclass==FontSubclass::Greek) return QPair(fd.symbolfontGreek, fd.symbolfontGreekEncoding); if (subclass==FontSubclass::Symbols) return QPair(fd.symbolfontSymbol, fd.symbolfontSymbolEncoding); @@ -4055,12 +4107,17 @@ JKQTMathText::tokenType JKQTMathText::getToken() { if (parsingMathEnvironment) { // inside math environments we split texts at every brace {[(|)]} so that // braces form their own MTtextNode and may be formated accordingly - if (c=='(' || c=='[' || c=='|' || c==')' || c==']') { + static QSet mathEnvironmentSpecialChars, mathEnvironmentSpecialEndChars; + if (mathEnvironmentSpecialChars.size()==0) { + mathEnvironmentSpecialChars<<'(' << '[' << '|' << ')' << ']' << '+' << '-' << '*' << '/' << '<' << '>' << '='; + mathEnvironmentSpecialEndChars<<'(' << '&' << '[' << '|' << ')' << ']' << '\\' << '$' << '{' << '}' << '_' << '^' << '+' << '-' << '/' << '*' << '=' << '<' << '>'; + } + if (mathEnvironmentSpecialChars.contains(c)) { currentTokenName=c; //std::cout<<"found text node '"<addNode(new MTtextNode(this, text, addWhite, parsingMathEnvironment)); + if (parsingMathEnvironment) { + static QSet mathEnvironmentSpecialText; + if (mathEnvironmentSpecialText.size()==0) { + mathEnvironmentSpecialText<<"+"<<"-"<<"="<<"*"<<"<"<<">"<<"|"<<"/"; + } + if (mathEnvironmentSpecialText.contains(text.trimmed())) { + nl->addNode(new MTsymbolNode(this, text, addWhite)); + } else { + nl->addNode(new MTtextNode(this, text, addWhite, parsingMathEnvironment)); + } + } else { + nl->addNode(new MTtextNode(this, text, addWhite, parsingMathEnvironment)); + } } else if (currentToken==MTTinstruction) { QString name=currentTokenName; if (name=="\\") break; // break on linebrak character @@ -4262,7 +4331,7 @@ JKQTMathText::MTnode* JKQTMathText::parseLatexString(bool get, const QString& qu nl->addNode(new MTdecoratedNode(this, MTDdoubleunderline, parseLatexString(true))); } else if (name=="ooline"||name=="ool") { nl->addNode(new MTdecoratedNode(this, MTDdoubleoverline, parseLatexString(true))); - } else if (name=="arrow") { + } else if (name=="arrow"||name=="overrightarrow"||name=="overarrow") { nl->addNode(new MTdecoratedNode(this, MTDarrow, parseLatexString(true))); } else if (name=="hat") { nl->addNode(new MTdecoratedNode(this, MTDhat, parseLatexString(true)));