webstasolutions / laratex
使用 LaTeX 在 Laravel 中创建 PDF 的软件包
Requires
- php: ^8.0
- ext-zip: *
- illuminate/filesystem: 5.4.x|5.5.X|5.6.X|5.7.X|5.8.X|^6.0|^7.0|^8.0|^9.3
- illuminate/support: 5.4.x|5.5.X|5.6.X|5.7.X|5.8.X|^6.0|^7.0|^8.0|^9.3
- symfony/process: ^2.0|^3.0|^4.0|^5.0|^6.0
README
LaraTeX
Laravel 包,用于使用 LaTeX 生成 PDF
· 报告错误 · 请求功能
为了更好的可视化,您可以在 这里 找到一个小型 演示 和 HTML 到 LaTeX 转换器。
目录
注意
在为这两个特殊过程使用软件包的同时,该软件包在两个不同的环境中进行了测试。如果在您使用软件包的整个过程中遇到任何问题,请提出问题,以便我可以通过每次更新来改进这个软件包 :)
入门
关于您的环境的重要信息
该软件包是在 Unix (FreeBSD) 服务器上开发和测试的,并在运行 pdflatex 的 Windows 机器上成功进行了测试。请始终确保正确写入您的路径 :)
该软件包使用 storage_path()
函数。在 Windows 上,绝对路径可能会以反斜杠的形式写出。Windows 在使用前斜杠和反斜杠的路径方面做得非常好,但请在 Windows 上遇到问题时不工作得很好时记住这一点。
先决条件
您需要在您的服务器上安装 texlive-full
。此程序具有 TeX 软件包和语言库,可以帮助您生成文档。注意:您也可以选择安装 textlive
,这是该软件包的较轻版本。
区别在于
- 当您安装
textlive
并希望使用任何附加的 TeX 软件包时,您需要手动安装它。 texlive-full
包含这些额外软件包。因此,它可能在您的服务器上占用一些额外的空间(用于存储软件包库文件)。
如果您选择的是不允许您自己安装应用程序的托管提供商,请确保已安装 pdflatex,或者询问是否可以安装。此外,请确保您有权访问服务器的 SSH,因为您可能需要它来确定您的 pdflatex 安装所在的路径。
安装
您可以使用 Composer 安装该软件包
composer require webstasolutions/laratex
配置
要使用 php artisan 运行配置文件,请运行以下命令
php artisan vendor:publish --tag=config
在此之后,请确保配置您的 LaraTeX 安装。在您的 LaraTeX 配置文件 \config\laratex.php
中,您可以配置两个设置
binPath 如果您的系统不允许仅运行命令行命令 "pdflatex",您可以指定正确的命令。在 Unix 系统上,您可以通过运行命令 which pdflatex
来找出要使用的 bin 路径
如果您在Windows系统上运行此包,请在cmd.exe中检查这一点。在那里您应该能够找到是否可以通过cmd运行pdflatex
命令,或者是否需要提供pdflatex应用程序的绝对路径。
tempPath 这指定了将临时文件保存到哪个文件夹中,在将tex文件渲染为PDF文件时。您始终需要在路径开头和结尾处添加一个斜杠(例如:app/pdf/)。
使用
干燥运行
在直接使用之前,请确保服务器上已正确安装所需的程序。该包附带了一个dryrun方法。如果服务器上一切设置正确,它将自动生成一个名为dryrun.pdf
的文件。如果不正确,请仔细检查上面的binPath
配置。
<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use webstasolutions\LaraTeX\LaraTeX; class TestController extends Controller { /** * Download PDF generated from latex * * @return Illuminate\Http\Response */ public function download(){ return (new LaraTeX)->dryRun(); } }
如果pdflatex设置正确,dryrun将下载一个漂亮的干净测试PDF。
使用此包,您有多种选择。您可以直接渲染PDF文件并下载它,保存到某个地方,仅获取tex内容,或批量下载包含多个生成PDF文件的ZIP文件。
准备带有 LaTeX 内容的 Laravel 视图
在resources/views/latex/tex.blade.php
中创建一个视图文件。当然,您可以在资源文件夹中的任何位置创建视图文件。只需确保正确定义要使用的视图即可。
\documentclass[a4paper,9pt,landscape]{article} \usepackage{adjustbox} \usepackage[english]{babel} \usepackage[scaled=.92]{helvet} \usepackage{fancyhdr} \usepackage[svgnames,table]{xcolor} \usepackage[a4paper,inner=1.5cm,outer=1.5cm,top=1cm,bottom=1cm,bindingoffset=0cm]{geometry} \usepackage{blindtext} \geometry{textwidth=\paperwidth, textheight=\paperheight, noheadfoot, nomarginpar} \renewcommand{\familydefault}{\sfdefault} \pagestyle{fancy} \fancyhead{} \renewcommand{\headrulewidth}{0pt} \fancyfoot{} \fancyfoot[LE,RO]{\thepage} \fancyfoot[C]{\fontsize{8pt}{8pt}\selectfont The above document is auto-generated.} \renewcommand{\footrulewidth}{0.2pt} \begin{document} \section*{\centering{LaraTeX Demo Document}} \begin{center} \item[Name :] {{ $Name }} \item[Date of Birth :] {{ $Dob }} \end{center} \blindtext \begin{table}[ht] \centering \renewcommand{\arraystretch}{2} \begin{tabular}{|c|l|} \hline \rowcolor[HTML]{E3E3E3} \textbf{ID} & \textbf{Language} \\ \hline\renewcommand{\arraystretch}{1.5} @foreach($languages as $key => $language) {{ $key }} & {{ $language }} \\ \hline @endforeach \end{tabular} \caption{Language Summary} \end{table} \begin{center} {!! $SpecialCharacters !!} \end{center} \end{document}
您可以看到我们如何轻松使用blade指令,如{{ $name }}
来显示一个名字或@foreach
来在表格中显示语言,以动态生成内容。
对于可能需要使用像{{ $var }}
这样的blade指令的复杂LaTeX文件,该指令已经在使用花括号(例如\textbf{}
)的LaTeX命令中,您始终可以使用Laravel的@php @endphp
方法或纯PHP,如<?php echo $var; ?>
或<?= $var ?>
(例如:\textbf{<?= $var ?>}
)。
使用HTML字符时的重要提示
当在blade模板中使用{{ }}
语句时,Laravel的blade引擎始终首先通过PHP函数htmlspecialchars()
发送数据。这将转换字符,如&
到&
和<
到<
,仅举几个例子。pdflatex不喜欢这些转换后的字符串,会抛出错误,如Misplaced alignment tab character &.
。
为了解决这个问题,您必须使用{!! !!}
语句,以便将未转义的文本写入您的tex模板。
在您的 LaTeX 文件中使用图形
关于pdflatex在何处查找包含在.tex文件中的图形,我并不是很确定。对我帮助最大的是始终为图形提供绝对路径,例如\includegraphics[scale=0.06]{/absolute/path/to/www/storage/graphics/file.pdf}
。如果您有更好的工作想法,请帮助我并与我分享您的知识:)
下载PDF文件
download(string $fileName = null)
<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use webstasolutions\LaraTeX\LaraTeX; class TestController extends Controller { /** * Download PDF generated from LaTex * * @return Illuminate\Http\Response */ public function download(){ return (new LaraTeX('latex.tex'))->with([ 'Name' => 'John Doe', 'Dob' => '01/01/1990', 'SpecialCharacters' => '$ (a < b) $', 'languages' => [ 'English', 'Spanish', 'Italian' ] ])->download('test.pdf'); } }
如果您的blade文件有不同的名称或它在另一个文件夹中,请确保正确设置blade文件:return (new LaraTeX('folder.file'))
保存 PDF 文件
要保存PDF文件,请使用savePdf
方法。
savePdf(string $location)
(new LaraTeX('latex.tex'))->with([ 'Name' => 'John Doe', 'Dob' => '01/01/1990', 'SpecialCharacters' => '$ (a < b) $', 'languages' => [ 'English', 'Spanish', 'Italian' ] ])->savePdf(storage_path('app/export/test.pdf'));
请确保目标文件夹位于您的storage
文件夹内。
返回 PDF 内容
要仅获取PDF内容作为RAW或base64,请使用content
方法。
content(string $type = 'raw')
默认是raw
。
(new LaraTeX('latex.tex'))->with([ 'Name' => 'John Doe', 'Dob' => '01/01/1990', 'SpecialCharacters' => '$ (a < b) $', 'languages' => [ 'English', 'Spanish', 'Italian' ] ])->content();
或使用base64
(new LaraTeX('latex.tex'))->with([ 'Name' => 'John Doe', 'Dob' => '01/01/1990', 'SpecialCharacters' => '$ (a < b) $', 'languages' => [ 'English', 'Spanish', 'Italian' ] ])->content('base64');
以内联形式返回 PDF
要仅获取PDF内联,请使用inline
方法。
inline(string $fileName = null)
(new LaraTeX('latex.tex'))->with([ 'Name' => 'John Doe', 'Dob' => '01/01/1990', 'SpecialCharacters' => '$ (a < b) $', 'languages' => [ 'English', 'Spanish', 'Italian' ] ])->inline('filename.pdf');
这将返回作为内联文档流显示的PDF,名称为filename.pdf
。
返回 TeX 数据
render()
$tex = new LaraTeX('latex.tex'))->with([ 'Name' => 'John Doe', 'Dob' => '01/01/1990', 'SpecialCharacters' => '$ (a < b) $', 'languages' => [ 'English', 'Spanish', 'Italian' ] ])->render();
使用原始 TeX
如果您不想使用视图作为tex文件,但已经具有tex内容,或正在使用其他库生成tex内容,可以使用RawTex
类而不是传递视图路径。
use webstasolutions\LaraTeX\LaraTeX; use webstasolutions\LaraTeX\RawTex; ... $tex = new RawTex('your_raw_tex_content_string.....'); return (new LaraTeX($tex))->with([ 'Name' => 'John Doe', 'Dob' => '01/01/1990', 'SpecialCharacters' => '$ (a < b) $', 'languages' => [ 'English', 'Spanish', 'Italian' ] ])->download('test.pdf');
批量下载 ZIP 存档
您想在一个ZIP压缩文件中导出多个PDF文件?本软件包已为您准备好该功能。这为您提供了极大的灵活性。然而,请确保不要一起传递过多的PDF文件,因为这会消耗大量服务器内存来导出这些文件。
$latexCollection = (new LaratexCollection()); $users = User::limit(10)->get(); foreach ($users as $user) { $pdfName = $user->first_name.'-'.$user->last_name.'-'.$user->id.'.pdf'; // Create LaraTeX instance $laratex= (new LaraTeX('latex.report'))->with([ 'user' => $user ])->setName($pdfName); // Add it to latex collection $latexCollection->add($laratex); } // Download the zip return $latexCollection->downloadZip('Users.zip'); // OR you can also save it $latexCollection->saveZip(storage_path('app/pdf/zips/Users.zip'));
HTML 到 LaTeX 转换 BETA
convertHtmlToLatex(string $Input, array $Override = NULL)
由于我之前遇到过将数据发送到LaTeX视图时数据以HTML格式存在的情况,我决定添加一个解析器,将基本的HTML字符串转换为LaTeX。其中包含一组HTML标签及其转换方式。注意:在转换结束时,所有不在默认转换集或覆盖转换集中的HTML标签都将通过strip_tags()
被移除。
如果您需要该功能但需要将某个HTML标签以不同的方式转换,可以向方法发送一个覆盖数组。这个覆盖数组需要如下所示:
$Override = array( array('tag' => 'img', 'extract' => 'src', 'replace' => '\begin{center}\includegraphics[scale=1]{$1}\end{center}'), array('tag' => 'body', 'extract' => 'value', 'replace' => '$1 \newline '), );
数组键的解释
下面的代码片段展示了转换过程是如何工作的
$HTMLString = '<h1>Heading 1</h1> <p>Text</p> <h2>Heading 2</h2> <p>Text</p> <h3>Heading 3</h3> <p>Text</p> <p>Normal text here with some <strong>strong</strong> and <strong>bold</strong> text.</p> <p>There is also text that could be <u>underlined</u>.</p> <p>Or of course we could have <em>em-wrapped</em> or <em>i-wrapped</em> text</p> <p>A special test could be a <u><em><strong>bold, underlined and italic</strong></em></u> text at the same time!</p> <p>For the mathematicians we also have calculations x<sup>2</sup> and chemical stuff H<sub>2</sub>O</p> <p>We also have lists that needs to be shown. For example an unordered and an ordered list.</p> <p>If there is alot of text we might also want to use a line break <br> to continue on the next line.</p> <ul> <li>UL Item 1 <ul> <li>UL Item 1.1</li> <li>UL Item 1.2</li> </ul> </li> <li>UL Item 2</li> <li>UL Item 3</li> </ul> <ol> <li>UL Item 1</li> <li>UL Item 2</li> <li>UL Item 3</li> </ol> <p>Last but not least. We have images.</p> <img src="/images/testimages/image1.png" /> <img src="/images/testimages/image2.png" >'; $Override = array( array('tag' => 'img', 'extract' => 'src', 'replace' => '\begin{center}\includegraphics[scale=1]{$1}\end{center}'), array('tag' => 'body', 'extract' => 'value', 'replace' => '$1 \newline '), ); $LatexString = (new LaraTeX)->convertHtmlToLatex($HTMLString, $Override);
此示例将返回以下LaTeX字符串
\section{Heading 1} Text \newline \subsection{Heading 2} Text \newline \subsubsection{Heading 3} Text \newline Normal text here with some \textbf{strong} and \textbf{bold} text. \newline There is also text that could be \underline{underlined}. \newline Or of course we could have \textit{em-wrapped} or \textit{i-wrapped} text \newline A special test could be a \underline{\textit{\textbf{bold, underlined and italic}}} text at the same time! \newline For the mathematicians we also have calculations x\textsuperscript{2} and chemical stuff H\textsubscript{2}O \newline We also have lists that needs to be shown. For example an unordered and an ordered list. \newline If there is alot of text we might also want to use a line break \newline to continue on the next line. The br tag can have a leading slash too. \newline \begin{itemize} \item UL Item 1 \begin{itemize} \item UL Item 1.1 \item UL Item 1.2 \end{itemize} \item UL Item 2 \item UL Item 3 \end{itemize} \begin{enumerate} \item UL Item 1 \item UL Item 2 \item UL Item 3 \end{enumerate} Last but not least. We have images. \newline \begin{center}\includegraphics[scale=1]{/images/testimages/image1.png}\end{center} \begin{center}\includegraphics[scale=1]{/images/testimages/image2.png}\end{center} \newline
监听事件
每当成功生成PDF时,都会触发事件LaratexPdfWasGenerated
。同样,每当PDF生成失败时,都会触发事件LaratexPdfFailed
。
如果您需要根据生成状态执行某些操作,如更新数据库,则这些事件非常重要。但大多数PDF文件都有一些元数据,例如PDF属于哪个用户或属于哪个订单。您可以在实例化LaraTeX
时作为第二个参数传递这些元数据。
然后,从触发的事件中返回这些元数据,这使得监听更加有意义。元数据可以是任何东西,可以是字符串、数字、数组、对象、集合等。您可以根据所需的逻辑传递元数据。
// $user will be our metadata in this example $user = Auth::user(); (new LaraTeX('latex.tex', $user))->with([ 'Name' => 'John Doe', 'Dob' => '01/01/1990', 'SpecialCharacters' => '$ (a < b) $', 'languages' => [ 'English', 'Spanish', 'Italian' ] ])->savePdf(storage_path('app/pdf/test.pdf'));
然后您可以定义一个监听器,如下所示:
<?php namespace App\Listeners; use webstasolutions\LaraTeX\LaratexPdfWasGenerated; class LaratexPdfWasGeneratedConfirmation { /** * Create the event listener. * * @return void */ public function __construct() { // } /** * Handle the event. * * @param LatexPdfWasGenerated $event * @return void */ public function handle(LaratexPdfWasGenerated$event) { // Path of pdf in case in was saved // OR // Downloaded name of pdf file in case it was downloaded in response directly $pdf = $event->pdf; // download OR savepdf $action = $event->action; // metadata $user in this example $user = $event->metadata; // Perform desired actions } }
垃圾收集
当您导出PDF时,pdflatex
会生成一些额外文件,与您的PDF文件一起(例如.aux
、.log
、.out
等)。软件包会内部处理垃圾收集过程,确保在生成PDF或发生任何错误时没有文件残留。
这确保服务器不会浪费空间保留这些文件。
错误处理
我们使用来自texlive
的应用pdflatex
生成PDF。如果您的tex文件中发生语法错误,它将错误记录到日志文件中。或者如果它被关闭,它将在控制台中显示输出。
软件包也会内部处理相同的逻辑并抛出ViewNotFoundException
。异常将包含有关错误的所有信息,便于您调试。
贡献
如果您想为此软件包添加新功能,请随时贡献。
鸣谢
此软件包受到了Techsemicolon创建的laravel-php-latex
软件包的很大启发。后来,由于另一个软件包缺乏支持,我开始创建自己的laravel-php-latex
版本webstasolutions/laravel-php-latex。
为了更好的兼容性和更好的配置处理,我决定创建这个软件包。
变更日志
有关任何重大变更的更多信息,请参阅变更日志。
许可
MIT许可证(MIT)。有关更多信息,请参阅许可文件。