1# -*- coding: utf-8 -*-
2
3# Copyright (C) 2005 Osmo Salomaa
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18"""Text markup for the MPL2 format."""
19
20import aeidon
21
22from collections import OrderedDict
23
24__all__ = ("MPL2",)
25
26
27class MPL2(aeidon.markups.MicroDVD):
28
29    """
30    Text markup for the MPL2 format.
31
32    MPL2 contains the following markup tags, all of which appear at the
33    beginning of the line and affect up to the end of the line. In addition,
34    any MicroDVD markup can be used.
35
36     * ``\\...`` (bold)
37     * ``/....`` (italic)
38     * ``_....`` (underline)
39    """
40
41    format = aeidon.formats.MPL2
42
43    def bolden(self, text, bounds=None):
44        """Return bolded `text`."""
45        return self._style_mpl2(text, "\\", bounds)
46
47    @property
48    def italic_tag(self):
49        """Regular expression for an italic markup tag."""
50        return self._get_regex(r"(^/)|(\{[Yy]:i\})")
51
52    def italicize(self, text, bounds=None):
53        """Return italicized `text`."""
54        return self._style_mpl2(text, "/", bounds)
55
56    def _main_decode(self, text):
57        """Return `text` with decodable markup decoded."""
58        text = self._decode_b(text, r"<\\>(.*?)</\\>", 1)
59        text = self._decode_i(text, r"</>(.*?)<//>", 1)
60        text = self._decode_u(text, r"<_>(.*?)</_>", 1)
61        return aeidon.markups.MicroDVD._main_decode(self, text)
62
63    def _pre_decode(self, text):
64        """Return `text` with markup prepared for decoding."""
65        text = self._pre_decode_identify(text)
66        return aeidon.markups.MicroDVD._pre_decode(self, text)
67
68    def _pre_decode_identify(self, text):
69        """
70        Return `text` with all tags identified and closed.
71
72        ``\\``, ``/`` and ``_`` characters at the beginnings of lines are
73        identified as tags and replaced with artificial tags ``<\\>``, ``</>``
74        and ``<_>``. Closing tags are added to the ends of lines as artificial
75        tags ``</\\>``, ``<//>`` and ``</_>``.
76        """
77        lines = text.split("\n")
78        re_tag = self._get_regex(r"^([\\/_]+)(.*)$")
79        for i, line in enumerate(lines):
80            match = re_tag.search(line)
81            if match is None: continue
82            lines[i] = match.group(2)
83            for tag in reversed(OrderedDict.fromkeys(match.group(1))):
84                lines[i] = "<{}>{}</{}>".format(tag, lines[i], tag)
85        return "\n".join(lines)
86
87    def _style_mpl2(self, text, tag, bounds=None):
88        """Return `text` wrapped in markup `tag`."""
89        a, z = bounds or (0, len(text))
90        prefix = text[:a].split("\n")[-1]
91        suffix = text[z:].split("\n")[0]
92        re_alpha = self._get_regex(r"\w")
93        # Return plain text if bounds does not define an entire line or
94        # subtitle and thus cannot be marked without side-effects.
95        if re_alpha.search(prefix): return text
96        if re_alpha.search(suffix): return text
97        styled_text = text[a:z].replace("\n", "\n{}".format(tag))
98        return "".join((text[:a], tag, styled_text, text[z:]))
99
100    @property
101    def tag(self):
102        """Regular expression for any markup tag."""
103        return self._get_regex(r"(^[\\/_]+)|(\{[CFSYcfsy]:.*?\})")
104
105    def underline(self, text, bounds=None):
106        """Return underlined `text`."""
107        return self._style_mpl2(text, "_", bounds)
108