写在前面
在学习和工作中,对于PDF的识别和导出都是相对让人头疼的事情;
在此,给大家安利一种鄙人认为还比较不错的 pdf导出方式;
本文基于 命令行导出工具 进行PDF导出;只是进行简单使用,没有进行深入探究;
如果需要进行深入学习的盆友,可移步官方文档进行学习。
GitHub:github.com/mikehaertl/…
wkhtmltopdf:wkhtmltopdf.org/
开发环境
- 服务器环境:
CentOS7 - php版本:
8.1.19 - phpwkhtmltopdf 扩展版本:
2.5.0 - wkhtmltopdf 工具版本:
wkhtmltox-0.12.6-1.centos7.x86_64.rpm
实操
安装扩展包
composer require mikehaertl/phpwkhtmltopdf
安装导出工具
-
下载
根据自己的实际开发环境,下载对应的工具即可
https://wkhtmltopdf.org/downloads.html -
安装
-
上传导出工具到服务器
-
执行安装
rpm -ivh wkhtmltox-0.12.6-1.centos7.x86_64.rpm -
安装问题
-
原因:缺少依赖
-
解决:安装依赖即可
yum install -y libXrender xorg-x11-fonts-75dpi xorg-x11-fonts-Type1 -
安装完依赖后,重新执行安装命令,即可安装成功
-
检测是否安装成功
-
-
代码准备
具体内容根据实际需求进行调整即可
页眉 (header.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>页眉</title>
<style>
.report-title{
text-align: left;
font-size: 24px;
font-weight: bold;
}
.report-title img{
width: 50px;
height: 25px;
}
</style>
</head>
<body>
<div style="padding: 0 0 20px 0;">
<div class="report-title">
<!--<img src="./images/header.jpg">-->
<!--需要注意:此处只能使用网络地址-->
<img src="http://IP地址:端口号/storage/images/header.jpg">
<span>这是页眉文本</span>
</div>
</div>
</body>
</html>
页脚 (footer.html)
其中,subst() 方法是固定写法,自定义页码信息
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>页脚</title>
<style>
.page-info {
float: right;
text-align: right;
}
</style>
<script>
function subst() {
var vars = {};
var x = document.location.search.substring(1).split('&');
for (var i in x) {
var z = x[i].split('=', 2);
vars[z[0]] = unescape(z[1]);
}
var x = ['frompage', 'topage', 'page', 'webpage', 'section', 'subsection', 'subsubsection'];
for (var i in x) {
var y = document.getElementsByClassName(x[i]);
for (var j = 0; j < y.length; ++j) y[j].textContent = vars[x[i]];
}
}
</script>
</head>
<body style="border:0; margin: 0;" onload="subst()">
<div>
<span>联系地址:XXXXXXXXXXXXXXX</span>
<div class="page-info">
Page <span class="page"></span> of <span class="topage"></span>
</div>
</div>
</body>
</html>
封面 (cover.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> 封面 </title>
<style>
body {
text-align: center;
}
</style>
</head>
<body>
<div>
<h1>这是封面页</h1>
</div>
</body>
</html>
目录 (toc.xsl)
xml 格式页面,其中,界面元素信息写法格式固定,class可以自定义,样式可以自定义
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:outline="http://wkhtmltopdf.org/outline"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
indent="yes" />
<xsl:template match="outline:outline">
<html>
<head>
<title>Table of Contents</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
<!--h1 {
text-align: center;
font-size: 20px;
font-family: arial;
}
div {
border-bottom: 1px rgb(200,200,200);
}
span {
float: right;
}
li {
list-style: none;
}
ul {
font-size: 20px;
font-family: arial;
}
ul ul {
font-size: 80%;
}
ul {
padding-left: 0em;
}
ul ul {
padding-left: 1em;
}
a {
text-decoration:none; color: black;
}-->
h1 {
text-align: center;
font-size: 20px;
}
li {
list-style: none;
}
a {
text-decoration:none; color: black;
}
.toc {
background-color: #666;
height: 50px;
line-height: 50px;
width: 20%;
border-status:
}
.toc span {float: left; font-size: 24px;}
.toc .en {font-size: 14px; padding: 5px 0 0 8px;}
li div{
background-color: #666;
margin-bottom: 10px;
display: flex;
}
.sub-ul li div{
background-color: red;
margin-top: 10px;
}
li a{
height: 50px;
line-height: 50px;
}
</style>
</head>
<body>
<div class="toc">
<span class="zh">目录</span>
<span class="en">CONTENTS</span>
</div>
<ul><xsl:apply-templates select="outline:item/outline:item"/></ul>
</body>
</html>
</xsl:template>
<xsl:template match="outline:item">
<li>
<xsl:if test="@title!=''">
<div>
<a>
<xsl:if test="@link">
<xsl:attribute name="href"><xsl:value-of select="@link"/></xsl:attribute>
</xsl:if>
<xsl:if test="@backLink">
<xsl:attribute name="name"><xsl:value-of select="@backLink"/></xsl:attribute>
</xsl:if>
<xsl:value-of select="@title" />
</a>
<!--<span> <xsl:value-of select="@page" /> </span>-->
</div>
</xsl:if>
<ul class="sub-ul">
<xsl:comment>added to prevent self-closing tags in QtXmlPatterns</xsl:comment>
<xsl:apply-templates select="outline:item"/>
</ul>
</li>
</xsl:template>
</xsl:stylesheet>
内容 (contents.html)
由于鄙人对前端样式不熟悉,就只能写最简单的方式说明使用即可
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
p {
font-size: 18px;
font-weight: bold;
background: #ffa854;
}
.red {
color: red;
}
.green {
color: green;
}
.blue {
color: blue;
}
table {
border: #000000 2px solid;
}
td {
border: #d23636 2px solid;
}
</style>
</head>
<body>
<div>
<p class="red">这里是自定义样式测试文本(红色)</p>
<p class="green">这里是自定义样式测试文本(绿色)</p>
<p class="blue">这里是自定义样式测试文本(蓝色)</p>
</div>
<div>
<table>
<thead>
<tr>
<td>编号</td><td>姓名</td><td>邮箱</td>
</tr>
</thead>
<tbody>
<tr>
<td>1</td><td>张三</td><td>zhangsan@qq.com</td>
</tr>
<tr>
<td>2</td><td>李四</td><td>lisi@qq.com</td>
</tr>
<tr>
<td>3</td><td>王麻子</td><td>wangmazi@qq.com</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
PHP代码
此处按照最简单的方式,新建一个控制器和方法
本文使用 hyperf 3.0 框架,所以代码格式按照框架格式编写,根据实际情况按照自己的方式编写即可
<?php
namespace App\Controller;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;
use mikehaertl\wkhtmlto\Pdf;
#[Controller(prefix: "pdf/export")]
class PdfExportController extends AbstractController
{
#[GetMapping(path: "test")]
public function test()
{
$header_html = BASE_PATH . '/pdf_export/header.html';
$footer_html = BASE_PATH . '/pdf_export/footer.html';
$cover_html = BASE_PATH . '/pdf_export/cover.html';
$contents_html = BASE_PATH . '/pdf_export/contents.html';
// 实例化
$pdf = new Pdf([
'commandOptions' => [
'useExec' => true,
'escapeArgs' => false,
'procOptions' => array(
// This will bypass the cmd.exe which seems to be recommended on Windows
'bypass_shell' => true,
// Also worth a try if you get unexplainable errors
'suppress_errors' => true,
),
],
// 默认页面选项
'disable-smart-shrinking',
'encoding' => 'UTF-8', // option with argument
// 页眉设置
'header-html' => $header_html,
'header-line',
'header-spacing' => 5,
// 设置页脚
'footer-html' => $footer_html,
'footer-line',
'footer-spacing' => 5,
]);
// 设置封面
$pdf->addCover($cover_html);
// 设置目录
$tocPath = BASE_PATH . '/pdf_export/toc.xsl';
$toc_options = [
'toc-header-text' => '目录',
'disable-dotted-lines',
'disable-toc-links',
'xsl-style-sheet' => $tocPath,
];
$pdf->addToc($toc_options);
// 添加内容
$pdf->addPage('<h1>Hello, 世界!</h1>');
$pdf->addPage($contents_html);
// 保存PDF文件
$pdfPath = BASE_PATH . '/pdf_export/export/测试PDF-' . time() . '.pdf';
$pdf->saveAs($pdfPath);
}
}
-
访问控制器方法进行 PDF 生成
-
当我们打开生成的PDF文件时,可能会出现中文显示问题。
-
原因:一般是 我们的服务器环境缺少相应的中文字体导致的
-
解决
-
可以从 windows 环境中找到已安装的字体
-
复制出来,并上传到服务器环境
/usr/share/fonts位置即可(此处我们复制 宋体 simsun.ttc) -
复制成功后,再次执行,即可显示正常
-
-