//http://www.w3.org/TR/ttaf1-dfxp/ //Timed Text Markup Language (TTML) 1.0 //W3C Recommendation 18 November 2010 using System; using System.Collections.Generic; using System.Globalization; using System.Text; using System.Text.RegularExpressions; using System.Xml; namespace Nikse.SubtitleEdit.Core.SubtitleFormats { public class TimedText10 : SubtitleFormat { public override string Extension => ".xml"; public const string NameOfFormat = "Timed Text 1.0"; public override string Name => NameOfFormat; public override bool IsTimeBased => true; public static string TtmlNamespace = "http://www.w3.org/ns/ttml"; public static string TtmlParameterNamespace = "http://www.w3.org/ns/ttml#parameter"; public static string TtmlStylingNamespace = "http://www.w3.org/ns/ttml#styling"; public static string TtmlMetadataNamespace = "http://www.w3.org/ns/ttml#metadata"; 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("xmlns:tts=\"http://www.w3.org/2006/04")) return false; if (xmlAsString.Contains("http://www.w3.org/ns/ttml")) { xmlAsString = xmlAsString.RemoveControlCharactersButWhiteSpace(); var xml = new XmlDocument { XmlResolver = null }; try { xml.LoadXml(xmlAsString); var nsmgr = new XmlNamespaceManager(xml.NameTable); nsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml"); var nds = xml.DocumentElement.SelectSingleNode("ttml:body", nsmgr); var paragraphs = nds.SelectNodes("//ttml:p", nsmgr); return paragraphs != null && paragraphs.Count > 0; } catch { try { xml.LoadXml(xmlAsString.Replace(" & ", " & ").Replace("Q&A", "Q&A")); var nsmgr = new XmlNamespaceManager(xml.NameTable); nsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml"); var nds = xml.DocumentElement.SelectSingleNode("ttml:body", nsmgr); var paragraphs = nds.SelectNodes("//ttml:p", nsmgr); if (paragraphs != null && (paragraphs.Count > 0 && new NetflixTimedText().IsMine(lines, fileName))) return false; if (paragraphs != null && (paragraphs.Count > 0 && new SmpteTt2052().IsMine(lines, fileName))) return false; return paragraphs != null && paragraphs.Count > 0; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); } } } return false; } internal static string ConvertToTimeString(TimeCode time) { var timeCodeFormat = Configuration.Settings.SubtitleSettings.TimedText10TimeCodeFormat.Trim().ToLowerInvariant(); if (timeCodeFormat == "source" && !string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.TimedText10TimeCodeFormatSource)) { timeCodeFormat = Configuration.Settings.SubtitleSettings.TimedText10TimeCodeFormatSource.Trim().ToLowerInvariant(); } switch (timeCodeFormat) { case "source": case "seconds": return string.Format(CultureInfo.InvariantCulture, "{0:0.0##}s", time.TotalSeconds); case "milliseconds": return string.Format(CultureInfo.InvariantCulture, "{0}ms", time.TotalMilliseconds); case "ticks": return string.Format(CultureInfo.InvariantCulture, "{0}t", TimeSpan.FromMilliseconds(time.TotalMilliseconds).Ticks); case "hh:mm:ss.ms": return string.Format(CultureInfo.InvariantCulture, "{0:00}:{1:00}:{2:00}.{3:000}", time.Hours, time.Minutes, time.Seconds, time.Milliseconds); case "hh:mm:ss.ms-two-digits": return string.Format(CultureInfo.InvariantCulture, "{0:00}:{1:00}:{2:00}.{3:00}", time.Hours, time.Minutes, time.Seconds, (int)Math.Round(time.Milliseconds / 10.0)); case "hh:mm:ss,ms": return string.Format(CultureInfo.InvariantCulture, "{0:00}:{1:00}:{2:00},{3:000}", time.Hours, time.Minutes, time.Seconds, time.Milliseconds); default: return string.Format(CultureInfo.InvariantCulture, "{0:00}:{1:00}:{2:00}:{3:00}", time.Hours, time.Minutes, time.Seconds, MillisecondsToFramesMaxFrameRate(time.Milliseconds)); } } public static void AddStyleToXml(XmlDocument xml, XmlNode head, XmlNamespaceManager nsmgr, string name, string fontFamily, string fontWeight, string fontStyle, string color, string fontSize) { var styleNode = xml.CreateNode(XmlNodeType.Element, string.Empty, "style", nsmgr.LookupNamespace("ttml")); XmlAttribute attr = xml.CreateAttribute("xml:id", TtmlStylingNamespace); attr.InnerText = name; styleNode.Attributes.Append(attr); attr = xml.CreateAttribute("tts:fontFamily", TtmlStylingNamespace); attr.InnerText = fontFamily; styleNode.Attributes.Append(attr); attr = xml.CreateAttribute("tts:fontWeight", TtmlStylingNamespace); attr.InnerText = fontWeight; styleNode.Attributes.Append(attr); attr = xml.CreateAttribute("tts:fontStyle", TtmlStylingNamespace); attr.InnerText = fontStyle; styleNode.Attributes.Append(attr); attr = xml.CreateAttribute("tts:color", TtmlStylingNamespace); attr.InnerText = color; styleNode.Attributes.Append(attr); attr = xml.CreateAttribute("tts:fontSize", TtmlStylingNamespace); attr.InnerText = fontSize; styleNode.Attributes.Append(attr); foreach (XmlNode innerNode in head.ChildNodes) { if (innerNode.Name == "styling") { innerNode.AppendChild(styleNode); break; } } } public override string ToText(Subtitle subtitle, string title) { bool hasStyleHead = false; bool convertedFromSubStationAlpha = false; if (subtitle.Header != null) { try { var x = new XmlDocument(); x.LoadXml(subtitle.Header); var xnsmgr = new XmlNamespaceManager(x.NameTable); xnsmgr.AddNamespace("ttml", TtmlNamespace); hasStyleHead = x.DocumentElement.SelectSingleNode("ttml:head", xnsmgr) != null; } catch { } if (!hasStyleHead && (subtitle.Header.Contains("[V4+ Styles]") || subtitle.Header.Contains("[V4 Styles]"))) { subtitle.Header = SubStationAlphaHeaderToTimedText(subtitle); // save new xml with styles in header convertedFromSubStationAlpha = true; hasStyleHead = true; } } var xml = new XmlDocument(); var nsmgr = new XmlNamespaceManager(xml.NameTable); nsmgr.AddNamespace("ttml", TtmlNamespace); nsmgr.AddNamespace("ttp", TtmlParameterNamespace); nsmgr.AddNamespace("tts", TtmlStylingNamespace); nsmgr.AddNamespace("ttm", TtmlMetadataNamespace); string xmlStructure = "" + Environment.NewLine + "" + Environment.NewLine + " " + Environment.NewLine + " " + Environment.NewLine + " " + Environment.NewLine + " " + Environment.NewLine + " " + Environment.NewLine + "