ctai/src/ctaiMathConvert.cpp

194 lines
6.2 KiB
C++

#include "ctaiMathConvert.h"
ctaiMathConvert::ctaiMathConvert()
{
}
ctaiMathConvert::~ctaiMathConvert()
{
}
void ctaiMathConvert::set_convert_opts()
{
}
void ctaiMathConvert::save_svg(QPixmap &img)
{
QString uid = QUuid::createUuid().toString(QUuid::WithoutBraces);
QString svgName = QDir::currentPath() + "/svg/" + uid + ".png";
bool ok = img.save(svgName);
}
QByteArray ctaiMathConvert::svg_to_base64(QPixmap &pix)
{
QByteArray data;
QBuffer buffer(&data);
QImage img = pix.toImage();
img.save(&buffer, "PNG");
data = data.toBase64();
return data;
}
QString ctaiMathConvert::math_convert_svg(const QString &math)
{
QString res = QDir::currentPath() + "/res/";
auto render = LaTeX::parse(math.toStdWString(), m_render_width, m_text_size, m_lineSpace, m_color);
QPixmap pix(render->getWidth() + m_padding * 2, render->getHeight() + m_padding * 2);
pix.fill(Qt::white);
QPainter painter(&pix);
painter.setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing);
Graphics2D_qt g2(&painter);
render->draw(g2, m_padding, m_padding);
// save_svg(pix);
return QString::fromUtf8(svg_to_base64(pix));
}
void ctaiMathConvert::debug_latex_match(const QString &text, const QRegularExpressionMatch &match)
{
qDebug() << "=== LaTeX匹配信息 ===";
qDebug() << "匹配位置:" << match.capturedStart() << "-" << match.capturedEnd();
qDebug() << "匹配文本:" << match.captured(0);
qDebug() << "数学内容:" << match.captured(1);
// 显示匹配位置的上下文
int contextSize = 20;
int start = qMax(0, match.capturedStart() - contextSize);
int end = qMin(text.length(), match.capturedEnd() + contextSize);
QString context = text.mid(start, end - start);
qDebug() << "上下文:" << context;
qDebug() << "==================";
}
QString ctaiMathConvert::clean_latex_for_mula(const QString &formula)
{
QString result = formula;
// 清理可能的多余空白字符
result = result.trimmed();
// 处理可能的换行符
result.replace(QRegularExpression(R"(\s+)"), " ");
return result;
}
QString ctaiMathConvert::replace_tags_svg(const QString &text, int font_size)
{
m_text_size = font_size * 1.5;
QString result = text;
// 使用改进的正则表达式模式
QRegularExpression latexPattern(
QString(R"(\\[(](.*?)\\[)]|\\[{](.*?)\\[}]|\\[[](.*?)\\])"),
QRegularExpression::MultilineOption |
QRegularExpression::DotMatchesEverythingOption);
// 添加调试信息
qDebug() << "输入文本:" << result;
qDebug() << "正则表达式:" << latexPattern.pattern();
qDebug() << "正则表达式是否有效:" << latexPattern.isValid();
QRegularExpressionMatchIterator iterator = latexPattern.globalMatch(result);
// 收集所有替换操作
QList<QPair<int, int>> replacements;
QStringList svgResults;
// 定义基础图片标签
const QString markdown_base = "![math](data:image/png;base64,";
while (iterator.hasNext())
{
QRegularExpressionMatch match = iterator.next();
QString matchedText = match.captured(0);
if (debug_node_print)
{
debug_latex_match(result, match);
}
// 清理和转换公式
QString cleanFormula = clean_latex_for_mula(matchedText);
QString svg_markdown = markdown_base + math_convert_svg(cleanFormula) + ")";
// 保存替换信息
replacements.prepend({match.capturedStart(), match.capturedLength()});
svgResults.prepend(svg_markdown);
}
// 从后向前执行替换,避免位置改变影响
for (int i = 0; i < replacements.size(); ++i)
{
const auto &rep = replacements[i];
result.replace(rep.first, rep.second, svgResults[i]);
}
return markdown_to_html(result);
}
QString ctaiMathConvert::markdown_to_html(const QString &text)
{
// 转换为UTF-8编码的字符串
QByteArray markdown = text.toUtf8();
// 启用所有 GFM 扩展选项
int options = CMARK_OPT_SOURCEPOS |
CMARK_OPT_VALIDATE_UTF8 |
CMARK_OPT_HARDBREAKS |
CMARK_OPT_SMART |
CMARK_OPT_GITHUB_PRE_LANG |
CMARK_OPT_LIBERAL_HTML_TAG |
CMARK_OPT_FOOTNOTES |
CMARK_OPT_STRIKETHROUGH_DOUBLE_TILDE |
CMARK_OPT_TABLE_PREFER_STYLE_ATTRIBUTES |
CMARK_NODE_TEXT|
CMARK_OPT_FULL_INFO_STRING;
// 解析Markdown为AST
cmark_node *doc = cmark_parse_document(markdown.constData(), markdown.size(), options);
if (debug_node_print)
{
// 递归打印AST节点
print_ast_node(doc, 0);
}
// 转换为HTML
char *html = cmark_render_html(doc, options, NULL);
QString result = QString::fromUtf8(html);
replace_symbol(result);
// 释放内存
cmark_node_free(doc);
free(html);
replace_css(result);
return fix_img_str_line_height(result.toUtf8());
}
void ctaiMathConvert::replace_css(QString& context){
context.replace("<li", "<li class=\"custom-height\"");
}
void ctaiMathConvert::replace_symbol(QString &context)
{
// 执行转换符号为半角
for (auto it = fullToHalfMap.begin(); it != fullToHalfMap.end(); ++it)
{
context.replace(it.key(), it.value());
}
}
// 添加新的辅助方法用于打印AST节点
void ctaiMathConvert::print_ast_node(cmark_node *node, int level)
{
if (!node)
return;
// 获取节点类型
const char *type_str = cmark_node_get_type_string(node);
// 打印缩进和节点类型
QString indent = QString(" ").repeated(level * 2);
qDebug() << indent << "Node Type:" << type_str;
// 获取并打印节点内容(如果有)
const char *literal = cmark_node_get_literal(node);
if (literal)
{
qDebug() << indent << "Content:" << literal;
}
// 递归处理子节点
cmark_node *child = cmark_node_first_child(node);
while (child)
{
print_ast_node(child, level + 1);
child = cmark_node_next(child);
}
}
QString ctaiMathConvert::fix_img_str_line_height(QString html)
{
// 修正图片样式,使其与文本在同一行显示
html.replace("<img ", "<img style='vertical-align: middle ;'");
return html;
}