Пример #1
0
def main(args):
	filename, glyphs = args[0], args[1:]
	if not glyphs:
		glyphs = ['e', 'o', 'I', 'slash', 'E', 'zero', 'eight', 'minus', 'equal']
	from fontTools.ttLib import TTFont
	font = TTFont(filename)
	glyphset = font.getGlyphSet()
	test(font.getGlyphSet(), font['head'].unitsPerEm, glyphs)
Пример #2
0
def main(args):
    filename, glyphs = args[0], args[1:]
    if not glyphs:
        glyphs = [
            'e', 'o', 'I', 'slash', 'E', 'zero', 'eight', 'minus', 'equal'
        ]
    from fontTools.ttLib import TTFont
    font = TTFont(filename)
    glyphset = font.getGlyphSet()
    test(font.getGlyphSet(), font['head'].unitsPerEm, glyphs)
Пример #3
0
class FontSize(object):
    def __init__(self, filepath):
        self.ttfont = TTFont(filepath)
        self.filepath = filepath
        self.total_glyphs = 0
        self.encoded_glyphs = 0
        self.total_bytes = 0
        self.per_enc_glyph_bytes = 0.0
        self.per_total_glyph_bytes = 0.0
        self.tables = {}
        self._calculate_sizes()

    def _calculate_sizes(self):
        self.total_bytes = os.path.getsize(self.filepath)
        self._calc_per_encoded_glyph_size()
        self._calc_per_total_glyph_size()
        self._calc_table_sizes()

    def _calc_per_encoded_glyph_size(self):
        self.encoded_glyphs = len(self.ttfont.getBestCmap())
        self.per_enc_glyph_bytes = self.total_bytes / self.encoded_glyphs

    def _calc_per_total_glyph_size(self):
        self.total_glyphs = len(self.ttfont.getGlyphSet())
        self.per_total_glyph_bytes = self.total_bytes / self.total_glyphs

    def _calc_table_sizes(self):
        tables = self.get_table_tags()
        for table in tables:
            self.tables[table] = self.ttfont.reader.tables[table].length

    def get_table_tags(self):
        return [tag for tag in self.ttfont.keys() if tag != "GlyphOrder"]
    def initAvailableChars(self):
        self.chars = []
        key = str(self.path.name)
        filename = 'cache/fonts/' + key + '.json'

        try:
            with open(filename, 'r') as f:
                cache = json.load(f)
        except:
            cache = dict()

        if 'ranges' in cache and str(TextGenerator.ranges) == cache['ranges']:
            self.chars = cache['chars']
        else:
            font = TTFont(str(self.path))
            glyphset = font.getGlyphSet()
            table = font.getBestCmap()

            for r in TextGenerator.ranges:
                #ugly check to know if char is supported by font
                self.chars += [
                    chr(x) for x in range(r[0], r[1] + 1)
                    if x in table.keys() and
                    (glyphset[table[x]]._glyph.bytecode != b' \x1d' if hasattr(
                        glyphset[table[x]]._glyph, 'bytecode'
                    ) else glyphset[table[x]]._glyph.numberOfContours > 0)
                ]

            cache = {'ranges': str(TextGenerator.ranges), 'chars': self.chars}

            with open(filename, 'w') as f:
                json.dump(cache, f)

        Fonts.total += len(self.chars)
Пример #5
0
class DetectIntersections(BaseDetectIntersections):
    def __init__(self, in_font):
        self.in_font = in_font
        self.font = TTFont(self.in_font)
        self._is_cjk = self.is_cjk()
        basename, ext = os.path.splitext(os.path.basename(in_font))
        self.degree = 2 if ext.lower() == ".ttf" else 3

    def run(self):
        for gname in self.font.getGlyphOrder():
            intersections = self.detect(gname)
            if intersections:
                print "{}:".format(gname), ", ".join(["({}, {})".format(pt[0], pt[1]) for pt in intersections])

    def detect(self, name):
        glyph = self.font.getGlyphSet()[name]
        pen = ConvPen(self.degree)
        glyph.draw(pen)
        return self.detect_intersections(pen.contours)

    def gid2name(self, gid):
        return self.font.getGlyphOrder()[gid]

    def is_cjk(self):
        if "CFF " not in self.font:
            return False
        return hasattr(self.font["CFF "].cff.topDictIndex[0], "ROS")
Пример #6
0
def main():
    parser = argparse.ArgumentParser(description='something something webfonts')
    parser.add_argument('files', metavar='FILE', nargs='*')
    args = parser.parse_args()
    for file in args.files:

        font = TTFont(file)



#for glyphName in font.getGlyphOrder():
#    glyphTable = font["glyf"]
#    glyph = glyphTable.glyphs.get(glyphName)
#    glyph.expand(glyphTable)
 
        glyphs = font.getGlyphSet()
        for key in glyphs.keys():
            value = glyphs[key]
            print("glyp width: " + key + ", " + str(value.width))
            # glyph.recalcBounds(glyphTable)

        hmtxTable = font['hmtx']
        correct_advance = (hmtxTable.metrics['A'][0], 0)
        for key, value in hmtxTable.metrics.items():
            print("advance: " + key + ", " + str(value))
            hmtxTable.metrics[key] = correct_advance

        name = os.path.splitext(file)[0]
        font.flavor = "woff"
        font.save(name + ".woff")

        font.flavor = "woff2"
        font.save(name + ".woff2")
Пример #7
0
def main():
    start_json = json.load(sys.stdin)

    for font in start_json:
        fontInfo = TTFont("../../fonts/KaTeX_" + font + ".ttf")
        glyf = fontInfo["glyf"]
        widths = fontInfo.getGlyphSet()
        unitsPerEm = float(fontInfo["head"].unitsPerEm)

        # We keep ALL Unicode cmaps, not just fontInfo["cmap"].getcmap(3, 1).
        # This is playing it extra safe, since it reports inconsistencies.
        # Platform 0 is Unicode, platform 3 is Windows. For platform 3,
        # encoding 1 is UCS-2 and encoding 10 is UCS-4.
        cmap = [
            t.cmap for t in fontInfo["cmap"].tables if (t.platformID == 0) or (
                t.platformID == 3 and t.platEncID in (1, 10))
        ]

        chars = metrics_to_extract.get(font, {})
        chars[u"\u0020"] = None  # space
        chars[u"\u00a0"] = None  # nbsp

        for char, base_char in chars.items():
            code = ord(char)
            names = set(t.get(code) for t in cmap)
            if not names:
                sys.stderr.write(
                    "Codepoint {} of font {} maps to no name\n".format(
                        code, font))
                continue
            if len(names) != 1:
                sys.stderr.write(
                    "Codepoint {} of font {} maps to multiple names: {}\n".
                    format(code, font, ", ".join(sorted(names))))
                continue
            name = names.pop()

            height = depth = italic = skew = width = 0
            glyph = glyf[name]
            if glyph.numberOfContours:
                height = glyph.yMax / unitsPerEm
                depth = -glyph.yMin / unitsPerEm
            width = widths[name].width / unitsPerEm
            if base_char:
                base_char_str = str(ord(base_char))
                base_metrics = start_json[font][base_char_str]
                italic = base_metrics["italic"]
                skew = base_metrics["skew"]
                width = base_metrics["width"]

            start_json[font][str(code)] = {
                "height": height,
                "depth": depth,
                "italic": italic,
                "skew": skew,
                "width": width
            }

    sys.stdout.write(
        json.dumps(start_json, separators=(',', ':'), sort_keys=True))
Пример #8
0
def test_check_whitespace_ink():
  """ Whitespace glyphs have ink? """
  from fontbakery.profiles.universal import com_google_fonts_check_whitespace_ink as check

  test_font = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf"))
  status, _ = list(check(test_font))[-1]
  assert status == PASS

  print ("Test for whitespace character having composites (with ink).")
  test_font["cmap"].tables[0].cmap[0x0020] = "uni1E17"
  status, _ = list(check(test_font))[-1]
  assert status == FAIL

  print ("Test for whitespace character having outlines (with ink).")
  test_font["cmap"].tables[0].cmap[0x0020] = "scedilla"
  status, _ = list(check(test_font))[-1]
  assert status == FAIL

  print ("Test for whitespace character having composites (without ink).")
  import fontTools.pens.ttGlyphPen
  pen = fontTools.pens.ttGlyphPen.TTGlyphPen(test_font.getGlyphSet())
  pen.addComponent("space", (1, 0, 0, 1, 0, 0))
  test_font["glyf"].glyphs["uni200B"] = pen.glyph()
  status, _ = list(check(test_font))[-1]
  assert status == FAIL
Пример #9
0
def fontToUFO(src, dst, fileType=None):
	from robofab.ufoLib import UFOWriter
	from robofab.pens.adapterPens import SegmentToPointPen
	if fileType is None:
		fileType = guessFileType(src)
		if fileType is None:
			raise ValueError, "Can't determine input file type"
	ufoWriter = UFOWriter(dst)
	if fileType == "TTF":
		from fontTools.ttLib import TTFont
		font = TTFont(src, 0)
	elif fileType == "Type 1":
		from fontTools.t1Lib import T1Font
		font = T1Font(src)
	else:
		assert 0, "unknown file type: %r" % fileType
	inGlyphSet = font.getGlyphSet()
	outGlyphSet = ufoWriter.getGlyphSet()
	for glyphName in inGlyphSet.keys():
		print "-", glyphName
		glyph = inGlyphSet[glyphName]
		def drawPoints(pen):
			pen = SegmentToPointPen(pen)
			glyph.draw(pen)
		outGlyphSet.writeGlyph(glyphName, glyph, drawPoints)
	outGlyphSet.writeContents()
	if fileType == "TTF":
		info = extractTTFFontInfo(font)
	elif fileType == "Type 1":
		info = extractT1FontInfo(font)
	ufoWriter.writeInfo(info)
Пример #10
0
def generate_pic(glyphname, font: TTFont, size=120, scale=0.1):
    """
    生成图片 -> 预处理 -> 保存到本地
    Args:
        glyphname:
        font:
        file_suffix:
        size:
        scale:

    Returns:

    """

    gs = font.getGlyphSet()
    pen = ReportLabPen(gs, Path(fillColor=colors.black, strokeWidth=1))
    g = gs[glyphname]
    g.draw(pen)

    w, h = size, size

    # Everything is wrapped in a group to allow transformations.
    g = Group(pen.path)
    # g.translate(10, 20)
    g.scale(scale, scale)

    d = Drawing(w, h)
    d.add(g)

    return renderPM.drawToPIL(d)
Пример #11
0
def img_save():
    img_path = PATH.rsplit('.', 1)[0]
    font = TTFont(
        PATH)  # it would work just as well with fontTools.t1Lib.T1Font
    glyf = font['glyf']
    if not os.path.exists(img_path):
        os.makedirs(img_path)
    for glyphName in glyf.keys():
        gs = font.getGlyphSet()
        pen = ReportLabPen(gs, Path(fillColor=colors.black, strokeWidth=1))
        g = gs[glyphName]
        g.draw(pen)
        # 调整图片大小
        w, h = 800, 800
        g = Group(pen.path)
        g.translate(10, 200)
        g.scale(0.3, 0.3)

        d = Drawing(w, h)
        d.add(g)

        image = renderPM.drawToPIL(d)
        little_image = image.resize((180, 180))
        fromImage = Image.new('RGBA', (360, 360), color=(255, 255, 255))
        fromImage.paste(little_image, (0, 0))
        # fromImage.show()
        glyphName = glyphName.replace('uni', '&#x')
        imageFile = img_path + "/%s.png" % glyphName
        fromImage.save(imageFile)
        break
Пример #12
0
def test_check_whitespace_ink():
    """ Whitespace glyphs have ink? """
    from fontbakery.profiles.universal import com_google_fonts_check_whitespace_ink as check

    test_font = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf"))
    status, _ = list(check(test_font))[-1]
    assert status == PASS

    print("Test for whitespace character having composites (with ink).")
    test_font["cmap"].tables[0].cmap[0x0020] = "uni1E17"
    status, _ = list(check(test_font))[-1]
    assert status == FAIL

    print("Test for whitespace character having outlines (with ink).")
    test_font["cmap"].tables[0].cmap[0x0020] = "scedilla"
    status, _ = list(check(test_font))[-1]
    assert status == FAIL

    print("Test for whitespace character having composites (without ink).")
    import fontTools.pens.ttGlyphPen
    pen = fontTools.pens.ttGlyphPen.TTGlyphPen(test_font.getGlyphSet())
    pen.addComponent("space", (1, 0, 0, 1, 0, 0))
    test_font["glyf"].glyphs["uni200B"] = pen.glyph()
    status, _ = list(check(test_font))[-1]
    assert status == FAIL
Пример #13
0
def ttCount(input, options):
    ttf = TTFont(input, fontNumber=options.fontNumber, lazy=True)
    reader = ttf.reader
    hasPrep = 'prep' in reader.tables
    hasFpgm = 'fpgm' in reader.tables
    glyf_program_counts = 0
    glyf_names = ttf.getGlyphSet().keys()
    for glyf_name in glyf_names:
        glyf = ttf['glyf'].glyphs[glyf_name]
        glyf.expand(ttf['glyf'])
        if hasattr(ttf['glyf'].glyphs[glyf_name], "program"):
            prog = ttf['glyf'].glyphs[glyf_name].program
            if (hasattr(prog, "bytecode") and len(prog.bytecode) > 0) or (hasattr(prog, "assembly") and len(prog.assembly) > 0):
                glyf_program_counts += 1
        #print ("%s %s" % (glyf, hasattr(ttf['glyf'].glyphs[glyf_name], "program")))
    hasSomeGlyfCode = glyf_program_counts > 0
    globalAnswer = hasPrep or hasFpgm or hasSomeGlyfCode
    print ("%s: %s, prep = %s, fpgm = %s, glyf = %s [%d/%d]" %
           (input,
            yes_or_no(globalAnswer),
            yes_or_no(hasPrep),
            yes_or_no(hasFpgm),
            yes_or_no(hasSomeGlyfCode),
            glyf_program_counts,
            len(glyf_names)))
    ttf.close()
Пример #14
0
def test_check_049():
    """ Whitespace glyphs have ink? """
    from fontbakery.specifications.general import com_google_fonts_check_049 as check

    test_font = TTFont(
        os.path.join("data", "test", "nunito", "Nunito-Regular.ttf"))
    status, _ = list(check(test_font))[-1]
    assert status == PASS

    print("Test for whitespace character having composites (with ink).")
    test_font["cmap"].tables[0].cmap[0x0020] = "uni1E17"
    status, _ = list(check(test_font))[-1]
    assert status == FAIL

    print("Test for whitespace character having outlines (with ink).")
    test_font["cmap"].tables[0].cmap[0x0020] = "scedilla"
    status, _ = list(check(test_font))[-1]
    assert status == FAIL

    print("Test for whitespace character having composites (without ink).")
    import fontTools.pens.ttGlyphPen
    pen = fontTools.pens.ttGlyphPen.TTGlyphPen(test_font.getGlyphSet())
    pen.addComponent("space", (1, 0, 0, 1, 0, 0))
    test_font["glyf"].glyphs["uni200B"] = pen.glyph()
    status, _ = list(check(test_font))[-1]
    assert status == FAIL
Пример #15
0
 def _init_glyphs_mapping(self):
     font = TTFont(io.BytesIO(requests.get(SAMPLE_FONT_URL).content))
     glyph_set = font.getGlyphSet()
     glyphs = glyph_set._glyphs.glyphs
     self.glyphs_mapping = {}
     for uni, number in SAMPLE_FONT_MAPPING.items():
         self.glyphs_mapping[glyphs[uni].data] = number
Пример #16
0
def test_check_whitespace_ink():
    """ Whitespace glyphs have ink? """
    check = CheckTester(universal_profile,
                        "com.google.fonts/check/whitespace_ink")

    test_font = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf"))
    assert_PASS(check(test_font))

    test_font["cmap"].tables[0].cmap[0x0020] = "uni1E17"
    assert_results_contain(
        check(test_font), FAIL, 'has-ink',
        'for whitespace character having composites (with ink).')

    test_font["cmap"].tables[0].cmap[0x0020] = "scedilla"
    assert_results_contain(
        check(test_font), FAIL, 'has-ink',
        'for whitespace character having outlines (with ink).')

    import fontTools.pens.ttGlyphPen
    pen = fontTools.pens.ttGlyphPen.TTGlyphPen(test_font.getGlyphSet())
    pen.addComponent("space", (1, 0, 0, 1, 0, 0))
    test_font["glyf"].glyphs["uni200B"] = pen.glyph()
    assert_results_contain(
        check(test_font),
        FAIL,
        'has-ink',  # should we give is a separate keyword? This looks wrong.
        'for whitespace character having composites (without ink).')
Пример #17
0
    def run(self):
        font = TTFont(self.in_font)
        gs = font.getGlyphSet()
        glyf = font["glyf"]
        hmtx = font["hmtx"]
        for gname, (adw, lsb) in hmtx.metrics.items():
            # https://github.com/fonttools/fonttools/blob/master/Lib/fontTools/ttLib/tables/_g_l_y_f.py#L189-L192
            # __getitem__ internally calls table__g_l_y_f.expand which is essential to obtain xMin
            g = glyf[gname]
            # obtain recalculated xMin from glyph's control bounds
            g.recalcBounds(glyf)
            hmtx.metrics[gname] = (adw, g.xMin)
        if self.update_vmtx:
            from fontTools.pens.boundsPen import BoundsPen
            vmtx = font["vmtx"]
            for gname, (adh, tsb) in vmtx.metrics.items():
                g = glyf[gname]
                # obtain yMax
                g.recalcBounds(glyf)
                pen = BoundsPen(gs)
                g.draw(pen, glyf)
                if pen.bounds is None:
                    continue
                left, bottom, right, top = pen.bounds
                vmtx.metrics[gname] = (adh, top + tsb - g.yMax)

        font.save(self.out_font)

        return 0
Пример #18
0
def main(font_path):
    font = TTFont(font_path, recalcBBoxes=False)

    # Force yMin and yMax
    font['head'].yMin = YMIN
    font['head'].yMax = YMAX

    # Enable Bold bits for Black styles
    if "Black" in font_path and "fvar" not in font:
        if "Italic" in font_path:
            font["OS/2"].fsSelection |= 32
        else:
            font["OS/2"].fsSelection ^= 64 | 32
        font["head"].macStyle |= 1

    # turn off round-to-grid flags in certain problem components
    # https://github.com/google/roboto/issues/153
    glyph_set = font.getGlyphSet()
    ellipsis = glyph_set['ellipsis']._glyph
    for component in ellipsis.components:
        component.flags &= ~(1 << 2)

    font_data.delete_from_cmap(
        font,
        [
            0x20E3,  # COMBINING ENCLOSING KEYCAP
            0x2191,  # UPWARDS ARROW
            0x2193,  # DOWNWARDS ARROW
        ])
    font.save(font_path)
Пример #19
0
def fontToUFO(src, dst, fileType=None):
    from robofab.ufoLib import UFOWriter
    from robofab.pens.adapterPens import SegmentToPointPen
    if fileType is None:
        fileType = guessFileType(src)
        if fileType is None:
            raise ValueError, "Can't determine input file type"
    ufoWriter = UFOWriter(dst)
    if fileType == "TTF":
        from fontTools.ttLib import TTFont
        font = TTFont(src, 0)
    elif fileType == "Type 1":
        from fontTools.t1Lib import T1Font
        font = T1Font(src)
    else:
        assert 0, "unknown file type: %r" % fileType
    inGlyphSet = font.getGlyphSet()
    outGlyphSet = ufoWriter.getGlyphSet()
    for glyphName in inGlyphSet.keys():
        print "-", glyphName
        glyph = inGlyphSet[glyphName]

        def drawPoints(pen):
            pen = SegmentToPointPen(pen)
            glyph.draw(pen)

        outGlyphSet.writeGlyph(glyphName, glyph, drawPoints)
    outGlyphSet.writeContents()
    if fileType == "TTF":
        info = extractTTFFontInfo(font)
    elif fileType == "Type 1":
        info = extractT1FontInfo(font)
    ufoWriter.writeInfo(info)
Пример #20
0
def _colr_v1_to_svgs(view_box: Rect, ttfont: ttLib.TTFont) -> Dict[str, SVG]:
    glyph_set = ttfont.getGlyphSet()
    return {
        g.BaseGlyph: SVG.fromstring(
            etree.tostring(
                _colr_v1_glyph_to_svg(ttfont, glyph_set, view_box, g)))
        for g in ttfont["COLR"].table.BaseGlyphList.BaseGlyphPaintRecord
    }
Пример #21
0
def _colr_v0_to_svgs(view_box: Rect, ttfont: ttLib.TTFont) -> Dict[str, SVG]:
    glyph_set = ttfont.getGlyphSet()
    return {
        g: SVG.fromstring(
            etree.tostring(
                _colr_v0_glyph_to_svg(ttfont, glyph_set, view_box, g)))
        for g in ttfont["COLR"].ColorLayers
    }
Пример #22
0
def fetchFontSpec(path):
    font = TTFont(path)
    cmap = font['cmap']
    t = cmap.getcmap(3, 1).cmap
    s = font.getGlyphSet()
    unitsPerEm = font['head'].unitsPerEm

    return {'font': font, 't': t, 's': s, 'upm': unitsPerEm}
Пример #23
0
    def __init__(self, fontName, pointSize):
        self.fontName = fontName
        self.pointSize = pointSize

        fontToolsFontFallback = TTFont(fontFallback)
        cmapFallback = fontToolsFontFallback['cmap']

        fontToolsFont = TTFont(fontName)
        cmap = fontToolsFont['cmap']

        self.t = cmap.getcmap(3, 1).cmap
        self.s = fontToolsFont.getGlyphSet()

        self.tFallback = cmapFallback.getcmap(3, 1).cmap
        self.sFallback = fontToolsFontFallback.getGlyphSet()

        self.units_per_emFallback = fontToolsFontFallback['head'].unitsPerEm
        self.units_per_em = fontToolsFont['head'].unitsPerEm
Пример #24
0
def generate_glyph_image(font):
    for index, label in enumerate(LABELS):
        bitmap_output_path = path.join(BITMAP_DIR,
                                       str(index) + '_' + label,
                                       font['post_script_name'] + ".png")
        if path.exists(bitmap_output_path):
            continue

        # bitmap
        canvas = Image.new("RGB", (IMAGE_WIDTH, IMAGE_HEIGHT), "black")
        draw = ImageDraw.Draw(canvas)
        ifont = ImageFont.truetype(font['path'], IMAGE_WIDTH - 10)
        w, h = draw.textsize(label, font=ifont)
        draw.text(((IMAGE_WIDTH - w) / 2, (IMAGE_HEIGHT - h) / 2),
                  label,
                  font=ifont,
                  fill="white")
        canvas.save(bitmap_output_path)

    # vector
    vector_output_path = path.join(PREPROCESSED_DIR,
                                   font['post_script_name'] + '.ttx')
    if path.exists(vector_output_path):
        return
    ttfont = TTFont(font['path'])
    glyph_set = ttfont.getGlyphSet()
    cmap = ttfont.getBestCmap()
    ttfont.saveXML(vector_output_path)
    ascender = ttfont['OS/2'].sTypoAscender
    descender = ttfont['OS/2'].sTypoDescender
    height = ascender - descender

    for index, label in enumerate(LABELS):
        glyph_name = cmap[ord(label)]
        glyph = glyph_set[glyph_name]
        width = glyph.width
        pen = RecordingPen()
        glyph.draw(pen)

        # [[x, y, isPenDown, isControlPoint, isContourEnd, isGlyphEnd], ...]
        matrix = []
        for command in pen.value:
            name = command[0]
            points = command[1]
            print('name:', name)
            print('points:', points)
            # if name == 'closePath':
            #     pass
            # if name == 'moveTo':
            #     matrix.append((points[0][0], points[0][1], 0, 0, 0, 0))
            # elif name == 'qCurveTo':
            #     matrix.append((points[0][0], points[0][1], 1, 1, 0, 0))
            #     matrix.append((points[1][0], points[1][1], 1, 0, 0, 0))
            # elif name == 'lineTo':
            #     matrix.append((points[1][0], points[1][1], 1, 0, 0, 0))

        os.exit()
Пример #25
0
def main(args):
    if not args:
        return
    filename, glyphs = args[0], args[1:]
    from fontTools.ttLib import TTFont
    font = TTFont(filename)
    if not glyphs:
        glyphs = font.getGlyphOrder()
    _test(font.getGlyphSet(), font['head'].unitsPerEm, glyphs)
Пример #26
0
def get_woff(font_text):
    base64_behind = re.split('\;base64\,', font_text)[1]
    font_content = re.split('\)', base64_behind)[0].strip()
    if font_content:
        bs_font = base64.b64decode(font_content)
        with open("new.woff", 'wb') as f:
            f.write(bs_font)
    font_ttf = TTFont("new.woff")
    data = font_ttf.getGlyphSet()._glyphs.glyphs
    return data
Пример #27
0
 def __init_font_map(self):
     """
     初始化猫眼的字符集模版,只在倒入模块时有构造方法调用一次
     """
     font_file = self.__save_font_file(self.url)
     font = TTFont(font_file)
     glyph_set = font.getGlyphSet()
     glyph_dict = glyph_set._glyphs.glyphs
     for k, v in self.manual_dict.items():
         self.font_dict[glyph_dict[k].data] = v
Пример #28
0
def get_rel():
    maoyan_fonts = TTFont('maoyan.woff')
    font_dict = {}
    base_num = {
        "uniF1D0": "4", "uniE13A": "3", "uniE64F": "0", "uniECF2": "1", "uniF382": "2",
        "uniE1FD": "8", "uniF5E4": "6", "uniF1B0": "9", "uniE71E": "7", "uniE979": "5"}
    _data = maoyan_fonts.getGlyphSet()._glyphs.glyphs
    for k, v in base_num.items():
        font_dict[_data[k].data] = v
    return font_dict
Пример #29
0
def text_path(t, pos, font_path):
    # Like text, but draws paths of the glyphs
    x, y = pos
    f = TTFont(font_path)
    gs = f.getGlyphSet()
    save()
    translate(x, y)
    for l in t:
        glyph = gs[l]
        drawGlyph(glyph)
        translate(glyph.width)
    restore()
Пример #30
0
def text_path(t, pos, font_path):
    # Like text, but draws paths of the glyphs
    x, y = pos
    f = TTFont(font_path)
    gs = f.getGlyphSet()
    save()
    translate(x, y)
    for l in t:
        glyph = gs[l]
        drawGlyph(glyph)
        translate(glyph.width)
    restore()
Пример #31
0
def _colr_v0_glyph_to_svg(ttfont: ttLib.TTFont, view_box: Rect,
                          glyph_name: str) -> etree.Element:

    svg_root = _svg_root(view_box)

    glyph_set = ttfont.getGlyphSet()
    for glyph_layer in ttfont["COLR"].ColorLayers[glyph_name]:
        svg_path = etree.SubElement(svg_root, "path")
        _solid_paint(svg_path, ttfont, glyph_layer.colorID)
        _draw_svg_path(svg_path, view_box, ttfont, glyph_layer.name, glyph_set)

    return svg_root
Пример #32
0
 def get_woff(self,html):
     selector = etree.HTML(html)
     font_text = selector.xpath('//style[@id="js-nuwa"]/text()')[0]
     base64_behind = re.split('\;base64\,', font_text)[1]
     font_content = re.split('\)', base64_behind)[0].strip()
     if font_content:
         bs_font = base64.b64decode(font_content)
         with open("new.woff",'wb') as f:
             f.write(bs_font)
     font_ttf = TTFont("new.woff")
     data = font_ttf.getGlyphSet()._glyphs.glyphs
     return data
Пример #33
0
    def test_glyphset(
        self, location, expected
    ):

        font = TTFont(self.getpath("I.ttf"))
        glyphset = font.getGlyphSet(location=location)
        pen = RecordingPen()
        glyph = glyphset['I']
        glyph.draw(pen)
        actual = pen.value

        assert actual == expected, (location, actual, expected)
Пример #34
0
    def convert_to_num(self, series, url):
        """
        获取unicode的对应的数字
        :param series: int
        :param url: 字符集文件的地址
        :return: int,series对应数字
        """
        font_file = self.__save_font_file(url)
        font = TTFont(font_file)
        cmap = font.getBestCmap()
        num = cmap.get(series)
        glyph_set = font.getGlyphSet()

        return self.font_dict[glyph_set._glyphs.glyphs[num].data]
Пример #35
0
 def run(self):
     font = TTFont(self.in_font)
     gs = font.getGlyphSet()
     for gname in font.getGlyphOrder():
         g = gs[gname]._glyph
         g.decompile()
         print("[{}]".format(gname))
         operands = []
         for b in g.program:
             if isinstance(b, int):
                 operands.append(b)
             else:
                 print("  [{}] << {} >>".format(", ".join(map(lambda v: str(v), operands)), b))
                 operands = []
         print("  -----")
Пример #36
0
def getTextWidth(text, pointSize):
    font = TTFont('‪C:\simsun.ttf')
    cmap = font['cmap']
    t = cmap.getcmap(3, 1).cmap
    s = font.getGlyphSet()
    units_per_em = font['head'].unitsPerEm

    total = 0
    for c in text:
        if ord(c) in t and t[ord(c)] in s:
            total += s[t[ord(c)]].width
        else:
            total += s['.notdef'].width
    total = total * float(pointSize) / units_per_em
    return total
Пример #37
0
class Font(LayoutEngine):

    def __init__(self, path, glyphClass=None):
        super(Font, self).__init__()
        self.path = path
        self._glyphs = {}
        if isinstance(path, TTFont):
            self.source = path
        else:
            self.source = TTFont(path)
        self.loadGlyphSet()
        self.loadCMAP()
        self.loadFeatures()
        self.loadInfo()
        if glyphClass is None:
            glyphClass = Glyph
        self.glyphClass = glyphClass

    def __del__(self):
        del self._glyphs
        self.source.close()
        del self.source

    # --------------
    # initialization
    # --------------

    def loadCMAP(self):
        cmap = extractCMAP(self.source)
        self.setCMAP(cmap)

    def loadGlyphSet(self):
        self.glyphSet = self.source.getGlyphSet()
        # the glyph order will be needed later
        # to assign the proper glyph index to
        # glyph objects.
        order = self.source.getGlyphOrder()
        self._glyphOrder = {}
        for index, glyphName in enumerate(order):
            self._glyphOrder[glyphName] = index

    def loadInfo(self):
        self.info = info = Info()
        head = self.source["head"]
        hhea = self.source["hhea"]
        os2 = self.source["OS/2"]
        info.unitsPerEm = head.unitsPerEm
        info.ascender = hhea.ascent
        info.descender = hhea.descent
        info.xHeight = os2.sxHeight
        info.capHeight = os2.sCapHeight
        # names
        nameIDs = {}
        for nameRecord in self.source["name"].names:
            nameID = nameRecord.nameID
            platformID = nameRecord.platformID
            platEncID = nameRecord.platEncID
            langID = nameRecord.langID
            nameIDs[nameID, platformID, platEncID, langID] = nameRecord.toUnicode()
        # to retrieve the family and style names, first start
        # with the preferred name entries and progress to less
        # specific entries until something is found.
        familyPriority = [(16, 1, 0, 0), (16, 1, None, None), (16, None, None, None),
                        (1, 1, 0, 0), (1, 1, None, None), (1, None, None, None)]
        familyName = self._skimNameIDs(nameIDs, familyPriority)
        stylePriority = [(17, 1, 0, 0), (17, 1, None, None), (17, None, None, None),
                        (2, 1, 0, 0), (2, 1, None, None), (2, None, None, None)]
        styleName = self._skimNameIDs(nameIDs, stylePriority)
        if familyName is None or styleName is None:
            raise CompositorError("Could not extract name data from name table.")
        self.info.familyName = familyName
        self.info.styleName = styleName
        # stylistic set names
        self.stylisticSetNames = {}
        if self.gsub:
            for featureRecord in self.gsub.FeatureList.FeatureRecord:
                params = featureRecord.Feature.FeatureParams
                if hasattr(params, "UINameID"):
                    ssNameID = params.UINameID
                    namePriority = [(ssNameID, 1, 0, 0), (ssNameID, 1, None, None), (ssNameID, 3, 1, 1033), (ssNameID, 3, None, None)]
                    ssName = self._skimNameIDs(nameIDs, namePriority)
                    if ssName:
                        self.stylisticSetNames[featureRecord.FeatureTag] = ssName

    def _skimNameIDs(self, nameIDs, priority):
        for (nameID, platformID, platEncID, langID) in priority:
            for (nID, pID, pEID, lID), text in nameIDs.items():
                if nID != nameID:
                    continue
                if pID != platformID and platformID is not None:
                    continue
                if pEID != platEncID and platEncID is not None:
                    continue
                if lID != langID and langID is not None:
                    continue
                return text

    def loadFeatures(self):
        gdef = None
        if "GDEF" in self.source:
            gdef = self.source["GDEF"]
        gsub = None
        if "GSUB" in self.source:
            gsub = self.source["GSUB"]
        gpos = None
        if "GPOS" in self.source:
            gpos = self.source["GPOS"]
        self.setFeatureTables(gdef, gsub, gpos)

    # -------------
    # dict behavior
    # -------------

    def keys(self):
        return self.glyphSet.keys()

    def __contains__(self, name):
        return name in self.glyphSet

    def __getitem__(self, name):
        if name not in self._glyphs:
            if name not in self.glyphSet:
                name = self.fallbackGlyph
            glyph = self.glyphSet[name]
            index = self._glyphOrder[name]
            glyph = self.glyphClass(name, index, glyph, self)
            self._glyphs[name] = glyph
        return self._glyphs[name]

    # -----------------
    # string processing
    # -----------------

    def stringToGlyphNames(self, string):
        glyphNames = []
        for c in string:
            c = unicode(c)
            v = ord(c)
            if v in self.cmap:
                glyphNames.append(self.cmap[v])
            elif self.fallbackGlyph is not None:
                glyphNames.append(self.fallbackGlyph)
        return glyphNames

    def stringToGlyphRecords(self, string):
        return [GlyphRecord(glyphName) for glyphName in self.stringToGlyphNames(string)]

    def didProcessingGSUB(self, glyphRecords):
        for glyphRecord in glyphRecords:
            glyphRecord.advanceWidth += self[glyphRecord.glyphName].width

    # -------------
    # Miscellaneous
    # -------------

    def getGlyphOrder(self):
        return self.source.getGlyphOrder()
Пример #38
0
class Font(object):

    def __init__(self, path, glyphClass=None):
        self.path = path
        self.fallbackGlyph = ".notdef"
        self._glyphs = {}
        if isinstance(path, TTFont):
            self.source = path
        else:
            self.source = TTFont(path)
        self.loadGlyphSet()
        self.loadInfo()
        self.loadCMAP()
        self.loadFeatures()
        if glyphClass is None:
            glyphClass = Glyph
        self.glyphClass = glyphClass

    def __del__(self):
        del self._glyphs
        self.source.close()
        del self.source

    # --------------
    # initialization
    # --------------

    def loadCMAP(self):
        self.cmap = extractCMAP(self.source)
        self.reversedCMAP = reverseCMAP(self.cmap)

    def loadGlyphSet(self):
        self.glyphSet = self.source.getGlyphSet()
        # the glyph order will be needed later
        # to assign the proper glyph index to
        # glyph objects.
        order = self.source.getGlyphOrder()
        self._glyphOrder = {}
        for index, glyphName in enumerate(order):
            self._glyphOrder[glyphName] = index

    def loadInfo(self):
        self.info = info = Info()
        head = self.source["head"]
        hhea = self.source["hhea"]
        os2 = self.source["OS/2"]
        info.unitsPerEm = head.unitsPerEm
        info.ascender = hhea.ascent
        info.descender = hhea.descent
        info.xHeight = os2.sxHeight
        info.capHeight = os2.sCapHeight
        # names
        nameIDs = {}
        for nameRecord in self.source["name"].names:
            nameID = nameRecord.nameID
            platformID = nameRecord.platformID
            platEncID = nameRecord.platEncID
            langID = nameRecord.langID
            text = nameRecord.string
            nameIDs[nameID, platformID, platEncID, langID] = text
        # to retrive the family and style names, first start
        # with the preferred name entries and progress to less
        # specific entries until something is found.
        familyPriority = [(16, 1, 0, 0), (16, 1, None, None), (16, None, None, None),
                        (1, 1, 0, 0), (1, 1, None, None), (1, None, None, None)]
        familyName = self._skimNameIDs(nameIDs, familyPriority)
        stylePriority = [(17, 1, 0, 0), (17, 1, None, None), (17, None, None, None),
                        (2, 1, 0, 0), (2, 1, None, None), (2, None, None, None)]
        styleName = self._skimNameIDs(nameIDs, stylePriority)
        if familyName is None or styleName is None:
            raise CompositorError("Could not extract name data from name table.")
        self.info.familyName = familyName
        self.info.styleName = styleName

    def _skimNameIDs(self, nameIDs, priority):
        for (nameID, platformID, platEncID, langID) in priority:
            for (nID, pID, pEID, lID), text in nameIDs.items():
                if nID != nameID:
                    continue
                if pID != platformID and platformID is not None:
                    continue
                if pEID != platEncID and platEncID is not None:
                    continue
                if lID != langID and langID is not None:
                    continue
                # make sure there are no endian issues
                # XXX right way to do this?
                text = "".join([i for i in text if i != "\x00"])
                return text

    def loadFeatures(self):
        self.gsub = None
        self.gpos = None
        self.gdef = None
        if self.source.has_key("GDEF"):
            self.gdef = GDEF().loadFromFontTools(self.source["GDEF"])
        if self.source.has_key("GSUB"):
            self.gsub = GSUB().loadFromFontTools(self.source["GSUB"], self.reversedCMAP, self.gdef)
        if self.source.has_key("GPOS"):
            self.gpos = GPOS().loadFromFontTools(self.source["GPOS"], self.reversedCMAP, self.gdef)

    # -------------
    # dict behavior
    # -------------

    def keys(self):
        return self.glyphSet.keys()

    def __contains__(self, name):
        return self.glyphSet.has_key(name)

    def __getitem__(self, name):
        if name not in self._glyphs:
            glyph = self.glyphSet[name]
            index = self._glyphOrder[name]
            glyph = self.glyphClass(name, index, glyph, self)
            self._glyphs[name] = glyph
        return self._glyphs[name]

    # -----------------
    # string processing
    # -----------------

    def stringToGlyphNames(self, string):
        glyphNames = []
        for c in string:
            c = unicode(c)
            v = ord(c)
            if v in self.cmap:
                glyphNames.append(self.cmap[v])
            elif self.fallbackGlyph is not None:
                glyphNames.append(self.fallbackGlyph)
        return glyphNames

    def stringToGlyphRecords(self, string):
        return [GlyphRecord(glyphName) for glyphName in self.stringToGlyphNames(string)]

    def glyphListToGlyphRecords(self, glyphList):
        glyphRecords = []
        for glyphName in glyphList:
            if glyphName not in self:
                if self.fallbackGlyph is None:
                    continue
                glyphName = self.fallbackGlyph
            record = GlyphRecord(glyphName)
            glyphRecords.append(record)
        return glyphRecords

    def process(self, stringOrGlyphList, script="latn", langSys=None, rightToLeft=False, case="unchanged", logger=None):
        if isinstance(stringOrGlyphList, basestring):
            stringOrGlyphList = self.stringToGlyphNames(stringOrGlyphList)
        if case != "unchanged":
            l = langSys
            if l is not None:
                l = l.strip()
            stringOrGlyphList = convertCase(case, stringOrGlyphList, self.cmap, self.reversedCMAP, l, self.fallbackGlyph)
        glyphRecords = self.glyphListToGlyphRecords(stringOrGlyphList)
        if rightToLeft:
            glyphRecords.reverse()
        if logger:
            logger.logStart()
            glyphNames = [r.glyphName for r in glyphRecords]
            logger.logMainSettings(glyphNames, script, langSys)
        if self.gsub is not None:
            if logger:
                logger.logTableStart(self.gsub)
            glyphRecords = self.gsub.process(glyphRecords, script=script, langSys=langSys, logger=logger)
            if logger:
                logger.logResults(glyphRecords)
                logger.logTableEnd()
        advancedRecords = []
        for glyphRecord in glyphRecords:
            glyphRecord.advanceWidth = self[glyphRecord.glyphName].width
            advancedRecords.append(glyphRecord)
        glyphRecords = advancedRecords
        if self.gpos is not None:
            if logger:
                logger.logTableStart(self.gpos)
            glyphRecords = self.gpos.process(glyphRecords, script=script, langSys=langSys, logger=logger)
            if logger:
                logger.logResults(glyphRecords)
                logger.logTableEnd()
        if logger:
            logger.logEnd()
        return glyphRecords

    # ------------------
    # feature management
    # ------------------

    def getScriptList(self):
        gsub = []
        gpos = []
        if self.gsub is not None:
            gsub = self.gsub.getScriptList()
        if self.gpos is not None:
            gpos = self.gpos.getScriptList()
        return sorted(set(gsub + gpos))

    def getLanguageList(self):
        gsub = []
        gpos = []
        if self.gsub is not None:
            gsub = self.gsub.getLanguageList()
        if self.gpos is not None:
            gpos = self.gpos.getLanguageList()
        return sorted(set(gsub + gpos))

    def getFeatureList(self):
        gsub = []
        gpos = []
        if self.gsub is not None:
            gsub = self.gsub.getFeatureList()
        if self.gpos is not None:
            gpos = self.gpos.getFeatureList()
        return sorted(set(gsub + gpos))

    def getFeatureState(self, featureTag):
        gsubState = None
        gposState = None
        if self.gsub is not None:
            if featureTag in self.gsub:
                gsubState = self.gsub.getFeatureState(featureTag)
        if self.gpos is not None:
            if featureTag in self.gpos:
                gposState = self.gpos.getFeatureState(featureTag)
        if gsubState is not None and gposState is not None:
            if gsubState != gposState:
                raise CompositorError("Inconsistently applied feature: %s" % featureTag)
        if gsubState is not None:
            return gsubState
        if gposState is not None:
            return gposState
        raise CompositorError("Feature %s is is not contained in GSUB or GPOS" % featureTag)

    def setFeatureState(self, featureTag, state):
        if self.gsub is not None:
            if featureTag in self.gsub:
                self.gsub.setFeatureState(featureTag, state)
        if self.gpos is not None:
            if featureTag in self.gpos:
                self.gpos.setFeatureState(featureTag, state)

    # -------------
    # Miscellaneous
    # -------------

    def getGlyphOrder(self):
        return self.source.getGlyphOrder()
Пример #39
0
class ShapeDiffFinder:
    """Provides methods to report diffs in glyph shapes between OT Fonts."""

    def __init__(
            self, file_a, file_b, stats, ratio_diffs=False, diff_threshold=0):
        self.path_a = file_a
        self.font_a = TTFont(self.path_a)
        self.glyph_set_a = self.font_a.getGlyphSet()
        self.gdef_a = {}
        if 'GDEF' in self.font_a:
            self.gdef_a = self.font_a['GDEF'].table.GlyphClassDef.classDefs

        self.path_b = file_b
        self.font_b = TTFont(self.path_b)
        self.glyph_set_b = self.font_b.getGlyphSet()
        self.gdef_b = {}
        if 'GDEF' in self.font_b:
            self.gdef_b = self.font_b['GDEF'].table.GlyphClassDef.classDefs

        for stat_type in (
                'compared', 'untested', 'unmatched', 'unicode_mismatch',
                'gdef_mark_mismatch', 'zero_width_mismatch', 'input_mismatch'):
            if stat_type not in stats:
                stats[stat_type] = []
        self.stats = stats

        self.ratio_diffs = ratio_diffs
        self.diff_threshold = diff_threshold
        self.basepath = os.path.basename(file_a)

    def find_area_diffs(self):
        """Report differences in glyph areas."""

        self.build_names()

        pen_a = GlyphAreaPen(self.glyph_set_a)
        pen_b = GlyphAreaPen(self.glyph_set_b)

        mismatched = {}
        for name in self.names:
            self.glyph_set_a[name].draw(pen_a)
            area_a = pen_a.pop()
            self.glyph_set_b[name].draw(pen_b)
            area_b = pen_b.pop()
            if area_a != area_b:
                mismatched[name] = (area_a, area_b)

        stats = self.stats['compared']
        calc = self._calc_ratio if self.ratio_diffs else self._calc_diff
        for name, areas in mismatched.items():
            stats.append((calc(areas), name, self.basepath, areas[0], areas[1]))

    def find_rendered_diffs(self, font_size=128, render_path=None):
        """Find diffs of glyphs as rendered by harfbuzz."""

        hb_input_generator_a = hb_input.HbInputGenerator(self.font_a)
        hb_input_generator_b = hb_input.HbInputGenerator(self.font_b)

        if render_path:
            font_name, _ = os.path.splitext(self.basepath)
            render_path = os.path.join(render_path, font_name)
            if not os.path.exists(render_path):
                os.makedirs(render_path)

        self.build_names()
        diffs = []
        for name in self.names:
            class_a = self.gdef_a.get(name, GDEF_UNDEF)
            class_b = self.gdef_b.get(name, GDEF_UNDEF)
            if GDEF_MARK in (class_a, class_b) and class_a != class_b:
                self.stats['gdef_mark_mismatch'].append((
                    self.basepath, name, GDEF_LABELS[class_a],
                    GDEF_LABELS[class_b]))
                continue

            width_a = self.glyph_set_a[name].width
            width_b = self.glyph_set_b[name].width
            zwidth_a = width_a == 0
            zwidth_b = width_b == 0
            if zwidth_a != zwidth_b:
                self.stats['zero_width_mismatch'].append((
                    self.basepath, name, width_a, width_b))
                continue

            hb_args_a = hb_input_generator_a.input_from_name(name, pad=zwidth_a)
            hb_args_b = hb_input_generator_b.input_from_name(name, pad=zwidth_b)
            if hb_args_a != hb_args_b:
                self.stats['input_mismatch'].append((
                    self.basepath, name, hb_args_a, hb_args_b))
                continue

            # ignore unreachable characters
            if not hb_args_a:
                self.stats['untested'].append((self.basepath, name))
                continue

            features, text = hb_args_a

            # ignore null character
            if unichr(0) in text:
                continue

            img_file_a = StringIO.StringIO(subprocess.check_output([
                'hb-view', '--font-size=%d' % font_size,
                '--features=%s' % ','.join(features), self.path_a, text]))
            img_file_b = StringIO.StringIO(subprocess.check_output([
                'hb-view', '--font-size=%d' % font_size,
                '--features=%s' % ','.join(features), self.path_b, text]))
            img_a = Image.open(img_file_a)
            img_b = Image.open(img_file_b)
            width_a, height_a = img_a.size
            width_b, height_b = img_b.size
            data_a = img_a.getdata()
            data_b = img_b.getdata()
            img_file_a.close()
            img_file_b.close()

            width, height = max(width_a, width_b), max(height_a, height_b)
            offset_ax = (width - width_a) // 2
            offset_ay = (height - height_a) // 2
            offset_bx = (width - width_b) // 2
            offset_by = (height - height_b) // 2

            diff = 0
            for y in range(height):
                for x in range(width):
                    ax, ay = x - offset_ax, y - offset_ay
                    bx, by = x - offset_bx, y - offset_by
                    if (ax < 0 or bx < 0 or ax >= width_a or bx >= width_b or
                        ay < 0 or by < 0 or ay >= height_a or by >= height_b):
                        diff += 1
                    else:
                        diff += abs(data_a[ax + ay * width_a] -
                                    data_b[bx + by * width_b]) / 255

            if self.ratio_diffs:
                diff /= (width * height)

            if render_path and diff > self.diff_threshold:
                img_cmp = Image.new('RGB', (width, height))
                data_cmp = list(img_cmp.getdata())
                self._project(data_a, width_a, height_a,
                              data_cmp, width, height, 1)
                self._project(data_b, width_b, height_b,
                              data_cmp, width, height, 0)
                for y in range(height):
                    for x in range(width):
                        i = x + y * width
                        r, g, b = data_cmp[i]
                        assert b == 0
                        data_cmp[i] = r, g, min(r, g)
                img_cmp.putdata(data_cmp)
                img_cmp.save(self._rendered_png(render_path, name))

            diffs.append((name, diff))

        mismatched = {}
        for name, diff in diffs:
            if diff > self.diff_threshold:
                mismatched[name] = diff

        stats = self.stats['compared']
        for name, diff in mismatched.items():
            stats.append((diff, name, self.basepath))

    def _project(
            self, src_data, src_width, src_height,
            dst_data, width, height, channel):
        """Project a single-channel image onto a channel of an RGB image."""

        offset_x = (width - src_width) // 2
        offset_y = (height - src_height) // 2
        for y in range(src_height):
            for x in range(src_width):
                src_i = x + y * src_width
                dst_i = x + offset_x + (y + offset_y) * width
                pixel = list(dst_data[dst_i])
                pixel[channel] = src_data[src_i]
                dst_data[dst_i] = tuple(pixel)

    def find_shape_diffs(self):
        """Report differences in glyph shapes, using BooleanOperations."""

        self.build_names()

        area_pen = GlyphAreaPen(None)
        pen = PointToSegmentPen(area_pen)
        mismatched = {}
        for name in self.names:
            glyph_a = Glyph()
            glyph_b = Glyph()
            self.glyph_set_a[name].draw(
                Qu2CuPen(glyph_a.getPen(), self.glyph_set_a))
            self.glyph_set_b[name].draw(
                Qu2CuPen(glyph_b.getPen(), self.glyph_set_b))
            booleanOperations.xor(list(glyph_a), list(glyph_b), pen)
            area = abs(area_pen.pop())
            if area:
                mismatched[name] = (area)

        stats = self.stats['compared']
        for name, area in mismatched.items():
            stats.append((area, name, self.basepath))

    def find_area_shape_diff_products(self):
        """Report product of differences in glyph areas and glyph shapes."""

        self.find_area_diffs()
        old_compared = self.stats['compared']
        self.stats['compared'] = []
        self.find_shape_diffs()
        new_compared = {n: d for d, n, _ in self.stats['compared']}
        for i, (diff, name, font, area_a, area_b) in enumerate(old_compared):
            if font != self.basepath:
                continue
            new_diff = diff * new_compared.get(name, 0)
            old_compared[i] = new_diff, name, font, area_a, area_b
        self.stats['compared'] = old_compared

    def build_names(self):
        """Build a list of glyph names shared between the fonts."""

        if hasattr(self, 'names'):
            return

        stats = self.stats['unmatched']
        names_a = set(self.font_a.getGlyphOrder())
        names_b = set(self.font_b.getGlyphOrder())
        if names_a != names_b:
            stats.append((self.basepath, names_a - names_b, names_b - names_a))
        self.names = names_a & names_b

        stats = self.stats['unicode_mismatch']
        reverse_cmap_a = hb_input.build_reverse_cmap(self.font_a)
        reverse_cmap_b = hb_input.build_reverse_cmap(self.font_b)
        mismatched = {}
        for name in self.names:
            unival_a = reverse_cmap_a.get(name)
            unival_b = reverse_cmap_b.get(name)
            if unival_a != unival_b:
                mismatched[name] = (unival_a, unival_b)
        if mismatched:
            stats.append((self.basepath, mismatched.items()))
            self.names -= set(mismatched.keys())

    @staticmethod
    def dump(stats, whitelist, out_lines, include_vals, multiple_fonts):
        """Return the results of run diffs.

        Args:
            stats: List of tuples with diff data which is sorted and printed.
            whitelist: Names of glyphs to exclude from report.
            out_lines: Number of diff lines to print.
            include_vals: Include the values that have been diffed in report.
            multiple_fonts: Designates whether stats have been accumulated from
                multiple fonts, if so then font names will be printed as well.
        """

        report = []

        compared = sorted(
            s for s in stats['compared'] if s[1] not in whitelist)
        compared.reverse()
        fmt = '%s %s'
        if include_vals:
            fmt += ' (%s vs %s)'
        if multiple_fonts:
            fmt = '%s ' + fmt
        report.append('%d differences in glyph shape' % len(compared))
        for line in compared[:out_lines]:
            # print <font> <glyph> <vals>; stats are sorted in reverse priority
            line = tuple(reversed(line[:3])) + tuple(line[3:])
            # ignore font name if just one pair of fonts was compared
            if not multiple_fonts:
                line = line[1:]
            report.append(fmt % line)
        report.append('')

        for font, set_a, set_b in stats['unmatched']:
            report.append("Glyph coverage doesn't match in %s" % font)
            report.append('  in A but not B: %s' % sorted(set_a))
            report.append('  in B but not A: %s' % sorted(set_b))
        report.append('')

        for font, mismatches in stats['unicode_mismatch']:
            report.append("Glyph unicode values don't match in %s" % font)
            for name, univals in sorted(mismatches):
                univals = [(('0x%04X' % v) if v else str(v)) for v in univals]
                report.append('  %s: %s in A, %s in B' %
                              (name, univals[0], univals[1]))
        report.append('')

        ShapeDiffFinder._add_simple_report(
            report, stats['gdef_mark_mismatch'],
            '%s: Mark class mismatch for %s (%s vs %s)')
        ShapeDiffFinder._add_simple_report(
            report, stats['zero_width_mismatch'],
            '%s: Zero-width mismatch for %s (%d vs %d)')
        ShapeDiffFinder._add_simple_report(
            report, stats['input_mismatch'],
            '%s: Harfbuzz input mismatch for %s (%s vs %s)')
        ShapeDiffFinder._add_simple_report(
            report, stats['untested'],
            '%s: %s not tested (unreachable?)')

        return '\n'.join(report)

    @staticmethod
    def _add_simple_report(report, stats, fmt):
        for stat in sorted(stats):
            report.append(fmt % stat)
        if stats:
            report.append('')

    def _calc_diff(self, vals):
        """Calculate an area difference."""

        a, b = vals
        return abs(a - b)

    def _calc_ratio(self, vals):
        """Calculate an area ratio."""

        a, b = vals
        if not (a or b):
            return 0
        if abs(a) > abs(b):
            a, b = b, a
        return 1 - a / b

    def _rendered_png(self, render_path, glyph_name):
        glyph_filename = re.sub(r'([A-Z_])', r'\1_', glyph_name) + '.png'
        return os.path.join(render_path, glyph_filename)
Пример #40
0
def makeLookup1():
    # make a variation of the shell TTX data
    f = open(shellSourcePath)
    ttxData = f.read()
    f.close()
    ttxData = ttxData.replace("__familyName__", "gsubtest-lookup1")
    tempShellSourcePath = shellSourcePath + ".temp"
    f = open(tempShellSourcePath, "wb")
    f.write(ttxData)
    f.close()
    
    # compile the shell
    shell = TTFont(sfntVersion="OTTO")
    shell.importXML(tempShellSourcePath)
    shell.save(shellTempPath)
    os.remove(tempShellSourcePath)
    
    # load the shell
    shell = TTFont(shellTempPath)
    
    # grab the PASS and FAIL data
    hmtx = shell["hmtx"]
    glyphSet = shell.getGlyphSet()
    
    failGlyph = glyphSet["F"]
    failGlyph.decompile()
    failGlyphProgram = list(failGlyph.program)
    failGlyphMetrics = hmtx["F"]
    
    passGlyph = glyphSet["P"]
    passGlyph.decompile()
    passGlyphProgram = list(passGlyph.program)
    passGlyphMetrics = hmtx["P"]
    
    # grab some tables
    hmtx = shell["hmtx"]
    cmap = shell["cmap"]
    
    # start the glyph order
    existingGlyphs = [".notdef", "space", "F", "P"]
    glyphOrder = list(existingGlyphs)
    
    # start the CFF
    cff = shell["CFF "].cff
    globalSubrs = cff.GlobalSubrs
    topDict = cff.topDictIndex[0]
    topDict.charset = existingGlyphs
    private = topDict.Private
    charStrings = topDict.CharStrings
    charStringsIndex = charStrings.charStringsIndex
    
    features = sorted(mapping)

    # build the outline, hmtx and cmap data
    cp = baseCodepoint
    for index, tag in enumerate(features):
    
    	# tag.pass
    	glyphName = "{0!s}.pass".format(tag)
    	glyphOrder.append(glyphName)
    	addGlyphToCFF(
    		glyphName=glyphName,
    		program=passGlyphProgram,
    		private=private,
    		globalSubrs=globalSubrs,
    		charStringsIndex=charStringsIndex,
    		topDict=topDict,
            charStrings=charStrings
    	)
    	hmtx[glyphName] = passGlyphMetrics
     
    	for table in cmap.tables:
    		if table.format == 4:
    			table.cmap[cp] = glyphName
    		else:
    			raise NotImplementedError, "Unsupported cmap table format: {0:d}".format(table.format)
    	cp += 1
    
    	# tag.fail
    	glyphName = "{0!s}.fail".format(tag)
    	glyphOrder.append(glyphName)
    	addGlyphToCFF(
    		glyphName=glyphName,
    		program=failGlyphProgram,
    		private=private,
    		globalSubrs=globalSubrs,
    		charStringsIndex=charStringsIndex,
    		topDict=topDict,
            charStrings=charStrings
    	)
    	hmtx[glyphName] = failGlyphMetrics
     
    	for table in cmap.tables:
    		if table.format == 4:
    			table.cmap[cp] = glyphName
    		else:
    			raise NotImplementedError, "Unsupported cmap table format: {0:d}".format(table.format)

        # bump this up so that the sequence is the same as the lookup 3 font
    	cp += 3

    # set the glyph order
    shell.setGlyphOrder(glyphOrder)
    
    # start the GSUB
    shell["GSUB"] = newTable("GSUB")
    gsub = shell["GSUB"].table = GSUB()
    gsub.Version = 1.0
    
    # make a list of all the features we will make
    featureCount = len(features)
    
    # set up the script list
    scriptList = gsub.ScriptList = ScriptList()
    scriptList.ScriptCount = 1
    scriptList.ScriptRecord = []
    scriptRecord = ScriptRecord()
    scriptList.ScriptRecord.append(scriptRecord)
    scriptRecord.ScriptTag = "DFLT"
    script = scriptRecord.Script = Script()
    defaultLangSys = script.DefaultLangSys = DefaultLangSys()
    defaultLangSys.FeatureCount = featureCount
    defaultLangSys.FeatureIndex = range(defaultLangSys.FeatureCount)
    defaultLangSys.ReqFeatureIndex = 65535
    defaultLangSys.LookupOrder = None
    script.LangSysCount = 0
    script.LangSysRecord = []
    
    # set up the feature list
    featureList = gsub.FeatureList = FeatureList()
    featureList.FeatureCount = featureCount
    featureList.FeatureRecord = []
    for index, tag in enumerate(features):
        # feature record
        featureRecord = FeatureRecord()
        featureRecord.FeatureTag = tag
        feature = featureRecord.Feature = Feature()
        featureList.FeatureRecord.append(featureRecord)
        # feature
        feature.FeatureParams = None
        feature.LookupCount = 1
        feature.LookupListIndex = [index]
    
    # write the lookups
    lookupList = gsub.LookupList = LookupList()
    lookupList.LookupCount = featureCount
    lookupList.Lookup = []
    for tag in features:
        # lookup
        lookup = Lookup()
        lookup.LookupType = 1
        lookup.LookupFlag = 0
        lookup.SubTableCount = 1
        lookup.SubTable = []
        lookupList.Lookup.append(lookup)
        # subtable
        subtable = SingleSubst()
        subtable.Format = 2
        subtable.LookupType = 1
        subtable.mapping = {
            "{0!s}.pass".format(tag) : "{0!s}.fail".format(tag),
            "{0!s}.fail".format(tag) : "{0!s}.pass".format(tag),
        }
        lookup.SubTable.append(subtable)
    
    path = outputPath % 1 + ".otf"
    if os.path.exists(path):
    	os.remove(path)
    shell.save(path)
    
    # get rid of the shell
    if os.path.exists(shellTempPath):
        os.remove(shellTempPath)
Пример #41
0
		print("  example: reportLabPen.py Arial.TTF R test.png")
		print("  (The file format will be PNG, regardless of the image file name supplied)")
		sys.exit(0)

	from fontTools.ttLib import TTFont
	from reportlab.lib import colors

	path = sys.argv[1]
	glyphName = sys.argv[2]
	if (len(sys.argv) > 3):
		imageFile = sys.argv[3]
	else:
		imageFile = "%s.png" % glyphName

	font = TTFont(path)  # it would work just as well with fontTools.t1Lib.T1Font
	gs = font.getGlyphSet()
	pen = ReportLabPen(gs, Path(fillColor=colors.red, strokeWidth=5))
	g = gs[glyphName]
	g.draw(pen)

	w, h = g.width, 1000
	from reportlab.graphics import renderPM
	from reportlab.graphics.shapes import Group, Drawing, scale

	# Everything is wrapped in a group to allow transformations.
	g = Group(pen.path)
	g.translate(0, 200)
	g.scale(0.3, 0.3)

	d = Drawing(w, h)
	d.add(g)
Пример #42
0
class Text(Grob, TransformMixin, ColorMixin):
    stateAttributes = ('_transform', '_transformmode', '_fillcolor', '_fontfile', '_fontsize', '_align', '_lineheight')
    
    FONT_SPECIFIER_NAME_ID   = 4
    FONT_SPECIFIER_FAMILY_ID = 1

    FAMILY_NAMES = {
	0: ("ANY",{}),
	1: ("SERIF-OLD", {
		0: "ANY",
		1: "ROUNDED-LEGIBILITY",
		2: "GARALDE",
		3: "VENETIAN",
		4: "VENETIAN-MODIFIED",
		5: "DUTCH-MODERN",
		6: "DUTCH-TRADITIONAL",
		7: "CONTEMPORARY",
		8: "CALLIGRAPHIC",
		15: "MISCELLANEOUS",
	}),
	2: ("SERIF-TRANSITIONAL", {
		0: "ANY",
		1: "DIRECT-LINE",
		2: "SCRIPT",
		15: "MISCELLANEOUS",
	}),
	3: ("SERIF", {
		0: "ANY",
		1: "ITALIAN",
		2: "SCRIPT",
		15: "MISCELLANEOUS",
	}),
	4: ("SERIF-CLARENDON",{
		0: "ANY",
		1: "CLARENDON",
		2: "MODERN",
		3: "TRADITIONAL",
		4: "NEWSPAPER",
		5: "STUB-SERIF",
		6: "MONOTYPE",
		7: "TYPEWRITER",
		15: "MISCELLANEOUS",
	}),
	5: ("SERIF-SLAB",{
		0: 'ANY',
		1: 'MONOTONE',
		2: 'HUMANIST',
		3: 'GEOMETRIC',
		4: 'SWISS',
		5: 'TYPEWRITER',
		15: 'MISCELLANEOUS',
	}),
	7: ("SERIF-FREEFORM",{
		0: 'ANY',
		1: 'MODERN',
		15: 'MISCELLANEOUS',
	}),
	8: ("SANS",{
		0: 'ANY',
		1: 'GOTHIC-NEO-GROTESQUE-IBM',
		2: 'HUMANIST',
		3: 'ROUND-GEOMETRIC-LOW-X',
		4: 'ROUND-GEOMETRIC-HIGH-X',
		5: 'GOTHIC-NEO-GROTESQUE',
		6: 'GOTHIC-NEO-GROTESQUE-MODIFIED',
		9: 'GOTHIC-TYPEWRITER',
		10: 'MATRIX',
		15: 'MISCELLANEOUS',
	}),
	9: ("ORNAMENTAL",{
		0: 'ANY',
		1: 'ENGRAVER',
		2: 'BLACK-LETTER',
		3: 'DECORATIVE',
		4: 'THREE-DIMENSIONAL',
		15: 'MISCELLANEOUS',
	}),
	10:("SCRIPT",{
		0: 'ANY',
		1: 'UNCIAL',
		2: 'BRUSH-JOINED',
		3: 'FORMAL-JOINED',
		4: 'MONOTONE-JOINED',
		5: 'CALLIGRAPHIC',
		6: 'BRUSH-UNJOINED',
		7: 'FORMAL-UNJOINED',
		8: 'MONOTONE-UNJOINED',
		15: 'MISCELLANEOUS',
	}),
	12:("SYMBOL",{
		0: 'ANY',
		3: 'MIXED-SERIF',
		6: 'OLDSTYLE-SERIF',
		7: 'NEO-GROTESQUE-SANS',
		15: 'MISCELLANEOUS',
                }),
        }

    WEIGHT_NAMES = {
        'thin':100,
	'extralight':200,
	'ultralight':200,
	'light':300,
	'normal':400,
	'regular':400,
	'plain':400,
	'medium':500,
	'semibold':600,
	'demibold':600,
	'bold':700,
	'extrabold':800,
	'ultrabold':800,
	'black':900,
	'heavy':900,
        }

    WEIGHT_NUMBERS = {}

    def __init__(self, context, text, x=0, y=0, path=None, **kwargs):
        self._ctx = context

        if context:
            copy_attrs(self._ctx, self, self.stateAttributes)

        self.font_handle = TTFont(self._fontfile)        

        self.x = x
        self.y = y

        self.text        = unicode(text)
        self.path        = path
        self.resolution  = 72
        self.width       = 0
        self.height      = 0
        self.text_xmin   = 0
        self.text_ymin   = 0
        self.text_xmax   = 0
        self.text_ymax   = 0
        self.posX        = 0
        self.posY        = 0
        self.kerning     = 0
        self.scale       = float(self._fontsize)/float(self.char_height)



        self.encoding    = None
        self.current_pt  = (0, 0)
        self.t_scale     = Scale(self.scale, self.scale)
        self.offset      = 0
        self.metrics     = {}

        ## cx, cy should be the center point
        fx, fy = (-self.scale, self.scale)
        C = math.cos(math.pi)
        S = math.sin(math.pi)
        
        self.aux_transform = Transform(fx*C, fx*S, -S*fy, C*fy, self.x, self.y)
        
        for key, value in self.WEIGHT_NAMES.items():
            self.WEIGHT_NUMBERS.setdefault(value,[]).append(key)
            
        if self.encoding is None:
            self.table = self.font_handle['cmap'].tables
            self.encoding = (self.table[0].platformID, self.table[0].platEncID)

        self.table = self.font_handle['cmap'].getcmap(*self.encoding)

        for glyph in self.text:
            glyfName = self.table.cmap.get(ord(glyph))
            self.width += self.glyph_width(glyfName)

        self.height = self.char_height * self.scale

        print "metrics: w:%f h:%f" % (self.width, self.height)
        #print "scale: %f" % (self.scale)
        ## self.vorg = self.font_handle['VORG']
        #print self.table.__dict__

        
    def _get_abs_bounds(self):
        return (self.x, self.y, self.width + self.x, self.height + self.y)
    abs_bounds = property(_get_abs_bounds)

    def _get_bounds(self):
        return (self.width, self.height)
    bounds = property(_get_bounds)

    def _get_center(self):
        (x1, y1, x2, y2) = self.abs_bounds
        x = (x1 + x2) / 2
        y = (y1 + y2) / 2
        return (x, y)
    center = property(_get_center)

    ### Drawing methods ###
    def _get_transform(self):
        trans = self._transform.copy()
        if (self._transformmode == CENTER):
            (x, y, w, h) = self.bounds
            deltax = x+w/2
            deltay = y+h/2

            t = Transform()
            t.translate(-deltax, -deltay)
            trans.prepend(t)

            t = Transform()
            t.translate(deltax, deltay)
            trans.append(t)

        return trans
    get_transform = property(_get_transform)
    

    def _get_ascent(self):
        return self.font_handle['OS/2'].sTypoAscender
    ascent = property(_get_ascent)

    def _get_descent(self):
        return self.font_handle['OS/2'].sTypoDescender
    descent = property(_get_descent)

    def _get_line_height(self):
        return self.char_height + self.font_handle['OS/2'].sTypoLineGap
    line_height = property(_get_line_height)

    def _get_char_height(self):
	if self.descent > 0:
            descent = -self.descent
        else:
            descent = self.descent
	return self.ascent - descent
    char_height = property(_get_char_height)
    
    def _get_short_name(self):
        """Get the short name from the font's names table"""
	name   = ""
	family = ""
	for record in self.font_handle['name'].names:
            if record.nameID == FONT_SPECIFIER_NAME_ID and not name:
                if '\000' in record.string:
                    name = unicode(record.string, 'utf-16-be').encode('utf-8')
                else:
                    name = record.string
            elif record.nameID == FONT_SPECIFIER_FAMILY_ID and not family:
                if '\000' in record.string:
                    family = unicode(record.string, 'utf-16-be').encode('utf-8')
                else:
                    family = record.string
            if name and family:
                break

        return name, family
    short_name = property(_get_short_name)
    
    def _get_family(self):
	"""Get the family (and sub-family) for a font"""
	HIBYTE = 65280
	LOBYTE = 255
	familyID = (self.font_handle['OS/2'].sFamilyClass&HIBYTE)>>8
	subFamilyID = self.font_handle['OS/2'].sFamilyClass&LOBYTE
        familyName, subFamilies = self.FAMILY_NAMES.get(familyID, ('RESERVED', None))

	if subFamilies:
            subFamily = subFamilies.get(subFamilyID, 'RESERVED')
	else:
            subFamily = 'ANY'

	return (familyName, subFamily)
    family = property(_get_family)
    
    def _get_weight(self):
        return self.font_handle['OS/2'].usWeightClass
    weight = property(_get_weight)

    def _get_italic(self):
        return (self.font_handle['OS/2'].fsSelection &1 or self.font_handle['head'].macStyle&2)
    italic = property(_get_italic)

    def decomposeQuad(self, points):
        n = len(points) - 1
	assert n > 0
	quadSegments = []
	for i in range(n - 1):
            x, y = points[i]
            nx, ny = points[i+1]
            impliedPt = (0.5 * (x + nx), 0.5 * (y + ny))
            quadSegments.append((points[i], impliedPt))

	quadSegments.append((points[-2], points[-1]))
	return quadSegments

    def _q_curveTo(self, *points):
        n = len(points) - 1

        if points[-1] is None:
            x, y   = points[-2]  ## last off-curve point
            nx, ny = points[0]   ## first off-curve point

            impliedStartPoint = (0.5 * (x + nx), 0.5 * (y + ny))
            impliedStartPoint = self.aux_transform.transformPoint(impliedStartPoint)
            self.currentPoint = impliedStartPoint
            self.path.moveto(impliedStartPoint)

            points = points[:-1] + (impliedStartPoint,)

        if n > 0:
            for pt1, pt2 in self.decomposeQuad(points):
                #self.path.moveto()
                pt0 = self.aux_transform.transformPoint(pt1)
                pt1 = self.aux_transform.transformPoint(pt2)
                self.path.curve3to(pt0[0], pt0[1], pt1[0], pt1[1])
                self.current_pt = (pt1[0], pt1[1])
        else: 
            (X, Y) = self.aux_transform.transformPoint((points[0][0], points[0][1]))
            self.path.lineto(X, Y)
            self.current_pt = (pt2[0], pt2[1])

    def glyph_width(self, glyph_name):
        try:
            return self.font_handle['hmtx'].metrics[glyph_name][0]*self.scale
	except KeyError:
            raise ValueError( """Couldn't find glyph for glyphName %r""" % (glyphName))

    def has_glyph(self, glyph_name, encoding=None):
	""" Check to see if font appears to have explicit glyph for char """
        cmap = self.font_handle['cmap']
	if encoding is None:
            table = self.font_handle['cmap'].tables
            encoding = (table.platformID, table.platEncID)

	table    = cmap.getcmap(*encoding)
	glyfName = table.cmap.get(ord(char))

	if glyfName is None:
            return False
        else:
            return True

    def kerning(self):
        kern = 0
        if idx+1 < len(self.text):
            L = self.text[idx]
            R = self.text[idx+1]

            left  = glyph_set._ttFont.getGlyphName(ord(L))
            right = glyph_set._ttFont.getGlyphName(ord(R))
            
            try:
                kern = glyph_set._ttFont['kern'].kernTables[0].__dict__['kernTable'][(L, R)]
            except:
                pass
        #c_width = 
        #if not self.metrics.has_key(letter):
        #    self.metrics[letter] = {}

        #self.metrics[letter].update(glyph_set._ttFont.tables['OS/2'].__dict__)
        #self.metrics[letter].update(glyph_set._ttFont.tables['maxp'].__dict__)
        #self.metrics[letter].update(glyph_set._ttFont.tables['hhea'].__dict__)
        #self.metrics[letter].update(glyph_set._ttFont.tables['head'].__dict__)
    
    def decomposeOutline(self, glyph, letter, glyph_set, halign='center'):
        hmtx = glyph_set._ttFont['hmtx']
        width_, lsb = hmtx[letter]
        rsb = width_ - lsb - (glyph.xMax - glyph.xMin)
        extent = lsb + (glyph.xMax - glyph.xMin)
        coordinates, end_points, flags = glyph.getCoordinates(self.glyph_table)
        
        offsetx_ = 0
        idx = self.text.index(letter)
        if idx+1 < len(self.text):
            _letter = self.text[idx+1]
            _glyph  = self.glyph_table[_letter]

            next_width = ((hmtx[_letter][0] + hmtx[_letter][1]) * self.scale)*0.5
            _offsetx = (glyph.xMin + next_width)

        if halign=='center':
            offsetx = (glyph.xMin + width_)
        elif halign=='right': 
            offsetx = glyph.xMin + width_
        else: 
            offsetx = glyph.xMin

        offsetx = (offsetx + offsetx_) * self.scale


        
        idx = self.text.index(letter)
        if idx+1 < len(self.text):
            n_width = hmtx[self.text[idx+1]]
        
        self.aux_transform *= Transform(dx=offsetx)

        if not coordinates.any():
            return
        
        ## in the rare case that it doesnt have an xMin
        if hasattr(glyph, "xMin"):
            #offset = (lsb_*self.scale - glyph.xMin*self.scale)
            offset = 0
        else:
            offset = 0

        start = 0
        for end in end_points:
            end = end + 1
            contour = coordinates[start:end].tolist()
            contour_flags = flags[start:end].tolist()
            start = end
            
            if 1 not in contour_flags:
                contour.append(None)
                self._q_curveTo(*contour)
            else:
                f_on_curve = contour_flags.index(1) + 1
                contour    = contour[f_on_curve:] + contour[:f_on_curve]
                contour_flags = contour_flags[f_on_curve:] + contour_flags[:f_on_curve]
                
                (X, Y) = self.aux_transform.transformPoint(contour[-1])
                self.path.moveto(X, Y)
                self.current_pt=(X, Y)

                while contour:
                    n_on_curve = contour_flags.index(1) + 1
                    if n_on_curve == 1:
                        (X, Y) = self.aux_transform.transformPoint(contour[0])
                        self.path.lineto(X, Y)
                        self.current_pt=(X, Y)
                    else:
                        self._q_curveTo(*contour[:n_on_curve])

                    contour        = contour[n_on_curve:]
                    contour_flags  = contour_flags[n_on_curve:]
                
            self.path.closepath()

        self.aux_transform *= Transform(dx=(rsb*self.scale))

    def sentence(self):
        for letter in self.text:
            rendered = self.draw(letter)


    def draw(self, letter):
        glyph_set   = self.font_handle.getGlyphSet()
        self.glyph_table = glyph_set._ttFont['glyf']
        #self.metrics[letter] = self.font_handle['hmtx'].metrics[letter]
        glyph = self.glyph_table[letter]
        self.decomposeOutline(glyph, letter, glyph_set)