# Converting font characters to contours

**URL:** <https://openrndr.discourse.group/t/converting-font-characters-to-contours/576>\
**Category:** Tutorials\
**Created:** [June 18, 2023, 5:07pm UTC](https://openrndr.discourse.group/t/converting-font-characters-to-contours/576 "2023-06-18T17:07:32Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [June 18, 2023, 5:07pm UTC](https://openrndr.discourse.group/t/converting-font-characters-to-contours/576/1 "2023-06-18T17:07:32Z")

</div>

![FontShape-2023-06-18-19.02.21](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/2dc68f4e993e50d425ddbab8bc7d8ffc0e02e7e0.png)

It is possible to convert font characters to shapes and contours. I believe it’s not yet documented, but here a quick example:

> **imports**
>
> ```kotlin
> import org.openrndr.application
> import org.openrndr.color.ColorRGBa
> import org.openrndr.draw.font.loadFace
> import org.openrndr.extensions.Screenshots
> import org.openrndr.shape.LineSegment
> 
> ```

```kotlin
fun main() = application {
    program {
        val face = loadFace("data/fonts/default.otf")
        val shape = face.glyphForCharacter('a').shape(750.0)
        val contours = shape.contours
        val normals = contours.map { c ->
            List(100) {
                val t = it / 99.0
                LineSegment(
                    c.position(t) - c.normal(t) * 2.0,
                    c.position(t) - c.normal(t) * 10.0
                )
            }
        }.flatten()
        extend(Screenshots())
        extend {
            drawer.clear(ColorRGBa.WHITE)
            drawer.translate(drawer.bounds.center - shape.bounds.center)

            drawer.fill = ColorRGBa.BLACK
            drawer.stroke = null
            drawer.shape(shape)

            drawer.stroke = ColorRGBa.BLACK
            drawer.strokeWeight = 2.0
            drawer.lineSegments(normals)
        }
    }
}

```

In this example I get the shape for the character `a`, get the contours out of it, then sample 100 points on each contour getting positions and normals. Note that it is possible to get those points evenly distributed but I can show that later.

It would make me happy to see images of any kind of experiment using this feature 🙂

---

<div class="post-metadata">

**Author:** ![Neurotypique](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/neurotypique/32/585_2.png) [@Neurotypique](https://openrndr.discourse.group/u/Neurotypique)\
**Post date:** [June 18, 2023, 6:37pm UTC](https://openrndr.discourse.group/t/converting-font-characters-to-contours/576/2 "2023-06-18T18:37:00Z")

</div>

Oh wow this is great! I started trying to implement it in my experiment, works great for converting the letter into shape, displaying the shape as well:

 ![Capture d'écran 2023-06-18 202726](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/1fe9a19d0c007a86e4b7b92c2dc8deb4041d7ae6.png)

EDIT:  
I managed to apply it to your example filtering points inside the shape, only it seems that “inside” is just what is inside the eye of the letter:

 ![Capture d'écran 2023-06-18 212148](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/4f33d93d7295c8437ecfedda3d9a71fb146d99ee.png)

```auto
fun main() = application {
    configure {
        width = 768
        height = 576
        windowResizable = true
    }

    program {

        val face = loadFace("data/fonts/default.otf")

            )
        )

        val letter = face.glyphForCharacter('P').shape(1000.0)

        extend {

            val r = Random(0)
            val area = Rectangle(letter.bounds.center.x - drawer.bounds.width / 2.0, letter.bounds.center.y - drawer.bounds.height / 2.0, width * 1.0, height * 1.0)
            val pts = area.scatter(20.0, random = r)

            val insidePoints = pts.filter{it in letter}

            drawer.translate(drawer.bounds.center - letter.bounds.center)

            drawer.fill = ColorRGBa.WHITE
            drawer.shape(letter)

            drawer.fill = ColorRGBa.BLUE
            drawer.stroke = null
            drawer.circles(pts,20.0)
            drawer.fill = ColorRGBa.RED
            drawer.circles(insidePoints, 20.0)

        }
    }
}

```

(I am learning so much trying this stuff thanks again, please keep in mind I’m a code noob so maybe my code includes some unforgiveable sin)

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [June 19, 2023, 1:05pm UTC](https://openrndr.discourse.group/t/converting-font-characters-to-contours/576/3 "2023-06-19T13:05:18Z")

</div>

Nice to see your experiments 🙂

A workaround for only the inner points being detected is this:

```
    val letter2 = Shape(letter.contours.map { it.reversed })
    val insidePoints = pts.filter { it in letter2 }

```

You can print `it.winding` for the contours to see why. It seems like the outer contour has `COUNTER_CLOCKWISE` and the inner one `CLOCKWISE` but it should be the other way around.

 ![image](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/36ca93e89c83985afde2d792272b7811ecd2d92e.png)

Update: a [PR](https://github.com/openrndr/openrndr/pull/373) to address this.

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [June 19, 2023, 3:20pm UTC](https://openrndr.discourse.group/t/converting-font-characters-to-contours/576/4 "2023-06-19T15:20:44Z")

</div>

By the way, maybe something useful: you can apply transformations to shapes:

```kotlin
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.font.loadFace
import org.openrndr.math.transforms.transform

fun main() = application {
    program {
        val face = loadFace("data/fonts/default.otf")
        val letter = face.glyphForCharacter('a').shape(1000.0)

        extend {
            drawer.translate(drawer.bounds.center)
            drawer.fill = ColorRGBa.WHITE
            drawer.shape(letter.transform(
                transform {
                    rotate(seconds * 10)
                    translate(-letter.bounds.center)
                }
            ))
        }
    }
}

```

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [June 19, 2023, 5:41pm UTC](https://openrndr.discourse.group/t/converting-font-characters-to-contours/576/5 "2023-06-19T17:41:05Z")

</div>

Playing with character contours:

> **[aBe (@hamoid@mastodon.art)](https://mastodon.art/@hamoid/110572089066064948)**
>
> Attached: 4 images
> 
> Characters on characters #creativecoding #openrndr
