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 #
|
|
|
|
# --------------------------------------------------------------------------- #
|
2011-12-27 10:33:55 +00:00
|
|
|
# Copyright (c) 2008-2012 Raoul Snyman #
|
|
|
|
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
|
2012-06-22 14:14:53 +00:00
|
|
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
|
|
|
# Meinert Jordan, Armin Köhler, Edwin Lunando, Joshua Miller, Stevan Pettit, #
|
|
|
|
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
|
|
|
|
# Simon Scudder, Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon #
|
|
|
|
# Tibble, Dave Warnock, 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
|
|
|
|
|
2011-08-20 11:45:06 +00:00
|
|
|
from PyQt4 import QtGui, QtCore, QtWebKit
|
2011-03-28 18:12:46 +00:00
|
|
|
|
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, \
|
2011-11-02 20:27:53 +00:00
|
|
|
ItemCapabilities, FormattingTags
|
2011-03-28 18:12:46 +00:00
|
|
|
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'
|
2011-09-26 18:48:47 +00:00
|
|
|
VERSE_FOR_LINE_COUNT = u'\n'.join(map(unicode, xrange(50)))
|
2011-03-28 18:12:46 +00:00
|
|
|
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
|
|
|
|
|
|
|
|
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
|
|
|
|
2012-06-04 10:51:50 +00:00
|
|
|
def __init__(self, image_manager, theme_manager):
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
2011-10-16 14:40:46 +00:00
|
|
|
Initialise the renderer.
|
2011-05-07 20:55:11 +00:00
|
|
|
|
2012-06-04 10:47:36 +00:00
|
|
|
``image_manager``
|
|
|
|
A image_manager instance which takes care of e. g. caching and
|
2012-05-20 18:26:24 +00:00
|
|
|
resizing images.
|
2011-05-07 20:55:11 +00:00
|
|
|
|
2012-06-04 10:51:50 +00:00
|
|
|
``theme_manager``
|
|
|
|
The theme_manager instance, used to get the current theme details.
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
2011-06-01 07:38:19 +00:00
|
|
|
log.debug(u'Initialisation started')
|
2012-06-04 10:51:50 +00:00
|
|
|
self.theme_manager = theme_manager
|
2012-06-04 10:47:36 +00:00
|
|
|
self.image_manager = image_manager
|
2012-05-20 20:56:11 +00:00
|
|
|
self.screens = ScreenList()
|
2012-05-23 16:46:54 +00:00
|
|
|
self.theme_level = ThemeLevel.Global
|
2012-06-04 10:47:36 +00:00
|
|
|
self.global_theme_name = u''
|
2012-05-23 16:46:54 +00:00
|
|
|
self.service_theme_name = u''
|
|
|
|
self.item_theme_name = u''
|
2011-03-28 18:12:46 +00:00
|
|
|
self.force_page = False
|
2012-06-04 10:47:36 +00:00
|
|
|
self.display = MainDisplay(None, self.image_manager, False, self)
|
2011-05-05 11:51:47 +00:00
|
|
|
self.display.setup()
|
2012-05-20 19:11:27 +00:00
|
|
|
self._theme_dimensions = {}
|
2012-05-20 18:26:24 +00:00
|
|
|
self._calculate_default()
|
2012-05-20 19:11:27 +00:00
|
|
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
|
|
|
QtCore.SIGNAL(u'theme_update_global'), self.set_global_theme)
|
2012-06-25 22:14:53 +00:00
|
|
|
self.web = None
|
2011-03-28 18:12:46 +00:00
|
|
|
|
|
|
|
def update_display(self):
|
|
|
|
"""
|
2011-10-16 14:40:46 +00:00
|
|
|
Updates the renderer's information about the current screen.
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
|
|
|
log.debug(u'Update Display')
|
2012-05-24 19:05:15 +00:00
|
|
|
self._calculate_default()
|
2011-06-08 21:39:07 +00:00
|
|
|
if self.display:
|
|
|
|
self.display.close()
|
2012-06-04 10:47:36 +00:00
|
|
|
self.display = MainDisplay(None, self.image_manager, False, self)
|
2011-03-28 18:12:46 +00:00
|
|
|
self.display.setup()
|
2012-05-24 18:00:43 +00:00
|
|
|
self._theme_dimensions = {}
|
2012-05-20 18:26:24 +00:00
|
|
|
|
2012-05-23 21:02:34 +00:00
|
|
|
def update_theme(self, theme_name, old_theme_name=None, only_delete=False):
|
2012-05-23 16:14:03 +00:00
|
|
|
"""
|
|
|
|
This method updates the theme in ``_theme_dimensions`` when a theme
|
|
|
|
has been edited or renamed.
|
|
|
|
|
|
|
|
``theme_name``
|
|
|
|
The current theme name.
|
|
|
|
|
|
|
|
``old_theme_name``
|
|
|
|
The old theme name. Has only to be passed, when the theme has been
|
|
|
|
renamed. Defaults to *None*.
|
2012-05-23 21:02:34 +00:00
|
|
|
|
|
|
|
``only_delete``
|
2012-05-24 18:00:43 +00:00
|
|
|
Only remove the given ``theme_name`` from the ``_theme_dimensions``
|
|
|
|
list. This can be used when a theme is permanently deleted.
|
2012-05-23 16:14:03 +00:00
|
|
|
"""
|
|
|
|
if old_theme_name is not None and \
|
|
|
|
old_theme_name in self._theme_dimensions:
|
|
|
|
del self._theme_dimensions[old_theme_name]
|
|
|
|
if theme_name in self._theme_dimensions:
|
|
|
|
del self._theme_dimensions[theme_name]
|
2012-05-23 21:02:34 +00:00
|
|
|
if not only_delete:
|
|
|
|
self._set_theme(theme_name)
|
2012-05-20 19:11:27 +00:00
|
|
|
|
|
|
|
def _set_theme(self, theme_name):
|
2012-05-20 18:26:24 +00:00
|
|
|
"""
|
2012-05-20 19:11:27 +00:00
|
|
|
Helper method to save theme names and theme data.
|
2012-05-23 16:14:03 +00:00
|
|
|
|
|
|
|
``theme_name``
|
|
|
|
The theme name.
|
2012-05-20 18:26:24 +00:00
|
|
|
"""
|
2012-05-20 19:11:27 +00:00
|
|
|
if theme_name not in self._theme_dimensions:
|
2012-06-04 10:51:50 +00:00
|
|
|
theme_data = self.theme_manager.getThemeData(theme_name)
|
2012-05-20 18:26:24 +00:00
|
|
|
main_rect = self.get_main_rectangle(theme_data)
|
|
|
|
footer_rect = self.get_footer_rectangle(theme_data)
|
2012-05-20 19:11:27 +00:00
|
|
|
self._theme_dimensions[theme_name] = \
|
|
|
|
[theme_data, main_rect, footer_rect]
|
2012-05-20 18:26:24 +00:00
|
|
|
else:
|
2012-05-20 19:11:27 +00:00
|
|
|
theme_data, main_rect, footer_rect = \
|
|
|
|
self._theme_dimensions[theme_name]
|
2012-05-20 18:26:24 +00:00
|
|
|
# if No file do not update cache
|
|
|
|
if theme_data.background_filename:
|
2012-06-04 10:47:36 +00:00
|
|
|
self.image_manager.addImage(theme_data.theme_name,
|
2012-05-20 18:26:24 +00:00
|
|
|
theme_data.background_filename, u'theme',
|
|
|
|
QtGui.QColor(theme_data.background_border_color))
|
2011-03-28 18:12:46 +00:00
|
|
|
|
2012-05-23 16:14:03 +00:00
|
|
|
def pre_render(self, override_theme_data=None):
|
2012-05-20 18:26:24 +00:00
|
|
|
"""
|
2012-05-23 16:14:03 +00:00
|
|
|
Set up the theme to be used before rendering an item.
|
|
|
|
|
|
|
|
``override_theme_data``
|
|
|
|
The theme data should be passed, when we want to use our own theme
|
|
|
|
data, regardless of the theme level. This should for example be used
|
|
|
|
in the theme manager. **Note**, this is **not** to be mixed up with
|
|
|
|
the ``set_item_theme`` method.
|
2012-05-20 18:26:24 +00:00
|
|
|
"""
|
|
|
|
# Just assume we use the global theme.
|
2012-05-23 16:14:03 +00:00
|
|
|
theme_to_use = self.global_theme_name
|
2012-05-24 18:00:43 +00:00
|
|
|
# The theme level is either set to Service or Item. Use the service
|
|
|
|
# theme if one is set. We also have to use the service theme, even when
|
|
|
|
# the theme level is set to Item, because the item does not necessarily
|
|
|
|
# have to have a theme.
|
|
|
|
if self.theme_level != ThemeLevel.Global:
|
2012-05-20 18:26:24 +00:00
|
|
|
# When the theme level is at Service and we actually have a service
|
|
|
|
# theme then use it.
|
2012-05-23 16:14:03 +00:00
|
|
|
if self.service_theme_name:
|
|
|
|
theme_to_use = self.service_theme_name
|
2012-05-24 18:00:43 +00:00
|
|
|
# If we have Item level and have an item theme then use it.
|
|
|
|
if self.theme_level == ThemeLevel.Song and self.item_theme_name:
|
2012-05-23 16:14:03 +00:00
|
|
|
theme_to_use = self.item_theme_name
|
2012-05-20 19:11:27 +00:00
|
|
|
if override_theme_data is None:
|
2012-05-24 18:00:43 +00:00
|
|
|
if theme_to_use not in self._theme_dimensions:
|
|
|
|
self._set_theme(theme_to_use)
|
2012-05-20 19:11:27 +00:00
|
|
|
theme_data, main_rect, footer_rect = \
|
|
|
|
self._theme_dimensions[theme_to_use]
|
|
|
|
else:
|
|
|
|
# Ignore everything and use own theme data.
|
2012-05-20 18:26:24 +00:00
|
|
|
theme_data = override_theme_data
|
2012-05-20 19:11:27 +00:00
|
|
|
main_rect = self.get_main_rectangle(override_theme_data)
|
|
|
|
footer_rect = self.get_footer_rectangle(override_theme_data)
|
2012-05-20 18:26:24 +00:00
|
|
|
self._set_text_rectangle(theme_data, main_rect, footer_rect)
|
|
|
|
return theme_data, self._rect, self._rect_footer
|
|
|
|
|
|
|
|
def set_theme_level(self, theme_level):
|
|
|
|
"""
|
2012-05-20 19:11:27 +00:00
|
|
|
Sets the theme level.
|
|
|
|
|
|
|
|
``theme_level``
|
|
|
|
The theme level to be used.
|
2012-05-20 18:26:24 +00:00
|
|
|
"""
|
|
|
|
self.theme_level = theme_level
|
|
|
|
|
|
|
|
def set_global_theme(self, global_theme_name):
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
2012-05-20 19:11:27 +00:00
|
|
|
Set the global-level theme name.
|
2011-03-28 18:12:46 +00:00
|
|
|
|
2012-05-20 19:11:27 +00:00
|
|
|
``global_theme_name``
|
|
|
|
The global-level theme's name.
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
2012-05-20 19:11:27 +00:00
|
|
|
self._set_theme(global_theme_name)
|
2012-05-23 16:14:03 +00:00
|
|
|
self.global_theme_name = global_theme_name
|
2011-03-28 18:12:46 +00:00
|
|
|
|
2012-05-20 18:26:24 +00:00
|
|
|
def set_service_theme(self, service_theme_name):
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
|
|
|
Set the service-level theme.
|
|
|
|
|
2012-05-20 19:11:27 +00:00
|
|
|
``service_theme_name``
|
|
|
|
The service level theme's name.
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
2012-05-20 19:11:27 +00:00
|
|
|
self._set_theme(service_theme_name)
|
2012-05-23 16:14:03 +00:00
|
|
|
self.service_theme_name = service_theme_name
|
2011-03-28 18:12:46 +00:00
|
|
|
|
2012-05-23 16:14:03 +00:00
|
|
|
def set_item_theme(self, item_theme_name):
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
2012-05-24 19:05:15 +00:00
|
|
|
Set the item-level theme. **Note**, this has to be done for each item we
|
|
|
|
are rendering.
|
2011-03-28 18:12:46 +00:00
|
|
|
|
2012-05-24 19:05:15 +00:00
|
|
|
``item_theme_name``
|
|
|
|
The item theme's name.
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
2012-05-23 16:14:03 +00:00
|
|
|
self._set_theme(item_theme_name)
|
|
|
|
self.item_theme_name = item_theme_name
|
2011-03-28 18:12:46 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
# build a service item to generate preview
|
|
|
|
serviceItem = ServiceItem()
|
|
|
|
if self.force_page:
|
|
|
|
# make big page for theme edit dialog to get line count
|
2012-06-10 13:39:45 +00:00
|
|
|
serviceItem.add_from_text(VERSE_FOR_LINE_COUNT)
|
2011-03-28 18:12:46 +00:00
|
|
|
else:
|
2012-06-04 10:47:36 +00:00
|
|
|
self.image_manager.deleteImage(theme_data.theme_name)
|
2012-06-10 13:39:45 +00:00
|
|
|
serviceItem.add_from_text(VERSE)
|
2011-03-29 16:25:48 +00:00
|
|
|
serviceItem.renderer = self
|
2011-03-28 18:12:46 +00:00
|
|
|
serviceItem.raw_footer = FOOTER
|
2012-05-20 19:11:27 +00:00
|
|
|
# if No file do not update cache
|
|
|
|
if theme_data.background_filename:
|
2012-06-04 10:47:36 +00:00
|
|
|
self.image_manager.addImage(theme_data.theme_name,
|
2012-05-20 19:11:27 +00:00
|
|
|
theme_data.background_filename, u'theme',
|
|
|
|
QtGui.QColor(theme_data.background_border_color))
|
2012-05-23 21:02:34 +00:00
|
|
|
theme_data, main, footer = self.pre_render(theme_data)
|
2012-05-20 19:11:27 +00:00
|
|
|
serviceItem.themedata = theme_data
|
|
|
|
serviceItem.main = main
|
|
|
|
serviceItem.footer = footer
|
|
|
|
serviceItem.render(True)
|
2011-03-28 18:12:46 +00:00
|
|
|
if not self.force_page:
|
|
|
|
self.display.buildHtml(serviceItem)
|
|
|
|
raw_html = serviceItem.get_rendered_frame(0)
|
2011-10-03 17:27:34 +00:00
|
|
|
self.display.text(raw_html)
|
|
|
|
preview = self.display.preview()
|
2011-03-28 18:12:46 +00:00
|
|
|
return preview
|
2011-06-13 09:47:18 +00:00
|
|
|
self.force_page = False
|
2011-03-28 18:12:46 +00:00
|
|
|
|
2011-06-13 08:16:50 +00:00
|
|
|
def format_slide(self, text, 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.
|
|
|
|
|
2011-06-13 08:16:50 +00:00
|
|
|
``item``
|
2011-06-13 09:47:18 +00:00
|
|
|
The :class:`~openlp.core.lib.serviceitem.ServiceItem` item object.
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
|
|
|
log.debug(u'format slide')
|
2011-06-13 08:16:50 +00:00
|
|
|
# Add line endings after each line of text used for bibles.
|
2011-07-16 09:00:30 +00:00
|
|
|
line_end = u'<br>'
|
2011-06-13 08:16:50 +00:00
|
|
|
if item.is_capable(ItemCapabilities.NoLineBreaks):
|
|
|
|
line_end = u' '
|
2011-06-13 11:26:06 +00:00
|
|
|
# Bibles
|
2011-08-31 07:49:57 +00:00
|
|
|
if item.is_capable(ItemCapabilities.CanWordSplit):
|
2011-08-05 08:10:53 +00:00
|
|
|
pages = self._paginate_slide_words(text.split(u'\n'), line_end)
|
2011-09-24 12:54:27 +00:00
|
|
|
# Songs and Custom
|
|
|
|
elif item.is_capable(ItemCapabilities.CanSoftBreak):
|
|
|
|
pages = []
|
2011-09-26 16:25:22 +00:00
|
|
|
if u'[---]' in text:
|
2011-08-28 15:53:27 +00:00
|
|
|
while True:
|
2011-08-30 13:35:06 +00:00
|
|
|
slides = text.split(u'\n[---]\n', 2)
|
|
|
|
# If there are (at least) two occurrences of [---] we use
|
2011-08-30 15:12:16 +00:00
|
|
|
# the first two slides (and neglect the last for now).
|
2011-08-30 13:35:06 +00:00
|
|
|
if len(slides) == 3:
|
|
|
|
html_text = expand_tags(u'\n'.join(slides[:2]))
|
2012-05-05 13:59:04 +00:00
|
|
|
# We check both slides to determine if the optional split is
|
|
|
|
# needed (there is only one optional split).
|
2011-08-30 13:35:06 +00:00
|
|
|
else:
|
|
|
|
html_text = expand_tags(u'\n'.join(slides))
|
2011-08-28 15:53:27 +00:00
|
|
|
html_text = html_text.replace(u'\n', u'<br>')
|
|
|
|
if self._text_fits_on_slide(html_text):
|
2012-04-18 21:55:44 +00:00
|
|
|
# The first two optional slides fit (as a whole) on one
|
2011-08-30 15:12:16 +00:00
|
|
|
# slide. Replace the first occurrence of [---].
|
2011-08-30 13:35:06 +00:00
|
|
|
text = text.replace(u'\n[---]', u'', 1)
|
2011-08-28 15:53:27 +00:00
|
|
|
else:
|
2012-04-18 21:55:44 +00:00
|
|
|
# The first optional slide fits, which means we have to
|
|
|
|
# render the first optional slide.
|
2012-05-05 13:59:04 +00:00
|
|
|
text_contains_split = u'[---]' in text
|
|
|
|
if text_contains_split:
|
2011-11-02 18:58:46 +00:00
|
|
|
try:
|
|
|
|
text_to_render, text = \
|
|
|
|
text.split(u'\n[---]\n', 1)
|
2012-06-09 15:46:01 +00:00
|
|
|
except ValueError:
|
2011-11-02 18:58:46 +00:00
|
|
|
text_to_render = text.split(u'\n[---]\n')[0]
|
|
|
|
text = u''
|
2012-05-05 14:36:47 +00:00
|
|
|
text_to_render, raw_tags, html_tags = \
|
|
|
|
self._get_start_tags(text_to_render)
|
|
|
|
if text:
|
|
|
|
text = raw_tags + text
|
2011-08-23 19:57:29 +00:00
|
|
|
else:
|
2011-08-30 15:12:16 +00:00
|
|
|
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:
|
2011-08-30 15:15:39 +00:00
|
|
|
# Add all slides apart from the last one the list.
|
2011-08-30 15:12:16 +00:00
|
|
|
pages.extend(slides[:-1])
|
2012-05-05 13:59:04 +00:00
|
|
|
if text_contains_split:
|
2011-08-30 15:12:16 +00:00
|
|
|
text = slides[-1] + u'\n[---]\n' + text
|
2011-08-28 17:46:52 +00:00
|
|
|
else:
|
2011-08-30 15:12:16 +00:00
|
|
|
text = slides[-1] + u'\n'+ text
|
|
|
|
text = text.replace(u'<br>', u'\n')
|
|
|
|
else:
|
|
|
|
pages.extend(slides)
|
2011-08-28 15:53:27 +00:00
|
|
|
if u'[---]' not in text:
|
2011-08-29 20:56:07 +00:00
|
|
|
lines = text.strip(u'\n').split(u'\n')
|
2011-06-13 11:26:06 +00:00
|
|
|
pages.extend(self._paginate_slide(lines, line_end))
|
2011-08-28 15:53:27 +00:00
|
|
|
break
|
2011-09-24 12:54:27 +00:00
|
|
|
else:
|
|
|
|
# Clean up line endings.
|
|
|
|
pages = self._paginate_slide(text.split(u'\n'), line_end)
|
2011-09-26 16:57:32 +00:00
|
|
|
else:
|
|
|
|
pages = self._paginate_slide(text.split(u'\n'), line_end)
|
2011-06-13 09:05:22 +00:00
|
|
|
new_pages = []
|
|
|
|
for page in pages:
|
2011-07-16 09:00:30 +00:00
|
|
|
while page.endswith(u'<br>'):
|
|
|
|
page = page[:-4]
|
2011-06-13 09:05:22 +00:00
|
|
|
new_pages.append(page)
|
|
|
|
return new_pages
|
2011-03-28 18:12:46 +00:00
|
|
|
|
2011-08-05 08:43:32 +00:00
|
|
|
def _calculate_default(self):
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
2012-03-21 22:27:04 +00:00
|
|
|
Calculate the default dimensions of the screen.
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
2011-08-05 08:47:31 +00:00
|
|
|
screen_size = self.screens.current[u'size']
|
|
|
|
self.width = screen_size.width()
|
|
|
|
self.height = screen_size.height()
|
2011-03-28 18:12:46 +00:00
|
|
|
self.screen_ratio = float(self.height) / float(self.width)
|
2011-08-05 08:47:31 +00:00
|
|
|
log.debug(u'_calculate default %s, %f' % (screen_size,
|
|
|
|
self.screen_ratio))
|
2011-03-28 18:12:46 +00:00
|
|
|
# 90% is start of footer
|
|
|
|
self.footer_start = int(self.height * 0.90)
|
|
|
|
|
2012-05-20 18:26:24 +00:00
|
|
|
def get_main_rectangle(self, theme_data):
|
2011-09-28 17:39:26 +00:00
|
|
|
"""
|
2011-10-03 16:53:54 +00:00
|
|
|
Calculates the placement and size of the main rectangle.
|
2011-09-28 17:39:26 +00:00
|
|
|
|
2012-05-20 18:26:24 +00:00
|
|
|
``theme_data``
|
2011-09-28 17:39:26 +00:00
|
|
|
The theme information
|
|
|
|
"""
|
2012-05-20 18:26:24 +00:00
|
|
|
if not theme_data.font_main_override:
|
|
|
|
return QtCore.QRect(10, 0, self.width, self.footer_start)
|
2011-03-28 18:12:46 +00:00
|
|
|
else:
|
2012-05-20 18:26:24 +00:00
|
|
|
return QtCore.QRect(theme_data.font_main_x, theme_data.font_main_y,
|
|
|
|
theme_data.font_main_width - 1, theme_data.font_main_height - 1)
|
2011-09-28 17:39:26 +00:00
|
|
|
|
2012-05-23 16:46:54 +00:00
|
|
|
def get_footer_rectangle(self, theme_data):
|
2011-09-28 17:39:26 +00:00
|
|
|
"""
|
2011-10-03 16:53:54 +00:00
|
|
|
Calculates the placement and size of the footer rectangle.
|
2011-09-28 17:39:26 +00:00
|
|
|
|
2012-05-23 16:46:54 +00:00
|
|
|
``theme_data``
|
|
|
|
The theme data.
|
2011-09-28 17:39:26 +00:00
|
|
|
"""
|
2012-05-23 16:46:54 +00:00
|
|
|
if not theme_data.font_footer_override:
|
2011-09-28 17:39:26 +00:00
|
|
|
return QtCore.QRect(10, self.footer_start, self.width - 20,
|
2011-03-28 18:12:46 +00:00
|
|
|
self.height - self.footer_start)
|
|
|
|
else:
|
2012-05-23 16:46:54 +00:00
|
|
|
return QtCore.QRect(theme_data.font_footer_x,
|
|
|
|
theme_data.font_footer_y, theme_data.font_footer_width - 1,
|
|
|
|
theme_data.font_footer_height - 1)
|
2011-03-28 18:12:46 +00:00
|
|
|
|
2012-05-20 18:26:24 +00:00
|
|
|
def _set_text_rectangle(self, theme_data, rect_main, rect_footer):
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
|
|
|
Sets the rectangle within which text should be rendered.
|
|
|
|
|
2012-05-23 16:46:54 +00:00
|
|
|
``theme_data``
|
|
|
|
The theme data.
|
|
|
|
|
2011-03-28 18:12:46 +00:00
|
|
|
``rect_main``
|
|
|
|
The main text block.
|
|
|
|
|
|
|
|
``rect_footer``
|
|
|
|
The footer text block.
|
|
|
|
"""
|
2011-06-06 17:35:47 +00:00
|
|
|
log.debug(u'_set_text_rectangle %s , %s' % (rect_main, rect_footer))
|
2012-06-25 22:14:53 +00:00
|
|
|
if not self.web:
|
|
|
|
self.web = QtWebKit.QWebView()
|
|
|
|
self.web.setVisible(False)
|
|
|
|
self.web_frame = self.web.page().mainFrame()
|
2011-03-28 18:12:46 +00:00
|
|
|
self._rect = rect_main
|
|
|
|
self._rect_footer = rect_footer
|
|
|
|
self.page_width = self._rect.width()
|
|
|
|
self.page_height = self._rect.height()
|
2012-05-20 18:26:24 +00:00
|
|
|
if theme_data.font_main_shadow:
|
|
|
|
self.page_width -= int(theme_data.font_main_shadow_size)
|
|
|
|
self.page_height -= int(theme_data.font_main_shadow_size)
|
2011-03-28 18:12:46 +00:00
|
|
|
self.web.resize(self.page_width, self.page_height)
|
2012-04-16 07:02:24 +00:00
|
|
|
# Adjust width and height to account for shadow. outline done in css.
|
2011-07-30 06:33:51 +00:00
|
|
|
html = u"""<!DOCTYPE html><html><head><script>
|
|
|
|
function show_text(newtext) {
|
|
|
|
var main = document.getElementById('main');
|
|
|
|
main.innerHTML = newtext;
|
2011-08-05 08:10:53 +00:00
|
|
|
// We need to be sure that the page is loaded, that is why we
|
|
|
|
// return the element's height (even though we do not use the
|
|
|
|
// returned value).
|
|
|
|
return main.offsetHeight;
|
2011-07-30 06:33:51 +00:00
|
|
|
}
|
2011-08-05 07:41:20 +00:00
|
|
|
</script><style>*{margin: 0; padding: 0; border: 0;}
|
|
|
|
#main {position: absolute; top: 0px; %s %s}</style></head><body>
|
2011-07-30 06:33:51 +00:00
|
|
|
<div id="main"></div></body></html>""" % \
|
2012-05-20 18:26:24 +00:00
|
|
|
(build_lyrics_format_css(theme_data, self.page_width,
|
|
|
|
self.page_height), build_lyrics_outline_css(theme_data))
|
2011-07-30 06:33:51 +00:00
|
|
|
self.web.setHtml(html)
|
2012-03-21 22:27:04 +00:00
|
|
|
self.empty_height = self.web_frame.contentsSize().height()
|
2011-03-28 18:12:46 +00:00
|
|
|
|
2011-06-13 08:16:50 +00:00
|
|
|
def _paginate_slide(self, lines, line_end):
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
|
|
|
Figure out how much text can appear on a slide, using the current
|
|
|
|
theme settings.
|
2011-08-05 08:10:53 +00:00
|
|
|
**Note:** The smallest possible "unit" of text for a slide is one line.
|
|
|
|
If the line is too long it will be cut off when displayed.
|
2011-03-28 18:12:46 +00:00
|
|
|
|
|
|
|
``lines``
|
2011-06-13 08:16:50 +00:00
|
|
|
The text to be fitted on the slide split into lines.
|
2011-03-28 18:12:46 +00:00
|
|
|
|
2011-06-13 08:16:50 +00:00
|
|
|
``line_end``
|
2011-07-16 09:00:30 +00:00
|
|
|
The text added after each line. Either ``u' '`` or ``u'<br>``.
|
2011-03-28 18:12:46 +00:00
|
|
|
"""
|
2011-04-26 17:03:19 +00:00
|
|
|
log.debug(u'_paginate_slide - Start')
|
2011-03-28 18:12:46 +00:00
|
|
|
formatted = []
|
2011-06-12 19:27:19 +00:00
|
|
|
previous_html = u''
|
|
|
|
previous_raw = u''
|
2011-07-16 09:00:30 +00:00
|
|
|
separator = u'<br>'
|
2011-06-12 19:27:19 +00:00
|
|
|
html_lines = map(expand_tags, lines)
|
2011-06-12 18:38:04 +00:00
|
|
|
# Text too long so go to next page.
|
2011-08-26 15:22:57 +00:00
|
|
|
if not self._text_fits_on_slide(separator.join(html_lines)):
|
2011-06-13 08:16:50 +00:00
|
|
|
html_text, previous_raw = self._binary_chop(formatted,
|
2011-06-13 11:20:47 +00:00
|
|
|
previous_html, previous_raw, html_lines, lines, separator, u'')
|
2011-06-12 18:38:04 +00:00
|
|
|
else:
|
2011-06-13 11:20:47 +00:00
|
|
|
previous_raw = separator.join(lines)
|
2011-06-12 19:27:19 +00:00
|
|
|
if previous_raw:
|
|
|
|
formatted.append(previous_raw)
|
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-08-05 08:10:53 +00:00
|
|
|
def _paginate_slide_words(self, lines, line_end):
|
2011-04-01 04:48:09 +00:00
|
|
|
"""
|
|
|
|
Figure out how much text can appear on a slide, using the current
|
2011-08-05 08:10:53 +00:00
|
|
|
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.
|
2011-04-01 04:48:09 +00:00
|
|
|
|
2011-08-05 08:10:53 +00:00
|
|
|
``lines``
|
|
|
|
The text to be fitted on the slide split into lines.
|
2011-04-03 06:19:03 +00:00
|
|
|
|
2011-06-13 08:16:50 +00:00
|
|
|
``line_end``
|
2011-07-16 09:00:30 +00:00
|
|
|
The text added after each line. Either ``u' '`` or ``u'<br>``.
|
2011-08-05 08:10:53 +00:00
|
|
|
This is needed 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-04-01 04:48:09 +00:00
|
|
|
formatted = []
|
2011-04-26 17:03:19 +00:00
|
|
|
previous_html = u''
|
|
|
|
previous_raw = u''
|
2011-04-01 04:48:09 +00:00
|
|
|
for line in lines:
|
2011-05-23 17:28:52 +00:00
|
|
|
line = line.strip()
|
2011-06-13 09:05:22 +00:00
|
|
|
html_line = expand_tags(line)
|
2011-05-23 18:41:49 +00:00
|
|
|
# Text too long so go to next page.
|
2011-08-26 15:22:57 +00:00
|
|
|
if not self._text_fits_on_slide(previous_html + html_line):
|
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:
|
2011-08-26 15:22:57 +00:00
|
|
|
if self._text_fits_on_slide(previous_html):
|
2011-04-28 06:44:48 +00:00
|
|
|
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.
|
2011-08-26 15:22:57 +00:00
|
|
|
if self._text_fits_on_slide(html_line):
|
2011-06-13 09:05:22 +00:00
|
|
|
previous_html = html_line + line_end
|
2011-04-28 06:44:48 +00:00
|
|
|
previous_raw = line + line_end
|
|
|
|
continue
|
2011-06-13 09:05:22 +00:00
|
|
|
# Figure out how many words of the line will fit on screen as
|
|
|
|
# the line will not fit as a whole.
|
2011-05-23 17:28:52 +00:00
|
|
|
raw_words = self._words_split(line)
|
2011-06-13 11:20:47 +00:00
|
|
|
html_words = map(expand_tags, raw_words)
|
2011-06-13 08:16:50 +00:00
|
|
|
previous_html, previous_raw = self._binary_chop(
|
2011-06-12 19:27:19 +00:00
|
|
|
formatted, previous_html, previous_raw, html_words,
|
2011-06-13 11:20:47 +00:00
|
|
|
raw_words, u' ', line_end)
|
2011-04-09 08:11:46 +00:00
|
|
|
else:
|
2011-06-13 09:05:22 +00:00
|
|
|
previous_html += html_line + line_end
|
2011-04-26 17:03:19 +00:00
|
|
|
previous_raw += line + line_end
|
|
|
|
formatted.append(previous_raw)
|
|
|
|
log.debug(u'_paginate_slide_words - End')
|
2011-04-01 04:48:09 +00:00
|
|
|
return formatted
|
|
|
|
|
2011-09-18 10:26:47 +00:00
|
|
|
def _get_start_tags(self, raw_text):
|
|
|
|
"""
|
|
|
|
Tests the given text for not closed formatting tags and returns a tuple
|
2011-09-19 16:00:27 +00:00
|
|
|
consisting of three unicode strings::
|
2011-09-18 10:26:47 +00:00
|
|
|
|
2011-09-19 16:13:38 +00:00
|
|
|
(u'{st}{r}Text text text{/r}{/st}', u'{st}{r}', u'<strong>
|
2011-09-19 16:00:27 +00:00
|
|
|
<span style="-webkit-text-fill-color:red">')
|
2011-09-18 10:26:47 +00:00
|
|
|
|
2011-09-19 16:00:27 +00:00
|
|
|
The first unicode string is the text, with correct closing tags. The
|
|
|
|
second unicode string are OpenLP's opening formatting tags and the third
|
|
|
|
unicode string the html opening formatting tags.
|
2011-09-18 10:26:47 +00:00
|
|
|
|
|
|
|
``raw_text``
|
|
|
|
The text to test. The text must **not** contain html tags, only
|
2011-09-19 16:00:27 +00:00
|
|
|
OpenLP formatting tags are allowed::
|
|
|
|
|
|
|
|
{st}{r}Text text text
|
2011-09-18 10:26:47 +00:00
|
|
|
"""
|
|
|
|
raw_tags = []
|
|
|
|
html_tags = []
|
2011-09-06 11:56:52 +00:00
|
|
|
for tag in FormattingTags.get_html_tags():
|
2011-09-18 10:26:47 +00:00
|
|
|
if tag[u'start tag'] == u'{br}':
|
2011-09-06 11:56:52 +00:00
|
|
|
continue
|
2011-09-20 15:50:43 +00:00
|
|
|
if raw_text.count(tag[u'start tag']) != \
|
|
|
|
raw_text.count(tag[u'end tag']):
|
2011-09-18 10:26:47 +00:00
|
|
|
raw_tags.append(
|
2011-09-19 16:00:27 +00:00
|
|
|
(raw_text.find(tag[u'start tag']), tag[u'start tag'],
|
|
|
|
tag[u'end tag']))
|
2011-09-18 10:26:47 +00:00
|
|
|
html_tags.append(
|
2012-05-05 13:59:04 +00:00
|
|
|
(raw_text.find(tag[u'start tag']), tag[u'start html']))
|
2011-09-18 10:26:47 +00:00
|
|
|
# Sort the lists, so that the tags which were opened first on the first
|
|
|
|
# slide (the text we are checking) will be opened first on the next
|
|
|
|
# slide as well.
|
2011-09-19 16:00:27 +00:00
|
|
|
raw_tags.sort(key=lambda tag: tag[0])
|
|
|
|
html_tags.sort(key=lambda tag: tag[0])
|
|
|
|
# Create a list with closing tags for the raw_text.
|
2012-05-02 18:25:37 +00:00
|
|
|
end_tags = []
|
|
|
|
start_tags = []
|
|
|
|
for tag in raw_tags:
|
|
|
|
start_tags.append(tag[1])
|
|
|
|
end_tags.append(tag[2])
|
2011-09-19 16:00:27 +00:00
|
|
|
end_tags.reverse()
|
2011-09-18 10:26:47 +00:00
|
|
|
# Remove the indexes.
|
2011-09-19 16:00:27 +00:00
|
|
|
html_tags = [tag[1] for tag in html_tags]
|
2012-05-02 18:25:37 +00:00
|
|
|
return raw_text + u''.join(end_tags), u''.join(start_tags), \
|
2011-09-19 16:00:27 +00:00
|
|
|
u''.join(html_tags)
|
2011-09-06 11:56:52 +00:00
|
|
|
|
2011-06-12 19:27:19 +00:00
|
|
|
def _binary_chop(self, formatted, previous_html, previous_raw, html_list,
|
2011-06-13 11:20:47 +00:00
|
|
|
raw_list, separator, line_end):
|
2011-06-12 18:38:04 +00:00
|
|
|
"""
|
2011-06-13 12:59:52 +00:00
|
|
|
This implements the binary chop algorithm for faster rendering. This
|
|
|
|
algorithm works line based (line by line) and word based (word by word).
|
2011-06-13 14:36:18 +00:00
|
|
|
It is assumed that this method is **only** called, when the lines/words
|
2011-06-23 14:34:43 +00:00
|
|
|
to be rendered do **not** fit as a whole.
|
2011-06-12 18:38:04 +00:00
|
|
|
|
|
|
|
``formatted``
|
2011-06-13 12:59:52 +00:00
|
|
|
The list to append any slides.
|
2011-06-12 18:38:04 +00:00
|
|
|
|
|
|
|
``previous_html``
|
|
|
|
The html text which is know to fit on a slide, but is not yet added
|
2011-06-12 19:27:19 +00:00
|
|
|
to the list of slides. (unicode string)
|
2011-06-12 18:38:04 +00:00
|
|
|
|
|
|
|
``previous_raw``
|
2011-07-30 07:19:16 +00:00
|
|
|
The raw text (with formatting tags) which is know to fit on a slide,
|
2011-06-12 19:27:19 +00:00
|
|
|
but is not yet added to the list of slides. (unicode string)
|
|
|
|
|
|
|
|
``html_list``
|
2011-06-13 12:59:52 +00:00
|
|
|
The elements which do not fit on a slide and needs to be processed
|
2011-06-13 09:47:18 +00:00
|
|
|
using the binary chop. The text contains html.
|
2011-06-12 18:38:04 +00:00
|
|
|
|
2011-06-12 19:27:19 +00:00
|
|
|
``raw_list``
|
2011-06-13 12:59:52 +00:00
|
|
|
The elements which do not fit on a slide and needs to be processed
|
2011-07-30 07:19:16 +00:00
|
|
|
using the binary chop. The elements can contain formatting tags.
|
2011-06-12 18:38:04 +00:00
|
|
|
|
2011-06-13 11:20:47 +00:00
|
|
|
``separator``
|
2011-07-16 09:00:30 +00:00
|
|
|
The separator for the elements. For lines this is ``u'<br>'`` and
|
2011-06-23 14:34:43 +00:00
|
|
|
for words this is ``u' '``.
|
2011-06-13 11:20:47 +00:00
|
|
|
|
2011-06-12 18:38:04 +00:00
|
|
|
``line_end``
|
2011-06-13 11:20:47 +00:00
|
|
|
The text added after each "element line". Either ``u' '`` or
|
2011-07-16 09:00:30 +00:00
|
|
|
``u'<br>``. This is needed for bibles.
|
2011-06-12 18:38:04 +00:00
|
|
|
"""
|
|
|
|
smallest_index = 0
|
2011-06-12 19:27:19 +00:00
|
|
|
highest_index = len(html_list) - 1
|
2011-06-12 18:38:04 +00:00
|
|
|
index = int(highest_index / 2)
|
|
|
|
while True:
|
2011-08-26 15:22:57 +00:00
|
|
|
if not self._text_fits_on_slide(
|
2011-07-30 06:33:51 +00:00
|
|
|
previous_html + separator.join(html_list[:index + 1]).strip()):
|
2011-06-12 18:38:04 +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)
|
|
|
|
# We found the number of words which will fit.
|
|
|
|
if smallest_index == index or highest_index == index:
|
|
|
|
index = smallest_index
|
2011-09-06 11:56:52 +00:00
|
|
|
text = previous_raw.rstrip(u'<br>') + \
|
|
|
|
separator.join(raw_list[:index + 1])
|
2011-09-19 16:00:27 +00:00
|
|
|
text, raw_tags, html_tags = self._get_start_tags(text)
|
2011-09-06 11:56:52 +00:00
|
|
|
formatted.append(text)
|
2011-06-12 18:38:04 +00:00
|
|
|
previous_html = u''
|
|
|
|
previous_raw = u''
|
2011-06-13 08:16:50 +00:00
|
|
|
# Stop here as the theme line count was requested.
|
|
|
|
if self.force_page:
|
|
|
|
Receiver.send_message(u'theme_line_count', index + 1)
|
|
|
|
break
|
2011-06-12 18:38:04 +00:00
|
|
|
else:
|
|
|
|
continue
|
2011-06-13 12:59:52 +00:00
|
|
|
# Check if the remaining elements fit on the slide.
|
2011-08-26 15:22:57 +00:00
|
|
|
if self._text_fits_on_slide(
|
2011-09-06 11:56:52 +00:00
|
|
|
html_tags + separator.join(html_list[index + 1:]).strip()):
|
|
|
|
previous_html = html_tags + separator.join(
|
2011-06-13 11:20:47 +00:00
|
|
|
html_list[index + 1:]).strip() + line_end
|
2011-09-06 11:56:52 +00:00
|
|
|
previous_raw = raw_tags + separator.join(
|
2011-06-13 11:20:47 +00:00
|
|
|
raw_list[index + 1:]).strip() + line_end
|
2011-06-12 18:38:04 +00:00
|
|
|
break
|
|
|
|
else:
|
2011-06-13 12:59:52 +00:00
|
|
|
# The remaining elements do not fit, thus reset the indexes,
|
|
|
|
# create a new list and continue.
|
2011-06-12 19:27:19 +00:00
|
|
|
raw_list = raw_list[index + 1:]
|
2011-09-06 11:56:52 +00:00
|
|
|
raw_list[0] = raw_tags + raw_list[0]
|
2011-06-12 19:27:19 +00:00
|
|
|
html_list = html_list[index + 1:]
|
2011-09-06 11:56:52 +00:00
|
|
|
html_list[0] = html_tags + html_list[0]
|
2011-06-12 18:38:04 +00:00
|
|
|
smallest_index = 0
|
2011-06-12 19:27:19 +00:00
|
|
|
highest_index = len(html_list) - 1
|
2011-06-12 18:38:04 +00:00
|
|
|
index = int(highest_index / 2)
|
2011-06-13 08:16:50 +00:00
|
|
|
return previous_html, previous_raw
|
2011-06-12 18:38:04 +00:00
|
|
|
|
2011-07-30 06:33:51 +00:00
|
|
|
def _text_fits_on_slide(self, text):
|
|
|
|
"""
|
2011-08-05 08:10:53 +00:00
|
|
|
Checks if the given ``text`` fits on a slide. If it does ``True`` is
|
2011-07-30 06:33:51 +00:00
|
|
|
returned, otherwise ``False``.
|
|
|
|
|
|
|
|
``text``
|
2011-08-23 19:57:29 +00:00
|
|
|
The text to check. It may contain HTML tags.
|
2011-07-30 06:33:51 +00:00
|
|
|
"""
|
|
|
|
self.web_frame.evaluateJavaScript(u'show_text("%s")' %
|
|
|
|
text.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
|
2012-03-21 22:27:04 +00:00
|
|
|
return self.web_frame.contentsSize().height() <= self.empty_height
|
2011-07-30 06:33:51 +00:00
|
|
|
|
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-06-13 11:20:47 +00:00
|
|
|
return line.split(u' ')
|