194 lines
6.2 KiB
C++
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 = ")
|
|
{
|
|
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;
|
|
} |