├── setup.cfg ├── test ├── empty.t ├── styles.t ├── paragraph.t └── lists.t ├── demo ├── README.md ├── send-inputs.sh ├── autoreload.sh ├── demo.sh └── demo.tsq ├── Makefile ├── bin └── rst2ansi ├── LICENSE ├── .travis.yml ├── rst2ansi ├── functional.py ├── visitor.py ├── unicode.py ├── wrap.py ├── __init__.py ├── get_terminal_size.py ├── table.py └── ansi.py ├── setup.py ├── README.rst └── TODO.md /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | python-tag = py3 3 | -------------------------------------------------------------------------------- /test/empty.t: -------------------------------------------------------------------------------- 1 | Test the rendering of an empty file: 2 | 3 | $ rst2ansi /dev/null 4 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | ## rst2ansi demo files 2 | 3 | These are the scripts and inputs used to record the demo screencast. 4 | -------------------------------------------------------------------------------- /demo/send-inputs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | pts=$1; shift 3 | 4 | reseq demo/demo.tsq --replay | sudo ttysend -i $pts 5 | wait 3 6 | tmux kill-session -t demo 7 | -------------------------------------------------------------------------------- /demo/autoreload.sh: -------------------------------------------------------------------------------- 1 | clear 2 | export PYTHONPATH=. 3 | while true; 4 | do inotifywait -e modify out.rst 2>/dev/null >/dev/null 5 | clear 6 | ./bin/rst2ansi out.rst 7 | done 8 | -------------------------------------------------------------------------------- /demo/demo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "" > out.rst 4 | 5 | tmux new-session -s demo -n rst2ansi -d 'nvim out.rst -c ":set nobackup" -c ":set noswapfile"' 6 | tmux splitw -t demo -h 'zsh ./demo/autoreload.sh' 7 | tmux lastp -t demo 8 | tmux -2 attach-session -t demo 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PY := python3 2 | 3 | export PATH := $(shell pwd)/bin:$(PATH) 4 | export PYTHONPATH=$(shell pwd) 5 | 6 | BUILD = $(PY) setup.py 7 | 8 | build: 9 | $(BUILD) build 10 | 11 | install: 12 | $(BUILD) install 13 | 14 | test: 15 | cram test -v 16 | 17 | clean: 18 | $(BUILD) clean 19 | 20 | .PHONY: build install test clean 21 | -------------------------------------------------------------------------------- /bin/rst2ansi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | from rst2ansi import rst2ansi 5 | import argparse 6 | import io 7 | 8 | parser = argparse.ArgumentParser(description='Prints a reStructuredText input in an ansi-decorated format suitable for console output.') 9 | parser.add_argument('file', type=str, nargs='?', help='A path to the file to open') 10 | 11 | args = parser.parse_args() 12 | 13 | def process_file(f): 14 | out = rst2ansi(f.read()) 15 | if out: 16 | try: 17 | print(out) 18 | except UnicodeEncodeError: 19 | print(out.encode('ascii', errors='backslashreplace').decode('ascii')) 20 | 21 | if args.file: 22 | with io.open(args.file, 'rb') as f: 23 | process_file(f) 24 | else: 25 | process_file(sys.stdin) 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2015-2016 Franklin "Snaipe" Mathieu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | sudo: false 4 | 5 | python: 6 | - 2.7 7 | - 3.3 8 | - 3.4 9 | - nightly 10 | 11 | matrix: 12 | include: 13 | - python: 3.5 14 | env: 15 | DEPLOY: true 16 | 17 | allow_failures: 18 | - python: nightly 19 | 20 | install: pip install docutils cram 21 | 22 | script: 23 | - make build 24 | - make test 25 | 26 | deploy: 27 | provider: pypi 28 | skip_cleanup: true 29 | user: Snaipe 30 | password: 31 | secure: CjyhyupM4hDysV9FAjB/CmbimFgoMiyN2JvV4sje3mB+iyFAIUp5imDQlW60mbgplrnROXiFyaUpk1OhKCW/Eu6KKnjhQevz/iY/d+Bfk1TnjvsWTNEKU54QAsWxN9AjL92y+e35hN0emFSDd1ZhzWYj+uTJ39zxBsjFmdJS+2xXkhlAdeAblGQkQ9cFRXr2JFTSYDLKTTqSviCzo1iLddl8uPb8fFlESjAyl8AnxrK2d50RC9GW7yz5umox/McjYPhX+VFQ0ITfG/b+wtJveOOoFIUvPKUMxv55sEWUiuMv1ZZ1PPaeXUGkDyvZ0zSoruo8m9ZZlTqUwJdEFuzxlfopod2h86J9fTYD4NKIhgNssO9sqRcgR2sgvNOWQ9wGmZggt3exluHeyL1iBJLU1z0CHDQQXmI3Au41Ziug+O0GokfSTnmCYkMHEkgZtaHj/UINKE9SyNPqEsa+N6KaclImw7+ZI6KUsl38LB2EOU6xouCMe6wBfwnVOmAzvzNH7BAtvu7VcSzyX9ikG4JQIZz73zOnm7Y28ENnsGM18ItRI33lf+D9wNCeff9whH9NTPA7RBrJ4DDP7PC5Mz3em1AlJdkEe360JI6plXZrVffSrpETN2fRAQp0x5fTUO333x1vbWNOW4/bryZ3EEG0VmOFYKcMxv6hL+78esCJuuI= 32 | on: 33 | repo: Snaipe/python-rst2ansi 34 | tags: true 35 | distributions: sdist bdist_wheel 36 | condition: $DEPLOY = true 37 | -------------------------------------------------------------------------------- /rst2ansi/functional.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | The MIT License (MIT) 4 | 5 | Copyright © 2015-2016 Franklin "Snaipe" Mathieu 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | """ 25 | 26 | def npartial(func, *args, **kwargs): 27 | """ 28 | Returns a partial node visitor function 29 | """ 30 | def wrapped(self, node): 31 | func(self, *args, **kwargs) 32 | return wrapped 33 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | try: 6 | from setuptools import setup 7 | except ImportError: 8 | from distutils.core import setup 9 | 10 | def read(fname): 11 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 12 | 13 | setup( 14 | name="rst2ansi", 15 | version="0.1.5", 16 | author="Snaipe", 17 | author_email="franklinmathieu@gmail.com", 18 | description="A rst converter to ansi-decorated console output", 19 | long_description=read('README.rst'), 20 | license="MIT", 21 | keywords="rst restructuredtext ansi console code converter", 22 | url="https://github.com/Snaipe/python-rst-to-ansi", 23 | packages=['rst2ansi'], 24 | requires=['docutils'], 25 | scripts=['bin/rst2ansi'], 26 | data_files=[], 27 | classifiers=[ 28 | "Development Status :: 4 - Beta", 29 | "Environment :: Console", 30 | "Intended Audience :: Developers", 31 | "Intended Audience :: End Users/Desktop", 32 | "License :: OSI Approved :: MIT License", 33 | "Operating System :: OS Independent", 34 | "Programming Language :: Python :: 2.7", 35 | "Programming Language :: Python :: 3", 36 | "Programming Language :: Python :: 3.3", 37 | "Programming Language :: Python :: 3.4", 38 | "Programming Language :: Python :: 3.5", 39 | "Topic :: Software Development :: Libraries :: Python Modules", 40 | "Topic :: Text Processing :: Markup", 41 | "Topic :: Utilities", 42 | ], 43 | ) 44 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | rst2ansi 2 | ======== 3 | 4 | |build| |downloads| |pyversions| |format| 5 | 6 | A python module dedicated to rendering RST (reStructuredText) documents 7 | to ansi-escaped strings suitable for display in a terminal. 8 | 9 | |asciicast| 10 | 11 | Installation 12 | ------------ 13 | 14 | Requirements 15 | ~~~~~~~~~~~~ 16 | 17 | Python 3.3+ 18 | 19 | PyPi package 20 | ~~~~~~~~~~~~ 21 | 22 | .. code:: bash 23 | 24 | pip install rst2ansi 25 | 26 | Usage 27 | ----- 28 | 29 | As a CLI utility: 30 | ~~~~~~~~~~~~~~~~~ 31 | 32 | .. code:: bash 33 | 34 | usage: rst2ansi [-h] [file] 35 | 36 | Prints a reStructuredText input in an ansi-decorated format suitable for 37 | console output. 38 | 39 | positional arguments: 40 | file A path to the file to open 41 | 42 | optional arguments: 43 | -h, --help show this help message and exit 44 | 45 | As a python module: 46 | ~~~~~~~~~~~~~~~~~~~ 47 | 48 | .. code:: python 49 | 50 | from rst2ansi import rst2ansi 51 | 52 | print(rst2ansi('I **love** reStructuredText!')) 53 | 54 | .. |build| image:: https://api.travis-ci.org/Snaipe/python-rst2ansi.svg 55 | :target: https://travis-ci.org/Snaipe/python-rst2ansi 56 | 57 | .. |downloads| image:: https://img.shields.io/pypi/dm/rst2ansi.svg 58 | :target: https://pypi.python.org/pypi/rst2ansi/ 59 | 60 | .. |pyversions| image:: https://img.shields.io/pypi/pyversions/rst2ansi.svg 61 | 62 | .. |format| image:: https://img.shields.io/pypi/format/rst2ansi.svg 63 | 64 | .. |asciicast| image:: https://asciinema.org/a/drykz69gtn557z3hxnbb1jybq.png 65 | :target: https://asciinema.org/a/drykz69gtn557z3hxnbb1jybq 66 | -------------------------------------------------------------------------------- /rst2ansi/visitor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | The MIT License (MIT) 4 | 5 | Copyright © 2015-2016 Franklin "Snaipe" Mathieu 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | """ 25 | 26 | from docutils import core, frontend, nodes, utils, writers, languages, io 27 | from docutils.utils.error_reporting import SafeString 28 | from docutils.transforms import writer_aux 29 | from docutils.parsers.rst import roles 30 | 31 | from .ansi import ANSITranslator 32 | 33 | class Writer(writers.Writer): 34 | 35 | def __init__(self, **options): 36 | writers.Writer.__init__(self) 37 | self.translator_class = ANSITranslator 38 | self.options = options 39 | 40 | def translate(self): 41 | visitor = self.translator_class(self.document, **self.options) 42 | self.document.walkabout(visitor) 43 | self.output = visitor.output 44 | 45 | def get_transforms(self): 46 | return writers.Writer.get_transforms(self) + [writer_aux.Admonitions] 47 | 48 | -------------------------------------------------------------------------------- /rst2ansi/unicode.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | The MIT License (MIT) 4 | 5 | Copyright © 2015-2016 Franklin "Snaipe" Mathieu 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | """ 25 | 26 | from __future__ import unicode_literals 27 | 28 | import sys 29 | 30 | def num_to_superscript(n): 31 | sups = { 32 | '0': '\u2070', 33 | '1': '\xb9', 34 | '2': '\xb2', 35 | '3': '\xb3', 36 | '4': '\u2074', 37 | '5': '\u2075', 38 | '6': '\u2076', 39 | '7': '\u2077', 40 | '8': '\u2078', 41 | '9': '\u2079' 42 | } 43 | return ''.join(sups.get(c, c) for c in str(n)) 44 | 45 | def ref_to_unicode(n): 46 | return '⁽' + num_to_superscript(n) + '⁾' 47 | 48 | def u(s): 49 | # Useful for very coarse version differentiation. 50 | PY2 = sys.version_info[0] == 2 51 | PY3 = sys.version_info[0] == 3 52 | if PY3: 53 | return s 54 | else: 55 | # Workaround for standalone backslash 56 | try: 57 | ret_s = unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") 58 | except TypeError: 59 | ret_s = s.replace(r'\\', r'\\\\') 60 | return ret_s 61 | -------------------------------------------------------------------------------- /rst2ansi/wrap.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | The MIT License (MIT) 4 | 5 | Copyright © 2015-2016 Franklin "Snaipe" Mathieu 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | """ 25 | 26 | import re 27 | 28 | _ANSI_CODE = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]') 29 | 30 | def word_size(word): 31 | return len(_ANSI_CODE.sub('', word)) 32 | 33 | def wrap(text, width=80, subsequent_indent=''): 34 | words = text.split() 35 | line_size = 0 36 | lines = [[]] 37 | 38 | for w in words: 39 | size = word_size(w) + 1 40 | if size == 0: 41 | continue 42 | if line_size + size - 1 > width and line_size > width / 2: 43 | line_size = len(subsequent_indent) 44 | lines.append([]) 45 | while line_size + size - 1 > width: 46 | stripped = width - line_size - 1 47 | lines[-1].append(w[:stripped] + '-') 48 | line_size = len(subsequent_indent) 49 | lines.append([]) 50 | w = w[stripped:] 51 | size -= stripped 52 | if size == 0: 53 | continue 54 | lines[-1].append(w) 55 | line_size += size 56 | return [subsequent_indent + ' '.join(words) for words in lines] 57 | 58 | -------------------------------------------------------------------------------- /rst2ansi/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | The MIT License (MIT) 4 | 5 | Copyright © 2015-2016 Franklin "Snaipe" Mathieu 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | """ 25 | 26 | from __future__ import unicode_literals 27 | 28 | from docutils import nodes, core 29 | from docutils.parsers.rst import roles 30 | 31 | from .visitor import Writer 32 | from .ansi import COLORS, STYLES 33 | 34 | def rst2ansi(input_string, output_encoding='utf-8'): 35 | 36 | overrides = {} 37 | overrides['input_encoding'] = 'unicode' 38 | 39 | def style_role(name, rawtext, text, lineno, inliner, options={}, content=[]): 40 | return [nodes.TextElement(rawtext, text, classes=[name])], [] 41 | 42 | for color in COLORS: 43 | roles.register_local_role('ansi-fg-' + color, style_role) 44 | roles.register_local_role('ansi-bg-' + color, style_role) 45 | for style in STYLES: 46 | roles.register_local_role('ansi-' + style, style_role) 47 | 48 | if hasattr(input_string, 'decode'): 49 | input_string = input_string.decode('utf-8') 50 | 51 | out = core.publish_string(input_string, settings_overrides=overrides, writer=Writer(unicode=output_encoding.startswith('utf'))) 52 | return out.decode(output_encoding) 53 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | This document describes the TODO list for supporing the full [reStructuredText specification][rstspec]. 2 | 3 | * [x] Document Structure 4 | * [x] Document 5 | * [x] Sections 6 | * [x] Transitions 7 | * [x] Body Elements 8 | * [x] Paragraphs 9 | * [x] Bullet Lists 10 | * [x] Enumerated Lists 11 | * [x] Definition Lists 12 | * [ ] Field Lists 13 | * [x] Option Lists 14 | * [x] Literal Blocks 15 | * [x] Line Blocks 16 | * [ ] Block Quotes 17 | * [x] Block Quote 18 | * [ ] Attribution 19 | * [ ] Doctest Blocks 20 | * [x] Tables 21 | * [ ] Explicit Markup Blocks 22 | * [ ] Footnotes 23 | * [ ] Citations 24 | * [x] Hyperlink Targets 25 | * [ ] Directives 26 | * [ ] Admonitions 27 | * [x] Images 28 | * [x] Image 29 | * [x] Figure 30 | * [ ] Body Elements 31 | * [ ] Topic 32 | * [ ] Sidebar 33 | * [x] Line Block 34 | * [x] Parsed Literal Block 35 | * [ ] Code 36 | * [ ] Math 37 | * [ ] Rubric 38 | * [ ] Epigraph 39 | * [ ] Highlights 40 | * [ ] Pull-Quote 41 | * [ ] Compound Paragraph 42 | * [ ] Container 43 | * [ ] Tables 44 | * [ ] Table 45 | * [ ] CSV Table 46 | * [ ] List Table 47 | * [ ] Document Parts 48 | * [ ] Table of Contents 49 | * [ ] Automatic Section Numbering 50 | * [ ] Document Header & Footer 51 | * [ ] References 52 | * [ ] Target Footnotes 53 | * [ ] Footnotes 54 | * [ ] Citations 55 | * [ ] Directives for Substitution Definitions 56 | * [ ] Replacement Text 57 | * [ ] Unicode Character Codes 58 | * [ ] Date 59 | * [ ] Miscellaneous 60 | * [x] Including an External Document Fragment 61 | * [x] Raw Data Pass-Through 62 | * [x] Class 63 | * [x] Custom Interpreted Text Roles 64 | * [x] Setting the Default Interpreted Text Role 65 | * [ ] Metadata Document Title 66 | * [x] Substitution Definitions 67 | * [x] Comments 68 | * [ ] Implicit Hyperlink Targets 69 | * [ ] Inline Markup 70 | * [x] Emphasis 71 | * [x] Strong Emphasis 72 | * [x] Interpreted Text 73 | * [x] Inline Literals 74 | * [x] Hyperlink References 75 | * [x] Inline Internal Targets 76 | * [ ] Footnote References 77 | * [ ] Citation References 78 | * [x] Standalone Hyperlinks 79 | * [ ] Error messages 80 | 81 | [rstspec]: http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html 82 | -------------------------------------------------------------------------------- /test/styles.t: -------------------------------------------------------------------------------- 1 | Test foreground colors: 2 | 3 | $ echo ':ansi-fg-black:`black`' > styles.rst 4 | $ echo ':ansi-fg-red:`red`' >> styles.rst 5 | $ echo ':ansi-fg-green:`green`' >> styles.rst 6 | $ echo ':ansi-fg-yellow:`yellow`' >> styles.rst 7 | $ echo ':ansi-fg-blue:`blue`' >> styles.rst 8 | $ echo ':ansi-fg-magenta:`magenta`' >> styles.rst 9 | $ echo ':ansi-fg-cyan:`cyan`' >> styles.rst 10 | $ echo ':ansi-fg-white:`white`' >> styles.rst 11 | $ rst2ansi styles.rst 12 | \x1b[30mblack\x1b[0m \x1b[31mred\x1b[0m \x1b[32mgreen\x1b[0m \x1b[33myellow\x1b[0m \x1b[34mblue\x1b[0m \x1b[35mmagenta\x1b[0m \x1b[36mcyan\x1b[0m \x1b[37mwhite\x1b[0m (esc) 13 | 14 | Test background colors: 15 | 16 | $ echo ':ansi-bg-black:`black`' > styles.rst 17 | $ echo ':ansi-bg-red:`red`' >> styles.rst 18 | $ echo ':ansi-bg-green:`green`' >> styles.rst 19 | $ echo ':ansi-bg-yellow:`yellow`' >> styles.rst 20 | $ echo ':ansi-bg-blue:`blue`' >> styles.rst 21 | $ echo ':ansi-bg-magenta:`magenta`' >> styles.rst 22 | $ echo ':ansi-bg-cyan:`cyan`' >> styles.rst 23 | $ echo ':ansi-bg-white:`white`' >> styles.rst 24 | $ rst2ansi styles.rst 25 | \x1b[40mblack\x1b[0m \x1b[41mred\x1b[0m \x1b[42mgreen\x1b[0m \x1b[43myellow\x1b[0m \x1b[44mblue\x1b[0m \x1b[45mmagenta\x1b[0m \x1b[46mcyan\x1b[0m \x1b[47mwhite\x1b[0m (esc) 26 | 27 | Test styles: 28 | 29 | $ echo ':ansi-bold:`bold`' > styles.rst 30 | $ echo ':ansi-dim:`dim`' >> styles.rst 31 | $ echo ':ansi-italic:`italic`' >> styles.rst 32 | $ echo ':ansi-underline:`underline`' >> styles.rst 33 | $ echo ':ansi-blink:`blink`' >> styles.rst 34 | $ echo ':ansi-blink-fast:`blink-fast`' >> styles.rst 35 | $ echo ':ansi-inverse:`inverse`' >> styles.rst 36 | $ echo ':ansi-conceal:`conceal`' >> styles.rst 37 | $ echo ':ansi-strikethrough:`strikethrough`' >> styles.rst 38 | $ rst2ansi styles.rst 39 | \x1b[1mbold\x1b[0m \x1b[2mdim\x1b[0m \x1b[3mitalic\x1b[0m \x1b[4munderline\x1b[0m \x1b[5mblink\x1b[0m \x1b[6mblink-fast\x1b[0m \x1b[7minverse\x1b[0m \x1b[8mconceal\x1b[0m \x1b[9mstrikethrough\x1b[0m (esc) 40 | 41 | Test standard reStructuredText text decorations: 42 | 43 | $ echo '*emphasis*' > styles.rst 44 | $ echo '**strong emphasis**' >> styles.rst 45 | $ rst2ansi styles.rst 46 | \x1b[3memphasis\x1b[0m \x1b[1mstrong emphasis\x1b[0m (esc) 47 | 48 | Test role coalescing: 49 | 50 | $ echo ' 51 | > .. role:: blue-and-bold 52 | > :class: ansi-bold ansi-fg-blue 53 | > 54 | > :blue-and-bold:`test` 55 | > ' > styles.rst 56 | $ rst2ansi styles.rst 57 | \x1b[1;34mtest\x1b[0m (esc) 58 | -------------------------------------------------------------------------------- /test/paragraph.t: -------------------------------------------------------------------------------- 1 | Test simple paragraph: 2 | 3 | $ echo "Lorem ipsum dolor sit amet" > paragraph.rst 4 | $ rst2ansi paragraph.rst 5 | Lorem ipsum dolor sit amet 6 | 7 | Test multi-line paragraph: 8 | 9 | $ echo "Lorem ipsum dolor sit amet, 10 | > consectetur adipiscing elit." > paragraph.rst 11 | $ rst2ansi paragraph.rst 12 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. 13 | 14 | Test multiple paragraphs: 15 | 16 | $ echo "Lorem ipsum dolor sit amet, consectetur adipiscing elit. 17 | > 18 | > Donec a diam lectus." > paragraph.rst 19 | $ rst2ansi paragraph.rst 20 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. 21 | 22 | Donec a diam lectus. 23 | 24 | Test paragraph wrapping: 25 | 26 | $ echo " 27 | > Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam 28 | > lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra 29 | > nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam 30 | > eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, 31 | > ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula 32 | > ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing 33 | > elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, 34 | > adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. 35 | > Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl 36 | > imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio 37 | > eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum 38 | > sociis natoque penatibus et magnis dis parturient montes, nascetur 39 | > ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem 40 | > facilisis semper ac in est. 41 | > " | tr -d '\n' > long_paragraph.rst 42 | $ rst2ansi long_paragraph.rst 43 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. 44 | Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec 45 | consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero 46 | egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem 47 | lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. 48 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida 49 | lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque 50 | auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit 51 | pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices 52 | accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada 53 | arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, 54 | nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem 55 | facilisis semper ac in est. 56 | -------------------------------------------------------------------------------- /test/lists.t: -------------------------------------------------------------------------------- 1 | Docutils bullet list: 2 | $ echo " 3 | > Docutils Bullet List 4 | > 5 | > - This is item 1 6 | > - This is item 2 7 | > 8 | > - Bullets are '-', '*' or '+'. 9 | > Continuing text must be aligned 10 | > after the bullet and whitespace. 11 | > 12 | > Note that a blank line is required 13 | > before the first item and after the 14 | > last, but is optional between items. 15 | > " > paragraph.rst 16 | $ rst2ansi paragraph.rst 17 | Docutils Bullet List 18 | 19 | \u2022 This is item 1 20 | \u2022 This is item 2 21 | \u2022 Bullets are '-', '*' or '+'. Continuing text must be aligned after the 22 | bullet and whitespace. 23 | 24 | Note that a blank line is required before the first item and after the last, but 25 | is optional between items. 26 | 27 | Docutils enumerated lists: 28 | $ echo " 29 | > Docutils Enumerated List 30 | > 31 | > 3. This is the first item 32 | > 4. This is the second item 33 | > 5. Enumerators are arabic numbers, 34 | > single letters, or roman numerals 35 | > 6. List items should be sequentially 36 | > numbered, but need not start at 1 37 | > (although not all formatters will 38 | > honour the first index). 39 | > #. This item is auto-enumerated 40 | > " > paragraph.rst 41 | $ rst2ansi paragraph.rst 42 | Docutils Enumerated List 43 | 44 | 3. This is the first item 45 | 4. This is the second item 46 | 5. Enumerators are arabic numbers, single letters, or roman numerals 47 | 6. List items should be sequentially numbered, but need not start at 1 48 | (although not all formatters will honour the first index). 49 | 7. This item is auto-enumerated 50 | 51 | Test bullet list: 52 | $ echo " 53 | > Simple bullet list 54 | > 55 | > * list 1 56 | > * list 2 with multiple 57 | > lines 58 | > * list 3 59 | > " > paragraph.rst 60 | $ rst2ansi paragraph.rst 61 | Simple bullet list 62 | 63 | \u2022 list 1 64 | \u2022 list 2 with multiple lines 65 | \u2022 list 3 66 | 67 | Test definition list: 68 | $ echo " 69 | > Definition list 70 | > 71 | > Dinner 72 | > An evening meal. 73 | > 74 | > Lunch 75 | > A meal 76 | > typically 77 | > taken 78 | > mid day 79 | > 80 | > Breakfast 81 | > Morning meal that 'breaks' the overnight 'fast'. 82 | > " > paragraph.rst 83 | $ rst2ansi paragraph.rst 84 | Definition list 85 | 86 | Dinner 87 | An evening meal. 88 | 89 | Lunch 90 | A meal typically taken mid day 91 | 92 | Breakfast 93 | Morning meal that 'breaks' the overnight 'fast'. 94 | 95 | Option list: 96 | $ echo " 97 | > Option List 98 | > 99 | > -a command-line option 'a' 100 | > -b file options can have arguments 101 | > and long descriptions 102 | > --long options can be long also 103 | > --input file long options can also have 104 | > arguments 105 | > TODO: This should work with --input=file also 106 | > /V DOS/VMS-style options too 107 | > --text This is a long text that should span the line so that I can 108 | > see if there is the correct word wrap. If there 109 | > isn't it could be problem, but maybe not. 110 | > " > paragraph.rst 111 | $ rst2ansi paragraph.rst 112 | Option List 113 | 114 | -a 115 | command-line option 'a' 116 | -b file 117 | options can have arguments and long descriptions 118 | --long 119 | options can be long also 120 | --input file 121 | long options can also have arguments TODO: This should work with 122 | --input=file also 123 | /V 124 | DOS/VMS-style options too 125 | --text 126 | This is a long text that should span the line so that I can see if there is 127 | the correct word wrap. If there isn't it could be problem, but maybe 128 | not. 129 | 130 | Check Sub List: 131 | $ echo " 132 | > #. numbered 133 | > #. list 134 | > 135 | > #. and a 136 | > #. sublist 137 | > 138 | > #. end 139 | > 140 | > * unordered 141 | > * list 142 | > " > paragraph.rst 143 | $ rst2ansi paragraph.rst 144 | 1. numbered 145 | 2. list 146 | 1. and a 147 | 2. sublist 148 | 3. end 149 | 150 | \u2022 unordered 151 | \u2022 list 152 | -------------------------------------------------------------------------------- /rst2ansi/get_terminal_size.py: -------------------------------------------------------------------------------- 1 | """This is a backport of shutil.get_terminal_size from Python 3.3. 2 | 3 | The original implementation is in C, but here we use the ctypes and 4 | fcntl modules to create a pure Python version of os.get_terminal_size. 5 | 6 | Pulled from https://github.com/chrippa/backports.shutil_get_terminal_size 7 | 8 | 9 | The MIT License (MIT) 10 | 11 | Copyright (c) 2014 Christopher Rosell 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in 21 | all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | THE SOFTWARE. 30 | """ 31 | 32 | import os 33 | import struct 34 | import sys 35 | 36 | from collections import namedtuple 37 | 38 | __all__ = ["get_terminal_size"] 39 | 40 | 41 | terminal_size = namedtuple("terminal_size", "columns lines") 42 | 43 | try: 44 | from ctypes import windll, create_string_buffer, WinError 45 | 46 | _handle_ids = { 47 | 0: -10, 48 | 1: -11, 49 | 2: -12, 50 | } 51 | 52 | def _get_terminal_size(fd): 53 | handle = windll.kernel32.GetStdHandle(_handle_ids[fd]) 54 | if handle == 0: 55 | raise OSError('handle cannot be retrieved') 56 | if handle == -1: 57 | raise WinError() 58 | csbi = create_string_buffer(22) 59 | res = windll.kernel32.GetConsoleScreenBufferInfo(handle, csbi) 60 | if res: 61 | res = struct.unpack("hhhhHhhhhhh", csbi.raw) 62 | left, top, right, bottom = res[5:9] 63 | columns = right - left + 1 64 | lines = bottom - top + 1 65 | return terminal_size(columns, lines) 66 | else: 67 | raise WinError() 68 | 69 | except ImportError: 70 | import fcntl 71 | import termios 72 | 73 | def _get_terminal_size(fd): 74 | try: 75 | res = fcntl.ioctl(fd, termios.TIOCGWINSZ, b"\x00" * 4) 76 | except IOError as e: 77 | raise OSError(e) 78 | lines, columns = struct.unpack("hh", res) 79 | 80 | return terminal_size(columns, lines) 81 | 82 | 83 | def get_terminal_size(fallback=(80, 24)): 84 | """Get the size of the terminal window. 85 | 86 | For each of the two dimensions, the environment variable, COLUMNS 87 | and LINES respectively, is checked. If the variable is defined and 88 | the value is a positive integer, it is used. 89 | 90 | When COLUMNS or LINES is not defined, which is the common case, 91 | the terminal connected to sys.__stdout__ is queried 92 | by invoking os.get_terminal_size. 93 | 94 | If the terminal size cannot be successfully queried, either because 95 | the system doesn't support querying, or because we are not 96 | connected to a terminal, the value given in fallback parameter 97 | is used. Fallback defaults to (80, 24) which is the default 98 | size used by many terminal emulators. 99 | 100 | The value returned is a named tuple of type os.terminal_size. 101 | """ 102 | # Try the environment first 103 | try: 104 | columns = int(os.environ["COLUMNS"]) 105 | except (KeyError, ValueError): 106 | columns = 0 107 | 108 | try: 109 | lines = int(os.environ["LINES"]) 110 | except (KeyError, ValueError): 111 | lines = 0 112 | 113 | # Only query if necessary 114 | if columns <= 0 or lines <= 0: 115 | try: 116 | size = _get_terminal_size(sys.__stdout__.fileno()) 117 | except (NameError, OSError): 118 | size = terminal_size(*fallback) 119 | 120 | if columns <= 0: 121 | columns = size.columns 122 | if lines <= 0: 123 | lines = size.lines 124 | 125 | return terminal_size(columns, lines) 126 | 127 | -------------------------------------------------------------------------------- /rst2ansi/table.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | The MIT License (MIT) 4 | 5 | Copyright © 2015-2016 Franklin "Snaipe" Mathieu 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | """ 25 | 26 | from __future__ import unicode_literals 27 | 28 | from docutils import nodes 29 | 30 | from textwrap import wrap 31 | 32 | from .unicode import u 33 | 34 | class CellDimCalculator(nodes.NodeVisitor): 35 | 36 | def __init__(self, document, cols, rows, width): 37 | nodes.NodeVisitor.__init__(self, document) 38 | self.cols = cols 39 | self.rows = rows 40 | self.width = width 41 | self.height = 0 42 | 43 | def visit_paragraph(self, node): 44 | first_line = node.astext().split('\n')[0] 45 | 46 | # handle weird table sizing from simple rst tables 47 | # disregard cells spanning multiple columns, as 48 | # these don't contribute to the cell width calculation 49 | if len(first_line) >= self.width: 50 | self.width = len(first_line) + 2 51 | 52 | sublines = wrap(node.astext(), width = self.width) 53 | self.height = int(len(sublines) / self.rows) 54 | raise nodes.StopTraversal 55 | 56 | def visit_table(self, node): 57 | c = TableSizeCalculator(self.document) 58 | node.walkabout(c) 59 | self.height = int(c.height / self.rows) 60 | raise nodes.StopTraversal 61 | 62 | def visit_literal_block(self, node): 63 | self.height = int(len(node.astext().split('\n')) / self.rows) 64 | raise nodes.StopTraversal 65 | 66 | visit_Text = visit_literal_block 67 | 68 | def __getattr__(self, name): 69 | if name.startswith('visit_') or name.startswith('depart_'): 70 | def noop(*args, **kwargs): 71 | pass 72 | return noop 73 | raise AttributeError(name) 74 | 75 | 76 | class TableSizeCalculator(nodes.NodeVisitor): 77 | 78 | def __init__(self, document): 79 | nodes.NodeVisitor.__init__(self, document) 80 | self.level = 0 81 | self.widths = [] 82 | self.heights = [] 83 | self.rows = 0 84 | 85 | def __getattr__(self, name): 86 | if name.startswith('visit_') or name.startswith('depart_'): 87 | def noop(*args, **kwargs): 88 | pass 89 | return noop 90 | raise AttributeError(name) 91 | 92 | def visit_table(self, node): 93 | if self.level > 0: 94 | raise nodes.SkipChildren 95 | self.level += 1 96 | 97 | def depart_table(self, node): 98 | self.width = sum(self.widths) + (len(self.widths) + 1) 99 | self.height = sum(self.heights) + (len(self.heights) + 1) 100 | self.level -= 1 101 | 102 | def visit_tgroup(self, node): 103 | self.cols = node.attributes['cols'] 104 | 105 | def visit_colspec(self, node): 106 | self.widths.append(node.attributes['colwidth']) 107 | 108 | def visit_row(self, node): 109 | self.rows += 1 110 | self.heights.append(1) 111 | self.col = 0 112 | 113 | def visit_entry(self, node): 114 | cols = node.attributes.get('morecols', 0) + 1 115 | rows = node.attributes.get('morerows', 0) + 1 116 | width = sum(self.widths[self.col:self.col + cols]) + (cols - 1) 117 | 118 | c = CellDimCalculator(self.document, cols, rows, width) 119 | node.walkabout(c) 120 | 121 | # Correct invalid column sizing for simple rst tables 122 | if c.width > width and cols == 1: 123 | self.widths[self.col] = c.width 124 | 125 | self.heights[-1] = max(self.heights[-1], c.height) 126 | self.col += 1 127 | raise nodes.SkipChildren 128 | 129 | class TableDrawer(nodes.NodeVisitor): 130 | 131 | def __init__(self, props, document, **options): 132 | nodes.NodeVisitor.__init__(self, document) 133 | self.props = props 134 | self.level = 0 135 | self.lines = [''] 136 | self.line = 0 137 | self.cursor = 0 138 | self.col = 0 139 | self.row = 0 140 | self.nb_rows = 0 141 | self.options = options 142 | 143 | def unicode_intersection(char, next): 144 | switch = { 145 | ('─', '│'): '┬', 146 | ('┐', '│'): '┐', 147 | ('┘', '│'): '┤', 148 | ('┘', '─'): '┴', 149 | ('┴', '│'): '┼', 150 | ('│', '─'): '├', 151 | ('┤', '─'): '┼', 152 | (' ', '─'): '┘', 153 | ('└', '─'): '└', 154 | 155 | ('═', '│'): '╤', 156 | ('╕', '│'): '╕', 157 | ('╛', '│'): '╡', 158 | ('╛', '═'): '╧', 159 | ('╧', '│'): '╪', 160 | ('│', '═'): '╞', 161 | ('╡', '═'): '╪', 162 | (' ', '═'): '╛', 163 | ('╘', '═'): '╘', 164 | } 165 | return switch[(u(char), u(next))] 166 | 167 | if options.get('unicode', False): 168 | self.char_single_rule = '─' 169 | self.char_double_rule = '═' 170 | self.char_vertical_rule = '│' 171 | self.get_intersection = unicode_intersection 172 | self.top_left = '┌' 173 | self.top_right = '┐' 174 | self.bottom_left = '╘' 175 | else: 176 | self.char_single_rule = '-' 177 | self.char_double_rule = '=' 178 | self.char_vertical_rule = '|' 179 | self.get_intersection = lambda *args: '+' 180 | self.top_left = self.bottom_left = self.top_right = '+' 181 | 182 | def __getattr__(self, name): 183 | if name.startswith('visit_') or name.startswith('depart_'): 184 | def noop(*args, **kwargs): 185 | pass 186 | return noop 187 | if name == 'curline': 188 | return self.lines[self.line] 189 | raise AttributeError(name) 190 | 191 | def _draw_rule(self): 192 | self.lines[self.line] += self.top_left + self.char_single_rule * (self.props.width - 2) + self.top_right 193 | self.lines.extend([self.char_vertical_rule + ' ' * (self.props.width - 1)] * (self.props.height - 2)) 194 | self.lines.extend([self.bottom_left + ' ' * (self.props.width - 1)]) 195 | self.line += 1 196 | self.cursor = 0 197 | 198 | def visit_table(self, node): 199 | if self.level > 0: 200 | raise nodes.SkipChildren 201 | self.level += 1 202 | self._draw_rule() 203 | 204 | def depart_table(self, node): 205 | self.level -= 1 206 | 207 | def visit_row(self, node): 208 | self.col = 0 209 | self.cursor = 0 210 | 211 | def depart_row(self, node): 212 | self.line += self.props.heights[self.row] + 1 213 | self.row += 1 214 | self.local_row += 1 215 | 216 | def visit_thead(self, node): 217 | self.nb_rows = len(node.children) 218 | self.local_row = 0 219 | 220 | visit_tbody = visit_thead 221 | 222 | def visit_entry(self, node): 223 | cols = node.attributes.get('morecols', 0) + 1 224 | rows = node.attributes.get('morerows', 0) + 1 225 | 226 | width = sum(self.props.widths[self.col:self.col + cols]) + (cols - 1) 227 | height = sum(self.props.heights[self.row:self.row + rows]) + (rows - 1) 228 | 229 | rule = self.char_double_rule if self.local_row + rows - 1 == self.nb_rows - 1 else self.char_single_rule 230 | sep = self.char_vertical_rule 231 | 232 | # Draw the horizontal rule 233 | 234 | line = self.lines[self.line + height] 235 | int1 = self.get_intersection(line[self.cursor], rule) 236 | int2 = self.get_intersection(line[self.cursor + width + 1], rule) 237 | line = line[:self.cursor] + int1 + (width * rule) + int2 + line[self.cursor + width + 2:] 238 | self.lines[self.line + height] = line 239 | 240 | # Draw the vertical rule 241 | 242 | for i in range(height): 243 | line = self.lines[self.line + i] 244 | line = line[:self.cursor + width + 1] + sep + line[self.cursor + width + 2:] 245 | self.lines[self.line + i] = line 246 | 247 | line = self.lines[self.line - 1] 248 | int3 = self.get_intersection(line[self.cursor + width + 1], sep) 249 | line = line[:self.cursor + width + 1] + int3 + line[self.cursor + width + 2:] 250 | self.lines[self.line - 1] = line 251 | 252 | self.col += cols 253 | self.cursor += width + 1 254 | 255 | # Do not recurse 256 | raise nodes.SkipChildren 257 | 258 | class TableWriter(nodes.NodeVisitor): 259 | 260 | def __init__(self, props, document, **options): 261 | nodes.NodeVisitor.__init__(self, document) 262 | self.props = props 263 | self.level = 0 264 | self.line = 0 265 | self.cursor = 0 266 | self.col = 0 267 | self.row = 0 268 | self.nb_rows = 0 269 | self.options = options 270 | 271 | def __getattr__(self, name): 272 | if name.startswith('visit_') or name.startswith('depart_'): 273 | def noop(*args, **kwargs): 274 | pass 275 | return noop 276 | raise AttributeError(name) 277 | 278 | def visit_table(self, node): 279 | drawer = TableDrawer(self.props, self.document, **self.options) 280 | node.walkabout(drawer) 281 | self.lines = drawer.lines 282 | 283 | def visit_row(self, node): 284 | self.col = 0 285 | self.cursor = 0 286 | 287 | def depart_row(self, node): 288 | self.line += self.props.heights[self.row] + 1 289 | self.row += 1 290 | self.local_row += 1 291 | 292 | def visit_thead(self, node): 293 | self.nb_rows = len(node.children) 294 | self.local_row = 0 295 | 296 | visit_tbody = visit_thead 297 | 298 | def visit_entry(self, node): 299 | cols = node.attributes.get('morecols', 0) + 1 300 | rows = node.attributes.get('morerows', 0) + 1 301 | 302 | width = sum(self.props.widths[self.col:self.col + cols]) + (cols - 1) 303 | height = sum(self.props.heights[self.row:self.row + rows]) + (rows - 1) 304 | 305 | from .ansi import ANSITranslator 306 | 307 | if node.children: 308 | v = ANSITranslator(self.document, termsize=(width - 2, height), **self.options) 309 | node.children[0].walkabout(v) 310 | v.strip_empty_lines() 311 | i = 1 312 | for l in v.lines: 313 | for sl in l.split('\n'): 314 | line = self.lines[self.line + i] 315 | line = line[:self.cursor + 2] + sl + line[self.cursor + 2 + len(sl):] 316 | self.lines[self.line + i] = line 317 | i += 1 318 | 319 | self.col += cols 320 | self.cursor += width + 1 321 | 322 | # Do not recurse 323 | raise nodes.SkipChildren 324 | -------------------------------------------------------------------------------- /rst2ansi/ansi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | The MIT License (MIT) 4 | 5 | Copyright © 2015-2016 Franklin "Snaipe" Mathieu 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | """ 25 | 26 | from __future__ import unicode_literals 27 | 28 | from docutils import core, frontend, nodes, utils, writers, languages, io 29 | from docutils.utils.error_reporting import SafeString 30 | from docutils.transforms import writer_aux 31 | from docutils.parsers.rst import roles 32 | 33 | from copy import deepcopy, copy 34 | from .wrap import wrap 35 | 36 | from .table import TableSizeCalculator, TableWriter 37 | from .unicode import ref_to_unicode, u 38 | 39 | from .get_terminal_size import get_terminal_size 40 | 41 | import shutil 42 | 43 | COLORS = ('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white') 44 | STYLES = ('bold', 'dim', 'italic', 'underline', 'blink', 'blink-fast', 'inverse', 'conceal', 'strikethrough') 45 | 46 | class ANSICodes(object): 47 | 48 | @staticmethod 49 | def get_color_code(code, fg): 50 | FG = 30 51 | BG = 40 52 | FG_256 = 38 53 | BG_256 = 48 54 | 55 | if code in COLORS: 56 | shift = FG if fg else BG 57 | return str(shift + COLORS.index(code)) 58 | elif isinstance(code, int) and 0 <= code <= 255: 59 | shift = FG_256 if fg else BG_256 60 | return str(shift) + ';5;%d' % int(code) 61 | elif not isinstance(code, str) and hasattr(code, "__len__") and len(code) == 3: 62 | for c in code: 63 | if not 0 <= c <= 255: 64 | raise Exception('Invalid color "%s"' % code) 65 | 66 | r, g, b = code 67 | shift = FG_256 if fg else BG_256 68 | return str(shift) + ';2;%d;%d;%d' % (int(r), int(g), int(b)) 69 | 70 | raise Exception('Invalid color "%s"' % code) 71 | 72 | @staticmethod 73 | def get_style_code(code): 74 | if code in STYLES: 75 | return str(1 + STYLES.index(code)) 76 | raise Exception('Invalid style "%s"' % code) 77 | 78 | @staticmethod 79 | def to_ansi(codes): 80 | return '\x1b[' + ';'.join(codes) + 'm' 81 | 82 | NONE = '0' 83 | RESET = to_ansi.__func__(NONE) 84 | 85 | from .functional import npartial 86 | 87 | class ANSITranslator(nodes.NodeVisitor): 88 | 89 | class Context(object): 90 | 91 | def __init__(self): 92 | self.output = '' 93 | self.indent_level = 0 94 | self.in_list = False 95 | self.has_title = False 96 | self.list_counter = 0 97 | self.node_type = '' 98 | 99 | class StyleContext(object): 100 | 101 | def __init__(self): 102 | self.styles = set() 103 | self.fg = ANSICodes.NONE 104 | self.bg = ANSICodes.NONE 105 | 106 | def __init__(self, document, termsize=None, **options): 107 | nodes.NodeVisitor.__init__(self, document) 108 | self.document = document 109 | self.output = '' 110 | self.lines = [''] 111 | self.line = 0 112 | self.indent_width = 2 113 | self.termsize = termsize or get_terminal_size((80,20)) 114 | self.options = options 115 | self.references = [] 116 | self.refcount = 0 117 | 118 | self.ctx = self.Context() 119 | self.ctx_stack = [] 120 | self.style = self.StyleContext() 121 | self.style_stack = [] 122 | 123 | def push_ctx(self, **kwargs): 124 | self.ctx_stack.append(self.ctx) 125 | self.ctx = deepcopy(self.ctx) 126 | for k, v in kwargs.items(): 127 | setattr(self.ctx, k, v) 128 | 129 | def pop_ctx(self): 130 | self.ctx = self.ctx_stack.pop() 131 | 132 | def push_style(self, fg=None, bg=None, styles=[]): 133 | self.style_stack.append(self.style) 134 | self.style = deepcopy(self.style) 135 | if fg: 136 | self.style.fg = ANSICodes.get_color_code(fg, True) 137 | if bg: 138 | self.style.bg = ANSICodes.get_color_code(bg, False) 139 | self.style.styles |= {ANSICodes.get_style_code(s) for s in styles} 140 | 141 | self._restyle() 142 | 143 | def pop_style(self): 144 | self.style = self.style_stack.pop() 145 | reset = self.style.fg == ANSICodes.NONE and \ 146 | self.style.bg == ANSICodes.NONE and \ 147 | not self.style.styles 148 | self._restyle(reset) 149 | 150 | def append(self, *args, **kwargs): 151 | try: 152 | strict = kwargs['strict'] 153 | except KeyError: 154 | strict = False 155 | if len(self.lines[self.line]) == 0 and not strict: 156 | self.lines[self.line] += ' ' * self.ctx.indent_level * self.indent_width 157 | 158 | for a in args: 159 | self.lines[self.line] += u(a) 160 | 161 | def newline(self, n=1): 162 | self.lines.extend([''] * n) 163 | self.line += n 164 | 165 | def prevline(self, n=1): 166 | self.line -= n 167 | 168 | def nextline(self, n=1): 169 | self.line += n 170 | 171 | def popline(self): 172 | l = self.lines.pop(self.line) 173 | self.line -= 1 174 | return l 175 | 176 | def replaceline(self, newline, strict=True): 177 | if strict: 178 | self.lines[self.line] = newline 179 | else: 180 | self.lines[self.line] = '' 181 | self.append(newline) 182 | 183 | def addlines(self, lines, strict=False): 184 | if strict: 185 | self.lines.extend(lines) 186 | self.line += len(lines) 187 | self.newline() 188 | else: 189 | for l in lines: 190 | self.append(l) 191 | self.newline() 192 | 193 | def _restyle(self, reset=False): 194 | if reset: 195 | self.append(ANSICodes.RESET) 196 | 197 | styles = list(self.style.styles) 198 | if self.style.fg != ANSICodes.NONE: 199 | styles.append(self.style.fg) 200 | if self.style.bg != ANSICodes.NONE: 201 | styles.append(self.style.bg) 202 | 203 | if styles: 204 | self.append(ANSICodes.to_ansi(styles)) 205 | 206 | def strip_empty_lines(self): 207 | remove_last_n = 0 208 | for x in self.lines[::-1]: 209 | if len(x.strip()) != 0: 210 | break 211 | remove_last_n += 1 212 | if remove_last_n != 0: 213 | self.lines = self.lines[:-remove_last_n] 214 | 215 | # Structural nodes 216 | 217 | def visit_document(self, node): 218 | self.push_ctx() 219 | 220 | def _print_references(self): 221 | if not self.references: 222 | return 223 | 224 | self.push_style(styles = ['bold']) 225 | self.append('References:') 226 | self.pop_style() 227 | self.newline(2) 228 | 229 | self.push_ctx(indent_level = self.ctx.indent_level + 1) 230 | for ref in self.references: 231 | self.append('[%s]: <' % ref[0]) 232 | self.push_style(fg = 'cyan', styles = ['underline']) 233 | self.append(ref[1]) 234 | self.pop_style() 235 | self.append('>') 236 | self.newline() 237 | self.references = [] 238 | self.pop_ctx() 239 | 240 | def depart_document(self, node): 241 | self._print_references() 242 | self.depart_section(node) 243 | 244 | self.pop_ctx() 245 | self.strip_empty_lines() 246 | 247 | self.output = '\n'.join(self.lines) 248 | 249 | def wrap_current_line(self): 250 | indent = self.ctx.indent_level * self.indent_width 251 | sublines = wrap(self.curline, width = self.termsize[0] - indent, 252 | subsequent_indent = ' ' * indent) 253 | self.popline() 254 | self.addlines(sublines, strict=True) 255 | 256 | def depart_paragraph(self, node): 257 | if self.options.get('wrap_paragraphs', True): 258 | self.wrap_current_line() 259 | if not self.ctx.in_list: 260 | self.newline() 261 | 262 | def visit_title(self, node): 263 | self.push_style(styles=['bold']) 264 | 265 | def depart_title(self, node): 266 | self.pop_style() 267 | self.push_ctx(has_title = True, indent_level = self.ctx.indent_level + 1) 268 | self.newline(2) 269 | 270 | def visit_subtitle(self, node): 271 | self.prevline(2) 272 | self.append(' - ') 273 | 274 | def depart_subtitle(self, node): 275 | self.nextline(2) 276 | 277 | def visit_Text(self, node): 278 | self.append(node.astext()) 279 | 280 | def depart_section(self, node): 281 | if self.ctx.has_title: 282 | self.pop_ctx() 283 | 284 | def depart_transition(self, node): 285 | indent = (self.ctx.indent_level + 2) * self.indent_width 286 | char = '╌' if self.options['unicode'] else '-' 287 | self.append(' ' * indent + char * (self.termsize[0] - 2 * indent) + ' ' * indent, strict=True) 288 | self.newline(2) 289 | 290 | def _get_uri(self, node): 291 | uri = node.attributes.get('refuri', '') 292 | if not uri: 293 | uri = node.attributes.get('uri', '') 294 | return uri 295 | 296 | def visit_reference(self, node): 297 | if self._get_uri(node) == node.astext().strip(): 298 | self.append('<') 299 | self.push_style(fg = 'cyan', styles = ['underline']) 300 | 301 | def depart_reference(self, node): 302 | self.pop_style() 303 | if self._get_uri(node) == node.astext().strip(): 304 | self.append('>') 305 | else: 306 | self.references.append((self.refcount, self._get_uri(node))) 307 | if self.options['unicode'] and self.options.get('unicode_superscript', False): 308 | self.append(ref_to_unicode(self.refcount)) 309 | else: 310 | self.append(' [%s]' % self.refcount) 311 | self.refcount += 1 312 | 313 | # Style nodes 314 | 315 | visit_strong = npartial(push_style, styles=['bold']) 316 | depart_strong = npartial(pop_style) 317 | 318 | visit_emphasis = npartial(push_style, styles=['italic']) 319 | depart_emphasis = npartial(pop_style) 320 | 321 | def visit_TextElement(self, node): 322 | ansi_props = [x[5:] for x in node.attributes['classes'] if x.startswith('ansi-')] 323 | style = { 324 | 'fg': next((x[3:] for x in ansi_props if x.startswith('fg-') and x[3:] in COLORS), None), 325 | 'bg': next((x[3:] for x in ansi_props if x.startswith('bg-') and x[3:] in COLORS), None), 326 | 'styles': (x for x in ansi_props if x in STYLES) 327 | } 328 | self.push_style(**style) 329 | 330 | def depart_TextElement(self, node): 331 | self.pop_style() 332 | 333 | visit_inline = visit_TextElement 334 | depart_inline = depart_TextElement 335 | 336 | # Lists 337 | 338 | def visit_enumerated_list(self, node): 339 | strt = node.attributes.get('start', 1) 340 | self.push_ctx(in_list = True, 341 | list_counter = strt) 342 | 343 | def depart_enumerated_list(self, node): 344 | self.pop_ctx() 345 | if not self.ctx.in_list: 346 | self.newline() 347 | 348 | def visit_bullet_list(self, node): 349 | self.push_ctx(in_list = True, 350 | list_counter = 0) 351 | 352 | def depart_bullet_list(self, node): 353 | self.pop_ctx() 354 | if not self.ctx.in_list: 355 | self.newline() 356 | 357 | def visit_list_item(self, node): 358 | if self.ctx.list_counter: 359 | self.append(str(self.ctx.list_counter) + '. ') 360 | self.ctx.list_counter += 1 361 | else: 362 | self.append('• ' if self.options['unicode'] else '* ') 363 | self.push_ctx(indent_level = self.ctx.indent_level + 1) 364 | 365 | def depart_list_item(self, node): 366 | self.pop_ctx() 367 | 368 | visit_definition_list = npartial(push_ctx, in_list=True, list_counter=0) 369 | depart_definition_list = npartial(pop_ctx) 370 | 371 | def visit_definition(self, node): 372 | self.newline() 373 | self.push_ctx(indent_level = self.ctx.indent_level + 1) 374 | 375 | def depart_definition(self, node): 376 | self.newline() 377 | self.pop_ctx() 378 | 379 | visit_option_list = npartial(push_ctx, in_list=True, list_counter=0) 380 | depart_option_list = npartial(pop_ctx) 381 | 382 | def depart_option(self, node): 383 | self.append(' | ') 384 | 385 | def depart_option_group(self, node): 386 | self.replaceline(self.lines[self.line][:-3], strict=True) 387 | self.push_ctx(indent_level = self.ctx.indent_level + 2) 388 | self.newline() 389 | 390 | def visit_option_argument(self, node): 391 | self.append(' ') 392 | 393 | def depart_option_list_item(self, node): 394 | self.pop_ctx() 395 | 396 | # Tables 397 | 398 | def visit_table(self, node): 399 | props = TableSizeCalculator(self.document) 400 | node.walkabout(props) 401 | 402 | writer = TableWriter(props, self.document, **self.options) 403 | node.walkabout(writer) 404 | self.addlines(writer.lines) 405 | 406 | # Do not recurse 407 | raise nodes.SkipChildren 408 | 409 | def depart_table(self, node): 410 | self.newline() 411 | 412 | # Misc 413 | 414 | def depart_image(self, node): 415 | if type(node.parent) == nodes.figure: 416 | self.visit_reference(node) 417 | self.append('[' + node.attributes.get('alt', 'Image') + ']') 418 | self.depart_reference(node) 419 | self.newline() 420 | else: 421 | self.append('[' + node.attributes.get('alt', 'Image') + ']') 422 | 423 | def depart_caption(self, node): 424 | self.newline(2) 425 | 426 | def visit_substitution_definition(self, node): 427 | raise nodes.SkipChildren 428 | 429 | def visit_comment(self, node): 430 | raise nodes.SkipChildren 431 | 432 | def depart_admonition(self, node): 433 | if self.ctx.has_title: 434 | self.pop_ctx() 435 | 436 | def visit_block_quote(self, node): 437 | self.push_ctx(indent_level = self.ctx.indent_level + 1) 438 | 439 | def depart_block_quote(self, node): 440 | self.pop_ctx() 441 | 442 | def depart_literal_block(self, node): 443 | sublines = self.curline.split('\n') 444 | self.replaceline(sublines[0]) 445 | self.newline() 446 | self.addlines(sublines[1:]) 447 | self.newline() 448 | 449 | def depart_line(self, node): 450 | if len(self.curline.strip()) == 0: 451 | self.newline() 452 | else: 453 | self.wrap_current_line() 454 | 455 | def visit_line_block(self, node): 456 | indent = self.ctx.indent_level + (1 if self.ctx.node_type == 'line_block' else 0) 457 | self.push_ctx(indent_level = indent, node_type = 'line_block') 458 | 459 | def depart_line_block(self, node): 460 | self.pop_ctx() 461 | if self.ctx.node_type != 'line_block': 462 | self.newline() 463 | 464 | def __getattr__(self, name): 465 | if name.startswith('visit_') or name.startswith('depart_'): 466 | def noop(*args, **kwargs): 467 | pass 468 | return noop 469 | if name == 'curline': 470 | return self.lines[self.line] 471 | raise AttributeError(name) 472 | 473 | -------------------------------------------------------------------------------- /demo/demo.tsq: -------------------------------------------------------------------------------- 1 | . ESC 2 | @ 0.200000 3 | |ggdGi| 4 | @ 0.100000 5 | |H| 6 | @ 0.100000 7 | |e| 8 | @ 0.100000 9 | |l| 10 | @ 0.100000 11 | |l| 12 | @ 0.100000 13 | |o| 14 | @ 0.100000 15 | |,| 16 | @ 0.100000 17 | | | 18 | @ 0.100000 19 | |w| 20 | @ 0.100000 21 | |o| 22 | @ 0.100000 23 | |r| 24 | @ 0.100000 25 | |l| 26 | @ 0.100000 27 | |d| 28 | @ 0.100000 29 | |!| 30 | @ 0.000815 31 | . LF/^J 32 | @ 0.000815 33 | . ESC 34 | @ 0.000815 35 | |:w| 36 | @ 0.000815 37 | . LF/^J 38 | 39 | @ 1.000000 40 | |13i=| 41 | @ 0.000815 42 | . ESC 43 | @ 0.200000 44 | |o| 45 | @ 0.100000 46 | |N| 47 | @ 0.100000 48 | |i| 49 | @ 0.100000 50 | |c| 51 | @ 0.100000 52 | |e| 53 | @ 0.100000 54 | | | 55 | @ 0.100000 56 | |d| 57 | @ 0.100000 58 | |a| 59 | @ 0.100000 60 | |y| 61 | @ 0.100000 62 | | | 63 | @ 0.100000 64 | |t| 65 | @ 0.100000 66 | |o| 67 | @ 0.100000 68 | |d| 69 | @ 0.100000 70 | |a| 71 | @ 0.100000 72 | |y| 73 | @ 0.100000 74 | |.| 75 | @ 0.000815 76 | . LF/^J 77 | @ 0.000100 78 | . ESC 79 | 80 | @ 0.200000 81 | |15i~| 82 | @ 0.000815 83 | . ESC 84 | @ 0.200000 85 | |:w| 86 | @ 0.000815 87 | . LF/^J 88 | 89 | @ 0.200000 90 | |o| 91 | @ 0.100815 92 | . LF/^J 93 | @ 0.100000 94 | |I| 95 | @ 0.100000 96 | | | 97 | @ 0.100000 98 | |*| 99 | @ 0.100000 100 | |l| 101 | @ 0.100000 102 | |o| 103 | @ 0.100000 104 | |v| 105 | @ 0.100000 106 | |e| 107 | @ 0.100000 108 | |*| 109 | @ 0.100000 110 | | | 111 | @ 0.100000 112 | |r| 113 | @ 0.100000 114 | |e| 115 | @ 0.100000 116 | |S| 117 | @ 0.100000 118 | |t| 119 | @ 0.100000 120 | |r| 121 | @ 0.100000 122 | |u| 123 | @ 0.100000 124 | |c| 125 | @ 0.100000 126 | |t| 127 | @ 0.100000 128 | |u| 129 | @ 0.100000 130 | |r| 131 | @ 0.100000 132 | |e| 133 | @ 0.100000 134 | |d| 135 | @ 0.100000 136 | |T| 137 | @ 0.100000 138 | |e| 139 | @ 0.100000 140 | |x| 141 | @ 0.100000 142 | |t| 143 | @ 0.100000 144 | |!| 145 | @ 0.000815 146 | . LF/^J 147 | @ 0.000100 148 | . ESC 149 | 150 | @ 0.200000 151 | |:w| 152 | @ 0.000815 153 | . LF/^J 154 | 155 | @ 0.200000 156 | |o| 157 | @ 0.100000 158 | |B| 159 | @ 0.100000 160 | |u| 161 | @ 0.100000 162 | |t| 163 | @ 0.100000 164 | | | 165 | @ 0.100000 166 | |I| 167 | @ 0.100000 168 | | | 169 | @ 0.100000 170 | |a| 171 | @ 0.100000 172 | |l| 173 | @ 0.100000 174 | |s| 175 | @ 0.100000 176 | |o| 177 | @ 0.100000 178 | | | 179 | @ 0.100000 180 | |l| 181 | @ 0.100000 182 | |o| 183 | @ 0.100000 184 | |v| 185 | @ 0.100000 186 | |e| 187 | @ 0.100000 188 | | | 189 | @ 0.100000 190 | |m| 191 | @ 0.100000 192 | |y| 193 | @ 0.100000 194 | | | 195 | @ 0.100000 196 | |t| 197 | @ 0.100000 198 | |e| 199 | @ 0.100000 200 | |r| 201 | @ 0.100000 202 | |m| 203 | @ 0.100000 204 | |i| 205 | @ 0.100000 206 | |n| 207 | @ 0.100000 208 | |a| 209 | @ 0.100000 210 | |l| 211 | @ 0.100000 212 | |.| 213 | @ 0.000815 214 | . LF/^J 215 | @ 0.000100 216 | . ESC 217 | 218 | @ 0.200000 219 | |:w| 220 | @ 0.000815 221 | . LF/^J 222 | 223 | @ 0.200000 224 | |o| 225 | @ 0.100000 226 | |S| 227 | @ 0.100000 228 | |o| 229 | @ 0.100000 230 | | | 231 | @ 0.100000 232 | |I| 233 | @ 0.100000 234 | | | 235 | @ 0.100000 236 | |m| 237 | @ 0.100000 238 | |a| 239 | @ 0.100000 240 | |d| 241 | @ 0.100000 242 | |e| 243 | @ 0.100000 244 | | | 245 | @ 0.100000 246 | |*| 247 | @ 0.100000 248 | |*| 249 | @ 0.100000 250 | |r| 251 | @ 0.100000 252 | |s| 253 | @ 0.100000 254 | |t| 255 | @ 0.100000 256 | |2| 257 | @ 0.100000 258 | |a| 259 | @ 0.100000 260 | |n| 261 | @ 0.100000 262 | |s| 263 | @ 0.100000 264 | |i| 265 | @ 0.100000 266 | |*| 267 | @ 0.100000 268 | |*| 269 | @ 0.100000 270 | |,| 271 | @ 0.100000 272 | | | 273 | @ 0.100000 274 | |t| 275 | @ 0.100000 276 | |o| 277 | @ 0.100000 278 | | | 279 | @ 0.100000 280 | |r| 281 | @ 0.100000 282 | |e| 283 | @ 0.100000 284 | |n| 285 | @ 0.100000 286 | |d| 287 | @ 0.100000 288 | |e| 289 | @ 0.100000 290 | |r| 291 | @ 0.100000 292 | | | 293 | @ 0.100000 294 | |r| 295 | @ 0.100000 296 | |e| 297 | @ 0.100000 298 | |S| 299 | @ 0.100000 300 | |t| 301 | @ 0.100000 302 | |r| 303 | @ 0.100000 304 | |u| 305 | @ 0.100000 306 | |c| 307 | @ 0.100000 308 | |t| 309 | @ 0.100000 310 | |u| 311 | @ 0.100000 312 | |r| 313 | @ 0.100000 314 | |e| 315 | @ 0.100000 316 | |d| 317 | @ 0.100000 318 | |T| 319 | @ 0.100000 320 | |e| 321 | @ 0.100000 322 | |x| 323 | @ 0.100000 324 | |t| 325 | @ 0.100000 326 | | | 327 | @ 0.100000 328 | |a| 329 | @ 0.100000 330 | |s| 331 | @ 0.100000 332 | | | 333 | @ 0.100000 334 | |s| 335 | @ 0.100000 336 | |o| 337 | @ 0.100000 338 | |m| 339 | @ 0.100000 340 | |e| 341 | @ 0.100000 342 | | | 343 | @ 0.100000 344 | |A| 345 | @ 0.100000 346 | |N| 347 | @ 0.100000 348 | |S| 349 | @ 0.100000 350 | |I| 351 | @ 0.100000 352 | |-| 353 | @ 0.100000 354 | |d| 355 | @ 0.100000 356 | |e| 357 | @ 0.100000 358 | |c| 359 | @ 0.100000 360 | |o| 361 | @ 0.100000 362 | |r| 363 | @ 0.100000 364 | |a| 365 | @ 0.100000 366 | |t| 367 | @ 0.100000 368 | |e| 369 | @ 0.100000 370 | |d| 371 | @ 0.100000 372 | | | 373 | @ 0.100000 374 | |o| 375 | @ 0.100000 376 | |u| 377 | @ 0.100000 378 | |t| 379 | @ 0.100000 380 | |p| 381 | @ 0.100000 382 | |u| 383 | @ 0.100000 384 | |t| 385 | @ 0.100000 386 | |,| 387 | @ 0.100000 388 | | | 389 | @ 0.100000 390 | |s| 391 | @ 0.100000 392 | |u| 393 | @ 0.100000 394 | |i| 395 | @ 0.100000 396 | |t| 397 | @ 0.100000 398 | |a| 399 | @ 0.100000 400 | |b| 401 | @ 0.100000 402 | |l| 403 | @ 0.100000 404 | |e| 405 | @ 0.100000 406 | | | 407 | @ 0.100000 408 | |f| 409 | @ 0.100000 410 | |o| 411 | @ 0.100000 412 | |r| 413 | @ 0.100000 414 | | | 415 | @ 0.100000 416 | |t| 417 | @ 0.100000 418 | |e| 419 | @ 0.100000 420 | |r| 421 | @ 0.100000 422 | |m| 423 | @ 0.100000 424 | |i| 425 | @ 0.100000 426 | |n| 427 | @ 0.100000 428 | |a| 429 | @ 0.100000 430 | |l| 431 | @ 0.100000 432 | |s| 433 | @ 0.100000 434 | |.| 435 | @ 0.100000 436 | . LF/^J 437 | @ 0.100000 438 | . ESC 439 | @ 0.200000 440 | |:| 441 | @ 0.100000 442 | |w| 443 | @ 0.100000 444 | . LF/^J 445 | 446 | @ 0.100000 447 | |o| 448 | @ 0.100000 449 | |-| 450 | @ 0.100000 451 | |-| 452 | @ 0.100000 453 | |-| 454 | @ 0.100000 455 | |-| 456 | @ 0.100000 457 | |-| 458 | @ 0.100000 459 | |-| 460 | @ 0.100000 461 | |-| 462 | @ 0.100000 463 | |-| 464 | @ 0.100000 465 | |-| 466 | @ 0.100000 467 | |-| 468 | @ 0.100000 469 | . LF/^J 470 | @ 0.100000 471 | . LF/^J 472 | @ 0.100000 473 | |P| 474 | @ 0.100000 475 | |a| 476 | @ 0.100000 477 | |r| 478 | @ 0.100000 479 | |a| 480 | @ 0.100000 481 | |g| 482 | @ 0.100000 483 | |r| 484 | @ 0.100000 485 | |a| 486 | @ 0.100000 487 | |p| 488 | @ 0.100000 489 | |h| 490 | @ 0.100000 491 | |s| 492 | @ 0.100000 493 | | | 494 | @ 0.100000 495 | |a| 496 | @ 0.100000 497 | |r| 498 | @ 0.100000 499 | |e| 500 | @ 0.100000 501 | | | 502 | @ 0.100000 503 | |a| 504 | @ 0.100000 505 | |u| 506 | @ 0.100000 507 | |t| 508 | @ 0.100000 509 | |o| 510 | @ 0.100000 511 | |m| 512 | @ 0.100000 513 | |a| 514 | @ 0.100000 515 | |t| 516 | @ 0.100000 517 | |i| 518 | @ 0.100000 519 | |c| 520 | @ 0.100000 521 | |a| 522 | @ 0.100000 523 | |l| 524 | @ 0.100000 525 | |l| 526 | @ 0.100000 527 | |y| 528 | @ 0.100000 529 | | | 530 | @ 0.100000 531 | |w| 532 | @ 0.100000 533 | |r| 534 | @ 0.100000 535 | |a| 536 | @ 0.100000 537 | |p| 538 | @ 0.100000 539 | |p| 540 | @ 0.100000 541 | |e| 542 | @ 0.100000 543 | |d| 544 | @ 0.100000 545 | | | 546 | @ 0.100000 547 | |t| 548 | @ 0.100000 549 | |o| 550 | @ 0.100000 551 | | | 552 | @ 0.100000 553 | |f| 554 | @ 0.100000 555 | |i| 556 | @ 0.100000 557 | |t| 558 | @ 0.100000 559 | | | 560 | @ 0.100000 561 | |i| 562 | @ 0.100000 563 | |n| 564 | @ 0.100000 565 | | | 566 | @ 0.100000 567 | |t| 568 | @ 0.100000 569 | |h| 570 | @ 0.100000 571 | |e| 572 | @ 0.100000 573 | | | 574 | @ 0.100000 575 | |c| 576 | @ 0.100000 577 | |u| 578 | @ 0.100000 579 | |r| 580 | @ 0.100000 581 | |r| 582 | @ 0.100000 583 | |e| 584 | @ 0.100000 585 | |n| 586 | @ 0.100000 587 | |t| 588 | @ 0.100000 589 | | | 590 | @ 0.100000 591 | |t| 592 | @ 0.100000 593 | |e| 594 | @ 0.100000 595 | |r| 596 | @ 0.100000 597 | |m| 598 | @ 0.100000 599 | |i| 600 | @ 0.100000 601 | |n| 602 | @ 0.100000 603 | |a| 604 | @ 0.100000 605 | |l| 606 | @ 0.100000 607 | | | 608 | @ 0.100000 609 | |w| 610 | @ 0.100000 611 | |i| 612 | @ 0.100000 613 | |n| 614 | @ 0.100000 615 | |d| 616 | @ 0.100000 617 | |o| 618 | @ 0.100000 619 | |w| 620 | @ 0.100000 621 | |.| 622 | @ 0.100000 623 | . LF/^J 624 | @ 0.100000 625 | . ESC 626 | @ 0.200000 627 | |:| 628 | @ 0.100000 629 | |w| 630 | @ 0.100000 631 | . LF/^J 632 | 633 | @ 1.500000 634 | |d| 635 | @ 0.100000 636 | |9| 637 | @ 0.100000 638 | |k| 639 | @ 0.100000 640 | . ESC 641 | @ 0.200000 642 | |o| 643 | @ 0.100000 644 | |P| 645 | @ 0.100000 646 | |a| 647 | @ 0.100000 648 | |r| 649 | @ 0.100000 650 | |a| 651 | @ 0.100000 652 | |g| 653 | @ 0.100000 654 | |r| 655 | @ 0.100000 656 | |a| 657 | @ 0.100000 658 | |p| 659 | @ 0.100000 660 | |h| 661 | @ 0.100000 662 | |s| 663 | @ 0.100000 664 | | | 665 | @ 0.100000 666 | |a| 667 | @ 0.100000 668 | |r| 669 | @ 0.100000 670 | |e| 671 | @ 0.100000 672 | | | 673 | @ 0.100000 674 | |i| 675 | @ 0.100000 676 | |n| 677 | @ 0.100000 678 | |d| 679 | @ 0.100000 680 | |e| 681 | @ 0.100000 682 | |n| 683 | @ 0.100000 684 | |t| 685 | @ 0.100000 686 | |e| 687 | @ 0.100000 688 | |d| 689 | @ 0.100000 690 | | | 691 | @ 0.100000 692 | |t| 693 | @ 0.100000 694 | |o| 695 | @ 0.100000 696 | | | 697 | @ 0.100000 698 | |r| 699 | @ 0.100000 700 | |e| 701 | @ 0.100000 702 | |f| 703 | @ 0.100000 704 | |l| 705 | @ 0.100000 706 | |e| 707 | @ 0.100000 708 | |c| 709 | @ 0.100000 710 | |t| 711 | @ 0.100000 712 | | | 713 | @ 0.100000 714 | |t| 715 | @ 0.100000 716 | |h| 717 | @ 0.100000 718 | |e| 719 | @ 0.100000 720 | | | 721 | @ 0.100000 722 | |s| 723 | @ 0.100000 724 | |e| 725 | @ 0.100000 726 | |c| 727 | @ 0.100000 728 | |t| 729 | @ 0.100000 730 | |i| 731 | @ 0.100000 732 | |o| 733 | @ 0.100000 734 | |n| 735 | @ 0.100000 736 | | | 737 | @ 0.100000 738 | |t| 739 | @ 0.100000 740 | |h| 741 | @ 0.100000 742 | |e| 743 | @ 0.100000 744 | |y| 745 | @ 0.100000 746 | | | 747 | @ 0.100000 748 | |a| 749 | @ 0.100000 750 | |r| 751 | @ 0.100000 752 | |e| 753 | @ 0.100000 754 | | | 755 | @ 0.100000 756 | |i| 757 | @ 0.100000 758 | |n| 759 | @ 0.100000 760 | |.| 761 | @ 0.100000 762 | . LF/^J 763 | @ 0.100000 764 | . ESC 765 | @ 0.200000 766 | |:| 767 | @ 0.100000 768 | |w| 769 | @ 0.100000 770 | . LF/^J 771 | @ 0.100000 772 | |o| 773 | @ 0.100000 774 | |T| 775 | @ 0.100000 776 | |h| 777 | @ 0.100000 778 | |i| 779 | @ 0.100000 780 | |s| 781 | @ 0.100000 782 | | | 783 | @ 0.100000 784 | |i| 785 | @ 0.100000 786 | |s| 787 | @ 0.100000 788 | | | 789 | @ 0.100000 790 | |a| 791 | @ 0.100000 792 | | | 793 | @ 0.100000 794 | |s| 795 | @ 0.100000 796 | |u| 797 | @ 0.100000 798 | |b| 799 | @ 0.100000 800 | |s| 801 | @ 0.100000 802 | |e| 803 | @ 0.100000 804 | |c| 805 | @ 0.100000 806 | |t| 807 | @ 0.100000 808 | |i| 809 | @ 0.100000 810 | |o| 811 | @ 0.100000 812 | |n| 813 | @ 0.100000 814 | . LF/^J 815 | @ 0.100000 816 | . ESC 817 | @ 0.200000 818 | |2| 819 | @ 0.100000 820 | |0| 821 | @ 0.100000 822 | |i| 823 | @ 0.100000 824 | |-| 825 | @ 0.100000 826 | . ESC 827 | @ 0.200000 828 | |o| 829 | @ 0.100000 830 | . LF/^J 831 | @ 0.100000 832 | |A| 833 | @ 0.100000 834 | |n| 835 | @ 0.100000 836 | |d| 837 | @ 0.100000 838 | | | 839 | @ 0.100000 840 | |a| 841 | @ 0.100000 842 | | | 843 | @ 0.100000 844 | |s| 845 | @ 0.100000 846 | |u| 847 | @ 0.100000 848 | |b| 849 | @ 0.100000 850 | |s| 851 | @ 0.100000 852 | |u| 853 | @ 0.100000 854 | |b| 855 | @ 0.100000 856 | |s| 857 | @ 0.100000 858 | |e| 859 | @ 0.100000 860 | |c| 861 | @ 0.100000 862 | |t| 863 | @ 0.100000 864 | |i| 865 | @ 0.100000 866 | |o| 867 | @ 0.100000 868 | |n| 869 | @ 0.100000 870 | . LF/^J 871 | @ 0.100000 872 | . ESC 873 | @ 0.400000 874 | |2| 875 | @ 0.100000 876 | |0| 877 | @ 0.100000 878 | |i| 879 | @ 0.100000 880 | |'| 881 | @ 0.100000 882 | . ESC 883 | @ 0.400000 884 | |o| 885 | @ 0.100000 886 | . LF/^J 887 | @ 0.100000 888 | |T| 889 | @ 0.100000 890 | |h| 891 | @ 0.100000 892 | |i| 893 | @ 0.100000 894 | |s| 895 | @ 0.100000 896 | | | 897 | @ 0.100000 898 | |v| 899 | @ 0.100000 900 | |i| 901 | @ 0.100000 902 | |s| 903 | @ 0.100000 904 | |u| 905 | @ 0.100000 906 | |a| 907 | @ 0.100000 908 | |l| 909 | @ 0.100000 910 | |l| 911 | @ 0.100000 912 | |y| 913 | @ 0.100000 914 | | | 915 | @ 0.100000 916 | |s| 917 | @ 0.100000 918 | |t| 919 | @ 0.100000 920 | |r| 921 | @ 0.100000 922 | |u| 923 | @ 0.100000 924 | |c| 925 | @ 0.100000 926 | |t| 927 | @ 0.100000 928 | |u| 929 | @ 0.100000 930 | |r| 931 | @ 0.100000 932 | |e| 933 | @ 0.100000 934 | |s| 935 | @ 0.100000 936 | | | 937 | @ 0.100000 938 | |t| 939 | @ 0.100000 940 | |h| 941 | @ 0.100000 942 | |e| 943 | @ 0.100000 944 | | | 945 | @ 0.100000 946 | |d| 947 | @ 0.100000 948 | |o| 949 | @ 0.100000 950 | |c| 951 | @ 0.100000 952 | |u| 953 | @ 0.100000 954 | |m| 955 | @ 0.100000 956 | |e| 957 | @ 0.100000 958 | |n| 959 | @ 0.100000 960 | |t| 961 | @ 0.100000 962 | | | 963 | @ 0.100000 964 | |f| 965 | @ 0.100000 966 | |o| 967 | @ 0.100000 968 | |r| 969 | @ 0.100000 970 | | | 971 | @ 0.100000 972 | |b| 973 | @ 0.100000 974 | |e| 975 | @ 0.100000 976 | |t| 977 | @ 0.100000 978 | |t| 979 | @ 0.100000 980 | |e| 981 | @ 0.100000 982 | |r| 983 | @ 0.100000 984 | | | 985 | @ 0.100000 986 | |r| 987 | @ 0.100000 988 | |e| 989 | @ 0.100000 990 | |a| 991 | @ 0.100000 992 | |d| 993 | @ 0.100000 994 | |a| 995 | @ 0.100000 996 | |b| 997 | @ 0.100000 998 | |i| 999 | @ 0.100000 1000 | |l| 1001 | @ 0.100000 1002 | |i| 1003 | @ 0.100000 1004 | |t| 1005 | @ 0.100000 1006 | |y| 1007 | @ 0.100000 1008 | |.| 1009 | @ 0.100000 1010 | . LF/^J 1011 | @ 0.100000 1012 | . ESC 1013 | @ 0.200000 1014 | |:| 1015 | @ 0.100000 1016 | |w| 1017 | @ 0.100000 1018 | . LF/^J 1019 | 1020 | @ 1.500000 1021 | |d| 1022 | @ 0.100000 1023 | |9| 1024 | @ 0.100000 1025 | |k| 1026 | @ 0.100000 1027 | . ESC 1028 | @ 0.200000 1029 | |o| 1030 | @ 0.100000 1031 | |T| 1032 | @ 0.100000 1033 | |a| 1034 | @ 0.100000 1035 | |b| 1036 | @ 0.100000 1037 | |l| 1038 | @ 0.100000 1039 | |e| 1040 | @ 0.100000 1041 | |s| 1042 | @ 0.100000 1043 | | | 1044 | @ 0.100000 1045 | |a| 1046 | @ 0.100000 1047 | |r| 1048 | @ 0.100000 1049 | |e| 1050 | @ 0.100000 1051 | | | 1052 | @ 0.100000 1053 | |p| 1054 | @ 0.100000 1055 | |r| 1056 | @ 0.100000 1057 | |e| 1058 | @ 0.100000 1059 | |t| 1060 | @ 0.100000 1061 | |t| 1062 | @ 0.100000 1063 | |y| 1064 | @ 0.100000 1065 | |-| 1066 | @ 0.100000 1067 | |p| 1068 | @ 0.100000 1069 | |r| 1070 | @ 0.100000 1071 | |i| 1072 | @ 0.100000 1073 | |n| 1074 | @ 0.100000 1075 | |t| 1076 | @ 0.100000 1077 | |e| 1078 | @ 0.100000 1079 | |d| 1080 | @ 0.100000 1081 | | | 1082 | @ 0.100000 1083 | |w| 1084 | @ 0.100000 1085 | |i| 1086 | @ 0.100000 1087 | |t| 1088 | @ 0.100000 1089 | |h| 1090 | @ 0.100000 1091 | | | 1092 | @ 0.100000 1093 | |b| 1094 | @ 0.100000 1095 | |o| 1096 | @ 0.100000 1097 | |x| 1098 | @ 0.100000 1099 | |-| 1100 | @ 0.100000 1101 | |d| 1102 | @ 0.100000 1103 | |r| 1104 | @ 0.100000 1105 | |a| 1106 | @ 0.100000 1107 | |w| 1108 | @ 0.100000 1109 | |i| 1110 | @ 0.100000 1111 | |n| 1112 | @ 0.100000 1113 | |g| 1114 | @ 0.100000 1115 | | | 1116 | @ 0.100000 1117 | |c| 1118 | @ 0.100000 1119 | |h| 1120 | @ 0.100000 1121 | |a| 1122 | @ 0.100000 1123 | |r| 1124 | @ 0.100000 1125 | |a| 1126 | @ 0.100000 1127 | |c| 1128 | @ 0.100000 1129 | |t| 1130 | @ 0.100000 1131 | |e| 1132 | @ 0.100000 1133 | |r| 1134 | @ 0.100000 1135 | |s| 1136 | @ 0.100000 1137 | | | 1138 | @ 0.100000 1139 | |i| 1140 | @ 0.100000 1141 | |f| 1142 | @ 0.100000 1143 | | | 1144 | @ 0.100000 1145 | |y| 1146 | @ 0.100000 1147 | |o| 1148 | @ 0.100000 1149 | |u| 1150 | @ 0.100000 1151 | |r| 1152 | @ 0.100000 1153 | | | 1154 | @ 0.100000 1155 | |t| 1156 | @ 0.100000 1157 | |e| 1158 | @ 0.100000 1159 | |r| 1160 | @ 0.100000 1161 | |m| 1162 | @ 0.100000 1163 | |i| 1164 | @ 0.100000 1165 | |n| 1166 | @ 0.100000 1167 | |a| 1168 | @ 0.100000 1169 | |l| 1170 | @ 0.100000 1171 | | | 1172 | @ 0.100000 1173 | |s| 1174 | @ 0.100000 1175 | |u| 1176 | @ 0.100000 1177 | |p| 1178 | @ 0.100000 1179 | |p| 1180 | @ 0.100000 1181 | |o| 1182 | @ 0.100000 1183 | |r| 1184 | @ 0.100000 1185 | |t| 1186 | @ 0.100000 1187 | |s| 1188 | @ 0.100000 1189 | | | 1190 | @ 0.100000 1191 | |u| 1192 | @ 0.100000 1193 | |n| 1194 | @ 0.100000 1195 | |i| 1196 | @ 0.100000 1197 | |c| 1198 | @ 0.100000 1199 | |o| 1200 | @ 0.100000 1201 | |d| 1202 | @ 0.100000 1203 | |e| 1204 | @ 0.100000 1205 | |,| 1206 | @ 0.100000 1207 | | | 1208 | @ 0.100000 1209 | |a| 1210 | @ 0.100000 1211 | |n| 1212 | @ 0.100000 1213 | |d| 1214 | @ 0.100000 1215 | | | 1216 | @ 0.100000 1217 | |f| 1218 | @ 0.100000 1219 | |a| 1220 | @ 0.100000 1221 | |l| 1222 | @ 0.100000 1223 | |l| 1224 | @ 0.100000 1225 | |s| 1226 | @ 0.100000 1227 | | | 1228 | @ 0.100000 1229 | |b| 1230 | @ 0.100000 1231 | |a| 1232 | @ 0.100000 1233 | |c| 1234 | @ 0.100000 1235 | |k| 1236 | @ 0.100000 1237 | | | 1238 | @ 0.100000 1239 | |t| 1240 | @ 0.100000 1241 | |o| 1242 | @ 0.100000 1243 | | | 1244 | @ 0.100000 1245 | |a| 1246 | @ 0.100000 1247 | |s| 1248 | @ 0.100000 1249 | |c| 1250 | @ 0.100000 1251 | |i| 1252 | @ 0.100000 1253 | |i| 1254 | @ 0.100000 1255 | | | 1256 | @ 0.100000 1257 | |i| 1258 | @ 0.100000 1259 | |f| 1260 | @ 0.100000 1261 | | | 1262 | @ 0.100000 1263 | |i| 1264 | @ 0.100000 1265 | |t| 1266 | @ 0.100000 1267 | | | 1268 | @ 0.100000 1269 | |d| 1270 | @ 0.100000 1271 | |o| 1272 | @ 0.100000 1273 | |e| 1274 | @ 0.100000 1275 | |s| 1276 | @ 0.100000 1277 | | | 1278 | @ 0.100000 1279 | |n| 1280 | @ 0.100000 1281 | |o| 1282 | @ 0.100000 1283 | |t| 1284 | @ 0.100000 1285 | |.| 1286 | @ 0.100000 1287 | . LF/^J 1288 | @ 0.100000 1289 | . ESC 1290 | @ 0.200000 1291 | |:| 1292 | @ 0.100000 1293 | |s| 1294 | @ 0.100000 1295 | |e| 1296 | @ 0.100000 1297 | |t| 1298 | @ 0.100000 1299 | | | 1300 | @ 0.100000 1301 | |p| 1302 | @ 0.100000 1303 | |a| 1304 | @ 0.100000 1305 | |s| 1306 | @ 0.100000 1307 | |t| 1308 | @ 0.100000 1309 | |e| 1310 | @ 0.100000 1311 | . LF/^J 1312 | @ 0.100000 1313 | |o===== ===== ======| 1314 | . LF/^J 1315 | | Inputs Output| 1316 | . LF/^J 1317 | |------------ ------| 1318 | . LF/^J 1319 | | A B A or B| 1320 | . LF/^J 1321 | |===== ===== ======| 1322 | . LF/^J 1323 | |False False False| 1324 | . LF/^J 1325 | |True False True| 1326 | . LF/^J 1327 | |False True True| 1328 | . LF/^J 1329 | |True True True| 1330 | . LF/^J 1331 | |===== ===== ======| 1332 | . LF/^J 1333 | @ 0.100000 1334 | . ESC 1335 | @ 0.200000 1336 | |:| 1337 | @ 0.100000 1338 | |s| 1339 | @ 0.100000 1340 | |e| 1341 | @ 0.100000 1342 | |t| 1343 | @ 0.100000 1344 | | | 1345 | @ 0.100000 1346 | |n| 1347 | @ 0.100000 1348 | |o| 1349 | @ 0.100000 1350 | |p| 1351 | @ 0.100000 1352 | |a| 1353 | @ 0.100000 1354 | |s| 1355 | @ 0.100000 1356 | |t| 1357 | @ 0.100000 1358 | |e| 1359 | @ 0.100000 1360 | . ESC 1361 | @ 0.200000 1362 | |:| 1363 | @ 0.100000 1364 | |w| 1365 | @ 0.100000 1366 | . LF/^J 1367 | 1368 | @ 1.500000 1369 | |d| 1370 | @ 0.100000 1371 | |1| 1372 | @ 0.100000 1373 | |2| 1374 | @ 0.100000 1375 | |k| 1376 | @ 0.100000 1377 | . ESC 1378 | @ 0.200000 1379 | |o| 1380 | @ 0.100000 1381 | |R| 1382 | @ 0.100000 1383 | |S| 1384 | @ 0.100000 1385 | |T| 1386 | @ 0.100000 1387 | | | 1388 | @ 0.100000 1389 | |c| 1390 | @ 0.100000 1391 | |l| 1392 | @ 0.100000 1393 | |a| 1394 | @ 0.100000 1395 | |s| 1396 | @ 0.100000 1397 | |s| 1398 | @ 0.100000 1399 | |e| 1400 | @ 0.100000 1401 | |s| 1402 | @ 0.100000 1403 | | | 1404 | @ 0.100000 1405 | |a| 1406 | @ 0.100000 1407 | |r| 1408 | @ 0.100000 1409 | |e| 1410 | @ 0.100000 1411 | | | 1412 | @ 0.100000 1413 | |a| 1414 | @ 0.100000 1415 | |v| 1416 | @ 0.100000 1417 | |a| 1418 | @ 0.100000 1419 | |i| 1420 | @ 0.100000 1421 | |l| 1422 | @ 0.100000 1423 | |a| 1424 | @ 0.100000 1425 | |b| 1426 | @ 0.100000 1427 | |l| 1428 | @ 0.100000 1429 | |e| 1430 | @ 0.100000 1431 | | | 1432 | @ 0.100000 1433 | |f| 1434 | @ 0.100000 1435 | |o| 1436 | @ 0.100000 1437 | |r| 1438 | @ 0.100000 1439 | | | 1440 | @ 0.100000 1441 | |u| 1442 | @ 0.100000 1443 | |s| 1444 | @ 0.100000 1445 | |e| 1446 | @ 0.100000 1447 | |r| 1448 | @ 0.100000 1449 | | | 1450 | @ 0.100000 1451 | |d| 1452 | @ 0.100000 1453 | |e| 1454 | @ 0.100000 1455 | |f| 1456 | @ 0.100000 1457 | |i| 1458 | @ 0.100000 1459 | |n| 1460 | @ 0.100000 1461 | |e| 1462 | @ 0.100000 1463 | |d| 1464 | @ 0.100000 1465 | | | 1466 | @ 0.100000 1467 | |r| 1468 | @ 0.100000 1469 | |o| 1470 | @ 0.100000 1471 | |l| 1472 | @ 0.100000 1473 | |e| 1474 | @ 0.100000 1475 | |s| 1476 | @ 0.100000 1477 | |:| 1478 | @ 0.100000 1479 | . LF/^J 1480 | @ 0.100000 1481 | . LF/^J 1482 | @ 0.100000 1483 | |.| 1484 | @ 0.100000 1485 | |.| 1486 | @ 0.100000 1487 | | | 1488 | @ 0.100000 1489 | |r| 1490 | @ 0.100000 1491 | |o| 1492 | @ 0.100000 1493 | |l| 1494 | @ 0.100000 1495 | |e| 1496 | @ 0.100000 1497 | |:| 1498 | @ 0.100000 1499 | |:| 1500 | @ 0.100000 1501 | | | 1502 | @ 0.100000 1503 | |i| 1504 | @ 0.100000 1505 | |m| 1506 | @ 0.100000 1507 | |p| 1508 | @ 0.100000 1509 | |o| 1510 | @ 0.100000 1511 | |r| 1512 | @ 0.100000 1513 | |t| 1514 | @ 0.100000 1515 | |a| 1516 | @ 0.100000 1517 | |n| 1518 | @ 0.100000 1519 | |t| 1520 | @ 0.100000 1521 | . LF/^J 1522 | @ 0.100000 1523 | | | 1524 | @ 0.100000 1525 | | | 1526 | @ 0.100000 1527 | | | 1528 | @ 0.100000 1529 | | | 1530 | @ 0.100000 1531 | |:| 1532 | @ 0.100000 1533 | |c| 1534 | @ 0.100000 1535 | |l| 1536 | @ 0.100000 1537 | |a| 1538 | @ 0.100000 1539 | |s| 1540 | @ 0.100000 1541 | |s| 1542 | @ 0.100000 1543 | |:| 1544 | @ 0.100000 1545 | | | 1546 | @ 0.100000 1547 | |a| 1548 | @ 0.100000 1549 | |n| 1550 | @ 0.100000 1551 | |s| 1552 | @ 0.100000 1553 | |i| 1554 | @ 0.100000 1555 | |-| 1556 | @ 0.100000 1557 | |f| 1558 | @ 0.100000 1559 | |g| 1560 | @ 0.100000 1561 | |-| 1562 | @ 0.100000 1563 | |r| 1564 | @ 0.100000 1565 | |e| 1566 | @ 0.100000 1567 | |d| 1568 | @ 0.100000 1569 | | | 1570 | @ 0.100000 1571 | |a| 1572 | @ 0.100000 1573 | |n| 1574 | @ 0.100000 1575 | |s| 1576 | @ 0.100000 1577 | |i| 1578 | @ 0.100000 1579 | |-| 1580 | @ 0.100000 1581 | |b| 1582 | @ 0.100000 1583 | |o| 1584 | @ 0.100000 1585 | |l| 1586 | @ 0.100000 1587 | |d| 1588 | @ 0.100000 1589 | . LF/^J 1590 | @ 0.100000 1591 | . LF/^J 1592 | @ 0.100000 1593 | |:| 1594 | @ 0.100000 1595 | |i| 1596 | @ 0.100000 1597 | |m| 1598 | @ 0.100000 1599 | |p| 1600 | @ 0.100000 1601 | |o| 1602 | @ 0.100000 1603 | |r| 1604 | @ 0.100000 1605 | |t| 1606 | @ 0.100000 1607 | |a| 1608 | @ 0.100000 1609 | |n| 1610 | @ 0.100000 1611 | |t| 1612 | @ 0.100000 1613 | |:| 1614 | @ 0.100000 1615 | |`| 1616 | @ 0.100000 1617 | |T| 1618 | @ 0.100000 1619 | |h| 1620 | @ 0.100000 1621 | |i| 1622 | @ 0.100000 1623 | |s| 1624 | @ 0.100000 1625 | | | 1626 | @ 0.100000 1627 | |i| 1628 | @ 0.100000 1629 | |s| 1630 | @ 0.100000 1631 | | | 1632 | @ 0.100000 1633 | |i| 1634 | @ 0.100000 1635 | |m| 1636 | @ 0.100000 1637 | |p| 1638 | @ 0.100000 1639 | |o| 1640 | @ 0.100000 1641 | |r| 1642 | @ 0.100000 1643 | |t| 1644 | @ 0.100000 1645 | |a| 1646 | @ 0.100000 1647 | |n| 1648 | @ 0.100000 1649 | |t| 1650 | @ 0.100000 1651 | |!| 1652 | @ 0.100000 1653 | |`| 1654 | @ 0.100000 1655 | . LF/^J 1656 | @ 0.100000 1657 | . ESC 1658 | @ 0.200000 1659 | |:| 1660 | @ 0.100000 1661 | |w| 1662 | @ 0.100000 1663 | . LF/^J 1664 | 1665 | # Last frame 1666 | 1667 | @ 1.000000 1668 | |d| 1669 | @ 0.100000 1670 | |6| 1671 | @ 0.100000 1672 | |k| 1673 | @ 0.100000 1674 | . ESC 1675 | @ 0.200000 1676 | |o| 1677 | @ 0.100000 1678 | |r| 1679 | @ 0.100000 1680 | |s| 1681 | @ 0.100000 1682 | |t| 1683 | @ 0.100000 1684 | |2| 1685 | @ 0.100000 1686 | |a| 1687 | @ 0.100000 1688 | |n| 1689 | @ 0.100000 1690 | |s| 1691 | @ 0.100000 1692 | |i| 1693 | @ 0.100000 1694 | | | 1695 | @ 0.100000 1696 | |i| 1697 | @ 0.100000 1698 | |s| 1699 | @ 0.100000 1700 | | | 1701 | @ 0.100000 1702 | |a| 1703 | @ 0.100000 1704 | |v| 1705 | @ 0.100000 1706 | |a| 1707 | @ 0.100000 1708 | |i| 1709 | @ 0.100000 1710 | |l| 1711 | @ 0.100000 1712 | |a| 1713 | @ 0.100000 1714 | |b| 1715 | @ 0.100000 1716 | |l| 1717 | @ 0.100000 1718 | |e| 1719 | @ 0.100000 1720 | | | 1721 | @ 0.100000 1722 | |o| 1723 | @ 0.100000 1724 | |n| 1725 | @ 0.100000 1726 | | | 1727 | @ 0.100000 1728 | |g| 1729 | @ 0.100000 1730 | |i| 1731 | @ 0.100000 1732 | |t| 1733 | @ 0.100000 1734 | |h| 1735 | @ 0.100000 1736 | |u| 1737 | @ 0.100000 1738 | |b| 1739 | @ 0.100000 1740 | | | 1741 | @ 0.100000 1742 | |a| 1743 | @ 0.100000 1744 | |t| 1745 | @ 0.100000 1746 | | | 1747 | @ 0.100000 1748 | |h| 1749 | @ 0.100000 1750 | |t| 1751 | @ 0.100000 1752 | |t| 1753 | @ 0.100000 1754 | |p| 1755 | @ 0.100000 1756 | |s| 1757 | @ 0.100000 1758 | |:| 1759 | @ 0.100000 1760 | |/| 1761 | @ 0.100000 1762 | |/| 1763 | @ 0.100000 1764 | |g| 1765 | @ 0.100000 1766 | |i| 1767 | @ 0.100000 1768 | |t| 1769 | @ 0.100000 1770 | |h| 1771 | @ 0.100000 1772 | |u| 1773 | @ 0.100000 1774 | |b| 1775 | @ 0.100000 1776 | |.| 1777 | @ 0.100000 1778 | |c| 1779 | @ 0.100000 1780 | |o| 1781 | @ 0.100000 1782 | |m| 1783 | @ 0.100000 1784 | |/| 1785 | @ 0.100000 1786 | |S| 1787 | @ 0.100000 1788 | |n| 1789 | @ 0.100000 1790 | |a| 1791 | @ 0.100000 1792 | |i| 1793 | @ 0.100000 1794 | |p| 1795 | @ 0.100000 1796 | |e| 1797 | @ 0.100000 1798 | |/| 1799 | @ 0.100000 1800 | |p| 1801 | @ 0.100000 1802 | |y| 1803 | @ 0.100000 1804 | |t| 1805 | @ 0.100000 1806 | |h| 1807 | @ 0.100000 1808 | |o| 1809 | @ 0.100000 1810 | |n| 1811 | @ 0.100000 1812 | |-| 1813 | @ 0.100000 1814 | |r| 1815 | @ 0.100000 1816 | |s| 1817 | @ 0.100000 1818 | |t| 1819 | @ 0.100000 1820 | |2| 1821 | @ 0.100000 1822 | |a| 1823 | @ 0.100000 1824 | |n| 1825 | @ 0.100000 1826 | |s| 1827 | @ 0.100000 1828 | |i| 1829 | @ 0.100000 1830 | . LF/^J 1831 | @ 0.100000 1832 | . LF/^J 1833 | @ 0.100000 1834 | |-| 1835 | @ 0.100000 1836 | |-| 1837 | @ 0.100000 1838 | |-| 1839 | @ 0.100000 1840 | |-| 1841 | @ 0.100000 1842 | |-| 1843 | @ 0.100000 1844 | |-| 1845 | @ 0.100000 1846 | |-| 1847 | @ 0.100000 1848 | |-| 1849 | @ 0.100000 1850 | |-| 1851 | @ 0.100000 1852 | |-| 1853 | @ 0.100000 1854 | . LF/^J 1855 | @ 0.100000 1856 | . LF/^J 1857 | @ 0.100000 1858 | |H| 1859 | @ 0.100000 1860 | |a| 1861 | @ 0.100000 1862 | |v| 1863 | @ 0.100000 1864 | |e| 1865 | @ 0.100000 1866 | | | 1867 | @ 0.100000 1868 | |a| 1869 | @ 0.100000 1870 | | | 1871 | @ 0.100000 1872 | |n| 1873 | @ 0.100000 1874 | |i| 1875 | @ 0.100000 1876 | |c| 1877 | @ 0.100000 1878 | |e| 1879 | @ 0.100000 1880 | | | 1881 | @ 0.100000 1882 | |d| 1883 | @ 0.100000 1884 | |a| 1885 | @ 0.100000 1886 | |y| 1887 | @ 0.100000 1888 | |!| 1889 | @ 0.100000 1890 | . ESC 1891 | @ 0.200000 1892 | |:| 1893 | @ 0.100000 1894 | |w| 1895 | @ 0.100000 1896 | . LF/^J 1897 | --------------------------------------------------------------------------------