From dcff9b7324d0a4189fc34ef2c6ea3fec449a82cf Mon Sep 17 00:00:00 2001 From: niksedk Date: Fri, 10 Jun 2011 12:54:40 +0000 Subject: [PATCH] Added "Advanced Sub Station Alpha" as a separate format (no longer treated as ssa) git-svn-id: https://subtitleedit.googlecode.com/svn/trunk@484 99eadd0c-20b8-1223-b5c4-2a2b2df33de2 --- src/Logic/Paragraph.cs | 2 + .../AdvancedSubStationAlpha.cs | 345 ++++++++++++++++++ src/Logic/SubtitleFormats/SubStationAlpha.cs | 44 ++- src/Logic/SubtitleFormats/SubtitleFormat.cs | 1 + src/SubtitleEdit.csproj | 1 + 5 files changed, 383 insertions(+), 10 deletions(-) create mode 100644 src/Logic/SubtitleFormats/AdvancedSubStationAlpha.cs diff --git a/src/Logic/Paragraph.cs b/src/Logic/Paragraph.cs index 8dea4d172..ad85e966d 100644 --- a/src/Logic/Paragraph.cs +++ b/src/Logic/Paragraph.cs @@ -28,6 +28,8 @@ namespace Nikse.SubtitleEdit.Logic public bool Forced { get; set; } + public string Extra { get; set; } + public Paragraph() { StartTime = new TimeCode(TimeSpan.FromSeconds(0)); diff --git a/src/Logic/SubtitleFormats/AdvancedSubStationAlpha.cs b/src/Logic/SubtitleFormats/AdvancedSubStationAlpha.cs new file mode 100644 index 000000000..99bbc9a56 --- /dev/null +++ b/src/Logic/SubtitleFormats/AdvancedSubStationAlpha.cs @@ -0,0 +1,345 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nikse.SubtitleEdit.Logic.SubtitleFormats +{ + class AdvancedSubStationAlpha : SubtitleFormat + { + public override string Extension + { + get { return ".ass"; } + } + + public override string Name + { + get { return "Advanced Sub Station Alpha"; } + } + + public override bool HasLineNumber + { + get { return false; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + + var sb = new StringBuilder(); + lines.ForEach(line => sb.AppendLine(line)); + string all = sb.ToString(); + if (!all.Contains("[V4+ Styles]")) + return false; + + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + const string header = +@"[Script Info] +; This is an Advanced Sub Station Alpha v4+ script. +Title: {0} +ScriptType: v4.00 +Collisions: Normal +PlayDepth: 0 + +[V4+ Styles] +Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding +Style: Default,Tahoma,25,&H00FFFFFF,&HF0000000,&H001509AE,&H32270EA8,-1,0,0,0,100,100,0,0.00,1,2,1,2,30,30,10,1 + +[Events] +Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text"; + + const string timeCodeFormat = "{0}:{1:00}:{2:00}.{3:00}"; // h:mm:ss.cc + const string paragraphWriteFormat = "Dialogue: Marked=0,{0},{1},{3},NTP,0000,0000,0000,,{2}"; + + var sb = new StringBuilder(); + System.Drawing.Color fontColor = System.Drawing.Color.FromArgb(Configuration.Settings.SsaStyle.FontColorArgb); + bool isValidAssHeader =!string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.Contains("[V4+ Styles]"); + List styles = new List(); + if (isValidAssHeader) + { + sb.AppendLine(subtitle.Header.Trim()); + sb.AppendLine("Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text"); + styles = GetStylesFromHeader(subtitle.Header); + } + else + { + sb.AppendLine(string.Format(header, + title, + Configuration.Settings.SsaStyle.FontName, + (int)Configuration.Settings.SsaStyle.FontSize, + System.Drawing.ColorTranslator.ToWin32(fontColor))); + } + foreach (Paragraph p in subtitle.Paragraphs) + { + string start = string.Format(timeCodeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds / 10); + string end = string.Format(timeCodeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds / 10); + string style = "Default"; + if (!string.IsNullOrEmpty(p.Extra) && isValidAssHeader && styles.Contains(p.Extra)) + style = p.Extra; + sb.AppendLine(string.Format(paragraphWriteFormat, start, end, FormatText(p), style)); + } + return sb.ToString().Trim(); + } + + public static List GetStylesFromHeader(string headerLines) + { + List list = new List(); + foreach (string line in headerLines.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) + { + if (line.StartsWith("Style:")) + { + int end = line.IndexOf(","); + if (end > 0) + list.Add(line.Substring(6, end - 6).Trim()); + } + } + return list; + } + + private static string FormatText(Paragraph p) + { + string text = p.Text.Replace(Environment.NewLine, "\\N"); + text = text.Replace("", @"{\i1}"); + text = text.Replace("", @"{\i0}"); + text = text.Replace("", @"{\u1}"); + text = text.Replace("", @"{\u0}"); + text = text.Replace("", @"{\b1}"); + text = text.Replace("", @"{\b0}"); + if (text.Contains("', start); + if (end > 0) + { + string fontTag = text.Substring(start + 4, end - (start + 4)); + text = text.Remove(start, end - start + 1); + text = text.Replace("", string.Empty); + + fontTag = FormatTag(ref text, start, fontTag, "face=\"", "\"", "fn", "}"); + fontTag = FormatTag(ref text, start, fontTag, "face='", "'", "fn", "}"); + + fontTag = FormatTag(ref text, start, fontTag, "size=\"", "\"", "fs", "}"); + fontTag = FormatTag(ref text, start, fontTag, "size='", "'", "fs", "}"); + + fontTag = FormatTag(ref text, start, fontTag, "color=\"", "\"", "c&H", "&}"); + fontTag = FormatTag(ref text, start, fontTag, "color='", "'", "c&H", "&}"); + } + } + return text; + } + + private static string FormatTag(ref string text, int start, string fontTag, string tag, string endSign, string ssaTagName, string endSsaTag) + { + if (fontTag.Contains(tag)) + { + int fontStart = fontTag.IndexOf(tag); + int fontEnd = fontTag.IndexOf(endSign, fontStart + tag.Length); + if (fontEnd > 0) + { + string subTag = fontTag.Substring(fontStart + tag.Length, fontEnd - (fontStart + tag.Length)); + if (tag.Contains("color")) + subTag = subTag.Replace("#", string.Empty); + fontTag = fontTag.Remove(fontStart, fontEnd - fontStart + 1); + text = text.Insert(start, @"{\" + ssaTagName + subTag + endSsaTag); + } + } + return fontTag; + } + + private static string GetFormattedText(string text) + { + text = text.Replace("\\N", Environment.NewLine).Replace("\\n", Environment.NewLine); + + text = text.Replace(@"{\i1}", ""); + text = text.Replace(@"{\i0}", ""); + if (Utilities.CountTagInText(text, "") > Utilities.CountTagInText(text, "")) + text += ""; + + text = text.Replace(@"{\u1}", ""); + text = text.Replace(@"{\u0}", ""); + if (Utilities.CountTagInText(text, "") > Utilities.CountTagInText(text, "")) + text += ""; + + text = text.Replace(@"{\b1}", ""); + text = text.Replace(@"{\b0}", ""); + if (Utilities.CountTagInText(text, "") > Utilities.CountTagInText(text, "")) + text += ""; + + for (int i = 0; i < 5; i++) // just look five times... + { + if (text.Contains(@"{\fn")) + { + int start = text.IndexOf(@"{\fn"); + int end = text.IndexOf('}', start); + if (end > 0) + { + string fontName = text.Substring(start + 4, end - (start + 4)); + text = text.Remove(start, end - start + 1); + text = text.Insert(start, ""); + text += ""; + } + } + + if (text.Contains(@"{\fs")) + { + int start = text.IndexOf(@"{\fs"); + int end = text.IndexOf('}', start); + if (end > 0) + { + string fontSize = text.Substring(start + 4, end - (start + 4)); + if (Utilities.IsInteger(fontSize)) + { + text = text.Remove(start, end - start + 1); + text = text.Insert(start, ""); + text += ""; + } + } + } + + if (text.Contains(@"{\c")) + { + int start = text.IndexOf(@"{\c"); + int end = text.IndexOf('}', start); + if (end > 0) + { + string color = text.Substring(start + 4, end - (start + 4)); + color = color.Replace("&", string.Empty).TrimStart('H'); + text = text.Remove(start, end - start + 1); + text = text.Insert(start, ""); + text += ""; + } + } + } + + return text; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + _errorCount = 0; + bool eventsStarted = false; + subtitle.Paragraphs.Clear(); + string[] format = "Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text".Split(','); + int indexStart = 1; + int indexEnd = 2; + int indexStyle = 3; + int indexText = 9; + + var header = new StringBuilder(); + foreach (string line in lines) + { + if (!eventsStarted) + header.AppendLine(line); + + if (line.Trim().ToLower() == "[events]") + { + eventsStarted = true; + } + else if (eventsStarted) + { + string s = line.Trim().ToLower(); + if (s.StartsWith("format:")) + { + if (line.Length > 10) + { + format = line.ToLower().Substring(8).Split(','); + for (int i = 0; i < format.Length; i++) + { + if (format[i].Trim().ToLower() == "start") + indexStart = i; + else if (format[i].Trim().ToLower() == "end") + indexEnd = i; + else if (format[i].Trim().ToLower() == "text") + indexText = i; + else if (format[i].Trim().ToLower() == "style") + indexStyle = i; + } + } + } + else + { + string text = string.Empty; + string start = string.Empty; + string end = string.Empty; + string style = string.Empty; + + string[] splittedLine; + + if (s.StartsWith("dialogue:")) + splittedLine = line.Substring(10).Split(','); + else + splittedLine = line.Split(','); + + for (int i = 0; i < splittedLine.Length; i++) + { + if (i == indexStart) + start = splittedLine[i].Trim(); + else if (i == indexEnd) + end = splittedLine[i].Trim(); + else if (i == indexStyle) + style = splittedLine[i].Trim(); + else if (i == indexText) + text = splittedLine[i]; + else if (i > indexText) + text += "," + splittedLine[i]; + } + + try + { + var p = new Paragraph(); + + p.StartTime = GetTimeCodeFromString(start); + p.EndTime = GetTimeCodeFromString(end); + p.Text = GetFormattedText(text); + if (!string.IsNullOrEmpty(style)) + p.Extra = style; + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + } + } + } + } + if (header.Length > 0) + subtitle.Header = header.ToString(); + subtitle.Renumber(1); + } + + private static TimeCode GetTimeCodeFromString(string time) + { + // h:mm:ss.cc + string[] timeCode = time.Split(':', '.'); + return new TimeCode(int.Parse(timeCode[0]), + int.Parse(timeCode[1]), + int.Parse(timeCode[2]), + int.Parse(timeCode[3]) * 10); + } + + public override void RemoveNativeFormatting(Subtitle subtitle) + { + foreach (Paragraph p in subtitle.Paragraphs) + { + int indexOfBegin = p.Text.IndexOf("{"); + while (indexOfBegin >= 0 && p.Text.IndexOf("}") > indexOfBegin) + { + int indexOfEnd = p.Text.IndexOf("}"); + p.Text = p.Text.Remove(indexOfBegin, (indexOfEnd - indexOfBegin) + 1); + + indexOfBegin = p.Text.IndexOf("{"); + } + } + } + + } +} diff --git a/src/Logic/SubtitleFormats/SubStationAlpha.cs b/src/Logic/SubtitleFormats/SubStationAlpha.cs index fb959a74b..314ff660f 100644 --- a/src/Logic/SubtitleFormats/SubStationAlpha.cs +++ b/src/Logic/SubtitleFormats/SubStationAlpha.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Text; -using Nikse.SubtitleEdit.Forms; namespace Nikse.SubtitleEdit.Logic.SubtitleFormats { @@ -52,21 +51,34 @@ Style: Default,{1},{2},{3},65535,65535,-2147483640,-1,0,1,3,0,2,30,30,30,0,0 Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"; const string timeCodeFormat = "{0}:{1:00}:{2:00}.{3:00}"; // h:mm:ss.cc - const string paragraphWriteFormat = "Dialogue: Marked=0,{0},{1},Default,NTP,0000,0000,0000,!Effect,{2}"; + const string paragraphWriteFormat = "Dialogue: Marked=0,{0},{1},{3},NTP,0000,0000,0000,!Effect,{2}"; var sb = new StringBuilder(); System.Drawing.Color fontColor = System.Drawing.Color.FromArgb(Configuration.Settings.SsaStyle.FontColorArgb); - sb.AppendLine(string.Format(header, - title, - Configuration.Settings.SsaStyle.FontName, - (int)Configuration.Settings.SsaStyle.FontSize, - System.Drawing.ColorTranslator.ToWin32(fontColor))); - + bool isValidAssHeader =!string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.Contains("[V4+ Styles]"); + List styles = new List(); + if (isValidAssHeader) + { + sb.AppendLine(subtitle.Header.Trim()); + sb.AppendLine("Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text"); + styles = AdvancedSubStationAlpha.GetStylesFromHeader(subtitle.Header); + } + else + { + sb.AppendLine(string.Format(header, + title, + Configuration.Settings.SsaStyle.FontName, + (int)Configuration.Settings.SsaStyle.FontSize, + System.Drawing.ColorTranslator.ToWin32(fontColor))); + } foreach (Paragraph p in subtitle.Paragraphs) { string start = string.Format(timeCodeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds / 10); string end = string.Format(timeCodeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds / 10); - sb.AppendLine(string.Format(paragraphWriteFormat, start, end, FormatText(p))); + string style = "Default"; + if (!string.IsNullOrEmpty(p.Extra) && isValidAssHeader && styles.Contains(p.Extra)) + style = p.Extra; + sb.AppendLine(string.Format(paragraphWriteFormat, start, end, FormatText(p), style)); } return sb.ToString().Trim(); } @@ -197,10 +209,15 @@ Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text string[] format = "Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text".Split(','); int indexStart = 1; int indexEnd = 2; - int indexText = 9; + int indexStyle = 3; + int indexText = 9; + var header = new StringBuilder(); foreach (string line in lines) { + if (!eventsStarted) + header.AppendLine(line); + if (line.Trim().ToLower() == "[events]") { eventsStarted = true; @@ -221,6 +238,8 @@ Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text indexEnd = i; else if (format[i].Trim().ToLower() == "text") indexText = i; + else if (format[i].Trim().ToLower() == "style") + indexStyle = i; } } } @@ -229,6 +248,7 @@ Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text string text = string.Empty; string start = string.Empty; string end = string.Empty; + string style = string.Empty; string[] splittedLine; @@ -256,6 +276,8 @@ Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text p.StartTime = GetTimeCodeFromString(start); p.EndTime = GetTimeCodeFromString(end); p.Text = GetFormattedText(text); + if (!string.IsNullOrEmpty(style)) + p.Extra = style; subtitle.Paragraphs.Add(p); } catch @@ -265,6 +287,8 @@ Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text } } } + if (header.Length > 0) + subtitle.Header = header.ToString(); subtitle.Renumber(1); } diff --git a/src/Logic/SubtitleFormats/SubtitleFormat.cs b/src/Logic/SubtitleFormats/SubtitleFormat.cs index 21da57b01..cf829aa0a 100644 --- a/src/Logic/SubtitleFormats/SubtitleFormat.cs +++ b/src/Logic/SubtitleFormats/SubtitleFormat.cs @@ -19,6 +19,7 @@ namespace Nikse.SubtitleEdit.Logic.SubtitleFormats new AdobeEncore(), new AdobeEncoreTab(), new AdobeEncoreWithLineNumbers(), + new AdvancedSubStationAlpha(), new AQTitle(), new Captionate(), new CaraokeXml(), diff --git a/src/SubtitleEdit.csproj b/src/SubtitleEdit.csproj index 7ec6faf79..b031f9eb0 100644 --- a/src/SubtitleEdit.csproj +++ b/src/SubtitleEdit.csproj @@ -537,6 +537,7 @@ +