{"id":2827,"date":"2024-04-16T13:23:33","date_gmt":"2024-04-16T13:23:33","guid":{"rendered":"https:\/\/www.bverhue.nl\/delphisvg\/?p=2827"},"modified":"2024-04-16T13:23:34","modified_gmt":"2024-04-16T13:23:34","slug":"implement-zoom-and-pan-on-tsvg2image","status":"publish","type":"post","link":"https:\/\/www.bverhue.nl\/delphisvg\/2024\/04\/16\/implement-zoom-and-pan-on-tsvg2image\/","title":{"rendered":"Implement Zoom And Pan on TSVG2Image"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">This example shows how Zoom and Pan can be implemented on the TSVG2Image control.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In SVG zooming and panning is controlled by the <strong>viewPort<\/strong>, <strong>viewBox <\/strong>and <strong>preserveAspectRatio <\/strong>attributes on the outer SVG element.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The outer SVG element is the first SVG element in the xml file. In most cases, there is only one SVG element within an SVG xml file.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">V<strong>iew port<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The viewPort is the window in which the SVG image is displayed. The viewPort is defined by the dimensions of the outer SVG element, so the width and height attribute or, if width and height are expressed as percentages, the client rectangle of the SVG&#8217;s container.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For our purposes we will override the width and height attributes of the outer SVG element and set them to 100%, so width = 100% and height=100%. With these settings the viewPort will be equal to the ClientRect of the TSVG2Image control.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>View box<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The viewBox attribute selects the part of the SVG image that is displayed. This is an optional attribute, not all SVG images have a viewBox defined. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For zooming and panning we need a viewBox, so if the SVG image has no viewBox defined, we will create one and initially set it to the same size as the viewPort.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Preserve aspect ratio<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The preserveAspectRatio defines how the viewBox is aligned within the viewPort. We want to preserve the aspect ratio of the SVG image and we want the center it. For this we need the setting xMidYMid.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.bverhue.nl\/delphisvg\/wp-content\/uploads\/2018\/06\/Aspect-ratio-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"589\" src=\"https:\/\/www.bverhue.nl\/delphisvg\/wp-content\/uploads\/2018\/06\/Aspect-ratio-2-1024x589.png\" alt=\"\" class=\"wp-image-514\" srcset=\"https:\/\/www.bverhue.nl\/delphisvg\/wp-content\/uploads\/2018\/06\/Aspect-ratio-2-1024x589.png 1024w, https:\/\/www.bverhue.nl\/delphisvg\/wp-content\/uploads\/2018\/06\/Aspect-ratio-2-600x345.png 600w, https:\/\/www.bverhue.nl\/delphisvg\/wp-content\/uploads\/2018\/06\/Aspect-ratio-2-300x173.png 300w, https:\/\/www.bverhue.nl\/delphisvg\/wp-content\/uploads\/2018\/06\/Aspect-ratio-2-768x442.png 768w, https:\/\/www.bverhue.nl\/delphisvg\/wp-content\/uploads\/2018\/06\/Aspect-ratio-2.png 1032w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Basics of zooming and panning in SVG<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For zooming and panning an SVG image we only need to modify the viewBox attribute.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For panning we simply change the top left point of the viewBox rectangle.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For zooming we change with equal proportion the width and height of the viewBox rectangle. The smaller the viewBox, the more we are zooming in.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Structure of the example application<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A form with on it one TSVG2Image set to align &#8220;alClient&#8221; to the form<br><\/li>\n\n\n\n<li>Property &#8220;DoubleBuffered&#8221; on the form is set to &#8220;True&#8221;, to ensure smooth zooming and panning<br><\/li>\n\n\n\n<li>Property &#8220;AutoViewbox&#8221; on the TSVG2Image is set to &#8220;True&#8221;, so the width and height of the outer SVG will be automatically set to 100%<br><\/li>\n\n\n\n<li>Property &#8220;AspectRatioAlign&#8221; will be set to &#8220;arXMidYMid&#8221; to preserve the aspect ratio of the SVG image and center it within the viewPort.<br><\/li>\n\n\n\n<li>Property &#8220;AspectRatioMeetOrSlice&#8221; can either be &#8220;Meet&#8221; or &#8220;Slice&#8221;<br><\/li>\n\n\n\n<li>Event &#8220;OnAfterParse&#8221; on TSVGImage will be used to read the &#8220;viewBox&#8221; attribute from the outer SVG element and store it in form variable &#8220;FViewBox&#8221;. If it does not exist, we will initialize FViewBox with the size of the viewPort.<br><\/li>\n\n\n\n<li>Event &#8220;OnDblClick&#8221; on TSVGImage will allow loading of a new SVG image<br><\/li>\n\n\n\n<li>Events &#8220;OnMouseDown&#8221;, &#8220;OnMouseMove&#8221; and &#8220;OnMouseUp&#8221; on TSVGImage will be used to pan the SVG image. Panning will be proportional to the zoom level.<br><\/li>\n\n\n\n<li>Event &#8220;OnMouseWheel&#8221; on the form will be used to increase or decrease the zoom on the SVG image. The focus of the zoom will be the position of the mouse pointer on the SVG image.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">So after creating the application project and setting the properties on the form and the TSVG2Image we need to implement the event handlers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>OnAfterParse<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After parsing the SVG xml file an in-memory node tree data structure will be available for us to read and modify SVG elements attributes. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We can access the element nodes through the TSVG2Image &#8220;SVGRoot&#8221; property.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The &#8220;SVG&#8221; property of SVGRoot will point to the outer SVG element of the SVG image.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In the code below, we assign the viewBox attribute value to the form variable FViewBox. If no viewBox is defined on the outer SVG we initialize FViewBox with the size of the viewPort.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>procedure TForm1.SVG2Image1AfterParse(Sender: TObject);\nvar\n  SVG: ISVG;\n  CR: TRect;\nbegin\n  if not assigned(SVG2Image1.SVGRoot) then\n    Exit;\n\n  SVG := SVG2Image1.SVGRoot.SVG;\n\n  if not assigned(SVG) then\n    Exit;\n\n  if SVG.ViewBox.IsUndefined then\n  begin\n    \/\/ No viewBox so we will create one with the same size as the viewPort.\n    \/\/ The viewPort is defined by the dimensions of the outer SVG element.\n\n    CR := SVG2Image1.ClientRect;\n\n    FViewBox := SVG2Image1.SVGRoot.CalcIntrinsicSize(SVGRect(0, 0, CR.Width, CR.Height));\n  end else\n    FViewBox := SVG.ViewBox;\nend;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Utility functions<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The basis for all calculations in the application is the conversion from Mouse point to ViewBox point. The formula for this depends on the preserveAspectRatio settings. If you choose an other setting than xMidYMid the calculations will be different.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>CalcZoomFactor<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First we create a function to calculate the current zoom factor.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Depending on the preserveAspectRatio setting &#8220;Meet&#8221; or &#8220;Slice&#8221; and the aspect ratios of the viewBox or the viewPort, the zoom factor is calculated from the width or height ratio between the viewBox and the viewPort.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>function TForm1.CalcZoomFactor: TSVGFloat;\nvar\n  ViewPort: TRect;\n  ViewPortRatioXY,\n  ViewBoxRatioXY: TSVGFloat;\nbegin\n  \/\/ This calculates the zoom factor between between the viewPort and the viewBox\n\n  ViewPort := SVG2Image1.ClientRect;\n\n  \/\/ We have to take into consideration the aspect ratio settings\n\n  ViewPortRatioXY := ViewPort.Width \/ ViewPort.Height;\n  ViewBoxRatioXY := FViewBox.Width \/ FViewBox.Height;\n\n  if SVG2Image1.AspectRatioMeetOrSlice = arSlice then\n  begin\n\n    if ViewPortRatioXY &gt; ViewBoxRatioXY then\n      Result := FViewBox.Width \/ ViewPort.Width\n    else\n      Result := FViewBox.Height \/ ViewPort.Height;\n\n  end else begin\n\n    if ViewPortRatioXY &gt; ViewBoxRatioXY then\n      Result := FViewBox.Height \/ ViewPort.Height\n    else\n      Result := FViewBox.Width \/ ViewPort.Width;\n\n  end;\nend;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><em>CalcViewBoxPt<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next we can create a function to calculate a viewBox point from a mouse point. This calculation is based on the xMidYMid setting.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So basically we calculate the distance of the mousepoint to the middle of the viewPort. Multiply this by the zoom factor to convert it to viewBox coordinates and then subtract it from the middle of the viewBox and finally add the top left point of the viewBox.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>function TForm1.CalcViewBoxPt(const aMousePt: TPoint): TSVGPoint;\nvar\n  ViewPort: TRect;\n  Zoom: TSVGFloat;\nbegin\n  ViewPort := SVG2Image1.ClientRect;\n\n  Zoom := CalcZoomFactor;\n\n  \/\/ Convert mousepoint to viewboxpoint\n\n  Result.X := FViewBox.Left + FViewBox.Width\/2 - (ViewPort.Width\/2 - aMousePt.X) * Zoom;\n  Result.Y := FViewBox.Top + FViewBox.Height\/2 - (ViewPort.Height\/2 - aMousePt.Y) * Zoom;\nend;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><em>SVGRepaint<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With the SVGRepaint procedure we set the modified form variable FViewBox on the outer SVG element and force a repaint of the TSVG2Image.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>procedure TForm1.SVGRepaint;\nvar\n  SVG: ISVG;\nbegin\n  \/\/ Get the outer SVG element\n\n  if not assigned(SVG2Image1.SVGRoot) then\n    Exit;\n\n  SVG := SVG2Image1.SVGRoot.SVG;\n\n  if not assigned(SVG) then\n    Exit;\n\n  \/\/ Set the viewBox attribute on the outer SVG element\n\n  SVG.ViewBox := FViewBox;\n  SVG2Image1.Repaint;\nend;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now we are ready to implement the event handlers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>OnMouseDown<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On the OnMouseDown event we start panning if the left mouse button is pressed and it is not a double click.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>procedure TForm1.SVG2Image1MouseDown(Sender: TObject; Button: TMouseButton;\n  Shift: TShiftState; X, Y: Integer);\nbegin\n  if (mbLeft = Button) and not(ssDouble in Shift) then\n  begin\n    \/\/ Save the mousedown point on mousedown\n\n    FMouseDown := True;\n    FMousePt := Point(X, Y);\n  end;\nend;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>OnMouseMove<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If we move the mouse we want to display the viewBox coordinates corresponding with the mouse pointer on the form caption.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If we are panning (FMouseDown = True) we calculate the new viewBox position from the delta mouse position.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>procedure TForm1.SVG2Image1MouseMove(Sender: TObject; Shift: TShiftState; X,\n  Y: Integer);\nvar\n  Delta: TPoint;\n  ViewPort: TRect;\n  Zoom: TSVGFLoat;\n  ViewBoxPt: TSVGPoint;\nbegin\n  \/\/ Convert the mouse coords to viewBox coords and display in caption\n  \/\/ Move the viewBox if the mouse btn is pressed\n\n  ViewBoxPt := CalcViewBoxPt(Point(X, Y));\n  Caption := Format('ViewBox Pt: %3.1f %3.1f', &#91;ViewBoxPt.X, ViewBoxPt.Y]);\n\n  if FMouseDown then\n  begin\n    \/\/ Compensate the mousemovent with the zoom factor so the image moves at\n    \/\/ the same rate as the mouse\n\n    ViewPort := SVG2Image1.ClientRect;\n\n    Zoom := CalcZoomFactor;\n\n    Delta := Point(FMousePt.X - X, FMousePt.Y - Y);\n\n    \/\/ Calculate the new viewport position.\n\n    FViewBox.Left := FViewBox.Left + Delta.X * Zoom;\n    FViewBox.Right := FViewBox.Right + Delta.X * Zoom;\n    FViewBox.Top := FViewBox.Top + Delta.Y * Zoom;\n    FViewBox.Bottom := FViewBox.Bottom + Delta.Y * Zoom;\n\n    \/\/ Save the new mousedown position\n\n    FMousePt := Point(X, Y);\n\n    \/\/ Set the new viewBox and repaint the SVG image.\n\n    SVGRepaint;\n  end;\nend;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>OnMouseUp<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On the mouseup event we reset the form variables that control panning.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>procedure TForm1.SVG2Image1MouseUp(Sender: TObject; Button: TMouseButton;\n  Shift: TShiftState; X, Y: Integer);\nbegin\n  FMousePt := Point(0, 0);\n  FMouseDown := False;\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>OnMouseWheel<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On every change of the mousewheel we increase or decrease the zoom level with a constant factor F. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  \/\/ Zoom with arbitrary factor\n\n  Sign := WheelDelta \/ abs(WheelDelta);\n  F := -Sign * 0.05;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So for zooming in and out we simply increase the width and height of the viewBox with this factor.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>FViewBox.Width = FViewBox.Width * (1 + F)\nFViewBox.Height = FViewBox.Height * (1 + F)<\/em><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The challenge now is to focus the zoom onto the current mousepointer. This means that we want the viewBox point under the mousepointer to remain the same after changing the zoom level. For this we need to move the viewBox a bit after zooming. This is what we now have to calculate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The viewBox points can be calculated from the mousepoint, in case of xMidYMid we have the following formula&#8217;s:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n<em>X = ViewBox.Left + ViewBox.Width\/2 + (MouseX - ViewPort.Width\/2) * Zoom\nY = ViewBox.Top + ViewBox.Height\/2 + (MouseY - ViewPort.Height\/2) * Zoom<\/em><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After changing the zoom with a factor F, the formula&#8217;s can be written as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>X = NewViewBox.Left + ViewBox.Width\/2 * (1 + F) + (MouseX - ViewPort.Width\/2) * Zoom * (1 + F)\nY = NewViewBox.Top + ViewBox.Height\/2 * (1 + F) + (MouseY - ViewPort.Height\/2) * Zoom * (1 + F)<\/em><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Because X and Y must remain the same before and after changing the zoom level, we can now calculate the values of NewViewBox.Left and NewViewBox.Top.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>NewViewBox.Left = ViewBox.Left - ViewBox.Width\/2 * F - (MouseX - ViewPort.Width\/2) * Zoom * F\nNewViewBox.Top = ViewBox.Top - ViewBox.Height\/2 * F - (MouseY - ViewPort.Height\/2) * Zoom * F<\/em><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So the complete MouseWheel event handler looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>procedure TForm1.FormMouseWheel(Sender: TObject; Shift: TShiftState;\n  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);\nvar\n  P: TPoint;\n  F: TSVGFloat;\n  Sign: TSVGFloat;\n  ViewPort: TRect;\n  ViewBoxOriginal: TSVGRect;\n  Zoom: TSVGFloat;\nbegin\n  \/\/ Zoom the SVG in or out on every change of the mousewheel\n\n  if WheelDelta = 0 then\n    Exit;\n\n  \/\/ Zoom with arbitrary factor\n\n  Sign := WheelDelta \/ abs(WheelDelta);\n  F := -Sign * 0.05;\n\n  ViewBoxOriginal := FViewBox;\n\n  ViewPort := SVG2Image1.ClientRect;\n\n  Zoom := CalcZoomFactor;\n\n  P := SVG2Image1.ScreenToClient(MousePos);\n\n  FViewBox.Left := ViewBoxOriginal.Left - ViewBoxOriginal.Width\/2 * F \n    - (P.X - ViewPort.Width\/2) * Zoom * F;\n\n  FViewBox.Top := ViewBoxOriginal.Top - ViewBoxOriginal.Height\/2 * F \n    - (P.Y - ViewPort.Height\/2) * Zoom * F;\n\n  FViewBox.Right := FViewBox.Left + ViewBoxOriginal.Width * (1 + F);\n  FViewBox.Bottom := FViewBox.Top + ViewBoxOriginal.Height * (1 + F);\n\n  SVGRepaint;\nend;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><br><strong>OnDblClick<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finally we create an even handler for loading an SVG file into the TSVG2Image control.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>procedure TForm1.SVG2Image1DblClick(Sender: TObject);\nbegin\n  \/\/ Load a new SVG image by dubbel clicking the TSVG2Image\n\n  if OpenDialog1.Execute then\n  begin\n    \/\/ Clear any SVG present in the SVG stringlist property, because it has\n    \/\/ priority over the SVG filename property\n\n    SVG2Image1.SVG.Clear;\n\n    FViewBox := TSVGRect.CreateUndefined;\n\n    SVG2Image1.Filename := OpenDialog1.FileName;\n  end;\nend;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The complete source code of this example project can be found <a href=\"https:\/\/github.com\/BVerhue\/delphi-svg-control-examples\/tree\/master\/SVGv240\/ZoomAndPan\/Vcl\" data-type=\"link\" data-id=\"https:\/\/github.com\/BVerhue\/delphi-svg-control-examples\/tree\/master\/SVGv240\/ZoomAndPan\/Vcl\">here<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This example shows how Zoom and Pan can be implemented on the TSVG2Image control. In SVG zooming and panning is controlled by the viewPort, viewBox and preserveAspectRatio attributes on the outer SVG element. The outer SVG element is the first SVG element in the xml file. In most cases, there is only one SVG element &#8230; <a title=\"Implement Zoom And Pan on TSVG2Image\" class=\"read-more\" href=\"https:\/\/www.bverhue.nl\/delphisvg\/2024\/04\/16\/implement-zoom-and-pan-on-tsvg2image\/\" aria-label=\"Read more about Implement Zoom And Pan on TSVG2Image\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":2832,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2827","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/posts\/2827","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/comments?post=2827"}],"version-history":[{"count":47,"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/posts\/2827\/revisions"}],"predecessor-version":[{"id":2876,"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/posts\/2827\/revisions\/2876"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/media\/2832"}],"wp:attachment":[{"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/media?parent=2827"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/categories?post=2827"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bverhue.nl\/delphisvg\/wp-json\/wp\/v2\/tags?post=2827"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}