~tomasgroth/openlp/portable-path

« back to all changes in this revision

Viewing changes to openlp/core/lib/htmlbuilder.py

  • Committer: Tomas Groth
  • Date: 2019-04-30 19:02:42 UTC
  • mfrom: (2829.2.32 openlp)
  • Revision ID: tomasgroth@yahoo.dk-20190430190242-6zwjk8724tyux70m
trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
3
 
 
4
 
###############################################################################
5
 
# OpenLP - Open Source Lyrics Projection                                      #
6
 
# --------------------------------------------------------------------------- #
7
 
# Copyright (c) 2008-2018 OpenLP Developers                                   #
8
 
# --------------------------------------------------------------------------- #
9
 
# This program is free software; you can redistribute it and/or modify it     #
10
 
# under the terms of the GNU General Public License as published by the Free  #
11
 
# Software Foundation; version 2 of the License.                              #
12
 
#                                                                             #
13
 
# This program is distributed in the hope that it will be useful, but WITHOUT #
14
 
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
15
 
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
16
 
# more details.                                                               #
17
 
#                                                                             #
18
 
# You should have received a copy of the GNU General Public License along     #
19
 
# with this program; if not, write to the Free Software Foundation, Inc., 59  #
20
 
# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
21
 
###############################################################################
22
 
r"""
23
 
This module is responsible for generating the HTML for :class:`~openlp.core.ui.maindisplay`. The ``build_html`` function
24
 
is the function which has to be called from outside. The generated and returned HTML will look similar to this::
25
 
 
26
 
        <!DOCTYPE html>
27
 
        <html>
28
 
        <head>
29
 
        <title>OpenLP Display</title>
30
 
        <style>
31
 
        *{
32
 
            margin: 0;
33
 
            padding: 0;
34
 
            border: 0;
35
 
            overflow: hidden;
36
 
            -webkit-user-select: none;
37
 
        }
38
 
        body {
39
 
            background-color: #000000;
40
 
        }
41
 
        .size {
42
 
            position: absolute;
43
 
            left: 0px;
44
 
            top: 0px;
45
 
            width: 100%;
46
 
            height: 100%;
47
 
        }
48
 
        #black {
49
 
            z-index: 8;
50
 
            background-color: black;
51
 
            display: none;
52
 
        }
53
 
        #bgimage {
54
 
            z-index: 1;
55
 
        }
56
 
        #image {
57
 
            z-index: 2;
58
 
        }
59
 
 
60
 
        #videobackboard {
61
 
            z-index:3;
62
 
            background-color: #000000;
63
 
        }
64
 
        #video {
65
 
            background-color: #000000;
66
 
            z-index:4;
67
 
        }
68
 
 
69
 
        #flash {
70
 
            z-index:5;
71
 
        }
72
 
 
73
 
            #alert {
74
 
                position: absolute;
75
 
                left: 0px;
76
 
                top: 0px;
77
 
                z-index: 10;
78
 
                width: 100%;
79
 
                vertical-align: bottom;
80
 
                font-family: DejaVu Sans;
81
 
                font-size: 40pt;
82
 
                color: #ffffff;
83
 
                background-color: #660000;
84
 
                word-wrap: break-word;
85
 
            }
86
 
 
87
 
        #footer {
88
 
            position: absolute;
89
 
            z-index: 6;
90
 
 
91
 
            left: 10px;
92
 
            bottom: 0px;
93
 
            width: 1580px;
94
 
            font-family: Nimbus Sans L;
95
 
            font-size: 12pt;
96
 
            color: #FFFFFF;
97
 
            text-align: left;
98
 
            white-space: nowrap;
99
 
 
100
 
        }
101
 
        /* lyric css */
102
 
 
103
 
        .lyricstable {
104
 
            z-index: 5;
105
 
            position: absolute;
106
 
            display: table;
107
 
            left: 10px; top: 0px;
108
 
        }
109
 
        .lyricscell {
110
 
            display: table-cell;
111
 
            word-wrap: break-word;
112
 
            -webkit-transition: opacity 0.4s ease;
113
 
            white-space:pre-wrap; word-wrap: break-word; text-align: left; vertical-align: top; font-family: Nimbus
114
 
            Sans L; font-size: 40pt; color: #FFFFFF; line-height: 100%; margin: 0;padding: 0; padding-bottom: 0;
115
 
            padding-left: 4px; width: 1580px; height: 810px;
116
 
        }
117
 
        .lyricsmain {
118
 
             -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF;  text-shadow: #000000 5px 5px;
119
 
        }
120
 
 
121
 
        sup {
122
 
            font-size: 0.6em;
123
 
            vertical-align: top;
124
 
            position: relative;
125
 
            top: -0.3em;
126
 
        }
127
 
        /* Chords css */
128
 
        .chordline {
129
 
          line-height: 1.0em;
130
 
        }
131
 
        .chordline span.chord span {
132
 
          position: relative;
133
 
        }
134
 
        .chordline span.chord span strong {
135
 
          position: absolute;
136
 
          top: -0.8em;
137
 
          left: 0;
138
 
          font-size: 75%;
139
 
          font-weight: normal;
140
 
          line-height: normal;
141
 
          display: none;
142
 
        }
143
 
        .firstchordline {
144
 
            line-height: 1.0em;
145
 
        }
146
 
        </style>
147
 
        <script>
148
 
            var timer = null;
149
 
            var transition = false;
150
 
 
151
 
            function show_video(state, path, volume, loop, variable_value){
152
 
                // Sometimes  video.currentTime stops slightly short of video.duration and video.ended is intermittent!
153
 
 
154
 
                var video = document.getElementById('video');
155
 
                if(volume != null){
156
 
                    video.volume = volume;
157
 
                }
158
 
                switch(state){
159
 
                    case 'load':
160
 
                        video.src = 'file:///' + path;
161
 
                        if(loop == true) {
162
 
                            video.loop = true;
163
 
                        }
164
 
                        video.load();
165
 
                        break;
166
 
                    case 'play':
167
 
                        video.play();
168
 
                        break;
169
 
                    case 'pause':
170
 
                        video.pause();
171
 
                        break;
172
 
                    case 'stop':
173
 
                        show_video('pause');
174
 
                        video.currentTime = 0;
175
 
                        break;
176
 
                    case 'close':
177
 
                        show_video('stop');
178
 
                        video.src = '';
179
 
                        break;
180
 
                    case 'length':
181
 
                        return video.duration;
182
 
                    case 'current_time':
183
 
                        return video.currentTime;
184
 
                    case 'seek':
185
 
                        video.currentTime = variable_value;
186
 
                        break;
187
 
                    case 'isEnded':
188
 
                        return video.ended;
189
 
                    case 'setVisible':
190
 
                        video.style.visibility = variable_value;
191
 
                        break;
192
 
                    case 'setBackBoard':
193
 
                        var back = document.getElementById('videobackboard');
194
 
                        back.style.visibility = variable_value;
195
 
                        break;
196
 
               }
197
 
            }
198
 
 
199
 
            function getFlashMovieObject(movieName)
200
 
            {
201
 
                if (window.document[movieName]){
202
 
                    return window.document[movieName];
203
 
                }
204
 
                if (document.embeds && document.embeds[movieName]){
205
 
                    return document.embeds[movieName];
206
 
                }
207
 
            }
208
 
 
209
 
            function show_flash(state, path, volume, variable_value){
210
 
                var text = document.getElementById('flash');
211
 
                var flashMovie = getFlashMovieObject("OpenLPFlashMovie");
212
 
                var src = "src = 'file:///" + path + "'";
213
 
                var view_parm = " wmode='opaque'" + " width='100%%'" + " height='100%%'";
214
 
                var swf_parm = " name='OpenLPFlashMovie'" + " autostart='true' loop='false' play='true'" +
215
 
                    " hidden='false' swliveconnect='true' allowscriptaccess='always'" + " volume='" + volume + "'";
216
 
 
217
 
                switch(state){
218
 
                    case 'load':
219
 
                        text.innerHTML = "<embed " + src + view_parm + swf_parm + "/>";
220
 
                        flashMovie = getFlashMovieObject("OpenLPFlashMovie");
221
 
                        flashMovie.Play();
222
 
                        break;
223
 
                    case 'play':
224
 
                        flashMovie.Play();
225
 
                        break;
226
 
                    case 'pause':
227
 
                        flashMovie.StopPlay();
228
 
                        break;
229
 
                    case 'stop':
230
 
                        flashMovie.StopPlay();
231
 
                        tempHtml = text.innerHTML;
232
 
                        text.innerHTML = '';
233
 
                        text.innerHTML = tempHtml;
234
 
                        break;
235
 
                    case 'close':
236
 
                        flashMovie.StopPlay();
237
 
                        text.innerHTML = '';
238
 
                        break;
239
 
                    case 'length':
240
 
                        return flashMovie.TotalFrames();
241
 
                    case 'current_time':
242
 
                        return flashMovie.CurrentFrame();
243
 
                    case 'seek':
244
 
        //                flashMovie.GotoFrame(variable_value);
245
 
                        break;
246
 
                    case 'isEnded':
247
 
                        //TODO check flash end
248
 
                        return false;
249
 
                    case 'setVisible':
250
 
                        text.style.visibility = variable_value;
251
 
                        break;
252
 
                }
253
 
            }
254
 
 
255
 
            function show_alert(alerttext, position){
256
 
                var text = document.getElementById('alert');
257
 
                text.innerHTML = alerttext;
258
 
                if(alerttext == '') {
259
 
                    text.style.visibility = 'hidden';
260
 
                    return 0;
261
 
                }
262
 
                if(position == ''){
263
 
                    position = getComputedStyle(text, '').verticalAlign;
264
 
                }
265
 
                switch(position)
266
 
                {
267
 
                    case 'top':
268
 
                        text.style.top = '0px';
269
 
                        break;
270
 
                    case 'middle':
271
 
                        text.style.top = ((window.innerHeight - text.clientHeight) / 2)
272
 
                            + 'px';
273
 
                        break;
274
 
                    case 'bottom':
275
 
                        text.style.top = (window.innerHeight - text.clientHeight)
276
 
                            + 'px';
277
 
                        break;
278
 
                }
279
 
                text.style.visibility = 'visible';
280
 
                return text.clientHeight;
281
 
            }
282
 
 
283
 
            function update_css(align, font, size, color, bgcolor){
284
 
                var text = document.getElementById('alert');
285
 
                text.style.fontSize = size + "pt";
286
 
                text.style.fontFamily = font;
287
 
                text.style.color = color;
288
 
                text.style.backgroundColor = bgcolor;
289
 
                switch(align)
290
 
                {
291
 
                    case 'top':
292
 
                        text.style.top = '0px';
293
 
                        break;
294
 
                    case 'middle':
295
 
                        text.style.top = ((window.innerHeight - text.clientHeight) / 2)
296
 
                            + 'px';
297
 
                        break;
298
 
                    case 'bottom':
299
 
                        text.style.top = (window.innerHeight - text.clientHeight)
300
 
                            + 'px';
301
 
                        break;
302
 
                }
303
 
            }
304
 
 
305
 
 
306
 
            function show_image(src){
307
 
                var img = document.getElementById('image');
308
 
                img.src = src;
309
 
                if(src == '')
310
 
                    img.style.display = 'none';
311
 
                else
312
 
                    img.style.display = 'block';
313
 
            }
314
 
 
315
 
            function show_blank(state){
316
 
                var black = 'none';
317
 
                var lyrics = '';
318
 
                switch(state){
319
 
                    case 'theme':
320
 
                        lyrics = 'hidden';
321
 
                        break;
322
 
                    case 'black':
323
 
                        black = 'block';
324
 
                        break;
325
 
                    case 'desktop':
326
 
                        break;
327
 
                }
328
 
                document.getElementById('black').style.display = black;
329
 
                document.getElementById('lyricsmain').style.visibility = lyrics;
330
 
                document.getElementById('image').style.visibility = lyrics;
331
 
                document.getElementById('footer').style.visibility = lyrics;
332
 
            }
333
 
 
334
 
            function show_footer(footertext){
335
 
                document.getElementById('footer').innerHTML = footertext;
336
 
            }
337
 
 
338
 
            function show_text(new_text){
339
 
                var match = /-webkit-text-fill-color:[^;"]+/gi;
340
 
                if(timer != null)
341
 
                    clearTimeout(timer);
342
 
                /*
343
 
                QtWebkit bug with outlines and justify causing outline alignment
344
 
                problems. (Bug 859950) Surround each word with a <span> to workaround,
345
 
                but only in this scenario.
346
 
                */
347
 
                var txt = document.getElementById('lyricsmain');
348
 
                if(window.getComputedStyle(txt).textAlign == 'justify'){
349
 
                    if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){
350
 
                        new_text = new_text.replace(/(\s|&nbsp;)+(?![^<]*>)/g,
351
 
                            function(match) {
352
 
                                return '</span>' + match + '<span>';
353
 
                            });
354
 
                        new_text = '<span>' + new_text + '</span>';
355
 
                    }
356
 
                }
357
 
                text_fade('lyricsmain', new_text);
358
 
            }
359
 
 
360
 
            function text_fade(id, new_text){
361
 
                /*
362
 
                Show the text.
363
 
                */
364
 
                var text = document.getElementById(id);
365
 
                if(text == null) return;
366
 
                if(!transition){
367
 
                    text.innerHTML = new_text;
368
 
                    return;
369
 
                }
370
 
                // Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
371
 
                text.style.opacity = '0.1';
372
 
                // Fade new text in after the old text has finished fading out.
373
 
                timer = window.setTimeout(function(){_show_text(text, new_text)}, 400);
374
 
            }
375
 
 
376
 
            function _show_text(text, new_text) {
377
 
                /*
378
 
                Helper function to show the new_text delayed.
379
 
                */
380
 
                text.innerHTML = new_text;
381
 
                text.style.opacity = '1';
382
 
                // Wait until the text is completely visible. We want to save the timer id, to be able to call
383
 
                // clearTimeout(timer) when the text has changed before finishing fading.
384
 
                timer = window.setTimeout(function(){timer = null;}, 400);
385
 
            }
386
 
 
387
 
            function show_text_completed(){
388
 
                return (timer == null);
389
 
            }
390
 
        </script>
391
 
        </head>
392
 
        <body>
393
 
        <img id="bgimage" class="size" style="display:none;" />
394
 
        <img id="image" class="size" style="display:none;" />
395
 
 
396
 
        <div id="videobackboard" class="size" style="visibility:hidden"></div>
397
 
        <video id="video" class="size" style="visibility:hidden" autobuffer preload></video>
398
 
 
399
 
        <div id="flash" class="size" style="visibility:hidden"></div>
400
 
 
401
 
            <div id="alert" style="visibility:hidden"></div>
402
 
 
403
 
        <div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
404
 
        <div id="footer" class="footer"></div>
405
 
        <div id="black" class="size"></div>
406
 
        </body>
407
 
        </html>
408
 
"""
409
 
import logging
410
 
from string import Template
411
 
 
412
 
from PyQt5 import QtWebKit
413
 
 
414
 
from openlp.core.common.settings import Settings
415
 
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, VerticalType, HorizontalType
416
 
 
417
 
log = logging.getLogger(__name__)
418
 
 
419
 
HTML_SRC = Template(r"""
420
 
    <!DOCTYPE html>
421
 
    <html>
422
 
    <head>
423
 
    <title>OpenLP Display</title>
424
 
    <style>
425
 
    *{
426
 
        margin: 0;
427
 
        padding: 0;
428
 
        border: 0;
429
 
        overflow: hidden;
430
 
        -webkit-user-select: none;
431
 
    }
432
 
    body {
433
 
        ${bg_css};
434
 
    }
435
 
    .size {
436
 
        position: absolute;
437
 
        left: 0px;
438
 
        top: 0px;
439
 
        width: 100%;
440
 
        height: 100%;
441
 
    }
442
 
    #black {
443
 
        z-index: 8;
444
 
        background-color: black;
445
 
        display: none;
446
 
    }
447
 
    #bgimage {
448
 
        z-index: 1;
449
 
    }
450
 
    #image {
451
 
        z-index: 2;
452
 
    }
453
 
    ${css_additions}
454
 
    #footer {
455
 
        position: absolute;
456
 
        z-index: 6;
457
 
        ${footer_css}
458
 
    }
459
 
    /* lyric css */${lyrics_css}
460
 
    sup {
461
 
        font-size: 0.6em;
462
 
        vertical-align: top;
463
 
        position: relative;
464
 
        top: -0.3em;
465
 
    }
466
 
    /* Chords css */${chords_css}
467
 
    </style>
468
 
    <script>
469
 
        var timer = null;
470
 
        var transition = ${transitions};
471
 
        ${js_additions}
472
 
 
473
 
        function show_image(src){
474
 
            var img = document.getElementById('image');
475
 
            img.src = src;
476
 
            if(src == '')
477
 
                img.style.display = 'none';
478
 
            else
479
 
                img.style.display = 'block';
480
 
        }
481
 
 
482
 
        function show_blank(state){
483
 
            var black = 'none';
484
 
            var lyrics = '';
485
 
            switch(state){
486
 
                case 'theme':
487
 
                    lyrics = 'hidden';
488
 
                    break;
489
 
                case 'black':
490
 
                    black = 'block';
491
 
                    break;
492
 
                case 'desktop':
493
 
                    break;
494
 
            }
495
 
            document.getElementById('black').style.display = black;
496
 
            document.getElementById('lyricsmain').style.visibility = lyrics;
497
 
            document.getElementById('image').style.visibility = lyrics;
498
 
            document.getElementById('footer').style.visibility = lyrics;
499
 
        }
500
 
 
501
 
        function show_footer(footertext){
502
 
            document.getElementById('footer').innerHTML = footertext;
503
 
        }
504
 
 
505
 
        function show_text(new_text){
506
 
            var match = /-webkit-text-fill-color:[^;\"]+/gi;
507
 
            if(timer != null)
508
 
                clearTimeout(timer);
509
 
            /*
510
 
            QtWebkit bug with outlines and justify causing outline alignment
511
 
            problems. (Bug 859950) Surround each word with a <span> to workaround,
512
 
            but only in this scenario.
513
 
            */
514
 
            var txt = document.getElementById('lyricsmain');
515
 
            if(window.getComputedStyle(txt).textAlign == 'justify'){
516
 
                if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){
517
 
                    new_text = new_text.replace(/(\s|&nbsp;)+(?![^<]*>)/g,
518
 
                        function(match) {
519
 
                            return '</span>' + match + '<span>';
520
 
                        });
521
 
                    new_text = '<span>' + new_text + '</span>';
522
 
                }
523
 
            }
524
 
            text_fade('lyricsmain', new_text);
525
 
        }
526
 
 
527
 
        function text_fade(id, new_text){
528
 
            /*
529
 
            Show the text.
530
 
            */
531
 
            var text = document.getElementById(id);
532
 
            if(text == null) return;
533
 
            if(!transition){
534
 
                text.innerHTML = new_text;
535
 
                return;
536
 
            }
537
 
            // Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
538
 
            text.style.opacity = '0.1';
539
 
            // Fade new text in after the old text has finished fading out.
540
 
            timer = window.setTimeout(function(){_show_text(text, new_text)}, 400);
541
 
        }
542
 
 
543
 
        function _show_text(text, new_text) {
544
 
            /*
545
 
            Helper function to show the new_text delayed.
546
 
            */
547
 
            text.innerHTML = new_text;
548
 
            text.style.opacity = '1';
549
 
            // Wait until the text is completely visible. We want to save the timer id, to be able to call
550
 
            // clearTimeout(timer) when the text has changed before finishing fading.
551
 
            timer = window.setTimeout(function(){timer = null;}, 400);
552
 
        }
553
 
 
554
 
        function show_text_completed(){
555
 
            return (timer == null);
556
 
        }
557
 
    </script>
558
 
    </head>
559
 
    <body>
560
 
    <img id="bgimage" class="size" ${bg_image} />
561
 
    <img id="image" class="size" ${image} />
562
 
    ${html_additions}
563
 
    <div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
564
 
    <div id="footer" class="footer"></div>
565
 
    <div id="black" class="size"></div>
566
 
    </body>
567
 
    </html>
568
 
    """)
569
 
 
570
 
LYRICS_SRC = Template("""
571
 
    .lyricstable {
572
 
        z-index: 5;
573
 
        position: absolute;
574
 
        display: table;
575
 
        ${stable}
576
 
    }
577
 
    .lyricscell {
578
 
        display: table-cell;
579
 
        word-wrap: break-word;
580
 
        -webkit-transition: opacity 0.4s ease;
581
 
        ${lyrics}
582
 
    }
583
 
    .lyricsmain {
584
 
       ${main}
585
 
    }
586
 
    """)
587
 
 
588
 
FOOTER_SRC = Template("""
589
 
    left: ${left}px;
590
 
    bottom: ${bottom}px;
591
 
    width: ${width}px;
592
 
    font-family: ${family};
593
 
    font-size: ${size}pt;
594
 
    color: ${color};
595
 
    text-align: left;
596
 
    white-space: ${space};
597
 
    """)
598
 
 
599
 
LYRICS_FORMAT_SRC = Template("""
600
 
    ${justify}word-wrap: break-word;
601
 
    text-align: ${align};
602
 
    vertical-align: ${valign};
603
 
    font-family: ${font};
604
 
    font-size: ${size}pt;
605
 
    color: ${color};
606
 
    line-height: ${line}%;
607
 
    margin: 0;
608
 
    padding: 0;
609
 
    padding-bottom: ${bottom};
610
 
    padding-left: ${left}px;
611
 
    width: ${width}px;
612
 
    height: ${height}px;${font_style}${font_weight}
613
 
    """)
614
 
 
615
 
CHORDS_FORMAT = Template("""
616
 
    .chordline {
617
 
      line-height: ${chord_line_height};
618
 
    }
619
 
    .chordline span.chord span {
620
 
      position: relative;
621
 
    }
622
 
    .chordline span.chord span strong {
623
 
      position: absolute;
624
 
      top: -0.8em;
625
 
      left: 0;
626
 
      font-size: 75%;
627
 
      font-weight: normal;
628
 
      line-height: normal;
629
 
      display: ${chords_display};
630
 
    }
631
 
    .firstchordline {
632
 
        line-height: ${first_chord_line_height};
633
 
    }
634
 
    .ws {
635
 
        display: ${chords_display};
636
 
        white-space: pre-wrap;
637
 
    }""")
638
 
 
639
 
 
640
 
def build_html(item, screen, is_live, background, image=None, plugins=None):
641
 
    """
642
 
    Build the full web paged structure for display
643
 
 
644
 
    :param item: Service Item to be displayed
645
 
    :param screen: Current display information
646
 
    :param is_live: Item is going live, rather than preview/theme building
647
 
    :param background:  Theme background image - bytes
648
 
    :param image: Image media item - bytes
649
 
    :param plugins: The List of available plugins
650
 
    """
651
 
    width = screen['size'].width()
652
 
    height = screen['size'].height()
653
 
    theme_data = item.theme_data
654
 
    # Image generated and poked in
655
 
    if background:
656
 
        bgimage_src = 'src="data:image/png;base64,{image}"'.format(image=background)
657
 
    elif item.bg_image_bytes:
658
 
        bgimage_src = 'src="data:image/png;base64,{image}"'.format(image=item.bg_image_bytes)
659
 
    else:
660
 
        bgimage_src = 'style="display:none;"'
661
 
    if image:
662
 
        image_src = 'src="data:image/png;base64,{image}"'.format(image=image)
663
 
    else:
664
 
        image_src = 'style="display:none;"'
665
 
    css_additions = ''
666
 
    js_additions = ''
667
 
    html_additions = ''
668
 
    if plugins:
669
 
        for plugin in plugins:
670
 
            css_additions += plugin.get_display_css()
671
 
            js_additions += plugin.get_display_javascript()
672
 
            html_additions += plugin.get_display_html()
673
 
    return HTML_SRC.substitute(bg_css=build_background_css(item, width),
674
 
                               css_additions=css_additions,
675
 
                               footer_css=build_footer_css(item, height),
676
 
                               lyrics_css=build_lyrics_css(item),
677
 
                               transitions='true' if (theme_data and
678
 
                                                      theme_data.display_slide_transition and
679
 
                                                      is_live) else 'false',
680
 
                               js_additions=js_additions,
681
 
                               bg_image=bgimage_src,
682
 
                               image=image_src,
683
 
                               html_additions=html_additions,
684
 
                               chords_css=build_chords_css())
685
 
 
686
 
 
687
 
def webkit_version():
688
 
    """
689
 
    Return the Webkit version in use. Note method added relatively recently, so return 0 if prior to this
690
 
    """
691
 
    try:
692
 
        webkit_ver = float(QtWebKit.qWebKitVersion())
693
 
        log.debug('Webkit version = {version}'.format(version=webkit_ver))
694
 
    except AttributeError:
695
 
        webkit_ver = 0.0
696
 
    return webkit_ver
697
 
 
698
 
 
699
 
def build_background_css(item, width):
700
 
    """
701
 
    Build the background css
702
 
 
703
 
    :param item: Service Item containing theme and location information
704
 
    :param width:
705
 
    """
706
 
    width = int(width) // 2
707
 
    theme = item.theme_data
708
 
    background = 'background-color: black'
709
 
    if theme:
710
 
        if theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
711
 
            background = ''
712
 
        elif theme.background_type == BackgroundType.to_string(BackgroundType.Solid):
713
 
            background = 'background-color: {theme}'.format(theme=theme.background_color)
714
 
        else:
715
 
            if theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal):
716
 
                background = 'background: -webkit-gradient(linear, left top, left bottom, from({start}), to({end})) ' \
717
 
                    'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
718
 
            elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftTop):
719
 
                background = 'background: -webkit-gradient(linear, left top, right bottom, from({start}), to({end})) ' \
720
 
                    'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
721
 
            elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftBottom):
722
 
                background = 'background: -webkit-gradient(linear, left bottom, right top, from({start}), to({end})) ' \
723
 
                    'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
724
 
            elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Vertical):
725
 
                background = 'background: -webkit-gradient(linear, left top, right top, from({start}), to({end})) ' \
726
 
                    'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
727
 
            else:
728
 
                background = 'background: -webkit-gradient(radial, {width} 50%, 100, {width} 50%, {width}, ' \
729
 
                    'from({start}), to({end})) fixed'.format(width=width,
730
 
                                                             start=theme.background_start_color,
731
 
                                                             end=theme.background_end_color)
732
 
    return background
733
 
 
734
 
 
735
 
def build_lyrics_css(item):
736
 
    """
737
 
    Build the lyrics display css
738
 
 
739
 
    :param item: Service Item containing theme and location information
740
 
    """
741
 
    theme_data = item.theme_data
742
 
    lyricstable = ''
743
 
    lyrics = ''
744
 
    lyricsmain = ''
745
 
    if theme_data and item.main:
746
 
        lyricstable = 'left: {left}px; top: {top}px;'.format(left=item.main.x(), top=item.main.y())
747
 
        lyrics = build_lyrics_format_css(theme_data, item.main.width(), item.main.height())
748
 
        lyricsmain += build_lyrics_outline_css(theme_data)
749
 
        if theme_data.font_main_shadow:
750
 
            lyricsmain += ' text-shadow: {theme} {shadow}px ' \
751
 
                '{shadow}px;'.format(theme=theme_data.font_main_shadow_color,
752
 
                                     shadow=theme_data.font_main_shadow_size)
753
 
    return LYRICS_SRC.substitute(stable=lyricstable, lyrics=lyrics, main=lyricsmain)
754
 
 
755
 
 
756
 
def build_lyrics_outline_css(theme_data):
757
 
    """
758
 
    Build the css which controls the theme outline. Also used by renderer for splitting verses
759
 
 
760
 
    :param theme_data: Object containing theme information
761
 
    """
762
 
    if theme_data.font_main_outline:
763
 
        size = float(theme_data.font_main_outline_size) / 16
764
 
        fill_color = theme_data.font_main_color
765
 
        outline_color = theme_data.font_main_outline_color
766
 
        return ' -webkit-text-stroke: {size}em {color}; -webkit-text-fill-color: {fill}; '.format(size=size,
767
 
                                                                                                  color=outline_color,
768
 
                                                                                                  fill=fill_color)
769
 
    return ''
770
 
 
771
 
 
772
 
def build_lyrics_format_css(theme_data, width, height):
773
 
    """
774
 
    Build the css which controls the theme format. Also used by renderer for splitting verses
775
 
 
776
 
    :param theme_data: Object containing theme information
777
 
    :param width: Width of the lyrics block
778
 
    :param height: Height of the lyrics block
779
 
    """
780
 
    align = HorizontalType.Names[theme_data.display_horizontal_align]
781
 
    valign = VerticalType.Names[theme_data.display_vertical_align]
782
 
    left_margin = (int(theme_data.font_main_outline_size) * 2) if theme_data.font_main_outline else 0
783
 
    # fix tag incompatibilities
784
 
    justify = '' if (theme_data.display_horizontal_align == HorizontalType.Justify) else '    white-space: pre-wrap;\n'
785
 
    padding_bottom = '0.5em' if (theme_data.display_vertical_align == VerticalType.Bottom) else '0'
786
 
    return LYRICS_FORMAT_SRC.substitute(justify=justify,
787
 
                                        align=align,
788
 
                                        valign=valign,
789
 
                                        font=theme_data.font_main_name,
790
 
                                        size=theme_data.font_main_size,
791
 
                                        color=theme_data.font_main_color,
792
 
                                        line='{line:d}'.format(line=100 + int(theme_data.font_main_line_adjustment)),
793
 
                                        bottom=padding_bottom,
794
 
                                        left=left_margin,
795
 
                                        width=width,
796
 
                                        height=height,
797
 
                                        font_style='\n    font-style: italic;' if theme_data.font_main_italics else '',
798
 
                                        font_weight='\n    font-weight: bold;' if theme_data.font_main_bold else '')
799
 
 
800
 
 
801
 
def build_footer_css(item, height):
802
 
    """
803
 
    Build the display of the item footer
804
 
 
805
 
    :param item: Service Item to be processed.
806
 
    :param height:
807
 
    """
808
 
    theme = item.theme_data
809
 
    if not theme or not item.footer:
810
 
        return ''
811
 
    bottom = height - int(item.footer.y()) - int(item.footer.height())
812
 
    whitespace = 'normal' if Settings().value('themes/wrap footer') else 'nowrap'
813
 
    return FOOTER_SRC.substitute(left=item.footer.x(), bottom=bottom, width=item.footer.width(),
814
 
                                 family=theme.font_footer_name, size=theme.font_footer_size,
815
 
                                 color=theme.font_footer_color, space=whitespace)
816
 
 
817
 
 
818
 
def build_chords_css():
819
 
    if Settings().value('songs/enable chords') and Settings().value('songs/mainview chords'):
820
 
        chord_line_height = '2.0em'
821
 
        chords_display = 'inline'
822
 
        first_chord_line_height = '2.1em'
823
 
    else:
824
 
        chord_line_height = '1.0em'
825
 
        chords_display = 'none'
826
 
        first_chord_line_height = '1.0em'
827
 
    return CHORDS_FORMAT.substitute(chord_line_height=chord_line_height, chords_display=chords_display,
828
 
                                    first_chord_line_height=first_chord_line_height)