Skip to content

Commit 78c5cba

Browse files
committed
Feat: SVG Image Type Support + CI
1 parent 6ca8c9f commit 78c5cba

File tree

10 files changed

+355
-4
lines changed

10 files changed

+355
-4
lines changed

samples/Sample_47_SVG.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
use PhpOffice\PhpWord\Element\Section;
4+
use PhpOffice\PhpWord\PhpWord;
5+
6+
include_once 'Sample_Header.php';
7+
8+
// New Word document
9+
echo date('H:i:s'), ' Create new PhpWord object', EOL;
10+
$phpWord = new PhpWord();
11+
12+
$section = $phpWord->addSection();
13+
$section->addText('SVG image without any styles:');
14+
$svg = $section->addImage(__DIR__ . '/resources/sample.svg');
15+
16+
printSeparator($section);
17+
18+
$section->addText('SVG image with styles:');
19+
$svg = $section->addImage(
20+
__DIR__ . '/resources/sample.svg',
21+
[
22+
'width' => 200,
23+
'height' => 200,
24+
'align' => 'center',
25+
'wrappingStyle' => PhpOffice\PhpWord\Style\Image::WRAPPING_STYLE_BEHIND,
26+
]
27+
);
28+
29+
function printSeparator(Section $section): void
30+
{
31+
$section->addTextBreak();
32+
$lineStyle = ['weight' => 0.2, 'width' => 150, 'height' => 0, 'align' => 'center'];
33+
$section->addLine($lineStyle);
34+
$section->addTextBreak(2);
35+
}
36+
37+
// Save file
38+
echo write($phpWord, basename(__FILE__, '.php'), $writers);
39+
if (!CLI) {
40+
include_once 'Sample_Footer.php';
41+
}

samples/resources/sample.svg

Lines changed: 96 additions & 0 deletions
Loading

src/PhpWord/Element/Image.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
namespace PhpOffice\PhpWord\Element;
2020

21+
use DOMDocument;
2122
use PhpOffice\PhpWord\Exception\CreateTemporaryFileException;
2223
use PhpOffice\PhpWord\Exception\InvalidImageException;
2324
use PhpOffice\PhpWord\Exception\UnsupportedImageTypeException;
@@ -432,6 +433,20 @@ private function checkImage(): void
432433
{
433434
$this->setSourceType();
434435

436+
$ext = strtolower(pathinfo($this->source, PATHINFO_EXTENSION));
437+
if ($ext === 'svg') {
438+
[$actualWidth, $actualHeight] = $this->getSvgDimensions($this->source);
439+
$this->imageType = 'image/svg+xml';
440+
$this->imageExtension = 'svg';
441+
$this->imageFunc = null;
442+
$this->imageQuality = null;
443+
$this->memoryImage = false;
444+
$this->sourceType = self::SOURCE_LOCAL;
445+
$this->setProportionalSize($actualWidth, $actualHeight);
446+
447+
return;
448+
}
449+
435450
// Check image data
436451
if ($this->sourceType == self::SOURCE_ARCHIVE) {
437452
$imageData = $this->getArchiveImageSize($this->source);
@@ -598,4 +613,38 @@ private function setProportionalSize($actualWidth, $actualHeight): void
598613
}
599614
}
600615
}
616+
617+
public function getSvgDimensions(string $file): array
618+
{
619+
$xml = @file_get_contents($file);
620+
if ($xml === false) {
621+
throw new InvalidImageException("Impossible de lire le fichier SVG: $file");
622+
}
623+
libxml_use_internal_errors(true);
624+
$dom = new DOMDocument();
625+
if (!$dom->loadXML($xml)) {
626+
throw new InvalidImageException('SVG invalide ou mal formé');
627+
}
628+
$svg = $dom->documentElement;
629+
630+
$wAttr = round((float) $svg->getAttribute('width'));
631+
$hAttr = round((float) $svg->getAttribute('height'));
632+
633+
$w = (int) filter_var($wAttr, FILTER_SANITIZE_NUMBER_INT);
634+
$h = (int) filter_var($hAttr, FILTER_SANITIZE_NUMBER_INT);
635+
636+
if ($w <= 0 || $h <= 0) {
637+
$vb = $svg->getAttribute('viewBox');
638+
if (preg_match('/^\s*[\d.+-]+[\s,]+[\d.+-]+[\s,]+([\d.+-]+)[\s,]+([\d.+-]+)\s*$/', $vb, $m)) {
639+
$w = (int) round((float) $m[1]);
640+
$h = (int) round((float) $m[2]);
641+
}
642+
}
643+
644+
if ($w <= 0 || $h <= 0) {
645+
throw new InvalidImageException('Impossible de déterminer width/height ou viewBox valides pour le SVG');
646+
}
647+
648+
return [$w, $h];
649+
}
601650
}

src/PhpWord/Shared/Microsoft/PasswordEncoder.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
namespace PhpOffice\PhpWord\Shared\Microsoft;
2020

21+
use InvalidArgumentException;
22+
2123
/**
2224
* Password encoder for microsoft office applications.
2325
*/
@@ -119,6 +121,11 @@ public static function hashPassword($password, $algorithmName = self::ALGORITHM_
119121
// Get the single-byte values by iterating through the Unicode characters of the truncated password.
120122
// For each character, if the low byte is not equal to 0, take it. Otherwise, take the high byte.
121123
$passUtf8 = mb_convert_encoding($password, 'UCS-2LE', 'UTF-8');
124+
125+
if (!is_string($passUtf8)) {
126+
throw new InvalidArgumentException('mb_convert_encoding() failed to convert password to UCS-2LE.');
127+
}
128+
122129
$byteChars = [];
123130

124131
for ($i = 0; $i < mb_strlen($password); ++$i) {

src/PhpWord/Shared/Text.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ public static function toUTF8($value = '')
148148
if (null !== $value && !self::isUTF8($value)) {
149149
// PHP8.2 : utf8_encode is deprecated, but mb_convert_encoding always usable
150150
$value = (function_exists('mb_convert_encoding')) ? mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1') : utf8_encode($value);
151+
if (is_bool($value)) {
152+
$value = null;
153+
}
151154
}
152155

153156
return $value;

src/PhpWord/Style/Chart.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,8 +457,7 @@ public function getValueLabelPosition()
457457
* "low" - sets labels are below the graph
458458
* "high" - sets labels above the graph.
459459
*
460-
* @param string
461-
* @param mixed $labelPosition
460+
* @param string $labelPosition
462461
*/
463462
public function setValueLabelPosition($labelPosition)
464463
{

0 commit comments

Comments
 (0)