using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using System.Xml; namespace Nikse.SubtitleEdit.Core.SubtitleFormats { public class BelleNuitSubtitler : SubtitleFormat { ///tc 00:00:35:09 00:00:38:05 private static readonly Regex RegexTimeCode = new Regex(@"^\/tc \d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d", RegexOptions.Compiled); private static readonly Regex RegexFileNum = new Regex(@"^\/file\s+\d+$", RegexOptions.Compiled); public override string Extension { get { return ".stp"; } } public override string Name { get { return "Belle Nuit Subtitler"; } } public override bool IsTimeBased { get { return true; } } public override bool IsMine(List lines, string fileName) { var subtitle = new Subtitle(); LoadSubtitle(subtitle, lines, fileName); return subtitle.Paragraphs.Count > _errorCount; } public override string ToText(Subtitle subtitle, string title) { const string paragraphWriteFormat = "/tc {0} {1}{2}{3}{2}"; var sb = new StringBuilder(); foreach (Paragraph p in subtitle.Paragraphs) { sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), Environment.NewLine, EncodeText(p.Text))); } var doc = new XmlDocument { XmlResolver = null }; doc.LoadXml("" + Environment.NewLine + @" document creator SICT type STLI version 1.4 applicationversion Belle Nuit Subtitler 1.7.8 creationdate 2012-03-13 16:30:32 modificationdate 2012-03-13 16:30:32 mainleft 40 maintop 48 mainwidth 825 mainheight 886 styledt exportdt previewdt moviedt exportformat TIFF style font Geneva size 26 spacing 1 leading 7 bold italic underline vertical 486 halin 1 valign 2 standard PAL height 576 width 720 widthreal 768 antialiasing 4 left 40 right 680 wrapmethod 2 interlaced textcolor #FBFFF2 textalpha 1 textsoft 0 bordercolor #F0F10 borderalpha 1 bordersoft 0 borderwidth 6 rectcolor #0 rectalpha 0 rectsoft 0 rectform 1 shadowcolor #7F7F7F shadowalpha 0 shadowsoft 0 shadowx 2 shadowy 2 framerate 25 folderpath prefix moviepath movieoffset 00:00:00:00 moviesyncoption pagesetup titlelist "); XmlNode node = doc.CreateElement("string"); node.InnerText = sb.ToString().Trim() + Environment.NewLine + Environment.NewLine; doc.DocumentElement.AppendChild(node); return ToUtf8XmlString(doc).Replace("\r\n", "\n"); } public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) { _errorCount = 0; var sb = new StringBuilder(); foreach (var line in lines) { sb.AppendLine(line); } var doc = new XmlDocument { XmlResolver = null }; try { doc.LoadXml(sb.ToString()); if (doc.DocumentElement == null || doc.DocumentElement.Name != "xmldict" || doc.DocumentElement.SelectSingleNode("string") == null) return; } catch (Exception) { _errorCount = 1; return; } string text = null; string keyName = string.Empty; foreach (XmlNode node in doc.DocumentElement.ChildNodes) { if (node.Name == "key") { keyName = node.InnerText; } else if (node.Name == "string" && keyName == "titlelist") { text = node.InnerText; break; } } if (text == null) return; subtitle.Paragraphs.Clear(); Paragraph paragraph = null; sb.Clear(); foreach (string line in text.Split(Utilities.NewLineChars)) { if (RegexTimeCode.IsMatch(line)) { string[] parts = line.Substring(4, 11).Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries); if (parts.Length == 4) { try { if (paragraph != null && !string.IsNullOrWhiteSpace(sb.ToString())) { paragraph.Text = DecodeText(sb); } var start = DecodeTimeCodeFramesFourParts(parts); parts = line.Substring(16, 11).Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries); var end = DecodeTimeCodeFramesFourParts(parts); paragraph = new Paragraph { StartTime = start, EndTime = end }; subtitle.Paragraphs.Add(paragraph); sb.Clear(); } catch { _errorCount++; } } } else if (RegexFileNum.IsMatch(line)) { continue; // skip Belle-Nuit's numbering lines ("/file 0001") } else if (paragraph != null) { sb.AppendLine(line); } else { _errorCount++; } } if (paragraph != null && !string.IsNullOrWhiteSpace(sb.ToString())) { paragraph.Text = DecodeText(sb); } subtitle.Renumber(); } private static string EncodeText(string s) { s = HtmlUtil.RemoveOpenCloseTags(s, HtmlUtil.TagBold, HtmlUtil.TagUnderline, HtmlUtil.TagFont); if (s.StartsWith("{\\an3}", StringComparison.Ordinal) || s.StartsWith("{\\an6}", StringComparison.Ordinal)) s = "/STYLE RIGHT" + Environment.NewLine + s.Remove(0, 6).Trim(); if (s.StartsWith("{\\an1}", StringComparison.Ordinal) || s.StartsWith("{\\an4}", StringComparison.Ordinal)) s = "/STYLE LEFT" + Environment.NewLine + s.Remove(0, 6).Trim(); if (s.StartsWith("{\\an7}", StringComparison.Ordinal) || s.StartsWith("{\\an8}", StringComparison.Ordinal) || s.StartsWith("{\\an9}", StringComparison.Ordinal)) s = "/STYLE VERTICAL(-25)" + Environment.NewLine + s.Remove(0, 6).Trim(); if (s.StartsWith("{\\an2}", StringComparison.Ordinal) || s.StartsWith("{\\an5}", StringComparison.Ordinal)) s = s.Remove(0, 6).Trim(); return s; } private static string DecodeText(StringBuilder sb) { var s = sb.ToString().Trim(); s = s.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine).Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); if (s.StartsWith("/STYLE RIGHT" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an3}" + s.Remove(0, 12).Trim(); if (s.StartsWith("/STYLE LEFT" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an1}" + s.Remove(0, 11).Trim(); if (s.StartsWith("/STYLE TOP" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an8}" + s.Remove(0, 10).Trim(); if (s.StartsWith("/STYLE VERTICAL(-25)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an8}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-24)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an8}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-23)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an8}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-22)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an8}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-21)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an8}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-20)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an8}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-19)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an8}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-18)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an5}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-17)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an5}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-16)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an5}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-15)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an5}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-14)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an5}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-13)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an5}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-12)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an5}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-11)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an5}" + s.Remove(0, 20).Trim(); if (s.StartsWith("/STYLE VERTICAL(-10)" + Environment.NewLine, StringComparison.Ordinal)) s = "{\\an5}" + s.Remove(0, 20).Trim(); s = HtmlUtil.FixInvalidItalicTags(s); return s; } private static string EncodeTimeCode(TimeCode time) { return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); } } }