2010-03-21 23:58:01 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2008-12-01 13:15:31 +00:00
|
|
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
2009-07-14 13:51:27 +00:00
|
|
|
|
2009-09-08 19:58:05 +00:00
|
|
|
###############################################################################
|
|
|
|
# OpenLP - Open Source Lyrics Projection #
|
|
|
|
# --------------------------------------------------------------------------- #
|
2010-12-26 11:04:47 +00:00
|
|
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
2011-05-26 16:25:54 +00:00
|
|
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
|
|
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
2011-05-26 17:11:22 +00:00
|
|
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
2011-05-26 16:25:54 +00:00
|
|
|
# Põldaru, Christian Richter, Philip Ridout, Jeffrey Smith, Maikel #
|
|
|
|
# Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
2009-09-08 19:58:05 +00:00
|
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
# This program is free software; you can redistribute it and/or modify it #
|
|
|
|
# under the terms of the GNU General Public License as published by the Free #
|
|
|
|
# Software Foundation; version 2 of the License. #
|
|
|
|
# #
|
|
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
|
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
|
|
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
|
|
|
# more details. #
|
|
|
|
# #
|
|
|
|
# You should have received a copy of the GNU General Public License along #
|
|
|
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
|
|
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
|
|
|
###############################################################################
|
2009-05-11 05:09:43 +00:00
|
|
|
|
2011-03-28 18:12:46 +00:00
|
|
|
import logging
|
|
|
|
|
|
|
|
from PyQt4 import QtCore, QtWebKit
|
|
|
|
|
2011-05-05 11:51:47 +00:00
|
|
|
from openlp.core.lib import ServiceItem, expand_tags, \
|
2011-03-28 18:12:46 +00:00
|
|
|
build_lyrics_format_css, build_lyrics_outline_css, Receiver, \
|
|
|
|
ItemCapabilities
|
|
|
|
from openlp.core.lib.theme import ThemeLevel
|
2011-05-04 17:31:02 +00:00
|
|
|
from openlp.core.ui import MainDisplay, ScreenList
|
2011-03-28 18:12:46 +00:00
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
VERSE = u'The Lord said to {r}Noah{/r}: \n' \
|
|
|
|
'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \
|
|
|
|
'The Lord said to {g}Noah{/g}:\n' \
|
|
|
|
'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n' \
|
|
|
|
'Get those children out of the muddy, muddy \n' \
|
|
|
|
'{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \
|
|
|
|
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
|
|
|
|
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
|
|
|
|
|
2011-04-03 06:19:03 +00:00
|
|
|
HTML_END = u'</div></body></html>'
|
|
|
|
|
2011-03-28 18:12:46 +00:00
|
|
|
class Renderer(object):
|
|
|
|
"""
|
|
|
|
Class to pull all Renderer interactions into one place. The plugins will
|
|
|
|
call helper methods to do the rendering but this class will provide
|
|
|
|
display defense code.
|
|
|
|
"""
|
2011-03-28 18:56:39 +00:00
|
|
|
log.info(u'Renderer Loaded')
|
2011-03-28 18:12:46 +00:00
|
|
|
|
2011-05-05 15:16:55 +00:00
|
|
|
def __init__(self, image_manager, theme_manager):
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
|
|
|
Initialise the render manager.
|
2011-05-07 20:55:11 +00:00
|
|
|
|
|
|
|
``image_manager``
|
|
|
|
A ImageManager instance which takes care of e. g. caching and resizing
|
|
|
|
images.
|
|
|
|
|
|
|
|
``theme_manager``
|
|
|
|
The ThemeManager instance, used to get the current theme details.
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
|
|
|
log.debug(u'Initilisation started')
|
|
|
|
self.theme_manager = theme_manager
|
2011-05-05 15:16:55 +00:00
|
|
|
self.image_manager = image_manager
|
2011-05-05 11:51:47 +00:00
|
|
|
self.screens = ScreenList.get_instance()
|
2011-03-28 18:12:46 +00:00
|
|
|
self.service_theme = u''
|
|
|
|
self.theme_level = u''
|
|
|
|
self.override_background = None
|
|
|
|
self.theme_data = None
|
2011-05-05 11:51:47 +00:00
|
|
|
self.bg_frame = None
|
2011-03-28 18:12:46 +00:00
|
|
|
self.force_page = False
|
2011-05-05 11:51:47 +00:00
|
|
|
self.display = MainDisplay(self, self.image_manager, False)
|
|
|
|
self.display.setup()
|
2011-03-28 18:12:46 +00:00
|
|
|
|
|
|
|
def update_display(self):
|
|
|
|
"""
|
|
|
|
Updates the render manager's information about the current screen.
|
|
|
|
"""
|
|
|
|
log.debug(u'Update Display')
|
|
|
|
self._calculate_default(self.screens.current[u'size'])
|
2011-05-05 11:51:47 +00:00
|
|
|
self.display = MainDisplay(self, self.image_manager, False)
|
2011-03-28 18:12:46 +00:00
|
|
|
self.display.setup()
|
|
|
|
self.bg_frame = None
|
|
|
|
self.theme_data = None
|
|
|
|
|
|
|
|
def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
|
|
|
|
"""
|
|
|
|
Set the global-level theme and the theme level.
|
|
|
|
|
|
|
|
``global_theme``
|
|
|
|
The global-level theme to be set.
|
|
|
|
|
|
|
|
``theme_level``
|
|
|
|
Defaults to *``ThemeLevel.Global``*. The theme level, can be
|
|
|
|
``ThemeLevel.Global``, ``ThemeLevel.Service`` or
|
|
|
|
``ThemeLevel.Song``.
|
|
|
|
"""
|
|
|
|
self.global_theme = global_theme
|
|
|
|
self.theme_level = theme_level
|
|
|
|
self.global_theme_data = \
|
|
|
|
self.theme_manager.getThemeData(self.global_theme)
|
|
|
|
self.theme_data = None
|
|
|
|
|
|
|
|
def set_service_theme(self, service_theme):
|
|
|
|
"""
|
|
|
|
Set the service-level theme.
|
|
|
|
|
|
|
|
``service_theme``
|
|
|
|
The service-level theme to be set.
|
|
|
|
"""
|
|
|
|
self.service_theme = service_theme
|
|
|
|
self.theme_data = None
|
|
|
|
|
2011-04-03 06:19:03 +00:00
|
|
|
def set_override_theme(self, override_theme, override_levels=False):
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
|
|
|
Set the appropriate theme depending on the theme level.
|
|
|
|
Called by the service item when building a display frame
|
|
|
|
|
|
|
|
``theme``
|
|
|
|
The name of the song-level theme. None means the service
|
|
|
|
item wants to use the given value.
|
|
|
|
|
2011-04-01 04:48:09 +00:00
|
|
|
``override_levels``
|
2011-03-28 18:12:46 +00:00
|
|
|
Used to force the theme data passed in to be used.
|
|
|
|
|
|
|
|
"""
|
2011-04-03 06:19:03 +00:00
|
|
|
log.debug(u'set override theme to %s', override_theme)
|
2011-03-28 18:12:46 +00:00
|
|
|
theme_level = self.theme_level
|
2011-04-01 04:48:09 +00:00
|
|
|
if override_levels:
|
2011-03-28 18:12:46 +00:00
|
|
|
theme_level = ThemeLevel.Song
|
|
|
|
if theme_level == ThemeLevel.Global:
|
2011-04-03 06:19:03 +00:00
|
|
|
theme = self.global_theme
|
2011-03-28 18:12:46 +00:00
|
|
|
elif theme_level == ThemeLevel.Service:
|
|
|
|
if self.service_theme == u'':
|
2011-04-03 06:19:03 +00:00
|
|
|
theme = self.global_theme
|
2011-03-28 18:12:46 +00:00
|
|
|
else:
|
2011-04-03 06:19:03 +00:00
|
|
|
theme = self.service_theme
|
2011-03-28 18:12:46 +00:00
|
|
|
else:
|
|
|
|
# Images have a theme of -1
|
2011-04-03 06:19:03 +00:00
|
|
|
if override_theme and override_theme != -1:
|
|
|
|
theme = override_theme
|
2011-03-28 18:12:46 +00:00
|
|
|
elif theme_level == ThemeLevel.Song or \
|
|
|
|
theme_level == ThemeLevel.Service:
|
|
|
|
if self.service_theme == u'':
|
2011-04-03 06:19:03 +00:00
|
|
|
theme = self.global_theme
|
2011-03-28 18:12:46 +00:00
|
|
|
else:
|
2011-04-03 06:19:03 +00:00
|
|
|
theme = self.service_theme
|
2011-03-28 18:12:46 +00:00
|
|
|
else:
|
2011-04-03 06:19:03 +00:00
|
|
|
theme = self.global_theme
|
|
|
|
log.debug(u'theme is now %s', theme)
|
2011-03-28 18:12:46 +00:00
|
|
|
# Force the theme to be the one passed in.
|
2011-04-01 04:48:09 +00:00
|
|
|
if override_levels:
|
2011-04-03 06:19:03 +00:00
|
|
|
self.theme_data = override_theme
|
2011-03-28 18:12:46 +00:00
|
|
|
else:
|
2011-04-03 06:19:03 +00:00
|
|
|
self.theme_data = self.theme_manager.getThemeData(theme)
|
2011-03-28 18:12:46 +00:00
|
|
|
self._calculate_default(self.screens.current[u'size'])
|
|
|
|
self._build_text_rectangle(self.theme_data)
|
2011-05-15 13:42:12 +00:00
|
|
|
# if No file do not update cache
|
|
|
|
if self.theme_data.background_filename:
|
|
|
|
self.image_manager.add_image(self.theme_data.theme_name,
|
|
|
|
self.theme_data.background_filename)
|
2011-03-28 18:12:46 +00:00
|
|
|
return self._rect, self._rect_footer
|
|
|
|
|
|
|
|
def generate_preview(self, theme_data, force_page=False):
|
|
|
|
"""
|
|
|
|
Generate a preview of a theme.
|
|
|
|
|
|
|
|
``theme_data``
|
|
|
|
The theme to generated a preview for.
|
|
|
|
|
|
|
|
``force_page``
|
|
|
|
Flag to tell message lines per page need to be generated.
|
|
|
|
"""
|
|
|
|
log.debug(u'generate preview')
|
|
|
|
# save value for use in format_slide
|
|
|
|
self.force_page = force_page
|
|
|
|
# set the default image size for previews
|
2011-03-29 16:25:48 +00:00
|
|
|
self._calculate_default(self.screens.preview[u'size'])
|
2011-03-28 18:12:46 +00:00
|
|
|
# build a service item to generate preview
|
|
|
|
serviceItem = ServiceItem()
|
|
|
|
serviceItem.theme = theme_data
|
|
|
|
if self.force_page:
|
|
|
|
# make big page for theme edit dialog to get line count
|
2011-05-11 22:32:25 +00:00
|
|
|
serviceItem.add_from_text(u'', VERSE + VERSE + VERSE)
|
2011-03-28 18:12:46 +00:00
|
|
|
else:
|
|
|
|
self.image_manager.del_image(theme_data.theme_name)
|
2011-05-11 22:32:25 +00:00
|
|
|
serviceItem.add_from_text(u'', VERSE)
|
2011-03-29 16:25:48 +00:00
|
|
|
serviceItem.renderer = self
|
2011-03-28 18:12:46 +00:00
|
|
|
serviceItem.raw_footer = FOOTER
|
|
|
|
serviceItem.render(True)
|
|
|
|
if not self.force_page:
|
|
|
|
self.display.buildHtml(serviceItem)
|
|
|
|
raw_html = serviceItem.get_rendered_frame(0)
|
|
|
|
preview = self.display.text(raw_html)
|
|
|
|
# Reset the real screen size for subsequent render requests
|
|
|
|
self._calculate_default(self.screens.current[u'size'])
|
|
|
|
return preview
|
|
|
|
|
2011-04-03 06:19:03 +00:00
|
|
|
def format_slide(self, text, line_break, item):
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
|
|
|
Calculate how much text can fit on a slide.
|
|
|
|
|
2011-04-03 06:19:03 +00:00
|
|
|
``text``
|
2011-03-28 18:12:46 +00:00
|
|
|
The words to go on the slides.
|
|
|
|
|
|
|
|
``line_break``
|
|
|
|
Add line endings after each line of text used for bibles.
|
|
|
|
"""
|
|
|
|
log.debug(u'format slide')
|
|
|
|
# clean up line endings
|
2011-04-08 15:35:33 +00:00
|
|
|
lines = self._lines_split(text)
|
2011-04-27 17:41:03 +00:00
|
|
|
pages = self._paginate_slide(lines, line_break, self.force_page)
|
|
|
|
if len(pages) > 1:
|
|
|
|
# Songs and Custom
|
|
|
|
if item.is_capable(ItemCapabilities.AllowsVirtualSplit):
|
|
|
|
# Do not forget the line breaks !
|
2011-04-29 06:15:09 +00:00
|
|
|
slides = text.split(u'[---]')
|
2011-04-27 17:41:03 +00:00
|
|
|
pages = []
|
|
|
|
for slide in slides:
|
2011-04-29 14:31:17 +00:00
|
|
|
lines = slide.strip(u'\n').split(u'\n')
|
2011-04-27 17:41:03 +00:00
|
|
|
new_pages = self._paginate_slide(lines, line_break,
|
|
|
|
self.force_page)
|
2011-04-29 08:28:41 +00:00
|
|
|
pages.extend(new_pages)
|
2011-04-27 17:41:03 +00:00
|
|
|
# Bibles
|
|
|
|
elif item.is_capable(ItemCapabilities.AllowsWordSplit):
|
|
|
|
pages = self._paginate_slide_words(text, line_break)
|
2011-03-28 18:12:46 +00:00
|
|
|
return pages
|
|
|
|
|
|
|
|
def _calculate_default(self, screen):
|
|
|
|
"""
|
|
|
|
Calculate the default dimentions of the screen.
|
|
|
|
|
|
|
|
``screen``
|
|
|
|
The QSize of the screen.
|
|
|
|
"""
|
|
|
|
log.debug(u'calculate default %s', screen)
|
|
|
|
self.width = screen.width()
|
|
|
|
self.height = screen.height()
|
|
|
|
self.screen_ratio = float(self.height) / float(self.width)
|
|
|
|
log.debug(u'calculate default %d, %d, %f',
|
|
|
|
self.width, self.height, self.screen_ratio)
|
|
|
|
# 90% is start of footer
|
|
|
|
self.footer_start = int(self.height * 0.90)
|
|
|
|
|
|
|
|
def _build_text_rectangle(self, theme):
|
|
|
|
"""
|
|
|
|
Builds a text block using the settings in ``theme``
|
|
|
|
and the size of the display screen.height.
|
2011-04-25 06:44:04 +00:00
|
|
|
Note the system has a 10 pixel border round the screen
|
2011-03-28 18:12:46 +00:00
|
|
|
|
|
|
|
``theme``
|
|
|
|
The theme to build a text block for.
|
|
|
|
"""
|
|
|
|
log.debug(u'_build_text_rectangle')
|
|
|
|
main_rect = None
|
|
|
|
footer_rect = None
|
|
|
|
if not theme.font_main_override:
|
|
|
|
main_rect = QtCore.QRect(10, 0, self.width - 20, self.footer_start)
|
|
|
|
else:
|
|
|
|
main_rect = QtCore.QRect(theme.font_main_x, theme.font_main_y,
|
|
|
|
theme.font_main_width - 1, theme.font_main_height - 1)
|
|
|
|
if not theme.font_footer_override:
|
|
|
|
footer_rect = QtCore.QRect(10, self.footer_start, self.width - 20,
|
|
|
|
self.height - self.footer_start)
|
|
|
|
else:
|
|
|
|
footer_rect = QtCore.QRect(theme.font_footer_x,
|
|
|
|
theme.font_footer_y, theme.font_footer_width - 1,
|
|
|
|
theme.font_footer_height - 1)
|
|
|
|
self._set_text_rectangle(main_rect, footer_rect)
|
|
|
|
|
|
|
|
def _set_text_rectangle(self, rect_main, rect_footer):
|
|
|
|
"""
|
|
|
|
Sets the rectangle within which text should be rendered.
|
|
|
|
|
|
|
|
``rect_main``
|
|
|
|
The main text block.
|
|
|
|
|
|
|
|
``rect_footer``
|
|
|
|
The footer text block.
|
|
|
|
"""
|
|
|
|
log.debug(u'set_text_rectangle %s , %s' % (rect_main, rect_footer))
|
|
|
|
self._rect = rect_main
|
|
|
|
self._rect_footer = rect_footer
|
|
|
|
self.page_width = self._rect.width()
|
|
|
|
self.page_height = self._rect.height()
|
|
|
|
if self.theme_data.font_main_shadow:
|
|
|
|
self.page_width -= int(self.theme_data.font_main_shadow_size)
|
|
|
|
self.page_height -= int(self.theme_data.font_main_shadow_size)
|
|
|
|
self.web = QtWebKit.QWebView()
|
|
|
|
self.web.setVisible(False)
|
|
|
|
self.web.resize(self.page_width, self.page_height)
|
|
|
|
self.web_frame = self.web.page().mainFrame()
|
|
|
|
# Adjust width and height to account for shadow. outline done in css
|
|
|
|
self.page_shell = u'<html><head><style>' \
|
|
|
|
u'*{margin: 0; padding: 0; border: 0;} '\
|
2011-05-17 18:55:45 +00:00
|
|
|
u'#main {position:absolute; top:0px; %s %s}</style></head><body>' \
|
2011-03-28 18:12:46 +00:00
|
|
|
u'<div id="main">' % \
|
|
|
|
(build_lyrics_format_css(self.theme_data, self.page_width,
|
|
|
|
self.page_height), build_lyrics_outline_css(self.theme_data))
|
|
|
|
|
|
|
|
def _paginate_slide(self, lines, line_break, force_page=False):
|
|
|
|
"""
|
|
|
|
Figure out how much text can appear on a slide, using the current
|
|
|
|
theme settings.
|
|
|
|
|
|
|
|
``lines``
|
|
|
|
The words to be fitted on the slide split into lines.
|
|
|
|
|
|
|
|
``line_break``
|
2011-04-26 17:03:19 +00:00
|
|
|
Add line endings after each line of text (used for bibles).
|
2011-03-28 18:12:46 +00:00
|
|
|
|
|
|
|
``force_page``
|
|
|
|
Flag to tell message lines in page.
|
|
|
|
|
|
|
|
"""
|
2011-04-26 17:03:19 +00:00
|
|
|
log.debug(u'_paginate_slide - Start')
|
2011-03-28 18:12:46 +00:00
|
|
|
line_end = u''
|
|
|
|
if line_break:
|
|
|
|
line_end = u'<br>'
|
|
|
|
formatted = []
|
|
|
|
html_text = u''
|
|
|
|
styled_text = u''
|
|
|
|
line_count = 0
|
|
|
|
for line in lines:
|
|
|
|
if line_count != -1:
|
|
|
|
line_count += 1
|
|
|
|
styled_line = expand_tags(line) + line_end
|
|
|
|
styled_text += styled_line
|
2011-04-03 06:19:03 +00:00
|
|
|
html = self.page_shell + styled_text + HTML_END
|
2011-03-28 18:12:46 +00:00
|
|
|
self.web.setHtml(html)
|
2011-05-23 18:41:49 +00:00
|
|
|
# Text too long so go to next page.
|
2011-03-28 18:12:46 +00:00
|
|
|
if self.web_frame.contentsSize().height() > self.page_height:
|
|
|
|
if force_page and line_count > 0:
|
|
|
|
Receiver.send_message(u'theme_line_count', line_count)
|
|
|
|
line_count = -1
|
2011-04-29 14:31:17 +00:00
|
|
|
while html_text.endswith(u'<br>'):
|
|
|
|
html_text = html_text[:-4]
|
2011-03-28 18:12:46 +00:00
|
|
|
formatted.append(html_text)
|
|
|
|
html_text = u''
|
|
|
|
styled_text = styled_line
|
|
|
|
html_text += line + line_end
|
2011-04-29 14:31:17 +00:00
|
|
|
while html_text.endswith(u'<br>'):
|
2011-04-29 14:36:29 +00:00
|
|
|
html_text = html_text[:-4]
|
2011-03-28 18:12:46 +00:00
|
|
|
formatted.append(html_text)
|
2011-04-26 17:03:19 +00:00
|
|
|
log.debug(u'_paginate_slide - End')
|
2011-03-28 18:12:46 +00:00
|
|
|
return formatted
|
|
|
|
|
2011-04-03 06:19:03 +00:00
|
|
|
def _paginate_slide_words(self, text, line_break):
|
2011-04-01 04:48:09 +00:00
|
|
|
"""
|
|
|
|
Figure out how much text can appear on a slide, using the current
|
2011-04-03 06:19:03 +00:00
|
|
|
theme settings. This version is to handle text which needs to be split
|
|
|
|
into words to get it to fit.
|
2011-04-01 04:48:09 +00:00
|
|
|
|
2011-04-03 06:19:03 +00:00
|
|
|
``text``
|
2011-04-01 04:48:09 +00:00
|
|
|
The words to be fitted on the slide split into lines.
|
2011-04-03 06:19:03 +00:00
|
|
|
|
|
|
|
``line_break``
|
|
|
|
Add line endings after each line of text used for bibles.
|
|
|
|
|
2011-04-01 04:48:09 +00:00
|
|
|
"""
|
2011-04-26 17:03:19 +00:00
|
|
|
log.debug(u'_paginate_slide_words - Start')
|
2011-05-23 17:28:52 +00:00
|
|
|
line_end = u' '
|
2011-04-01 04:48:09 +00:00
|
|
|
if line_break:
|
|
|
|
line_end = u'<br>'
|
|
|
|
formatted = []
|
2011-04-26 17:03:19 +00:00
|
|
|
previous_html = u''
|
|
|
|
previous_raw = u''
|
2011-04-29 14:31:17 +00:00
|
|
|
lines = text.split(u'\n')
|
2011-04-01 04:48:09 +00:00
|
|
|
for line in lines:
|
2011-05-23 17:28:52 +00:00
|
|
|
line = line.strip()
|
2011-04-09 08:11:46 +00:00
|
|
|
styled_line = expand_tags(line)
|
2011-04-26 17:03:19 +00:00
|
|
|
html = self.page_shell + previous_html + styled_line + HTML_END
|
2011-04-09 08:11:46 +00:00
|
|
|
self.web.setHtml(html)
|
2011-05-23 18:41:49 +00:00
|
|
|
# Text too long so go to next page.
|
2011-04-09 08:11:46 +00:00
|
|
|
if self.web_frame.contentsSize().height() > self.page_height:
|
2011-04-28 06:44:48 +00:00
|
|
|
# Check if there was a verse before the current one and append
|
|
|
|
# it, when it fits on the page.
|
|
|
|
if previous_html:
|
|
|
|
html = self.page_shell + previous_html + HTML_END
|
|
|
|
self.web.setHtml(html)
|
|
|
|
if self.web_frame.contentsSize().height() <= \
|
|
|
|
self.page_height:
|
2011-04-29 14:31:17 +00:00
|
|
|
while previous_raw.endswith(u'<br>'):
|
|
|
|
previous_raw = previous_raw[:-4]
|
2011-04-28 06:44:48 +00:00
|
|
|
formatted.append(previous_raw)
|
|
|
|
previous_html = u''
|
|
|
|
previous_raw = u''
|
|
|
|
html = self.page_shell + styled_line + HTML_END
|
|
|
|
self.web.setHtml(html)
|
|
|
|
# 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.web_frame.contentsSize().height() <= \
|
|
|
|
self.page_height:
|
|
|
|
previous_html = styled_line + line_end
|
|
|
|
previous_raw = line + line_end
|
|
|
|
continue
|
2011-05-24 07:02:13 +00:00
|
|
|
# Figure out how many words of the line will fit on screen by
|
|
|
|
# using the algorithm known as "binary chop".
|
2011-05-23 17:28:52 +00:00
|
|
|
raw_words = self._words_split(line)
|
|
|
|
html_words = [expand_tags(word) for word in raw_words]
|
|
|
|
smallest_index = 0
|
|
|
|
highest_index = len(html_words) - 1
|
|
|
|
index = int(highest_index / 2)
|
|
|
|
while True:
|
|
|
|
html = self.page_shell + previous_html + \
|
|
|
|
u''.join(html_words[:index + 1]).strip() + HTML_END
|
2011-04-26 17:03:19 +00:00
|
|
|
self.web.setHtml(html)
|
2011-04-26 19:09:56 +00:00
|
|
|
if self.web_frame.contentsSize().height() > \
|
|
|
|
self.page_height:
|
2011-05-23 17:28:52 +00:00
|
|
|
# We know that it does not fit, so change/calculate the
|
|
|
|
# new index and highest_index accordingly.
|
|
|
|
highest_index = index
|
|
|
|
index = int(index - (index - smallest_index) / 2)
|
|
|
|
else:
|
|
|
|
smallest_index = index
|
|
|
|
index = int(index + (highest_index - index) / 2)
|
2011-05-23 18:41:49 +00:00
|
|
|
# We found the number of words which will fit.
|
2011-05-23 17:28:52 +00:00
|
|
|
if smallest_index == index or highest_index == index:
|
2011-05-24 07:02:13 +00:00
|
|
|
index = smallest_index
|
2011-05-23 17:28:52 +00:00
|
|
|
formatted.append(previous_raw.rstrip(u'<br>') +
|
|
|
|
u''.join(raw_words[:index + 1]))
|
2011-04-26 17:03:19 +00:00
|
|
|
previous_html = u''
|
|
|
|
previous_raw = u''
|
2011-05-23 17:28:52 +00:00
|
|
|
else:
|
|
|
|
continue
|
|
|
|
# Check if the rest of the line fits on the slide. If it
|
|
|
|
# does we do not have to do the much more intensive "word by
|
|
|
|
# word" checking.
|
|
|
|
html = self.page_shell + \
|
|
|
|
u''.join(html_words[index + 1:]).strip() + HTML_END
|
|
|
|
self.web.setHtml(html)
|
|
|
|
if self.web_frame.contentsSize().height() <= \
|
|
|
|
self.page_height:
|
|
|
|
previous_html = \
|
|
|
|
u''.join(html_words[index + 1:]).strip() + line_end
|
|
|
|
previous_raw = \
|
|
|
|
u''.join(raw_words[index + 1:]).strip() + line_end
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
# The other words do not fit, thus reset the indexes,
|
|
|
|
# create a new list and continue with "word by word".
|
|
|
|
raw_words = raw_words[index + 1:]
|
|
|
|
html_words = html_words[index + 1:]
|
|
|
|
smallest_index = 0
|
|
|
|
highest_index = len(html_words) - 1
|
|
|
|
index = int(highest_index / 2)
|
2011-04-09 08:11:46 +00:00
|
|
|
else:
|
2011-04-26 17:03:19 +00:00
|
|
|
previous_html += styled_line + line_end
|
|
|
|
previous_raw += line + line_end
|
2011-04-29 14:31:17 +00:00
|
|
|
while previous_raw.endswith(u'<br>'):
|
|
|
|
previous_raw = previous_raw[:-4]
|
2011-04-26 17:03:19 +00:00
|
|
|
formatted.append(previous_raw)
|
|
|
|
log.debug(u'_paginate_slide_words - End')
|
2011-04-01 04:48:09 +00:00
|
|
|
return formatted
|
|
|
|
|
2011-04-26 19:09:56 +00:00
|
|
|
def _words_split(self, line):
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
|
|
|
Split the slide up by word so can wrap better
|
|
|
|
"""
|
2011-04-08 15:35:33 +00:00
|
|
|
# this parse we are to be wordy
|
2011-04-26 19:09:56 +00:00
|
|
|
line = line.replace(u'\n', u' ')
|
2011-04-29 07:40:44 +00:00
|
|
|
words = line.split(u' ')
|
|
|
|
return [word + u' ' for word in words]
|
2011-04-08 15:35:33 +00:00
|
|
|
|
|
|
|
def _lines_split(self, text):
|
|
|
|
"""
|
|
|
|
Split the slide up by physical line
|
|
|
|
"""
|
|
|
|
# this parse we do not want to use this so remove it
|
2011-04-27 19:44:21 +00:00
|
|
|
text = text.replace(u'\n[---]', u'')
|
2011-04-08 15:35:33 +00:00
|
|
|
lines = text.split(u'\n')
|
2011-04-29 07:40:44 +00:00
|
|
|
return [line.replace(u'[---]', u'') for line in lines]
|