Font Design with Bézier Splines

Design of curves and shapes in .svg (scalable vector graphics) for glyphs

Prof. Dr. Bernhard Zgraggen, HSR

({{, }})

Einführung in dieses Notebook

Dieses Dokument ist ein "selbsterklärendes" Mathematica-Notebook, das interaktiv bearbeitet werden kann ("Hands-On"). Input-Zellen können mittels der Tastenkombination ⇧⌤ (in Worten: Shift-Enter) ausgeführt werden. Falls Textzellen geschlossen sind, so können sie durch Klick auf das Icon ▼ oder auf die blaue Zellklammer rechterhand geöffnet werden.

Wenn Sie in Mathematica auf Befehle oder Begriffe stossen, die Sie nicht verstehen, so steht Ihnen die mächtige Hilfe/Doc Center zur Verfügung; diese ist interaktiv.

Allfällige Aufgaben/Übungen befinden sich am Ende des Dokuments.

Quadratic and cubic Bézier-Splines in .svg (scalable vector graphics)

The .svg specification provides - among many other interesting things - the possibility to draw quadratic and cubic Bézier splines in the <path> element.

Paths 
These are simple or compound shape outlines drawn with curved or straight lines, can be filled in or outlined (or used as a clipping path) and are expressed in a highly compact coding in which, for example, M (from' move to') precedes the initial numeric X and Y coordinates and L (line to) will precede a subsequent point to which a line should be drawn. Special lines are horizontal lines (H) or vertical ones (V). Further command letters (C, S, Q, T and A) precede data that is used to draw various Bézier and elliptical curves.Z is used to close a path. In all cases, absolute coordinates follow capital letter commands and relative coordinates are used after the equivalent lower - case letters.
See http://www.w3.org/TR/SVG11/paths.html for the official reference.

The cubic Bézier commands

Command: C (absolute) or c (relative)
Parameters: (x1 y1 x2 y2 x y)+
Description: 
Draws a cubic Bézier curve from the current point to (x,y) using (x1,y1) as the control point at the beginning of the curve and (x2,y2) as the control point at the end of the curve. C (uppercase) indicates that absolute coordinates will follow; c (lowercase) indicates that relative coordinates will follow. Multiple sets of coordinates may be specified to draw a polybézier. At the end of the command, the new current point becomes the final (x,y) coordinate pair used in the polybézier.

Command: S (absolute) or s (relative)
Parameters: (x2 y2 x y)+
Description: 
Draws a cubic Bézier curve from the current point to (x,y). The first control point is assumed to be the reflection of the second control point on the previous command relative to the current point. (If there is no previous command or if the previous command was not an C, c, S or s, assume the first control point is coincident with the current point.) (x2,y2) is the second control point (i.e., the control point at the end of the curve). S (uppercase) indicates that absolute coordinates will follow; s (lowercase) indicates that relative coordinates will follow. Multiple sets of coordinates may be specified to draw a polybézier. At the end of the command, the new current point becomes the final (x,y) coordinate pair used in the polybézier.

Example: Glyph Font Design

({{, }})

The .svg encoding

The picture of the glyph "a" on the left has the following .svg encoding (scalable vector graphics). The picture on the right shows the control points and the corresponding composite Bézier curves (poly Bézier); dashed lines are simple lines (commands L or l, V or v, H or h):

<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
	<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
	<!ENTITY ns_svg "http://www.w3.org/2000/svg">
	<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg  version="1.1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
	 width="336" height="336" viewBox="-0.658 -0.725 336 336" enable-background="new -0.658 -0.725 336 336" xml:space="preserve">
<defs>
</defs>
<rect x="30" y="30" fill="#FFFFFF" stroke="#000000" stroke-width="3" width="275" height="275"/>
<line fill="none" stroke="#000000" stroke-width="2" x1="0" y1="167.5" x2="335" y2="167.5"/>
<line fill="none" stroke="#000000" stroke-width="2" x1="167.5" y1="0" x2="167.5" y2="335"/>
<circle fill="none" stroke="#000000" cx="167.5" cy="167.5" r="137.5"/>
<line fill="none" stroke="#000000" stroke-width="2" x1="30" y1="30" x2="306.171" y2="306.171"/>
<path fill="#E32322" d="

M 221.852, 222.566

c 0,35.345,6.339,46.505,15.891,46.505
c 8.473,0,15.891-4.948,18.542-9.294
c 2.114-3.078,3.169-4.34,5.304-2.471

l 5.305,4.947

c 1.574,1.869,2.63,4.947-0.539,11.157
c -4.248,9.341-14.317,30.399-41.333,30.399
c -19.082,0-32.858-16.107-37.623-41.557

h-1.057

c -11.126,15.501-28.611,28.53-52.459,37.871
c -10.069,3.686-19.599,3.686-31.263,3.686
c -12.721,0-31.803-12.372-31.803-51.455
c 0-28.58,11.125-53.371,52.997-75.084
c 23.848-12.419,51.401-27.314,59.875-36.001

v -39.688

c -1.056-10.553-1.595-26.71-7.417-34.79
c -5.822-8.029-16.429-13.634-31.803-13.634
c -10.587,0-27.016,4.949-34.433,13.028
c -7.417,8.079-2.652,17.37-2.652,28.528
c 0,21.713-6.36,35.396-28.073,35.396
c -8.495,0-12.72-5.604-12.72-19.843
c 0-19.895,14.834-43.475,36.567-58.371
c 22.25-15.502,40.793-21.713,64.641-21.713
c 24.902,0,34.971,8.687,42.388,16.765
c 11.125,13.027,11.664,27.317,11.664,44.081

v 94.928
V 222.566
z  

M 183.171,159.905
c -19.081,16.712-71.022,29.131-71.022,80.636
c 0,22.975,11.665,34.132,27.556,34.132
c 15.372,0,43.467-21.71,43.467-38.475

V 159.905

z"/>

</svg>

Computing the cubic Bézier splines → {BezierFunction, BezierCurve}

The first three cubic splines

The first 4 lines in the definition attribute of the path-element are:

M 221.852, 222.566

c 0, 35.345, 6.339, 46.505, 15.891, 46.505
c 8.473, 0, 15.891 - 4.948, 18.542 - 9.294
c 2.114 - 3.078, 3.169 - 4.34, 5.304 - 2.471

We move (M) to the starting point {221.852, 222.566} and subsequently add the offsets of the first cubic Bézier spline (c). The last point of the first spline is the first point of the second spline to which the corresponding offsets have to be added. Notice that the first point of the spline is not in the list of the c-command. This yields the first 10 points. The y-axis is inverted since the y-axis in .svg coordinates is the other way round compared to Mathematica:

pts=({#[[1]],-#[[2]]})&/@{{221.852`,222.566`},{221.852`,257.911`},{228.191`,269.071`},{237.743`,269.071`},{246.216`,269.071`},{253.634`,264.12300000000005`},{256.28499999999997`,259.77700000000004`},{258.39899999999994`,256.69900000000007`},{259.45399999999995`,255.43700000000004`},{261.58899999999994`,257.30600000000004`}}

The powerful Mathematica command BezierFunction. This command has an option SplineDegree. Using this command requires that every single cubic Bézier spline is computed as a single function from its corresponding four control points.

r1a=BezierFunction[{{221.852`,-222.566`},{221.852`,-257.911`},{228.191`,-269.071`},{237.743`,-269.071`}},SplineDegree->3]

r1b=BezierFunction[{{237.743`,-269.071`},{246.216`,-269.071`},{253.634`,-264.12300000000005`},{256.28499999999997`,-259.77700000000004`}},SplineDegree->3]

r1c=BezierFunction[{{256.28499999999997`,-259.77700000000004`},{258.39899999999994`,-256.69900000000007`},{259.45399999999995`,-255.43700000000004`},{261.58899999999994`,-257.30600000000004`}},SplineDegree->3]

The functional objects computed by BezierFunction. allow many mathematical or numeric operations typical for functions as ParametricPlot. (Here nested in combined graphical output including the control points and its segments.)

Show[Graphics[{Red,Point[pts],Green,Line[pts]}],ParametricPlot[{r1a[t],r1b[t],r1c[t]},{t,0,1}]]

We can display the composite Bézier spline using Mathematica's command BezierCurve. This command has an option SplineDegree; setting this to 3 will automatically compute a composite spline from the given set of points consisting of three cubic splines. Notice that BezierCurve is a graphics primitive.

g1=Graphics[{BezierCurve[pts,SplineDegree->3],Green,Line[pts],Red,Point[pts]},ImageSize->Small]

Smoothness checking for the first three cubic splines

Derivative vectors at common control points:

r1a'[1]
r1b'[0]
r1b'[1]
r1c'[0]

Length (norms) of control segments meeting at common control points. The lengths are different, so the composite Bézier function is not smooth ((C^1)) at the common control points.

Norm[{237.743`,-269.071`}-{228.191`,-269.071`}]
Norm[{246.216`,-269.071`}-{237.743`,-269.071`}]

Norm[{253.634`,-264.12300000000005`}-{256.28499999999997`,-259.77700000000004`}]
Norm[{256.28499999999997`,-259.77700000000004`}-{258.39899999999994`,-256.69900000000007`}]

{253.634`,-264.12300000000005`}-{256.28499999999997`,-259.77700000000004`}
{256.28499999999997`,-259.77700000000004`}-{258.39899999999994`,-256.69900000000007`}

The segments corresponding to the first common control point are collinear thus the curve has a common tangent direction at this point. For the second control point this is not fulfilled.

MatrixRank[{{237.743`,-269.071`}-{228.191`,-269.071`},
{246.216`,-269.071`}-{237.743`,-269.071`}}]

MatrixRank[{{253.634`,-264.12300000000005`}-{256.28499999999997`,-259.77700000000004`},
{256.28499999999997`,-259.77700000000004`}-{258.39899999999994`,-256.69900000000007`}}]

The other cubic splines

The same procedure with BezierCurve is repeated with the remaining point sets corresponding to the other c-commands in the .svg path-element.

pts=({#[[1]],-#[[2]]})&/@{{266.89399999999995`,262.25300000000004`},{268.46799999999996`,264.12200000000007`},{269.52399999999994`,267.20000000000005`},{266.35499999999996`,273.41`},{262.10699999999997`,282.75100000000003`},{252.03799999999995`,303.809`},{225.02199999999996`,303.809`},{205.93999999999997`,303.809`},{192.16399999999996`,287.702`},{187.39899999999997`,262.252`}}

g2=Graphics[{BezierCurve[pts,SplineDegree->3],Green,Line[pts],Red,Point[pts]},ImageSize->Small]

pts=({#[[1]],-#[[2]]})&/@{{186.34199999999998`,262.252`},{175.21599999999998`,277.753`},{157.731`,290.78200000000004`},{133.88299999999998`,300.123`},{123.81399999999998`,303.80899999999997`},{114.28399999999998`,303.80899999999997`},{102.61999999999998`,303.80899999999997`},{89.89899999999997`,303.80899999999997`},{70.81699999999998`,291.43699999999995`},{70.81699999999998`,252.35399999999998`},{70.81699999999998`,223.774`},{81.94199999999998`,198.98299999999998`},{123.81399999999998`,177.26999999999998`},{147.66199999999998`,164.85099999999997`},{175.21499999999997`,149.956`},{183.68899999999996`,141.26899999999998`}}

g3=Graphics[{BezierCurve[pts,SplineDegree->3],Green,Line[pts],Red,Point[pts]},ImageSize->Small]

pts=({#[[1]],-#[[2]]})&/@{{183.68899999999996`,101.58099999999997`},{182.63299999999995`,91.02799999999998`},{182.09399999999997`,74.87099999999998`},{176.27199999999996`,66.79099999999997`},{170.44999999999996`,58.76199999999997`},{159.84299999999996`,53.15699999999997`},{144.46899999999997`,53.15699999999997`},{133.88199999999998`,53.15699999999997`},{117.45299999999997`,58.105999999999966`},{110.03599999999997`,66.18499999999997`},{102.61899999999997`,74.26399999999998`},{107.38399999999997`,83.55499999999998`},{107.38399999999997`,94.71299999999997`},{107.38399999999997`,116.42599999999996`},{101.02399999999997`,130.10899999999998`},{79.31099999999998`,130.10899999999998`},{70.81599999999997`,130.10899999999998`},{66.59099999999998`,124.50499999999998`},{66.59099999999998`,110.26599999999998`},{66.59099999999998`,90.37099999999998`},{81.42499999999998`,66.79099999999997`},{103.15799999999999`,51.894999999999975`},{125.40799999999999`,36.39299999999997`},{143.951`,30.181999999999974`},{167.79899999999998`,30.181999999999974`},{192.70099999999996`,30.181999999999974`},{202.76999999999998`,38.86899999999997`},{210.18699999999998`,46.946999999999974`},{221.31199999999998`,59.973999999999975`},{221.85099999999997`,74.26399999999998`},{221.85099999999997`,91.02799999999998`}}

g4=Graphics[{BezierCurve[pts,SplineDegree->3],Green,Line[pts],Red,Point[pts]},ImageSize->Small]

pts=({#[[1]],#[[2]]})&/@{{183.171`,159.905`},{164.09`,176.617`},{112.14899999999999`,189.036`},{112.14899999999999`,240.541`},{112.14899999999999`,263.516`},{123.814`,274.673`},{139.70499999999998`,274.673`},{155.077`,274.673`},{183.17199999999997`,252.963`},{183.17199999999997`,236.198`}}

g5=Graphics[{Blue,BezierCurve[pts,SplineDegree->3],Green,Line[pts],Red,Point[pts]},ImageSize->Small]

(* Generation of Control Points *)
Partition[pts,4,3]

(* Bernstein Basis *)
Table[BernsteinBasis[3,i,t],{i,0,3}]

(* Generation of parametric splines *)
{r1,r2,r3}=Map[Transpose[#].Table[BernsteinBasis[3,i,t],{i,0,3}]&,Partition[pts,4,3]]

(* Parameter Plots, incl. reflected y coordinate *)
ParametricPlot[Evaluate[{{1,0},{0,-1}}.#&/@{r1,r2,r3}],{t,0,1}]

(* Smoothness Checking C1 *)
{D[r1,t]/.{t->1},D[r2,t]/.{t->0}}
{D[r2,t]/.{t->1},D[r3,t]/.{t->0}}

(* Smoothness Checking C2 *)
{D[r1,{t,2}]/.{t->1},D[r2,{t,2}]/.{t->0}}
{D[r2,{t,2}]/.{t->1},D[r3,{t,2}]/.{t->0}}

The composite Bézier spline

With Show we can combine all partial cubic splines of the previous sections into one graphics. There are five missing straight lines which are not realized as Bézier splines.

gall=Show[{g1,g2,g3,g4,g5}]

Example: Glyph Font Design Helvetica S

The .svg encoding

<?xml version=”1.0” encoding=”utf-8”?>
  <!--Generator : Adobe Illustrator 15.0 .2, SVG Export Plug - In.SVG Version : 6.00 Build 0)-->
  <!DOCTYPE svg PUBLIC “-//W3C//DTD SVG 1.1//EN” “http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd” >
    <svg version = “1.1” id = “Layer_1” xmlns = “http://www.w3.org/2000/svg” xmlns:xlink = “http://www.w3.org/1999/xlink” x = “0px” y = “0px”
width = “106.703px” height = “35.856px” viewBox = “1.776 6.048 106.703 35.856” enable-background = “new 1.776 6.048 106.703 35.856”
xml:space = “preserve” >
   <g >
   <path d = “
   M 20.904,11.544
   
   c -1.52 -1.104 -3.464 -1.655 -5.832 -1.655
   c -0.96,0 -1.903,0.096 -2.831,0.287
   c -0.93,0.192 -1.753,0.513 -2.473,0.96
   
   c -0.72,0.449 -1.296,1.049 -1.728,1.801
   c -0.433,0.752 -0.648,1.688 -0.648,2.808
   c 0,1.056, 0.312,1.913, 0.937,2.568
   
   s 1.456,1.192, 2.496,1.607
   
   c 1.038,0.416, 2.215,0.761, 3.527,1.032
   
   C 15.664,21.224, 17,21.52, 18.36,21.84
   
   c 1.36,0.32, 2.696,0.696, 4.009,1.128
   c 1.312,0.433, 2.487,1.017, 3.527,1.753
   c 1.04,0.735, 1.872,1.663, 2.496,2.783
    		
   s 0.937,2.528, 0.937,4.225 
   c 0,1.823 -0.408,3.384 -1.225,4.68
   c -0.815,1.296 -1.864,2.352 -3.144,3.168
   
   c -1.28,0.816 -2.712,1.409 -4.296,1.776
   c -1.584,0.368 -3.161,0.552 -4.729,0.552
   c -1.92,0 -3.735-0.24 -5.448-0.72
    		
   C 8.776,40.704, 7.272,39.976, 5.976,39
   
   c -1.296-0.976 -2.321-2.224 -3.072-3.744
   c -0.752-1.52 -1.128-3.319 -1.128-5.399
   
   h 4.32
   
   c 0,1.439,0.28, 2.68,0.84, 3.72
   c 0.561,1.041, 1.296,1.896, 2.208,2.568
   
   s 1.977,1.168, 3.192,1.487 
   c 1.216,0.32, 2.464,0.48, 3.744,0.48
   
   c 1.022,0,2.056 -0.096,3.096 -0.288
   
   s 1.976-0.52, 2.808-0.984
   
   c 0.832-0.463, 1.504-1.096, 2.017-1.896
   c 0.512-0.8, 0.768-1.824, 0.768-3.072
   c 0-1.183 -0.312-2.144 -0.936-2.88
   
   s -1.457-1.336 -2.496-1.8
   
   c -1.04-0.464 -2.216-0.84 -3.528-1.128
   c -1.312-0.288 -2.647-0.583 -4.008-0.889
   c -1.36-0.304 -2.696-0.655 -4.008-1.056
   c -1.312-0.398 -2.488-0.928 -3.528-1.584
   c -1.041-0.655 -1.872-1.503 -2.496-2.544
   c -0.624-1.04 -0.936-2.344 -0.936-3.912
   c 0-1.728,0.352 -3.223,1.056 -4.488
   c 0.703-1.264, 1.639-2.304, 2.808-3.119
   c 1.169-0.816, 2.496-1.424, 3.984-1.824
   
   s 3.016-0.601, 4.584-0.601
   
   c 1.759,0, 3.391,0.208, 4.896,0.624
   c 1.503,0.416, 2.823,1.072, 3.96,1.969
   c 1.135,0.896, 2.031,2.023, 2.688,3.384
   c 0.655,1.359, 1.017,2.983, 1.08,4.872
   
   h -4.32
   C 23.312,14.432, 22.423,12.648, 20.904,11.544
   
   z”/>
   </g >
   </svg >

Computing the cubic Bézier splines → {BezierFunction, BezierCurve}

The first  6 relative cubic splines c: Graphics g1

The first lines in the definition attribute of the path-element are:

M 20.904,11.544
   
   c -1.52 -1.104 -3.464 -1.655 -5.832 -1.655
   c -0.96,0 -1.903,0.096 -2.831,0.287
   c -0.93,0.192 -1.753,0.513 -2.473,0.96
   
   c -0.72,0.449 -1.296,1.049 -1.728,1.801
   c -0.433,0.752 -0.648,1.688 -0.648,2.808
   c 0,1.056, 0.312,1.913, 0.937,2.568

We move (M) to the starting point {..., ...} and subsequently add the offsets of the first cubic Bézier spline (c). The last point of the first spline is the first point of the second spline to which the corresponding offsets have to be added. Notice that the first point of the spline is not in the list of the c-command. This yields the first 10 points. The y-axis is inverted since the y-axis in .svg coordinates is the other way round compared to Mathematica:

pts=({#[[1]],-#[[2]]})&/@
Join[
(({20.904,11.544}+#)&/@
{{0,0},{-1.52 ,-1.104},{-3.464 ,-1.655},{-5.832 ,-1.655}}),

(({15.072`,9.889000000000001`}+#)&/@
{{0,0},{-0.96,0},{-1.903,0.096},{-2.831,0.287}}),

(({12.241`,10.176000000000002`}+#)&/@
{{0,0},{-0.93,0.192},{-1.753,0.513},{-2.473,0.96}}),

(({9.768`,11.136000000000003`}+#)&/@
{{0,0},{-0.72,0.449}, {-1.296,1.049},{ -1.728,1.801}}),

(({8.040000000000001`,12.937000000000003`}+#)&/@
{{0,0},{-0.433,0.752},{ -0.648,1.688 },{-0.648,2.808}}),

(({7.392000000000001`,15.745000000000003`}+#)&/@
{{0,0},{0,1.056}, {0.312,1.913},{ 0.937,2.568}})




]

g1=Graphics[{BezierCurve[Partition[Take[pts,8],4],SplineDegree->3],Green,Line[Take[pts,8]], Red,Point[Take[pts,8]]},ImageSize->Small]

The first 1 relative quadratic spline s: Graphics g2

s 1.456,1.192, 2.496,1.607

We move (M) to the starting point {..., ...} and subsequently add the offsets of the first cubic Bézier spline (c). The last point of the first spline is the first point of the second spline to which the corresponding offsets have to be added. Notice that the first point of the spline is not in the list of the c-command. This yields the first 10 points. The y-axis is inverted since the y-axis in .svg coordinates is the other way round compared to Mathematica:

pts=({#[[1]],-#[[2]]})&/@
Join[
(({8.329`,18.313000000000002`}+#)&/@
{{0,0},{1.456,1.192}, {2.496,1.607}})
]

g2=Graphics[{BezierCurve[Partition[pts,3],SplineDegree->2],Green,Line[pts], Red,Point[pts]},ImageSize->Small]

Show[g1,g2, AspectRatio->1]

The next single  1 relative cubic spline c: Graphics g3

The first 4 lines in the definition attribute of the path-element are:

c 1.038,0.416, 2.215,0.761, 3.527,1.032

We move (M) to the starting point {..., ...} and subsequently add the offsets of the first cubic Bézier spline (c). The last point of the first spline is the first point of the second spline to which the corresponding offsets have to be added. Notice that the first point of the spline is not in the list of the c-command. This yields the first 10 points. The y-axis is inverted since the y-axis in .svg coordinates is the other way round compared to Mathematica:

pts=({#[[1]],-#[[2]]})&/@
Join[
(({10.825000000000001`,19.92`}+#)&/@
{{0,0},{1.038,0.416}, {2.215,0.761}, {3.527,1.032}})
]

g3=Graphics[{BezierCurve[Partition[pts,4],SplineDegree->3],Green,Line[pts], Red,Point[pts]},ImageSize->Small]

Show[g1,g2, g3, AspectRatio->1]

The next single 1  absolute cubic spline c: Graphics g4

The first 4 lines in the definition attribute of the path-element are:

C 15.664,21.224, 17,21.52, 18.36,21.84

We move (M) to the starting point {..., ...} and subsequently add the offsets of the first cubic Bézier spline (c). The last point of the first spline is the first point of the second spline to which the corresponding offsets have to be added. Notice that the first point of the spline is not in the list of the c-command. This yields the first 10 points. The y-axis is inverted since the y-axis in .svg coordinates is the other way round compared to Mathematica:

pts=({#[[1]],-#[[2]]})&/@
Join[
{{14.352`,20.952`}, {15.664,21.224}, {17,21.52}, {18.36,21.84}}
]

g4=Graphics[{BezierCurve[Partition[pts,4],SplineDegree->3],Green,Line[pts], Red,Point[pts]},ImageSize->Small]

Show[g1,g2, g3,g4, AspectRatio->1]

The next 3 relative cubic splines : Graphics g5

The first 4 lines in the definition attribute of the path-element are:

c 1.36,0.32, 2.696,0.696, 4.009,1.128
  c 1.312,0.433, 2.487,1.017, 3.527,1.753
  c 1.04,0.735, 1.872,1.663, 2.496,2.783

We move (M) to the starting point {..., ...} and subsequently add the offsets of the first cubic Bézier spline (c). The last point of the first spline is the first point of the second spline to which the corresponding offsets have to be added. Notice that the first point of the spline is not in the list of the c-command. This yields the first 10 points. The y-axis is inverted since the y-axis in .svg coordinates is the other way round compared to Mathematica:

pts=({#[[1]],-#[[2]]})&/@
Join[
(({18.36`,21.84`}+#)&/@
{{0,0},{1.36,0.32}, {2.696,0.696}, {4.009,1.128}}),

(({22.369`,22.968`}+#)&/@
{{0,0},{ 1.312,0.433}, {2.487,1.017}, {3.527,1.753}}),

(({25.896`,24.721`}+#)&/@
{{0,0},{1.04,0.735}, {1.872,1.663}, {2.496,2.783}})


]

g5=Graphics[{BezierCurve[Partition[pts,4],SplineDegree->3],Green,Line[pts], Red,Point[pts]},ImageSize->Small]

Show[g1,g2, g3,g4,g5, AspectRatio->1]

The composite Bézier spline

With Show we can combine all partial cubic splines of the previous sections into one graphics. There are five missing straight lines which are not realized as Bézier splines.

gall=Show[{g1,g2,g3,g4,g5}]