diff --git a/application/api/controller/Latex.php b/application/api/controller/Latex.php new file mode 100644 index 0000000..ada2341 --- /dev/null +++ b/application/api/controller/Latex.php @@ -0,0 +1,338 @@ +request->post(); + $rule = new Validate([ + 'p_article_id' => 'require|number' + ]); + + if (!$rule->check($data)) { + return jsonError($rule->getError()); + } + + try { + // 获取文章信息 + $p_info = $this->production_article_obj->where('p_article_id', $data['p_article_id'])->find(); + if (!$p_info) { + return jsonError('文章实例不存在'); + } + + // 获取作者信息 + $authors = $this->production_article_author_obj->where('p_article_id', $data['p_article_id'])->where('state', 0)->select(); + + // 获取机构信息 + $organs = $this->production_article_organ_obj->where('p_article_id', $data['p_article_id'])->where('state', 0)->select(); + + // 获取正文内容 + $main_contents = $this->production_article_main_obj->where('p_article_id', $data['p_article_id'])->where('state', 0)->order('p_main_id')->select(); + + // 获取参考文献 + $references = $this->production_article_refer_obj->where('p_article_id', $data['p_article_id'])->where('state', 0)->order('index')->select(); + + // 构建LaTeX内容 + $latex_content = $this->buildLatexContent($p_info, $authors, $organs, $main_contents, $references); + + // 创建临时目录 + $temp_dir = ROOT_PATH . 'public' . DS . 'temp_latex' . DS; + if (!is_dir($temp_dir)) { + mkdir($temp_dir, 0755, true); + } + + // 生成唯一的文件名 + $filename = 'article_' . $p_info['p_article_id'] . '_' . time(); + $tex_file = $temp_dir . $filename . '.tex'; + $pdf_file = $temp_dir . $filename . '.pdf'; + + // 写入LaTeX文件 + file_put_contents($tex_file, $latex_content); + + // 执行LaTeX编译命令生成PDF + $command = sprintf( + 'cd %s && pdflatex -interaction=nonstopmode -output-directory=%s %s', + escapeshellarg($temp_dir), + escapeshellarg($temp_dir), + escapeshellarg($tex_file) + ); + + // 执行命令 + $output = []; + $return_var = 0; + exec($command, $output, $return_var); + + // 检查PDF是否生成成功 + if (!file_exists($pdf_file) || filesize($pdf_file) == 0) { + return jsonError('PDF生成失败,请检查LaTeX内容或系统环境'); + } + + // 移动PDF到正式目录 + $pdf_dir = ROOT_PATH . 'public' . DS . 'latex_pdfs' . DS; + if (!is_dir($pdf_dir)) { + mkdir($pdf_dir, 0755, true); + } + + $final_pdf_path = $pdf_dir . $filename . '.pdf'; + rename($pdf_file, $final_pdf_path); + + // 清理临时文件 + if (file_exists($tex_file)) { + unlink($tex_file); + } + + // 清理辅助文件(.aux, .log等) + $aux_file = $temp_dir . $filename . '.aux'; + $log_file = $temp_dir . $filename . '.log'; + if (file_exists($aux_file)) unlink($aux_file); + if (file_exists($log_file)) unlink($log_file); + + // 返回PDF路径 + $relative_path = 'latex_pdfs/' . $filename . '.pdf'; + return jsonSuccess([ + 'pdf_url' => $relative_path, + 'message' => 'PDF生成成功' + ]); + + } catch (\Exception $e) { + return jsonError('生成过程中发生错误: ' . $e->getMessage()); + } + } + + /** + * 构建LaTeX文档内容 + * @param $p_info + * @param $authors + * @param $organs + * @param $main_contents + * @param $references + * @return string + */ + private function buildLatexContent($p_info, $authors, $organs, $main_contents, $references) + { + // 构建机构映射 + $organ_map = []; + foreach ($organs as $index => $organ) { + $organ_map[$organ['p_article_organ_id']] = $index + 1; + } + + // LaTeX文档开始 + $latex = '\documentclass[twocolumn]{article}' . "\n"; + $latex .= '\usepackage[utf8]{inputenc}' . "\n"; + $latex .= '\usepackage[T1]{fontenc}' . "\n"; + $latex .= '\usepackage{geometry}' . "\n"; + $latex .= '\usepackage{graphicx}' . "\n"; + $latex .= '\usepackage{amsmath}' . "\n"; + $latex .= '\usepackage{amsfonts}' . "\n"; + $latex .= '\usepackage{amssymb}' . "\n"; + $latex .= '\usepackage{setspace}' . "\n"; + $latex .= '\usepackage{titlesec}' . "\n"; + $latex .= '\usepackage{hyperref}' . "\n"; + $latex .= '\usepackage{caption}' . "\n"; + $latex .= '\usepackage{multicol}' . "\n"; + $latex .= '\usepackage{abstract}' . "\n"; + + $latex .= '\geometry{left=1.5cm,right=1.5cm,top=2.5cm,bottom=2.5cm}' . "\n"; + $latex .= '\setstretch{1.5}' . "\n"; + + $latex .= '\title{' . $this->escapeLatexText($p_info['title']) . '}' . "\n"; + + // 处理作者和机构 + if (!empty($authors)) { + $author_text = ''; + $affiliations = []; + + foreach ($authors as $author) { + if ($author_text !== '') { + $author_text .= ', '; + } + $author_text .= $this->escapeLatexText($author['first_name'] . ' ' . $author['last_name']); + + // 获取作者的机构 + $author_organs = $this->production_article_author_to_organ_obj + ->where('p_article_author_id', $author['p_article_author_id']) + ->where('state', 0) + ->select(); + + foreach ($author_organs as $ao) { + if (isset($organ_map[$ao['p_article_organ_id']])) { + $author_text .= '$^{' . $organ_map[$ao['p_article_organ_id']] . '}$'; + } + } + + if ($author['is_report'] == 1) { + $author_text .= '$^{*}$'; + } + } + + $latex .= '\author{' . $author_text . "\n"; + + // 添加机构信息 + foreach ($organs as $index => $organ) { + $latex .= '\\\\$' . ($index + 1) . '$ ' . $this->escapeLatexText($organ['organ_name']) . "\n"; + } + if (!empty($authors)) { + // 查找通讯作者 + $corresponding_authors = array_filter($authors, function($author) { + return $author['is_report'] == 1; + }); + + if (!empty($corresponding_authors)) { + $corr_author = reset($corresponding_authors); + $latex .= '\\\\$*$ Corresponding author: ' . + $this->escapeLatexText($corr_author['email']) . "\n"; + } + } + + $latex .= '}' . "\n"; + } + + $latex .= '\date{}' . "\n"; + $latex .= '\begin{document}' . "\n"; + $latex .= '\maketitle' . "\n"; + + // 摘要 + if (!empty($p_info['abstract'])) { + $latex .= '\begin{abstract}' . "\n"; + $latex .= $this->escapeLatexText($p_info['abstract']) . "\n"; + $latex .= '\end{abstract}' . "\n"; + } + + // 关键词 + if (!empty($p_info['keywords'])) { + $latex .= '\textbf{Keywords:} ' . $this->escapeLatexText($p_info['keywords']) . "\n"; + $latex .= '\sectionbreak' . "\n"; + } + + // 正文内容 + $in_section = false; + $section_count = 0; + + foreach ($main_contents as $content) { + $text = trim($content['content']); + + // 跳过空内容 + if (empty($text)) { + continue; + } + + // 检查是否是标题 + $clean_text = strip_tags($text); + $lower_text = strtolower($clean_text); + + // 判断是否为章节标题 + if (stripos($lower_text, 'introduction') === 0 || + stripos($lower_text, 'methods') === 0 || + stripos($lower_text, 'results') === 0 || + stripos($lower_text, 'discussion') === 0 || + stripos($lower_text, 'conclusion') === 0 || + stripos($lower_text, 'references') === 0) { + + // 结束前一节 + if ($in_section) { + $latex .= "\n" . '\sectionbreak' . "\n"; + } + + // 开始新节 + $section_count++; + $section_title = $this->escapeLatexText($clean_text); + $latex .= '\section{' . $section_title . '}' . "\n"; + $in_section = true; + } + // 检查是否是图片或表格占位符 + else if (preg_match('/]*imageId=[\'"]([^\'"]+)[\'"][^>]*>/i', $text, $img_matches)) { + $image_id = $img_matches[1]; + // 这里可以添加图片处理逻辑 + $latex .= '% 图片插入位置 (ID: ' . $image_id . ')' . "\n"; + $latex .= '\begin{figure}[htbp]' . "\n"; + $latex .= '\centering' . "\n"; + $latex .= '\includegraphics[width=0.8\textwidth]{image_' . $image_id . '}' . "\n"; + $latex .= '\caption{图片说明}' . "\n"; + $latex .= '\end{figure}' . "\n"; + } + else if (preg_match('/]*tableId=[\'"]([^\'"]+)[\'"][^>]*>/i', $text, $table_matches)) { + $table_id = $table_matches[1]; + // 这里可以添加表格处理逻辑 + $latex .= '% 表格插入位置 (ID: ' . $table_id . ')' . "\n"; + $latex .= '\begin{table}[htbp]' . "\n"; + $latex .= '\centering' . "\n"; + $latex .= '\caption{表格说明}' . "\n"; + $latex .= '\begin{tabular}{|c|c|c|}' . "\n"; + $latex .= '\hline' . "\n"; + $latex .= '列1 & 列2 & 列3 \\\\' . "\n"; + $latex .= '\hline' . "\n"; + $latex .= '% 表格数据' . "\n"; + $latex .= '\hline' . "\n"; + $latex .= '\end{tabular}' . "\n"; + $latex .= '\end{table}' . "\n"; + } + // 普通段落文本 + else { + // 移除HTML标签并转义特殊字符 + $plain_text = $this->escapeLatexText(strip_tags($text)); + $latex .= $plain_text . "\n\n"; + } + } + + // 参考文献 + if (!empty($references)) { + $latex .= '\section*{References}' . "\n"; + $latex .= '\begin{thebibliography}{99}' . "\n"; + + foreach ($references as $index => $ref) { + $ref_text = isset($ref['refer_frag']) ? $ref['refer_frag'] : $ref['refer_content']; + $latex .= '\bibitem{} ' . $this->escapeLatexText($ref_text) . "\n"; + } + + $latex .= '\end{thebibliography}' . "\n"; + } + + $latex .= '\end{document}' . "\n"; + + return $latex; + } + + /** + * 转义LaTeX特殊字符 + * @param string $text + * @return string + */ + private function escapeLatexText($text) + { + // 移除HTML标签 + $text = strip_tags($text); + + // 转义LaTeX特殊字符 + $escape_chars = [ + '\\' => '\\textbackslash{}', + '&' => '\\&', + '%' => '\\%', + '$' => '\\$', + '#' => '\\#', + '_' => '\\_', + '{' => '\\{', + '}' => '\\}', + '~' => '\\textasciitilde{}', + '^' => '\\textasciicircum{}', + '"' => '\\"{}', + '\'' => '\\\'{}', + '<' => '\\textless{}', + '>' => '\\textgreater{}', + ]; + + foreach ($escape_chars as $char => $replacement) { + $text = str_replace($char, $replacement, $text); + } + + return $text; + } + + +} \ No newline at end of file diff --git a/application/api/controller/Production.php b/application/api/controller/Production.php index 5d9cac8..769f20f 100644 --- a/application/api/controller/Production.php +++ b/application/api/controller/Production.php @@ -1250,338 +1250,6 @@ class Production extends Base // return jsonSuccess([]); // } -/** - * 生成LaTeX文档并转换为PDF - * @return \think\response\Json - */ -public function generateLatexPdf() -{ - $data = $this->request->post(); - $rule = new Validate([ - 'p_article_id' => 'require|number' - ]); - - if (!$rule->check($data)) { - return jsonError($rule->getError()); - } - - try { - // 获取文章信息 - $p_info = $this->production_article_obj->where('p_article_id', $data['p_article_id'])->find(); - if (!$p_info) { - return jsonError('文章实例不存在'); - } - - // 获取作者信息 - $authors = $this->production_article_author_obj->where('p_article_id', $data['p_article_id'])->where('state', 0)->select(); - - // 获取机构信息 - $organs = $this->production_article_organ_obj->where('p_article_id', $data['p_article_id'])->where('state', 0)->select(); - - // 获取正文内容 - $main_contents = $this->production_article_main_obj->where('p_article_id', $data['p_article_id'])->where('state', 0)->order('p_main_id')->select(); - - // 获取参考文献 - $references = $this->production_article_refer_obj->where('p_article_id', $data['p_article_id'])->where('state', 0)->order('index')->select(); - - // 构建LaTeX内容 - $latex_content = $this->buildLatexContent($p_info, $authors, $organs, $main_contents, $references); - - // 创建临时目录 - $temp_dir = ROOT_PATH . 'public' . DS . 'temp_latex' . DS; - if (!is_dir($temp_dir)) { - mkdir($temp_dir, 0755, true); - } - - // 生成唯一的文件名 - $filename = 'article_' . $p_info['p_article_id'] . '_' . time(); - $tex_file = $temp_dir . $filename . '.tex'; - $pdf_file = $temp_dir . $filename . '.pdf'; - - // 写入LaTeX文件 - file_put_contents($tex_file, $latex_content); - - // 执行LaTeX编译命令生成PDF - $command = sprintf( - 'cd %s && pdflatex -interaction=nonstopmode -output-directory=%s %s', - escapeshellarg($temp_dir), - escapeshellarg($temp_dir), - escapeshellarg($tex_file) - ); - - // 执行命令 - $output = []; - $return_var = 0; - exec($command, $output, $return_var); - - // 检查PDF是否生成成功 - if (!file_exists($pdf_file) || filesize($pdf_file) == 0) { - return jsonError('PDF生成失败,请检查LaTeX内容或系统环境'); - } - - // 移动PDF到正式目录 - $pdf_dir = ROOT_PATH . 'public' . DS . 'latex_pdfs' . DS; - if (!is_dir($pdf_dir)) { - mkdir($pdf_dir, 0755, true); - } - - $final_pdf_path = $pdf_dir . $filename . '.pdf'; - rename($pdf_file, $final_pdf_path); - - // 清理临时文件 - if (file_exists($tex_file)) { - unlink($tex_file); - } - - // 清理辅助文件(.aux, .log等) - $aux_file = $temp_dir . $filename . '.aux'; - $log_file = $temp_dir . $filename . '.log'; - if (file_exists($aux_file)) unlink($aux_file); - if (file_exists($log_file)) unlink($log_file); - - // 返回PDF路径 - $relative_path = 'latex_pdfs/' . $filename . '.pdf'; - return jsonSuccess([ - 'pdf_url' => $relative_path, - 'message' => 'PDF生成成功' - ]); - - } catch (\Exception $e) { - return jsonError('生成过程中发生错误: ' . $e->getMessage()); - } -} - -/** - * 构建LaTeX文档内容 - * @param $p_info - * @param $authors - * @param $organs - * @param $main_contents - * @param $references - * @return string - */ -private function buildLatexContent($p_info, $authors, $organs, $main_contents, $references) -{ - // 构建机构映射 - $organ_map = []; - foreach ($organs as $index => $organ) { - $organ_map[$organ['p_article_organ_id']] = $index + 1; - } - - // LaTeX文档开始 - $latex = '\documentclass[twocolumn]{article}' . "\n"; - $latex .= '\usepackage[utf8]{inputenc}' . "\n"; - $latex .= '\usepackage[T1]{fontenc}' . "\n"; - $latex .= '\usepackage{geometry}' . "\n"; - $latex .= '\usepackage{graphicx}' . "\n"; - $latex .= '\usepackage{amsmath}' . "\n"; - $latex .= '\usepackage{amsfonts}' . "\n"; - $latex .= '\usepackage{amssymb}' . "\n"; - $latex .= '\usepackage{setspace}' . "\n"; - $latex .= '\usepackage{titlesec}' . "\n"; - $latex .= '\usepackage{hyperref}' . "\n"; - $latex .= '\usepackage{caption}' . "\n"; - $latex .= '\usepackage{multicol}' . "\n"; - $latex .= '\usepackage{abstract}' . "\n"; - - $latex .= '\geometry{left=1.5cm,right=1.5cm,top=2.5cm,bottom=2.5cm}' . "\n"; - $latex .= '\setstretch{1.5}' . "\n"; - - $latex .= '\title{' . $this->escapeLatexText($p_info['title']) . '}' . "\n"; - - // 处理作者和机构 - if (!empty($authors)) { - $author_text = ''; - $affiliations = []; - - foreach ($authors as $author) { - if ($author_text !== '') { - $author_text .= ', '; - } - $author_text .= $this->escapeLatexText($author['first_name'] . ' ' . $author['last_name']); - - // 获取作者的机构 - $author_organs = $this->production_article_author_to_organ_obj - ->where('p_article_author_id', $author['p_article_author_id']) - ->where('state', 0) - ->select(); - - foreach ($author_organs as $ao) { - if (isset($organ_map[$ao['p_article_organ_id']])) { - $author_text .= '$^{' . $organ_map[$ao['p_article_organ_id']] . '}$'; - } - } - - if ($author['is_report'] == 1) { - $author_text .= '$^{*}$'; - } - } - - $latex .= '\author{' . $author_text . "\n"; - - // 添加机构信息 - foreach ($organs as $index => $organ) { - $latex .= '\\\\$' . ($index + 1) . '$ ' . $this->escapeLatexText($organ['organ_name']) . "\n"; - } - if (!empty($authors)) { - // 查找通讯作者 - $corresponding_authors = array_filter($authors, function($author) { - return $author['is_report'] == 1; - }); - - if (!empty($corresponding_authors)) { - $corr_author = reset($corresponding_authors); - $latex .= '\\\\$*$ Corresponding author: ' . - $this->escapeLatexText($corr_author['email']) . "\n"; - } - } - - $latex .= '}' . "\n"; - } - - $latex .= '\date{}' . "\n"; - $latex .= '\begin{document}' . "\n"; - $latex .= '\maketitle' . "\n"; - - // 摘要 - if (!empty($p_info['abstract'])) { - $latex .= '\begin{abstract}' . "\n"; - $latex .= $this->escapeLatexText($p_info['abstract']) . "\n"; - $latex .= '\end{abstract}' . "\n"; - } - - // 关键词 - if (!empty($p_info['keywords'])) { - $latex .= '\textbf{Keywords:} ' . $this->escapeLatexText($p_info['keywords']) . "\n"; - $latex .= '\sectionbreak' . "\n"; - } - - // 正文内容 - $in_section = false; - $section_count = 0; - - foreach ($main_contents as $content) { - $text = trim($content['content']); - - // 跳过空内容 - if (empty($text)) { - continue; - } - - // 检查是否是标题 - $clean_text = strip_tags($text); - $lower_text = strtolower($clean_text); - - // 判断是否为章节标题 - if (stripos($lower_text, 'introduction') === 0 || - stripos($lower_text, 'methods') === 0 || - stripos($lower_text, 'results') === 0 || - stripos($lower_text, 'discussion') === 0 || - stripos($lower_text, 'conclusion') === 0 || - stripos($lower_text, 'references') === 0) { - - // 结束前一节 - if ($in_section) { - $latex .= "\n" . '\sectionbreak' . "\n"; - } - - // 开始新节 - $section_count++; - $section_title = $this->escapeLatexText($clean_text); - $latex .= '\section{' . $section_title . '}' . "\n"; - $in_section = true; - } - // 检查是否是图片或表格占位符 - else if (preg_match('/]*imageId=[\'"]([^\'"]+)[\'"][^>]*>/i', $text, $img_matches)) { - $image_id = $img_matches[1]; - // 这里可以添加图片处理逻辑 - $latex .= '% 图片插入位置 (ID: ' . $image_id . ')' . "\n"; - $latex .= '\begin{figure}[htbp]' . "\n"; - $latex .= '\centering' . "\n"; - $latex .= '\includegraphics[width=0.8\textwidth]{image_' . $image_id . '}' . "\n"; - $latex .= '\caption{图片说明}' . "\n"; - $latex .= '\end{figure}' . "\n"; - } - else if (preg_match('/]*tableId=[\'"]([^\'"]+)[\'"][^>]*>/i', $text, $table_matches)) { - $table_id = $table_matches[1]; - // 这里可以添加表格处理逻辑 - $latex .= '% 表格插入位置 (ID: ' . $table_id . ')' . "\n"; - $latex .= '\begin{table}[htbp]' . "\n"; - $latex .= '\centering' . "\n"; - $latex .= '\caption{表格说明}' . "\n"; - $latex .= '\begin{tabular}{|c|c|c|}' . "\n"; - $latex .= '\hline' . "\n"; - $latex .= '列1 & 列2 & 列3 \\\\' . "\n"; - $latex .= '\hline' . "\n"; - $latex .= '% 表格数据' . "\n"; - $latex .= '\hline' . "\n"; - $latex .= '\end{tabular}' . "\n"; - $latex .= '\end{table}' . "\n"; - } - // 普通段落文本 - else { - // 移除HTML标签并转义特殊字符 - $plain_text = $this->escapeLatexText(strip_tags($text)); - $latex .= $plain_text . "\n\n"; - } - } - - // 参考文献 - if (!empty($references)) { - $latex .= '\section*{References}' . "\n"; - $latex .= '\begin{thebibliography}{99}' . "\n"; - - foreach ($references as $index => $ref) { - $ref_text = isset($ref['refer_frag']) ? $ref['refer_frag'] : $ref['refer_content']; - $latex .= '\bibitem{} ' . $this->escapeLatexText($ref_text) . "\n"; - } - - $latex .= '\end{thebibliography}' . "\n"; - } - - $latex .= '\end{document}' . "\n"; - - return $latex; -} - -/** - * 转义LaTeX特殊字符 - * @param string $text - * @return string - */ -private function escapeLatexText($text) -{ - // 移除HTML标签 - $text = strip_tags($text); - - // 转义LaTeX特殊字符 - $escape_chars = [ - '\\' => '\\textbackslash{}', - '&' => '\\&', - '%' => '\\%', - '$' => '\\$', - '#' => '\\#', - '_' => '\\_', - '{' => '\\{', - '}' => '\\}', - '~' => '\\textasciitilde{}', - '^' => '\\textasciicircum{}', - '"' => '\\"{}', - '\'' => '\\\'{}', - '<' => '\\textless{}', - '>' => '\\textgreater{}', - ]; - - foreach ($escape_chars as $char => $replacement) { - $text = str_replace($char, $replacement, $text); - } - - return $text; -} - - - - public function addProductTopic(){ $data = $this->request->post(); $rule = new Validate([ @@ -1705,8 +1373,8 @@ private function escapeLatexText($text) $typesetInfo['website'] = $journal_info['website']; $typesetInfo['acknowledgment'] = $p_info['acknowledgment']; - $received_info = $this->article_msg_obj->where("article_id",$p_info['article_id'])->where("state_from",4)->where("state_to",1)->find(); - $received_data = $received_info?$received_info['ctime']:$article_info['ctime']; +// $received_info = $this->article_msg_obj->where("article_id",$p_info['article_id'])->where("state_from",4)->where("state_to",1)->find(); + $received_data = $article_info['received_time']>0?$article_info['received_time']:$article_info['ctime']; $typesetInfo['received_date'] = date("d F Y", $received_data); $super_num = $this->article_author_obj->where("article_id",$p_info['article_id'])->where("state",0)->where("is_super",1)->count();