Skip to content

Commit c6718ae

Browse files
committed
Ghostscript + Base64 File input
1 parent 1b23a85 commit c6718ae

16 files changed

+235
-15
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ docs
44
vendor
55
coverage
66
.phpunit.result.cache
7+
.DS_Store

.travis.yml

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ before_install:
2323

2424
install:
2525
- travis_retry composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction --no-suggest
26+
- sudo apt-get -qq update
27+
- sudo apt-get install -y --allow-unauthenticated ghostscript
2628

2729
script:
2830
- vendor/bin/phpunit

composer.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
"require-dev": {
2323
"moneyphp/money": "^3.3",
2424
"orchestra/testbench": "^4.0 || ^5.0",
25-
"phpunit/phpunit": "^8.5"
25+
"phpunit/phpunit": "^8.5",
26+
"symfony/process": "^5.1"
2627
},
2728
"suggest": {
28-
"moneyphp/money": "Represent money"
29+
"moneyphp/money": "Blade directives to represent money",
30+
"symfony/process": "To run Ghostscript"
2931
},
3032
"autoload": {
3133
"psr-4": {

src/Pdf/CanRegeneratePDF.php

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace ProtoneMedia\LaravelMixins\Pdf;
4+
5+
interface CanRegeneratePDF
6+
{
7+
public function regeneratePdf(string $pdfContent): string;
8+
}

src/Pdf/Ghostscript.php

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace ProtoneMedia\LaravelMixins\Pdf;
4+
5+
use Symfony\Component\Process\Process;
6+
7+
class Ghostscript implements CanRegeneratePDF
8+
{
9+
private $bin;
10+
11+
public function __construct(string $bin = 'gs')
12+
{
13+
$this->bin = $bin;
14+
}
15+
16+
private static function tempFile(): string
17+
{
18+
return tempnam(sys_get_temp_dir(), 'ghostscript') . '.pdf';
19+
}
20+
21+
public function regeneratePdf(string $pdfContent): string
22+
{
23+
file_put_contents($input = static::tempFile(), $pdfContent);
24+
25+
(new Process([
26+
$this->bin,
27+
'-sDEVICE=pdfwrite',
28+
'-dPDFSETTINGS=/prepress',
29+
'-sOutputFile=' . $destination = static::tempFile(),
30+
$input,
31+
]))->run();
32+
33+
return tap(@file_get_contents($destination), function () use ($input, $destination) {
34+
@unlink($input);
35+
@unlink($destination);
36+
});
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace ProtoneMedia\LaravelMixins\Request;
4+
5+
use Illuminate\Http\UploadedFile;
6+
use Illuminate\Support\Collection;
7+
use Illuminate\Support\Str;
8+
9+
trait ConvertsBase64ImagesToFiles
10+
{
11+
protected function base64ImageKeys(): array
12+
{
13+
return [];
14+
}
15+
16+
protected function prepareForValidation()
17+
{
18+
Collection::make($this->base64ImageKeys())->each(function ($filename, $key) {
19+
rescue(function () use ($key, $filename) {
20+
$base64Value = $this->input($key);
21+
22+
if (!$base64Value) {
23+
return;
24+
}
25+
26+
$tmpFilePath = tempnam(sys_get_temp_dir(), $filename);
27+
28+
if (Str::startsWith($base64Value, 'data:') && count(explode(',', $base64Value)) > 1) {
29+
$source = fopen($base64Value, 'r');
30+
$destination = fopen($tmpFilePath, 'w');
31+
32+
stream_copy_to_stream($source, $destination);
33+
34+
fclose($source);
35+
fclose($destination);
36+
} else {
37+
file_put_contents($tmpFilePath, base64_decode($base64Value, true));
38+
}
39+
40+
$uploadedFile = new UploadedFile($tmpFilePath, $filename, null, null, true);
41+
42+
$this->request->remove($key);
43+
$this->files->set($key, $uploadedFile);
44+
}, null, false);
45+
});
46+
}
47+
}

tests/Blade/DecimalMoneyFormatter.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ protected function setUp(): void
1414
{
1515
parent::setUp();
1616

17-
$this->setViewPath(__DIR__ . '/money');
17+
$this->setViewPath(__DIR__ . '/templates');
1818

1919
DecimalMoneyFormatter::directive();
2020
}

tests/Blade/IntlFormatterTest.php

+17-12
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,40 @@ protected function setUp(): void
1414
{
1515
parent::setUp();
1616

17-
$this->setViewPath(__DIR__ . '/money');
17+
$this->setViewPath(__DIR__ . '/templates');
1818

1919
IntlMoneyFormatter::directive();
2020
}
2121

22+
private static function replaceNonBreakingSpace(string $value): string
23+
{
24+
return str_replace("\u{00A0}", " ", $value);
25+
}
26+
2227
/** @test */
2328
public function it_has_a_blade_directive_to_format_money()
2429
{
2530
// @money(99)
26-
// $this->assertEquals('€ 0,99', $this->renderView('intl', ['cents' => 99]));
27-
// $this->assertEquals('€ 1,00', $this->renderView('intl', ['cents' => 100]));
28-
// $this->assertEquals('€ 1.000,00', $this->renderView('intl', ['cents' => 100 * 1000]));
31+
$this->assertEquals('€ 0,99', static::replaceNonBreakingSpace($this->renderView('intl', ['cents' => 99])));
32+
$this->assertEquals('€ 1,00', static::replaceNonBreakingSpace($this->renderView('intl', ['cents' => 100])));
33+
$this->assertEquals('€ 1.000,00', static::replaceNonBreakingSpace($this->renderView('intl', ['cents' => 100 * 1000])));
2934

3035
// @money(99, 'USD')
31-
$this->assertEquals('US$ 0,99', $this->renderView('intl', ['cents' => 99, 'code' => 'USD']));
32-
$this->assertEquals('US$ 1,00', $this->renderView('intl', ['cents' => 100, 'code' => 'USD']));
33-
$this->assertEquals('US$ 1.000,00', $this->renderView('intl', ['cents' => 100 * 1000, 'code' => 'USD']));
36+
$this->assertEquals('US$ 0,99', static::replaceNonBreakingSpace($this->renderView('intl', ['cents' => 99, 'code' => 'USD'])));
37+
$this->assertEquals('US$ 1,00', static::replaceNonBreakingSpace($this->renderView('intl', ['cents' => 100, 'code' => 'USD'])));
38+
$this->assertEquals('US$ 1.000,00', static::replaceNonBreakingSpace($this->renderView('intl', ['cents' => 100 * 1000, 'code' => 'USD'])));
3439

3540
// or set a default
3641
IntlMoneyFormatter::directive('money', 'USD');
37-
$this->assertEquals('US$ 0,99', $this->renderView('intl', ['cents' => 99]));
42+
$this->assertEquals('US$ 0,99', static::replaceNonBreakingSpace($this->renderView('intl', ['cents' => 99])));
3843

3944
// @money(99, 'USD', 'en')
40-
$this->assertEquals('$0.99', $this->renderView('intl', ['cents' => 99, 'code' => 'USD', 'locale' => 'en']));
41-
$this->assertEquals('1,00 $', $this->renderView('intl', ['cents' => 100, 'code' => 'USD', 'locale' => 'de']));
42-
$this->assertEquals('1 000,00 $US', $this->renderView('intl', ['cents' => 100 * 1000, 'code' => 'USD', 'locale' => 'fr']));
45+
$this->assertEquals('$0.99', static::replaceNonBreakingSpace($this->renderView('intl', ['cents' => 99, 'code' => 'USD', 'locale' => 'en'])));
46+
$this->assertEquals('1,00 $', static::replaceNonBreakingSpace($this->renderView('intl', ['cents' => 100, 'code' => 'USD', 'locale' => 'de'])));
47+
$this->assertEquals('1 000,00 $US', static::replaceNonBreakingSpace($this->renderView('intl', ['cents' => 100 * 1000, 'code' => 'USD', 'locale' => 'fr'])));
4348

4449
// or set a default
4550
IntlMoneyFormatter::directive('money', 'USD', 'fr');
46-
$this->assertEquals('1 000,00 $US', $this->renderView('intl', ['cents' => 100 * 1000]));
51+
$this->assertEquals('1 000,00 $US', static::replaceNonBreakingSpace($this->renderView('intl', ['cents' => 100 * 1000])));
4752
}
4853
}

tests/Pdf/GhostscriptTest.php

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Tests\Unit\Accounting;
4+
5+
use Illuminate\Support\Str;
6+
7+
use Orchestra\Testbench\TestCase;
8+
use ProtoneMedia\LaravelMixins\Pdf\Ghostscript;
9+
10+
class GhostscriptTest extends TestCase
11+
{
12+
/** @test */
13+
public function it_returns_a_string_of_regenerates_pdf_content()
14+
{
15+
$regeneratedPdf = (new Ghostscript)->regeneratePdf(
16+
file_get_contents(__DIR__ . '/dummy.pdf')
17+
);
18+
19+
$this->assertTrue(Str::contains($regeneratedPdf, 'PDF-1.'));
20+
$this->assertTrue(Str::contains($regeneratedPdf, 'youtube.com'));
21+
$this->assertTrue(Str::contains($regeneratedPdf, 'Producer(GPL Ghostscript'));
22+
}
23+
}

tests/Pdf/dummy.pdf

11.9 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
namespace Tests\Unit\Request;
4+
5+
use Illuminate\Foundation\Http\FormRequest;
6+
use Orchestra\Testbench\TestCase;
7+
use ProtoneMedia\LaravelMixins\Request\ConvertsBase64ImagesToFiles;
8+
use ZipArchive;
9+
10+
class ImageRequest extends FormRequest
11+
{
12+
use ConvertsBase64ImagesToFiles;
13+
14+
protected function base64ImageKeys(): array
15+
{
16+
return [
17+
'png_image' => 'Logo1.png',
18+
'jpeg_image' => 'Logo2.jpeg',
19+
];
20+
}
21+
22+
public function rules()
23+
{
24+
return [];
25+
}
26+
}
27+
28+
class ZipRequest extends FormRequest
29+
{
30+
use ConvertsBase64ImagesToFiles;
31+
32+
protected function base64ImageKeys(): array
33+
{
34+
return [
35+
'zip' => 'Logo.zip',
36+
];
37+
}
38+
39+
public function rules()
40+
{
41+
return [];
42+
}
43+
}
44+
45+
class ConvertsBase64ImagesToFilesTest extends TestCase
46+
{
47+
/** @test */
48+
public function it_converts_the_base64_images_to_illuminate_file_uploads()
49+
{
50+
$request = ImageRequest::create('/', 'POST', [
51+
'png_image' => file_get_contents(__DIR__ . '/base64_png'),
52+
'jpeg_image' => file_get_contents(__DIR__ . '/base64_jpeg'),
53+
]);
54+
55+
$request->setContainer(app());
56+
$request->validateResolved();
57+
58+
$pngFile = $request->file('png_image');
59+
$jpegFile = $request->file('jpeg_image');
60+
61+
$this->assertNotNull($pngFile);
62+
$this->assertNotNull($jpegFile);
63+
64+
$pngSize = getimagesize($pngFile->getRealPath());
65+
$jpegSize = getimagesize($jpegFile->getRealPath());
66+
67+
$this->assertEquals('Logo1.png', $pngFile->getClientOriginalName());
68+
$this->assertEquals('Logo2.jpeg', $jpegFile->getClientOriginalName());
69+
70+
$this->assertEquals('width="300" height="300"', $pngSize[3]);
71+
$this->assertEquals('width="300" height="300"', $jpegSize[3]);
72+
}
73+
74+
/** @test */
75+
public function it_converts_a_base64_zip_to_a_illuminate_file_upload()
76+
{
77+
$request = ZipRequest::create('/', 'POST', [
78+
'zip' => file_get_contents(__DIR__ . '/base64_zip'),
79+
]);
80+
81+
$request->setContainer(app());
82+
$request->validateResolved();
83+
84+
$zipFile = $request->file('zip');
85+
86+
$this->assertNotNull($zipFile);
87+
$this->assertTrue(
88+
(new ZipArchive)->open($zipFile->getRealPath(), ZipArchive::CHECKCONS)
89+
);
90+
}
91+
}

tests/Request/base64_jpeg

+1
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)