File: CTFont.pyx

package info (click to toggle)
python-enable 4.3.0-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 7,280 kB
  • ctags: 13,899
  • sloc: cpp: 48,447; python: 28,502; ansic: 9,004; makefile: 315; sh: 44
file content (189 lines) | stat: -rw-r--r-- 6,364 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# :Author:    John Wiggins
# :Copyright: 2011, Enthought, Inc.
# :License:   BSD Style


include "CoreFoundation.pxi"
include "CoreGraphics.pxi"
include "CoreText.pxi"


cdef object _cf_string_to_pystring(CFStringRef cf_string):
    cdef char* c_string
    cdef char buf[256]
    c_string = CFStringGetCStringPtr(cf_string, kCFStringEncodingMacRoman)
    if c_string == NULL:
        success = CFStringGetCString(cf_string, buf, 256,
                    kCFStringEncodingMacRoman)
        retval = str(buf)
    else:
        retval = str(c_string)
    return retval

cdef CFArrayRef _get_system_fonts():
        cdef CFIndex value = 1
        cdef CFNumberRef cf_number
        cdef CFMutableDictionaryRef cf_options_dict
        cdef CTFontCollectionRef cf_font_collection
        cdef CFArrayRef cf_font_descriptors

        cf_options_dict = CFDictionaryCreateMutable(NULL, 0,
                            &kCFTypeDictionaryKeyCallBacks,
                            &kCFTypeDictionaryValueCallBacks)

        if cf_options_dict != NULL:
            cf_number = CFNumberCreate(NULL, kCFNumberCFIndexType, &value)
            CFDictionaryAddValue(cf_options_dict, <void*>kCTFontCollectionRemoveDuplicatesOption,
                <void*>cf_number)
            CFRelease(cf_number)
        else:
            msg = "unknown error building options dictionary for font list"
            raise RuntimeError(msg)

        cf_font_collection = CTFontCollectionCreateFromAvailableFonts(cf_options_dict)
        CFRelease(cf_options_dict)

        cf_font_descriptors = CTFontCollectionCreateMatchingFontDescriptors(cf_font_collection)
        CFRelease(cf_font_collection)

        return cf_font_descriptors

cdef class CTFont:
    cdef CTFontRef ct_font
    def __cinit__(self, *args, **kwargs):
        self.ct_font = NULL

    def __dealloc__(self):
        if self.ct_font != NULL:
            CFRelease(self.ct_font)

    cpdef size_t get_pointer(self):
        return <size_t>self.ct_font

    cdef set_pointer(self, CTFontRef pointer):
        self.ct_font = pointer


cdef class CTFontStyle:
    cdef CTFontDescriptorRef ct_font_descriptor
    cdef CFDictionaryRef attribute_dictionary
    cdef readonly object family_name
    cdef readonly object style
    
    def __cinit__(self, *args, **kwargs):
        self.ct_font_descriptor = NULL
        self.attribute_dictionary = NULL

    def __dealloc__(self):
        if self.attribute_dictionary != NULL:
            CFRelease(self.attribute_dictionary)
        if self.ct_font_descriptor != NULL:
            CFRelease(self.ct_font_descriptor)

    def __init__(self, name, style='regular'):
        self.family_name = name
        self.style = style
        self.attribute_dictionary = self._build_attribute_dictionary(name, style)
        self.ct_font_descriptor = CTFontDescriptorCreateWithAttributes(self.attribute_dictionary)

    def get_descent_ascent(self, object text):
        """ Get the descent and ascent font metrics for a given string of text.
        """

        cdef CTFontRef ct_font
        cdef float ascent, descent

        ct_font = CTFontCreateWithFontDescriptor(self.ct_font_descriptor, 0.0, NULL)
        ascent = CTFontGetAscent(ct_font)
        descent = CTFontGetDescent(ct_font)
        CFRelease(ct_font)

        return descent, ascent

    def get_font(self, float font_size):
        """ Get a CTFont matching the descriptor at the given size.
        """
        cdef CTFontRef ct_font
        
        ct_font = CTFontCreateWithFontDescriptor(self.ct_font_descriptor,
                    font_size, NULL)
        font = CTFont()
        font.set_pointer(ct_font)
        return font

    property postcript_name:
        def __get__(self):
            cdef CFStringRef cf_ps_name
            cf_ps_name = <CFStringRef>CTFontDescriptorCopyAttribute(self.ct_font_descriptor,
                                        kCTFontNameAttribute)
            retval = _cf_string_to_pystring(cf_ps_name)
            CFRelease(cf_ps_name)

            return retval

    cdef CFDictionaryRef _build_attribute_dictionary(self, name, style):
        cdef CFStringRef cf_name, cf_style
        cdef CFMutableDictionaryRef cf_dict

        mac_name = name.strip().encode('utf-8')
        cf_name = CFStringCreateWithCString(NULL, mac_name,
            kCFStringEncodingUTF8)

        style = style.title()
        mac_style = style.strip().encode('utf-8')
        cf_style = CFStringCreateWithCString(NULL, mac_style,
            kCFStringEncodingUTF8)

        cf_dict = CFDictionaryCreateMutable(NULL, 0,
                    &kCFTypeDictionaryKeyCallBacks,
                    &kCFTypeDictionaryValueCallBacks)

        if cf_dict != NULL:
            CFDictionaryAddValue(cf_dict, <void*>kCTFontFamilyNameAttribute, cf_name)
            CFDictionaryAddValue(cf_dict, <void*>kCTFontStyleNameAttribute, cf_style)
        else:
            msg = "unknown error building descriptor dictionary for font %r"%mac_name
            raise RuntimeError(msg)

        CFRelease(cf_name)
        CFRelease(cf_style)

        return cf_dict


cdef class FontLookup:
    cdef readonly object cache
    cdef public object default_font

    def lookup(self, name=None, style='regular'):
        return CTFontStyle(name or self.default_font, style)

    def names(self):
        cdef CFIndex idx, count
        cdef CFStringRef cf_fam_name
        cdef CFArrayRef cf_font_descriptors
        cdef CTFontDescriptorRef cf_font_descriptor

        cf_font_descriptors = _get_system_fonts()
        families = []
        count = CFArrayGetCount(cf_font_descriptors)
        for idx from 0 <= idx < count:
            cf_font_descriptor = <CTFontDescriptorRef>CFArrayGetValueAtIndex(cf_font_descriptors, idx)
            cf_fam_name = <CFStringRef>CTFontDescriptorCopyAttribute(cf_font_descriptor,
                                        kCTFontFamilyNameAttribute)
            families.append(_cf_string_to_pystring(cf_fam_name))
            CFRelease(cf_fam_name)
        CFRelease(cf_font_descriptors)

        families.sort()
        return list(set(families))

    def styles(self, font_name):
        raise NotImplementedError

    def list_fonts(self):
        for name in self.names():
            print name, self.styles(name)

default_font_info = FontLookup()
default_font_info.default_font = 'Helvetica'