File indexing completed on 2024-06-16 04:38:18

0001 /*
0002     SPDX-FileCopyrightText: 2007-2009 Sergio Pistone <sergio_pistone@yahoo.com.ar>
0003     SPDX-FileCopyrightText: 2010-2022 Mladen Milinkovic <max@smoothware.net>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #ifndef MICRODVDINPUTFORMAT_H
0009 #define MICRODVDINPUTFORMAT_H
0010 
0011 #include "core/richtext/richdocument.h"
0012 #include "helpers/common.h"
0013 #include "formats/inputformat.h"
0014 
0015 #include <QRegularExpression>
0016 #include <QStringBuilder>
0017 
0018 namespace SubtitleComposer {
0019 class MicroDVDInputFormat : public InputFormat
0020 {
0021     friend class FormatManager;
0022 
0023 protected:
0024     MicroDVDInputFormat()
0025         : InputFormat($("MicroDVD"), QStringList() << $("sub") << $("txt"))
0026     {}
0027 
0028     bool parseSubtitles(Subtitle &subtitle, const QString &data) const override
0029     {
0030         staticRE$(lineRE, "\\{(\\d+)\\}\\{(\\d+)\\}([^\n]+)\n", REu | REi);
0031         staticRE$(styleRE, "\\{([yc]):([^}]*)\\}", REu | REi);
0032 
0033         QRegularExpressionMatchIterator itLine = lineRE.globalMatch(data);
0034         if(!itLine.hasNext())
0035             return false; // couldn't find first line (content or FPS)
0036 
0037         QRegularExpressionMatch mLine = itLine.next();
0038 
0039         // if present, the FPS must by indicated by the first entry with both initial and final frames at 1
0040         bool ok;
0041         double fps = mLine.captured(3).toDouble(&ok);
0042         if(ok && mLine.captured(1) == QLatin1String("1") && mLine.captured(2) == QLatin1String("1")) {
0043             // first line contained the frames per second
0044             subtitle.setFramesPerSecond(fps);
0045         } else {
0046             // first line doesn't contain the FPS, use the value loaded by default
0047             fps = subtitle.framesPerSecond();
0048         }
0049 
0050         if(!itLine.hasNext())
0051             return false;
0052 
0053         do {
0054             mLine = itLine.next();
0055 
0056             Time showTime(static_cast<long>((mLine.captured(1).toLong() / fps) * 1000));
0057             Time hideTime(static_cast<long>((mLine.captured(2).toLong() / fps) * 1000));
0058 
0059             RichString richText;
0060 
0061             const QString text = mLine.captured(3);
0062             QRegularExpressionMatchIterator itText = styleRE.globalMatch(text);
0063 
0064             int globalStyle = 0, currentStyle = 0;
0065             QRgb globalColor = 0, currentColor = 0;
0066             int offsetPos = 0;
0067             while(itText.hasNext()) {
0068                 QRegularExpressionMatch mText = itText.next();
0069                 QString tag(mText.captured(1)), val(mText.captured(2).toLower());
0070 
0071                 int newStyle = currentStyle;
0072                 QRgb newColor = currentColor;
0073 
0074                 if(tag == QChar('Y')) {
0075                     globalStyle = 0;
0076                     if(val.contains('b'))
0077                         globalStyle |= RichString::Bold;
0078                     if(val.contains('i'))
0079                         globalStyle |= RichString::Italic;
0080                     if(val.contains('u'))
0081                         globalStyle |= RichString::Underline;
0082                 } else if(tag == QLatin1String("C")) {
0083                     globalColor = val.length() != 7 ? 0 : QColor(QChar('#') % val.mid(5, 2) % val.mid(3, 2) % val.mid(1, 2)).rgb();
0084                 } else if(tag == QLatin1String("y")) {
0085                     newStyle = 0;
0086                     if(val.contains('b'))
0087                         newStyle |= RichString::Bold;
0088                     if(val.contains('i'))
0089                         newStyle |= RichString::Italic;
0090                     if(val.contains('u'))
0091                         newStyle |= RichString::Underline;
0092                 } else if(tag == QLatin1String("c")) {
0093                     newColor = val.length() != 7 ? 0 : QColor(QChar('#') % val.mid(5, 2) % val.mid(3, 2) % val.mid(1, 2)).rgb();
0094                 }
0095 
0096                 if(newStyle != currentStyle || currentColor != newColor) {
0097                     QString token(text.mid(offsetPos, mText.capturedStart() - offsetPos));
0098                     richText += RichString(token, currentStyle | (currentColor == 0 ? 0 : RichString::Color), currentColor);
0099                     currentStyle = newStyle;
0100                     currentColor = newColor;
0101                 }
0102 
0103                 offsetPos = mText.capturedEnd();
0104             }
0105 
0106             QString token(text.mid(offsetPos));
0107             richText += RichString(token, currentStyle | (currentColor == 0 ? 0 : RichString::Color), currentColor);
0108 
0109             if(globalColor != 0)
0110                 globalStyle |= RichString::Color;
0111             if(globalStyle != 0) {
0112                 for(int i = 0, sz = richText.length(); i < sz; i++) {
0113                     if(richText.styleFlagsAt(i) == 0) {
0114                         richText.setStyleFlagsAt(i, globalStyle);
0115                         richText.setStyleColorAt(i, globalColor);
0116                     }
0117                 }
0118             }
0119 
0120             SubtitleLine *l = new SubtitleLine(showTime, hideTime);
0121             l->primaryDoc()->setRichText(richText.replace('|', '\n'), true);
0122             subtitle.insertLine(l);
0123         } while(itLine.hasNext());
0124 
0125         return true;
0126     }
0127 };
0128 }
0129 
0130 #endif