def render(self, real=False): """Return a tuple (width, height) to create the image with the user constraints. 2 differents methods are used: * if the user does not set the width, split the line and calculate max width + height * if the user sets a width, blit per glyph """ options = self.options render_text = self._render_text get_extents = self.get_extents uw, uh = self.text_size max_lines = int(options.get("max_lines", 0)) w, h = 0, 0 x, y = 0, 0 if real: self._render_begin() halign = options["halign"] valign = options["valign"] if valign == "bottom": y = self.height - self._internal_height elif valign == "middle": y = int((self.height - self._internal_height) / 2) else: self._internal_height = 0 glyph_space_w = get_extents(" ")[0] # no width specified, faster method if uw is None: index = 0 text, rtl, has_rtl = get_display(self.text, base_dir="L") for line in text.split("\n"): index += 1 if max_lines > 0 and index > max_lines: break lw, lh = get_extents(line) lh = lh * options["line_height"] if real: x = 0 if halign[0] == "c": # center x = int((self.width - lw) / 2.0) elif halign[0] == "r": # right x = int(self.width - lw) if len(line): render_text(line, x, y) y += int(lh) else: w = max(w, int(lw)) self._internal_height += int(lh) h = self._internal_height if uh is None else uh # constraint else: text = self.text width = uw + int(options.get("padding_x", 0)) * 2 # Shorten the text that we actually display if options["shorten"]: last_word_width = get_extents(text[text.rstrip().rfind(" ") :])[0] if get_extents(text)[0] > uw - last_word_width: text = self.shorten(text) index = 0 if self.force_single_line: # test if we need to reverse order of words: if self.rtl: rtext, rtl, has_rtl = get_display(text, base_dir="R") lines = [rtext] else: rtext, rtl, has_rtl = get_display(text, base_dir="L") lines = [rtext] else: lines = self._split_smart(text) for line in lines: index += 1 if max_lines > 0 and index > max_lines: break lw, lh = get_extents(line) lh = lh * options["line_height"] if real: x = 0 if halign[0] == "c": # center x = int((width - lw) / 2.0) elif halign[0] == "r": # right x = int(width - lw) elif halign[0] == "j": # justify, recalc avg spaces words = line.split() sw = get_extents(" ")[0] _spaces = len(words) - 1 if _spaces > 0: just_space = ((uw - lw + sw * _spaces) * 1.0) / (_spaces * 1.0) else: just_space = 0 # render per word with new spaces for word in words: cw = get_extents(word)[0] render_text(word, x, y) x += cw + just_space line = "" if len(line): x = max(1, x) render_text(line, x, y) y += int(lh) else: w = uw if self.rtl and self.force_single_line and not real: w = max(lw, uw) self._internal_height += int(lh) h = self._internal_height if uh is None else uh if not real: # was only the first pass # return with/height w = int(max(w, 1)) h = int(max(h, 1)) return w, h # get data from provider data = self._render_end() assert data # If the text is 1px width, usually, the data is black. # Don't blit that kind of data, otherwise, you have a little black bar. if data is not None and data.width > 1: self.texture.blit_data(data)
def _split_smart(self, text): # Do a "smart" split. If autowidth or autosize is set, # we are not doing smart split, just a split on line break. # Otherwise, we are trying to split as soon as possible, to prevent # overflow on the widget. # depend of the options, split the text on line, or word if not self.text_size[0]: lines = text.split(u"\n") return lines # return the text width + tab support def text_width(text, _tab_width): text = text.replace("\t", " " * _tab_width) w, h = self.get_extents(text) return w def _tokenize(text): # Tokenize a text string from some delimiters if text is None: return delimiters = self.delimiters # u' ,\'".;:\n\r\t' oldindex = 0 for index, char in enumerate(text): if char not in delimiters: continue if oldindex != index: yield text[oldindex:index] yield text[index : index + 1] oldindex = index + 1 yield text[oldindex:] # no autosize, do wordwrap. FL_IS_NEWLINE = 1 x = flags = 0 line = [] lines = [] _join = u"".join lines_append = lines.append width = self.text_size[0] _tab_width = 4 ## # try to add each word on current line. for word in _tokenize(text): # if word==u'': # continue is_newline = word == u"\n" w = text_width(word, _tab_width) # if we have more than the width, or if it's a newline, # push the current line, and create a new one if (x + w > width) or is_newline: # now we check if we already started a new line and # the word is still to big if x == 0 or w > width: # if the word is too long commit the current and start a new one if w > width and line: lines_append(_join(line)) flags = 0 line = [] x = 0 # try to find the max chars that fits in the line newword = word wordfound = True while len(newword) > 0 and wordfound: wordfound = False for i in range(0, len(newword)): word = newword[0 : len(newword) - i] w = text_width(word, _tab_width) if x + w <= width: newword = newword[len(newword) - i :] wordfound = True if newword: lines_append(_join(word)) flags = 0 line = [] x = 0 break elif line: lines_append(_join(line)) flags = 0 line = [] x = 0 else: x += w line.append(word) if is_newline: flags |= FL_IS_NEWLINE else: x += w line.append(word) if line or flags & FL_IS_NEWLINE: lines_append(_join(line)) # test if we need to reverse order of words: if self.rtl: for i, l in enumerate(lines): # rtext = reverse_tokenize(l) rtext, rtl, has_rtl = get_display(l, base_dir="R") lines[i] = rtext return lines