2016-02-08 21:11:03 +01:00
|
|
|
|
//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;
|
2016-12-14 09:16:01 +01:00
|
|
|
|
using System.Text.RegularExpressions;
|
2016-02-08 21:11:03 +01:00
|
|
|
|
using System.Xml;
|
|
|
|
|
|
|
|
|
|
namespace Nikse.SubtitleEdit.Core.SubtitleFormats
|
|
|
|
|
{
|
|
|
|
|
public class TimedText10 : SubtitleFormat
|
|
|
|
|
{
|
2017-02-22 15:37:48 +01:00
|
|
|
|
public override string Extension => ".xml";
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
2016-06-02 19:01:20 +02:00
|
|
|
|
public const string NameOfFormat = "Timed Text 1.0";
|
|
|
|
|
|
2017-02-22 15:37:48 +01:00
|
|
|
|
public override string Name => NameOfFormat;
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
2019-01-26 12:11:57 +01:00
|
|
|
|
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";
|
2016-12-27 16:20:38 +01:00
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
public override bool IsMine(List<string> 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"))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
return false;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
|
|
|
|
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);
|
2017-02-22 15:37:48 +01:00
|
|
|
|
return paragraphs != null && paragraphs.Count > 0;
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
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);
|
2016-07-31 15:41:37 +02:00
|
|
|
|
|
2017-02-22 15:37:48 +01:00
|
|
|
|
if (paragraphs != null && (paragraphs.Count > 0 && new NetflixTimedText().IsMine(lines, fileName)))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-02-22 15:37:48 +01:00
|
|
|
|
return false;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2016-12-23 17:08:09 +01:00
|
|
|
|
|
2017-02-22 15:37:48 +01:00
|
|
|
|
if (paragraphs != null && (paragraphs.Count > 0 && new SmpteTt2052().IsMine(lines, fileName)))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-07-31 15:41:37 +02:00
|
|
|
|
return false;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2016-12-23 17:08:09 +01:00
|
|
|
|
|
2017-02-22 15:37:48 +01:00
|
|
|
|
return paragraphs != null && paragraphs.Count > 0;
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
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"));
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
XmlAttribute attr = xml.CreateAttribute("xml:id", TtmlStylingNamespace);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
attr.InnerText = name;
|
|
|
|
|
styleNode.Attributes.Append(attr);
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
attr = xml.CreateAttribute("tts:fontFamily", TtmlStylingNamespace);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
attr.InnerText = fontFamily;
|
|
|
|
|
styleNode.Attributes.Append(attr);
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
attr = xml.CreateAttribute("tts:fontWeight", TtmlStylingNamespace);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
attr.InnerText = fontWeight;
|
|
|
|
|
styleNode.Attributes.Append(attr);
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
attr = xml.CreateAttribute("tts:fontStyle", TtmlStylingNamespace);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
attr.InnerText = fontStyle;
|
|
|
|
|
styleNode.Attributes.Append(attr);
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
attr = xml.CreateAttribute("tts:color", TtmlStylingNamespace);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
attr.InnerText = color;
|
|
|
|
|
styleNode.Attributes.Append(attr);
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
attr = xml.CreateAttribute("tts:fontSize", TtmlStylingNamespace);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
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)
|
|
|
|
|
{
|
2016-06-02 19:01:20 +02:00
|
|
|
|
bool hasStyleHead = false;
|
|
|
|
|
bool convertedFromSubStationAlpha = false;
|
2016-02-08 21:11:03 +01:00
|
|
|
|
if (subtitle.Header != null)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var x = new XmlDocument();
|
|
|
|
|
x.LoadXml(subtitle.Header);
|
|
|
|
|
var xnsmgr = new XmlNamespaceManager(x.NameTable);
|
2017-06-13 12:30:29 +02:00
|
|
|
|
xnsmgr.AddNamespace("ttml", TtmlNamespace);
|
2016-06-02 19:01:20 +02:00
|
|
|
|
hasStyleHead = x.DocumentElement.SelectSingleNode("ttml:head", xnsmgr) != null;
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
}
|
2016-08-10 04:40:15 +02:00
|
|
|
|
if (!hasStyleHead && (subtitle.Header.Contains("[V4+ Styles]") || subtitle.Header.Contains("[V4 Styles]")))
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2016-06-02 19:01:20 +02:00
|
|
|
|
subtitle.Header = SubStationAlphaHeaderToTimedText(subtitle); // save new xml with styles in header
|
|
|
|
|
convertedFromSubStationAlpha = true;
|
|
|
|
|
hasStyleHead = true;
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var xml = new XmlDocument();
|
|
|
|
|
var nsmgr = new XmlNamespaceManager(xml.NameTable);
|
2017-06-13 12:30:29 +02:00
|
|
|
|
nsmgr.AddNamespace("ttml", TtmlNamespace);
|
|
|
|
|
nsmgr.AddNamespace("ttp", TtmlParameterNamespace);
|
|
|
|
|
nsmgr.AddNamespace("tts", TtmlStylingNamespace);
|
|
|
|
|
nsmgr.AddNamespace("ttm", TtmlMetadataNamespace);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
string xmlStructure = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" + Environment.NewLine +
|
2017-06-13 12:30:29 +02:00
|
|
|
|
"<tt xmlns=\"" + TtmlNamespace + "\" xmlns:ttp=\"" + TtmlParameterNamespace + "\" ttp:timeBase=\"media\" xmlns:tts=\"" + TtmlStylingNamespace + "\" xml:lang=\"en\" xmlns:ttm=\"" + TtmlMetadataNamespace + "\">" + Environment.NewLine +
|
2016-02-08 21:11:03 +01:00
|
|
|
|
" <head>" + Environment.NewLine +
|
|
|
|
|
" <metadata>" + Environment.NewLine +
|
|
|
|
|
" <ttm:title></ttm:title>" + Environment.NewLine +
|
|
|
|
|
" </metadata>" + Environment.NewLine +
|
|
|
|
|
" <styling>" + Environment.NewLine +
|
2017-02-28 12:47:38 +01:00
|
|
|
|
" <style xml:id=\"s0\" tts:backgroundColor=\"black\" tts:fontStyle=\"normal\" tts:fontSize=\"16px\" tts:fontFamily=\"sansSerif\" tts:color=\"white\" />" + Environment.NewLine +
|
2016-02-08 21:11:03 +01:00
|
|
|
|
" </styling>" + Environment.NewLine +
|
2017-02-28 18:02:02 +01:00
|
|
|
|
" <layout>" + Environment.NewLine +
|
|
|
|
|
// Left column
|
|
|
|
|
" <region tts:extent=\"80% 40%\" tts:origin=\"10% 10%\" tts:displayAlign=\"before\" tts:textAlign=\"start\" xml:id=\"topLeft\" />" + Environment.NewLine +
|
|
|
|
|
" <region tts:extent=\"80% 40%\" tts:origin=\"10% 30%\" tts:displayAlign=\"center\" tts:textAlign=\"start\" xml:id=\"centerLeft\" />" + Environment.NewLine +
|
|
|
|
|
" <region tts:extent=\"80% 40%\" tts:origin=\"10% 50%\" tts:displayAlign=\"after\" tts:textAlign=\"start\" xml:id=\"bottomLeft\" />" + Environment.NewLine +
|
|
|
|
|
// Midle column
|
|
|
|
|
" <region tts:extent=\"80% 40%\" tts:origin=\"10% 10%\" tts:displayAlign=\"before\" tts:textAlign=\"center\" xml:id=\"topCenter\" />" + Environment.NewLine +
|
|
|
|
|
" <region tts:extent=\"80% 40%\" tts:origin=\"10% 30%\" tts:displayAlign=\"center\" tts:textAlign=\"center\" xml:id=\"centerСenter\" />" + Environment.NewLine +
|
|
|
|
|
" <region tts:extent=\"80% 40%\" tts:origin=\"10% 50%\" tts:displayAlign=\"after\" tts:textAlign=\"center\" xml:id=\"bottomCenter\" />" + Environment.NewLine +
|
|
|
|
|
// Right column
|
|
|
|
|
" <region tts:extent=\"80% 40%\" tts:origin=\"10% 10%\" tts:displayAlign=\"before\" tts:textAlign=\"end\" xml:id=\"topRight\" />" + Environment.NewLine +
|
|
|
|
|
" <region tts:extent=\"80% 40%\" tts:origin=\"10% 30%\" tts:displayAlign=\"center\" tts:textAlign=\"end\" xml:id=\"centerRight\" />" + Environment.NewLine +
|
|
|
|
|
" <region tts:extent=\"80% 40%\" tts:origin=\"10% 50%\" tts:displayAlign=\"after\" tts:textAlign=\"end\" xml:id=\"bottomRight\" />" + Environment.NewLine +
|
|
|
|
|
" </layout>" + Environment.NewLine +
|
2016-02-08 21:11:03 +01:00
|
|
|
|
" </head>" + Environment.NewLine +
|
|
|
|
|
" <body style=\"s0\">" + Environment.NewLine +
|
|
|
|
|
" <div />" + Environment.NewLine +
|
|
|
|
|
" </body>" + Environment.NewLine +
|
|
|
|
|
"</tt>";
|
2016-06-23 00:25:10 +02:00
|
|
|
|
if (!hasStyleHead || string.IsNullOrEmpty(subtitle.Header))
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
|
|
|
|
xml.LoadXml(xmlStructure);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
xml.LoadXml(subtitle.Header);
|
|
|
|
|
XmlNode bodyNode = xml.DocumentElement.SelectSingleNode("//ttml:body", nsmgr);
|
2018-03-07 19:27:52 +01:00
|
|
|
|
XmlNode divNode = null;
|
|
|
|
|
if (bodyNode != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2018-03-07 19:27:52 +01:00
|
|
|
|
divNode = bodyNode.SelectSingleNode("ttml:div", nsmgr);
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
if (divNode == null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
divNode = xml.DocumentElement.SelectSingleNode("//ttml:body", nsmgr).FirstChild;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
if (divNode != null)
|
|
|
|
|
{
|
|
|
|
|
// Remove all but first div
|
|
|
|
|
int innerNodeCount = 0;
|
|
|
|
|
var innerNodeList = new List<XmlNode>();
|
|
|
|
|
foreach (XmlNode innerNode in bodyNode.SelectNodes("ttml:div", nsmgr))
|
|
|
|
|
{
|
|
|
|
|
if (innerNodeCount > 0)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
innerNodeList.Add(innerNode);
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
innerNodeCount++;
|
|
|
|
|
}
|
|
|
|
|
foreach (XmlNode child in innerNodeList)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
bodyNode.RemoveChild(child);
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
|
|
|
|
var lst = new List<XmlNode>();
|
|
|
|
|
foreach (XmlNode child in divNode.ChildNodes)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
lst.Add(child);
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
foreach (XmlNode child in lst)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
divNode.RemoveChild(child);
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
else if (bodyNode == null) // Don't reload xml if body node exists, otherwise we rewrite header edited by styling form
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
|
|
|
|
xml.LoadXml(xmlStructure);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-14 15:36:42 +01:00
|
|
|
|
// Declare namespaces in the root node if not declared
|
2017-06-13 12:30:29 +02:00
|
|
|
|
xml.DocumentElement.SetAttribute("xmlns", TtmlNamespace);
|
|
|
|
|
xml.DocumentElement.SetAttribute("xmlns:ttp", TtmlParameterNamespace);
|
|
|
|
|
xml.DocumentElement.SetAttribute("xmlns:tts", TtmlStylingNamespace);
|
|
|
|
|
xml.DocumentElement.SetAttribute("xmlns:ttm", TtmlMetadataNamespace);
|
2016-12-14 09:16:01 +01:00
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
XmlNode body = xml.DocumentElement.SelectSingleNode("ttml:body", nsmgr);
|
|
|
|
|
string defaultStyle = Guid.NewGuid().ToString();
|
|
|
|
|
if (body.Attributes["style"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
defaultStyle = body.Attributes["style"].InnerText;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
|
|
|
|
XmlNode div = xml.DocumentElement.SelectSingleNode("//ttml:body", nsmgr).SelectSingleNode("ttml:div", nsmgr);
|
|
|
|
|
if (div == null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
div = xml.DocumentElement.SelectSingleNode("//ttml:body", nsmgr).FirstChild;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
2017-04-26 18:51:34 +02:00
|
|
|
|
if (div == null)
|
2017-08-29 20:26:24 +02:00
|
|
|
|
{
|
2017-04-26 18:51:34 +02:00
|
|
|
|
div = xml.CreateElement("div");
|
|
|
|
|
body.AppendChild(div);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
int no = 0;
|
2017-02-28 18:02:02 +01:00
|
|
|
|
var headerStyles = GetStylesFromHeader(ToUtf8XmlString(xml));
|
|
|
|
|
var regions = GetRegionsFromHeader(ToUtf8XmlString(xml));
|
2016-02-08 21:11:03 +01:00
|
|
|
|
var languages = GetUsedLanguages(subtitle);
|
|
|
|
|
if (languages.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
var divParentNode = div.ParentNode;
|
|
|
|
|
|
|
|
|
|
foreach (Paragraph p in subtitle.Paragraphs)
|
|
|
|
|
{
|
|
|
|
|
if (p.Language == null)
|
|
|
|
|
{
|
|
|
|
|
if (p.NewSection)
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
div = xml.CreateElement("div", TtmlNamespace);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
divParentNode.AppendChild(div);
|
|
|
|
|
}
|
|
|
|
|
XmlNode paragraph = MakeParagraph(subtitle, xml, defaultStyle, no, headerStyles, regions, p);
|
|
|
|
|
div.AppendChild(paragraph);
|
|
|
|
|
no++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (string language in languages)
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
div = xml.CreateElement("div", TtmlNamespace);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
XmlAttribute attr = xml.CreateAttribute("xml:lang", "http://www.w3.org/XML/1998/namespace");
|
|
|
|
|
attr.Value = language;
|
|
|
|
|
div.Attributes.Append(attr);
|
|
|
|
|
divParentNode.AppendChild(div);
|
|
|
|
|
bool firstParagraph = true;
|
|
|
|
|
foreach (Paragraph p in subtitle.Paragraphs)
|
|
|
|
|
{
|
2018-01-17 19:59:48 +01:00
|
|
|
|
if (p.Language != null && p.Language.Equals(language, StringComparison.OrdinalIgnoreCase))
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
|
|
|
|
if (p.NewSection && !firstParagraph)
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
div = xml.CreateElement("div", TtmlNamespace);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
attr = xml.CreateAttribute("xml:lang", "http://www.w3.org/XML/1998/namespace");
|
|
|
|
|
attr.Value = language;
|
|
|
|
|
div.Attributes.Append(attr);
|
|
|
|
|
divParentNode.AppendChild(div);
|
|
|
|
|
}
|
|
|
|
|
firstParagraph = false;
|
|
|
|
|
XmlNode paragraph = MakeParagraph(subtitle, xml, defaultStyle, no, headerStyles, regions, p);
|
|
|
|
|
div.AppendChild(paragraph);
|
|
|
|
|
no++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
|
|
|
|
|
if (divParentNode != null && divParentNode.HasChildNodes && !divParentNode.FirstChild.HasChildNodes)
|
|
|
|
|
{
|
|
|
|
|
divParentNode.RemoveChild(divParentNode.FirstChild);
|
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var divParentNode = div.ParentNode;
|
|
|
|
|
|
|
|
|
|
foreach (Paragraph p in subtitle.Paragraphs)
|
|
|
|
|
{
|
|
|
|
|
if (p.NewSection)
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
div = xml.CreateElement("div", TtmlNamespace);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
divParentNode.AppendChild(div);
|
|
|
|
|
}
|
2016-06-02 19:01:20 +02:00
|
|
|
|
if (convertedFromSubStationAlpha && string.IsNullOrEmpty(p.Style))
|
|
|
|
|
{
|
|
|
|
|
p.Style = p.Extra;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
XmlNode paragraph = MakeParagraph(subtitle, xml, defaultStyle, no, headerStyles, regions, p);
|
|
|
|
|
div.AppendChild(paragraph);
|
|
|
|
|
no++;
|
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
|
|
|
|
|
if (divParentNode != null && divParentNode.HasChildNodes && !divParentNode.FirstChild.HasChildNodes)
|
|
|
|
|
{
|
|
|
|
|
divParentNode.RemoveChild(divParentNode.FirstChild);
|
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-14 15:36:42 +01:00
|
|
|
|
return ToUtf8XmlString(xml).Replace(" xmlns=\"\"", string.Empty).Replace(" xmlns:tts=\"http://www.w3.org/ns/10/ttml#style\">", ">");
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-06-02 19:01:20 +02:00
|
|
|
|
public static string SubStationAlphaHeaderToTimedText(Subtitle subtitle)
|
|
|
|
|
{
|
|
|
|
|
var x = new XmlDocument();
|
|
|
|
|
x.LoadXml(new TimedText10().ToText(new Subtitle(), "tt")); // load default xml
|
|
|
|
|
var xnsmgr = new XmlNamespaceManager(x.NameTable);
|
2017-06-13 12:30:29 +02:00
|
|
|
|
xnsmgr.AddNamespace("ttml", TtmlNamespace);
|
2016-06-02 19:01:20 +02:00
|
|
|
|
var styleHead = x.DocumentElement.SelectSingleNode("ttml:head", xnsmgr);
|
|
|
|
|
styleHead.SelectSingleNode("ttml:styling", xnsmgr).RemoveAll();
|
|
|
|
|
foreach (string styleName in AdvancedSubStationAlpha.GetStylesFromHeader(subtitle.Header))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var ssaStyle = AdvancedSubStationAlpha.GetSsaStyle(styleName, subtitle.Header);
|
|
|
|
|
if (ssaStyle != null)
|
|
|
|
|
{
|
|
|
|
|
string fontStyle = "normal";
|
|
|
|
|
if (ssaStyle.Italic)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-06-02 19:01:20 +02:00
|
|
|
|
fontStyle = "italic";
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-06-02 19:01:20 +02:00
|
|
|
|
string fontWeight = "normal";
|
|
|
|
|
if (ssaStyle.Bold)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-06-02 19:01:20 +02:00
|
|
|
|
fontWeight = "bold";
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-06-02 19:01:20 +02:00
|
|
|
|
AddStyleToXml(x, styleHead, xnsmgr, ssaStyle.Name, ssaStyle.FontName, fontWeight, fontStyle, Utilities.ColorToHex(ssaStyle.Primary), ssaStyle.FontSize.ToString(CultureInfo.InvariantCulture));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
// ignored
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return x.OuterXml;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
private static XmlNode MakeParagraph(Subtitle subtitle, XmlDocument xml, string defaultStyle, int no, List<string> headerStyles, List<string> regions, Paragraph p)
|
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
XmlNode paragraph = xml.CreateElement("p", "http://www.w3.org/ns/ttml");
|
2016-03-28 15:29:26 +02:00
|
|
|
|
string text = p.Text.RemoveControlCharactersButWhiteSpace();
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
2016-02-15 23:04:02 +01:00
|
|
|
|
string region = GetEffect(p, "region");
|
2017-04-26 18:51:34 +02:00
|
|
|
|
if (p != null && !string.IsNullOrEmpty(p.Region) && regions.Contains(region))
|
|
|
|
|
{
|
|
|
|
|
region = p.Region;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-28 18:02:02 +01:00
|
|
|
|
if (string.IsNullOrEmpty(region))
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2017-03-15 11:43:28 +01:00
|
|
|
|
if (text.StartsWith("{\\an1}", StringComparison.Ordinal) && AddDefaultRegionIfNotExists(xml, "bottomLeft"))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-02-28 18:02:02 +01:00
|
|
|
|
region = "bottomLeft";
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-02-28 18:02:02 +01:00
|
|
|
|
|
2017-03-15 11:43:28 +01:00
|
|
|
|
if (text.StartsWith("{\\an2}", StringComparison.Ordinal) && AddDefaultRegionIfNotExists(xml, "bottomCenter"))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-02-28 18:02:02 +01:00
|
|
|
|
region = "bottomCenter";
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-02-28 18:02:02 +01:00
|
|
|
|
|
2017-03-15 11:43:28 +01:00
|
|
|
|
if (text.StartsWith("{\\an3}", StringComparison.Ordinal) && AddDefaultRegionIfNotExists(xml, "bottomRight"))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-02-28 18:02:02 +01:00
|
|
|
|
region = "bottomRight";
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-02-28 18:02:02 +01:00
|
|
|
|
|
2017-03-15 11:43:28 +01:00
|
|
|
|
if (text.StartsWith("{\\an4}", StringComparison.Ordinal) && AddDefaultRegionIfNotExists(xml, "centerLeft"))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-02-28 18:02:02 +01:00
|
|
|
|
region = "centerLeft";
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-02-28 18:02:02 +01:00
|
|
|
|
|
2017-03-15 11:43:28 +01:00
|
|
|
|
if (text.StartsWith("{\\an5}", StringComparison.Ordinal) && AddDefaultRegionIfNotExists(xml, "centerСenter"))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-02-28 18:02:02 +01:00
|
|
|
|
region = "centerСenter";
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-02-28 18:02:02 +01:00
|
|
|
|
|
2017-03-15 11:43:28 +01:00
|
|
|
|
if (text.StartsWith("{\\an6}", StringComparison.Ordinal) && AddDefaultRegionIfNotExists(xml, "centerRight"))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-02-28 18:02:02 +01:00
|
|
|
|
region = "centerRight";
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-02-28 18:02:02 +01:00
|
|
|
|
|
2017-03-15 11:43:28 +01:00
|
|
|
|
if (text.StartsWith("{\\an7}", StringComparison.Ordinal) && AddDefaultRegionIfNotExists(xml, "topLeft"))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-02-28 18:02:02 +01:00
|
|
|
|
region = "topLeft";
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-02-28 18:02:02 +01:00
|
|
|
|
|
2017-03-15 11:43:28 +01:00
|
|
|
|
if (text.StartsWith("{\\an8}", StringComparison.Ordinal) && AddDefaultRegionIfNotExists(xml, "topCenter"))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-01-26 22:02:22 +01:00
|
|
|
|
region = "topCenter";
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-02-28 18:02:02 +01:00
|
|
|
|
|
2017-03-15 11:43:28 +01:00
|
|
|
|
if (text.StartsWith("{\\an9}", StringComparison.Ordinal) && AddDefaultRegionIfNotExists(xml, "topRight"))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-02-28 18:02:02 +01:00
|
|
|
|
region = "topRight";
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
text = Utilities.RemoveSsaTags(text);
|
2017-02-04 10:18:59 +01:00
|
|
|
|
text = HtmlUtil.FixInvalidItalicTags(text);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
2016-12-14 09:16:01 +01:00
|
|
|
|
// Trying to parse and convert pararagraph content
|
|
|
|
|
try
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2016-12-14 09:16:01 +01:00
|
|
|
|
text = string.Join("<br/>", text.SplitToLines());
|
|
|
|
|
XmlDocument paragraphContent = new XmlDocument();
|
2019-06-26 21:13:56 +02:00
|
|
|
|
paragraphContent.LoadXml($"<root>{text.Replace("&", "&")}</root>");
|
2017-06-13 12:30:29 +02:00
|
|
|
|
ConvertParagraphNodeToTtmlNode(paragraphContent.DocumentElement, xml, paragraph);
|
2016-12-14 09:16:01 +01:00
|
|
|
|
}
|
2016-12-23 17:08:09 +01:00
|
|
|
|
catch // Wrong markup, clear it
|
2016-12-14 09:16:01 +01:00
|
|
|
|
{
|
|
|
|
|
text = Regex.Replace(text, "[<>]", "");
|
|
|
|
|
paragraph.AppendChild(xml.CreateTextNode(text));
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XmlAttribute start = xml.CreateAttribute("begin");
|
|
|
|
|
start.InnerText = ConvertToTimeString(p.StartTime);
|
|
|
|
|
paragraph.Attributes.Append(start);
|
|
|
|
|
|
2017-02-28 12:47:38 +01:00
|
|
|
|
XmlAttribute id = xml.CreateAttribute("xml:id");
|
2016-02-08 21:11:03 +01:00
|
|
|
|
id.InnerText = "p" + no;
|
|
|
|
|
paragraph.Attributes.Append(id);
|
|
|
|
|
|
|
|
|
|
XmlAttribute end = xml.CreateAttribute("end");
|
|
|
|
|
end.InnerText = ConvertToTimeString(p.EndTime);
|
|
|
|
|
paragraph.Attributes.Append(end);
|
|
|
|
|
|
2016-02-15 23:04:02 +01:00
|
|
|
|
if (!string.IsNullOrEmpty(region))
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
|
|
|
|
XmlAttribute regionAttribute = xml.CreateAttribute("region");
|
|
|
|
|
regionAttribute.InnerText = region;
|
|
|
|
|
paragraph.Attributes.Append(regionAttribute);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-15 23:04:02 +01:00
|
|
|
|
string xmlSpace = GetEffect(p, "xml:space");
|
|
|
|
|
if (!string.IsNullOrEmpty(xmlSpace))
|
|
|
|
|
{
|
2016-01-26 22:02:22 +01:00
|
|
|
|
XmlAttribute xmlSpaceAttribute = xml.CreateAttribute("xml:space");
|
2016-02-15 23:04:02 +01:00
|
|
|
|
xmlSpaceAttribute.InnerText = xmlSpace;
|
|
|
|
|
paragraph.Attributes.Append(xmlSpaceAttribute);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string ttsFontSize = GetEffect(p, "tts:fontSize");
|
|
|
|
|
if (!string.IsNullOrEmpty(ttsFontSize))
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
XmlAttribute ttsFontSizeAttribute = xml.CreateAttribute("tts:fontSize", TtmlStylingNamespace);
|
2016-02-15 23:04:02 +01:00
|
|
|
|
ttsFontSizeAttribute.InnerText = ttsFontSize;
|
|
|
|
|
paragraph.Attributes.Append(ttsFontSizeAttribute);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string ttsFontFamily = GetEffect(p, "tts:fontFamily");
|
|
|
|
|
if (!string.IsNullOrEmpty(ttsFontFamily))
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
XmlAttribute ttsFontFamilyAttribute = xml.CreateAttribute("tts:fontFamily", TtmlStylingNamespace);
|
2016-02-15 23:04:02 +01:00
|
|
|
|
ttsFontFamilyAttribute.InnerText = ttsFontFamily;
|
|
|
|
|
paragraph.Attributes.Append(ttsFontFamilyAttribute);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string ttsBackgroundColor = GetEffect(p, "tts:backgroundColor");
|
|
|
|
|
if (!string.IsNullOrEmpty(ttsBackgroundColor))
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
XmlAttribute ttsBackgroundColorAttribute = xml.CreateAttribute("tts:backgroundColor", TtmlStylingNamespace);
|
2016-02-15 23:04:02 +01:00
|
|
|
|
ttsBackgroundColorAttribute.InnerText = ttsBackgroundColor;
|
|
|
|
|
paragraph.Attributes.Append(ttsBackgroundColorAttribute);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string ttsOrigin = GetEffect(p, "tts:origin");
|
|
|
|
|
if (!string.IsNullOrEmpty(ttsOrigin))
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
XmlAttribute ttsOriginAttribute = xml.CreateAttribute("tts:origin", TtmlStylingNamespace);
|
2016-02-15 23:04:02 +01:00
|
|
|
|
ttsOriginAttribute.InnerText = ttsOrigin;
|
|
|
|
|
paragraph.Attributes.Append(ttsOriginAttribute);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string ttsExtent = GetEffect(p, "tts:extent");
|
|
|
|
|
if (!string.IsNullOrEmpty(ttsExtent))
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
XmlAttribute ttsExtentAttribute = xml.CreateAttribute("tts:extent", TtmlStylingNamespace);
|
2016-02-15 23:04:02 +01:00
|
|
|
|
ttsExtentAttribute.InnerText = ttsExtent;
|
|
|
|
|
paragraph.Attributes.Append(ttsExtentAttribute);
|
|
|
|
|
}
|
2016-01-26 22:02:22 +01:00
|
|
|
|
|
2016-02-17 18:48:02 +01:00
|
|
|
|
string ttsTextAlign = GetEffect(p, "tts:textAlign");
|
|
|
|
|
if (!string.IsNullOrEmpty(ttsTextAlign))
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
XmlAttribute ttsTextAlignAttribute = xml.CreateAttribute("tts:textAlign", TtmlStylingNamespace);
|
2016-02-17 18:48:02 +01:00
|
|
|
|
ttsTextAlignAttribute.InnerText = ttsTextAlign;
|
|
|
|
|
paragraph.Attributes.Append(ttsTextAlignAttribute);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
if (subtitle.Header != null && p.Style != null && headerStyles.Contains(p.Style))
|
|
|
|
|
{
|
|
|
|
|
if (p.Style != defaultStyle)
|
|
|
|
|
{
|
|
|
|
|
XmlAttribute styleAttr = xml.CreateAttribute("style");
|
|
|
|
|
styleAttr.InnerText = p.Style;
|
|
|
|
|
paragraph.Attributes.Append(styleAttr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return paragraph;
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
private static void ConvertParagraphNodeToTtmlNode(XmlNode node, XmlDocument ttmlXml, XmlNode ttmlNode)
|
2017-03-14 15:36:42 +01:00
|
|
|
|
{
|
|
|
|
|
foreach (XmlNode child in node.ChildNodes)
|
|
|
|
|
{
|
|
|
|
|
if (child is XmlText)
|
|
|
|
|
{
|
|
|
|
|
ttmlNode.AppendChild(ttmlXml.CreateTextNode(child.Value));
|
|
|
|
|
}
|
|
|
|
|
else if (child.Name == "br")
|
|
|
|
|
{
|
|
|
|
|
XmlNode br = ttmlXml.CreateElement("br");
|
|
|
|
|
ttmlNode.AppendChild(br);
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
ConvertParagraphNodeToTtmlNode(child, ttmlXml, br);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
}
|
|
|
|
|
else if (child.Name == "i")
|
|
|
|
|
{
|
|
|
|
|
XmlNode span = ttmlXml.CreateElement("span");
|
2017-06-13 12:30:29 +02:00
|
|
|
|
XmlAttribute attr = ttmlXml.CreateAttribute("tts:fontStyle", TtmlStylingNamespace);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
attr.InnerText = "italic";
|
|
|
|
|
span.Attributes.Append(attr);
|
|
|
|
|
ttmlNode.AppendChild(span);
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
ConvertParagraphNodeToTtmlNode(child, ttmlXml, span);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
}
|
|
|
|
|
else if (child.Name == "b")
|
|
|
|
|
{
|
|
|
|
|
XmlNode span = ttmlXml.CreateElement("span");
|
2017-06-13 12:30:29 +02:00
|
|
|
|
XmlAttribute attr = ttmlXml.CreateAttribute("tts:fontWeight", TtmlStylingNamespace);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
attr.InnerText = "bold";
|
|
|
|
|
span.Attributes.Append(attr);
|
|
|
|
|
ttmlNode.AppendChild(span);
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
ConvertParagraphNodeToTtmlNode(child, ttmlXml, span);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
}
|
|
|
|
|
else if (child.Name == "u")
|
|
|
|
|
{
|
|
|
|
|
XmlNode span = ttmlXml.CreateElement("span");
|
2017-06-13 12:30:29 +02:00
|
|
|
|
XmlAttribute attr = ttmlXml.CreateAttribute("tts:textDecoration", TtmlStylingNamespace);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
attr.InnerText = "underline";
|
|
|
|
|
span.Attributes.Append(attr);
|
|
|
|
|
ttmlNode.AppendChild(span);
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
ConvertParagraphNodeToTtmlNode(child, ttmlXml, span);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
}
|
|
|
|
|
else if (child.Name == "font")
|
|
|
|
|
{
|
|
|
|
|
XmlNode span = ttmlXml.CreateElement("span");
|
|
|
|
|
|
|
|
|
|
if (child.Attributes["face"] != null)
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
XmlAttribute attr = ttmlXml.CreateAttribute("tts:fontFamily", TtmlStylingNamespace);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
attr.InnerText = child.Attributes["face"].Value;
|
|
|
|
|
span.Attributes.Append(attr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (child.Attributes["color"] != null)
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
XmlAttribute attr = ttmlXml.CreateAttribute("tts:color", TtmlStylingNamespace);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
attr.InnerText = child.Attributes["color"].Value;
|
|
|
|
|
span.Attributes.Append(attr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ttmlNode.AppendChild(span);
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
ConvertParagraphNodeToTtmlNode(child, ttmlXml, span);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
}
|
|
|
|
|
else // Default - skip node
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
ConvertParagraphNodeToTtmlNode(child, ttmlXml, ttmlNode);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-15 11:43:28 +01:00
|
|
|
|
public static bool AddDefaultRegionIfNotExists(XmlDocument xml, string region)
|
|
|
|
|
{
|
|
|
|
|
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xml.NameTable);
|
2017-06-13 12:30:29 +02:00
|
|
|
|
nsmgr.AddNamespace("ttml", TtmlNamespace);
|
2017-03-15 11:43:28 +01:00
|
|
|
|
|
|
|
|
|
if (xml.DocumentElement.SelectSingleNode(string.Format("ttml:head//ttml:region[@xml:id='{0}']", region), nsmgr) != null)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XmlDocument defaultXml = new XmlDocument();
|
|
|
|
|
defaultXml.LoadXml(new TimedText10().ToText(new Subtitle(), "tt"));
|
|
|
|
|
|
2017-04-26 18:51:34 +02:00
|
|
|
|
XmlNode regionNode = defaultXml.DocumentElement.SelectSingleNode(string.Format("ttml:head//ttml:region[@xml:id='{0}']", region), nsmgr);
|
2017-03-15 11:43:28 +01:00
|
|
|
|
|
|
|
|
|
if (regionNode == null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XmlNode importedRegionNode = xml.ImportNode(regionNode, true);
|
|
|
|
|
XmlNode layoutNode = xml.DocumentElement.SelectSingleNode("ttml:head/ttml:layout", nsmgr);
|
|
|
|
|
|
|
|
|
|
if (layoutNode == null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
layoutNode.AppendChild(importedRegionNode);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
|
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
_errorCount = 0;
|
2016-12-23 17:08:09 +01:00
|
|
|
|
|
2017-03-14 15:36:42 +01:00
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
lines.ForEach(line => sb.AppendLine(line));
|
|
|
|
|
var xml = new XmlDocument { XmlResolver = null };
|
2016-02-08 21:11:03 +01:00
|
|
|
|
try
|
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
xml.LoadXml(sb.ToString().RemoveControlCharactersButWhiteSpace().Trim());
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
xml.LoadXml(sb.ToString().Replace(" & ", " & ").Replace("Q&A", "Q&A").RemoveControlCharactersButWhiteSpace().Trim());
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-14 15:36:42 +01:00
|
|
|
|
const string ns = "http://www.w3.org/ns/ttml";
|
|
|
|
|
var nsmgr = new XmlNamespaceManager(xml.NameTable);
|
|
|
|
|
nsmgr.AddNamespace("ttml", ns);
|
|
|
|
|
XmlNode body = xml.DocumentElement.SelectSingleNode("ttml:body", nsmgr);
|
|
|
|
|
if (body == null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
return;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2016-12-23 17:08:09 +01:00
|
|
|
|
|
2017-03-14 15:36:42 +01:00
|
|
|
|
var frameRateAttr = xml.DocumentElement.Attributes["ttp:frameRate"];
|
|
|
|
|
if (frameRateAttr != null)
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2019-01-26 12:11:57 +01:00
|
|
|
|
if (double.TryParse(frameRateAttr.Value, out var fr))
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
if (fr > 20 && fr < 100)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
Configuration.Settings.General.CurrentFrameRate = fr;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
|
|
|
|
|
var frameRateMultiplier = xml.DocumentElement.Attributes["ttp:frameRateMultiplier"];
|
|
|
|
|
if (frameRateMultiplier != null)
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2017-10-02 16:37:45 +02:00
|
|
|
|
if (frameRateMultiplier.InnerText == "999 1000" && Math.Abs(fr - 30) < 0.01)
|
2017-03-14 15:36:42 +01:00
|
|
|
|
{
|
2017-10-02 16:37:45 +02:00
|
|
|
|
Configuration.Settings.General.CurrentFrameRate = 29.97;
|
|
|
|
|
}
|
|
|
|
|
else if (frameRateMultiplier.InnerText == "999 1000" && Math.Abs(fr - 24) < 0.01)
|
|
|
|
|
{
|
|
|
|
|
Configuration.Settings.General.CurrentFrameRate = 23.976;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var arr = frameRateMultiplier.InnerText.Split();
|
|
|
|
|
if (arr.Length == 2 && Utilities.IsInteger(arr[0]) && Utilities.IsInteger(arr[1]) && int.Parse(arr[1]) > 0)
|
|
|
|
|
{
|
|
|
|
|
fr = double.Parse(arr[0]) / double.Parse(arr[1]);
|
|
|
|
|
if (fr > 20 && fr < 100)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-10-02 16:37:45 +02:00
|
|
|
|
Configuration.Settings.General.CurrentFrameRate = fr;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-10-02 16:37:45 +02:00
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
if (BatchSourceFrameRate.HasValue)
|
|
|
|
|
{
|
|
|
|
|
Configuration.Settings.General.CurrentFrameRate = BatchSourceFrameRate.Value;
|
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
2017-03-14 15:36:42 +01:00
|
|
|
|
Configuration.Settings.SubtitleSettings.TimedText10TimeCodeFormatSource = null;
|
|
|
|
|
subtitle.Header = sb.ToString();
|
|
|
|
|
var styles = GetStylesFromHeader(subtitle.Header);
|
|
|
|
|
string defaultStyle = null;
|
|
|
|
|
if (body.Attributes["style"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
defaultStyle = body.Attributes["style"].InnerText;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
2017-03-14 15:36:42 +01:00
|
|
|
|
var headerStyleNodes = new List<XmlNode>();
|
|
|
|
|
try
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
XmlNode head = xml.DocumentElement.SelectSingleNode("ttml:head", nsmgr);
|
|
|
|
|
foreach (XmlNode node in head.SelectNodes("//ttml:style", nsmgr))
|
|
|
|
|
{
|
|
|
|
|
headerStyleNodes.Add(node);
|
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
catch
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-12 10:41:43 +02:00
|
|
|
|
var topRegions = GetRegionsTopFromHeader(xml.OuterXml);
|
|
|
|
|
XmlNode lastDiv = null;
|
2017-03-14 15:36:42 +01:00
|
|
|
|
foreach (XmlNode node in body.SelectNodes("//ttml:p", nsmgr))
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
// Parse and convert paragraph text
|
2016-02-08 21:11:03 +01:00
|
|
|
|
var pText = new StringBuilder();
|
2017-04-10 18:40:32 +02:00
|
|
|
|
ReadParagraph(pText, node, styles, xml);
|
2016-12-27 14:41:10 +01:00
|
|
|
|
|
2017-03-14 15:36:42 +01:00
|
|
|
|
// Timecodes
|
2019-01-26 12:11:57 +01:00
|
|
|
|
ExtractTimeCodes(node, subtitle, out var begin, out var end);
|
2016-12-27 14:41:10 +01:00
|
|
|
|
|
2017-03-14 15:36:42 +01:00
|
|
|
|
// Style
|
2017-06-13 12:30:29 +02:00
|
|
|
|
var p = new Paragraph(begin, end, pText.ToString()) { Style = LookupForAttribute("style", node, nsmgr) };
|
2017-03-14 15:36:42 +01:00
|
|
|
|
|
|
|
|
|
// Lang
|
2019-01-26 12:11:57 +01:00
|
|
|
|
string lang = LookupForAttribute("xml:lang", node, nsmgr) ?? LookupForAttribute("lang", node, nsmgr);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
if (lang != null)
|
|
|
|
|
{
|
|
|
|
|
p.Language = lang;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-26 18:51:34 +02:00
|
|
|
|
// Region
|
2019-01-26 12:11:57 +01:00
|
|
|
|
string regionP = LookupForAttribute("xml:region", node, nsmgr) ?? LookupForAttribute("region", node, nsmgr);
|
2017-04-26 18:51:34 +02:00
|
|
|
|
if (regionP != null)
|
|
|
|
|
{
|
|
|
|
|
p.Region = regionP;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-14 15:36:42 +01:00
|
|
|
|
// Saving attibutes
|
2019-01-26 12:11:57 +01:00
|
|
|
|
var effectsToSave = new List<string>
|
|
|
|
|
{
|
|
|
|
|
"xml:space",
|
|
|
|
|
"tts:fontSize",
|
|
|
|
|
"tts:fontFamily",
|
|
|
|
|
"tts:backgroundColor",
|
|
|
|
|
"tts:color",
|
|
|
|
|
"tts:origin",
|
|
|
|
|
"tts:extent",
|
|
|
|
|
"tts:textAlign"
|
|
|
|
|
};
|
2017-03-14 15:36:42 +01:00
|
|
|
|
|
|
|
|
|
foreach (string effect in effectsToSave)
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
string value = LookupForAttribute(effect, node, nsmgr);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
2016-12-27 14:41:10 +01:00
|
|
|
|
if (!string.IsNullOrWhiteSpace(value))
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2016-12-27 14:41:10 +01:00
|
|
|
|
SetEffect(p, effect, value);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-28 18:02:02 +01:00
|
|
|
|
// Convert region to {\an} tag or add it to effects
|
2017-03-14 15:36:42 +01:00
|
|
|
|
string region = LookupForAttribute("region", node, nsmgr);
|
2017-02-28 18:02:02 +01:00
|
|
|
|
if (!string.IsNullOrEmpty(region))
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2017-02-28 18:02:02 +01:00
|
|
|
|
bool regionCorrespondToTag = false;
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
var regionTags = new List<KeyValuePair<string, string>>
|
|
|
|
|
{
|
|
|
|
|
new KeyValuePair<string, string>("bottomLeft", "{\\an1}"),
|
|
|
|
|
new KeyValuePair<string, string>("bottomCenter", "{\\an2}"),
|
|
|
|
|
new KeyValuePair<string, string>("bottomRight", "{\\an3}"),
|
|
|
|
|
new KeyValuePair<string, string>("centerLeft", "{\\an4}"),
|
|
|
|
|
new KeyValuePair<string, string>("centerСenter", "{\\an5}"),
|
|
|
|
|
new KeyValuePair<string, string>("centerRight", "{\\an6}"),
|
|
|
|
|
new KeyValuePair<string, string>("topLeft", "{\\an7}"),
|
|
|
|
|
new KeyValuePair<string, string>("topCenter", "{\\an8}"),
|
|
|
|
|
new KeyValuePair<string, string>("topRight", "{\\an9}")
|
|
|
|
|
};
|
2017-02-28 18:02:02 +01:00
|
|
|
|
|
|
|
|
|
foreach (var regionTag in regionTags)
|
|
|
|
|
{
|
|
|
|
|
if (region == regionTag.Key)
|
|
|
|
|
{
|
|
|
|
|
p.Text = regionTag.Value + p.Text;
|
|
|
|
|
regionCorrespondToTag = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-08-29 20:26:24 +02:00
|
|
|
|
|
2017-02-28 18:02:02 +01:00
|
|
|
|
if (!regionCorrespondToTag)
|
|
|
|
|
{
|
2017-04-10 19:14:22 +02:00
|
|
|
|
if (topRegions.Contains(region))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-04-10 19:14:22 +02:00
|
|
|
|
p.Text = "{\\an8}" + p.Text;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-28 18:02:02 +01:00
|
|
|
|
SetEffect(p, "region", region);
|
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-12 10:41:43 +02:00
|
|
|
|
if (node.ParentNode.Name == "div")
|
|
|
|
|
{
|
|
|
|
|
// check language
|
|
|
|
|
if (node.ParentNode.Attributes["xml:lang"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2018-08-12 10:41:43 +02:00
|
|
|
|
p.Language = node.ParentNode.Attributes["xml:lang"].InnerText;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2018-08-12 10:41:43 +02:00
|
|
|
|
else if (node.ParentNode.Attributes["lang"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2018-08-12 10:41:43 +02:00
|
|
|
|
p.Language = node.ParentNode.Attributes["lang"].InnerText;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2018-08-12 10:41:43 +02:00
|
|
|
|
|
|
|
|
|
// check for new div
|
|
|
|
|
if (lastDiv != null && node.ParentNode != lastDiv)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2018-08-12 10:41:43 +02:00
|
|
|
|
p.NewSection = true;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-12 10:41:43 +02:00
|
|
|
|
lastDiv = node.ParentNode;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-15 11:43:28 +01:00
|
|
|
|
p.Extra = SetExtra(p);
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
p.Text = p.Text.Trim();
|
|
|
|
|
while (p.Text.Contains(Environment.NewLine + Environment.NewLine))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
p.Text = p.Text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
subtitle.Paragraphs.Add(p);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
System.Diagnostics.Debug.WriteLine(ex.Message);
|
|
|
|
|
_errorCount++;
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
subtitle.Renumber();
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-27 14:41:10 +01:00
|
|
|
|
private void ExtractTimeCodes(XmlNode paragraph, Subtitle subtitle, out TimeCode begin, out TimeCode end)
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
string beginAttr = TryGetAttribute(paragraph, "begin", TtmlNamespace);
|
|
|
|
|
string endAttr = TryGetAttribute(paragraph, "end", TtmlNamespace);
|
|
|
|
|
string durAttr = TryGetAttribute(paragraph, "dur", TtmlNamespace);
|
2016-12-27 14:41:10 +01:00
|
|
|
|
|
|
|
|
|
begin = new TimeCode();
|
|
|
|
|
if (beginAttr.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
begin = GetTimeCode(beginAttr, IsFrames(beginAttr));
|
|
|
|
|
}
|
|
|
|
|
else if (subtitle.Paragraphs.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
begin = new TimeCode(subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.Milliseconds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end = new TimeCode(begin.TotalMilliseconds + 3000);
|
|
|
|
|
if (endAttr.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
end = GetTimeCode(endAttr, IsFrames(endAttr));
|
|
|
|
|
}
|
|
|
|
|
else if (durAttr.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
end = new TimeCode(GetTimeCode(durAttr, IsFrames(durAttr)).TotalMilliseconds + begin.TotalMilliseconds);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-25 21:04:18 +01:00
|
|
|
|
private static string TryGetAttribute(XmlNode node, string attr, string @namespace)
|
2016-12-27 14:41:10 +01:00
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
if (node.Attributes[attr, @namespace] != null)
|
2016-12-27 14:41:10 +01:00
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
return node.Attributes[attr, @namespace].InnerText;
|
2016-12-27 14:41:10 +01:00
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
else if (node.Attributes[attr] != null)
|
2016-12-27 14:41:10 +01:00
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
return node.Attributes[attr].InnerText;
|
2016-12-27 14:41:10 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-25 21:04:18 +01:00
|
|
|
|
private static string LookupForAttribute(string attr, XmlNode node, XmlNamespaceManager nsmgr)
|
2016-12-27 14:41:10 +01:00
|
|
|
|
{
|
|
|
|
|
XmlNode currentNode = node;
|
|
|
|
|
|
|
|
|
|
while (currentNode != null && currentNode.NodeType != XmlNodeType.Document)
|
|
|
|
|
{
|
|
|
|
|
if (currentNode.Attributes[attr] != null)
|
|
|
|
|
{
|
|
|
|
|
return currentNode.Attributes[attr].Value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentNode = currentNode.ParentNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-15 23:04:02 +01:00
|
|
|
|
private static void SetEffect(Paragraph paragraph, string tag, string value)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(paragraph.Effect))
|
|
|
|
|
{
|
|
|
|
|
paragraph.Effect = tag + "=" + value;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var list = paragraph.Effect.Split('|');
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
bool found = false;
|
|
|
|
|
foreach (var s in list)
|
|
|
|
|
{
|
|
|
|
|
string addValue = s;
|
|
|
|
|
var arr = s.Split('=');
|
|
|
|
|
if (arr.Length == 2)
|
|
|
|
|
{
|
|
|
|
|
if (arr[0] == tag)
|
|
|
|
|
{
|
|
|
|
|
addValue = tag + "=" + value;
|
|
|
|
|
found = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sb.Append(addValue + "|");
|
|
|
|
|
}
|
|
|
|
|
if (!found)
|
|
|
|
|
{
|
|
|
|
|
sb.Append("|" + tag + "=" + value);
|
|
|
|
|
}
|
|
|
|
|
paragraph.Effect = sb.ToString().TrimEnd('|');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetEffect(Paragraph paragraph, string tag)
|
|
|
|
|
{
|
2019-01-26 12:11:57 +01:00
|
|
|
|
if (paragraph?.Effect == null)
|
2016-02-15 23:15:15 +01:00
|
|
|
|
{
|
|
|
|
|
return string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-15 23:04:02 +01:00
|
|
|
|
var list = paragraph.Effect.Split('|');
|
|
|
|
|
foreach (var s in list)
|
|
|
|
|
{
|
|
|
|
|
var arr = s.Split('=');
|
|
|
|
|
if (arr.Length == 2)
|
|
|
|
|
{
|
|
|
|
|
if (arr[0] == tag)
|
|
|
|
|
{
|
|
|
|
|
return arr[1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
private static bool IsFrames(string timeCode)
|
|
|
|
|
{
|
|
|
|
|
if (timeCode.Length == 12 && (timeCode[8] == '.' || timeCode[8] == ',')) // 00:00:08.292 or 00:00:08,292
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
return false;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
if (timeCode.Length == 11 && timeCode[8] == '.') // 00:00:08.12 (last part is milliseconds / 10)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
return false;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string SetExtra(Paragraph p)
|
|
|
|
|
{
|
|
|
|
|
string style = p.Style;
|
|
|
|
|
if (string.IsNullOrEmpty(style))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
style = "-";
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
string lang = p.Language;
|
|
|
|
|
if (string.IsNullOrEmpty(lang))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
lang = "-";
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-26 12:11:57 +01:00
|
|
|
|
return $"{style} / {lang}";
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-10 18:40:32 +02:00
|
|
|
|
private static void ReadParagraph(StringBuilder pText, XmlNode node, List<string> styles, XmlDocument xml)
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2016-12-14 11:07:08 +01:00
|
|
|
|
foreach (XmlNode child in node.ChildNodes)
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2016-12-14 11:07:08 +01:00
|
|
|
|
if (child.NodeType == XmlNodeType.Text)
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2016-12-14 11:07:08 +01:00
|
|
|
|
pText.Append(child.Value);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
2017-05-30 19:24:10 +02:00
|
|
|
|
else if (child.Name == "br" || child.Name == "tt:br")
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2016-12-14 11:07:08 +01:00
|
|
|
|
pText.AppendLine();
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
2017-05-30 19:24:10 +02:00
|
|
|
|
else if (child.Name == "span" || child.Name == "tt:span")
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2016-12-14 11:07:08 +01:00
|
|
|
|
bool isItalic = false;
|
|
|
|
|
bool isBold = false;
|
|
|
|
|
bool isUnderlined = false;
|
|
|
|
|
string fontFamily = null;
|
|
|
|
|
string color = null;
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
2016-12-14 11:07:08 +01:00
|
|
|
|
// Composing styles
|
2017-04-10 18:40:32 +02:00
|
|
|
|
|
|
|
|
|
if (child.Attributes["style"] != null)
|
|
|
|
|
{
|
|
|
|
|
string styleName = child.Attributes["style"].Value;
|
|
|
|
|
if (styles.Contains(styleName))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var nsmgr = new XmlNamespaceManager(xml.NameTable);
|
|
|
|
|
nsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml");
|
|
|
|
|
XmlNode head = xml.DocumentElement.SelectSingleNode("ttml:head", nsmgr);
|
|
|
|
|
foreach (XmlNode styleNode in head.SelectNodes("//ttml:style", nsmgr))
|
|
|
|
|
{
|
|
|
|
|
string currentStyle = null;
|
|
|
|
|
if (styleNode.Attributes["xml:id"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-04-10 18:40:32 +02:00
|
|
|
|
currentStyle = styleNode.Attributes["xml:id"].Value;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-04-10 18:40:32 +02:00
|
|
|
|
else if (styleNode.Attributes["id"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-04-10 18:40:32 +02:00
|
|
|
|
currentStyle = styleNode.Attributes["id"].Value;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-10 18:40:32 +02:00
|
|
|
|
if (currentStyle == styleName)
|
|
|
|
|
{
|
|
|
|
|
if (styleNode.Attributes["tts:fontStyle"] != null && styleNode.Attributes["tts:fontStyle"].Value == "italic")
|
|
|
|
|
{
|
|
|
|
|
isItalic = true;
|
|
|
|
|
}
|
|
|
|
|
if (styleNode.Attributes["tts:fontWeight"] != null && styleNode.Attributes["tts:fontWeight"].Value == "bold")
|
|
|
|
|
{
|
|
|
|
|
isBold = true;
|
|
|
|
|
}
|
|
|
|
|
if (styleNode.Attributes["tts:textDecoration"] != null && styleNode.Attributes["tts:textDecoration"].Value == "underline")
|
|
|
|
|
{
|
|
|
|
|
isUnderlined = true;
|
|
|
|
|
}
|
|
|
|
|
if (styleNode.Attributes["tts:fontFamily"] != null)
|
|
|
|
|
{
|
|
|
|
|
fontFamily = styleNode.Attributes["tts:fontFamily"].Value;
|
|
|
|
|
}
|
2017-07-27 13:31:54 +02:00
|
|
|
|
if (styleNode.Attributes["tts:color"] != null)
|
2017-04-10 18:40:32 +02:00
|
|
|
|
{
|
|
|
|
|
color = styleNode.Attributes["tts:color"].Value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
System.Diagnostics.Debug.WriteLine(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-14 11:07:08 +01:00
|
|
|
|
if (child.Attributes["tts:fontStyle"] != null && child.Attributes["tts:fontStyle"].Value == "italic")
|
|
|
|
|
{
|
|
|
|
|
isItalic = true;
|
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
2016-12-14 11:07:08 +01:00
|
|
|
|
if (child.Attributes["tts:fontWeight"] != null && child.Attributes["tts:fontWeight"].Value == "bold")
|
|
|
|
|
{
|
|
|
|
|
isBold = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (child.Attributes["tts:textDecoration"] != null && child.Attributes["tts:textDecoration"].Value == "underline")
|
|
|
|
|
{
|
|
|
|
|
isUnderlined = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (child.Attributes["tts:fontFamily"] != null)
|
|
|
|
|
{
|
|
|
|
|
fontFamily = child.Attributes["tts:fontFamily"].Value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (child.Attributes["tts:color"] != null)
|
|
|
|
|
{
|
|
|
|
|
color = child.Attributes["tts:color"].Value;
|
|
|
|
|
}
|
2017-08-29 20:26:24 +02:00
|
|
|
|
|
2017-04-10 18:40:32 +02:00
|
|
|
|
|
2016-12-14 11:07:08 +01:00
|
|
|
|
// Applying styles
|
|
|
|
|
if (isItalic)
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
|
|
|
|
pText.Append("<i>");
|
|
|
|
|
}
|
2016-12-14 11:07:08 +01:00
|
|
|
|
|
|
|
|
|
if (isBold)
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
|
|
|
|
pText.Append("<b>");
|
|
|
|
|
}
|
2016-12-14 11:07:08 +01:00
|
|
|
|
|
|
|
|
|
if (isUnderlined)
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2016-12-14 11:07:08 +01:00
|
|
|
|
pText.Append("<u>");
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
2016-12-14 11:07:08 +01:00
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(fontFamily) || !string.IsNullOrEmpty(color))
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2016-12-14 11:07:08 +01:00
|
|
|
|
pText.Append("<font");
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(fontFamily))
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
pText.Append($" face=\"{fontFamily}\"");
|
2016-12-14 11:07:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(color))
|
|
|
|
|
{
|
2017-06-13 12:30:29 +02:00
|
|
|
|
pText.Append($" color=\"{color}\"");
|
2016-12-14 11:07:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pText.Append(">");
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
2016-12-14 11:07:08 +01:00
|
|
|
|
|
2017-04-10 18:40:32 +02:00
|
|
|
|
ReadParagraph(pText, child, styles, xml);
|
2016-12-14 11:07:08 +01:00
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(fontFamily) || !string.IsNullOrEmpty(color))
|
|
|
|
|
{
|
|
|
|
|
pText.Append("</font>");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isUnderlined)
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2016-12-14 11:07:08 +01:00
|
|
|
|
pText.Append("</u>");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isBold)
|
|
|
|
|
{
|
|
|
|
|
pText.Append("</b>");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isItalic)
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2016-12-14 11:07:08 +01:00
|
|
|
|
pText.Append("</i>");
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-26 12:11:57 +01:00
|
|
|
|
public static TimeCode GetTimeCode(string input, bool frames)
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
2019-01-26 12:11:57 +01:00
|
|
|
|
var s = input;
|
2016-02-08 21:11:03 +01:00
|
|
|
|
if (s.EndsWith("ms", StringComparison.Ordinal))
|
|
|
|
|
{
|
|
|
|
|
Configuration.Settings.SubtitleSettings.TimedText10TimeCodeFormatSource = "milliseconds";
|
|
|
|
|
s = s.TrimEnd('s');
|
|
|
|
|
s = s.TrimEnd('m');
|
|
|
|
|
return new TimeCode(double.Parse(s.Replace(",", "."), CultureInfo.InvariantCulture));
|
|
|
|
|
}
|
|
|
|
|
if (s.EndsWith('s'))
|
|
|
|
|
{
|
|
|
|
|
Configuration.Settings.SubtitleSettings.TimedText10TimeCodeFormatSource = "seconds";
|
|
|
|
|
s = s.TrimEnd('s');
|
|
|
|
|
return TimeCode.FromSeconds(double.Parse(s.Replace(",", "."), CultureInfo.InvariantCulture));
|
|
|
|
|
}
|
|
|
|
|
if (s.EndsWith('t'))
|
|
|
|
|
{
|
|
|
|
|
Configuration.Settings.SubtitleSettings.TimedText10TimeCodeFormatSource = "ticks";
|
|
|
|
|
s = s.TrimEnd('t');
|
|
|
|
|
var ts = TimeSpan.FromTicks(long.Parse(s, CultureInfo.InvariantCulture));
|
|
|
|
|
return new TimeCode(ts.TotalMilliseconds);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-02 16:37:45 +02:00
|
|
|
|
var parts = s.Split(':', '.', ',', ';');
|
2016-02-08 21:11:03 +01:00
|
|
|
|
if (s.Length == 12 && s[2] == ':' && s[5] == ':' && s[8] == '.') // 00:01:39.946
|
|
|
|
|
{
|
|
|
|
|
Configuration.Settings.SubtitleSettings.TimedText10TimeCodeFormatSource = "hh:mm:ss.ms";
|
|
|
|
|
}
|
2017-08-29 20:26:24 +02:00
|
|
|
|
else if (s.Length == 12 && s[2] == ':' && s[5] == ':' && s[8] == ',') // 00:01:39,946
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
|
|
|
|
Configuration.Settings.SubtitleSettings.TimedText10TimeCodeFormatSource = "hh:mm:ss,ms";
|
|
|
|
|
}
|
|
|
|
|
else if (!frames && s.Length == 11 && s[2] == ':' && s[5] == ':' && s[8] == '.') // 00:01:39.96
|
|
|
|
|
{
|
|
|
|
|
Configuration.Settings.SubtitleSettings.TimedText10TimeCodeFormatSource = "hh:mm:ss.ms-two-digits";
|
|
|
|
|
return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]) * 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (frames)
|
2016-02-29 07:27:12 +01:00
|
|
|
|
{
|
|
|
|
|
Configuration.Settings.SubtitleSettings.TimedText10TimeCodeFormatSource = "frames";
|
2016-02-08 21:11:03 +01:00
|
|
|
|
return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), FramesToMillisecondsMax999(int.Parse(parts[3])));
|
2016-02-29 07:27:12 +01:00
|
|
|
|
}
|
2017-08-29 20:26:24 +02:00
|
|
|
|
return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), parts.Length > 3 ? int.Parse(parts[3]) : 0);
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
public override List<string> AlternateExtensions => new List<string> { ".itt", ".dfxp", ".ttml" };
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
2017-06-13 12:30:29 +02:00
|
|
|
|
public override bool HasStyleSupport => Configuration.Settings.SubtitleSettings.TimedText10ShowStyleAndLanguage;
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
|
|
|
|
public static List<string> GetStylesFromHeader(string xmlAsString)
|
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
var list = new List<string>();
|
2016-02-08 21:11:03 +01:00
|
|
|
|
var xml = new XmlDocument();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
xml.LoadXml(xmlAsString);
|
|
|
|
|
var nsmgr = new XmlNamespaceManager(xml.NameTable);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
nsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml");
|
|
|
|
|
XmlNode head = xml.DocumentElement.SelectSingleNode("ttml:head", nsmgr);
|
|
|
|
|
foreach (XmlNode node in head.SelectNodes("//ttml:style", nsmgr))
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
|
|
|
|
if (node.Attributes["xml:id"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
list.Add(node.Attributes["xml:id"].Value);
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
else if (node.Attributes["id"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
list.Add(node.Attributes["id"].Value);
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
return list;
|
2017-08-29 20:26:24 +02:00
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
|
|
|
|
|
public static List<string> GetRegionsFromHeader(string xmlAsString)
|
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
var list = new List<string>();
|
2016-02-08 21:11:03 +01:00
|
|
|
|
var xml = new XmlDocument();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
xml.LoadXml(xmlAsString);
|
|
|
|
|
var nsmgr = new XmlNamespaceManager(xml.NameTable);
|
2017-03-14 15:36:42 +01:00
|
|
|
|
nsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml");
|
|
|
|
|
XmlNode head = xml.DocumentElement.SelectSingleNode("ttml:head", nsmgr);
|
|
|
|
|
foreach (XmlNode node in head.SelectNodes("//ttml:region", nsmgr))
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
|
|
|
|
if (node.Attributes["xml:id"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
list.Add(node.Attributes["xml:id"].Value);
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
else if (node.Attributes["id"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-03-14 15:36:42 +01:00
|
|
|
|
list.Add(node.Attributes["id"].Value);
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
catch
|
2016-02-08 21:11:03 +01:00
|
|
|
|
{
|
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
return list;
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-25 19:01:19 +01:00
|
|
|
|
public static List<string> GetRegionsTopFromHeader(string xmlAsString)
|
|
|
|
|
{
|
|
|
|
|
var list = new List<string>();
|
|
|
|
|
var xml = new XmlDocument();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
xml.LoadXml(xmlAsString);
|
|
|
|
|
var nsmgr = new XmlNamespaceManager(xml.NameTable);
|
|
|
|
|
nsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml");
|
|
|
|
|
XmlNode head = xml.DocumentElement.SelectSingleNode("ttml:head", nsmgr);
|
|
|
|
|
foreach (XmlNode node in head.SelectNodes("//ttml:region", nsmgr))
|
|
|
|
|
{
|
|
|
|
|
bool top = false;
|
|
|
|
|
foreach (XmlNode styleNode in node.ChildNodes)
|
|
|
|
|
{
|
2017-09-16 10:52:52 +02:00
|
|
|
|
top = GetIfTopAligned(styleNode);
|
|
|
|
|
if (top)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-09-16 10:52:52 +02:00
|
|
|
|
break;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-04-14 13:33:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!top && node.Attributes != null)
|
|
|
|
|
{
|
2017-09-16 10:52:52 +02:00
|
|
|
|
top = GetIfTopAligned(node);
|
2017-01-25 19:01:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (top)
|
|
|
|
|
{
|
|
|
|
|
if (node.Attributes["xml:id"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-01-25 19:01:19 +01:00
|
|
|
|
list.Add(node.Attributes["xml:id"].Value);
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-01-25 19:01:19 +01:00
|
|
|
|
else if (node.Attributes["id"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-01-25 19:01:19 +01:00
|
|
|
|
list.Add(node.Attributes["id"].Value);
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-01-25 19:01:19 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
2019-01-26 12:11:57 +01:00
|
|
|
|
// ignored
|
2017-01-25 19:01:19 +01:00
|
|
|
|
}
|
2019-01-26 12:11:57 +01:00
|
|
|
|
|
2017-01-25 19:01:19 +01:00
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-16 10:52:52 +02:00
|
|
|
|
private static bool GetIfTopAligned(XmlNode styleNode)
|
|
|
|
|
{
|
|
|
|
|
if (styleNode?.Attributes == null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-09-16 10:52:52 +02:00
|
|
|
|
return false;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-09-16 10:52:52 +02:00
|
|
|
|
|
|
|
|
|
var origin = string.Empty;
|
|
|
|
|
if (styleNode.Attributes["tts:origin"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-09-16 10:52:52 +02:00
|
|
|
|
origin = styleNode.Attributes["tts:origin"].Value;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-09-16 10:52:52 +02:00
|
|
|
|
else if (styleNode.Attributes["origin"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-09-16 10:52:52 +02:00
|
|
|
|
origin = styleNode.Attributes["origin"].Value;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-16 10:52:52 +02:00
|
|
|
|
var originArr = origin.Split(' ');
|
|
|
|
|
|
|
|
|
|
var extent = string.Empty;
|
|
|
|
|
if (styleNode.Attributes["tts:extent"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-09-16 10:52:52 +02:00
|
|
|
|
extent = styleNode.Attributes["tts:extent"].Value;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-09-16 10:52:52 +02:00
|
|
|
|
else if (styleNode.Attributes["extent"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-09-16 10:52:52 +02:00
|
|
|
|
extent = styleNode.Attributes["extent"].Value;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-16 10:52:52 +02:00
|
|
|
|
var extentArr = extent.Split(' ');
|
|
|
|
|
|
|
|
|
|
var displayAlign = string.Empty;
|
|
|
|
|
if (styleNode.Attributes["tts:displayAlign"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-09-16 10:52:52 +02:00
|
|
|
|
displayAlign = styleNode.Attributes["tts:displayAlign"].Value;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-09-16 10:52:52 +02:00
|
|
|
|
else if (styleNode.Attributes["displayAlign"] != null)
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2017-09-16 10:52:52 +02:00
|
|
|
|
displayAlign = styleNode.Attributes["displayAlign"].Value;
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2017-09-16 10:52:52 +02:00
|
|
|
|
|
|
|
|
|
if (originArr.Length == 2 && originArr[0].EndsWith("%", StringComparison.Ordinal) && originArr[1].EndsWith("%", StringComparison.Ordinal) &&
|
|
|
|
|
extentArr.Length == 2 && extentArr[0].EndsWith("%", StringComparison.Ordinal) && extentArr[1].EndsWith("%", StringComparison.Ordinal) &&
|
|
|
|
|
!string.IsNullOrEmpty(displayAlign))
|
|
|
|
|
{
|
|
|
|
|
var yPos = Convert.ToDouble(originArr[1].TrimEnd('%'), CultureInfo.InvariantCulture);
|
|
|
|
|
var yExtent = Convert.ToDouble(extentArr[1].TrimEnd('%'), CultureInfo.InvariantCulture);
|
|
|
|
|
if (yPos <= 15 && yExtent < 40)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else if (yPos <= 25 && displayAlign == "before") // before = top align
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
public static List<string> GetUsedLanguages(Subtitle subtitle)
|
|
|
|
|
{
|
|
|
|
|
var list = new List<string>();
|
|
|
|
|
foreach (Paragraph p in subtitle.Paragraphs)
|
|
|
|
|
{
|
|
|
|
|
if (p.Language != null)
|
|
|
|
|
{
|
2019-01-21 09:53:15 +01:00
|
|
|
|
string l = p.Language.ToLowerInvariant().Trim();
|
2016-02-08 21:11:03 +01:00
|
|
|
|
if (!list.Contains(l))
|
2019-01-19 14:40:37 +01:00
|
|
|
|
{
|
2016-02-08 21:11:03 +01:00
|
|
|
|
list.Add(l);
|
2019-01-19 14:40:37 +01:00
|
|
|
|
}
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return list;
|
|
|
|
|
}
|
2017-03-14 15:36:42 +01:00
|
|
|
|
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
}
|