def on_font_size(self, instance, value): """Helper function to manage strings with metrics passed as arguments (eg '12dp') """ try: fValue = float(value) except: fValue = dpi2px(value[:-2], value[-2:]) self.fFontSize = fValue
def metricToPixels(value): result = None if isinstance(value, basestring): match = re.match(r"([0-9]+)([a-z]+)", value, re.I) if match: res = match.groups() return dpi2px(res[0], res[1]) elif type(value) is int: return value return 100
def get_size_in_pixels(size): res = 0 if size: try: res = float(size) except ValueError: regex = r'([\d]+)(\D+)' searched = re.findall(regex, size) if searched: resize = searched[0] res = dpi2px(resize[0], resize[1]) return res
def pt(value): '''Convert from points to pixels ''' return dpi2px(value, 'pt')
def _pre_render(self): # split markup, words, and lines # result: list of word with position and width/height # during the first pass, we don't care about h/valign self._lines = lines = [] self._refs = {} self._anchors = {} spush = self._push_style spop = self._pop_style options = self.options options['_ref'] = None options['script'] = 'normal' for item in self.markup: if item == '[b]': spush('bold') options['bold'] = True self.resolve_font_name() elif item == '[/b]': spop('bold') self.resolve_font_name() elif item == '[i]': spush('italic') options['italic'] = True self.resolve_font_name() elif item == '[/i]': spop('italic') self.resolve_font_name() elif item[:6] == '[size=': item = item[6:-1] try: if item[-2:] in ('px', 'pt', 'in', 'cm', 'mm', 'dp', 'sp'): size = dpi2px(item[:-2], item[-2:]) else: size = int(item) except ValueError: raise size = options['font_size'] spush('font_size') options['font_size'] = size elif item == '[/size]': spop('font_size') elif item[:7] == '[color=': color = parse_color(item[7:-1]) spush('color') options['color'] = color elif item == '[/color]': spop('color') elif item[:6] == '[font=': fontname = item[6:-1] spush('font_name') options['font_name'] = fontname self.resolve_font_name() elif item == '[/font]': spop('font_name') self.resolve_font_name() elif item[:5] == '[sub]': spush('font_size') spush('script') options['font_size'] = options['font_size'] * .5 options['script'] = 'subscript' elif item == '[/sub]': spop('font_size') spop('script') elif item[:5] == '[sup]': spush('font_size') spush('script') options['font_size'] = options['font_size'] * .5 options['script'] = 'superscript' elif item == '[/sup]': spop('font_size') spop('script') elif item[:5] == '[ref=': ref = item[5:-1] spush('_ref') options['_ref'] = ref elif item == '[/ref]': spop('_ref') elif item[:8] == '[anchor=': ref = item[8:-1] if len(lines): x, y = lines[-1][0:2] else: x = y = 0 self._anchors[ref] = x, y else: item = item.replace('&bl;', '[').replace('&br;', ']').replace('&', '&') self._pre_render_label(item, options, lines) # calculate the texture size w, h = self.text_size if h is None or h < 0: h = None if w is None or w < 0: w = None if w is None: if not lines: w = 1 else: w = max([line[0] for line in lines]) if h is None: if not lines: h = 1 else: h = sum([line[1] for line in lines]) return int(ceil(w)), int(ceil(h))
def _pre_render(self): # split markup, words, and lines # result: list of word with position and width/height # during the first pass, we don't care about h/valign self._cached_lines = lines = [] self._refs = {} self._anchors = {} clipped = False w = h = 0 uw, uh = self.text_size spush = self._push_style spop = self._pop_style opts = options = self.options options['_ref'] = None options['script'] = 'normal' shorten = options['shorten'] # if shorten, then don't split lines to fit uw, because it will be # flattened later when shortening and broken up lines if broken # mid-word will have space mid-word when lines are joined uw_temp = None if shorten else uw xpad = options['padding_x'] uhh = (None if uh is not None and options['valign'][-1] != 'p' or options['shorten'] else uh) options['strip'] = options['strip'] or options['halign'][-1] == 'y' for item in self.markup: if item == '[b]': spush('bold') options['bold'] = True self.resolve_font_name() elif item == '[/b]': spop('bold') self.resolve_font_name() elif item == '[i]': spush('italic') options['italic'] = True self.resolve_font_name() elif item == '[/i]': spop('italic') self.resolve_font_name() elif item[:6] == '[size=': item = item[6:-1] try: if item[-2:] in ('px', 'pt', 'in', 'cm', 'mm', 'dp', 'sp'): size = dpi2px(item[:-2], item[-2:]) else: size = int(item) except ValueError: raise size = options['font_size'] spush('font_size') options['font_size'] = size elif item == '[/size]': spop('font_size') elif item[:7] == '[color=': color = parse_color(item[7:-1]) spush('color') options['color'] = color elif item == '[/color]': spop('color') elif item[:6] == '[font=': fontname = item[6:-1] spush('font_name') options['font_name'] = fontname self.resolve_font_name() elif item == '[/font]': spop('font_name') self.resolve_font_name() elif item[:5] == '[sub]': spush('font_size') spush('script') options['font_size'] = options['font_size'] * .5 options['script'] = 'subscript' elif item == '[/sub]': spop('font_size') spop('script') elif item[:5] == '[sup]': spush('font_size') spush('script') options['font_size'] = options['font_size'] * .5 options['script'] = 'superscript' elif item == '[/sup]': spop('font_size') spop('script') elif item[:5] == '[ref=': ref = item[5:-1] spush('_ref') options['_ref'] = ref elif item == '[/ref]': spop('_ref') elif not clipped and item[:8] == '[anchor=': ref = item[8:-1] if len(lines): x, y = lines[-1].x, lines[-1].y else: x = y = 0 self._anchors[ref] = x, y elif not clipped: item = item.replace('&bl;', '[').replace( '&br;', ']').replace('&', '&') opts = copy(options) extents = self.get_cached_extents() opts['space_width'] = extents(' ')[0] w, h, clipped = layout_text(item, lines, (w, h), (uw_temp, uhh), opts, extents, True, False) if len(lines): # remove any trailing spaces from the last line old_opts = self.options self.options = copy(opts) w, h, clipped = layout_text('', lines, (w, h), (uw_temp, uhh), self.options, self.get_cached_extents(), True, True) self.options = old_opts if shorten: options['_ref'] = None # no refs for you! w, h, lines = self.shorten_post(lines, w, h) self._cached_lines = lines # when valign is not top, for markup we layout everything (text_size[1] # is temporarily set to None) and after layout cut to size if too tall elif uh != uhh and h > uh and len(lines) > 1: if options['valign'][-1] == 'm': # bottom i = 0 while i < len(lines) - 1 and h > uh: h -= lines[i].h i += 1 del lines[:i] else: # middle i = 0 top = int(h / 2. + uh / 2.) # remove extra top portion while i < len(lines) - 1 and h > top: h -= lines[i].h i += 1 del lines[:i] i = len(lines) - 1 # remove remaining bottom portion while i and h > uh: h -= lines[i].h i -= 1 del lines[i + 1:] # now justify the text if options['halign'][-1] == 'y' and uw is not None: # XXX: update refs to justified pos # when justify, each line shouldv'e been stripped already split = partial(re.split, re.compile('( +)')) uww = uw - 2 * xpad chr = type(self.text) space = chr(' ') empty = chr('') for i in range(len(lines)): line = lines[i] words = line.words # if there's nothing to justify, we're done if (not line.w or int(uww - line.w) <= 0 or not len(words) or line.is_last_line): continue done = False parts = [None, ] * len(words) # contains words split by space idxs = [None, ] * len(words) # indices of the space in parts # break each word into spaces and add spaces until it's full # do first round of split in case we don't need to split all for w in range(len(words)): word = words[w] sw = word.options['space_width'] p = parts[w] = split(word.text) idxs[w] = [v for v in range(len(p)) if p[v].startswith(' ')] # now we have the indices of the spaces in split list for k in idxs[w]: # try to add single space at each space if line.w + sw > uww: done = True break line.w += sw word.lw += sw p[k] += space if done: break # there's not a single space in the line? if not any(idxs): continue # now keep adding spaces to already split words until done while not done: for w in range(len(words)): if not idxs[w]: continue word = words[w] sw = word.options['space_width'] p = parts[w] for k in idxs[w]: # try to add single space at each space if line.w + sw > uww: done = True break line.w += sw word.lw += sw p[k] += space if done: break # if not completely full, push last words to right edge diff = int(uww - line.w) if diff > 0: # find the last word that had a space for w in range(len(words) - 1, -1, -1): if not idxs[w]: continue break old_opts = self.options self.options = word.options word = words[w] # split that word into left/right and push right till uww l_text = empty.join(parts[w][:idxs[w][-1]]) r_text = empty.join(parts[w][idxs[w][-1]:]) left = LayoutWord(word.options, self.get_extents(l_text)[0], word.lh, l_text) right = LayoutWord(word.options, self.get_extents(r_text)[0], word.lh, r_text) left.lw = max(left.lw, word.lw + diff - right.lw) self.options = old_opts # now put words back together with right/left inserted for k in range(len(words)): if idxs[k]: words[k].text = empty.join(parts[k]) words[w] = right words.insert(w, left) else: for k in range(len(words)): if idxs[k]: words[k].text = empty.join(parts[k]) line.w = uww w = max(w, uww) self._internal_size = w, h if uw: w = uw if uh: h = uh if h > 1 and w < 2: w = 2 if w < 1: w = 1 if h < 1: h = 1 return w, h
def _pre_render(self): # split markup, words, and lines # result: list of word with position and width/height # during the first pass, we don't care about h/valign self._cached_lines = lines = [] self._refs = {} self._anchors = {} clipped = False w = h = 0 uw, uh = self.text_size spush = self._push_style spop = self._pop_style options = self.options options['_ref'] = None options['_anchor'] = None options['script'] = 'normal' shorten = options['shorten'] # if shorten, then don't split lines to fit uw, because it will be # flattened later when shortening and broken up lines if broken # mid-word will have space mid-word when lines are joined uw_temp = None if shorten else uw xpad = options['padding_x'] uhh = (None if uh is not None and options['valign'] != 'top' or options['shorten'] else uh) options['strip'] = options['strip'] or options['halign'] == 'justify' find_base_dir = Label.find_base_direction base_dir = options['base_direction'] self._resolved_base_dir = None for item in self.markup: if item == '[b]': spush('bold') options['bold'] = True self.resolve_font_name() elif item == '[/b]': spop('bold') self.resolve_font_name() elif item == '[i]': spush('italic') options['italic'] = True self.resolve_font_name() elif item == '[/i]': spop('italic') self.resolve_font_name() elif item == '[u]': spush('underline') options['underline'] = True self.resolve_font_name() elif item == '[/u]': spop('underline') self.resolve_font_name() elif item == '[s]': spush('strikethrough') options['strikethrough'] = True self.resolve_font_name() elif item == '[/s]': spop('strikethrough') self.resolve_font_name() elif item[:6] == '[size=': item = item[6:-1] try: if item[-2:] in ('px', 'pt', 'in', 'cm', 'mm', 'dp', 'sp'): size = dpi2px(item[:-2], item[-2:]) else: size = int(item) except ValueError: raise size = options['font_size'] spush('font_size') options['font_size'] = size elif item == '[/size]': spop('font_size') elif item[:7] == '[color=': color = parse_color(item[7:-1]) spush('color') options['color'] = color elif item == '[/color]': spop('color') elif item[:6] == '[font=': fontname = item[6:-1] spush('font_name') options['font_name'] = fontname self.resolve_font_name() elif item == '[/font]': spop('font_name') self.resolve_font_name() elif item[:13] == '[font_family=': spush('font_family') options['font_family'] = item[13:-1] elif item == '[/font_family]': spop('font_family') elif item[:14] == '[font_context=': fctx = item[14:-1] if not fctx or fctx.lower() == 'none': fctx = None spush('font_context') options['font_context'] = fctx elif item == '[/font_context]': spop('font_context') elif item[:15] == '[font_features=': spush('font_features') options['font_features'] = item[15:-1] elif item == '[/font_features]': spop('font_features') elif item[:15] == '[text_language=': lang = item[15:-1] if not lang or lang.lower() == 'none': lang = None spush('text_language') options['text_language'] = lang elif item == '[/text_language]': spop('text_language') elif item[:5] == '[sub]': spush('font_size') spush('script') options['font_size'] = options['font_size'] * .5 options['script'] = 'subscript' elif item == '[/sub]': spop('font_size') spop('script') elif item[:5] == '[sup]': spush('font_size') spush('script') options['font_size'] = options['font_size'] * .5 options['script'] = 'superscript' elif item == '[/sup]': spop('font_size') spop('script') elif item[:5] == '[ref=': ref = item[5:-1] spush('_ref') options['_ref'] = ref elif item == '[/ref]': spop('_ref') elif not clipped and item[:8] == '[anchor=': options['_anchor'] = item[8:-1] elif not clipped: item = item.replace('&bl;', '[').replace('&br;', ']').replace('&', '&') if not base_dir: base_dir = self._resolved_base_dir = find_base_dir(item) opts = copy(options) extents = self.get_cached_extents() opts['space_width'] = extents(' ')[0] w, h, clipped = layout_text(item, lines, (w, h), (uw_temp, uhh), opts, extents, append_down=True, complete=False) if len(lines): # remove any trailing spaces from the last line old_opts = self.options self.options = copy(opts) w, h, clipped = layout_text('', lines, (w, h), (uw_temp, uhh), self.options, self.get_cached_extents(), append_down=True, complete=True) self.options = old_opts self.is_shortened = False if shorten: options['_ref'] = None # no refs for you! options['_anchor'] = None w, h, lines = self.shorten_post(lines, w, h) self._cached_lines = lines # when valign is not top, for markup we layout everything (text_size[1] # is temporarily set to None) and after layout cut to size if too tall elif uh != uhh and h > uh and len(lines) > 1: if options['valign'] == 'bottom': i = 0 while i < len(lines) - 1 and h > uh: h -= lines[i].h i += 1 del lines[:i] else: # middle i = 0 top = int(h / 2. + uh / 2.) # remove extra top portion while i < len(lines) - 1 and h > top: h -= lines[i].h i += 1 del lines[:i] i = len(lines) - 1 # remove remaining bottom portion while i and h > uh: h -= lines[i].h i -= 1 del lines[i + 1:] # now justify the text if options['halign'] == 'justify' and uw is not None: # XXX: update refs to justified pos # when justify, each line should've been stripped already split = partial(re.split, re.compile('( +)')) uww = uw - 2 * xpad chr = type(self.text) space = chr(' ') empty = chr('') for i in range(len(lines)): line = lines[i] words = line.words # if there's nothing to justify, we're done if (not line.w or int(uww - line.w) <= 0 or not len(words) or line.is_last_line): continue done = False parts = [ None, ] * len(words) # contains words split by space idxs = [ None, ] * len(words) # indices of the space in parts # break each word into spaces and add spaces until it's full # do first round of split in case we don't need to split all for w in range(len(words)): word = words[w] sw = word.options['space_width'] p = parts[w] = split(word.text) idxs[w] = [ v for v in range(len(p)) if p[v].startswith(' ') ] # now we have the indices of the spaces in split list for k in idxs[w]: # try to add single space at each space if line.w + sw > uww: done = True break line.w += sw word.lw += sw p[k] += space if done: break # there's not a single space in the line? if not any(idxs): continue # now keep adding spaces to already split words until done while not done: for w in range(len(words)): if not idxs[w]: continue word = words[w] sw = word.options['space_width'] p = parts[w] for k in idxs[w]: # try to add single space at each space if line.w + sw > uww: done = True break line.w += sw word.lw += sw p[k] += space if done: break # if not completely full, push last words to right edge diff = int(uww - line.w) if diff > 0: # find the last word that had a space for w in range(len(words) - 1, -1, -1): if not idxs[w]: continue break old_opts = self.options self.options = word.options word = words[w] # split that word into left/right and push right till uww l_text = empty.join(parts[w][:idxs[w][-1]]) r_text = empty.join(parts[w][idxs[w][-1]:]) left = LayoutWord(word.options, self.get_extents(l_text)[0], word.lh, l_text) right = LayoutWord(word.options, self.get_extents(r_text)[0], word.lh, r_text) left.lw = max(left.lw, word.lw + diff - right.lw) self.options = old_opts # now put words back together with right/left inserted for k in range(len(words)): if idxs[k]: words[k].text = empty.join(parts[k]) words[w] = right words.insert(w, left) else: for k in range(len(words)): if idxs[k]: words[k].text = empty.join(parts[k]) line.w = uww w = max(w, uww) self._internal_size = w, h if uw: w = uw if uh: h = uh if h > 1 and w < 2: w = 2 if w < 1: w = 1 if h < 1: h = 1 return int(w), int(h)
def sp(value): '''Convert from scale-independent pixels to pixels ''' return dpi2px(value, 'sp')
def mm(value): '''Convert from millimeters to pixels ''' return dpi2px(value, 'mm')
def inch(value): '''Convert from inches to pixels ''' return dpi2px(value, 'in')
def _pre_render(self): # split markup, words, and lines # result: list of word with position and width/height # during the first pass, we don't care about h/valign self._lines = lines = [] self._refs = {} self._anchors = {} spush = self._push_style spop = self._pop_style options = self.options options["_ref"] = None options["script"] = "normal" for item in self.markup: if item == "[b]": spush("bold") options["bold"] = True self.resolve_font_name() elif item == "[/b]": spop("bold") self.resolve_font_name() elif item == "[i]": spush("italic") options["italic"] = True self.resolve_font_name() elif item == "[/i]": spop("italic") self.resolve_font_name() elif item[:6] == "[size=": item = item[6:-1] try: if item[-2:] in ("px", "pt", "in", "cm", "mm", "dp", "sp"): size = dpi2px(item[:-2], item[-2:]) else: size = int(item) except ValueError: raise size = options["font_size"] spush("font_size") options["font_size"] = size elif item == "[/size]": spop("font_size") elif item[:7] == "[color=": color = parse_color(item[7:-1]) spush("color") options["color"] = color elif item == "[/color]": spop("color") elif item[:6] == "[font=": fontname = item[6:-1] spush("font_name") options["font_name"] = fontname self.resolve_font_name() elif item == "[/font]": spop("font_name") self.resolve_font_name() elif item[:5] == "[sub]": spush("font_size") spush("script") options["font_size"] = options["font_size"] * 0.5 options["script"] = "subscript" elif item == "[/sub]": spop("font_size") spop("script") elif item[:5] == "[sup]": spush("font_size") spush("script") options["font_size"] = options["font_size"] * 0.5 options["script"] = "superscript" elif item == "[/sup]": spop("font_size") spop("script") elif item[:5] == "[ref=": ref = item[5:-1] spush("_ref") options["_ref"] = ref elif item == "[/ref]": spop("_ref") elif item[:8] == "[anchor=": ref = item[8:-1] if len(lines): x, y = lines[-1][0:2] else: x = y = 0 self._anchors[ref] = x, y else: item = item.replace("&bl;", "[").replace("&br;", "]").replace("&", "&") self._pre_render_label(item, options, lines) # calculate the texture size w, h = self.text_size if h < 0: h = None if w < 0: w = None if w is None: if not lines: w = 1 else: w = max([line[0] for line in lines]) if h is None: if not lines: h = 1 else: h = sum([line[1] for line in lines]) return w, h
def cm(value): '''Convert from centimeters to pixels ''' return dpi2px(value, 'cm')
def dp(value): '''Convert from density-independent pixels to pixels ''' return dpi2px(value, 'dp')
def _pre_render(self): # split markup, words, and lines # result: list of word with position and width/height # during the first pass, we don't care about h/valign self._cached_lines = lines = [] self._refs = {} self._anchors = {} clipped = False w = h = 0 uw, uh = self.text_size spush = self._push_style spop = self._pop_style opts = options = self.options options["_ref"] = None options["_anchor"] = None options["script"] = "normal" shorten = options["shorten"] # if shorten, then don't split lines to fit uw, because it will be # flattened later when shortening and broken up lines if broken # mid-word will have space mid-word when lines are joined uw_temp = None if shorten else uw xpad = options["padding_x"] uhh = None if uh is not None and options["valign"][-1] != "p" or options["shorten"] else uh options["strip"] = options["strip"] or options["halign"][-1] == "y" for item in self.markup: if item == "[b]": spush("bold") options["bold"] = True self.resolve_font_name() elif item == "[/b]": spop("bold") self.resolve_font_name() elif item == "[i]": spush("italic") options["italic"] = True self.resolve_font_name() elif item == "[/i]": spop("italic") self.resolve_font_name() elif item[:6] == "[size=": item = item[6:-1] try: if item[-2:] in ("px", "pt", "in", "cm", "mm", "dp", "sp"): size = dpi2px(item[:-2], item[-2:]) else: size = int(item) except ValueError: raise size = options["font_size"] spush("font_size") options["font_size"] = size elif item == "[/size]": spop("font_size") elif item[:7] == "[color=": color = parse_color(item[7:-1]) spush("color") options["color"] = color elif item == "[/color]": spop("color") elif item[:6] == "[font=": fontname = item[6:-1] spush("font_name") options["font_name"] = fontname self.resolve_font_name() elif item == "[/font]": spop("font_name") self.resolve_font_name() elif item[:5] == "[sub]": spush("font_size") spush("script") options["font_size"] = options["font_size"] * 0.5 options["script"] = "subscript" elif item == "[/sub]": spop("font_size") spop("script") elif item[:5] == "[sup]": spush("font_size") spush("script") options["font_size"] = options["font_size"] * 0.5 options["script"] = "superscript" elif item == "[/sup]": spop("font_size") spop("script") elif item[:5] == "[ref=": ref = item[5:-1] spush("_ref") options["_ref"] = ref elif item == "[/ref]": spop("_ref") elif not clipped and item[:8] == "[anchor=": options["_anchor"] = item[8:-1] elif not clipped: item = item.replace("&bl;", "[").replace("&br;", "]").replace("&", "&") opts = copy(options) extents = self.get_cached_extents() opts["space_width"] = extents(" ")[0] w, h, clipped = layout_text(item, lines, (w, h), (uw_temp, uhh), opts, extents, True, False) if len(lines): # remove any trailing spaces from the last line old_opts = self.options self.options = copy(opts) w, h, clipped = layout_text( "", lines, (w, h), (uw_temp, uhh), self.options, self.get_cached_extents(), True, True ) self.options = old_opts if shorten: options["_ref"] = None # no refs for you! options["_anchor"] = None w, h, lines = self.shorten_post(lines, w, h) self._cached_lines = lines # when valign is not top, for markup we layout everything (text_size[1] # is temporarily set to None) and after layout cut to size if too tall elif uh != uhh and h > uh and len(lines) > 1: if options["valign"][-1] == "m": # bottom i = 0 while i < len(lines) - 1 and h > uh: h -= lines[i].h i += 1 del lines[:i] else: # middle i = 0 top = int(h / 2.0 + uh / 2.0) # remove extra top portion while i < len(lines) - 1 and h > top: h -= lines[i].h i += 1 del lines[:i] i = len(lines) - 1 # remove remaining bottom portion while i and h > uh: h -= lines[i].h i -= 1 del lines[i + 1 :] # now justify the text if options["halign"][-1] == "y" and uw is not None: # XXX: update refs to justified pos # when justify, each line shouldv'e been stripped already split = partial(re.split, re.compile("( +)")) uww = uw - 2 * xpad chr = type(self.text) space = chr(" ") empty = chr("") for i in range(len(lines)): line = lines[i] words = line.words # if there's nothing to justify, we're done if not line.w or int(uww - line.w) <= 0 or not len(words) or line.is_last_line: continue done = False parts = [None] * len(words) # contains words split by space idxs = [None] * len(words) # indices of the space in parts # break each word into spaces and add spaces until it's full # do first round of split in case we don't need to split all for w in range(len(words)): word = words[w] sw = word.options["space_width"] p = parts[w] = split(word.text) idxs[w] = [v for v in range(len(p)) if p[v].startswith(" ")] # now we have the indices of the spaces in split list for k in idxs[w]: # try to add single space at each space if line.w + sw > uww: done = True break line.w += sw word.lw += sw p[k] += space if done: break # there's not a single space in the line? if not any(idxs): continue # now keep adding spaces to already split words until done while not done: for w in range(len(words)): if not idxs[w]: continue word = words[w] sw = word.options["space_width"] p = parts[w] for k in idxs[w]: # try to add single space at each space if line.w + sw > uww: done = True break line.w += sw word.lw += sw p[k] += space if done: break # if not completely full, push last words to right edge diff = int(uww - line.w) if diff > 0: # find the last word that had a space for w in range(len(words) - 1, -1, -1): if not idxs[w]: continue break old_opts = self.options self.options = word.options word = words[w] # split that word into left/right and push right till uww l_text = empty.join(parts[w][: idxs[w][-1]]) r_text = empty.join(parts[w][idxs[w][-1] :]) left = LayoutWord(word.options, self.get_extents(l_text)[0], word.lh, l_text) right = LayoutWord(word.options, self.get_extents(r_text)[0], word.lh, r_text) left.lw = max(left.lw, word.lw + diff - right.lw) self.options = old_opts # now put words back together with right/left inserted for k in range(len(words)): if idxs[k]: words[k].text = empty.join(parts[k]) words[w] = right words.insert(w, left) else: for k in range(len(words)): if idxs[k]: words[k].text = empty.join(parts[k]) line.w = uww w = max(w, uww) self._internal_size = w, h if uw: w = uw if uh: h = uh if h > 1 and w < 2: w = 2 if w < 1: w = 1 if h < 1: h = 1 return int(w), int(h)
def _pre_render(self): # split markup, words, and lines # result: list of word with position and width/height # during the first pass, we don't care about h/valign self._lines = lines = [] self._refs = {} self._anchors = {} spush = self._push_style spop = self._pop_style options = self.options options['_ref'] = None for item in self.markup: if item == '[b]': spush('bold') options['bold'] = True self.resolve_font_name() elif item == '[/b]': spop('bold') self.resolve_font_name() elif item == '[i]': spush('italic') options['italic'] = True self.resolve_font_name() elif item == '[/i]': spop('italic') self.resolve_font_name() elif item[:6] == '[size=': item = item[6:-1] try: if item[-2:] in ('px', 'pt', 'in', 'cm', 'mm'): size = dpi2px(item[:-2], item[-2:]) else: size = int(item) except ValueError: raise size = options['font_size'] spush('font_size') options['font_size'] = size elif item == '[/size]': spop('font_size') elif item[:7] == '[color=': color = parse_color(item[7:-1]) spush('color') options['color'] = color elif item == '[/color]': spop('color') elif item[:6] == '[font=': fontname = item[6:-1] spush('font_name') options['font_name'] = fontname self.resolve_font_name() elif item == '[/font]': spop('font_name') self.resolve_font_name() elif item[:5] == '[ref=': ref = item[5:-1] spush('_ref') options['_ref'] = ref elif item == '[/ref]': spop('_ref') elif item[:8] == '[anchor=': ref = item[8:-1] if len(lines): x, y = lines[-1][0:2] else: x = y = 0 self._anchors[ref] = x, y else: item = item.replace('&bl;', '[').replace( '&br;', ']').replace('&', '&') self._pre_render_label(item, options, lines) # calculate the texture size w, h = self.text_size if h < 0: h = None if w < 0: w = None if w is None: w = max([line[0] for line in lines]) if h is None: h = sum([line[1] for line in lines]) return w, h
class cScrollableLabelLargeInner(RecycleView): """ The "real' scrollable label (without background) """ # to have similar properties as a Label font_size = Property('20sp') text = StringProperty('') oOrcaWidget = Property(None) # Internal Property which handles fonmt resizing (not working as RecycleView can't manage change of cached widget) fFontSize = BoundedNumericProperty(dpi2px(20, 'sp'), min=4.0, max=96.0, errorhandler=lambda x: 96.0 if x > 96.0 else 4.0) def __init__(self, **kwargs): #we create a new class on the fly top ass the font args to the creation process, as the view adapter creates without arguments self.cLineLayout = type('cLineLayout', cLineLayoutBase.__bases__, dict(cLineLayoutBase.__dict__)) # passes myself to the embedded class. Not good style but Recycleview limits passing customized parameters self.cLineLayout.oScrollableLabelLargeInner = self self.oOrcaWidget = kwargs.get('ORCAWIDGET', None) # maximal len (in chars) of a single ine of the given text self.iMaxLen = 0 # Setting the scrolltypes / bars for the Recycleview self.scroll_type = ['bars', 'content'] self.scroll_wheel_distance = dp(114) self.bar_width = dp(10) # The original passed Data array self.aData = [] # Internal Flag to distinguish between first show and (re) setting text self.bInit = False # The maximum width of a char self.iMaxCharwidth = 0 # The maximum characters per line self.iMaxCharsPerLine = 0 if "font_size" in kwargs: self.on_font_size(None, kwargs["font_size"]) # Retieving the genuine font propertes of a label to pass only those arguments to the label (removing pos, hints, background colors , etc self.aFontProperties = Label._font_properties + ("background_color", ) # standard font args, if nothing is given self.kwFontArgs = { "halign": "left", "valign": "top", "max_lines": 1, "font_size": 20 } # add / update the font args to be passed to the Label for k in kwargs: if k in self.aFontProperties: self.kwFontArgs[k] = kwargs[k] self.kwFontArgs["font_size"] = self.fFontSize self.kwFontArgs.pop("text", None) # Parameter Flag to disable horizontal scrolling self.bNoXScroll = kwargs.get("noxscroll", False) self.bMarkup = kwargs.get("markup", False) #A dummy label to get th width a the larges character self.oLabel = Label(**RemoveNoClassArgs(self.kwFontArgs, Label)) super(self.__class__, self).__init__(**RemoveNoClassArgs(kwargs, RecycleView)) # This manages the distance between lines self.layout_manager.default_size = ( None, self.oLabel._label.get_extents('W')[1]) #self.layout_manager.default_size = (None, self.fFontSize*1.1) self.layout_manager.orientation = 'vertical' # we need to handle size changes self.bind(size=self.update_size) self.bind(text=self.on_textinner) self.text = kwargs.get("text", "") def on_fFontSize(self, instance, value): """ Will handle font size changes """ if self.layout_manager is not None: self.kwFontArgs["font_size"] = self.fFontSize self.oLabel.font_size = self.fFontSize self.layout_manager.default_size = ( None, self.oLabel._label.get_extents('W')[1]) self.SetData(self.aData) def on_font_size(self, instance, value): """Helper function to manage strings with metrics passed as arguments (eg '12dp') """ try: fValue = float(value) except: fValue = dpi2px(value[:-2], value[-2:]) self.fFontSize = fValue def on_textinner(self, instance, value): """ helper to have a Label like funtionality to set the caption """ self.update_size(None, None) def IncreaseFontSize(self, *args): """ Increase the Font size """ self.fFontSize += 1.0 def DecreaseFontSize(self, *args): """ Decrease the Font size """ self.fFontSize -= 1.0 def SetData(self, aData): """ Passes the data to the Recycle view and sets the layout manager size """ self.data = [{ 'text': ToUnicode(x), "font_size": self.fFontSize } for x in aData] if self.bNoXScroll: self.layout_manager.width = self.width else: self.layout_manager.width = ( self.iMaxCharwidth) * self.iMaxCharsPerLine self.viewclass = self.cLineLayout self.refresh_from_data() def update_size(self, instance, value): """ Fits the text into layout_manager line. If noxscroll, all line with be split up to fit to the widget size. if x scrolling is enabled, we look, if the the maximum line lenght exceed the TEXTURE SIZE. In that case we split the lines as well and set the scrolling window size to the texture size. if x scrolling is enabled, and all lines fit to the texture size, we pass the unchanged array """ if self.size == [100, 100]: return bDoLineBreak = False self.iMaxCharwidth = self.oLabel._label.get_extents('W')[0] self.iMaxCharsPerLine = int(self.width / self.iMaxCharwidth) if not self.bNoXScroll: self.aData = self.text.split('\n') self.iMaxLen = len(max(self.aData, key=len)) if (self.iMaxCharwidth * self.iMaxLen) > GL_MAX_TEXTURE_SIZE: self.iMaxCharsPerLine = int(GL_MAX_TEXTURE_SIZE / self.iMaxCharwidth) bDoLineBreak = True else: self.iMaxCharsPerLine = self.iMaxLen else: bDoLineBreak = True if bDoLineBreak: if self.oLabel is not None: if len(self.text) > 10000: aData = self.text.split('\n') i = 0 iEnd = len(aData) while i < iEnd: if len(aData[i]) > self.iMaxCharsPerLine: aData.insert(i + 1, aData[i][self.iMaxCharsPerLine:]) aData[i] = aData[i][:self.iMaxCharsPerLine] iEnd += 1 i += 1 else: self.oLabel.size = self.size self.oLabel.text_size = (self.width, None) self.oLabel.text = self.text self.oLabel._label.render() aData = [] for oLine in self.oLabel._label._cached_lines: if len(oLine.words) > 0: uText = u'' for oWord in oLine.words: if self.bMarkup: uText += self.AddMarkUps(oWord) else: uText += oWord.text aData.append(uText) else: aData.append(u'') self.oLabel.text = "" self.aData = aData self.SetData(aData) else: self.SetData(self.aData) def AddMarkUps(self, oWord): uText = oWord.text if oWord.options["bold"]: uText = self.AddMarkUp(uText, "b") if oWord.options["italic"]: uText = self.AddMarkUp(uText, "i") if oWord.options["underline"]: uText = self.AddMarkUp(uText, "u") if oWord.options["strikethrough"]: uText = self.AddMarkUp(uText, "s") if oWord.options["font_name"] != "Roboto": uText = self.AddMarkUp(uText, "font", oWord.options["font_name"]) if oWord.options["font_size"] != self.fFontSize: uText = self.AddMarkUp(uText, "size", ToUnicode(oWord.options["font_size"])) if oWord.options["color"] != [1, 1, 1, 1]: uHexColor = u'' for iColor in oWord.options["color"]: uHexColor += ToHex(int(iColor * 255)) uText = self.AddMarkUp(uText, "color", '#' + uHexColor) return uText def AddMarkUp(self, uText, uMarkUp, uValue=None): if uValue is None: return "[{1}]{0}[/{1}]".format(uText, uMarkUp) else: return "[{1}={2}]{0}[/{1}]".format(uText, uMarkUp, uValue)