def get_Bound_Glyph(abc): # Setting Starting position of the first glyph glyph_pos = Vector(0, 0) # Resetting the buffer buf.reset() # adding string to buffer buf.add_str(abc) # Figuring out segmentation properties buf.guess_segment_properties() # Gernerating Shapes for the text in buffer using the font hb.shape(hb_font, buf) # Getting glyphs out of buffer (list format) glyphs, end_glyph_pos = buf.get_glyphs(glyph_pos) # Creating fontface qah_face = qah.FontFace.create_for_ft_face(ft_face) glyph_extents = (qah.Context.create_for_dummy().set_font_face( qah_face).set_font_size(text_size).glyph_extents(glyphs)) # Getting the bound of the [glyphs] figure_bounds = math.ceil(glyph_extents.bounds) # Returning glyph and the figure bound return (figure_bounds, glyphs)
def runHB(font, buf, direction, script, language, features, text, positions): buf.clear_contents() buf.add_str(text) buf.direction = hb.direction_from_string(direction) buf.script = hb.script_from_string(script) if language: buf.language = hb.Language.from_string(language) if features: features = [hb.Feature.from_string(fea) for fea in features.split(',')] else: features = [] hb.shape(font, buf, features) info = buf.glyph_infos if positions: pos = buf.glyph_positions glyphs = [] x = 0 for i, p in zip(info, pos): glyph = font.get_glyph_name(i.codepoint) glyph += "@(%d,%d)" % (x + p.x_offset, p.y_offset) glyph += "+%d" % p.x_advance glyphs.append(glyph) x += p.x_advance out = "|".join(glyphs) else: out = "|".join([font.get_glyph_name(i.codepoint) for i in info]) return "[%s]" % out
def process(self, text): if not text: return [] if self._needsInternalUpdate: self._updateEngine() # TODO: reuse buffer? buf = hb.Buffer.create() if isinstance(text, list): unicodes = [] for name in text: # TODO: use a dict instead? gid = self._glyphOrder.index(name) unicodes.append(CH_GID_PREFIX + gid) buf.add_codepoints(unicodes, len(unicodes), 0, len(unicodes)) else: buf.add_str(text) buf.guess_segment_properties() hb.shape(self._hbFont, buf, list(self._fontFeatures.values())) glyphRecords = [] for info, pos in zip(buf.glyph_infos, buf.glyph_positions): glyphName = self._glyphOrder[info.codepoint] if glyphName not in self.font: continue record = GlyphRecord() record.glyph = self.font[glyphName] record.cluster = info.cluster record.xOffset = pos.x_offset record.yOffset = pos.y_offset record.xAdvance = pos.x_advance record.yAdvance = pos.y_advance glyphRecords.append(record) del buf return glyphRecords
def reshape(self, glyphs, variations): font = self.make_font(variations, self._font_funcs) buf = self.clear_buffer() codepoints = [g.index + GID_OFFSET for g in reversed(glyphs)] buf.add_codepoints(codepoints, len(codepoints), 0, len(codepoints)) hb.shape(font, buf) return buf.get_glyphs()[0]
def adjust_widths_by_letter(boxes): buf = hb.Buffer.create() buf.add_str(''.join(b.letter for b in boxes)) buf.guess_segment_properties() font_lib = ft.get_default_lib() face = font_lib.find_face('Arial') face.set_char_size(size=1, resolution=64) font = hb.Font.ft_create(face) hb.shape(font, buf) # at this point buf.glyph_positions has all the data we need for box, position in zip(boxes, buf.glyph_positions): box.w = position.x_advance
def adjust_widths_by_letter(boxes): """Takes a list of boxes as arguments, and uses harfbuzz to adjust the width of each box to match the harfbuzz text shaping.""" buf = hb.Buffer.create() buf.add_str(''.join(b.letter for b in boxes)) buf.guess_segment_properties() font_lib = ft.get_default_lib() face = font_lib.find_face('Arial') face.set_char_size(size=1, resolution=64) font = hb.Font.ft_create(face) hb.shape(font, buf) # at this point buf.glyph_positions has all the data we need for box, position in zip(boxes, buf.glyph_positions): box.w = position.x_advance
def shape_word(self, word): """ Shapes a single word and returns the corresponding box. To speed things a bit, we cache the shaped words. We assume all our text is in Arabic script and language. The direction is almost always right-to-left, (we are cheating a bit to avoid doing proper bidirectional text as it is largely superfluous for us here). """ assert word text = word if ord(word[0]) > Q_PUA: text = word[1:] if text not in self.doc.word_cache: self.buffer.clear_contents() self.buffer.add_str(text) # Everything is RTL except aya numbers and other digits-only words. if text[0] in ("\u06DD", "(") or text.isdigit(): self.buffer.direction = hb.HARFBUZZ.DIRECTION_LTR else: self.buffer.direction = hb.HARFBUZZ.DIRECTION_RTL self.buffer.script = hb.HARFBUZZ.SCRIPT_ARABIC self.buffer.language = hb.Language.from_string("ar") self.buffer.cluster_level = ( hb.HARFBUZZ.BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) hb.shape(self.font, self.buffer) self.doc.word_cache[text] = Word(text, self.buffer) box = Box(self.doc, self.doc.word_cache[text]) # Flag boxes with “quarter” symbol, as it needs some special # handling later. if ord(word[0]) > Q_PUA: box.quarter = ord(word[0]) - Q_PUA if word.startswith(P_STR): box.prostration = True return box
def render_text(context, config, style, layer, text, center, rotation=None): if style.description_font is None: return font = config.fonts[style.description_font] context.save() buffer = harfbuzz.Buffer.create() buffer.add_str(text) buffer.guess_segment_properties() features = ( harfbuzz.Feature(tag=harfbuzz.HB.TAG([ord(c) for c in 'kern']), value=1), harfbuzz.Feature(tag=harfbuzz.HB.TAG([ord(c) for c in 'liga']), value=1), ) harfbuzz.shape(font.as_hb_font(), buffer, features) # reset context cliprect = context.clip_rectangle_list[0] matrix = context.matrix context.transform(matrix.inv()) # project center into screenspace x = center.x - cliprect.left y = center.y - cliprect.top x *= config.width / cliprect.width y *= config.height / cliprect.height new_glyphs, endpos = buffer.get_glyphs() context.set_font_face(font.as_qah_font()) context.set_font_size(font.size) extends = context.glyph_extents(new_glyphs) # attention y is flipped context.translate((x, config.height - y)) if rotation is not None: context.rotate(rotation) context.translate((-extends.width / 2.0, -extends.y_bearing / 2.0)) context.glyph_path(new_glyphs) path = context.copy_path().extents() # map boxes back to pixel space #mx = context.matrix.inv() mx = qah.Matrix.identity mx *= qah.Matrix.translate((-x, -(config.height - y))) if rotation is not None: mx *= qah.Matrix.rotate(rotation) mx *= qah.Matrix.translate( (-extends.width / 2.0, -extends.y_bearing / 2.0)) p1 = mx.map((path.left, path.top)) p2 = mx.map((path.right, path.top)) p3 = mx.map((path.right, path.bottom)) p4 = mx.map((path.left, path.bottom)) poly = Polygon([ (-p1.x, -p1.y), (-p2.x, -p2.y), (-p3.x, -p3.y), (-p4.x, -p4.y), ]) if not config.is_occupied(poly, auto_register=True): context.source_colour = style.halo_color.color_value context.line_width = font.halo_size * 2 context.stroke() context.source_colour = style.text_color.color_value context.show_glyphs(new_glyphs) else: context.new_path() # # calculate bounding box in pixel space # topleft = context.device_to_user(path.topleft) # bottomright = context.device_to_user(path.botright) # topleft.x *= -1 # topleft.y *= -1 # bottomright.x *= -1 # bottomright.y *= -1 # print(topleft, bottomright) # if not config.is_occupied(topleft, bottomright, auto_register=True): # context.source_colour = style.halo_color.color_value # context.line_width = font.halo_size * 2 # context.stroke() # context.source_colour = style.text_color.color_value # context.show_glyphs(new_glyphs) # else: # print(" <> Space occupied") # context.new_path() context.restore()
import sys import qahirah as qah import harfbuzz as hb ft = qah.get_ft_lib() text = "kivy" buf = hb.Buffer.create() buf.add_str(text) buf.guess_segment_properties() ft_face = ft.find_face("Scheherazade") ft_face.set_char_size(size=1,resolution = qah.base_dpi) hb_font = hb.Font.ft_create(ft_face) hb.shape(hb_font,buf) print (buf.glyph_infos) print (buf.glyph_positions)
def shape(self, text, direction): buf = self.clear_buffer(direction) buf.add_str(text) hb.shape(self.font, buf) return buf