diff options
Diffstat (limited to 'third_party/freetype-py/freetype/__init__.py')
-rw-r--r-- | third_party/freetype-py/freetype/__init__.py | 1989 |
1 files changed, 1989 insertions, 0 deletions
diff --git a/third_party/freetype-py/freetype/__init__.py b/third_party/freetype-py/freetype/__init__.py new file mode 100644 index 0000000..3f9934e --- /dev/null +++ b/third_party/freetype-py/freetype/__init__.py @@ -0,0 +1,1989 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011-2014 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 os +import sys +import platform +from ctypes import * +from freetype.ft_types import * +from freetype.ft_enums import * +from freetype.ft_errors import * +from freetype.ft_structs import * +import ctypes.util + +# Hack to get unicode class in python3 +PY3 = sys.version_info[0] == 3 +if PY3: unicode = str + + +__dll__ = None +__handle__ = None + +# on windows all ctypes does when checking for the library +# is to append .dll to the end and look for an exact match +# within any entry in PATH. +filename = ctypes.util.find_library('freetype') + +if filename is None: + if platform.system() == 'Windows': + # Check current working directory for dll as ctypes fails to do so + filename = os.path.join(os.path.realpath('.'), 'freetype.dll') + else: + filename = 'libfreetype.so.6' + +try: + dll = ctypes.CDLL(filename) + _found = True +except (OSError, TypeError): + _found = False + +if not _found: + raise RuntimeError('Freetype library not found') + +__dll__ = dll +FT_Library_filename = filename + +# ----------------------------------------------------------------------------- +# High-level API of FreeType 2 +# ----------------------------------------------------------------------------- +FT_Init_FreeType = __dll__.FT_Init_FreeType +FT_Done_FreeType = __dll__.FT_Done_FreeType +FT_Library_Version = __dll__.FT_Library_Version + +def __del_library__(self): + global __handle__ + if __handle__: + try: + FT_Done_FreeType(self) + __handle__ = None + except: + pass +FT_Library.__del__ = __del_library__ + +def get_handle(): + ''' + Get unique FT_Library handle + ''' + global __handle__ + if not __handle__: + __handle__ = FT_Library( ) + error = FT_Init_FreeType( byref(__handle__) ) + if error: raise FT_Exception(error) + try: + set_lcd_filter( FT_LCD_FILTER_DEFAULT ) + except: + pass + if error: raise FT_Exception(error) + 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) + + +try: + FT_Library_SetLcdFilter= __dll__.FT_Library_SetLcdFilter +except: + def FT_Library_SetLcdFilter (*args, **kwargs): + return 0 +if version()>=(2,4,0): + FT_Library_SetLcdFilterWeights = __dll__.FT_Library_SetLcdFilterWeights +FT_New_Face = __dll__.FT_New_Face +FT_New_Memory_Face = __dll__.FT_New_Memory_Face +FT_Open_Face = __dll__.FT_Open_Face +FT_Attach_File = __dll__.FT_Attach_File +FT_Attach_Stream = __dll__.FT_Attach_Stream +if version()>=(2,4,2): + FT_Reference_Face = __dll__.FT_Reference_Face +FT_Done_Face = __dll__.FT_Done_Face +FT_Done_Glyph = __dll__.FT_Done_Glyph +FT_Select_Size = __dll__.FT_Select_Size +FT_Request_Size = __dll__.FT_Request_Size +FT_Set_Char_Size = __dll__.FT_Set_Char_Size +FT_Set_Pixel_Sizes = __dll__.FT_Set_Pixel_Sizes +FT_Load_Glyph = __dll__.FT_Load_Glyph +FT_Load_Char = __dll__.FT_Load_Char +FT_Set_Transform = __dll__.FT_Set_Transform +FT_Render_Glyph = __dll__.FT_Render_Glyph +FT_Get_Kerning = __dll__.FT_Get_Kerning +FT_Get_Track_Kerning = __dll__.FT_Get_Track_Kerning +FT_Get_Glyph_Name = __dll__.FT_Get_Glyph_Name +FT_Get_Glyph = __dll__.FT_Get_Glyph + +FT_Glyph_Get_CBox = __dll__.FT_Glyph_Get_CBox + +FT_Get_Postscript_Name = __dll__.FT_Get_Postscript_Name +FT_Get_Postscript_Name.restype = c_char_p +FT_Select_Charmap = __dll__.FT_Select_Charmap +FT_Set_Charmap = __dll__.FT_Set_Charmap +FT_Get_Charmap_Index = __dll__.FT_Get_Charmap_Index +FT_Get_CMap_Language_ID= __dll__.FT_Get_CMap_Language_ID +FT_Get_CMap_Format = __dll__.FT_Get_CMap_Format +FT_Get_Char_Index = __dll__.FT_Get_Char_Index +FT_Get_First_Char = __dll__.FT_Get_First_Char +FT_Get_Next_Char = __dll__.FT_Get_Next_Char +FT_Get_Name_Index = __dll__.FT_Get_Name_Index +FT_Get_SubGlyph_Info = __dll__.FT_Get_SubGlyph_Info +if version()>=(2,3,8): + FT_Get_FSType_Flags = __dll__.FT_Get_FSType_Flags + FT_Get_FSType_Flags.restype = c_ushort + +FT_Get_X11_Font_Format = __dll__.FT_Get_X11_Font_Format +FT_Get_X11_Font_Format.restype = c_char_p + +FT_Get_Sfnt_Name_Count = __dll__.FT_Get_Sfnt_Name_Count +FT_Get_Sfnt_Name = __dll__.FT_Get_Sfnt_Name +FT_Get_Advance = __dll__.FT_Get_Advance + + +FT_Outline_GetInsideBorder = __dll__.FT_Outline_GetInsideBorder +FT_Outline_GetOutsideBorder = __dll__.FT_Outline_GetOutsideBorder +FT_Outline_Get_BBox = __dll__.FT_Outline_Get_BBox +FT_Outline_Get_CBox = __dll__.FT_Outline_Get_CBox +FT_Stroker_New = __dll__.FT_Stroker_New +FT_Stroker_Set = __dll__.FT_Stroker_Set +FT_Stroker_Rewind = __dll__.FT_Stroker_Rewind +FT_Stroker_ParseOutline = __dll__.FT_Stroker_ParseOutline +FT_Stroker_BeginSubPath = __dll__.FT_Stroker_BeginSubPath +FT_Stroker_EndSubPath = __dll__.FT_Stroker_EndSubPath +FT_Stroker_LineTo = __dll__.FT_Stroker_LineTo +FT_Stroker_ConicTo = __dll__.FT_Stroker_ConicTo +FT_Stroker_CubicTo = __dll__.FT_Stroker_CubicTo +FT_Stroker_GetBorderCounts = __dll__.FT_Stroker_GetBorderCounts +FT_Stroker_ExportBorder = __dll__.FT_Stroker_ExportBorder +FT_Stroker_GetCounts = __dll__.FT_Stroker_GetCounts +FT_Stroker_Export = __dll__.FT_Stroker_Export +FT_Stroker_Done = __dll__.FT_Stroker_Done +FT_Glyph_Stroke = __dll__.FT_Glyph_Stroke +FT_Glyph_StrokeBorder = __dll__.FT_Glyph_StrokeBorder +FT_Glyph_To_Bitmap = __dll__.FT_Glyph_To_Bitmap + + + +# ----------------------------------------------------------------------------- +# 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 + + + +# ----------------------------------------------------------------------------- +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( 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_GetInsideBorder( 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 + + 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() + error = FT_Outline_Get_CBox(byref(self._FT_Outline), byref(bbox)) + if error: raise FT_Exception(error) + return BBox(bbox) + + + + +# ----------------------------------------------------------------------------- +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. + + :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): + ''' + 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() + error = FT_Glyph_Get_CBox(byref(self._FT_Glyph), bbox_mode,byref(bbox)) + if error: raise FT_Exception(error) + 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 get_glyph( self ): + ''' + A function used to extract a glyph image from a slot. Note that the + created FT_Glyph object must be released with FT_Done_Glyph. + ''' + aglyph = FT_Glyph() + error = FT_Get_Glyph( self._FT_GlyphSlot, byref(aglyph) ) + if error: raise FT_Exception( error ) + return Glyph( aglyph ) + + def _get_bitmap( self ): + return Bitmap( self._FT_GlyphSlot.contents.bitmap ) + bitmap = property( _get_bitmap, + doc = '''This field is used as a bitmap descriptor when the slot format + is FT_GLYPH_FORMAT_BITMAP. Note that the address and content of + the bitmap buffer can change between calls of FT_Load_Glyph and + a few other functions.''') + + def _get_metrics( self ): + return GlyphMetrics( self._FT_GlyphSlot.contents.metrics ) + metrics = property( _get_metrics, + doc = '''The metrics of the last loaded glyph in the slot. The returned + values depend on the last load flags (see the FT_Load_Glyph API + function) and can be expressed either in 26.6 fractional pixels or font + units. Note that even when the glyph image is transformed, the metrics + are not.''') + + def _get_next( self ): + return GlyphSlot( self._FT_GlyphSlot.contents.next ) + next = property( _get_next, + doc = '''In some cases (like some font tools), several glyph slots per + face object can be a good thing. As this is rare, the glyph slots + are listed through a direct, single-linked list using its 'next' + field.''') + + advance = property( lambda self: self._FT_GlyphSlot.contents.advance, + doc = '''This shorthand is, depending on FT_LOAD_IGNORE_TRANSFORM, the + transformed advance width for the glyph (in 26.6 fractional + pixel format). As specified with FT_LOAD_VERTICAL_LAYOUT, it + uses either the 'horiAdvance' or the 'vertAdvance' value of + 'metrics' field.''') + + def _get_outline( self ): + return Outline( self._FT_GlyphSlot.contents.outline ) + outline = property( _get_outline, + doc = '''The outline descriptor for the current glyph image if its + format is FT_GLYPH_FORMAT_OUTLINE. Once a glyph is loaded, + 'outline' can be transformed, distorted, embolded, + etc. However, it must not be freed.''') + + format = property( lambda self: self._FT_GlyphSlot.contents.format, + doc = '''This field indicates the format of the image contained in the + glyph slot. Typically FT_GLYPH_FORMAT_BITMAP, + FT_GLYPH_FORMAT_OUTLINE, or FT_GLYPH_FORMAT_COMPOSITE, but + others are possible.''') + + bitmap_top = property( lambda self: + self._FT_GlyphSlot.contents.bitmap_top, + doc = '''This is the bitmap's top bearing expressed in integer + pixels. Remember that this is the distance from the + baseline to the top-most glyph scanline, upwards y + coordinates being positive.''') + + bitmap_left = property( lambda self: + self._FT_GlyphSlot.contents.bitmap_left, + doc = '''This is the bitmap's left bearing expressed in integer + pixels. Of course, this is only valid if the format is + FT_GLYPH_FORMAT_BITMAP.''') + + linearHoriAdvance = property( lambda self: + self._FT_GlyphSlot.contents.linearHoriAdvance, + doc = '''The advance width of the unhinted glyph. Its value + is expressed in 16.16 fractional pixels, unless + FT_LOAD_LINEAR_DESIGN is set when loading the glyph. + This field can be important to perform correct + WYSIWYG layout. Only relevant for outline glyphs.''') + + linearVertAdvance = property( lambda self: + self._FT_GlyphSlot.contents.linearVertAdvance, + doc = '''The advance height of the unhinted glyph. Its value + is expressed in 16.16 fractional pixels, unless + FT_LOAD_LINEAR_DESIGN is set when loading the glyph. + This field can be important to perform correct + WYSIWYG layout. Only relevant for outline glyphs.''') + + +# ----------------------------------------------------------------------------- +# Face wrapper +# ----------------------------------------------------------------------------- +class Face( object ): + ''' + FT_Face wrapper + + FreeType root face class structure. A face object models a typeface in a + font file. + ''' + def __init__( self, filename, index = 0 ): + ''' + Build a new Face + + :param str filename: + A path to the font file. + + :param int index: + The index of the face within the font. + The first face has index 0. + ''' + library = get_handle( ) + face = FT_Face( ) + self._FT_Face = None + #error = FT_New_Face( library, filename, 0, byref(face) ) + self._filebodys = [] + try: + u_filename = c_char_p(_encode_filename(filename)) + error = FT_New_Face( library, u_filename, index, byref(face) ) + except UnicodeError: + with open(filename, mode='rb') as f: + filebody = f.read() + error = FT_New_Memory_Face( library, filebody, len(filebody), + index, byref(face) ) + self._filebodys.append(filebody) # prevent gc + if error: raise FT_Exception( error ) + self._filename = filename + self._index = index + self._FT_Face = face + + def __del__( self ): + ''' + Discard face object, as well as all of its child slots and sizes. + ''' + if self._FT_Face is not None: + FT_Done_Face( self._FT_Face ) + + + def attach_file( self, filename ): + ''' + Attach data to a face object. Normally, this is used to read + additional information for the face object. For example, you can attach + an AFM file that comes with a Type 1 font to get the kerning values and + other metrics. + + :param filename: Filename to attach + + **Note** + + The meaning of the 'attach' (i.e., what really happens when the new + file is read) is not fixed by FreeType itself. It really depends on the + font format (and thus the font driver). + + Client applications are expected to know what they are doing when + invoking this function. Most drivers simply do not implement file + attachments. + ''' + + try: + u_filename = c_char_p(_encode_filename(filename)) + error = FT_Attach_File( self._FT_Face, u_filename ) + except UnicodeError: + with open(filename, mode='rb') as f: + filebody = f.read() + parameters = FT_Open_Args() + parameters.flags = FT_OPEN_MEMORY + parameters.memory_base = filebody + parameters.memory_size = len(filebody) + parameters.stream = None + error = FT_Attach_Stream( self._FT_Face, parameters ) + self._filebodys.append(filebody) # prevent gc + if error: raise FT_Exception( error) + + + def set_char_size( self, width=0, height=0, hres=72, vres=72 ): + ''' + This function calls FT_Request_Size to request the nominal size (in + points). + + :param float width: The nominal width, in 26.6 fractional points. + + :param float height: The nominal height, in 26.6 fractional points. + + :param float hres: The horizontal resolution in dpi. + + :param float vres: The vertical resolution in dpi. + + **Note** + + If either the character width or height is zero, it is set equal to the + other value. + + If either the horizontal or vertical resolution is zero, it is set + equal to the other value. + + A character width or height smaller than 1pt is set to 1pt; if both + resolution values are zero, they are set to 72dpi. + + Don't use this function if you are using the FreeType cache API. + ''' + error = FT_Set_Char_Size( self._FT_Face, width, height, hres, vres ) + if error: raise FT_Exception( error) + + def set_pixel_sizes( self, width, height ): + ''' + This function calls FT_Request_Size to request the nominal size (in + pixels). + + :param width: The nominal width, in pixels. + + :param height: The nominal height, in pixels. + ''' + error = FT_Set_Pixel_Sizes( self._FT_Face, width, height ) + if error: raise FT_Exception(error) + + def select_charmap( self, encoding ): + ''' + Select a given charmap by its encoding tag (as listed in 'freetype.h'). + + **Note**: + + This function returns an error if no charmap in the face corresponds to + the encoding queried here. + + Because many fonts contain more than a single cmap for Unicode + encoding, this function has some special code to select the one which + covers Unicode best ('best' in the sense that a UCS-4 cmap is preferred + to a UCS-2 cmap). It is thus preferable to FT_Set_Charmap in this case. + ''' + error = FT_Select_Charmap( self._FT_Face, encoding ) + if error: raise FT_Exception(error) + + def set_charmap( self, charmap ): + ''' + Select a given charmap for character code to glyph index mapping. + + :param charmap: A handle to the selected charmap. + ''' + error = FT_Set_Charmap( self._FT_Face, charmap._FT_Charmap ) + if error : raise FT_Exception(error) + + def get_char_index( self, charcode ): + ''' + Return the glyph index of a given character code. This function uses a + charmap object to do the mapping. + + :param charcode: The character code. + + **Note**: + + If you use FreeType to manipulate the contents of font files directly, + be aware that the glyph index returned by this function doesn't always + correspond to the internal indices used within the file. This is done + to ensure that value 0 always corresponds to the 'missing glyph'. + ''' + if isinstance(charcode, (str,unicode)): + charcode = ord(charcode) + return FT_Get_Char_Index( self._FT_Face, charcode ) + + def get_first_char( self ): + ''' + This function is used to return the first character code in the current + charmap of a given face. It also returns the corresponding glyph index. + + :return: Glyph index of first character code. 0 if charmap is empty. + + **Note**: + + You should use this function with get_next_char to be able to parse + all character codes available in a given charmap. The code should look + like this: + + Note that 'agindex' is set to 0 if the charmap is empty. The result + itself can be 0 in two cases: if the charmap is empty or if the value 0 + is the first valid character code. + ''' + agindex = FT_UInt() + charcode = FT_Get_First_Char( self._FT_Face, byref(agindex) ) + return charcode, agindex.value + + def get_next_char( self, charcode, agindex ): + ''' + This function is used to return the next character code in the current + charmap of a given face following the value 'charcode', as well as the + corresponding glyph index. + + :param charcode: The starting character code. + + :param agindex: Glyph index of next character code. 0 if charmap is empty. + + **Note**: + + You should use this function with FT_Get_First_Char to walk over all + character codes available in a given charmap. See the note for this + function for a simple code example. + + Note that 'agindex' is set to 0 when there are no more codes in the + charmap. + ''' + agindex = FT_UInt( 0 ) #agindex ) + charcode = FT_Get_Next_Char( self._FT_Face, charcode, byref(agindex) ) + return charcode, agindex.value + + def get_name_index( self, name ): + ''' + Return the glyph index of a given glyph name. This function uses driver + specific objects to do the translation. + + :param name: The glyph name. + ''' + return FT_Get_Name_Index( self._FT_Face, name ) + + def set_transform( self, matrix, delta ): + ''' + A function used to set the transformation that is applied to glyph + images when they are loaded into a glyph slot through FT_Load_Glyph. + + :param matrix: A pointer to the transformation's 2x2 matrix. + Use 0 for the identity matrix. + + :parm delta: A pointer to the translation vector. + Use 0 for the null vector. + + **Note**: + + The transformation is only applied to scalable image formats after the + glyph has been loaded. It means that hinting is unaltered by the + transformation and is performed on the character size given in the last + call to FT_Set_Char_Size or FT_Set_Pixel_Sizes. + + Note that this also transforms the 'face.glyph.advance' field, but + not the values in 'face.glyph.metrics'. + ''' + FT_Set_Transform( self._FT_Face, + byref(matrix), byref(delta) ) + + def select_size( self, strike_index ): + ''' + Select a bitmap strike. + + :param strike_index: The index of the bitmap strike in the + 'available_sizes' field of Face object. + ''' + error = FT_Select_Size( self._FT_Face, strike_index ) + if error: raise FT_Exception( error ) + + def load_glyph( self, index, flags = FT_LOAD_RENDER ): + ''' + A function used to load a single glyph into the glyph slot of a face + object. + + :param index: The index of the glyph in the font file. For CID-keyed + fonts (either in PS or in CFF format) this argument + specifies the CID value. + + :param flags: A flag indicating what to load for this glyph. The FT_LOAD_XXX + constants can be used to control the glyph loading process + (e.g., whether the outline should be scaled, whether to load + bitmaps or not, whether to hint the outline, etc). + + **Note**: + + The loaded glyph may be transformed. See FT_Set_Transform for the + details. + + For subsetted CID-keyed fonts, 'FT_Err_Invalid_Argument' is returned + for invalid CID values (this is, for CID values which don't have a + corresponding glyph in the font). See the discussion of the + FT_FACE_FLAG_CID_KEYED flag for more details. + ''' + error = FT_Load_Glyph( self._FT_Face, index, flags ) + if error: raise FT_Exception( error ) + + def load_char( self, char, flags = FT_LOAD_RENDER ): + ''' + A function used to load a single glyph into the glyph slot of a face + object, according to its character code. + + :param char: The glyph's character code, according to the current + charmap used in the face. + + :param flags: A flag indicating what to load for this glyph. The + FT_LOAD_XXX constants can be used to control the glyph + loading process (e.g., whether the outline should be + scaled, whether to load bitmaps or not, whether to hint + the outline, etc). + + **Note**: + + This function simply calls FT_Get_Char_Index and FT_Load_Glyph. + ''' + + if len(char) == 1: + char = ord(char) + error = FT_Load_Char( self._FT_Face, char, flags ) + if error: raise FT_Exception( error ) + + + def get_advance( self, gindex, flags ): + ''' + Retrieve the advance value of a given glyph outline in an FT_Face. By + default, the unhinted advance is returned in font units. + + :param gindex: The glyph index. + + :param flags: A set of bit flags similar to those used when calling + FT_Load_Glyph, used to determine what kind of advances + you need. + + :return: The advance value, in either font units or 16.16 format. + + If FT_LOAD_VERTICAL_LAYOUT is set, this is the vertical + advance corresponding to a vertical layout. Otherwise, it is + the horizontal advance in a horizontal layout. + ''' + + padvance = FT_Fixed(0) + error = FT_Get_Advance( self._FT_Face, gindex, flags, byref(padvance) ) + if error: raise FT_Exception( error ) + return padvance.value + + + + def get_kerning( self, left, right, mode = FT_KERNING_DEFAULT ): + ''' + Return the kerning vector between two glyphs of a same face. + + :param left: The index of the left glyph in the kern pair. + + :param right: The index of the right glyph in the kern pair. + + :param mode: See FT_Kerning_Mode for more information. Determines the scale + and dimension of the returned kerning vector. + + **Note**: + + Only horizontal layouts (left-to-right & right-to-left) are supported + by this method. Other layouts, or more sophisticated kernings, are out + of the scope of this API function -- they can be implemented through + format-specific interfaces. + ''' + left_glyph = self.get_char_index( left ) + right_glyph = self.get_char_index( right ) + kerning = FT_Vector(0,0) + error = FT_Get_Kerning( self._FT_Face, + left_glyph, right_glyph, mode, byref(kerning) ) + if error: raise FT_Exception( error ) + return kerning + + def get_format(self): + ''' + Return a string describing the format of a given face, using values + which can be used as an X11 FONT_PROPERTY. Possible values are + 'TrueType', 'Type 1', 'BDF', ‘PCF', ‘Type 42', ‘CID Type 1', ‘CFF', + 'PFR', and ‘Windows FNT'. + ''' + + return FT_Get_X11_Font_Format( self._FT_Face ) + + + def get_fstype(self): + ''' + Return the fsType flags for a font (embedding permissions). + + The return value is a tuple containing the freetype enum name + as a string and the actual flag as an int + ''' + + flag = FT_Get_FSType_Flags( self._FT_Face ) + for k, v in FT_FSTYPE_XXX.items(): + if v == flag: + return k, v + + + def _get_sfnt_name_count(self): + return FT_Get_Sfnt_Name_Count( self._FT_Face ) + sfnt_name_count = property(_get_sfnt_name_count, + doc = '''Number of name strings in the SFNT 'name' table.''') + + def get_sfnt_name( self, index ): + ''' + Retrieve a string of the SFNT 'name' table for a given index + + :param index: The index of the 'name' string. + + **Note**: + + The 'string' array returned in the 'aname' structure is not + null-terminated. The application should deallocate it if it is no + longer in use. + + Use FT_Get_Sfnt_Name_Count to get the total number of available + 'name' table entries, then do a loop until you get the right + platform, encoding, and name ID. + ''' + name = FT_SfntName( ) + error = FT_Get_Sfnt_Name( self._FT_Face, index, byref(name) ) + if error: raise FT_Exception( error ) + return SfntName( name ) + + def _get_postscript_name( self ): + return FT_Get_Postscript_Name( self._FT_Face ) + postscript_name = property( _get_postscript_name, + doc = '''ASCII PostScript name of face, if available. This only + works with PostScript and TrueType fonts.''') + + def _has_horizontal( self ): + return bool( self.face_flags & FT_FACE_FLAG_HORIZONTAL ) + has_horizontal = property( _has_horizontal, + doc = '''True whenever a face object contains horizontal metrics + (this is true for all font formats though).''') + + def _has_vertical( self ): + return bool( self.face_flags & FT_FACE_FLAG_VERTICAL ) + has_vertical = property( _has_vertical, + doc = '''True whenever a face object contains vertical metrics.''') + + def _has_kerning( self ): + return bool( self.face_flags & FT_FACE_FLAG_KERNING ) + has_kerning = property( _has_kerning, + doc = '''True whenever a face object contains kerning data that can + be accessed with FT_Get_Kerning.''') + + def _is_scalable( self ): + return bool( self.face_flags & FT_FACE_FLAG_SCALABLE ) + is_scalable = property( _is_scalable, + doc = '''true whenever a face object contains a scalable font face + (true for TrueType, Type 1, Type 42, CID, OpenType/CFF, + and PFR font formats.''') + + def _is_sfnt( self ): + return bool( self.face_flags & FT_FACE_FLAG_SFNT ) + is_sfnt = property( _is_sfnt, + doc = '''true whenever a face object contains a font whose format is + based on the SFNT storage scheme. This usually means: TrueType + fonts, OpenType fonts, as well as SFNT-based embedded bitmap + fonts. + + If this macro is true, all functions defined in + FT_SFNT_NAMES_H and FT_TRUETYPE_TABLES_H are available.''') + + def _is_fixed_width( self ): + return bool( self.face_flags & FT_FACE_FLAG_FIXED_WIDTH ) + is_fixed_width = property( _is_fixed_width, + doc = '''True whenever a face object contains a font face that + contains fixed-width (or 'monospace', 'fixed-pitch', + etc.) glyphs.''') + + def _has_fixed_sizes( self ): + return bool( self.face_flags & FT_FACE_FLAG_FIXED_SIZES ) + has_fixed_sizes = property( _has_fixed_sizes, + doc = '''True whenever a face object contains some embedded + bitmaps. See the 'available_sizes' field of the FT_FaceRec + structure.''') + + def _has_glyph_names( self ): + return bool( self.face_flags & FT_FACE_FLAG_GLYPH_NAMES ) + has_glyph_names = property( _has_glyph_names, + doc = '''True whenever a face object contains some glyph names + that can be accessed through FT_Get_Glyph_Name.''') + + def _has_multiple_masters( self ): + return bool( self.face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS ) + has_multiple_masters = property( _has_multiple_masters, + doc = '''True whenever a face object contains some + multiple masters. The functions provided by + FT_MULTIPLE_MASTERS_H are then available to + choose the exact design you want.''') + + def _is_cid_keyed( self ): + return bool( self.face_flags & FT_FACE_FLAG_CID_KEYED ) + is_cid_keyed = property( _is_cid_keyed, + doc = '''True whenever a face object contains a CID-keyed + font. See the discussion of FT_FACE_FLAG_CID_KEYED for + more details. + + If this macro is true, all functions defined in FT_CID_H + are available.''') + + def _is_tricky( self ): + return bool( self.face_flags & FT_FACE_FLAG_TRICKY ) + is_tricky = property( _is_tricky, + doc = '''True whenever a face represents a 'tricky' font. See the + discussion of FT_FACE_FLAG_TRICKY for more details.''') + + + num_faces = property(lambda self: self._FT_Face.contents.num_faces, + doc = '''The number of faces in the font file. Some font formats can + have multiple faces in a font file.''') + + face_index = property(lambda self: self._FT_Face.contents.face_index, + doc = '''The index of the face in the font file. It is set to 0 if + there is only one face in the font file.''') + + face_flags = property(lambda self: self._FT_Face.contents.face_flags, + doc = '''A set of bit flags that give important information about + the face; see FT_FACE_FLAG_XXX for the details.''') + + style_flags = property(lambda self: self._FT_Face.contents.style_flags, + doc = '''A set of bit flags indicating the style of the face; see + FT_STYLE_FLAG_XXX for the details.''') + + num_glyphs = property(lambda self: self._FT_Face.contents.num_glyphs, + doc = '''The number of glyphs in the face. If the face is scalable + and has sbits (see 'num_fixed_sizes'), it is set to the number of + outline glyphs. + + For CID-keyed fonts, this value gives the highest CID used in the + font.''') + + family_name = property(lambda self: self._FT_Face.contents.family_name, + doc = '''The face's family name. This is an ASCII string, usually + in English, which describes the typeface's family (like + 'Times New Roman', 'Bodoni', 'Garamond', etc). This is a + least common denominator used to list fonts. Some formats + (TrueType & OpenType) provide localized and Unicode + versions of this string. Applications should use the + format specific interface to access them. Can be NULL + (e.g., in fonts embedded in a PDF file).''') + + style_name = property(lambda self: self._FT_Face.contents.style_name, + doc = '''The face's style name. This is an ASCII string, usually in + English, which describes the typeface's style (like + 'Italic', 'Bold', 'Condensed', etc). Not all font formats + provide a style name, so this field is optional, and can be + set to NULL. As for 'family_name', some formats provide + localized and Unicode versions of this string. Applications + should use the format specific interface to access them.''') + + num_fixed_sizes = property(lambda self: self._FT_Face.contents.num_fixed_sizes, + doc = '''The number of bitmap strikes in the face. Even if the + face is scalable, there might still be bitmap strikes, + which are called 'sbits' in that case.''') + + def _get_available_sizes( self ): + sizes = [] + n = self.num_fixed_sizes + FT_sizes = self._FT_Face.contents.available_sizes + for i in range(n): + sizes.append( BitmapSize(FT_sizes[i]) ) + return sizes + available_sizes = property(_get_available_sizes, + doc = '''A list of FT_Bitmap_Size for all bitmap strikes in the + face. It is set to NULL if there is no bitmap strike.''') + + num_charmaps = property(lambda self: self._FT_Face.contents.num_charmaps) + def _get_charmaps( self ): + charmaps = [] + n = self._FT_Face.contents.num_charmaps + FT_charmaps = self._FT_Face.contents.charmaps + for i in range(n): + charmaps.append( Charmap(FT_charmaps[i]) ) + return charmaps + charmaps = property(_get_charmaps, + doc = '''A list of the charmaps of the face.''') + + # ('generic', FT_Generic), + + def _get_bbox( self ): + return BBox( self._FT_Face.contents.bbox ) + bbox = property( _get_bbox, + doc = '''The font bounding box. Coordinates are expressed in font units + (see 'units_per_EM'). The box is large enough to contain any + glyph from the font. Thus, 'bbox.yMax' can be seen as the + 'maximal ascender', and 'bbox.yMin' as the 'minimal + descender'. Only relevant for scalable formats. + + Note that the bounding box might be off by (at least) one pixel + for hinted fonts. See FT_Size_Metrics for further discussion.''') + + units_per_EM = property(lambda self: self._FT_Face.contents.units_per_EM, + doc = '''The number of font units per EM square for this + face. This is typically 2048 for TrueType fonts, and 1000 + for Type 1 fonts. Only relevant for scalable formats.''') + + ascender = property(lambda self: self._FT_Face.contents.ascender, + doc = '''The typographic ascender of the face, expressed in font + units. For font formats not having this information, it is + set to 'bbox.yMax'. Only relevant for scalable formats.''') + + descender = property(lambda self: self._FT_Face.contents.descender, + doc = '''The typographic descender of the face, expressed in font + units. For font formats not having this information, it is + set to 'bbox.yMin'. Note that this field is usually + negative. Only relevant for scalable formats.''') + + height = property(lambda self: self._FT_Face.contents.height, + doc = '''The height is the vertical distance between two consecutive + baselines, expressed in font units. It is always positive. Only + relevant for scalable formats.''') + + max_advance_width = property(lambda self: self._FT_Face.contents.max_advance_width, + doc = '''The maximal advance width, in font units, for all + glyphs in this face. This can be used to make word + wrapping computations faster. Only relevant for + scalable formats.''') + + max_advance_height = property(lambda self: self._FT_Face.contents.max_advance_height, + doc = '''The maximal advance height, in font units, for all + glyphs in this face. This is only relevant for + vertical layouts, and is set to 'height' for fonts + that do not provide vertical metrics. Only relevant + for scalable formats.''') + + underline_position = property(lambda self: self._FT_Face.contents.underline_position, + doc = '''The position, in font units, of the underline line + for this face. It is the center of the underlining + stem. Only relevant for scalable formats.''') + + underline_thickness = property(lambda self: self._FT_Face.contents.underline_thickness, + doc = '''The thickness, in font units, of the underline for + this face. Only relevant for scalable formats.''') + + + def _get_glyph( self ): + return GlyphSlot( self._FT_Face.contents.glyph ) + glyph = property( _get_glyph, + doc = '''The face's associated glyph slot(s).''') + + def _get_size( self ): + size = self._FT_Face.contents.size + metrics = size.contents.metrics + return SizeMetrics(metrics) + size = property( _get_size, + doc = '''The current active size for this face.''') + + def _get_charmap( self ): + return Charmap( self._FT_Face.contents.charmap) + charmap = property( _get_charmap, + doc = '''The current active charmap for this face.''') + + + +# ----------------------------------------------------------------------------- +# SfntName wrapper +# ----------------------------------------------------------------------------- +class SfntName( object ): + ''' + SfntName wrapper + + A structure used to model an SFNT 'name' table entry. + ''' + def __init__(self, name): + ''' + Create a new SfntName object. + + :param name : SFNT 'name' table entry. + + ''' + self._FT_SfntName = name + + platform_id = property(lambda self: self._FT_SfntName.platform_id, + doc = '''The platform ID for 'string'.''') + + encoding_id = property(lambda self: self._FT_SfntName.encoding_id, + doc = '''The encoding ID for 'string'.''') + + language_id = property(lambda self: self._FT_SfntName.language_id, + doc = '''The language ID for 'string'.''') + + name_id = property(lambda self: self._FT_SfntName.name_id, + doc = '''An identifier for 'string'.''') + + #string = property(lambda self: self._FT_SfntName.string) + + string_len = property(lambda self: self._FT_SfntName.string_len, + doc = '''The length of 'string' in bytes.''') + + def _get_string(self): + # #s = self._FT_SfntName + s = string_at(self._FT_SfntName.string, self._FT_SfntName.string_len) + return s + # #return s.decode('utf-16be', 'ignore') + # return s.decode('utf-8', 'ignore') + # #n = s.string_len + # #data = [s.string[i] for i in range(n)] + # #return data + string = property(_get_string, + doc = '''The 'name' string. Note that its format differs depending on + the (platform,encoding) pair. It can be a Pascal String, a + UTF-16 one, etc. + + Generally speaking, the string is not zero-terminated. Please + refer to the TrueType specification for details.''') + + + +# ----------------------------------------------------------------------------- +class Stroker( object ): + ''' + FT_Stroker wrapper + + This component generates stroked outlines of a given vectorial glyph. It + also allows you to retrieve the 'outside' and/or the 'inside' borders of + the stroke. + + This can be useful to generate 'bordered' glyph, i.e., glyphs displayed + with a coloured (and anti-aliased) border around their shape. + ''' + + def __init__( self ): + ''' + Create a new Stroker object. + ''' + library = get_handle( ) + stroker = FT_Stroker( ) + error = FT_Stroker_New( library, byref(stroker) ) + if error: raise FT_Exception( error ) + self._FT_Stroker = stroker + + + def __del__( self ): + ''' + Destroy object. + ''' + FT_Stroker_Done( self._FT_Stroker ) + + + def set( self, radius, line_cap, line_join, miter_limit ): + ''' + Reset a stroker object's attributes. + + :param radius: The border radius. + + :param line_cap: The line cap style. + + :param line_join: The line join style. + + :param miter_limit: The miter limit for the FT_STROKER_LINEJOIN_MITER + style, expressed as 16.16 fixed point value. + + **Note**: + + The radius is expressed in the same units as the outline coordinates. + ''' + FT_Stroker_Set( self._FT_Stroker, + radius, line_cap, line_join, miter_limit ) + + + def rewind( self ): + ''' + Reset a stroker object without changing its attributes. You should call + this function before beginning a new series of calls to + FT_Stroker_BeginSubPath or FT_Stroker_EndSubPath. + ''' + FT_Stroker_Rewind( self._FT_Stroker ) + + + def parse_outline( self, outline, opened ): + ''' + A convenience function used to parse a whole outline with the + stroker. The resulting outline(s) can be retrieved later by functions + like FT_Stroker_GetCounts and FT_Stroker_Export. + + :param outline: The source outline. + + :pram opened: A boolean. If 1, the outline is treated as an open path + instead of a closed one. + + **Note**: + + If 'opened' is 0 (the default), the outline is treated as a closed + path, and the stroker generates two distinct 'border' outlines. + + If 'opened' is 1, the outline is processed as an open path, and the + stroker generates a single 'stroke' outline. + + This function calls 'rewind' automatically. + ''' + error = FT_Stroker_ParseOutline( self._FT_Stroker, outline, opened) + if error: raise FT_Exception( error ) + + + def begin_subpath( self, to, _open ): + ''' + Start a new sub-path in the stroker. + + :param to A pointer to the start vector. + + :param _open: A boolean. If 1, the sub-path is treated as an open one. + + **Note**: + + This function is useful when you need to stroke a path that is not + stored as an 'Outline' object. + ''' + error = FT_Stroker_BeginSubPath( self._FT_Stroker, to, _open ) + if error: raise FT_Exception( error ) + + + def end_subpath( self ): + ''' + Close the current sub-path in the stroker. + + **Note**: + + You should call this function after 'begin_subpath'. If the subpath + was not 'opened', this function 'draws' a single line segment to the + start position when needed. + ''' + error = FT_Stroker_EndSubPath( self._FT_Stroker) + if error: raise FT_Exception( error ) + + + def line_to( self, to ): + ''' + 'Draw' a single line segment in the stroker's current sub-path, from + the last position. + + :param to: A pointer to the destination point. + + **Note**: + + You should call this function between 'begin_subpath' and + 'end_subpath'. + ''' + error = FT_Stroker_LineTo( self._FT_Stroker, to ) + if error: raise FT_Exception( error ) + + + def conic_to( self, control, to ): + ''' + 'Draw' a single quadratic Bezier in the stroker's current sub-path, + from the last position. + + :param control: A pointer to a Bezier control point. + + :param to: A pointer to the destination point. + + **Note**: + + You should call this function between 'begin_subpath' and + 'end_subpath'. + ''' + error = FT_Stroker_ConicTo( self._FT_Stroker, control, to ) + if error: raise FT_Exception( error ) + + + def cubic_to( self, control1, control2, to ): + ''' + 'Draw' a single quadratic Bezier in the stroker's current sub-path, + from the last position. + + :param control1: A pointer to the first Bezier control point. + + :param control2: A pointer to second Bezier control point. + + :param to: A pointer to the destination point. + + **Note**: + + You should call this function between 'begin_subpath' and + 'end_subpath'. + ''' + error = FT_Stroker_CubicTo( self._FT_Stroker, control1, control2, to ) + if error: raise FT_Exception( error ) + + + def get_border_counts( self, border ): + ''' + Call this function once you have finished parsing your paths with the + stroker. It returns the number of points and contours necessary to + export one of the 'border' or 'stroke' outlines generated by the + stroker. + + :param border: The border index. + + :return: number of points, number of contours + ''' + anum_points = FT_UInt() + anum_contours = FT_UInt() + error = FT_Stroker_GetBorderCounts( self._FT_Stroker, border, + byref(anum_points), byref(anum_contours) ) + if error: raise FT_Exception( error ) + return anum_points.value, anum_contours.value + + + def export_border( self , border, outline ): + ''' + Call this function after 'get_border_counts' to export the + corresponding border to your own 'Outline' structure. + + Note that this function appends the border points and contours to your + outline, but does not try to resize its arrays. + + :param border: The border index. + + :param outline: The target outline. + + **Note**: + + Always call this function after get_border_counts to get sure that + there is enough room in your 'Outline' object to receive all new + data. + + When an outline, or a sub-path, is 'closed', the stroker generates two + independent 'border' outlines, named 'left' and 'right' + + When the outline, or a sub-path, is 'opened', the stroker merges the + 'border' outlines with caps. The 'left' border receives all points, + while the 'right' border becomes empty. + + Use the function export instead if you want to retrieve all borders + at once. + ''' + FT_Stroker_ExportBorder( self._FT_Stroker, border, outline._FT_Outline ) + + + def get_counts( self ): + ''' + Call this function once you have finished parsing your paths with the + stroker. It returns the number of points and contours necessary to + export all points/borders from the stroked outline/path. + + :return: number of points, number of contours + ''' + + anum_points = FT_UInt() + anum_contours = FT_UInt() + error = FT_Stroker_GetCounts( self._FT_Stroker, + byref(anum_points), byref(anum_contours) ) + if error: raise FT_Exception( error ) + return anum_points.value, anum_contours.value + + + def export( self, outline ): + ''' + Call this function after get_border_counts to export all borders to + your own 'Outline' structure. + + Note that this function appends the border points and contours to your + outline, but does not try to resize its arrays. + + :param outline: The target outline. + ''' + FT_Stroker_Export( self._FT_Stroker, outline._FT_Outline ) |