Files
tougao/application/common/LatexContent.php
2025-12-31 16:06:35 +08:00

733 lines
34 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace app\common;
use think\Db;
use app\common\LatexTable;
/**
* LaTeX内容生成类
*/
class LatexContent{
//期刊相关的信息
private $iYear = 2025;//年
private $iPeriod = 1;//期
private $sAbbr = 'tmr';//期刊缩写
private $sWebsite = 'https://www.tmrjournals.com/tmr';//期刊官网
//模版地址
private $sLatexUrl = '/public/latex/';
//期刊官网接口地址
private $sJournalUrl = 'http://zmzm.journal.dev.com/';//'http://journalapi.tmrjournals.com/public/index.php/';//
//文章图片
private $sArticleIcon = '/public/articleicon/';
//文章图片
private $sArticleMainImage = '/public/articleImage/';
//编辑系统URL
private $sSubmissionUrl = 'https://submission.tmrjournals.com/';
//图片模版
private $sImageTempalte = '\begin{figure}[hbt!]
\centering
\includegraphics[width=0.75\linewidth]{{{img_url}}}
\caption{{{img_title}}}
\label{{{img_fig_sim}}}
\end{figure}' ;
//首页内容
private $sFirst = '\documentclass[
journal={{journal_abbr}},
journalname={{{{journal_title}}}},
layout=largetwo,
year={{stage_year}},%年
volume={{stage_vol}},%卷
no={{stage_no}},%期
page={{stage_page}},%号
]{../tmr-tex}
\doi{{{doi}}}
\journalweb{{{website}}}
% \def\ORCIdICONNAME{{{CLSFILEURL}}/orcid_icon.png} %定义ORCID图片
% \def\TMRLOGONAME{{{CLSFILEURL}}/tmr-logo.pdf} % 定义期刊LOGO图片
\usepackage{amsmath}
\usepackage[nopatch]{microtype}
\usepackage{booktabs}
\usepackage[backend=biber]{biblatex}
\usepackage{xcolor}
\usepackage{tabularray}
\addbibresource{references_{{p_article_id}}.bib}
\definecolor{evenRowColor}{RGB}{250,231,232}
\title{{{article_title}}}
{{author_info}}
{{correspondence_info}}
\keywords{keyword entry 1, keyword entry 2, keyword entry 3} %% First letter not capped
{{author_contribution}}
\competinginterests{The authors declare no conflicts of interest.}
{{article_acknowledgment}}
\Peerreviewinformation{{{journal_title}} thanks all anonymous reviewers for their contribution to the peer review of this paper}
{{article_abbreviation}}
{{article_cite}}
\received{{{received_date}}}
\revised{{{revision_date}}}
\accepted{{{accepted_date}}}
\Availableonline{{{pub_date}}}
{{executive_editor}}
{{abstract}}
{{keywords}}
{{article_icon}}';
//正文内容
private $sMain = '\begin{document}
\twocolumn
{{tmr_highlight}}
{{article_main}}
\nocite{*}
\printbibliography[title={References}]
\end{document}';
/**
* 生成初稿-基本内容
* @param article_id 文章ID
*/
public function buildFirst($aProductionArticle = []){
//获取模版
$sTemplateInfo = $this->sFirst;
//查询期刊信息
$iJournalId = empty($aProductionArticle['journal_id']) ? 0 : $aProductionArticle['journal_id'];
if(!empty($iJournalId)){
$aWhere = ['journal_id' => $iJournalId,'state' => 0];
$aJournal = Db::name('journal')->where($aWhere)->find();
if(!empty($aJournal)){
//获取编辑信息
$aWhere = ['user_id' => $aJournal['editor_id'],'state' => 0];
$aUser = Db::name('user')->field('realname')->where($aWhere)->find();
}
}
//子刊信息
$iJournalStageId = empty($aProductionArticle['journal_stage_id']) ? 0 : $aProductionArticle['journal_stage_id'];
if(!empty($iJournalStageId)){
$aJournalStage = $this->getJournalStage(['journal_stage_id' => $iJournalStageId]);
$aJournalStage = empty($aJournalStage['data']) ? [] : $aJournalStage['data'];
}
//期刊简写
$sAbbr = empty($aJournal['abbr']) ? $this->sAbbr : $aJournal['abbr'];
$sAbbr = strtolower($sAbbr);
$sJournalTitle = empty($aJournal['title']) ? '' : $aJournal['title'];
//年
$iYear = empty($aJournalStage['stage_year']) ? date('Y') : $aJournalStage['stage_year'];
//卷
$iVolume = empty($aJournalStage['stage_vol']) ? 0 : $aJournalStage['stage_vol'];
//期
$iPeriod = empty($aJournalStage['stage_no']) ? 0 : $aJournalStage['stage_no'];
//页
$sPage = empty($aJournalStage['stage_page']) ? '' : $aJournalStage['stage_page'];
//期刊web
$sWebsite = empty($aJournal['website']) ? $this->sWebsite : $aJournal['website'];
$aSearch = ['{{journal_abbr}}' => $sAbbr,'{{stage_year}}' => $iYear,'{{stage_vol}}' => $iVolume,'{{stage_no}}' => $iPeriod,'{{stage_page}}' => $sPage,'{{website}}' => $sWebsite,'{{journal_title}}' => $sJournalTitle];
//获取DOI
$aSearch['{{doi}}'] = empty($aProductionArticle['doi']) ? '' : $aProductionArticle['doi'];
//获取作者信息
$aAuthoInfo = $this->dealAuthor(['p_article_id' => $aProductionArticle['p_article_id']]);
$aSearch['{{author_info}}'] = empty($aAuthoInfo['author']) ? '' : $aAuthoInfo['author'];
//通讯作者信息
$aSearch['{{correspondence_info}}'] = empty($aAuthoInfo['correspondence']) ? '' : '\Correspondence{'.$aAuthoInfo['correspondence'].'}';
//文章基本信息处理
//标题
$sTitle = empty($aProductionArticle['title']) ? '' : $this->dealContent($aProductionArticle['title']);
if(!empty($sTitle)){
if(!empty($aProductionArticle['ltai'])){
$aLtai = explode(',', $aProductionArticle['ltai']);
foreach ($aLtai as $key => $value) {
if(empty($value)){
continue;
}
$sTitle = str_replace($value, '\textit{'.$value.'}', $sTitle);
}
}
}
$aSearch['{{article_title}}'] = $sTitle;
//author_contribution
$aSearch['{{author_contribution}}'] = empty($aProductionArticle['author_contribution']) ? '' : '\authorcontributions{'.$this->dealContent($aProductionArticle['author_contribution']).'}';
//acknowledgment
$aSearch['{{article_acknowledgment}}'] = empty($aProductionArticle['acknowledgment']) ? '' : '\Acknowledgments{'.$this->dealContent($aProductionArticle['acknowledgment']).'}';
//abbreviation
$aSearch['{{article_abbreviation}}'] = empty($aProductionArticle['abbreviation']) ? '' : '\Abbreviations{'.$this->dealContent($aProductionArticle['abbreviation']).'}';
//文章引用信息
$sCite = $this->getArticleCite($aProductionArticle,$aJournal,$aJournalStage);
$aSearch['{{article_cite}}'] = empty($sCite) ? '' : '\Citation{'.$sCite.'}';
//获取文章时间
$aTime = $this->getArticleTime(['article_id' => $aProductionArticle['article_id']]);
$aTime = empty($aTime['data']) ? [] : $aTime['data'];
$aSearch['{{received_date}}'] = empty($aTime['received_date']) ? '' : $aTime['received_date'];
$aSearch['{{revision_date}}'] = empty($aTime['revision_date']) ? '' : $aTime['revision_date'];
$aSearch['{{accepted_date}}'] = empty($aTime['accepted_date']) ? '' : $aTime['accepted_date'];
$aSearch['{{pub_date}}'] = empty($aProductionArticle['pub_date']) ? '' : $aProductionArticle['pub_date'];
//获取编辑信息
$aSearch['{{executive_editor}}'] = empty($aUser['realname']) ? '' : '\Executiveeditor{'.$this->dealContent($aUser['realname']).'}';
//摘要替换
$sAbstract = empty($aProductionArticle['abstract']) ? '' : $this->dealContent($aProductionArticle['abstract']);
if(!empty($sAbstract)){
$aRelace = ['Background:' => '\textcolor[HTML]{0070C0}{\textbf{Background:}}','Methods:' => '\textcolor[HTML]{0070C0}{\textbf{Methods:}}','Results:' => '\textcolor[HTML]{0070C0}{\textbf{Results:}}','Conclusion:' => '\textcolor[HTML]{0070C0}{\textbf{Conclusion:}}'];
$sAbstract = str_replace(array_keys($aRelace), array_values($aRelace), $sAbstract);
$sAbstract = '\tmrabstract{'.$sAbstract.'}';
}
$aSearch['{{abstract}}'] = $sAbstract;
//关键词
$sKeywords = empty($aProductionArticle['keywords']) ? '' : '\keywords{'.$this->dealContent($aProductionArticle['keywords']).'}';
$aSearch['{{keywords}}'] = $sKeywords;
//文章图片地址
$sIcon = empty($aProductionArticle['icon']) ? '' : $aProductionArticle['icon'];
if(!empty($sIcon)){
// $sIconUrl = rtrim(ROOT_PATH).$this->sArticleIcon.'/'.$sIcon;
// if(file_exists($sIconUrl)){
// $sIcon = '\KeywordImage{'.$sIconUrl.'}';
// }else{
// $sIcon = '';
// }
//下载图片
$sImagePath = trim($this->sSubmissionUrl,'/').$this->sArticleIcon.'/'.$sIcon;
$aImageInfo = $this->getImage($sImagePath,$aProductionArticle['p_article_id']);
if(!empty($aImageInfo['data'])){
$sIcon = '\KeywordImage{'.$aImageInfo['data'].'}';
}else{
$sIcon = '';
}
}
$aSearch['{{article_icon}}'] = $sIcon;
$aSearch['{{keywords}}'] = $sKeywords;
$aSearch['{{CLSFILEURL}}'] = ROOT_PATH.'public/latex/cls';
$aSearch['{{p_article_id}}'] = $aProductionArticle['p_article_id'];
//模版内容替换
$sTemplateInfo = str_replace(array_keys($aSearch), array_values($aSearch), $sTemplateInfo);
//返回内容
return ['status' => 1,'msg' => 'success','data' => $sTemplateInfo];
}
/**
* 生成初稿-主内容
* @param article_id 文章ID
*/
public function buildMain($aParam = []){
//生产环境文章信息
$aProductionArticle = empty($aParam['production_article']) ? [] : $aParam['production_article'];
//参考文献信息
$aReferences = empty($aParam['references']) ? [] : $aParam['references'];
//获取模版
$sTemplateInfo = $this->sMain;
//数据替换
$aSearch = ['{{tmr_highlight}}' => '','{{article_main}}' => ''];
$sHighlight = '';
if($aProductionArticle['journal_id'] == 1){//tmr独有
$tradition_tag = empty($aProductionArticle['tradition_tag']) ? '' : strtolower( $aProductionArticle['tradition_tag']);
$tradition = empty($aProductionArticle['tradition']) ? '' : $this->dealContent($aProductionArticle['tradition']);
$pattern = '/<em[^>]*>(.*?)<\/em>/is';
$replacement = '\\emph{$1}';
// $tradition = preg_replace($pattern, $replacement, $tradition);
if(in_array($tradition_tag, ['highlight','highlights']) && !empty($tradition)){
$sHighlight .= '\highlightitem{Highlights}{'.$tradition.'}'."\n";
}
$mhoo = empty($aProductionArticle['mhoo']) ? '' : $this->dealContent( $aProductionArticle['mhoo']);
$mhoo = preg_replace($pattern, $replacement, $mhoo);
if(!empty($mhoo)){
$sHighlight .= '\highlightitem{Medical history of objective}{'.$mhoo.'}'."\n";
}
}
$aSearch['{{tmr_highlight}}'] = empty($sHighlight) ? '' : '\begin{highlight}'."\n".$sHighlight."\n".'\end{highlight}'."\n";
//查询正文信息
// $aProductionArticle['article_id'] = 4477;
$aWhere = ['article_id' => $aProductionArticle['article_id'],'state' => 0];
$aArticleMain = Db::name('article_main')->field('am_id,article_id,type,content,ami_id,amt_id,is_h1,is_h2,is_h3,sort')->where($aWhere)->order('sort')->select();
if(!empty($aArticleMain)){
//查询图片
$aAmiId = array_unique(array_column($aArticleMain, 'ami_id'));
$aWhere = ['ami_id' => ['in',$aAmiId],'state' => 0];
$aArticleMainImage = Db::name('article_main_image')->field('ami_id,url,note')->where($aWhere)->select();
$aArticleMainImage = empty($aArticleMainImage) ? [] : array_column($aArticleMainImage, null,'ami_id');
// //查询表格
// $aAmtId = array_unique(array_column($aArticleMain, 'amt_id'));
// $aWhere = ['amt_id' => ['in',$aAmtId],'state' => 0];
// $aArticleMainTable = Db::name('article_main_table')->field('amt_id,type,table_data,url,title,note')->where($aWhere)->select();
// $aArticleMainTable = empty($aArticleMainTable) ? [] : array_column($aArticleMainTable, null,'amt_id');
//获取图片模版
$sImageTempalte = $this->sImageTempalte;
//数据处理
$iStart = 0;
$sMain = '';
$oLatexTable = new LaTeXTable;
//字符串处理
$oProduction = new \app\api\controller\Production;
foreach ($aArticleMain as $key => $value) {
if(empty($iStart)){
if($value['is_h1'] == 0){
continue;
}else{
$iStart = $value['am_id'];
}
}
if($value['am_id'] >= $iStart){
if($value['is_h1'] == 1){
if(empty($value['content'])){
continue;
}
$sMain .= '\section{'.$this->dealContent($value['content']).'}'."\n";
}
if($value['is_h2'] == 1){
if(empty($value['content'])){
continue;
}
$sMain .= '\subsection{'.$this->dealContent($value['content']).'}'."\n";
}
if($value['is_h3'] == 1){
if(empty($value['content'])){
continue;
}
$sMain .= '\subsubsection{'.$this->dealContent($value['content']).'}'."\n";
}
if($value['is_h1'] == 0 && $value['is_h2'] == 0 && $value['is_h3'] == 0){
if($value['type'] == 0 ){
$sMain .= $this->dealContent($value['content'],$aReferences)."\\par\n";
}
if($value['type'] == 1 ){//图片
$aImageInfo = empty($aArticleMainImage[$value['ami_id']]) ? [] : $aArticleMainImage[$value['ami_id']];
if(empty($aImageInfo['url'])){
continue;
}
// $sIconUrl = rtrim(ROOT_PATH).$this->sArticleIcon.'/'.$sIcon;
// if(file_exists($sIconUrl)){
// $sIcon = '\KeywordImage{'.$sIconUrl.'}';
// }else{
// $sIcon = '';
// }
//下载图片
$sImagePath = trim($this->sSubmissionUrl,'/').$this->sArticleMainImage.'/'.$aImageInfo['url'];
$aResult = $this->getImage($sImagePath,$aProductionArticle['p_article_id']);
if(empty($aResult['data'])){
continue;
}
$aImageSearch = ['{{img_url}}' => $aResult['data'],'{{img_title}}' => $aImageInfo['note'],'{{img_fig_sim}}' => 'img_fig_sim_'.$value['ami_id']];
$sImageTempalteInfo = str_replace(array_keys($aImageSearch), array_values($aImageSearch), $sImageTempalte);
$sMain .= $sImageTempalteInfo."\\par\n";
}
if($value['type'] == 2 ){//表格
$sTableInfo = $oProduction->tableCovertLatex($value['amt_id'],$aReferences);
var_dump($sTableInfo);
$sMain .= $sTableInfo."\\par\n";
// $aTableInfo = empty($aArticleMainTable[$value['amt_id']]) ? [] : $aArticleMainTable[$value['amt_id']];
// if(empty($aTableInfo['table_data'])){
// continue;
// }
// $aTableResult = $oLatexTable->generateTable($aTableInfo);
// var_dump($aTableResult);exit;
// if(empty($aTableResult['data'])){
// continue;
// }
}
}
}
}
}
$aSearch['{{article_main}}'] = empty($sMain) ? '' : $sMain."\n";
//模版内容替换
$sTemplateInfo = str_replace(array_keys($aSearch), array_values($aSearch), $sTemplateInfo);
//返回内容
return ['status' => 1,'msg' => 'success','data' => $sTemplateInfo];
}
/**
* 生成初稿-作者信息
*/
private function dealAuthor($aParam = []){
// 获取作者信息
$aParam = empty($aParam) ? $this->request->post() : $aParam;
//必填值验证
$iPArticleId = empty($aParam['p_article_id']) ? '' : $aParam['p_article_id'];
if(empty($iPArticleId)){
return json_encode(array('status' => 2,'msg' => 'Please select an article' ));
}
//
//查询作者信息
$aWhere = ['p_article_id' => $iPArticleId,'state' => 0];
$aAuthor = Db::name('production_article_author')->where($aWhere)->select();
if(empty($aAuthor)){
return ['status' => 3,'msg' => 'Author information is empty'];
}
//查询作者机构
$aWhere['p_article_author_id'] = ['in',array_column($aAuthor, 'p_article_author_id')];
$aAuthorOrgan = Db::name('production_article_author_to_organ')->field('p_article_author_id,p_article_organ_id')->where($aWhere)->select();
//查询机构信息
$aAuthorOrganList = [];
if(!empty($aAuthorOrgan)){
unset($aWhere['p_article_author_id']);
$aOrganId = array_unique(array_column($aAuthorOrgan, 'p_article_organ_id'));
$aWhere['p_article_organ_id'] = ['in',$aOrganId];
$aOrgan = Db::name('production_article_organ')->where($aWhere)->column('p_article_organ_id,organ_name');
if(!empty($aOrgan)) {
foreach ($aAuthorOrgan as $key => $value) {
if(empty($aOrgan[$value['p_article_organ_id']])){
continue;
}
$aAuthorOrganList[$value['p_article_author_id']][] = $aOrgan[$value['p_article_organ_id']];
}
}
}
//处理作者
$sAuthor = $sCorrespondence = '';
foreach ($aAuthor as $key => $value) {
//作者姓名
$sName = empty($value['fifirst_name']) ? '' : $value['fifirst_name'];
$sName = empty($sName) ? '' : $sName . ' ' .$value['last_name'];
if(empty($sName)){
$sName = empty($value['author_name']) ? '' : $value['author_name'];
}
if(empty($sName)){
continue;
}
$sAuthor .= '\author{'.$this->dealContent($sName).'}'."\n";
if(!empty($value['email']) && $value['is_report'] == 1){
$sAuthor .= '\email{'.$this->dealContent($value['email']).'}'."\n";
}
//作者机构
$aOrganList = empty($aAuthorOrganList[$value['p_article_author_id']]) ? [] : $aAuthorOrganList[$value['p_article_author_id']];
if(!empty($aOrganList)){
$i = 0;
foreach($aOrganList as $organ){
if(empty($organ)){
continue;
}
if($i == 0){
$sAuthor .= '\affiliation{'.$this->dealContent($organ).'}'."\n";
}else{
$sAuthor .= '\alsoaffiliation{'.$this->dealContent($organ).'}'."\n";
}
$i++;
}
}
if($value['is_first'] == 1){
$sAuthor .= '\firstauthor'."\n";
}
if(!empty($value['orcid'])){
$sAuthor .= '\orcid{'.$this->dealContent($value['orcid']).'}'."\n";
}
if($value['is_report'] == 1){
$sCorrespondence .= $sName;
if(!empty($aOrganList[0])){
$sCorrespondence .= ','.trim($this->dealContent($aOrganList[0]),'.');
}
if(!empty($value['email'])){
$sCorrespondence .= '.E-mail: '.trim($this->dealContent($value['email']),'.').'.';
}
}
}
return ['author' => $sAuthor,'correspondence' => $sCorrespondence];
}
/**
* 生成初稿-获取期刊分期信息
*/
private function getJournalStage($aParam = []){
// 获取分期ID
$aParam = empty($aParam) ? $this->request->post() : $aParam;
//必填值验证
$iJournalStageId = empty($aParam['journal_stage_id']) ? '' : $aParam['journal_stage_id'];
if(empty($iJournalStageId)){
return json_encode(array('status' => 2,'msg' => 'Please choose journal installment' ));
}
$sUrl = $this->sJournalUrl."api/Supplementary/getJournalStage";
$aResult = object_to_array(json_decode(myPost1($sUrl,$aParam)));
return $aResult;
}
/**
* 处理文章引用
* @param string $html
* @return string
*/
private function getArticleCite($aArticle = [],$aJournal = [],$aJournalStage = []){
$no = empty($aJournalStage['stage_no']) ? ':' : '(' . $aJournalStage['stage_no'] . '):';
$jabbr = empty($aJournal['jabbr']) ? '' : $aJournal['jabbr'];
$stage_year = empty($aJournalStage['stage_year']) ? '' : $aJournalStage['stage_year'];
$stage_vol = empty($aJournalStage['stage_vol']) ? '' : $aJournalStage['stage_vol'];
$aAbbr = empty($aArticle['abbr']) ? '' : explode(',', trim($aArticle['abbr']));
if(!empty($aAbbr)){
$sEnd = end($aAbbr);
if($sEnd != 'et al'){
$aArticle['abbr'] = implode(',', array_slice($aAbbr, 0,3));
if(!empty($aArticle['abbr'])){
$aArticle['abbr'] .= ', et al';
}
}
}
$sCite = '';
if ($aArticle['journal_id'] == 22) {
$sCite = $aArticle['abbr'] . '. ' . $aArticle['title'] . '[J]. ' . choiseJabbr($aArticle['article_id'],$jabbr) . ',' . $stage_year . ',' . $stage_vol . $no . $aArticle['npp'] . '. doi:' . $aArticle['doi'];
$sCite = $this->dealContent($sCite);
} else {
$sCite = $this->dealContent($aArticle['abbr']) . '. ' . $this->dealContent($aArticle['title']) . '. \textit{' . $this->dealContent(choiseJabbr($aArticle['article_id'], $jabbr)) . '}. ' . $stage_year . ';' . $stage_vol . $no . $aArticle['npp'] . '. doi:' . $aArticle['doi'];
}
return $sCite;
}
private function getArticleTime($aParam = []){
//必填值验证
$iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id'];
if(empty($iArticleId)){
return array('status' => 2,'msg' => 'Please select an article' );
}
//查询文章
$aWhere = ['article_id' => $iArticleId];
$aArticle= Db::name('article')->field('ctime,received_time')->where($aWhere)->find();
if(empty($aArticle)){
return array('status' => 2,'msg' => 'The article does not exist' );
}
//获取文章记录
$aWhere = ['article_id' => $iArticleId,'state' => 0,'state_to' => ['in',[0,1,5]]];
$aArticleMsg = Db::name('article_msg')->field('state_from,state_to,ctime')->where($aWhere)->order('ctime deac')->select();
$iReceivedTime = $iRevisionTime = $iAcceptedTime = 0;
if(!empty($aArticleMsg)){
foreach ($aArticleMsg as $key => $value) {
if(empty($iReceivedTime) && $value['state_to'] == 0){
$iReceivedTime = $value['ctime'];
}
if(empty($iRevisionTime) && $value['state_to'] == 1){
$iRevisionTime = $value['ctime'];
}
if(empty($iAcceptedTime) && $value['state_to'] == 5){
$iAcceptedTime = $value['ctime'];
}
}
}
$iReceivedTime = empty($iReceivedTime) ? $aArticle['received_time'] : $iReceivedTime;
$iReceivedTime = empty($iReceivedTime) ? $aArticle['ctime'] : $iReceivedTime;
$aTime = [];
$aTime['received_date'] = empty($iReceivedTime) ? '' : $this->timestampToEnglishDate($iReceivedTime);
$aTime['revision_date'] = empty($iRevisionTime) ? '' : $this->timestampToEnglishDate($iRevisionTime);
$aTime['accepted_date'] = empty($iAcceptedTime) ? '' : $this->timestampToEnglishDate($iAcceptedTime);
return ['status' => 1,'msg' => 'success','data' => $aTime];
}
/**
* 时间戳转换为「d F Y」格式如 12 June 2025
* @param int $timestamp 时间戳如1749600000
* @return string 格式化后的英文日期
*/
private function timestampToEnglishDate($timestamp) {
//验证时间戳有效性
if (!is_numeric($timestamp) || $timestamp < 0) {
return '';
}
//设置本地化确保月份为英文全称兼容Linux/Windows
$locale = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'
? 'English_United States.1252'
: 'en_US.UTF-8';
setlocale(LC_TIME, $locale);
//格式化d=日无前导零、F=英文月份全称、Y=四位年 strftime的%e在Linux下是无前导零的日Windows用%#d
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$dateStr = strftime('%#d %B %Y', $timestamp); // Windows
} else {
$dateStr = strftime('%e %B %Y', $timestamp); // Linux/Mac
}
return trim($dateStr);
}
/**
* 内容处理
*/
// private function dealContent($content = '', $target = 'latex',$iIsDeal = 1) {
// // 空值直接返回
// if (empty($content)) {
// return '';
// }
// //统一编码为UTF-8
// $content = mb_convert_encoding($content, 'UTF-8', mb_detect_encoding($content));
// //过滤不可见/非法字符
// $content = preg_replace('/[\x00-\x1F\x7F]/u', '', $content);
// //整合所有替换规则按优先级排序先处理HTML实体再处理LaTeX特殊字符
// $replaceRules = [
// // ========== HTML实体替换 ==========
// '&nbsp;' => '~', // HTML非断行空格 → LaTeX非断行空格
// '&nbsp' => '~', // 兼容无分号的&nbsp
// '&amp;' => '\&', // HTML& → LaTeX转义&
// '&lt;' => '<', // HTML< → 直接保留
// '&gt;' => '>', // HTML> → 直接保留
// // ========== LaTeX特殊字符转义 ==========
// '{' => '\{', // 左花括号转义
// '}' => '\}', // 右花括号转义
// '&' => '\&', // 原生&转义(需在&amp;之后,避免覆盖)
// '%' => '\%', // 百分号转义LaTeX注释符
// '_' => '\_', // 下划线转义LaTeX下标符
// 'κ' => '$\kappa$', // 希腊字母κ → LaTeX数学环境避免乱码
// '-' => '\text{-}', // 短横线强制保留形态
// ':' => ':', // 冒号无需转义(占位,便于统一维护)
// ];
// //执行批量替换
// $content = strtr($content, $replaceRules);
// //清理多余空格/换行
// $content = preg_replace('/\s+/u', ' ', trim($content));
// if($iIsDeal == 1){
// $content = $this->htmlToLaTeX($content);
// }
// return $content;
// }
private function dealContent($content = '',$aReferences = []) {
// 空值直接返回
if(empty($content)){
return '';
}
//统一编码为UTF-8
$content = mb_convert_encoding($content, 'UTF-8', mb_detect_encoding($content));
//过滤不可见/非法字符
$content = preg_replace('/[\x00-\x1F\x7F]/u', '', $content);
//字符串处理
$oProduction = new \app\api\controller\Production;
$sContent = $oProduction->convertToLatex($content,$aReferences);
$content = $this->htmlToLaTeX($content);
return $sContent;
// //整合所有替换规则按优先级排序先处理HTML实体再处理LaTeX特殊字符
// $replaceRules = [
// // ========== HTML实体替换 ==========
// '&nbsp;' => '~', // HTML非断行空格 → LaTeX非断行空格
// '&nbsp' => '~', // 兼容无分号的&nbsp
// '&amp;' => '\&', // HTML& → LaTeX转义&
// '&lt;' => '<', // HTML< → 直接保留
// '&gt;' => '>', // HTML> → 直接保留
// // ========== LaTeX特殊字符转义 ==========
// '{' => '\{', // 左花括号转义
// '}' => '\}', // 右花括号转义
// '&' => '\&', // 原生&转义(需在&amp;之后,避免覆盖)
// '%' => '\%', // 百分号转义LaTeX注释符
// '_' => '\_', // 下划线转义LaTeX下标符
// 'κ' => '$\kappa$', // 希腊字母κ → LaTeX数学环境避免乱码
// '-' => '\text{-}', // 短横线强制保留形态
// ':' => ':', // 冒号无需转义(占位,便于统一维护)
// ];
// //执行批量替换
// $content = strtr($content, $replaceRules);
// //清理多余空格/换行
// $content = preg_replace('/\s+/u', ' ', trim($content));
// if($iIsDeal == 1){
// $content = $this->htmlToLaTeX($content);
// }
// return $content;
}
private function htmlToLaTeX($text = ''){
if(empty($text)){
return '';
}
$replaceMap = [
// 基础标签转换
'<i>' => '\textit{',
'</i>' => '}',
'<em>' => '\textit{',
'</em>' => '}',
'<b>' => '\textbf{',
'</b>' => '}',
'<blue>' => '\textcolor{blue}{',
'</blue>' => '}',
'<p>' => '',
'</p>' => '',
'<sub>' => '$_{',
'</sub>' => '}$',
'<sup>' => '$^{',
'</sup>' => '}$',
'~' => '--', // 波浪号→范围符
'' => '(', // 全角左括号→半角
'' => ')', // 全角右括号→半角
'(' => '(', // 重复括号去重
')' => ')',
// ========== 新增:<span> 标签处理 ==========
// 场景1纯空 <span> 标签(无属性)→ 直接删除,保留内容
'<span>' => '',
'</span>' => '',
// 场景2带蓝色样式的 <span style="color:blue"> → 转为 LaTeX 蓝色命令
'<span style="color:blue">' => '\textcolor{blue}{',
'<span style="color: Blue;">' => '\textcolor{blue}{', // 兼容大小写/空格
'<span style="color:red">' => '\textcolor{red}{', // 扩展:红色
'</span>' => '}', // 所有 span 闭标签统一转为 }(匹配开标签的 LaTeX 命令)
// 扩展:其他常用 span 样式(按需添加)
'<span style="font-weight:bold">' => '\textbf{',
'<span style="font-style:italic">' => '\textit{',
];
// 第一步:替换所有标签(优先处理带属性的 span再处理空 span
$text = strtr($text, $replaceMap);
// 第二步:清理残留的特殊 span 标签(如未匹配的属性,直接删除标签保留内容)
// 匹配 <span ...> 形式的标签(任意属性),仅删除标签,保留内容
$text = preg_replace('/<span(\s+[^>]*?)?>/i', '', $text);
$text = preg_replace('/<\/span>/i', '', $text);
return $text;
}
/**
* @title curl 请求获取图片保存到本地
* @param sPath 图片链接地址
*/
private function getImage($sPath = '',$iId = 0){
//判断参数
if(empty($sPath) || empty($iId)){
return ['status' => 2,'msg' => 'url is empty','data' => ''];
}
//获取图片名称
$aImageInfo = pathinfo($sPath);
//图片后缀名
$sExtension = empty($aImageInfo['extension']) ? 'jpg' : $aImageInfo['extension'];
//图片地址
$sDir = ROOT_PATH.trim($this->sLatexUrl,'/').'/article_pdf/'.$iId.'/';
$sImagePath = $sDir.basename($sPath);
if (file_exists($sImagePath)) {
return ['status' => 1,'msg' => 'success','data' => $sImagePath];
}
//下载图片
if (!is_dir($sDir)) {
mkdir($sDir, 0755, true);
}
//curl 请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$sPath); // 设置请求URL
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 设置返回数据而不是直接输出
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 禁用SSL验证
$response = curl_exec($ch);
if (curl_errno($ch)) {
return ['status' => 3,'msg' => 'Error:' . curl_error($ch),'data' => $sImagePath];
}
//保存图片
file_put_contents($sImagePath, $response);
curl_close($ch);
return ['status' => 1,'msg' => 'success','data' => $sImagePath];
}
}
?>