From afa14cbbfb645e9fedd43f7fe309907c8d3b7bae Mon Sep 17 00:00:00 2001 From: jkriege2 Date: Sun, 7 Aug 2022 17:57:52 +0200 Subject: [PATCH] NEW: additional method JKQTMathtext::getSizeDetail() that returns all size-properties as a convenient struct, also added matching varinat JKQTMathTextNode::getSize() NEW: additional method JKQTMathtext::drawIntoPixmap(), JKQTMathtext::drawIntoPicture(), JKQTMathtext::drawIntoImage() which returns a QPixmap, QPicture and QImage respectively that contains the render result of the currently parsed markup --- doc/dox/whatsnew.dox | 2 + lib/jkqtmathtext/jkqtmathtext.cpp | 131 ++++++++++++++++++++-- lib/jkqtmathtext/jkqtmathtext.h | 162 ++++++++++++++++++++------- lib/jkqtmathtext/jkqtmathtexttools.h | 2 +- 4 files changed, 245 insertions(+), 52 deletions(-) diff --git a/doc/dox/whatsnew.dox b/doc/dox/whatsnew.dox index d679e6ccbc..0cf39bc20c 100644 --- a/doc/dox/whatsnew.dox +++ b/doc/dox/whatsnew.dox @@ -80,6 +80,8 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
  • NEW: added support for \c \\char\"HEX , \c \\char\'OCTAL and \c \\charDECIMAL for inserting any uicode character code
  • NEW: added support for \\bigl,\\bigr,\\Bigr,... commands for fixed-size but enlarged paramtheses
  • NEW: added support for \\begin{verbatim}...\\end{verbatim}, \\begin{verbatim*}...\\end{verbatim*}
  • +
  • NEW: additional method JKQTMathtext::getSizeDetail() that returns all size-properties as a convenient struct, also added matching varinat JKQTMathTextNode::getSize()
  • +
  • NEW: additional method JKQTMathtext::drawIntoPixmap(), JKQTMathtext::drawIntoPicture(), JKQTMathtext::drawIntoImage() which returns a QPixmap, QPicture and QImage respectively that contains the render result of the currently parsed markup
  • diff --git a/lib/jkqtmathtext/jkqtmathtext.cpp b/lib/jkqtmathtext/jkqtmathtext.cpp index 0fba7928b2..9c59429b1e 100644 --- a/lib/jkqtmathtext/jkqtmathtext.cpp +++ b/lib/jkqtmathtext/jkqtmathtext.cpp @@ -979,6 +979,11 @@ QStringList JKQTMathText::getErrorList() const { return this->error_list; } +bool JKQTMathText::hadErrors() const +{ + return error_list.size()>0; +} + void JKQTMathText::addToErrorList(const QString &error) { error_list.append(error); @@ -2141,23 +2146,25 @@ double JKQTMathText::getAscent(QPainter& painter) { } void JKQTMathText::getSizeDetail(QPainter& painter, double& width, double& ascent, double& descent, double& strikeoutPos) { - width=0; - ascent=0; - descent=0; - strikeoutPos=0; + JKQTMathTextNodeSize s=getSizeDetail(painter); + width=s.width; + ascent=s.baselineHeight; + descent=s.getDescent(); + strikeoutPos=s.strikeoutPos; +} + +JKQTMathTextNodeSize JKQTMathText::getSizeDetail(QPainter &painter) +{ + JKQTMathTextNodeSize s; if (getNodeTree()!=nullptr) { JKQTMathTextEnvironment ev; ev.color=fontColor; ev.fontSize=fontSize; ev.fontSizeUnit=fontSizeUnits; - double overallHeight=0; - getNodeTree()->getSize(painter, ev, width, ascent, overallHeight, strikeoutPos); - descent=overallHeight-ascent; - ascent=ascent*1.1; - descent=qMax(ascent*0.1, descent*1.1); - strikeoutPos=strikeoutPos*1.1; + s=getNodeTree()->getSize(painter, ev); } + return s; } void JKQTMathText::draw(QPainter &painter, QPointF x, bool drawBoxes) @@ -2218,6 +2225,110 @@ void JKQTMathText::draw(QPainter& painter, unsigned int flags, QRectF rect, bool } } +QPixmap JKQTMathText::drawIntoPixmap(bool drawBoxes, QColor backgroundColor, int sizeincrease, qreal devicePixelRatio) +{ + // 1. generate dummy QPixmap that is needed to use a QPainter + // we need the dummy, because we first need to determine the size of the render output + // for which we need a QPainter. + QPixmap pix(1,1); + pix.setDevicePixelRatio(devicePixelRatio); + { + QPainter painter; + + // 2. now we determine the size and additional parameters, + // such as the ascent(or "baseline height") + painter.begin(&pix); + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::TextAntialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + const JKQTMathTextNodeSize size=getSizeDetail(painter); + const QSize pixsize=size.getIntSize()+QSize(2*sizeincrease,2*sizeincrease); + painter.end(); + + // 3. finally we can generate a QPixmap with the appropriate + // size to contain the full rendering. We fill it with the + // color white and finally paint the math markup/LaTeX string + pix=QPixmap(pixsize); + pix.setDevicePixelRatio(devicePixelRatio); + pix.fill(backgroundColor); + painter.begin(&pix); + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::TextAntialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + draw(painter, Qt::AlignVCenter|Qt::AlignHCenter, QRect(QPoint(0,0),pixsize), drawBoxes); + painter.end(); + } + return pix; +} + +QImage JKQTMathText::drawIntoImage(bool drawBoxes, QColor backgroundColor, int sizeincrease, qreal devicePixelRatio, unsigned int resolution_dpi) +{ + // 1. generate dummy QPixmap that is needed to use a QPainter + // we need the dummy, because we first need to determine the size of the render output + // for which we need a QPainter. + QImage img(1,1,QImage::Format_ARGB32); + img.setDevicePixelRatio(devicePixelRatio); + img.setDotsPerMeterX(resolution_dpi*(10000/254)); + img.setDotsPerMeterY(resolution_dpi*(10000/254)); + { + QPainter painter; + + // 2. now we determine the size and additional parameters, + // such as the ascent(or "baseline height") + painter.begin(&img); + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::TextAntialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + const JKQTMathTextNodeSize size=getSizeDetail(painter); + const QSize pixsize=size.getIntSize()+QSize(2*sizeincrease,2*sizeincrease); + painter.end(); + + // 3. finally we can generate a QPixmap with the appropriate + // size to contain the full rendering. We fill it with the + // color white and finally paint the math markup/LaTeX string + img=QImage(pixsize,QImage::Format_ARGB32); + img.setDevicePixelRatio(devicePixelRatio); + img.fill(backgroundColor); + painter.begin(&img); + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::TextAntialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + draw(painter, Qt::AlignVCenter|Qt::AlignHCenter, QRect(QPoint(0,0),pixsize), drawBoxes); + painter.end(); + } + return img; +} +QPicture JKQTMathText::drawIntoPicture(bool drawBoxes) +{ + // 1. generate dummy QPixmap that is needed to use a QPainter + // we need the dummy, because we first need to determine the size of the render output + // for which we need a QPainter. + QPicture pic; + { + QPainter painter; + + // 2. now we determine the size and additional parameters, + // such as the ascent(or "baseline height") + painter.begin(&pic); + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::TextAntialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + const JKQTMathTextNodeSize size=getSizeDetail(painter); + painter.end(); + + // 3. finally we can generate a QPixmap with the appropriate + // size to contain the full rendering. We fill it with the + // color white and finally paint the math markup/LaTeX string + painter.begin(&pic); + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::TextAntialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + draw(painter, 0, size.baselineHeight, drawBoxes); + painter.end(); + } + return pic; +} + JKQTMathTextNode *JKQTMathText::getNodeTree() const { if (useUnparsed) return unparsedNode; diff --git a/lib/jkqtmathtext/jkqtmathtext.h b/lib/jkqtmathtext/jkqtmathtext.h index 60bb5843ce..caa32e2f26 100644 --- a/lib/jkqtmathtext/jkqtmathtext.h +++ b/lib/jkqtmathtext/jkqtmathtext.h @@ -54,8 +54,55 @@ class JKQTMathTextNode; // forward \see See \ref jkqtmathtext_supportedlatex for a description of the supported LaTeX subset and \ref jkqtmathtext_renderingmodel for a description of the rendering model. + \section JKQTMathTextUsage Usage - \subsection JKQTMathTextUsageDirect Direct Usage + + \subsection JKQTMathTextUsageDirect Drawing Functions + The class provides different variants of the drawing function draw() that paints using an + externlly provided QPainter. These variants + either paint into a QRect, or starting from a single location (x,y). + + The QRect-variants can align the render result inside the rectangle, whereas the + location-variants draw from a position that is on the left-hand side of the output's baseline: + + \image html jkqtmathtext/jkqtmathtext_node_geo.png + + + + \subsection JKQTMathTextUsageConvenience Convenience Functions + + Alternatively you can use these methods to directly generate a QPixmap or QPicture: + - drawIntoPixmap() + - drawIntoImage() + - drawIntoPicture() + . + + + \subsection JKQTMathTextSizing Determining the size of an equation + + In addition there are also functions that allow to calculate the size of the equation, + before drawing it (just like the functions in QFontMetrics + or QFontMetricsF): + - getSizeDetail() + - getSize() + - getAscent(), getDescent() + . + + + \subsection JKQTMathTextUsageQLabel Usage within a QLabel class JKQTMathTextLabel + + Finally, there is also a QLabel-derived class JKQTMathTextLabel which can be used for drawing a LaTeX string onto a Qt form. + + \see JKQTMathTextLabel + + + \subsection JKQTMathTextErrorHandling Error Handling + + The class is designed to be as robust as possible and will still return some output, even if the equation contains some errors. + Nevertheless, several errors are detected while parsing. You can get a list of error messages using getErrorList() after calling parse(). + Also parse() will return \c false if an error occured while parsing. + + \subsection JKQTMathTextUsageExample Example Code This small piece of C++ code may serve as an example of the usage and capabilities of the class: \code // create a JKQTMathText object. @@ -68,46 +115,44 @@ class JKQTMathTextNode; // forward // parse some LaTeX code (the Schroedinger's equation) mathText.parse("$\\left[-\\frac{\\hbar^2}{2m}\\frac{\\partial^2}{\\partial x^2}+V(x)\\right]\\Psi(x)=\\mathrm{i}\\hbar\\frac{\\partial}{\\partial t}\\Psi(x)$"); + // draw the result into a QPixmap + QPixmap result=mathText.drawIntoPixmap(); + \endcode + + Alternatively you can also use this class with a QPainter: + + \code // use the draw() methods to draw the equation using a QPainter (here onto a QPixmap) QPainter painter; - QPixmap pix(600,400); - painter.begin(&pix); + + // first we determine the size of the render output: + const JKQTMathTextNodeSize size=getSizeDetail(painter); + + // now we can us that size information to render: mathText.draw(painter, Qt::AlignCenter, QRectF(0,0,pix.width(), pix.height()), false); - painter.end(); \endcode - - \subsection JKQTMathTextSizing Determining the size of an equation - - In addition there are also functions that allow to calculate the size of the equation, before drawing it (just like the functions in QFontMetrics and QFontMetricsF): - - getSizeDetail() - - getSize() - - getAscent(), getDescent() - . - - \subsection JKQTMathTextErrorHandling Error Handling - - The class is designed to be as robust as possible and will still return some output, even if the equation contains some errors. - Nevertheless, several errors are detected while parsing. You can get a list of error messages using getErrorList() after calling parse(). - Also parse() will return \c false if an error occured while parsing. + + + + + \section JKQTMathTextToHTML Convert to HTML + + The method toHtml() may be used to get a HTML representation of the LaTeX string, if possible (only for simple LaTeX equations!). Whether + the transformation was possible is returned as a call by value argument! - \subsection JKQTMathTextUsageQLabel Usage within a QLabel class JKQTMathTextLabel - - Finally, there is also a QLabel-derived class JKQTMathTextLabel which can be used for drawing a LaTeX string onto a Qt form. - - \see JKQTMathTextLabel - - - \section JKQTMathTextExamples Examples + \section JKQTMathTextExamples Example Projects Examples for the usage of this class can be found here: - \ref JKQTMathTextSimpleExample + - \ref JKQTMathTextRenderCmdLineTool - \ref JKQTMathTextTestApp . - \section JKQTMathTextSuppoertedFonts Font Handling - + \section JKQTMathTextInternalDetails Implementation Details + \subsection JKQTMathTextSuppoertedFonts Font Handling + Several fonts are defined as properties to the class: - A "roman" (MTEroman / MTEmathRoman) font used as the standard font ( setFontRoman() and for use in math mode setFontMathRoman() ) - A "sans-serif" (MTEsans / MTEmathSans) font which may be activated with \c \\sf ... ( setFontSans() and for use in math mode setFontMathSans() ) @@ -142,13 +187,7 @@ class JKQTMathTextNode; // forward - if the character is not found, it is looked for in the fallback fonts MTEFallbackSymbols - as a last resort, some symbols can be created otherwise, so if neither of the two options above contain the required symbol, the symbol might be synthesized otherwise, or a rectangle with the size of "X" is drawn instead - - - \section JKQTMathTextToHTML Convert to HTML - - The method toHtml() may be used to get a HTML representation of the LaTeX string, if possible (only for simple LaTeX equations!). Whether - the transformation was possible is returned as a call by value argument! - + . */ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { @@ -176,6 +215,8 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { double getAscent(QPainter& painter); /** \brief return the detailes sizes of the text */ void getSizeDetail(QPainter& painter, double& width, double& ascent, double& descent, double& strikeoutPos); + /** \brief return the detailes sizes of the text */ + JKQTMathTextNodeSize getSizeDetail(QPainter& painter); /** \brief draw a representation to the object at the specified position \a x , \a y * * \param painter the QPainter to use for drawing @@ -202,13 +243,41 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { * \param painter the QPainter to use for drawing * \param rect rectangle to draw the text/expression into (see sketch below) * \param flags alignment within \a rect (see below), use e.g. Qt::AlignHCenter | Qt::AlignVCenter to center the expression inside \a rect + * The flags (dark-red is the rectangle \a rect) are interpreted in the following way: + * \image html jkqtmathtext/jkqtmathtext_draw_flags.png * \param drawBoxes if \c true boxes defining the size of each node are drawn, example output: \image html jkqtmathtext/jkqtmathtext_drawboxes.png * - * These options are interpreted for \a flags (dark-red is the rectangle \a rect): - * \image html jkqtmathtext/jkqtmathtext_draw_flags.png + * + * + * \see drawIntoPixmap(), drawIntoPicture(), getSize(), getSizeDetail() */ void draw(QPainter& painter, unsigned int flags, QRectF rect, bool drawBoxes=false); + /** \brief render the last parse result into a QPixmap + * + * \param drawBoxes if \c true boxes defining the size of each node are drawn, example output: \image html jkqtmathtext/jkqtmathtext_drawboxes.png + * \param backgroundColor fill color for the returnes QPixmap + * \param sizeincrease margin around the tight size of the rendering result for the returned QPixmap + * \param devicePixelRatio the devicePixelRatio of the returned QPixmap + */ + QPixmap drawIntoPixmap(bool drawBoxes=false, QColor backgroundColor=QColor(Qt::white), int sizeincrease=0, qreal devicePixelRatio=1.0); + + /** \brief render the last parse result into a QImage + * + * \param drawBoxes if \c true boxes defining the size of each node are drawn, example output: \image html jkqtmathtext/jkqtmathtext_drawboxes.png + * \param backgroundColor fill color for the returnes QPixmap + * \param sizeincrease margin around the tight size of the rendering result for the returned QPixmap + * \param devicePixelRatio the devicePixelRatio of the returned QImage + * \param resolution_dpi resolution in dots/inch + */ + QImage drawIntoImage(bool drawBoxes=false, QColor backgroundColor=QColor(Qt::white), int sizeincrease=0, qreal devicePixelRatio=1.0, unsigned int resolution_dpi=96); + + /** \brief render the last parse result into a QPicture + * + * \param drawBoxes if \c true boxes defining the size of each node are drawn, example output: \image html jkqtmathtext/jkqtmathtext_drawboxes.png + */ + QPicture drawIntoPicture(bool drawBoxes=false); + /** \brief convert LaTeX to HTML. returns \c ok=true on success and \c ok=false else. */ QString toHtml(bool* ok=nullptr, double fontPointSize=10); @@ -533,6 +602,8 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { bool isUsingUnparsed() const; /** \copydoc error_list */ QStringList getErrorList() const; + /** \brief returns \c true when errors were registered in the system \see error_list */ + bool hadErrors() const; /** \copydoc error_list */ void addToErrorList(const QString& error); @@ -697,14 +768,23 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { double sqrt_width_Xfactor; /** \brief height-increase of the sqrt-symbol, as factor of the child's height */ double sqrt_height_factor; - /** \brief a list that will be filled with error messages while parsing, if any error occur */ + /** \brief a list that will be filled with error messages while parsing, if any error occur + * + * This list of errors is (mostly) filled during a call to parse(). During rendering (e.g. with draw() ) + * only very few errors will be detected, as most errors are caused by wrong markup. + * + * A call to parse() also clears this list. + * + * \see getErrorList(), hadErrors() and addToErrorList() + * + */ QStringList error_list; /** \brief the result of parsing the last string supplied to the object via parse() */ JKQTMathTextNode* parsedNode; /** \brief a tree containing the unparsed text as a single node */ JKQTMathTextNode* unparsedNode; - /** \brief if true, the unparsedNode is drawn */ + /** \brief if true, the unparsedNode is drawn \see unparsedNode */ bool useUnparsed; /** \brief returns the syntax tree of JKQTMathTextNode's that was created by the last parse call */ @@ -717,7 +797,7 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { MTTinstruction, /*!< \brief an instruction, started by \c "\", e.g. \c "\\textbf", ... */ MTTinstructionNewline, /*!< \brief a newline instruction \c "\\" */ MTTinstructionVerbatim, /*!< \brief a verbatim instruction, e.g. \c \\verb!verbatimtext! was found: currentTokenName will contain the text enclode by the verbatim delimiters */ - MTTinstructionVerbatimVisibleSpace, /*!< \brief a verbatim instruction that generates visible whitespaces, e.g. \c \\begin{verbatim}...\end{verbatim} was found: currentTokenName will contain the text enclode by the verbatim delimiters */ + MTTinstructionVerbatimVisibleSpace, /*!< \brief a verbatim instruction that generates visible whitespaces, e.g. \c \\begin{verbatim}...\\end{verbatim} was found: currentTokenName will contain the text enclode by the verbatim delimiters */ MTTinstructionBegin, /*!< \brief a \c '\\begin{...}' instruction, currentTokenName is the name of the environment */ MTTinstructionEnd, /*!< \brief a \c '\\end{...}' instruction, currentTokenName is the name of the environment */ MTTunderscore, /*!< \brief the character \c "_" */ @@ -734,7 +814,7 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { MTTemdash, /*!< \brief the em-dash character sequence \c "---" in text-mode */ }; - /** \biref convert a tokenType into a string, e.g. for debugging output */ + /** \brief convert a tokenType into a string, e.g. for debugging output */ static QString tokenType2String(tokenType type); /** \brief tokenizer for the LaTeX parser */ diff --git a/lib/jkqtmathtext/jkqtmathtexttools.h b/lib/jkqtmathtext/jkqtmathtexttools.h index 754e9a1803..f2eac41ee6 100644 --- a/lib/jkqtmathtext/jkqtmathtexttools.h +++ b/lib/jkqtmathtext/jkqtmathtexttools.h @@ -315,7 +315,7 @@ struct JKQTMATHTEXT_LIB_EXPORT JKQTMathTextNodeSize { /** \brief calculate the overall size in floating-point precision */ inline QSizeF getSize() const { return QSizeF(width, overallHeight); } /** \brief calculate the overall size in floating-point precision */ - inline QSize getIntSize() const { return QSize(qCeil(width), qCeil(overallHeight)); } + inline QSize getIntSize() const { return QSize(qCeil(width+1.0), qCeil(overallHeight+1.0)); } }; /** \brief summarizes all information available on a font for a specific MTenvironmentFont