2
0
Fork 0

Started working on guide, corrected color parsing to include bad parsing from Netscape

This commit is contained in:
Thomas Touhey 2018-01-23 21:24:16 +01:00
parent 5c72c3c18b
commit a5445e78f8
No known key found for this signature in database
GPG Key ID: 2ECEB0517AD947FB
13 changed files with 211 additions and 134 deletions

119
GUIDE.md
View File

@ -1,31 +1,112 @@
# User's guide to the textout BBcode
<TODO: insert a piece of history about how BBcode has been invented and how
it has been adopted and implemented in Planète Casio to this day>
The BBcode markup language mainly uses tags, which the starting mark looks
like `[xxx]` and the ending mark looks like `[/xxx]`. You can add an attribute
to the starting mark to modify the tag's behaviour.
## Align the text
To align the text, you can use either `[<alignment mode>]` or
`[align=<alignment mode>]` with any of the following modes:
- `left`: the text is left-aligned;
- `right`: the text is right-aligned;
- `center`: the text is aligned at the horizontal middle of the document;
- `justify`: the text is justified, i.e. the text spaces are optimized for
the right'n'word to end at the right of the line.
For example, to right-align some text, you could do something like this:
[right]some text[/right]
## Styling the text
You can add some basic text style by using the following tags:
- `[b]` for **bold** text;
- `[i]` for _italic_ text;
- `[u]` for underlined text;
- `[s]` or `[striked]` for ~striked~ text.
## Changing the text font
You can change the font of the text by using the `[font=xxx]` (or `[xxx]`
directly, where `xxx` represents the font identifier) tag. The following
fonts are available:
- `arial` represents Arial;
- `comic` represents Comic MS;
- `tahoma` represents Tahoma;
- `courier` represents Courier;
- `haettenschweiler` represents Haettenschweiler;
- `mono` and `monospace` represents the basic monospace font.
## Changing the text color
You can change the color of the text using the `[color=xxx]` (or `[xxx]`
directly for simple colors, where `xxx` represents the color) tag. This
tag accepts several types of values:
- simple color names (inspired from CSS <TODO: link required>) as `red`,
`blue`, `green`, `transparent`;
- color hex codes using `#` followed by hex digits, e.g. `#01020F`,
where the first group of hex digits represents the red component from
0 to 255, the second group of hex digits represents the green component
from 0 to 255, and the third group of hex digits represents the blue
component from 0 to 255. Incomplete composites will be filled by zero
on the left (e.g. `#0000F` is equivalent to `#00000F`), invalid characters
such as `A` will be replaced by `0`s;
- three hex digits codes using `#` followed by three hex digits, e.g. `#123`
which will be translated to `#112233`;
- `rgb(<red>, <green>, <blue>)`, where the red, green and blue components are
represented using decimal digits and are between 0 and 255 included;
- `rgba(<red>, <green>, <blue>, <alpha>)`, where the red, green and blue
components are expressed as said before and the alpha component is either
expressed as a percentage (e.g. `12.34%`) or as a proportion between `0.0`
and `1.0`;
- `hsl(<hue>, <saturation>, <light>)` or `hls(<hue>, <light>,<saturation>)`;
- `hsl(<hue>, <saturation>, <light>, <alpha>)` or
`hls(<hue>, <light>, <saturation>, <alpha>`;
- `hwb(<hue>, <white proportion>, <black proportion>)`.
Here are some examples:
[blue]I'm blue![/]
[color=rgb(255, 255,255,0.4)]I'm black![/]
[color=hsl(0,100%, 0.5)]I'm red![/]
## Inserting an image
In order to insert an image, you will have to use the `[img]` tag. It will
make a new paragraph containing the image which the URL is given in the
tag content. The tag can be bundled with some attributes, separated with
the pipe (`|`) character:
- `<width>x<height>`: set the dimensions to the image. If the width isn't
given (i.e. if this attribute starts with `x`), the height will be set
automatically. If the height isn't given (i.e. no `x` or nothing after
the `x`), the width will be set automatically;
- `left`, `right`, `center`: align the image accordingly;
- `float`: make the image float, i.e. let the text be where the image isn't.
For example, `[img=right|float|x24]https://example.org/image.jpg[/img]`
is a right-aligned image, floating (which means text will be allowed on
the left of the image), and with a height of 24 pixels and an automatic
width.
Planète Casio admins can use the `[adimg]` tag which is equivalent to the
`[img]` tag but adds the special admin image folder prefix to the image
URLs, so `[adimg]incredible.jpg[/adimg]` is possible.
## Inserting hyperlinks
Creating hyperlinks on a bunch of text is possible through the `[url]` tag.
## Other tags (TODO)
Some day, I will write a complete guide, but as a quickstart, here are the
different tags you can use:
[center]text[/center] / [align=center]text[/align]
[justify]text[/justify] / [align=justify]text[/align]
[b]Bold[/b]
[i]Italic[/i]
[u]Underlined[/u]
[s]Striked[/s] / [strike]Striked[/strike]
[arial]Arial text[/arial]
[font=arial]Arial text again[/font]
[blue]I'm blue dabadee dabadah[/blue]
[color=rgb(255, 255, 255, 0.4)]BLACKNESS[/color]
[color=hsl(0, 100%, 0.5)]Redrum[/color]
[code]Some multiline
code[/code]
[inlinecode]Some inline code[/inlinecode] / `Some inline code`
[noeval][center]Code notevaluated[/center][/noeval]
[img]https://planet-casio.com/images/ad/incredible.jpg[/img]
[img=center]https://example.org/centered-image.jpg[/img]
[img=12x24]https://example.org/some-image-to-resize.png[/img]
[img=x24|right]https://example.org/height-set-to-24-and-aligned-right.jpg[/img]
[adimg]incredible.jpg[/adimg]
[url]https://planet-casio.com[/url]
[url=https://planet-casio.com]Planète Casio[/url]
[url=/relative/url.php][/url]

View File

@ -5,7 +5,7 @@ from .__base__ import *
__all__ = ["TextoutAlignTag"]
class TextoutAlignTag(TextoutParagraphTag):
class TextoutAlignTag(TextoutBlockTag):
""" Main tag for aligning text.
Example uses:
@ -13,12 +13,12 @@ class TextoutAlignTag(TextoutParagraphTag):
[justify]This text is justified.[/justify]
"""
aliases = ('[align]', '[center]', '[justify]')
aliases = ('[align]', '[center]', '[left]', '[right]', '[justify]')
def prepare(self, name, value):
if name != 'align':
value = name[1:-1]
if not value in ('center', 'justify'):
if not value in ('left', 'right', 'center', 'justify'):
raise Exception
self._align = value

View File

@ -5,11 +5,12 @@ from .__base__ import *
__all__ = ["TextoutCodeTag", "TextoutInlineCodeTag", "TextoutNoEvalTag"]
class TextoutCodeTag(TextoutRawParagraphTag):
class TextoutCodeTag(TextoutRawBlockTag):
""" The basic code tag, for displaying code.
Example uses:
[code]int main() {
[code]int main()
{
printf("hello, world");
}[/code] """
@ -32,9 +33,9 @@ class TextoutInlineCodeTag(TextoutRawInlineTag):
the content and uses monospace font.
Example uses:
`some_inline_code`
`some inline code`
[inlinecode][b]The tags will be shown verbatim.[/b][/inlinecode]
[inlinecode][i]This also works![/inlinecode]
[inlinecode][inlinecode][i]This also[/inlinecode] works![/inlinecode]
"""
aliases = ('`', '[inlinecode]')

View File

@ -24,18 +24,27 @@ class TextoutImageTag(TextoutRawBlockTag):
self._width = None
self._height = None
self._align = None
self._float = False
for arg in ("", value)[value != None].split('|'):
if not arg:
pass
elif arg[0] in '0123456789x':
self._width = None
self._height = None
dim = arg.split('x')
try: self._width = int(dim[0])
except: pass
try: self._height = int(dim[1])
except: pass
elif arg in ('center', 'right', 'float-left', 'float-right'):
elif arg in ('center', 'left', 'right'):
self._align = arg
elif arg in ('float-left', 'float-right'):
self._align = arg[6:]
self._float = True
elif arg in ('float', 'floating'):
self._float = True
def preprocess(self, content):
for prefix in ('http://', 'https://', 'ftp://', '/'):
@ -51,18 +60,23 @@ class TextoutImageTag(TextoutRawBlockTag):
def content_html(self):
style = []
if self.width:
cls = []
if self._width:
style.append('width: {}px'.format(self._width))
elif self.height:
elif self._height:
style.append('width: auto')
if self.height:
if self._height:
style.append('height: {}px'.format(self._height))
elif self.width:
elif self._width:
style.append('height: auto')
if self._float:
cls.append('img-float')
if self._align:
cls.append('img-{}'.format(self._align))
return '<img src="{}"{}{}>'.format(\
self._geturl(_htmlescape(self._url)),
' class="{}"'.format(self._align) if self._align else '',
' class="{}"'.format(' '.join(cls)) if cls else '',
' style="{}"'.format('; '.join(style)) if style else '')
class TextoutAdminImageTag(TextoutImageTag):

View File

@ -7,7 +7,7 @@ from ..smileys import htmlsmileys as _htmlsmileys
__all__ = ["TextoutQuoteTag"]
class TextoutQuoteTag(TextoutParagraphTag):
class TextoutQuoteTag(TextoutBlockTag):
""" The main tag for quoting someone.
Example uses:

27
textoutpc/Tags/Show.py Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from .__base__ import *
from html import escape as _htmlescape
__all__ = ["TextoutShowTag"]
class TextoutShowTag(TextoutBlockTag):
""" Tag which shows the HTML code that is produced by textout().
Example uses:
[show][b]hello world![/show]
"""
aliases = ('[show]',)
def preprocess_html(self, content):
return _htmlescape(content)
def begin_html(self):
return '<span style="font-family: monospace;">'
def end_html(self):
return '</span>'
# End of file.

View File

@ -1,33 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from .__base__ import *
__all__ = ["TextoutSizeTag"]
class TextoutSizeTag(TextoutInlineTag):
""" Main tag for setting the font size.
Example uses:
[big]WOW, THIS IS BIG.[/big]
[size=small]and this is tiny.[/size]
"""
aliases = ('[size]', '[big]', '[small]')
def prepare(self, name, value):
name = name[1:-1]
if name != 'size':
value = name
if not value in ('small', 'big'):
raise Exception
self._sz = value
def begin_html(self):
return '<span style="font-size: {};">' \
.format({'big': '15px', 'small': '9px'}[self._sz])
def end_html(self):
return '</span>'
# End of file.

View File

@ -7,7 +7,7 @@ from ..smileys import htmlsmileys as _htmlsmileys
__all__ = ["TextoutSpoilerTag"]
class TextoutSpoilerTag(TextoutParagraphTag):
class TextoutSpoilerTag(TextoutBlockTag):
""" Hide content at first glance, force people to click to read content.
These elements can contain 'secret' elements such as solutions, source
code, or various other things.

View File

@ -10,6 +10,9 @@ __all__ = ["TextoutTextTag"]
# Data.
# ---
_big_size = 2.00
_sml_size = 0.75
_fonts = {
"arial": "Arial",
"comic": "Comic MS",
@ -42,6 +45,7 @@ class TextoutTextTag(TextoutInlineTag):
aliases = ('[b]', '[i]', '[u]', '[s]', '[strike]',
'[monospace]', '[mono]', '[font]', '[color]',
'[size]', '[big]', '[small]',
'[arial]', '[comic]', '[tahoma]', '[courier]',
'[haettenschweiler]', '[red]', '[green]', '[blue]',
'[yellow]', '[maroon]', '[purple]', '[gray]',
@ -54,6 +58,7 @@ class TextoutTextTag(TextoutInlineTag):
self._strike = False
self._font = None
self._color = None
self._size = None
name = name[1:-1]
if name == "b":
@ -69,6 +74,19 @@ class TextoutTextTag(TextoutInlineTag):
elif name == "font":
assert value in _fonts
self._font = value
elif name in ('size', 'big', 'small'):
if name != 'size':
value = name
if value == 'big':
self._size = _big_size
elif value == 'small':
self._size = _sml_size
else:
self._size = int(value) / 100.0
assert 0 < self._size <= 3.0
if self._size == 1.0:
self._size = None
elif name in _fonts:
self._font = value
else:
@ -86,6 +104,8 @@ class TextoutTextTag(TextoutInlineTag):
if self._color[3] < 1.0:
props.append('color: rgba({}, {}, {}, {})' \
.format(*self._color))
if self._size:
props.append('font-size: {}em'.format(self._size))
self._has_props = bool(props)
props = '<span style="{}">'.format('; '.join(props)) if props else ''

View File

@ -5,48 +5,18 @@ from functools import partial as _p
__all__ = ["TextoutRawTag",
"TextoutBlockTag", "TextoutRawBlockTag",
"TextoutParagraphTag", "TextoutRawParagraphTag",
"TextoutInlineTag", "TextoutRawInlineTag",
"TextoutInlineBlockTag", "TextoutRawInlineBlockTag"]
"TextoutInlineTag", "TextoutRawInlineTag"]
# ---
# Main base tag class.
# For more about defining a tag, see `/TAGS.md`.
# ---
# There are three types of tags:
#
# - Those who know how to are valid straight when they receive their
# name and value (attribute), and include their content without
# processing it.
# - Those who are valid straight when they receive their name and value,
# but process their content.
# - Those who don't know if they are valid until they receive their content,
# and process it.
#
# All categories can set the `_prepare()` method to process the name
# and value to check their validity and prepare some things.
# The second and third category needs to set the `_content()` method
# (which the existence will be checked after the `_prepare()` method
# is called), which may, as the `_prepare()` method, raise an exception
# if there is a problem with the content.
# ---
# The classes can define the following methods (not including helpers):
#
# - `_prepare()`: will be called at class initialization. `self.name` and
# `self.value` (attribute) will be set. Exception means the tag with
# this name/attribute combination is invalid;
# - `_content()`: will be called when the content is set. `self.name`,
# `self.value` and `self.content` will be set. Exception means the tag
# with this name/attribute/content combination is invalid;
# - `begin_<fmt>()`: put the beginning of the produced form (by default,
# nothing);
# - `process_<fmt>()`: put the main content of the produced form (by default,
# the raw/escaped content for the format);
# - `end_<fmt>()`: put the end of the produced form (by default, nothing).
class TextoutTag:
""" The textout tag base class.
Is initialized with these values:
<special><content><special>
[<name>]<content>[/<name>]
[<name>=<value>]<content>[/<name>] """
@ -81,7 +51,7 @@ class TextoutTag:
self.prepare(name, value)
# Prepare the preprocessing elements.
if not hasattr(self, 'preprocess'):
if hasattr(self, 'preprocess'):
if hasattr(self, 'preprocess_' + ot):
self.__preprocess0 = self.preprocess
self.preprocess = self.__preprocess_double
@ -142,19 +112,9 @@ class TextoutBlockTag(TextoutTag):
class TextoutRawBlockTag(TextoutBlockTag, TextoutRawTag):
pass
class TextoutParagraphTag(TextoutBlockTag):
class TextoutInlineTag(TextoutTag):
pass
class TextoutRawParagraphTag(TextoutParagraphTag, TextoutRawTag):
pass
class TextoutInlineBlockTag(TextoutTag):
pass
class TextoutRawInlineBlockTag(TextoutInlineBlockTag, TextoutRawTag):
pass
class TextoutInlineTag(TextoutInlineBlockTag):
pass
class TextoutRawInlineTag(TextoutInlineBlockTag, TextoutRawTag):
class TextoutRawInlineTag(TextoutInlineTag, TextoutRawTag):
pass
# End of file.

View File

@ -75,15 +75,7 @@ _colors = {
}
_cr = _re.compile('^\s*('
'#'
'(?P<thr_r>[0-9a-f])'
'(?P<thr_g>[0-9a-f])'
'(?P<thr_b>[0-9a-f])'
'|#'
'(?P<six_r>[0-9a-f]{2})'
'(?P<six_g>[0-9a-f]{2})'
'(?P<six_b>[0-9a-f]{2})'
'|rgba?\s*\('
'rgba?\s*\('
'\s*' '(?P<rgb_r>[0-9]{1,3})' '\s*,'
'\s*' '(?P<rgb_g>[0-9]{1,3})' '\s*,'
'\s*' '(?P<rgb_b>[0-9]{1,3})' '\s*(,'
@ -118,6 +110,8 @@ _cr = _re.compile('^\s*('
# '\s*' '((?P<hwb_blk_per>0*[0-9]{1,3})%'
# '|(?P<hwb_blk_flt>0*(\.[0-9]*)?))' '\s*'
# '\)'
'|\#?'
'(?P<hex_digits>[0-9a-z]*)'
')\s*$', _re.I)
# ---
@ -142,7 +136,7 @@ def get_color(value):
Raises an exception if there's a problem. """
# Check if is a color name.
try: value = _colors[value]
try: value = _colors[value.lower()]
except: pass
# Initialize the alpha.
@ -150,14 +144,25 @@ def get_color(value):
# Get the match.
match = _cr.match(value).groupdict()
if match['thr_r']:
r = int(match['thr_r'] * 2, 16)
g = int(match['thr_g'] * 2, 16)
b = int(match['thr_b'] * 2, 16)
elif match['six_r']:
r = int(match['six_r'], 16)
g = int(match['six_g'], 16)
b = int(match['six_b'], 16)
if match['hex_digits']:
# Imitate the Netscape behaviour. Find more about this here:
# https://stackoverflow.com/a/8333464
hx = match['hex_digits'].lower()
hx = ''.join(c if c in '0123456789abcdef' \
else ('0', '00')[ord(c) > 0xFFFF] for c in hx)[:128]
if len(hx) == 3:
hx = hx[0] * 2 + hx[1] * 2 + hx[2] * 2
iv = _math.ceil(len(hx) / 3)
of = iv - 8 if iv > 8 else 0
sz = iv - of
gr = list(map(lambda i: hx[i * iv + of:i * iv + iv] \
.ljust(sz, '0'), range(3)))
pre = min(map(lambda x: len(x) - len(x.lstrip('0')), gr))
pre = min(pre, sz - 2)
r, g, b = map(lambda x: int('0' + x[pre:pre + 2], 16), gr)
elif match['rgb_r']:
r = int(match['rgb_r'])
g = int(match['rgb_g'])
@ -264,7 +269,7 @@ def get_color(value):
if wht > 1 or blk > 1:
raise Exception
r, g, b = _hwb_to_rgb(hue, wht, blk)
r, g, b = map(lambda x:int(round(x * 255)), (r, g, b))
r, g, b = map(lambda x: int(round(x * 255)), (r, g, b))
if r < 0 or r > 255 or g < 0 or g > 255 or b < 0 or b > 255:
raise Exception

View File

@ -12,7 +12,7 @@ from .Link import *
from .Progress import *
from .Quote import *
from .Rot import *
from .Size import *
from .Show import *
from .Spoiler import *
from .Text import *
from .Title import *
@ -21,7 +21,7 @@ from .Video import *
_tags = (TextoutAlignTag, TextoutCodeTag, TextoutInlineCodeTag,
TextoutNoEvalTag, TextoutImageTag, TextoutAdminImageTag,
TextoutLabelTag, TextoutTargetTag, TextoutLinkTag, TextoutProfileTag,
TextoutProgressTag, TextoutQuoteTag, TextoutRotTag, TextoutSizeTag,
TextoutProgressTag, TextoutQuoteTag, TextoutRotTag, TextoutShowTag,
TextoutSpoilerTag, TextoutTextTag, TextoutTitleTag, TextoutVideoTag)
_aliases = {alias: tag for alias, tag in \
[(alias, tag) for tag in _tags for alias in tag.aliases]}

View File

@ -308,6 +308,8 @@ class Translator:
self.put_code(tag.begin())
if hasattr(tag, 'content'):
self.put_code(tag.content())
elif not isinstance(tag, TextoutRawTag):
self.put_code(content)
else:
self.put_text(content)
elif hasattr(tag, 'content'):