示例#1
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)
示例#2
0
 def prepare_shaper(self):
     face = hb.Face(self.fontdata)
     font = hb.Font(face)
     upem = face.upem
     font.scale = (upem, upem)
     hb.ot_font_set_funcs(font)
     self.hbfont = font
示例#3
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)
示例#4
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
示例#5
0
def ttf_font(ttf_path):
    with open(ttf_path, 'rb') as font_file:
        font_data = font_file.read()
    face = hb.Face(font_data)
    font = hb.Font(face)
    upem = face.upem
    font.scale = (upem, upem)
    hb.ot_font_set_funcs(font)

    return font
示例#6
0
def latest_ttf(latest_ttf_path):
    """HB Font from latest TTF"""

    with open(latest_ttf_path, 'rb') as font_file:
        font_data = font_file.read()
    face = hb.Face(font_data)
    font = hb.Font(face)
    upem = face.upem
    font.scale = (upem, upem)
    hb.ot_font_set_funcs(font)

    return font
示例#7
0
def opensans():
    """Return a subset of OpenSans.ttf containing the following glyphs/characters:
    [
        {gid=0, name=".notdef"},
        {gid=1, name="A", code=0x41},
    ]
    """
    face = hb.Face(OPEN_SANS_TTF_PATH.read_bytes())
    font = hb.Font(face)
    upem = face.upem
    font.scale = (upem, upem)
    hb.ot_font_set_funcs(font)
    return font
示例#8
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)
示例#9
0
def get_mhbfont( ctx, path ):
  import uharfbuzz  as HB ### NOTE import after ctx available ###
  cache = _get_cache( ctx )
  R     = cache.fonts.get( path, None )
  if R != None:
    # ctx.log( '^myharfbuzz/get_mhbfont@334^', "font cached: {}".format( path ) )
    return R
  #.........................................................................................................
  ctx.log( '^myharfbuzz/get_mhbfont@335^', "reading font: {}".format( path ) )
  with open( path, 'rb' ) as fontfile: fontdata = fontfile.read()
  R             = ctx.AttributeDict()
  R.face        = HB.Face( fontdata )
  R.font        = HB.Font( R.face   )
  R.upem        = R.face.upem
  R.font.scale  = ( R.upem, R.upem, )
  HB.ot_font_set_funcs( R.font )
  #.........................................................................................................
  cache.fonts[ path ] = R
  return R
示例#10
0
def blankfont():
    """Return a subset of AdobeBlank.ttf containing the following glyphs/characters:
    [
        {gid=0, name=".notdef"},
        {gid=1, name="a", code=0x61},
        {gid=2, name="b", code=0x62},
        {gid=3, name="c", code=0x63},
        {gid=4, name="d", code=0x64},
        {gid=5, name="e", code=0x65},
        {gid=6, name="ccedilla", code=0x62},
        {gid=7, name="uni0431", code=0x0431},  # CYRILLIC SMALL LETTER BE
        {gid=8, name="u1F4A9", code=0x1F4A9},  # PILE OF POO
    ]
    """
    face = hb.Face(ADOBE_BLANK_TTF_PATH.read_bytes())
    font = hb.Font(face)
    upem = face.upem
    font.scale = (upem, upem)
    hb.ot_font_set_funcs(font)
    return font
示例#11
0
def shapeHB(text, font_name, font_size, features: Dict[str, bool] = None):
    font = pdfmetrics.getFont(font_name)
    if not isinstance(font, TTFont):
        # TODO make valid for all types of fonts
        raise RLKerningError("Not a TTF font")

    fontdata = font.face._ttf_data
    face = hb.Face(fontdata)
    font = hb.Font(face)

    # HB scales to integers in offset and advance so very big scale
    # will divide by SCALE_MULT to get the actual size in fractional points
    font.scale = (font_size * SCALE_MULT, font_size * SCALE_MULT)
    hb.ot_font_set_funcs(font)
    buf = hb.Buffer()
    buf.add_str(text)
    buf.guess_segment_properties()
    hb.shape(font, buf, features)

    return buf
def shaper(s, tf, size):
    face = hb.Face.create_for_tables(getSkiaFontTable, tf)
    font = hb.Font(face)
    upem = size * 64.0
    font.scale = (upem, upem)
    hb.ot_font_set_funcs(font)
    buf = hb.Buffer()

    buf.add_utf8(s.encode('utf-8'))
    # buf.add_str(s)
    buf.guess_segment_properties()

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

    infos = buf.glyph_infos
    positions = buf.glyph_positions
    glyphs = [info.codepoint for info in infos]
    positions = [(pos.x_advance / 64.0, pos.x_offset / 64.0,
                  pos.y_offset / 64.0) for pos in positions]
    return (glyphs, positions)
def emojiSupported(emoji: str, fontdata) -> bool:
    # Load font (has to be done for call):
    face = Face(fontdata)
    font = BuzzFont(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)
示例#14
0
    def _to_png(self,
                font,
                font_position=None,
                dst=None,
                limit=800,
                size=1500,
                tab_width=1500,
                padding_characters=""):
        """Use HB, FreeType and Cairo to produce a png for a table.

        Parameters
        ----------
        font: DFont
        font_position: str
            Label indicating which font has been used. 
        dst: str
            Path to output image. If no path is given, return in-memory 
        """
        # TODO (M Foley) better packaging for pycairo, freetype-py
        # and uharfbuzz.
        # Users should be able to pip install these bindings without needing
        # to install the correct libs.

        # A special mention to the individuals who maintain these packages. Using
        # these dependencies has sped up the process of creating diff images
        # significantly. It's an incredible age we live in.
        y_tab = int(1500 / 25)
        x_tab = int(tab_width / 64)
        width, height = 1024, 200

        cells_per_row = int((width - x_tab) / x_tab)
        # Compute height of image
        x, y, baseline = x_tab, 0, 0
        for idx, row in enumerate(self._data[:limit]):
            x += x_tab

            if idx % cells_per_row == 0:
                y += y_tab
                x = x_tab
        height += y
        height += 100

        # draw image
        Z = ImageSurface(FORMAT_ARGB32, width, height)
        ctx = Context(Z)
        ctx.rectangle(0, 0, width, height)
        ctx.set_source_rgb(1, 1, 1)
        ctx.fill()

        # label image
        ctx.set_font_size(30)
        ctx.set_source_rgb(0.5, 0.5, 0.5)
        ctx.move_to(x_tab, 50)
        ctx.show_text("{}: {}".format(self.table_name, len(self._data)))
        ctx.move_to(x_tab, 100)
        if font_position:
            ctx.show_text("Font Set: {}".format(font_position))
        if len(self._data) > limit:
            ctx.set_font_size(20)
            ctx.move_to(x_tab, 150)
            ctx.show_text(
                "Warning: {} different items. Only showing most serious {}".
                format(len(self._data), limit))

        hb.ot_font_set_funcs(font.hbfont)

        # Draw glyphs
        x, y, baseline = x_tab, 200, 0
        x_pos = x_tab
        y_pos = 200
        for idx, row in enumerate(self._data[:limit]):
            string = "{}{}{}".format(padding_characters, row['string'],
                                     padding_characters)
            buf = self._shape_string(font, string, row['features'])
            char_info = buf.glyph_infos
            char_pos = buf.glyph_positions
            for info, pos in zip(char_info, char_pos):
                gid = info.codepoint
                font.ftfont.load_glyph(gid, flags=6)
                bitmap = font.ftslot.bitmap

                if bitmap.width > 0:
                    ctx.set_source_rgb(0, 0, 0)
                    glyph_surface = _make_image_surface(
                        font.ftfont.glyph.bitmap, copy=False)
                    ctx.set_source_surface(
                        glyph_surface,
                        x_pos + font.ftslot.bitmap_left + (pos.x_offset / 64.),
                        y_pos - font.ftslot.bitmap_top - (pos.y_offset / 64.))
                    glyph_surface.flush()
                    ctx.paint()
                x_pos += (pos.x_advance) / 64.
                y_pos += (pos.y_advance) / 64.

            x_pos += x_tab - (x_pos % x_tab)
            if idx % cells_per_row == 0:
                # add label
                if font_position:
                    ctx.set_source_rgb(0.5, 0.5, 0.5)
                    ctx.set_font_size(10)
                    ctx.move_to(width - 20, y_pos)
                    ctx.rotate(1.5708)
                    ctx.show_text(font_position)
                    ctx.set_source_rgb(0, 0, 0)
                    ctx.rotate(-1.5708)
                # Start a new row
                y_pos += y_tab
                x_pos = x_tab
        Z.flush()
        if dst:
            Z.write_to_png(dst)
        else:
            img = StringIO()
            Z.write_to_png(img)
            return Image.open(img)
示例#15
0
def _shape(face,
           font,
           text,
           fontSize=None,
           startPos=(0, 0),
           startCluster=0,
           flippedCanvas=False,
           *,
           features=None,
           variations=None,
           direction=None,
           language=None,
           script=None):
    if features is None:
        features = {}
    if variations is None:
        variations = {}

    if fontSize is None:
        fontScaleX = fontScaleY = 1
    else:
        fontScaleX = fontScaleY = fontSize / face.upem
    if flippedCanvas:
        fontScaleY = -fontScaleY

    font.scale = (face.upem, face.upem)
    font.set_variations(variations)

    hb.ot_font_set_funcs(font)

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

    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(font, buf, features)

    gids = [info.codepoint for info in buf.glyph_infos]
    clusters = [info.cluster + startCluster for info in buf.glyph_infos]
    positions = []
    startPosX, startPosY = startPos
    x = y = 0
    for pos in buf.glyph_positions:
        dx, dy, ax, ay = pos.position
        positions.append((
            startPosX + (x + dx) * fontScaleX,
            startPosY + (y + dy) * fontScaleY,
        ))
        x += ax
        y += ay
    endPos = startPosX + x * fontScaleX, startPosY + y * fontScaleY
    return SimpleNamespace(
        gids=gids,
        clusters=clusters,
        positions=positions,
        endPos=endPos,
    )
示例#16
0
 def __init__(self, path: Path):
     self.path = path
     face = hb.Face(path.read_bytes())  # type: ignore
     self.font = hb.Font(face)  # type: ignore
     hb.ot_font_set_funcs(self.font)  # type: ignore