6

Does anyone know of a library or tool ( preferrably Python)to convert true type font glyphs into polygonal shapes (for testing GIS algorithms)?

1 Answer 1

11

1) You can use TTFQuery as in Retrieving bounding box and bezier curve data for all glyphs in a .ttf font file in python

from ttfquery import describe
from ttfquery import glyphquery
import ttfquery.glyph as glyph
char = "a"
font_url ="arial.ttf"
font = describe.openFont(font_url)
g = glyph.Glyph(char) # or g = glyph.Glyph(glyphquery.glyphName(font, char))

Now use glyph calculateContours()

contours = g.calculateContours(font) 
# number of contours (interior, exterior)
print len(contours)
2
exterior = contours[0]
print exterior # coordinates, tag
[((828, 131), 1), ((728, 46), 0), ((543, -24), 0), ((437, -24), 1), ((262, -24), 0), ((74, 147), 0), ((74, 280), 1), ((74, 358), 0), ((145, 487), 0), ((260, 565), 0), ((332, 585), 1), ((385, 599), 0), ((492, 612), 1), ((710, 638), 0), ((813, 674), 1), ((814, 711), 0), ((814, 721), 1), ((814, 831), 0), ((763, 876), 1), ((694, 937), 0), ((558,937), 1), ((431, 937), 0), ((310, 848), 0), ((281, 735), 1), ((105, 759), 1), ((129, 872), 0), ((239, 1011), 0), ((447, 1086), 0), ((584, 1086), 1), ((720, 1086), 0), ((890, 1022), 0), ((970, 925), 0), ((986, 851), 1), ((995, 805), 0), ((995, 685), 1), ((995, 445), 1), ((995, 194), 0), ((1018, 61), 0), ((1052, 0), 1), ((864, 0), 1), ((836, 56), 0), ((828, 131), 1)]
interior = contours[1]
print interior
[((813, 533), 1), ((715, 493), 0), ((519, 465), 1), ((408, 449), 0), ((316, 409), 0), ((266, 332), 0), ((266, 285), 1), ((266, 213), 0), ((375, 117), 0), ((480, 117), 1), ((584, 117), 0), ((746, 208), 0), ((784, 287), 1), ((813, 348), 0), ((813, 467), 1), ((813, 533), 1)]

Convert to WKT format with Shapely for example

from shapely.geometry import Polygon
print Polygon([i[0] for i in exterior]).wkt
POLYGON ((828 131, 728 46, 543 -24, 437 -24, 262 -24, 74 147, 74 280, 74 358, 145 487, 260 565, 332 585, 385 599, 492 612, 710 638, 813 674, 814 711, 814 721, 814 831, 763 876, 694 937, 558 937, 431 937, 310 848, 281 735, 105 759, 129 872, 239 1011, 447 1086, 584 1086, 720 1086, 890 1022, 970 925, 986 851, 995 805, 995 685, 995 445, 995 194,1018 61, 1052 0, 864 0, 836 56, 828 131))
print Polygon([i[0] for i in interior]).wkt
POLYGON ((813 533, 715 493, 519 465, 408 449, 316 409, 266 332, 266 285, 266 213, 375 117, 480 117, 584 117, 746 208, 784 287, 813 348, 813 467, 813 533))

enter image description here

2) you can also use freetype-py

from freetype import *
face = Face('arial.ttf')
flags = FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP
face.set_char_size( 32*64  )
face.load_char('a', flags )
slot = face.glyph
outline = slot.outline
print outline.points
[(828, 128), (728, 58), (543, 0), (437, 0), (262, 0), (74, 172), (74, 307), (74, 385), (145, 515), (260, 594), (332, 614), (385, 628), (492, 641), (710, 668), (813, 704), (814, 731), (814, 738), (814, 819), (763, 851), (694, 896), (558, 896), (431, 896), (310, 840), (281, 768), (105, 768), (129, 879), (239, 1015), (447, 1088), (584, 1088), (720, 1088), (890, 1023), (970, 924), (986, 848), (995, 801), (995, 696), (995, 485), (995, 264), (1018, 83), (1052, 0), (864, 0), (836, 55), (813, 512), (715, 481), (519, 460), (408, 447), (316, 417), (266, 357), (266, 321), (266, 266), (375, 192), (480, 192), (584, 192), (746, 277), (784, 334), (813, 378), (813, 464)]
print(Polygon(outline.points).wkt)
POLYGON ((828 128, 728 58, 543 0, 437 0, 262 0, 74 172, 74 307, 74 385, 145 515, 260 594, 332 614, 385 628, 492 641, 710 668, 813 704, 814 731, 814 738, 814 819, 763 851, 694 896, 558 896, 431 896, 310 840, 281 768, 105 768, 129 879, 239 1015, 447 1088, 584 1088, 720 1088, 890 1023, 970 924, 986 848, 995 801, 995 696, 995 485, 995 264, 1018 83, 1052 0, 864 0, 836 55, 813 512, 715 481, 519 460, 408 447, 316 417, 266 357, 266 321, 266 266, 375 192, 480 192, 584 192, 746 277, 784 334, 813 378, 813 464, 828 128))

enter image description here

1
  • fantastic, thank you very very much, just what I was after.
    – MartinT
    Commented Oct 11, 2017 at 22:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.