@@ -400,6 +400,136 @@ public function testCreateSkipsUnrecognizedShapeElementsGracefully(): void
400400 self ::assertStringContainsString ('1 0 0 rg ' , $ xObject ->stream );
401401 }
402402
403+ public function testCreateWithEmptySvgStringThrows (): void
404+ {
405+ $ factory = new SvgPdfXObjectFactory ();
406+
407+ $ this ->expectException (InvalidArgumentException::class);
408+ $ this ->expectExceptionMessage ('Unable to parse SVG source "/tmp/empty.svg". ' );
409+
410+ $ factory ->create ('' , '/tmp/empty.svg ' );
411+ }
412+
413+ public function testCreateWithNonSvgRootElementThrows (): void
414+ {
415+ $ factory = new SvgPdfXObjectFactory ();
416+
417+ $ this ->expectException (InvalidArgumentException::class);
418+ $ this ->expectExceptionMessage ('Unable to parse SVG source "/tmp/wrong-root.svg". ' );
419+
420+ $ factory ->create ('<?xml version="1.0"?><root></root> ' , '/tmp/wrong-root.svg ' );
421+ }
422+
423+ public function testCreateWithMissingDimensionsOrViewBoxThrows (): void
424+ {
425+ $ factory = new SvgPdfXObjectFactory ();
426+
427+ $ this ->expectException (InvalidArgumentException::class);
428+ $ this ->expectExceptionMessage (
429+ 'SVG source "/tmp/no-viewport.svg" must define either a valid viewBox or positive width/height. ' ,
430+ );
431+
432+ $ factory ->create (
433+ '<svg xmlns="http://www.w3.org/2000/svg"><path d="M0,0"/></svg> ' ,
434+ '/tmp/no-viewport.svg ' ,
435+ );
436+ }
437+
438+ public function testCreateWithZeroDimensionsThrows (): void
439+ {
440+ $ factory = new SvgPdfXObjectFactory ();
441+
442+ $ this ->expectException (InvalidArgumentException::class);
443+ $ this ->expectExceptionMessage (
444+ 'SVG source "/tmp/zero-dims.svg" must define a positive viewBox. ' ,
445+ );
446+
447+ $ factory ->create (
448+ '<svg viewBox="0 0 0 0" xmlns="http://www.w3.org/2000/svg"><path d="M0,0"/></svg> ' ,
449+ '/tmp/zero-dims.svg ' ,
450+ );
451+ }
452+
453+ public function testCreateWithViewBoxButNegativeHeightThrows (): void
454+ {
455+ $ factory = new SvgPdfXObjectFactory ();
456+
457+ $ this ->expectException (InvalidArgumentException::class);
458+ $ this ->expectExceptionMessage (
459+ 'SVG source "/tmp/negative-height.svg" must define a positive viewBox. ' ,
460+ );
461+
462+ $ factory ->create (
463+ '<svg viewBox="0 0 10 -5" xmlns="http://www.w3.org/2000/svg"><path d="M0,0"/></svg> ' ,
464+ '/tmp/negative-height.svg ' ,
465+ );
466+ }
467+
468+ public function testCreateWithLeadingSignInNumericDimension (): void
469+ {
470+ $ factory = new SvgPdfXObjectFactory ();
471+
472+ $ xObject = $ factory ->create (
473+ <<<'SVG'
474+ <svg width="+100" height="-50" viewBox="+0 +0 +100 +50" xmlns="http://www.w3.org/2000/svg">
475+ <rect x="0" y="0" width="100" height="50" fill="#000000"/>
476+ </svg>
477+ SVG,
478+ '/tmp/signed-dims.svg ' ,
479+ );
480+
481+ self ::assertSame ([0.0 , 0.0 , 100.0 , 50.0 ], $ xObject ->dictionary ['BBox ' ]);
482+ }
483+
484+ public function testCreateWithFractionalDimensions (): void
485+ {
486+ $ factory = new SvgPdfXObjectFactory ();
487+
488+ $ xObject = $ factory ->create (
489+ <<<'SVG'
490+ <svg width="12.5" height="7.25" xmlns="http://www.w3.org/2000/svg">
491+ <rect x="0" y="0" width="12.5" height="7.25" fill="#000000"/>
492+ </svg>
493+ SVG,
494+ '/tmp/fractional-dims.svg ' ,
495+ );
496+
497+ self ::assertSame ([0.0 , 0.0 , 12.5 , 7.25 ], $ xObject ->dictionary ['BBox ' ]);
498+ }
499+
500+ public function testCreateWithStrokeWidthFromStyleAttribute (): void
501+ {
502+ $ factory = new SvgPdfXObjectFactory ();
503+
504+ $ xObject = $ factory ->create (
505+ <<<'SVG'
506+ <svg width="10" height="10" xmlns="http://www.w3.org/2000/svg">
507+ <path d="M0,0 L10,10" stroke="#000000" style="stroke-width:2.5"/>
508+ </svg>
509+ SVG,
510+ '/tmp/style-stroke.svg ' ,
511+ );
512+
513+ self ::assertStringContainsString ('2.500000 w ' , $ xObject ->stream );
514+ }
515+
516+ public function testCreateWithNegativeStrokeWidthClamped (): void
517+ {
518+ $ factory = new SvgPdfXObjectFactory ();
519+
520+ $ xObject = $ factory ->create (
521+ <<<'SVG'
522+ <svg width="10" height="10" xmlns="http://www.w3.org/2000/svg">
523+ <path d="M0,0 L10,10" stroke="#000000" stroke-width="-5"/>
524+ </svg>
525+ SVG,
526+ '/tmp/negative-stroke.svg ' ,
527+ );
528+
529+ // Negative stroke width should be clamped to 0.0
530+ self ::assertStringContainsString ('0.000000 w ' , $ xObject ->stream );
531+ }
532+
403533 public static function providePaintModeScenarios (): iterable
404534 {
405535 yield 'stroke only path without fill ' => [
0 commit comments