# GAMBAS BOOK 3.19.1

## 2D-Graphic

### Chapters

08.10.2022
k25:k25.1.3:start

## 25.1.3 Cairo - Methods

The class Cairo (gb.cairo) only has (static) methods. These methods are documented in groups in the tables below. After the tables you will find notes on the use of selected methods.

## 25.1.3.1 Selected methods

The Cairo class has these methods:

MethodDescription
Begin ( Device As Object )Starts drawing on the specified device (drawing surfaces: Image, CairoPdfSurface, CairoPsSurface and CairoSvgSurface). The call can be nested.
End ( )Ends the drawing. You must call this method just as often as the Cairo.Begin(..) method → Finish method of the Surface class.
NewPath ( )Deletes the current path. After the call, neither a current (start) point nor a path exists.
NewSubPath ( ) Starts a new sub-path. Please note that the existing, current path is not affected. No current point exists after this call. In many cases, this call will not be necessary, as new (sub)paths are often started with Cairo.MoveTo(..). A call to Cairo.NewSubPath is particularly useful if a new sub-path is created at the beginning with Cairo.Arc or Cairo.ArcNegative. This makes things easier as it is no longer necessary to manually calculate initial coordinates of the arc for a call.
ClosePath ( )Adds a line segment to the current path, from the current point to the start point of the current sub-path. This is the point that was last passed to Cairo.MoveTo(). This closes the current path and the current point becomes the common start and end point of the path.
Scale ( SX As Float, SY As Float )Changes the current transformation matrix (CTM) by scaling the x and y user space axes with the factors SX and SY. SX: Scaling factor for the x-direction, SY: Scaling factor for the y-direction
Translate ( TX As Float, TY As Float )Changes the current transformation matrix by shifting the coordinate origin by TX and TY. TX: Displacement width in x-direction, TY: Displacement width in y-direction
Rotate ( Angle As Float )Changes the current transformation matrix by rotating the coordinate axes for a given angle of rotation Angle (radian measure).
Clip ( [ Preserve As Boolean ] )Creates a new clip region by intersecting the current clip region with the current path - taking into account the current fill rules (FillRule). After Clip() is applied, the current path is deleted unless the Preserve argument is TRUE. The current clip region affects all drawing operations, ignoring any changes outside this region. A call to Clip() can only make the clip region smaller, never larger. The current clip is a property of the Cairo state. You can achieve a temporary clip restriction by clipping within a Cairo.Save()/Cairo.Restore() pair. Only Cairo.ResetClip() enlarges the clip region again.
ResetClip ( )Resets the current clip region to its original, unrestricted state.
Save ( )Creates a copy of the current Cairo state and saves it in an (internal) stack of saved states. When Restore() is called, the drawing state is restored from the saved state. Several calls for saving and restoring can be nested.
Restore ( )Restores the Cairo state by retrieving the saved state. The saved state is then deleted from the stack of saved states.
CopyPage ( )Emits the current page for backends - such as a printer - that support multiple pages. The content of the current page is retained and copied to the next page.
ShowPage ( )Issues the current page and then clears the following page for backends that support multiple pages. Use the → Cairo.CopyPage() method if the emitted page content is to be copied to the subsequent page.

## 25.1.3.2 Methods - Colour and pattern

MethodDescription
ColorPattern ( Color As Integer ) As CairoPatternCreates a new brush corresponding to a translucent colour. The colour 'Color' is defined as for all GUI components → Chapter 25.3.5 Working with colours.
Function SolidPattern ( Red As Float, Green As Float, Blue As Float [ , Alpha As Float ] ) As CairoPatternGenerates a new colour pattern. The RGB colour components and the alpha channel are numbers of type Float in the range from 0.0 to 1.0.
ImagePattern ( Image As Image [ , X As Float, Y As Float, Extend As Integer, Filter As Integer ] ) As CairoPatternCreates a new pattern from an image. Image is the image object and X and Y are the (optional) values (data type float) from the pattern matrix and specify the initial translation of the image. See also → Extend and Filter.
LinearGradient ( X0 As Float, Y0 As Float, X1 As Float, Y1 As Float, Colors As Float [ ][ ] ) As CairoPattern Creates a new linear gradient brush along the line defined by (X0|Y0) and (X1|Y1) and defines colour stops from the colour and position arguments in the multidimensional Colors array.
RadialGradient ( CX0 As Float, CY0 As Float, Radius0 As Float, CX1 As Float, CY1 As Float, Radius1 As Float, Colors As Float [ ][ ] ) As CairoPatternCreates a new radial gradient brush, where the colours are interpolated between a focal point (FX|FY) and the end point on a circular area defined by (CX0|CY0, Radius0) and defines colour stops from the colour and position arguments in the multidimensional Colors array.

## 25.1.3.3 Methods - Lines, areas and selected operators

MethodDescription
MoveTo ( X As Float, Y As Float )Starts a new (sub)path. After this call, the current point has the coordinates P(X|Y).
RelMoveTo(DX, DY)Starts a new (sub)path. After this call, the current point P' has an offset of DX and DY from the starting point P. With a starting point P(x|y), the current point has the coordinates P'(x+DX | y+DY) after using RelMoveTo(DX,DY).
LineTo ( X As Float, Y As Float )Adds a line (straight line segment) from the existing start point P(x0|y0) to the point P'(X|Y) in user space coordinates to the path. If no start point exists, the MoveTo method must be called first!
RelLineTo ( DX As Float, DY As Float )Adds a distance (straight line segment) from the start point P(x0|y0) to the current point P'(x0+DX | y0+DY) in user space coordinates to the path.
CurveTo ( X1 As Float, Y1 As Float, X2 As Float, Y2 As Float, X3 As Float, Y3 As Float )Adds a cubic Bezier curve (spline) from the current position P0(X0|Y0) to the position (X3|Y3) in user space coordinates to the path, where (X1|Y1), (X2|Y2) and (X3|Y3) are support points. After this call, (X3|Y3) is the current point.
RelCurveTo ( X1 As Float, Y1 As Float, X2 As Float, Y2 As Float, X3 As Float, Y3 As Float )Adds - just like CurveTo() - a cubic Bezier curve. Here, however, all arguments are taken as offsets relative to the current point.
Rectangle ( X0 As Float, Y0 As Float, Width As Float, Height As Float )Adds a closed sub-path to the existing path in user space coordinates for the rectangle of the specified size. If there is no starting point S(X0|Y0), the MoveTo method must be called first.
Arc ( XM As Float, YM As Float, Radius As Float [ , Angle1 As Float, Angle2 As Float ] )Adds an arc with the given radius to the path. The arc is centred at (XM, YM) and begins with the (start) angle Angle1 and rotates the centre angle (in the original Cairo coordinate system) in the direction of increasing angles until the (end) angle Angle2 is reached. If a current point exists, a line segment is added to the path to connect the current point to the start of the arc. If this line is undesirable, it can be avoided by calling NewSubPath() before calling Arc. The reference axis for 0 degrees is the positive x-axis. All angles must be specified in radians. Use the wheel function to convert a degree measurement to radians.
ArcNegative ( XC As Float, YC As Float, Radius As Float [ , Angle1 As Float, Angle2 As Float ] )Difference to the Arc() documentation: It is run from Angle1 to Angle2 (after any shift of Angle2 into the period of Angle1) in the direction of descending angles.
Stroke ( [ Preserve As Boolean ] )The (line) path is traced with the current line definition (stroke width, stroke shape and line end shape). The path is deleted after Cairo.Stroke() - unless the Preserve argument is set to the value TRUE.
InStroke ( X As Float, Y As Float ) As BooleanChecks whether the specified point P(X|Y) lies within the region that would be affected by a Cairo.Stroke operation with regard to path and parameters when drawing. Drawing area dimensions and clipping are not taken into account.
Fill ( [ Preserve As Boolean ] )Fills the area limited by the current path using the current area definition (→ FillRule). Each partial path is automatically closed beforehand. After Cairo.Fill(…) the path is deleted - but only if the optional Preserve argument is not set to TRUE.
InFill ( X As Float, Y As Float ) As BooleanChecks whether the specified point P(X|Y) lies within the region that would be affected by a Cairo.Fill operation with regard to path and fill parameters. Drawing area dimensions and clipping are not taken into account.
Mask ( Pattern As CairoPattern )A drawing operator that paints with the alpha channel of Pattern as a mask. Opaque pattern areas are drawn with the brush values, while transparent areas are not.
Paint ( [ Alpha As Float ] )Drawing operator that paints with the current path values within the current clip region. If the (optional) alpha value is specified, a constant alpha mask is used. The result of the drawing is displayed faded using the alpha value.

## 25.1.3.4 Methods - Text and images

MethodDescription
Text ( Text As String [ , X As Float, Y As Float, Width As Float, Height As Float, Alignment As Integer ] )Adds the specified text to the path. The optional arguments define a (text) box in which the text is drawn. The current font is used, which is previously set via the font property. The default font for Cairo is “Sans Serif”.
TextExtents ( Text As String ) As CairoExtentsDetermines the extents (data type CairoExtents) for the text string. The extents describe a user-space rectangle that encloses the actually drawn text as it would have been created by Cairo.Text and Cairo.Fill without a transformation matrix.
DrawText ( Text As String [ , X As Float, Y As Float, Width As Float, Height As Float, Alignment As Integer ] )Draws the specified text. If you specify the optional parameters, the text is limited by the specified rectangle and aligned according to the alignment parameter. This method is faster than drawing the text with Cairo.Text and then Cairo.Fill.

## 25.1.3.5 Notes

The following notes supplement the contents of the above tables. For selected methods, an example is presented with the source code used. All examples can be found in a project in the download area.

## Circle and circle parts

Figure 25.1.3.5.1: Circular arc - circle sectors

Source code:

```Public Sub CairoScriptArcs()
Dim fAngle1, fAngle2 As Float

GenerateNewImage()
SetImageBorder()

Cairo.Begin(hImage)
Cairo.Translate(xTranslate, yTranslate)
Cairo.Scale(xScale, yScale) ' +y ▲
DrawCoordinateSystem()
Cairo.LineWidth = 1
Cairo.Source = Cairo.ColorPattern(Color.Red)
'-- Circular arc 270° - open
Cairo.NewPath()
fAngle2 = fAngle1 + 3 * Pi / 2 '-- End
Cairo.Arc(100, 140, 70, fAngle1, fAngle2)
Cairo.Stroke()
'-- Circular arc 270° - closed
fAngle1 = Pi / 4
Cairo.MoveTo(260, 140)
Cairo.Arc(260, 140, 70, fAngle1, fAngle2)
Cairo.LineTo(260, 140)
Cairo.Stroke()
'-- Circular sector 270° - filled
fAngle1 = Pi / 4
fAngle2 = fAngle1 + 3 * Pi / 2
Cairo.MoveTo(420, 140)
Cairo.Arc(420, 140, 70, fAngle1, fAngle2)
Cairo.LineTo(420, 140)
Cairo.Fill()
'-- Dotted arc line for the 90° sector
Cairo.Dash = [2, 2]
fAngle1 = Pi / 4 + 3 * Pi / 2
Cairo.Arc(420, 140, 70, fAngle1, fAngle2)
Cairo.Stroke()
Cairo.Dash = []  '-- Alternative: Cairo.Dash = Null

Cairo.End

End```

## Ellipse

In this example, an ellipse is drawn that is inclined by 30° and whose narrow edge is blue. The surface has the colour red.

Source code snippet:

```  Cairo.Save()
Cairo.Translate(400, 160)
Cairo.Rotate(Pi() / 6)
Cairo.Scale(0.5, 1.0)
Cairo.Arc(0, 0, 130, 0, Pi(2))
Cairo.Source = Cairo.ColorPattern(Color.Blue)
Cairo.LineWidth = 5
Cairo.Stroke(True) '-- Border
Cairo.Source = Cairo.ColorPattern(Color.Red)
Cairo.Fill() '-- Area
Cairo.Restore()```

## Fill pattern

Figure 25.1.3.5.2: Fill pattern

Source code:

```Public Sub CairoScriptPattern()
Dim hPattern As Image

GenerateNewImage()
SetImageBorder()

Cairo.Begin(hImage)
Cairo.Translate(xTranslate, yTranslate)
Cairo.Scale(xScale, yScale) ' +y ▲
DrawCoordinateSystem()
Cairo.AntiAlias = Cairo.AntiAliasDefault

Cairo.Source = Cairo.ImagePattern(hPattern, 30, 160, 1)
Cairo.Arc(100, 210, 63)
Cairo.Fill

'-- Ellipse inclined by 30° with special filling pattern
Cairo.Source = Cairo.ImagePattern(hPattern, 0, 0, 1)
Cairo.Save()
Cairo.Translate(400, 160)
Cairo.Rotate(Pi / 6)
Cairo.Scale(0.55, 1.0)
Cairo.Arc(0, 0, 140, 0, Pi(2))
Cairo.Fill()
Cairo.Restore()

Cairo.LineWidth = 1
Cairo.Source = Cairo.ColorPattern(Color.Blue)
Cairo.Rectangle(40, 20, 240, 110)
Cairo.Stroke(True)
Cairo.Source = Cairo.ImagePattern(hPattern, 0, 0, 1)
Cairo.Rectangle(40, 20, 240, 110)
Cairo.Fill()
Cairo.End
End```

Note the two methods Cairo.Save and Cairo.Restore for drawing the ellipse with pattern. If you had set this source code part last, you could do without the two methods, because then the script for drawing ends and the methods Cairo.Rotate and Cairo.Scale have no effect on the further drawing!

Figure 25.1.3.5.3: Two circles and 30 ellipses

Source code:

```Public Sub CairoScriptEllipses()

Dim i, iLoops As Integer

GenerateNewImage()
SetImageBorder()
Cairo.Begin(hImage)
Cairo.Translate(daCanvas.W / 2, daCanvas.H / 2)
Cairo.Scale(xScale, yScale) '-- +y ▲
Cairo.AntiAlias = 0
Cairo.LineWidth = 1
Cairo.Source = Cairo.ColorPattern(Color.DarkBlue)
Cairo.Arc(0, 0, 160) '-- Circle 1
Cairo.Stroke()
Cairo.Arc(0, 0, 170) '-- Circle 2
Cairo.Stroke()
Cairo.LineWidth = 1.5 * 1
iLoops = 2 * 12
For i = 1 To iLoops
Cairo.Save()
Cairo.Rotate(i * Pi / iLoops)
Cairo.Scale(0.25, 1)
Cairo.Arc(0, 0, 150, 0, Pi(2))
Cairo.Restore()
Cairo.Stroke()
Next
Cairo.End()
End```

## Polygons - polygons

Figure 25.1.3.5.4: Convex polygon - triangle

Source code:

```Public Sub CairoScriptTriangle()
GenerateNewImage()
SetImageBorder()
Cairo.Begin(hImage)
Cairo.Translate(xTranslate, yTranslate)
Cairo.Scale(xScale, yScale) ' +y ▲
Cairo.AntiAlias = False
DrawCoordinateSystem()
'-- Polygon as convex triangle
Cairo.Source = Cairo.ColorPattern(Color.Red)
Cairo.MoveTo(60, 100)
Cairo.LineTo(460, 270)
Cairo.LineTo(270, 30)
'-- You do not have to go back to the starting point because the end point is automatically the starting point.
Print Cairo.InFill(210, 210) ' → False
Print Cairo.InFill(300, 160) ' → True
Cairo.Fill()
Cairo.End
End```

You can use the two print statements to determine whether two specified points lie in the area.

Figure 25.1.3.5.5: Non-convex polygon

Source code:

```Public Sub CairoScriptPolygon()
GenerateNewImage()
SetImageBorder()
Cairo.Begin(hImage)
Cairo.Translate(xTranslate, yTranslate)
Cairo.Scale(xScale, yScale) ' +y ▲
Cairo.AntiAlias = False
DrawCoordinateSystem()
'-- Polygon as non-convex polygon
Cairo.Source = Cairo.ColorPattern(Color.Red)
Cairo.MoveTo(60, 100)
Cairo.LineTo(460, 270)
Cairo.LineTo(270, 30)
Cairo.LineTo(90, 180)
'-- You do not have to go back to the starting point because the end point is automatically the starting point.
Print Cairo.InFill(120, 90)
Print Cairo.InFill(300, 150)
Cairo.Fill()
Cairo.End()
End```

## Diagrams - circle, circle parts, rectangles and text

Figure 25.1.3.5.6: Circle diagram

Source text circle diagram:

```Public Sub CairoScriptChart()

Dim i As Integer
Dim fSumme As Float = 0, fAngle As Float
Dim textDimension As RectF

fMx = 170
fMy = 140

'-- Inline array with the values to be displayed
aData = [2.1, 4.2, 2.6, 5.2, 3.1, 2.8, 5.2, 3.3, 4.5, 5.1, 1.5, 3.2]
For i = 0 To aData.Max
Next

'-- aAngle => Array for the data angle equivalents (in radians)

For i = 0 To aData.Max
'-- Conversion 'value' into its relative(!) 'angle equivalent' with 360°≡1
aAngle[i] = (aData[i] / fTotal) * Pi(2)
Next

GenerateNewImage()
SetImageBorder()

Cairo.Begin(hImage)
Cairo.Translate(xTranslate, yTranslate)
Cairo.Scale(xScale, yScale) ' +y ▲

fStartAngle = 0
'-- Draw circle sectors - Quantity: aData.Max
For i = 0 To aData.Max
Cairo.Source = Cairo.ColorPattern(Color.RGB(Rnd(0, 255), Rnd(0, 255), Rnd(0, 255)))
Cairo.MoveTo(fMx, fMy)
Cairo.Arc(fMx, fMy, fRadius, fStartAngle, fStartAngle + aAngle[i]) '-- Piece of cake
Cairo.LineTo(fMx, fMy)
Cairo.Fill()
fStartAngle = fStartAngle + aAngle[i] '-- New start angle
Next

'-- Draw value to the sector - Quantity: aData.Max
For i = 0 To aData.Max
fStartAngle = fSumme + aAngle[i] / 2
fSumme = fSumme + aAngle[i]

fPx = fPx - (Cairo.TextExtents(aData[i]).Width / 2)
fPy = fPy - (Cairo.TextExtents(aData[i]).Height / 3)

Cairo.Scale(1, -1) ' +y ▼
Cairo.Source = Cairo.ColorPattern(Color.Black)
Cairo.Font.Name = "Monospace"
Cairo.Font.Size = 14
Cairo.MoveTo(fPx, - fPy)
Cairo.Scale(1, -1) ' +y ▲
Next

'-- 2nd image with highlighted sector
Cairo.Source = Cairo.ColorPattern(Color.RGB(Rnd(0, 255), Rnd(0, 255), Rnd(0, 255)))
Cairo.MoveTo(440, fMy)
fAngle = Pi / 4
Cairo.Arc(440, fMy, 70, fAngle, fAngle + 3 * Pi / 2)
Cairo.LineTo(440, fMy)
Cairo.Fill()
Cairo.Source = Cairo.ColorPattern(Color.Green)
Cairo.MoveTo(440 + 7, fMy)
fAngle = - Pi(0.25)
Cairo.Arc(440 + 7, fMy, 70, fAngle, fAngle + Rad(90))
Cairo.LineTo(440 + 7, fMy)
Cairo.Fill()

Cairo.End()

End```

Figure 25.1.3.5.7: Bar chart

Source text column chart:

```Public Sub CairoScriptBarChart()

Dim i As Integer
Dim fDeltaX, fDeltaY, fBeginX, fEndX, fEndY, fMaxValue As Float
Dim sCaption As String

'-- Inline array with the values to be displayed
aData = [2.2, 4, 2.4, 5.4, 3, 5.0, 5.7, 3.6, 4.5, 2.0, 1.6, 3.2]
aDataC.Sort(gb.Descent) '-- Descending sorting of the elements
fMaxValue = aDatac[0]   '-- The 1st element is now the largest value in the array
fBeginX = 10 '-- Definition for the abscissa drawing area
fEndX = 540
fEndY = 240  '-- Definition for the ordinate drawing area
fDeltaX = Round((fEndX - fBeginX) / aData.Count, 0) '-- Standardised strip width (unit)
fDeltaY = Round(fEndY / fMaxValue, 0) '-- Standardised strip height (unit)

GenerateNewImage()
SetImageBorder()
Cairo.Begin(hImage)
Cairo.Translate(xTranslate, yTranslate)
Cairo.Scale(xScale, yScale) ' +y ▲
Draw_X_Axis()
SetOrigin()
Draw_Y_Axis()
Draw_Y_AxisArrow()

For i = 0 To aData.Max
'-- Calculation and display of all stripes (rectangles) in the diagram with random stripe color and value
Cairo.Rectangle(fBeginX + i * fDeltaX, 0, fDeltaX, fDeltaY * aData[i])
Cairo.Source = Cairo.ColorPattern(Color.RGB(Rnd(0, 255), Rnd(0, 255), Rnd(0, 255)))
Cairo.Fill()
Cairo.Scale(1, -1) ' +y ▼
Cairo.Source = Cairo.ColorPattern(Color.Black)
Cairo.Font.Name = "Arial"
Cairo.Font.Size = 14
Cairo.MoveTo(fBeginX + 0.25 * fDeltaX + i * fDeltaX, - (fDeltaY * aData[i] + 10))
Cairo.Font.Name = "Arial"
Cairo.Font.Size = 12
Cairo.MoveTo(fBeginX + 0.33 * fDeltaX + i * fDeltaX, 22)
Cairo.DrawText(Str(i + 1))
Cairo.Scale(1, -1) ' +y ▲
Next

'-- Label the ordinate
Cairo.Scale(1, -1) ' +y ▼
sCaption = ("Value")
If Cairo.TextExtents(sCaption).Width / 2 > xTranslate Then
Cairo.MoveTo(0 - xTranslate / 2, -283)
Cairo.DrawText(sCaption)
Else
Cairo.MoveTo(0 - Cairo.TextExtents(sCaption).Width / 2, -283)
Cairo.DrawText(sCaption)
Endif
Cairo.Font.Name = "Monospace"
Cairo.Font.Size = 10
Cairo.Scale(1, -1) ' +y ▲
Cairo.End

End```

For the use of the two methods Cairo.LinarGradient and Cairo.RadialGradient, a function by Tobias Boege is used to define the colour gradients and ColorStops:

```Private Function BuildColorStops(aColorsG As Integer[], Optional aPositionsG As Float[]) As Float[][]
Dim iIndex As Integer, fPosition As Float
Dim aColors As New Float[][]

For iIndex = 0 To aColorsG.Max
With Color[aColorsG[iIndex]]
If Not aPositionsG Then
fPosition = iIndex / aColorsG.Count '-- Uniformly distributed
Else
fPosition = aPositionsG[iIndex]
Endif
End With
Next
Return aColors
End```

```Public Sub CairoScriptLinearGradient()
Dim aColorsLG As Integer[] = [Color.Red, Color.Yellow, Color.Blue]
Dim aColors As Float[][]

aColors = BuildColorStops(aColorsLG, Null)
GenerateNewImage()
SetImageBorder()

Cairo.Begin(hImage)
Cairo.Translate(xTranslate, yTranslate)
Cairo.Scale(xScale, yScale) ' +y ▲
DrawCoordinateSystem()
Cairo.MoveTo(50, 40)
Cairo.Source = Cairo.LinearGradient(50, 40, 450, 200, aColors)
Cairo.Rectangle(50, 40, 450, 200)
Cairo.Fill()
Cairo.End()
End```

```Public Sub CairoScriptRadialGradient()
Dim aColorsRG As Float[][]

aColorsRG = BuildColorStops([Color.Blue, Color.White], [1, 0.05])
GenerateNewImage()
SetImageBorder()

Cairo.Begin(hImage)

Cairo.Translate(xTranslate, yTranslate)
Cairo.Scale(xScale, yScale) ' +y ▲
DrawCoordinateSystem()
'-- Illuminated or mirror point top left
Cairo.AntiAlias = True
Cairo.Arc(150, 140, 120)
Cairo.Fill()

'-- Illuminated or mirrored point in the center of the object
Cairo.Arc(410, 140, 120)
Cairo.Fill()
Cairo.AntiAlias = False

Cairo.End()

End```

## Clip and text

The following example demonstrates the use of the Cairo.Clip method in conjunction with the two methods Cairo.Save and Cairo.Restore, which must necessarily be used because text is also to be inserted in several lines.

The result with an image in the clip area and matching text is appealing:

Figure 25.1.3.5.10: Image in the clip area and text

Source code:

```Public Sub CairoScriptImageClip()
Dim imgImage As Image
Dim fMx, fMy, fRadius, fYOffset As Float
Dim i, X0, Y0 As Integer
Dim aText As New String[]

aText.Add("Wo hast du die Punkte her?")
aText.Add("Eins und zwei und drei und vier")

GenerateNewImage()
SetImageBorder()
Cairo.Begin(hImage)
Cairo.Translate(xTranslate, yTranslate)
Cairo.Scale(xScale, yScale) ' +y ▲
fMx = 150
fMy = 150
Cairo.AntiAlias = Cairo.AntiAliasDefault '-- Default = 0

'-- Image - Clip area
Cairo.Save
Cairo.Clip()
imgImage = imgImage.Rotate(Pi(0.5))
imgImage = imgImage.Stretch(imgImage.W / 2, imgImage.H / 2)
Cairo.Source = Cairo.ImagePattern(imgImage, -30, -80)
Cairo.Paint()
Cairo.Restore()

'-- Multiline text - outside the clip area
Cairo.Scale(1, -1) ' +y ▼
Cairo.Source = Cairo.ColorPattern(Color.DarkBlue)
Cairo.Font.Name = "Arial"
Cairo.Font.Size = 18
Cairo.Font.Bold = True

x0 = 305
Y0 = 90
fYOffset = 0
Cairo.MoveTo(X0, - Y0)
Cairo.DrawText(aText[0])
Cairo.MoveTo(X0, - Y0 + 1 * Cairo.Font.Extents.Height + fYOffset)
Cairo.DrawText(aText[1])
Cairo.MoveTo(X0, - Y0 + 2 * Cairo.Font.Extents.Height + fYOffset)
Cairo.DrawText(aText[2])
Cairo.MoveTo(X0, - Y0 + 3 * Cairo.Font.Extents.Height + fYOffset)
Cairo.DrawText(aText[3])
Cairo.MoveTo(X0, - Y0 + 4 * Cairo.Font.Extents.Height + fYOffset)
Cairo.Font.Size = 12
Cairo.DrawText(aText[4])
Cairo.Scale(1, -1)
Cairo.AntiAlias = Cairo.AntiAliasNone ' -> 1
Cairo.End()

End```