#include "mathtext.hpp" #include "latinmodern-fonts.hpp" #include #include #include #include #include namespace ZShell::llm { namespace { // A few px of breathing room around the equation. constexpr int kRenderMargin = 2; constexpr unsigned int kResolutionDpi = 96; // Registers the embedded Latin Modern faces with the font database // (once per process) and reports which families became available. struct LatinModern { bool roman = false; bool math = false; }; const LatinModern& loadLatinModern() { static const LatinModern fonts = [] { LatinModern result; // addApplicationFont(QByteArray) fails in this environment, so // the embedded bytes are staged to a per-process temp file and // registered through the (stable) file-based API. const auto add = [](const unsigned char* data, size_t size, const QString& fileName, const QString& family) { const QString path = QDir::tempPath() + QLatin1Char('/') + fileName; { QFile f(path); if (!f.open(QIODevice::WriteOnly) || f.write(reinterpret_cast(data), static_cast(size)) != static_cast(size)) return false; } const int key = QFontDatabase::addApplicationFont(path); if (key < 0) return false; return QFontDatabase::applicationFontFamilies(key).contains(family); }; result.roman = add(lmfont::lmroman10_regular, sizeof(lmfont::lmroman10_regular), QStringLiteral("lmroman10-regular.otf"), QStringLiteral("LMRoman10")) && add(lmfont::lmroman10_italic, sizeof(lmfont::lmroman10_italic), QStringLiteral("lmroman10-italic.otf"), QStringLiteral("LMRoman10")) && add(lmfont::lmroman10_bold, sizeof(lmfont::lmroman10_bold), QStringLiteral("lmroman10-bold.otf"), QStringLiteral("LMRoman10")) && add(lmfont::lmroman10_bolditalic, sizeof(lmfont::lmroman10_bolditalic), QStringLiteral("lmroman10-bolditalic.otf"), QStringLiteral("LMRoman10")); result.math = add( lmfont::latinmodern_math, sizeof(lmfont::latinmodern_math), QStringLiteral("latinmodern-math.otf"), QStringLiteral("Latin Modern Math")); return result; }(); return fonts; } } // namespace LlmMathText::LlmMathText(QObject* parent) : QObject(parent), m_renderer(parent, /* useFontsForGUI */ true) { // Latin Modern is the default font of modern LaTeX; use the embedded // faces instead of whatever the system happens to have installed. const LatinModern& fonts = loadLatinModern(); if (fonts.roman) m_renderer.setFontRomanAndMath(QStringLiteral("LMRoman10"), JKQTMathTextFontEncoding::MTFEUnicode); if (fonts.math) { // Same pattern as JKQTMathText's useXITS(): the OpenType math // font supplies the math alphabet and operators from its MATH // table. m_renderer.setFontMathRoman(QStringLiteral("Latin Modern Math"), JKQTMathTextFontEncoding::MTFEUnicode); m_renderer.setFallbackFontSymbols( QStringLiteral("Latin Modern Math"), JKQTMathTextFontEncoding::MTFEUnicode); } } void LlmMathText::setLatex(const QString& value) { if (m_latex == value) return; m_latex = value; reRender(); } void LlmMathText::setColor(const QColor& value) { if (m_color == value) return; m_color = value; reRender(); } void LlmMathText::setFontPointSize(double value) { if (qFuzzyCompare(m_fontPointSize, value)) return; m_fontPointSize = value; reRender(); } void LlmMathText::setDevicePixelRatio(qreal value) { if (qFuzzyCompare(m_devicePixelRatio, value)) return; m_devicePixelRatio = value; reRender(); } void LlmMathText::reRender() { if (m_latex.trimmed().isEmpty()) { m_image = QImage(); m_imageUrl = QUrl(); m_width = 0; m_height = 0; m_ok = false; Q_EMIT changed(); return; } m_renderer.setFontPointSize(m_fontPointSize); m_renderer.setFontColor(m_color); const bool ok = m_renderer.parse( m_latex, JKQTMathText::LatexParser, JKQTMathText::DefaultParseOptions); if (!ok) { m_image = QImage(); m_imageUrl = QUrl(); m_width = 0; m_height = 0; m_ok = false; Q_EMIT changed(); return; } const QImage image = m_renderer.drawIntoImage( /* drawBoxes */ false, QColor(Qt::transparent), kRenderMargin, m_devicePixelRatio, kResolutionDpi); if (image.isNull()) { m_image = QImage(); m_imageUrl = QUrl(); m_width = 0; m_height = 0; m_ok = false; Q_EMIT changed(); return; } m_image = image; QByteArray png; { QBuffer buffer(&png); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "PNG"); } m_imageUrl = QUrl( QStringLiteral("data:image/png;base64,") + QString::fromLatin1(png.toBase64())); // drawIntoImage renders at devicePixelRatio; convert back to // logical pixels. m_width = image.width() / m_devicePixelRatio; m_height = image.height() / m_devicePixelRatio; m_ok = true; Q_EMIT changed(); } } // namespace ZShell::llm