Ticket #10194: srt_cc708_v5.patch

File srt_cc708_v5.patch, 51.2 KB (added by Jim Stichnoth <stichnot@…>, 8 years ago)

Updated to latest master.

  • mythtv/libs/libmythtv/avformatdecoder.cpp

    diff --git a/mythtv/libs/libmythtv/avformatdecoder.cpp b/mythtv/libs/libmythtv/avformatdecoder.cpp
    index 0ff837d..5e948be 100644
    a b AvFormatDecoder::AvFormatDecoder(MythPlayer *parent, 
    321321
    322322    cc608_build_parity_table(cc608_parity_table);
    323323
    324     if (gCoreContext->GetNumSetting("CCBackground", 0))
    325         CC708Window::forceBlackBackground = true;
    326 
    327324    LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("PlayerFlags: 0x%1")
    328325        .arg(playerFlags, 0, 16));
    329326}
  • mythtv/libs/libmythtv/cc708window.cpp

    diff --git a/mythtv/libs/libmythtv/cc708window.cpp b/mythtv/libs/libmythtv/cc708window.cpp
    index e6a57cc..420137d 100644
    a b const uint k708AttrOpacityFlash = 1; 
    106106const uint k708AttrOpacityTranslucent = 2;
    107107const uint k708AttrOpacityTransparent = 3;
    108108
    109 bool CC708Window::forceBlackBackground = false;
    110 
    111109CC708Window::CC708Window()
    112110    : priority(0),              visible(0),
    113111      anchor_point(0),          relative_pos(0),
    void CC708Window::DefineWindow(int _priority, int _visible, 
    175173        for (uint i = 0; i < old_row * old_col; i++)
    176174            new_text[i] = text[i];
    177175        for (uint i = old_row * old_col; i < num; i++)
    178         {
    179176            new_text[i].attr = pen.attr;
    180             if (forceBlackBackground)
    181             {
    182                 new_text[i].attr.fg_opacity = k708AttrOpacityTransparent;
    183                 new_text[i].attr.bg_opacity = k708AttrOpacityTransparent;
    184             }
    185         }
    186177        delete [] text;
    187178        text = new_text;
    188179    }
    void CC708Window::DefineWindow(int _priority, int _visible, 
    200191        pen.column = 0;
    201192        pen.row    = 0;
    202193        for (uint i = 0; i < num; i++)
    203         {
    204194            text[i].attr = pen.attr;
    205             if (forceBlackBackground)
    206             {
    207                 text[i].attr.fg_opacity = k708AttrOpacityTransparent;
    208                 text[i].attr.bg_opacity = k708AttrOpacityTransparent;
    209             }
    210         }
    211195    }
    212196
    213197    exists  = true;
    void CC708Window::Clear(void) 
    241225    {
    242226        text[i].character = QChar(' ');
    243227        text[i].attr = pen.attr;
    244         if (forceBlackBackground)
    245         {
    246             text[i].attr.fg_opacity = k708AttrOpacityTransparent;
    247             text[i].attr.bg_opacity = k708AttrOpacityTransparent;
    248         }
    249228    }
    250229    changed = true;
    251230}
    void CC708Window::AddChar(QChar ch) 
    370349    }
    371350
    372351    GetCCChar().attr      = pen.attr;
    373     if (forceBlackBackground)
    374     {
    375         GetCCChar().attr.bg_color   = k708AttrColorBlack;
    376         GetCCChar().attr.bg_opacity = k708AttrOpacityTranslucent;
    377     }
    378352    GetCCChar().character = ch;
    379353    int c = pen.column;
    380354    int r = pen.row;
    void CC708Pen::SetPenStyle(uint style) 
    531505    attr.font_tag   = style2font[style];
    532506    attr.italics    = 0;
    533507    attr.underline  = 0;
     508    attr.boldface   = 0;
    534509    attr.edge_type  = 0;
    535510    attr.fg_color   = k708AttrColorWhite;
    536511    attr.fg_opacity = k708AttrOpacitySolid;
    void CC708Pen::SetPenStyle(uint style) 
    538513    attr.bg_opacity = (style<6) ?
    539514        k708AttrOpacitySolid : k708AttrOpacityTransparent;
    540515    attr.edge_color = k708AttrColorBlack;
     516    attr.override_fg_color = false;
    541517}
    542518
    543519CC708Character::CC708Character(const CC708Window &win)
    544520{
    545521    attr = win.pen.attr;
    546     if (CC708Window::forceBlackBackground)
    547     {
    548         attr.fg_opacity = k708AttrOpacityTransparent;
    549         attr.bg_opacity = k708AttrOpacityTransparent;
    550     }
    551522    character = ' ';
    552523}
    553524
  • mythtv/libs/libmythtv/cc708window.h

    diff --git a/mythtv/libs/libmythtv/cc708window.h b/mythtv/libs/libmythtv/cc708window.h
    index ccce79e..c5657db 100644
    a b using namespace std; 
    1313
    1414#include "mythtvexp.h"
    1515
     16extern const uint k708JustifyLeft;
     17extern const uint k708JustifyRight;
     18extern const uint k708JustifyCenter;
     19extern const uint k708JustifyFull;
     20
     21extern const uint k708EffectSnap;
     22extern const uint k708EffectFade;
     23extern const uint k708EffectWipe;
     24
     25extern const uint k708BorderNone;
     26extern const uint k708BorderRaised;
     27extern const uint k708BorderDepressed;
     28extern const uint k708BorderUniform;
     29extern const uint k708BorderShadowLeft;
     30extern const uint k708BorderShadowRight;
     31
     32extern const uint k708DirLeftToRight;
     33extern const uint k708DirRightToLeft;
     34extern const uint k708DirTopToBottom;
     35extern const uint k708DirBottomToTop;
     36
     37extern const uint k708AttrSizeSmall;
     38extern const uint k708AttrSizeStandard;
     39extern const uint k708AttrSizeLarge;
     40
     41extern const uint k708AttrOffsetSubscript;
     42extern const uint k708AttrOffsetNormal;
     43extern const uint k708AttrOffsetSuperscript;
     44
     45extern const uint k708AttrFontDefault;
     46extern const uint k708AttrFontMonospacedSerif;
     47extern const uint k708AttrFontProportionalSerif;
     48extern const uint k708AttrFontMonospacedSansSerif;
     49extern const uint k708AttrFontProportionalSansSerif;
     50extern const uint k708AttrFontCasual;
     51extern const uint k708AttrFontCursive;
     52extern const uint k708AttrFontSmallCaps;
     53
     54extern const uint k708AttrEdgeNone;
     55extern const uint k708AttrEdgeRaised;
     56extern const uint k708AttrEdgeDepressed;
     57extern const uint k708AttrEdgeUniform;
     58extern const uint k708AttrEdgeLeftDropShadow;
     59extern const uint k708AttrEdgeRightDropShadow;
     60
     61extern const uint k708AttrColorBlack;
     62extern const uint k708AttrColorWhite;
     63
     64extern const uint k708AttrOpacitySolid;
     65extern const uint k708AttrOpacityFlash;
     66extern const uint k708AttrOpacityTranslucent;
     67extern const uint k708AttrOpacityTransparent;
     68
    1669class CC708CharacterAttribute
    1770{
    1871  public:
    class CC708CharacterAttribute 
    2578    uint edge_type;
    2679    uint underline;
    2780    uint italics;
     81    uint boldface;
    2882
    2983    uint fg_color;
    3084    uint fg_opacity;
    class CC708CharacterAttribute 
    3286    uint bg_opacity;
    3387    uint edge_color;
    3488
     89    bool override_fg_color; // for a color not in the 6-bit palette
     90    QColor actual_fg_color;
     91
     92    CC708CharacterAttribute(bool isItalic, bool isBold, bool isUnderline,
     93                            QColor fgColor, bool hasBackground) :
     94        pen_size(k708AttrSizeStandard),
     95        offset(k708AttrOffsetNormal),
     96        text_tag(0), // "dialog", ignored
     97        font_tag(0), // system font
     98        edge_type(k708AttrEdgeNone),
     99        underline(isUnderline),
     100        italics(isItalic),
     101        boldface(isBold),
     102        fg_color(k708AttrColorWhite), // will be overridden
     103        fg_opacity(k708AttrOpacitySolid), // solid
     104        bg_color(k708AttrColorBlack),
     105        bg_opacity(hasBackground ? k708AttrOpacitySolid :
     106                   k708AttrOpacityTransparent),
     107        edge_color(k708AttrColorBlack),
     108        override_fg_color(true),
     109        actual_fg_color(fgColor)
     110    {
     111    }
     112    CC708CharacterAttribute(void) : override_fg_color(false) {}
     113
    35114    // remove this
    36115    uint FontIndex(void) const
    37116    {
    class CC708CharacterAttribute 
    41120    static QColor ConvertToQColor(uint eia708color);
    42121    QColor GetFGColor(void) const
    43122    {
    44         QColor fg = ConvertToQColor(fg_color);
     123        QColor fg = (override_fg_color ?
     124                     actual_fg_color : ConvertToQColor(fg_color));
    45125        fg.setAlpha(GetFGAlpha());
    46126        return fg;
    47127    }
    class CC708Pen 
    88168        attr.edge_type = edge_type;
    89169        attr.underline = underline;
    90170        attr.italics   = italics;
     171        attr.boldface  = 0;
    91172    }
    92173  public:
    93174    CC708CharacterAttribute attr;
    class MTV_PUBLIC CC708Window 
    200281    bool            exists;
    201282    bool            changed;
    202283
    203     static bool     forceBlackBackground;
    204 
    205284    mutable QMutex  lock;
    206285};
    207286
    class CC708Service 
    215294    CC708Window windows[8];
    216295};
    217296
    218 extern const uint k708JustifyLeft;
    219 extern const uint k708JustifyRight;
    220 extern const uint k708JustifyCenter;
    221 extern const uint k708JustifyFull;
    222 
    223 extern const uint k708EffectSnap;
    224 extern const uint k708EffectFade;
    225 extern const uint k708EffectWipe;
    226 
    227 extern const uint k708BorderNone;
    228 extern const uint k708BorderRaised;
    229 extern const uint k708BorderDepressed;
    230 extern const uint k708BorderUniform;
    231 extern const uint k708BorderShadowLeft;
    232 extern const uint k708BorderShadowRight;
    233 
    234 extern const uint k708DirLeftToRight;
    235 extern const uint k708DirRightToLeft;
    236 extern const uint k708DirTopToBottom;
    237 extern const uint k708DirBottomToTop;
    238 
    239 extern const uint k708AttrSizeSmall;
    240 extern const uint k708AttrSizeStandard;
    241 extern const uint k708AttrSizeLarge;
    242 
    243 extern const uint k708AttrOffsetSubscript;
    244 extern const uint k708AttrOffsetNormal;
    245 extern const uint k708AttrOffsetSuperscript;
    246 
    247 extern const uint k708AttrFontDefault;
    248 extern const uint k708AttrFontMonospacedSerif;
    249 extern const uint k708AttrFontProportionalSerif;
    250 extern const uint k708AttrFontMonospacedSansSerif;
    251 extern const uint k708AttrFontProportionalSansSerif;
    252 extern const uint k708AttrFontCasual;
    253 extern const uint k708AttrFontCursive;
    254 extern const uint k708AttrFontSmallCaps;
    255 
    256 extern const uint k708AttrEdgeNone;
    257 extern const uint k708AttrEdgeRaised;
    258 extern const uint k708AttrEdgeDepressed;
    259 extern const uint k708AttrEdgeUniform;
    260 extern const uint k708AttrEdgeLeftDropShadow;
    261 extern const uint k708AttrEdgeRightDropShadow;
    262 
    263 extern const uint k708AttrColorBlack;
    264 extern const uint k708AttrColorWhite;
    265 
    266 extern const uint k708AttrOpacitySolid;
    267 extern const uint k708AttrOpacityFlash;
    268 extern const uint k708AttrOpacityTranslucent;
    269 extern const uint k708AttrOpacityTransparent;
    270 
    271297#endif // _CC708_WINDOW_
  • mythtv/libs/libmythtv/mythccextractorplayer.cpp

    diff --git a/mythtv/libs/libmythtv/mythccextractorplayer.cpp b/mythtv/libs/libmythtv/mythccextractorplayer.cpp
    index 5173256..6ac17dc 100644
    a b using namespace std; 
    2828#include "teletextextractorreader.h"
    2929#include "mythccextractorplayer.h"
    3030#include "avformatdecoder.h"
     31#include "subtitlescreen.h"
    3132#include "srtwriter.h"
    3233
    3334
    void MythCCExtractorPlayer::Ingest608Captions(void) 
    319320                continue;
    320321            }
    321322
    322             QStringList content;
    323             vector<CC608Text*>::iterator bit = textlist->buffers.begin();
    324             for (; bit != textlist->buffers.end(); ++bit)
    325                 content += CC608Decoder::ToASCII((*bit)->text, true);
     323            FormattedTextSubtitle fsub;
     324            fsub.InitFromCC608(textlist->buffers);
     325            QStringList content = fsub.ToSRT();
    326326
    327327            textlist->lock.unlock();
    328328
    void MythCCExtractorPlayer::Ingest708Captions(void) 
    411411                    if (win.visible)
    412412                        strings = win.GetStrings();
    413413                    Ingest708Caption(it.key(), serviceIdx, windowIdx,
    414                                      win.pen.row, win.pen.column, strings);
     414                                     win.pen.row, win.pen.column, win, strings);
    415415                    while (!strings.empty())
    416416                    {
    417417                        delete strings.back();
    void MythCCExtractorPlayer::Ingest708Captions(void) 
    427427void MythCCExtractorPlayer::Ingest708Caption(
    428428    uint streamId, uint serviceIdx,
    429429    uint windowIdx, uint start_row, uint start_column,
     430    const CC708Window &win,
    430431    const vector<CC708String*> &content)
    431432{
    432     bool empty = true;
    433     QStringList winContent;
    434 
    435     vector<CC708String*>::const_iterator it = content.begin();
    436     for (; it != content.end(); ++it)
    437     {
    438         QString tmp = (*it)->str.trimmed();
    439         if (!tmp.isEmpty())
    440             winContent += tmp;
    441         empty &= tmp.isEmpty();
    442     }
     433    FormattedTextSubtitle fsub;
     434    fsub.InitFromCC708(win, windowIdx, content);
     435    QStringList winContent = fsub.ToSRT();
    443436
    444437    QMap<int, Window> &cc708win = m_cc708_windows[streamId][serviceIdx];
    445438    cc708win[windowIdx].row = start_row;
  • mythtv/libs/libmythtv/mythccextractorplayer.h

    diff --git a/mythtv/libs/libmythtv/mythccextractorplayer.h b/mythtv/libs/libmythtv/mythccextractorplayer.h
    index 88d7983..5660f39 100644
    a b class MTV_PUBLIC MythCCExtractorPlayer : public MythPlayer 
    139139    void Ingest708Captions(void);
    140140    void Ingest708Caption(uint streamId, uint serviceIdx, uint windowIdx,
    141141                          uint start_row, uint start_column,
     142                          const CC708Window &win,
    142143                          const vector<CC708String*> &content);
    143144    void Process708Captions(uint flags);
    144145
  • mythtv/libs/libmythtv/subtitlescreen.cpp

    diff --git a/mythtv/libs/libmythtv/subtitlescreen.cpp b/mythtv/libs/libmythtv/subtitlescreen.cpp
    index c9b951e..e0c6568 100644
    a b  
    1414#define PAD_WIDTH  0.20
    1515#define PAD_HEIGHT 0.04
    1616
    17 static MythFontProperties* gTextSubFont;
     17static const float LINE_SPACING = 1.1765f;
     18
    1819static QHash<int, MythFontProperties*> gCC708Fonts;
     20int SubtitleScreen::g_708fontSizes[4] = {36, 45, 60};
    1921
    2022SubtitleScreen::SubtitleScreen(MythPlayer *player, const char * name,
    2123                               int fontStretch) :
    SubtitleScreen::SubtitleScreen(MythPlayer *player, const char * name, 
    2628    m_textFontZoom(100),                    m_refreshArea(false),
    2729    m_fontStretch(fontStretch)
    2830{
    29     m_708fontSizes[0] = 36;
    30     m_708fontSizes[1] = 45;
    31     m_708fontSizes[2] = 60;
    3231    m_removeHTML.setMinimal(true);
    3332
    3433#ifdef USING_LIBASS
    void SubtitleScreen::DisplayAVSubtitles(void) 
    346345
    347346void SubtitleScreen::DisplayTextSubtitles(void)
    348347{
    349     if (!InitialiseFont(m_fontStretch) || !m_player || !m_subreader)
     348    if (!Initialise708Fonts(m_fontStretch) || !m_player || !m_subreader)
    350349        return;
    351350
    352351    bool changed = false;
    void SubtitleScreen::DisplayTextSubtitles(void) 
    421420
    422421void SubtitleScreen::DisplayRawTextSubtitles(void)
    423422{
    424     if (!InitialiseFont(m_fontStretch) || !m_player || !m_subreader)
     423    if (!Initialise708Fonts(m_fontStretch) || !m_player || !m_subreader)
    425424        return;
    426425
    427426    uint64_t duration;
    void SubtitleScreen::DisplayRawTextSubtitles(void) 
    445444void SubtitleScreen::DrawTextSubtitles(QStringList &wrappedsubs,
    446445                                       uint64_t start, uint64_t duration)
    447446{
    448     FormattedTextSubtitle fsub(m_safeArea);
     447    FormattedTextSubtitle fsub(m_safeArea, m_useBackground);
    449448    fsub.InitFromSRT(wrappedsubs, m_textFontZoom);
    450449    fsub.WrapLongLines();
    451     fsub.Draw(this);
    452     m_refreshArea = true;
     450    fsub.Layout();
     451    m_refreshArea = m_refreshArea || fsub.Draw(this, 0, start, duration);
    453452}
    454453
    455454void SubtitleScreen::DisplayDVDButton(AVSubtitle* dvdButton, QRect &buttonPos)
    static QString extract_cc608( 
    583582
    584583void SubtitleScreen::DisplayCC608Subtitles(void)
    585584{
    586     if (!InitialiseFont(m_fontStretch) || !m_608reader)
     585    if (!Initialise708Fonts(m_fontStretch) || !m_608reader)
    587586        return;
    588587
    589588    bool changed = false;
    void SubtitleScreen::DisplayCC608Subtitles(void) 
    619618        return;
    620619    }
    621620
    622     FormattedTextSubtitle fsub(m_safeArea);
     621    FormattedTextSubtitle fsub(m_safeArea, m_useBackground);
    623622    fsub.InitFromCC608(textlist->buffers);
    624     fsub.Draw(this);
    625     m_refreshArea = true;
     623    fsub.Layout();
     624    m_refreshArea = m_refreshArea || fsub.Draw(this);
    626625    textlist->lock.unlock();
    627626}
    628627
    void SubtitleScreen::DisplayCC708Subtitles(void) 
    643642            for (uint i = 0; i < 8; i++)
    644643                cc708service->windows[i].changed = true;
    645644            int size = (m_safeArea.height() * m_textFontZoom) / 2000;
    646             m_708fontSizes[1] = size;
    647             m_708fontSizes[0] = size * 32 / 42;
    648             m_708fontSizes[2] = size * 42 / 32;
     645            g_708fontSizes[1] = size;
     646            g_708fontSizes[0] = size * 32 / 42;
     647            g_708fontSizes[2] = size * 42 / 32;
    649648        }
    650649    }
    651650    else
    void SubtitleScreen::DisplayCC708Subtitles(void) 
    669668        QMutexLocker locker(&win.lock);
    670669        vector<CC708String*> list = win.GetStrings();
    671670        if (!list.empty())
    672             Display708Strings(win, i, video_aspect, list);
     671        {
     672            FormattedTextSubtitle fsub(m_safeArea, m_useBackground);
     673            fsub.InitFromCC708(win, i, list, video_aspect, m_textFontZoom);
     674            fsub.Layout();
     675            // Draw the window background after calculating bounding
     676            // rectangle in Layout()
     677            if (win.GetFillAlpha()) // TODO border?
     678            {
     679                QBrush fill(win.GetFillColor(), Qt::SolidPattern);
     680                MythUIShape *shape =
     681                    new MythUIShape(this, QString("cc708bg%1").arg(i));
     682                shape->SetFillBrush(fill);
     683                shape->SetArea(MythRect(fsub.m_bounds));
     684                m_708imageCache[i].append(shape);
     685                m_refreshArea = true;
     686            }
     687            m_refreshArea =
     688                m_refreshArea || fsub.Draw(this, &m_708imageCache[i]);
     689        }
    673690        for (uint j = 0; j < list.size(); j++)
    674691            delete list[j];
    675692        win.changed = false;
    void SubtitleScreen::Clear708Cache(int num) 
    686703    }
    687704}
    688705
    689 void SubtitleScreen::Display708Strings(const CC708Window &win, int num,
    690                                        float aspect, vector<CC708String*> &list)
    691 {
    692     LOG(VB_VBI, LOG_INFO,LOC +
    693         QString("Display Win %1, Anchor_id %2, x_anch %3, y_anch %4, "
    694                 "relative %5")
    695             .arg(num).arg(win.anchor_point).arg(win.anchor_horizontal)
    696             .arg(win.anchor_vertical).arg(win.relative_pos));
    697 
    698     bool display = false;
    699     MythFontProperties *mythfont;
    700     uint max_row_width = 0;
    701     uint total_height = 0;
    702     uint i = 0;
    703     for (uint row = 0; (row < win.true_row_count) && (i < list.size()); row++)
    704     {
    705         uint row_width = 0, max_row_height = 0;
    706         for (; (i < list.size()) && list[i] && (list[i]->y <= row); i++)
    707         {
    708             if (list[i]->y < row)
    709                 continue;
    710 
    711             mythfont = Get708Font(list[i]->attr);
    712             if (!mythfont)
    713                 continue;
    714 
    715             QString text = list[i]->str.trimmed();
    716             if (!text.isEmpty())
    717                 display = true;
    718 
    719             QFontMetrics font(*(mythfont->GetFace()));
    720             uint height = (uint)font.height() * (1 + PAD_HEIGHT);
    721 
    722             row_width += font.width(list[i]->str) +
    723                          (font.maxWidth() * PAD_WIDTH * 2);
    724             max_row_height = max(max_row_height, height);
    725         }
    726 
    727         max_row_width = max(max_row_width, row_width);
    728         total_height += max_row_height;
    729     }
    730 
    731     if (!display)
    732         return;
    733 
    734     float xrange  = win.relative_pos ? 100.0f :
    735                     (aspect > 1.4f) ? 210.0f : 160.0f;
    736     float yrange  = win.relative_pos ? 100.0f : 75.0f;
    737     float xmult   = (float)m_safeArea.width() / xrange;
    738     float ymult   = (float)m_safeArea.height() / yrange;
    739     uint anchor_x = (uint)(xmult * (float)win.anchor_horizontal);
    740     uint anchor_y = (uint)(ymult * (float)win.anchor_vertical);
    741 
    742     if (win.anchor_point % 3 == 1)
    743         anchor_x -= (((int)max_row_width) / 2);
    744     if (win.anchor_point % 3 == 2)
    745         anchor_x -= (int)max_row_width;
    746     if (win.anchor_point / 3 == 1)
    747         anchor_y -= (((int)total_height) / 2);
    748     if (win.anchor_point / 3 == 2)
    749         anchor_y -= (int)total_height;
    750 
    751     if (win.GetFillAlpha()) // TODO border?
    752     {
    753         QRect bg(anchor_x, anchor_y, max_row_width, total_height);
    754         QBrush fill(win.GetFillColor(), Qt::SolidPattern);
    755         MythUIShape *shape = new MythUIShape(this,
    756                 QString("cc708bg%1").arg(num));
    757         shape->SetFillBrush(fill);
    758         shape->SetArea(MythRect(bg));
    759         m_708imageCache[num].append(shape);
    760         m_refreshArea = true;
    761     }
    762 
    763     i = 0;
    764     int y = anchor_y;
    765     for (uint row = 0; (row < win.true_row_count) && (i < list.size()); row++)
    766     {
    767         uint maxheight = 0;
    768         int  x = anchor_x;
    769         bool first = true;
    770         for (; (i < list.size()) && list[i] && (list[i]->y <= row); i++)
    771         {
    772             bool last = ((i + 1) == list.size());
    773             if (!last)
    774                 last = (list[i + 1]->y > row);
    775 
    776             QString rawstring = list[i]->str;
    777             mythfont = Get708Font(list[i]->attr);
    778 
    779             if ((list[i]->y < row) || !mythfont || rawstring.isEmpty())
    780                 continue;
    781 
    782             QString trimmed = rawstring.trimmed();
    783             if (!trimmed.size() && last)
    784                 continue;
    785 
    786             QFontMetrics font(*(mythfont->GetFace()));
    787             uint height = (uint)font.height() * (1 + PAD_HEIGHT);
    788             maxheight   = max(maxheight, height);
    789             uint spacewidth = font.width(QString(" "));
    790             uint textwidth  = font.width(trimmed);
    791 
    792             int leading  = 0;
    793             int trailing = 0;
    794             if (trimmed.size() != rawstring.size())
    795             {
    796                 if (trimmed.size())
    797                 {
    798                     leading  = rawstring.indexOf(trimmed.at(0));
    799                     trailing = rawstring.size() - trimmed.size() - leading;
    800                 }
    801                 else
    802                 {
    803                     leading = rawstring.size();
    804                 }
    805                 leading  *= spacewidth;
    806                 trailing *= spacewidth;
    807             }
    808 
    809             if (!leading)
    810                 textwidth += spacewidth * PAD_WIDTH;
    811             if (!trailing)
    812                 textwidth += spacewidth * PAD_WIDTH;
    813 
    814             bool background = list[i]->attr.GetBGAlpha();
    815             QBrush bgfill = QBrush((list[i]->attr.GetBGColor(), Qt::SolidPattern));
    816 
    817             if (leading && background && !first)
    818             {
    819                 // draw background for leading space
    820                 QRect space(x, y, leading, height);
    821                 MythUIShape *shape = new MythUIShape(this,
    822                         QString("cc708shape%1x%2lead").arg(row).arg(i));
    823                 shape->SetFillBrush(bgfill);
    824                 shape->SetArea(MythRect(space));
    825                 m_708imageCache[num].append(shape);
    826                 m_refreshArea = true;
    827             }
    828 
    829             x += leading;
    830             QRect rect(x, y, textwidth, height);
    831 
    832             if (trimmed.size() && textwidth && background)
    833             {
    834                 MythUIShape *shape = new MythUIShape(this,
    835                         QString("cc708shape%1x%2main").arg(row).arg(i));
    836                 shape->SetFillBrush(bgfill);
    837                 shape->SetArea(MythRect(rect));
    838                 m_708imageCache[num].append(shape);
    839                 m_refreshArea = true;
    840             }
    841 
    842             if (trimmed.size() && textwidth)
    843             {
    844                 MythUISimpleText *text = new MythUISimpleText
    845                                          (list[i]->str, *mythfont,
    846                                           rect, Qt::AlignCenter,
    847                                           (MythUIType*)this,
    848                                   QString("cc708text%1x%2").arg(row).arg(i));
    849                 m_708imageCache[num].append(text);
    850                 m_refreshArea = true;
    851             }
    852 
    853             x += textwidth;
    854 
    855             if (trailing && background && !last)
    856             {
    857                 // draw background for trailing space
    858                 QRect space(x, y, trailing, height);
    859                 MythUIShape *shape = new MythUIShape(this,
    860                         QString("cc708shape%1x%2trail").arg(row).arg(i));
    861                 shape->SetFillBrush(bgfill);
    862                 shape->SetArea(MythRect(space));
    863                 m_708imageCache[num].append(shape);
    864                 m_refreshArea = true;
    865             }
    866 
    867             x += trailing;
    868             first = false;
    869             LOG(VB_VBI, LOG_INFO, QString("Win %1 row %2 String '%3'")
    870                 .arg(num).arg(row).arg(list[i]->str));
    871         }
    872         y += maxheight;
    873     }
    874 }
    875 
    876706void SubtitleScreen::AddScaledImage(QImage &img, QRect &pos)
    877707{
    878708    VideoOutput *vo = m_player->GetVideoOutput();
    void SubtitleScreen::AddScaledImage(QImage &img, QRect &pos) 
    904734    }
    905735}
    906736
    907 bool SubtitleScreen::InitialiseFont(int fontStretch)
    908 {
    909     static bool initialised = false;
    910     QString font = gCoreContext->GetSetting("OSDSubFont", "FreeSans");
    911     if (initialised)
    912     {
    913         if (gTextSubFont->face().family() == font &&
    914             gTextSubFont->face().stretch() == fontStretch)
    915         {
    916             return true;
    917         }
    918         delete gTextSubFont;
    919     }
    920 
    921     MythFontProperties *mythfont = new MythFontProperties();
    922     if (mythfont)
    923     {
    924         QFont newfont(font);
    925         newfont.setStretch(fontStretch);
    926         font.detach();
    927         mythfont->SetFace(newfont);
    928         mythfont->SetOutline(true, Qt::black, 2, 255);
    929         gTextSubFont = mythfont;
    930     }
    931     else
    932         return false;
    933 
    934     initialised = true;
    935     LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Loaded main subtitle font '%1'")
    936         .arg(font));
    937     return true;
    938 }
    939 
    940737bool SubtitleScreen::Initialise708Fonts(int fontStretch)
    941738{
    942739    static bool initialised = false;
    bool SubtitleScreen::Initialise708Fonts(int fontStretch) 
    950747    LOG(VB_GENERAL, LOG_INFO, "Initialise708Fonts()");
    951748
    952749    QStringList fonts;
    953     fonts.append("Droid Sans Mono"); // default
     750    QString defaultFont = gCoreContext->GetSetting("OSDSubFont", "FreeSans");
     751    fonts.append(defaultFont);       // default
    954752    fonts.append("FreeMono");        // mono serif
    955753    fonts.append("DejaVu Serif");    // prop serif
    956754    fonts.append("Droid Sans Mono"); // mono sans
    MythFontProperties* SubtitleScreen::Get708Font(CC708CharacterAttribute attr) 
    986784        return NULL;
    987785
    988786    mythfont->GetFace()->setItalic(attr.italics);
    989     mythfont->GetFace()->setPixelSize(m_708fontSizes[attr.pen_size & 0x3]);
     787    mythfont->GetFace()->setPixelSize(g_708fontSizes[attr.pen_size & 0x3]);
    990788    mythfont->GetFace()->setUnderline(attr.underline);
     789    mythfont->GetFace()->setBold(attr.boldface);
    991790    mythfont->SetColor(attr.GetFGColor());
    992791
    993     int off = m_708fontSizes[attr.pen_size & 0x3] / 20;
     792    int off = g_708fontSizes[attr.pen_size & 0x3] / 20;
    994793    QPoint shadowsz(off, off);
    995794    QColor colour = attr.GetEdgeColor();
    996795    int alpha     = attr.GetFGAlpha();
    MythFontProperties* SubtitleScreen::Get708Font(CC708CharacterAttribute attr) 
    1019818    return mythfont;
    1020819}
    1021820
    1022 QSize FormattedTextChunk::CalcSize(int pixelSize) const
     821static QString srtColorString(QColor color)
    1023822{
    1024     QFont *font = gTextSubFont->GetFace();
    1025     font->setItalic(isItalic);
    1026     font->setBold(isBold);
    1027     font->setUnderline(isUnderline);
    1028     font->setPixelSize(pixelSize);
     823    return QString("#%1%2%3")
     824        .arg(color.red(),   2, 16, QLatin1Char('0'))
     825        .arg(color.green(), 2, 16, QLatin1Char('0'))
     826        .arg(color.blue(),  2, 16, QLatin1Char('0'));
     827}
     828
     829QSize FormattedTextChunk::CalcSize(void) const
     830{
     831    QFont *font = SubtitleScreen::Get708Font(format)->GetFace();
    1029832    QFontMetrics fm(*font);
    1030833    int width = fm.width(text) + fm.maxWidth() * PAD_WIDTH;
    1031834    int height = fm.height() * (1 + PAD_HEIGHT);
    void FormattedTextSubtitle::InitFromCC608(vector<CC608Text*> &buffers) 
    1044847        return;
    1045848    vector<CC608Text*>::iterator i = buffers.begin();
    1046849    bool teletextmode = (*i)->teletextmode;
    1047     m_useBackground = m_useBackground && !teletextmode;
     850    bool useBackground = m_useBackground && !teletextmode;
    1048851    int xscale = teletextmode ? 40 : 36;
    1049852    int yscale = teletextmode ? 25 : 17;
    1050     m_pixelSize = m_safeArea.height() / (yscale * 1.1765f);
     853    int pixelSize = m_safeArea.height() / (yscale * LINE_SPACING);
     854    SubtitleScreen::g_708fontSizes[k708AttrSizeStandard] = pixelSize;
    1051855
    1052856    for (; i != buffers.end(); ++i)
    1053857    {
    void FormattedTextSubtitle::InitFromCC608(vector<CC608Text*> &buffers) 
    1063867        int orig_y = teletextmode ? cc->x : cc->y;
    1064868        int y = (int)(((float)orig_y / (float)yscale) *
    1065869                      (float)m_safeArea.height());
    1066         FormattedTextLine line(x, y);
     870        FormattedTextLine line(x, y, orig_x, orig_y);
    1067871        for (int chunk = 0; text != QString::null; chunk++)
    1068872        {
    1069873            QString captionText =
    1070874                extract_cc608(text, cc->teletextmode,
    1071875                              color, isItalic, isUnderline);
    1072             FormattedTextChunk chunk(captionText,
    1073                                      isItalic, isBold, isUnderline,
    1074                                      clr[min(max(0, color), 7)]);
     876            CC708CharacterAttribute attr(isItalic, isBold, isUnderline,
     877                                         clr[min(max(0, color), 7)],
     878                                         useBackground);
     879            FormattedTextChunk chunk(captionText, attr);
    1075880            line.chunks += chunk;
    1076881            LOG(VB_VBI, LOG_INFO,
    1077                 QString("Adding cc608 chunk: x=%1 y=%2 "
    1078                         "uline=%3 ital=%4 color=%5 text='%6'")
    1079                 .arg(cc->x).arg(cc->y)
    1080                 .arg(isUnderline).arg(isItalic).arg(color).arg(captionText));
     882                QString("Adding cc608 chunk (%1,%2): %3")
     883                .arg(cc->x).arg(cc->y).arg(chunk.ToLogString()));
    1081884        }
    1082885        m_lines += line;
    1083886    }
    1084887}
    1085888
     889void FormattedTextSubtitle::InitFromCC708(const CC708Window &win, int num,
     890                                          const vector<CC708String*> &list,
     891                                          float aspect,
     892                                          int textFontZoom)
     893{
     894    LOG(VB_VBI, LOG_INFO,LOC +
     895        QString("Display Win %1, Anchor_id %2, x_anch %3, y_anch %4, "
     896                "relative %5")
     897            .arg(num).arg(win.anchor_point).arg(win.anchor_horizontal)
     898            .arg(win.anchor_vertical).arg(win.relative_pos));
     899    int pixelSize = (m_safeArea.height() * textFontZoom) / 2000;
     900    SubtitleScreen::g_708fontSizes[k708AttrSizeSmall]    = pixelSize * 32 / 42;
     901    SubtitleScreen::g_708fontSizes[k708AttrSizeStandard] = pixelSize;
     902    SubtitleScreen::g_708fontSizes[k708AttrSizeLarge]    = pixelSize * 42 / 32;
     903
     904    float xrange  = win.relative_pos ? 100.0f :
     905                    (aspect > 1.4f) ? 210.0f : 160.0f;
     906    float yrange  = win.relative_pos ? 100.0f : 75.0f;
     907    float xmult   = (float)m_safeArea.width() / xrange;
     908    float ymult   = (float)m_safeArea.height() / yrange;
     909    uint anchor_x = (uint)(xmult * (float)win.anchor_horizontal);
     910    uint anchor_y = (uint)(ymult * (float)win.anchor_vertical);
     911    m_xAnchorPoint = win.anchor_point % 3;
     912    m_yAnchorPoint = win.anchor_point / 3;
     913    m_xAnchor = anchor_x;
     914    m_yAnchor = anchor_y;
     915
     916    for (uint i = 0; i < list.size(); i++)
     917    {
     918        if (list[i]->y >= (uint)m_lines.size())
     919            m_lines.resize(list[i]->y + 1);
     920        if (m_useBackground)
     921        {
     922            list[i]->attr.bg_color = k708AttrColorBlack;
     923            list[i]->attr.bg_opacity = k708AttrOpacitySolid;
     924        }
     925        FormattedTextChunk chunk(list[i]->str, list[i]->attr);
     926        m_lines[list[i]->y].chunks += chunk;
     927        LOG(VB_VBI, LOG_INFO, QString("Adding cc708 chunk: win %1 row %2: %3")
     928            .arg(num).arg(i).arg(chunk.ToLogString()));
     929    }
     930}
     931
    1086932void FormattedTextSubtitle::InitFromSRT(QStringList &subs, int textFontZoom)
    1087933{
    1088934    // Does a simplistic parsing of HTML tags from the strings.
    void FormattedTextSubtitle::InitFromSRT(QStringList &subs, int textFontZoom) 
    1099945    // <font color="#xxyyzz"> - change font color
    1100946    // </font> - reset font color to white
    1101947
    1102     m_pixelSize = (m_safeArea.height() * textFontZoom) / 2000;
     948    int pixelSize = (m_safeArea.height() * textFontZoom) / 2000;
     949    SubtitleScreen::g_708fontSizes[k708AttrSizeStandard] = pixelSize;
     950    m_xAnchorPoint = 1; // center
     951    m_yAnchorPoint = 2; // bottom
     952    m_xAnchor = m_safeArea.width() / 2;
     953    m_yAnchor = m_safeArea.height();
    1103954
    1104955    bool isItalic = false;
    1105956    bool isBold = false;
    void FormattedTextSubtitle::InitFromSRT(QStringList &subs, int textFontZoom) 
    1117968            int pos = text.indexOf(htmlTag);
    1118969            if (pos != 0) // don't add a zero-length string
    1119970            {
    1120                 FormattedTextChunk chunk(
    1121                     text.left(pos), isItalic, isBold, isUnderline, color);
     971                CC708CharacterAttribute attr(isItalic, isBold, isUnderline,
     972                                             color, m_useBackground);
     973                FormattedTextChunk chunk(text.left(pos), attr);
    1122974                line.chunks += chunk;
    1123975                text = (pos < 0 ? "" : text.mid(pos));
    1124                 LOG(VB_VBI, LOG_INFO, QString("Adding SRT chunk '%1'")
    1125                     .arg(chunk.text));
     976                LOG(VB_VBI, LOG_INFO, QString("Adding SRT chunk: %1")
     977                    .arg(chunk.ToLogString()));
    1126978            }
    1127979            if (pos >= 0)
    1128980            {
    void FormattedTextSubtitle::InitFromSRT(QStringList &subs, int textFontZoom) 
    11711023
    11721024                LOG(VB_VBI, LOG_INFO,
    11731025                    QString("SRT formatting change '%1', "
    1174                             "new ital=%2 bold=%3 uline=%4 color=#%5%6%7)")
     1026                            "new ital=%2 bold=%3 uline=%4 color=%5)")
    11751027                    .arg(html).arg(isItalic).arg(isBold).arg(isUnderline)
    1176                     .arg(color.red(),   2, 16, QLatin1Char('0'))
    1177                     .arg(color.green(), 2, 16, QLatin1Char('0'))
    1178                     .arg(color.blue(),  2, 16, QLatin1Char('0')));
     1028                    .arg(srtColorString(color)));
    11791029            }
    11801030        }
    11811031        m_lines += line;
    bool FormattedTextChunk::Split(FormattedTextChunk &newChunk) 
    11931043            QString("Failed to split chunk '%1'").arg(text));
    11941044        return false;
    11951045    }
    1196     newChunk.isItalic = isItalic;
    1197     newChunk.isBold = isBold;
    1198     newChunk.isUnderline = isUnderline;
    1199     newChunk.color = color;
     1046    newChunk.format = format;
    12001047    newChunk.text = text.mid(lastSpace + 1).trimmed() + ' ';
    12011048    text = text.left(lastSpace).trimmed();
    12021049    LOG(VB_VBI, LOG_INFO,
    bool FormattedTextChunk::Split(FormattedTextChunk &newChunk) 
    12041051    return true;
    12051052}
    12061053
     1054QString FormattedTextChunk::ToLogString(void) const
     1055{
     1056    QString str("");
     1057    str += QString("fg=%1.%2 ")
     1058        .arg(srtColorString(format.GetFGColor()))
     1059        .arg(format.GetFGAlpha());
     1060    str += QString("bg=%1.%2 ")
     1061        .arg(srtColorString(format.GetBGColor()))
     1062        .arg(format.GetBGAlpha());
     1063    str += QString("edge=%1.%2 ")
     1064        .arg(srtColorString(format.GetEdgeColor()))
     1065        .arg(format.edge_type);
     1066    str += QString("off=%1 pensize=%2 ")
     1067        .arg(format.offset)
     1068        .arg(format.pen_size);
     1069    str += QString("it=%1 ul=%2 bf=%3 ")
     1070        .arg(format.italics)
     1071        .arg(format.underline)
     1072        .arg(format.boldface);
     1073    str += QString("font=%1 ").arg(format.font_tag);
     1074    str += QString(" text='%1'").arg(text);
     1075    return str;
     1076}
     1077
    12071078void FormattedTextSubtitle::WrapLongLines(void)
    12081079{
    12091080    int maxWidth = m_safeArea.width();
    12101081    for (int i = 0; i < m_lines.size(); i++)
    12111082    {
    1212         int width = m_lines[i].CalcSize(m_pixelSize).width();
     1083        int width = m_lines[i].CalcSize().width();
    12131084        // Move entire chunks to the next line as necessary.  Leave at
    12141085        // least one chunk on the current line.
    12151086        while (width > maxWidth && m_lines[i].chunks.size() > 1)
    12161087        {
    1217             width -= m_lines[i].chunks.back().CalcSize(m_pixelSize).width();
     1088            width -= m_lines[i].chunks.back().CalcSize().width();
    12181089            // Make sure there's a next line to wrap into.
    12191090            if (m_lines.size() == i + 1)
    12201091                m_lines += FormattedTextLine(m_lines[i].x_indent,
    void FormattedTextSubtitle::WrapLongLines(void) 
    12381109                    m_lines += FormattedTextLine(m_lines[i].x_indent,
    12391110                                                 m_lines[i].y_indent);
    12401111                m_lines[i+1].chunks.prepend(newChunk);
    1241                 width = m_lines[i].CalcSize(m_pixelSize).width();
     1112                width = m_lines[i].CalcSize().width();
    12421113            }
    12431114        }
    12441115    }
    12451116}
    12461117
    1247 void FormattedTextSubtitle::Draw(SubtitleScreen *parent,
    1248                                  uint64_t start, uint64_t duration) const
     1118// Resolves any TBD x_indent and y_indent values in FormattedTextLine
     1119// objects.  Calculates m_bounds.  Prunes most leading and all
     1120// trailing whitespace from each line so that displaying with a black
     1121// background doesn't look clumsy.
     1122void FormattedTextSubtitle::Layout(void)
    12491123{
    1250     bool useBackground = m_useBackground && parent->GetUseBackground();
    1251     gTextSubFont->GetFace()->setPixelSize(m_pixelSize);
    1252     QFontMetrics font(*(gTextSubFont->GetFace()));
    1253     int pad_width = font.maxWidth() * PAD_WIDTH;
    1254     QBrush bgfill = QBrush(QColor(0, 0, 0), Qt::SolidPattern);
     1124    // Calculate dimensions of bounding rectangle
     1125    int anchor_width = 0;
     1126    int anchor_height = 0;
     1127    for (int i = 0; i < m_lines.size(); i++)
     1128    {
     1129        QSize sz = m_lines[i].CalcSize();
     1130        anchor_width = max(anchor_width, sz.width());
     1131        anchor_height += sz.height() * LINE_SPACING;
     1132    }
    12551133
     1134    // Adjust the anchor point according to actual width and height
     1135    int anchor_x = m_xAnchor;
     1136    int anchor_y = m_yAnchor;
     1137    if (m_xAnchorPoint == 1)
     1138        anchor_x -= anchor_width / 2;
     1139    else if (m_xAnchorPoint == 2)
     1140        anchor_x -= anchor_width;
     1141    if (m_yAnchorPoint == 1)
     1142        anchor_y -= anchor_height / 2;
     1143    else if (m_yAnchorPoint == 2)
     1144        anchor_y -= anchor_height;
     1145
     1146    m_bounds = QRect(anchor_x, anchor_y, anchor_width, anchor_height);
     1147
     1148    // Fill in missing coordinates
     1149    int y = anchor_y;
    12561150    for (int i = 0; i < m_lines.size(); i++)
    12571151    {
    1258         int x = m_lines[i].x_indent, y = m_lines[i].y_indent;
    1259         QSize sz;
    1260         if ((x < 0) || (y < 0) || useBackground)
    1261             sz = m_lines[i].CalcSize(m_pixelSize);
    1262         if (x < 0) // centering
    1263             x = (m_safeArea.width() - sz.width()) / 2;
    1264         if (y < 0) // stack lines at bottom
     1152        if (m_lines[i].x_indent < 0)
     1153            m_lines[i].x_indent = anchor_x;
     1154        if (m_lines[i].y_indent < 0)
     1155            m_lines[i].y_indent = y;
     1156        y += m_lines[i].CalcSize().height() * LINE_SPACING;
     1157        // Prune leading all-whitespace chunks.
     1158        while (!m_lines[i].chunks.isEmpty() &&
     1159               m_lines[i].chunks.first().text.trimmed().isEmpty())
    12651160        {
    1266             y = m_safeArea.height();
    1267             y -= (m_lines.size() - i) * sz.height() * 1.1765f;
     1161            m_lines[i].x_indent +=
     1162                m_lines[i].chunks.first().CalcSize().width();
     1163            m_lines[i].chunks.removeFirst();
    12681164        }
    1269 
    1270         if (useBackground)
     1165        // Prune trailing all-whitespace chunks.
     1166        while (!m_lines[i].chunks.isEmpty() &&
     1167               m_lines[i].chunks.last().text.trimmed().isEmpty())
    12711168        {
    1272             QRect bgrect(x - pad_width, y,
    1273                          sz.width() + pad_width, sz.height());
    1274             // Special case.  If the first chunk is entirely
    1275             // whitespace, don't put a black background behind that
    1276             // chunk.
    1277             //
    1278             // This often happens when a line of cc608 caption text
    1279             // begins with a mid-row format control like italics or a
    1280             // color change.  The spec says the mid-row control
    1281             // implies a space character, which needs to be preserved
    1282             // so that the rest of the text is accurately laid out.  A
    1283             // leading space looks clumsy against the black
    1284             // background, so we adjust the background accordingly.
    1285             if (!m_lines[i].chunks.isEmpty() &&
    1286                 m_lines[i].chunks[0].text.trimmed().isEmpty())
    1287             {
    1288                 bgrect.setLeft(
    1289                     bgrect.left() +
    1290                     m_lines[i].chunks[0].CalcSize(m_pixelSize).width());
    1291             }
    1292             MythUIShape *shape =
    1293                 new MythUIShape(parent, QString("subbg%1_%2").arg(x).arg(y));
    1294             shape->SetFillBrush(bgfill);
    1295             shape->SetArea(MythRect(bgrect));
    1296             if (duration > 0)
    1297                 parent->RegisterExpiration(shape, start + duration);
     1169            m_lines[i].chunks.removeLast();
     1170        }
     1171        // Trim trailing whitespace from last chunk.  (Trimming
     1172        // leading whitespace from all chunks is handled in the Draw()
     1173        // routine.)
     1174        if (!m_lines[i].chunks.isEmpty())
     1175        {
     1176            QString *str = &m_lines[i].chunks.last().text;
     1177            int idx = str->length() - 1;
     1178            while (idx >= 0 && str->at(idx) == ' ')
     1179                --idx;
     1180            str->truncate(idx + 1);
    12981181        }
     1182    }
     1183}
     1184
     1185// Returns true if anything new was drawn, false if not.  The caller
     1186// should call SubtitleScreen::OptimiseDisplayedArea() if true is
     1187// returned.
     1188bool FormattedTextSubtitle::Draw(SubtitleScreen *parent,
     1189                                 QList<MythUIType*> *imageCache,
     1190                                 uint64_t start, uint64_t duration) const
     1191{
     1192    bool result = false;
    12991193
     1194    for (int i = 0; i < m_lines.size(); i++)
     1195    {
     1196        int x = m_lines[i].x_indent, y = m_lines[i].y_indent;
     1197        int height = m_lines[i].CalcSize().height();
    13001198        QList<FormattedTextChunk>::const_iterator chunk;
     1199        bool first = true;
    13011200        for (chunk = m_lines[i].chunks.constBegin();
    13021201             chunk != m_lines[i].chunks.constEnd();
    13031202             ++chunk)
    13041203        {
     1204            MythFontProperties *mythfont =
     1205                SubtitleScreen::Get708Font((*chunk).format);
     1206            if (!mythfont)
     1207                continue;
     1208            QFontMetrics font(*(mythfont->GetFace()));
    13051209            // If the chunk starts with whitespace, the leading
    13061210            // whitespace ultimately gets lost due to the
    13071211            // text.trimmed() operation in the MythUISimpleText
    void FormattedTextSubtitle::Draw(SubtitleScreen *parent, 
    13141218                ++count;
    13151219            }
    13161220            int x_adjust = count * font.width(" ");
    1317             gTextSubFont->GetFace()->setItalic((*chunk).isItalic);
    1318             gTextSubFont->GetFace()->setBold((*chunk).isBold);
    1319             gTextSubFont->GetFace()->setUnderline((*chunk).isUnderline);
    1320             gTextSubFont->SetColor((*chunk).color);
    1321             QSize chunk_sz = (*chunk).CalcSize(m_pixelSize);
     1221            QSize chunk_sz = (*chunk).CalcSize();
     1222            if ((*chunk).format.GetBGAlpha())
     1223            {
     1224                QBrush bgfill = QBrush((*chunk).format.GetBGColor());
     1225                QRect bgrect(x, y, chunk_sz.width(), height);
     1226                if (first)
     1227                    bgrect.setLeft(bgrect.left() + x_adjust -
     1228                                   font.maxWidth() * PAD_WIDTH);
     1229                MythUIShape *bgshape = new MythUIShape(parent,
     1230                        QString("subbg%1x%2@%3,%4")
     1231                                     .arg(chunk_sz.width())
     1232                                     .arg(height)
     1233                                     .arg(x).arg(y));
     1234                bgshape->SetFillBrush(bgfill);
     1235                bgshape->SetArea(MythRect(bgrect));
     1236                if (imageCache)
     1237                    imageCache->append(bgshape);
     1238                if (duration > 0)
     1239                    parent->RegisterExpiration(bgshape, start + duration);
     1240                result = true;
     1241            }
    13221242            QRect rect(x + x_adjust, y,
    1323                        chunk_sz.width() - x_adjust, chunk_sz.height());
     1243                       chunk_sz.width() - x_adjust, height);
    13241244
    13251245            MythUISimpleText *text =
    1326                 new MythUISimpleText((*chunk).text, *gTextSubFont, rect,
     1246                new MythUISimpleText((*chunk).text, *mythfont, rect,
    13271247                                     Qt::AlignLeft, (MythUIType*)parent,
    13281248                                     QString("subtxt%1x%2@%3,%4")
    13291249                                     .arg(chunk_sz.width())
    1330                                      .arg(chunk_sz.height())
     1250                                     .arg(height)
    13311251                                     .arg(x).arg(y));
     1252            if (imageCache)
     1253                imageCache->append(text);
    13321254            if (duration > 0)
    13331255                parent->RegisterExpiration(text, start + duration);
     1256            result = true;
    13341257
    13351258            LOG(VB_VBI, LOG_INFO,
    1336                 QString("Drawing chunk at (%1,%2) with "
    1337                         "ital=%3 bold=%4 uline=%5 color=#%6%7%8 "
    1338                         "text='%9'")
    1339                 .arg(x).arg(y)
    1340                 .arg((*chunk).isItalic)
    1341                 .arg((*chunk).isBold)
    1342                 .arg((*chunk).isUnderline)
    1343                 .arg((*chunk).color.red(),   2, 16, QLatin1Char('0'))
    1344                 .arg((*chunk).color.green(), 2, 16, QLatin1Char('0'))
    1345                 .arg((*chunk).color.blue(),  2, 16, QLatin1Char('0'))
    1346                 .arg((*chunk).text));
     1259                QString("Drawing chunk at (%1,%2): %3")
     1260                .arg(x).arg(y).arg((*chunk).ToLogString()));
    13471261
    13481262            x += chunk_sz.width();
     1263            first = false;
    13491264        }
    13501265    }
     1266    return result;
    13511267}
    13521268
     1269QStringList FormattedTextSubtitle::ToSRT(void) const
     1270{
     1271    QStringList result;
     1272    for (int i = 0; i < m_lines.size(); i++)
     1273    {
     1274        QString line;
     1275        if (m_lines[i].orig_x > 0)
     1276            line.fill(' ', m_lines[i].orig_x);
     1277        QList<FormattedTextChunk>::const_iterator chunk;
     1278        for (chunk = m_lines[i].chunks.constBegin();
     1279             chunk != m_lines[i].chunks.constEnd();
     1280             ++chunk)
     1281        {
     1282            const QString &text = (*chunk).text;
     1283            const CC708CharacterAttribute &attr = (*chunk).format;
     1284            bool isBlank = !attr.underline && text.trimmed().isEmpty();
     1285            if (!isBlank)
     1286            {
     1287                if (attr.boldface)
     1288                    line += "<b>";
     1289                if (attr.italics)
     1290                    line += "<i>";
     1291                if (attr.underline)
     1292                    line += "<u>";
     1293                if (attr.GetFGColor() != Qt::white)
     1294                    line += QString("<font color=\"%1\">")
     1295                        .arg(srtColorString(attr.GetFGColor()));
     1296            }
     1297            line += text;
     1298            if (!isBlank)
     1299            {
     1300                if (attr.GetFGColor() != Qt::white)
     1301                    line += QString("</font>");
     1302                if (attr.underline)
     1303                    line += "</u>";
     1304                if (attr.italics)
     1305                    line += "</i>";
     1306                if (attr.boldface)
     1307                    line += "</b>";
     1308            }
     1309         }
     1310        if (!line.trimmed().isEmpty())
     1311            result += line;
     1312     }
     1313    return result;
     1314 }
     1315 
    13531316#ifdef USING_LIBASS
    13541317static void myth_libass_log(int level, const char *fmt, va_list vl, void *ctx)
    13551318{
  • mythtv/libs/libmythtv/subtitlescreen.h

    diff --git a/mythtv/libs/libmythtv/subtitlescreen.h b/mythtv/libs/libmythtv/subtitlescreen.h
    index c8f805a..2f231bd 100644
    a b extern "C" { 
    2323
    2424class SubtitleScreen : public MythScreenType
    2525{
    26     static bool InitialiseFont(int fontStretch = QFont::Unstretched);
    2726    static bool Initialise708Fonts(int fontStretch = QFont::Unstretched);
    2827
    2928  public:
    class SubtitleScreen : public MythScreenType 
    5150    virtual bool Create(void);
    5251    virtual void Pulse(void);
    5352
     53    static int g_708fontSizes[4];
     54    static MythFontProperties* Get708Font(CC708CharacterAttribute attr);
     55
    5456  private:
    5557    void OptimiseDisplayedArea(void);
    5658    void DisplayAVSubtitles(void);
    class SubtitleScreen : public MythScreenType 
    6264    void DisplayCC708Subtitles(void);
    6365    void AddScaledImage(QImage &img, QRect &pos);
    6466    void Clear708Cache(int num);
    65     void Display708Strings(const CC708Window &win, int num,
    66                            float aspect, vector<CC708String*> &list);
    67     MythFontProperties* Get708Font(CC708CharacterAttribute attr);
    6867
    6968    MythPlayer        *m_player;
    7069    SubtitleReader    *m_subreader;
    class SubtitleScreen : public MythScreenType 
    7574    QRegExp            m_removeHTML;
    7675    int                m_subtitleType;
    7776    QHash<MythUIType*, long long> m_expireTimes;
    78     int                m_708fontSizes[3];
    79     int                m_textFontZoom;
     77    int                m_textFontZoom; // valid for 708 & text subs
    8078    bool               m_refreshArea;
    8179    QHash<int,QList<MythUIType*> > m_708imageCache;
    8280    int                m_fontStretch;
    class SubtitleScreen : public MythScreenType 
    102100class FormattedTextChunk
    103101{
    104102  public:
    105     FormattedTextChunk(
    106         const QString &t, bool ital, bool bold, bool uline, QColor clr) :
    107         text(t), isItalic(ital), isBold(bold), isUnderline(uline), color(clr)
     103    FormattedTextChunk(const QString &t, CC708CharacterAttribute formatting)
     104        : text(t), format(formatting)
    108105    {
    109106    }
    110107    FormattedTextChunk(void) {}
    111108
    112     QSize CalcSize(int pixelSize) const;
     109    QSize CalcSize(void) const;
    113110    bool Split(FormattedTextChunk &newChunk);
     111    QString ToLogString(void) const;
    114112
    115113    QString text;
    116     bool isItalic;
    117     bool isBold;
    118     bool isUnderline;
    119     QColor color;
     114    CC708CharacterAttribute format;
    120115};
    121116
    122117class FormattedTextLine
    123118{
    124119  public:
    125     FormattedTextLine(int x = -1, int y = -1) : x_indent(x), y_indent(y) {}
     120    FormattedTextLine(int x = -1, int y = -1, int o_x = -1, int o_y = -1)
     121        : x_indent(x), y_indent(y), orig_x(o_x), orig_y(o_y) {}
    126122
    127     QSize CalcSize(int pixelSize) const
     123    QSize CalcSize(void) const
    128124    {
    129125        int height = 0, width = 0;
    130126        QList<FormattedTextChunk>::const_iterator it;
    131127        for (it = chunks.constBegin(); it != chunks.constEnd(); ++it)
    132128        {
    133             QSize tmp = (*it).CalcSize(pixelSize);
     129            QSize tmp = (*it).CalcSize();
    134130            height = max(height, tmp.height());
    135131            width += tmp.width();
    136132        }
    class FormattedTextLine 
    140136    QList<FormattedTextChunk> chunks;
    141137    int x_indent; // -1 means TBD i.e. centered
    142138    int y_indent; // -1 means TBD i.e. relative to bottom
     139    int orig_x, orig_y; // original, unscaled coordinates
    143140};
    144141
    145142class FormattedTextSubtitle
    146143{
    147144  public:
    148     FormattedTextSubtitle(const QRect &safearea) :
    149         m_safeArea(safearea), m_useBackground(true) {}
     145    FormattedTextSubtitle(const QRect &safearea, bool useBackground)
     146        : m_safeArea(safearea), m_useBackground(useBackground) {}
     147    FormattedTextSubtitle(void)
     148        : m_safeArea(QRect(0, 0, 640, 360)), m_useBackground(false) {}
    150149    void InitFromCC608(vector<CC608Text*> &buffers);
     150    void InitFromCC708(const CC708Window &win, int num,
     151                       const vector<CC708String*> &list,
     152                       float aspect = 1.77777f,
     153                       int textFontZoom = 100);
    151154    void InitFromSRT(QStringList &subs, int textFontZoom);
    152155    void WrapLongLines(void);
    153     void Draw(SubtitleScreen *parent,
     156    void Layout(void);
     157    bool Draw(SubtitleScreen *parent,
     158              QList<MythUIType*> *imageCache = 0,
    154159              uint64_t start = 0, uint64_t duration = 0) const;
     160    QStringList ToSRT(void) const;
     161    QRect m_bounds;
    155162
    156163  private:
    157164    QVector<FormattedTextLine> m_lines;
    158165    const QRect m_safeArea;
    159     bool m_useBackground;
    160     int m_pixelSize;
     166    const bool m_useBackground;
     167    int m_xAnchorPoint; // 0=left, 1=center, 2=right
     168    int m_yAnchorPoint; // 0=top,  1=center, 2=bottom
     169    int m_xAnchor; // pixels from left
     170    int m_yAnchor; // pixels from top
    161171};
    162172
    163173#endif // SUBTITLESCREEN_H