Add a separate SSA subtitle format and move all of the SSA writing stuff there

This commit is contained in:
Thomas Goyne 2014-04-29 09:33:22 -07:00
parent 31af9c575f
commit 1eba2f035c
17 changed files with 161 additions and 113 deletions

View file

@ -212,6 +212,7 @@
<ClInclude Include="$(SrcDir)subtitle_format_microdvd.h" />
<ClInclude Include="$(SrcDir)subtitle_format_mkv.h" />
<ClInclude Include="$(SrcDir)subtitle_format_srt.h" />
<ClInclude Include="$(SrcDir)subtitle_format_ssa.h" />
<ClInclude Include="$(SrcDir)subtitle_format_transtation.h" />
<ClInclude Include="$(SrcDir)subtitle_format_ttxt.h" />
<ClInclude Include="$(SrcDir)subtitle_format_txt.h" />
@ -404,6 +405,7 @@
<ClCompile Include="$(SrcDir)subtitle_format_microdvd.cpp" />
<ClCompile Include="$(SrcDir)subtitle_format_mkv.cpp" />
<ClCompile Include="$(SrcDir)subtitle_format_srt.cpp" />
<ClCompile Include="$(SrcDir)subtitle_format_ssa.cpp" />
<ClCompile Include="$(SrcDir)subtitle_format_transtation.cpp" />
<ClCompile Include="$(SrcDir)subtitle_format_ttxt.cpp" />
<ClCompile Include="$(SrcDir)subtitle_format_txt.cpp" />

View file

@ -618,6 +618,9 @@
<ClInclude Include="$(SrcDir)grid_column.h">
<Filter>Main UI\Grid</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)subtitle_format_ssa.h">
<Filter>Subtitle formats</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(SrcDir)ass_dialogue.cpp">
@ -1169,13 +1172,12 @@
<ClCompile Include="$(SrcDir)grid_column.cpp">
<Filter>Main UI\Grid</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)subtitle_format_ssa.cpp">
<Filter>Subtitle formats</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="$(SrcDir)res/res.rc">
<Filter>Resources</Filter>
</ResourceCompile>
<ResourceCompile Include="$(SrcDir)res/strings.rc">
<Filter>Resources</Filter>
</ResourceCompile>
<ResourceCompile Include="$(SrcDir)res\res.rc" />
<ResourceCompile Include="$(SrcDir)res\strings.rc" />
</ItemGroup>
</Project>

View file

@ -223,6 +223,7 @@ SRC += \
subtitle_format_microdvd.cpp \
subtitle_format_mkv.cpp \
subtitle_format_srt.cpp \
subtitle_format_ssa.cpp \
subtitle_format_transtation.cpp \
subtitle_format_ttxt.cpp \
subtitle_format_txt.cpp \

View file

@ -47,7 +47,6 @@ public:
std::string GetFileName(bool raw=false) const;
std::string const& GetEntryData() const { return entry_data; }
std::string const& GetSSAText() const { return entry_data; }
AssEntryGroup Group() const override { return group; }
AssAttachment(AssAttachment const& rgt);

View file

@ -163,14 +163,11 @@ void append_unsafe_str(std::string &out, std::string const& str) {
out += ',';
}
std::string AssDialogue::GetData(bool ssa) const {
std::string AssDialogue::GetEntryData() const {
std::string str = Comment ? "Comment: " : "Dialogue: ";
str.reserve(51 + Style.get().size() + Actor.get().size() + Effect.get().size() + Text.get().size());
if (ssa)
append_str(str, "Marked=0");
else
append_int(str, Layer);
append_int(str, Layer);
append_str(str, Start.GetAssFormated());
append_str(str, End.GetAssFormated());
append_unsafe_str(str, Style);

View file

@ -153,8 +153,6 @@ struct AssDialogueBase {
};
class AssDialogue final : public AssEntry, public AssDialogueBase, public AssEntryListHook {
std::string GetData(bool ssa) const;
/// @brief Parse raw ASS data into everything else
/// @param data ASS line
void Parse(std::string const& data);
@ -172,10 +170,8 @@ public:
/// Update the text of the line from parsed blocks
void UpdateText(std::vector<std::unique_ptr<AssDialogueBlock>>& blocks);
std::string GetEntryData() const { return GetData(false); }
std::string GetEntryData() const;
/// Get the line as SSA rather than ASS
std::string GetSSAText() const { return GetData(true); }
/// Does this line collide with the passed line?
bool CollidesWith(const AssDialogue *target) const;

View file

@ -21,7 +21,7 @@
#include "ass_entry.h"
std::string const& AssEntry::GroupHeader(bool ssa) const {
std::string const& AssEntry::GroupHeader() const {
static std::string ass_headers[] = {
"[Script Info]",
"[V4+ Styles]",
@ -31,16 +31,5 @@ std::string const& AssEntry::GroupHeader(bool ssa) const {
"[Aegisub Extradata]",
""
};
static std::string ssa_headers[] = {
"[Script Info]",
"[V4 Styles]",
"[Fonts]",
"[Graphics]",
"[Events]",
"[Aegisub Extradata]",
""
};
return (ssa ? ssa_headers : ass_headers)[(int)Group()];
return ass_headers[(int)Group()];
}

View file

@ -57,5 +57,5 @@ public:
virtual AssEntryGroup Group() const=0;
/// ASS or SSA Section header for this entry's group
std::string const& GroupHeader(bool ssa=false) const;
std::string const& GroupHeader() const;
};

View file

@ -25,6 +25,7 @@
#include <algorithm>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem/path.hpp>
#include <cassert>

View file

@ -16,8 +16,6 @@
#include "ass_entry.h"
#include <boost/algorithm/string/predicate.hpp>
class AssInfo final : public AssEntry {
std::string key;
std::string value;
@ -28,7 +26,6 @@ public:
AssEntryGroup Group() const override { return AssEntryGroup::INFO; }
std::string GetEntryData() const { return key + ": " + value; }
std::string GetSSAText() const { return boost::iequals(key, "scripttype: v4.00+") ? "ScriptType: v4.00" : GetEntryData(); }
std::string Key() const { return key; }
std::string Value() const { return value; }

View file

@ -191,17 +191,6 @@ void AssStyle::UpdateData() {
% Margin[0] % Margin[1] % Margin[2] % encoding);
}
std::string AssStyle::GetSSAText() const {
return str(boost::format("Style: %s,%s,%g,%s,%s,0,%s,%d,%d,%d,%g,%g,%d,%d,%d,%d,0,%i")
% name % font % fontsize
% primary.GetSsaFormatted()
% secondary.GetSsaFormatted()
% shadow.GetSsaFormatted()
% (bold? -1 : 0) % (italic ? -1 : 0)
% borderstyle % outline_w % shadow_w % AssToSsa(alignment)
% Margin[0] % Margin[1] % Margin[2] % encoding);
}
void AssStyle::GetEncodings(wxArrayString &encodingStrings) {
encodingStrings.Clear();
encodingStrings.Add(wxString("0 - ") + _("ANSI"));

View file

@ -78,7 +78,6 @@ public:
AssStyle(std::string const& data, int version=1);
std::string const& GetEntryData() const { return data; }
std::string GetSSAText() const ;
AssEntryGroup Group() const override { return AssEntryGroup::STYLE; }
/// Convert an ASS alignment to the equivalent SSA alignment

View file

@ -60,11 +60,11 @@
#include <algorithm>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/format.hpp>
#include <boost/scope_exit.hpp>
#include <cassert>
#include <cstdint>
#include <lauxlib.h>
#include <mutex>
#include <wx/clipbrd.h>
#include <wx/filefn.h>

View file

@ -32,19 +32,6 @@
DEFINE_SIMPLE_EXCEPTION(AssParseError, SubtitleFormatParseError, "subtitle_io/parse/ass")
AssSubtitleFormat::AssSubtitleFormat()
: SubtitleFormat("Advanced Substation Alpha")
{
}
std::vector<std::string> AssSubtitleFormat::GetReadWildcards() const {
return {"ass", "ssa"};
}
std::vector<std::string> AssSubtitleFormat::GetWriteWildcards() const {
return {"ass", "ssa"};
}
void AssSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const {
TextFileReader file(filename, encoding);
int version = !agi::fs::HasExtension(filename, "ssa");
@ -68,27 +55,16 @@ void AssSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename,
#endif
namespace {
const char *format(AssEntryGroup group, bool ssa) {
if (group == AssEntryGroup::DIALOGUE) {
if (ssa)
return "Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" LINEBREAK;
else
return "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" LINEBREAK;
}
if (group == AssEntryGroup::STYLE) {
if (ssa)
return "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding" LINEBREAK;
else
return "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding" LINEBREAK;
}
const char *format(AssEntryGroup group) {
if (group == AssEntryGroup::DIALOGUE)
return "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" LINEBREAK;
if (group == AssEntryGroup::STYLE)
return "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding" LINEBREAK;
return nullptr;
}
struct Writer {
TextFileWriter file;
bool ssa = false;
AssEntryGroup group = AssEntryGroup::INFO;
Writer(std::ostream &ostr) : file(ostr) {
@ -97,7 +73,6 @@ struct Writer {
Writer(agi::fs::path const& filename, std::string const& encoding)
: file(filename, encoding)
, ssa(agi::fs::HasExtension(filename, "ssa"))
{
file.WriteLineToFile("[Script Info]");
file.WriteLineToFile(std::string("; Script generated by Aegisub ") + GetAegisubLongVersionString());
@ -111,14 +86,14 @@ struct Writer {
// Add a blank line between each group
file.WriteLineToFile("");
file.WriteLineToFile(line.GroupHeader(ssa));
if (const char *str = format(line.Group(), ssa))
file.WriteLineToFile(line.GroupHeader());
if (const char *str = format(line.Group()))
file.WriteLineToFile(str, false);
group = line.Group();
}
file.WriteLineToFile(ssa ? line.GetSSAText() : line.GetEntryData());
file.WriteLineToFile(line.GetEntryData());
}
}

View file

@ -1,47 +1,29 @@
// Copyright (c) 2006, Rodrigo Braz Monteiro
// All rights reserved.
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
/// @file subtitle_format_ass.h
/// @see subtitle_format_ass.cpp
/// @ingroup subtitle_io
///
#include "subtitle_format.h"
class AssSubtitleFormat final : public SubtitleFormat {
public:
AssSubtitleFormat();
AssSubtitleFormat() : SubtitleFormat("Advanced SubStation Alpha") { }
std::vector<std::string> GetReadWildcards() const override;
std::vector<std::string> GetWriteWildcards() const override;
std::vector<std::string> GetReadWildcards() const override { return {"ass", "ssa"}; }
std::vector<std::string> GetWriteWildcards() const override { return {"ass"}; }
// Naturally the ASS subtitle format can save all Ass files
// Naturally the ASS subtitle format can save all ASS files
bool CanSave(const AssFile*) const override { return true; }
void ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& forceEncoding) const override;

View file

@ -0,0 +1,93 @@
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include "subtitle_format_ssa.h"
#include "ass_attachment.h"
#include "ass_dialogue.h"
#include "ass_info.h"
#include "ass_file.h"
#include "ass_style.h"
#include "text_file_writer.h"
#include "version.h"
#include <libaegisub/fs.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/format.hpp>
namespace {
std::string replace_commas(std::string str) {
boost::replace_all(str, ",", ";");
return str;
}
std::string strip_newlines(std::string str) {
boost::replace_all(str, "\n", "");
boost::replace_all(str, "\r", "");
return str;
}
}
void SsaSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const&, std::string const& encoding) const {
TextFileWriter file(filename, encoding);
file.WriteLineToFile("[Script Info]");
file.WriteLineToFile(std::string("; Script generated by Aegisub ") + GetAegisubLongVersionString());
file.WriteLineToFile("; http://www.aegisub.org/");
for (auto const& line : src->Info)
file.WriteLineToFile(boost::iequals(line.Key(), "scripttype") ? "ScriptType: v4.00" : line.GetEntryData());
file.WriteLineToFile("");
file.WriteLineToFile("[V4 Styles]");
file.WriteLineToFile("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding");
for (auto const& line : src->Styles)
file.WriteLineToFile(str(boost::format("Style: %s,%s,%g,%s,%s,0,%s,%d,%d,%d,%g,%g,%d,%d,%d,%d,0,%i")
% line.name % line.font % line.fontsize
% line.primary.GetSsaFormatted()
% line.secondary.GetSsaFormatted()
% line.shadow.GetSsaFormatted()
% (line.bold? -1 : 0) % (line.italic ? -1 : 0)
% line.borderstyle % line.outline_w % line.shadow_w % AssStyle::AssToSsa(line.alignment)
% line.Margin[0] % line.Margin[1] % line.Margin[2] % line.encoding));
file.WriteLineToFile("");
file.WriteLineToFile("[Fonts]");
for (auto const& line : src->Attachments) {
if (line.Group() == AssEntryGroup::FONT)
file.WriteLineToFile(line.GetEntryData());
}
file.WriteLineToFile("");
file.WriteLineToFile("[Graphics]");
for (auto const& line : src->Attachments) {
if (line.Group() == AssEntryGroup::GRAPHIC)
file.WriteLineToFile(line.GetEntryData());
}
file.WriteLineToFile("");
file.WriteLineToFile("[Events]");
file.WriteLineToFile("Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
for (auto const& line : src->Events)
file.WriteLineToFile(str(boost::format("%s: Marked=0,%s,%s,%s,%s,%d,%d,%d,%s,%s")
% (line.Comment ? "Comment" : "Dialogue")
% line.Start.GetAssFormated() % line.End.GetAssFormated()
% replace_commas(line.Style) % replace_commas(line.Actor)
% line.Margin[0] % line.Margin[1] % line.Margin[2]
% replace_commas(line.Effect)
% strip_newlines(line.Text)));
}

26
src/subtitle_format_ssa.h Normal file
View file

@ -0,0 +1,26 @@
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include "subtitle_format.h"
class SsaSubtitleFormat final : public SubtitleFormat {
public:
SsaSubtitleFormat() : SubtitleFormat("SubStation Alpha") { }
std::vector<std::string> GetWriteWildcards() const override { return {"ssa"}; }
/// @todo Not actually true
bool CanSave(const AssFile*) const override { return true; }
void WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const override;
};