-
Notifications
You must be signed in to change notification settings - Fork 87
/
__init__.py
2413 lines (1864 loc) · 92.4 KB
/
__init__.py
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
#
# FreeType high-level python API - Copyright 2011-2015 Nicolas P. Rougier
# Distributed under the terms of the new BSD license.
#
# -----------------------------------------------------------------------------
'''
FreeType high-level python API
This the bindings for the high-level API of FreeType (that must be installed
somewhere on your system).
Note: C Library will be searched using the ctypes.util.find_library. However,
this search might fail. In such a case (or for other reasons), you may
have to specify an explicit path below.
'''
import io
import sys
from ctypes import *
import ctypes.util
import struct
from freetype.raw import *
# Hack to get unicode class in python3
PY3 = sys.version_info[0] == 3
if PY3: unicode = str
def unmake_tag(i):
# roughly opposite of FT_MAKE_TAG, converts 32-bit int to Python string
# could do with .to_bytes if limited to Python 3.2 or higher...
b = struct.pack('>I', i)
return b.decode('ascii', errors='replace')
_handle = None
FT_Library_filename = filename
class _FT_Library_Wrapper(FT_Library):
'''Subclass of FT_Library to help with calling FT_Done_FreeType'''
# for some reason this doesn't get carried over and ctypes complains
_type_ = FT_Library._type_
# Store ref to FT_Done_FreeType otherwise it will be deleted before needed.
_ft_done_freetype = FT_Done_FreeType
def __del__(self):
# call FT_Done_FreeType
# This does not work properly (seg fault on sime system (OSX))
# self._ft_done_freetype(self)
pass
def _init_freetype():
global _handle
_handle = _FT_Library_Wrapper()
error = FT_Init_FreeType( byref(_handle) )
if error: raise FT_Exception(error)
try:
set_lcd_filter( FT_LCD_FILTER_DEFAULT )
except:
pass
# -----------------------------------------------------------------------------
# High-level API of FreeType 2
# -----------------------------------------------------------------------------
def get_handle():
'''
Get unique FT_Library handle
'''
if not _handle:
_init_freetype()
return _handle
def version():
'''
Return the version of the FreeType library being used as a tuple of
( major version number, minor version number, patch version number )
'''
amajor = FT_Int()
aminor = FT_Int()
apatch = FT_Int()
library = get_handle()
FT_Library_Version(library, byref(amajor), byref(aminor), byref(apatch))
return (amajor.value, aminor.value, apatch.value)
# -----------------------------------------------------------------------------
# Stand alone functions
# -----------------------------------------------------------------------------
def set_lcd_filter(filt):
'''
This function is used to apply color filtering to LCD decimated bitmaps,
like the ones used when calling FT_Render_Glyph with FT_RENDER_MODE_LCD or
FT_RENDER_MODE_LCD_V.
**Note**
This feature is always disabled by default. Clients must make an explicit
call to this function with a 'filter' value other than FT_LCD_FILTER_NONE
in order to enable it.
Due to PATENTS covering subpixel rendering, this function doesn't do
anything except returning 'FT_Err_Unimplemented_Feature' if the
configuration macro FT_CONFIG_OPTION_SUBPIXEL_RENDERING is not defined in
your build of the library, which should correspond to all default builds of
FreeType.
The filter affects glyph bitmaps rendered through FT_Render_Glyph,
FT_Outline_Get_Bitmap, FT_Load_Glyph, and FT_Load_Char.
It does not affect the output of FT_Outline_Render and
FT_Outline_Get_Bitmap.
If this feature is activated, the dimensions of LCD glyph bitmaps are
either larger or taller than the dimensions of the corresponding outline
with regards to the pixel grid. For example, for FT_RENDER_MODE_LCD, the
filter adds up to 3 pixels to the left, and up to 3 pixels to the right.
The bitmap offset values are adjusted correctly, so clients shouldn't need
to modify their layout and glyph positioning code when enabling the filter.
'''
library = get_handle()
error = FT_Library_SetLcdFilter(library, filt)
if error: raise FT_Exception(error)
def set_lcd_filter_weights(a,b,c,d,e):
'''
Use this function to override the filter weights selected by
FT_Library_SetLcdFilter. By default, FreeType uses the quintuple (0x00,
0x55, 0x56, 0x55, 0x00) for FT_LCD_FILTER_LIGHT, and (0x10, 0x40, 0x70,
0x40, 0x10) for FT_LCD_FILTER_DEFAULT and FT_LCD_FILTER_LEGACY.
**Note**
Only available if version > 2.4.0
'''
if version()>=(2,4,0):
library = get_handle()
weights = FT_Char(5)(a,b,c,d,e)
error = FT_Library_SetLcdFilterWeights(library, weights)
if error: raise FT_Exception(error)
else:
raise RuntimeError(
'set_lcd_filter_weights require freetype > 2.4.0')
def _encode_filename(filename):
encoded = filename.encode(sys.getfilesystemencoding())
if "?" not in filename and b"?" in encoded:
# A bug, decoding mbcs always ignore exception, still isn't fixed in Python 2,
# view http://bugs.python.org/issue850997 for detail
raise UnicodeError()
return encoded
# -----------------------------------------------------------------------------
# Direct wrapper (simple renaming)
# -----------------------------------------------------------------------------
Vector = FT_Vector
Matrix = FT_Matrix
# -----------------------------------------------------------------------------
# Handling for FT_Done_MM_Var, which was added in FreeType 2.9. Prior to that,
# we need to import libc and use libc free on the memory allocated for the
# FT_MM_Var data structure. See Face.get_variation_info().
# -----------------------------------------------------------------------------
if version() < (2,9,1):
if platform.system() == "Windows":
libcpath = ctypes.util.find_library("msvcrt")
else:
libcpath = ctypes.util.find_library("c")
libc = CDLL(libcpath)
libc.free.argtypes = [c_void_p]
libc.free.restype = None
def FT_Done_MM_Var_func(p):
libc.free(p)
else:
def FT_Done_MM_Var_func(p):
error = FT_Done_MM_Var(get_handle(), p)
if error:
raise FT_Exception("Failure calling FT_Done_MM_Var")
# -----------------------------------------------------------------------------
class BBox( object ):
'''
FT_BBox wrapper.
A structure used to hold an outline's bounding box, i.e., the coordinates
of its extrema in the horizontal and vertical directions.
**Note**
The bounding box is specified with the coordinates of the lower left and
the upper right corner. In PostScript, those values are often called
(llx,lly) and (urx,ury), respectively.
If 'yMin' is negative, this value gives the glyph's descender. Otherwise,
the glyph doesn't descend below the baseline. Similarly, if 'ymax' is
positive, this value gives the glyph's ascender.
'xMin' gives the horizontal distance from the glyph's origin to the left
edge of the glyph's bounding box. If 'xMin' is negative, the glyph
extends to the left of the origin.
'''
def __init__(self, bbox):
'''
Create a new BBox object.
:param bbox: a FT_BBox or a tuple of 4 values
'''
if type(bbox) is FT_BBox:
self._FT_BBox = bbox
else:
self._FT_BBox = FT_BBox(*bbox)
xMin = property(lambda self: self._FT_BBox.xMin,
doc = 'The horizontal minimum (left-most).')
yMin = property(lambda self: self._FT_BBox.yMin,
doc = 'The vertical minimum (bottom-most).')
xMax = property(lambda self: self._FT_BBox.xMax,
doc = 'The horizontal maximum (right-most).')
yMax = property(lambda self: self._FT_BBox.yMax,
doc = 'The vertical maximum (top-most).')
# -----------------------------------------------------------------------------
class GlyphMetrics( object ):
'''
A structure used to model the metrics of a single glyph. The values are
expressed in 26.6 fractional pixel format; if the flag FT_LOAD_NO_SCALE has
been used while loading the glyph, values are expressed in font units
instead.
**Note**
If not disabled with FT_LOAD_NO_HINTING, the values represent dimensions of
the hinted glyph (in case hinting is applicable).
Stroking a glyph with an outside border does not increase ‘horiAdvance’ or
‘vertAdvance’; you have to manually adjust these values to account for the
added width and height.
'''
def __init__(self, metrics ):
'''
Create a new GlyphMetrics object.
:param metrics: a FT_Glyph_Metrics
'''
self._FT_Glyph_Metrics = metrics
width = property( lambda self: self._FT_Glyph_Metrics.width,
doc = '''The glyph's width.''' )
height = property( lambda self: self._FT_Glyph_Metrics.height,
doc = '''The glyph's height.''' )
horiBearingX = property( lambda self: self._FT_Glyph_Metrics.horiBearingX,
doc = '''Left side bearing for horizontal layout.''' )
horiBearingY = property( lambda self: self._FT_Glyph_Metrics.horiBearingY,
doc = '''Top side bearing for horizontal layout.''' )
horiAdvance = property( lambda self: self._FT_Glyph_Metrics.horiAdvance,
doc = '''Advance width for horizontal layout.''' )
vertBearingX = property( lambda self: self._FT_Glyph_Metrics.vertBearingX,
doc = '''Left side bearing for vertical layout.''' )
vertBearingY = property( lambda self: self._FT_Glyph_Metrics.vertBearingY,
doc = '''Top side bearing for vertical layout. Larger positive values
mean further below the vertical glyph origin.''' )
vertAdvance = property( lambda self: self._FT_Glyph_Metrics.vertAdvance,
doc = '''Advance height for vertical layout. Positive values mean the
glyph has a positive advance downward.''' )
# -----------------------------------------------------------------------------
class SizeMetrics( object ):
'''
The size metrics structure gives the metrics of a size object.
**Note**
The scaling values, if relevant, are determined first during a size
changing operation. The remaining fields are then set by the driver. For
scalable formats, they are usually set to scaled values of the
corresponding fields in Face.
Note that due to glyph hinting, these values might not be exact for certain
fonts. Thus they must be treated as unreliable with an error margin of at
least one pixel!
Indeed, the only way to get the exact metrics is to render all glyphs. As
this would be a definite performance hit, it is up to client applications
to perform such computations.
The SizeMetrics structure is valid for bitmap fonts also.
'''
def __init__(self, metrics ):
'''
Create a new SizeMetrics object.
:param metrics: a FT_SizeMetrics
'''
self._FT_Size_Metrics = metrics
x_ppem = property( lambda self: self._FT_Size_Metrics.x_ppem,
doc = '''The width of the scaled EM square in pixels, hence the term
'ppem' (pixels per EM). It is also referred to as 'nominal
width'.''' )
y_ppem = property( lambda self: self._FT_Size_Metrics.y_ppem,
doc = '''The height of the scaled EM square in pixels, hence the term
'ppem' (pixels per EM). It is also referred to as 'nominal
height'.''' )
x_scale = property( lambda self: self._FT_Size_Metrics.x_scale,
doc = '''A 16.16 fractional scaling value used to convert horizontal
metrics from font units to 26.6 fractional pixels. Only
relevant for scalable font formats.''' )
y_scale = property( lambda self: self._FT_Size_Metrics.y_scale,
doc = '''A 16.16 fractional scaling value used to convert vertical
metrics from font units to 26.6 fractional pixels. Only
relevant for scalable font formats.''' )
ascender = property( lambda self: self._FT_Size_Metrics.ascender,
doc = '''The ascender in 26.6 fractional pixels. See Face for the
details.''' )
descender = property( lambda self: self._FT_Size_Metrics.descender,
doc = '''The descender in 26.6 fractional pixels. See Face for the
details.''' )
height = property( lambda self: self._FT_Size_Metrics.height,
doc = '''The height in 26.6 fractional pixels. See Face for the details.''' )
max_advance = property(lambda self: self._FT_Size_Metrics.max_advance,
doc = '''The maximal advance width in 26.6 fractional pixels. See
Face for the details.''' )
# -----------------------------------------------------------------------------
class BitmapSize( object ):
'''
FT_Bitmap_Size wrapper
This structure models the metrics of a bitmap strike (i.e., a set of glyphs
for a given point size and resolution) in a bitmap font. It is used for the
'available_sizes' field of Face.
**Note**
Windows FNT: The nominal size given in a FNT font is not reliable. Thus
when the driver finds it incorrect, it sets 'size' to some calculated
values and sets 'x_ppem' and 'y_ppem' to the pixel width and height given
in the font, respectively.
TrueType embedded bitmaps: 'size', 'width', and 'height' values are not
contained in the bitmap strike itself. They are computed from the global
font parameters.
'''
def __init__(self, size ):
'''
Create a new SizeMetrics object.
:param size: a FT_Bitmap_Size
'''
self._FT_Bitmap_Size = size
height = property( lambda self: self._FT_Bitmap_Size.height,
doc = '''The vertical distance, in pixels, between two consecutive
baselines. It is always positive.''')
width = property( lambda self: self._FT_Bitmap_Size.width,
doc = '''The average width, in pixels, of all glyphs in the strike.''')
size = property( lambda self: self._FT_Bitmap_Size.size,
doc = '''The nominal size of the strike in 26.6 fractional points. This
field is not very useful.''')
x_ppem = property( lambda self: self._FT_Bitmap_Size.x_ppem,
doc = '''The horizontal ppem (nominal width) in 26.6 fractional
pixels.''')
y_ppem = property( lambda self: self._FT_Bitmap_Size.y_ppem,
doc = '''The vertical ppem (nominal width) in 26.6 fractional
pixels.''')
# -----------------------------------------------------------------------------
class Bitmap(object):
'''
FT_Bitmap wrapper
A structure used to describe a bitmap or pixmap to the raster. Note that we
now manage pixmaps of various depths through the 'pixel_mode' field.
*Note*:
For now, the only pixel modes supported by FreeType are mono and
grays. However, drivers might be added in the future to support more
'colorful' options.
'''
def __init__(self, bitmap):
'''
Create a new Bitmap object.
:param bitmap: a FT_Bitmap
'''
self._FT_Bitmap = bitmap
rows = property(lambda self: self._FT_Bitmap.rows,
doc = '''The number of bitmap rows.''')
width = property(lambda self: self._FT_Bitmap.width,
doc = '''The number of pixels in bitmap row.''')
pitch = property(lambda self: self._FT_Bitmap.pitch,
doc = '''The pitch's absolute value is the number of bytes taken by one
bitmap row, including padding. However, the pitch is positive
when the bitmap has a 'down' flow, and negative when it has an
'up' flow. In all cases, the pitch is an offset to add to a
bitmap pointer in order to go down one row.
Note that 'padding' means the alignment of a bitmap to a byte
border, and FreeType functions normally align to the smallest
possible integer value.
For the B/W rasterizer, 'pitch' is always an even number.
To change the pitch of a bitmap (say, to make it a multiple of
4), use FT_Bitmap_Convert. Alternatively, you might use callback
functions to directly render to the application's surface; see
the file 'example2.py' in the tutorial for a demonstration.''')
def _get_buffer(self):
data = [self._FT_Bitmap.buffer[i] for i in range(self.rows*self.pitch)]
return data
buffer = property(_get_buffer,
doc = '''A typeless pointer to the bitmap buffer. This value should be
aligned on 32-bit boundaries in most cases.''')
num_grays = property(lambda self: self._FT_Bitmap.num_grays,
doc = '''This field is only used with FT_PIXEL_MODE_GRAY; it gives
the number of gray levels used in the bitmap.''')
pixel_mode = property(lambda self: self._FT_Bitmap.pixel_mode,
doc = '''The pixel mode, i.e., how pixel bits are stored. See
FT_Pixel_Mode for possible values.''')
palette_mode = property(lambda self: self._FT_Bitmap.palette_mode,
doc ='''This field is intended for paletted pixel modes; it
indicates how the palette is stored. Not used currently.''')
palette = property(lambda self: self._FT_Bitmap.palette,
doc = '''A typeless pointer to the bitmap palette; this field is
intended for paletted pixel modes. Not used currently.''')
# -----------------------------------------------------------------------------
class Charmap( object ):
'''
FT_Charmap wrapper.
A handle to a given character map. A charmap is used to translate character
codes in a given encoding into glyph indexes for its parent's face. Some
font formats may provide several charmaps per font.
Each face object owns zero or more charmaps, but only one of them can be
'active' and used by FT_Get_Char_Index or FT_Load_Char.
The list of available charmaps in a face is available through the
'face.num_charmaps' and 'face.charmaps' fields of FT_FaceRec.
The currently active charmap is available as 'face.charmap'. You should
call FT_Set_Charmap to change it.
**Note**:
When a new face is created (either through FT_New_Face or FT_Open_Face),
the library looks for a Unicode charmap within the list and automatically
activates it.
**See also**:
See FT_CharMapRec for the publicly accessible fields of a given character
map.
'''
def __init__( self, charmap ):
'''
Create a new Charmap object.
Parameters:
-----------
charmap : a FT_Charmap
'''
self._FT_Charmap = charmap
encoding = property( lambda self: self._FT_Charmap.contents.encoding,
doc = '''An FT_Encoding tag identifying the charmap. Use this with
FT_Select_Charmap.''')
platform_id = property( lambda self: self._FT_Charmap.contents.platform_id,
doc = '''An ID number describing the platform for the following
encoding ID. This comes directly from the TrueType
specification and should be emulated for other
formats.''')
encoding_id = property( lambda self: self._FT_Charmap.contents.encoding_id,
doc = '''A platform specific encoding number. This also comes from
the TrueType specification and should be emulated
similarly.''')
def _get_encoding_name(self):
encoding = self.encoding
for key,value in FT_ENCODINGS.items():
if encoding == value:
return key
return 'Unknown encoding'
encoding_name = property( _get_encoding_name,
doc = '''A platform specific encoding name. This also comes from
the TrueType specification and should be emulated
similarly.''')
def _get_index( self ):
return FT_Get_Charmap_Index( self._FT_Charmap )
index = property( _get_index,
doc = '''The index into the array of character maps within the face to
which 'charmap' belongs. If an error occurs, -1 is returned.''')
def _get_cmap_language_id( self ):
return FT_Get_CMap_Language_ID( self._FT_Charmap )
cmap_language_id = property( _get_cmap_language_id,
doc = '''The language ID of 'charmap'. If 'charmap' doesn't
belong to a TrueType/sfnt face, just return 0 as the
default value.''')
def _get_cmap_format( self ):
return FT_Get_CMap_Format( self._FT_Charmap )
cmap_format = property( _get_cmap_format,
doc = '''The format of 'charmap'. If 'charmap' doesn't belong to a
TrueType/sfnt face, return -1.''')
# -----------------------------------------------------------------------------
class Outline( object ):
'''
FT_Outline wrapper.
This structure is used to describe an outline to the scan-line converter.
'''
def __init__( self, outline ):
'''
Create a new Outline object.
:param charmap: a FT_Outline
'''
self._FT_Outline = outline
n_contours = property(lambda self: self._FT_Outline.n_contours)
def _get_contours(self):
n = self._FT_Outline.n_contours
data = [self._FT_Outline.contours[i] for i in range(n)]
return data
contours = property(_get_contours,
doc = '''The number of contours in the outline.''')
n_points = property(lambda self: self._FT_Outline.n_points)
def _get_points(self):
n = self._FT_Outline.n_points
data = []
for i in range(n):
v = self._FT_Outline.points[i]
data.append( (v.x,v.y) )
return data
points = property( _get_points,
doc = '''The number of points in the outline.''')
def _get_tags(self):
n = self._FT_Outline.n_points
data = [self._FT_Outline.tags[i] for i in range(n)]
return data
tags = property(_get_tags,
doc = '''A list of 'n_points' chars, giving each outline point's type.
If bit 0 is unset, the point is 'off' the curve, i.e., a Bezier
control point, while it is 'on' if set.
Bit 1 is meaningful for 'off' points only. If set, it indicates a
third-order Bezier arc control point; and a second-order control
point if unset.
If bit 2 is set, bits 5-7 contain the drop-out mode (as defined
in the OpenType specification; the value is the same as the
argument to the SCANMODE instruction).
Bits 3 and 4 are reserved for internal purposes.''')
flags = property(lambda self: self._FT_Outline.flags,
doc = '''A set of bit flags used to characterize the outline and give
hints to the scan-converter and hinter on how to
convert/grid-fit it. See FT_OUTLINE_FLAGS.''')
def get_inside_border( self ):
'''
Retrieve the FT_StrokerBorder value corresponding to the 'inside'
borders of a given outline.
:return: The border index. FT_STROKER_BORDER_RIGHT for empty or invalid
outlines.
'''
return FT_Outline_GetInsideBorder( byref(self._FT_Outline) )
def get_outside_border( self ):
'''
Retrieve the FT_StrokerBorder value corresponding to the 'outside'
borders of a given outline.
:return: The border index. FT_STROKER_BORDER_RIGHT for empty or invalid
outlines.
'''
return FT_Outline_GetOutsideBorder( byref(self._FT_Outline) )
def get_bbox(self):
'''
Compute the exact bounding box of an outline. This is slower than
computing the control box. However, it uses an advanced algorithm which
returns very quickly when the two boxes coincide. Otherwise, the
outline Bezier arcs are traversed to extract their extrema.
'''
bbox = FT_BBox()
error = FT_Outline_Get_BBox(byref(self._FT_Outline), byref(bbox))
if error: raise FT_Exception(error)
return BBox(bbox)
def get_cbox(self):
'''
Return an outline's 'control box'. The control box encloses all the
outline's points, including Bezier control points. Though it coincides
with the exact bounding box for most glyphs, it can be slightly larger
in some situations (like when rotating an outline which contains Bezier
outside arcs).
Computing the control box is very fast, while getting the bounding box
can take much more time as it needs to walk over all segments and arcs
in the outline. To get the latter, you can use the 'ftbbox' component
which is dedicated to this single task.
'''
bbox = FT_BBox()
FT_Outline_Get_CBox(byref(self._FT_Outline), byref(bbox))
return BBox(bbox)
_od_move_to_noop = FT_Outline_MoveToFunc(lambda a, b: 0)
def _od_move_to_builder(self, cb):
if cb is None:
return self._od_move_to_noop
def move_to(a, b):
return cb(a[0], b) or 0
return FT_Outline_MoveToFunc(move_to)
_od_line_to_noop = FT_Outline_LineToFunc(lambda a, b: 0)
def _od_line_to_builder(self, cb):
if cb is None:
return self._od_line_to_noop
def line_to(a, b):
return cb(a[0], b) or 0
return FT_Outline_LineToFunc(line_to)
_od_conic_to_noop = FT_Outline_ConicToFunc(lambda a, b, c: 0)
def _od_conic_to_builder(self, cb):
if cb is None:
return self._od_conic_to_noop
def conic_to(a, b, c):
return cb(a[0], b[0], c) or 0
return FT_Outline_ConicToFunc(conic_to)
_od_cubic_to_noop = FT_Outline_CubicToFunc(lambda a, b, c, d: 0)
def _od_cubic_to_builder(self, cb):
if cb is None:
return self._od_cubic_to_noop
def cubic_to(a, b, c, d):
return cb(a[0], b[0], c[0], d) or 0
return FT_Outline_CubicToFunc(cubic_to)
def decompose(self, context=None, move_to=None, line_to=None, conic_to=None, cubic_to=None, shift=0, delta=0):
'''
Decompose the outline into a sequence of move, line, conic, and
cubic segments.
:param context: Arbitrary contextual object which will be passed as
the last parameter of all callbacks. Typically an
object to be drawn to, but can be anything.
:param move_to: Callback which will be passed an `FT_Vector`
control point and the context. Called when outline
needs to jump to a new path component.
:param line_to: Callback which will be passed an `FT_Vector`
control point and the context. Called to draw a
straight line from the current position to the
control point.
:param conic_to: Callback which will be passed two `FT_Vector`
control points and the context. Called to draw a
second-order Bézier curve from the current
position using the passed control points.
:param curve_to: Callback which will be passed three `FT_Vector`
control points and the context. Called to draw a
third-order Bézier curve from the current position
using the passed control points.
:param shift: Passed to FreeType which will transform vectors via
`x = (x << shift) - delta` and `y = (y << shift) - delta`
:param delta: Passed to FreeType which will transform vectors via
`x = (x << shift) - delta` and `y = (y << shift) - delta`
:since: 1.3
'''
func = FT_Outline_Funcs(
move_to = self._od_move_to_builder(move_to),
line_to = self._od_line_to_builder(line_to),
conic_to = self._od_conic_to_builder(conic_to),
cubic_to = self._od_cubic_to_builder(cubic_to),
shift = shift,
delta = FT_Pos(delta),
)
error = FT_Outline_Decompose( byref(self._FT_Outline), byref(func), py_object(context) )
if error: raise FT_Exception( error )
# -----------------------------------------------------------------------------
class Glyph( object ):
'''
FT_Glyph wrapper.
The root glyph structure contains a given glyph image plus its advance
width in 16.16 fixed float format.
'''
def __init__( self, glyph ):
'''
Create Glyph object from an FT glyph.
:param glyph: valid FT_Glyph object
'''
self._FT_Glyph = glyph
def __del__( self ):
'''
Destroy glyph.
'''
FT_Done_Glyph( self._FT_Glyph )
def _get_format( self ):
return self._FT_Glyph.contents.format
format = property( _get_format,
doc = '''The format of the glyph's image.''')
def stroke( self, stroker, destroy=False ):
'''
Stroke a given outline glyph object with a given stroker.
:param stroker: A stroker handle.
:param destroy: A Boolean. If 1, the source glyph object is destroyed on
success.
**Note**:
The source glyph is untouched in case of error.
'''
error = FT_Glyph_Stroke( byref(self._FT_Glyph),
stroker._FT_Stroker, destroy )
if error: raise FT_Exception( error )
def to_bitmap( self, mode, origin, destroy=False ):
'''
Convert a given glyph object to a bitmap glyph object.
:param mode: An enumeration that describes how the data is rendered.
:param origin: A pointer to a vector used to translate the glyph image
before rendering. Can be 0 (if no translation). The origin is
expressed in 26.6 pixels.
We also detect a plain vector and make a pointer out of it,
if that's the case.
:param destroy: A boolean that indicates that the original glyph image
should be destroyed by this function. It is never destroyed
in case of error.
**Note**:
This function does nothing if the glyph format isn't scalable.
The glyph image is translated with the 'origin' vector before
rendering.
The first parameter is a pointer to an FT_Glyph handle, that will be
replaced by this function (with newly allocated data). Typically, you
would use (omitting error handling):
'''
if ( type(origin) == FT_Vector ):
error = FT_Glyph_To_Bitmap( byref(self._FT_Glyph),
mode, byref(origin), destroy )
else:
error = FT_Glyph_To_Bitmap( byref(self._FT_Glyph),
mode, origin, destroy )
if error: raise FT_Exception( error )
return BitmapGlyph( self._FT_Glyph )
def get_cbox(self, bbox_mode):
'''
Return an outline's 'control box'. The control box encloses all the
outline's points, including Bezier control points. Though it coincides
with the exact bounding box for most glyphs, it can be slightly larger
in some situations (like when rotating an outline which contains Bezier
outside arcs).
Computing the control box is very fast, while getting the bounding box
can take much more time as it needs to walk over all segments and arcs
in the outline. To get the latter, you can use the 'ftbbox' component
which is dedicated to this single task.
:param mode: The mode which indicates how to interpret the returned
bounding box values.
**Note**:
Coordinates are relative to the glyph origin, using the y upwards
convention.
If the glyph has been loaded with FT_LOAD_NO_SCALE, 'bbox_mode' must be
set to FT_GLYPH_BBOX_UNSCALED to get unscaled font units in 26.6 pixel
format. The value FT_GLYPH_BBOX_SUBPIXELS is another name for this
constant.
Note that the maximum coordinates are exclusive, which means that one
can compute the width and height of the glyph image (be it in integer
or 26.6 pixels) as:
width = bbox.xMax - bbox.xMin;
height = bbox.yMax - bbox.yMin;
Note also that for 26.6 coordinates, if 'bbox_mode' is set to
FT_GLYPH_BBOX_GRIDFIT, the coordinates will also be grid-fitted, which
corresponds to:
bbox.xMin = FLOOR(bbox.xMin);
bbox.yMin = FLOOR(bbox.yMin);
bbox.xMax = CEILING(bbox.xMax);
bbox.yMax = CEILING(bbox.yMax);
To get the bbox in pixel coordinates, set 'bbox_mode' to
FT_GLYPH_BBOX_TRUNCATE.
To get the bbox in grid-fitted pixel coordinates, set 'bbox_mode' to
FT_GLYPH_BBOX_PIXELS.
'''
bbox = FT_BBox()
FT_Glyph_Get_CBox(byref(self._FT_Glyph.contents), bbox_mode, byref(bbox))
return BBox(bbox)
# -----------------------------------------------------------------------------
class BitmapGlyph( object ):
'''
FT_BitmapGlyph wrapper.
A structure used for bitmap glyph images. This really is a 'sub-class' of
FT_GlyphRec.
'''
def __init__( self, glyph ):
'''
Create Glyph object from an FT glyph.
Parameters:
-----------
glyph: valid FT_Glyph object
'''
self._FT_BitmapGlyph = cast(glyph, FT_BitmapGlyph)
# def __del__( self ):
# '''
# Destroy glyph.
# '''
# FT_Done_Glyph( cast(self._FT_BitmapGlyph, FT_Glyph) )
def _get_format( self ):
return self._FT_BitmapGlyph.contents.format
format = property( _get_format,
doc = '''The format of the glyph's image.''')
def _get_bitmap( self ):
return Bitmap( self._FT_BitmapGlyph.contents.bitmap )
bitmap = property( _get_bitmap,
doc = '''A descriptor for the bitmap.''')
def _get_left( self ):
return self._FT_BitmapGlyph.contents.left
left = property( _get_left,
doc = '''The left-side bearing, i.e., the horizontal distance from the
current pen position to the left border of the glyph bitmap.''')
def _get_top( self ):
return self._FT_BitmapGlyph.contents.top
top = property( _get_top,
doc = '''The top-side bearing, i.e., the vertical distance from the
current pen position to the top border of the glyph bitmap.
This distance is positive for upwards y!''')
# -----------------------------------------------------------------------------
class GlyphSlot( object ):
'''
FT_GlyphSlot wrapper.
FreeType root glyph slot class structure. A glyph slot is a container where
individual glyphs can be loaded, be they in outline or bitmap format.
'''
def __init__( self, slot ):
'''
Create GlyphSlot object from an FT glyph slot.
Parameters:
-----------
glyph: valid FT_GlyphSlot object
'''
self._FT_GlyphSlot = slot
def render( self, render_mode ):
'''
Convert a given glyph image to a bitmap. It does so by inspecting the
glyph image format, finding the relevant renderer, and invoking it.
:param render_mode: The render mode used to render the glyph image into
a bitmap. See FT_Render_Mode for a list of possible
values.
If FT_RENDER_MODE_NORMAL is used, a previous call
of FT_Load_Glyph with flag FT_LOAD_COLOR makes
FT_Render_Glyph provide a default blending of
colored glyph layers associated with the current
glyph slot (provided the font contains such layers)
instead of rendering the glyph slot's outline.
This is an experimental feature; see FT_LOAD_COLOR
for more information.
**Note**:
To get meaningful results, font scaling values must be set with
functions like FT_Set_Char_Size before calling FT_Render_Glyph.