Ticket #10194: srt_formatting_v1.patch

File srt_formatting_v1.patch, 24.2 KB (added by Jim Stichnoth <stichnot@…>, 9 years ago)
  • mythtv/libs/libmythtv/subtitlescreen.cpp

    diff --git a/mythtv/libs/libmythtv/subtitlescreen.cpp b/mythtv/libs/libmythtv/subtitlescreen.cpp
    index 9f6820c..d343f31 100644
    a b void SubtitleScreen::DisplayTextSubtitles(void) 
    423423        return;
    424424    }
    425425
    426     OptimiseTextSubs(rawsubs);
    427426    subs->Unlock();
    428427    DrawTextSubtitles(rawsubs, 0, 0);
    429428}
    void SubtitleScreen::DisplayRawTextSubtitles(void) 
    461460
    462461    // delete old subs that may still be on screen
    463462    DeleteAllChildren();
    464     OptimiseTextSubs(subs);
    465463    DrawTextSubtitles(subs, currentFrame->timecode, duration);
    466464}
    467465
    468 void SubtitleScreen::OptimiseTextSubs(QStringList &rawsubs)
    469 {
    470     QFontMetrics font(*(gTextSubFont->GetFace()));
    471     int maxwidth = m_safeArea.width();
    472     QStringList wrappedsubs;
    473     QString wrappedtext = "";
    474     int i = 0;
    475     while ((i < rawsubs.size()) || !wrappedtext.isEmpty())
    476     {
    477         QString nextline = wrappedtext;
    478         if (i < rawsubs.size())
    479             nextline += rawsubs[i].remove((const QRegExp&) m_removeHTML);
    480         wrappedtext = "";
    481 
    482         while (font.width(nextline) > maxwidth)
    483         {
    484             QString word = nextline.section(" ", -1, -1,
    485                                             QString::SectionSkipEmpty);
    486             if (word.isEmpty() || font.width(word) > maxwidth)
    487                 break;
    488             wrappedtext = word + " " + wrappedtext;
    489             nextline.chop(word.size() + 1);
    490         }
    491         if (!nextline.isEmpty())
    492             wrappedsubs.append(nextline);
    493         i++;
    494     }
    495     rawsubs = wrappedsubs;
    496 }
    497 
    498466void SubtitleScreen::DrawTextSubtitles(QStringList &wrappedsubs,
    499467                                       uint64_t start, uint64_t duration)
    500468{
    501     QFontMetrics font(*(gTextSubFont->GetFace()));
    502     int height = font.height() * (1 + PAD_HEIGHT);
    503     int pad_width = font.maxWidth() * PAD_WIDTH;
    504     int y = m_safeArea.height() - (height * wrappedsubs.size());
    505     int centre = m_safeArea.width() / 2;
    506     QBrush bgfill = QBrush(QColor(0, 0, 0), Qt::SolidPattern);
    507     foreach (QString subtitle, wrappedsubs)
    508     {
    509         if (subtitle.isEmpty())
    510             continue;
    511         int width = font.width(subtitle) + pad_width * 2;
    512         int x = centre - (width / 2) - pad_width;
    513         QRect rect(x, y, width, height);
    514 
    515         if (m_useBackground)
    516         {
    517             MythUIShape *shape = new MythUIShape(this,
    518                 QString("tsubbg%1%2").arg(x).arg(y));
    519             shape->SetFillBrush(bgfill);
    520             shape->SetArea(MythRect(rect));
    521             if (duration > 0)
    522                 m_expireTimes.insert(shape, start + duration);
    523         }
    524         MythUISimpleText* text = new MythUISimpleText
    525                                  (subtitle, *gTextSubFont, rect,
    526                                   Qt::AlignCenter, this,
    527                                   QString("tsub%1%2").arg(x).arg(y));
    528         y += height;
    529         LOG(VB_PLAYBACK, LOG_INFO, LOC + subtitle);
    530         m_refreshArea = true;
    531 
    532         if (duration > 0)
    533         {
    534             m_expireTimes.insert(text, start + duration);
    535             LOG(VB_PLAYBACK, LOG_INFO, LOC +
    536                 QString("Display text subtitle for %1 ms").arg(duration));
    537         }
    538     }
     469    FormattedTextSubtitle fsub(m_safeArea);
     470    fsub.InitFromSRT(wrappedsubs, m_textFontZoom);
     471    fsub.WrapLongLines();
     472    fsub.Draw(this);
     473    m_refreshArea = true;
    539474}
    540475
    541476void SubtitleScreen::DisplayDVDButton(AVSubtitle* dvdButton, QRect &buttonPos)
    void SubtitleScreen::DisplayDVDButton(AVSubtitle* dvdButton, QRect &buttonPos) 
    604539/// are corresondingly updated (and the control character is stripped).
    605540static QString extract_cc608(
    606541    QString &text, bool teletextmode, int &color,
    607     bool &isItalic, bool &isUnderline, bool &showedNonControl)
     542    bool &isItalic, bool &isUnderline)
    608543{
    609544    QString result;
    610545    QString orig(text);
    static QString extract_cc608( 
    613548    {
    614549        result = text;
    615550        text = QString::null;
    616         showedNonControl = true;
    617551        return result;
    618552    }
    619553
    static QString extract_cc608( 
    651585    {
    652586        result = text;
    653587        text = QString::null;
    654         showedNonControl = true;
    655588    }
    656589    else
    657590    {
    static QString extract_cc608( 
    664597        if (text[nextControl] < (0x7000 + 0x10))
    665598            result += " ";
    666599        text = text.mid(nextControl);
    667         if (nextControl > 0)
    668             showedNonControl = true;
    669600    }
    670601
    671602    return result;
    static QString extract_cc608( 
    673604
    674605void SubtitleScreen::DisplayCC608Subtitles(void)
    675606{
    676     static const QColor clr[8] =
    677     {
    678         Qt::white,   Qt::green,   Qt::blue,    Qt::cyan,
    679         Qt::red,     Qt::yellow,  Qt::magenta, Qt::white,
    680     };
    681 
    682607    if (!InitialiseFont(m_fontStretch) || !m_608reader)
    683608        return;
    684609
    void SubtitleScreen::DisplayCC608Subtitles(void) 
    715640        return;
    716641    }
    717642
    718     vector<CC608Text*>::iterator i = textlist->buffers.begin();
    719     bool teletextmode = (*i)->teletextmode;
    720     int xscale = teletextmode ? 40 : 36;
    721     int yscale = teletextmode ? 25 : 17;
    722     gTextSubFont->GetFace()->setPixelSize(m_safeArea.height() / (yscale * 1.2));
    723     QBrush bgfill = QBrush(QColor(0, 0, 0), Qt::SolidPattern);
    724 
    725     for (; i != textlist->buffers.end(); ++i)
    726     {
    727         CC608Text *cc = (*i);
    728         int color = 0;
    729         bool isItalic = false, isUnderline = false;
    730         bool first = true;
    731         bool showedNonControl = false;
    732         int x = 0, width = 0;
    733         QString text(cc->text);
    734 
    735         for (int chunk = 0; text != QString::null; first = false, chunk++)
    736         {
    737             QString captionText =
    738                 extract_cc608(text, cc->teletextmode,
    739                               color, isItalic, isUnderline,
    740                               showedNonControl);
    741             gTextSubFont->GetFace()->setItalic(isItalic);
    742             gTextSubFont->GetFace()->setUnderline(isUnderline);
    743             gTextSubFont->SetColor(clr[min(max(0, color), 7)]);
    744             QFontMetrics font(*(gTextSubFont->GetFace()));
    745             // XXX- could there be different heights across the same line?
    746             int height = font.height() * (1 + PAD_HEIGHT);
    747             if (first)
    748             {
    749                 x = teletextmode ? cc->y : (cc->x + 3);
    750                 x = (int)(((float)x / (float)xscale) *
    751                           (float)m_safeArea.width());
    752             }
    753             else
    754             {
    755                 x += width; // bump x by the previous width
    756             }
    757 
    758             int pad_width = font.maxWidth() * PAD_WIDTH;
    759             width = font.width(captionText) + pad_width;
    760             int y = teletextmode ? cc->x : cc->y;
    761             y = (int)(((float)y / (float)yscale) * (float)m_safeArea.height());
    762             // Sometimes a line of caption text begins with a mid-row
    763             // format control like italics or a color change.  The
    764             // spec says the mid-row control also includes a space
    765             // character.  But this looks clumsy when using a
    766             // background, so we suppress it after the placement is
    767             // calculated.
    768             if (!showedNonControl)
    769                 continue;
    770             QRect rect(x, y, width, height);
    771 
    772             if (!teletextmode && m_useBackground)
    773             {
    774                 MythUIShape *shape = new MythUIShape(this,
    775                     QString("cc608bg%1%2%3").arg(cc->x).arg(cc->y).arg(width));
    776                 shape->SetFillBrush(bgfill);
    777                 QRect bgrect(x - pad_width, y, width + pad_width, height);
    778                 shape->SetArea(MythRect(bgrect));
    779             }
    780 
    781             new MythUISimpleText(captionText, *gTextSubFont, rect,
    782                                  Qt::AlignLeft, (MythUIType*)this,
    783                                  QString("cc608txt%1%2%3%4")
    784                                      .arg(cc->x).arg(cc->y)
    785                                      .arg(width).arg(chunk));
    786 
    787             m_refreshArea = true;
    788 
    789             LOG(VB_VBI, LOG_INFO,
    790                 QString("x %1 y %2 uline=%4 ital=%5 "
    791                         "color=%6 coord=%7,%8 String: '%3'")
    792                 .arg(cc->x).arg(cc->y).arg(captionText)
    793                 .arg(isUnderline).arg(isItalic).arg(color).arg(x).arg(y));
    794         }
    795     }
     643    FormattedTextSubtitle fsub(m_safeArea);
     644    fsub.InitFromCC608(textlist->buffers);
     645    fsub.Draw(this);
     646    m_refreshArea = true;
    796647    textlist->lock.unlock();
    797648}
    798649
    MythFontProperties* SubtitleScreen::Get708Font(CC708CharacterAttribute attr) 
    11891040    return mythfont;
    11901041}
    11911042
     1043void FormattedTextChunk::ComputeSize(void)
     1044{
     1045    QFont *font = gTextSubFont->GetFace();
     1046    font->setItalic(isItalic);
     1047    font->setBold(isBold);
     1048    font->setUnderline(isUnderline);
     1049    QFontMetrics fm(*font);
     1050    width = fm.width(text) + fm.maxWidth() * PAD_WIDTH;
     1051    height = fm.height() * (1 + PAD_HEIGHT);
     1052}
     1053
     1054void FormattedTextSubtitle::InitFromCC608(vector<CC608Text*> &buffers)
     1055{
     1056    static const QColor clr[8] =
     1057    {
     1058        Qt::white,   Qt::green,   Qt::blue,    Qt::cyan,
     1059        Qt::red,     Qt::yellow,  Qt::magenta, Qt::white,
     1060    };
     1061
     1062    if (buffers.empty())
     1063        return;
     1064    vector<CC608Text*>::iterator i = buffers.begin();
     1065    bool teletextmode = (*i)->teletextmode;
     1066    m_useBackground = m_useBackground && !teletextmode;
     1067    int xscale = teletextmode ? 40 : 36;
     1068    int yscale = teletextmode ? 25 : 17;
     1069    m_pixelSize = m_safeArea.height() / (yscale * 1.2);
     1070
     1071    for (; i != buffers.end(); ++i)
     1072    {
     1073        CC608Text *cc = (*i);
     1074        int color = 0;
     1075        bool isItalic = false, isUnderline = false;
     1076        const bool isBold = false;
     1077        QString text(cc->text);
     1078
     1079        int orig_x = teletextmode ? cc->y : (cc->x + 3);
     1080        int x = (int)(((float)orig_x / (float)xscale) * (float)m_safeArea.width());
     1081        int orig_y = teletextmode ? cc->x : cc->y;
     1082        int y = (int)(((float)orig_y / (float)yscale) * (float)m_safeArea.height());
     1083        FormattedTextLine line(x, y);
     1084        for (int chunk = 0; text != QString::null; chunk++)
     1085        {
     1086            QString captionText =
     1087                extract_cc608(text, cc->teletextmode,
     1088                              color, isItalic, isUnderline);
     1089            FormattedTextChunk chunk(captionText,
     1090                                     isItalic, isBold, isUnderline,
     1091                                     clr[min(max(0, color), 7)],
     1092                                     orig_x, orig_y);
     1093            line.chunks += chunk;
     1094            LOG(VB_VBI, LOG_INFO,
     1095                QString("Adding cc608 chunk: x=%1 y=%2 "
     1096                        "uline=%3 ital=%4 color=%5 text='%6'")
     1097                .arg(cc->x).arg(cc->y)
     1098                .arg(isUnderline).arg(isItalic).arg(color).arg(captionText));
     1099        }
     1100        m_lines += line;
     1101    }
     1102}
     1103
     1104void FormattedTextSubtitle::InitFromSRT(QStringList &subs, int textFontZoom)
     1105{
     1106    // Does a simplistic parsing of HTML tags from the strings.
     1107    // Nesting is not implemented.  Stray whitespace may cause
     1108    // legitimate tags to be ignored.  All other HTML tags are
     1109    // stripped and ignored.
     1110    //
     1111    // <i> - enable italics
     1112    // </i> - disable italics
     1113    // <b> - enable boldface
     1114    // </b> - disable boldface
     1115    // <u> - enable underline
     1116    // </u> - disable underline
     1117    // <font color="#xxyyzz"> - change font color
     1118    // </font> - reset font color to white
     1119
     1120    m_pixelSize = (m_safeArea.height() * textFontZoom) / 1800;
     1121
     1122    bool isItalic = false;
     1123    bool isBold = false;
     1124    bool isUnderline = false;
     1125    QColor color(Qt::white);
     1126    QRegExp htmlTag("</?.+>");
     1127    QString htmlPrefix("<font color=\""), htmlSuffix("\">");
     1128    htmlTag.setMinimal(true);
     1129    foreach (QString subtitle, subs)
     1130    {
     1131        FormattedTextLine line;
     1132        QString text(subtitle);
     1133        while (!text.isEmpty())
     1134        {
     1135            int pos = text.indexOf(htmlTag);
     1136            if (pos != 0) // don't add a zero-length string
     1137            {
     1138                FormattedTextChunk chunk(text.left(pos),
     1139                                         isItalic, isBold, isUnderline,
     1140                                         color, -1, -1);
     1141                line.chunks += chunk;
     1142                text = (pos < 0 ? "" : text.mid(pos));
     1143                LOG(VB_VBI, LOG_INFO, QString("Adding SRT chunk '%1'").arg(chunk.text));
     1144            }
     1145            if (pos >= 0)
     1146            {
     1147                int htmlLen = htmlTag.matchedLength();
     1148                QString html = text.left(htmlLen).toLower();
     1149                text = text.mid(htmlLen);
     1150                if (html == "<i>")
     1151                    isItalic = true;
     1152                else if (html == "</i>")
     1153                    isItalic = false;
     1154                else if (html.startsWith(htmlPrefix) &&
     1155                         html.endsWith(htmlSuffix))
     1156                {
     1157                    int colorLen = html.length() -
     1158                        (htmlPrefix.length() + htmlSuffix.length());
     1159                    QString colorString(html.mid(htmlPrefix.length(), colorLen));
     1160                    QColor newColor(colorString);
     1161                    if (newColor.isValid())
     1162                        color = newColor;
     1163                    else
     1164                        LOG(VB_VBI, LOG_INFO,
     1165                            QString("Ignoring invalid SRT color specification: "
     1166                                    "'%1'").arg(colorString));
     1167                }
     1168                else if (html == "</font>")
     1169                    color = Qt::white;
     1170                else if (html == "<b>")
     1171                    isBold = true;
     1172                else if (html == "</b>")
     1173                    isBold = false;
     1174                else if (html == "<u>")
     1175                    isUnderline = true;
     1176                else if (html == "</u>")
     1177                    isUnderline = false;
     1178                else
     1179                    LOG(VB_VBI, LOG_INFO,
     1180                        QString("Ignoring unknown SRT formatting: '%1'").arg(html));
     1181
     1182                LOG(VB_VBI, LOG_INFO,
     1183                    QString("SRT formatting change '%1', "
     1184                            "new ital=%2 bold=%3 uline=%4 color=(%5,%6,%7)")
     1185                    .arg(html).arg(isItalic).arg(isBold).arg(isUnderline)
     1186                    .arg(color.red(),   2, 16, QLatin1Char('0'))
     1187                    .arg(color.green(), 2, 16, QLatin1Char('0'))
     1188                    .arg(color.blue(),  2, 16, QLatin1Char('0')));
     1189            }
     1190        }
     1191        m_lines += line;
     1192    }
     1193}
     1194
     1195bool FormattedTextChunk::Split(FormattedTextChunk &newChunk)
     1196{
     1197    LOG(VB_VBI, LOG_INFO,
     1198        QString("Attempting to split chunk '%1'").arg(text));
     1199    int lastSpace = text.lastIndexOf(' ', -2); // -2 to ignore trailing space
     1200    if (lastSpace < 0)
     1201    {
     1202        LOG(VB_VBI, LOG_INFO,
     1203            QString("Failed to split chunk '%1'").arg(text));
     1204        return false;
     1205    }
     1206    newChunk.isItalic = isItalic;
     1207    newChunk.isBold = isBold;
     1208    newChunk.isUnderline = isUnderline;
     1209    newChunk.color = color;
     1210    newChunk.debug_x = debug_x;
     1211    newChunk.debug_y = debug_y;
     1212    newChunk.text = text.mid(lastSpace + 1).trimmed() + ' ';
     1213    text = text.left(lastSpace).trimmed();
     1214    newChunk.ComputeSize();
     1215    ComputeSize();
     1216    LOG(VB_VBI, LOG_INFO,
     1217        QString("Split chunk into '%1' + '%2'").arg(text).arg(newChunk.text));
     1218    return true;
     1219}
     1220
     1221void FormattedTextSubtitle::WrapLongLines(void)
     1222{
     1223    int maxWidth = m_safeArea.width();
     1224    QList<FormattedTextLine>::iterator i = m_lines.begin();
     1225    while (i != m_lines.end())
     1226    {
     1227        int width = (*i).Width();
     1228        // Move entire chunks to the next line as necessary.  Leave at
     1229        // least one chunk on the current line.
     1230        while (width > maxWidth && (*i).chunks.size() > 1)
     1231        {
     1232            width -= (*i).chunks.back().width;
     1233            // Make sure there's a next line to wrap into.
     1234            if (i + 1 == m_lines.end())
     1235                m_lines += FormattedTextLine((*i).x_indent, (*i).y_indent);
     1236            (*(i+1)).chunks.prepend((*i).chunks.takeLast());
     1237            LOG(VB_VBI, LOG_INFO,
     1238                QString("Wrapping chunk to next line: '%1'")
     1239                .arg((*(i+1)).chunks[0].text));
     1240        }
     1241        // Split the last chunk until width is small enough or until
     1242        // no more splits are possible.
     1243        bool isSplitPossible = true;
     1244        while (width > maxWidth && isSplitPossible)
     1245        {
     1246            FormattedTextChunk newChunk;
     1247            isSplitPossible = (*i).chunks.back().Split(newChunk);
     1248            if (isSplitPossible)
     1249            {
     1250                // Make sure there's a next line to split into.
     1251                if (i + 1 == m_lines.end())
     1252                    m_lines += FormattedTextLine((*i).x_indent, (*i).y_indent);
     1253                (*(i+1)).chunks.prepend(newChunk);
     1254                width = (*i).Width();
     1255            }
     1256        }
     1257        ++i;
     1258    }
     1259}
     1260
     1261void FormattedTextSubtitle::Draw(SubtitleScreen *parent,
     1262                                 uint64_t start, uint64_t duration) const
     1263{
     1264    bool useBackground = m_useBackground && parent->GetUseBackground();
     1265    gTextSubFont->GetFace()->setPixelSize(m_pixelSize);
     1266    QFontMetrics font(*(gTextSubFont->GetFace()));
     1267    int pad_width = font.maxWidth() * PAD_WIDTH;
     1268    QBrush bgfill = QBrush(QColor(0, 0, 0), Qt::SolidPattern);
     1269
     1270    int lineNum = 0;
     1271    QList<FormattedTextLine>::const_iterator line = m_lines.constBegin();
     1272    for (; line != m_lines.constEnd(); ++line, ++lineNum)
     1273    {
     1274        int x = (*line).x_indent;
     1275        if (x < 0) // centering
     1276            x = (m_safeArea.width() - (*line).Width()) / 2;
     1277        int y = (*line).y_indent;
     1278        if (y < 0) // stack lines at bottom
     1279            y = m_safeArea.height() -
     1280                (m_lines.size() - lineNum) * (*line).Height();
     1281
     1282        if (useBackground)
     1283        {
     1284            QRect bgrect(x - pad_width, y,
     1285                         (*line).Width() + pad_width, (*line).Height());
     1286            // Special case.  If the first chunk is entirely
     1287            // whitespace, don't put a black background behind that
     1288            // chunk.
     1289            //
     1290            // This often happens when a line of cc608 caption text
     1291            // begins with a mid-row format control like italics or a
     1292            // color change.  The spec says the mid-row control
     1293            // implies a space character, which needs to be preserved
     1294            // so that the rest of the text is accurately laid out.  A
     1295            // leading space looks clumsy against the black
     1296            // background, so we adjust the background accordingly.
     1297            if (!(*line).chunks.isEmpty() &&
     1298                (*line).chunks[0].text.trimmed().isEmpty())
     1299            {
     1300                bgrect.setLeft(bgrect.left() + (*line).chunks[0].width);
     1301            }
     1302            MythUIShape *shape =
     1303                new MythUIShape(parent, QString("subbg%1_%2").arg(x).arg(y));
     1304            shape->SetFillBrush(bgfill);
     1305            shape->SetArea(MythRect(bgrect));
     1306            if (duration > 0)
     1307                parent->RegisterExpiration(shape, start + duration);
     1308        }
     1309        QList<FormattedTextChunk>::const_iterator chunk;
     1310        for (chunk = (*line).chunks.constBegin();
     1311             chunk != (*line).chunks.constEnd();
     1312             ++chunk)
     1313        {
     1314            // If the chunk starts with whitespace, the leading
     1315            // whitespace ultimately gets lost due to the
     1316            // text.trimmed() operation in the MythUISimpleText
     1317            // constructor.  To compensate, we manually indent the
     1318            // chunk accordingly.
     1319            int count = 0;
     1320            while (count < (*chunk).text.length() && (*chunk).text.at(count) == ' ')
     1321                ++count;
     1322            int x_adjust = count * font.width(" ");
     1323            gTextSubFont->GetFace()->setItalic((*chunk).isItalic);
     1324            gTextSubFont->GetFace()->setBold((*chunk).isBold);
     1325            gTextSubFont->GetFace()->setUnderline((*chunk).isUnderline);
     1326            gTextSubFont->SetColor((*chunk).color);
     1327            QRect rect(x + x_adjust, y, (*chunk).width - x_adjust, (*chunk).height);
     1328
     1329            MythUISimpleText *text =
     1330                new MythUISimpleText((*chunk).text, *gTextSubFont, rect,
     1331                                     Qt::AlignLeft, (MythUIType*)parent,
     1332                                     QString("subtxt%1x%2@%3,%4")
     1333                                     .arg((*chunk).width).arg((*chunk).height)
     1334                                     .arg(x).arg(y));
     1335            if (duration > 0)
     1336                parent->RegisterExpiration(text, start + duration);
     1337
     1338            LOG(VB_VBI, LOG_INFO,
     1339                QString("Drawing chunk at (%1,%2) with "
     1340                        "ital=%3 bold=%4 uline=%5 color=(%6,%7,%8) "
     1341                        "text='%9'")
     1342                .arg(x).arg(y)
     1343                .arg((*chunk).isItalic)
     1344                .arg((*chunk).isBold)
     1345                .arg((*chunk).isUnderline)
     1346                .arg((*chunk).color.red(),   2, 16, QLatin1Char('0'))
     1347                .arg((*chunk).color.green(), 2, 16, QLatin1Char('0'))
     1348                .arg((*chunk).color.blue(),  2, 16, QLatin1Char('0'))
     1349                .arg((*chunk).text));
     1350
     1351            x += (*chunk).width;
     1352        }
     1353    }
     1354}
     1355
    11921356#ifdef USING_LIBASS
    11931357static void myth_libass_log(int level, const char *fmt, va_list vl, void *ctx)
    11941358{
  • mythtv/libs/libmythtv/subtitlescreen.h

    diff --git a/mythtv/libs/libmythtv/subtitlescreen.h b/mythtv/libs/libmythtv/subtitlescreen.h
    index f233220..7b72e42 100644
    a b class SubtitleScreen : public MythScreenType 
    3232    void ExpireSubtitles(void);
    3333    void DisplayDVDButton(AVSubtitle* dvdButton, QRect &buttonPos);
    3434
     35    void RegisterExpiration(MythUIType *shape, long long endTime) {
     36        m_expireTimes.insert(shape, endTime);
     37    }
     38
     39    bool GetUseBackground(void) { return m_useBackground; }
     40
    3541    // MythScreenType methods
    3642    virtual bool Create(void);
    3743    virtual void Pulse(void);
    class SubtitleScreen : public MythScreenType 
    4147    void DisplayAVSubtitles(void);
    4248    void DisplayTextSubtitles(void);
    4349    void DisplayRawTextSubtitles(void);
    44     void OptimiseTextSubs(QStringList &list);
    4550    void DrawTextSubtitles(QStringList &wrappedsubs, uint64_t start,
    4651                           uint64_t duration);
    4752    void DisplayCC608Subtitles(void);
    class SubtitleScreen : public MythScreenType 
    8590#endif // USING_LIBASS
    8691};
    8792
     93struct FormattedTextChunk
     94{
     95    FormattedTextChunk(const QString &t, bool ital, bool bold, bool uline,
     96                       QColor clr, int dx, int dy)
     97        : text(t), isItalic(ital), isBold(bold), isUnderline(uline),
     98          color(clr), debug_x(dx), debug_y(dy) {
     99        ComputeSize();
     100    }
     101    FormattedTextChunk(void) {}
     102    void ComputeSize(void);
     103    bool Split(FormattedTextChunk &newChunk);
     104    QString text;
     105    bool isItalic;
     106    bool isBold;
     107    bool isUnderline;
     108    QColor color;
     109    int width;
     110    int height;
     111    int debug_x, debug_y;
     112};
     113
     114struct FormattedTextLine
     115{
     116    FormattedTextLine(int x = -1, int y = -1) : x_indent(x), y_indent(y) {}
     117    int Height(void) const {
     118        int result = 0;
     119        QList<FormattedTextChunk>::const_iterator it;
     120        for (it = chunks.constBegin(); it != chunks.constEnd(); ++it)
     121            result = max(result, (*it).height);
     122        return result;
     123    }
     124    int Width(void) const {
     125        int result = 0;
     126        QList<FormattedTextChunk>::const_iterator it;
     127        for (it = chunks.constBegin(); it != chunks.constEnd(); ++it)
     128            result += (*it).width;
     129        return result;
     130    }
     131    QList<FormattedTextChunk> chunks;
     132    int x_indent; // -1 means TBD i.e. centered
     133    int y_indent; // -1 means TBD i.e. relative to bottom
     134};
     135
     136class FormattedTextSubtitle
     137{
     138public:
     139    FormattedTextSubtitle(const QRect &safearea)
     140        : m_safeArea(safearea), m_useBackground(true) {}
     141    void InitFromCC608(vector<CC608Text*> &buffers);
     142    void InitFromSRT(QStringList &subs, int textFontZoom);
     143    void WrapLongLines(void);
     144    void Draw(SubtitleScreen *parent,
     145              uint64_t start = 0, uint64_t duration = 0) const;
     146private:
     147    QList<FormattedTextLine> m_lines;
     148    const QRect m_safeArea;
     149    bool m_useBackground;
     150    int m_pixelSize;
     151};
     152
    88153#endif // SUBTITLESCREEN_H