diff --git a/docs/configuration.rst b/docs/configuration.rst index 6eb8b951..7341124d 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -28,8 +28,11 @@ The default configuration for the bundle looks like : wait_delay: null # None wait_for_expression: null # None emulated_media_type: null # 'print' + cookies: null # None extra_http_headers: null # None + fail_on_http_status_codes: null # [499-599] fail_on_console_exceptions: null # false + skip_network_idle_event: null # false pdf_format: null # None pdf_universal_access: null # false url: @@ -48,8 +51,11 @@ The default configuration for the bundle looks like : wait_delay: null # None wait_for_expression: null # None emulated_media_type: null # 'print' + cookies: null # None extra_http_headers: null # None + fail_on_http_status_codes: null # [499-599] fail_on_console_exceptions: null # false + skip_network_idle_event: null # false pdf_format: null # None pdf_universal_access: null # false markdown: @@ -68,8 +74,11 @@ The default configuration for the bundle looks like : wait_delay: null # None wait_for_expression: null # None emulated_media_type: null # 'print' + cookies: null # None extra_http_headers: null # None + fail_on_http_status_codes: null # [499-599] fail_on_console_exceptions: null # false + skip_network_idle_event: null # false pdf_format: null # None pdf_universal_access: null # false office: @@ -100,9 +109,46 @@ HTTP headers to send by Chromium while loading the HTML document. sensiolabs_gotenberg: base_uri: 'http://localhost:3000' - options: - extra_http_headers: - - { name: 'My-Header', value: 'MyValue' } + default_options: + html: + extra_http_headers: + - { name: 'My-Header', value: 'MyValue' } + +.. tip:: + + For more information about `custom HTTP headers`_. + +Invalid HTTP Status Codes +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To return a 409 Conflict response if the HTTP status code from the main page is not acceptable. + +.. code-block:: yaml + + sensiolabs_gotenberg: + base_uri: 'http://localhost:3000' + default_options: + html: + fail_on_http_status_codes: [401, 403] + +.. tip:: + + For more information about `Invalid HTTP Status Codes`_. + +Cookies +~~~~~~~ + +Cookies to store in the Chromium cookie jar. + +.. code-block:: yaml + + sensiolabs_gotenberg: + base_uri: 'http://localhost:3000' + default_options: + html: + cookies: + - { name: 'yummy_cookie', value: 'choco', domain: 'example.com' } + - { name: 'my_cookie', value: 'symfony', domain: 'symfony.com', secure: true, httpOnly: true, sameSite: 'Lax' } .. tip:: @@ -110,3 +156,4 @@ HTTP headers to send by Chromium while loading the HTML document. .. _defaults properties: https://gotenberg.dev/docs/routes#page-properties-chromium .. _custom HTTP headers: https://gotenberg.dev/docs/routes#custom-http-headers +.. _Invalid HTTP Status Codes: https://gotenberg.dev/docs/routes#invalid-http-status-codes-chromium diff --git a/docs/customization.rst b/docs/customization.rst index 7ecee9d0..703e4ddf 100644 --- a/docs/customization.rst +++ b/docs/customization.rst @@ -304,6 +304,54 @@ Some websites have dedicated CSS rules for print. Using ``screen`` allows you to For more information about `emulated Media Type`_. +Cookies +------- + +``default: None`` + +Cookies to store in the Chromium cookie jar. + +.. code-block:: php + + $twigPdfBuilder + ->content('path/to/template.html.twig') + ->cookies([ + [ + 'name' => 'my_cookie', + 'value' => 'symfony', + 'domain' => 'symfony.com', + 'secure' => true, + 'httpOnly' => true, + 'sameSite' => 'Lax', + ], + ]); + +.. warning:: + + `cookies` method overrides any previous cookies. + +If you want to add cookies from the ones already loaded in the configuration you +can use `addCookie`. + +.. code-block:: php + + $twigPdfBuilder + ->content('path/to/template.html.twig') + ->addCookies([ + [ + 'name' => 'my_cookie', + 'value' => 'symfony', + 'domain' => 'symfony.com', + 'secure' => true, + 'httpOnly' => true, + 'sameSite' => 'Lax', + ], + ]); + +.. tip:: + + For more information about `cookies`_. + Extra HTTP headers ------------------ @@ -323,6 +371,23 @@ HTTP headers to send by Chromium while loading the HTML document. For more information about `custom HTTP headers`_. +Invalid HTTP Status Codes +------------------------- + +``default: [499,599]`` + +To return a 409 Conflict response if the HTTP status code from the main page is not acceptable.. + +.. code-block:: php + + $twigPdfBuilder + ->content('path/to/template.html.twig') + ->failOnHttpStatusCodes([401, 403]); + +.. tip:: + + For more information about `invalid HTTP Status Codes`_. + Console Exceptions ------------------ @@ -340,6 +405,25 @@ Return a 409 Conflict response if there are exceptions in the Chromium console. For more information about `console Exceptions`_. +Performance Mode +---------------- + +``default: false`` + +Gotenberg, by default, waits for the network idle event to ensure that the majority of the page is rendered during conversion. +However, this often significantly slows down the conversion process. +Setting this form field to true can greatly enhance the conversion speed. + +.. code-block:: php + + $twigPdfBuilder + ->content('path/to/template.html.twig') + ->skipNetworkIdleEvent(); + +.. tip:: + + For more information about `performance mode`_. + PDF Format ---------- @@ -381,6 +465,9 @@ Enable PDF for Universal Access for optimal accessibility. .. _delay: https://gotenberg.dev/docs/routes#wait-before-rendering .. _wait for expression: https://gotenberg.dev/docs/routes#wait-before-rendering .. _emulated Media Type: https://gotenberg.dev/docs/routes#emulated-media-type +.. _cookies: https://gotenberg.dev/docs/routes#cookies-chromium .. _custom HTTP headers: https://gotenberg.dev/docs/routes#custom-http-headers +.. _invalid HTTP Status Codes: https://gotenberg.dev/docs/routes#invalid-http-status-codes-chromium .. _console Exceptions: https://gotenberg.dev/docs/routes#console-exceptions +.. _performance mode: https://gotenberg.dev/docs/routes#performance-mode-chromium .. _pdf formats: https://gotenberg.dev/docs/routes#pdfa-chromium diff --git a/src/Builder/AbstractChromiumPdfBuilder.php b/src/Builder/AbstractChromiumPdfBuilder.php index b806d86a..8a32150b 100644 --- a/src/Builder/AbstractChromiumPdfBuilder.php +++ b/src/Builder/AbstractChromiumPdfBuilder.php @@ -8,6 +8,7 @@ use Sensiolabs\GotenbergBundle\Exception\InvalidBuilderConfiguration; use Sensiolabs\GotenbergBundle\Exception\PdfPartRenderingException; use Sensiolabs\GotenbergBundle\Formatter\AssetBaseDirFormatter; +use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\Mime\Part\DataPart; use Symfony\Component\Mime\Part\File as DataPartFile; use Twig\Environment; @@ -307,6 +308,51 @@ public function emulatedMediaType(string $mediaType): static return $this; } + /** + * Cookies to store in the Chromium cookie jar. (overrides any previous cookies). + * + * @see https://gotenberg.dev/docs/routes#cookies-chromium + * + * @param list $cookies + */ + public function cookies(array $cookies): static + { + $this->formFields['cookies'] = []; + + foreach ($cookies as $cookie) { + $this->setCookie($cookie['name'], $cookie); + } + + return $this; + } + + /** + * @param array{name: string, value: string, domain: string, path?: string|null, secure?: bool|null, httpOnly?: bool|null, sameSite?: 'Strict'|'Lax'|null} $cookie + */ + public function setCookie(string $key, array $cookie): static + { + $this->formFields['cookies'] ??= []; + $this->formFields['cookies'][$key] = $cookie; + + return $this; + } + + /** + * Add cookies to store in the Chromium cookie jar. + * + * @see https://gotenberg.dev/docs/routes#cookies-chromium + * + * @param list $cookies + */ + public function addCookies(array $cookies): static + { + foreach ($cookies as $cookie) { + $this->setCookie($cookie['name'], $cookie); + } + + return $this; + } + /** * Sets extra HTTP headers that Chromium will send when loading the HTML * document. (default None). (overrides any previous headers). @@ -337,6 +383,21 @@ public function addExtraHttpHeaders(array $headers): static return $this; } + /** + * Return a 409 Conflict response if the HTTP status code from + * the main page is not acceptable. (default [499,599]). (overrides any previous configuration). + * + * @see https://gotenberg.dev/docs/routes#invalid-http-status-codes-chromium + * + * @param array $statusCodes + */ + public function failOnHttpStatusCodes(array $statusCodes): static + { + $this->formFields['failOnHttpStatusCodes'] = $statusCodes; + + return $this; + } + /** * Forces Gotenberg to return a 409 Conflict response if there are * exceptions in the Chromium console. (default false). @@ -350,6 +411,13 @@ public function failOnConsoleExceptions(bool $bool = true): static return $this; } + public function skipNetworkIdleEvent(bool $bool = true): static + { + $this->formFields['skipNetworkIdleEvent'] = $bool; + + return $this; + } + /** * Sets the PDF format of the resulting PDF. (default None). * @@ -429,8 +497,11 @@ private function addConfiguration(string $configurationName, mixed $value): void 'wait_delay' => $this->waitDelay($value), 'wait_for_expression' => $this->waitForExpression($value), 'emulated_media_type' => $this->emulatedMediaType($value), + 'cookies' => $this->cookies($value), 'extra_http_headers' => $this->extraHttpHeaders($value), + 'fail_on_http_status_codes' => $this->failOnHttpStatusCodes($value), 'fail_on_console_exceptions' => $this->failOnConsoleExceptions($value), + 'skip_network_idle_event' => $this->skipNetworkIdleEvent($value), default => throw new InvalidBuilderConfiguration(sprintf('Invalid option "%s": no method does not exist in class "%s" to configured it.', $configurationName, static::class)), }; } diff --git a/src/Builder/AbstractPdfBuilder.php b/src/Builder/AbstractPdfBuilder.php index c2408aac..4507237e 100644 --- a/src/Builder/AbstractPdfBuilder.php +++ b/src/Builder/AbstractPdfBuilder.php @@ -5,7 +5,7 @@ use Sensiolabs\GotenbergBundle\Client\GotenbergClientInterface; use Sensiolabs\GotenbergBundle\Client\PdfResponse; use Sensiolabs\GotenbergBundle\Enum\PdfPart; -use Sensiolabs\GotenbergBundle\Exception\ExtraHttpHeadersJsonEncodingException; +use Sensiolabs\GotenbergBundle\Exception\JsonEncodingException; use Sensiolabs\GotenbergBundle\Formatter\AssetBaseDirFormatter; use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\HttpFoundation\HeaderUtils; @@ -32,14 +32,8 @@ public function __construct( protected readonly AssetBaseDirFormatter $asset, ) { $this->normalizers = [ - 'extraHttpHeaders' => static function (mixed $value): array { - try { - $extraHttpHeaders = json_encode($value, \JSON_THROW_ON_ERROR); - } catch (\JsonException $exception) { - throw new ExtraHttpHeadersJsonEncodingException('Could not encode extra HTTP headers into JSON', previous: $exception); - } - - return ['extraHttpHeaders' => $extraHttpHeaders]; + 'extraHttpHeaders' => function (mixed $value): array { + return $this->encodeData('extraHttpHeaders', $value); }, 'assets' => static function (array $value): array { return ['files' => $value]; @@ -53,9 +47,29 @@ public function __construct( PdfPart::FooterPart->value => static function (DataPart $value): array { return ['files' => $value]; }, + 'failOnHttpStatusCodes' => function (mixed $value): array { + return $this->encodeData('failOnHttpStatusCodes', $value); + }, + 'cookies' => function (mixed $value): array { + return $this->encodeData('cookies', array_values($value)); + }, ]; } + /** + * @return array + */ + private function encodeData(string $key, mixed $value): array + { + try { + $encodedValue = json_encode($value, \JSON_THROW_ON_ERROR); + } catch (\JsonException $exception) { + throw new JsonEncodingException(sprintf('Could not encode property "%s" into JSON', $key), previous: $exception); + } + + return [$key => $encodedValue]; + } + /** * The Gotenberg API endpoint path. */ diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 6b12525b..6e5cde3e 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -159,12 +159,37 @@ private function addChromiumOptionsNode(NodeBuilder $treeBuilder): void ->values([EmulatedMediaType::Screen->value, EmulatedMediaType::Print->value]) ->defaultNull() ->end() + ->arrayNode('cookies') + ->info('Cookies to store in the Chromium cookie jar - default None. https://gotenberg.dev/docs/routes#cookies-chromium') + ->defaultValue([]) + ->arrayPrototype() + ->children() + ->scalarNode('name')->end() + ->scalarNode('value')->end() + ->scalarNode('domain')->end() + ->scalarNode('path') + ->defaultNull() + ->end() + ->booleanNode('secure') + ->defaultNull() + ->end() + ->booleanNode('httpOnly') + ->defaultNull() + ->end() + ->enumNode('sameSite') + ->info('Accepted values are "Strict", "Lax" or "None". https://gotenberg.dev/docs/routes#cookies-chromium') + ->values(['Strict', 'Lax', 'None']) + ->defaultNull() + ->end() + ->end() + ->end() + ->end() ->arrayNode('extra_http_headers') ->info('HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers') ->defaultValue([]) ->useAttributeAsKey('name') - ->arrayPrototype() - ->children() + ->arrayPrototype() + ->children() ->scalarNode('name') ->validate() ->ifTrue(static function ($option) { @@ -184,10 +209,20 @@ private function addChromiumOptionsNode(NodeBuilder $treeBuilder): void ->end() ->end() ->end() + ->arrayNode('fail_on_http_status_codes') + ->info('Return a 409 Conflict response if the HTTP status code from the main page is not acceptable. - default [499,599]. https://gotenberg.dev/docs/routes#invalid-http-status-codes-chromium') + ->defaultValue([]) + ->integerPrototype() + ->end() + ->end() ->booleanNode('fail_on_console_exceptions') ->info('Return a 409 Conflict response if there are exceptions in the Chromium console - default false. https://gotenberg.dev/docs/routes#console-exceptions') ->defaultNull() ->end() + ->booleanNode('skip_network_idle_event') + ->info('Do not wait for Chromium network to be idle. - default false. https://gotenberg.dev/docs/routes#performance-mode-chromium') + ->defaultNull() + ->end() ->enumNode('pdf_format') ->info('Convert the resulting PDF into the given PDF/A format - default None. https://gotenberg.dev/docs/routes#pdfa-chromium') ->values([PdfFormat::Pdf1b->value, PdfFormat::Pdf2b->value, PdfFormat::Pdf3b->value]) diff --git a/src/Exception/ExtraHttpHeadersJsonEncodingException.php b/src/Exception/ExtraHttpHeadersJsonEncodingException.php deleted file mode 100644 index 91cef42e..00000000 --- a/src/Exception/ExtraHttpHeadersJsonEncodingException.php +++ /dev/null @@ -1,7 +0,0 @@ -getMultipartFormData(); - self::assertCount(20, $multipartFormData); + self::assertCount(23, $multipartFormData); self::assertIsArray($multipartFormData[0]); self::assertCount(1, $multipartFormData[0]); @@ -52,10 +52,13 @@ public function testWithConfigurations(): void self::assertSame(['waitDelay' => '10s'], $multipartFormData[13]); self::assertSame(['waitForExpression' => 'window.globalVar === "ready"'], $multipartFormData[14]); self::assertSame(['emulatedMediaType' => 'screen'], $multipartFormData[15]); - self::assertSame(['extraHttpHeaders' => '{"MyHeader":"Value","User-Agent":"MyValue"}'], $multipartFormData[16]); - self::assertSame(['failOnConsoleExceptions' => 'true'], $multipartFormData[17]); - self::assertSame(['pdfa' => 'PDF/A-1b'], $multipartFormData[18]); - self::assertSame(['pdfua' => 'true'], $multipartFormData[19]); + self::assertSame(['cookies' => '[{"name":"cook_me","value":"sensio","domain":"sensiolabs.com","secure":true,"httpOnly":true,"sameSite":"Lax"},{"name":"yummy_cookie","value":"choco","domain":"example.com"}]'], $multipartFormData[16]); + self::assertSame(['extraHttpHeaders' => '{"MyHeader":"Value","User-Agent":"MyValue"}'], $multipartFormData[17]); + self::assertSame(['failOnHttpStatusCodes' => '[401,403]'], $multipartFormData[18]); + self::assertSame(['failOnConsoleExceptions' => 'true'], $multipartFormData[19]); + self::assertSame(['skipNetworkIdleEvent' => 'true'], $multipartFormData[20]); + self::assertSame(['pdfa' => 'PDF/A-1b'], $multipartFormData[21]); + self::assertSame(['pdfua' => 'true'], $multipartFormData[22]); } public function testWithTemplate(): void @@ -151,8 +154,8 @@ public function testInvalidTwigTemplate(): void public function testInvalidExtraHttpHeaders(): void { - $this->expectException(ExtraHttpHeadersJsonEncodingException::class); - $this->expectExceptionMessage('Could not encode extra HTTP headers into JSON'); + $this->expectException(JsonEncodingException::class); + $this->expectExceptionMessage('Could not encode property "extraHttpHeaders" into JSON'); $client = $this->createMock(GotenbergClientInterface::class); $assetBaseDirFormatter = new AssetBaseDirFormatter(new Filesystem(), self::FIXTURE_DIR, self::FIXTURE_DIR); @@ -188,11 +191,28 @@ private static function getUserConfig(): array 'wait_delay' => '10s', 'wait_for_expression' => 'window.globalVar === "ready"', 'emulated_media_type' => 'screen', + 'cookies' => [ + [ + 'name' => 'cook_me', + 'value' => 'sensio', + 'domain' => 'sensiolabs.com', + 'secure' => true, + 'httpOnly' => true, + 'sameSite' => 'Lax', + ], + [ + 'name' => 'yummy_cookie', + 'value' => 'choco', + 'domain' => 'example.com', + ], + ], 'extra_http_headers' => [ 'MyHeader' => 'Value', 'User-Agent' => 'MyValue', ], + 'fail_on_http_status_codes' => [401, 403], 'fail_on_console_exceptions' => true, + 'skip_network_idle_event' => true, 'pdf_format' => 'PDF/A-1b', 'pdf_universal_access' => true, ]; diff --git a/tests/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php index bc413a64..e83cd1f5 100644 --- a/tests/DependencyInjection/ConfigurationTest.php +++ b/tests/DependencyInjection/ConfigurationTest.php @@ -39,6 +39,9 @@ public static function provideValidHtmlConfiguration(): iterable yield 'pdf format configuration' => [['default_options' => ['html' => ['pdf_format' => 'PDF/A-3b']]]]; yield 'pdf universal configuration' => [['default_options' => ['html' => ['pdf_universal_access' => true]]]]; yield 'both pdf configuration' => [['default_options' => ['html' => ['pdf_format' => 'PDF/A-3b', 'pdf_universal_access' => true]]]]; + yield 'Update accepted status codes from the main page' => [['default_options' => ['html' => ['fail_on_http_status_codes' => [401, 403]]]]]; + yield 'waits for the network idle' => [['default_options' => ['html' => ['skip_network_idle_event' => true]]]]; + yield 'add cookies to store' => [['default_options' => ['html' => ['cookies' => [['name' => 'my_cookie', 'value' => 'symfony', 'domain' => 'symfony.com', 'path' => null, 'secure' => true, 'httpOnly' => true, 'sameSite' => 'Lax']]]]]]; } public function testDefaultConfig(): void @@ -133,8 +136,11 @@ private static function getBundleDefaultConfig(): array 'wait_delay' => null, 'wait_for_expression' => null, 'emulated_media_type' => null, + 'cookies' => [], 'extra_http_headers' => [], + 'fail_on_http_status_codes' => [], 'fail_on_console_exceptions' => null, + 'skip_network_idle_event' => null, 'pdf_format' => null, 'pdf_universal_access' => null, ], @@ -154,8 +160,11 @@ private static function getBundleDefaultConfig(): array 'wait_delay' => null, 'wait_for_expression' => null, 'emulated_media_type' => null, + 'cookies' => [], 'extra_http_headers' => [], + 'fail_on_http_status_codes' => [], 'fail_on_console_exceptions' => null, + 'skip_network_idle_event' => null, 'pdf_format' => null, 'pdf_universal_access' => null, ], @@ -175,8 +184,11 @@ private static function getBundleDefaultConfig(): array 'wait_delay' => null, 'wait_for_expression' => null, 'emulated_media_type' => null, + 'cookies' => [], 'extra_http_headers' => [], + 'fail_on_http_status_codes' => [], 'fail_on_console_exceptions' => null, + 'skip_network_idle_event' => null, 'pdf_format' => null, 'pdf_universal_access' => null, ], diff --git a/tests/DependencyInjection/SensiolabsGotenbergExtensionTest.php b/tests/DependencyInjection/SensiolabsGotenbergExtensionTest.php index 5019a593..3d9e60e2 100644 --- a/tests/DependencyInjection/SensiolabsGotenbergExtensionTest.php +++ b/tests/DependencyInjection/SensiolabsGotenbergExtensionTest.php @@ -37,8 +37,19 @@ public function testGotenbergConfiguredWithValidConfig(): void 'wait_delay' => '10s', 'wait_for_expression' => 'window.globalVar === "ready"', 'emulated_media_type' => 'screen', + 'cookies' => [[ + 'name' => 'cook_me', + 'value' => 'sensio', + 'domain' => 'sensiolabs.com', + 'secure' => true, + 'httpOnly' => true, + 'sameSite' => 'Lax', + 'path' => null, + ]], 'extra_http_headers' => ['MyHeader' => 'MyValue', 'User-Agent' => 'MyValue'], + 'fail_on_http_status_codes' => [401], 'fail_on_console_exceptions' => true, + 'skip_network_idle_event' => true, 'pdf_format' => 'PDF/A-1b', 'pdf_universal_access' => true, ], @@ -59,7 +70,9 @@ public function testGotenbergConfiguredWithValidConfig(): void 'wait_for_expression' => 'window.globalVar === "ready"', 'emulated_media_type' => 'screen', 'extra_http_headers' => ['MyHeader' => 'MyValue', 'User-Agent' => 'MyValue'], + 'fail_on_http_status_codes' => [401, 403], 'fail_on_console_exceptions' => false, + 'skip_network_idle_event' => false, 'pdf_format' => PdfFormat::Pdf2b->value, 'pdf_universal_access' => false, ], @@ -80,7 +93,9 @@ public function testGotenbergConfiguredWithValidConfig(): void 'wait_for_expression' => 'window.globalVar === "ready"', 'emulated_media_type' => 'screen', 'extra_http_headers' => ['MyHeader' => 'MyValue', 'User-Agent' => 'MyValue'], + 'fail_on_http_status_codes' => [404], 'fail_on_console_exceptions' => false, + 'skip_network_idle_event' => true, 'pdf_format' => PdfFormat::Pdf3b->value, 'pdf_universal_access' => true, ], @@ -165,8 +180,18 @@ private static function getValidConfig(): array 'wait_delay' => '10s', 'wait_for_expression' => 'window.globalVar === "ready"', 'emulated_media_type' => 'screen', + 'cookies' => [[ + 'name' => 'cook_me', + 'value' => 'sensio', + 'domain' => 'sensiolabs.com', + 'secure' => true, + 'httpOnly' => true, + 'sameSite' => 'Lax', + ]], 'extra_http_headers' => [['name' => 'MyHeader', 'value' => 'MyValue'], ['name' => 'User-Agent', 'value' => 'MyValue']], + 'fail_on_http_status_codes' => [401], 'fail_on_console_exceptions' => true, + 'skip_network_idle_event' => true, 'pdf_format' => PdfFormat::Pdf1b->value, 'pdf_universal_access' => true, ], @@ -187,7 +212,9 @@ private static function getValidConfig(): array 'wait_for_expression' => 'window.globalVar === "ready"', 'emulated_media_type' => 'screen', 'extra_http_headers' => [['name' => 'MyHeader', 'value' => 'MyValue'], ['name' => 'User-Agent', 'value' => 'MyValue']], + 'fail_on_http_status_codes' => [401, 403], 'fail_on_console_exceptions' => false, + 'skip_network_idle_event' => false, 'pdf_format' => PdfFormat::Pdf2b->value, 'pdf_universal_access' => false, ], @@ -208,7 +235,9 @@ private static function getValidConfig(): array 'wait_for_expression' => 'window.globalVar === "ready"', 'emulated_media_type' => 'screen', 'extra_http_headers' => [['name' => 'MyHeader', 'value' => 'MyValue'], ['name' => 'User-Agent', 'value' => 'MyValue']], + 'fail_on_http_status_codes' => [404], 'fail_on_console_exceptions' => false, + 'skip_network_idle_event' => true, 'pdf_format' => PdfFormat::Pdf3b->value, 'pdf_universal_access' => true, ],