qui demarre le fichier et la parser pour renvoyer un tableau de ses attributs * @param string $fichier * @return array|bool */ function svg_lire_balise_svg($fichier) { if (!$debut_fichier = svg_charger($fichier, 4096)) { return false; } if (($ps = stripos($debut_fichier, "", $ps); $balise_svg = substr($debut_fichier, $ps, $pe - $ps +1); if (preg_match_all(",([\w:\-]+)=,Uims", $balise_svg, $matches)) { if (!function_exists('extraire_attribut')) { include_spip('inc/filtres'); } $attributs = []; foreach ($matches[1] as $att) { $attributs[$att] = extraire_attribut($balise_svg, $att); } return [$balise_svg, $attributs]; } } return false; } /** * Attributs de la balise SVG * @param string $img * @return array|bool */ function svg_lire_attributs($img) { if ($svg_infos = svg_lire_balise_svg($img)) { list($balise_svg, $attributs) = $svg_infos; return $attributs; } return false; } /** * Convertir l'attribut widht/height d'un SVG en pixels * (approximatif eventuellement, du moment qu'on respecte le ratio) * @param $dimension * @return bool|float|int */ function svg_dimension_to_pixels($dimension, $precision = 2) { if (preg_match(',^(-?\d+(\.\d+)?)([^\d]*),i', trim($dimension), $m)){ switch (strtolower($m[2])) { case '%': // on ne sait pas faire :( return false; break; case 'em': return round($m[1]*16, $precision); // 16px font-size par defaut break; case 'ex': return round($m[1]*16, $precision); // 16px font-size par defaut break; case 'pc': return round($m[1]*16, $precision); // 1/6 inch = 96px/6 in CSS break; case 'cm': return round($m[1]*96/2.54, $precision); // 96px / 2.54cm; break; case 'mm': return round($m[1]*96/25.4, $precision); // 96px / 25.4mm; break; case 'in': return round($m[1]*96, $precision); // 1 inch = 96px in CSS break; case 'px': case 'pt': default: return $m[1]; break; } } return false; } /** * Modifier la balise SVG en entete du source * @param string $svg * @param string $old_balise_svg * @param array $attributs * @return string */ function svg_change_balise_svg($svg, $old_balise_svg, $attributs) { $new_balise_svg = "$v) { $new_balise_svg .= " $k=\"".entites_html($v)."\""; } $new_balise_svg .= ">"; $p = strpos($svg, $old_balise_svg); $svg = substr_replace($svg, $new_balise_svg, $p, strlen($old_balise_svg)); return $svg; } /** * @param string $svg * @param string $shapes * @param bool|string $start * inserer au debut (true) ou a la fin (false) * @return string */ function svg_insert_shapes($svg, $shapes, $start=true) { if ($start === false or $start === 'end') { $svg = str_replace("", $shapes . "", $svg); } else { $p = stripos($svg, "", $p); $svg = substr_replace($svg, $shapes, $p+1, 0); } return $svg; } /** * Clipper le SVG dans une box * @param string $svg * @param int $x * @param int $y * @param int $width * @param int $height * @return string */ function svg_clip_in_box($svg, $x, $y, $width, $height) { $rect = ""; $id = 'clip-'.substr(md5($rect . strlen($svg)),0,8); $clippath = "$rect"; $g = ""; $svg = svg_insert_shapes($svg, $clippath . $g); $svg = svg_insert_shapes($svg, "", false); return $svg; } /** * Redimensionner le SVG via le width/height de la balise * @param string $img * @param $new_width * @param $new_height * @return bool|string */ function svg_redimensionner($img, $new_width, $new_height) { if ($svg = svg_charger($img) and $svg_infos = svg_lire_balise_svg($svg)) { list($balise_svg, $attributs) = $svg_infos; if (!isset($attributs['viewBox'])) { $attributs['viewBox'] = "0 0 " . $attributs['width'] . " " . $attributs['height']; } $attributs['width'] = strval($new_width); $attributs['height'] = strval($new_height); $svg = svg_change_balise_svg($svg, $balise_svg, $attributs); return $svg; } return $img; } /** * Transformer une couleur extraite du SVG en hexa * @param string $couleur * @return string */ function svg_couleur_to_hexa($couleur) { if (strpos($couleur, "rgb(")===0) { $c = explode(',', substr($couleur, 4)); $couleur = _couleur_dec_to_hex(intval($c[0]), intval($c[1]), intval($c[2])); } else { $couleur = couleur_html_to_hex($couleur); } $couleur = '#'.ltrim($couleur,'#'); return $couleur; } /** * Transformer une couleur extraite du SVG en rgb * @param string $couleur * @return array */ function svg_couleur_to_rgb($couleur) { if (strpos($couleur, "rgb(")===0) { $c = explode(',', substr($couleur, 4)); return ['red' => intval($c[0]),'green' => intval($c[1]),'blue' => intval($c[2])]; } return _couleur_hex_to_dec($couleur); } /** * Calculer les dimensions width/heigt/viewBox du SVG d'apres les attributs de la balise * @param array $attributs * @return array */ function svg_getimagesize_from_attr($attributs) { $width = 350; // default width $height = 150; // default height $viewBox = "0 0 $width $height"; if (isset($attributs['viewBox'])) { $viewBox = $attributs['viewBox']; $viewBox = preg_replace(",\s+,", " ", $viewBox); } // et on la convertit en px $viewBox = explode(' ', $viewBox); $viewBox = array_map('svg_dimension_to_pixels', $viewBox); if (!$viewBox[2]) { $viewBox[2] = $width; } if (!$viewBox[3]) { $viewBox[3] = $height; } $coeff = 1; if (isset($attributs['width']) and $w = svg_dimension_to_pixels($attributs['width'])) { $width = $w; } else { // si on recupere la taille de la viewbox mais si la viewbox est petite on met un multiplicateur pour la taille finale $width = $viewBox[2]; if ($width < 1) { $coeff = max($coeff, 1000); } elseif ($width < 10) { $coeff = max($coeff, 100); } elseif ($width < 100) { $coeff = max($coeff, 10); } } if (isset($attributs['height']) and $h = svg_dimension_to_pixels($attributs['height'])) { $height = $h; } else { $height = $viewBox[3]; if ($height < 1) { $coeff = max($coeff, 1000); } elseif ($height < 10) { $coeff = max($coeff, 100); } elseif ($height < 100) { $coeff = max($coeff, 10); } } // arrondir le width et height en pixel in fine $width = round($coeff * $width); $height = round($coeff * $height); $viewBox = implode(' ', $viewBox); return array($width, $height, $viewBox); } /** * Forcer la viewBox du SVG, en px * cree l'attribut viewBox si il n'y en a pas * convertit les unites en px si besoin * * Les manipulations d'image par les filtres images se font en px, on a donc besoin d'une viewBox en px * Certains svg produits avec des unites exotiques subiront donc peut etre des deformations... * * @param string $img * @param bool $force_width_and_height * @return string */ function svg_force_viewBox_px($img, $force_width_and_height = false) { if ($svg = svg_charger($img) and $svg_infos = svg_lire_balise_svg($svg)){ list($balise_svg, $attributs) = $svg_infos; list($width, $height, $viewBox) = svg_getimagesize_from_attr($attributs); if ($force_width_and_height) { $attributs['width'] = $width; $attributs['height'] = $height; } $attributs['viewBox'] = $viewBox; $svg = svg_change_balise_svg($svg, $balise_svg, $attributs); return $svg; } return $img; } /** * Extract all colors in SVG * @param $img * @return array|mixed */ function svg_extract_couleurs($img) { if ($svg = svg_charger($img)) { if (preg_match_all("/(#[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f])|(rgb\([\s\d]+,[\s\d]+,[\s\d]+\))|(#[0-9a-f][0-9a-f][0-9a-f])/imS", $svg, $matches)) { return $matches[0]; } } return []; } /** * Redimensionner le SVG via le width/height de la balise * @param string $img * @param $new_width * @param $new_height * @return bool|string */ function svg_recadrer($img, $new_width, $new_height, $offset_width, $offset_height, $background_color='') { if ($svg = svg_force_viewBox_px($img) and $svg_infos = svg_lire_balise_svg($svg)) { list($balise_svg, $attributs) = $svg_infos; $viewBox = explode(' ', $attributs['viewBox']); $viewport_w = $new_width; $viewport_h = $new_height; $viewport_ox = $offset_width; $viewport_oy = $offset_height; // si on a un width/height qui rescale, il faut rescaler if (isset ($attributs['width']) and $w = svg_dimension_to_pixels($attributs['width']) and isset ($attributs['height']) and $h = svg_dimension_to_pixels($attributs['height'])) { $xscale = $viewBox[2] / $w; $viewport_w = round($viewport_w * $xscale, 2); $viewport_ox = round($viewport_ox * $xscale, 2); $yscale = $viewBox[3] / $h; $viewport_h = round($viewport_h * $yscale, 2); $viewport_oy = round($viewport_oy * $yscale, 2); } if ($viewport_w>$viewBox[2] or $viewport_h>$viewBox[3]) { $svg = svg_clip_in_box($svg, $viewBox[0], $viewBox[1], $viewBox[2], $viewBox[3]); } // maintenant on redefinit la viewBox $viewBox[0] += $viewport_ox; $viewBox[1] += $viewport_oy; $viewBox[2] = $viewport_w; $viewBox[3] = $viewport_h; $attributs['viewBox'] = implode(' ', $viewBox); $attributs['width'] = strval($new_width); $attributs['height'] = strval($new_height); $svg = svg_change_balise_svg($svg, $balise_svg, $attributs); // ajouter un background if ($background_color and $background_color!=='transparent') { $svg = svg_ajouter_background($svg, $background_color); } return $svg; } return $img; } /** * Ajouter un background au SVG : un rect pleine taille avec la bonne couleur * @param $img * @param $background_color * @return bool|string */ function svg_ajouter_background($img, $background_color) { if ($svg = svg_charger($img) and $svg_infos = svg_lire_balise_svg($svg)){ if ($background_color and $background_color!=='transparent') { list($balise_svg, $attributs) = $svg_infos; $background_color = svg_couleur_to_hexa($background_color); if (isset($attributs['viewBox'])) { $viewBox = explode(' ', $attributs['viewBox']); $rect = ""; } else { $rect = ""; } $svg = svg_insert_shapes($svg, $rect); } return $svg; } return $img; } /** * Ajouter un voile au SVG : un rect pleine taille avec la bonne couleur/opacite, en premier plan * @param $img * @param $background_color * @return bool|string */ function svg_ajouter_voile($img, $background_color, $opacity) { if ($svg = svg_charger($img) and $svg_infos = svg_lire_balise_svg($svg)){ if ($background_color and $background_color!=='transparent') { list($balise_svg, $attributs) = $svg_infos; $background_color = svg_couleur_to_hexa($background_color); if (isset($attributs['viewBox'])) { $viewBox = explode(' ', $attributs['viewBox']); $rect = ""; } else { $rect = ""; } $svg = svg_insert_shapes($svg, $rect, false); } return $svg; } return $img; } /** * Ajouter un background au SVG : un rect pleine taille avec la bonne couleur * @param $img * @array $attributs * @return bool|string */ function svg_transformer($img, $attributs) { if ($svg = svg_charger($img) and $svg_infos = svg_lire_balise_svg($svg)){ if ($attributs) { list($balise_svg, ) = $svg_infos; $g = "$v) { if (strlen($v)) { $g .= " $k=\"".attribut_html($v)."\""; } } if (strlen($g) > 2) { $g .= ">"; $svg = svg_insert_shapes($svg, $g); $svg = svg_insert_shapes($svg, "", false); } } return $svg; } return $img; } /** * Ajouter + appliquer un filtre a un svg * @param string $img * @param string $filter_def * definition du filtre (contenu de ... * @return bool|string */ function svg_apply_filter($img, $filter_def) { if ($svg = svg_charger($img) and $svg_infos = svg_lire_balise_svg($svg)){ if ($filter_def) { list($balise_svg, ) = $svg_infos; $filter_id = "filter-". substr(md5($filter_def . strlen($svg)), 0, 8); $filter = "$filter_def"; $g = ""; $svg = svg_insert_shapes($svg, $filter . $g); $svg = svg_insert_shapes($svg, "", false); } return $svg; } return $img; } /** * Filtre blur en utilisant * @param string $img * @param int $blur_width * @return string */ function svg_filter_blur($img, $blur_width) { $blur_width = intval($blur_width); return svg_apply_filter($img, ""); } /** * Filtre grayscale en utilisant * @param string $img * @param float $intensity * @return bool|string */ function svg_filter_grayscale($img, $intensity) { $value = round(1.0 - $intensity, 2); //$filter = ""; $filter = ""; return svg_apply_filter($img, $filter); } /** * Filtre sepia en utilisant * @param $img * @param $intensity * @return bool|string */ function svg_filter_sepia($img, $intensity) { $filter = ""; return svg_apply_filter($img, $filter); } /** * Ajouter un background au SVG : un rect pleine taille avec la bonne couleur * @param $img * @array string $HorV * @return bool|string */ function svg_flip($img, $HorV) { if ($svg = svg_force_viewBox_px($img) and $svg_infos = svg_lire_balise_svg($svg)){ list($balise_svg, $atts) = $svg_infos; $viewBox = explode(' ', $atts['viewBox']); if (!in_array($HorV, ['h', 'H'])) { $transform = "scale(-1,1)"; $x = intval($viewBox[0]) + intval($viewBox[2]/2); $mx = -$x; $transform = "translate($x, 0) $transform translate($mx, 0)"; } else { $transform = "scale(1,-1)"; $y = intval($viewBox[1]) + intval($viewBox[3]/2); $my = -$y; $transform = "translate(0, $y) $transform translate(0, $my)"; } $svg = svg_transformer($svg, ['transform' => $transform]); return $svg; } return $img; } /** * @param string $img * @param int/float $angle * angle en degres * @param $center_x * centre X de la rotation entre 0 et 1, relatif a la pleine largeur (0=bord gauche, 1=bord droit) * @param $center_y * centre Y de la rotation entre 0 et 1, relatif a la pleine hauteur (0=bord top, 1=bord bottom) * @return bool|string */ function svg_rotate($img, $angle, $center_x, $center_y) { if ($svg = svg_force_viewBox_px($img) and $svg_infos = svg_lire_balise_svg($svg)){ list($balise_svg, $atts) = $svg_infos; $viewBox = explode(' ', $atts['viewBox']); $center_x = round($viewBox[0] + $center_x * $viewBox[2]); $center_y = round($viewBox[1] + $center_y * $viewBox[3]); $svg = svg_transformer($svg, ['transform' => "rotate($angle $center_x $center_y)"]); return $svg; } return $img; } /** * Filtrer les couleurs d'un SVG avec une callback * (peut etre lent si beaucoup de couleurs) * * @param $img * @param $callback_filter * @return bool|mixed|string */ function svg_filtrer_couleurs($img, $callback_filter) { if ($svg = svg_force_viewBox_px($img) and $colors = svg_extract_couleurs($svg)) { $colors = array_unique($colors); $short = []; $long = []; while (count($colors)) { $c = array_shift($colors); if (strlen($c) == 4) { $short[] = $c; } else { $long[] = $c; } } $colors = array_merge($long, $short); $new_colors = []; $colors = array_flip($colors); foreach ($colors as $c => $k) { $colors[$c] = "@@@COLOR$$k$@@@"; } foreach ($colors as $original => $replace) { $new = svg_couleur_to_hexa($original); $new_colors[$replace] = $callback_filter($new); } $svg = str_replace(array_keys($colors), array_values($colors), $svg); $svg = str_replace(array_keys($new_colors), array_values($new_colors), $svg); return $svg; } return $img; }