/* Copyright (c) 2008-2022 Jan W. Krieger () 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 . */ #include "jkqtmathtext/jkqtmathtexttools.h" #include "jkqtmathtext/jkqtmathtext.h" #include "jkqtcommon/jkqtpcodestructuring.h" #include "jkqtcommon/jkqtpstringtools.h" #include #include #include #include #include #include #include void initJKQTMathTextResources() { static bool initialized=false; if (!initialized) { #ifdef JKQTMATHTEXT_COMPILED_WITH_XITS Q_INIT_RESOURCE(xits); #endif initialized=true; } } JKQTMathTextFontSpecifier::JKQTMathTextFontSpecifier(): m_fontName(""), m_mathFontName("") { } JKQTMathTextFontSpecifier::JKQTMathTextFontSpecifier(const QString &_fontName, const QString &_mathFontName): m_fontName(_fontName), m_mathFontName(_mathFontName) { } JKQTMathTextFontSpecifier JKQTMathTextFontSpecifier::fromFontSpec(const QString &fontSpec) { JKQTMathTextFontSpecifier s; s.setFontSpec(fontSpec); return s; } void JKQTMathTextFontSpecifier::setFontSpec(const QString &fontSpec) { QStringList splitspec=fontSpec.split('+'); if (splitspec.size()==0) { m_fontName=m_mathFontName=""; } else if (splitspec.size()==1) { m_fontName=splitspec[0]; m_mathFontName=""; } else if (splitspec.size()==2) { m_fontName=splitspec[0]; m_mathFontName=splitspec[1]; } else if (splitspec.size()>2) { m_fontName=splitspec.mid(0, splitspec.size()-1).join('+'); m_mathFontName=splitspec.last(); } } QString JKQTMathTextFontSpecifier::getFontSpec() const { QString res=m_fontName; if (m_mathFontName.size()>0) res+="+"+m_mathFontName; return res; } QString JKQTMathTextFontSpecifier::fontName() const { return transformFontName(m_fontName); } QString JKQTMathTextFontSpecifier::mathFontName() const { return transformFontName(m_mathFontName); } QString JKQTMathTextFontSpecifier::transformFontName(const QString &fontName) { const QString fnt=fontName.trimmed().toLower(); QFont testFnt; if (fnt=="serif") { testFnt.setStyleHint(QFont::StyleHint::Serif); return testFnt.defaultFamily(); } if (fnt=="sans-serif" || fnt=="sansserif" || fnt=="sans" || fnt=="sans serif") { testFnt.setStyleHint(QFont::StyleHint::SansSerif); return testFnt.defaultFamily(); } if (fnt=="cursive") { testFnt.setStyleHint(QFont::StyleHint::Cursive); return testFnt.defaultFamily(); } if (fnt=="typewriter") { testFnt.setStyleHint(QFont::StyleHint::TypeWriter); return testFnt.defaultFamily(); } if (fnt=="monospace") { testFnt.setStyleHint(QFont::StyleHint::Monospace); return testFnt.defaultFamily(); } if (fnt=="fantasy") { testFnt.setStyleHint(QFont::StyleHint::Fantasy); return testFnt.defaultFamily(); } if (fnt=="system") { testFnt.setStyleHint(QFont::StyleHint::System); return testFnt.defaultFamily(); } if (fnt=="decorative") { testFnt.setStyleHint(QFont::StyleHint::Decorative); return testFnt.defaultFamily(); } if (fnt=="default" || fnt=="app" || fnt=="application") { return QGuiApplication::font().family(); } #if QT_VERSION >= QT_VERSION_CHECK(6,0,0) if (fnt=="fixed") { return QFontDatabase::systemFont(QFontDatabase::SystemFont::FixedFont).family(); } if (fnt=="smallest_readable" || fnt=="smallestreadable" || fnt=="smallest readable" || fnt=="smallest") { return QFontDatabase::systemFont(QFontDatabase::SystemFont::SmallestReadableFont).family(); } if (fnt=="title") { return QFontDatabase::systemFont(QFontDatabase::SystemFont::TitleFont).family(); } if (fnt=="general") { return QFontDatabase::systemFont(QFontDatabase::SystemFont::GeneralFont).family(); } #elif QT_VERSION >= QT_VERSION_CHECK(5,2,0) QFontDatabase fontDB; if (fnt=="fixed") { return fontDB.systemFont(QFontDatabase::SystemFont::FixedFont).family(); } if (fnt=="smallest_readable" || fnt=="smallestreadable" || fnt=="smallest readable" || fnt=="smallest") { return fontDB.systemFont(QFontDatabase::SystemFont::SmallestReadableFont).family(); } if (fnt=="title") { return fontDB.systemFont(QFontDatabase::SystemFont::TitleFont).family(); } if (fnt=="general") { return fontDB.systemFont(QFontDatabase::SystemFont::GeneralFont).family(); } #endif return fontName; } QString JKQTMathTextFontSpecifier::transformFontNameAndDecodeSpecialFonts(const QString &fontName) { const QString fnt=fontName.toLower().trimmed(); if (fnt=="xits") { return getXITSFamilies().fontName(); } else if (fnt=="asana") { return getASANAFamilies().fontName(); } else if (fnt=="stix") { return getSTIXFamilies().fontName(); } return transformFontName(fontName); } bool JKQTMathTextFontSpecifier::hasFontName() const { return !m_fontName.isEmpty(); } bool JKQTMathTextFontSpecifier::hasMathFontName() const { return !m_mathFontName.isEmpty(); } JKQTMathTextFontSpecifier JKQTMathTextFontSpecifier::getXITSFamilies() { #if (QT_VERSION0 && fontSpec.m_fontName.size()>0) { break; } } if (fontSpec.m_mathFontName.isEmpty() && !fontSpec.m_fontName.isEmpty()) { fontSpec.m_mathFontName=fontSpec.m_fontName; } else if (!fontSpec.m_mathFontName.isEmpty() && fontSpec.m_fontName.isEmpty()) { fontSpec.m_fontName=fontSpec.m_mathFontName; } } return fontSpec; } JKQTMathTextFontSpecifier JKQTMathTextFontSpecifier::getASANAFamilies() { #if (QT_VERSION0 && fontSpec.m_fontName.size()>0) { break; } } if (fontSpec.m_mathFontName.isEmpty() && !fontSpec.m_fontName.isEmpty()) { fontSpec.m_mathFontName=fontSpec.m_fontName; } else if (!fontSpec.m_mathFontName.isEmpty() && fontSpec.m_fontName.isEmpty()) { fontSpec.m_fontName=fontSpec.m_mathFontName; } } return fontSpec; } JKQTMathTextFontSpecifier JKQTMathTextFontSpecifier::getSTIXFamilies() { static QStringList mathNames{"STIX Two Math", "STIX Math", "STIX Two Math Standard", "STIX Math Standard"}; static QStringList textNames{"STIX", "STIXGeneral", "STIX General"}; static JKQTMathTextFontSpecifier fontSpec; if (fontSpec.m_fontName.isEmpty() && fontSpec.m_mathFontName.isEmpty()) { #if (QT_VERSION0) { break; } } if (fontSpec.m_mathFontName.size()>0) { break; } } for (const QString& name:textNames) { for (int i=0; i0) { break; } } if (fontSpec.m_fontName.size()>0) { break; } } if (fontSpec.m_mathFontName.isEmpty() && !fontSpec.m_fontName.isEmpty()) { fontSpec.m_mathFontName=fontSpec.m_fontName; } else if (!fontSpec.m_mathFontName.isEmpty() && fontSpec.m_fontName.isEmpty()) { fontSpec.m_fontName=fontSpec.m_mathFontName; } } return fontSpec; } QString JKQTMathTextFontEncoding2String(JKQTMathTextFontEncoding e) { switch(e) { case MTFEunicode: return "MTFEunicode"; case MTFEStandard: return "MTFEStandard"; case MTFEunicodeLimited: return "MTFEunicodeLimited"; case MTFEwinSymbol: return "MTFEwinSymbol"; } return "???"; } JKQTMathTextEnvironment::JKQTMathTextEnvironment() { color=QColor("black"); font=MTEroman; fontSize=10; bold=false; italic=false; smallCaps=false; underlined=false; overline=false; strike=false; insideMath=false; } QFont JKQTMathTextEnvironment::getFont(JKQTMathText* parent) const { QFont f; switch (font) { case MTEsans: if (insideMath) { f.setFamily(parent->getFontMathSans()); } else { f.setFamily(parent->getFontSans()); } break; case MTEmathSans: f.setFamily(parent->getFontMathSans()); break; case MTEtypewriter: f.setFamily(parent->getFontTypewriter()); break; case MTEscript: f.setFamily(parent->getFontScript()); break; case MTEcaligraphic: f.setFamily(parent->getFontCaligraphic()); break; case MTEblackboard: f.setFamily(parent->getFontBlackboard()); break; case MTEfraktur: f.setFamily(parent->getFontFraktur()); break; case MTEmathRoman: f.setFamily(parent->getFontMathRoman()); break; default: case MTEroman: if (insideMath) { f.setFamily(parent->getFontMathRoman()); } else { f.setFamily(parent->getFontRoman()); } break; } f.setBold(bold); f.setItalic(italic); f.setUnderline(underlined); f.setOverline(overline); f.setStrikeOut(strike); f.setCapitalization(QFont::MixedCase); if (smallCaps) f.setCapitalization(QFont::SmallCaps); f.setPointSizeF(fontSize); f.setStyleStrategy(QFont::NoFontMerging); return f; } QString JKQTMathTextEnvironment::toHtmlStart(JKQTMathTextEnvironment defaultEv) const { QString s; s=s+"font-size: "+QLocale::c().toString(fontSize)+"pt; "; if (insideMath) { if (defaultEv.italic) { if (!italic) s=s+"font-style: italic; "; if (italic) s=s+"font-style: normal; "; } else { if (!italic) s=s+"font-style: italic; "; } } else { if (!defaultEv.italic && italic) s=s+"font-style: italic; "; } if (bold && !defaultEv.bold) s=s+"font-weight: bold"; QStringList td; if (underlined && !defaultEv.underlined) td<<"underline"; if (overline && !defaultEv.overline) td<<"overline"; if (strike && !defaultEv.strike) td<<"line-through"; if (td.size()>0) s=s+"text-decoration: "+td.join(", "); return ""; } QString JKQTMathTextEnvironment::toHtmlAfter(JKQTMathTextEnvironment /*defaultEv*/) const { return ""; } JKQTMathTextNodeSize::JKQTMathTextNodeSize(): width(0), baselineHeight(0), overallHeight(0), strikeoutPos() { } JKQTMathTextFontDefinition::JKQTMathTextFontDefinition(): fontName("Times New Roman"), fontEncoding(MTFEStandard), symbolfontGreek("Symbol"), symbolfontGreekEncoding(MTFEwinSymbol), symbolfontSymbol("Symbol"), symbolfontSymbolEncoding(MTFEwinSymbol) { } QPainterPath JKQTMathTextMakeDArrow(double x, double y, double width, double arrowW, bool left, bool right) { double x1=x; double x2=x+width; double dx=arrowW/4.0; double y1=y-dx; double y2=y+dx; double x3=x2-arrowW/2.0; double y3u=y-arrowW/2.0; double y3d=y+arrowW/2.0; double x3l=x+arrowW/2.0; QPainterPath path; path.moveTo(x1+dx, y1); path.lineTo(x2-dx, y1); path.moveTo(x1+dx, y2); path.lineTo(x2-dx, y2); if (right) { path.moveTo(x3, y3u); path.lineTo(x2, y); path.lineTo(x3, y3d); } if (left) { path.moveTo(x3l, y3u); path.lineTo(x1, y); path.lineTo(x3l, y3d); } return path; } QPainterPath JKQTMathTextMakeArrow(double x, double y, double width, double arrowW, bool left, bool right) { double x1=x; double x2=x+width; double x3=x2-arrowW/2.0; double y3u=y-arrowW/2.0; double y3d=y+arrowW/2.0; double x3l=x+arrowW/2.0; QPainterPath path; path.moveTo(x1, y); path.lineTo(x2, y); if (right) { path.moveTo(x3, y3u); path.lineTo(x2, y); path.lineTo(x3, y3d); } if (left) { path.moveTo(x3l, y3u); path.lineTo(x1, y); path.lineTo(x3l, y3d); } return path; } QPainterPath JKQTMathTextMakeHBracePath(double x, double ybrace, double width, double bw, double cubicshrink, double cubiccontrolfac) { double xl1=x-(width)*cubicshrink+bw*cubicshrink; double xr2=x+(width)*cubicshrink-bw*cubicshrink; double xl2=x-bw*cubicshrink; double xr1=x+bw*cubicshrink; QPainterPath path; path.moveTo(xl1-bw*cubicshrink, ybrace-bw*cubicshrink); path.cubicTo(xl1-bw*cubicshrink, ybrace-bw*cubicshrink+bw*cubiccontrolfac, xl1-bw*cubiccontrolfac, ybrace, xl1, ybrace); path.lineTo(xl2, ybrace); path.cubicTo(xl2+bw*cubiccontrolfac, ybrace, (xl2+xr1)/2.0, ybrace+bw*cubicshrink-bw*cubiccontrolfac, (xl2+xr1)/2.0, ybrace+bw*cubicshrink); path.cubicTo((xl2+xr1)/2.0, ybrace+bw*cubicshrink-bw*cubiccontrolfac, xr1-bw*cubiccontrolfac, ybrace, xr1, ybrace); path.lineTo(xr2, ybrace); path.cubicTo(xr2+bw*cubiccontrolfac, ybrace, xr2+bw*cubicshrink, ybrace-bw*cubicshrink+bw*cubiccontrolfac, xr2+bw*cubicshrink, ybrace-bw*cubicshrink); return path; } JKQTMathTextTBRData::JKQTMathTextTBRData(const QFont &f, const QString &text, QPaintDevice *pd): fm(f, pd) { this->text=text; this->tbr=this->fm.tightBoundingRect(text); this->f=f; //this->pd=pd; if (pd) { ldpiX=pd->logicalDpiX(); ldpiY=pd->logicalDpiY(); pdpiX=pd->physicalDpiX(); pdpiY=pd->physicalDpiY(); } else { ldpiX=0; ldpiY=0; pdpiX=0; pdpiY=0; } } bool JKQTMathTextTBRData::operator==(const JKQTMathTextTBRData &other) const { return ldpiX==other.ldpiX && ldpiY==other.ldpiY && text==other.text && f==other.f; } JKQTMathTextTBRDataH::JKQTMathTextTBRDataH(const QFont &f, const QString &text, QPaintDevice *pd) { this->text=text; this->f=f; if (pd) { ldpiX=pd->logicalDpiX(); ldpiY=pd->logicalDpiY(); pdpiX=pd->physicalDpiX(); pdpiY=pd->physicalDpiY(); } else { ldpiX=0; ldpiY=0; pdpiX=0; pdpiY=0; } } bool JKQTMathTextTBRDataH::operator==(const JKQTMathTextTBRDataH &other) const { return ldpiX==other.ldpiX && ldpiY==other.ldpiY && text==other.text && f==other.f; } QRectF JKQTMathTextGetTightBoundingRect(const QFont &fm, const QString &text, QPaintDevice *pd) { static QList JKQTMathText_tbrs=QList(); static QHash JKQTMathText_tbrh=QHash(); JKQTMathTextTBRDataH dh(fm, text, pd); if (pd) { if (JKQTMathText_tbrh.contains(dh)) return JKQTMathText_tbrh[dh]; /*for (int i=0; ilogicalDpiX() && tbrs[i].ldpiY==pd->logicalDpiY() && tbrs[i].pdpiX==pd->physicalDpiX() && tbrs[i].pdpiY==pd->physicalDpiY())) { //qDebug()<<" ### "<