@@ -189,6 +189,97 @@ protected function applyTransform($attributes)
189189 }
190190 }
191191
192+ /**
193+ * Apply a viewBox transform to the element
194+ *
195+ * @param array $attributes
196+ */
197+ protected function applyViewbox ($ attributes ) {
198+ if (!isset ($ attributes ["viewbox " ])) {
199+ return ;
200+ }
201+
202+ $ surface = $ this ->document ->getSurface ();
203+ $ viewBox = preg_split ('/[\s,]+/is ' , trim ($ attributes ['viewbox ' ]));
204+ if (count ($ viewBox ) != 4 ) {
205+ return ;
206+ }
207+
208+ // Computing the equivalent transform of an SVG viewport
209+ // https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform
210+
211+ // 1. Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the viewBox attribute respectively.
212+ [$ vbX , $ vbY , $ vbWidth , $ vbHeight ] = $ viewBox ;
213+
214+ if ($ vbWidth < 0 || $ vbHeight < 0 ) {
215+ return ;
216+ }
217+
218+ // correct solution is to not render, for now scaling to 0 below
219+ //if ($vbWidth == 0 || $vbHeight == 0) {
220+ //}
221+
222+ // 2. Let e-x, e-y, e-width, e-height be the position and size of the element respectively.
223+ $ eX = $ attributes ["x " ] ?? 0 ;
224+ $ eY = $ attributes ["y " ] ?? 0 ;
225+ $ eWidth = $ attributes ["width " ] ?? $ this ->document ->getWidth ();
226+ $ eHeight = $ attributes ["height " ] ?? $ this ->document ->getHeight ();
227+
228+ // 3. Let align be the align value of preserveAspectRatio, or 'xMidYMid' if preserveAspectRatio is not defined.
229+ $ preserveAspectRatio = explode (" " , $ attributes ["preserveAspectRatio " ] ?? "xMidYMid meet " );
230+ $ align = $ preserveAspectRatio [0 ];
231+
232+ // 4. Let meetOrSlice be the meetOrSlice value of preserveAspectRatio, or 'meet' if preserveAspectRatio is not defined or if meetOrSlice is missing from this value.
233+ $ meetOrSlice = $ meetOrSlice ?? "meet " ;
234+
235+ // 5. Initialize scale-x to e-width/vb-width.
236+ $ scaleX = $ vbWidth == 0 ? 0 : ($ eWidth / $ vbWidth );
237+
238+ // 6. Initialize scale-y to e-height/vb-height.
239+ $ scaleY = $ vbHeight == 0 ? 0 : ($ eHeight / $ vbHeight );
240+
241+ // 7. If align is not 'none' and meetOrSlice is 'meet', set the larger of scale-x and scale-y to the smaller.
242+ if ($ align !== "none " && $ meetOrSlice === "meet " ) {
243+ $ scaleX = min ($ scaleX , $ scaleY );
244+ $ scaleY = min ($ scaleX , $ scaleY );
245+ }
246+
247+ // 8. Otherwise, if align is not 'none' and meetOrSlice is 'slice', set the smaller of scale-x and scale-y to the larger.
248+ elseif ($ align !== "none " && $ meetOrSlice === "slice " ) {
249+ $ scaleX = max ($ scaleX , $ scaleY );
250+ $ scaleY = max ($ scaleX , $ scaleY );
251+ }
252+
253+ // 9. Initialize translate-x to e-x - (vb-x * scale-x).
254+ $ translateX = $ eX - ($ vbX * $ scaleX );
255+
256+ // 10. Initialize translate-y to e-y - (vb-y * scale-y)
257+ $ translateY = $ eY - ($ vbY * $ scaleY );
258+
259+ // 11. If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x.
260+ if (strpos ($ align , "xMid " ) !== false ) {
261+ $ translateX += ($ eWidth - $ vbWidth * $ scaleX ) / 2 ;
262+ }
263+
264+ // 12. If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x.
265+ if (strpos ($ align , "xMax " ) !== false ) {
266+ $ translateX += ($ eWidth - $ vbWidth * $ scaleX );
267+ }
268+
269+ // 13. If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y.
270+ if (strpos ($ align , "yMid " ) !== false ) {
271+ $ translateX += ($ eHeight - $ vbHeight * $ scaleY ) / 2 ;
272+ }
273+
274+ // 14. If align contains 'yMax', add (e-height - vb-height * scale-y) to translate-y.
275+ if (strpos ($ align , "yMid " ) !== false ) {
276+ $ translateX += ($ eHeight - $ vbHeight * $ scaleY );
277+ }
278+
279+ $ surface ->translate ($ translateX , $ translateY );
280+ $ surface ->scale ($ scaleX , $ scaleY );
281+ }
282+
192283 /**
193284 * Convert the given size for the context of this current tag.
194285 * Takes a pixel-based reference, which is usually specific to the context of the size,
0 commit comments