Exemplo n.º 1
0
def get_font(font="SourceCodePro-Bold.otf"):
    """
    Get the font renderer from freetype
    """
    font_renderer = Face(font)
    font_renderer.set_char_size(32 * 64)  # 32pt size
    return font_renderer
Exemplo n.º 2
0
    def ttf_load(self, fontname, fontsize):
        """
        Loads a font by the full path fontname with fontsize.
        Returns a tuple (errorcode, id of the loaded font)
        """
        try:
            newface = Face(fontname, 0)
        except FT_Exception as exc:
            return (exc.errcode, -1)
        
        id = len(self.ttf_face)
        self.ttf_face.append(newface)
        
        if fontsize == 0:
            self.ttf_fontsize.append(10)
        else:
            self.ttf_fontsize.append(fontsize)

        if newface.is_scalable:
            try:
                newface.set_char_size(0, self.ttf_fontsize[id] * 64, 90, 0)
            except FT_Exception as exc:
                return (exc.errcode, id)
                
        return (0, id)
Exemplo n.º 3
0
def main():
    '''入口点'''
    ttf_in = ask_input('unifont-*.ttf')
    if ttf_in is None:
        raise KeyboardInterrupt
    face = Face(ttf_in)
    face.set_char_size(16 * 64)
    file_name_out = ask_output()
    if file_name_out is None:
        raise KeyboardInterrupt
    buffer = bytearray()

    print('正在写入内存... ', flush=True)
    time_start = time()
    for char in range(65536):
        print('Unicode:', char, end='\r', flush=True)
        b = convert_char(face, char)
        if len(b) != 16 * 16 // 8:
            raise RuntimeError(char, chr(char))
        buffer += b
    print('\n用时 %.2f 秒' % (time() - time_start))

    print('正在写入磁盘... ', end='', flush=True)
    time_start = time()
    with open(file_name_out, 'wb') as f:
        f.write(buffer)
    print('用时 %.2f 秒' % (time() - time_start))
Exemplo n.º 4
0
def plotTextString(stringToPlot, kerning=False, startXY=(0,0)):
    fontPath = "/home/meawoppl/Dropbox/repos/babyfood/cmr10.pfb"
    typeFace = Face(fontPath)
    typeFace.attach_file("/home/meawoppl/Dropbox/repos/babyfood/cmr10.pfm")
    typeFace.set_char_size(48 * 64)

    figure()
    startX, startY = startXY
    for n, char in enumerate(stringToPlot):
        typeFace.load_char(char)
        loopz = unpackCharacter(typeFace.glyph)
        loopz = shiftLoopSet(loopz, startX, startY)
        startX += typeFace.glyph.advance.x
        startY += typeFace.glyph.advance.y
        if kerning and (n != 0):
            kv = typeFace.get_kerning(char, stringToPlot[n-1])
            print(char, stringToPlot[n-1])
            print(kv.x, kv.y)
            print(dir(kv))
            startX += kv.x

        plotLoopSets(loopz)
    axis("equal")
    show()
    close()
Exemplo n.º 5
0
    def __get_path(self, c, fname=expanduser('~/Library/Fonts/keifont.ttf')):
        """ This function is presented originally in freetype-py GitHub
            repository: we can refer the source 'glyph-vector.py' in
            the examples directory.
        """
        face = Face(fname)
        face.set_char_size(24 * 64)
        face.load_char(c)
        slot = face.glyph
        outline = slot.outline

        start, end = 0, 0
        VERTS, CODES = [], []
        for i in xrange(len(outline.contours)):
            end = outline.contours[i]
            points = outline.points[start:end + 1]
            points.append(points[0])
            tags = outline.tags[start:end + 1]
            tags.append(tags[0])

            segments = [
                [
                    points[0],
                ],
            ]
            for j in range(1, len(points)):
                segments[-1].append(points[j])
                if tags[j] & (1 << 0) and j < (len(points) - 1):
                    segments.append([
                        points[j],
                    ])
            verts = [
                points[0],
            ]
            codes = [
                Path.MOVETO,
            ]
            for segment in segments:
                if len(segment) == 2:
                    verts.extend(segment[1:])
                    codes.extend([Path.LINETO])
                elif len(segment) == 3:
                    verts.extend(segment[1:])
                    codes.extend([Path.CURVE3, Path.CURVE3])
                else:
                    verts.append(segment[1])
                    codes.append(Path.CURVE3)
                    for i in range(1, len(segment) - 2):
                        A, B = segment[i], segment[i + 1]
                        C = ((A[0] + B[0]) / 2.0, (A[1] + B[1]) / 2.0)
                        verts.extend([C, B])
                        codes.extend([Path.CURVE3, Path.CURVE3])
                    verts.append(segment[-1])
                    codes.append(Path.CURVE3)
            VERTS.extend(verts)
            CODES.extend(codes)
            start = end + 1
        return Path(VERTS, CODES)
Exemplo n.º 6
0
 def get_bounding_box(self):
     face = Face('UbuntuMono-R.ttf')
     face.set_char_size(self.size)
     face.load_char('g')
     total_width = 0
     total_height = 0
     for char in self.text:
         total_width += face.glyph.metrics.width
         total_height = max(total_height, face.glyph.metrics.width)
     # For the moment return
     return Box(self.position, [total_width, total_height])
Exemplo n.º 7
0
 def get_bounding_box(self):
     face = Face('UbuntuMono-R.ttf')
     face.set_char_size(self.size)
     face.load_char('g')
     total_width = 0
     total_height = 0
     for char in self.text: 
         total_width += face.glyph.metrics.width
         total_height = max(total_height, face.glyph.metrics.width)
     # For the moment return
     return Box(self.position, [total_width, total_height])
Exemplo n.º 8
0
def fn_SetCharSize(o_FreetypeFace: freetype.Face, i_Size: int) -> None:
    """
    :
    :  Sets character size for the given font face. (In-place).
    :
    :
    :  Args:
    :      freetype.Face o_FreetypeFace : Font face used to determine / set character size
    :      int           i_Size         : Target size of font face
    :
    :  Returns:
    :      None
    :
    :
    """
    o_FreetypeFace.set_char_size(i_Size)
Exemplo n.º 9
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'):
                try:
                    face.load_char(uchar, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT)
                    bitmap = face.glyph.bitmap
    
                    self.charcode2unichr[charcode] = uchar
    
                    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)
                except (FT_Exception):
                    print("Warning: TextBox stim could not load font face for charcode / uchar / category: ",  charcode, " / ", uchar, " / ", ud.category(uchar))

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

        self.max_ascender = max_ascender
        self.max_descender = max_descender
        
        #print('linearHoriAdvance:', face.glyph.linearHoriAdvance/65536)
        #print('max_advance:', face.max_advance_width/64)
        self.max_tile_width = int(face.glyph.metrics.horiAdvance/64)
        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 ['\t', '\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
Exemplo n.º 10
0
class TextRenderer(object):
    _VERT_SHADER_SRC_PATH = os.path.join(_here, 'shaders',
                                         'text_renderer_vert.glsl')
    _FRAG_SHADER_SRC_PATH = os.path.join(_here, 'shaders',
                                         'text_renderer_frag.glsl')
    _VERT_SHADER_SRC = None
    _FRAG_SHADER_SRC = None

    def __init__(self,
                 font_file=os.path.join(_here, 'fonts', 'VeraMono.ttf'),
                 size=72 * 16):
        _logger.debug('loading %s...', font_file)
        self._font_file = font_file
        self._face = Face(font_file)
        self._face.set_char_size(size)
        width, max_asc, max_desc = 0, 0, 0
        widths = []
        for c in range(32, 128):
            self._face.load_char(chr(c),
                                 FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT)
            bitmap = self._face.glyph.bitmap
            width = max(width, bitmap.width)
            max_asc = max(max_asc, self._face.glyph.bitmap_top)
            max_desc = max(max_desc, bitmap.rows - self._face.glyph.bitmap_top)
            widths.append(bitmap.width)
        self._max_asc = max_asc
        self._widths = np.array(widths)
        self._width = width
        self._height = max_asc + max_desc
        self._read_shader_src()
        self._screen_size = (800.0, 600.0)
        self._texcoords = np.zeros((32, 2), dtype=np.float32)
        self._gl_initialized = False

    def set_screen_size(self, screen_size):
        self._screen_size = screen_size

    def init_gl(self):
        if self._gl_initialized:
            return
        import OpenGL.GL as gl
        self._texture_unit = 4
        vs_id = gl.glCreateShader(gl.GL_VERTEX_SHADER)
        gl.glShaderSource(vs_id, self._VERT_SHADER_SRC)
        gl.glCompileShader(vs_id)
        if not gl.glGetShaderiv(vs_id, gl.GL_COMPILE_STATUS):
            raise Exception('failed to compile %s vertex shader:\n%s' %
                            (self.__class__.__name__,
                             gl.glGetShaderInfoLog(vs_id).decode()))
        fs_id = gl.glCreateShader(gl.GL_FRAGMENT_SHADER)
        gl.glShaderSource(fs_id, self._FRAG_SHADER_SRC)
        gl.glCompileShader(fs_id)
        if not gl.glGetShaderiv(fs_id, gl.GL_COMPILE_STATUS):
            raise Exception('failed to compile %s fragment shader:\n%s' %
                            (self.__class__.__name__,
                             gl.glGetShaderInfoLog(fs_id).decode()))
        self._program_id = gl.glCreateProgram()
        gl.glAttachShader(self._program_id, vs_id)
        gl.glAttachShader(self._program_id, fs_id)
        gl.glLinkProgram(self._program_id)
        gl.glDetachShader(self._program_id, vs_id)
        gl.glDetachShader(self._program_id, fs_id)
        if not gl.glGetProgramiv(self._program_id, gl.GL_LINK_STATUS):
            raise Exception('failed to link program for %s' %
                            self.__class__.__name__)
        self._attribute_locations = {
            attribute: gl.glGetAttribLocation(self._program_id, attribute)
            for attribute in self._ATTRIBUTES
        }
        self._uniform_locations = {
            uniform: gl.glGetUniformLocation(self._program_id, uniform)
            for uniform in self._UNIFORMS
        }
        width, height = self._width, self._height
        self._image_width, self._image_height = image_width, image_height = width * 16, height * 6
        bitmap_buffer = np.zeros((image_height, image_width), dtype=np.ubyte)
        self._char_to_texcoords = {}
        for j in range(6):
            for i in range(16):
                i_char = j * 16 + i
                char = chr(32 + i_char)
                self._char_to_texcoords[char] = (i / 16.0, j / 6.0)
                self._face.load_char(char,
                                     FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT)
                glyph = self._face.glyph
                bitmap = glyph.bitmap
                x = i * width + glyph.bitmap_left
                y = j * height + self._max_asc - glyph.bitmap_top
                bitmap_buffer[y:y + bitmap.rows,
                              x:x + bitmap.width].flat = bitmap.buffer
        self._texture_id = gl.glGenTextures(1)
        gl.glBindTexture(gl.GL_TEXTURE_2D, self._texture_id)
        self._sampler_id = gl.glGenSamplers(1)
        gl.glSamplerParameteri(self._sampler_id, gl.GL_TEXTURE_MIN_FILTER,
                               gl.GL_LINEAR)
        gl.glSamplerParameteri(self._sampler_id, gl.GL_TEXTURE_MAG_FILTER,
                               gl.GL_LINEAR)
        gl.glSamplerParameteri(self._sampler_id, gl.GL_TEXTURE_WRAP_S,
                               gl.GL_CLAMP_TO_EDGE)
        gl.glSamplerParameteri(self._sampler_id, gl.GL_TEXTURE_WRAP_T,
                               gl.GL_CLAMP_TO_EDGE)
        gl.glBindSampler(self._texture_unit, self._sampler_id)
        gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1)
        gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RED, image_width,
                        image_height, 0, gl.GL_RED, gl.GL_UNSIGNED_BYTE,
                        bitmap_buffer)
        gl.glGenerateMipmap(gl.GL_TEXTURE_2D)
        gl.glBindTexture(gl.GL_TEXTURE_2D, 0)
        if gl.glGetError() != gl.GL_NO_ERROR:
            raise Exception('failed to create font texture')
        self._gl_initialized = True
        _logger.debug('%s.init_gl: OK', self.__class__.__name__)

    def draw_text(self,
                  text,
                  color=(1.0, 1.0, 0.0, 0.0),
                  screen_position=(0.0, 0.0)):
        import OpenGL.GL as gl
        gl.glUseProgram(self._program_id)
        gl.glActiveTexture(gl.GL_TEXTURE0 + self._texture_unit)
        gl.glBindTexture(gl.GL_TEXTURE_2D, self._texture_id)
        #gl.glBindSampler(tex_unit, self._sampler_id)
        gl.glUniform1i(self._uniform_locations['u_fonttex'],
                       self._texture_unit)
        gl.glUniform4f(self._uniform_locations['u_color'], *color)
        gl.glUniform2f(self._uniform_locations['u_screen_size'],
                       *self._screen_size)
        gl.glUniform2f(self._uniform_locations['u_char_size'], self._width,
                       self._height)
        gl.glUniform2f(self._uniform_locations['u_screen_position'],
                       *screen_position)
        gl.glUniform2f(self._uniform_locations['u_fonttex_size'],
                       self._image_width, self._image_height)
        nchars = len(text)
        gl.glUniform1ui(self._uniform_locations['u_nchars'], nchars)
        self._texcoords[:nchars] = [self._char_to_texcoords[c] for c in text]
        gl.glUniform2fv(self._uniform_locations['u_texcoords'], nchars,
                        self._texcoords)
        gl.glEnable(gl.GL_BLEND)
        gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
        gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
        gl.glDisable(gl.GL_BLEND)

    def save_image(self, filename=None):
        from PIL import Image
        if filename is None:
            filename = os.path.split_ext(os.path.basename(
                self._font_file))[0] + '.png'
        _logger.debug('filename = %s', filename)
        width, height = self._width, self._height
        image_width, image_height = width * 16, height * 6
        bitmap_buffer = np.zeros((image_height, image_width), dtype=np.ubyte)
        for j in range(6):
            for i in range(16):
                i_char = j * 16 + i
                char = chr(32 + i_char)
                self._face.load_char(char,
                                     FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT)
                glyph = self._face.glyph
                bitmap = glyph.bitmap
                x = i * width + glyph.bitmap_left
                y = j * height + self._max_asc - glyph.bitmap_top
                bitmap_buffer[y:y + bitmap.rows,
                              x:x + bitmap.width].flat = bitmap.buffer
        image = Image.new('L',
                          (bitmap_buffer.shape[1], bitmap_buffer.shape[0]))
        image.putdata(list(bitmap_buffer.ravel()))
        image.save(filename)
        _logger.info('...saved to %s', filename)

    @classmethod
    def _read_shader_src(cls):
        if cls._VERT_SHADER_SRC is None:
            with open(cls._VERT_SHADER_SRC_PATH) as f:
                cls._VERT_SHADER_SRC = f.read()
            attr_matches = _ATTRIBUTE_DECL_RE.finditer(cls._VERT_SHADER_SRC)
            attributes = []
            for m in attr_matches:
                attributes.append(m['attribute_name'])
            cls._ATTRIBUTES = attributes
        if cls._FRAG_SHADER_SRC is None:
            with open(cls._FRAG_SHADER_SRC_PATH) as f:
                cls._FRAG_SHADER_SRC = f.read()
            unif_matches = _UNIFORM_DECL_RE.finditer(cls._FRAG_SHADER_SRC)
            uniforms = []
            for m in unif_matches:
                uniforms.append(m['uniform_name'])
            cls._UNIFORMS = uniforms
Exemplo n.º 11
0
    def renderLabel(self, inString):
        dwg = svgwrite.Drawing()  # SVG drawing in memory
        strIdx = 0  # Used to iterate over inString
        xOffset = 100  # Cumulative character placement offset
        yOffset = 0  # Cumulative character placement offset
        charSizeX = 8  # Character size constant
        charSizeY = 8  # Character size constant
        baseline = 170  # Y value of text baseline
        glyphBounds = [
        ]  # List of boundingBox objects to track rendered character size
        finalSegments = []  # List of output paths
        escaped = False  # Track whether the current character was preceded by a '\'
        lineover = False  # Track whether the current character needs to be lined over
        lineoverList = []

        # If we can't find the typeface that the user requested, we have to quit
        try:
            face = Face(
                os.path.dirname(os.path.abspath(__file__)) + '/typeface/' +
                self.fontName + '.ttf')
            face.set_char_size(charSizeX, charSizeY, 200, 200)
        except Exception as e:
            print(e)
            print("WARN: No Typeface found with the name " + self.fontName +
                  ".ttf")
            sys.exit(0)  # quit Python

        # If the typeface that the user requested exists, but there's no position table for it, we'll continue with a warning
        try:
            table = __import__(
                'KiBuzzard.KiBuzzard.buzzard.typeface.' + self.fontName,
                globals(), locals(), ['glyphPos'])
            glyphPos = table.glyphPos
            spaceDistance = table.spaceDistance
        except:
            glyphPos = 0
            spaceDistance = 60
            print(
                "WARN: No Position Table found for this typeface. Composition will be haphazard at best."
            )

        # If there's lineover text, drop the text down to make room for the line
        dropBaseline = False
        a = False
        x = 0
        while x < len(inString):
            if x > 0 and inString[x] == '\\':
                a = True
                if x != len(inString) - 1:
                    x += 1
            if inString[x] == '!' and not a:
                dropBaseline = True
            a = False
            x += 1
        if dropBaseline:
            baseline = 190

        # Draw and compose the glyph portion of the tag
        for charIdx in range(len(inString)):
            # Check whether this character is a space
            if inString[charIdx] == ' ':
                glyphBounds.append(boundingBox(0, 0, 0, 0))
                xOffset += spaceDistance
                continue
            # Check whether this character is a backslash that isn't escaped
            # and isn't the first character (denoting a backslash-shaped tag)
            if inString[charIdx] == '\\' and charIdx > 0 and not escaped:
                glyphBounds.append(boundingBox(0, 0, 0, 0))
                escaped = True
                continue
            # If this is a non-escaped '!' mark the beginning of lineover
            if inString[charIdx] == '!' and not escaped:
                glyphBounds.append(boundingBox(0, 0, 0, 0))
                lineover = True
                # If we've hit the end of the string but not the end of the lineover
                # go ahead and finish it out
                if charIdx == len(inString) - 1 and len(lineoverList) > 0:
                    linePaths = []
                    linePaths.append(
                        Line(start=complex(lineoverList[0], 10),
                             end=complex(xOffset, 10)))
                    linePaths.append(
                        Line(start=complex(xOffset, 10),
                             end=complex(xOffset, 30)))
                    linePaths.append(
                        Line(start=complex(xOffset, 30),
                             end=complex(lineoverList[0], 30)))
                    linePaths.append(
                        Line(start=complex(lineoverList[0], 30),
                             end=complex(lineoverList[0], 10)))
                    linepath = Path(*linePaths)
                    linepath = elPath(linepath.d())
                    finalSegments.append(linepath)
                    lineover = False
                    lineoverList.clear()
                continue
            # All special cases end in 'continue' so if we've gotten here we can clear our flags
            if escaped:
                escaped = False

            face.load_char(
                inString[charIdx])  # Load character curves from font
            outline = face.glyph.outline  # Save character curves to var
            y = [t[1] for t in outline.points]
            # flip the points
            outline_points = [(p[0], max(y) - p[1]) for p in outline.points]
            start, end = 0, 0
            paths = []
            box = 0
            yOffset = 0

            for i in range(len(outline.contours)):
                end = outline.contours[i]
                points = outline_points[start:end + 1]
                points.append(points[0])
                tags = outline.tags[start:end + 1]
                tags.append(tags[0])
                segments = [
                    [
                        points[0],
                    ],
                ]
                box = boundingBox(points[0][0], points[0][1], points[0][0],
                                  points[0][1])
                for j in range(1, len(points)):
                    if not tags[j]:  # if this point is off-path
                        if tags[j - 1]:  # and the last point was on-path
                            segments[-1].append(
                                points[j])  # toss this point onto the segment
                        elif not tags[j -
                                      1]:  # and the last point was off-path
                            # get center point of two
                            newPoint = ((points[j][0] + points[j - 1][0]) /
                                        2.0,
                                        (points[j][1] + points[j - 1][1]) /
                                        2.0)
                            segments[-1].append(
                                newPoint
                            )  # toss this new point onto the segment
                            segments.append(
                                [
                                    newPoint,
                                    points[j],
                                ]
                            )  # and start a new segment with the new point and this one
                    elif tags[j]:  # if this point is on-path
                        segments[-1].append(
                            points[j])  # toss this point onto the segment
                        if j < (len(points) - 1):
                            segments.append(
                                [
                                    points[j],
                                ]
                            )  # and start a new segment with this point if we're not at the end

                for segment in segments:
                    if len(segment) == 2:
                        paths.append(
                            Line(start=tuple_to_imag(segment[0]),
                                 end=tuple_to_imag(segment[1])))

                    elif len(segment) == 3:
                        paths.append(
                            QuadraticBezier(start=tuple_to_imag(segment[0]),
                                            control=tuple_to_imag(segment[1]),
                                            end=tuple_to_imag(segment[2])))
                start = end + 1

            # Derive bounding box of character
            for segment in paths:
                i = 0
                while i < 10:
                    point = segment.point(0.1 * i)
                    if point.real > box.xMax:
                        box.xMax = point.real
                    if point.imag > box.yMax:
                        box.yMax = point.imag
                    if point.real < box.xMin:
                        box.xMin = point.real
                    if point.imag < box.yMin:
                        box.yMin = point.imag
                    i += 1

            glyphBounds.append(box)
            path = Path(*paths)
            if glyphPos != 0:
                try:
                    xOffset += glyphPos[inString[charIdx]].real
                    yOffset = glyphPos[inString[charIdx]].imag
                except:
                    pass
            if lineover and len(lineoverList) == 0:
                lineoverList.append(xOffset)
                lineover = False

            if (lineover and len(lineoverList) > 0):
                linePaths = []
                linePaths.append(
                    Line(start=complex(lineoverList[0], 10),
                         end=complex(xOffset, 10)))
                linePaths.append(
                    Line(start=complex(xOffset, 10), end=complex(xOffset, 30)))
                linePaths.append(
                    Line(start=complex(xOffset, 30),
                         end=complex(lineoverList[0], 30)))
                linePaths.append(
                    Line(start=complex(lineoverList[0], 30),
                         end=complex(lineoverList[0], 10)))
                linepath = Path(*linePaths)
                linepath = elPath(linepath.d())
                finalSegments.append(linepath)
                lineover = False
                lineoverList.clear()

            pathTransform = Matrix.translate(xOffset,
                                             baseline + yOffset - box.yMax)
            path = elPath(path.d()) * pathTransform
            path = elPath(path.d())
            finalSegments.append(path)
            xOffset += 30
            if glyphPos != 0:
                try:
                    xOffset -= glyphPos[inString[charIdx]].real
                except:
                    pass
            xOffset += (glyphBounds[charIdx].xMax - glyphBounds[charIdx].xMin)
            strIdx += 1

        if self.leftCap == '' and self.rightCap == '':
            for i in range(len(finalSegments)):
                svgObj = dwg.add(dwg.path(finalSegments[i].d()))
                svgObj['fill'] = "#000000"
        else:
            #draw the outline of the label as a filled shape and
            #subtract each latter from it
            tagPaths = []
            if self.rightCap == 'round':
                tagPaths.append(
                    Line(start=complex(100, 0), end=complex(xOffset, 0)))
                tagPaths.append(
                    Arc(start=complex(xOffset, 0),
                        radius=complex(100, 100),
                        rotation=180,
                        large_arc=1,
                        sweep=1,
                        end=complex(xOffset, 200)))
            elif self.rightCap == 'square':
                tagPaths.append(
                    Line(start=complex(100, 0), end=complex(xOffset, 0)))
                tagPaths.append(
                    Line(start=complex(xOffset, 0),
                         end=complex(xOffset + 50, 0)))
                tagPaths.append(
                    Line(start=complex(xOffset + 50, 0),
                         end=complex(xOffset + 50, 200)))
                tagPaths.append(
                    Line(start=complex(xOffset + 50, 200),
                         end=complex(xOffset, 200)))
            elif self.rightCap == 'pointer':
                tagPaths.append(
                    Line(start=complex(100, 0), end=complex(xOffset, 0)))
                tagPaths.append(
                    Line(start=complex(xOffset, 0),
                         end=complex(xOffset + 50, 0)))
                tagPaths.append(
                    Line(start=complex(xOffset + 50, 0),
                         end=complex(xOffset + 100, 100)))
                tagPaths.append(
                    Line(start=complex(xOffset + 100, 100),
                         end=complex(xOffset + 50, 200)))
                tagPaths.append(
                    Line(start=complex(xOffset + 50, 200),
                         end=complex(xOffset, 200)))
            elif self.rightCap == 'flagtail':
                tagPaths.append(
                    Line(start=complex(100, 0), end=complex(xOffset, 0)))
                tagPaths.append(
                    Line(start=complex(xOffset, 0),
                         end=complex(xOffset + 100, 0)))
                tagPaths.append(
                    Line(start=complex(xOffset + 100, 0),
                         end=complex(xOffset + 50, 100)))
                tagPaths.append(
                    Line(start=complex(xOffset + 50, 100),
                         end=complex(xOffset + 100, 200)))
                tagPaths.append(
                    Line(start=complex(xOffset + 100, 200),
                         end=complex(xOffset, 200)))
            elif self.rightCap == 'fslash':
                tagPaths.append(
                    Line(start=complex(100, 0), end=complex(xOffset, 0)))
                tagPaths.append(
                    Line(start=complex(xOffset, 0),
                         end=complex(xOffset + 50, 0)))
                tagPaths.append(
                    Line(start=complex(xOffset + 50, 0),
                         end=complex(xOffset, 200)))
            elif self.rightCap == 'bslash':
                tagPaths.append(
                    Line(start=complex(100, 0), end=complex(xOffset, 0)))
                tagPaths.append(
                    Line(start=complex(xOffset, 0),
                         end=complex(xOffset + 50, 200)))
                tagPaths.append(
                    Line(start=complex(xOffset + 50, 200),
                         end=complex(xOffset, 200)))
            elif self.rightCap == '' and self.leftCap != '':
                tagPaths.append(
                    Line(start=complex(100, 0), end=complex(xOffset, 0)))
                tagPaths.append(
                    Line(start=complex(xOffset, 0), end=complex(xOffset, 200)))

            if self.leftCap == 'round':
                tagPaths.append(
                    Line(start=complex(xOffset, 200), end=complex(100, 200)))
                tagPaths.append(
                    Arc(start=complex(100, 200),
                        radius=complex(100, 100),
                        rotation=180,
                        large_arc=0,
                        sweep=1,
                        end=complex(100, 0)))
            elif self.leftCap == 'square':
                tagPaths.append(
                    Line(start=complex(xOffset, 200), end=complex(100, 200)))
                tagPaths.append(
                    Line(start=complex(100, 200), end=complex(50, 200)))
                tagPaths.append(
                    Line(start=complex(50, 200), end=complex(50, 0)))
                tagPaths.append(Line(start=complex(50, 0), end=complex(100,
                                                                       0)))
            elif self.leftCap == 'pointer':
                tagPaths.append(
                    Line(start=complex(xOffset, 200), end=complex(100, 200)))
                tagPaths.append(
                    Line(start=complex(100, 200), end=complex(50, 200)))
                tagPaths.append(
                    Line(start=complex(50, 200), end=complex(0, 100)))
                tagPaths.append(Line(start=complex(0, 100), end=complex(50,
                                                                        0)))
                tagPaths.append(Line(start=complex(50, 0), end=complex(100,
                                                                       0)))
            elif self.leftCap == 'flagtail':
                tagPaths.append(
                    Line(start=complex(xOffset, 200), end=complex(100, 200)))
                tagPaths.append(
                    Line(start=complex(100, 200), end=complex(0, 200)))
                tagPaths.append(
                    Line(start=complex(0, 200), end=complex(50, 100)))
                tagPaths.append(Line(start=complex(50, 100), end=complex(0,
                                                                         0)))
                tagPaths.append(Line(start=complex(0, 0), end=complex(100, 0)))
            elif self.leftCap == 'fslash':
                tagPaths.append(
                    Line(start=complex(xOffset, 200), end=complex(100, 200)))
                tagPaths.append(
                    Line(start=complex(100, 200), end=complex(50, 200)))
                tagPaths.append(
                    Line(start=complex(50, 200), end=complex(100, 0)))
            elif self.leftCap == 'bslash':
                tagPaths.append(
                    Line(start=complex(xOffset, 200), end=complex(100, 200)))
                tagPaths.append(
                    Line(start=complex(100, 200), end=complex(50, 0)))
                tagPaths.append(Line(start=complex(50, 0), end=complex(100,
                                                                       0)))
            elif self.leftCap == '' and self.rightCap != '':
                tagPaths.append(
                    Line(start=complex(xOffset, 200), end=complex(100, 200)))
                tagPaths.append(
                    Line(start=complex(100, 200), end=complex(100, 0)))

            path = Path(*tagPaths)
            for i in range(len(finalSegments)):
                path = elPath(path.d() + " " + finalSegments[i].reverse())
            tagObj = dwg.add(dwg.path(path.d()))
            tagObj['fill'] = "#000000"

        dwg['width'] = xOffset + 100
        dwg['height'] = 250

        #dwg.saveas('out.svg')

        print('create svg')

        return dwg
Exemplo n.º 12
0
def text2pathd(text, group_transform=(1, 0, 0, 1, 0, 0)):
    attributes = dom2dict(text)
    if "font-size" in attributes:
        font_size = float(attributes["font-size"])
    elif "style" in attributes:
        if attributes["style"].find("font-size") >= 0:
            font_size = attributes["style"].split("font-size:")[1].split(
                ";")[0]
            font_size = float(font_size.replace("px", ""))
        else:
            font_size = 12
    else:
        font_size = 12
    if "x" in attributes:
        x_global_offset = float(attributes["x"])
    else:
        x_global_offset = 0
    if "y" in attributes:
        y_global_offset = float(attributes["y"])
    else:
        y_global_offset = 0
    if hasattr(text.childNodes[0], "data"):
        text_string = text.childNodes[0].data
    else:
        flow_para = text.getElementsByTagName('flowPara')
        if flow_para:
            text_string = flow_para[0].childNodes[0].data
    # strip newline characters from the string, they aren't rendered in svg
    text_string = text_string.replace("\n", "").replace("\r", "")

    def tuple_to_imag(t):
        return t[0] + t[1] * 1j

    # keep fonts with repository, as dealing with importing fonts across platforms is a
    # nightmare
    foldername = os_path.dirname(os_path.abspath(__file__))
    face = Face(os_path.join(foldername, 'Vera.ttf'))

    face.set_char_size(48 * 64)
    scale = font_size / face.size.height
    outlines = []
    current_x = 0
    transform = get_transform(text)
    transform = combine_transforms(transform, group_transform)
    x_global_offset, y_global_offset = transform_point(
        [x_global_offset, y_global_offset], transform)
    for i, letter in enumerate(text_string):
        face.load_char(letter)
        outline = face.glyph.outline
        if i != 0:
            kerning = face.get_kerning(text_string[i - 1], text_string[i])
            kerning_x = kerning.x
        else:
            kerning_x = 0

        if text_string[i] == ' ':
            # a space is usually 30% of the widest character, capital W
            char_width = face.size.max_advance * 0.3
            char_height = 0
            char_offset = 0
        else:
            char_width = outline.get_bbox().xMax
            char_offset = face.size.height - outline.get_bbox().yMax
            char_height = outline.get_bbox().yMax

        outline_dict = {}
        current_x += kerning_x
        outline_dict["points"] = [
            (scale * (p[0] + current_x) + x_global_offset,
             scale * (char_offset + char_height - p[1]) + y_global_offset)
            for p in outline.points
        ]
        outline_dict["contours"] = outline.contours
        outline_dict["tags"] = outline.tags
        outlines.append(outline_dict)
        current_x += char_width

    paths = []
    for outline in outlines:
        start, end = 0, 0
        for i in range(len(outline["contours"])):
            end = outline["contours"][i]
            points = outline["points"][start:end + 1]
            points.append(points[0])
            tags = outline["tags"][start:end + 1]
            tags.append(tags[0])

            segments = [
                [
                    points[0],
                ],
            ]
            for j in range(1, len(points)):
                segments[-1].append(points[j])
                if tags[j] and j < (len(points) - 1):
                    segments.append([
                        points[j],
                    ])
            for segment in segments:
                if len(segment) == 2:
                    paths.append(
                        Line(start=tuple_to_imag(segment[0]),
                             end=tuple_to_imag(segment[1])))
                elif len(segment) == 3:
                    paths.append(
                        QuadraticBezier(start=tuple_to_imag(segment[0]),
                                        control=tuple_to_imag(segment[1]),
                                        end=tuple_to_imag(segment[2])))
                elif len(segment) == 4:
                    C = ((segment[1][0] + segment[2][0]) / 2.0,
                         (segment[1][1] + segment[2][1]) / 2.0)

                    paths.append(
                        QuadraticBezier(start=tuple_to_imag(segment[0]),
                                        control=tuple_to_imag(segment[1]),
                                        end=tuple_to_imag(C)))
                    paths.append(
                        QuadraticBezier(start=tuple_to_imag(C),
                                        control=tuple_to_imag(segment[2]),
                                        end=tuple_to_imag(segment[3])))
            start = end + 1

    path = Path(*paths)
    return path.d()
Exemplo n.º 13
0
def char(ch):
    def tuple_to_imag(t):
        return t[0] + t[1] * 1j

    from freetype import Face
    #face = Face('/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf')
    face = Face(ddd.DATA_DIR + '/fonts/OpenSansEmoji.ttf')
    face.set_char_size(48 * 64)
    face.load_char(ch)

    #kerning = face.get_kerning(ch, 'x')  # or from previous, actually?
    #print(kerning)

    outline = face.glyph.outline
    y = [t[1] for t in outline.points]
    # flip the points
    outline_points = [(p[0], max(y) - p[1]) for p in outline.points]

    start, end = 0, 0
    paths = []

    for i in range(len(outline.contours)):
        end = outline.contours[i]
        points = outline_points[start:end + 1]
        points.append(points[0])
        tags = outline.tags[start:end + 1]
        tags.append(tags[0])

        segments = [
            [
                points[0],
            ],
        ]
        for j in range(1, len(points)):
            segments[-1].append(points[j])
            if tags[j] and j < (len(points) - 1):
                segments.append([
                    points[j],
                ])

        for segment in segments:
            if len(segment) == 2:
                paths.append(
                    Line(start=tuple_to_imag(segment[0]),
                         end=tuple_to_imag(segment[1])))
            elif len(segment) == 3:
                paths.append(
                    QuadraticBezier(start=tuple_to_imag(segment[0]),
                                    control=tuple_to_imag(segment[1]),
                                    end=tuple_to_imag(segment[2])))
            elif len(segment) == 4:
                C = ((segment[1][0] + segment[2][0]) / 2.0,
                     (segment[1][1] + segment[2][1]) / 2.0)

                paths.append(
                    QuadraticBezier(start=tuple_to_imag(segment[0]),
                                    control=tuple_to_imag(segment[1]),
                                    end=tuple_to_imag(C)))
                paths.append(
                    QuadraticBezier(start=tuple_to_imag(C),
                                    control=tuple_to_imag(segment[2]),
                                    end=tuple_to_imag(segment[3])))

        start = end + 1

    path = Path(*paths)
    #wsvg(path, filename="/tmp/test.svg")
    path_d = path.d()

    # https://gis.stackexchange.com/questions/301605/how-to-create-shape-in-shapely-from-an-svg-path-element
    # This page also has info about SVG reading!

    from svgpath2mpl import parse_path
    #svgpath = 'M10 10 C 20 20, 40 20, 50 10Z'
    mpl_path = parse_path(path_d)
    coords = mpl_path.to_polygons()

    # Add or subtract
    char_2d = ddd.polygon(coords[0])
    for c in coords[1:]:
        ng = ddd.polygon(c)
        #print (ng.geom.is_valid)
        if not ng.geom.is_valid: continue
        if char_2d.contains(ng):
            char_2d = char_2d.subtract(ng)
        else:
            char_2d = char_2d.union(ng)

    #result = ddd.group([ddd.polygon(c) for c in coords], empty=2)
    result = char_2d
    result = result.scale([1.0 / (48 * 64), -1.0 / (48 * 64)])

    result = result.simplify(0.005)  #

    return result
from svgpathtools import wsvg, Line, QuadraticBezier, Path

from freetype import Face


def tuple_to_imag(t):
    return t[0] + t[1] * 1j


face = Face('./Vera.ttf')
face.set_char_size(48 * 64)
face.load_char('a')
outline = face.glyph.outline
y = [t[1] for t in outline.points]
# flip the points
outline_points = [(p[0], max(y) - p[1]) for p in outline.points]
start, end = 0, 0
paths = []

for i in range(len(outline.contours)):
    end = outline.contours[i]
    points = outline_points[start:end + 1]
    points.append(points[0])
    tags = outline.tags[start:end + 1]
    tags.append(tags[0])

    segments = [[points[0], ], ]
    for j in range(1, len(points)):
        segments[-1].append(points[j])
        if tags[j] and j < (len(points) - 1):
            segments.append([points[j], ])
Exemplo n.º 15
0
class TextRenderer(object):
    _VERT_SHADER_SRC_PATH = os.path.join(_here, 'shaders', 'text_renderer_vert.glsl')
    _FRAG_SHADER_SRC_PATH = os.path.join(_here, 'shaders', 'text_renderer_frag.glsl')
    _VERT_SHADER_SRC = None
    _FRAG_SHADER_SRC = None

    def __init__(self,
                 font_file=os.path.join(_here, 'fonts', 'VeraMono.ttf'),
                 size=72*16):
        _logger.debug('loading %s...', font_file)
        self._font_file = font_file
        self._face = Face(font_file)
        self._face.set_char_size(size)
        width, max_asc, max_desc = 0, 0, 0
        widths = []
        for c in range(32, 128):
            self._face.load_char(chr(c), FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT)
            bitmap = self._face.glyph.bitmap
            width = max(width, bitmap.width)
            max_asc = max(max_asc, self._face.glyph.bitmap_top)
            max_desc = max(max_desc, bitmap.rows-self._face.glyph.bitmap_top)
            widths.append(bitmap.width)
        self._max_asc = max_asc
        self._widths = np.array(widths)
        self._width = width
        self._height = max_asc + max_desc
        self._read_shader_src()
        self._screen_size = (800.0, 600.0)
        self._texcoords = np.zeros((32, 2), dtype=np.float32)
        self._gl_initialized = False

    def set_screen_size(self, screen_size):
        self._screen_size = screen_size

    def init_gl(self):
        if self._gl_initialized:
            return
        import OpenGL.GL as gl
        self._texture_unit = 4
        vs_id = gl.glCreateShader(gl.GL_VERTEX_SHADER)
        gl.glShaderSource(vs_id, self._VERT_SHADER_SRC)
        gl.glCompileShader(vs_id)
        if not gl.glGetShaderiv(vs_id, gl.GL_COMPILE_STATUS):
            raise Exception('failed to compile %s vertex shader:\n%s' %
                            (self.__class__.__name__, gl.glGetShaderInfoLog(vs_id).decode()))
        fs_id = gl.glCreateShader(gl.GL_FRAGMENT_SHADER)
        gl.glShaderSource(fs_id, self._FRAG_SHADER_SRC)
        gl.glCompileShader(fs_id)
        if not gl.glGetShaderiv(fs_id, gl.GL_COMPILE_STATUS):
            raise Exception('failed to compile %s fragment shader:\n%s' %
                            (self.__class__.__name__, gl.glGetShaderInfoLog(fs_id).decode()))
        self._program_id = gl.glCreateProgram()
        gl.glAttachShader(self._program_id, vs_id)
        gl.glAttachShader(self._program_id, fs_id)
        gl.glLinkProgram(self._program_id)
        gl.glDetachShader(self._program_id, vs_id)
        gl.glDetachShader(self._program_id, fs_id)
        if not gl.glGetProgramiv(self._program_id, gl.GL_LINK_STATUS):
            raise Exception('failed to link program for %s' % self.__class__.__name__)
        self._attribute_locations = {attribute: gl.glGetAttribLocation(self._program_id, attribute)
                                     for attribute in self._ATTRIBUTES}
        self._uniform_locations = {uniform: gl.glGetUniformLocation(self._program_id, uniform)
                                   for uniform in self._UNIFORMS}
        width, height = self._width, self._height
        self._image_width, self._image_height = image_width, image_height = width * 16, height * 6
        bitmap_buffer = np.zeros((image_height, image_width), dtype=np.ubyte)
        self._char_to_texcoords = {}
        for j in range(6):
            for i in range(16):
                i_char = j * 16 + i
                char = chr(32 + i_char)
                self._char_to_texcoords[char] = (i / 16.0, j / 6.0)
                self._face.load_char(char, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT)
                glyph = self._face.glyph
                bitmap = glyph.bitmap
                x = i*width + glyph.bitmap_left
                y = j*height + self._max_asc - glyph.bitmap_top
                bitmap_buffer[y:y+bitmap.rows,x:x+bitmap.width].flat = bitmap.buffer
        self._texture_id = gl.glGenTextures(1)
        gl.glBindTexture(gl.GL_TEXTURE_2D, self._texture_id)
        self._sampler_id = gl.glGenSamplers(1)
        gl.glSamplerParameteri(self._sampler_id, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)
        gl.glSamplerParameteri(self._sampler_id, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
        gl.glSamplerParameteri(self._sampler_id, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE)
        gl.glSamplerParameteri(self._sampler_id, gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE)
        gl.glBindSampler(self._texture_unit, self._sampler_id)
        gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1)
        gl.glTexImage2D(gl.GL_TEXTURE_2D, 0,
                        gl.GL_RED,
                        image_width, image_height, 0,
                        gl.GL_RED, gl.GL_UNSIGNED_BYTE,
                        bitmap_buffer)
        gl.glGenerateMipmap(gl.GL_TEXTURE_2D)
        gl.glBindTexture(gl.GL_TEXTURE_2D, 0)
        if gl.glGetError() != gl.GL_NO_ERROR:
            raise Exception('failed to create font texture')
        self._gl_initialized = True
        _logger.debug('%s.init_gl: OK', self.__class__.__name__)

    def draw_text(self, text,
                  color=(1.0, 1.0, 0.0, 0.0),
                  screen_position=(0.0, 0.0)):
        import OpenGL.GL as gl
        gl.glUseProgram(self._program_id)
        gl.glActiveTexture(gl.GL_TEXTURE0+self._texture_unit)
        gl.glBindTexture(gl.GL_TEXTURE_2D, self._texture_id)
        #gl.glBindSampler(tex_unit, self._sampler_id)
        gl.glUniform1i(self._uniform_locations['u_fonttex'], self._texture_unit)
        gl.glUniform4f(self._uniform_locations['u_color'], *color)
        gl.glUniform2f(self._uniform_locations['u_screen_size'], *self._screen_size)
        gl.glUniform2f(self._uniform_locations['u_char_size'], self._width, self._height)
        gl.glUniform2f(self._uniform_locations['u_screen_position'], *screen_position)
        gl.glUniform2f(self._uniform_locations['u_fonttex_size'], self._image_width, self._image_height)
        nchars = len(text)
        gl.glUniform1ui(self._uniform_locations['u_nchars'], nchars)
        self._texcoords[:nchars] = [self._char_to_texcoords[c] for c in text]
        gl.glUniform2fv(self._uniform_locations['u_texcoords'], nchars, self._texcoords)
        gl.glEnable(gl.GL_BLEND)
        gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
        gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
        gl.glDisable(gl.GL_BLEND)

    def save_image(self, filename=None):
        from PIL import Image
        if filename is None:
            filename = os.path.split_ext(os.path.basename(self._font_file))[0] + '.png'
        _logger.debug('filename = %s', filename)
        width, height = self._width, self._height
        image_width, image_height = width * 16, height * 6
        bitmap_buffer = np.zeros((image_height, image_width), dtype=np.ubyte)
        for j in range(6):
            for i in range(16):
                i_char = j * 16 + i
                char = chr(32 + i_char)
                self._face.load_char(char, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT)
                glyph = self._face.glyph
                bitmap = glyph.bitmap
                x = i*width + glyph.bitmap_left
                y = j*height + self._max_asc - glyph.bitmap_top
                bitmap_buffer[y:y+bitmap.rows,x:x+bitmap.width].flat = bitmap.buffer
        image = Image.new('L', (bitmap_buffer.shape[1], bitmap_buffer.shape[0]))
        image.putdata(list(bitmap_buffer.ravel()))
        image.save(filename)
        _logger.info('...saved to %s', filename)

    @classmethod
    def _read_shader_src(cls):
        if cls._VERT_SHADER_SRC is None:
            with open(cls._VERT_SHADER_SRC_PATH) as f:
                cls._VERT_SHADER_SRC = f.read()
            attr_matches = _ATTRIBUTE_DECL_RE.finditer(cls._VERT_SHADER_SRC)
            attributes = []
            for m in attr_matches:
                attributes.append(m['attribute_name'])
            cls._ATTRIBUTES = attributes
        if cls._FRAG_SHADER_SRC is None:
            with open(cls._FRAG_SHADER_SRC_PATH) as f:
                cls._FRAG_SHADER_SRC = f.read()
            unif_matches = _UNIFORM_DECL_RE.finditer(cls._FRAG_SHADER_SRC)
            uniforms = []
            for m in unif_matches:
                uniforms.append(m['uniform_name'])
            cls._UNIFORMS = uniforms
Exemplo n.º 16
0
    def char(self, ch):
        def tuple_to_imag(t):
            return t[0] + t[1] * 1j

        #face = Face('/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf')
        face = Face(ddd.DATA_DIR + '/fonts/OpenSansEmoji.ttf')
        face.set_char_size(self.char_size)
        face.load_char(ch)

        #kerning = face.get_kerning(ch, 'x')  # or from previous, actually?
        #print(kerning)

        outline = face.glyph.outline
        y = [t[1] for t in outline.points]
        # flip the points
        outline_points = [(p[0], max(y) - p[1]) for p in outline.points]

        start, end = 0, 0
        paths = []

        for i in range(len(outline.contours)):
            end = outline.contours[i]
            points = outline_points[start:end + 1]
            points.append(points[0])
            tags = outline.tags[start:end + 1]
            tags.append(tags[0])

            segments = [
                [
                    points[0],
                ],
            ]
            for j in range(1, len(points)):
                segments[-1].append(points[j])
                if tags[j] and j < (len(points) - 1):
                    segments.append([
                        points[j],
                    ])

            for segment in segments:
                if len(segment) == 2:
                    paths.append(
                        Line(start=tuple_to_imag(segment[0]),
                             end=tuple_to_imag(segment[1])))
                elif len(segment) == 3:
                    paths.append(
                        QuadraticBezier(start=tuple_to_imag(segment[0]),
                                        control=tuple_to_imag(segment[1]),
                                        end=tuple_to_imag(segment[2])))
                elif len(segment) == 4:
                    paths.append(
                        CubicBezier(start=tuple_to_imag(segment[0]),
                                    control1=tuple_to_imag(segment[1]),
                                    control2=tuple_to_imag(segment[2]),
                                    end=tuple_to_imag(segment[3])))
                    #C = ((segment[1][0] + segment[2][0]) / 2.0,
                    #     (segment[1][1] + segment[2][1]) / 2.0)
                    #paths.append(QuadraticBezier(start=tuple_to_imag(segment[0]),
                    #                             control=tuple_to_imag(segment[1]),
                    #                             end=tuple_to_imag(C)))
                    #paths.append(QuadraticBezier(start=tuple_to_imag(C),
                    #                             control=tuple_to_imag(segment[2]),
                    #                             end=tuple_to_imag(segment[3])))

            start = end + 1

        path = Path(*paths)
        #wsvg(path, filename="/tmp/test.svg")
        path_d = path.d()

        # https://gis.stackexchange.com/questions/301605/how-to-create-shape-in-shapely-from-an-svg-path-element
        # This page also has info about SVG reading!

        #svgpath = 'M10 10 C 20 20, 40 20, 50 10Z'
        mpl_path = parse_path(path_d)

        coords = mpl_path.to_polygons(closed_only=True)

        item = None
        for c in coords:  # coords[1:]:
            if len(c) < 3: continue
            ng = ddd.polygon(c)  #.clean(eps=char_size / 100)  #.convex_hull()
            #ng.show()
            if item is None:
                item = ng
            elif item.contains(ng):
                item = item.subtract(ng)
            else:
                item = item.union(ng)
            item = item.clean(
                eps=self.char_size /
                200)  # Note that this is effectively limiting resolution

        #result = ddd.group([ddd.polygon(c) for c in coords], empty=2)
        result = item
        result = result.scale([1.0 / self.char_size, -1.0 / self.char_size])
        result = result.simplify(
            0.005)  # Note that this is effectively limiting resolution

        return (result, face)