diff --git a/phpstan.neon b/phpstan.neon
index 37ff335..41b6b74 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -9,5 +9,5 @@ parameters:
level: 8
ignoreErrors:
# This is a global alias that cannot be detected by Larastan.
- - '#Call to static method loadHtml\(\) on an unknown class PDF\.#'
- - '#Call to static method loadHtml\(\) on an unknown class Pdf\.#'
+ - '#Call to static method loadHTML\(\) on an unknown class PDF\.#'
+ - '#Call to static method loadHTML\(\) on an unknown class Pdf\.#'
diff --git a/src/PDF.php b/src/PDF.php
index 0378fee..c2c8027 100644
--- a/src/PDF.php
+++ b/src/PDF.php
@@ -11,6 +11,8 @@
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
+use Illuminate\Support\Str;
+use Symfony\Component\HttpFoundation\HeaderUtils;
/**
* A Laravel wrapper for Dompdf
@@ -210,9 +212,11 @@ public function save(string $filename, string $disk = null): self
public function download(string $filename = 'document.pdf'): Response
{
$output = $this->output();
+ $fallback = $this->fallbackName($filename);
+
return new Response($output, 200, [
'Content-Type' => 'application/pdf',
- 'Content-Disposition' => 'attachment; filename="' . $filename . '"',
+ 'Content-Disposition' => HeaderUtils::makeDisposition('attachment', $filename, $fallback),
'Content-Length' => strlen($output),
]);
}
@@ -223,9 +227,12 @@ public function download(string $filename = 'document.pdf'): Response
public function stream(string $filename = 'document.pdf'): Response
{
$output = $this->output();
+ $fallback = $this->fallbackName($filename);
+
+
return new Response($output, 200, [
'Content-Type' => 'application/pdf',
- 'Content-Disposition' => 'inline; filename="' . $filename . '"',
+ 'Content-Disposition' => HeaderUtils::makeDisposition('inline', $filename, $fallback),
]);
}
@@ -301,4 +308,12 @@ public function __call($method, $parameters)
throw new \UnexpectedValueException("Method [{$method}] does not exist on PDF instance.");
}
+
+ /**
+ * Make a safe fallback filename
+ */
+ protected function fallbackName(string $filename): string
+ {
+ return str_replace('%', '', Str::ascii($filename));
+ }
}
diff --git a/tests/PdfTest.php b/tests/PdfTest.php
index 27bbe13..5f87da3 100644
--- a/tests/PdfTest.php
+++ b/tests/PdfTest.php
@@ -10,62 +10,62 @@ class PdfTest extends TestCase
{
public function testAlias(): void
{
- $pdf = \Pdf::loadHtml('
Test
');
+ $pdf = \Pdf::loadHTML('Test
');
/** @var Response $response */
$response = $pdf->download('test.pdf');
$this->assertInstanceOf(Response::class, $response);
$this->assertNotEmpty($response->getContent());
$this->assertEquals('application/pdf', $response->headers->get('Content-Type'));
- $this->assertEquals('attachment; filename="test.pdf"', $response->headers->get('Content-Disposition'));
+ $this->assertEquals('attachment; filename=test.pdf', $response->headers->get('Content-Disposition'));
}
-
+
public function testAliasCaps(): void
{
- $pdf = \PDF::loadHtml('Test
');
+ $pdf = \PDF::loadHTML('Test
');
/** @var Response $response */
$response = $pdf->download('test.pdf');
$this->assertInstanceOf(Response::class, $response);
$this->assertNotEmpty($response->getContent());
$this->assertEquals('application/pdf', $response->headers->get('Content-Type'));
- $this->assertEquals('attachment; filename="test.pdf"', $response->headers->get('Content-Disposition'));
+ $this->assertEquals('attachment; filename=test.pdf', $response->headers->get('Content-Disposition'));
}
public function testFacade(): void
{
- $pdf = Facade\Pdf::loadHtml('Test
');
+ $pdf = Facade\Pdf::loadHTML('Test
');
/** @var Response $response */
$response = $pdf->download('test.pdf');
$this->assertInstanceOf(Response::class, $response);
$this->assertNotEmpty($response->getContent());
$this->assertEquals('application/pdf', $response->headers->get('Content-Type'));
- $this->assertEquals('attachment; filename="test.pdf"', $response->headers->get('Content-Disposition'));
+ $this->assertEquals('attachment; filename=test.pdf', $response->headers->get('Content-Disposition'));
}
public function testDownload(): void
{
- $pdf = Facade\Pdf::loadHtml('Test
');
+ $pdf = Facade\Pdf::loadHTML('Test
');
/** @var Response $response */
$response = $pdf->download('test.pdf');
$this->assertInstanceOf(Response::class, $response);
$this->assertNotEmpty($response->getContent());
$this->assertEquals('application/pdf', $response->headers->get('Content-Type'));
- $this->assertEquals('attachment; filename="test.pdf"', $response->headers->get('Content-Disposition'));
+ $this->assertEquals('attachment; filename=test.pdf', $response->headers->get('Content-Disposition'));
}
public function testStream(): void
{
- $pdf = Facade\Pdf::loadHtml('Test
');
+ $pdf = Facade\Pdf::loadHTML('Test
');
/** @var Response $response */
$response = $pdf->stream('test.pdf');
$this->assertInstanceOf(Response::class, $response);
$this->assertNotEmpty($response->getContent());
$this->assertEquals('application/pdf', $response->headers->get('Content-Type'));
- $this->assertEquals('inline; filename="test.pdf"', $response->headers->get('Content-Disposition'));
+ $this->assertEquals('inline; filename=test.pdf', $response->headers->get('Content-Disposition'));
}
public function testView(): void
@@ -77,7 +77,31 @@ public function testView(): void
$this->assertInstanceOf(Response::class, $response);
$this->assertNotEmpty($response->getContent());
$this->assertEquals('application/pdf', $response->headers->get('Content-Type'));
- $this->assertEquals('attachment; filename="test.pdf"', $response->headers->get('Content-Disposition'));
+ $this->assertEquals('attachment; filename=test.pdf', $response->headers->get('Content-Disposition'));
+ }
+
+ public function testQuoteFilename(): void
+ {
+ $pdf = Facade\Pdf::loadHTML('Test
');
+ /** @var Response $response */
+ $response = $pdf->download('Test file.pdf');
+
+ $this->assertInstanceOf(Response::class, $response);
+ $this->assertNotEmpty($response->getContent());
+ $this->assertEquals('application/pdf', $response->headers->get('Content-Type'));
+ $this->assertEquals('attachment; filename="Test file.pdf"', $response->headers->get('Content-Disposition'));
+ }
+
+ public function testFallbackFilename(): void
+ {
+ $pdf = Facade\Pdf::loadHTML('Test
');
+ /** @var Response $response */
+ $response = $pdf->download('Test%file.pdf');
+
+ $this->assertInstanceOf(Response::class, $response);
+ $this->assertNotEmpty($response->getContent());
+ $this->assertEquals('application/pdf', $response->headers->get('Content-Type'));
+ $this->assertEquals("attachment; filename=Testfile.pdf; filename*=utf-8''Test%25file.pdf", $response->headers->get('Content-Disposition'));
}
public function testSaveOnDisk(): void
@@ -130,8 +154,8 @@ public function testSave(): void
public function testMultipleInstances(): void
{
- $pdf1 = Facade\Pdf::loadHtml('Test
');
- $pdf2 = Facade\Pdf::loadHtml('Test
');
+ $pdf1 = Facade\Pdf::loadHTML('Test
');
+ $pdf2 = Facade\Pdf::loadHTML('Test
');
$pdf1->getDomPDF()->setBaseHost('host1');
$pdf2->getDomPDF()->setBaseHost('host2');