From 67dc74575d6dac8e4fbf73c447f1960d1804d58e Mon Sep 17 00:00:00 2001 From: wangjinlei <751475802@qq.com> Date: Sun, 18 Jan 2026 17:23:25 +0800 Subject: [PATCH] latex update --- application/api/controller/Production.php | 411 +++++++++++++++++++++- 1 file changed, 402 insertions(+), 9 deletions(-) diff --git a/application/api/controller/Production.php b/application/api/controller/Production.php index 6d5d5b2..c2f7194 100644 --- a/application/api/controller/Production.php +++ b/application/api/controller/Production.php @@ -789,6 +789,9 @@ class Production extends Base if ($p_info['file_sub2'] != '') { $pra['file_sub2'] = $p_info['file_sub2']; } + if($p_info['file_sub_table'] != ''){ + $pra['file_sub_table'] = $p_info['file_sub_table']; + } if ($p_info['file_cdf'] != '') { $pra['file_cdf'] = $p_info['file_cdf']; } @@ -2150,6 +2153,9 @@ class Production extends Base $latexLines[] = " \\centering"; $latexLines[] = " \\includegraphics[width=0.9\\textwidth]{" . $fileName . "}"; } + //添加图片的label + $latexLines[] = " \\label{fig:" . $ami_id . "}"; + if(!empty($ami_info['title'])&&!empty($ami_info['note'])){ $escapedTitle = '{\fontspec{Calibri}\footnotesize\bfseries\color{figerTitleColor} '.$this->convertToLatex(preg_replace('/^Figure\s+\d+\s*/i', '', $ami_info['title']),[]).'}\\\\'; @@ -2239,7 +2245,7 @@ class Production extends Base - public function convertToLatex($content,$references) { + public function convertToLatex($content,$references,$check_refer = true) { // 空内容直接返回 if (empty(trim($content))) { return ''; @@ -2271,7 +2277,7 @@ class Production extends Base $content = $this->removeIgnoredTags($content); // 步骤2:解析剩余标签并生成LaTeX内容 - $latexContent = $this->textRenderCreate($content,$refArray); + $latexContent = $this->textRenderCreate($content,$refArray,$check_refer); // $latexContent = $this->convertReferencesToLatex($content, $refArray); return $latexContent; @@ -2296,7 +2302,7 @@ class Production extends Base * @param string $content 待解析的内容 * @return string 解析后的LaTeX内容 */ - private function textRenderCreate($content,$references) { + private function textRenderCreate($content,$references,$check_refer) { $latex = ''; while (!empty($content)) { $nextTag = $this->determineNextTag($content); @@ -2332,7 +2338,7 @@ class Production extends Base //这里处理引用 - if (preg_match('/(?:)?\[(\d+(?:[-,]\d+)*)\](?:<\/blue>)?/', $tagWrappedContent, $matches)) { + if ($check_refer&&preg_match('/(?:)?\[(\d+(?:[-,]\d+)*)\](?:<\/blue>)?/', $tagWrappedContent, $matches)) { // 去除匹配中的 $cleanedMatch = str_replace(['', ''], '', $matches[0]); // 提取引用编号部分(去掉方括号) @@ -2473,6 +2479,39 @@ class Production extends Base $styledContent = $this->applyStyleToContent($latexContent, $style); $textRenderData[] = $styledContent; } + }elseif ($tag === 'myfigure'){//对图片的正文处理 + $pattern = '/data-id=["\'](\d+)["\']/'; + if (preg_match($pattern, $now, $matches)) { + $figureId = $matches[1]; + //处理查找到id之后的逻辑 + $textRenderData[] = "\\ref{fig:".$figureId."}"; + } + }elseif ($tag === 'mytable'){//对表格的正文处理 + $pattern = '/data-id=["\'](\d+)["\']/'; + if (preg_match($pattern, $now, $matches)) { + $tableId = $matches[1]; + $cache_table = $this->article_main_table_obj->where("amt_id",$tableId)->find(); + if(!$cache_table){ + return ; + } + $cache_list = $this->article_main_obj + ->join("t_article_main_table","t_article_main_table.amt_id=t_article_main.amt_id","left") + ->where("t_article_main.article_id",$cache_table['article_id']) + ->where("t_article_main.type",2) + ->where("CHAR_LENGTH(t_article_main_table.table_data)",">",6000) + ->order("t_article_main.sort")->select(); + $key_num = 0; + foreach ($cache_list as $cache_key =>$cache_list_item){ + if($cache_list_item['amt_id']==$tableId){ + $key_num = $cache_key+1; + } + } + if(strlen($cache_table['table_data'])>6000){ + $textRenderData[] = "\\textcolor[HTML]{".$this->colorMap['blue']."}{Supplementary Table ".$key_num."}}"; + }else{ + $textRenderData[] = "\\ref{tab:".$tableId."}"; + } + } }else{ // 应用当前标签的样式 $this->addStyle($style, $tag); @@ -3330,10 +3369,323 @@ class Production extends Base } + public function mytestlongtable(){ + $data = $this->request->post(); + $rule = new Validate([ + "table_id"=>"require" + ]); + if(!$rule->check($data)){ + return jsonError($rule->getError()); + } + $res = $this->longTablePdfCreate([$data['table_id'], $data['table_id']+1]); + return json($res); + } + + public function longTablePdfCreate(array $table_ids) + { + + // 获取表格数据 + $table_info_f = $this->article_main_table_obj->where("amt_id", $table_ids[0])->find(); + if (!$table_info_f) { + return jsonError("Table not found"); + } + $article_id = $table_info_f['article_id']; + $production_info = $this->production_article_obj->where("article_id", $article_id)->find(); + if (!$production_info) { + return jsonError("Production not found"); + } + $references = $this->production_article_refer_obj->where("p_article_id", $production_info['p_article_id'])->select(); + // 解析表格数据 +// $tableData = json_decode($table_info['table_data'], true); +// if (!$tableData) { +// return jsonError("Invalid table data"); +// } + +// $latex = []; + $tex_content = "\\documentclass[8pt]{article}\n"; + $tex_content .= "\\usepackage[UTF8]{ctex} % 设置字体\n"; + $tex_content .= "\\usepackage{tabularray} % longtblr核心宏包\n"; + $tex_content .= "\\usepackage{xcolor} % 颜色支持\n\n"; + $tex_content .= "\\usepackage{geometry} \n"; + $tex_content .= "\\usepackage{textcomp} \n"; + $tex_content .= "\\usepackage[english]{babel} \n"; + $tex_content .= "\\geometry{ + a4paper, % A4纸张 + left=1.5cm, % 左页边距 + right=1.5cm, % 右页边距 + top=2cm, % 上页边距 + bottom=2cm, % 下页边距 + }"; + $tex_content .= "\\linespread{1.05}"; + $tex_content .= "\\definecolor{tablegray}{RGB}{250,231,232}\n\n"; + $tex_content .= "\\begin{document}\n\n"; + foreach ($table_ids as $value){ + $latex = []; + $table_info = $this->article_main_table_obj->where("amt_id", $value)->find(); + $tableData = json_decode($table_info['table_data'], true); + if (!$tableData) { + continue; + } + $maxColCount = 0; + $str_list = []; + foreach ($tableData as $row) { + $colCount = 0; + foreach ($row as $cell) { + $colCount += $cell['colspan']; // 跨列会占用多个列位置 + if($cell['colspan']==1){ + $colIndex = $colCount; // 当前列的索引 + + // 计算当前单元格的字符数 + $cellTextLength = strlen(strip_tags($cell['text'])); // 或者使用 mb_strlen($cell['text'], 'UTF-8') 来处理多字节字符 + + if(isset($str_list[$colIndex])){ + $str_list[$colIndex] = max($cellTextLength, $str_list[$colIndex]); + } else { + $str_list[$colIndex] = $cellTextLength; + } + } + } + if ($colCount > $maxColCount) { + $maxColCount = $colCount; + } + } + + // 根据 $str_list 计算列宽比例(比例范围:0.2-1) + $columnSpec = ''; + if (!empty($str_list)) { + // 找到最大字符数,用于比例计算 + $maxTextLength = max($str_list); + + if ($maxTextLength > 0) { + // 为每一列计算相对宽度(比例范围:0.2-1) + for ($i = 1; $i <= $maxColCount; $i++) { + if (isset($str_list[$i]) && $str_list[$i] > 0) { + // 计算当前列的相对比例(0-1之间) + $relativeRatio = round($str_list[$i] / $maxTextLength, 1); + // 将比例调整到0.2-1范围内 + $adjustedRatio = max(0.3, min(1, $relativeRatio)); + // 转换为整数(乘以10便于处理小数) +// $widthValue = round($adjustedRatio * 10); + $columnSpec .= "X[$adjustedRatio] "; + } else { + // 如果某列没有数据,给一个默认最小宽度(对应0.2比例) + $columnSpec .= "X[0.3] "; // 0.2 * 10 = 2 + } + } + } else { + // 如果没有有效数据,使用平均分配 + $columnSpec = str_repeat('X[1] ', $maxColCount); + } + } else { + // 如果 $str_list 为空,使用平均分配 + $columnSpec = str_repeat('X[1] ', $maxColCount); + } + // 整理title信息 + $title = strip_tags($table_info['title']); + $label = "tab-".$table_info['amt_id']; + // 开始构建LaTeX代码 +// $latex = []; + + // 遍历表格数据,生成每一行 + $table = []; + foreach ($tableData as $rowIndex => $row) { + $table[$rowIndex] = []; + $currentCol = 0; // 记录当前处理到的列位置 + + // 处理当前行的每个单元格 + foreach ($row as $cell) { + while (isset($table[$rowIndex][$currentCol])){ + $currentCol += 1; + } + // 处理单元格文本:移除HTML标签,替换为LaTeX粗体 + $text = $this->convertToLatex($cell['text'], $references,false); + + // 处理合并单元格:跨列(colspan)和跨行(rowspan) + $colspan = $cell['colspan'] ?? 1; + $rowspan = $cell['rowspan'] ?? 1; + + $cellContent = $text; + + // 处理跨列(使用tabularray的c选项,跨多列) + if ($colspan > 1 && $rowspan > 1) { + // 跨行跨列 + $cellContent = "\\SetCell[c={$colspan},r={$rowspan}]{l,bg=white} {$cellContent}"; + $table[$rowIndex][$currentCol] = $cellContent; + for($n_c=1;$n_c<$colspan;$n_c++){ + $table[$rowIndex][$currentCol+$n_c] = ""; + } + for ($a_r=1;$a_r<$rowspan;$a_r++){ + for ($a_c=0;$a_c<$colspan;$a_c++){ + $table[$rowIndex+$a_r][$currentCol+$a_c] = ""; + } + } + + } elseif ($colspan > 1) { + // 仅跨列 + $cellContent = "\\SetCell[c={$colspan}]{l,bg=white} {$cellContent}"; + $table[$rowIndex][$currentCol] = $cellContent; + for($n_c=1;$n_c<$colspan;$n_c++){ + $table[$rowIndex][$currentCol+$n_c] = ""; + } + } elseif ($rowspan > 1) { + // 仅跨行 + $cellContent = "\\SetCell[r={$rowspan}]{l,bg=white} {$cellContent}"; + $table[$rowIndex][$currentCol] = $cellContent; + for ($a_r=1;$a_r<$rowspan;$a_r++){ + $table[$rowIndex+$a_r][$currentCol] = ""; + } + }else{ + $table[$rowIndex][$currentCol] = $cellContent; + } + // 如果是跨列单元格,跳过后续的colspan-1个位置 + $currentCol += $colspan; + } + } + foreach ($table as $row){ + $rowContent = implode(' & ', $row) . " \\\\"; + $latex[] = " {$rowContent}"; + } + $tex_content .= "\t\\begin{longtblr}[\n"; + $tex_content .= "\t\tcaption = {" . $title . "},\n"; + $tex_content .= "\t\tlabel = {" . $label . "},\n"; + $tex_content .= "\t\t]{\n"; + $tex_content .= "\t\t\t% 你指定的列格式:自定义宽度比例 + 左对齐 + 防止文字溢出\n"; + $tex_content .= "\t\t\tcolspec={" . $columnSpec . "},\n"; + $tex_content .= "\t\t\twidth = \\textwidth, % 表格宽度占满行宽(配合X列生效)\n"; + $tex_content .= "\t\t\trowhead = 1, % 表头跨页重复\n"; + $tex_content .= "\t\t\t% 三线表核心配置\n"; + $tex_content .= "\t\t\thline{1} = {1pt}, % 顶线(粗)\n"; + $tex_content .= "\t\t\thline{2} = {0.5pt}, % 表头分隔线(细)\n"; + $tex_content .= "\t\t\thline{Z} = {1pt}, % 底线(粗)\n"; + $tex_content .= "\t\t\t% 隔行变色 + 样式优化\n"; + $tex_content .= "\t\t\trow{1} = {bg=gray!30, font=\\bfseries, abovesep=5pt, belowsep=5pt}, % 表头样式\n"; + $tex_content .= "\t\t\trow{even} = {bg=tablegray}, % 偶数行浅灰\n"; + $tex_content .= "\t\t\trow{odd} = {bg=white}, % 奇数行白色\n"; + $tex_content .= "\t\t\trowsep = 4pt, % 行间距\n"; + $tex_content .= "\t\t}\n"; + $tex_content .= "\t\t% 表头\n"; + $tex_content .= implode("\n", $latex); + $tex_content .= "\n\t\\end{longtblr}\n\n"; + } + $tex_content .= "\\end{document}"; + + +// echo $tex_content; + + + // 创建输出目录 + $outputPath = ROOT_PATH . 'public' . DS . 'latex' . DS . 'tex_'.$article_id; + if (!file_exists($outputPath)) { + mkdir($outputPath, 0755, true); + } + + // 生成tex文件 + $tex_file = $outputPath . DS . 'table.tex'; + file_put_contents($tex_file, $tex_content); + + $pdfFilePath = $outputPath . DS . 'table.pdf'; + + $success = $this->generatePdfFromTex($tex_file, $pdfFilePath); + + if($success){ + $this->production_article_obj->where("p_article_id",$production_info['p_article_id'])->update(['file_sub_table'=>'tex_'.$article_id.DS."table.pdf"]); + } + $frag['success'] = $success; + $frag['file'] = $pdfFilePath; + return $frag; + +// if ($success) { +// return jsonSuccess([ +// 'pdf_file' => $pdfFilePath, +// 'message' => 'PDF生成成功' +// ]); +// } else { +// return jsonError('PDF生成失败'); +// } + +// return $tex_file; + // 返回文件路径 +// return jsonSuccess([ +// 'file' => $tex_file, +// 'url' => '/public/latex/table_tex/table_' . $table_id . '.tex' +// ]); + } + + + + /** + * 根据tex文件生成对应的pdf文件 + * @param string $texFilePath .tex文件路径 + * @param string $pdfFilePath 输出PDF文件路径 + * @return bool 是否生成成功 + */ + public function generatePdfFromTex($texFilePath, $pdfFilePath) + { + try { + // 检查.tex文件是否存在 + if (!file_exists($texFilePath)) { + throw new Exception("LaTeX文件不存在: " . $texFilePath); + } + + // 确保输出目录存在 + $pdfDir = dirname($pdfFilePath); + if (!is_dir($pdfDir)) { + mkdir($pdfDir, 0755, true); + } + + // 方法1: 使用pdflatex命令(推荐) + $command = "pdflatex -include-directory=" . dirname($texFilePath) . + " -output-directory=" . $pdfDir . " " . escapeshellarg($texFilePath); + + $output = []; + $returnVar = 0; + exec($command, $output, $returnVar); + + // 检查命令执行结果 + if ($returnVar !== 0) { + error_log("LaTeX to PDF conversion failed: " . implode("\n", $output)); + return false; + } + + // 检查PDF文件是否生成成功 + if (!file_exists($pdfFilePath)) { + error_log("PDF文件未生成: " . $pdfFilePath); + return false; + } + + return true; + + } catch (Exception $e) { + error_log("生成PDF失败: " . $e->getMessage()); + return false; + } + } + + + private function escapeLatexSpecialCharsArr($text) { + if (is_null($text) || $text === '') { + return ''; + } + + $text = strval($text); + + // 转义LaTeX特殊字符 + $search = ['\\', '&', '%', '$', '#', '_', '{', '}', '~', '^']; + $replace = ['\\textbackslash{}', '\\&', '\\%', '\\$', '\\#', '\\_', '\\{', '\\}', '\\textasciitilde{}', '\\textasciicircum{}']; + + $text = str_replace($search, $replace, $text); + + // 处理换行符 + $text = str_replace(["\r\n", "\r", "\n"], ' ', $text); + + return $text; + } + + public function myTableTest(){ $references = $this->production_article_refer_obj->where("p_article_id",3328)->where("state",0)->order("index asc")->select(); - $res = $this->tableCovertLatex(1052,$references); + $res = $this->tableCovertLatex(1047,$references); echo $res; } @@ -3352,9 +3704,9 @@ class Production extends Base if (json_last_error() !== JSON_ERROR_NONE || !is_array($tableData) || empty($tableData)) { return "% 无效的JSON数据"; } -// if(strlen($table_info['table_data'])>6000){ -// return "执行附表程序"; -// } + if(strlen($table_info['table_data'])>6000){ + return "执行附表程序"; + } // 获取表格最大列数(遍历所有行,找到列数最大值,处理合并后列数变化的情况) $maxColCount = 0; @@ -3417,7 +3769,7 @@ class Production extends Base // 整理title信息 $title = strip_tags($table_info['title']); - $label = "tab-".$table_info['amt_id']; + $label = "tab:".$table_info['amt_id']; $notes = $this->escapeLatexSpecialChars(trim(strip_tags($table_info['note']),"*. \t\n\r\0\x0B")); @@ -3533,6 +3885,47 @@ class Production extends Base return trim($pureTitle); } + /**解决附加内容的表格内容 + * @return void + */ + private function tableForComp($table_data,$references){ + // 解码表格数据 + $tableData = json_decode($table_data, true); + if (!$tableData || !isset($tableData['rows']) || !is_array($tableData['rows'])) { + return ''; + } + + $latexLines = []; + + // 开始longtblr环境 + $latexLines[] = "\\begin{longtblr}["; + + // 设置表格基本属性 + $columns = count($tableData['rows'][0] ?? []); + $colSpec = str_repeat('X[c]', $columns); // 默认居中列 + + $latexLines[] = " colspec={{$colSpec}},"; + $latexLines[] = " width=\\textwidth,"; + $latexLines[] = " hlines,"; + $latexLines[] = " vlines,"; + $latexLines[] = " rowhead=1,"; // 如果第一行是表头 + $latexLines[] = "]"; + + // 处理表格行 + foreach ($tableData['rows'] as $rowIndex => $row) { + $rowCells = []; + foreach ($row as $cell) { + // 转换单元格内容中的特殊标签 + $convertedCell = $this->convertToLatex($cell, $references); + $rowCells[] = $convertedCell; + } + $latexLines[] = " " . implode(" & ", $rowCells) . " \\\\"; + } + + $latexLines[] = "\\end{longtblr}"; + + return implode("\n", $latexLines); + }