/*
    Copyright (c) 2008-2024 Jan W. Krieger (<jan@jkrieger.de>)

    

    This software is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License (LGPL) as published by
    the Free Software Foundation, either version 2.1 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License (LGPL) for more details.

    You should have received a copy of the GNU Lesser General Public License (LGPL)
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/



#include "jkqtmathtext/jkqtmathtext.h"
#include "jkqtmathtext/nodes/jkqtmathtextnode.h"
#include "jkqtcommon/jkqtpcodestructuring.h"
#include "jkqtcommon/jkqtpstringtools.h"
#include "jkqtcommon/jkqtpdebuggingtools.h"
#include "jkqtmathtext/nodes/jkqtmathtextnodetools.h"
#include "jkqtmathtext/nodes/jkqtmathtexttextnode.h"
#include "jkqtmathtext/nodes/jkqtmathtextbracenode.h"
#include "jkqtmathtext/nodes/jkqtmathtextdecoratednode.h"
#include "jkqtmathtext/nodes/jkqtmathtextfracnode.h"
#include "jkqtmathtext/nodes/jkqtmathtextinstructionnode.h"
#include "jkqtmathtext/nodes/jkqtmathtextlistnode.h"
#include "jkqtmathtext/nodes/jkqtmathtextmatrixnode.h"
#include "jkqtmathtext/nodes/jkqtmathtextsqrtnode.h"
#include "jkqtmathtext/nodes/jkqtmathtextsubsupernode.h"
#include "jkqtmathtext/nodes/jkqtmathtextsymbolnode.h"
#include "jkqtmathtext/nodes/jkqtmathtextwhitespacenode.h"
#include "jkqtmathtext/nodes/jkqtmathtextboxinstructionnode.h"
#include "jkqtmathtext/nodes/jkqtmathtexthorizontallistnode.h"
#include "jkqtmathtext/nodes/jkqtmathtextmodifyenvironmentnode.h"
#include "jkqtmathtext/nodes/jkqtmathtextnoopnode.h"
#include "jkqtmathtext/nodes/jkqtmathtextverbatimnode.h"
#include "jkqtmathtext/nodes/jkqtmathtextverticallistnode.h"
#include "jkqtmathtext/parsers/jkqtmathtextlatexparser.h"
#include <cmath>
#include <QFontMetricsF>
#include <QDebug>
#include <QFontDatabase>
#include <mutex>
#include <typeinfo>
#include <QPainterPath>


// --------------------------------------------------------------------------------------------------
// -- instanciation of the static JKQTMathText members
// --------------------------------------------------------------------------------------------------
const double JKQTMathText::ABS_MIN_LINEWIDTH=0.02;
QString JKQTMathText::init_serifFont="serif";
QString JKQTMathText::init_sansFont="sans";
QString JKQTMathText::init_symbolFont="symbol";
QString JKQTMathText::init_scriptFont="script";
QString JKQTMathText::init_typewriterFont="typewriter";
QString JKQTMathText::init_caligraphicFont="decorative";
QString JKQTMathText::init_blackboardFont="blackboard";
QString JKQTMathText::init_fracturFont="fraktur";
bool JKQTMathText::s_firstStart=true;
std::mutex JKQTMathText::s_mutex;




// --------------------------------------------------------------------------------------------------
// -- implementation of the JKQTMathText methods
// --------------------------------------------------------------------------------------------------
JKQTMathText::JKQTMathText(QObject* parent, bool useFontsForGUI):
    QObject(parent)
{
    //std::chrono::high_resolution_clock::time_point t0=std::chrono::high_resolution_clock::now();
    initJKQTMathTextResources();
    //qDebug()<<"init_resoucre: "<<std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()-t0).count()/1000.0<<"ms"; t0=std::chrono::high_resolution_clock::now();

    fontSize=10;
    fontSizeUnits=JKQTMathTextEnvironment::POINTS;
    fontOptions=BaseFontDefault;
    fontColor=QColor("black");
    brace_factor=1.04;
    brace_y_shift_factor=0.7;//-1;

    subsuper_size_factor=0.65;
    subsuper_mode_selection_by_size_factor=1.01;
    sub_shift_factor=0.4;
    super_shift_factor=0.7;
    special_sub_shift_factor=0.4;
    special_super_shift_factor=0.4;

    frac_factor=1.0;
    frac_nested_factor=0.7;
    frac_shift_factor=0.4;

    underbrace_factor=0.75;
    underbrace_separation_xfactor=0.25;
    underbrace_bracesize_xfactor=0.5;
    underset_factor=0.7;
    decoration_height_factor=0.3;
    decoration_width_reduction_Xfactor=0.2;
    decoration_separation_factor=0.1;

    operatorsubsuper_size_factor=0.65;
    operatorsubsuper_distance_factor=0.35;
    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;

    sqrt_width_Xfactor=0.8;
    sqrt_height_factor=1.2;
    sqrt_smallfont_factor=0.57;

    matrix_linewidth_thin_factor=0.4;
    matrix_linewidth_heavy_factor=1.5;
    matrix_line_separation_factor=2.0;
    matrix_xSeparation_factor=0.75;
    matrix_ySeparation_factor=0.6;
    matrix_xPadding_factor=0.75;
    matrix_yPadding_factor=0.75;

    blackboradFontMode=MTBBDMdefault;



    std::lock_guard<std::mutex> lock(s_mutex);
    if (s_firstStart) {
        //t0=std::chrono::high_resolution_clock::now();

        s_firstStart=false;
#if (QT_VERSION<QT_VERSION_CHECK(6, 0, 0))
        QFontDatabase fdb;
        const auto fonts=fdb.families();
#else
        const auto fonts=QFontDatabase::families();
#endif


        auto checkForFonts=[&fonts](QString& targetfont, const QStringList& fontoptions) {
            for (auto& f: fontoptions) {
                if (fonts.contains(f)) {
                    targetfont=f;
                    break;
                }
            }
        };

        checkForFonts(init_serifFont, QStringList {"Times New Roman", "Times", "FreeSerif", "DejaVu Serif"});
        //qDebug()<<"check 1st font: "<<std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()-t0).count()/1000.0<<"ms";

        checkForFonts(init_sansFont, QStringList {"Segoe UI", "Arial Unicode MS", "Arial Unicode", "Lucida Sans Unicode", "Arial", "Helvetica", "FreeSans", "DejaVu Sans", "Lucida Sans"});
        checkForFonts(init_symbolFont, QStringList {"Segoe UI Symbol", "XITS Math", "Cambria Math", "Fira Math", "Lucida Bright Math", "SymbolStandard", "Symbol"});
        checkForFonts(init_typewriterFont, QStringList {"Courier New", "Courier", "Courier Std", "FreeMono", "CMU Typewriter Text", "UM Typewriter"});
        checkForFonts(init_blackboardFont, QStringList {"Double Stroke", "CloisterOpenFace BT", "GoudyHandtooled BT", "Castellar", "MathJax_AMS", "Castellar Standard", "MathJax_AMS Standard", "Colonna MT"});
        checkForFonts(init_caligraphicFont, QStringList {"Lucida Calligraphy", "Cookie", "Segoe Print", "Comic Sans", "Comic Sans MS", "Gabriola", "Gabriola Standard", "Lucida Handwriting Kursiv", "Lucida Handwriting", "Pristina", "Pristina Standard", "MathJax_Caligraphics"});
        checkForFonts(init_scriptFont, QStringList {"Lucida Handwriting", "Dancing Script", "Amazone BT", "ScriptS", "ScriptC", "ScriptC Standard", "Script", "Brush Script MT", "Brush Script MT Kursiv", "MathJax_Script"});
        checkForFonts(init_fracturFont, QStringList {"Old English Text MT", "Old English Text MT Standard", "UnifrakturMaguntia Standard", "UnifrakturMaguntia", "MathJax_Fraktur", "UnifrakturCook Fett"});
        //qDebug()<<"check all font: "<<std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()-t0).count()/1000.0<<"ms";
    }

    //t0=std::chrono::high_resolution_clock::now();
    if (init_serifFont!="serif") addReplacementFont("serif", init_serifFont);
    if (init_sansFont!="sans") addReplacementFont("sans", init_sansFont);
    if (init_symbolFont!="symbol") addReplacementFont("symbol", init_symbolFont);
    if (init_scriptFont!="script") addReplacementFont("script", init_scriptFont);
    if (init_typewriterFont!="typewriter") addReplacementFont("typewriter", init_typewriterFont);
    if (init_caligraphicFont!="decorative") addReplacementFont("decorative", init_caligraphicFont);
    if (init_fracturFont!="fraktur") addReplacementFont("fraktur", init_fracturFont);
    if (init_blackboardFont!="blackboard") {
        addReplacementFont("blackboard", init_blackboardFont);
    }
    //qDebug()<<"add replacement fonts: "<<std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()-t0).count()/1000.0<<"ms"; t0=std::chrono::high_resolution_clock::now();
    setFontSans(init_sansFont, estimateJKQTMathTextFontEncoding(init_sansFont));
    setFontMathSans(init_sansFont, estimateJKQTMathTextFontEncoding(init_sansFont));
    setFontTypewriter(init_typewriterFont, estimateJKQTMathTextFontEncoding(init_typewriterFont));
    setFontRoman(init_serifFont, estimateJKQTMathTextFontEncoding(init_serifFont));
    setFontMathRoman(init_serifFont, estimateJKQTMathTextFontEncoding(init_serifFont));
    setFontCaligraphic(init_caligraphicFont, estimateJKQTMathTextFontEncoding(init_caligraphicFont));
    if (init_blackboardFont!="blackboard") {
        setFontBlackboard(init_blackboardFont, estimateJKQTMathTextFontEncoding(init_blackboardFont));
        setFontBlackboradMode(MTBBDMunicodeCharactersOrFontDirectly);
    } else {
        setFontBlackboard(init_sansFont, estimateJKQTMathTextFontEncoding(init_sansFont));
        setFontBlackboradMode(MTBBDMunicodeCharactersOrSimulate);
    }
    setFontScript(init_scriptFont, estimateJKQTMathTextFontEncoding(init_scriptFont));
    setFontFraktur(init_fracturFont, estimateJKQTMathTextFontEncoding(init_fracturFont));
    setFallbackFontSymbols(init_symbolFont, estimateJKQTMathTextFontEncoding(init_symbolFont));
    //qDebug()<<"set fonts: "<<std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()-t0).count()/1000.0<<"ms"; t0=std::chrono::high_resolution_clock::now();
    useXITS();
    //qDebug()<<"useXITS: "<<std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()-t0).count()/1000.0<<"ms"; t0=std::chrono::high_resolution_clock::now();
    if (useFontsForGUI) useGuiFonts();
    parsedNode=nullptr;

}

JKQTMathText::~JKQTMathText() {
    deleteParsedNode();
}

void JKQTMathText::loadSettings(const QSettings& settings, const QString& group){
    fontSize=settings.value(group+"font_size", fontSize).toDouble();
    fontSizeUnits=JKQTMathTextEnvironment::String2FontSizeUnit(settings.value(group+"font_size_units", JKQTMathTextEnvironment::FontSizeUnit2String(fontSizeUnits)).toString());
    fontOptions=String2BaseFontOptions(settings.value(group+"font_options", BaseFontOptions2String(fontOptions)).toString());
    fontColor=jkqtp_String2QColor(settings.value(group+"font_color", jkqtp_QColor2String(fontColor)).toString());
    brace_factor=settings.value(group+"brace_factor", brace_factor).toDouble();
    subsuper_size_factor=settings.value(group+"subsuper_size_factor", subsuper_size_factor).toDouble();
    subsuper_mode_selection_by_size_factor=settings.value(group+"subsuper_mode_selection_by_size_factor", subsuper_mode_selection_by_size_factor).toDouble();
    super_shift_factor=settings.value(group+"super_shift_factor", super_shift_factor).toDouble();
    sub_shift_factor=settings.value(group+"sub_shift_factor", sub_shift_factor).toDouble();
    special_super_shift_factor=settings.value(group+"special_super_shift_factor", special_super_shift_factor).toDouble();
    special_sub_shift_factor=settings.value(group+"special_sub_shift_factor", special_sub_shift_factor).toDouble();
    frac_factor=settings.value(group+"frac_factor", frac_factor).toDouble();
    frac_shift_factor=settings.value(group+"frac_shift_factor", frac_shift_factor).toDouble();
    underbrace_factor=settings.value(group+"underbrace_factor", underbrace_factor).toDouble();
    underbrace_bracesize_xfactor=settings.value(group+"underbrace_bracesize_xfactor", underbrace_bracesize_xfactor).toDouble();
    underbrace_separation_xfactor=settings.value(group+"underbrace_separation_xfactor", underbrace_separation_xfactor).toDouble();
    underset_factor=settings.value(group+"underset_factor", underset_factor).toDouble();
    brace_y_shift_factor=settings.value(group+"brace_y_shift_factor", brace_y_shift_factor).toDouble();
    decoration_height_factor=settings.value(group+"decoration_height_factor", decoration_height_factor).toDouble();
    decoration_separation_factor=settings.value(group+"decoration_separation_factor", decoration_separation_factor).toDouble();
    decoration_width_reduction_Xfactor=settings.value(group+"decoration_width_reduction_xfactor", decoration_width_reduction_Xfactor).toDouble();
    operatorsubsuper_size_factor=settings.value(group+"operatorsubsuper_size_factor", operatorsubsuper_size_factor).toDouble();
    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();
    sqrt_width_Xfactor=settings.value(group+"sqrt_width_Xfactor", sqrt_width_Xfactor).toDouble();
    sqrt_height_factor=settings.value(group+"sqrt_height_factor", sqrt_height_factor).toDouble();
    sqrt_smallfont_factor=settings.value(group+"sqrt_smallfont_factor", sqrt_smallfont_factor).toDouble();
    bigmathoperator_font_factor=settings.value(group+"bigmathoperator_font_factor", bigmathoperator_font_factor).toDouble();
    frac_nested_factor=settings.value(group+"frac_nested_factor", frac_nested_factor).toDouble();

    matrix_linewidth_thin_factor=settings.value(group+"matrix_linewidth_thin_factor", matrix_linewidth_thin_factor).toDouble();
    matrix_linewidth_heavy_factor=settings.value(group+"matrix_linewidth_heavy_factor", matrix_linewidth_heavy_factor).toDouble();
    matrix_line_separation_factor=settings.value(group+"matrix_line_separation_factor", matrix_line_separation_factor).toDouble();
    matrix_xSeparation_factor=settings.value(group+"matrix_xSeparation_factor", matrix_xSeparation_factor).toDouble();
    matrix_ySeparation_factor=settings.value(group+"matrix_ySeparation_factor", matrix_ySeparation_factor).toDouble();
    matrix_xPadding_factor=settings.value(group+"matrix_xPadding_factor", matrix_xPadding_factor).toDouble();
    matrix_yPadding_factor=settings.value(group+"matrix_yPadding_factor", matrix_yPadding_factor).toDouble();

    if (settings.value(group+"use_stix_fonts", false).toBool()) useSTIX();
    if (settings.value(group+"use_xits_fonts", false).toBool()) useXITS();
    if (settings.value(group+"use_asana_fonts", false).toBool()) useASANA();
}


void JKQTMathText::saveSettings(QSettings& settings, const QString& group) const{
    settings.setValue(group+"font_size", fontSize);
    settings.setValue(group+"font_size_units", JKQTMathTextEnvironment::FontSizeUnit2String(fontSizeUnits));
    settings.setValue(group+"font_options", BaseFontOptions2String(fontOptions));
    settings.setValue(group+"font_color", jkqtp_QColor2String(fontColor));
    settings.setValue(group+ "brace_factor", brace_factor);
    settings.setValue(group+ "subsuper_size_factor", subsuper_size_factor);
    settings.setValue(group+ "subsuper_mode_selection_by_size_factor", subsuper_mode_selection_by_size_factor);
    settings.setValue(group+ "sub_shift_factor", sub_shift_factor);
    settings.setValue(group+ "super_shift_factor", super_shift_factor);
    settings.setValue(group+ "special_sub_shift_factor", special_sub_shift_factor);
    settings.setValue(group+ "special_super_shift_factor", special_super_shift_factor);
    settings.setValue(group+ "frac_factor", frac_factor);
    settings.setValue(group+ "frac_shift_factor", frac_shift_factor);
    settings.setValue(group+ "underbrace_factor", underbrace_factor);
    settings.setValue(group+ "underbrace_bracesize_xfactor", underbrace_bracesize_xfactor);
    settings.setValue(group+ "underbrace_separation_xfactor", underbrace_separation_xfactor);
    settings.setValue(group+ "underset_factor", underset_factor);
    settings.setValue(group+ "operatorsubsuper_size_factor", operatorsubsuper_size_factor);
    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_separation_factor", decoration_separation_factor);
    settings.setValue(group+ "decoration_width_reduction_xfactor", decoration_width_reduction_Xfactor);
    settings.setValue(group+ "sqrt_width_Xfactor", sqrt_width_Xfactor);
    settings.setValue(group+ "sqrt_height_factor", sqrt_height_factor);
    settings.setValue(group+ "sqrt_smallfont_factor", sqrt_smallfont_factor);
    settings.setValue(group+ "bigmathoperator_font_factor", bigmathoperator_font_factor);
    settings.setValue(group+ "frac_nested_factor", frac_nested_factor);
    settings.setValue(group+ "matrix_linewidth_thin_factor", matrix_linewidth_thin_factor);
    settings.setValue(group+ "matrix_linewidth_heavy_factor", matrix_linewidth_heavy_factor);
    settings.setValue(group+ "matrix_line_separation_factor", matrix_line_separation_factor);
    settings.setValue(group+ "matrix_xSeparation_factor", matrix_xSeparation_factor);
    settings.setValue(group+ "matrix_ySeparation_factor", matrix_ySeparation_factor);
    settings.setValue(group+ "matrix_xPadding_factor", matrix_xPadding_factor);
    settings.setValue(group+ "matrix_yPadding_factor", matrix_yPadding_factor);
}

bool JKQTMathText::parse(const QString &markup, DefaultParserTypes markupType, ParseOptions options)
{
#ifdef JKQTBP_AUTOTIMER
    JKQTPAutoOutputTimer jkaat(QString("JKQTMathText[%1]::parse('%2')").arg(objectName()).arg(markup));
#endif
    if (markupType==LatexParser) return parse<JKQTMathTextLatexParser>(markup, options);
    return false;
}

bool JKQTMathText::useSTIX(bool mathModeOnly, bool useAsFallbackSymbol) {

    const JKQTMathTextFontSpecifier stixs=JKQTMathTextFontSpecifier::getSTIXFamilies();
    bool res=false;

    if (!mathModeOnly && !stixs.fontName().isEmpty()) {
        setFontRoman(stixs.fontName(), MTFEUnicode);
        res=true;
    }
    if (!stixs.mathFontName().isEmpty()) {
        setFontMathRoman(stixs.mathFontName(), MTFEUnicode);
        if (useAsFallbackSymbol) setFallbackFontSymbols(stixs.fallbackSymbolsFontName(), MTFEUnicode);
        res=true;
    } else if (!stixs.fontName().isEmpty()) {
        setFontMathRoman(stixs.fontName(), MTFEUnicode);
        if (useAsFallbackSymbol) setFallbackFontSymbols(stixs.fallbackSymbolsFontName(), MTFEUnicode);
        res=true;
    }

    return res;
}

bool JKQTMathText::useXITS(bool mathModeOnly, bool useAsFallbackSymbol)
{
    const JKQTMathTextFontSpecifier xits=JKQTMathTextFontSpecifier::getXITSFamilies();
    bool res=false;

    if (!mathModeOnly && !xits.fontName().isEmpty()) {
        setFontRoman(xits.fontName(), MTFEUnicode);
        if (useAsFallbackSymbol) setFallbackFontSymbols(xits.fallbackSymbolsFontName(), MTFEUnicode);
        res=true;
    }
    if (!xits.mathFontName().isEmpty()) {
        setFontMathRoman(xits.mathFontName(), MTFEUnicode);
        if (useAsFallbackSymbol) setFallbackFontSymbols(xits.fallbackSymbolsFontName(), MTFEUnicode);
        res=true;
    }

    return res;
}

bool JKQTMathText::useFiraMath(bool mathModeOnly, bool useAsFallbackSymbol)
{
    const JKQTMathTextFontSpecifier fira=JKQTMathTextFontSpecifier::getFIRAFamilies();
    bool res=false;
    //qDebug()<<"useFiraMath("<<mathModeOnly<<", "<<useAsFallbackSymbol<<")";

    if (!mathModeOnly && !fira.fontName().isEmpty()) {
        setFontRoman(fira.fontName(), MTFEUnicode);
        if (useAsFallbackSymbol) setFallbackFontSymbols(fira.fallbackSymbolsFontName(), MTFEUnicode);
        res=true;
    }
    if (!fira.mathFontName().isEmpty()) {
        setFontMathRoman(fira.mathFontName(), MTFEUnicode);
        if (useAsFallbackSymbol) setFallbackFontSymbols(fira.fallbackSymbolsFontName(), MTFEUnicode);
        res=true;
    }

    //std::cout<<"useFiraMath("<<mathModeOnly<<", "<<useAsFallbackSymbol<<") roman="<<getFontRoman().toStdString()<<", mathRoman="<<getFontMathRoman().toStdString()<<", symbol="<<getFallbackFontSymbols().toStdString();
    return res;
}

bool JKQTMathText::useASANA(bool mathModeOnly, bool useAsFallbackSymbol)
{

    const JKQTMathTextFontSpecifier asana=JKQTMathTextFontSpecifier::getASANAFamilies();
    bool res=false;

    if (!mathModeOnly && !asana.fontName().isEmpty()) {
        setFontRoman(asana.fontName(), MTFEUnicode);
        if (useAsFallbackSymbol) setFallbackFontSymbols(asana.fallbackSymbolsFontName(), MTFEUnicode);
        res=true;
    }
    if (!asana.mathFontName().isEmpty()) {
        setFontMathRoman(asana.mathFontName(), MTFEUnicode);
        if (useAsFallbackSymbol) setFallbackFontSymbols(asana.fallbackSymbolsFontName(), MTFEUnicode);
        res=true;
    }

    return res;
}

bool JKQTMathText::useGuiFonts()
{
    const JKQTMathTextFontSpecifier rm=JKQTMathTextFontSpecifier::getAppFontFamilies();
    const JKQTMathTextFontSpecifier sf=JKQTMathTextFontSpecifier::getAppFontSFFamilies();

    if (rm.hasFontName()) setFontRoman(rm.fontName(), MTFEUnicode);
    if (rm.hasMathFontName()) setFontMathRoman(rm.mathFontName(), MTFEUnicode);
    if (rm.hasFallbackSymbolFontName()) setFallbackFontSymbols(rm.fallbackSymbolsFontName(), MTFEUnicode);
    if (sf.hasFontName()) setFontSans(sf.fontName(), MTFEUnicode);
    if (sf.hasMathFontName()) setFontMathSans(sf.mathFontName(), MTFEUnicode);
    return true;
}

void JKQTMathText::useAnyUnicode(QString timesFont, const QString &sansFont, JKQTMathTextFontEncoding encodingTimes, JKQTMathTextFontEncoding encodingSans)
{
    if (!timesFont.isEmpty()) {
        setFontRoman(timesFont, encodingTimes);
        setFontMathRoman(timesFont, encodingTimes);
    }
    if (!sansFont.isEmpty()) {
        setFontSans(sansFont, encodingSans);
        setFontMathSans(sansFont, encodingSans);
    }
}

void JKQTMathText::useAnyUnicodeForMathOnly(QString timesFont, const QString &sansFont, JKQTMathTextFontEncoding encodingTimes, JKQTMathTextFontEncoding encodingSans)
{
    if (!timesFont.isEmpty()) { setFontMathRoman(timesFont, encodingTimes); }
    if (!sansFont.isEmpty()) { setFontMathSans(sansFont, encodingSans); }
}

void JKQTMathText::useAnyUnicodeForTextOnly(QString timesFont, const QString &sansFont, JKQTMathTextFontEncoding encodingTimes, JKQTMathTextFontEncoding encodingSans)
{
    if (!timesFont.isEmpty()) { setFontRoman(timesFont, encodingTimes); }
    if (!sansFont.isEmpty()) { setFontSans(sansFont, encodingSans); }
}


QString JKQTMathText::toHtml(bool *ok, double fontPointSize) {
    QString s;
    bool okk=false;
    if (getNodeTree()!=nullptr) {
        JKQTMathTextEnvironment ev;
        modifyEnvironmentFromFontSettings(ev);
        ev.fontSize=fontPointSize;
        ev.fontSizeUnit=JKQTMathTextEnvironment::PIXELS;

        JKQTMathTextEnvironment defaultev;
        const auto defColor=defaultev.color;
        modifyEnvironmentFromFontSettings(defaultev);
        defaultev.color=defColor;
        ev.fontSize=fontPointSize;
        ev.fontSizeUnit=JKQTMathTextEnvironment::PIXELS;

        okk=getNodeTree()->toHtml(s, ev, defaultev);
    }
    if (ok) *ok=okk;
    return s;
}

QString JKQTMathText::BaseFontOptions2String(JKQTMathText::BaseFontOptions opt)
{
    QStringList res;
    if (opt.testFlag(BaseFontBold)) res<<"BOLD";
    if (opt.testFlag(BaseFontItalic)) res<<"ITALIC";
    if (opt.testFlag(BaseFontUnderlined)) res<<"UNDERLINED";
    if (opt.testFlag(BaseFontSmallCaps)) res<<"SMALLCAPS";

    return res.join('+');
}

JKQTMathText::BaseFontOptions JKQTMathText::String2BaseFontOptions(const QString &s)
{
    BaseFontOptions res=BaseFontDefault;
    const auto opts=s.toUpper().trimmed().simplified().split('+');
    for (const auto& o: opts) {
        if (o=="BOLD" || o=="BF" || o=="B") res.setFlag(BaseFontBold);
        else if (o=="ITALIC" || o=="IT" || o=="I") res.setFlag(BaseFontItalic);
        else if (o=="UNDERLINED" || o=="UL" || o=="U") res.setFlag(BaseFontUnderlined);
        else if (o=="SMALLCAPS" || o=="SC" || o=="S") res.setFlag(BaseFontSmallCaps);
    }
    return res;
}


void JKQTMathText::setFontColor(const QColor &__value)
{
    this->fontColor = __value;
}

QColor JKQTMathText::getFontColor() const
{
    return this->fontColor;
}

void JKQTMathText::setFontSize(double __value)
{
    setFontPointSize(__value);
}

void JKQTMathText::setFontPointSize(double __value)
{
    fontSize = __value;
    fontSizeUnits=JKQTMathTextEnvironment::POINTS;
}

void JKQTMathText::setFontSizePixels(double __value)
{
    fontSize = __value;
    fontSizeUnits=JKQTMathTextEnvironment::PIXELS;
}

double JKQTMathText::getFontSize() const
{
    return getFontPointSize();
}

double JKQTMathText::getFontPointSize() const
{
    if (fontSizeUnits==JKQTMathTextEnvironment::POINTS) return fontSize;
    else return -1;
}

double JKQTMathText::getFontSizePixels() const
{
    if (fontSizeUnits==JKQTMathTextEnvironment::PIXELS) return fontSize;
    else return -1;
}

void JKQTMathText::setFontOptions(JKQTMathText::BaseFontOptions opts)
{
    fontOptions=opts;
}

void JKQTMathText::setFontOption(JKQTMathText::BaseFontOption opt, bool enabled)
{
    fontOptions.setFlag(opt, enabled);
}

JKQTMathText::BaseFontOptions JKQTMathText::getFontOptions() const
{
    return fontOptions;
}

void JKQTMathText::addReplacementFont(const QString &nonUseFont, const QString &useFont, JKQTMathTextFontEncoding useFontEncoding) {
    fontReplacements.insert(nonUseFont, useFont);
    fontEncodingReplacements.insert(nonUseFont, useFontEncoding);
}

void JKQTMathText::addReplacementFont(const QString &nonUseFont, const QString &useFont) {
    fontReplacements.insert(nonUseFont, useFont);
    auto it=fontEncodingReplacements.find(nonUseFont);
    if (it!=fontEncodingReplacements.end()) fontEncodingReplacements.erase(it);
}

QPair<QString,JKQTMathTextFontEncoding> JKQTMathText::getReplacementFont(const QString &nonUseFont, const QString &defaultFont, JKQTMathTextFontEncoding defaultFontEncoding) const {
    QPair<QString,JKQTMathTextFontEncoding> res(defaultFont, defaultFontEncoding);
    for (auto it=fontReplacements.begin(); it!=fontReplacements.end(); ++it) {
        if (it.key().toLower()==nonUseFont.toLower()) {
            res.first=it.value();
            res.second=fontEncodingReplacements.value(res.first, res.second);
            return res;
        }
    }
    return res;
}

QPair<QString, JKQTMathTextFontEncoding> JKQTMathText::getFontData(JKQTMathTextEnvironmentFont font, bool in_math_environment) const
{
    if (in_math_environment) {
        if (font==MTEroman) font=MTEmathRoman;
        if (font==MTEsans) font=MTEmathSans;
    }
    const auto fd=fontDefinitions.value(font);
    return QPair<QString, JKQTMathTextFontEncoding>(fd.fontName, fd.fontEncoding);
}


void JKQTMathText::setFontRomanOrSpecial(const QString &__value, JKQTMathTextFontEncoding encoding)
{
    setFontRomanOrSpecial(JKQTMathTextFontSpecifier::fromFontSpec(__value), encoding);
}

void JKQTMathText::setFontRomanOrSpecial(const JKQTMathTextFontSpecifier &fontName, JKQTMathTextFontEncoding encoding)
{
    if (!fontName.hasMathFontName()) {
        setFontRoman(fontName.fontName(), encoding);
        setFontMathRoman(fontName.fontName(), encoding);
    } else {
        setFontRoman(fontName.fontName(), encoding);
        setFontMathRoman(fontName.mathFontName(), encoding);
    }
    if (fontName.hasFallbackSymbolFontName()) setFallbackFontSymbols(fontName.fallbackSymbolsFontName(), encoding);
}

void JKQTMathText::setFontSpecial(const QString &fontSpec)
{
    if (fontSpec.trimmed().size()==0) return;
    QString beforePlus=fontSpec;
    QStringList afterPlus;
    afterPlus=fontSpec.split('+');
    if (afterPlus.size()>1) {
        beforePlus=afterPlus.first();
        afterPlus.removeFirst();
    } else {
        afterPlus.clear();
    }


    if (beforePlus.toUpper()=="GUI") useGuiFonts();
    else {
        const QStringList splitSlash=beforePlus.split('/');
        if (splitSlash.size()==1) {
            setFontRoman(JKQTMathTextFontSpecifier::transformFontNameAndDecodeSpecialFonts(splitSlash[0]), MTFEUnicode);
            setFontMathRoman(JKQTMathTextFontSpecifier::transformFontNameAndDecodeSpecialFonts(splitSlash[0]), MTFEUnicode);
            setFontSans(init_sansFont, estimateJKQTMathTextFontEncoding(init_sansFont));
            setFontMathSans(init_sansFont, estimateJKQTMathTextFontEncoding(init_sansFont));
        } else if (splitSlash.size()==2) {
            setFontRoman(JKQTMathTextFontSpecifier::transformFontNameAndDecodeSpecialFonts(splitSlash[0]), MTFEUnicode);
            setFontMathRoman(JKQTMathTextFontSpecifier::transformFontNameAndDecodeSpecialFonts(splitSlash[0]), MTFEUnicode);
            setFontSans(JKQTMathTextFontSpecifier::transformFontNameAndDecodeSpecialFonts(splitSlash[1]), MTFEUnicode);
            setFontMathSans(JKQTMathTextFontSpecifier::transformFontNameAndDecodeSpecialFonts(splitSlash[1]), MTFEUnicode);
        } else if (splitSlash.size()==4) {
            setFontRoman(JKQTMathTextFontSpecifier::transformFontNameAndDecodeSpecialFonts(splitSlash[0]), MTFEUnicode);
            setFontMathRoman(JKQTMathTextFontSpecifier::transformFontNameAndDecodeSpecialFonts(splitSlash[2]), MTFEUnicode);
            setFontSans(JKQTMathTextFontSpecifier::transformFontNameAndDecodeSpecialFonts(splitSlash[1]), MTFEUnicode);
            setFontMathSans(JKQTMathTextFontSpecifier::transformFontNameAndDecodeSpecialFonts(splitSlash[3]), MTFEUnicode);
        } else {
            qDebug()<<"JKQTMathText::setFontSpecial(): undecodable fontSpec '"<<fontSpec<<"'";
        }
    }
    BaseFontOptions opt=BaseFontDefault;
    for (const auto& afterIn: afterPlus) {
        const auto after=afterIn.toUpper();
        if (after=="XITS") useXITS();
        else if (after=="STIX") useSTIX();
        else if (after=="ASANA") useASANA();
        else if (after=="FIRA") useFiraMath();
        else if (after=="BOLD" || after=="BF" || after=="B") opt.setFlag(BaseFontBold);
        else if (after=="ITALIC" || after=="IT" || after=="I") opt.setFlag(BaseFontItalic);
        else if (after=="UNDERLINED" || after=="UL" || after=="U") opt.setFlag(BaseFontUnderlined);
        else if (after=="SMALLCAPS" || after=="SC" || after=="S") opt.setFlag(BaseFontSmallCaps);
        else {
            qDebug()<<"JKQTMathText::setFontSpecial() didn't recognize font name component '"<<after<<"'";
        }
    }
    setFontOptions(opt);
}

void JKQTMathText::setFontRoman(const QString &__value, JKQTMathTextFontEncoding encoding)
{
    auto f=getReplacementFont(__value, __value, encoding);
    fontDefinitions[MTEroman].fontName = f.first;
    fontDefinitions[MTEroman].fontEncoding = f.second;
}

void JKQTMathText::setFontRomanAndMath(const QString &fontName, JKQTMathTextFontEncoding encoding)
{
    setFontRoman(fontName, encoding);
    setFontMathRoman(fontName, encoding);
}

QString JKQTMathText::getFontRoman() const
{
    return fontDefinitions[MTEroman].fontName;
}

JKQTMathTextFontEncoding JKQTMathText::getFontEncodingRoman() const
{
    return fontDefinitions[MTEroman].fontEncoding;
}

void JKQTMathText::setFontSans(const QString &__value, JKQTMathTextFontEncoding encoding)
{
    auto f=getReplacementFont(__value, __value, encoding);
    fontDefinitions[MTEsans].fontName = f.first;
    fontDefinitions[MTEsans].fontEncoding = f.second;
}

QString JKQTMathText::getFontSans() const
{
    return fontDefinitions[MTEsans].fontName;
}

JKQTMathTextFontEncoding JKQTMathText::getFontEncodingSans() const
{
    return fontDefinitions[MTEsans].fontEncoding;
}

void JKQTMathText::setFontTypewriter(const QString &__value, JKQTMathTextFontEncoding encoding)
{
    auto f=getReplacementFont(__value, __value, encoding);
    fontDefinitions[MTEtypewriter].fontName = f.first;
    fontDefinitions[MTEtypewriter].fontEncoding = f.second;
}

QString JKQTMathText::getFontTypewriter() const
{
    return fontDefinitions[MTEtypewriter].fontName;
}

JKQTMathTextFontEncoding JKQTMathText::getFontEncodingTypewriter() const
{
    return fontDefinitions[MTEtypewriter].fontEncoding;
}

void JKQTMathText::setFontScript(const QString &__value, JKQTMathTextFontEncoding encoding)
{
    auto f=getReplacementFont(__value, __value, encoding);
    fontDefinitions[MTEscript].fontName = f.first;
    fontDefinitions[MTEscript].fontEncoding = f.second;
}

QString JKQTMathText::getFontScript() const
{
    return fontDefinitions[MTEscript].fontName;
}

JKQTMathTextFontEncoding JKQTMathText::getFontEncodingScript() const
{
    return fontDefinitions[MTEscript].fontEncoding;
}

void JKQTMathText::setFontFraktur(const QString &__value, JKQTMathTextFontEncoding encoding)
{
    auto f=getReplacementFont(__value, __value, encoding);
    fontDefinitions[MTEfraktur].fontName = f.first;
    fontDefinitions[MTEfraktur].fontEncoding = f.second;
}

QString JKQTMathText::getFontFraktur() const
{
    return fontDefinitions[MTEfraktur].fontName;
}

JKQTMathTextFontEncoding JKQTMathText::getFontEncodingFraktur() const
{
    return fontDefinitions[MTEfraktur].fontEncoding;
}

void JKQTMathText::setFallbackFontSymbols(const QString &fontName, JKQTMathTextFontEncoding encoding)
{
    auto f=getReplacementFont(fontName, fontName, encoding);
    fontDefinitions[MTEFallbackSymbols].fontName = f.first;
    fontDefinitions[MTEFallbackSymbols].fontEncoding = f.second;
}


QString JKQTMathText::getFallbackFontSymbols() const
{
    return fontDefinitions[MTEFallbackSymbols].fontName;
}


JKQTMathTextFontEncoding JKQTMathText::getFontEncodingFallbackFontSymbols() const
{
    return fontDefinitions[MTEFallbackSymbols].fontEncoding;
}

void JKQTMathText::setFontCaligraphic(const QString &__value, JKQTMathTextFontEncoding encoding)
{
    auto f=getReplacementFont(__value, __value, encoding);
    fontDefinitions[MTEcaligraphic].fontName = f.first;
    fontDefinitions[MTEcaligraphic].fontEncoding = f.second;
}

QString JKQTMathText::getFontCaligraphic() const
{
    return fontDefinitions[MTEcaligraphic].fontName;
}

JKQTMathTextFontEncoding JKQTMathText::getFontEncodingCaligraphic() const
{
    return fontDefinitions[MTEcaligraphic].fontEncoding;
}

void JKQTMathText::setFontMathRoman(const QString &fontName, JKQTMathTextFontEncoding encoding)
{
    auto f=getReplacementFont(fontName, fontName, encoding);
    fontDefinitions[MTEmathRoman].fontName = f.first;
    fontDefinitions[MTEmathRoman].fontEncoding = f.second;
}

QString JKQTMathText::getFontMathRoman() const
{
    return fontDefinitions[MTEmathRoman].fontName;
}

void JKQTMathText::setFontMathSans(const QString &fontName, JKQTMathTextFontEncoding encoding)
{
    auto f=getReplacementFont(fontName, fontName, encoding);
    fontDefinitions[MTEmathSans].fontName = f.first;
    fontDefinitions[MTEmathSans].fontEncoding = f.second;
}

QString JKQTMathText::getFontMathSans() const
{
    return fontDefinitions[MTEmathSans].fontName;
}

JKQTMathTextFontEncoding JKQTMathText::getFontEncodingMathSans() const
{
    return fontDefinitions[MTEmathSans].fontEncoding;
}

JKQTMathTextFontEncoding JKQTMathText::getFontEncodingMathRoman() const
{
    return fontDefinitions[MTEmathRoman].fontEncoding;
}

void JKQTMathText::setFontBlackboard(const QString &__value, JKQTMathTextFontEncoding encoding)
{
    auto f=getReplacementFont(__value, __value, encoding);
    fontDefinitions[MTEblackboard].fontName = f.first;
    fontDefinitions[MTEblackboard].fontEncoding = f.second;
}

JKQTMathTextBlackboradDrawingMode JKQTMathText::getFontBlackboradMode() const
{
    return blackboradFontMode;
}

void JKQTMathText::setFontBlackboradMode(JKQTMathTextBlackboradDrawingMode mode)
{
    blackboradFontMode=mode;
}

QString JKQTMathText::getFontBlackboard() const
{
    return fontDefinitions[MTEblackboard].fontName;
}

JKQTMathTextFontEncoding JKQTMathText::getFontEncodingBlackboard() const
{
    return fontDefinitions[MTEblackboard].fontEncoding;
}


void JKQTMathText::setBraceFactor(double __value)
{
    this->brace_factor = __value;
}

double JKQTMathText::getBraceFactor() const
{
    return this->brace_factor;
}

void JKQTMathText::setSubsuperSizeFactor(double __value)
{
    this->subsuper_size_factor = __value;
}

double JKQTMathText::getSubsuperSizeFactor() const
{
    return this->subsuper_size_factor;
}

void JKQTMathText::setSubsuperModeSelectionBySizeFactor(double __value)
{
    subsuper_mode_selection_by_size_factor=__value;
}

double JKQTMathText::getSubsuperModeSelectionBySizeFactor() const
{
    return subsuper_mode_selection_by_size_factor;
}

void JKQTMathText::setOperatorsubsuperSizeFactor(double __value)
{
    this->operatorsubsuper_size_factor = __value;
}

double JKQTMathText::getOperatorsubsuperSizeFactor() const
{
    return this->operatorsubsuper_size_factor;
}

void JKQTMathText::setOperatorsubsuperDistanceFactor(double __value)
{
    this->operatorsubsuper_distance_factor = __value;
}

double JKQTMathText::getOperatorsubsuperDistanceFactor() const
{
    return this->operatorsubsuper_distance_factor;
}

void JKQTMathText::setOperatorsubsuperExtraSpaceFactor(double __value)
{
    operatorsubsuper_extraspace_factor=__value;
}

double JKQTMathText::getOperatorsubsuperExtraSpaceFactor() const
{
    return operatorsubsuper_extraspace_factor;
}

void JKQTMathText::setMathoperatorWidthFactor(double __value)
{
    this->mathoperator_width_factor = __value;
}

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;
}

double JKQTMathText::getBigMathoperatorFontFactor() const
{
    return bigmathoperator_font_factor;
}

void JKQTMathText::setSuperShiftFactor(double __value)
{
    this->super_shift_factor = __value;
}

double JKQTMathText::getSuperShiftFactor() const
{
    return this->super_shift_factor;
}

void JKQTMathText::setSubShiftFactor(double __value)
{
    this->sub_shift_factor = __value;
}

double JKQTMathText::getSubShiftFactor() const
{
    return this->sub_shift_factor;
}

double JKQTMathText::getSpecialSuperShiftFactor() const
{
    return special_super_shift_factor;
}

void JKQTMathText::setSpecialSuperShiftFactor(double __value)
{
    special_super_shift_factor=__value;
}

void JKQTMathText::setSpecialSubShiftFactor(double __value)
{
    special_sub_shift_factor=__value;
}

double JKQTMathText::getSpecialSubShiftFactor() const
{
    return special_sub_shift_factor;
}

void JKQTMathText::setUnderbraceFactor(double __value)
{
    this->underbrace_factor = __value;
}

double JKQTMathText::getUnderbraceFactor() const
{
    return this->underbrace_factor;
}

void JKQTMathText::setUnderbraceSeparationXFactor(double __value)
{
    underbrace_separation_xfactor=__value;
}

double JKQTMathText::getUnderbraceSeparationXFactor() const
{
    return underbrace_separation_xfactor;
}

void JKQTMathText::setUnderbraceBraceSizeXFactor(double __value)
{
    underbrace_bracesize_xfactor=__value;
}

double JKQTMathText::getUnderbraceBraceSizeXFactor() const
{
    return underbrace_bracesize_xfactor;
}

void JKQTMathText::setUndersetFactor(double __value)
{
    this->underset_factor = __value;
}

double JKQTMathText::getUndersetFactor() const
{
    return this->underset_factor;
}

void JKQTMathText::setFracFactor(double __value)
{
    this->frac_factor = __value;
}

double JKQTMathText::getFracFactor() const
{
    return this->frac_factor;
}

void JKQTMathText::setFracNestedFactor(double __value)
{
    frac_nested_factor=__value;
}

double JKQTMathText::getFracNestedFactor() const
{
    return frac_nested_factor;
}

void JKQTMathText::setFracShiftFactor(double __value)
{
    this->frac_shift_factor = __value;
}

double JKQTMathText::getFracShiftFactor() const
{
    return this->frac_shift_factor;
}

void JKQTMathText::setBraceYShiftFactor(double __value)
{
    this->brace_y_shift_factor = __value;
}

double JKQTMathText::getBraceYShiftFactor() const
{
    return this->brace_y_shift_factor;
}

void JKQTMathText::setDecorationHeightFactor(double __value)
{
    this->decoration_height_factor = __value;
}

double JKQTMathText::getDecorationHeightFactor() const
{
    return this->decoration_height_factor;
}

void JKQTMathText::setDecorationSeparationXFactor(double __value)
{
    decoration_separation_factor=__value;
}

double JKQTMathText::getDecorationSeparationFactor() const
{
    return decoration_separation_factor;
}

void JKQTMathText::setDecorationWidthReductionFactor(double __value)
{
    decoration_width_reduction_Xfactor=__value;
}

double JKQTMathText::getDecorationWidthReductionXFactor() const
{
    return decoration_width_reduction_Xfactor;
}

void JKQTMathText::setSqrtWidthXFactor(double __value)
{
    sqrt_width_Xfactor=__value;
}

double JKQTMathText::getSqrtWidthXFactor() const
{
    return sqrt_width_Xfactor;
}

void JKQTMathText::setSqrtHeightFactor(double __value)
{
    sqrt_height_factor=__value;
}

double JKQTMathText::getSqrtHeightFactor() const
{
    return sqrt_height_factor;
}

void JKQTMathText::setSqrtSmallFontFactor(double __value)
{
    sqrt_smallfont_factor=__value;
}

double JKQTMathText::getSqrtSmallFontFactor() const
{
    return sqrt_smallfont_factor;
}

double JKQTMathText::getMatrixLinewidthThinFactor()
{
    return matrix_linewidth_thin_factor;
}

void JKQTMathText::setMatrixLinewidthThinFactor(double factor)
{
    matrix_linewidth_thin_factor=factor;
}

double JKQTMathText::getMatrixLinewidthHeavyFactor()
{
    return matrix_linewidth_heavy_factor;
}

void JKQTMathText::setMatrixLinewidthHeavyFactor(double factor)
{
    matrix_linewidth_heavy_factor=factor;
}

double JKQTMathText::getMatrixLineSeparationFactor()
{
    return matrix_line_separation_factor;
}

void JKQTMathText::setMatrixLineSeparationFactor(double factor)
{
    matrix_line_separation_factor=factor;
}

double JKQTMathText::getMatrixXSeparationFactor()
{
    return matrix_xSeparation_factor;
}

void JKQTMathText::setMatrixXSeparationFactor(double factor)
{
    matrix_xSeparation_factor=factor;
}

double JKQTMathText::getMatrixYSeparationFactor()
{
    return matrix_ySeparation_factor;
}

void JKQTMathText::setMatrixYSeparationFactor(double factor)
{
    matrix_ySeparation_factor=factor;
}

double JKQTMathText::getMatrixXPaddingFactor()
{
    return matrix_xPadding_factor;
}

void JKQTMathText::setMatrixXPaddingFactor(double factor)
{
    matrix_xPadding_factor=factor;
}

double JKQTMathText::getMatrixYPaddingFactor()
{
    return matrix_yPadding_factor;
}

void JKQTMathText::setMatrixYPaddingFactor(double factor)
{
    matrix_yPadding_factor=factor;
}

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);
}


void JKQTMathText::clearErrorList()
{
    error_list.clear();
}

void JKQTMathText::deleteParsedNode()
{
    if (parsedNode) delete parsedNode;
    parsedNode=nullptr;
}


const JKQTMathTextNode *JKQTMathText::getNodeTree() const {
    return this->parsedNode;
}






QSizeF JKQTMathText::getSize(QPainter& painter){
    if (getNodeTree()!=nullptr) {
        const JKQTMathTextNodeSize s=getSizeDetail(painter);
        return s.getSize();
    }
    return QSizeF(0,0);
}

QSize JKQTMathText::getIntSize(QPainter &painter)
{
    if (getNodeTree()!=nullptr) {
        const JKQTMathTextNodeSize s=getSizeDetail(painter);
        return s.getIntSize();
    }
    return QSize(0,0);
}

double JKQTMathText::getDescent(QPainter& painter) {
    const JKQTMathTextNodeSize s=getSizeDetail(painter);
    return s.getDescent();
}

double JKQTMathText::getAscent(QPainter& painter) {
    const JKQTMathTextNodeSize s=getSizeDetail(painter);
    return s.baselineHeight;
}

void JKQTMathText::getSizeDetail(QPainter& painter, double& width, double& ascent, double& descent, double& strikeoutPos) {
    JKQTMathTextNodeSize s=getSizeDetail(painter);
    width=s.width;
    ascent=s.baselineHeight;
    descent=s.getDescent();
    strikeoutPos=s.strikeoutPos;
}


void JKQTMathText::modifyEnvironmentFromFontSettings(JKQTMathTextEnvironment &ev) const
{
    ev.color=fontColor;
    ev.fontSize=fontSize;
    ev.fontSizeUnit=fontSizeUnits;
    ev.bold=fontOptions.testFlag(BaseFontBold);
    ev.italic=fontOptions.testFlag(BaseFontItalic);
    ev.underlined=fontOptions.testFlag(BaseFontUnderlined);
    if (fontOptions.testFlag(BaseFontSmallCaps)) ev.capitalization=QFont::SmallCaps;
}

JKQTMathTextNodeSize JKQTMathText::getSizeDetail(QPainter &painter)
{
#ifdef JKQTBP_AUTOTIMER
    JKQTPAutoOutputTimer jkaat(QString("JKQTMathText[%1]::getSizeDetail()").arg(objectName()));
#endif
    JKQTMathTextNodeSize s;
    if (getNodeTree()!=nullptr) {
        JKQTMathTextEnvironment ev;
        modifyEnvironmentFromFontSettings(ev);
        s=getNodeTree()->getSize(painter, ev);
    }
    return s;
}

void JKQTMathText::draw(QPainter &painter, QPointF x, bool drawBoxes)
{
    draw(painter, x.x(), x.y(), drawBoxes);
}

double JKQTMathText::draw(QPainter& painter, double x, double y, bool drawBoxes){
#ifdef JKQTBP_AUTOTIMER
    JKQTPAutoOutputTimer jkaat(QString("JKQTMathText[%1]::draw(x,y)").arg(objectName()));
#endif
    if (getNodeTree()!=nullptr) {
        painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
        painter.setPen(fontColor);
        painter.setBrush(Qt::NoBrush);
        JKQTMathTextEnvironment ev;
        modifyEnvironmentFromFontSettings(ev);
        getNodeTree()->setDrawBoxes(drawBoxes);
        const double xend=getNodeTree()->draw(painter, x, y, ev);
        return xend;
    }
    return x;
}

void JKQTMathText::draw(QPainter& painter, unsigned int flags, QRectF rect, bool drawBoxes) {
#ifdef JKQTBP_AUTOTIMER
    JKQTPAutoOutputTimer jkaat(QString("JKQTMathText[%1]::draw(rect)").arg(objectName()));
#endif
    if (getNodeTree()!=nullptr) {
        painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
        painter.setPen(fontColor);
        painter.setBrush(Qt::NoBrush);

        JKQTMathTextEnvironment ev;
        modifyEnvironmentFromFontSettings(ev);
        getNodeTree()->setDrawBoxes(drawBoxes);

        const JKQTMathTextNodeSize size= getSizeDetail(painter);

        // align left top
        double x=rect.left();
        double y=rect.top()+size.baselineHeight;

        // care for horizontal align
        if ((flags & Qt::AlignRight) != 0) x=x+rect.width()-size.width;
        else if ((flags & Qt::AlignHCenter) != 0) x=x+(rect.width()-size.width)/2.0;

        // care for vertical align
        if ((flags & Qt::AlignBottom) != 0) y=y+rect.height()-size.overallHeight;
        else if ((flags & Qt::AlignVCenter) != 0) y=y+(rect.height()-size.overallHeight)/2.0;

        // finally draw
        /*const double xend=*/getNodeTree()->draw(painter, x, y, ev);
    }
}

QPixmap JKQTMathText::drawIntoPixmap(bool drawBoxes, QColor backgroundColor, int sizeincrease, qreal devicePixelRatio)
{
#ifdef JKQTBP_AUTOTIMER
    JKQTPAutoOutputTimer jkaat(QString("JKQTMathText[%1]::drawIntoPixmap()").arg(objectName()));
#endif
    // 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)
{
#ifdef JKQTBP_AUTOTIMER
    JKQTPAutoOutputTimer jkaat(QString("JKQTMathText[%1]::drawIntoImage()").arg(objectName()));
#endif
    // 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_Premultiplied);
    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*devicePixelRatio,QImage::Format_ARGB32_Premultiplied);
        img.setDevicePixelRatio(devicePixelRatio);
        img.setDotsPerMeterX(resolution_dpi*(10000/254));
        img.setDotsPerMeterY(resolution_dpi*(10000/254));
        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)
{
#ifdef JKQTBP_AUTOTIMER
    JKQTPAutoOutputTimer jkaat(QString("JKQTMathText[%1]::drawIntoPicture()").arg(objectName()));
#endif
    // 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() {
    return parsedNode;
}
