├── .DS_Store ├── FontTools & DrawBot ├── .ipynb_checkpoints │ └── Navigating TTFs with fontTools-checkpoint.ipynb ├── LICENSE_OFL.txt ├── Navigating TTFs with fontTools.ipynb ├── NotoSans-Regular.ttf └── drawBotImage.png ├── LICENSE.txt └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynneyun/Tutorials/7a095f27f2bb59d3053cc346cc0a945618c20754/.DS_Store -------------------------------------------------------------------------------- /FontTools & DrawBot/.ipynb_checkpoints/Navigating TTFs with fontTools-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Navigating TTFs via FontTools#\n", 8 | "\n", 9 | "by [Lynne Yun](https://www.lynneyun.com)\n", 10 | "\n", 11 | "If you've ever tried to parse font files like TTFs, you'll know that it's no simple task. However, there is a powerful python module called FontTools that can help you! In this mini-tutorial, I'll go over how to use some functions of `fontTools`, and eventually illustrate what we can do with them via `drawBot`. This post was inspired by [Allison Parrish](https://www.decontextualize.com/)'s Notebook on [Manipulating Font Data](https://github.com/aparrish/material-of-language/blob/master/manipulating-font-data.ipynb), so check that out if you want more context!" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "This first cell demonstrates installing drawBot and fonttools if you don't have them already. Uncomment and install if necessary:" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "# !pip install git+https://github.com/typemytype/drawbot\n", 28 | "# !pip install fonttools" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "## fontTools ##\n", 36 | "What is fontTools you say? You can check out the library here: [fontTools Python library](https://rsms.me/fonttools-docs/). For our first example, let's try to grab basic information, such as the GlyphID. I'm importing an open-source typeface here, called Noto Sans.\n", 37 | "\n", 38 | "### Calling the GlyphID ###" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 1, 44 | "metadata": { 45 | "scrolled": true 46 | }, 47 | "outputs": [ 48 | { 49 | "name": "stdout", 50 | "output_type": "stream", 51 | "text": [ 52 | "glyphID is: 68\n" 53 | ] 54 | } 55 | ], 56 | "source": [ 57 | "from fontTools.ttLib import TTFont\n", 58 | "font = TTFont(\"./NotoSans-Regular.ttf\")\n", 59 | "print('glyphID is: ' + str(font.getGlyphID('a')))" 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [ 66 | "Now, that's the `glyphID`, which is not the Unicode Codepoint. Glyph IDs are the order in which the glyphs have been arranged in a font file, and may not be the same across different fonts. \n", 67 | "\n", 68 | "### Calling the Unicode Codepoint ###\n", 69 | "In case you want to grab the unicode codepoint represented as an integer, you can use `ord`." 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 2, 75 | "metadata": { 76 | "scrolled": true 77 | }, 78 | "outputs": [ 79 | { 80 | "name": "stdout", 81 | "output_type": "stream", 82 | "text": [ 83 | "97\n" 84 | ] 85 | } 86 | ], 87 | "source": [ 88 | "print(ord('a'))" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "### Calling the Glyph Width ###\n", 96 | "In font files, there are 'widths' of each glyph. They include the left and right sidebearings. Access the advances with the font object's `.width` attribute. You will have to call `getGlyphSet()` in order to do so." 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 3, 102 | "metadata": { 103 | "scrolled": true 104 | }, 105 | "outputs": [ 106 | { 107 | "name": "stdout", 108 | "output_type": "stream", 109 | "text": [ 110 | "width of glyph is 561\n" 111 | ] 112 | } 113 | ], 114 | "source": [ 115 | "glyph = font.getGlyphSet()['a']\n", 116 | "print('width of glyph is ' + str(glyph.width))" 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "### Units Per Em Value ###\n", 124 | "\n", 125 | "You can also get the `unitsPerEm` attribute of the font object from the [`ttLib` package](https://rsms.me/fonttools-docs/ttLib/index.html?highlight=unitsperem). " 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": 4, 131 | "metadata": {}, 132 | "outputs": [ 133 | { 134 | "name": "stdout", 135 | "output_type": "stream", 136 | "text": [ 137 | "units per em is: 1000\n" 138 | ] 139 | } 140 | ], 141 | "source": [ 142 | "units_per_em = font['head'].unitsPerEm\n", 143 | "print('units per em is: ' + str(units_per_em))" 144 | ] 145 | }, 146 | { 147 | "cell_type": "markdown", 148 | "metadata": {}, 149 | "source": [ 150 | "### Glyph Names ###\n", 151 | "\n", 152 | "Keep in mind that `Glyph Names` are how a certain glyph is named, even if that's not how the character itself is represented. For example, when you want to get information about the `\"ã\"` chracter, you'll have to point to it as `\"atilde\"`." 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "You can also get all the Glyph Names in a font file, using `getGlyphNames()`. Using this, we can also get how many characters are in this font file:" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 5, 165 | "metadata": { 166 | "scrolled": true 167 | }, 168 | "outputs": [ 169 | { 170 | "data": { 171 | "text/plain": [ 172 | "3246" 173 | ] 174 | }, 175 | "execution_count": 5, 176 | "metadata": {}, 177 | "output_type": "execute_result" 178 | } 179 | ], 180 | "source": [ 181 | "len(font.getGlyphNames())" 182 | ] 183 | }, 184 | { 185 | "cell_type": "markdown", 186 | "metadata": {}, 187 | "source": [ 188 | "Let's get the first 10 glyph names to see what this list looks like." 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 6, 194 | "metadata": { 195 | "scrolled": true 196 | }, 197 | "outputs": [ 198 | { 199 | "data": { 200 | "text/plain": [ 201 | "['.notdef',\n", 202 | " 'A',\n", 203 | " 'AE',\n", 204 | " 'AEacute',\n", 205 | " 'Aacute',\n", 206 | " 'Abreve',\n", 207 | " 'Acircumflex',\n", 208 | " 'Adieresis',\n", 209 | " 'Agrave',\n", 210 | " 'Alpha']" 211 | ] 212 | }, 213 | "execution_count": 6, 214 | "metadata": {}, 215 | "output_type": "execute_result" 216 | } 217 | ], 218 | "source": [ 219 | "font.getGlyphNames()[:10]" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "### Mapping Unicode Values to Glyph Names ###\n", 227 | "So considering what we just learned about `Glyph Names` you might be scratching your head about how to convert unicode values to glyph names, and vice versa. Thanks to [Just van Rossum](https://twitter.com/justvanrossum) for letting me know about this super useful feature!\n", 228 | "\n", 229 | "Here's a handy method for doing just that! Remember in an earlier example we got a unicode value using `ord`? This will return a dict that maps unicode to Glyph Names." 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": null, 235 | "metadata": {}, 236 | "outputs": [], 237 | "source": [ 238 | "font.getBestCmap()" 239 | ] 240 | }, 241 | { 242 | "cell_type": "markdown", 243 | "metadata": {}, 244 | "source": [ 245 | "So, using this, here is an example of converting `ã` to a unicode value and getting the `Glyphname` from it:" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": 7, 251 | "metadata": {}, 252 | "outputs": [ 253 | { 254 | "data": { 255 | "text/plain": [ 256 | "'atilde'" 257 | ] 258 | }, 259 | "execution_count": 7, 260 | "metadata": {}, 261 | "output_type": "execute_result" 262 | } 263 | ], 264 | "source": [ 265 | "font.getBestCmap()[ord('ã')]" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "### Getting Glyph Outlines via RecordingPen ###\n", 273 | "Let's try to do something a bit more interesting, perhaps — let's grab all the points from the glyph using a `RecordingPen`." 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": 8, 279 | "metadata": {}, 280 | "outputs": [ 281 | { 282 | "data": { 283 | "text/plain": [ 284 | "[('moveTo', ((288, 545),)),\n", 285 | " ('qCurveTo', ((386, 545), (480, 459), (480, 365))),\n", 286 | " ('lineTo', ((480, 0),)),\n", 287 | " ('lineTo', ((416, 0),)),\n", 288 | " ('lineTo', ((399, 76),)),\n", 289 | " ('lineTo', ((395, 76),)),\n", 290 | " ('qCurveTo', ((360, 32), (282, -10), (215, -10))),\n", 291 | " ('qCurveTo', ((142, -10), (46, 67), (46, 149))),\n", 292 | " ('qCurveTo', ((46, 229), (172, 316), (303, 320))),\n", 293 | " ('lineTo', ((394, 323),)),\n", 294 | " ('lineTo', ((394, 355),)),\n", 295 | " ('qCurveTo', ((394, 422), (336, 474), (283, 474))),\n", 296 | " ('qCurveTo', ((241, 474), (165, 449), (132, 433))),\n", 297 | " ('lineTo', ((105, 499),)),\n", 298 | " ('qCurveTo', ((140, 518), (236, 545), (288, 545))),\n", 299 | " ('closePath', ()),\n", 300 | " ('moveTo', ((393, 262),)),\n", 301 | " ('lineTo', ((314, 259),)),\n", 302 | " ('qCurveTo', ((214, 255), (137, 199), (137, 148))),\n", 303 | " ('qCurveTo', ((137, 103), (192, 61), (235, 61))),\n", 304 | " ('qCurveTo', ((302, 61), (393, 136), (393, 214))),\n", 305 | " ('closePath', ())]" 306 | ] 307 | }, 308 | "execution_count": 8, 309 | "metadata": {}, 310 | "output_type": "execute_result" 311 | } 312 | ], 313 | "source": [ 314 | "from fontTools.pens.recordingPen import RecordingPen\n", 315 | "glyph = font.getGlyphSet()['a']\n", 316 | "p = RecordingPen()\n", 317 | "glyph.draw(p)\n", 318 | "p.value" 319 | ] 320 | }, 321 | { 322 | "cell_type": "markdown", 323 | "metadata": {}, 324 | "source": [ 325 | "If you're not sure about what `moveTo`, `qCurveTo`, `lineTo` is doing refer to the [DrawBot BezierPath Documentation](https://www.drawbot.com/content/shapes/bezierPath.html). \n", 326 | "\n", 327 | "I'm going to make a function to make grabbing the curve information easier:" 328 | ] 329 | }, 330 | { 331 | "cell_type": "code", 332 | "execution_count": 9, 333 | "metadata": {}, 334 | "outputs": [], 335 | "source": [ 336 | "def get_outline(font, ch):\n", 337 | " glyph = font.getGlyphSet()[ch]\n", 338 | " p = RecordingPen()\n", 339 | " glyph.draw(p)\n", 340 | " return p.value" 341 | ] 342 | }, 343 | { 344 | "cell_type": "markdown", 345 | "metadata": {}, 346 | "source": [ 347 | "you can see that now this one line is all we need:" 348 | ] 349 | }, 350 | { 351 | "cell_type": "code", 352 | "execution_count": null, 353 | "metadata": { 354 | "scrolled": true 355 | }, 356 | "outputs": [], 357 | "source": [ 358 | "get_outline(font, \"a\")" 359 | ] 360 | }, 361 | { 362 | "cell_type": "markdown", 363 | "metadata": {}, 364 | "source": [ 365 | "If you are playing with the above code though, you'll notice that glyphs that are made out of composites like the 'atilde' give us components, not the actual outlines:" 366 | ] 367 | }, 368 | { 369 | "cell_type": "code", 370 | "execution_count": 10, 371 | "metadata": {}, 372 | "outputs": [ 373 | { 374 | "data": { 375 | "text/plain": [ 376 | "[('addComponent', ('a', (1, 0, 0, 1, 0, 0))),\n", 377 | " ('addComponent', ('tilde', (1, 0, 0, 1, 57, 0)))]" 378 | ] 379 | }, 380 | "execution_count": 10, 381 | "metadata": {}, 382 | "output_type": "execute_result" 383 | } 384 | ], 385 | "source": [ 386 | "get_outline(font, \"atilde\")" 387 | ] 388 | }, 389 | { 390 | "cell_type": "markdown", 391 | "metadata": {}, 392 | "source": [ 393 | "### Decomposing Recording Pen ###\n", 394 | "\n", 395 | "To grab these, we will need the `Decomposing Pen` to decompose them. Here is the modified function from above to grab all outlines. Now trying to grab `atilde` should work!" 396 | ] 397 | }, 398 | { 399 | "cell_type": "code", 400 | "execution_count": 11, 401 | "metadata": {}, 402 | "outputs": [], 403 | "source": [ 404 | "from fontTools.pens.recordingPen import DecomposingRecordingPen\n", 405 | "\n", 406 | "def get_all_outlines(font, ch):\n", 407 | " glyphset = font.getGlyphSet()\n", 408 | " glyph = glyphset[ch]\n", 409 | " p = DecomposingRecordingPen(glyphset)\n", 410 | " glyph.draw(p)\n", 411 | " return p.value\n", 412 | "\n", 413 | "# print(get_all_outlines(font, \"atilde\"))" 414 | ] 415 | }, 416 | { 417 | "cell_type": "markdown", 418 | "metadata": {}, 419 | "source": [ 420 | "## Let's do fun things with Drawbot! ##\n", 421 | "\n", 422 | "Okay, what can we do with all this information besides just plain information, you say? Let's do some fun things, illustrated with [DrawBot](https://drawbot.com). I'm going to use `Drawbot` as a python module here, so let's import it." 423 | ] 424 | }, 425 | { 426 | "cell_type": "code", 427 | "execution_count": 12, 428 | "metadata": {}, 429 | "outputs": [], 430 | "source": [ 431 | "import drawBot as draw" 432 | ] 433 | }, 434 | { 435 | "cell_type": "markdown", 436 | "metadata": {}, 437 | "source": [ 438 | "I'm using drawbot as a module here, so I need to set it up so I can see it on my Jupyter Notebook. I'm setting a `startdraw` function and `show` so I can quickly call it while I'm drawing things, since drawbot needs the same lines to start and end a drawing.\n", 439 | "\n", 440 | "This incorporates the IPython module, so you may need to install it if you don't already have it." 441 | ] 442 | }, 443 | { 444 | "cell_type": "code", 445 | "execution_count": 13, 446 | "metadata": {}, 447 | "outputs": [], 448 | "source": [ 449 | "from IPython.display import Image, display\n", 450 | "\n", 451 | "def startdraw(canvas_width,canvas_height):\n", 452 | " draw.newDrawing()\n", 453 | " draw.newPage(canvas_width, canvas_height)\n", 454 | " \n", 455 | "def show():\n", 456 | " draw.saveImage(\"drawBotImage.png\")\n", 457 | " draw.endDrawing()\n", 458 | " drawing = Image(filename = \"drawBotImage.png\")\n", 459 | " display(drawing)" 460 | ] 461 | }, 462 | { 463 | "cell_type": "markdown", 464 | "metadata": {}, 465 | "source": [ 466 | "### Drawing fontTools Glyphs in Drawbot ###\n", 467 | "Thanks to [@Drawbotapp](https://twitter.com/drawbotapp), I learned that a `BezierPath()` is also a pen! It makes it super easy to draw the fontTools glyphs." 468 | ] 469 | }, 470 | { 471 | "cell_type": "code", 472 | "execution_count": 14, 473 | "metadata": { 474 | "scrolled": true 475 | }, 476 | "outputs": [ 477 | { 478 | "data": { 479 | "image/png": "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\n", 480 | "text/plain": [ 481 | "" 482 | ] 483 | }, 484 | "metadata": {}, 485 | "output_type": "display_data" 486 | } 487 | ], 488 | "source": [ 489 | "def draw_outline(font, ch, scale_num):\n", 490 | " path = draw.BezierPath()\n", 491 | " glyph = font.getGlyphSet()[ch]\n", 492 | " glyph.draw(path)\n", 493 | " path.scale(scale_num)\n", 494 | " return path\n", 495 | "\n", 496 | "startdraw(200,200)\n", 497 | "draw.drawPath(draw_outline(font,\"a\",0.3))\n", 498 | "show()" 499 | ] 500 | }, 501 | { 502 | "cell_type": "markdown", 503 | "metadata": {}, 504 | "source": [ 505 | "### Modifying Outline Paths ###\n", 506 | "Using the `get_outline` function we made before, let's draw the letter 'a'. I'm parsing information from `get_outline`, making a `BezierPath` object, and adding all the paths in there. Take a look at the [Drawbot Documentation for BezierPath](http://www.drawbot.com/content/shapes/bezierPath.html) if you need a refresher. You'll notice that I'm using the `.scale` to reduce the size before doing the line too." 507 | ] 508 | }, 509 | { 510 | "cell_type": "code", 511 | "execution_count": 15, 512 | "metadata": {}, 513 | "outputs": [ 514 | { 515 | "data": { 516 | "image/png": "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\n", 517 | "text/plain": [ 518 | "" 519 | ] 520 | }, 521 | "metadata": {}, 522 | "output_type": "display_data" 523 | } 524 | ], 525 | "source": [ 526 | "path_orig = get_outline(font, \"a\")\n", 527 | "startdraw(200,200)\n", 528 | "\n", 529 | "paths = []\n", 530 | "for i in path_orig:\n", 531 | " if i[0] == 'moveTo':\n", 532 | " path = draw.BezierPath()\n", 533 | " path.moveTo((i[1][0]))\n", 534 | " if i[0] == 'lineTo':\n", 535 | " path.lineTo((i[1][0]))\n", 536 | " if i[0] == 'qCurveTo':\n", 537 | " path.qCurveTo(*(i[1]))\n", 538 | " if i[0] == 'closePath':\n", 539 | " path.closePath()\n", 540 | " paths.append(path)\n", 541 | "\n", 542 | "finalpath = paths[0].difference(paths[1])\n", 543 | "finalpath.scale(0.3) #let's scale it down!\n", 544 | "draw.fill(0.5)\n", 545 | "draw.stroke(0)\n", 546 | "draw.drawPath(finalpath)\n", 547 | "show()" 548 | ] 549 | }, 550 | { 551 | "cell_type": "markdown", 552 | "metadata": {}, 553 | "source": [ 554 | "Ta-da! We made just one single letter, no big deal, you might say. Let's start manipulating all the points to give it a distorted filter:" 555 | ] 556 | }, 557 | { 558 | "cell_type": "code", 559 | "execution_count": 20, 560 | "metadata": {}, 561 | "outputs": [ 562 | { 563 | "data": { 564 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAAEGWlDQ1BrQ0dDb2xvclNwYWNlR2VuZXJpY1JHQgAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia219thzg25abkRE/BpDc3pqvphHvRFys2weqvp+krbWKIX7nhDbzLOItiM8358pTwdirqpPFnMF2xLc1WvLyOwTAibpbmvHHcvttU57y5+XqNZrLe3lE/Pq8eUj2fXKfOe3pfOjzhJYtB/yll5SDFcSDiH+hRkH25+L+sdxKEAMZahrlSX8ukqMOWy/jXW2m6M9LDBc31B9LFuv6gVKg/0Szi3KAr1kGq1GMjU/aLbnq6/lRxc4XfJ98hTargX++DbMJBSiYMIe9Ck1YAxFkKEAG3xbYaKmDDgYyFK0UGYpfoWYXG+fAPPI6tJnNwb7ClP7IyF+D+bjOtCpkhz6CFrIa/I6sFtNl8auFXGMTP34sNwI/JhkgEtmDz14ySfaRcTIBInmKPE32kxyyE2Tv+thKbEVePDfW/byMM1Kmm0XdObS7oGD/MypMXFPXrCwOtoYjyyn7BV29/MZfsVzpLDdRtuIZnbpXzvlf+ev8MvYr/Gqk4H/kV/G3csdazLuyTMPsbFhzd1UabQbjFvDRmcWJxR3zcfHkVw9GfpbJmeev9F08WW8uDkaslwX6avlWGU6NRKz0g/SHtCy9J30o/ca9zX3Kfc19zn3BXQKRO8ud477hLnAfc1/G9mrzGlrfexZ5GLdn6ZZrrEohI2wVHhZywjbhUWEy8icMCGNCUdiBlq3r+xafL549HQ5jH+an+1y+LlYBifuxAvRN/lVVVOlwlCkdVm9NOL5BE4wkQ2SMlDZU97hX86EilU/lUmkQUztTE6mx1EEPh7OmdqBtAvv8HdWpbrJS6tJj3n0CWdM6busNzRV3S9KTYhqvNiqWmuroiKgYhshMjmhTh9ptWhsF7970j/SbMrsPE1suR5z7DMC+P/Hs+y7ijrQAlhyAgccjbhjPygfeBTjzhNqy28EdkUh8C+DU9+z2v/oyeH791OncxHOs5y2AtTc7nb/f73TWPkD/qwBnjX8BoJ98VQNcC+8AAAA4ZVhJZk1NACoAAAAIAAGHaQAEAAAAAQAAABoAAAAAAAKgAgAEAAAAAQAAASygAwAEAAAAAQAAASwAAAAA+6ig3QAAK5RJREFUeAHtnQm0VNWZ78+dZ+Z5ngdlkFHQGNoJQYU4BJeixvXI6vap3Z2npvNW0qbTdMeYlTZpjUbbuX2SvKWNtGiUKA4EgYAoiAKCIIgyz5c7wx3e77vvXrxc7lRVZ6qq/1lr36pbdWoPv1PnX3t/+9vfdhwdIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIuAHgRQ/ClEZzRJI450cUjbJnteQqutSKY/lJB0iIAJ1BCRY7n8VepLlAFI/Uq/U1NS+GRkZA3jsVVNT04nUrqqqKr+ystJEipdTT6WlpVXxWM17qSS7JjW8n2H/814F7xXzeDglJWU/n9158uTJTZyznfR5XTKR0yECCU9AguXOJT6PbNZbVpmZmWXt2rWr6NSpU0r79u2zSVn5+flOQUGBk52dfTrl5OSYWrVYOuLkIE5OeXm5U1RU5JSUlNQ+Hjp0qIJUfuTIkRRey6bMbZy7/NSpU2+R4TLS8RYz1psiEKcEJFixX7jrEIwF1113Xc6gQYNMsGLPMYIcTMz279/v7N27t3rLli1Fe/bsyaFHt53XnyebhaQdEWSnU0Ug1AQkWLFdnlqxmjdvXk6PHj1iy8mlT1uvbNeuXc6nn35atnnzZrOJfV5RUfFrHk28ZBNzibOyCYaABCt67qETq8ZNqa6udrZu3eqsXr3ael41/P8A6THOO9H4XP0vAvFAQIL1zVUyI3hXUre6x848FtSn9PT0fIzedo4Zw7thFJ991113OV272kfCfxw+fNh55513SrZt21ZF3X9MjZ8kVYa/5qqhCHxD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\n", 565 | "text/plain": [ 566 | "" 567 | ] 568 | }, 569 | "metadata": {}, 570 | "output_type": "display_data" 571 | } 572 | ], 573 | "source": [ 574 | "import random\n", 575 | "path_orig = get_outline(font, \"a\")\n", 576 | "startdraw(300,300)\n", 577 | "paths = []\n", 578 | "for i in path_orig:\n", 579 | " disturbance = random.randint(-35, 35)\n", 580 | " if i[0] == 'moveTo':\n", 581 | " x = i[1][0][0] + disturbance\n", 582 | " y = i[1][0][1] + disturbance\n", 583 | " path = draw.BezierPath()\n", 584 | " path.moveTo((x,y))\n", 585 | " if i[0] == 'lineTo':\n", 586 | " x = i[1][0][0] + disturbance\n", 587 | " y = i[1][0][1]+ disturbance\n", 588 | " path.lineTo((x,y))\n", 589 | " if i[0] == 'qCurveTo':\n", 590 | " temp = []\n", 591 | " for cord in i[1]:\n", 592 | " x = cord[0] + disturbance\n", 593 | " y = cord[1] + disturbance\n", 594 | " temp.append((x,y))\n", 595 | " path.qCurveTo(*temp)\n", 596 | " if i[0] == 'closePath':\n", 597 | " path.closePath()\n", 598 | " paths.append(path)\n", 599 | "finalpath = paths[0].difference(paths[1])\n", 600 | "finalpath.scale(0.3) #let's scale it down!\n", 601 | "draw.fill(0.5)\n", 602 | "draw.stroke(0)\n", 603 | "draw.drawPath(finalpath)\n", 604 | "show()" 605 | ] 606 | }, 607 | { 608 | "cell_type": "markdown", 609 | "metadata": {}, 610 | "source": [ 611 | "## Parting words ##\n", 612 | "See, lots of fun things! :-) That's it from me (at least for now.)\n", 613 | "\n", 614 | "Check out the wiki Page on Github for fontTools if you would like to dig deeper:\n", 615 | "[Wiki Page on Github for FontTools](https://github.com/fonttools/fonttools/wiki) \n", 616 | "\n", 617 | "Wish you the best of luck on your font-navigating journey!" 618 | ] 619 | } 620 | ], 621 | "metadata": { 622 | "kernelspec": { 623 | "display_name": "Python 3", 624 | "language": "python", 625 | "name": "python3" 626 | }, 627 | "language_info": { 628 | "codemirror_mode": { 629 | "name": "ipython", 630 | "version": 3 631 | }, 632 | "file_extension": ".py", 633 | "mimetype": "text/x-python", 634 | "name": "python", 635 | "nbconvert_exporter": "python", 636 | "pygments_lexer": "ipython3", 637 | "version": "3.7.7" 638 | } 639 | }, 640 | "nbformat": 4, 641 | "nbformat_minor": 4 642 | } 643 | -------------------------------------------------------------------------------- /FontTools & DrawBot/LICENSE_OFL.txt: -------------------------------------------------------------------------------- 1 | This Font Software is licensed under the SIL Open Font License, 2 | Version 1.1. 3 | 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | ----------------------------------------------------------- 8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 9 | ----------------------------------------------------------- 10 | 11 | PREAMBLE 12 | The goals of the Open Font License (OFL) are to stimulate worldwide 13 | development of collaborative font projects, to support the font 14 | creation efforts of academic and linguistic communities, and to 15 | provide a free and open framework in which fonts may be shared and 16 | improved in partnership with others. 17 | 18 | The OFL allows the licensed fonts to be used, studied, modified and 19 | redistributed freely as long as they are not sold by themselves. The 20 | fonts, including any derivative works, can be bundled, embedded, 21 | redistributed and/or sold with any software provided that any reserved 22 | names are not used by derivative works. The fonts and derivatives, 23 | however, cannot be released under any other type of license. The 24 | requirement for fonts to remain under this license does not apply to 25 | any document created using the fonts or their derivatives. 26 | 27 | DEFINITIONS 28 | "Font Software" refers to the set of files released by the Copyright 29 | Holder(s) under this license and clearly marked as such. This may 30 | include source files, build scripts and documentation. 31 | 32 | "Reserved Font Name" refers to any names specified as such after the 33 | copyright statement(s). 34 | 35 | "Original Version" refers to the collection of Font Software 36 | components as distributed by the Copyright Holder(s). 37 | 38 | "Modified Version" refers to any derivative made by adding to, 39 | deleting, or substituting -- in part or in whole -- any of the 40 | components of the Original Version, by changing formats or by porting 41 | the Font Software to a new environment. 42 | 43 | "Author" refers to any designer, engineer, programmer, technical 44 | writer or other person who contributed to the Font Software. 45 | 46 | PERMISSION & CONDITIONS 47 | Permission is hereby granted, free of charge, to any person obtaining 48 | a copy of the Font Software, to use, study, copy, merge, embed, 49 | modify, redistribute, and sell modified and unmodified copies of the 50 | Font Software, subject to the following conditions: 51 | 52 | 1) Neither the Font Software nor any of its individual components, in 53 | Original or Modified Versions, may be sold by itself. 54 | 55 | 2) Original or Modified Versions of the Font Software may be bundled, 56 | redistributed and/or sold with any software, provided that each copy 57 | contains the above copyright notice and this license. These can be 58 | included either as stand-alone text files, human-readable headers or 59 | in the appropriate machine-readable metadata fields within text or 60 | binary files as long as those fields can be easily viewed by the user. 61 | 62 | 3) No Modified Version of the Font Software may use the Reserved Font 63 | Name(s) unless explicit written permission is granted by the 64 | corresponding Copyright Holder. This restriction only applies to the 65 | primary font name as presented to the users. 66 | 67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 68 | Software shall not be used to promote, endorse or advertise any 69 | Modified Version, except to acknowledge the contribution(s) of the 70 | Copyright Holder(s) and the Author(s) or with their explicit written 71 | permission. 72 | 73 | 5) The Font Software, modified or unmodified, in part or in whole, 74 | must be distributed entirely under this license, and must not be 75 | distributed under any other license. The requirement for fonts to 76 | remain under this license does not apply to any document created using 77 | the Font Software. 78 | 79 | TERMINATION 80 | This license becomes null and void if any of the above conditions are 81 | not met. 82 | 83 | DISCLAIMER 84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 92 | OTHER DEALINGS IN THE FONT SOFTWARE. 93 | -------------------------------------------------------------------------------- /FontTools & DrawBot/Navigating TTFs with fontTools.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Navigating TTFs via FontTools#\n", 8 | "\n", 9 | "by [Lynne Yun](https://www.lynneyun.com)\n", 10 | "\n", 11 | "If you've ever tried to parse font files like TTFs, you'll know that it's no simple task. However, there is a powerful python module called FontTools that can help you! In this mini-tutorial, I'll go over how to use some functions of `fontTools`, and eventually illustrate what we can do with them via `drawBot`. This post was inspired by [Allison Parrish](https://www.decontextualize.com/)'s Notebook on [Manipulating Font Data](https://github.com/aparrish/material-of-language/blob/master/manipulating-font-data.ipynb), so check that out if you want more context!" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "This first cell demonstrates installing drawBot and fonttools if you don't have them already. Uncomment and install if necessary:" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "# !pip install git+https://github.com/typemytype/drawbot\n", 28 | "# !pip install fonttools" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "## fontTools ##\n", 36 | "What is fontTools you say? You can check out the library here: [fontTools Python library](https://rsms.me/fonttools-docs/). For our first example, let's try to grab basic information, such as the GlyphID. I'm importing an open-source typeface here, called Noto Sans.\n", 37 | "\n", 38 | "### Calling the GlyphID ###" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 1, 44 | "metadata": { 45 | "scrolled": true 46 | }, 47 | "outputs": [ 48 | { 49 | "name": "stdout", 50 | "output_type": "stream", 51 | "text": [ 52 | "glyphID is: 68\n" 53 | ] 54 | } 55 | ], 56 | "source": [ 57 | "from fontTools.ttLib import TTFont\n", 58 | "font = TTFont(\"./NotoSans-Regular.ttf\")\n", 59 | "print('glyphID is: ' + str(font.getGlyphID('a')))" 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [ 66 | "Now, that's the `glyphID`, which is not the Unicode Codepoint. Glyph IDs are the order in which the glyphs have been arranged in a font file, and may not be the same across different fonts. \n", 67 | "\n", 68 | "### Calling the Unicode Codepoint ###\n", 69 | "In case you want to grab the unicode codepoint represented as an integer, you can use `ord`." 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 2, 75 | "metadata": { 76 | "scrolled": true 77 | }, 78 | "outputs": [ 79 | { 80 | "name": "stdout", 81 | "output_type": "stream", 82 | "text": [ 83 | "97\n" 84 | ] 85 | } 86 | ], 87 | "source": [ 88 | "print(ord('a'))" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "### Calling the Glyph Width ###\n", 96 | "In font files, there are 'widths' of each glyph. They include the left and right sidebearings. Access the advances with the font object's `.width` attribute. You will have to call `getGlyphSet()` in order to do so." 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 3, 102 | "metadata": { 103 | "scrolled": true 104 | }, 105 | "outputs": [ 106 | { 107 | "name": "stdout", 108 | "output_type": "stream", 109 | "text": [ 110 | "width of glyph is 561\n" 111 | ] 112 | } 113 | ], 114 | "source": [ 115 | "glyph = font.getGlyphSet()['a']\n", 116 | "print('width of glyph is ' + str(glyph.width))" 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "### Units Per Em Value ###\n", 124 | "\n", 125 | "You can also get the `unitsPerEm` attribute of the font object from the [`ttLib` package](https://rsms.me/fonttools-docs/ttLib/index.html?highlight=unitsperem). " 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": 4, 131 | "metadata": {}, 132 | "outputs": [ 133 | { 134 | "name": "stdout", 135 | "output_type": "stream", 136 | "text": [ 137 | "units per em is: 1000\n" 138 | ] 139 | } 140 | ], 141 | "source": [ 142 | "units_per_em = font['head'].unitsPerEm\n", 143 | "print('units per em is: ' + str(units_per_em))" 144 | ] 145 | }, 146 | { 147 | "cell_type": "markdown", 148 | "metadata": {}, 149 | "source": [ 150 | "### Glyph Names ###\n", 151 | "\n", 152 | "Keep in mind that `Glyph Names` are how a certain glyph is named, even if that's not how the character itself is represented. For example, when you want to get information about the `\"ã\"` chracter, you'll have to point to it as `\"atilde\"`." 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "You can also get all the Glyph Names in a font file, using `getGlyphNames()`. Using this, we can also get how many characters are in this font file:" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 5, 165 | "metadata": { 166 | "scrolled": true 167 | }, 168 | "outputs": [ 169 | { 170 | "data": { 171 | "text/plain": [ 172 | "3246" 173 | ] 174 | }, 175 | "execution_count": 5, 176 | "metadata": {}, 177 | "output_type": "execute_result" 178 | } 179 | ], 180 | "source": [ 181 | "len(font.getGlyphNames())" 182 | ] 183 | }, 184 | { 185 | "cell_type": "markdown", 186 | "metadata": {}, 187 | "source": [ 188 | "Let's get the first 10 glyph names to see what this list looks like." 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 6, 194 | "metadata": { 195 | "scrolled": true 196 | }, 197 | "outputs": [ 198 | { 199 | "data": { 200 | "text/plain": [ 201 | "['.notdef',\n", 202 | " 'A',\n", 203 | " 'AE',\n", 204 | " 'AEacute',\n", 205 | " 'Aacute',\n", 206 | " 'Abreve',\n", 207 | " 'Acircumflex',\n", 208 | " 'Adieresis',\n", 209 | " 'Agrave',\n", 210 | " 'Alpha']" 211 | ] 212 | }, 213 | "execution_count": 6, 214 | "metadata": {}, 215 | "output_type": "execute_result" 216 | } 217 | ], 218 | "source": [ 219 | "font.getGlyphNames()[:10]" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "### Mapping Unicode Values to Glyph Names ###\n", 227 | "So considering what we just learned about `Glyph Names` you might be scratching your head about how to convert unicode values to glyph names, and vice versa. Thanks to [Just van Rossum](https://twitter.com/justvanrossum) for letting me know about this super useful feature!\n", 228 | "\n", 229 | "Here's a handy method for doing just that! Remember in an earlier example we got a unicode value using `ord`? This will return a dict that maps unicode to Glyph Names." 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": null, 235 | "metadata": {}, 236 | "outputs": [], 237 | "source": [ 238 | "font.getBestCmap()" 239 | ] 240 | }, 241 | { 242 | "cell_type": "markdown", 243 | "metadata": {}, 244 | "source": [ 245 | "So, using this, here is an example of converting `ã` to a unicode value and getting the `Glyphname` from it:" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": 7, 251 | "metadata": {}, 252 | "outputs": [ 253 | { 254 | "data": { 255 | "text/plain": [ 256 | "'atilde'" 257 | ] 258 | }, 259 | "execution_count": 7, 260 | "metadata": {}, 261 | "output_type": "execute_result" 262 | } 263 | ], 264 | "source": [ 265 | "font.getBestCmap()[ord('ã')]" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "### Getting Glyph Outlines via RecordingPen ###\n", 273 | "Let's try to do something a bit more interesting, perhaps — let's grab all the points from the glyph using a `RecordingPen`." 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": 8, 279 | "metadata": {}, 280 | "outputs": [ 281 | { 282 | "data": { 283 | "text/plain": [ 284 | "[('moveTo', ((288, 545),)),\n", 285 | " ('qCurveTo', ((386, 545), (480, 459), (480, 365))),\n", 286 | " ('lineTo', ((480, 0),)),\n", 287 | " ('lineTo', ((416, 0),)),\n", 288 | " ('lineTo', ((399, 76),)),\n", 289 | " ('lineTo', ((395, 76),)),\n", 290 | " ('qCurveTo', ((360, 32), (282, -10), (215, -10))),\n", 291 | " ('qCurveTo', ((142, -10), (46, 67), (46, 149))),\n", 292 | " ('qCurveTo', ((46, 229), (172, 316), (303, 320))),\n", 293 | " ('lineTo', ((394, 323),)),\n", 294 | " ('lineTo', ((394, 355),)),\n", 295 | " ('qCurveTo', ((394, 422), (336, 474), (283, 474))),\n", 296 | " ('qCurveTo', ((241, 474), (165, 449), (132, 433))),\n", 297 | " ('lineTo', ((105, 499),)),\n", 298 | " ('qCurveTo', ((140, 518), (236, 545), (288, 545))),\n", 299 | " ('closePath', ()),\n", 300 | " ('moveTo', ((393, 262),)),\n", 301 | " ('lineTo', ((314, 259),)),\n", 302 | " ('qCurveTo', ((214, 255), (137, 199), (137, 148))),\n", 303 | " ('qCurveTo', ((137, 103), (192, 61), (235, 61))),\n", 304 | " ('qCurveTo', ((302, 61), (393, 136), (393, 214))),\n", 305 | " ('closePath', ())]" 306 | ] 307 | }, 308 | "execution_count": 8, 309 | "metadata": {}, 310 | "output_type": "execute_result" 311 | } 312 | ], 313 | "source": [ 314 | "from fontTools.pens.recordingPen import RecordingPen\n", 315 | "glyph = font.getGlyphSet()['a']\n", 316 | "p = RecordingPen()\n", 317 | "glyph.draw(p)\n", 318 | "p.value" 319 | ] 320 | }, 321 | { 322 | "cell_type": "markdown", 323 | "metadata": {}, 324 | "source": [ 325 | "If you're not sure about what `moveTo`, `qCurveTo`, `lineTo` is doing refer to the [DrawBot BezierPath Documentation](https://www.drawbot.com/content/shapes/bezierPath.html). \n", 326 | "\n", 327 | "I'm going to make a function to make grabbing the curve information easier:" 328 | ] 329 | }, 330 | { 331 | "cell_type": "code", 332 | "execution_count": 9, 333 | "metadata": {}, 334 | "outputs": [], 335 | "source": [ 336 | "def get_outline(font, ch):\n", 337 | " glyph = font.getGlyphSet()[ch]\n", 338 | " p = RecordingPen()\n", 339 | " glyph.draw(p)\n", 340 | " return p.value" 341 | ] 342 | }, 343 | { 344 | "cell_type": "markdown", 345 | "metadata": {}, 346 | "source": [ 347 | "you can see that now this one line is all we need:" 348 | ] 349 | }, 350 | { 351 | "cell_type": "code", 352 | "execution_count": null, 353 | "metadata": { 354 | "scrolled": true 355 | }, 356 | "outputs": [], 357 | "source": [ 358 | "get_outline(font, \"a\")" 359 | ] 360 | }, 361 | { 362 | "cell_type": "markdown", 363 | "metadata": {}, 364 | "source": [ 365 | "If you are playing with the above code though, you'll notice that glyphs that are made out of composites like the 'atilde' give us components, not the actual outlines:" 366 | ] 367 | }, 368 | { 369 | "cell_type": "code", 370 | "execution_count": 10, 371 | "metadata": {}, 372 | "outputs": [ 373 | { 374 | "data": { 375 | "text/plain": [ 376 | "[('addComponent', ('a', (1, 0, 0, 1, 0, 0))),\n", 377 | " ('addComponent', ('tilde', (1, 0, 0, 1, 57, 0)))]" 378 | ] 379 | }, 380 | "execution_count": 10, 381 | "metadata": {}, 382 | "output_type": "execute_result" 383 | } 384 | ], 385 | "source": [ 386 | "get_outline(font, \"atilde\")" 387 | ] 388 | }, 389 | { 390 | "cell_type": "markdown", 391 | "metadata": {}, 392 | "source": [ 393 | "### Decomposing Recording Pen ###\n", 394 | "\n", 395 | "To grab these, we will need the `Decomposing Pen` to decompose them. Here is the modified function from above to grab all outlines. Now trying to grab `atilde` should work!" 396 | ] 397 | }, 398 | { 399 | "cell_type": "code", 400 | "execution_count": 11, 401 | "metadata": {}, 402 | "outputs": [], 403 | "source": [ 404 | "from fontTools.pens.recordingPen import DecomposingRecordingPen\n", 405 | "\n", 406 | "def get_all_outlines(font, ch):\n", 407 | " glyphset = font.getGlyphSet()\n", 408 | " glyph = glyphset[ch]\n", 409 | " p = DecomposingRecordingPen(glyphset)\n", 410 | " glyph.draw(p)\n", 411 | " return p.value\n", 412 | "\n", 413 | "# print(get_all_outlines(font, \"atilde\"))" 414 | ] 415 | }, 416 | { 417 | "cell_type": "markdown", 418 | "metadata": {}, 419 | "source": [ 420 | "## Let's do fun things with Drawbot! ##\n", 421 | "\n", 422 | "Okay, what can we do with all this information besides just plain information, you say? Let's do some fun things, illustrated with [DrawBot](https://drawbot.com). I'm going to use `Drawbot` as a python module here, so let's import it." 423 | ] 424 | }, 425 | { 426 | "cell_type": "code", 427 | "execution_count": 12, 428 | "metadata": {}, 429 | "outputs": [], 430 | "source": [ 431 | "import drawBot as draw" 432 | ] 433 | }, 434 | { 435 | "cell_type": "markdown", 436 | "metadata": {}, 437 | "source": [ 438 | "I'm using drawbot as a module here, so I need to set it up so I can see it on my Jupyter Notebook. I'm setting a `startdraw` function and `show` so I can quickly call it while I'm drawing things, since drawbot needs the same lines to start and end a drawing.\n", 439 | "\n", 440 | "This incorporates the IPython module, so you may need to install it if you don't already have it." 441 | ] 442 | }, 443 | { 444 | "cell_type": "code", 445 | "execution_count": 13, 446 | "metadata": {}, 447 | "outputs": [], 448 | "source": [ 449 | "from IPython.display import Image, display\n", 450 | "\n", 451 | "def startdraw(canvas_width,canvas_height):\n", 452 | " draw.newDrawing()\n", 453 | " draw.newPage(canvas_width, canvas_height)\n", 454 | " \n", 455 | "def show():\n", 456 | " draw.saveImage(\"drawBotImage.png\")\n", 457 | " draw.endDrawing()\n", 458 | " drawing = Image(filename = \"drawBotImage.png\")\n", 459 | " display(drawing)" 460 | ] 461 | }, 462 | { 463 | "cell_type": "markdown", 464 | "metadata": {}, 465 | "source": [ 466 | "### Drawing fontTools Glyphs in Drawbot ###\n", 467 | "Thanks to [@Drawbotapp](https://twitter.com/drawbotapp), I learned that a `BezierPath()` is also a pen! It makes it super easy to draw the fontTools glyphs." 468 | ] 469 | }, 470 | { 471 | "cell_type": "code", 472 | "execution_count": 14, 473 | "metadata": { 474 | "scrolled": true 475 | }, 476 | "outputs": [ 477 | { 478 | "data": { 479 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAEGWlDQ1BrQ0dDb2xvclNwYWNlR2VuZXJpY1JHQgAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia219thzg25abkRE/BpDc3pqvphHvRFys2weqvp+krbWKIX7nhDbzLOItiM8358pTwdirqpPFnMF2xLc1WvLyOwTAibpbmvHHcvttU57y5+XqNZrLe3lE/Pq8eUj2fXKfOe3pfOjzhJYtB/yll5SDFcSDiH+hRkH25+L+sdxKEAMZahrlSX8ukqMOWy/jXW2m6M9LDBc31B9LFuv6gVKg/0Szi3KAr1kGq1GMjU/aLbnq6/lRxc4XfJ98hTargX++DbMJBSiYMIe9Ck1YAxFkKEAG3xbYaKmDDgYyFK0UGYpfoWYXG+fAPPI6tJnNwb7ClP7IyF+D+bjOtCpkhz6CFrIa/I6sFtNl8auFXGMTP34sNwI/JhkgEtmDz14ySfaRcTIBInmKPE32kxyyE2Tv+thKbEVePDfW/byMM1Kmm0XdObS7oGD/MypMXFPXrCwOtoYjyyn7BV29/MZfsVzpLDdRtuIZnbpXzvlf+ev8MvYr/Gqk4H/kV/G3csdazLuyTMPsbFhzd1UabQbjFvDRmcWJxR3zcfHkVw9GfpbJmeev9F08WW8uDkaslwX6avlWGU6NRKz0g/SHtCy9J30o/ca9zX3Kfc19zn3BXQKRO8ud477hLnAfc1/G9mrzGlrfexZ5GLdn6ZZrrEohI2wVHhZywjbhUWEy8icMCGNCUdiBlq3r+xafL549HQ5jH+an+1y+LlYBifuxAvRN/lVVVOlwlCkdVm9NOL5BE4wkQ2SMlDZU97hX86EilU/lUmkQUztTE6mx1EEPh7OmdqBtAvv8HdWpbrJS6tJj3n0CWdM6busNzRV3S9KTYhqvNiqWmuroiKgYhshMjmhTh9ptWhsF7970j/SbMrsPE1suR5z7DMC+P/Hs+y7ijrQAlhyAgccjbhjPygfeBTjzhNqy28EdkUh8C+DU9+z2v/oyeH791OncxHOs5y2AtTc7nb/f73TWPkD/qwBnjX8BoJ98VQNcC+8AAAA4ZVhJZk1NACoAAAAIAAGHaQAEAAAAAQAAABoAAAAAAAKgAgAEAAAAAQAAAMigAwAEAAAAAQAAAMgAAAAAuJMfrwAAENRJREFUeAHtnXvQFlUdx1FBCUGI5I7wAilohIAmFwsQuzBjVmYxkpNTMmaTRVojk9WkOfmHNUk1keBY2gWV0pRpiiJD0AzNS3kHURFFIEgEEbkY1Pcbzzs87/O+++zz7Lns2bPf38x39nl295zzO59zfns9u9upk0wEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAERCAGAofFUAnPdeiF8vpCx0I9oR5Q94q6YNoZ4vRwaH9F/8F0L7Srojcw3Qa9WhF//xeSBUZAAdK+Qfph1siKRmA6pKKhmHIZO79t24cMN0IboPXQ2opWY/o0tAeS5UCgzAHCLfxoaDw0FhoHjYG4hwjJuBdaAz0KrYL+Bj0Bcb7MMYEyBUhXsDwdmgJNhiZAPDwqom2H08uhZdDvIe55ZCLQNIGTkeIK6C/QbojH+bHpAOr0AHQ5NBCSiUAigaOw5CxoIfQyFFswpNWHFwP+AJ0D8RBSJgKduoHBTOg26HUorROVZfkLYHEp9DZIVjICvJJ0NrQI4iXTsnT6LPXcDD6XQdy7yiInwHOKedAWKEtnKXOa58HsY5AsMgI8hLoQeggqcwe3VfffgaNO5gGh6NaCClwH8ZKmrc6hfA6yJNNZkKyABE6Dz7+BeEVGHdotgwVgrHMTQCiCTYWTvPGloPDL4H4w57gyWaAEJsIv3hVWYOTH4Dnwb4FkgRHgVRUFRhgMXkJbcHCmrIpA3mOxjoEvr0AcLi7LnwCDZDLENpGBQN7DEXjnmzf7ZGEQGAI3lkLccMkCITAWfugwKywGS9AmeR9dBNE9jwjACw6FmAENDsAXuXCQwEhM9kJ/LTuQEAKEbfAWxBGosnAITIUrHBm8KRyX/HsSym60K6rOE8Pe/hGoxDoEnsKyUyDuTUppoexBeNecz3tPKlgr8LHXHRAHT/4L4uEiA30rxPl8ScM+iBuAvC+IwIWmrS9S8EGz+5pOGUmCUPYgxHkCxJcUhOTTa/DnSYgvUeBoWD5jwZcr8LCDwbATasQYHOxsHCR4HPTuisZgyuP9kOoMd9oYg3wUpMd622DJ58/dKDavK1rc4nOoy9UQn0ocBPkwBg4HDv4UCnUY/40+QKiMdALnYhVfAcLDupXQ16EJUAiHm13gx0egOyBeuPDFIq0c+jICkuVMoDPK5yFMWoNlXc73S90FfRoK/YJAC3xcCPEEOWt9babTXgQNEYLxEMdmw3JPwUOnz0I9oaIZ724vgWwyyZIXT9b7FA1ejP4ORqXYqbM0YnUaPoF4CcRj/Bjs46gELw5U19H372/EADKGOvAwKEvj8/Iqn1nnVaIYbQAqxTcrZmFjIw2v5IV8xS3GNu+wTjOa6AQHsO4fIW5heZIbux2JCt4E2ejwWfJ4X+yAi1A/bqW4tarXgNux/AfQ8VDZjHx+AdXj42rZ9WWDHWp95yZ0AN64uxg6OlTHPfnFy9J3Qq4CISlfXmXUYZanRq5XDK+Y8LJsa0Pdi99nQWocQKgYn9tYB7Uy8jWd2OqApvkS+CWK51ZSDZLcDjwn2A/5Cg6Wc2WyO1rik0AId7d91jdrWfOR0GeArMzqqNKJQB4EePn3TchXkPDufilein14Hq2pMq0T4A3En1jPNTlDXmoel7xYS0QgPAL94ZKNEQiN7oUuDQ+BfY+0B7HPNK8c+XzKnzwWfqrHsnIrSgGSG3onBf/cSa4dZ/qujmdrrgiES+AouManHBs9TDJZj6N7o9/ARl/BcPuyE894deleJzm3z7QrZrW0nx3XHAVIXO3J2iz3WKUWj2XlUpQCJBfsTgtVgFjEqwCxCDOQrB6DH7xp6MP4tGPUpgCJr3n5fMwznqrVz1M5uRWjAMkNvdOCn3Ka+6HM+x76GecvBUic7aoAsdSuChBLIAPLZq0nf6L/jogCxFNP8lwMh534sB4+CsmzDAVInvTdlc0XafswBYgPyirDOgFfAcK76VGb9iBxNu8uVItybXxVbNSmAIm3eRUgFtpWAWIBYqBZcOCia9MexDVh5e+MgI8AceZ8KBlrDxJKS9j3QwFigakCxALEQLNQgFhoGAWIBYiBZsGnBWWGBBQghgCVPG4CCpC421e1MySgADEEqORxE1CAxN2+qp0hAQWIIUAlj5uAAiTu9lXtDAkoQAwBKnncBBQgcbevamdIIPrBZoZ8bCfnV3j5DAXF72t0NO1oXu269dZpXTYQ+csMCShAmgPI7yPyTR781ACnfaqmvfCb6lmZ8nntbhXxg6P8Ld6AUCRTg7VtLQbAYGgENLxqynkUt8r8eIysJATKHCDc+o+HxkInVTQK0+6QTAT+T6AsAcJj+NOgyRC/mHsKNAiSiUBdArEGCL+TcTp0ZkUMiFjriqrJXBGIqdPw/ODsiqZjyr2GTASMCBQ9QBgUM6HzIB5C8SRbJgLWCBQxQHj4dC40G5oG6WYnIMjcEChSgAwDgjnQBVBvNziUqwgUj8AEuHw75PMb4CYftyxb2uL1qEg85jnFUqhsHa5o9Y2kuxWnGryLfacCozAbhuL0rIJ7yvFK34X4upqibUXL7G/Bu11990M5Sf8A3LwBaqnvrpaKgF8CeV8i5c28hdAyqAWSiUBQBPLcg4wGicUQBwrKRCBIAnntQT4BGg9CCo4gu4WcaiWQxx7kKhT+LUjDQlpboePpfsx+E9pdmfJ37f/qZdW/+czKNZDMkIDPTsq91QLoIkOfQ07Oq1mvQVsq+nflP+dth3ZW9AamVHWnrv29D8uz2gAk3Jg1cZPpfPahJl0zX93XHoTlLIJmmrucaw4HUPp66FloXUUvYroBegVip3wLkkVCwEeAcM9xM1S04OAW/yHon9Bj0BMQA2MPJCsJAR8BMh8szy8AT+4ZVlS0ClMGAw+ZZCLgjMBXkHOod5l5EnwPdBk0EorJeA7ii3tM3LzWZQZKYyf01VCNlvMwfLoE6gfFagqQwFt2IPzbCjXaaV2vx/OGmyC+waQMpgAJuJV52W855LrTN5I/A+NHEAO2TKYACbi1Pw/fGum8rtfhkPlhAXNy6ZoCxCVdg7y5pd4Bue789fLnTTo+s15mU4AE2vo3w696ndf1skdQ/nGBsvHplgLEJ+0GyzoZ6+V51eoWlK93YR1sLAVIg53W52pLUJjrPURS/vN8VrQAZSlAAmskDlvnOKWkDuxy/vWBsQjBHQVICK1Q5cPP8NtlECTl/SuUy8vKsrYEeB6WxMz2/LYl6187AvxQzC7INvi0/J5Emd3aeaMZJMDL22n8bC2PmriNJwo/BUK+OyoD8pMQHyCStSdwZPtZmpOFgI0AmZWlYMM0VyD9M4Z5xJy8e8yVK1Ld+sJZ35d2n0aZPobpF6kdan2djhm2DqHS8qktO6r/pnuQD4OGaR7NAv0qEvA9vbJkAr2SF2lJMwRMOze3VD6NT/gt9VlgQcvqX1C/g3PbNEDO8Fwj3fNoDHjZRi83RiXDWiYBMgTl+WwIvhnktgx1LGMSjUez1OomATLOkg+NZvNbrLi70ZVLvt7xJa+/teqbBIjvp/OWWat1/BkpQCy1sUmA+GwEjvO621KdY8+G47COjb2SvupnEiDDfTmJch6Htnksr8hF+T70LTKrVN9NAqQlNXd7K+iueeMs39P4qlozjYBJgPRJy9zi8tUW84o9qymxV9Bn/bIGSE846XO4xxqfUApcFr8hP6nA/gfnetYAebvnmmz2XF5Ri5sGx/XYscXWyxog3FL5NH4qQJZOgGPjZBYJKEAswsw5qyNQftlfd2S9CbIGCBvDp/EBKVl9AtOxmPdAZBYJZA0Q38PN9YRceqPPTl9FazRLIGuA+P6Kku9HepvlmPf6HDSqwysHrZA1QPY68KVelkfXW6hlneaAgc/L7qVBnjVAXvdMSA8AJQPnuCt+70TmgEDWANnhwJd6WZb1Le31mLQu+xp+6CUNrTQsT7MGCE/Sd1r2pV52LfUWlnjZCag7D69kjghkDRC6s8mRTx1l6/vZk458CHHej+FUlxAdk08Hvwib9koYW8v5gjh1hLa97nP4a4uvST5tvYrsn8keZL1HFhxfNN5jeaEXNQoOfj90J2PwzyRA1noGcI7n8kItjpe874B0Yu6hhYoUIOeBx2EemIRcBIf4LIb4uQlZ4AROhH8mx65Z0r4/cCYu3ePG4cYcmKe1k8s6Fzpv7n04iDANoM3lKwpNLLvzDI4FkE2WtvLKXqsSpLwvh0abVgKu1VXkQM1FkK0ObTufal/1u4bANTk03GqU2bXGj1j/8u3590C2O7XN/GJlb6VeH8qp8b5nxfuwM5kK9zbmxLeZAAqbYs7e8f6E7/MQNh5fJHd+znV3VTzP7b4JcThPMx01r3VdcYgm3yU5NSSH3Md2VWs06pTHeZ1JcEXTkV1V5DPI2ASwSdo9KJv3R4pufCDsWogPopnwyCNt0dk7978HSuBYqTwah2XycOvbUBEfGOKNvwug9VBe/EzLheuyNAIhXIZ8EE6OTHM0kOUM5guh5yDTDpp3+kCQhu3G5EAamuclHMTn+8V2jbZOf6w4F1oHuezYPFSbBXHv5LIc5i1rkMDfsZ7rxmg0/1fhy3cgdsi8jTf6PgrxYoavc4wvVirN+0XcaDTKLct6laI0SSPATpAFsMs07By3QfTN59sgh6C8i6G7ID556bKOtXkvQHnV9gD+1K5j8391WfqdQuARx41h0rDb4Rs77JehcRC37DasNzLh5ebLoVuhZyETP03S/hll1z5YNs+xP8g+XuMgOJt2BjJbbjNDh3nxRtxaaDXEO9aboC0Qv4O4B9oHca/DS7C8IUrxGYxBED+SObgyDeV85x/wZypU+66AmZi3GHJltvuQKz+DyZeNYbIVVNrm+T0P5v0SegCD2SXThGI1O4nAACzgSbLLRlHeh/hy7/fOpMaozH/ZYXukFF3sxSZPFCbVnIcqX0haqPlWCWxGbjys5f2Ueraq3kItSybgIkBYGg+zbkguVkssEOD50pnQmgbyUoA0AMn3KjzBdX2JsayHWhvA9sQmGnQi1nXFqgk3tGotgT6YwStFrhqnjPlyjzG0FnTKf17S5pU5F7xSitbiNALDsYLLk0QXjR5qng+DJTc6Wex+JHJRryy+FCaNq3OQagAv4M8UaH31TP1umsDtSEGOW5tOeTCBzkMygvOVbCAKehRysRWLPc+rwc30hhw/sOOCE7KV2SLAtwL+GnLRUDHmuQ2sOI7MhnED5YKRDd+URw2Bi/A/j2fZXXQQV3nykGhoDTfTvy8iA9v+mvqk9AkETsL8xyHbDVb0/DgG7EqodtAhZhnbrcjBNh9jp5RBMoGuWHQVxAGCthuuiPnxHG0M5MrmIGPbXFz5qnyrCAzDbw5Ft914RcmP5xpfgjpDLu1UZG6biUt/lXcNgRn4H/IzJbY7Fx/o4lei3lHDwdVfHrbZfsGGK1+Vbx0CH8SyFZDtDhlKfuykP4T4TIlvuxcF2uTg23+VV0VgEn7fAtne6tnsIM3kxYez5kJ9obzsWhTcjM9p6+ZVD5VbReAY/J4NrYQOQGmNFtLyl+DvfOi9UAjG+yo2+YRQJ2c+/A8iwu6luZro8QAAAABJRU5ErkJggg==\n", 480 | "text/plain": [ 481 | "" 482 | ] 483 | }, 484 | "metadata": {}, 485 | "output_type": "display_data" 486 | } 487 | ], 488 | "source": [ 489 | "def draw_outline(font, ch, scale_num):\n", 490 | " path = draw.BezierPath()\n", 491 | " glyph = font.getGlyphSet()[ch]\n", 492 | " glyph.draw(path)\n", 493 | " path.scale(scale_num)\n", 494 | " return path\n", 495 | "\n", 496 | "startdraw(200,200)\n", 497 | "draw.drawPath(draw_outline(font,\"a\",0.3))\n", 498 | "show()" 499 | ] 500 | }, 501 | { 502 | "cell_type": "markdown", 503 | "metadata": {}, 504 | "source": [ 505 | "### Modifying Outline Paths ###\n", 506 | "Using the `get_outline` function we made before, let's draw the letter 'a'. I'm parsing information from `get_outline`, making a `BezierPath` object, and adding all the paths in there. Take a look at the [Drawbot Documentation for BezierPath](http://www.drawbot.com/content/shapes/bezierPath.html) if you need a refresher. You'll notice that I'm using the `.scale` to reduce the size before doing the line too." 507 | ] 508 | }, 509 | { 510 | "cell_type": "code", 511 | "execution_count": 15, 512 | "metadata": {}, 513 | "outputs": [ 514 | { 515 | "data": { 516 | "image/png": "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\n", 517 | "text/plain": [ 518 | "" 519 | ] 520 | }, 521 | "metadata": {}, 522 | "output_type": "display_data" 523 | } 524 | ], 525 | "source": [ 526 | "path_orig = get_outline(font, \"a\")\n", 527 | "startdraw(200,200)\n", 528 | "\n", 529 | "paths = []\n", 530 | "for i in path_orig:\n", 531 | " if i[0] == 'moveTo':\n", 532 | " path = draw.BezierPath()\n", 533 | " path.moveTo((i[1][0]))\n", 534 | " if i[0] == 'lineTo':\n", 535 | " path.lineTo((i[1][0]))\n", 536 | " if i[0] == 'qCurveTo':\n", 537 | " path.qCurveTo(*(i[1]))\n", 538 | " if i[0] == 'closePath':\n", 539 | " path.closePath()\n", 540 | " paths.append(path)\n", 541 | "\n", 542 | "finalpath = paths[0].difference(paths[1])\n", 543 | "finalpath.scale(0.3) #let's scale it down!\n", 544 | "draw.fill(0.5)\n", 545 | "draw.stroke(0)\n", 546 | "draw.drawPath(finalpath)\n", 547 | "show()" 548 | ] 549 | }, 550 | { 551 | "cell_type": "markdown", 552 | "metadata": {}, 553 | "source": [ 554 | "Ta-da! We made just one single letter, no big deal, you might say. Let's start manipulating all the points to give it a distorted filter:" 555 | ] 556 | }, 557 | { 558 | "cell_type": "code", 559 | "execution_count": 20, 560 | "metadata": {}, 561 | "outputs": [ 562 | { 563 | "data": { 564 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAAEGWlDQ1BrQ0dDb2xvclNwYWNlR2VuZXJpY1JHQgAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia219thzg25abkRE/BpDc3pqvphHvRFys2weqvp+krbWKIX7nhDbzLOItiM8358pTwdirqpPFnMF2xLc1WvLyOwTAibpbmvHHcvttU57y5+XqNZrLe3lE/Pq8eUj2fXKfOe3pfOjzhJYtB/yll5SDFcSDiH+hRkH25+L+sdxKEAMZahrlSX8ukqMOWy/jXW2m6M9LDBc31B9LFuv6gVKg/0Szi3KAr1kGq1GMjU/aLbnq6/lRxc4XfJ98hTargX++DbMJBSiYMIe9Ck1YAxFkKEAG3xbYaKmDDgYyFK0UGYpfoWYXG+fAPPI6tJnNwb7ClP7IyF+D+bjOtCpkhz6CFrIa/I6sFtNl8auFXGMTP34sNwI/JhkgEtmDz14ySfaRcTIBInmKPE32kxyyE2Tv+thKbEVePDfW/byMM1Kmm0XdObS7oGD/MypMXFPXrCwOtoYjyyn7BV29/MZfsVzpLDdRtuIZnbpXzvlf+ev8MvYr/Gqk4H/kV/G3csdazLuyTMPsbFhzd1UabQbjFvDRmcWJxR3zcfHkVw9GfpbJmeev9F08WW8uDkaslwX6avlWGU6NRKz0g/SHtCy9J30o/ca9zX3Kfc19zn3BXQKRO8ud477hLnAfc1/G9mrzGlrfexZ5GLdn6ZZrrEohI2wVHhZywjbhUWEy8icMCGNCUdiBlq3r+xafL549HQ5jH+an+1y+LlYBifuxAvRN/lVVVOlwlCkdVm9NOL5BE4wkQ2SMlDZU97hX86EilU/lUmkQUztTE6mx1EEPh7OmdqBtAvv8HdWpbrJS6tJj3n0CWdM6busNzRV3S9KTYhqvNiqWmuroiKgYhshMjmhTh9ptWhsF7970j/SbMrsPE1suR5z7DMC+P/Hs+y7ijrQAlhyAgccjbhjPygfeBTjzhNqy28EdkUh8C+DU9+z2v/oyeH791OncxHOs5y2AtTc7nb/f73TWPkD/qwBnjX8BoJ98VQNcC+8AAAA4ZVhJZk1NACoAAAAIAAGHaQAEAAAAAQAAABoAAAAAAAKgAgAEAAAAAQAAASygAwAEAAAAAQAAASwAAAAA+6ig3QAAK5RJREFUeAHtnQm0VNWZ78+dZ+Z5ngdlkFHQGNoJQYU4BJeixvXI6vap3Z2npvNW0qbTdMeYlTZpjUbbuX2SvKWNtGiUKA4EgYAoiAKCIIgyz5c7wx3e77vvXrxc7lRVZ6qq/1lr36pbdWoPv1PnX3t/+9vfdhwdIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIuAHgRQ/ClEZzRJI450cUjbJnteQqutSKY/lJB0iIAJ1BCRY7n8VepLlAFI/Uq/U1NS+GRkZA3jsVVNT04nUrqqqKr+ystJEipdTT6WlpVXxWM17qSS7JjW8n2H/814F7xXzeDglJWU/n9158uTJTZyznfR5XTKR0yECCU9AguXOJT6PbNZbVpmZmWXt2rWr6NSpU0r79u2zSVn5+flOQUGBk52dfTrl5OSYWrVYOuLkIE5OeXm5U1RU5JSUlNQ+Hjp0qIJUfuTIkRRey6bMbZy7/NSpU2+R4TLS8RYz1psiEKcEJFixX7jrEIwF1113Xc6gQYNMsGLPMYIcTMz279/v7N27t3rLli1Fe/bsyaFHt53XnyebhaQdEWSnU0Ug1AQkWLFdnlqxmjdvXk6PHj1iy8mlT1uvbNeuXc6nn35atnnzZrOJfV5RUfFrHk28ZBNzibOyCYaABCt67qETq8ZNqa6udrZu3eqsXr3ael41/P8A6THOO9H4XP0vAvFAQIL1zVUyI3hXUre6x848FtSn9PT0fIzedo4Zw7thFJ991113OV272kfCfxw+fNh55513SrZt21ZF3X9MjZ8kVYa/5qqhCHxD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\n", 565 | "text/plain": [ 566 | "" 567 | ] 568 | }, 569 | "metadata": {}, 570 | "output_type": "display_data" 571 | } 572 | ], 573 | "source": [ 574 | "import random\n", 575 | "path_orig = get_outline(font, \"a\")\n", 576 | "startdraw(300,300)\n", 577 | "paths = []\n", 578 | "for i in path_orig:\n", 579 | " disturbance = random.randint(-35, 35)\n", 580 | " if i[0] == 'moveTo':\n", 581 | " x = i[1][0][0] + disturbance\n", 582 | " y = i[1][0][1] + disturbance\n", 583 | " path = draw.BezierPath()\n", 584 | " path.moveTo((x,y))\n", 585 | " if i[0] == 'lineTo':\n", 586 | " x = i[1][0][0] + disturbance\n", 587 | " y = i[1][0][1]+ disturbance\n", 588 | " path.lineTo((x,y))\n", 589 | " if i[0] == 'qCurveTo':\n", 590 | " temp = []\n", 591 | " for cord in i[1]:\n", 592 | " x = cord[0] + disturbance\n", 593 | " y = cord[1] + disturbance\n", 594 | " temp.append((x,y))\n", 595 | " path.qCurveTo(*temp)\n", 596 | " if i[0] == 'closePath':\n", 597 | " path.closePath()\n", 598 | " paths.append(path)\n", 599 | "finalpath = paths[0].difference(paths[1])\n", 600 | "finalpath.scale(0.3) #let's scale it down!\n", 601 | "draw.fill(0.5)\n", 602 | "draw.stroke(0)\n", 603 | "draw.drawPath(finalpath)\n", 604 | "show()" 605 | ] 606 | }, 607 | { 608 | "cell_type": "markdown", 609 | "metadata": {}, 610 | "source": [ 611 | "## Parting words ##\n", 612 | "See, lots of fun things! :-) That's it from me (at least for now.)\n", 613 | "\n", 614 | "Check out the wiki Page on Github for fontTools if you would like to dig deeper:\n", 615 | "[Wiki Page on Github for FontTools](https://fonttools.readthedocs.io/en/latest/#) \n", 616 | "\n", 617 | "Wish you the best of luck on your font-navigating journey!" 618 | ] 619 | } 620 | ], 621 | "metadata": { 622 | "kernelspec": { 623 | "display_name": "Python 3", 624 | "language": "python", 625 | "name": "python3" 626 | }, 627 | "language_info": { 628 | "codemirror_mode": { 629 | "name": "ipython", 630 | "version": 3 631 | }, 632 | "file_extension": ".py", 633 | "mimetype": "text/x-python", 634 | "name": "python", 635 | "nbconvert_exporter": "python", 636 | "pygments_lexer": "ipython3", 637 | "version": "3.7.7" 638 | } 639 | }, 640 | "nbformat": 4, 641 | "nbformat_minor": 4 642 | } 643 | -------------------------------------------------------------------------------- /FontTools & DrawBot/NotoSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynneyun/Tutorials/7a095f27f2bb59d3053cc346cc0a945618c20754/FontTools & DrawBot/NotoSans-Regular.ttf -------------------------------------------------------------------------------- /FontTools & DrawBot/drawBotImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynneyun/Tutorials/7a095f27f2bb59d3053cc346cc0a945618c20754/FontTools & DrawBot/drawBotImage.png -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Lynne Yun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tutorials 2 | Random assortment of tutorial posts, mostly Jupyter Notebook formats 3 | --------------------------------------------------------------------------------