using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Text; using System.Xml; using System.Xml.Schema; namespace Nikse.SubtitleEdit.Core.SubtitleFormats { public class DCinemaSmpte2007 : SubtitleFormat { // // // urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e // 2001, A Space Odissey // This is a subtitle file // 2012-06-26T12:33:59.000-00:00 // 1 // fr // 25 1 // 25 // 00:00:00:00 // urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391 // // // Hallo // // public string Errors { get; private set; } private double _frameRate = 24; public int Version { get; set; } public override string Extension { get { return ".xml"; } } public override string Name { get { return "D-Cinema SMPTE 2007"; } } public override bool IsTimeBased { get { return true; } } public override bool IsMine(List lines, string fileName) { var sb = new StringBuilder(); lines.ForEach(line => sb.AppendLine(line)); string xmlAsString = sb.ToString().Trim(); if (xmlAsString.Contains("http://www.smpte-ra.org/schemas/428-7/2010/DCST")) return false; if (xmlAsString.Contains(" 0; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return false; } } return false; } public override string ToText(Subtitle subtitle, string title) { Errors = null; var ss = Configuration.Settings.SubtitleSettings; if (!string.IsNullOrEmpty(ss.CurrentDCinemaEditRate)) { string[] temp = ss.CurrentDCinemaEditRate.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); double d1, d2; if (temp.Length == 2 && double.TryParse(temp[0], out d1) && double.TryParse(temp[1], out d2)) _frameRate = d1 / d2; } string xmlStructure = "" + Environment.NewLine + " urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e" + Environment.NewLine + " " + Environment.NewLine + " This is a subtitle file" + Environment.NewLine + " 2012-06-26T12:33:59.000-00:00" + Environment.NewLine + " 1" + Environment.NewLine + " en" + Environment.NewLine + " 25 1" + Environment.NewLine + " 25" + Environment.NewLine + " 00:00:00:00 " + Environment.NewLine + " urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391" + Environment.NewLine + " " + Environment.NewLine + " " + Environment.NewLine + " " + Environment.NewLine + " " + Environment.NewLine + ""; var xml = new XmlDocument(); xml.LoadXml(xmlStructure); xml.PreserveWhitespace = true; var nsmgr = new XmlNamespaceManager(xml.NameTable); nsmgr.AddNamespace("dcst", xml.DocumentElement.NamespaceURI); if (string.IsNullOrEmpty(ss.CurrentDCinemaMovieTitle)) ss.CurrentDCinemaMovieTitle = title; if (ss.CurrentDCinemaFontSize == 0 || string.IsNullOrEmpty(ss.CurrentDCinemaFontEffect)) Configuration.Settings.SubtitleSettings.InitializeDCinameSettings(true); xml.DocumentElement.SelectSingleNode("dcst:ContentTitleText", nsmgr).InnerText = ss.CurrentDCinemaMovieTitle; if (string.IsNullOrEmpty(ss.CurrentDCinemaSubtitleId) || !ss.CurrentDCinemaSubtitleId.StartsWith("urn:uuid:")) ss.CurrentDCinemaSubtitleId = "urn:uuid:" + Guid.NewGuid(); xml.DocumentElement.SelectSingleNode("dcst:Id", nsmgr).InnerText = ss.CurrentDCinemaSubtitleId; xml.DocumentElement.SelectSingleNode("dcst:ReelNumber", nsmgr).InnerText = ss.CurrentDCinemaReelNumber; xml.DocumentElement.SelectSingleNode("dcst:IssueDate", nsmgr).InnerText = ss.CurrentDCinemaIssueDate; if (string.IsNullOrEmpty(ss.CurrentDCinemaLanguage)) ss.CurrentDCinemaLanguage = "en"; xml.DocumentElement.SelectSingleNode("dcst:Language", nsmgr).InnerText = ss.CurrentDCinemaLanguage; if (ss.CurrentDCinemaEditRate == null && ss.CurrentDCinemaTimeCodeRate == null) { if (Configuration.Settings.General.CurrentFrameRate == 24) { ss.CurrentDCinemaEditRate = "24 1"; ss.CurrentDCinemaTimeCodeRate = "24"; } else { ss.CurrentDCinemaEditRate = "25 1"; ss.CurrentDCinemaTimeCodeRate = "25"; } } xml.DocumentElement.SelectSingleNode("dcst:EditRate", nsmgr).InnerText = ss.CurrentDCinemaEditRate; xml.DocumentElement.SelectSingleNode("dcst:TimeCodeRate", nsmgr).InnerText = ss.CurrentDCinemaTimeCodeRate; if (string.IsNullOrEmpty(ss.CurrentDCinemaStartTime)) ss.CurrentDCinemaStartTime = "00:00:00:00"; xml.DocumentElement.SelectSingleNode("dcst:StartTime", nsmgr).InnerText = ss.CurrentDCinemaStartTime; xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).InnerText = ss.CurrentDCinemaFontUri; int fontSize = ss.CurrentDCinemaFontSize; string loadedFontId = "Font1"; if (!string.IsNullOrEmpty(ss.CurrentDCinemaFontId)) loadedFontId = ss.CurrentDCinemaFontId; xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).Attributes["ID"].Value = loadedFontId; xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Size"].Value = fontSize.ToString(); xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Color"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontColor).TrimStart('#').ToUpper(); xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["ID"].Value = loadedFontId; xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Effect"].Value = ss.CurrentDCinemaFontEffect; xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["EffectColor"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontEffectColor).TrimStart('#').ToUpper(); XmlNode mainListFont = xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr); int no = 0; foreach (Paragraph p in subtitle.Paragraphs) { if (p.Text != null) { XmlNode subNode = xml.CreateElement("dcst:Subtitle", "dcst"); XmlAttribute id = xml.CreateAttribute("SpotNumber"); id.InnerText = (no + 1).ToString(); subNode.Attributes.Append(id); XmlAttribute fadeUpTime = xml.CreateAttribute("FadeUpTime"); fadeUpTime.InnerText = "00:00:00:00"; //Configuration.Settings.SubtitleSettings.DCinemaFadeUpDownTime.ToString(); subNode.Attributes.Append(fadeUpTime); XmlAttribute fadeDownTime = xml.CreateAttribute("FadeDownTime"); fadeDownTime.InnerText = "00:00:00:00"; //Configuration.Settings.SubtitleSettings.DCinemaFadeUpDownTime.ToString(); subNode.Attributes.Append(fadeDownTime); XmlAttribute start = xml.CreateAttribute("TimeIn"); start.InnerText = ConvertToTimeString(p.StartTime); subNode.Attributes.Append(start); XmlAttribute end = xml.CreateAttribute("TimeOut"); end.InnerText = ConvertToTimeString(p.EndTime); subNode.Attributes.Append(end); bool alignLeft = p.Text.StartsWith("{\\a1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a9}", StringComparison.Ordinal) || // sub station alpha p.Text.StartsWith("{\\an1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an7}", StringComparison.Ordinal); // advanced sub station alpha bool alignRight = p.Text.StartsWith("{\\a3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a7}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a11}", StringComparison.Ordinal) || // sub station alpha p.Text.StartsWith("{\\an3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal); // advanced sub station alpha bool alignVTop = p.Text.StartsWith("{\\a5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a7}", StringComparison.Ordinal) || // sub station alpha p.Text.StartsWith("{\\an7}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an8}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal); // advanced sub station alpha bool alignVCenter = p.Text.StartsWith("{\\a9}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a10}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a11}", StringComparison.Ordinal) || // sub station alpha p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal); // advanced sub station alpha string text = Utilities.RemoveSsaTags(p.Text); var lines = text.SplitToLines(); int vPos = 1 + lines.Length * 7; int vPosFactor = (int)Math.Round(fontSize / 7.4); if (alignVTop) { vPos = Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8 } else if (alignVCenter) { vPos = (int)Math.Round((lines.Length * vPosFactor * -1) / 2.0); } else { vPos = (lines.Length * vPosFactor) - vPosFactor + Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8 } bool isItalic = false; int fontNo = 0; Stack fontColors = new Stack(); foreach (string line in lines) { XmlNode textNode = xml.CreateElement("dcst:Text", "dcst"); XmlAttribute vPosition = xml.CreateAttribute("Vposition"); vPosition.InnerText = vPos.ToString(); textNode.Attributes.Append(vPosition); XmlAttribute vAlign = xml.CreateAttribute("Valign"); if (alignVTop) vAlign.InnerText = "top"; else if (alignVCenter) vAlign.InnerText = "center"; else vAlign.InnerText = "bottom"; textNode.Attributes.Append(vAlign); textNode.Attributes.Append(vAlign); XmlAttribute hAlign = xml.CreateAttribute("Halign"); if (alignLeft) hAlign.InnerText = "left"; else if (alignRight) hAlign.InnerText = "right"; else hAlign.InnerText = "center"; textNode.Attributes.Append(hAlign); XmlAttribute direction = xml.CreateAttribute("Direction"); direction.InnerText = "ltr"; textNode.Attributes.Append(direction); int i = 0; var txt = new StringBuilder(); var html = new StringBuilder(); XmlNode nodeTemp = xml.CreateElement("temp"); while (i < line.Length) { if (!isItalic && line.Substring(i).StartsWith("")) { if (txt.Length > 0) { nodeTemp.InnerText = txt.ToString(); html.Append(nodeTemp.InnerXml); txt.Clear(); } isItalic = true; i += 2; } else if (isItalic && line.Substring(i).StartsWith("")) { if (txt.Length > 0) { XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst"); XmlAttribute italic = xml.CreateAttribute("Italic"); italic.InnerText = "yes"; fontNode.Attributes.Append(italic); if (line.Length > i + 5 && line.Substring(i + 4).StartsWith("")) { XmlAttribute fontColor = xml.CreateAttribute("Color"); fontColor.InnerText = fontColors.Pop(); fontNode.Attributes.Append(fontColor); fontNo--; i += 7; } fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString()); html.Append(fontNode.OuterXml); txt.Clear(); } isItalic = false; i += 3; } else if (line.Substring(i).StartsWith(" 0 && line.Substring(i).StartsWith("")) { if (txt.Length > 0) { XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst"); XmlAttribute fontColor = xml.CreateAttribute("Color"); fontColor.InnerText = fontColors.Pop(); fontNode.Attributes.Append(fontColor); if (line.Length > i + 9 && line.Substring(i + 7).StartsWith("")) { XmlAttribute italic = xml.CreateAttribute("Italic"); italic.InnerText = "yes"; fontNode.Attributes.Append(italic); isItalic = false; i += 4; } fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString()); html.Append(fontNode.OuterXml); txt.Clear(); } fontNo--; i += 6; } else { txt.Append(line[i]); } i++; } if (fontNo > 0) { if (txt.Length > 0) { XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst"); XmlAttribute fontColor = xml.CreateAttribute("Color"); fontColor.InnerText = fontColors.Peek(); fontNode.Attributes.Append(fontColor); if (isItalic) { XmlAttribute italic = xml.CreateAttribute("Italic"); italic.InnerText = "yes"; fontNode.Attributes.Append(italic); } fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString()); html.Append(fontNode.OuterXml); } else if (html.Length > 0 && html.ToString().StartsWith("" + html.ToString().Replace("dcst:Font", "Font") + ""); XmlNode fontNode = xml.CreateElement("dcst:Font"); fontNode.InnerXml = temp.DocumentElement.SelectSingleNode("Font").InnerXml; foreach (XmlAttribute a in temp.DocumentElement.SelectSingleNode("Font").Attributes) { XmlAttribute newA = xml.CreateAttribute(a.Name); newA.InnerText = a.InnerText; fontNode.Attributes.Append(newA); } XmlAttribute fontColor = xml.CreateAttribute("Color"); fontColor.InnerText = fontColors.Peek(); fontNode.Attributes.Append(fontColor); html.Clear(); html.Append(fontNode.OuterXml); } } else if (isItalic) { if (txt.Length > 0) { XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst"); XmlAttribute italic = xml.CreateAttribute("Italic"); italic.InnerText = "yes"; fontNode.Attributes.Append(italic); fontNode.InnerText = HtmlUtil.RemoveHtmlTags(line); html.Append(fontNode.OuterXml); } } else { if (txt.Length > 0) { nodeTemp.InnerText = txt.ToString(); html.Append(nodeTemp.InnerXml); } } textNode.InnerXml = html.ToString(); subNode.AppendChild(textNode); if (alignVTop) vPos += vPosFactor; else vPos -= vPosFactor; } if (subNode.InnerXml.Length == 0) { // Empty text is just one space XmlNode textNode = xml.CreateElement("dcst:Text", "dcst"); textNode.InnerXml = " "; subNode.AppendChild(textNode); XmlAttribute vPosition = xml.CreateAttribute("Vposition"); vPosition.InnerText = vPos.ToString(); textNode.Attributes.Append(vPosition); XmlAttribute vAlign = xml.CreateAttribute("Valign"); vAlign.InnerText = "bottom"; textNode.Attributes.Append(vAlign); } mainListFont.AppendChild(subNode); no++; } } string result = ToUtf8XmlString(xml).Replace("encoding=\"utf-8\"", "encoding=\"UTF-8\"").Replace(" xmlns:dcst=\"dcst\"", string.Empty); const string res = "Nikse.SubtitleEdit.Resources.SMPTE-428-7-2007-DCST.xsd.gz"; System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); Stream strm = asm.GetManifestResourceStream(res); if (strm != null) { try { var xmld = new XmlDocument(); var rdr = new StreamReader(strm); var zip = new GZipStream(rdr.BaseStream, CompressionMode.Decompress); xmld.LoadXml(result); using (var xr = XmlReader.Create(zip)) { xmld.Schemas.Add(null, xr); xmld.Validate(ValidationCallBack); } } catch (Exception exception) { Errors = "Error validating xml via SMPTE - 428 - 7 - 2007 - DCST.xsd: " + exception.Message; } } return DCinemaSmpte2010.FixDcsTextSameLine(result); } private void ValidationCallBack(object sender, ValidationEventArgs e) { throw new Exception(e.Message); } public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) { _errorCount = 0; var sb = new StringBuilder(); lines.ForEach(line => sb.AppendLine(line)); var xml = new XmlDocument { XmlResolver = null }; xml.LoadXml(sb.ToString().Replace(" 0 && lastVPosition.Length > 0) pText.AppendLine(); lastVPosition = vPosition; } } bool alignLeft = false; bool alignRight = false; bool alignVTop = false; bool alignVCenter = false; if (innerNode.Attributes["Halign"] != null) { string hAlign = innerNode.Attributes["Halign"].InnerText; if (hAlign == "left") alignLeft = true; else if (hAlign == "right") alignRight = true; } if (innerNode.Attributes["Valign"] != null) { string hAlign = innerNode.Attributes["Valign"].InnerText; if (hAlign == "top") alignVTop = true; else if (hAlign == "center") alignVCenter = true; } if (alignLeft || alignRight || alignVCenter || alignVTop) { if (!pText.ToString().StartsWith("{\\an")) { string pre = string.Empty; if (alignVTop) { if (alignLeft) pre = "{\\an7}"; else if (alignRight) pre = "{\\an9}"; else pre = "{\\an8}"; } else if (alignVCenter) { if (alignLeft) pre = "{\\an4}"; else if (alignRight) pre = "{\\an6}"; else pre = "{\\an5}"; } else { if (alignLeft) pre = "{\\an1}"; else if (alignRight) pre = "{\\an3}"; } string temp = pre + pText; pText.Clear(); pText.Append(temp); } } if (innerNode.ChildNodes.Count == 0) { pText.Append(innerNode.InnerText); } else { foreach (XmlNode innerInnerNode in innerNode) { if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Italic"] != null && innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase)) { if (innerInnerNode.Attributes["Color"] != null) pText.Append("" + innerInnerNode.InnerText + ""); else pText.Append("" + innerInnerNode.InnerText + ""); } else if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Color"] != null) { if (innerInnerNode.Attributes["Italic"] != null && innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase)) pText.Append("" + innerInnerNode.InnerText + ""); else pText.Append("" + innerInnerNode.InnerText + ""); } else { pText.Append(innerInnerNode.InnerText); } } } break; default: pText.Append(innerNode.InnerText); break; } } string start = node.Attributes["TimeIn"].InnerText; string end = node.Attributes["TimeOut"].InnerText; if (node.ParentNode.Name == "Font" && node.ParentNode.Attributes["Italic"] != null && node.ParentNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase) && !pText.ToString().Contains("")) { string text = pText.ToString(); if (text.StartsWith("{\\an") && text.Length > 6) text = text.Insert(6, "") + ""; else text = "" + text + ""; pText = new StringBuilder(text); } subtitle.Paragraphs.Add(new Paragraph(GetTimeCode(start), GetTimeCode(end), pText.ToString())); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); _errorCount++; } } if (subtitle.Paragraphs.Count > 0) subtitle.Header = xml.OuterXml; // save id/language/font for later use subtitle.Renumber(); } private static string GetColorStringFromDCinema(string p) { string s = p.ToLower().Trim(); if (s.Replace("#", string.Empty). Replace("0", string.Empty). Replace("1", string.Empty). Replace("2", string.Empty). Replace("3", string.Empty). Replace("4", string.Empty). Replace("5", string.Empty). Replace("6", string.Empty). Replace("7", string.Empty). Replace("8", string.Empty). Replace("9", string.Empty). Replace("a", string.Empty). Replace("b", string.Empty). Replace("c", string.Empty). Replace("d", string.Empty). Replace("e", string.Empty). Replace("f", string.Empty).Length == 0) { if (s.StartsWith('#')) return s; return "#" + s; } return p; } private TimeCode GetTimeCode(string s) { var parts = s.Split(new[] { ':', '.', ',' }); int milliseconds = (int)Math.Round(int.Parse(parts[3]) * (TimeCode.BaseUnit / _frameRate)); if (milliseconds > 999) milliseconds = 999; return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), milliseconds); } private string ConvertToTimeString(TimeCode time) { return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, DCinemaSmpte2010.MsToFramesMaxFrameRate(time.Milliseconds, _frameRate)); } } }