Example #1
0
    def addFontFiles(self, font_paths, monospace_only=True):
        """ Add a list of font files to the FontManger font search space.
        Each element of the font_paths list must be a valid path including
        the font file name. Relative paths can be used, with the current
        working directory being the origin.

        If monospace_only is True, each font file will only be added if it is
        a monospace font (as only monospace fonts are currently supported by
        TextBox).

        Adding fonts to the FontManager is not persistent across runs of
        the script, so any extra font paths need to be added each time the
        script starts.
        """

        fi_list = []
        for fp in font_paths:
            if os.path.isfile(fp) and os.path.exists(fp):
                face = Face(fp)
                if monospace_only:
                    if face.is_fixed_width:
                        fi_list.append(self._createFontInfo(fp, face))
                else:
                    fi_list.append(self._createFontInfo(fp, face))

        self.font_family_styles.sort()

        return fi_list
Example #2
0
    def __init__(self, font_info, size, dpi):
        self.font_info = font_info
        self.size = size
        self.dpi = dpi
        self.id = self.getIdFromArgs(font_info, size, dpi)
        self._face = Face(font_info.path)
        self._face.set_char_size(height=self.size * 64, vres=self.dpi)

        self.charcode2glyph = None
        self.charcode2unichr = None
        self.charcode2displaylist = None
        self.max_ascender = None
        self.max_descender = None
        self.max_tile_width = None
        self.max_tile_height = None
        self.max_bitmap_size = None
        self.total_bitmap_area = 0
        self.atlas = None
Example #3
0
    def __init__(self,font_info,size,dpi):
        self.font_info=font_info
        self.size=size
        self.dpi=dpi
        self.id=self.getIdFromArgs(font_info,size,dpi)
        self._face=Face(font_info.path)
        self._face.set_char_size(height=self.size*64,vres=self.dpi)

        self.charcode2glyph=None
        self.charcode2unichr=None
        self.charcode2displaylist=None
        self.max_ascender = None
        self.max_descender = None
        self.max_tile_width = None
        self.max_tile_height = None
        self.max_bitmap_size = None
        self.total_bitmap_area=0
        self.atlas=None
Example #4
0
    def addFontFiles(self,font_paths,monospace_only=True):
        """
        Add a list of font files to the FontManger font search space. Each element
        of the font_paths list must be a valid path including the font file name.
        Relative paths can be used, with the current working directory being
        the origin.

        If monospace_only is True, each font file will only be added if it is a
        monospace font (as only monospace fonts are currently supported by
        TextBox).

        Adding fonts to the FontManager is not persistant across runs of
        the script, so any extra font paths need to be added each time the
        script starts.
        """

        fi_list=[]
        for fp in font_paths:
            if os.path.isfile(fp) and os.path.exists(fp):
                try:
                    face = Face(fp)
                    if monospace_only:
                        if face.is_fixed_width:
                            fi_list.append(self._createFontInfo(fp,face))
                    else:
                        fi_list.append(self._createFontInfo(fp,face))
                except FT_Exception, fte:
                    pass
                except Exception, e:
                    print
                    print ' --- Error --- '
                    print 'Error opening font path:', fp
                    print 'Loaded OK count:', len(fi_list)
                    import traceback
                    traceback.print_exc()
                    return None
Example #5
0
class MonospaceFontAtlas(object):
    def __init__(self,font_info,size,dpi):
        self.font_info=font_info
        self.size=size
        self.dpi=dpi
        self.id=self.getIdFromArgs(font_info,size,dpi)
        self._face=Face(font_info.path)
        self._face.set_char_size(height=self.size*64,vres=self.dpi)

        self.charcode2glyph=None
        self.charcode2unichr=None
        self.charcode2displaylist=None
        self.max_ascender = None
        self.max_descender = None
        self.max_tile_width = None
        self.max_tile_height = None
        self.max_bitmap_size = None
        self.total_bitmap_area=0
        self.atlas=None

    def getID(self):
        return self.id

    @staticmethod
    def getIdFromArgs(font_info,size,dpi):
        return "%s_%d_%d"%(font_info.getID(),size,dpi)

    def createFontAtlas(self):
        if self.atlas:
            self.atlas.free()
            self.atlas=None
        self.charcode2glyph={}
        self.charcode2unichr={}
        self.max_ascender = None
        self.max_descender = None
        self.max_tile_width = None
        self.max_tile_height = None
        self.max_bitmap_size = None
        self.total_bitmap_area=0
        # load font glyphs and calculate max. char size.
        # This is used when the altas is created to properly size the tex.
        # i.e. max glyph size * num glyphs
        #

        max_w,max_h=0,0
        max_ascender, max_descender, max_tile_width = 0, 0, 0
        face=self._face
        face.set_char_size(height=self.size*64,vres=self.dpi)

        # Create texAtlas for glyph set.
        x_ppem=face.size.x_ppem
        y_ppem=face.size.x_ppem
        units_ppem=self.font_info.units_per_em
        est_max_width=(face.bbox.xMax-face.bbox.xMin)/float(units_ppem)*x_ppem
        est_max_height=face.size.ascender/float(units_ppem)*y_ppem
        target_atlas_area=int(est_max_width*est_max_height)*face.num_glyphs
        # make sure it is big enough. ;)
        # height is trimmed before sending to video ram anyhow.
        target_atlas_area=target_atlas_area*3.0
        pow2_area=nextPow2(target_atlas_area)
        atlas_width=2048
        atlas_height=pow2_area/atlas_width
        self.atlas=TextureAtlas(atlas_width,atlas_height)
        charcode, gindex=face.get_first_char()

        while gindex:
            uchar = unichr(charcode)
            if ud.category(uchar) not in (u'Zl',u'Zp',u'Cc',u'Cf',u'Cs',u'Co',u'Cn'):
                self.charcode2unichr[charcode]=uchar
                face.load_char(uchar, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT )
                bitmap = face.glyph.bitmap

                self.total_bitmap_area+=bitmap.width*bitmap.rows
                max_ascender = max( max_ascender, face.glyph.bitmap_top)
                max_descender = max( max_descender, bitmap.rows - face.glyph.bitmap_top )
                max_tile_width = max( max_tile_width,bitmap.width)
                max_w=max(bitmap.width,max_w)
                max_h=max(bitmap.rows,max_h)

                x,y,w,h = self.atlas.get_region(bitmap.width+2, bitmap.rows+2)

                if x < 0:
                    raise Exception("MonospaceFontAtlas.get_region failed for: {0}, requested area: {1}. Atlas Full!".format(charcode,(bitmap.width+2, bitmap.rows+2)))
                x,y = x+1, y+1
                w,h = w-2, h-2
                data = np.array(bitmap._FT_Bitmap.buffer[:(bitmap.rows*bitmap.width)],dtype=np.ubyte).reshape(h,w,1)
                self.atlas.set_region((x,y,w,h), data)

                self.charcode2glyph[charcode]=dict(
                            offset=(face.glyph.bitmap_left, face.glyph.bitmap_top),
                            size=(w,h),
                            atlas_coords=(x,y,w,h),
                            texcoords = [x, y, x + w, y + h],
                            index=gindex,
                            unichar=uchar
                            )

            charcode, gindex = face.get_next_char(charcode, gindex)

        self.max_ascender = max_ascender
        self.max_descender = max_descender
        self.max_tile_width = max_tile_width
        self.max_tile_height = max_ascender+max_descender
        self.max_bitmap_size=max_w,max_h

        # resize atlas
        height=nextPow2(self.atlas.max_y+1)
        self.atlas.resize(height)
        self.atlas.upload()
        self.createDisplayLists()
        self._face=None
        #print 'w_max_glyth info:',w_max_glyph
        #print 'h_max_glyth info:',h_max_glyph

    def createDisplayLists(self):
        glyph_count=len(self.charcode2unichr)
        max_tile_width,max_tile_height=self.max_tile_width,self.max_tile_height
        display_lists_for_chars={}

        base = glGenLists(glyph_count)
        for i,(charcode,glyph) in enumerate( self.charcode2glyph.iteritems()):
            dl_index=base+i
            uchar=self.charcode2unichr[charcode]

            # update tex coords to reflect earlier resize of atlas height.
            gx1,gy1,gx2,gy2=glyph['texcoords']
            gx1=gx1/float(self.atlas.width)
            gy1=gy1/float(self.atlas.height)
            gx2=gx2/float(self.atlas.width)
            gy2=gy2/float(self.atlas.height)
            glyph['texcoords'] =[gx1,gy1,gx2,gy2]

            glNewList(dl_index, GL_COMPILE)
            if uchar not in [u'\t',u'\n']:
                glBegin( GL_QUADS )
                x1 = glyph['offset'][0]
                x2 = x1+glyph['size'][0]
                y1=(self.max_ascender-glyph['offset'][1])
                y2=y1+glyph['size'][1]

                glTexCoord2f( gx1, gy2 ),    glVertex2f( x1,-y2 )
                glTexCoord2f( gx1, gy1 ),    glVertex2f( x1,-y1 )
                glTexCoord2f( gx2, gy1 ),    glVertex2f( x2,-y1 )
                glTexCoord2f( gx2, gy2 ),    glVertex2f( x2,-y2 )
                glEnd( )
                glTranslatef( max_tile_width,0,0)
            glEndList( )

            display_lists_for_chars[charcode]=dl_index

        self.charcode2displaylist=display_lists_for_chars

    def saveGlyphBitmap(self,file_name=None):
        if file_name is None:
            import os
            #print 'CWD:',os.getcwd()
            file_name=os.path.join(os.getcwd(),self.getID().lower().replace(u' ',u'_')+'.png')
        from scipy import misc
        if self.atlas is None:
            self.loadAtlas()
        if self.atlas.depth==1:
            misc.imsave(file_name, self.atlas.data.reshape(self.atlas.data.shape[:2]))
        else:
            misc.imsave(file_name, self.atlas.data)


    def __del__(self):
        self._face=None
        if self.atlas.texid:
            glDeleteTextures(1, self.atlas.texid)
            self.atlas.texid=None
            self.atlas=None
        if self.charcode2displaylist:
            for dl in self.charcode2displaylist.values():
                glDeleteLists(dl, 1)
            self.charcode2displaylist.clear()
        self.charcode2displaylist=None
        if self.charcode2glyph:
            self.charcode2glyph.clear()
            self.charcode2glyph=None
        if self.charcode2unichr:
            self.charcode2unichr.clear()
            self.charcode2unichr=None
Example #6
0
class MonospaceFontAtlas(object):
    def __init__(self, font_info, size, dpi):
        self.font_info = font_info
        self.size = size
        self.dpi = dpi
        self.id = self.getIdFromArgs(font_info, size, dpi)
        self._face = Face(font_info.path)
        self._face.set_char_size(height=self.size * 64, vres=self.dpi)

        self.charcode2glyph = None
        self.charcode2unichr = None
        self.charcode2displaylist = None
        self.max_ascender = None
        self.max_descender = None
        self.max_tile_width = None
        self.max_tile_height = None
        self.max_bitmap_size = None
        self.total_bitmap_area = 0
        self.atlas = None

    def getID(self):
        return self.id

    @staticmethod
    def getIdFromArgs(font_info, size, dpi):
        return "%s_%d_%d" % (font_info.getID(), size, dpi)

    def createFontAtlas(self):
        if self.atlas:
            self.atlas.free()
            self.atlas = None
        self.charcode2glyph = {}
        self.charcode2unichr = {}
        self.max_ascender = None
        self.max_descender = None
        self.max_tile_width = None
        self.max_tile_height = None
        self.max_bitmap_size = None
        self.total_bitmap_area = 0
        # load font glyphs and calculate max. char size.
        # This is used when the altas is created to properly size the tex.
        # i.e. max glyph size * num glyphs

        max_w, max_h = 0, 0
        max_ascender, max_descender, max_tile_width = 0, 0, 0
        face = self._face
        face.set_char_size(height=self.size * 64, vres=self.dpi)

        # Create texAtlas for glyph set.
        x_ppem = face.size.x_ppem
        y_ppem = face.size.x_ppem
        units_ppem = self.font_info.units_per_em
        est_max_width = ((face.bbox.xMax - face.bbox.xMin) /
                         float(units_ppem) * x_ppem)
        est_max_height = face.size.ascender / float(units_ppem) * y_ppem
        target_atlas_area = int(
            est_max_width * est_max_height) * face.num_glyphs
        # make sure it is big enough. ;)
        # height is trimmed before sending to video ram anyhow.
        target_atlas_area = target_atlas_area * 3.0
        pow2_area = nextPow2(target_atlas_area)
        atlas_width = 2048
        atlas_height = pow2_area / atlas_width
        self.atlas = TextureAtlas(atlas_width, atlas_height * 2)
        charcode, gindex = face.get_first_char()

        while gindex:
            uchar = chr(charcode)
            if ud.category(uchar) not in (u'Zl', u'Zp', u'Cc', u'Cf', u'Cs',
                                          u'Co', u'Cn'):
                self.charcode2unichr[charcode] = uchar
                face.load_char(uchar, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT)
                bitmap = face.glyph.bitmap

                self.total_bitmap_area += bitmap.width * bitmap.rows
                max_ascender = max(max_ascender, face.glyph.bitmap_top)
                max_descender = max(max_descender,
                                    bitmap.rows - face.glyph.bitmap_top)
                max_tile_width = max(max_tile_width, bitmap.width)
                max_w = max(bitmap.width, max_w)
                max_h = max(bitmap.rows, max_h)

                x, y, w, h = self.atlas.get_region(bitmap.width + 2,
                                                   bitmap.rows + 2)

                if x < 0:
                    msg = ("MonospaceFontAtlas.get_region failed "
                           "for: {0}, requested area: {1}. Atlas Full!")
                    vals = charcode, (bitmap.width + 2, bitmap.rows + 2)
                    raise Exception(msg.format(vals))
                x, y = x + 1, y + 1
                w, h = w - 2, h - 2
                data = np.array(bitmap._FT_Bitmap.buffer[:(bitmap.rows *
                                                           bitmap.width)],
                                dtype=np.ubyte).reshape(h, w, 1)
                self.atlas.set_region((x, y, w, h), data)

                self.charcode2glyph[charcode] = dict(
                    offset=(face.glyph.bitmap_left, face.glyph.bitmap_top),
                    size=(w, h),
                    atlas_coords=(x, y, w, h),
                    texcoords=[x, y, x + w, y + h],
                    index=gindex,
                    unichar=uchar)

            charcode, gindex = face.get_next_char(charcode, gindex)

        self.max_ascender = max_ascender
        self.max_descender = max_descender
        self.max_tile_width = max_tile_width
        self.max_tile_height = max_ascender + max_descender
        self.max_bitmap_size = max_w, max_h

        # resize atlas
        height = nextPow2(self.atlas.max_y + 1)
        self.atlas.resize(height)
        self.atlas.upload()
        self.createDisplayLists()
        self._face = None

    def createDisplayLists(self):
        glyph_count = len(self.charcode2unichr)
        max_tile_width = self.max_tile_width
        max_tile_height = self.max_tile_height
        display_lists_for_chars = {}

        base = glGenLists(glyph_count)
        for i, (charcode, glyph) in enumerate(self.charcode2glyph.items()):
            dl_index = base + i
            uchar = self.charcode2unichr[charcode]

            # update tex coords to reflect earlier resize of atlas height.
            gx1, gy1, gx2, gy2 = glyph['texcoords']
            gx1 = gx1 / float(self.atlas.width)
            gy1 = gy1 / float(self.atlas.height)
            gx2 = gx2 / float(self.atlas.width)
            gy2 = gy2 / float(self.atlas.height)
            glyph['texcoords'] = [gx1, gy1, gx2, gy2]

            glNewList(dl_index, GL_COMPILE)
            if uchar not in [u'\t', u'\n']:
                glBegin(GL_QUADS)
                x1 = glyph['offset'][0]
                x2 = x1 + glyph['size'][0]
                y1 = (self.max_ascender - glyph['offset'][1])
                y2 = y1 + glyph['size'][1]

                glTexCoord2f(gx1, gy2), glVertex2f(x1, -y2)
                glTexCoord2f(gx1, gy1), glVertex2f(x1, -y1)
                glTexCoord2f(gx2, gy1), glVertex2f(x2, -y1)
                glTexCoord2f(gx2, gy2), glVertex2f(x2, -y2)
                glEnd()
                glTranslatef(max_tile_width, 0, 0)
            glEndList()

            display_lists_for_chars[charcode] = dl_index

        self.charcode2displaylist = display_lists_for_chars

    def saveGlyphBitmap(self, file_name=None):
        if file_name is None:
            import os
            file_name = os.path.join(
                os.getcwd(),
                self.getID().lower().replace(u' ', u'_') + '.png')
        from scipy import misc
        if self.atlas is None:
            self.loadAtlas()
        if self.atlas.depth == 1:
            misc.imsave(file_name,
                        self.atlas.data.reshape(self.atlas.data.shape[:2]))
        else:
            misc.imsave(file_name, self.atlas.data)

    def __del__(self):
        self._face = None
        if self.atlas.texid is not None:
            #glDeleteTextures(1, self.atlas.texid)
            self.atlas.texid = None
            self.atlas = None
        if self.charcode2displaylist is not None:
            # for dl in self.charcode2displaylist.values():
            #    glDeleteLists(dl, 1)
            self.charcode2displaylist.clear()
            self.charcode2displaylist = None
        if self.charcode2glyph is not None:
            self.charcode2glyph.clear()
            self.charcode2glyph = None
        if self.charcode2unichr is not None:
            self.charcode2unichr.clear()
            self.charcode2unichr = None