File indexing completed on 2024-12-22 04:40:08
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 SUBSTATIONALPHAINPUTFORMAT_H 0009 #define SUBSTATIONALPHAINPUTFORMAT_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 #include <QDebug> 0018 0019 namespace SubtitleComposer { 0020 class SubStationAlphaInputFormat : public InputFormat 0021 { 0022 friend class FormatManager; 0023 friend class AdvancedSubStationAlphaInputFormat; 0024 0025 protected: 0026 RichString toRichString(const QString &string) const 0027 { 0028 staticRE$(reCommands, "\\{([^\\}]+)\\}", REu); 0029 0030 RichString ret; 0031 0032 int currentStyle = 0; 0033 QRgb currentColor = 0; 0034 0035 QRegularExpressionMatchIterator itCommands = reCommands.globalMatch(string); 0036 int offset = 0; 0037 while(itCommands.hasNext()) { 0038 QRegularExpressionMatch mCommands = itCommands.next(); 0039 int newStyleFlags = currentStyle; 0040 QRgb newColor = currentColor; 0041 0042 QString commands(mCommands.captured(1)); 0043 QStringList commandsList(commands.split('\\')); 0044 for(QStringList::ConstIterator it = commandsList.constBegin(), end = commandsList.constEnd(); it != end; ++it) { 0045 if(it->isEmpty()) { 0046 continue; 0047 } else if(*it == QLatin1String("i0")) { 0048 newStyleFlags &= ~RichString::Italic; 0049 } else if(*it == QLatin1String("b0")) { 0050 newStyleFlags &= ~RichString::Bold; 0051 } else if(*it == QLatin1String("u0")) { 0052 newStyleFlags &= ~RichString::Underline; 0053 } else if(*it == QLatin1String("i1")) { 0054 newStyleFlags |= RichString::Italic; 0055 } else if(it->at(0) == 'b') { 0056 // it's usually followed 1, but can be weight of the font: 400, 700, ... 0057 newStyleFlags |= RichString::Bold; 0058 } else if(*it == QLatin1String("u1")) { 0059 newStyleFlags |= RichString::Underline; 0060 } else if(it->at(0) == 'c') { 0061 QString val = ($("000000") + it->mid(3).chopped(1)).right(6); 0062 if(val == QLatin1String("000000")) { 0063 newStyleFlags &= ~RichString::Color; 0064 newColor = 0; 0065 } else { 0066 newStyleFlags |= RichString::Color; 0067 newColor = QColor(QChar('#') % val.mid(4, 2) % val.mid(2, 2) % val.mid(0, 2)).rgb(); 0068 } 0069 } 0070 } 0071 0072 const QString text = string.mid(offset, mCommands.capturedStart() - offset) 0073 .replace(QLatin1String("\\N"), QLatin1String("\n"), Qt::CaseInsensitive); 0074 ret.append(RichString(text, currentStyle, currentColor)); 0075 0076 currentStyle = newStyleFlags; 0077 currentColor = newColor; 0078 offset = mCommands.capturedEnd(); 0079 } 0080 const QString text = string.mid(offset) 0081 .replace(QLatin1String("\\N"), QLatin1String("\n"), Qt::CaseInsensitive); 0082 ret.append(RichString(text, currentStyle, currentColor)); 0083 0084 return ret; 0085 } 0086 0087 bool parseSubtitles(Subtitle &subtitle, const QString &data) const override 0088 { 0089 staticRE$(reScriptInfo, "^ *\\[Script Info\\] *[\r\n]+", REu); 0090 if(!reScriptInfo.globalMatch(data).hasNext()) 0091 return false; 0092 0093 staticRE$(reStyles, "[\r\n]+ *\\[[vV]4\\+? Styles\\] *[\r\n]+", REu); 0094 QRegularExpressionMatchIterator itStyles = reStyles.globalMatch(data); 0095 if(!itStyles.hasNext()) 0096 return false; 0097 const int stylesStart = itStyles.next().capturedStart(); 0098 0099 FormatData formatData = createFormatData(); 0100 0101 formatData.setValue($("ScriptInfo"), data.mid(0, stylesStart)); 0102 0103 staticRE$(reEvents, "[\r\n]+ *\\[Events\\] *[\r\n]+", REu); 0104 QRegularExpressionMatchIterator itEvents = reEvents.globalMatch(data, stylesStart); 0105 if(!itEvents.hasNext()) 0106 return false; 0107 int eventsStart = itEvents.next().capturedStart(); 0108 0109 formatData.setValue($("Styles"), data.mid(stylesStart, eventsStart - stylesStart)); 0110 0111 staticRE$(reFormat, " *Format: *(\\w+,? *)+[\r\n]+", REu); 0112 QRegularExpressionMatchIterator itFormat = reFormat.globalMatch(data, eventsStart); 0113 if(!itFormat.hasNext()) 0114 return false; 0115 0116 setFormatData(subtitle, &formatData); 0117 formatData.clear(); 0118 0119 staticRE$(reDialogue, " *Dialogue: *[^,]+, *([^,]+), *([^,]+), *[^,]*, *[^,]*, *[^,]*, *[^,]*, *[^,]*, *[^,]*, *([^\r\n]*)[\r\n]+", REu); 0120 staticRE$(reDialogueData, " *(Dialogue: *[^,]+, *)[^,]+(, *)[^,]+(, *[^,]+, *[^,]*, *[^,]*, *[^,]*, *[^,]*, *[^,]*, *).*", REu); 0121 staticRE$(reTime, "(\\d+):(\\d+):(\\d+).(\\d+)", REu); 0122 0123 do { 0124 QRegularExpressionMatch mFormat = itFormat.next(); 0125 QRegularExpressionMatchIterator itDialogue = reDialogue.globalMatch(data, mFormat.capturedEnd()); 0126 while(itDialogue.hasNext()) { 0127 QRegularExpressionMatch mDialogue = itDialogue.next(); 0128 0129 QRegularExpressionMatchIterator itTime = reTime.globalMatch(mDialogue.captured(1)); 0130 if(!itTime.hasNext()) { 0131 qWarning() << "SubStationAlpha failed to match showTime"; 0132 break; 0133 } 0134 QRegularExpressionMatch mTime = itTime.next(); 0135 Time showTime(mTime.captured(1).toInt(), mTime.captured(2).toInt(), mTime.captured(3).toInt(), mTime.captured(4).toInt() * 10); 0136 0137 itTime = reTime.globalMatch(mDialogue.captured(2)); 0138 if(!itTime.hasNext()) { 0139 qWarning() << "SubStationAlpha failed to match hideTime"; 0140 break; 0141 } 0142 mTime = itTime.next(); 0143 Time hideTime(mTime.captured(1).toInt(), mTime.captured(2).toInt(), mTime.captured(3).toInt(), mTime.captured(4).toInt() * 10); 0144 0145 SubtitleLine *line = new SubtitleLine(showTime, hideTime); 0146 line->primaryDoc()->setRichText(toRichString(mDialogue.captured(3)), true); 0147 0148 formatData.setValue($("Dialogue"), mDialogue.captured(0).replace(reDialogueData, $("\\1%1\\2%2\\3%3\n"))); 0149 setFormatData(line, &formatData); 0150 0151 subtitle.insertLine(line); 0152 } 0153 } while(itFormat.hasNext()); 0154 0155 return true; 0156 } 0157 0158 SubStationAlphaInputFormat( 0159 const QString &name = $("SubStation Alpha"), 0160 const QStringList &extensions = QStringList($("ssa"))) 0161 : InputFormat(name, extensions) 0162 {} 0163 }; 0164 0165 class AdvancedSubStationAlphaInputFormat : public SubStationAlphaInputFormat 0166 { 0167 friend class FormatManager; 0168 0169 protected: 0170 AdvancedSubStationAlphaInputFormat() 0171 : SubStationAlphaInputFormat($("Advanced SubStation Alpha"), QStringList($("ass"))) 0172 {} 0173 }; 0174 0175 } 0176 0177 #endif