使用PHP将带有样式(css)的html转换为pdf

Posted

技术标签:

【中文标题】使用PHP将带有样式(css)的html转换为pdf【英文标题】:Convert html with styling (css) to pdf using PHP 【发布时间】:2020-10-21 03:18:43 【问题描述】:

我的代码有一些问题!我正在尝试将我的 html 发票转换为 pdf 发票。付款后,发票由以下代码生成。然后pdf文件被存储在服务器上并通过邮件发送给用户。这是我正在使用的发票:invoice。但是当我将 html 转换为 pdf 时,不会应用样式并且不会显示图像。

这是我的 php 代码,用于使用 DomPDF 将 html 转换为 pdf:

<?php
require 'vendor/autoload.php';
// reference the Dompdf namespace

$files = glob("/invoices/*.pdf");

foreach ($files as $file) include_once($file);

// instantiate and use the dompdf class
$dompdf = new Dompdf();
$dompdf->loadHtml($html);
$dompdf->setPaper('A4', 'portrait'); //landscape / portrait

$options = new Options();
$options->set('isRemoteEnabled', true);
$options->set('defaultFont', 'OpenSans');

// Render the HTML as PDF
$dompdf->render();

$output = $dompdf->output();
file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/invoices/" . $order_id . ".pdf", $output);

我认为我的问题在于HTML 代码。

css 的问题

我已经尝试了几件事,但这些都不适合我!这是我尝试过的:

试试 1

我尝试添加带有link 标签的css,如下所示:

$html .= '&lt;link type="text/css" href="/absolute/path/to/pdf.css" rel="stylesheet" /&gt;'; 我也尝试像正常一样添加link(在文件头部),但是这两次我都没有成功。

试试 2

我还尝试将css 放在style 标记中,就像这样:

<style>
    /* my css here */
</style>

这行不通。我对css 的想法已经不多了。

图片的问题

html 代码包含img 时:

<img src="/path/to/file"  />

图片不显示。即使我将src 更改为https://pathtoimg.com/path/to/file/img.png

希望您知道图像的解决方案以及我正在努力解决的css 问题!已经感谢您的努力!

编辑

我愿意接受有关其他库或其他方法的建议。我还没有找到一个稳定的解决方案,所以我可能看错了方向。如果还有其他解决方案,请告诉我!

【问题讨论】:

这正是 *** 上一个好问题的样子。关于图片,试试这个:***.com/questions/15153139/… 您是否尝试在 CSS href 中包含域?我正在使用另一个库 Knp Snappy 来生成 PDF,这就是我需要做的。 @MeesEgberts 我上面的评论中有一个链接。 dompdf:“从本质上讲,dompdf 是(大部分)符合 CSS 2.1 的 HTML 布局,处理大多数 CSS 2.1 和一些 CSS3 属性,” id 建议只使用其他更新的东西。我使用mpdf.github.io,没有任何问题。 @fubar 谢谢你的回答!我会看看并试一试! 【参考方案1】:

这不是一个完整的答案,但我至少可以帮助您解决有关图像的一些问题,并解释为什么Dompdf 不能与invoice 一起使用。

问题

首先,查看您的代码,您似乎也使用了过时的函数set(..) 并且$options 也没有传递给Dompdf。新的例子可以在the develop branch 中找到,所以我们可以通过以下方式解决第一个问题:

// Include autoloader
require 'vendor/autoload.php';
// reference the Dompdf namespace

$files = glob("/invoices/*.pdf");

foreach ($files as $file) include_once($file);

// Reference the Dompdf namespace
use Dompdf\Dompdf;
//Also reference the options namespace
use Dompdf\Options;

//First initialise the options
$options = new Options();
//This way of setting options is new see: https://github.com/dompdf/dompdf/issues/2181

$options->setIsRemoteEnabled(true);
$options->setDefaultFont('OpenSans');
// Instantiate and use the dompdf class with the options
$dompdf = new Dompdf($options);

// Load content from html file
$html = ...; //file_get_contents("invoice.html");
$dompdf->loadHtml($html);
$dompdf->setPaper('A4', 'portrait'); //landscape / portrait



// Render the HTML as PDF
$dompdf->render();
$output = $dompdf->output();
file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/invoices/" . $order_id . ".pdf", $output);

使用这段代码,我们得到以下结果:

(使用jsFiddle 来防止图像占用太多空间。现在它们是内联的)

<img src="https://i.stack.imgur.com/86xNQ.png"  />
<img src="https://i.stack.imgur.com/coyyn.png"  />

这样,至少会呈现徽标。那么还剩下什么? 查看结果,只有部分 html 被正确呈现。所以css文件实际上正在工作。但这里唯一的问题是发票使用flex 等属性,如display: flex 等。然而,这还不被支持,正如 in this GitHub issue 所见。这就是为什么不能正确渲染所有元素的原因。

到目前为止,一种可能的解决方案是使用html2canvas 截取屏幕截图,然后使用jsPdf 将其“转换”为pdf,如in this SO answer 所示。但我不确定这是否适合您的用例。

可能的解决方案

无论如何,从Snappy has no support for flex as well 开始,我已经为这个特定情况创建了一个经过调整的示例,使用html2canvas(确实支持flex)和jsPdf

首先对例如invoice.php在页面顶部添加以下php函数。这个函数toBase64 会将任何'foreign' svg 转换为本地base64 字符串,可以由html2canvas 处理:

function toBase64($url) 
  $data = file_get_contents($url);
  $base64 = 'data:image/svg+xml;base64,' . base64_encode($data);
  return $base64;

要仅制作实际发票的 pdf,请将 id="invoice" 添加到 &lt;div class="card-body p-5 p-md-7 p-lg-11"&gt;,这似乎是发票的容器。

要有效使用php功能,请将带有徽标的&lt;img&gt;标签更改为:

<img class="mb-2" src="<?= toBase64('https://streamlabs.nyc3.cdn.digitaloceanspaces.com/assets/svg/logos/logo-short.svg')?>" >

然后像这样更改“打印”按钮的onclick功能:

<button type="button" class="btn btn-info transition-3d-hover" onclick="showPDF();">
          <i class="fas fa-print mr-2"></i>
          Print
        </button>

最后,在页面底部添加这个javascript函数:

function showPDF() 
   //Create screenshot of body
   html2canvas(document.getElementById('invoice'), scale: 3,scrollY: -window.scrollY).then(canvas => 
      var imgData = canvas.toDataURL('image/jpeg');
      //add the image to, and create the pdf
      var pdf = new jsPDF('P', 'pt', [canvas.width, canvas.height]);
      pdf.addImage(imgData, 'jpeg', 0, 0, canvas.width, canvas.height);

      //set the pdfFinal to be later downloaded
      //convert the pdf to a base64 string to make it uploadable
      let output = btoa(pdf.output());
      $.ajax(
         method: "POST",
         url: "upload.php",
         data: data: output, fileName: 'order-123.pdf',
      ).done(function(data) 
         console.log('all done');
         //view errors
         console.log(data);
      );
        
        //Then, let the client print the document. 
        window.print();
      );


这基本上是对发票进行截图,创建一个base64字符串,并将base64字符串放入pdf中并上传到服务器。 在此示例中,upload.php 是实际将 pdf 保存在服务器上的页面,如下所示:

if(isset($_POST['data']))
  $data = base64_decode($_POST['data']);
  if (isset($_POST['fileName'])) 
    $file = $_POST['fileName'];
   else 
    $file = "order-" . rand(10,1000) . ".pdf";
  
 file_put_contents($file, $data);
 echo "Succes";
 else 
  echo "No Data Sent";

exit();

它将创建以下 pdf:

Chrome and firefox
<img src="https://i.stack.imgur.com/m4gBc.png"  />
Safari
<img src="https://i.stack.imgur.com/w2x8K.png"  />

如您所见,它在 chrome 和 firefox 上比在 safari 上效果更好,因为 fontawesomehtml2canvas 在 safari 上不能很好地协同工作(不知道为什么)。

我希望我能澄清一些事情。如有任何问题,请发表评论!

编辑

因为我在服务器端找不到任何其他支持“flex”的“Dom to pdf”,所以我创建了一个适用于客户端的示例。

【讨论】:

不错的答案!这肯定会帮助人们! 你不能从图像创建一个 pdf 并将其存储在服务器上吗?这样您就可以通过邮件将发票发送给客户! @MeesEgberts 你的意思是截屏,将该图像发送到服务器,然后在服务器上从它创建一个 pdf? 是的,就是这样!这样你就可以将pdf文件存储在服务器上并发送给客户端! @MeesEgberts 我还不知道该怎么做,但也许有人可以编辑我的答案以适应它【参考方案2】:

我以前用过mpdf,所以决定试一试。在我的 Windows 10 上使用 xampp、php 7.4.8

 composer require mpdf/mpdf

代码

<?php
//Fadil Eldin
//8/16/2020
require_once __DIR__ . '/vendor/autoload.php';
$mpdf = new \Mpdf\Mpdf();
$html = file_get_contents('https://gostreamlabs.com/front/html/pages/invoice.html');
$mpdf->WriteHTML("");
$mpdf->WriteHTML($html);
$mpdf->Output();
?>

输出

第二页

【讨论】:

【参考方案3】:

确保您的 HTML&lt;!DOCTYPE html&gt; 开头 并且在head中还包含以下标签&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"/&gt; 使用以下

更新 Dompdf 脚本
$dompdf = new Dompdf();
$dompdf->setPaper('A4', 'portrait');
$dompdf->set_option( 'isHtml5ParserEnabled', true);
$dompdf->set_option( 'isRemoteEnabled', true);
$dompdf->set_option( 'defaultMediaType', 'all' );
$dompdf->set_option( 'isFontSubsettingEnabled', true );
$dompdf->set_option('defaultFont', 'OpenSans');
$dompdf->loadHtml( $html, 'UTF-8' );
$dompdf->render();
// Attachment value 1 for download and 0 for preview PDF
//$dompdf->stream( 'testing.pdf', array( 'Attachment' => 1 ) ); 
$output = $dompdf->output();
file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/invoices/" . $order_id . ".pdf", $output);

【讨论】:

【参考方案4】:

请包含$dompdf-&gt;set_base_path('localhost/exampls/style.css');

<?php
require_once "dompdf/dompdf_config.inc.php";
$dompdf = new DOMPDF();

$html ="
<html>
<head>
<link type="text/css" href="localhost/exampls/style.css" rel="stylesheet" />
</head>
<body>
<table>
  <tr >
    <td class='abc'>testing table</td>
    </tr>
  <tr>
   <td class='def'>Testng header</td>
  </tr>
</table>
</body>
</html>";

$dompdf->load_html($html);
$dompdf->render();
$dompdf->set_base_path('localhost/exampls/style.css');
$dompdf->stream("hello.pdf");
?>

【讨论】:

【参考方案5】:

您是否尝试将图像转换为 base64,然后添加为 url ?它也会使其独立于 url。

这可以帮助您转换为 base64 How to convert an image to base64 encoding?

对于我尝试过的 CSS,它适用于我。

【讨论】:

以上是关于使用PHP将带有样式(css)的html转换为pdf的主要内容,如果未能解决你的问题,请参考以下文章

将 CSS 对象转换为样式标签

使用Java将HTML文件转换为带有图片和样式的PDF [重复]

如何在 ReactJS 中将带有属性的 CSS 转换为 MaterialUI 样式

在 ASP.NET 中将 HTML 转换为 PDF 时保持 CSS 样式[关闭]

使用带有Notify.js jQuery插件的外部自定义CSS

如何将外部 CSS 添加到 HTML '样式'