[Laravel5] Xuất ra file PDF sử dụng Laravel-Snappy(wkhtmltopdf)
Bài đăng này đã không được cập nhật trong 6 năm
Việc xuất ra file PDF trong Laravel bạn có thể search Google ra rất nhiều kết quả và có lẽ TCPDF sẽ xuất hiện nhiều nhất trên các trang tut. Tôi cũng đã thử nó, thực sự là rất dễ dàng để đưa ra được PDF. Nhưng mà nó có mặt hạn chế về CSS. Ví dụ khi tôi thư background: red;
thì không có hiệu quả. Thật là ... !
TCPDF nó chỉ có su bóp những CSS sau mà thôi :
- font-family
- font-size
- font-weight
- font-style
- color
- background-color
- text-decoration
- width
- height
- text-align
Nguồn tham khảo : http://stackoverflow.com/questions/11395171/why-does-tcpdf-ignore-my-inline-css
Và tôi đã tìm kiếm thư viện không những xuất ra được PDF mà còn phải hỗ trợ tốt những CSS bình thường, tôi đã thấy - chính là Laravel-Snappy !
Nhưng thư viện này liệu có dùng cho các trang thương mại ko? Có chứ. Bởi nó là MIT license, tuyệt ! Bạn có thể tham khảo tại đây !
Implement
Chuẩn bị thư viện cần thiết
Thư viện cơ bẩn nhất cần thiết là wkhtmltopdf
.
Khi tôi cài thực tế thì bị báo lỗi error while loading shared libraries: libXrender.so.1
, và để mà giải quyết thì cần thực hiện yum install libXrender.so.1
. Đoạn này thì phức tạp hơn TCPDF, nhưng có lẽ có cách tốt hơn mà tôi chưa tìm ra thôi.
yum install libXext libXext.i686 libXrender libXrender.so.1 fontconfig libfontconfig.so.1 zlib.i686 libstdc++.i686
Còn nếu là bản 64bit thì chỉ cần thế này là ngon ! Nhưng cần chú ý phần dưới sẽ cần thay chỗ 「i386」thành 「amd64」.
yum install wkhtmltopdf
Cài đặt
composer require h4cc/wkhtmltopdf-i386 0.12.x
composer require h4cc/wkhtmltoimage-i386 0.12.x
composer require barryvdh/laravel-snappy
Đăng kí alias
config/app.php
'providers' => [
Barryvdh\Snappy\ServiceProvider::class,
];
'aliases' => [
'PDF' => Barryvdh\Snappy\Facades\SnappyPdf::class,
'SnappyImage' => Barryvdh\Snappy\Facades\SnappyImage::class,
];
Tiếp theo sẽ chuẩn bị trong thư mục config file snappy.php
php artisan vendor:publish --provider="Barryvdh\Snappy\ServiceProvider"
Ở chỗ /vendor/barryvdh/laravel-snappy/config/snappy.php
có file setting của snappy, và đọc wkhtmltopdf
từ /usr/local/bin/wkhtmltopdf
.
Nhưng tôi sẽ override lại :
config/snappy.php
<?php
return array(
'pdf' => array(
'enabled' => true,
'binary' => base_path('vendor/h4cc/wkhtmltopdf-i386/bin/wkhtmltopdf-i386'), // Tôi thay đổi chỗ này
'timeout' => false,
'options' => array(),
'env' => array(),
),
'image' => array(
'enabled' => true,
'binary' => base_path('vendor/h4cc/wkhtmltoimage-i386/bin/wkhtmltoimage-i386'), // và chỗ này
'timeout' => false,
'options' => array(),
'env' => array(),
),
);
Ở trong docs của nó có ghi là wkhtmltopdf-amd64
nhưng mà đó là trường hợp bạn cài đặt bản 64bit ( composer require h4cc/wkhtmltopdf-amd64 0.12.x ).
Còn trường hợp tôi cài là bản 32bit nên sẽ là i386.
Hello World
Nào, đến đây thì đã có thể chuẩn bị để xuất ra file PDF được rồi đó !
Đầu tiên là sẽ chuẩn bị file View có chữ Hello World
, gọi bằng Controller để export ra.
Chuẩn bị file cần thiết
php artisan make:controller TestController
touch resources/views/print.blade.php
View
resources/views/print.blade.php
<p style="background: red">hello world</p>
Controller
app/Http/Controller/TestController.php
public function getPrint()
{
$pdf = \PDF::loadView('print');
return $pdf->download('print.pdf');
}
Routing
Nếu như tôi đang dùng thì 5.2 sẽ là file app/Http/routes.php
.
routes/web.php
Route::get('/print', 'TestController@getPrint')->name('getprint');
Run
Nếu khi bạn access vào example.com/print
mà PDF được download thì đã ok, việc tiếp theo sẽ là lưu các giá trị của forrm vào biến và hiển thị nó ra.
In ra giá trị của POST
Chuẩn bị file cần thiết
touch resources/views/index.blade.php
View
resources/views/index.blade.php
<div>
<p>Exporting PDF Test</p>
<form action="print" method="post">
{{ csrf_field() }}
<input type="text" name="test" value="テスト" />
<input type="submit" value="Send" />
</form>
</div>
resources/views/print.blade.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<meta name="Keywords" content="" />
<meta name="Description" content="" />
<title></title>
</head>
<body>
<p style="background: red">{{ $data['test'] }}</p>
</body>
</html>
Controller
app/Http/Controller/TestController.php
public function index()
{
return view('index');
}
public function getPrint()
{
return redirect('/');
}
public function postPrint()
{
$data = \Request::all();
$pdf = \PDF::loadView('print', ['data' => $data]);
return $pdf->download('print.pdf');
}
Routing
routes/web.php
Route::get('/', 'TestController@index')->name('index');
Route::get('/print', 'TestController@getPrint')->name('getprint');
Route::post('/print', 'TestController@postPrint')->name('postprint');
Run
Khi mà truy cập vào example.com/
thì bạn chắc sẽ thấy chữ 「テスト」đã có sẵn trong form do đã chỉ định value rồi, nếu click nút Send, thì bạn sẽ thấy PDF được download về và có nội dung ghi 「テスト」. Nếu trường hợp mà bạn không nhìn thấy được tiếng Nhật hiển thị đúng thì có lẽ cần phải cài thêm font :
yum install ipa-gothic-fonts ipa-mincho-fonts ipa-pgothic-fonts ipa-pmincho-fonts
Nếu như mà không muốn download về mà preview trước thì cũng rất đơn giản không phức tạp như TCPDF :
app/Http/Controller/TestController.php
public function postPrint()
{
$data = \Request::all();
$pdf = \PDF::loadView('print', ['data' => $data]);
//return $pdf->download('print.pdf');
return $pdf->inline('print.pdf');
}
Vậy là xong !
More
Giờ tôi không muốn preview nữa mà sẽ upload lên thư mục chỉ định (tôi dùng public) để có thể truy cập vào được, và còn muốn đặt tên file là dạng kiểu 「test_20161116140000.pdf」.
Tạo thư mục
mkdir public/pdf
Gán quyền
chown -R apache:apache public/pdf
Chuẩn bị file cần thiết
touch resources/views/complete.blade.php
Controller
Do postPrint
không cần nữa nên tôi xoá đi.
app/Http/Controller/TestController.php
class TestController extends Controller
{
public function index()
{
return view('index');
}
public function complete()
{
$data = \Request::all();
$location = public_path() . '/pdf/'; // sẽ kiểu là path public : example.com/pdf/test_20161116140000.pdf
$filename = 'test_' . date('YmdHis') . '.pdf';
$pdf = \PDF::loadView('print', ['data' => $data]);
$pdf -> save($location . $filename);
return view('complete') -> with(['filename' => $filename]);
}
}
View
resources/views/index.blade.php
<div>
<p>Exporting PDF Test</p>
<form action="complete" method="post">
{{ csrf_field() }}
<input type="text" name="test" value="テスト" />
<input type="submit" value="Send" />
</form>
</div>
Riêng resources/views/print.blade.php
thì tôi không đổi gì cả.
resources/views/complete.blade.php
<div>
<p>Export Successfully !</p>
<p><a href="/pdf/{{ $filename }}">Download here</a></p>
</div>
Routing
routes/web.php
Route::get('/', 'TestController@index')->name('index');
Route::get('/complete', function () {
return redirect('/');
});
Route::post('/complete', 'TestController@complete')->name('complete');
Vậy là xong demo đơn giản với background : red;
, nhưng bạn hoàn toàn có thể thử những style khác !
Hy vọng bài viết đã giúp ích cho bạn
Nguồn : Qiita.com
All rights reserved