# Copyright 2004-2019 Tom Rothamel # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Other text-related things. from __future__ import print_function import renpy.text from renpy.text.textsupport import TAG, PARAGRAPH import renpy.text.textsupport as textsupport # A list of text tags, mapping from the text tag prefix to if it # requires a closing tag. text_tags = dict( alpha=True, art=True, image=False, p=False, w=False, fast=False, b=True, i=True, u=True, a=True, plain=True, font=True, color=True, outlinecolor=True, size=True, nw=False, s=True, rt=True, rb=True, k=True, cps=True, space=False, vspace=False ) text_tags[""] = True # This checks the text tags in a string to be sure they are all matched, and # properly nested. It returns an error message, or None if the line is okay. def check_text_tags(s): """ :doc: lint Checks the text tags in s for correctness. Returns an error string if there is an error, or None if there is no error. """ all_tags = dict(text_tags) custom_tags = renpy.config.custom_text_tags if custom_tags: all_tags.update(custom_tags) self_closing_custom_tags = renpy.config.self_closing_custom_text_tags if self_closing_custom_tags: all_tags.update(dict.fromkeys(self_closing_custom_tags, False)) try: tokens = textsupport.tokenize(unicode(s)) except Exception as e: return e.args[0] tag_stack = [ ] for type, text in tokens: # @ReservedAssignment if type != TAG: continue if text[0] == "#": continue # Strip off arguments for tags. if text.find('=') != -1: text = text[:text.find('=')] # Closing tag. if text and text[0] == '/': if not tag_stack: return "Close text tag '%s' does not match an open text tag." % text if tag_stack[-1] != text[1:]: return "Close text tag '%s' does not match open text tag '%s'." % (text, tag_stack[-1]) tag_stack.pop() continue if text not in all_tags: return "Text tag '%s' is not known." % text if all_tags[text]: tag_stack.append(text) if tag_stack: return "One or more text tags were left open at the end of the string: " + ", ".join([ "'" + i + "'" for i in tag_stack]) return None def filter_text_tags(s, allow=None, deny=None): """ :doc: text_utility Returns a copy of `s` with the text tags filtered. Exactly one of the `allow` and `deny` keyword arguments must be given. `allow` A set of tags that are allowed. If a tag is not in this list, it is removed. `deny` A set of tags that are denied. If a tag is not in this list, it is kept in the string. """ if (allow is None) and (deny is None): raise Exception("Only one of the allow and deny keyword arguments should be given to filter_text_tags.") if (allow is not None) and (deny is not None): raise Exception("Only one of the allow and deny keyword arguments should be given to filter_text_tags.") tokens = textsupport.tokenize(unicode(s)) rv = [ ] for tokentype, text in tokens: if tokentype == PARAGRAPH: rv.append("\n") elif tokentype == TAG: kind = text.partition("=")[0] if kind and (kind[0] == "/"): kind = kind[1:] if allow is not None: if kind in allow: rv.append("{" + text + "}") else: if kind not in deny: rv.append("{" + text + "}") else: rv.append(text) return "".join(rv) class ParameterizedText(object): """ :name: ParameterizedText :doc: text This is a displayable that can be shown with an additional string parameter, which then shows that string as if it was an image. This is usually used as part of the pre-defined ``text`` image. For example, one can do:: show text "Hello, World" at truecenter with dissolve pause 1 hide text with dissolve You can use ParameterizedText directly to define similar images with different style properties. For example, one can write:: image top_text = ParameterizedText(xalign=0.5, yalign=0.0) """ def __init__(self, style='default', **properties): self.style = style self.properties = properties _duplicatable = True def _duplicate(self, args): if len(args.args) == 0: raise Exception("'%s' takes a single string parameter." % ' '.join(args.name)) param = "".join(args.args) string = renpy.python.py_eval(param) return renpy.text.text.Text(string, style=self.style, **self.properties) def textwrap(s, width=78, asian=False): """ Wraps the unicode string `s`, and returns a list of strings. `width` The number of half-width characters that fit on a line. `asian` True if we should make ambiguous width characters full-width, as is done in Asian encodings. """ import unicodedata glyphs = [ ] for c in unicode(s): eaw = unicodedata.east_asian_width(c) if (eaw == "F") or (eaw =="W"): gwidth = 20 elif (eaw == "A"): if asian: gwidth = 20 else: gwidth = 10 else: gwidth = 10 g = textsupport.Glyph() g.character = ord(c) g.ascent = 10 g.line_spacing = 10 g.width = gwidth g.advance = gwidth glyphs.append(g) textsupport.annotate_unicode(glyphs, False, 2) renpy.text.texwrap.linebreak_tex(glyphs, width * 10, width * 10, False) return textsupport.linebreak_list(glyphs)