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 SUBSTATIONALPHAOUTPUTFORMAT_H
0009 #define SUBSTATIONALPHAOUTPUTFORMAT_H
0010 
0011 #include "formats/outputformat.h"
0012 #include "core/formatdata.h"
0013 #include "core/richtext/richdocument.h"
0014 #include "core/subtitleiterator.h"
0015 
0016 namespace SubtitleComposer {
0017 class SubStationAlphaOutputFormat : public OutputFormat
0018 {
0019     friend class FormatManager;
0020 
0021 public:
0022     QString fromRichString(const RichString &text) const
0023     {
0024 
0025         QString subtitle;
0026 
0027         int prevStyle = 0;
0028         QRgb prevColor = 0;
0029         for(int i = 0, sz = text.length(); i < sz; i++) {
0030             int curStyle = text.styleFlagsAt(i);
0031             QRgb curColor = (curStyle & RichString::Color) != 0 ? text.styleColorAt(i) : 0;
0032             curStyle &= RichString::Bold | RichString::Italic | RichString::Underline;
0033             if(prevStyle != curStyle) {
0034                 int diff = curStyle ^ prevStyle;
0035                 subtitle += '{';
0036                 if(diff & RichString::Bold)
0037                     subtitle += curStyle & RichString::Bold ? QStringLiteral("\\b1") : QStringLiteral("\\b0");
0038                 if(diff & RichString::Italic)
0039                     subtitle += curStyle & RichString::Italic ? QStringLiteral("\\i1") : QStringLiteral("\\i0");
0040                 if(diff & RichString::Underline)
0041                     subtitle += curStyle & RichString::Underline ? QStringLiteral("\\u1") : QStringLiteral("\\u0");
0042                 subtitle += "}";
0043             }
0044             if(prevColor != curColor) {
0045                 subtitle += QStringLiteral("{\\c&H");
0046                 if(curColor != 0) {
0047                     subtitle += (QChar('0') + QString::number(qBlue(curColor), 16)).toUpper().right(2);
0048                     subtitle += (QChar('0') + QString::number(qGreen(curColor), 16)).toUpper().right(2);
0049                     subtitle += (QChar('0') + QString::number(qRed(curColor), 16)).toUpper().right(2);
0050                 } else {
0051                     subtitle += QStringLiteral("00");
0052                 }
0053                 subtitle += QStringLiteral("&}");
0054             }
0055 
0056             subtitle += text.at(i);
0057 
0058             prevStyle = curStyle;
0059             prevColor = curColor;
0060         }
0061 
0062         if(prevStyle) {
0063             subtitle +='{';
0064             if(prevStyle & RichString::Bold)
0065                 subtitle += QStringLiteral("\\b0");
0066             if(prevStyle & RichString::Italic)
0067                 subtitle += QStringLiteral("\\i0");
0068             if(prevStyle & RichString::Underline)
0069                 subtitle += QStringLiteral("\\u0");
0070             subtitle += '}';
0071         }
0072 
0073         subtitle = subtitle.replace(QStringLiteral("\r\n"), QStringLiteral("\\N"));
0074         subtitle = subtitle.replace(QStringLiteral("\n"), QStringLiteral("\\N"));
0075         subtitle = subtitle.replace(QStringLiteral("\r"), QStringLiteral("\\N"));
0076 
0077         return subtitle;
0078     }
0079 
0080 protected:
0081     static QString normalizeBlock(const QString &data) {
0082         int begin = 0, end = data.length() - 1;
0083 
0084         while(data.at(begin).isSpace() && begin < end)
0085             begin++;
0086 
0087         while(data.at(end).isSpace() && end >= begin)
0088             end--;
0089 
0090         return data.mid(begin, end - begin + 1) + QStringLiteral("\n\n");
0091     }
0092 
0093     QString dumpSubtitles(const Subtitle &subtitle, bool primary) const override
0094     {
0095         FormatData *formatData = this->formatData(subtitle);
0096 
0097         QString ret = normalizeBlock(formatData ? formatData->value(QStringLiteral("ScriptInfo")) : m_defaultScriptInfo)
0098                 + normalizeBlock(formatData ? formatData->value(QStringLiteral("Styles")) : m_defaultStyles)
0099                 + normalizeBlock(m_events);
0100 
0101         for(SubtitleIterator it(subtitle); it.current(); ++it) {
0102             const SubtitleLine *line = it.current();
0103 
0104             const Time showTime = line->showTime();
0105             const QString showTimeArg = QString::asprintf("%01d:%02d:%02d.%02d",
0106                                                   showTime.hours(),
0107                                                   showTime.minutes(),
0108                                                   showTime.seconds(),
0109                                                   (showTime.millis() + 5) / 10);
0110 
0111             const Time hideTime = line->hideTime();
0112             const QString hideTimeArg = QString::asprintf("%01d:%02d:%02d.%02d",
0113                                                   hideTime.hours(),
0114                                                   hideTime.minutes(),
0115                                                   hideTime.seconds(),
0116                                                   (hideTime.millis() + 5) / 10);
0117 
0118             formatData = this->formatData(line);
0119 
0120             RichString stext = (primary ? line->primaryDoc() : line->secondaryDoc())->toRichText();
0121             ret += QString(formatData ? formatData->value(QStringLiteral("Dialogue")) : m_dialogueBuilder)
0122                     .arg(showTimeArg, hideTimeArg, fromRichString(stext));
0123         }
0124         return ret;
0125     }
0126 
0127     SubStationAlphaOutputFormat(
0128             const QString &name = QStringLiteral("SubStation Alpha"),
0129             const QStringList &extensions = QStringList(QStringLiteral("ssa")),
0130             const QString &scriptInfo = s_defaultScriptInfo,
0131             const QString &styles = s_defaultStyles,
0132             const QString &events = s_events,
0133             const QString &dialogueBuilder = s_dialogueBuilder) :
0134         OutputFormat(name, extensions),
0135         m_defaultScriptInfo(scriptInfo),
0136         m_defaultStyles(styles),
0137         m_events(events),
0138         m_dialogueBuilder(dialogueBuilder)
0139     {}
0140 
0141     const QString m_defaultScriptInfo;
0142     const QString m_defaultStyles;
0143     const QString m_events;
0144     const QString m_dialogueBuilder;
0145     const QString m_textBuilder;
0146 
0147     static const char *s_defaultScriptInfo;
0148     static const char *s_defaultStyles;
0149     static const char *s_events;
0150     static const char *s_dialogueBuilder;
0151 };
0152 
0153 const char *SubStationAlphaOutputFormat::s_defaultScriptInfo = "[Script Info]\n"
0154         "Title: Untitled\n"
0155         "ScriptType: V4.00\n"
0156         "Collisions: Normal\n"
0157         "Timer: 100\n"
0158         "WrapStyle: 0\n";
0159 const char *SubStationAlphaOutputFormat::s_defaultStyles = "[V4 Styles]\n"
0160         "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, "
0161         "Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding\n"
0162         "Style: Default, Sans, 24, 16777215, 16777215, 16777215, 12632256, -1, 0, 1, 1, 1, 6, 30, 30, 415, 0, 0\n";
0163 const char *SubStationAlphaOutputFormat::s_events = "[Events]\n"
0164         "Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n";
0165 const char *SubStationAlphaOutputFormat::s_dialogueBuilder = "Dialogue: Marked=0,%1,%2,Default,,0000,0000,0000,,%3\n";
0166 
0167 class AdvancedSubStationAlphaOutputFormat : public SubStationAlphaOutputFormat
0168 {
0169     friend class FormatManager;
0170 
0171 protected:
0172     AdvancedSubStationAlphaOutputFormat() :
0173         SubStationAlphaOutputFormat(QStringLiteral("Advanced SubStation Alpha"), QStringList(QStringLiteral("ass")), s_defaultScriptInfo, s_defaultStyles, s_events, s_dialogueBuilder)
0174     {}
0175 
0176     static const char *s_defaultScriptInfo;
0177     static const char *s_defaultStyles;
0178     static const char *s_events;
0179     static const char *s_dialogueBuilder;
0180 };
0181 
0182 const char *AdvancedSubStationAlphaOutputFormat::s_defaultScriptInfo = "[Script Info]\n"
0183         "Title: Untitled\n"
0184         "ScriptType: V4.00+\n"
0185         "Collisions: Normal\n"
0186         "Timer: 100\n"
0187         "WrapStyle: 0\n";
0188 const char *AdvancedSubStationAlphaOutputFormat::s_defaultStyles = "[V4+ Styles]\n"
0189         "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeThrough, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n"
0190         "Style: Default, Sans, 16, &H00FFFFFF, &H00FFFFFF, &H00674436, &HFFFFFFF8, -1, 0, 0, 0, 100, 90, 0, 0, 1, 2.1, 0.5, 2, 15, 15, 15, 0\n";
0191 const char *AdvancedSubStationAlphaOutputFormat::s_events = "[Events]\n"
0192         "Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text\n";
0193 const char *AdvancedSubStationAlphaOutputFormat::s_dialogueBuilder = "Dialogue: 0,%1,%2,Default,,0000,0000,0000,,%3\n";
0194 }
0195 
0196 #endif