Ejemplo n.º 1
0
    def alert(self, text, location):
        """
        Display an alert.

        :param text: The text to be displayed.
        :param location: Where on the screen is the text to be displayed
        """
        # First we convert <>& marks to html variants, then apply
        # formattingtags, finally we double all backslashes for JavaScript.
        text_prepared = expand_tags(html.escape(text)).replace('\\', '\\\\').replace('\"', '\\\"')
        if self.height() != self.screen['size'].height() or not self.isVisible():
            shrink = True
            js = 'show_alert("%s", "%s")' % (text_prepared, 'top')
        else:
            shrink = False
            js = 'show_alert("%s", "")' % text_prepared
        height = self.frame.evaluateJavaScript(js)
        if shrink:
            if text:
                alert_height = int(height)
                self.resize(self.width(), alert_height)
                self.setVisible(True)
                if location == AlertLocation.Middle:
                    self.move(self.screen['size'].left(), (self.screen['size'].height() - alert_height) // 2)
                elif location == AlertLocation.Bottom:
                    self.move(self.screen['size'].left(), self.screen['size'].height() - alert_height)
            else:
                self.setVisible(False)
                self.setGeometry(self.screen['size'])
Ejemplo n.º 2
0
    def alert(self, text, location):
        """
        Display an alert.

        :param text: The text to be displayed.
        :param location: Where on the screen is the text to be displayed
        """
        # First we convert <>& marks to html variants, then apply
        # formattingtags, finally we double all backslashes for JavaScript.
        text_prepared = expand_tags(html.escape(text)).replace('\\', '\\\\').replace('\"', '\\\"')
        if self.height() != self.screen['size'].height() or not self.isVisible():
            shrink = True
            js = 'show_alert("%s", "%s")' % (text_prepared, 'top')
        else:
            shrink = False
            js = 'show_alert("%s", "")' % text_prepared
        height = self.frame.evaluateJavaScript(js)
        if shrink:
            if text:
                alert_height = int(height)
                self.resize(self.width(), alert_height)
                self.setVisible(True)
                if location == AlertLocation.Middle:
                    self.move(self.screen['size'].left(), (self.screen['size'].height() - alert_height) // 2)
                elif location == AlertLocation.Bottom:
                    self.move(self.screen['size'].left(), self.screen['size'].height() - alert_height)
            else:
                self.setVisible(False)
                self.setGeometry(self.screen['size'])
Ejemplo n.º 3
0
    def alert(self, text, location):
        """
        Display an alert.

        :param text: The text to be displayed.
        :param location: Where on the screen is the text to be displayed
        """
        # First we convert <>& marks to html variants, then apply
        # formattingtags, finally we double all backslashes for JavaScript.
        text_prepared = expand_tags(html.escape(text)).replace('\\', '\\\\').replace('\"', '\\\"')
        if self.height() != self.screen['size'].height() or not self.isVisible():
            shrink = True
            js = 'show_alert("{text}", "{top}")'.format(text=text_prepared, top='top')
        else:
            shrink = False
            js = 'show_alert("{text}", "")'.format(text=text_prepared)
        height = self.frame.evaluateJavaScript(js)
        if shrink:
            if text:
                alert_height = int(height)
                self.resize(self.width(), alert_height)
                self.setVisible(True)
                if location == AlertLocation.Middle:
                    self.move(self.screen['size'].left(), (self.screen['size'].height() - alert_height) // 2)
                elif location == AlertLocation.Bottom:
                    self.move(self.screen['size'].left(), self.screen['size'].height() - alert_height)
            else:
                self.setVisible(False)
                self.setGeometry(self.screen['size'])
        # Workaround for bug #1531319, should not be needed with PyQt 5.6.
        if is_win():
            self.shake_web_view()
Ejemplo n.º 4
0
    def render(self, provides_own_theme_data=False):
        """
        The render method is what generates the frames for the screen and obtains the display information from the
        renderer. At this point all slides are built for the given display size.

        :param provides_own_theme_data: This switch disables the usage of the item's theme. However, this is
            disabled by default. If this is used, it has to be taken care, that
            the renderer knows the correct theme data. However, this is needed
            for the theme manager.
        """
        log.debug('Render called')
        self._display_frames = []
        self.bg_image_bytes = None
        if not provides_own_theme_data:
            self.renderer.set_item_theme(self.theme)
            self.theme_data, self.main, self.footer = self.renderer.pre_render(
            )
        if self.service_item_type == ServiceItemType.Text:
            log.debug('Formatting slides: {title}'.format(title=self.title))
            # Save rendered pages to this dict. In the case that a slide is used twice we can use the pages saved to
            # the dict instead of rendering them again.
            previous_pages = {}
            for slide in self._raw_frames:
                verse_tag = slide['verseTag']
                if verse_tag in previous_pages and previous_pages[verse_tag][
                        0] == slide['raw_slide']:
                    pages = previous_pages[verse_tag][1]
                else:
                    pages = self.renderer.format_slide(slide['raw_slide'],
                                                       self)
                    previous_pages[verse_tag] = (slide['raw_slide'], pages)
                for page in pages:
                    page = page.replace('<br>', '{br}')
                    html_data = expand_tags(html.escape(page.rstrip()))
                    self._display_frames.append({
                        'title':
                        clean_tags(page),
                        'text':
                        clean_tags(page.rstrip()),
                        'html':
                        html_data.replace('&amp;nbsp;', '&nbsp;'),
                        'verseTag':
                        verse_tag
                    })
        elif self.service_item_type == ServiceItemType.Image or self.service_item_type == ServiceItemType.Command:
            pass
        else:
            log.error('Invalid value renderer: {item}'.format(
                item=self.service_item_type))
        self.title = clean_tags(self.title)
        # The footer should never be None, but to be compatible with a few
        # nightly builds between 1.9.4 and 1.9.5, we have to correct this to
        # avoid tracebacks.
        if self.raw_footer is None:
            self.raw_footer = []
        self.foot_text = '<br>'.join([_f for _f in self.raw_footer if _f])
Ejemplo n.º 5
0
    def render(self, provides_own_theme_data=False):
        """
        The render method is what generates the frames for the screen and
        obtains the display information from the renderer. At this point all
        slides are built for the given display size.

        ``provides_own_theme_data``
            This switch disables the usage of the item's theme. However, this is
            disabled by default. If this is used, it has to be taken care, that
            the renderer knows the correct theme data. However, this is needed
            for the theme manager.
        """
        log.debug(u'Render called')
        self._display_frames = []
        self.bg_image_bytes = None
        if not provides_own_theme_data:
            self.renderer.set_item_theme(self.theme)
            self.themedata, self.main, self.footer = self.renderer.pre_render()
        if self.service_item_type == ServiceItemType.Text:
            log.debug(u'Formatting slides: %s' % self.title)
            # Save rendered pages to this dict. In the case that a slide is used
            # twice we can use the pages saved to the dict instead of rendering
            # them again.
            previous_pages = {}
            for slide in self._raw_frames:
                verse_tag = slide[u'verseTag']
                if verse_tag in previous_pages and previous_pages[verse_tag][0] == slide[u'raw_slide']:
                    pages = previous_pages[verse_tag][1]
                else:
                    pages = self.renderer.format_slide(slide[u'raw_slide'], self)
                    previous_pages[verse_tag] = (slide[u'raw_slide'], pages)
                for page in pages:
                    page = page.replace(u'<br>', u'{br}')
                    html = expand_tags(cgi.escape(page.rstrip()))
                    self._display_frames.append({
                        u'title': clean_tags(page),
                        u'text': clean_tags(page.rstrip()),
                        u'html': html.replace(u'&amp;nbsp;', u'&nbsp;'),
                        u'verseTag': verse_tag
                    })
        elif self.service_item_type == ServiceItemType.Image or self.service_item_type == ServiceItemType.Command:
            pass
        else:
            log.error(u'Invalid value renderer: %s' % self.service_item_type)
        self.title = clean_tags(self.title)
        # The footer should never be None, but to be compatible with a few
        # nightly builds between 1.9.4 and 1.9.5, we have to correct this to
        # avoid tracebacks.
        if self.raw_footer is None:
            self.raw_footer = []
        self.foot_text = u'<br>'.join(filter(None, self.raw_footer))
Ejemplo n.º 6
0
    def _paginate_slide_words(self, lines, line_end):
        """
        Figure out how much text can appear on a slide, using the current
        theme settings.
        **Note:** The smallest possible "unit" of text for a slide is one word.
        If one line is too long it will be processed word by word. This is
        sometimes need for **bible** verses.

        ``lines``
            The text to be fitted on the slide split into lines.

        ``line_end``
            The text added after each line. Either ``u' '`` or ``u'<br>``.
            This is needed for **bibles**.
        """
        log.debug(u'_paginate_slide_words - Start')
        formatted = []
        previous_html = u''
        previous_raw = u''
        for line in lines:
            line = line.strip()
            html_line = expand_tags(line)
            # Text too long so go to next page.
            if not self._text_fits_on_slide(previous_html + html_line):
                # Check if there was a verse before the current one and append
                # it, when it fits on the page.
                if previous_html:
                    if self._text_fits_on_slide(previous_html):
                        formatted.append(previous_raw)
                        previous_html = u''
                        previous_raw = u''
                        # Now check if the current verse will fit, if it does
                        # not we have to start to process the verse word by
                        # word.
                        if self._text_fits_on_slide(html_line):
                            previous_html = html_line + line_end
                            previous_raw = line + line_end
                            continue
                # Figure out how many words of the line will fit on screen as
                # the line will not fit as a whole.
                raw_words = self._words_split(line)
                html_words = map(expand_tags, raw_words)
                previous_html, previous_raw = \
                    self._binary_chop(formatted, previous_html, previous_raw, html_words, raw_words, u' ', line_end)
            else:
                previous_html += html_line + line_end
                previous_raw += line + line_end
        formatted.append(previous_raw)
        log.debug(u'_paginate_slide_words - End')
        return formatted
Ejemplo n.º 7
0
    def test_expand_chords2(self):
        """
        Test that the expanding of chords works as expected when special chars are involved.
        """
        # GIVEN: A lyrics-line with chords
        text_with_chords = "I[D]'M NOT MOVED BY WHAT I SEE HALLE[F]LUJA[C]H"

        # WHEN: Expanding the chords
        text_with_expanded_chords = expand_tags(text_with_chords, True)

        # THEN: We should get html that looks like below
        expected_html = '<span class="chordline firstchordline">I<span class="chord"><span><strong>D</strong></span>' \
                        '</span>&#x27;M NOT MOVED BY WHAT I SEE HALLE<span class="chord"><span><strong>F</strong>' \
                        '</span></span>LUJA<span class="chord"><span><strong>C</strong></span></span>H</span>'
        assert expected_html == text_with_expanded_chords, 'The expanded chords should look as expected!'
Ejemplo n.º 8
0
    def test_expand_tags(self):
        """
        Test the expand_tags() method.
        """
        with patch('openlp.core.lib.FormattingTags.get_html_tags'
                   ) as mocked_get_tags:
            # GIVEN: Mocked get_html_tags() method.
            mocked_get_tags.return_value = [{
                'desc': 'Black',
                'start tag': '{b}',
                'start html': '<span style="-webkit-text-fill-color:black">',
                'end tag': '{/b}',
                'end html': '</span>',
                'protected': True,
                'temporary': False
            }, {
                'desc': 'Yellow',
                'start tag': '{y}',
                'start html': '<span style="-webkit-text-fill-color:yellow">',
                'end tag': '{/y}',
                'end html': '</span>',
                'protected': True,
                'temporary': False
            }, {
                'desc': 'Green',
                'start tag': '{g}',
                'start html': '<span style="-webkit-text-fill-color:green">',
                'end tag': '{/g}',
                'end html': '</span>',
                'protected': True,
                'temporary': False
            }]
            string_to_pass = '******'
            wanted_string = '<span style="-webkit-text-fill-color:black">black</span>' + \
                '<span style="-webkit-text-fill-color:yellow">yellow</span>'

            # WHEN: Replace the tags.
            result_string = expand_tags(string_to_pass)

            # THEN: The strings should be identical.
            self.assertEqual(wanted_string, result_string,
                             'The strings should be identical.')
Ejemplo n.º 9
0
    def _paginate_slide_words(self, lines, line_end):
        """
        Figure out how much text can appear on a slide, using the current theme settings.

        **Note:** The smallest possible "unit" of text for a slide is one word. If one line is too long it will be
        processed word by word. This is sometimes need for **bible** verses.

        :param lines: The text to be fitted on the slide split into lines.
        :param line_end: The text added after each line. Either ``' '`` or ``'<br>``. This is needed for **bibles**.
        """
        formatted = []
        previous_html = ''
        previous_raw = ''
        for line in lines:
            line = line.strip()
            html_line = expand_tags(line)
            # Text too long so go to next page.
            if not self._text_fits_on_slide(previous_html + html_line):
                # Check if there was a verse before the current one and append it, when it fits on the page.
                if previous_html:
                    if self._text_fits_on_slide(previous_html):
                        formatted.append(previous_raw)
                        previous_html = ''
                        previous_raw = ''
                        # Now check if the current verse will fit, if it does not we have to start to process the verse
                        # word by word.
                        if self._text_fits_on_slide(html_line):
                            previous_html = html_line + line_end
                            previous_raw = line + line_end
                            continue
                # Figure out how many words of the line will fit on screen as the line will not fit as a whole.
                raw_words = words_split(line)
                html_words = list(map(expand_tags, raw_words))
                previous_html, previous_raw = \
                    self._binary_chop(formatted, previous_html, previous_raw, html_words, raw_words, ' ', line_end)
            else:
                previous_html += html_line + line_end
                previous_raw += line + line_end
        formatted.append(previous_raw)
        return formatted
Ejemplo n.º 10
0
    def test_expand_tags(self):
        """
        Test the expand_tags() method.
        """
        with patch('openlp.core.lib.FormattingTags.get_html_tags') as mocked_get_tags:
            # GIVEN: Mocked get_html_tags() method.
            mocked_get_tags.return_value = [
                {
                    'desc': 'Black',
                    'start tag': '{b}',
                    'start html': '<span style="-webkit-text-fill-color:black">',
                    'end tag': '{/b}', 'end html': '</span>', 'protected': True,
                    'temporary': False
                },
                {
                    'desc': 'Yellow',
                    'start tag': '{y}',
                    'start html': '<span style="-webkit-text-fill-color:yellow">',
                    'end tag': '{/y}', 'end html': '</span>', 'protected': True,
                    'temporary': False
                },
                {
                    'desc': 'Green',
                    'start tag': '{g}',
                    'start html': '<span style="-webkit-text-fill-color:green">',
                    'end tag': '{/g}', 'end html': '</span>', 'protected': True,
                    'temporary': False
                }
            ]
            string_to_pass = '******'
            wanted_string = '<span style="-webkit-text-fill-color:black">black</span>' + \
                '<span style="-webkit-text-fill-color:yellow">yellow</span>'

            # WHEN: Replace the tags.
            result_string = expand_tags(string_to_pass)

            # THEN: The strings should be identical.
            self.assertEqual(wanted_string, result_string, 'The strings should be identical.')
Ejemplo n.º 11
0
    def alert(self, text, location):
        """
        Display an alert.

        :param text: The text to be displayed.
        :param location: Where on the screen is the text to be displayed
        """
        # First we convert <>& marks to html variants, then apply
        # formattingtags, finally we double all backslashes for JavaScript.
        text_prepared = expand_tags(html.escape(text)).replace(
            '\\', '\\\\').replace('\"', '\\\"')
        if self.height() != self.screen['size'].height() or not self.isVisible(
        ):
            shrink = True
            js = 'show_alert("{text}", "{top}")'.format(text=text_prepared,
                                                        top='top')
        else:
            shrink = False
            js = 'show_alert("{text}", "")'.format(text=text_prepared)
        height = self.frame.evaluateJavaScript(js)
        if shrink:
            if text:
                alert_height = int(height)
                self.resize(self.width(), alert_height)
                self.setVisible(True)
                if location == AlertLocation.Middle:
                    self.move(self.screen['size'].left(),
                              (self.screen['size'].height() - alert_height) //
                              2)
                elif location == AlertLocation.Bottom:
                    self.move(self.screen['size'].left(),
                              self.screen['size'].height() - alert_height)
            else:
                self.setVisible(False)
                self.setGeometry(self.screen['size'])
        # Workaround for bug #1531319, should not be needed with PyQt 5.6.
        if is_win():
            self.shake_web_view()
Ejemplo n.º 12
0
    def format_slide(self, text, item):
        """
        Calculate how much text can fit on a slide.

        :param text:  The words to go on the slides.
        :param item: The :class:`~openlp.core.lib.serviceitem.ServiceItem` item object.

        """
        self.log_debug('format slide')
        # Add line endings after each line of text used for bibles.
        line_end = '<br>'
        if item.is_capable(ItemCapabilities.NoLineBreaks):
            line_end = ' '
        # Bibles
        if item.is_capable(ItemCapabilities.CanWordSplit):
            pages = self._paginate_slide_words(text.split('\n'), line_end)
        # Songs and Custom
        elif item.is_capable(ItemCapabilities.CanSoftBreak):
            pages = []
            if '[---]' in text:
                # Remove two or more option slide breaks next to each other (causing infinite loop).
                while '\n[---]\n[---]\n' in text:
                    text = text.replace('\n[---]\n[---]\n', '\n[---]\n')
                while ' [---]' in text:
                    text = text.replace(' [---]', '[---]')
                while '[---] ' in text:
                    text = text.replace('[---] ', '[---]')
                count = 0
                # only loop 5 times as there will never be more than 5 incorrect logical splits on a single slide.
                while True and count < 5:
                    slides = text.split('\n[---]\n', 2)
                    # If there are (at least) two occurrences of [---] we use the first two slides (and neglect the last
                    # for now).
                    if len(slides) == 3:
                        html_text = expand_tags('\n'.join(slides[:2]))
                    # We check both slides to determine if the optional split is needed (there is only one optional
                    # split).
                    else:
                        html_text = expand_tags('\n'.join(slides))
                    html_text = html_text.replace('\n', '<br>')
                    if self._text_fits_on_slide(html_text):
                        # The first two optional slides fit (as a whole) on one slide. Replace the first occurrence
                        # of [---].
                        text = text.replace('\n[---]', '', 1)
                    else:
                        # The first optional slide fits, which means we have to render the first optional slide.
                        text_contains_split = '[---]' in text
                        if text_contains_split:
                            try:
                                text_to_render, text = text.split(
                                    '\n[---]\n', 1)
                            except ValueError:
                                text_to_render = text.split('\n[---]\n')[0]
                                text = ''
                            text_to_render, raw_tags, html_tags = get_start_tags(
                                text_to_render)
                            if text:
                                text = raw_tags + text
                        else:
                            text_to_render = text
                            text = ''
                        lines = text_to_render.strip('\n').split('\n')
                        slides = self._paginate_slide(lines, line_end)
                        if len(slides) > 1 and text:
                            # Add all slides apart from the last one the list.
                            pages.extend(slides[:-1])
                            if text_contains_split:
                                text = slides[-1] + '\n[---]\n' + text
                            else:
                                text = slides[-1] + '\n' + text
                            text = text.replace('<br>', '\n')
                        else:
                            pages.extend(slides)
                    if '[---]' not in text:
                        lines = text.strip('\n').split('\n')
                        pages.extend(self._paginate_slide(lines, line_end))
                        break
                    count += 1
            else:
                # Clean up line endings.
                pages = self._paginate_slide(text.split('\n'), line_end)
        else:
            pages = self._paginate_slide(text.split('\n'), line_end)
        new_pages = []
        for page in pages:
            while page.endswith('<br>'):
                page = page[:-4]
            new_pages.append(page)
        return new_pages
Ejemplo n.º 13
0
    def format_slide(self, text, item):
        """
        Calculate how much text can fit on a slide.

        :param text:  The words to go on the slides.
        :param item: The :class:`~openlp.core.lib.serviceitem.ServiceItem` item object.

        """
        self.log_debug('format slide')
        # Add line endings after each line of text used for bibles.
        line_end = '<br>'
        if item.is_capable(ItemCapabilities.NoLineBreaks):
            line_end = ' '
        # Bibles
        if item.is_capable(ItemCapabilities.CanWordSplit):
            pages = self._paginate_slide_words(text.split('\n'), line_end)
        # Songs and Custom
        elif item.is_capable(ItemCapabilities.CanSoftBreak):
            pages = []
            if '[---]' in text:
                # Remove two or more option slide breaks next to each other (causing infinite loop).
                while '\n[---]\n[---]\n' in text:
                    text = text.replace('\n[---]\n[---]\n', '\n[---]\n')
                while ' [---]' in text:
                    text = text.replace(' [---]', '[---]')
                while '[---] ' in text:
                    text = text.replace('[---] ', '[---]')
                count = 0
                # only loop 5 times as there will never be more than 5 incorrect logical splits on a single slide.
                while True and count < 5:
                    slides = text.split('\n[---]\n', 2)
                    # If there are (at least) two occurrences of [---] we use the first two slides (and neglect the last
                    # for now).
                    if len(slides) == 3:
                        html_text = expand_tags('\n'.join(slides[:2]))
                    # We check both slides to determine if the optional split is needed (there is only one optional
                    # split).
                    else:
                        html_text = expand_tags('\n'.join(slides))
                    html_text = html_text.replace('\n', '<br>')
                    if self._text_fits_on_slide(html_text):
                        # The first two optional slides fit (as a whole) on one slide. Replace the first occurrence
                        # of [---].
                        text = text.replace('\n[---]', '', 1)
                    else:
                        # The first optional slide fits, which means we have to render the first optional slide.
                        text_contains_split = '[---]' in text
                        if text_contains_split:
                            try:
                                text_to_render, text = text.split('\n[---]\n', 1)
                            except ValueError:
                                text_to_render = text.split('\n[---]\n')[0]
                                text = ''
                            text_to_render, raw_tags, html_tags = get_start_tags(text_to_render)
                            if text:
                                text = raw_tags + text
                        else:
                            text_to_render = text
                            text = ''
                        lines = text_to_render.strip('\n').split('\n')
                        slides = self._paginate_slide(lines, line_end)
                        if len(slides) > 1 and text:
                            # Add all slides apart from the last one the list.
                            pages.extend(slides[:-1])
                            if text_contains_split:
                                text = slides[-1] + '\n[---]\n' + text
                            else:
                                text = slides[-1] + '\n' + text
                            text = text.replace('<br>', '\n')
                        else:
                            pages.extend(slides)
                    if '[---]' not in text:
                        lines = text.strip('\n').split('\n')
                        pages.extend(self._paginate_slide(lines, line_end))
                        break
                    count += 1
            else:
                # Clean up line endings.
                pages = self._paginate_slide(text.split('\n'), line_end)
        else:
            pages = self._paginate_slide(text.split('\n'), line_end)
        new_pages = []
        for page in pages:
            while page.endswith('<br>'):
                page = page[:-4]
            new_pages.append(page)
        return new_pages
Ejemplo n.º 14
0
    def render(self, provides_own_theme_data=False):
        """
        The render method is what generates the frames for the screen and obtains the display information from the
        renderer. At this point all slides are built for the given display size.

        :param provides_own_theme_data: This switch disables the usage of the item's theme. However, this is
            disabled by default. If this is used, it has to be taken care, that
            the renderer knows the correct theme data. However, this is needed
            for the theme manager.
        """
        log.debug('Render called')
        self._display_frames = []
        self.bg_image_bytes = None
        if not provides_own_theme_data:
            self.renderer.set_item_theme(self.theme)
            self.theme_data, self.main, self.footer = self.renderer.pre_render()
        if self.service_item_type == ServiceItemType.Text:
            log.debug('Formatting slides: %s' % self.title)
            # Save rendered pages to this dict. In the case that a slide is used twice we can use the pages saved to
            # the dict instead of rendering them again.

            if self.get_plugin_name() == 'songs':
                if not self.extra_data_dict:
                    self.extra_data_dict = {}
                    if not self.xml_version.startswith('<?xml') and not self.xml_version.startswith('<song'):
                        # This is an old style song, without XML.
                        verses = self.xml_version.split('\n\n')
                        for count, verse in enumerate(verses):
                            self.extra_data_dict['v' + str(count)] = str(verse).split('\n')

                    elif self.xml_version.startswith('<?xml'):
                        # New style song, output as XML.
                        song_xml_split = re.split('(<verse[\w"= ]*>)', self.xml_version)
                        current_section = ''

                        for song_part in song_xml_split:
                            if song_part.startswith("<verse name"):
                                current_section = song_part.replace('<verse name="', '').replace('">', '')
                            elif song_part.startswith("<lines"):
                                # New line needed between <lines> sections
                                song_part = song_part.replace('</lines><line', '</lines><br/><line')
                                # Split out <lines>, </lines>, </verse>, </lyrics> and </song> tags
                                song_part = ''.join(re.split('<lines[\w"= ]*>', song_part))
                                song_part = song_part.replace('</lines>','').replace('</verse>', '')
                                song_part = song_part.replace('</lyrics>','').replace('</song>', '')
                                # Convert to list
                                self.extra_data_dict[current_section] = song_part.split('<br/>')

                previous_pages = {}

                # Get key and transpose amount, if used in this song
                if "<key>" in self.xml_version:
                    current_key = self.xml_version[(self.xml_version.index('<key>') + 5):(self.xml_version.index('<key>')+7)]
                    if current_key[1] == '<':
                        current_key = current_key[0]
                else:
                    current_key = ''

                if "<transposition>" in self.xml_version:
                    current_transpose = self.xml_version[(self.xml_version.index('<transposition>') + 15):(self.xml_version.index('<transposition>')+18)]
                    # Possible options: 1,...,9, 10, 11, -1, ... -9, -10, -11
                    if current_transpose[1] == '<':
                        current_transpose = int(current_transpose[0])
                    elif current_transpose[2] == '<':
                        current_transpose = int(current_transpose[0:2])
                else:
                    current_transpose = 0

                # Calculate resultant key for musician oriented view
                if current_key != '':
                    resultant_key = Chords.transpose_chord(current_key, current_key, current_transpose)
                else:
                    resultant_key = ''

                for slide in self._raw_frames:
                    verse_tag = slide['verseTag']
                    if verse_tag in previous_pages and previous_pages[verse_tag][0] == slide['raw_slide']:
                        pages = previous_pages[verse_tag][1]
                    else:
                        pages = self.renderer.format_slide(slide['raw_slide'], self)
                        previous_pages[verse_tag] = (slide['raw_slide'], pages)

                    xml_lines = self.extra_data_dict[verse_tag.lower()]
                    xml_line_lower, xml_line_upper = 0, 0

                    subpage_count = 0
                    for page in pages:
                        subpage_count += 1
                        page_lines = page.split('<br>')

                        # Given the current page_lines, calculate the corresponding xml_lines
                        xml_segment = ''
                        xml_line_lower = xml_line_upper

                        for line in page_lines:
                            while (xml_line_upper < len(xml_lines)) and not (''.join(re.split('<chord[\w\+#"=// ]* />', xml_lines[xml_line_upper])).strip() == line.strip()):
                                xml_line_upper += 1

                        xml_line_upper += 1
                        page_xml = xml_lines[xml_line_lower: xml_line_upper]

                        # Exclude any [br] or [---] elements from the XML list
                        page_xml_short = []
                        for item in page_xml:
                            if item != '[br]' and item != '[---]':
								# split item by chord tags, then transpose each chord tag
                                if current_transpose != 0:
                                    item_sections = re.split('(<chord[\w\+#"=// ]* />)', item)
                                    transposed_item = ''

                                    for item_section in item_sections:
                                        if item_section.startswith('<chord name'):
                                            transposed_item = transposed_item + Chords.transpose_chord_tag(item_section, current_key, current_transpose)
                                        else:
                                            transposed_item = transposed_item + item_section
                                    page_xml_short.append(transposed_item)
                                else:
                                    page_xml_short.append(item)

                        page = page.replace('<br>', '{br}')
                        html_data = expand_tags(html.escape(page.rstrip()))
                        self._display_frames.append({
                            'title': clean_tags(page),
                            'text': clean_tags(page.rstrip()),
                            'html': html_data.replace('&amp;nbsp;', '&nbsp;'),
                            'verseTag': verse_tag,
                            'verseSubpage': subpage_count,
                            'extraInfo': '<br>'.join(page_xml_short),
                            'playedKey': resultant_key
                        })

                        # Deal with any [br] tag in XML before processing next page
                        # TODO: Check if we need to also do this for [---] tags
                        if xml_line_upper < len(xml_lines) and xml_lines[xml_line_upper].strip() == '[br]':
                            xml_line_upper += 1

            else:
                previous_pages = {}
                for slide in self._raw_frames:
                    verse_tag = slide['verseTag']
                    if verse_tag in previous_pages and previous_pages[verse_tag][0] == slide['raw_slide']:
                        pages = previous_pages[verse_tag][1]
                    else:
                        pages = self.renderer.format_slide(slide['raw_slide'], self)
                        previous_pages[verse_tag] = (slide['raw_slide'], pages)

                    for page in pages:
                        page = page.replace('<br>', '{br}')
                        html_data = expand_tags(html.escape(page.rstrip()))
                        self._display_frames.append({
                            'title': clean_tags(page),
                            'text': clean_tags(page.rstrip()),
                            'html': html_data.replace('&amp;nbsp;', '&nbsp;'),
                            'verseTag': verse_tag,
                        })

        elif self.service_item_type == ServiceItemType.Image or self.service_item_type == ServiceItemType.Command:
            pass
        else:
            log.error('Invalid value renderer: %s' % self.service_item_type)
        self.title = clean_tags(self.title)
        # The footer should never be None, but to be compatible with a few
        # nightly builds between 1.9.4 and 1.9.5, we have to correct this to
        # avoid tracebacks.
        if self.raw_footer is None:
            self.raw_footer = []
        self.foot_text = '<br>'.join([_f for _f in self.raw_footer if _f])
Ejemplo n.º 15
0
    def format_slide(self, text, item):
        """
        Calculate how much text can fit on a slide.

        ``text``
            The words to go on the slides.

        ``item``
            The :class:`~openlp.core.lib.serviceitem.ServiceItem` item object.
        """
        log.debug(u'format slide')
        # Add line endings after each line of text used for bibles.
        line_end = u'<br>'
        if item.is_capable(ItemCapabilities.NoLineBreaks):
            line_end = u' '
        # Bibles
        if item.is_capable(ItemCapabilities.CanWordSplit):
            pages = self._paginate_slide_words(text.split(u'\n'), line_end)
        # Songs and Custom
        elif item.is_capable(ItemCapabilities.CanSoftBreak):
            pages = []
            if u'[---]' in text:
                while True:
                    slides = text.split(u'\n[---]\n', 2)
                    # If there are (at least) two occurrences of [---] we use
                    # the first two slides (and neglect the last for now).
                    if len(slides) == 3:
                        html_text = expand_tags(u'\n'.join(slides[:2]))
                    # We check both slides to determine if the optional split is
                    # needed (there is only one optional split).
                    else:
                        html_text = expand_tags(u'\n'.join(slides))
                    html_text = html_text.replace(u'\n', u'<br>')
                    if self._text_fits_on_slide(html_text):
                        # The first two optional slides fit (as a whole) on one
                        # slide. Replace the first occurrence of [---].
                        text = text.replace(u'\n[---]', u'', 1)
                    else:
                        # The first optional slide fits, which means we have to
                        # render the first optional slide.
                        text_contains_split = u'[---]' in text
                        if text_contains_split:
                            try:
                                text_to_render, text = text.split(u'\n[---]\n', 1)
                            except ValueError:
                                text_to_render = text.split(u'\n[---]\n')[0]
                                text = u''
                            text_to_render, raw_tags, html_tags = self._get_start_tags(text_to_render)
                            if text:
                                text = raw_tags + text
                        else:
                            text_to_render = text
                            text = u''
                        lines = text_to_render.strip(u'\n').split(u'\n')
                        slides = self._paginate_slide(lines, line_end)
                        if len(slides) > 1 and text:
                            # Add all slides apart from the last one the list.
                            pages.extend(slides[:-1])
                            if  text_contains_split:
                                text = slides[-1] + u'\n[---]\n' + text
                            else:
                                text = slides[-1] + u'\n' + text
                            text = text.replace(u'<br>', u'\n')
                        else:
                            pages.extend(slides)
                    if u'[---]' not in text:
                        lines = text.strip(u'\n').split(u'\n')
                        pages.extend(self._paginate_slide(lines, line_end))
                        break
            else:
                # Clean up line endings.
                pages = self._paginate_slide(text.split(u'\n'), line_end)
        else:
            pages = self._paginate_slide(text.split(u'\n'), line_end)
        new_pages = []
        for page in pages:
            while page.endswith(u'<br>'):
                page = page[:-4]
            new_pages.append(page)
        return new_pages