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 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
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 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 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]
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
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}")
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() hb.buffer_set_message_func(buf, debugger.message, 1, 0) ## ## Add text to buffer ## #
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() hb.buffer_set_message_func (buf, debugger.message, 1, 0) ## ## Add text to buffer ## # # See https://github.com/harfbuzz/harfbuzz/pull/271 # if False: # If you do not care about cluster values reflecting Python