This commit is contained in:
chengxl
2026-01-19 13:58:45 +08:00

View File

@@ -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('/(?:<blue>)?\[(\d+(?:[-,]\d+)*)\](?:<\/blue>)?/', $tagWrappedContent, $matches)) {
if ($check_refer&&preg_match('/(?:<blue>)?\[(\d+(?:[-,]\d+)*)\](?:<\/blue>)?/', $tagWrappedContent, $matches)) {
// 去除匹配中的 <blue> 和 </blue>
$cleanedMatch = str_replace(['<blue>', '</blue>'], '', $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);
}