2026-01-05 14:33:21 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
// app/service/pdf/MpdfDriver.php
|
|
|
|
|
|
namespace app\common\utils\pdf\service;
|
|
|
|
|
|
|
|
|
|
|
|
use Mpdf\Mpdf;
|
|
|
|
|
|
use Mpdf\Config\ConfigVariables;
|
|
|
|
|
|
use Mpdf\Config\FontVariables;
|
|
|
|
|
|
use Mpdf\HTMLParserMode;
|
|
|
|
|
|
|
|
|
|
|
|
class MpdfDriver
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
public function htmlToPdf(string $html, array $options = []): string
|
|
|
|
|
|
{
|
|
|
|
|
|
$opts = array_merge([
|
2026-01-09 13:08:17 +08:00
|
|
|
|
'format' => 'A4',
|
|
|
|
|
|
'orientation' => 'L',
|
|
|
|
|
|
'margin_top' => 12,
|
2026-01-05 14:33:21 +08:00
|
|
|
|
'margin_bottom' => 12,
|
2026-01-09 13:08:17 +08:00
|
|
|
|
'margin_left' => 10,
|
|
|
|
|
|
'margin_right' => 10,
|
|
|
|
|
|
'tempDir' => runtime_path('mpdf'),
|
|
|
|
|
|
'header_html' => '',
|
|
|
|
|
|
'footer_html' => '',
|
|
|
|
|
|
'watermark_text' => '',
|
|
|
|
|
|
'css_files' => [],
|
|
|
|
|
|
'default_font' => 'notosanssc',
|
|
|
|
|
|
'font_dirs' => [public_path('fonts')],
|
|
|
|
|
|
'font_data' => [
|
|
|
|
|
|
'notosanssc' => ['R' => 'NotoSansSC-Regular.ttf', 'B' => 'NotoSansSC-Bold.ttf'],
|
2026-01-05 14:33:21 +08:00
|
|
|
|
],
|
2026-01-09 13:08:17 +08:00
|
|
|
|
'asset_base' => '',
|
2026-01-05 14:33:21 +08:00
|
|
|
|
'enable_html5_parser' => true,
|
|
|
|
|
|
], $options);
|
|
|
|
|
|
|
2026-01-09 13:08:17 +08:00
|
|
|
|
$defaultConfig = (new ConfigVariables())->getDefaults();
|
2026-01-05 14:33:21 +08:00
|
|
|
|
$defaultFontConfig = (new FontVariables())->getDefaults();
|
|
|
|
|
|
|
|
|
|
|
|
$fontDirs = array_merge($defaultConfig['fontDir'], $opts['font_dirs']);
|
|
|
|
|
|
$fontData = array_merge($defaultFontConfig['fontdata'], $opts['font_data']);
|
|
|
|
|
|
|
|
|
|
|
|
$mpdf = new Mpdf([
|
2026-01-09 13:08:17 +08:00
|
|
|
|
'format' => $opts['format'],
|
|
|
|
|
|
'orientation' => $opts['orientation'],
|
|
|
|
|
|
'margin_left' => $opts['margin_left'],
|
|
|
|
|
|
'margin_right' => $opts['margin_right'],
|
|
|
|
|
|
'margin_top' => $opts['margin_top'],
|
2026-01-05 14:33:21 +08:00
|
|
|
|
'margin_bottom' => $opts['margin_bottom'],
|
2026-01-09 13:08:17 +08:00
|
|
|
|
'tempDir' => $opts['tempDir'],
|
|
|
|
|
|
'fontDir' => $fontDirs,
|
|
|
|
|
|
'fontdata' => $fontData,
|
|
|
|
|
|
'default_font' => $opts['default_font'],
|
2026-01-05 14:33:21 +08:00
|
|
|
|
'enable_html5_parser' => $opts['enable_html5_parser'],
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
if ($opts['header_html']) $mpdf->SetHTMLHeader($opts['header_html']);
|
|
|
|
|
|
if ($opts['footer_html']) $mpdf->SetHTMLFooter($opts['footer_html']);
|
|
|
|
|
|
|
|
|
|
|
|
if (!empty($opts['watermark_text'])) {
|
|
|
|
|
|
$mpdf->SetWatermarkText($opts['watermark_text']);
|
|
|
|
|
|
$mpdf->showWatermarkText = true;
|
|
|
|
|
|
$mpdf->watermarkTextAlpha = 0.08;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 资源绝对化(如果你有相对路径的图片等)
|
|
|
|
|
|
$html = PdfHelper::absolutizeAssetUrls($html, $opts['asset_base']);
|
|
|
|
|
|
|
|
|
|
|
|
// === 关键改动:把 <style>…</style> 抽出来,作为 HEADER_CSS 写入 ===
|
|
|
|
|
|
[$inlineCss, $cleanHtml] = $this->extractCssAndCleanHtml($html);
|
|
|
|
|
|
|
|
|
|
|
|
// 先写外部 css_files
|
|
|
|
|
|
if (!empty($opts['css_files'])) {
|
|
|
|
|
|
foreach ($opts['css_files'] as $cssFile) {
|
|
|
|
|
|
if (is_file($cssFile)) {
|
|
|
|
|
|
$mpdf->WriteHTML(file_get_contents($cssFile), HTMLParserMode::HEADER_CSS);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 再写内联 <style> 的 CSS
|
|
|
|
|
|
if ($inlineCss !== '') {
|
|
|
|
|
|
$mpdf->WriteHTML($inlineCss, HTMLParserMode::HEADER_CSS);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 最后写 HTML 正文(已剔除 <style>,避免任何情况下把 CSS 当文字打印)
|
|
|
|
|
|
$mpdf->WriteHTML($cleanHtml, HTMLParserMode::HTML_BODY);
|
|
|
|
|
|
|
|
|
|
|
|
return $mpdf->Output('', \Mpdf\Output\Destination::STRING_RETURN);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 把所有 <style>...</style> 的内容拼成一段 CSS,并把这些 <style> 标签从 HTML 中移除
|
|
|
|
|
|
* @return array{0:string,1:string} [css, htmlWithoutStyle]
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function extractCssAndCleanHtml(string $html): array
|
|
|
|
|
|
{
|
|
|
|
|
|
$cssParts = [];
|
|
|
|
|
|
// 抓取所有 <style ...>...</style>,DOTALL 支持跨行
|
|
|
|
|
|
if (preg_match_all('/<style\b[^>]*>(.*?)<\/style>/is', $html, $m)) {
|
|
|
|
|
|
foreach ($m[1] as $block) {
|
|
|
|
|
|
// 去掉 HTML 注释,以防注释把 CSS 包起来
|
|
|
|
|
|
$block = preg_replace('/<!--|-->/', '', $block);
|
|
|
|
|
|
$cssParts[] = trim($block);
|
|
|
|
|
|
}
|
|
|
|
|
|
// 从 HTML 删除所有 <style> 块
|
|
|
|
|
|
$html = preg_replace('/<style\b[^>]*>.*?<\/style>/is', '', $html);
|
|
|
|
|
|
}
|
|
|
|
|
|
$css = implode("\n\n", $cssParts);
|
|
|
|
|
|
|
|
|
|
|
|
// 额外保险:把 <head> 也清理掉,防止某些解析器把 head 里残余文本当正文
|
|
|
|
|
|
$html = preg_replace('/<head\b[^>]*>.*?<\/head>/is', '', $html);
|
|
|
|
|
|
|
|
|
|
|
|
// 确保剩余 HTML 有 <body>,没有就包一层
|
|
|
|
|
|
if (!preg_match('/<body\b/i', $html)) {
|
|
|
|
|
|
$html = '<body>' . $html . '</body>';
|
|
|
|
|
|
}
|
|
|
|
|
|
return [$css, $html];
|
|
|
|
|
|
}
|
2026-01-09 13:08:17 +08:00
|
|
|
|
|
|
|
|
|
|
public function exportUsedCarPdf($car)
|
|
|
|
|
|
{
|
|
|
|
|
|
/*$car = [
|
|
|
|
|
|
'title' => '奥迪Q3 2013款 35 TFSI 舒适型',
|
|
|
|
|
|
'vin' => '',
|
|
|
|
|
|
'price' => 450000,
|
|
|
|
|
|
'source_city' => '北京',
|
|
|
|
|
|
'register_city' => '随州',
|
|
|
|
|
|
'register_date' => '2026-01-05',
|
|
|
|
|
|
'displacement' => '2.1T',
|
|
|
|
|
|
'mileage' => 45000,
|
|
|
|
|
|
'power' => 350,
|
|
|
|
|
|
'color' => '',
|
|
|
|
|
|
'created_at' => '2026-01-05 17:23:34',
|
|
|
|
|
|
'images' => [
|
|
|
|
|
|
'https://xxx.com/1.jpg',
|
|
|
|
|
|
'https://xxx.com/2.jpg',
|
|
|
|
|
|
'https://xxx.com/3.jpg',
|
|
|
|
|
|
],
|
|
|
|
|
|
'description' => '该车内饰干净整洁,车况精品...'
|
|
|
|
|
|
];*/
|
|
|
|
|
|
|
|
|
|
|
|
ob_start();
|
|
|
|
|
|
//include app_path() . '/view/pdf/car_detail_pdf.php';
|
|
|
|
|
|
$tpl = base_path('app/common/utils/pdf/template/usedCar.stub');
|
|
|
|
|
|
include $tpl;
|
|
|
|
|
|
$html = ob_get_clean();
|
|
|
|
|
|
|
|
|
|
|
|
$mpdf = new Mpdf([
|
|
|
|
|
|
'mode' => 'utf-8',
|
|
|
|
|
|
'format' => 'A4',
|
|
|
|
|
|
'margin_top' => 10,
|
|
|
|
|
|
'margin_bottom' => 10,
|
|
|
|
|
|
'margin_left' => 10,
|
|
|
|
|
|
'margin_right' => 10,
|
|
|
|
|
|
'default_font' => 'sun-exta'
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
$mpdf->WriteHTML($html);
|
|
|
|
|
|
|
|
|
|
|
|
$pdf = $mpdf->Output('车辆详情.pdf', 'S');
|
|
|
|
|
|
$dir = runtime_path('pdf');
|
|
|
|
|
|
if (!is_dir($dir)) mkdir($dir, 0777, true);
|
|
|
|
|
|
$pdfName = '/invoice-' . date('YmdHis').'-'.$car['id'] . '.pdf';
|
|
|
|
|
|
$path = $dir . $pdfName;
|
|
|
|
|
|
file_put_contents($path, $pdf);
|
|
|
|
|
|
|
|
|
|
|
|
return $path;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-05 14:33:21 +08:00
|
|
|
|
}
|