Example #1
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)
Example #2
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])
Example #3
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()
Example #4
0
def fn_SortGlyphs(o_FreetypeFace: freetype.Face,
                  s_Chars: str,
                  b_Invert: bool = False) -> list:
    """
    :
    :  Sorts glyphs based on pixel density.
    :
    :
    :  Args:
    :      freetype.Face o_FreetypeFace : Font face used to calculate pixel densities
    :      str           s_Chars        : Character set over which pixel densities will be taken
    :      bool          b_Invert       : Whether to invert pixel density mapping order (default False)
    :
    :  Returns
    :
    :
    """
    l_Output = []

    # Profile pixel density over character set
    # ...
    for c in s_Chars:
        o_FreetypeFace.load_char(c)
        b_Buffer = o_FreetypeFace.glyph.bitmap.buffer
        l_Output.append((sum(b_Buffer), c))

    # Sort output list by pixel density
    # ...
    l_Output = [y[1] for y in sorted(l_Output, key=lambda x: x[0])]

    # Optionally invert order
    # ...
    if b_Invert:
        l_Output = l_Output[::-1]

    # Return it
    # ...
    return l_Output
    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)
Example #6
0
def _load_font(face, bold, italic):
    from freetype import Face, FT_FACE_FLAG_SCALABLE
    key = '%s-%s-%s' % (face, bold, italic)
    if key in _font_dict:
        return _font_dict[key]
    if face in _vispy_fonts:
        fname = _get_vispy_font_filename(face, bold, italic)
    else:
        fname = find_font(face, bold, italic)
    font = Face(fname)
    if (FT_FACE_FLAG_SCALABLE & font.face_flags) == 0:
        raise RuntimeError('Font %s is not scalable, so cannot be loaded'
                           % face)
    _font_dict[key] = font
    return font
Example #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])
Example #8
0
def open_cjk_font(font_name: str) -> fontforge.font:
    """Opening CJK Font

    This code is borrowed/inspired from
    https://github.com/HinTak/freetype-py/blob/fontval-diag/examples/cjk-multi-fix.py .
    It is licensed undet the MIT License.
    The most of part is different from the original code.
    """
    # Create the reverse lookup table and list of charcodes
    face = Face(font_name)
    face.set_charmap(face.charmap)
    reverse_lookup = {}  # key: glyph_index, value: charcode
    charcodes = []
    charcode, glyph_index = face.get_first_char()
    while (glyph_index):
        charcodes.append(charcode)
        reverse_lookup[glyph_index] = [
            *reverse_lookup.get(glyph_index, []), charcode
        ]
        charcode, glyph_index = face.get_next_char(charcode, glyph_index)
    del face
    # Remove duplicated glyphs
    font = fontforge.open(font_name)
    # CJK fonts usually contain subfonts
    # It is difficult to organize
    if (font.cidfontname != ""):
        font.cidFlatten()
    # As some glyphs conflists other glyphs in UTF-8,
    # we encode it to UTF-8, a.k.a. UCS4.
    font.reencode("ucs4")
    # Put same glyph to charchdes,
    # which has same glyph_ndex.
    for charcodes in reverse_lookup.values():
        main_charcode = charcodes[-1]
        if (not font[main_charcode].isWorthOutputting()):
            print("Source is empty.")
            continue
        for charcode in charcodes[:-1]:
            try:
                font[charcode]
            except TypeError:
                font.selection.select(main_charcode)
                font.copy()
                font.selection.select(charcode)
                font.paste()
            else:
                print("Destination is full.")
    # Remove glyphs over 0xffff
    excess = sum(1 for _ in font.glyphs()) - 0xffff
    font.selection.select(*charcodes[-excess:])
    font.clear()
    # This font must contains glyphs,
    # only which charcode is less than 0xffff
    return font
Example #9
0
def make_table(font_filename, output):
    import sys
    from datetime import datetime
    from freetype import Face
    from pprint import pformat

    font_face = Face(font_filename)
    date = datetime.now().strftime('%Y-%m-%d')
    cmd = ' '.join(sys.argv)
    charwidths = extract_widths(font_face)

    print('# vim:fileencoding=utf8', file=output)
    print(file=output)

    print('# GENERATED FILE - DO NOT EDIT.', file=output)
    print('# Generated by {0}'.format(cmd), file=output)
    print('# Date: {0}'.format(date), file=output)
    print('# Font family: {0}'.format(font_face.family_name), file=output)
    print('# Font style: {0}'.format(font_face.style_name), file=output)
    print('# Font version: {0}'.format(get_font_version(font_face)), file=output)
    print(file=output)
    print('charwidths = {0}'.format(pformat(charwidths).encode('UTF-8')), file=output)
def main():
    alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+={[}]|\\:;\"'<,>.?/"

    font_paths = list(pathlib.Path(".").glob("*.ttf"))
    if font_paths:
        face = Face(str(font_paths[0].resolve(strict=True)))
    else:
        raise FileNotFoundError(
            "Can't find font file.\nPlease download .ttf font file into project root"
        )

    # Compute all kerning vectors for different characters next to each other
    vectors = starmap(face.get_kerning, combinations(alphabet, 2))

    # Extract all the x and y values into a single, flat list
    flat_vectors = list(
        chain.from_iterable([(vec.x, vec.y) for vec in vectors]))

    # Find and print the max and min
    print(
        f"The maximum value found was: {max(flat_vectors)}\nThe minimum value found was: {min(flat_vectors)}"
    )
Example #11
0
 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
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], ])
Example #13
0
	def setup(self):
		'''
		Construct the texture atlas for the font
		'''
		face = Face(self.filename)
		face.set_pixel_sizes(0, self.size)
	
		rowh, roww = 0,0
		
		# Determine image size
		for i in xrange(32,128):
			face.load_char( chr(i), FT_LOAD_RENDER)
			bitmap	= face.glyph.bitmap
			
			if roww + bitmap.width + 1 >= 1024: # max texture width
				self.w  = max(self.w, roww)
				self.h	+= rowh
				roww = 0
				rowh = 0
			roww += bitmap.width + 1
			rowh = max(rowh, bitmap.rows)
			
		self.w = max(self.w, roww)
		self.h += rowh
	
	
		## Create texture to hold ASCII glyphs
		
		# Ensure no texture is currently selected
		glActiveTexture(GL_TEXTURE0) 
		self.texid = glGenTextures(1)
		glBindTexture(GL_TEXTURE_2D, self.texid)
		
		glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, self.w, self.h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0)
		
		# We require 1 byte alignment when uploading texture data
		glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
		
		# Clamping to edges is important to prevent artifacts when scaling
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
		
		# Linear filtering looks better for text
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
		
	
		# Add glyphs to texture
		ox = 0
		oy = 0
		rowh = 0
		
		# class to hold data
		class CharInfo:
			pass
		
		for i in xrange(32,128):
			face.load_char( chr(i), FT_LOAD_RENDER)
			g = face.glyph
			bitmap = g.bitmap
			
			if ox + bitmap.width + 1 >= 1024: # max texture width
				oy += rowh
				rowh = 0
				ox = 0		
			
			glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, bitmap.width, bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.buffer)
			
			ci = CharInfo()
			ci.ax = float(g.advance.x >> 6)
			ci.ay = float(g.advance.y >> 6)
			
			ci.bw = float(bitmap.width)
			ci.bh = float(bitmap.rows)
			
			ci.bl = float(g.bitmap_left)
			ci.bt = float(g.bitmap_top)
			
			ci.tx = float(ox) / float(self.w)
			ci.ty = float(oy) / float(self.h)
			self.c[chr(i)] = ci
			
			rowh = max(rowh, bitmap.rows)
			ox += bitmap.width + 1
Example #14
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
Example #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
Example #16
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()
Example #17
0
                        "--color",
                        help="Color of font image",
                        default="#ffffff")
    parser.add_argument("--font_top_margin",
                        help="Top margin for font",
                        default=0,
                        type=int)
    parser.add_argument("--font_rel_size",
                        help="Font relative size factor",
                        default=0,
                        type=int)
    args = parser.parse_args()

    output = path_join(args.output_dir, args.name)
    with open(args.fif_json, "r", encoding="utf-8") as json_f:
        obj = json.load(json_f, object_pairs_hook=OrderedDict)
        fif = FifFile.import_obj(obj)
    font_size = px_to_pt(fif.character_height) - 2 + args.font_rel_size
    font = ImageFont.truetype(args.font, size=font_size)
    font_face = Face(args.font)
    alternative_font = ImageFont.truetype(args.alternative_font,
                                          size=font_size)
    characters = get_characters(args.text_json, args.index)

    update_fif(fif, characters)
    new_entries = generate_entries(fif, characters, font, font_face,
                                   alternative_font, font_size, args.color,
                                   args.font_top_margin, output)
    fif.entries = new_entries
    fif.save(output + ".fif")
Example #18
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
Example #19
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
Example #20
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
Example #21
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)