From 1eba2f035c8616c8899f289cf330cb91544a62d2 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Tue, 29 Apr 2014 09:33:22 -0700 Subject: [PATCH] Add a separate SSA subtitle format and move all of the SSA writing stuff there --- build/Aegisub/Aegisub.vcxproj | 2 + build/Aegisub/Aegisub.vcxproj.filters | 14 ++-- src/Makefile | 1 + src/ass_attachment.h | 1 - src/ass_dialogue.cpp | 7 +- src/ass_dialogue.h | 6 +- src/ass_entry.cpp | 15 +---- src/ass_entry.h | 2 +- src/ass_file.cpp | 1 + src/ass_info.h | 3 - src/ass_style.cpp | 11 ---- src/ass_style.h | 1 - src/auto4_lua.cpp | 2 +- src/subtitle_format_ass.cpp | 41 +++--------- src/subtitle_format_ass.h | 48 +++++--------- src/subtitle_format_ssa.cpp | 93 +++++++++++++++++++++++++++ src/subtitle_format_ssa.h | 26 ++++++++ 17 files changed, 161 insertions(+), 113 deletions(-) create mode 100644 src/subtitle_format_ssa.cpp create mode 100644 src/subtitle_format_ssa.h diff --git a/build/Aegisub/Aegisub.vcxproj b/build/Aegisub/Aegisub.vcxproj index b8c742bf5..1b543cae7 100644 --- a/build/Aegisub/Aegisub.vcxproj +++ b/build/Aegisub/Aegisub.vcxproj @@ -212,6 +212,7 @@ + @@ -404,6 +405,7 @@ + diff --git a/build/Aegisub/Aegisub.vcxproj.filters b/build/Aegisub/Aegisub.vcxproj.filters index 9fc3e3d32..64750b2fd 100644 --- a/build/Aegisub/Aegisub.vcxproj.filters +++ b/build/Aegisub/Aegisub.vcxproj.filters @@ -618,6 +618,9 @@ Main UI\Grid + + Subtitle formats + @@ -1169,13 +1172,12 @@ Main UI\Grid + + Subtitle formats + - - Resources - - - Resources - + + diff --git a/src/Makefile b/src/Makefile index 1195d03f3..3cfde47f0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 \ diff --git a/src/ass_attachment.h b/src/ass_attachment.h index 2a7efe249..4f42b4085 100644 --- a/src/ass_attachment.h +++ b/src/ass_attachment.h @@ -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); diff --git a/src/ass_dialogue.cpp b/src/ass_dialogue.cpp index 80378172c..14e2d2d25 100644 --- a/src/ass_dialogue.cpp +++ b/src/ass_dialogue.cpp @@ -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); diff --git a/src/ass_dialogue.h b/src/ass_dialogue.h index e9273255c..996586a50 100644 --- a/src/ass_dialogue.h +++ b/src/ass_dialogue.h @@ -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>& 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; diff --git a/src/ass_entry.cpp b/src/ass_entry.cpp index bdf486b0f..9befe02fe 100644 --- a/src/ass_entry.cpp +++ b/src/ass_entry.cpp @@ -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()]; } diff --git a/src/ass_entry.h b/src/ass_entry.h index f830e0d92..f1f6b3f4c 100644 --- a/src/ass_entry.h +++ b/src/ass_entry.h @@ -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; }; diff --git a/src/ass_file.cpp b/src/ass_file.cpp index 2e6a8b712..966e9cc19 100644 --- a/src/ass_file.cpp +++ b/src/ass_file.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include diff --git a/src/ass_info.h b/src/ass_info.h index 3f491e8f7..af6463d78 100644 --- a/src/ass_info.h +++ b/src/ass_info.h @@ -16,8 +16,6 @@ #include "ass_entry.h" -#include - 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; } diff --git a/src/ass_style.cpp b/src/ass_style.cpp index a7c6282e9..21efc713f 100644 --- a/src/ass_style.cpp +++ b/src/ass_style.cpp @@ -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")); diff --git a/src/ass_style.h b/src/ass_style.h index 432db702a..4ce0fccdd 100644 --- a/src/ass_style.h +++ b/src/ass_style.h @@ -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 diff --git a/src/auto4_lua.cpp b/src/auto4_lua.cpp index 0116b933c..98960bea9 100644 --- a/src/auto4_lua.cpp +++ b/src/auto4_lua.cpp @@ -60,11 +60,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include diff --git a/src/subtitle_format_ass.cpp b/src/subtitle_format_ass.cpp index e5261193a..494041676 100644 --- a/src/subtitle_format_ass.cpp +++ b/src/subtitle_format_ass.cpp @@ -32,19 +32,6 @@ DEFINE_SIMPLE_EXCEPTION(AssParseError, SubtitleFormatParseError, "subtitle_io/parse/ass") -AssSubtitleFormat::AssSubtitleFormat() -: SubtitleFormat("Advanced Substation Alpha") -{ -} - -std::vector AssSubtitleFormat::GetReadWildcards() const { - return {"ass", "ssa"}; -} - -std::vector 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()); } } diff --git a/src/subtitle_format_ass.h b/src/subtitle_format_ass.h index 3071276d8..10d47dc38 100644 --- a/src/subtitle_format_ass.h +++ b/src/subtitle_format_ass.h @@ -1,47 +1,29 @@ -// Copyright (c) 2006, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2014, Thomas Goyne // -// 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 GetReadWildcards() const override; - std::vector GetWriteWildcards() const override; + std::vector GetReadWildcards() const override { return {"ass", "ssa"}; } + std::vector 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; diff --git a/src/subtitle_format_ssa.cpp b/src/subtitle_format_ssa.cpp new file mode 100644 index 000000000..1cd790377 --- /dev/null +++ b/src/subtitle_format_ssa.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2014, Thomas Goyne +// +// 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 + +#include +#include +#include + +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))); +} diff --git a/src/subtitle_format_ssa.h b/src/subtitle_format_ssa.h new file mode 100644 index 000000000..66ba45d03 --- /dev/null +++ b/src/subtitle_format_ssa.h @@ -0,0 +1,26 @@ +// Copyright (c) 2014, Thomas Goyne +// +// 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 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; +}; \ No newline at end of file