Commit | Line | Data |
---|---|---|
5c386472 DW |
1 | <?php\r |
2 | //\r | |
858d457d | 3 | // FPDI - Version 1.5.4\r |
5c386472 | 4 | //\r |
858d457d | 5 | // Copyright 2004-2015 Setasign - Jan Slabon\r |
5c386472 DW |
6 | //\r |
7 | // Licensed under the Apache License, Version 2.0 (the "License");\r | |
8 | // you may not use this file except in compliance with the License.\r | |
9 | // You may obtain a copy of the License at\r | |
10 | //\r | |
11 | // http://www.apache.org/licenses/LICENSE-2.0\r | |
12 | //\r | |
13 | // Unless required by applicable law or agreed to in writing, software\r | |
14 | // distributed under the License is distributed on an "AS IS" BASIS,\r | |
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r | |
16 | // See the License for the specific language governing permissions and\r | |
17 | // limitations under the License.\r | |
18 | //\r | |
19 | \r | |
858d457d AG |
20 | if (!class_exists('pdf_parser')) {\r |
21 | require_once('pdf_parser.php');\r | |
22 | }\r | |
5c386472 | 23 | \r |
858d457d AG |
24 | /**\r |
25 | * Class fpdi_pdf_parser\r | |
26 | */\r | |
27 | class fpdi_pdf_parser extends pdf_parser\r | |
28 | {\r | |
5c386472 DW |
29 | /**\r |
30 | * Pages\r | |
858d457d AG |
31 | *\r |
32 | * Index begins at 0\r | |
5c386472 DW |
33 | *\r |
34 | * @var array\r | |
35 | */\r | |
858d457d | 36 | protected $_pages;\r |
5c386472 DW |
37 | \r |
38 | /**\r | |
39 | * Page count\r | |
858d457d | 40 | *\r |
5c386472 DW |
41 | * @var integer\r |
42 | */\r | |
858d457d | 43 | protected $_pageCount;\r |
5c386472 DW |
44 | \r |
45 | /**\r | |
858d457d AG |
46 | * Current page number\r |
47 | *\r | |
5c386472 DW |
48 | * @var integer\r |
49 | */\r | |
858d457d | 50 | public $pageNo;\r |
5c386472 DW |
51 | \r |
52 | /**\r | |
858d457d AG |
53 | * PDF version of imported document\r |
54 | *\r | |
5c386472 DW |
55 | * @var string\r |
56 | */\r | |
858d457d | 57 | public $_pdfVersion;\r |
5c386472 DW |
58 | \r |
59 | /**\r | |
60 | * Available BoxTypes\r | |
61 | *\r | |
62 | * @var array\r | |
63 | */\r | |
858d457d | 64 | public $availableBoxes = array('/MediaBox', '/CropBox', '/BleedBox', '/TrimBox', '/ArtBox');\r |
5c386472 DW |
65 | \r |
66 | /**\r | |
858d457d | 67 | * The constructor.\r |
5c386472 | 68 | *\r |
858d457d | 69 | * @param string $filename The source filename\r |
5c386472 | 70 | */\r |
858d457d AG |
71 | public function __construct($filename)\r |
72 | {\r | |
73 | parent::__construct($filename);\r | |
5c386472 DW |
74 | \r |
75 | // resolve Pages-Dictonary\r | |
858d457d | 76 | $pages = $this->resolveObject($this->_root[1][1]['/Pages']);\r |
5c386472 DW |
77 | \r |
78 | // Read pages\r | |
858d457d | 79 | $this->_readPages($pages, $this->_pages);\r |
5c386472 DW |
80 | \r |
81 | // count pages;\r | |
858d457d | 82 | $this->_pageCount = count($this->_pages);\r |
5c386472 DW |
83 | }\r |
84 | \r | |
85 | /**\r | |
858d457d | 86 | * Get page count from source file.\r |
5c386472 DW |
87 | *\r |
88 | * @return int\r | |
89 | */\r | |
858d457d AG |
90 | public function getPageCount()\r |
91 | {\r | |
92 | return $this->_pageCount;\r | |
5c386472 DW |
93 | }\r |
94 | \r | |
5c386472 | 95 | /**\r |
858d457d | 96 | * Set the page number.\r |
5c386472 | 97 | *\r |
858d457d AG |
98 | * @param int $pageNo Page number to use\r |
99 | * @throws InvalidArgumentException\r | |
5c386472 | 100 | */\r |
858d457d AG |
101 | public function setPageNo($pageNo)\r |
102 | {\r | |
103 | $pageNo = ((int) $pageNo) - 1;\r | |
5c386472 | 104 | \r |
858d457d AG |
105 | if ($pageNo < 0 || $pageNo >= $this->getPageCount()) {\r |
106 | throw new InvalidArgumentException('Invalid page number!');\r | |
5c386472 DW |
107 | }\r |
108 | \r | |
858d457d | 109 | $this->pageNo = $pageNo;\r |
5c386472 DW |
110 | }\r |
111 | \r | |
112 | /**\r | |
113 | * Get page-resources from current page\r | |
114 | *\r | |
858d457d | 115 | * @return array|boolean\r |
5c386472 | 116 | */\r |
858d457d AG |
117 | public function getPageResources()\r |
118 | {\r | |
119 | return $this->_getPageResources($this->_pages[$this->pageNo]);\r | |
5c386472 DW |
120 | }\r |
121 | \r | |
122 | /**\r | |
858d457d | 123 | * Get page-resources from a /Page dictionary.\r |
5c386472 DW |
124 | *\r |
125 | * @param array $obj Array of pdf-data\r | |
858d457d | 126 | * @return array|boolean\r |
5c386472 | 127 | */\r |
858d457d AG |
128 | protected function _getPageResources($obj)\r |
129 | {\r | |
130 | $obj = $this->resolveObject($obj);\r | |
5c386472 DW |
131 | \r |
132 | // If the current object has a resources\r | |
133 | // dictionary associated with it, we use\r | |
134 | // it. Otherwise, we move back to its\r | |
135 | // parent object.\r | |
858d457d AG |
136 | if (isset($obj[1][1]['/Resources'])) {\r |
137 | $res = $this->resolveObject($obj[1][1]['/Resources']);\r | |
138 | if ($res[0] == pdf_parser::TYPE_OBJECT)\r | |
5c386472 DW |
139 | return $res[1];\r |
140 | return $res;\r | |
5c386472 | 141 | }\r |
5c386472 | 142 | \r |
858d457d AG |
143 | if (!isset($obj[1][1]['/Parent'])) {\r |
144 | return false;\r | |
145 | }\r | |
146 | \r | |
147 | $res = $this->_getPageResources($obj[1][1]['/Parent']);\r | |
148 | if ($res[0] == pdf_parser::TYPE_OBJECT)\r | |
149 | return $res[1];\r | |
150 | return $res;\r | |
151 | }\r | |
5c386472 DW |
152 | \r |
153 | /**\r | |
858d457d | 154 | * Get content of current page.\r |
5c386472 | 155 | *\r |
858d457d | 156 | * If /Contents is an array, the streams are concatenated\r |
5c386472 DW |
157 | *\r |
158 | * @return string\r | |
159 | */\r | |
858d457d AG |
160 | public function getContent()\r |
161 | {\r | |
5c386472 DW |
162 | $buffer = '';\r |
163 | \r | |
858d457d AG |
164 | if (isset($this->_pages[$this->pageNo][1][1]['/Contents'])) {\r |
165 | $contents = $this->_getPageContent($this->_pages[$this->pageNo][1][1]['/Contents']);\r | |
166 | foreach ($contents AS $tmpContent) {\r | |
167 | $buffer .= $this->_unFilterStream($tmpContent) . ' ';\r | |
5c386472 DW |
168 | }\r |
169 | }\r | |
170 | \r | |
171 | return $buffer;\r | |
172 | }\r | |
858d457d | 173 | \r |
5c386472 | 174 | /**\r |
858d457d | 175 | * Resolve all content objects.\r |
5c386472 | 176 | *\r |
858d457d | 177 | * @param array $contentRef\r |
5c386472 DW |
178 | * @return array\r |
179 | */\r | |
858d457d AG |
180 | protected function _getPageContent($contentRef)\r |
181 | {\r | |
5c386472 DW |
182 | $contents = array();\r |
183 | \r | |
858d457d AG |
184 | if ($contentRef[0] == pdf_parser::TYPE_OBJREF) {\r |
185 | $content = $this->resolveObject($contentRef);\r | |
186 | if ($content[1][0] == pdf_parser::TYPE_ARRAY) {\r | |
5c386472 DW |
187 | $contents = $this->_getPageContent($content[1]);\r |
188 | } else {\r | |
189 | $contents[] = $content;\r | |
190 | }\r | |
858d457d AG |
191 | } else if ($contentRef[0] == pdf_parser::TYPE_ARRAY) {\r |
192 | foreach ($contentRef[1] AS $tmp_content_ref) {\r | |
193 | $contents = array_merge($contents, $this->_getPageContent($tmp_content_ref));\r | |
5c386472 DW |
194 | }\r |
195 | }\r | |
196 | \r | |
197 | return $contents;\r | |
198 | }\r | |
199 | \r | |
5c386472 | 200 | /**\r |
858d457d | 201 | * Get a boundary box from a page\r |
5c386472 | 202 | *\r |
858d457d | 203 | * Array format is same as used by FPDF_TPL.\r |
5c386472 | 204 | *\r |
858d457d AG |
205 | * @param array $page a /Page dictionary\r |
206 | * @param string $boxIndex Type of box {see {@link $availableBoxes})\r | |
5c386472 | 207 | * @param float Scale factor from user space units to points\r |
858d457d AG |
208 | *\r |
209 | * @return array|boolean\r | |
5c386472 | 210 | */\r |
858d457d AG |
211 | protected function _getPageBox($page, $boxIndex, $k)\r |
212 | {\r | |
213 | $page = $this->resolveObject($page);\r | |
5c386472 | 214 | $box = null;\r |
858d457d AG |
215 | if (isset($page[1][1][$boxIndex])) {\r |
216 | $box = $page[1][1][$boxIndex];\r | |
217 | }\r | |
5c386472 | 218 | \r |
858d457d AG |
219 | if (!is_null($box) && $box[0] == pdf_parser::TYPE_OBJREF) {\r |
220 | $tmp_box = $this->resolveObject($box);\r | |
5c386472 DW |
221 | $box = $tmp_box[1];\r |
222 | }\r | |
223 | \r | |
858d457d AG |
224 | if (!is_null($box) && $box[0] == pdf_parser::TYPE_ARRAY) {\r |
225 | $b = $box[1];\r | |
226 | return array(\r | |
227 | 'x' => $b[0][1] / $k,\r | |
228 | 'y' => $b[1][1] / $k,\r | |
229 | 'w' => abs($b[0][1] - $b[2][1]) / $k,\r | |
230 | 'h' => abs($b[1][1] - $b[3][1]) / $k,\r | |
231 | 'llx' => min($b[0][1], $b[2][1]) / $k,\r | |
232 | 'lly' => min($b[1][1], $b[3][1]) / $k,\r | |
233 | 'urx' => max($b[0][1], $b[2][1]) / $k,\r | |
234 | 'ury' => max($b[1][1], $b[3][1]) / $k,\r | |
235 | );\r | |
236 | } else if (!isset($page[1][1]['/Parent'])) {\r | |
5c386472 DW |
237 | return false;\r |
238 | } else {\r | |
858d457d | 239 | return $this->_getPageBox($this->resolveObject($page[1][1]['/Parent']), $boxIndex, $k);\r |
5c386472 DW |
240 | }\r |
241 | }\r | |
242 | \r | |
243 | /**\r | |
858d457d | 244 | * Get all page boundary boxes by page number\r |
5c386472 | 245 | * \r |
858d457d AG |
246 | * @param int $pageNo The page number\r |
247 | * @param float $k Scale factor from user space units to points\r | |
5c386472 | 248 | * @return array\r |
858d457d | 249 | * @throws InvalidArgumentException\r |
5c386472 | 250 | */\r |
858d457d AG |
251 | public function getPageBoxes($pageNo, $k)\r |
252 | {\r | |
253 | if (!isset($this->_pages[$pageNo - 1])) {\r | |
254 | throw new InvalidArgumentException('Page ' . $pageNo . ' does not exists.');\r | |
255 | }\r | |
256 | \r | |
257 | return $this->_getPageBoxes($this->_pages[$pageNo - 1], $k);\r | |
5c386472 DW |
258 | }\r |
259 | \r | |
260 | /**\r | |
858d457d | 261 | * Get all boxes from /Page dictionary\r |
5c386472 | 262 | *\r |
858d457d AG |
263 | * @param array $page A /Page dictionary\r |
264 | * @param float $k Scale factor from user space units to points\r | |
5c386472 DW |
265 | * @return array\r |
266 | */\r | |
858d457d AG |
267 | protected function _getPageBoxes($page, $k)\r |
268 | {\r | |
5c386472 DW |
269 | $boxes = array();\r |
270 | \r | |
271 | foreach($this->availableBoxes AS $box) {\r | |
858d457d | 272 | if ($_box = $this->_getPageBox($page, $box, $k)) {\r |
5c386472 DW |
273 | $boxes[$box] = $_box;\r |
274 | }\r | |
275 | }\r | |
276 | \r | |
277 | return $boxes;\r | |
278 | }\r | |
279 | \r | |
280 | /**\r | |
858d457d | 281 | * Get the page rotation by page number\r |
5c386472 | 282 | *\r |
858d457d AG |
283 | * @param integer $pageNo\r |
284 | * @throws InvalidArgumentException\r | |
5c386472 DW |
285 | * @return array\r |
286 | */\r | |
858d457d AG |
287 | public function getPageRotation($pageNo)\r |
288 | {\r | |
289 | if (!isset($this->_pages[$pageNo - 1])) {\r | |
290 | throw new InvalidArgumentException('Page ' . $pageNo . ' does not exists.');\r | |
291 | }\r | |
292 | \r | |
293 | return $this->_getPageRotation($this->_pages[$pageNo - 1]);\r | |
5c386472 | 294 | }\r |
858d457d AG |
295 | \r |
296 | /**\r | |
297 | * Get the rotation value of a page\r | |
298 | *\r | |
299 | * @param array $obj A /Page dictionary\r | |
300 | * @return array|bool\r | |
301 | */\r | |
302 | protected function _getPageRotation($obj)\r | |
303 | {\r | |
304 | $obj = $this->resolveObject($obj);\r | |
305 | if (isset($obj[1][1]['/Rotate'])) {\r | |
306 | $res = $this->resolveObject($obj[1][1]['/Rotate']);\r | |
307 | if ($res[0] == pdf_parser::TYPE_OBJECT)\r | |
5c386472 DW |
308 | return $res[1];\r |
309 | return $res;\r | |
5c386472 | 310 | }\r |
858d457d AG |
311 | \r |
312 | if (!isset($obj[1][1]['/Parent'])) {\r | |
313 | return false;\r | |
314 | }\r | |
315 | \r | |
316 | $res = $this->_getPageRotation($obj[1][1]['/Parent']);\r | |
317 | if ($res[0] == pdf_parser::TYPE_OBJECT)\r | |
318 | return $res[1];\r | |
319 | \r | |
320 | return $res;\r | |
5c386472 | 321 | }\r |
858d457d | 322 | \r |
5c386472 | 323 | /**\r |
858d457d | 324 | * Read all pages\r |
5c386472 | 325 | *\r |
858d457d AG |
326 | * @param array $pages /Pages dictionary\r |
327 | * @param array $result The result array\r | |
328 | * @throws Exception\r | |
5c386472 | 329 | */\r |
858d457d AG |
330 | protected function _readPages(&$pages, &$result)\r |
331 | {\r | |
5c386472 | 332 | // Get the kids dictionary\r |
858d457d AG |
333 | $_kids = $this->resolveObject($pages[1][1]['/Kids']);\r |
334 | \r | |
335 | if (!is_array($_kids)) {\r | |
336 | throw new Exception('Cannot find /Kids in current /Page-Dictionary');\r | |
5c386472 | 337 | }\r |
858d457d AG |
338 | \r |
339 | if ($_kids[0] === self::TYPE_OBJECT) {\r | |
340 | $_kids = $_kids[1];\r | |
341 | }\r | |
342 | \r | |
343 | $kids = $_kids[1];\r | |
344 | \r | |
5c386472 | 345 | foreach ($kids as $v) {\r |
858d457d | 346 | $pg = $this->resolveObject($v);\r |
5c386472 DW |
347 | if ($pg[1][1]['/Type'][1] === '/Pages') {\r |
348 | // If one of the kids is an embedded\r | |
349 | // /Pages array, resolve it as well.\r | |
858d457d | 350 | $this->_readPages($pg, $result);\r |
5c386472 DW |
351 | } else {\r |
352 | $result[] = $pg;\r | |
353 | }\r | |
354 | }\r | |
355 | }\r | |
858d457d | 356 | } |