├── .gitignore
├── README.md
├── composer.json
├── composer.lock
├── phpunit.xml
├── src
├── Area.php
├── Areas.php
├── Camelot.php
└── Exceptions
│ ├── BackgroundLinesNotSupportedException.php
│ ├── ColumnSeparatorsNotSupportedException.php
│ ├── NotSupportedException.php
│ └── PdfEncryptedException.php
└── tests
├── Feature
├── AdvancedProcessingTest.php
├── EncryptedPdfTest.php
├── PagesTest.php
├── ParseExportTest.php
└── TableHintsTest.php
├── Helpers
└── CsvAssertions.php
├── TestCase.php
└── files
├── background_lines_1.pdf
├── column_separators.pdf
├── copy_text.pdf
├── edge_tol.pdf
├── foo_lattice.pdf
├── health_protected.pdf
├── health_stream.pdf
├── row_tol.pdf
├── short_lines.pdf
├── strip.pdf
├── superscript.pdf
├── table_areas.pdf
├── table_region.pdf
└── twotables_2.pdf
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | .idea/
3 | .phpunit.result.cache
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # randomstate/camelot-php
2 |
3 | A PHP wrapper for Camelot, the python PDF table extraction library
4 |
5 | ## Installation
6 |
7 | `composer require randomstate/camelot-php`
8 |
9 | ## Usage
10 |
11 | The package adheres closely with the camelot CLI API Usage.
12 | Default output is in CSV format as a simple string. If you need to parse CSV strings we recommend the `league/csv` package (https://csv.thephpleague.com/)
13 |
14 | ```php
15 | extract();
22 |
23 | $csv = Reader::createFromString($tables[0]);
24 | $allRecords = $csv->getRecords();
25 | ```
26 |
27 | ### Advanced Processing
28 |
29 | ##### Saving / Extracting
30 | **Note: No Camelot operations are run until one of these methods is run**
31 | ```php
32 | $camelot->extract(); // uses temporary files and automatically grabs the table contents for you from each
33 | $camelot->save('/path/to/my-file.csv'); // mirrors the behaviour of Camelot and saves files in the format /path/to/my-file-page-*-table-*.csv
34 | $camelot->plot(); // useful for debugging, it will plot it in a separate window (see Visual Debugging below)
35 | ```
36 |
37 | ##### [Set Format](https://camelot-py.readthedocs.io/en/master/user/quickstart.html#read-the-pdf)
38 | ```
39 | $camelot->json();
40 | $camelot->csv();
41 | $camelot->html();
42 | $camelot->excel();
43 | $camelot->sqlite();
44 | ```
45 | ##### [Specify Page Numbers](https://camelot-py.readthedocs.io/en/master/user/quickstart.html#specify-page-numbers)
46 |
47 | `$camelot->pages('1,2,3-4,8-end')`
48 |
49 | ##### [Reading encrypted PDFs](https://camelot-py.readthedocs.io/en/master/user/quickstart.html#reading-encrypted-pdfs)
50 |
51 | `$camelot->password('my-pass')`
52 |
53 | ##### [Processing background lines](https://camelot-py.readthedocs.io/en/master/user/advanced.html#process-background-lines)
54 | `$camelot->stream()->processBackgroundLines()`
55 |
56 | ##### [Visual debugging](https://camelot-py.readthedocs.io/en/master/user/advanced.html#visual-debugging)
57 |
58 | `$camelot->plot()`
59 |
60 | ##### [Specify table areas](https://camelot-py.readthedocs.io/en/master/user/advanced.html#specify-table-areas)
61 |
62 | ```php
63 | inAreas(
70 | Areas::from($xTopLeft, $yTopLeft, $xBottomRight, $yBottomRight)
71 | // ->add($xTopLeft2, $yTopLeft2, $xBottomRight2, $yBottomRight2)
72 | // ->add($xTopLeft3, $yTopLeft3, $xBottomRight3, $yBottomRight3)
73 | );
74 | ```
75 |
76 | ##### [Specify table regions](https://camelot-py.readthedocs.io/en/master/user/advanced.html#specify-table-regions)
77 |
78 | ```php
79 | inRegions(
86 | Areas::from($xTopLeft, $yTopLeft, $xBottomRight, $yBottomRight)
87 | // ->add($xTopLeft2, $yTopLeft2, $xBottomRight2, $yBottomRight2)
88 | // ->add($xTopLeft3, $yTopLeft3, $xBottomRight3, $yBottomRight3)
89 | );
90 | ```
91 |
92 | ##### [Specify column separators](https://camelot-py.readthedocs.io/en/master/user/advanced.html#specify-column-separators)
93 |
94 | `$camelot->stream()->setColumnSeparators($x1,$x2...)`
95 |
96 | ##### [Split text along separators](https://camelot-py.readthedocs.io/en/master/user/advanced.html#split-text-along-separators)
97 |
98 | `$camelot->split()`
99 |
100 | ##### [Flag superscripts and subscripts](https://camelot-py.readthedocs.io/en/master/user/advanced.html#flag-superscripts-and-subscripts)
101 |
102 | `$camelot->flagSize()`
103 |
104 | ##### [Strip characters from text](https://camelot-py.readthedocs.io/en/master/user/advanced.html#strip-characters-from-text)
105 |
106 | `$camelot->strip("\n")`
107 |
108 | ##### [Improve guessed table areas](https://camelot-py.readthedocs.io/en/master/user/advanced.html#improve-guessed-table-areas)
109 |
110 | `$camelot->setEdgeTolerance(500)`
111 |
112 | ##### [Improve guessed table rows](https://camelot-py.readthedocs.io/en/master/user/advanced.html#improve-guessed-table-rows)
113 |
114 | `$camelot->setRowTolerance(15)`
115 |
116 | ##### [Detect short lines](https://camelot-py.readthedocs.io/en/master/user/advanced.html#detect-short-lines)
117 |
118 | `$camelot->lineScale(20)`
119 |
120 |
121 | ##### [Shift text in spanning cells](https://camelot-py.readthedocs.io/en/master/user/advanced.html#shift-text-in-spanning-cells)
122 |
123 | `$camelot->shiftText('r', 'b')`
124 |
125 | ##### [Copy text in spanning cells](https://camelot-py.readthedocs.io/en/master/user/advanced.html#copy-text-in-spanning-cells)
126 |
127 | `$camelot->copyTextSpanningCells('r', 'b')`
128 |
129 |
130 | ## License
131 |
132 | MIT. Use at your own risk, we accept no liability for how this code is used.
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "randomstate/camelot-php",
3 | "description": "PHP Wrapper library for interfacing with the Camelot PDF table extraction library built in Python",
4 | "type": "library",
5 | "authors": [
6 | {
7 | "name": "Connor Forsyth",
8 | "email": "connor@randomstate.co.uk"
9 | }
10 | ],
11 | "require": {
12 | "symfony/process": "^4.0|^5.0",
13 | "spatie/temporary-directory": "^1.2"
14 | },
15 | "autoload": {
16 | "psr-4": {
17 | "RandomState\\Camelot\\": "src/"
18 | }
19 | },
20 | "autoload-dev": {
21 | "psr-4": {
22 | "RandomState\\Camelot\\Tests\\": "tests/"
23 | }
24 | },
25 | "require-dev": {
26 | "phpunit/phpunit": "^8.5",
27 | "league/csv": "^9.4"
28 | },
29 | "license": "MIT"
30 | }
31 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "0f35e614384fdc9d42ad84de6649e2a1",
8 | "packages": [
9 | {
10 | "name": "spatie/temporary-directory",
11 | "version": "1.2.1",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/spatie/temporary-directory.git",
15 | "reference": "3e51af9a8361f85cffc1fb2c52135f3e064758cc"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/3e51af9a8361f85cffc1fb2c52135f3e064758cc",
20 | "reference": "3e51af9a8361f85cffc1fb2c52135f3e064758cc",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "php": "^7.2"
25 | },
26 | "require-dev": {
27 | "phpunit/phpunit": "^8.0"
28 | },
29 | "type": "library",
30 | "autoload": {
31 | "psr-4": {
32 | "Spatie\\TemporaryDirectory\\": "src"
33 | }
34 | },
35 | "notification-url": "https://packagist.org/downloads/",
36 | "license": [
37 | "MIT"
38 | ],
39 | "authors": [
40 | {
41 | "name": "Alex Vanderbist",
42 | "email": "alex@spatie.be",
43 | "homepage": "https://spatie.be",
44 | "role": "Developer"
45 | }
46 | ],
47 | "description": "Easily create, use and destroy temporary directories",
48 | "homepage": "https://github.com/spatie/temporary-directory",
49 | "keywords": [
50 | "spatie",
51 | "temporary-directory"
52 | ],
53 | "time": "2019-08-28T06:53:51+00:00"
54 | },
55 | {
56 | "name": "symfony/process",
57 | "version": "v5.0.1",
58 | "source": {
59 | "type": "git",
60 | "url": "https://github.com/symfony/process.git",
61 | "reference": "1568a2e8370fbc7416ef64eb5a698e4a05db5ff4"
62 | },
63 | "dist": {
64 | "type": "zip",
65 | "url": "https://api.github.com/repos/symfony/process/zipball/1568a2e8370fbc7416ef64eb5a698e4a05db5ff4",
66 | "reference": "1568a2e8370fbc7416ef64eb5a698e4a05db5ff4",
67 | "shasum": ""
68 | },
69 | "require": {
70 | "php": "^7.2.5"
71 | },
72 | "type": "library",
73 | "extra": {
74 | "branch-alias": {
75 | "dev-master": "5.0-dev"
76 | }
77 | },
78 | "autoload": {
79 | "psr-4": {
80 | "Symfony\\Component\\Process\\": ""
81 | },
82 | "exclude-from-classmap": [
83 | "/Tests/"
84 | ]
85 | },
86 | "notification-url": "https://packagist.org/downloads/",
87 | "license": [
88 | "MIT"
89 | ],
90 | "authors": [
91 | {
92 | "name": "Fabien Potencier",
93 | "email": "fabien@symfony.com"
94 | },
95 | {
96 | "name": "Symfony Community",
97 | "homepage": "https://symfony.com/contributors"
98 | }
99 | ],
100 | "description": "Symfony Process Component",
101 | "homepage": "https://symfony.com",
102 | "time": "2019-11-28T14:20:16+00:00"
103 | }
104 | ],
105 | "packages-dev": [
106 | {
107 | "name": "doctrine/instantiator",
108 | "version": "1.3.0",
109 | "source": {
110 | "type": "git",
111 | "url": "https://github.com/doctrine/instantiator.git",
112 | "reference": "ae466f726242e637cebdd526a7d991b9433bacf1"
113 | },
114 | "dist": {
115 | "type": "zip",
116 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1",
117 | "reference": "ae466f726242e637cebdd526a7d991b9433bacf1",
118 | "shasum": ""
119 | },
120 | "require": {
121 | "php": "^7.1"
122 | },
123 | "require-dev": {
124 | "doctrine/coding-standard": "^6.0",
125 | "ext-pdo": "*",
126 | "ext-phar": "*",
127 | "phpbench/phpbench": "^0.13",
128 | "phpstan/phpstan-phpunit": "^0.11",
129 | "phpstan/phpstan-shim": "^0.11",
130 | "phpunit/phpunit": "^7.0"
131 | },
132 | "type": "library",
133 | "extra": {
134 | "branch-alias": {
135 | "dev-master": "1.2.x-dev"
136 | }
137 | },
138 | "autoload": {
139 | "psr-4": {
140 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
141 | }
142 | },
143 | "notification-url": "https://packagist.org/downloads/",
144 | "license": [
145 | "MIT"
146 | ],
147 | "authors": [
148 | {
149 | "name": "Marco Pivetta",
150 | "email": "ocramius@gmail.com",
151 | "homepage": "http://ocramius.github.com/"
152 | }
153 | ],
154 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
155 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
156 | "keywords": [
157 | "constructor",
158 | "instantiate"
159 | ],
160 | "time": "2019-10-21T16:45:58+00:00"
161 | },
162 | {
163 | "name": "league/csv",
164 | "version": "9.4.1",
165 | "source": {
166 | "type": "git",
167 | "url": "https://github.com/thephpleague/csv.git",
168 | "reference": "bf83acc23a7d60978fce36a384f97bf9d7d2ea0c"
169 | },
170 | "dist": {
171 | "type": "zip",
172 | "url": "https://api.github.com/repos/thephpleague/csv/zipball/bf83acc23a7d60978fce36a384f97bf9d7d2ea0c",
173 | "reference": "bf83acc23a7d60978fce36a384f97bf9d7d2ea0c",
174 | "shasum": ""
175 | },
176 | "require": {
177 | "ext-dom": "*",
178 | "ext-json": "*",
179 | "ext-mbstring": "*",
180 | "php": ">=7.0.10"
181 | },
182 | "require-dev": {
183 | "ext-curl": "*",
184 | "friendsofphp/php-cs-fixer": "^2.12",
185 | "phpstan/phpstan": "^0.9.2",
186 | "phpstan/phpstan-phpunit": "^0.9.4",
187 | "phpstan/phpstan-strict-rules": "^0.9.0",
188 | "phpunit/phpunit": "^6.0"
189 | },
190 | "suggest": {
191 | "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters"
192 | },
193 | "type": "library",
194 | "extra": {
195 | "branch-alias": {
196 | "dev-master": "9.x-dev"
197 | }
198 | },
199 | "autoload": {
200 | "psr-4": {
201 | "League\\Csv\\": "src"
202 | },
203 | "files": [
204 | "src/functions_include.php"
205 | ]
206 | },
207 | "notification-url": "https://packagist.org/downloads/",
208 | "license": [
209 | "MIT"
210 | ],
211 | "authors": [
212 | {
213 | "name": "Ignace Nyamagana Butera",
214 | "email": "nyamsprod@gmail.com",
215 | "homepage": "https://github.com/nyamsprod/",
216 | "role": "Developer"
217 | }
218 | ],
219 | "description": "Csv data manipulation made easy in PHP",
220 | "homepage": "http://csv.thephpleague.com",
221 | "keywords": [
222 | "csv",
223 | "export",
224 | "filter",
225 | "import",
226 | "read",
227 | "write"
228 | ],
229 | "time": "2019-10-17T06:05:32+00:00"
230 | },
231 | {
232 | "name": "myclabs/deep-copy",
233 | "version": "1.9.3",
234 | "source": {
235 | "type": "git",
236 | "url": "https://github.com/myclabs/DeepCopy.git",
237 | "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea"
238 | },
239 | "dist": {
240 | "type": "zip",
241 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea",
242 | "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea",
243 | "shasum": ""
244 | },
245 | "require": {
246 | "php": "^7.1"
247 | },
248 | "replace": {
249 | "myclabs/deep-copy": "self.version"
250 | },
251 | "require-dev": {
252 | "doctrine/collections": "^1.0",
253 | "doctrine/common": "^2.6",
254 | "phpunit/phpunit": "^7.1"
255 | },
256 | "type": "library",
257 | "autoload": {
258 | "psr-4": {
259 | "DeepCopy\\": "src/DeepCopy/"
260 | },
261 | "files": [
262 | "src/DeepCopy/deep_copy.php"
263 | ]
264 | },
265 | "notification-url": "https://packagist.org/downloads/",
266 | "license": [
267 | "MIT"
268 | ],
269 | "description": "Create deep copies (clones) of your objects",
270 | "keywords": [
271 | "clone",
272 | "copy",
273 | "duplicate",
274 | "object",
275 | "object graph"
276 | ],
277 | "time": "2019-08-09T12:45:53+00:00"
278 | },
279 | {
280 | "name": "phar-io/manifest",
281 | "version": "1.0.3",
282 | "source": {
283 | "type": "git",
284 | "url": "https://github.com/phar-io/manifest.git",
285 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4"
286 | },
287 | "dist": {
288 | "type": "zip",
289 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
290 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
291 | "shasum": ""
292 | },
293 | "require": {
294 | "ext-dom": "*",
295 | "ext-phar": "*",
296 | "phar-io/version": "^2.0",
297 | "php": "^5.6 || ^7.0"
298 | },
299 | "type": "library",
300 | "extra": {
301 | "branch-alias": {
302 | "dev-master": "1.0.x-dev"
303 | }
304 | },
305 | "autoload": {
306 | "classmap": [
307 | "src/"
308 | ]
309 | },
310 | "notification-url": "https://packagist.org/downloads/",
311 | "license": [
312 | "BSD-3-Clause"
313 | ],
314 | "authors": [
315 | {
316 | "name": "Arne Blankerts",
317 | "email": "arne@blankerts.de",
318 | "role": "Developer"
319 | },
320 | {
321 | "name": "Sebastian Heuer",
322 | "email": "sebastian@phpeople.de",
323 | "role": "Developer"
324 | },
325 | {
326 | "name": "Sebastian Bergmann",
327 | "email": "sebastian@phpunit.de",
328 | "role": "Developer"
329 | }
330 | ],
331 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
332 | "time": "2018-07-08T19:23:20+00:00"
333 | },
334 | {
335 | "name": "phar-io/version",
336 | "version": "2.0.1",
337 | "source": {
338 | "type": "git",
339 | "url": "https://github.com/phar-io/version.git",
340 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6"
341 | },
342 | "dist": {
343 | "type": "zip",
344 | "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6",
345 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6",
346 | "shasum": ""
347 | },
348 | "require": {
349 | "php": "^5.6 || ^7.0"
350 | },
351 | "type": "library",
352 | "autoload": {
353 | "classmap": [
354 | "src/"
355 | ]
356 | },
357 | "notification-url": "https://packagist.org/downloads/",
358 | "license": [
359 | "BSD-3-Clause"
360 | ],
361 | "authors": [
362 | {
363 | "name": "Arne Blankerts",
364 | "email": "arne@blankerts.de",
365 | "role": "Developer"
366 | },
367 | {
368 | "name": "Sebastian Heuer",
369 | "email": "sebastian@phpeople.de",
370 | "role": "Developer"
371 | },
372 | {
373 | "name": "Sebastian Bergmann",
374 | "email": "sebastian@phpunit.de",
375 | "role": "Developer"
376 | }
377 | ],
378 | "description": "Library for handling version information and constraints",
379 | "time": "2018-07-08T19:19:57+00:00"
380 | },
381 | {
382 | "name": "phpdocumentor/reflection-common",
383 | "version": "2.0.0",
384 | "source": {
385 | "type": "git",
386 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
387 | "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a"
388 | },
389 | "dist": {
390 | "type": "zip",
391 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a",
392 | "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a",
393 | "shasum": ""
394 | },
395 | "require": {
396 | "php": ">=7.1"
397 | },
398 | "require-dev": {
399 | "phpunit/phpunit": "~6"
400 | },
401 | "type": "library",
402 | "extra": {
403 | "branch-alias": {
404 | "dev-master": "2.x-dev"
405 | }
406 | },
407 | "autoload": {
408 | "psr-4": {
409 | "phpDocumentor\\Reflection\\": "src/"
410 | }
411 | },
412 | "notification-url": "https://packagist.org/downloads/",
413 | "license": [
414 | "MIT"
415 | ],
416 | "authors": [
417 | {
418 | "name": "Jaap van Otterdijk",
419 | "email": "opensource@ijaap.nl"
420 | }
421 | ],
422 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
423 | "homepage": "http://www.phpdoc.org",
424 | "keywords": [
425 | "FQSEN",
426 | "phpDocumentor",
427 | "phpdoc",
428 | "reflection",
429 | "static analysis"
430 | ],
431 | "time": "2018-08-07T13:53:10+00:00"
432 | },
433 | {
434 | "name": "phpdocumentor/reflection-docblock",
435 | "version": "4.3.2",
436 | "source": {
437 | "type": "git",
438 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
439 | "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e"
440 | },
441 | "dist": {
442 | "type": "zip",
443 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
444 | "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
445 | "shasum": ""
446 | },
447 | "require": {
448 | "php": "^7.0",
449 | "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0",
450 | "phpdocumentor/type-resolver": "~0.4 || ^1.0.0",
451 | "webmozart/assert": "^1.0"
452 | },
453 | "require-dev": {
454 | "doctrine/instantiator": "^1.0.5",
455 | "mockery/mockery": "^1.0",
456 | "phpunit/phpunit": "^6.4"
457 | },
458 | "type": "library",
459 | "extra": {
460 | "branch-alias": {
461 | "dev-master": "4.x-dev"
462 | }
463 | },
464 | "autoload": {
465 | "psr-4": {
466 | "phpDocumentor\\Reflection\\": [
467 | "src/"
468 | ]
469 | }
470 | },
471 | "notification-url": "https://packagist.org/downloads/",
472 | "license": [
473 | "MIT"
474 | ],
475 | "authors": [
476 | {
477 | "name": "Mike van Riel",
478 | "email": "me@mikevanriel.com"
479 | }
480 | ],
481 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
482 | "time": "2019-09-12T14:27:41+00:00"
483 | },
484 | {
485 | "name": "phpdocumentor/type-resolver",
486 | "version": "1.0.1",
487 | "source": {
488 | "type": "git",
489 | "url": "https://github.com/phpDocumentor/TypeResolver.git",
490 | "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9"
491 | },
492 | "dist": {
493 | "type": "zip",
494 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9",
495 | "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9",
496 | "shasum": ""
497 | },
498 | "require": {
499 | "php": "^7.1",
500 | "phpdocumentor/reflection-common": "^2.0"
501 | },
502 | "require-dev": {
503 | "ext-tokenizer": "^7.1",
504 | "mockery/mockery": "~1",
505 | "phpunit/phpunit": "^7.0"
506 | },
507 | "type": "library",
508 | "extra": {
509 | "branch-alias": {
510 | "dev-master": "1.x-dev"
511 | }
512 | },
513 | "autoload": {
514 | "psr-4": {
515 | "phpDocumentor\\Reflection\\": "src"
516 | }
517 | },
518 | "notification-url": "https://packagist.org/downloads/",
519 | "license": [
520 | "MIT"
521 | ],
522 | "authors": [
523 | {
524 | "name": "Mike van Riel",
525 | "email": "me@mikevanriel.com"
526 | }
527 | ],
528 | "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
529 | "time": "2019-08-22T18:11:29+00:00"
530 | },
531 | {
532 | "name": "phpspec/prophecy",
533 | "version": "1.9.0",
534 | "source": {
535 | "type": "git",
536 | "url": "https://github.com/phpspec/prophecy.git",
537 | "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203"
538 | },
539 | "dist": {
540 | "type": "zip",
541 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203",
542 | "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203",
543 | "shasum": ""
544 | },
545 | "require": {
546 | "doctrine/instantiator": "^1.0.2",
547 | "php": "^5.3|^7.0",
548 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
549 | "sebastian/comparator": "^1.1|^2.0|^3.0",
550 | "sebastian/recursion-context": "^1.0|^2.0|^3.0"
551 | },
552 | "require-dev": {
553 | "phpspec/phpspec": "^2.5|^3.2",
554 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
555 | },
556 | "type": "library",
557 | "extra": {
558 | "branch-alias": {
559 | "dev-master": "1.8.x-dev"
560 | }
561 | },
562 | "autoload": {
563 | "psr-4": {
564 | "Prophecy\\": "src/Prophecy"
565 | }
566 | },
567 | "notification-url": "https://packagist.org/downloads/",
568 | "license": [
569 | "MIT"
570 | ],
571 | "authors": [
572 | {
573 | "name": "Konstantin Kudryashov",
574 | "email": "ever.zet@gmail.com",
575 | "homepage": "http://everzet.com"
576 | },
577 | {
578 | "name": "Marcello Duarte",
579 | "email": "marcello.duarte@gmail.com"
580 | }
581 | ],
582 | "description": "Highly opinionated mocking framework for PHP 5.3+",
583 | "homepage": "https://github.com/phpspec/prophecy",
584 | "keywords": [
585 | "Double",
586 | "Dummy",
587 | "fake",
588 | "mock",
589 | "spy",
590 | "stub"
591 | ],
592 | "time": "2019-10-03T11:07:50+00:00"
593 | },
594 | {
595 | "name": "phpunit/php-code-coverage",
596 | "version": "7.0.10",
597 | "source": {
598 | "type": "git",
599 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
600 | "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf"
601 | },
602 | "dist": {
603 | "type": "zip",
604 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f1884187926fbb755a9aaf0b3836ad3165b478bf",
605 | "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf",
606 | "shasum": ""
607 | },
608 | "require": {
609 | "ext-dom": "*",
610 | "ext-xmlwriter": "*",
611 | "php": "^7.2",
612 | "phpunit/php-file-iterator": "^2.0.2",
613 | "phpunit/php-text-template": "^1.2.1",
614 | "phpunit/php-token-stream": "^3.1.1",
615 | "sebastian/code-unit-reverse-lookup": "^1.0.1",
616 | "sebastian/environment": "^4.2.2",
617 | "sebastian/version": "^2.0.1",
618 | "theseer/tokenizer": "^1.1.3"
619 | },
620 | "require-dev": {
621 | "phpunit/phpunit": "^8.2.2"
622 | },
623 | "suggest": {
624 | "ext-xdebug": "^2.7.2"
625 | },
626 | "type": "library",
627 | "extra": {
628 | "branch-alias": {
629 | "dev-master": "7.0-dev"
630 | }
631 | },
632 | "autoload": {
633 | "classmap": [
634 | "src/"
635 | ]
636 | },
637 | "notification-url": "https://packagist.org/downloads/",
638 | "license": [
639 | "BSD-3-Clause"
640 | ],
641 | "authors": [
642 | {
643 | "name": "Sebastian Bergmann",
644 | "email": "sebastian@phpunit.de",
645 | "role": "lead"
646 | }
647 | ],
648 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
649 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
650 | "keywords": [
651 | "coverage",
652 | "testing",
653 | "xunit"
654 | ],
655 | "time": "2019-11-20T13:55:58+00:00"
656 | },
657 | {
658 | "name": "phpunit/php-file-iterator",
659 | "version": "2.0.2",
660 | "source": {
661 | "type": "git",
662 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
663 | "reference": "050bedf145a257b1ff02746c31894800e5122946"
664 | },
665 | "dist": {
666 | "type": "zip",
667 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946",
668 | "reference": "050bedf145a257b1ff02746c31894800e5122946",
669 | "shasum": ""
670 | },
671 | "require": {
672 | "php": "^7.1"
673 | },
674 | "require-dev": {
675 | "phpunit/phpunit": "^7.1"
676 | },
677 | "type": "library",
678 | "extra": {
679 | "branch-alias": {
680 | "dev-master": "2.0.x-dev"
681 | }
682 | },
683 | "autoload": {
684 | "classmap": [
685 | "src/"
686 | ]
687 | },
688 | "notification-url": "https://packagist.org/downloads/",
689 | "license": [
690 | "BSD-3-Clause"
691 | ],
692 | "authors": [
693 | {
694 | "name": "Sebastian Bergmann",
695 | "email": "sebastian@phpunit.de",
696 | "role": "lead"
697 | }
698 | ],
699 | "description": "FilterIterator implementation that filters files based on a list of suffixes.",
700 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
701 | "keywords": [
702 | "filesystem",
703 | "iterator"
704 | ],
705 | "time": "2018-09-13T20:33:42+00:00"
706 | },
707 | {
708 | "name": "phpunit/php-text-template",
709 | "version": "1.2.1",
710 | "source": {
711 | "type": "git",
712 | "url": "https://github.com/sebastianbergmann/php-text-template.git",
713 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
714 | },
715 | "dist": {
716 | "type": "zip",
717 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
718 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
719 | "shasum": ""
720 | },
721 | "require": {
722 | "php": ">=5.3.3"
723 | },
724 | "type": "library",
725 | "autoload": {
726 | "classmap": [
727 | "src/"
728 | ]
729 | },
730 | "notification-url": "https://packagist.org/downloads/",
731 | "license": [
732 | "BSD-3-Clause"
733 | ],
734 | "authors": [
735 | {
736 | "name": "Sebastian Bergmann",
737 | "email": "sebastian@phpunit.de",
738 | "role": "lead"
739 | }
740 | ],
741 | "description": "Simple template engine.",
742 | "homepage": "https://github.com/sebastianbergmann/php-text-template/",
743 | "keywords": [
744 | "template"
745 | ],
746 | "time": "2015-06-21T13:50:34+00:00"
747 | },
748 | {
749 | "name": "phpunit/php-timer",
750 | "version": "2.1.2",
751 | "source": {
752 | "type": "git",
753 | "url": "https://github.com/sebastianbergmann/php-timer.git",
754 | "reference": "1038454804406b0b5f5f520358e78c1c2f71501e"
755 | },
756 | "dist": {
757 | "type": "zip",
758 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e",
759 | "reference": "1038454804406b0b5f5f520358e78c1c2f71501e",
760 | "shasum": ""
761 | },
762 | "require": {
763 | "php": "^7.1"
764 | },
765 | "require-dev": {
766 | "phpunit/phpunit": "^7.0"
767 | },
768 | "type": "library",
769 | "extra": {
770 | "branch-alias": {
771 | "dev-master": "2.1-dev"
772 | }
773 | },
774 | "autoload": {
775 | "classmap": [
776 | "src/"
777 | ]
778 | },
779 | "notification-url": "https://packagist.org/downloads/",
780 | "license": [
781 | "BSD-3-Clause"
782 | ],
783 | "authors": [
784 | {
785 | "name": "Sebastian Bergmann",
786 | "email": "sebastian@phpunit.de",
787 | "role": "lead"
788 | }
789 | ],
790 | "description": "Utility class for timing",
791 | "homepage": "https://github.com/sebastianbergmann/php-timer/",
792 | "keywords": [
793 | "timer"
794 | ],
795 | "time": "2019-06-07T04:22:29+00:00"
796 | },
797 | {
798 | "name": "phpunit/php-token-stream",
799 | "version": "3.1.1",
800 | "source": {
801 | "type": "git",
802 | "url": "https://github.com/sebastianbergmann/php-token-stream.git",
803 | "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff"
804 | },
805 | "dist": {
806 | "type": "zip",
807 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff",
808 | "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff",
809 | "shasum": ""
810 | },
811 | "require": {
812 | "ext-tokenizer": "*",
813 | "php": "^7.1"
814 | },
815 | "require-dev": {
816 | "phpunit/phpunit": "^7.0"
817 | },
818 | "type": "library",
819 | "extra": {
820 | "branch-alias": {
821 | "dev-master": "3.1-dev"
822 | }
823 | },
824 | "autoload": {
825 | "classmap": [
826 | "src/"
827 | ]
828 | },
829 | "notification-url": "https://packagist.org/downloads/",
830 | "license": [
831 | "BSD-3-Clause"
832 | ],
833 | "authors": [
834 | {
835 | "name": "Sebastian Bergmann",
836 | "email": "sebastian@phpunit.de"
837 | }
838 | ],
839 | "description": "Wrapper around PHP's tokenizer extension.",
840 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
841 | "keywords": [
842 | "tokenizer"
843 | ],
844 | "time": "2019-09-17T06:23:10+00:00"
845 | },
846 | {
847 | "name": "phpunit/phpunit",
848 | "version": "8.5.0",
849 | "source": {
850 | "type": "git",
851 | "url": "https://github.com/sebastianbergmann/phpunit.git",
852 | "reference": "3ee1c1fd6fc264480c25b6fb8285edefe1702dab"
853 | },
854 | "dist": {
855 | "type": "zip",
856 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3ee1c1fd6fc264480c25b6fb8285edefe1702dab",
857 | "reference": "3ee1c1fd6fc264480c25b6fb8285edefe1702dab",
858 | "shasum": ""
859 | },
860 | "require": {
861 | "doctrine/instantiator": "^1.2.0",
862 | "ext-dom": "*",
863 | "ext-json": "*",
864 | "ext-libxml": "*",
865 | "ext-mbstring": "*",
866 | "ext-xml": "*",
867 | "ext-xmlwriter": "*",
868 | "myclabs/deep-copy": "^1.9.1",
869 | "phar-io/manifest": "^1.0.3",
870 | "phar-io/version": "^2.0.1",
871 | "php": "^7.2",
872 | "phpspec/prophecy": "^1.8.1",
873 | "phpunit/php-code-coverage": "^7.0.7",
874 | "phpunit/php-file-iterator": "^2.0.2",
875 | "phpunit/php-text-template": "^1.2.1",
876 | "phpunit/php-timer": "^2.1.2",
877 | "sebastian/comparator": "^3.0.2",
878 | "sebastian/diff": "^3.0.2",
879 | "sebastian/environment": "^4.2.2",
880 | "sebastian/exporter": "^3.1.1",
881 | "sebastian/global-state": "^3.0.0",
882 | "sebastian/object-enumerator": "^3.0.3",
883 | "sebastian/resource-operations": "^2.0.1",
884 | "sebastian/type": "^1.1.3",
885 | "sebastian/version": "^2.0.1"
886 | },
887 | "require-dev": {
888 | "ext-pdo": "*"
889 | },
890 | "suggest": {
891 | "ext-soap": "*",
892 | "ext-xdebug": "*",
893 | "phpunit/php-invoker": "^2.0.0"
894 | },
895 | "bin": [
896 | "phpunit"
897 | ],
898 | "type": "library",
899 | "extra": {
900 | "branch-alias": {
901 | "dev-master": "8.5-dev"
902 | }
903 | },
904 | "autoload": {
905 | "classmap": [
906 | "src/"
907 | ]
908 | },
909 | "notification-url": "https://packagist.org/downloads/",
910 | "license": [
911 | "BSD-3-Clause"
912 | ],
913 | "authors": [
914 | {
915 | "name": "Sebastian Bergmann",
916 | "email": "sebastian@phpunit.de",
917 | "role": "lead"
918 | }
919 | ],
920 | "description": "The PHP Unit Testing framework.",
921 | "homepage": "https://phpunit.de/",
922 | "keywords": [
923 | "phpunit",
924 | "testing",
925 | "xunit"
926 | ],
927 | "time": "2019-12-06T05:41:38+00:00"
928 | },
929 | {
930 | "name": "sebastian/code-unit-reverse-lookup",
931 | "version": "1.0.1",
932 | "source": {
933 | "type": "git",
934 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
935 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
936 | },
937 | "dist": {
938 | "type": "zip",
939 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
940 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
941 | "shasum": ""
942 | },
943 | "require": {
944 | "php": "^5.6 || ^7.0"
945 | },
946 | "require-dev": {
947 | "phpunit/phpunit": "^5.7 || ^6.0"
948 | },
949 | "type": "library",
950 | "extra": {
951 | "branch-alias": {
952 | "dev-master": "1.0.x-dev"
953 | }
954 | },
955 | "autoload": {
956 | "classmap": [
957 | "src/"
958 | ]
959 | },
960 | "notification-url": "https://packagist.org/downloads/",
961 | "license": [
962 | "BSD-3-Clause"
963 | ],
964 | "authors": [
965 | {
966 | "name": "Sebastian Bergmann",
967 | "email": "sebastian@phpunit.de"
968 | }
969 | ],
970 | "description": "Looks up which function or method a line of code belongs to",
971 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
972 | "time": "2017-03-04T06:30:41+00:00"
973 | },
974 | {
975 | "name": "sebastian/comparator",
976 | "version": "3.0.2",
977 | "source": {
978 | "type": "git",
979 | "url": "https://github.com/sebastianbergmann/comparator.git",
980 | "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da"
981 | },
982 | "dist": {
983 | "type": "zip",
984 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
985 | "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
986 | "shasum": ""
987 | },
988 | "require": {
989 | "php": "^7.1",
990 | "sebastian/diff": "^3.0",
991 | "sebastian/exporter": "^3.1"
992 | },
993 | "require-dev": {
994 | "phpunit/phpunit": "^7.1"
995 | },
996 | "type": "library",
997 | "extra": {
998 | "branch-alias": {
999 | "dev-master": "3.0-dev"
1000 | }
1001 | },
1002 | "autoload": {
1003 | "classmap": [
1004 | "src/"
1005 | ]
1006 | },
1007 | "notification-url": "https://packagist.org/downloads/",
1008 | "license": [
1009 | "BSD-3-Clause"
1010 | ],
1011 | "authors": [
1012 | {
1013 | "name": "Jeff Welch",
1014 | "email": "whatthejeff@gmail.com"
1015 | },
1016 | {
1017 | "name": "Volker Dusch",
1018 | "email": "github@wallbash.com"
1019 | },
1020 | {
1021 | "name": "Bernhard Schussek",
1022 | "email": "bschussek@2bepublished.at"
1023 | },
1024 | {
1025 | "name": "Sebastian Bergmann",
1026 | "email": "sebastian@phpunit.de"
1027 | }
1028 | ],
1029 | "description": "Provides the functionality to compare PHP values for equality",
1030 | "homepage": "https://github.com/sebastianbergmann/comparator",
1031 | "keywords": [
1032 | "comparator",
1033 | "compare",
1034 | "equality"
1035 | ],
1036 | "time": "2018-07-12T15:12:46+00:00"
1037 | },
1038 | {
1039 | "name": "sebastian/diff",
1040 | "version": "3.0.2",
1041 | "source": {
1042 | "type": "git",
1043 | "url": "https://github.com/sebastianbergmann/diff.git",
1044 | "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29"
1045 | },
1046 | "dist": {
1047 | "type": "zip",
1048 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
1049 | "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
1050 | "shasum": ""
1051 | },
1052 | "require": {
1053 | "php": "^7.1"
1054 | },
1055 | "require-dev": {
1056 | "phpunit/phpunit": "^7.5 || ^8.0",
1057 | "symfony/process": "^2 || ^3.3 || ^4"
1058 | },
1059 | "type": "library",
1060 | "extra": {
1061 | "branch-alias": {
1062 | "dev-master": "3.0-dev"
1063 | }
1064 | },
1065 | "autoload": {
1066 | "classmap": [
1067 | "src/"
1068 | ]
1069 | },
1070 | "notification-url": "https://packagist.org/downloads/",
1071 | "license": [
1072 | "BSD-3-Clause"
1073 | ],
1074 | "authors": [
1075 | {
1076 | "name": "Kore Nordmann",
1077 | "email": "mail@kore-nordmann.de"
1078 | },
1079 | {
1080 | "name": "Sebastian Bergmann",
1081 | "email": "sebastian@phpunit.de"
1082 | }
1083 | ],
1084 | "description": "Diff implementation",
1085 | "homepage": "https://github.com/sebastianbergmann/diff",
1086 | "keywords": [
1087 | "diff",
1088 | "udiff",
1089 | "unidiff",
1090 | "unified diff"
1091 | ],
1092 | "time": "2019-02-04T06:01:07+00:00"
1093 | },
1094 | {
1095 | "name": "sebastian/environment",
1096 | "version": "4.2.3",
1097 | "source": {
1098 | "type": "git",
1099 | "url": "https://github.com/sebastianbergmann/environment.git",
1100 | "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368"
1101 | },
1102 | "dist": {
1103 | "type": "zip",
1104 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368",
1105 | "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368",
1106 | "shasum": ""
1107 | },
1108 | "require": {
1109 | "php": "^7.1"
1110 | },
1111 | "require-dev": {
1112 | "phpunit/phpunit": "^7.5"
1113 | },
1114 | "suggest": {
1115 | "ext-posix": "*"
1116 | },
1117 | "type": "library",
1118 | "extra": {
1119 | "branch-alias": {
1120 | "dev-master": "4.2-dev"
1121 | }
1122 | },
1123 | "autoload": {
1124 | "classmap": [
1125 | "src/"
1126 | ]
1127 | },
1128 | "notification-url": "https://packagist.org/downloads/",
1129 | "license": [
1130 | "BSD-3-Clause"
1131 | ],
1132 | "authors": [
1133 | {
1134 | "name": "Sebastian Bergmann",
1135 | "email": "sebastian@phpunit.de"
1136 | }
1137 | ],
1138 | "description": "Provides functionality to handle HHVM/PHP environments",
1139 | "homepage": "http://www.github.com/sebastianbergmann/environment",
1140 | "keywords": [
1141 | "Xdebug",
1142 | "environment",
1143 | "hhvm"
1144 | ],
1145 | "time": "2019-11-20T08:46:58+00:00"
1146 | },
1147 | {
1148 | "name": "sebastian/exporter",
1149 | "version": "3.1.2",
1150 | "source": {
1151 | "type": "git",
1152 | "url": "https://github.com/sebastianbergmann/exporter.git",
1153 | "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e"
1154 | },
1155 | "dist": {
1156 | "type": "zip",
1157 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e",
1158 | "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e",
1159 | "shasum": ""
1160 | },
1161 | "require": {
1162 | "php": "^7.0",
1163 | "sebastian/recursion-context": "^3.0"
1164 | },
1165 | "require-dev": {
1166 | "ext-mbstring": "*",
1167 | "phpunit/phpunit": "^6.0"
1168 | },
1169 | "type": "library",
1170 | "extra": {
1171 | "branch-alias": {
1172 | "dev-master": "3.1.x-dev"
1173 | }
1174 | },
1175 | "autoload": {
1176 | "classmap": [
1177 | "src/"
1178 | ]
1179 | },
1180 | "notification-url": "https://packagist.org/downloads/",
1181 | "license": [
1182 | "BSD-3-Clause"
1183 | ],
1184 | "authors": [
1185 | {
1186 | "name": "Sebastian Bergmann",
1187 | "email": "sebastian@phpunit.de"
1188 | },
1189 | {
1190 | "name": "Jeff Welch",
1191 | "email": "whatthejeff@gmail.com"
1192 | },
1193 | {
1194 | "name": "Volker Dusch",
1195 | "email": "github@wallbash.com"
1196 | },
1197 | {
1198 | "name": "Adam Harvey",
1199 | "email": "aharvey@php.net"
1200 | },
1201 | {
1202 | "name": "Bernhard Schussek",
1203 | "email": "bschussek@gmail.com"
1204 | }
1205 | ],
1206 | "description": "Provides the functionality to export PHP variables for visualization",
1207 | "homepage": "http://www.github.com/sebastianbergmann/exporter",
1208 | "keywords": [
1209 | "export",
1210 | "exporter"
1211 | ],
1212 | "time": "2019-09-14T09:02:43+00:00"
1213 | },
1214 | {
1215 | "name": "sebastian/global-state",
1216 | "version": "3.0.0",
1217 | "source": {
1218 | "type": "git",
1219 | "url": "https://github.com/sebastianbergmann/global-state.git",
1220 | "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4"
1221 | },
1222 | "dist": {
1223 | "type": "zip",
1224 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4",
1225 | "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4",
1226 | "shasum": ""
1227 | },
1228 | "require": {
1229 | "php": "^7.2",
1230 | "sebastian/object-reflector": "^1.1.1",
1231 | "sebastian/recursion-context": "^3.0"
1232 | },
1233 | "require-dev": {
1234 | "ext-dom": "*",
1235 | "phpunit/phpunit": "^8.0"
1236 | },
1237 | "suggest": {
1238 | "ext-uopz": "*"
1239 | },
1240 | "type": "library",
1241 | "extra": {
1242 | "branch-alias": {
1243 | "dev-master": "3.0-dev"
1244 | }
1245 | },
1246 | "autoload": {
1247 | "classmap": [
1248 | "src/"
1249 | ]
1250 | },
1251 | "notification-url": "https://packagist.org/downloads/",
1252 | "license": [
1253 | "BSD-3-Clause"
1254 | ],
1255 | "authors": [
1256 | {
1257 | "name": "Sebastian Bergmann",
1258 | "email": "sebastian@phpunit.de"
1259 | }
1260 | ],
1261 | "description": "Snapshotting of global state",
1262 | "homepage": "http://www.github.com/sebastianbergmann/global-state",
1263 | "keywords": [
1264 | "global state"
1265 | ],
1266 | "time": "2019-02-01T05:30:01+00:00"
1267 | },
1268 | {
1269 | "name": "sebastian/object-enumerator",
1270 | "version": "3.0.3",
1271 | "source": {
1272 | "type": "git",
1273 | "url": "https://github.com/sebastianbergmann/object-enumerator.git",
1274 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5"
1275 | },
1276 | "dist": {
1277 | "type": "zip",
1278 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5",
1279 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5",
1280 | "shasum": ""
1281 | },
1282 | "require": {
1283 | "php": "^7.0",
1284 | "sebastian/object-reflector": "^1.1.1",
1285 | "sebastian/recursion-context": "^3.0"
1286 | },
1287 | "require-dev": {
1288 | "phpunit/phpunit": "^6.0"
1289 | },
1290 | "type": "library",
1291 | "extra": {
1292 | "branch-alias": {
1293 | "dev-master": "3.0.x-dev"
1294 | }
1295 | },
1296 | "autoload": {
1297 | "classmap": [
1298 | "src/"
1299 | ]
1300 | },
1301 | "notification-url": "https://packagist.org/downloads/",
1302 | "license": [
1303 | "BSD-3-Clause"
1304 | ],
1305 | "authors": [
1306 | {
1307 | "name": "Sebastian Bergmann",
1308 | "email": "sebastian@phpunit.de"
1309 | }
1310 | ],
1311 | "description": "Traverses array structures and object graphs to enumerate all referenced objects",
1312 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
1313 | "time": "2017-08-03T12:35:26+00:00"
1314 | },
1315 | {
1316 | "name": "sebastian/object-reflector",
1317 | "version": "1.1.1",
1318 | "source": {
1319 | "type": "git",
1320 | "url": "https://github.com/sebastianbergmann/object-reflector.git",
1321 | "reference": "773f97c67f28de00d397be301821b06708fca0be"
1322 | },
1323 | "dist": {
1324 | "type": "zip",
1325 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be",
1326 | "reference": "773f97c67f28de00d397be301821b06708fca0be",
1327 | "shasum": ""
1328 | },
1329 | "require": {
1330 | "php": "^7.0"
1331 | },
1332 | "require-dev": {
1333 | "phpunit/phpunit": "^6.0"
1334 | },
1335 | "type": "library",
1336 | "extra": {
1337 | "branch-alias": {
1338 | "dev-master": "1.1-dev"
1339 | }
1340 | },
1341 | "autoload": {
1342 | "classmap": [
1343 | "src/"
1344 | ]
1345 | },
1346 | "notification-url": "https://packagist.org/downloads/",
1347 | "license": [
1348 | "BSD-3-Clause"
1349 | ],
1350 | "authors": [
1351 | {
1352 | "name": "Sebastian Bergmann",
1353 | "email": "sebastian@phpunit.de"
1354 | }
1355 | ],
1356 | "description": "Allows reflection of object attributes, including inherited and non-public ones",
1357 | "homepage": "https://github.com/sebastianbergmann/object-reflector/",
1358 | "time": "2017-03-29T09:07:27+00:00"
1359 | },
1360 | {
1361 | "name": "sebastian/recursion-context",
1362 | "version": "3.0.0",
1363 | "source": {
1364 | "type": "git",
1365 | "url": "https://github.com/sebastianbergmann/recursion-context.git",
1366 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8"
1367 | },
1368 | "dist": {
1369 | "type": "zip",
1370 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
1371 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
1372 | "shasum": ""
1373 | },
1374 | "require": {
1375 | "php": "^7.0"
1376 | },
1377 | "require-dev": {
1378 | "phpunit/phpunit": "^6.0"
1379 | },
1380 | "type": "library",
1381 | "extra": {
1382 | "branch-alias": {
1383 | "dev-master": "3.0.x-dev"
1384 | }
1385 | },
1386 | "autoload": {
1387 | "classmap": [
1388 | "src/"
1389 | ]
1390 | },
1391 | "notification-url": "https://packagist.org/downloads/",
1392 | "license": [
1393 | "BSD-3-Clause"
1394 | ],
1395 | "authors": [
1396 | {
1397 | "name": "Jeff Welch",
1398 | "email": "whatthejeff@gmail.com"
1399 | },
1400 | {
1401 | "name": "Sebastian Bergmann",
1402 | "email": "sebastian@phpunit.de"
1403 | },
1404 | {
1405 | "name": "Adam Harvey",
1406 | "email": "aharvey@php.net"
1407 | }
1408 | ],
1409 | "description": "Provides functionality to recursively process PHP variables",
1410 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
1411 | "time": "2017-03-03T06:23:57+00:00"
1412 | },
1413 | {
1414 | "name": "sebastian/resource-operations",
1415 | "version": "2.0.1",
1416 | "source": {
1417 | "type": "git",
1418 | "url": "https://github.com/sebastianbergmann/resource-operations.git",
1419 | "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9"
1420 | },
1421 | "dist": {
1422 | "type": "zip",
1423 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
1424 | "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
1425 | "shasum": ""
1426 | },
1427 | "require": {
1428 | "php": "^7.1"
1429 | },
1430 | "type": "library",
1431 | "extra": {
1432 | "branch-alias": {
1433 | "dev-master": "2.0-dev"
1434 | }
1435 | },
1436 | "autoload": {
1437 | "classmap": [
1438 | "src/"
1439 | ]
1440 | },
1441 | "notification-url": "https://packagist.org/downloads/",
1442 | "license": [
1443 | "BSD-3-Clause"
1444 | ],
1445 | "authors": [
1446 | {
1447 | "name": "Sebastian Bergmann",
1448 | "email": "sebastian@phpunit.de"
1449 | }
1450 | ],
1451 | "description": "Provides a list of PHP built-in functions that operate on resources",
1452 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
1453 | "time": "2018-10-04T04:07:39+00:00"
1454 | },
1455 | {
1456 | "name": "sebastian/type",
1457 | "version": "1.1.3",
1458 | "source": {
1459 | "type": "git",
1460 | "url": "https://github.com/sebastianbergmann/type.git",
1461 | "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3"
1462 | },
1463 | "dist": {
1464 | "type": "zip",
1465 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3",
1466 | "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3",
1467 | "shasum": ""
1468 | },
1469 | "require": {
1470 | "php": "^7.2"
1471 | },
1472 | "require-dev": {
1473 | "phpunit/phpunit": "^8.2"
1474 | },
1475 | "type": "library",
1476 | "extra": {
1477 | "branch-alias": {
1478 | "dev-master": "1.1-dev"
1479 | }
1480 | },
1481 | "autoload": {
1482 | "classmap": [
1483 | "src/"
1484 | ]
1485 | },
1486 | "notification-url": "https://packagist.org/downloads/",
1487 | "license": [
1488 | "BSD-3-Clause"
1489 | ],
1490 | "authors": [
1491 | {
1492 | "name": "Sebastian Bergmann",
1493 | "email": "sebastian@phpunit.de",
1494 | "role": "lead"
1495 | }
1496 | ],
1497 | "description": "Collection of value objects that represent the types of the PHP type system",
1498 | "homepage": "https://github.com/sebastianbergmann/type",
1499 | "time": "2019-07-02T08:10:15+00:00"
1500 | },
1501 | {
1502 | "name": "sebastian/version",
1503 | "version": "2.0.1",
1504 | "source": {
1505 | "type": "git",
1506 | "url": "https://github.com/sebastianbergmann/version.git",
1507 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
1508 | },
1509 | "dist": {
1510 | "type": "zip",
1511 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
1512 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
1513 | "shasum": ""
1514 | },
1515 | "require": {
1516 | "php": ">=5.6"
1517 | },
1518 | "type": "library",
1519 | "extra": {
1520 | "branch-alias": {
1521 | "dev-master": "2.0.x-dev"
1522 | }
1523 | },
1524 | "autoload": {
1525 | "classmap": [
1526 | "src/"
1527 | ]
1528 | },
1529 | "notification-url": "https://packagist.org/downloads/",
1530 | "license": [
1531 | "BSD-3-Clause"
1532 | ],
1533 | "authors": [
1534 | {
1535 | "name": "Sebastian Bergmann",
1536 | "email": "sebastian@phpunit.de",
1537 | "role": "lead"
1538 | }
1539 | ],
1540 | "description": "Library that helps with managing the version number of Git-hosted PHP projects",
1541 | "homepage": "https://github.com/sebastianbergmann/version",
1542 | "time": "2016-10-03T07:35:21+00:00"
1543 | },
1544 | {
1545 | "name": "symfony/polyfill-ctype",
1546 | "version": "v1.13.1",
1547 | "source": {
1548 | "type": "git",
1549 | "url": "https://github.com/symfony/polyfill-ctype.git",
1550 | "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3"
1551 | },
1552 | "dist": {
1553 | "type": "zip",
1554 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
1555 | "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
1556 | "shasum": ""
1557 | },
1558 | "require": {
1559 | "php": ">=5.3.3"
1560 | },
1561 | "suggest": {
1562 | "ext-ctype": "For best performance"
1563 | },
1564 | "type": "library",
1565 | "extra": {
1566 | "branch-alias": {
1567 | "dev-master": "1.13-dev"
1568 | }
1569 | },
1570 | "autoload": {
1571 | "psr-4": {
1572 | "Symfony\\Polyfill\\Ctype\\": ""
1573 | },
1574 | "files": [
1575 | "bootstrap.php"
1576 | ]
1577 | },
1578 | "notification-url": "https://packagist.org/downloads/",
1579 | "license": [
1580 | "MIT"
1581 | ],
1582 | "authors": [
1583 | {
1584 | "name": "Gert de Pagter",
1585 | "email": "BackEndTea@gmail.com"
1586 | },
1587 | {
1588 | "name": "Symfony Community",
1589 | "homepage": "https://symfony.com/contributors"
1590 | }
1591 | ],
1592 | "description": "Symfony polyfill for ctype functions",
1593 | "homepage": "https://symfony.com",
1594 | "keywords": [
1595 | "compatibility",
1596 | "ctype",
1597 | "polyfill",
1598 | "portable"
1599 | ],
1600 | "time": "2019-11-27T13:56:44+00:00"
1601 | },
1602 | {
1603 | "name": "theseer/tokenizer",
1604 | "version": "1.1.3",
1605 | "source": {
1606 | "type": "git",
1607 | "url": "https://github.com/theseer/tokenizer.git",
1608 | "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9"
1609 | },
1610 | "dist": {
1611 | "type": "zip",
1612 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
1613 | "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
1614 | "shasum": ""
1615 | },
1616 | "require": {
1617 | "ext-dom": "*",
1618 | "ext-tokenizer": "*",
1619 | "ext-xmlwriter": "*",
1620 | "php": "^7.0"
1621 | },
1622 | "type": "library",
1623 | "autoload": {
1624 | "classmap": [
1625 | "src/"
1626 | ]
1627 | },
1628 | "notification-url": "https://packagist.org/downloads/",
1629 | "license": [
1630 | "BSD-3-Clause"
1631 | ],
1632 | "authors": [
1633 | {
1634 | "name": "Arne Blankerts",
1635 | "email": "arne@blankerts.de",
1636 | "role": "Developer"
1637 | }
1638 | ],
1639 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
1640 | "time": "2019-06-13T22:48:21+00:00"
1641 | },
1642 | {
1643 | "name": "webmozart/assert",
1644 | "version": "1.6.0",
1645 | "source": {
1646 | "type": "git",
1647 | "url": "https://github.com/webmozart/assert.git",
1648 | "reference": "573381c0a64f155a0d9a23f4b0c797194805b925"
1649 | },
1650 | "dist": {
1651 | "type": "zip",
1652 | "url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925",
1653 | "reference": "573381c0a64f155a0d9a23f4b0c797194805b925",
1654 | "shasum": ""
1655 | },
1656 | "require": {
1657 | "php": "^5.3.3 || ^7.0",
1658 | "symfony/polyfill-ctype": "^1.8"
1659 | },
1660 | "conflict": {
1661 | "vimeo/psalm": "<3.6.0"
1662 | },
1663 | "require-dev": {
1664 | "phpunit/phpunit": "^4.8.36 || ^7.5.13"
1665 | },
1666 | "type": "library",
1667 | "autoload": {
1668 | "psr-4": {
1669 | "Webmozart\\Assert\\": "src/"
1670 | }
1671 | },
1672 | "notification-url": "https://packagist.org/downloads/",
1673 | "license": [
1674 | "MIT"
1675 | ],
1676 | "authors": [
1677 | {
1678 | "name": "Bernhard Schussek",
1679 | "email": "bschussek@gmail.com"
1680 | }
1681 | ],
1682 | "description": "Assertions to validate method input/output with nice error messages.",
1683 | "keywords": [
1684 | "assert",
1685 | "check",
1686 | "validate"
1687 | ],
1688 | "time": "2019-11-24T13:36:37+00:00"
1689 | }
1690 | ],
1691 | "aliases": [],
1692 | "minimum-stability": "stable",
1693 | "stability-flags": [],
1694 | "prefer-stable": false,
1695 | "prefer-lowest": false,
1696 | "platform": [],
1697 | "platform-dev": []
1698 | }
1699 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests/Unit
16 |
17 |
18 |
19 | ./tests/Feature
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/Area.php:
--------------------------------------------------------------------------------
1 | xTopLeft = $xTopLeft;
17 | $this->yTopLeft = $yTopLeft;
18 | $this->xBottomRight = $xBottomRight;
19 | $this->yBottomRight = $yBottomRight;
20 | }
21 |
22 | public function xTopLeft()
23 | {
24 | return $this->xTopLeft;
25 | }
26 |
27 | public function yTopLeft()
28 | {
29 | return $this->yTopLeft;
30 | }
31 |
32 | public function xBottomRight()
33 | {
34 | return $this->xBottomRight;
35 | }
36 |
37 | public function yBottomRight()
38 | {
39 | return $this->yBottomRight;
40 | }
41 |
42 | public function coords()
43 | {
44 | return implode(',', [
45 | $this->xTopLeft,
46 | $this->yTopLeft,
47 | $this->xBottomRight,
48 | $this->yBottomRight,
49 | ]);
50 | }
51 | }
--------------------------------------------------------------------------------
/src/Areas.php:
--------------------------------------------------------------------------------
1 | push(new Area($xTopLeft, $yTopLeft, $xBottomRight, $yBottomRight));
18 |
19 | return $areas;
20 | }
21 |
22 | public function add($xTopLeft, $yTopLeft, $xBottomRight, $yBottomRight)
23 | {
24 | $this->areas[] = new Area($xTopLeft, $yTopLeft, $xBottomRight, $yBottomRight);
25 |
26 | return $this;
27 | }
28 |
29 | public function push(Area $area)
30 | {
31 | $this->areas[] = $area;
32 |
33 | return $this;
34 | }
35 |
36 | public function toDelimitedString($join)
37 | {
38 | $coords = array_map(function(Area $area) {
39 | return $area->coords();
40 | }, $this->areas);
41 |
42 | return $join . implode($join, $coords);
43 | }
44 | }
--------------------------------------------------------------------------------
/src/Camelot.php:
--------------------------------------------------------------------------------
1 | path = $path;
115 | $this->mode = $mode ?? static::MODE_LATTICE;
116 | }
117 |
118 | public static function lattice($path)
119 | {
120 | return new self($path);
121 | }
122 |
123 | public static function stream($path)
124 | {
125 | return new self($path, static::MODE_STREAM);
126 | }
127 |
128 | public function pages(string $pages)
129 | {
130 | $this->pages = $pages;
131 |
132 | return $this;
133 | }
134 |
135 | public function csv()
136 | {
137 | $this->format = 'csv';
138 | }
139 |
140 | public function save($path)
141 | {
142 | // run the process
143 | $this->runCommand($path);
144 | return $this->getFilesContents($path);
145 | }
146 |
147 | public function extract()
148 | {
149 | $dir = (new TemporaryDirectory())->create();
150 | $path = $dir->path('extract.txt');
151 |
152 | $this->runCommand($path);
153 |
154 | $output = $this->getFilesContents($path);
155 |
156 | $dir->delete();
157 |
158 | return $output;
159 | }
160 |
161 | protected function getFilesContents($filePath)
162 | {
163 | $pathInfo = pathinfo($filePath);
164 | $filename = $pathInfo['filename'];
165 | $directory = $pathInfo['dirname'];
166 |
167 | $files = scandir($directory);
168 | $files = array_values(array_filter($files, function ($file) use ($filename) {
169 | return preg_match("/{$filename}-.*-table-.*\..*/", $file);
170 | }));
171 |
172 | $output = [];
173 |
174 | foreach ($files as $file) {
175 | $output[] = $content = file_get_contents($directory . DIRECTORY_SEPARATOR . $file);
176 | }
177 |
178 | return $output;
179 | }
180 |
181 | protected function runCommand($outputPath = null)
182 | {
183 | $output = $outputPath ? " --output $outputPath" : "";
184 | $mode = " {$this->mode}";
185 | $pages = $this->pages ? " --pages {$this->pages}" : "";
186 | $password = $this->password ? " --password {$this->password}" : "";
187 |
188 | // Advanced options
189 | $background = $this->processBackgroundLines ? " --process_background " : "";
190 | $plot = $this->plot ? " -plot {$this->plot}" : "";
191 | $split = ($this->splitAlongSeparators && $this->columnSeparators) ? " -split" : "";
192 | $flagSize = $this->flagSize ? " -flag" : "";
193 | $columnSeparators = $this->columnSeparators ? " -C " . implode(",",$this->columnSeparators) : "";
194 | $strip = $this->unwantedCharacters ? " -strip '{$this->unwantedCharacters}'" : "";
195 | $edgeTolerance = $this->edgeTolerance ? " -e {$this->edgeTolerance}" : "";
196 | $rowTolerance = $this->rowTolerance ? " -r {$this->rowTolerance}" : "";
197 | $lineScale = $this->lineScale ? " -scale {$this->lineScale}" : "";
198 | $textShift = $this->textShift ? " -shift " . implode(" -shift ", $this->textShift) : "";
199 | $copyText = $this->copyTextDirections ? " -copy " . implode(" -copy ", $this->copyTextDirections) : "";
200 |
201 | // Table areas/regions
202 | $areas = $this->areas ? $this->areas->toDelimitedString(" -T ") : "";
203 | $regions = $this->regions ? $this->regions->toDelimitedString(" -R ") : "";
204 |
205 | $cmd = "camelot --format csv {$output}{$pages}{$password}{$flagSize}{$split}{$strip}{$mode}{$textShift}{$copyText}{$lineScale}{$edgeTolerance}{$rowTolerance}{$background}{$plot}{$areas}{$regions}{$columnSeparators} " . $this->path;
206 |
207 | $process = Process::fromShellCommandline($cmd);
208 | $process->run();
209 |
210 | if (!$process->isSuccessful()) {
211 | $this->throwError($this->path, $process->getErrorOutput(), $cmd);
212 | }
213 | }
214 |
215 | public function json()
216 | {
217 | $this->format = 'json';
218 |
219 | return $this;
220 | }
221 |
222 | public function html()
223 | {
224 | $this->format = 'html';
225 |
226 | return $this;
227 | }
228 |
229 | public function sqlite()
230 | {
231 | $this->format = 'sqlite';
232 |
233 | return $this;
234 | }
235 |
236 | public function excel()
237 | {
238 | $this->format = 'excel';
239 |
240 | return $this;
241 | }
242 |
243 | public function getFormat()
244 | {
245 | return $this->format;
246 | }
247 |
248 | public function getMode()
249 | {
250 | return $this->mode;
251 | }
252 |
253 | /**
254 | * @return string
255 | */
256 | public function getPages()
257 | {
258 | return $this->pages;
259 | }
260 |
261 | public function password($password)
262 | {
263 | $this->password = $password;
264 |
265 | return $this;
266 | }
267 |
268 | protected function throwError($path, string $getErrorOutput, string $cmd)
269 | {
270 | if (strpos($getErrorOutput, 'file has not been decrypted') > -1) {
271 | throw new PdfEncryptedException($path);
272 | }
273 |
274 | throw new \Exception("Unexpected Camelot error.\r\nCommand: $cmd\r\nOutput:\r\n-----------\r\n$getErrorOutput");
275 | }
276 |
277 | public function processBackgroundLines()
278 | {
279 | if ($this->mode !== static::MODE_LATTICE) {
280 | throw new BackgroundLinesNotSupportedException($this->mode);
281 | }
282 |
283 | $this->processBackgroundLines = true;
284 |
285 | return $this;
286 | }
287 |
288 | public function plot($kind = 'text')
289 | {
290 | $this->plot = $kind;
291 |
292 | $this->runCommand();
293 |
294 | return $this;
295 | }
296 |
297 | public function inAreas(Areas $areas)
298 | {
299 | $this->areas = $areas;
300 |
301 | return $this;
302 | }
303 |
304 | public function inRegions(Areas $regions)
305 | {
306 | $this->regions = $regions;
307 |
308 | return $this;
309 | }
310 |
311 | public function setColumnSeparators(array $xCoords, $split = false)
312 | {
313 | if ($this->mode !== static::MODE_STREAM) {
314 | throw new ColumnSeparatorsNotSupportedException($this->mode);
315 | }
316 |
317 | $this->columnSeparators = $xCoords;
318 | $this->splitAlongSeparators = $split;
319 |
320 | return $this;
321 | }
322 |
323 | public function flagSize($flag = true)
324 | {
325 | $this->flagSize = $flag;
326 |
327 | return $this;
328 | }
329 |
330 | public function strip(string $unwantedCharacters)
331 | {
332 | $this->unwantedCharacters = $unwantedCharacters;
333 |
334 | return $this;
335 | }
336 |
337 | public function setEdgeTolerance(int $edgeTolerance)
338 | {
339 | $this->edgeTolerance = $edgeTolerance;
340 |
341 | return $this;
342 | }
343 |
344 | public function setRowTolerance(int $rowTolerance)
345 | {
346 | $this->rowTolerance = $rowTolerance;
347 |
348 | return $this;
349 | }
350 |
351 | public function setLineScale(int $lineScale)
352 | {
353 | $this->lineScale = $lineScale;
354 |
355 | return $this;
356 | }
357 |
358 | public function shiftText(...$directions)
359 | {
360 | $this->textShift = $directions;
361 |
362 | return $this;
363 | }
364 |
365 | public function copyTextSpanningCells(...$directions)
366 | {
367 | $this->copyTextDirections = $directions;
368 |
369 | return $this;
370 | }
371 |
372 | }
--------------------------------------------------------------------------------
/src/Exceptions/BackgroundLinesNotSupportedException.php:
--------------------------------------------------------------------------------
1 | featureName() . ". It can be used with ". $this->validModesString().".");
12 | }
13 |
14 | private function validModesString()
15 | {
16 | $validModes = $this->validModes();
17 | $modes = implode(' | ', $validModes);
18 |
19 | return count($validModes) > 1 ? $modes . ' modes' : $modes . ' mode';
20 | }
21 | abstract protected function validModes(): array;
22 | abstract protected function featureName(): string;
23 | }
--------------------------------------------------------------------------------
/src/Exceptions/PdfEncryptedException.php:
--------------------------------------------------------------------------------
1 | password('my_password').");
14 | }
15 | }
--------------------------------------------------------------------------------
/tests/Feature/AdvancedProcessingTest.php:
--------------------------------------------------------------------------------
1 | file('background_lines_1.pdf'))
20 | ->processBackgroundLines()
21 | ->extract();
22 |
23 | $csv = $this->csvFromString($tables[1]);
24 | $csv->setHeaderOffset(0);
25 |
26 | $this->assertCount(8, $header = $csv->getHeader());
27 | $this->assertEquals('State', $header[0]);
28 | }
29 |
30 | /**
31 | * @test
32 | */
33 | public function cannot_use_background_line_processing_with_stream()
34 | {
35 | $this->expectException(BackgroundLinesNotSupportedException::class);
36 |
37 | Camelot::stream($this->file('background_lines_1.pdf'))
38 | ->processBackgroundLines()
39 | ->extract();
40 | }
41 |
42 | /**
43 | * @test
44 | */
45 | public function set_column_separators()
46 | {
47 | $tables = Camelot::stream($this->file('column_separators.pdf'))
48 | ->setColumnSeparators([72,95,209,327,442,529,566,606,683])
49 | ->extract();
50 |
51 | $this->assertCount(1, $tables);
52 | $csv = $this->csvFromString($tables[0]);
53 | $csv->setHeaderOffset(3);
54 | $this->assertEquals('LICENSE', $csv->getHeader()[0]);
55 | $this->assertEquals('PREMISE', $csv->getHeader()[4]);
56 | }
57 |
58 | /**
59 | * @test
60 | */
61 | public function cannot_set_column_separators_for_lattice_mode()
62 | {
63 | $this->expectException(ColumnSeparatorsNotSupportedException::class);
64 |
65 | Camelot::lattice($this->file('column_separators.pdf'))
66 | ->setColumnSeparators([72,95,209,327,442,529,566,606,683])
67 | ->extract();
68 | }
69 |
70 | /**
71 | * @test
72 | */
73 | public function enable_split_text_along_separators()
74 | {
75 | $tables = Camelot::stream($this->file('column_separators.pdf'))
76 | ->setColumnSeparators([72,95,209,327,442,529,566,606,683], true)
77 | ->extract();
78 |
79 | $this->assertCount(1, $tables);
80 | $csv = $this->csvFromString($tables[0]);
81 | $csv->setHeaderOffset(4);
82 | $this->assertEquals('NUMBER', $csv->getHeader()[0]);
83 | $this->assertEquals('TYPE', $csv->getHeader()[1]);
84 | }
85 |
86 | /**
87 | * @test
88 | */
89 | public function flag_superscripts_and_subscripts()
90 | {
91 | $tables = Camelot::stream($this->file('superscript.pdf'))
92 | ->flagSize()
93 | ->extract();
94 |
95 | $this->assertCount(1, $tables);
96 | $csv = $this->csvFromString($tables[0]);
97 |
98 | $this->assertStringContainsString('', $csv->fetchOne(18)[2]);
99 | $this->assertStringContainsString('', $csv->fetchOne(18)[2]);
100 | }
101 |
102 | /**
103 | * @test
104 | */
105 | public function strip_characters_from_text()
106 | {
107 | $tables = Camelot::stream($this->file('strip.pdf'))
108 | ->strip(" .\n") // space, period or new lines
109 | ->extract();
110 |
111 | $csv = $this->csvFromString($tables[0]);
112 | $this->assertEquals('Robbery', $csv->fetchOne(12)[0]);
113 | }
114 |
115 | /**
116 | * @test
117 | */
118 | public function can_adjust_edge_tolerance()
119 | {
120 | $tables = Camelot::stream($this->file('edge_tol.pdf'))
121 | ->strip("\n")
122 | ->setEdgeTolerance(500)
123 | ->extract();
124 |
125 | $this->assertStringContainsString('Total investment result per unit', $tables[0]);
126 | }
127 |
128 | /**
129 | * @test
130 | */
131 | public function can_adjust_row_tolerance()
132 | {
133 | $tables = Camelot::stream($this->file('row_tol.pdf'))
134 | ->strip("\n")
135 | ->setRowTolerance(10)
136 | ->extract();
137 |
138 | $this->assertCount(1, $tables);
139 | $csv = $this->csvFromString($tables[0]);
140 | $csv->setHeaderOffset(0);
141 |
142 | $this->assertEquals('Nombre Entidad', $csv->getHeader()[1]);
143 | }
144 |
145 | /**
146 | * @test
147 | */
148 | public function can_set_line_scale()
149 | {
150 | $tables = Camelot::lattice($this->file('short_lines.pdf'))
151 | ->strip("-\n")
152 | ->setLineScale(40)
153 | ->extract();
154 |
155 | $this->assertCount(1, $tables);
156 | $csv = $this->csvFromString($tables[0]);
157 | $csv->setHeaderOffset(0);
158 |
159 | $this->assertEquals('Prevalence', $csv->getHeader()[3]);
160 | $this->assertEquals('C.I*', $csv->getHeader()[4]);
161 | $this->assertEquals('RelativePrecision', $csv->getHeader()[5]);
162 | $this->assertEquals('Sample sizeper State', $csv->getHeader()[6]);
163 | }
164 |
165 | /**
166 | * @test
167 | */
168 | public function can_set_text_shift_in_spanning_cells()
169 | {
170 | $tables = Camelot::lattice($this->file('short_lines.pdf'))
171 | ->strip("-\n")
172 | ->setLineScale(40)
173 | ->shiftText('r', 'b')
174 | ->extract();
175 |
176 | $this->assertCount(1, $tables);
177 | $csv = $this->csvFromString($tables[0]);
178 |
179 | $row = $csv->fetchOne(3);
180 | $this->assertEquals(2400, $row[1]);
181 | }
182 |
183 | /**
184 | * @test
185 | */
186 | public function can_copy_text_in_spanning_cells()
187 | {
188 | $tables = Camelot::lattice($this->file('copy_text.pdf'))
189 | ->copyTextSpanningCells('v')
190 | ->extract();
191 |
192 | $this->assertCount(1, $tables);
193 | $csv = $this->csvFromString($tables[0]);
194 |
195 | $this->assertEquals(4, $csv->fetchOne(4)[0]);
196 | $this->assertEquals('West Bengal', $csv->fetchOne(4)[1]);
197 |
198 | $this->assertEquals(4, $csv->fetchOne(5)[0]);
199 | $this->assertEquals('West Bengal', $csv->fetchOne(5)[1]);
200 | }
201 | }
--------------------------------------------------------------------------------
/tests/Feature/EncryptedPdfTest.php:
--------------------------------------------------------------------------------
1 | expectException(PdfEncryptedException::class);
19 | Camelot::lattice($this->file('health_protected.pdf'))->extract();
20 | }
21 |
22 | /**
23 | * @test
24 | */
25 | public function can_set_password_to_read_encrypted_pdf()
26 | {
27 | $tables = Camelot::lattice($this->file('health_protected.pdf'))
28 | ->password('ownerpass')
29 | ->extract();
30 |
31 | $this->assertCount(1, $tables);
32 | }
33 | }
--------------------------------------------------------------------------------
/tests/Feature/PagesTest.php:
--------------------------------------------------------------------------------
1 | pages($pages = '1-2,4,5-end');
18 |
19 | $this->assertEquals($pages,$camelot->getPages());
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/tests/Feature/ParseExportTest.php:
--------------------------------------------------------------------------------
1 | file('foo_lattice.pdf'))
15 | ->extract()[0];
16 |
17 | $csv = $this->csvFromString($tables);
18 | $csv->setHeaderOffset(0);
19 |
20 | $this->assertCount(7, $csv->getHeader());
21 | }
22 |
23 | /**
24 | * @test
25 | */
26 | public function can_set_stream_mode()
27 | {
28 | $tables = Camelot::stream($this->file('health_stream.pdf'))
29 | ->extract()[0];
30 |
31 | $csv = $this->csvFromString($tables);
32 | $csv->setHeaderOffset(1);
33 |
34 | $this->assertCount(8, $header = $csv->getHeader());
35 | $this->assertEquals('States-A', $header[0]);
36 | }
37 |
38 | /**
39 | * @test
40 | */
41 | public function can_set_output_filename_base()
42 | {
43 | Camelot::stream($this->file('health_stream.pdf'))
44 | ->save(__DIR__ . '/output-custom.txt');
45 |
46 | $this->assertFileExists(__DIR__ . '/output-custom-page-1-table-1.txt');
47 | unlink(__DIR__ . '/output-custom-page-1-table-1.txt');
48 | }
49 |
50 | /**
51 | * @test
52 | */
53 | public function can_set_output_format()
54 | {
55 | $camelot = Camelot::lattice($this->file('foo_lattice.pdf'));
56 | $this->assertEquals('csv', $camelot->getFormat());
57 |
58 | $camelot->json();
59 | $this->assertEquals('json', $camelot->getFormat());
60 |
61 | $camelot->csv();
62 | $this->assertEquals('csv', $camelot->getFormat());
63 |
64 | $camelot->html();
65 | $this->assertEquals('html', $camelot->getFormat());
66 |
67 | $camelot->sqlite();
68 | $this->assertEquals('sqlite', $camelot->getFormat());
69 |
70 | $camelot->excel();
71 | $this->assertEquals('excel', $camelot->getFormat());
72 | }
73 |
74 | /**
75 | * @test
76 | */
77 | public function defaults_to_lattice_algorithm()
78 | {
79 | $camelot = new Camelot('foo.pdf');
80 | $this->assertEquals('lattice', $camelot->getMode());
81 | }
82 | }
--------------------------------------------------------------------------------
/tests/Feature/TableHintsTest.php:
--------------------------------------------------------------------------------
1 | file('table_areas.pdf'))
21 | ->inAreas($area)
22 | ->extract();
23 |
24 | $this->assertCount(1, $tables);
25 | $csv = $this->csvFromString($tables[0]);
26 | $csv->setHeaderOffset(1);
27 | $this->assertEquals('Payroll Period', $csv->getHeader()[0]);
28 | }
29 |
30 | /**
31 | * @test
32 | */
33 | public function can_set_more_than_one_table_area()
34 | {
35 | $area = Areas::from(89, 694,529,446)
36 | ->add(89, 383, 529, 143)
37 | ;
38 |
39 | $tables = Camelot::stream($this->file('twotables_2.pdf'))
40 | ->inAreas($area)
41 | ->extract();
42 |
43 | $this->assertCount(2, $tables);
44 | $csv = $this->csvFromString($tables[0]);
45 | $csv->setHeaderOffset(1);
46 |
47 | $this->assertEquals('State', $csv->getHeader()[0]);
48 | $this->assertEquals('n', $csv->getHeader()[1]);
49 |
50 | $csv = $this->csvFromString($tables[1]);
51 | $csv->setHeaderOffset(1);
52 | $this->assertEquals('State', $csv->getHeader()[0]);
53 | $this->assertEquals('n', $csv->getHeader()[1]);
54 | }
55 |
56 | /**
57 | * @test
58 | */
59 | public function can_suggest_table_region()
60 | {
61 | $area = Areas::from(170,370,560,270);
62 |
63 | $tables = Camelot::lattice($this->file('table_region.pdf'))
64 | ->inRegions($area)
65 | ->extract();
66 |
67 | $this->assertCount(1, $tables);
68 | $csv = $this->csvFromString($tables[0]);
69 | $csv->setHeaderOffset(0);
70 |
71 | $this->assertEquals('Età dell’Assicurato
72 | all’epoca del decesso', $csv->getHeader()[0]);
73 | }
74 | }
--------------------------------------------------------------------------------
/tests/Helpers/CsvAssertions.php:
--------------------------------------------------------------------------------
1 |