Html2Pdf.php 6.39 KB
Newer Older
1
2
3
4
5
6
<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 4/17/17
 * Time: 10:17 PM
Carsten  Rose's avatar
Carsten Rose committed
7
8
9
 *
 * Check: CODING.md > 'Print' and 'Download'
 *
10
11
12
13
14
 */

namespace qfq;

require_once(__DIR__ . '/../store/Config.php');
15
require_once(__DIR__ . '/../store/Session.php');
16
require_once(__DIR__ . '/../store/Sip.php');
17
18
require_once(__DIR__ . '/../Constants.php');
require_once(__DIR__ . '/../helper/KeyValueStringParser.php');
19
require_once(__DIR__ . '/../helper/SessionCookie.php');
20
require_once(__DIR__ . '/../helper/Logger.php');
21
22


23
24
25
26
/**
 * Class Html2Pdf
 * @package qfq
 */
27
28
29
30
31
32
33
class Html2Pdf {

    /**
     * @var array
     */
    private $config = array();

34
35
36
37
38
    /**
     * @var \qfq\Session
     */
    private $session = null;

39
40
41
42
43
44
45
46
47
48
    /**
     * @var \qfq\SessionCookie
     */
    private $sessionCookie = null;

    /**
     * @var \qfq\Sip
     */
    private $sip = null;

49
50
51
    /**
     * @var string
     */
Carsten  Rose's avatar
Carsten Rose committed
52
    private $logFile = '';
53

54
55
56
57
58
    /**
     * Read QFQ config. Only SYSTEM_BASE_URL_PRINT and SYSTEM_WKHTMLTOPDF will be used.
     * Check and get all clean _GET Parameter. Build a URL based on SYSTEM_BASE_URL_PRINT and the delivered URL params.
     *
     * @param array $config
Carsten  Rose's avatar
Carsten Rose committed
59
60
     * @param       $phpUnit
     *
61
62
63
     * @throws UserFormException
     * @throws \exception
     */
64
    public function __construct(array $config = array(), $phpUnit = false) {
65
66

        if (count($config) == 0) {
67
            $config = Config::readConfig('');
68
69
70
71
        }

        $this->config = $config;

72
        if (!isset($config[SYSTEM_BASE_URL]) || $config[SYSTEM_BASE_URL] == '') {
73
            throw new \exception('Please configure ' . SYSTEM_BASE_URL, ERROR_HTML2PDF_MISSING_CONFIG);
74
75
        }

76
        if (!isset($config[SYSTEM_CMD_WKHTMLTOPDF]) || $config[SYSTEM_CMD_WKHTMLTOPDF] == '') {
77
            throw new \exception('Please configure ' . SYSTEM_CMD_WKHTMLTOPDF, ERROR_HTML2PDF_MISSING_CONFIG);
78
        }
79

80
        $this->session = Session::getInstance($phpUnit);
81
        $this->sessionCookie = new SessionCookie($config);
82
        $this->sip = new Sip($phpUnit);
83

84
        $this->logFile = $config[SYSTEM_SQL_LOG];
85
86
87
88
89
    }

    /**
     * Return an array with GET params who are clean - they do not violate $pattern.
     *
90
     * @param array $get
91
92
93
94
95
     * @return array
     */
    private function readCleanGetParam(array $get) {

        $param = array();
96
        $pattern = '^[\-_\.,;:\/a-zA-Z0-9]*$';
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

        foreach ($get as $key => $value) {
            if (preg_match("/$pattern/", $value) === 1) {
                $param[$key] = $value;
            }
        }

        return $param;
    }

    /**
     * Set HTML Header to initiate PDF download.
     *
     * @param $filename
     */
    private function setHeader($filename) {

        header("Content-Disposition: inline; filename=\"$filename\"");
        header("Content-Type: application/pdf");
        header("Content-Transfer-Encoding: binary");
    }

119

120
    /**
Carsten  Rose's avatar
Carsten Rose committed
121
122
     * Converts a Webpage (URL) to a PDF file.
     * The URL might be a local Typo3 page (without hostname, starting with the parameter) or a full URL.
Carsten  Rose's avatar
Carsten Rose committed
123
     *
124
     * @param string $token TOKEN_URL | TOKEN_URL_PARAM | TOKEN_PAGE | TOKEN_UID
Carsten  Rose's avatar
Carsten Rose committed
125
     * @param string $url id=exportPage&r=123, www.nzz.ch/issue?id=456
Carsten  Rose's avatar
Carsten Rose committed
126
     *
127
128
129
     * @return string rendered file - please delete later
     * @throws CodeException
     * @throws UserFormException
130
131
     * @throws \exception
     */
132
    public function page2pdf($token, $url) {
133
        $rcArgs = array();
Carsten  Rose's avatar
Carsten Rose committed
134
135
136
137
        $urlParamString = '';
        $host = '';

        switch ($token) {
138
            case TOKEN_UID:
Carsten  Rose's avatar
Carsten Rose committed
139
140
141
            case TOKEN_URL:
                $arr = explode('?', $url, 2);
                $host = $arr[0];
142
                $urlParamString = $arr[1] ?? '';
Carsten  Rose's avatar
Carsten Rose committed
143
144
                break;
            case TOKEN_URL_PARAM:
145
            case TOKEN_PAGE:
146
                $host = $this->config[SYSTEM_BASE_URL];
Carsten  Rose's avatar
Carsten Rose committed
147
148
149
150
                $urlParamString = $url;
                break;
            default:
                break;
151
152
        }

153
        $rcSipEncode = false;
154
        $urlParam = OnString::splitParam($urlParamString, $rcArgs, $rcSipEncode);
155
156
157
158
159

        $rcArgs = OnArray::arrayEscapeshellarg($rcArgs);
        $options = KeyValueStringParser::unparse($rcArgs, ' ', ' ');

        $urlParamString = KeyValueStringParser::unparse($urlParam, '=', '&');
160
        if ($rcSipEncode) {
161
            $urlParamString = $this->sip->queryStringToSip($urlParamString, RETURN_URL);
Carsten  Rose's avatar
Carsten Rose committed
162
163
        }

164
        $url = Support::mergeUrlComponents('', $host, $urlParamString);
Carsten  Rose's avatar
Carsten Rose committed
165

166
        if (substr($url, 0, 4) != 'http' && $token != TOKEN_UID) {
Carsten  Rose's avatar
Carsten Rose committed
167
168
            $url = 'http://' . $url;
        }
Carsten  Rose's avatar
Carsten Rose committed
169
170

        $urlPrint = escapeshellarg($url);
171
        $wkhtmlToPdf = $this->config[SYSTEM_CMD_WKHTMLTOPDF];
172

173
        $filename = HelperFile::tempnam();
174
175
        $filenameEscape = escapeshellarg($filename);

176
        $cookieOptions = '--cookie-jar ' . escapeshellarg($this->sessionCookie->getFile());
177
        $customHeader = '--custom-header User-Agent ' . escapeshellarg($_SERVER['HTTP_USER_AGENT']) . ' --custom-header-propagation'; // By default 'Typo3' expects the same User-Agent for the FE-Session
178

179
        // Very important: The current lock on session SESSION_NAME has to be freed, cause wkhtmltopdf will use the same
Carsten  Rose's avatar
Carsten Rose committed
180
        // session in a few moments and this script remains active during that time and that would cause a deadlock else.
181
        $this->session->close();
182
        $cmd = "$wkhtmlToPdf $customHeader $cookieOptions $options $urlPrint $filenameEscape";
183

184
185
        if ($this->logFile != '') {
            Logger::logMessage("Html2Pdf: $cmd", $this->logFile);
186
        }
187

Carsten  Rose's avatar
Carsten Rose committed
188
        $rc = 0;
189
190
191
        $line = system($cmd, $rc);

        if ($rc != 0) {
192
193
            throw new \exception("Error [RC=$rc] $line: $cmd - in case of trouble: check carefully that *all* CSS, JS, " .
                "images are accessible. 'wkhtml' does not report problems but fails.", ERROR_HTML2PDF_WKHTML_FAILED);
194
195
196
197
198
199
200
201
202
203
204
        }

        return $filename;
    }

    /**
     * @throws \exception
     */
    public function outputHtml2Pdf() {

        $get = $this->readCleanGetParam($_GET);
Carsten  Rose's avatar
Carsten Rose committed
205
        $urlParam = KeyValueStringParser::unparse($get, '=', '&');
206
207
        $pageId = Support::setIfNotSet($get, HTML2PDF_PAGEID, 0);

Carsten  Rose's avatar
Carsten Rose committed
208
        $filename = $this->page2pdf(TOKEN_URL_PARAM, $urlParam);
209
210
211
212
213
214
215
216
217
218

        $this->setHeader('print.' . $pageId . '.pdf');
        @readfile($filename);
        @unlink($filename);

        exit; // Do an extremely hard exit here to make sure there are no more additional bytes sent (makes the delivered PDF unusable).

    }

}