def setup_harfbuzz() -> Tuple[HarfBuzz.font_t, HarfBuzz.buffer_t]: """ Finds a font for HarfBuzz to use, and creates a buffer Because this is wrapped with functools.lru_cache, the buffer returned may have text still in it, which means gi.repository.HarfBuzz.buffer_clear_contents() will need to be called on the buffer to return it to its empty state """ font_file = get_font_file() font_blob = HarfBuzz.glib_blob_create( GLib.Bytes.new(font_file.read_bytes())) face = HarfBuzz.face_create(font_blob, 0) del font_blob font = HarfBuzz.font_create(face) upem = HarfBuzz.face_get_upem(face) del face HarfBuzz.font_set_scale(font, 100, 100) HarfBuzz.ot_font_set_funcs(font) buffer = HarfBuzz.buffer_create() # HarfBuzz.buffer_set_message_func( # buffer, # lambda *args: True, # 1, # 0, # ) return (font, upem, buffer)
def buildCompatChars(sfd, ttf): zwj = u'\u200D' ranges = ( (0xfb50, 0xfbb1), (0xfbd3, 0xfd3d), (0xfd50, 0xfdf9), (0xfdfc, 0xfdfc), (0xfe70, 0xfefc), ) with open(ttf, "rb") as f: data = f.read() blob = HarfBuzz.glib_blob_create(GLib.Bytes.new(data)) face = HarfBuzz.face_create(blob, 0) hbfont = HarfBuzz.font_create(face) upem = HarfBuzz.face_get_upem(face) HarfBuzz.font_set_scale(hbfont, upem, upem) HarfBuzz.ot_font_set_funcs(hbfont) ttfont = TTFont(ttf) for r in ranges: for c in range(r[0], r[1] + 1): dec = ucd.decomposition(unichr(c)).split() if dec: keyword = dec[0] text = u'' for i in dec[1:]: text += unichr(int(str(i), 16)) if keyword == '<initial>': text = text + zwj elif keyword == '<final>': text = zwj + text elif keyword == '<medial>': text = zwj + text + zwj components = shape(text, hbfont) if components: glyph = sfd.createChar(c) glyph.clear() glyph.color = 0xff0000 # red color x = 0 for component in components: gid = component[0] name = ttfont.getGlyphName(gid) x_advance = component[1] x_offset = component[2] y_offset = component[3] matrix = psMat.translate(x + x_offset, y_offset) # ignore blank glyphs, e.g. space or ZWJ if sfd[name].foreground or sfd[name].references: glyph.addReference(name, matrix) x += x_advance glyph.width = x
def buildCompatChars(sfd, ttf): zwj = u'\u200D' ranges = ( (0xfb50, 0xfbb1), (0xfbd3, 0xfd3d), (0xfd50, 0xfdf9), (0xfdfc, 0xfdfc), (0xfe70, 0xfefc), ) with open(ttf, "rb") as f: data = f.read() blob = HarfBuzz.glib_blob_create(GLib.Bytes.new(data)) face = HarfBuzz.face_create(blob, 0) hbfont = HarfBuzz.font_create(face) upem = HarfBuzz.face_get_upem(face) HarfBuzz.font_set_scale(hbfont, upem, upem) HarfBuzz.ot_font_set_funcs(hbfont) ttfont = TTFont(ttf) for r in ranges: for c in range(r[0], r[1]+1): dec = ucd.decomposition(unichr(c)).split() if dec: keyword = dec[0] text = u'' for i in dec[1:]: text += unichr(int(str(i),16)) if keyword == '<initial>': text = text + zwj elif keyword == '<final>': text = zwj + text elif keyword == '<medial>': text = zwj + text + zwj components = shape(text, hbfont) if components: glyph = sfd.createChar(c) glyph.clear() glyph.color = 0xff0000 # red color x = 0 for component in components: gid = component[0] name = ttfont.getGlyphName(gid) x_advance = component[1] x_offset = component[2] y_offset = component[3] matrix = psMat.translate(x + x_offset, y_offset) # ignore blank glyphs, e.g. space or ZWJ if sfd[name].foreground or sfd[name].references: glyph.addReference(name, matrix) x += x_advance glyph.width = x
def character_index(self, character): try: return self._ordinals[character] except KeyError: self._ordinals[character] = i = hb.font_get_glyph( self._hb_font, ord(character), 0)[1] return i
def ot_feature(bytename, value): b, O = hb.feature_from_string(bytename) if b: O.value = value return O else: raise ValueError('invalid opentype feature name ' + repr(str(bytename)))
def advance_pixel_width(self, character): try: return self._widths[character] except KeyError: self._widths[character] = p = hb.font_get_glyph_h_advance( self._hb_font, self.character_index(character)) * self._upem_fac return p
def getHbFont(fontname): font = open(fontname, "rb") data = font.read() font.close() blob = HarfBuzz.glib_blob_create(GLib.Bytes.new(data)) face = HarfBuzz.face_create(blob, 0) font = HarfBuzz.font_create(face) upem = HarfBuzz.face_get_upem(face) HarfBuzz.font_set_scale(font, upem, upem) HarfBuzz.ot_font_set_funcs(font) return font
def runTest(tests, refs, fontname): failed = {} passed = [] font = getHbFont(fontname) buf = HarfBuzz.buffer_create() ttfont = TTFont(fontname) for i, (text, ref) in enumerate(zip(tests, refs)): result = runHB(text, buf, font, ttfont) if ref == result: passed.append(i + 1) else: failed[i + 1] = (text, ref, result) return passed, failed
def __init__(self, font_path): try: assert os.path.isfile(font_path) font_file = open(font_path, "br") fontdata = font_file.read() blob = hb.glib_blob_create(GLib.Bytes.new(fontdata)) del fontdata face = hb.face_create(blob, 0) del blob self.__font = hb.font_create(face) upem = hb.face_get_upem(face) del face font_file.close() hb.font_set_scale(self.__font, upem, upem) #hb.ft_font_set_funcs (font) hb.ot_font_set_funcs(self.__font) except Exception: print(font_path)
def get_ot_font(path, overwrite=False): if path not in _ot_type_registry or overwrite: # check if is sfd or binary fontname, ext = os.path.splitext(path) if ext == '.sfd': print('Warning: implicit OTF build triggered') os.system('fontforge -script IO/sfd2otf.pe ' + path) filepath = fontname + '.otf' else: filepath = path print('\033[92mLoading font\033[0m :', filepath) HB_face = _hb_face_from_path(filepath) HB_font = hb.font_create(HB_face) upem = hb.face_get_upem(HB_face) hb.font_set_scale(HB_font, upem, upem) hb.ot_font_set_funcs(HB_font) CR_face = fontloader.create_cairo_font_face_for_file(filepath) _ot_type_registry[path] = upem, HB_face, HB_font, CR_face return _ot_type_registry[path]
def _hb_get_advance(hb_font, char): return hb.font_get_glyph_h_advance( hb_font, hb.font_get_glyph(hb_font, ord(char), 0)[1])
def _hb_face_from_path(filepath): with open(filepath, 'rb') as fi: fontdata = fi.read() return hb.face_create(hb.glib_blob_create(Bytes.new(fontdata)), 0)
def glyph_metrics(string: str) -> Iterable[Metrics]: font, upem, buffer = setup_harfbuzz() HarfBuzz.buffer_clear_contents(buffer) if False: HarfBuzz.buffer_add_utf8(buffer, string.encode("utf-8"), 0, -1) elif sys.maxunicode == 0x10FFFF: HarfBuzz.buffer_add_utf32( buffer, array.array("I", string.encode("utf-32"))[1:], 0, -1) else: HarfBuzz.buffer_add_utf16( buffer, array.array("H", string.encode("utf-16"))[1:], 0, -1) # If this doesn't get run, the Python interpreter crashes HarfBuzz.buffer_guess_segment_properties(buffer) HarfBuzz.shape(font, buffer, []) codepoints = [ info.codepoint for info in HarfBuzz.buffer_get_glyph_infos(buffer) ] positions = HarfBuzz.buffer_get_glyph_positions(buffer) for extents, pos in zip(glyphs_extents(font, codepoints), positions): yield Metrics( Positions(pos.x_advance, pos.y_advance, pos.x_offset, pos.y_offset), extents, upem, )
def glyphs_extents(font: HarfBuzz.font_t, codepoints: Iterable[int]) -> Iterable[Extents]: for extent in (HarfBuzz.font_get_glyph_extents(font, codepoint)[1] for codepoint in codepoints): yield Extents(extent.x_bearing, extent.y_bearing, extent.width, extent.height)
def runHB(text, buf, font, ttfont): HarfBuzz.buffer_clear_contents(buf) HarfBuzz.buffer_add_utf8(buf, text.encode('utf-8'), 0, -1) HarfBuzz.buffer_set_direction(buf, HarfBuzz.direction_t.RTL) HarfBuzz.buffer_set_script(buf, HarfBuzz.script_t.ARABIC) HarfBuzz.buffer_set_language(buf, HarfBuzz.language_from_string(b"ar")) HarfBuzz.shape(font, buf, []) info = HarfBuzz.buffer_get_glyph_infos(buf) positions = HarfBuzz.buffer_get_glyph_positions(buf) out = [] for i, p in zip(info, positions): text = "" text += ttfont.getGlyphName(i.codepoint) text += " w=%d" % p.x_advance if p.x_offset: text += " x=%d" % p.x_offset if p.y_offset: text += " y=%d" % p.y_offset out.append(text) return "[%s]" % "|".join(out)
def shape(text, font): text = toUnicode(text) buf = HarfBuzz.buffer_create() HarfBuzz.buffer_add_utf8(buf, text.encode('utf-8'), 0, -1) HarfBuzz.buffer_set_direction(buf, HarfBuzz.direction_t.RTL) HarfBuzz.buffer_set_script(buf, HarfBuzz.script_t.ARABIC) HarfBuzz.buffer_set_language(buf, HarfBuzz.language_from_string("ar")) HarfBuzz.shape(font, buf, [HarfBuzz.feature_from_string("+ss01")[1]]) glyphs = HarfBuzz.buffer_get_glyph_infos(buf) positions = HarfBuzz.buffer_get_glyph_positions(buf) return [(g.codepoint, p.x_advance, p.x_offset, p.y_offset) for g, p in zip(glyphs, positions)]
def character_index(self, character): try: return self._ordinals[character] except KeyError: self._ordinals[character] = i = hb.font_get_glyph(self._hb_font, ord(character), 0)[1] return i
def advance_pixel_width(self, character): try: return self._widths[character] except KeyError: self._widths[character] = p = hb.font_get_glyph_h_advance(self._hb_font, self.character_index(character)) * self._upem_fac return p
def _hb_get_advance(hb_font, char): return hb.font_get_glyph_h_advance(hb_font, hb.font_get_glyph(hb_font, ord(char), 0)[1])
try: unicode except NameError: unicode = str def tounicode(s, encoding='utf-8'): if not isinstance(s, unicode): return s.decode(encoding) else: return s fontdata = open (sys.argv[1], 'rb').read () text = tounicode(sys.argv[2]) # Need to create GLib.Bytes explicitly until this bug is fixed: # https://bugzilla.gnome.org/show_bug.cgi?id=729541 blob = hb.glib_blob_create (GLib.Bytes.new (fontdata)) face = hb.face_create (blob, 0) del blob font = hb.font_create (face) upem = hb.face_get_upem (face) del face hb.font_set_scale (font, upem, upem) #hb.ft_font_set_funcs (font) hb.ot_font_set_funcs (font) buf = hb.buffer_create () class Debugger(object): def message (self, buf, font, msg, data, _x_what_is_this): print(msg) return True debugger = Debugger()
def runHB(text, buf, font, ttfont): HarfBuzz.buffer_clear_contents(buf) HarfBuzz.buffer_add_utf8(buf, text.encode('utf-8'), 0, -1) HarfBuzz.buffer_set_direction(buf, HarfBuzz.direction_t.RTL) HarfBuzz.buffer_set_script(buf, HarfBuzz.script_t.ARABIC) HarfBuzz.buffer_set_language(buf, HarfBuzz.language_from_string(b"ar")) HarfBuzz.shape(font, buf, []) info = HarfBuzz.buffer_get_glyph_infos(buf) out = "|".join([ttfont.getGlyphName(i.codepoint) for i in info]) return "[%s]" % out
except NameError: unicode = str def tounicode(s, encoding='utf-8'): if not isinstance(s, unicode): return s.decode(encoding) else: return s fontdata = open(sys.argv[1], 'rb').read() text = tounicode(sys.argv[2]) # Need to create GLib.Bytes explicitly until this bug is fixed: # https://bugzilla.gnome.org/show_bug.cgi?id=729541 blob = hb.glib_blob_create(GLib.Bytes.new(fontdata)) face = hb.face_create(blob, 0) del blob font = hb.font_create(face) upem = hb.face_get_upem(face) del face hb.font_set_scale(font, upem, upem) #hb.ft_font_set_funcs (font) hb.ot_font_set_funcs(font) buf = hb.buffer_create() hb.buffer_add_utf8(buf, text.encode('utf-8'), 0, -1) hb.buffer_guess_segment_properties(buf) hb.shape(font, buf, []) del font
def get_text_shape(text, font=None, weight=None, path=None, debug=False): if weight is not None: font = f'{font}:{weight}' if path is None: path = get_font_path(font) base, ext = os.path.splitext(path) ext = ext[1:] with open(path, 'rb') as fid: fontdata = fid.read() bdata = GLib.Bytes.new(fontdata) blob = hb.glib_blob_create(bdata) face = hb.face_create(blob, 0) font = hb.font_create(face) upem = hb.face_get_upem(face) hb.font_set_scale(font, upem, upem) # hb.font_set_ptem(font, font_size) if ext == 'woff': hb.ft_font_set_funcs(font) buf = hb.buffer_create() hb.buffer_add_utf8(buf, text.encode('utf-8'), 0, -1) hb.buffer_guess_segment_properties(buf) hb.shape(font, buf, []) infos = hb.buffer_get_glyph_infos(buf) positions = hb.buffer_get_glyph_positions(buf) extents = [hb.font_get_glyph_extents(font, i.codepoint) for i in infos] if debug: return font, infos, positions, extents norm = upem wh_extract = lambda ext: (ext.extents.width / norm, -ext.extents.height / norm) cluster = [(i.codepoint, i.cluster) for i in infos] shapes = [wh_extract(e) for e in extents] deltas = [(p.x_advance / norm, p.y_advance / norm) for p in positions] offsets = [(p.x_offset / norm, p.y_offset / norm) for p in positions] return cluster, shapes, deltas, offsets
try: unicode except NameError: unicode = str def tounicode(s, encoding='utf-8'): if not isinstance(s, unicode): return s.decode(encoding) else: return s fontdata = open (sys.argv[1], 'rb').read () text = tounicode(sys.argv[2]) # Need to create GLib.Bytes explicitly until this bug is fixed: # https://bugzilla.gnome.org/show_bug.cgi?id=729541 blob = hb.glib_blob_create (GLib.Bytes.new (fontdata)) face = hb.face_create (blob, 0) del blob font = hb.font_create (face) upem = hb.face_get_upem (face) del face hb.font_set_scale (font, upem, upem) #hb.ft_font_set_funcs (font) hb.ot_font_set_funcs (font) buf = hb.buffer_create () hb.buffer_add_utf8 (buf, text.encode('utf-8'), 0, -1) hb.buffer_guess_segment_properties (buf) hb.shape (font, buf, []) del font
def shape(self, text_="abvd", flag=False): text = text_ # Need to create GLib.Bytes explicitly until this bug is fixed: # https://bugzilla.gnome.org/show_bug.cgi?id=729541 buf = hb.buffer_create() # class Debugger(object): # def message(self, buf, font, msg, data, _x_what_is_this): # print(msg) # return True # debugger = Debugger() # hb.buffer_set_message_func (buf, debugger.message, 1, 0) ## ## Add text to buffer ## # # See https://github.com/harfbuzz/harfbuzz/pull/271 # if flag: # If you do not care about cluster values reflecting Python # string indices, then this is quickest way to add text to # buffer: # void hb_buffer_add_utf8 (hb_buffer_t *buffer, # const char *text, # int text_length, # unsigned int item_offset, # int item_length); hb.buffer_add_utf8(buf, text.encode('utf-8'), 0, -1) # Otherwise, then following handles both narrow and wide # Python builds: elif sys.maxunicode == 0x10FFFF: hb.buffer_add_utf32(buf, array.array('I', text.encode('utf-32le')), 0, -1) else: hb.buffer_add_utf16(buf, array.array('H', text.encode('utf-16le')), 0, -1) hb.buffer_guess_segment_properties(buf) hb.shape(self.__font, buf, []) # del font infos = hb.buffer_get_glyph_infos(buf) # positions = hb.buffer_get_glyph_positions(buf) return [info.codepoint for info in infos]
#!/usr/bin/python import sys from gi.repository import HarfBuzz as hb def nothing(): pass fontdata = file(sys.argv[1]).read() blob = hb.blob_create(fontdata, hb.memory_mode_t.WRITABLE, None, nothing) print blob buffer = hb.buffer_create()
#!/usr/bin/python import sys from gi.repository import HarfBuzz as hb def nothing(): pass fontdata = file (sys.argv[1]).read () blob = hb.blob_create (fontdata, hb.memory_mode_t.WRITABLE, None, nothing) print blob buffer = hb.buffer_create ()
def runHB(direction, script, language, features, text, fontname, positions): font = getHbFont(fontname) buf = HarfBuzz.buffer_create() text = toUnicode(text) HarfBuzz.buffer_add_utf8(buf, text.encode('utf-8'), 0, -1) HarfBuzz.buffer_set_direction(buf, HarfBuzz.direction_from_string(toBytes(direction))) HarfBuzz.buffer_set_script(buf, HarfBuzz.script_from_string(toBytes(script))) if language: HarfBuzz.buffer_set_language(buf, HarfBuzz.language_from_string(toBytes(language))) if features: features = [HarfBuzz.feature_from_string(toBytes(fea))[1] for fea in features.split(',')] else: features = [] HarfBuzz.shape(font, buf, features) info = HarfBuzz.buffer_get_glyph_infos(buf) ttfont = getTtFont(fontname) if positions: pos = HarfBuzz.buffer_get_glyph_positions(buf) glyphs = [] for i, p in zip(info, pos): glyph = ttfont.getGlyphName(i.codepoint) if p.x_offset or p.y_offset: glyph += "@%d,%d" % (p.x_offset, p.y_offset) glyph += "+%d" % p.x_advance if p.y_advance: glyph += ",%d" % p.y_advance glyphs.append(glyph) out = "|".join(glyphs) else: out = "|".join([ttfont.getGlyphName(i.codepoint) for i in info]) return "[%s]" % out
unicode except NameError: unicode = str def tounicode(s, encoding='utf-8'): if not isinstance(s, unicode): return s.decode(encoding) else: return s fontdata = open (sys.argv[1], 'rb').read () text = tounicode(sys.argv[2]) codepoints = list(map(ord, text)) # Need to create GLib.Bytes explicitly until this bug is fixed: # https://bugzilla.gnome.org/show_bug.cgi?id=729541 blob = hb.glib_blob_create (GLib.Bytes.new (fontdata)) face = hb.face_create (blob, 0) del blob font = hb.font_create (face) upem = hb.face_get_upem (face) del face hb.font_set_scale (font, upem, upem) #hb.ft_font_set_funcs (font) hb.ot_font_set_funcs (font) buf = hb.buffer_create () class Debugger(object): def message (self, buf, font, msg, data, _x_what_is_this): print(msg) return True debugger = Debugger()
# Python 2/3 compatibility try: unicode except NameError: unicode = str def tounicode(s, encoding='utf-8'): if not isinstance(s, unicode): return s.decode(encoding) else: return s if (hb.version_atleast(1, 9, 0)): pass else: raise RuntimeError('HarfBuzz too old') fontdata = open(sys.argv[1], 'rb').read() text = tounicode(sys.argv[2]) # Need to create GLib.Bytes explicitly until this bug is fixed: # https://bugzilla.gnome.org/show_bug.cgi?id=729541 blob = hb.glib_blob_create(GLib.Bytes.new(fontdata)) face = hb.face_create(blob, 0) del blob font = hb.font_create(face) upem = hb.face_get_upem(face) del face hb.font_set_scale(font, upem, upem)
from gi.repository import HarfBuzz as hb from gi.repository import GLib # Python 2/3 compatibility try: unicode except NameError: unicode = str def tounicode(s, encoding='utf-8'): if not isinstance(s, unicode): return s.decode(encoding) else: return s if (hb.version_atleast(2,1,2)): pass else: raise RuntimeError('HarfBuzz too old') ##################################################################### ### Derived from harfbuzz:src/sample.py fontdata = open (sys.argv[1], 'rb').read () text = tounicode(sys.argv[2]) # Need to create GLib.Bytes explicitly until this bug is fixed: # https://bugzilla.gnome.org/show_bug.cgi?id=729541 blob = hb.glib_blob_create (GLib.Bytes.new (fontdata)) face = hb.face_create (blob, 0) del blob font = hb.font_create (face)
def main(): font_file = barcode_wheel.get_font_file() font_blob = HarfBuzz.glib_blob_create(GLib.Bytes.new(font_file.read_bytes())) face = HarfBuzz.face_create(font_blob, 0) del font_blob font = HarfBuzz.font_create(face) upem = HarfBuzz.face_get_upem(face) del face HarfBuzz.font_set_scale(font, upem, upem) text = sys.argv[1] HarfBuzz.ot_font_set_funcs(font) buf = HarfBuzz.buffer_create() HarfBuzz.buffer_set_message_func( buf, # lambda *args: [print(x) for x in args] and True, lambda *args: True, 1, 0, ) if False: HarfBuzz.buffer_add_utf8(buf, text.encode("utf-8"), 0, -1) elif sys.maxunicode == 0x10FFFF: HarfBuzz.buffer_add_utf32( buf, array.array("I", text.encode("utf-32"))[1:], 0, -1 ) else: HarfBuzz.buffer_add_utf16( buf, array.array("H", text.encode("utf-16"))[1:], 0, -1 ) HarfBuzz.buffer_guess_segment_properties(buf) HarfBuzz.shape(font, buf, []) del font infos = HarfBuzz.buffer_get_glyph_infos(buf) positions = HarfBuzz.buffer_get_glyph_positions(buf) for info, pos in zip(infos, positions): gid = info.codepoint cluster = info.cluster print(f"gid {gid} = {cluster} @ {pos.x_advance}, {pos.x_offset}+{pos.y_offset}")
except NameError: unicode = str def tounicode(s, encoding='utf-8'): if not isinstance(s, unicode): return s.decode(encoding) else: return s fontdata = open(sys.argv[1], 'rb').read() text = tounicode(sys.argv[2]) # Need to create GLib.Bytes explicitly until this bug is fixed: # https://bugzilla.gnome.org/show_bug.cgi?id=729541 blob = hb.glib_blob_create(GLib.Bytes.new(fontdata)) face = hb.face_create(blob, 0) del blob font = hb.font_create(face) upem = hb.face_get_upem(face) del face hb.font_set_scale(font, upem, upem) #hb.ft_font_set_funcs (font) hb.ot_font_set_funcs(font) buf = hb.buffer_create() class Debugger(object): def message(self, buf, font, msg, data, _x_what_is_this): print(msg)