From fc557c9affa45749494a473c5bd569e641112e5e Mon Sep 17 00:00:00 2001 From: jkriege2 Date: Mon, 27 Jun 2022 22:57:49 +0200 Subject: [PATCH] IMPROVEMENT: JKQTMathText: added x-correction for sub/superscript above/below/besides integrals --- doc/dox/whatsnew.dox | 1 + lib/jkqtmathtext/jkqtmathtext.cpp | 26 ++++ lib/jkqtmathtext/jkqtmathtext.h | 25 ++++ .../nodes/jkqtmathtextlistnode.cpp | 112 ++++++++++++++---- .../nodes/jkqtmathtextsymbolnode.cpp | 26 +++- .../nodes/jkqtmathtextsymbolnode.h | 16 +++ 6 files changed, 182 insertions(+), 24 deletions(-) diff --git a/doc/dox/whatsnew.dox b/doc/dox/whatsnew.dox index 22a2446428..058713ff6d 100644 --- a/doc/dox/whatsnew.dox +++ b/doc/dox/whatsnew.dox @@ -37,6 +37,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
  • IMPROVED: typesetting of sub-/supercripts, especially for large math operators and braces
  • MODIFIED: brace node now calculates the extension of the child height above or below the strikeoutPos, in order to center braces around the strikeoutPos
  • IMPROVED: improved drawing of parantheses, square brackets ... , underbrace/overbrace
  • +
  • IMPROVED: added x-correction for sub/superscript above/below/besides integrals
  • remove/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!
  • diff --git a/lib/jkqtmathtext/jkqtmathtext.cpp b/lib/jkqtmathtext/jkqtmathtext.cpp index 9f44662b3f..fa2cae730b 100644 --- a/lib/jkqtmathtext/jkqtmathtext.cpp +++ b/lib/jkqtmathtext/jkqtmathtext.cpp @@ -87,6 +87,8 @@ JKQTMathText::JKQTMathText(QObject* parent): operatorsubsuper_size_factor=0.65; operatorsubsuper_distance_factor=0.25; operatorsubsuper_extraspace_factor=0.5; + intsubsuper_xcorrection_factor=0.25; + intsubbesides_xcorrection_xfactor=0.33; mathoperator_width_factor=1.5; bigmathoperator_font_factor=1.8; @@ -230,6 +232,8 @@ void JKQTMathText::loadSettings(const QSettings& settings, const QString& group) operatorsubsuper_distance_factor=settings.value(group+"operatorsubsuper_distance_factor", operatorsubsuper_distance_factor).toDouble(); operatorsubsuper_extraspace_factor=settings.value(group+"operatorsubsuper_extraspace_factor", operatorsubsuper_extraspace_factor).toDouble(); mathoperator_width_factor=settings.value(group+"mathoperator_width_factor", mathoperator_width_factor).toDouble(); + intsubsuper_xcorrection_factor=settings.value(group+"intsubsuper_xcorrection_factor", intsubsuper_xcorrection_factor).toDouble(); + intsubbesides_xcorrection_xfactor=settings.value(group+"intsubbesides_xcorrection_xfactor", intsubbesides_xcorrection_xfactor).toDouble(); if (settings.value(group+"use_stix_fonts", false).toBool()) useSTIX(); @@ -260,6 +264,8 @@ void JKQTMathText::saveSettings(QSettings& settings, const QString& group) const settings.setValue(group+ "operatorsubsuper_distance_factor", operatorsubsuper_distance_factor); settings.setValue(group+ "operatorsubsuper_extraspace_factor", operatorsubsuper_extraspace_factor); settings.setValue(group+ "mathoperator_width_factor", mathoperator_width_factor); + settings.setValue(group+ "intsubsuper_xcorrection_factor", intsubsuper_xcorrection_factor); + settings.setValue(group+ "intsubbesides_xcorrection_xfactor", intsubbesides_xcorrection_xfactor); settings.setValue(group+ "brace_y_shift_factor", brace_y_shift_factor); settings.setValue(group+ "decoration_height_factor", decoration_height_factor); settings.setValue(group+ "decoration_width_reduction_xfactor", decoration_width_reduction_Xfactor); @@ -722,6 +728,26 @@ double JKQTMathText::getMathoperatorWidthFactor() const return this->mathoperator_width_factor; } +void JKQTMathText::setIntSubSuperXCorrectionFactor(double __value) +{ + intsubsuper_xcorrection_factor=__value; +} + +double JKQTMathText::getIntSubSuperXCorrectionFactor() const +{ + return intsubsuper_xcorrection_factor; +} + +void JKQTMathText::setIntSubBesidesXCorrectionXFactor(double __value) +{ + intsubbesides_xcorrection_xfactor=__value; +} + +double JKQTMathText::getIntSubBesidesXCorrectionXFactor() const +{ + return intsubbesides_xcorrection_xfactor; +} + void JKQTMathText::setBigMathoperatorFontFactor(double __value) { bigmathoperator_font_factor=__value; diff --git a/lib/jkqtmathtext/jkqtmathtext.h b/lib/jkqtmathtext/jkqtmathtext.h index c5a878b9d6..9d43a31d39 100644 --- a/lib/jkqtmathtext/jkqtmathtext.h +++ b/lib/jkqtmathtext/jkqtmathtext.h @@ -458,6 +458,14 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { void setMathoperatorWidthFactor(double __value); /** \copydoc mathoperator_width_factor */ double getMathoperatorWidthFactor() const; + /** \copydoc intsubsuper_xcorrection_factor */ + void setIntSubSuperXCorrectionFactor(double __value); + /** \copydoc intsubsuper_xcorrection_factor */ + double getIntSubSuperXCorrectionFactor() const; + /** \copydoc intsubbesides_xcorrection_xfactor */ + void setIntSubBesidesXCorrectionXFactor(double __value); + /** \copydoc intsubbesides_xcorrection_xfactor */ + double getIntSubBesidesXCorrectionXFactor() const; /** \copydoc bigmathoperator_font_factor */ void setBigMathoperatorFontFactor(double __value); /** \copydoc bigmathoperator_font_factor */ @@ -592,6 +600,23 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { * \image html jkqtmathtext_subsuper_with_limits.png */ double operatorsubsuper_extraspace_factor; + /** \brief for integrals (\c \\int , \c \\oint , ...) the sub-/superscripts above/below the symbol have to be shifted a bit to the left/right to accomodate the shape of the operator symbol (i.e. some free space at the top-left and bottom-right) + * + * This factor is multiplied by the symbol width: xshift=intsubsuper_xcorrection_factor*symbolWidth + * Then the subscript below is placed at centerx(symbol)-xshift and the superscript at centerx(symbol)+shiftx. + * This is also used to correct a subset next to the symbol by shifting it to rightx(symbol)-xshift. + * + * This correction is applied to \\int, \\iint, \\iiint, \\oint, ... + */ + double intsubsuper_xcorrection_factor; + /** \brief for integrals (\c \\int , \c \\oint , ...) the subscripts besides the symbol have to be shifted to the left a bit to the left to accomodate the shape of the operator symbol (i.e. some free space at the bottom-right) + * + * This factor is multiplied by the width of an x: xshift=intsubbesides_xcorrection_xfactor*xWidth + * Then the subscript besides the symbol is shifted by xshift to the left + * + * This correction is applied to \\int, \\iint, \\iiint, \\oint, ... + */ + double intsubbesides_xcorrection_xfactor; /** \brief factor, used to extend the size of an operator in math mode * * The next image demonstrates the effect of this property, which adds extra space diff --git a/lib/jkqtmathtext/nodes/jkqtmathtextlistnode.cpp b/lib/jkqtmathtext/nodes/jkqtmathtextlistnode.cpp index 4fefc17369..bd0b552534 100644 --- a/lib/jkqtmathtext/nodes/jkqtmathtextlistnode.cpp +++ b/lib/jkqtmathtext/nodes/jkqtmathtextlistnode.cpp @@ -72,9 +72,13 @@ void JKQTMathTextListNode::getSizeInternal(QPainter& painter, JKQTMathTextEnviro JKQTMathTextNodeSize prevNodeSize; JKQTMathTextNodeSize* prevNodeSizePtrForSubscript=nullptr; JKQTMathTextNodeSize* prevNodeSizePtrForSuperscript=nullptr; + double subSuperXCorrection=0; + double subBesidesXCorrection=0; if (i>0) { - nodes[i-1]->getSize(painter, currentEv, prevNodeSize.width, prevNodeSize.baselineHeight, prevNodeSize.overallHeight, prevNodeSize.strikeoutPos); + JKQTMathTextSymbolNode* symbN=dynamic_cast(nodes[i-1]); + if (symbN) symbN->getSymbolSize(painter, currentEv, prevNodeSize.width, prevNodeSize.baselineHeight, prevNodeSize.overallHeight, prevNodeSize.strikeoutPos, subSuperXCorrection, subBesidesXCorrection); + else nodes[i-1]->getSize(painter, currentEv, prevNodeSize.width, prevNodeSize.baselineHeight, prevNodeSize.overallHeight, prevNodeSize.strikeoutPos); const double prevAscent=prevNodeSize.baselineHeight; const double prevDescent=prevNodeSize.overallHeight-prevNodeSize.baselineHeight; const bool shouldUseSpecialSubscriptMode=prevAscent>=subsuperSpecialModeAscent; @@ -117,7 +121,7 @@ void JKQTMathTextListNode::getSizeInternal(QPainter& painter, JKQTMathTextEnviro overallHeight=baselineHeight+oh-bh; strikeoutPos=sp; } - xnew+=qMax(w1+spaceWidth, w2); + xnew+=qMax(w1+spaceWidth, w2+subBesidesXCorrection); doDraw=false; //qDebug()<<"### super+sub"; @@ -150,13 +154,28 @@ void JKQTMathTextListNode::getSizeInternal(QPainter& painter, JKQTMathTextEnviro overallHeight=baselineHeight+oh-bh; strikeoutPos=sp; } - xnew+=qMax(w1, w2+spaceWidth); + xnew+=qMax(w1+subBesidesXCorrection, w2+spaceWidth); doDraw=false; //qDebug()<<"### sub+super"; //qDebug()<<"### subsupop: sub+super1 overallHeight="<getOperatorsubsuperSizeFactor(); double w1=0, wsub=0, wsup=0; double oh1=0, ohsub=0, ohsup=0; @@ -187,18 +207,18 @@ void JKQTMathTextListNode::getSizeInternal(QPainter& painter, JKQTMathTextEnviro //qDebug()<<"sub_super: node: "<getTypeName()<<" w2="<getTypeName()<<" w3="<draw(painter, xnew, ynew, currentEv, prevNodeSizePtrForSubscript); + double xnew2=nodes[i]->draw(painter, xnew-subSuperXCorrection, ynew, currentEv, prevNodeSizePtrForSubscript); //i++; xnew=qMax(xnew1, xnew2); doDraw=false; @@ -341,7 +387,7 @@ double JKQTMathTextListNode::draw(QPainter& painter, double x, double y, JKQTMat if (dynamic_cast(nodes[i+1])) { // is this subscript? //painter.setPen(QPen("red")); //painter.drawEllipse(xnew-4,ynew+shift-(ccOverallHeight-ccBaselineHeight)-4,8,8); - double xnew1=nodes[i]->draw(painter, xnew, ynew, currentEv, prevNodeSizePtrForSubscript); + double xnew1=nodes[i]->draw(painter, xnew-subSuperXCorrection, ynew, currentEv, prevNodeSizePtrForSubscript); i++; //painter.setPen(QPen("magenta")); //painter.drawEllipse(xnew-4,ynew-4,8,8); @@ -350,6 +396,9 @@ double JKQTMathTextListNode::draw(QPainter& painter, double x, double y, JKQTMat xnew=qMax(xnew1, xnew2); doDraw=false; } + } else { + xnew=nodes[i]->draw(painter, xnew-subBesidesXCorrection, ynew, currentEv, prevNodeSizePtrForSubscript); + doDraw=false; } } else { @@ -375,7 +424,14 @@ double JKQTMathTextListNode::draw(QPainter& painter, double x, double y, JKQTMat double w1=0, wsub=0, wsup=0; double oh1=0, ohsub=0, ohsup=0; double bh1=0, bhsub=0, bhsup=0, spsub, spsup, sp; - nodes[i]->getSize(painter, currentEv, w1, bh1, oh1, sp); + JKQTMathTextSymbolNode* symbN=dynamic_cast(nodes[i]); + if (symbN) { + symbN->getSymbolSize(painter, currentEv, w1, bh1, oh1, sp, subSuperXCorrection, subBesidesXCorrection); + } else { + nodes[i]->getSize(painter, currentEv, w1, bh1, oh1, sp); + } + + subn->getChild()->getSize(painter, ev, wsub, bhsub, ohsub, spsub); supn->getChild()->getSize(painter, ev, wsup, bhsup, ohsup, spsup); const double descent1=oh1-bh1; @@ -387,10 +443,10 @@ double JKQTMathTextListNode::draw(QPainter& painter, double x, double y, JKQTMat const double xn1=nodes[i]->draw(painter, xnew+(neww-w1)/2.0, ynew, currentEv); i++; //double xnew2= - const double xnsub=subn->getChild()->draw(painter, xnew+(neww-wsub)/2.0, ynew+bhsub+descent1+subsupershift, ev); + const double xnsub=subn->getChild()->draw(painter, xnew+(neww-wsub)/2.0-subSuperXCorrection, ynew+bhsub+descent1+subsupershift, ev); i++; //double xnew3= - const double xnsup=supn->getChild()->draw(painter, xnew+(neww-wsup)/2.0, ynew-bh1-descent3-subsupershift, ev); + const double xnsup=supn->getChild()->draw(painter, xnew+(neww-wsup)/2.0+subSuperXCorrection, ynew-bh1-descent3-subsupershift, ev); doDraw=false; xnew=qMax(qMax(xn1, xnsub), xnsup)+subsuperextrawidth/2.0; } else if (subn) { // is this subscript and no following superscript? @@ -399,7 +455,12 @@ double JKQTMathTextListNode::draw(QPainter& painter, double x, double y, JKQTMat double w1=0, wsub=0; double oh1=0, ohsub=0; double bh1=0, bhsub=0, sp=0, spsub=0; - nodes[i]->getSize(painter, currentEv, w1, bh1, oh1, sp); + JKQTMathTextSymbolNode* symbN=dynamic_cast(nodes[i]); + if (symbN) { + symbN->getSymbolSize(painter, currentEv, w1, bh1, oh1, sp, subSuperXCorrection, subBesidesXCorrection); + } else { + nodes[i]->getSize(painter, currentEv, w1, bh1, oh1, sp); + } subn->getChild()->getSize(painter, ev, wsub, bhsub, ohsub, spsub); const double descent1=oh1-bh1; //double d2=oh2-bh2; @@ -409,7 +470,7 @@ double JKQTMathTextListNode::draw(QPainter& painter, double x, double y, JKQTMat const double xn1=nodes[i]->draw(painter, xnew+(neww-w1)/2.0, ynew, currentEv); i++; //double xnew2= - const double xnsub=subn->getChild()->draw(painter, xnew+(neww-wsub)/2.0, ynew+bhsub+descent1+subsupershift, ev)+subsupershift; + const double xnsub=subn->getChild()->draw(painter, xnew+(neww-wsub)/2.0-subSuperXCorrection, ynew+bhsub+descent1+subsupershift, ev)+subsupershift; doDraw=false; //xnew+=w; xnew=qMax(xnsub, xn1)+subsuperextrawidth/2.0; @@ -419,7 +480,12 @@ double JKQTMathTextListNode::draw(QPainter& painter, double x, double y, JKQTMat double w1=0, wsup=0; double oh1=0, ohsup=0; double bh1=0, bhsup=0, sp, spsup; - nodes[i]->getSize(painter, currentEv, w1, bh1, oh1, sp); + JKQTMathTextSymbolNode* symbN=dynamic_cast(nodes[i]); + if (symbN) { + symbN->getSymbolSize(painter, currentEv, w1, bh1, oh1, sp, subSuperXCorrection, subBesidesXCorrection); + } else { + nodes[i]->getSize(painter, currentEv, w1, bh1, oh1, sp); + } supn->getChild()->getSize(painter, ev, wsup, bhsup, ohsup, spsup); //double d1=oh1-bh1; //double d2=oh2-bh2; @@ -430,7 +496,7 @@ double JKQTMathTextListNode::draw(QPainter& painter, double x, double y, JKQTMat const double xn1=nodes[i]->draw(painter, xnew+(neww-w1)/2.0, ynew, currentEv); i++; //double xnew3= - const double xnsup=supn->getChild()->draw(painter, xnew+(neww-wsup)/2.0, ynew-bh1-descent3-subsupershift, ev); + const double xnsup=supn->getChild()->draw(painter, xnew+(neww-wsup)/2.0+subSuperXCorrection, ynew-bh1-descent3-subsupershift, ev); doDraw=false; xnew=qMax(xn1, xnsup)+subsuperextrawidth/2.0; } @@ -439,7 +505,7 @@ double JKQTMathTextListNode::draw(QPainter& painter, double x, double y, JKQTMat if (idraw(painter, xnew, ynew, currentEv, prevNodeSizePtrForSuperscript); - else if (nodeI_SubScript) xnew=nodes[i]->draw(painter, xnew, ynew, currentEv, prevNodeSizePtrForSubscript); + else if (nodeI_SubScript) xnew=nodes[i]->draw(painter, xnew-subBesidesXCorrection, ynew, currentEv, prevNodeSizePtrForSubscript); else xnew=nodes[i]->draw(painter, xnew, ynew, currentEv, nullptr); } } diff --git a/lib/jkqtmathtext/nodes/jkqtmathtextsymbolnode.cpp b/lib/jkqtmathtext/nodes/jkqtmathtextsymbolnode.cpp index ef5534fb56..410ca67458 100644 --- a/lib/jkqtmathtext/nodes/jkqtmathtextsymbolnode.cpp +++ b/lib/jkqtmathtext/nodes/jkqtmathtextsymbolnode.cpp @@ -891,7 +891,13 @@ JKQTMathTextSymbolNode::SymbolProps JKQTMathTextSymbolNode::getSymbolProp(const } -void JKQTMathTextSymbolNode::getSizeInternal(QPainter& painter, JKQTMathTextEnvironment currentEv, double& width, double& baselineHeight, double& overallHeight, double& strikeoutPos, const JKQTMathTextNodeSize* /*prevNodeSize*/) { +void JKQTMathTextSymbolNode::getSizeInternal(QPainter& painter, JKQTMathTextEnvironment currentEv, double& width, double& baselineHeight, double& overallHeight, double& strikeoutPos, const JKQTMathTextNodeSize* prevNodeSize) { + double dummy1, dummy2; + getSymbolSizeInternal(painter, currentEv, width, baselineHeight, overallHeight, strikeoutPos, dummy1, dummy2, prevNodeSize); +} + +void JKQTMathTextSymbolNode::getSymbolSizeInternal(QPainter &painter, JKQTMathTextEnvironment currentEv, double &width, double &baselineHeight, double &overallHeight, double &strikeoutPos, double &subSuperXCorrection, double &subBesidesXCorrection, const JKQTMathTextNodeSize *prevNodeSize) +{ QFont f=currentEv.getFont(parentMathText); auto props=getSymbolProp(symbolName, currentEv); f.setFamily(props.font); @@ -942,6 +948,11 @@ void JKQTMathTextSymbolNode::getSizeInternal(QPainter& painter, JKQTMathTextEnvi if (props.extendWidthInMathmode && currentEv.insideMath) width=width*parentMathText->getMathoperatorWidthFactor(); + static QSet intCorrectionSymbolNames=QSet()<<"int"<<"iint"<<"iiint"<<"oint"<<"oiint"<<"oiiint"; + if (intCorrectionSymbolNames.contains(symbolName)) { + subSuperXCorrection=parentMathText->getIntSubSuperXCorrectionFactor()*tbr.width(); + subBesidesXCorrection=parentMathText->getIntSubBesidesXCorrectionXFactor()*JKQTMathTextGetTightBoundingRect(f, "X", painter.device()).width(); + } } double JKQTMathTextSymbolNode::draw(QPainter& painter, double x, double y, JKQTMathTextEnvironment currentEv, const JKQTMathTextNodeSize* /*prevNodeSize*/) { @@ -1316,3 +1327,16 @@ bool JKQTMathTextSymbolNode::getAddWhitespace() const return addWhitespace; } +void JKQTMathTextSymbolNode::getSymbolSize(QPainter &painter, JKQTMathTextEnvironment currentEv, double &width, double &baselineHeight, double &overallHeight, double &strikeoutPos, double &subSuperXCorrection, double &subBesidesXCorrection, const JKQTMathTextNodeSize *prevNodeSize) +{ + double w=width, b=baselineHeight, o=overallHeight, s=strikeoutPos; + getSymbolSizeInternal(painter, currentEv, w, b, o, s, subSuperXCorrection, subBesidesXCorrection, prevNodeSize); + + if (w<1e5) width=w; + if (b<1e5) baselineHeight=b; + if (o<1e5) overallHeight=o; + if (s<1e5) strikeoutPos=s; + +} + + diff --git a/lib/jkqtmathtext/nodes/jkqtmathtextsymbolnode.h b/lib/jkqtmathtext/nodes/jkqtmathtextsymbolnode.h index c5fbdf085a..c564a7dcfa 100644 --- a/lib/jkqtmathtext/nodes/jkqtmathtextsymbolnode.h +++ b/lib/jkqtmathtext/nodes/jkqtmathtextsymbolnode.h @@ -51,9 +51,25 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathTextSymbolNode: public JKQTMathTextNode { QString getSymbolfontName() const; /** \copydoc addWhitespace */ bool getAddWhitespace() const; + /** \brief determine the size of the node, calls getSizeInternal() implementation of the actual type \see getSizeInternal() + * + * \param painter painter to use for determining the size + * \param currentEv current environment object + * \param[out] width width of the block/node + * \param[out] baselineHeight distance from the bottom of the block/node-box to the baseline + * \param[out] overallHeight overall height (bottom to top) of the node, the ascent is \c overallHeight-baselineHeight + * \param[out] strikeoutPos position of the strikeout-line + * \param[out] subSuperXCorrection x-correction as described for JKQTMathParser::intsubsuper_xcorrection_factor for placing sub-/superscript below/above the symbol + * \param[out] subBesidesXCorrection x-correction as described for JKQTMathParser::intsubbesides_xcorrection_xfactor for placing sub-/superscript below/above the symbol + * \param[in] prevNodeSize optional parameter, describing the size of the previous node (on the left). This may be used for layout of some nodes (e.g. sub/super to move correctly next to large parantheses ...) + * + */ + void getSymbolSize(QPainter& painter, JKQTMathTextEnvironment currentEv, double& width, double& baselineHeight, double& overallHeight, double& strikeoutPos, double& subSuperXCorrection, double& subBesidesXCorrection, const JKQTMathTextNodeSize* prevNodeSize=nullptr); protected: /** \copydoc JKQTMathTextNode::getSizeInternal() */ virtual void getSizeInternal(QPainter& painter, JKQTMathTextEnvironment currentEv, double& width, double& baselineHeight, double& overallHeight, double& strikeoutPos, const JKQTMathTextNodeSize* prevNodeSize=nullptr) override; + /** \copydoc JKQTMathTextNode::getSizeInternal() */ + virtual void getSymbolSizeInternal(QPainter& painter, JKQTMathTextEnvironment currentEv, double& width, double& baselineHeight, double& overallHeight, double& strikeoutPos, double& subSuperXCorrection, double& subBesidesXCorrection, const JKQTMathTextNodeSize* prevNodeSize=nullptr) ; /** \brief this string will be sent to the drawText method with properly set fonts */ QString symbolName;