示例#1
0
    def shape(self, text, onchange=None):
        """Shapes a text

    This shapes a piece of text, return a uharfbuzz `Buffer` object.

    Additionally, if an `onchange` function is provided, this will be called
    every time the buffer changes *during* shaping, with the following arguments:

    - ``self``: the vharfbuzz object.
    - ``stage``: either "GSUB" or "GPOS"
    - ``lookupid``: the current lookup ID
    - ``buffer``: a copy of the buffer as a list of lists (glyphname, cluster, position)
    """

        self.prepare_shaper()
        buf = hb.Buffer()
        buf.add_str(text)
        buf.guess_segment_properties()
        self.stage = "GSUB"
        if onchange:
            f = self.make_message_handling_function(buf, onchange)
            buf.set_message_func(f)
        hb.shape(self.hbfont, buf, shapers=self.shapers)
        self.stage = "GPOS"
        return buf
示例#2
0
    def shape(self, text, *, features=None, varLocation=None,
              direction=None, language=None, script=None):
        if features is None:
            features = {}
        if varLocation is None:
            varLocation = {}

        self.font.scale = (self.face.upem, self.face.upem)
        self.font.set_variations(varLocation)

        hb.ot_font_set_funcs(self.font)

        if self._funcs is not None:
            self.font.funcs = self._funcs

        buf = hb.Buffer.create()
        buf.add_str(str(text))  # add_str() does not accept str subclasses
        buf.guess_segment_properties()

        if direction is not None:
            buf.direction = direction
        if language is not None:
            buf.language = language
        if script is not None:
            buf.script = script

        hb.shape(self.font, buf, features)

        glyphOrder = self.glyphOrder
        infos = []
        for info, pos in zip(buf.glyph_infos, buf.glyph_positions):
            infos.append(GlyphInfo(info.codepoint, glyphOrder[info.codepoint], info.cluster, *pos.position))

        return infos
def test_rvrn(latest_otf, otf_font, latest_ttf, ttf_font, wght_val):
    """
    Ensure that the 'rvrn' feature is activated/not activated at expected
    variations. The 'rvrn' feature of this font substitutes a design variation
    of '$' and '¢' at heavier weights.
    """
    test_str = "$2.00 5¢"

    for expected, actual in ((latest_otf, otf_font), (latest_ttf, ttf_font)):
        buf_expected = hb.Buffer()
        buf_expected.add_str(test_str)
        buf_expected.guess_segment_properties()

        buf_actual = hb.Buffer()
        buf_actual.add_str(test_str)
        buf_actual.guess_segment_properties()

        expected.set_variations({"wght": wght_val})
        hb.shape(expected, buf_expected, None)
        infos_expected = buf_expected.glyph_infos

        actual.set_variations({"wght": wght_val})
        hb.shape(actual, buf_actual, None)
        infos_actual = buf_actual.glyph_infos

        assert len(infos_actual) == len(infos_expected)

        for i in range(len(infos_expected)):
            gn_expected = expected.get_glyph_name(infos_expected[i].codepoint)
            gn_actual = actual.get_glyph_name(infos_actual[i].codepoint)

            assert gn_actual == gn_expected
示例#4
0
 def shape_a_text(self, text):
     buf = hb.Buffer()
     buf.add_str(text)
     buf.guess_segment_properties()
     hb.shape(self.hbfont, buf)
     self.direction = buf.direction
     return buf
示例#5
0
  def pair_kerning(self, left, right):
    """The kerning between two glyphs (specified by name), in font units."""
    if self.face.has_kerning:
      return (self.face.get_kerning(left, right).x >> 6) * self.scale_factor
    else:
      if not self.hbFont:
        with open(self.filename, "rb") as fontfile:
          fontdata = fontfile.read()
        face = hb.Face(fontdata)
        font = hb.Font(face)
        scale = face.upem * self.scale_factor
        font.scale = (scale, scale)
        self.hbFont = font

      buf = hb.Buffer()
      buf.add_str(left+right)
      buf.guess_segment_properties()
      hb.shape(self.hbFont, buf, {"kern":True})
      pos = buf.glyph_positions[0].x_advance
      buf = hb.Buffer()
      buf.add_str(left+right)
      buf.guess_segment_properties()
      hb.shape(self.hbFont, buf, {"kern":False})
      pos2 = buf.glyph_positions[0].x_advance
      return pos-pos2
示例#6
0
def shaping_string(fontdata, glyphOrder, text, language=None):
    face = hb.Face(fontdata)
    font = hb.Font(face)
    upem = face.upem
    font.scale = (upem, upem)
    hb.ot_font_set_funcs(font)

    buf = hb.Buffer()

    buf.add_str(text)
    buf.guess_segment_properties()
    if language:
        buf.language = language

    features = {"kern": True, "liga": True}
    hb.shape(font, buf, features)

    infos = buf.glyph_infos
    positions = buf.glyph_positions
    outs = []
    for info, pos in zip(buf.glyph_infos, buf.glyph_positions):
        name = glyphOrder[info.codepoint]
        if name in ignorables:
            continue
        outs.append("%s=%i" % (name, info.cluster))
        if pos.position[0] != 0 or pos.position[1] != 0:
            outs[-1] = outs[-1] + "<%i,%i>" % (pos.position[0],
                                               pos.position[1])
    return "|".join(outs)
示例#7
0
 def test_gid_and_cluster_no_features(self, blankfont, string, expected):
     buf = hb.Buffer()
     buf.add_str(string)
     buf.guess_segment_properties()
     hb.shape(blankfont, buf)
     infos = [(g.codepoint, g.cluster) for g in buf.glyph_infos]
     assert infos == expected
示例#8
0
def metrics_from_text( ctx, font_path, text ):
  import uharfbuzz  as HB ### NOTE import after ctx available ###
  mhbfont       = get_mhbfont( ctx, font_path )
  bfr           = HB.Buffer()
  bfr.add_str( text )
  bfr.guess_segment_properties()
  features      = { 'kern': True, 'liga': True, }
  HB.shape( mhbfont.font, bfr, features )
  infos         = bfr.glyph_infos
  positions     = bfr.glyph_positions
  scale         = 1000 / mhbfont.upem
  R             = ctx.AttributeDict()
  width         = 0
  R.width       = width
  R.parts       = []
  for info, position in zip( infos, positions ):
    part                  = ctx.AttributeDict()
    x_advance             = position.x_advance  * scale
    part.dx               = round( x_advance )
    width                += x_advance
    # part.x_offset        = position.x_offset   * scale
    # part.y_advance       = position.y_advance  * scale
    # part.y_offset        = position.y_offset   * scale
    part.fid              = 'f123' ### NOTE fake font ID, to be replaced by viable ID of font ###
    part.gid              = info.codepoint
    R.parts.append( part )
  # ctx.log( '^77767^', part )
  R.width = round( width )
  return R
示例#9
0
    def test_message_func(self, blankfont):
        # Glyph IDs 1, 2, 3, 4, 5 map to glyphs a, b, c, d, e.
        # The calt feature replaces c by a in the context e, d, c', b, a.
        # The kern feature kerns b, a by +100.
        string = "edcba"
        buf = hb.Buffer()
        buf.add_str(string)
        buf.guess_segment_properties()

        messages = []
        infos_trace = []
        positions_trace = []

        def message(msg):
            messages.append(msg)
            infos_trace.append(buf.glyph_infos)
            positions_trace.append(buf.glyph_positions)

        buf.set_message_func(message)
        hb.shape(blankfont, buf)
        gids = [g.codepoint for g in buf.glyph_infos]
        assert gids == [5, 4, 1, 2, 1]
        pos = [g.x_advance for g in buf.glyph_positions]
        assert pos == [0, 0, 0, 100, 0]
        # messages: start GSUB lookup, end GSUB lookup, start GPOS lookup, end GPOS lookup
        assert messages == [
            'start lookup 0', 'end lookup 0', 'start lookup 0', 'end lookup 0'
        ]
        gids_trace = [[g.codepoint for g in infos] for infos in infos_trace]
        assert gids_trace == [[5, 4, 3, 2, 1], [5, 4, 1, 2, 1],
                              [5, 4, 1, 2, 1], [5, 4, 1, 2, 1]]
        advances_trace = [[g.x_advance for g in pos]
                          for pos in positions_trace]
        assert advances_trace == [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0],
                                  [0, 0, 0, 0, 0], [0, 0, 0, 100, 0]]
def test_zero_regression(latest_otf, otf_font, latest_ttf, ttf_font):
    zero_str = "90125"
    features = {"zero": True}

    for expected, actual in ((latest_otf, otf_font), (latest_ttf, ttf_font)):
        buf_expected = hb.Buffer()
        buf_expected.add_str(zero_str)
        buf_expected.guess_segment_properties()

        buf_actual = hb.Buffer()
        buf_actual.add_str(zero_str)
        buf_actual.guess_segment_properties()

        hb.shape(expected, buf_expected, features)
        infos_expected = buf_expected.glyph_infos

        hb.shape(actual, buf_actual, features)
        infos_actual = buf_actual.glyph_infos

        assert len(infos_expected) == len(infos_actual)

        for i in range(len(infos_expected)):
            gn_expected = expected.get_glyph_name(infos_expected[i].codepoint)
            gn_actual = actual.get_glyph_name(infos_actual[i].codepoint)

            assert gn_actual == gn_expected

            cl_expected = infos_expected[i].cluster
            cl_actual = infos_actual[i].cluster

            assert cl_actual == cl_expected
示例#11
0
def draw(surface, paths, text, features):
    bounds = None
    lines = []
    y = 0
    for path in paths:
        font = BlackRendererFont(path)

        buf = hb.Buffer()
        buf.add_str(text)
        buf.guess_segment_properties()
        hb.shape(font.hbFont, buf, features)

        line, rect, height = makeLine(buf, font, y)
        lines.append((font, line, rect, y))

        if bounds is None:
            bounds = rect
        bounds = unionRect(bounds, rect)
        y += height

    with surface.canvas(bounds) as canvas:
        for font, line, rect, y in lines:
            with canvas.savedState():
                # Center align the line.
                x = (bounds[2] - rect[2]) / 2
                canvas.translate(x, y)
                for glyph in line:
                    with canvas.savedState():
                        canvas.translate(glyph.xOffset, glyph.yOffset)
                        font.drawGlyph(glyph.name, canvas)
                    canvas.translate(glyph.xAdvance, glyph.yAdvance)
示例#12
0
    def shape_text_to_glyph_names(
        self,
        text: str,
        features: dict = None,
        gid_to_name: dict[int, str] = None,
    ) -> list[str]:

        buffer = hb.Buffer()  # type: ignore
        buffer.add_str(text)
        buffer.guess_segment_properties()

        hb.shape(self.font, buffer, features)  # type: ignore

        names = []
        for info, position in zip(buffer.glyph_infos, buffer.glyph_positions):
            gid = info.codepoint
            if gid_to_name is None:
                name = self.font.get_glyph_name(gid)
            else:
                name = gid_to_name.get(gid, f"gid{gid}")
            if name == "space" and position.x_advance == 0:  # HarfBuzz pseudo space for invisible glyphs
                name = "_invisible"
            names.append(name)

        return names
def test_figs_regression(latest_otf, otf_font, latest_ttf, ttf_font,
                         features_on, result_suffix):
    """Compare figure/digit substitutions against latest release."""
    digit_str = "0123456789"
    features = {feat: True for feat in features_on}

    for expected, actual in ((latest_otf, otf_font), (latest_ttf, ttf_font)):
        buf_expected = hb.Buffer()
        buf_expected.add_str(digit_str)
        buf_expected.guess_segment_properties()

        buf_actual = hb.Buffer()
        buf_actual.add_str(digit_str)
        buf_actual.guess_segment_properties()

        hb.shape(expected, buf_expected, features)
        infos_expected = buf_expected.glyph_infos

        hb.shape(actual, buf_actual, features)
        infos_actual = buf_actual.glyph_infos

        assert len(infos_expected) == len(infos_actual)

        for i in range(len(infos_expected)):
            gn_expected = expected.get_glyph_name(infos_expected[i].codepoint)
            gn_actual = actual.get_glyph_name(infos_actual[i].codepoint)

            assert gn_actual == gn_expected

            cl_expected = infos_expected[i].cluster
            cl_actual = infos_actual[i].cluster

            assert cl_actual == cl_expected
def test_kern_regression(latest_otf, otf_font, latest_ttf, ttf_font, string,
                         use_kerning):

    for expected, actual in ((latest_otf, otf_font), (latest_ttf, ttf_font)):
        features = {"kern": use_kerning}

        buf_expected = hb.Buffer()
        buf_expected.add_str(string)
        buf_expected.guess_segment_properties()

        buf_actual = hb.Buffer()
        buf_actual.add_str(string)
        buf_actual.guess_segment_properties()

        hb.shape(expected, buf_expected, features)
        infos_expected = buf_expected.glyph_infos
        positions_expected = buf_expected.glyph_positions

        hb.shape(actual, buf_actual, features)
        infos_actual = buf_actual.glyph_infos
        positions_actual = buf_actual.glyph_positions

        assert len(infos_expected) == len(infos_actual)

        for i in range(len(infos_expected)):
            gn_expected = expected.get_glyph_name(infos_expected[i].codepoint)
            gn_actual = actual.get_glyph_name(infos_actual[i].codepoint)

            assert gn_actual == gn_expected

            pos_expected = positions_expected[i].x_advance
            pos_actual = positions_actual[i].x_advance

            assert pos_actual == pos_expected
示例#15
0
    def test_message_func_return_false(self, blankfont):
        # Glyph IDs 1, 2, 3, 4, 5 map to glyphs a, b, c, d, e.
        # The calt feature replaces c by a in the context e, d, c', b, a.
        # The kern feature kerns b, a by +100.
        string = "edcba"
        buf = hb.Buffer()
        buf.add_str(string)
        buf.guess_segment_properties()

        messages = []
        infos_trace = []
        positions_trace = []

        def message(msg):
            messages.append(msg)
            infos_trace.append(buf.glyph_infos)
            positions_trace.append(buf.glyph_positions)
            return False

        buf.set_message_func(message)
        hb.shape(blankfont, buf)
        gids = [g.codepoint for g in buf.glyph_infos]
        assert gids == [5, 4, 3, 2, 1]
        pos = [g.x_advance for g in buf.glyph_positions]
        assert pos == [0, 0, 0, 0, 0]
        expected_messages = [
            'start table GSUB',
            'start table GPOS',
        ]
        assert messages == expected_messages
        gids_trace = [[g.codepoint for g in infos] for infos in infos_trace]
        assert gids_trace == [[5, 4, 3, 2, 1], [5, 4, 3, 2, 1]]
        advances_trace = [[g.x_advance for g in pos] for pos in positions_trace
                          if pos]
        assert advances_trace == [[0, 0, 0, 0, 0]]
示例#16
0
    def __isEmojiSupportedByFont(self, emoji: Emoji) -> bool:
        # Load font (has to be done for call):
        face = Face(self.fontdata)
        font = Font(face)
        upem = face.upem
        font.scale = (upem, upem)
        ot_font_set_funcs(font)

        # Create text buffer:
        buf = Buffer()
        buf.add_str(emoji.emoji)
        buf.guess_segment_properties()

        # Shape text:
        features = {"kern": True, "liga": True}
        shape(font, buf, features)
        infos = buf.glyph_infos

        # Remove all variant selectors:
        while len(infos) > 0 and infos[-1].codepoint == 3:
            infos = infos[:-1]

        # Filter empty:
        if len(infos) <= 0:
            return False

        # Remove uncombined ending with skin tone like "👭🏿":
        lastCp = infos[-1].codepoint
        if lastCp == 1076 or lastCp == 1079 or lastCp == 1082 or lastCp == 1085 or lastCp == 1088:
            return False
            
        # If there is a code point 0 => Emoji not fully supported by font:
        return all(info.codepoint != 0 and info.codepoint != 3 for info in infos)
示例#17
0
 def test_message_func_crash(self, blankfont):
     string = "edcba"
     buf = hb.Buffer()
     buf.add_str(string)
     buf.guess_segment_properties()
     message_collector = MessageCollector()
     buf.set_message_func(message_collector.message)
     hb.shape(blankfont, buf)
示例#18
0
    def test_features_slice(self, blankfont, string, features, expected):
        buf = hb.Buffer()
        buf.add_str(string)
        buf.guess_segment_properties()
        hb.shape(blankfont, buf, features)

        glyph_names = [blankfont.glyph_to_string(g.codepoint) for g in buf.glyph_infos]
        assert glyph_names == expected
示例#19
0
 def shape(self, text, onchange=None):
     self.prepare_shaper()
     buf = hb.Buffer()
     buf.add_str(text)
     buf.guess_segment_properties()
     test = self.make_message_handling_function(buf, onchange)
     if onchange:
         buf.set_message_func(test)
     hb.shape(self.hbfont, buf)
示例#20
0
 def test_shape_set_shaper(self, blankfont):
     string = "abcde"
     expected = []
     buf = hb.Buffer()
     buf.add_str(string)
     buf.guess_segment_properties()
     hb.shape(blankfont, buf, shapers=["fallback"])
     pos = [g.position for g in buf.glyph_positions]
     expected = [(0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)]
     assert pos == expected
示例#21
0
 def _shape_string(self, font, string, ot_features):
     buf = hb.Buffer.create()
     buf.add_str(string)
     buf.guess_segment_properties()
     try:
         features = {f: True for f in ot_features}
         hb.shape(font.hbfont, buf, features)
     except KeyError:
         hb.shape(font.hbfont, buf)
     return buf
示例#22
0
 def set_text(self, text):
     newtrace = []
     self.clear()
     buf = hb.Buffer()
     buf.add_str(text)
     buf.guess_segment_properties()
     buf.set_message_func(self.process_msg)
     self.stack = [QTreeWidgetItem(["GSUB", ""])]
     self.addTopLevelItem(self.stack[0])
     hb.shape(self.font.vharfbuzz.hbfont, buf)
示例#23
0
    def shape(self, text, parameters=None, onchange=None):
        """Shapes a text

    This shapes a piece of text.

    Args:
        text (str): A string of text
        parameters: A dictionary containing parameters to pass to Harfbuzz.
            Relevant keys include ``script``, ``direction``, ``language``
            (these three are normally guessed from the string contents),
            ``features``, ``variations`` and ``shaper``.
        onchange: An optional function with three parameters. See below.

    Additionally, if an `onchange` function is provided, this will be called
    every time the buffer changes *during* shaping, with the following arguments:

    - ``self``: the vharfbuzz object.
    - ``stage``: either "GSUB" or "GPOS"
    - ``lookupid``: the current lookup ID
    - ``buffer``: a copy of the buffer as a list of lists (glyphname, cluster, position)

    Returns:
        A uharfbuzz ``hb.Buffer`` object
    """
        if not parameters:
            parameters = {}
        self.prepare_shaper()
        buf = hb.Buffer()
        buf.add_str(text)
        buf.guess_segment_properties()
        if "script" in parameters and parameters["script"]:
            buf.script = parameters["script"]
        if "direction" in parameters and parameters["direction"]:
            buf.direction = parameters["direction"]
        if "language" in parameters and parameters["language"]:
            buf.language = parameters["language"]
        shapers = self.shapers
        if "shaper" in parameters and parameters["shaper"]:
            shapers = [parameters["shaper"]]

        features = parameters.get("features")
        if "variations" in parameters:
            self.hbfont.set_variations(parameters["variations"])
        self.stage = "GSUB"
        if onchange:
            f = self.make_message_handling_function(buf, onchange)
            buf.set_message_func(f)
        hb.shape(self.hbfont, buf, features, shapers=shapers)
        self.stage = "GPOS"
        return buf
示例#24
0
 def test_glyh_name_no_features(self, blankfont, string, expected):
     buf = hb.Buffer()
     buf.add_str(string)
     buf.guess_segment_properties()
     hb.shape(blankfont, buf)
     # font.get_glyph_name() returns None if the font does not contain glyph names
     # or if the glyph ID does not exist.
     glyph_names = [blankfont.get_glyph_name(g.codepoint) for g in buf.glyph_infos]
     assert glyph_names == expected
     assert blankfont.get_glyph_name(1000) is None
     # font.glyph_to_string() return "gidN" if the font does not contain glyph names
     # or if the glyph ID does not exist.
     glyph_names = [blankfont.glyph_to_string(g.codepoint) for g in buf.glyph_infos]
     assert glyph_names == expected
     assert blankfont.glyph_to_string(1000) == 'gid1000'
示例#25
0
def emojiSupported(emoji: str, fontdata) -> bool:
    """
    This function checks for support for a given emoji
    in a font file, particularly the multi-byte ZWJ
    sequences.

    Many thanks to StackOverflow user COM8 for this code.
    https://stackoverflow.com/a/55560968/1174966
    """

    # Load font (has to be done for call):
    face = Face(fontdata)
    font = UFont(face)
    upem = face.upem
    font.scale = (upem, upem)
    ot_font_set_funcs(font)

    # Create text buffer:
    buf = Buffer()
    buf.add_str(emoji)
    buf.guess_segment_properties()

    # Shape text:
    features = {"kern": True, "liga": True}
    shape(font, buf, features)

    infos = buf.glyph_infos

    # Remove all variant selectors:
    while len(infos) > 0 and infos[-1].codepoint == 3:
        infos = infos[:-1]

    # Filter empty:
    if len(infos) <= 0:
        return False

    # Remove uncombined, ending with skin tone like "👭�":
    lastCp = infos[-1].codepoint

    print(lastCp)

    badCp = [1076, 1079, 1082, 1085, 1088]

    if lastCp in badCp:
        return False

    # If there is a code point 0 or 3 => Emoji not fully supported by font:
    return all(info.codepoint != 0 and info.codepoint != 3 for info in infos)
示例#26
0
    def test_glyph_h_advance_func(self, blankfont):
        string = "abcde"
        expected = [456, 456, 456, 456, 456]
        buf = hb.Buffer()
        buf.add_str(string)
        buf.guess_segment_properties()

        def h_advance_func(font, gid, data):
            return 456

        funcs = hb.FontFuncs.create()
        funcs.set_glyph_h_advance_func(h_advance_func, None)
        blankfont.funcs = funcs

        hb.shape(blankfont, buf)
        infos = [pos.x_advance for pos in buf.glyph_positions]
        assert infos == expected
示例#27
0
    def test_nominal_glyph_func(self, blankfont):
        string = "abcde"
        expected = [97, 98, 99, 100, 101]
        buf = hb.Buffer()
        buf.add_str(string)
        buf.guess_segment_properties()

        def nominal_glyph_func(font, code_point, data):
            return code_point

        funcs = hb.FontFuncs.create()
        funcs.set_nominal_glyph_func(nominal_glyph_func, None)
        blankfont.funcs = funcs

        hb.shape(blankfont, buf)
        infos = [g.codepoint for g in buf.glyph_infos]
        assert infos == expected
示例#28
0
 async def shape(self, text):
     if not text:
         return ShapeResult()
     buffer = hb.Buffer()
     buffer.add_str(text)
     font = self.font
     features = self.features_dict
     if font.is_vertical:
         buffer.direction = 'ttb'
         assert features and features['vert']
     else:
         buffer.direction = 'ltr'
         assert not features or not features.get('vert')
     if self.language:
         buffer.language = f'x-hbot{self.language}'
         # buffer.set_language_from_ot_tag(self.language)
     if self.script:
         buffer.script = self.script
         # buffer.set_script_from_ot_tag(self.script)
     logger.debug('%s lang=%s script=%s features=%s',
                  ' '.join(f'U+{ord(ch):04X}' for ch in text),
                  self.language, self.script, features)
     # logger.debug('lang=%s, script=%s, features=%s', buffer.language,
     #              buffer.script, features)
     if utils._log_shaper_logs:
         buffer.set_message_func(
             lambda message: logger.debug('uharfbuzz: %s', message))
     # buffer.cluster_level = hb.BufferClusterLevel.DEFAULT
     # buffer.guess_segment_properties()
     hb.shape(font.hbfont, buffer, features, self._shapers)
     infos = buffer.glyph_infos
     positions = buffer.glyph_positions
     assert len(infos) == len(positions)
     if font.is_vertical:
         glyphs = (GlyphData(info.codepoint, info.cluster, -pos.y_advance,
                             -pos.y_offset)
                   for info, pos in zip(infos, positions))
     else:
         glyphs = (GlyphData(info.codepoint, info.cluster, pos.x_advance,
                             pos.x_offset)
                   for info, pos in zip(infos, positions))
     result = ShapeResult(glyphs)
     self._log_result(result, text)
     return result
示例#29
0
    def shape(self,
              text,
              *,
              features=None,
              varLocation=None,
              direction=None,
              language=None,
              script=None):
        if features is None:
            features = {}
        if varLocation is None:
            varLocation = {}

        self.font.set_variations(varLocation)

        if self._funcs is not None:
            self.font.funcs = self._funcs

        buf = hb.Buffer.create()
        buf.add_str(str(text))  # add_str() does not accept str subclasses
        buf.guess_segment_properties()
        msgfunc, history = self.buildMessageHistoryFunction(buf)
        buf.set_message_func(msgfunc)

        buf.cluster_level = hb.BufferClusterLevel.MONOTONE_CHARACTERS

        if direction is not None:
            buf.direction = direction
        if language is not None:
            buf.set_language_from_ot_tag(language)
        if script is not None:
            buf.set_script_from_ot_tag(script)

        hb.shape(self.font, buf, features)

        glyphOrder = self.glyphOrder
        infos = []
        for info, pos in zip(buf.glyph_infos, buf.glyph_positions):
            infos.append(
                GlyphInfo(info.codepoint, glyphOrder[info.codepoint],
                          info.cluster, *pos.position))

        return infos, history
示例#30
0
def pair_kerning(font, left, right):
    """The kerning between two glyphs (specified by name), in font units."""
    with open(font, "rb") as fontfile:
        fontdata = fontfile.read()
    face = hb.Face(fontdata)
    font = hb.Font(face)
    scale = face.upem
    font.scale = (scale, scale)
    buf = hb.Buffer()
    buf.add_str(left + right)
    buf.guess_segment_properties()
    hb.shape(font, buf, {"kern": True})
    pos = buf.glyph_positions[0].x_advance
    buf = hb.Buffer()
    buf.add_str(left + right)
    buf.guess_segment_properties()
    hb.shape(font, buf, {"kern": False})
    pos2 = buf.glyph_positions[0].x_advance
    return pos - pos2