Spaces:
Runtime error
Runtime error
text-2-character-anim
/
pyrender
/.eggs
/pyglet-2.0.5-py3.10.egg
/pyglet
/text
/formats
/structured.py
"""Base class for structured (hierarchical) document formats. | |
""" | |
import re | |
import pyglet | |
from pyglet.gl import * | |
class _InlineElementGroup(pyglet.graphics.Group): | |
def __init__(self, texture, program, order=0, parent=None): | |
super().__init__(order, parent) | |
self.texture = texture | |
self.program = program | |
def set_state(self): | |
self.program.use() | |
glActiveTexture(GL_TEXTURE0) | |
glBindTexture(self.texture.target, self.texture.id) | |
glEnable(GL_BLEND) | |
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) | |
def unset_state(self): | |
glDisable(GL_BLEND) | |
self.program.stop() | |
def __eq__(self, other): | |
return (self.__class__ is other.__class__ and | |
self._order == other.order and | |
self.program == other.program and | |
self.parent == other.parent and | |
self.texture.target == other.texture.target and | |
self.texture.id == other.texture.id) | |
def __hash__(self): | |
return hash((self._order, self.program, self.parent, | |
self.texture.target, self.texture.id)) | |
class ImageElement(pyglet.text.document.InlineElement): | |
def __init__(self, image, width=None, height=None): | |
self.image = image.get_texture() | |
self.width = width is None and image.width or width | |
self.height = height is None and image.height or height | |
self.vertex_lists = {} | |
anchor_y = self.height // image.height * image.anchor_y | |
ascent = max(0, self.height - anchor_y) | |
descent = min(0, -anchor_y) | |
super().__init__(ascent, descent, self.width) | |
def place(self, layout, x, y, z): | |
program = pyglet.text.layout.get_default_image_layout_shader() | |
group = _InlineElementGroup(self.image.get_texture(), program, 0, layout.group) | |
x1 = x | |
y1 = y + self.descent | |
x2 = x + self.width | |
y2 = y + self.height + self.descent | |
vertex_list = program.vertex_list_indexed(4, pyglet.gl.GL_TRIANGLES, [0, 1, 2, 0, 2, 3], | |
layout.batch, group, | |
position=('f', (x1, y1, z, x2, y1, z, x2, y2, z, x1, y2, z)), | |
tex_coords=('f', self.image.tex_coords)) | |
self.vertex_lists[layout] = vertex_list | |
def remove(self, layout): | |
self.vertex_lists[layout].delete() | |
del self.vertex_lists[layout] | |
def _int_to_roman(number): | |
# From http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81611 | |
if not 0 < number < 4000: | |
raise ValueError("Argument must be between 1 and 3999") | |
integers = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1) | |
numerals = ('M', 'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I') | |
result = "" | |
for i in range(len(integers)): | |
count = int(number // integers[i]) | |
result += numerals[i] * count | |
number -= integers[i] * count | |
return result | |
class ListBuilder: | |
def begin(self, decoder, style): | |
"""Begin a list. | |
:Parameters: | |
`decoder` : `StructuredTextDecoder` | |
Decoder. | |
`style` : dict | |
Style dictionary that applies over the entire list. | |
""" | |
left_margin = decoder.current_style.get('margin_left') or 0 | |
tab_stops = decoder.current_style.get('tab_stops') | |
if tab_stops: | |
tab_stops = list(tab_stops) | |
else: | |
tab_stops = [] | |
tab_stops.append(left_margin + 50) | |
style['margin_left'] = left_margin + 50 | |
style['indent'] = -30 | |
style['tab_stops'] = tab_stops | |
def item(self, decoder, style, value=None): | |
"""Begin a list item. | |
:Parameters: | |
`decoder` : `StructuredTextDecoder` | |
Decoder. | |
`style` : dict | |
Style dictionary that applies over the list item. | |
`value` : str | |
Optional value of the list item. The meaning is list-type | |
dependent. | |
""" | |
mark = self.get_mark(value) | |
if mark: | |
decoder.add_text(mark) | |
decoder.add_text('\t') | |
def get_mark(self, value=None): | |
"""Get the mark text for the next list item. | |
:Parameters: | |
`value` : str | |
Optional value of the list item. The meaning is list-type | |
dependent. | |
:rtype: str | |
""" | |
return '' | |
class UnorderedListBuilder(ListBuilder): | |
def __init__(self, mark): | |
"""Create an unordered list with constant mark text. | |
:Parameters: | |
`mark` : str | |
Mark to prepend to each list item. | |
""" | |
self.mark = mark | |
def get_mark(self, value): | |
return self.mark | |
class OrderedListBuilder(ListBuilder): | |
format_re = re.compile('(.*?)([1aAiI])(.*)') | |
def __init__(self, start, fmt): | |
"""Create an ordered list with sequentially numbered mark text. | |
The format is composed of an optional prefix text, a numbering | |
scheme character followed by suffix text. Valid numbering schemes | |
are: | |
``1`` | |
Decimal Arabic | |
``a`` | |
Lowercase alphanumeric | |
``A`` | |
Uppercase alphanumeric | |
``i`` | |
Lowercase Roman | |
``I`` | |
Uppercase Roman | |
Prefix text may typically be ``(`` or ``[`` and suffix text is | |
typically ``.``, ``)`` or empty, but either can be any string. | |
:Parameters: | |
`start` : int | |
First list item number. | |
`fmt` : str | |
Format style, for example ``"1."``. | |
""" | |
self.next_value = start | |
self.prefix, self.numbering, self.suffix = self.format_re.match(fmt).groups() | |
assert self.numbering in '1aAiI' | |
def get_mark(self, value): | |
if value is None: | |
value = self.next_value | |
self.next_value = value + 1 | |
if self.numbering in 'aA': | |
try: | |
mark = 'abcdefghijklmnopqrstuvwxyz'[value - 1] | |
except ValueError: | |
mark = '?' | |
if self.numbering == 'A': | |
mark = mark.upper() | |
return f'{self.prefix}{mark}{self.suffix}' | |
elif self.numbering in 'iI': | |
try: | |
mark = _int_to_roman(value) | |
except ValueError: | |
mark = '?' | |
if self.numbering == 'i': | |
mark = mark.lower() | |
return f'{self.prefix}{mark}{self.suffix}' | |
else: | |
return f'{self.prefix}{value}{self.suffix}' | |
class StructuredTextDecoder(pyglet.text.DocumentDecoder): | |
def decode(self, text, location=None): | |
self.len_text = 0 | |
self.current_style = {} | |
self.next_style = {} | |
self.stack = [] | |
self.list_stack = [] | |
self.document = pyglet.text.document.FormattedDocument() | |
if location is None: | |
location = pyglet.resource.FileLocation('') | |
self.decode_structured(text, location) | |
return self.document | |
def decode_structured(self, text, location): | |
raise NotImplementedError('abstract') | |
def push_style(self, key, styles): | |
old_styles = {} | |
for name in styles.keys(): | |
old_styles[name] = self.current_style.get(name) | |
self.stack.append((key, old_styles)) | |
self.current_style.update(styles) | |
self.next_style.update(styles) | |
def pop_style(self, key): | |
# Don't do anything if key is not in stack | |
for match, _ in self.stack: | |
if key == match: | |
break | |
else: | |
return | |
# Remove all innermost elements until key is closed. | |
while True: | |
match, old_styles = self.stack.pop() | |
self.next_style.update(old_styles) | |
self.current_style.update(old_styles) | |
if match == key: | |
break | |
def add_text(self, text): | |
self.document.insert_text(self.len_text, text, self.next_style) | |
self.next_style.clear() | |
self.len_text += len(text) | |
def add_element(self, element): | |
self.document.insert_element(self.len_text, element, self.next_style) | |
self.next_style.clear() | |
self.len_text += 1 | |