1# from: http://cairographics.org/freetypepython/
2
3import ctypes as ct
4import cairo
5
6
7class PycairoContext(ct.Structure):
8    _fields_ = \
9        [
10            ("PyObject_HEAD", ct.c_byte * object.__basicsize__),
11            ("ctx", ct.c_void_p),
12            ("base", ct.c_void_p),
13        ]
14
15
16_initialized = False
17
18
19def create_cairo_font_face_for_file(filename, faceindex=0, loadoptions=0):
20    """ Given the name of a font file, and optional faceindex to pass to FT_New_Face
21        and loadoptions to pass to cairo_ft_font_face_create_for_ft_face, creates
22        a cairo.FontFace object that may be used to render text with that font. """
23    global _initialized
24    global _freetype_so
25    global _cairo_so
26    global _ft_lib
27    global _ft_destroy_key
28    global _surface
29
30    CAIRO_STATUS_SUCCESS = 0
31    FT_Err_Ok = 0
32
33    if not _initialized:
34        # find shared objects
35        _freetype_so = ct.CDLL("libfreetype.so.6")
36        _cairo_so = ct.CDLL("libcairo.so.2")
37        _cairo_so.cairo_ft_font_face_create_for_ft_face.restype = ct.c_void_p
38        _cairo_so.cairo_ft_font_face_create_for_ft_face.argtypes = [ct.c_void_p, ct.c_int]
39        _cairo_so.cairo_font_face_get_user_data.restype = ct.c_void_p
40        _cairo_so.cairo_font_face_get_user_data.argtypes = (ct.c_void_p, ct.c_void_p)
41        _cairo_so.cairo_font_face_set_user_data.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
42        _cairo_so.cairo_set_font_face.argtypes = [ct.c_void_p, ct.c_void_p]
43        _cairo_so.cairo_font_face_status.argtypes = [ct.c_void_p]
44        _cairo_so.cairo_font_face_destroy.argtypes = (ct.c_void_p,)
45        _cairo_so.cairo_status.argtypes = [ct.c_void_p]
46        # initialize freetype
47        _ft_lib = ct.c_void_p()
48        status = _freetype_so.FT_Init_FreeType(ct.byref(_ft_lib))
49        if status != FT_Err_Ok:
50            raise RuntimeError("Error %d initializing FreeType library." % status)
51
52        _surface = cairo.ImageSurface(cairo.FORMAT_A8, 0, 0)
53        _ft_destroy_key = ct.c_int()  # dummy address
54        _initialized = True
55
56    ft_face = ct.c_void_p()
57    cr_face = None
58    try:
59        # load FreeType face
60        status = _freetype_so.FT_New_Face(_ft_lib, filename.encode("utf-8"), faceindex, ct.byref(ft_face))
61        if status != FT_Err_Ok:
62            raise RuntimeError("Error %d creating FreeType font face for %s" % (status, filename))
63
64        # create Cairo font face for freetype face
65        cr_face = _cairo_so.cairo_ft_font_face_create_for_ft_face(ft_face, loadoptions)
66        status = _cairo_so.cairo_font_face_status(cr_face)
67        if status != CAIRO_STATUS_SUCCESS:
68            raise RuntimeError("Error %d creating cairo font face for %s" % (status, filename))
69
70        # Problem: Cairo doesn't know to call FT_Done_Face when its font_face object is
71        # destroyed, so we have to do that for it, by attaching a cleanup callback to
72        # the font_face. This only needs to be done once for each font face, while
73        # cairo_ft_font_face_create_for_ft_face will return the same font_face if called
74        # twice with the same FT Face.
75        # The following check for whether the cleanup has been attached or not is
76        # actually unnecessary in our situation, because each call to FT_New_Face
77        # will return a new FT Face, but we include it here to show how to handle the
78        # general case.
79        if _cairo_so.cairo_font_face_get_user_data(cr_face, ct.byref(_ft_destroy_key)) is None:
80            status = _cairo_so.cairo_font_face_set_user_data(
81                cr_face,
82                ct.byref(_ft_destroy_key),
83                ft_face,
84                _freetype_so.FT_Done_Face)
85            if status != CAIRO_STATUS_SUCCESS:
86                raise RuntimeError("Error %d doing user_data dance for %s" % (status, filename))
87            ft_face = None  # Cairo has stolen my reference
88
89        # set Cairo font face into Cairo context
90        cairo_ctx = cairo.Context(_surface)
91        cairo_t = PycairoContext.from_address(id(cairo_ctx)).ctx
92        _cairo_so.cairo_set_font_face(cairo_t, cr_face)
93        status = _cairo_so.cairo_font_face_status(cairo_t)
94        if status != CAIRO_STATUS_SUCCESS:
95            raise RuntimeError("Error %d creating cairo font face for %s" % (status, filename))
96
97    finally:
98        _cairo_so.cairo_font_face_destroy(cr_face)
99        _freetype_so.FT_Done_Face(ft_face)
100
101    # get back Cairo font face as a Python object
102    face = cairo_ctx.get_font_face()
103    return face
104
105
106if __name__ == '__main__':
107    face = create_cairo_font_face_for_file("../../../pieces/ttf/harlequin.ttf",
108                                           0)
109
110    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 200, 128)
111
112    ctx = cairo.Context(surface)
113
114    ctx.set_font_face(face)
115    ctx.set_font_size(30)
116    ctx.move_to(0, 44)
117    ctx.show_text("pnbrqk")
118
119    ctx.move_to(0, 74)
120    ctx.show_text("omvtwl")
121
122    del ctx
123
124    surface.write_to_png("0pieces.png")
125