using System; using System.Collections.Generic; using System.Globalization; using System.Text; using System.Xml; namespace Nikse.SubtitleEdit.Core.SubtitleFormats { // - Mom, when you were my age what did you want to do? public class FinalCutProTest2Xml : SubtitleFormat { public override string Extension => ".xml"; public override string Name => "Final Cut Pro Test2 Xml"; public static string GetFrameRateAsString() { if (Configuration.Settings.General.CurrentFrameRate < 24) { return "24"; // ntsc 23.976 } if (Configuration.Settings.General.CurrentFrameRate < 25) { return "24"; } if (Configuration.Settings.General.CurrentFrameRate < 29) { return "25"; } if (Configuration.Settings.General.CurrentFrameRate < 30) { return "30"; // ntsc 29.97 } if (Configuration.Settings.General.CurrentFrameRate < 40) { return "30"; } if (Configuration.Settings.General.CurrentFrameRate < 60) { return "60"; // ntsc 59.94 } return "60"; } public static string GetNtsc() { if (Configuration.Settings.General.CurrentFrameRate < 24) { return "TRUE"; // ntsc 23.976 } if (Configuration.Settings.General.CurrentFrameRate < 25) { return "FALSE"; } return "TRUE"; } public override string ToText(Subtitle subtitle, string title) { int duration = 0; if (subtitle.Paragraphs.Count > 0) { duration = (int)Math.Round(subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalSeconds * Configuration.Settings.General.CurrentFrameRate); } string seString = "Subtitle Edit at " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString(); string xmlStructure = "" + Environment.NewLine + "" + Environment.NewLine + " " + Environment.NewLine + " EC466A7D-8B45-4682-9978-D15D630C882Eadd" + seString + "" + duration + ">" + GetNtsc() + @"" + GetFrameRateAsString() + @"" + GetNtsc() + @"" + GetFrameRateAsString() + @"01:00:00:0090000sourceNDF-1-1"; const string xmlTrackStructure = "Text3000FALSE251375148615041615FALSEblackFALSETextTextTextgeneratorvideostrTextA finales de los años sesenta, una joven pareja, Guy y Rosemary, fontnameFontLucida GrandefontsizeSize01000[FONTSIZE]fontstyleStyle14Plain1Bold2Italic3Bold/Italic41fontalignAlignment13Left1Center2Right32fontcolorFont Color255255255255originOrigin00fonttrackTracking-2002001leadingLeading-1001000aspectAspect0.151autokernAuto KerningTRUEsubpixelUse SubpixelTRUEBasic MotionbasicmotionmotionvideoscaleScale01000100rotationRotation-864086400centerCenter0.004709580.396648centerOffsetAnchor Point00video3506ED18-CB4D-41B8-A760-4D42356E4F321E6E96FD-94F6-4975-BDFE-7B360E909111"; if (string.IsNullOrEmpty(title)) { title = "Subtitle Edit subtitle"; } var xml = new XmlDocument(); xml.LoadXml(xmlStructure); xml.DocumentElement.SelectSingleNode("sequence").Attributes["id"].Value = title; xml.DocumentElement.SelectSingleNode("sequence/name").InnerText = title; xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = Guid.NewGuid().ToString().ToUpperInvariant(); if (!string.IsNullOrEmpty(subtitle.Header)) { var header = new XmlDocument(); try { header.LoadXml(subtitle.Header); var node = header.DocumentElement.SelectSingleNode("sequence/uuid"); if (node != null) { xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = node.InnerText; } } catch { } } XmlNode trackNode = xml.DocumentElement.SelectSingleNode("sequence/media/video/track[2]"); const string newLine = "_____@___"; int number = 1; foreach (Paragraph p in subtitle.Paragraphs) { XmlNode generatorItem = xml.CreateElement("generatoritem"); string fontStyle = "1"; //1==plain var s = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont).Trim(); if ((s.StartsWith("") && s.EndsWith("")) || (s.StartsWith("") && s.EndsWith(""))) { fontStyle = "4"; //4==bold/italic } else if (s.StartsWith("") && s.EndsWith("")) { fontStyle = "3"; //3==italic } generatorItem.InnerXml = xmlTrackStructure.Replace("[NUMBER]", number.ToString()).Replace("[FONTSTYLE]", fontStyle).Replace("[FONTSIZE]", Configuration.Settings.SubtitleSettings.FcpFontSize.ToString(CultureInfo.InvariantCulture)); double frameRate = Configuration.Settings.General.CurrentFrameRate; XmlNode start = generatorItem.SelectSingleNode("generatoritem/start"); start.InnerText = ((int)Math.Round(p.StartTime.TotalSeconds * frameRate)).ToString(); XmlNode end = generatorItem.SelectSingleNode("generatoritem/end"); end.InnerText = ((int)Math.Round(p.EndTime.TotalSeconds * frameRate)).ToString(); XmlNode text = generatorItem.SelectSingleNode("generatoritem/effect/parameter[parameterid='str']/value"); text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text); text.InnerXml = text.InnerXml.Replace(Environment.NewLine, newLine); trackNode.AppendChild(generatorItem.SelectSingleNode("generatoritem")); number++; } string xmlAsText = ToUtf8XmlString(xml); xmlAsText = xmlAsText.Replace("xmeml[]", "xmeml"); xmlAsText = xmlAsText.Replace(newLine, " "); return xmlAsText; } public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) { _errorCount = 0; var frameRate = Configuration.Settings.General.CurrentFrameRate; var sb = new StringBuilder(); lines.ForEach(line => sb.AppendLine(line)); var xml = new XmlDocument { XmlResolver = null }; try { xml.LoadXml(sb.ToString().Trim()); var header = new XmlDocument { XmlResolver = null }; header.LoadXml(sb.ToString()); if (header.SelectSingleNode("sequence/media/video/track") != null) { header.RemoveChild(header.SelectSingleNode("sequence/media/video/track")); } subtitle.Header = header.OuterXml; if (xml.DocumentElement.SelectSingleNode("sequence/rate") != null && xml.DocumentElement.SelectSingleNode("sequence/rate/timebase") != null) { try { frameRate = double.Parse(xml.DocumentElement.SelectSingleNode("sequence/rate/timebase").InnerText); } catch { frameRate = Configuration.Settings.General.CurrentFrameRate; } } foreach (XmlNode node in xml.SelectNodes("//video/track")) { try { foreach (XmlNode generatorItemNode in node.SelectNodes("generatoritem")) { XmlNode rate = generatorItemNode.SelectSingleNode("rate"); XmlNode timebase = rate?.SelectSingleNode("timebase"); if (timebase != null) { frameRate = double.Parse(timebase.InnerText); } double startFrame = 0; double endFrame = 0; XmlNode startNode = generatorItemNode.SelectSingleNode("start"); if (startNode != null) { startFrame = double.Parse(startNode.InnerText); } XmlNode endNode = generatorItemNode.SelectSingleNode("end"); if (endNode != null) { endFrame = double.Parse(endNode.InnerText); } string text = string.Empty; foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str']")) { XmlNode valueNode = parameterNode.SelectSingleNode("value"); if (valueNode != null) { text += valueNode.InnerText; } } bool italic = false; bool bold = false; foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='style']")) { XmlNode valueNode = parameterNode.SelectSingleNode("value"); var valueEntries = parameterNode.SelectNodes("valuelist/valueentry"); if (valueNode != null) { int no; if (int.TryParse(valueNode.InnerText, out no)) { no--; if (no < valueEntries.Count) { var styleNameNode = valueEntries[no].SelectSingleNode("name"); if (styleNameNode != null) { string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim(); italic = styleName == "italic" || styleName == "bold/italic"; bold = styleName == "bold" || styleName == "bold/italic"; } } } } } if (!bold && !italic) { foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='fontstyle']")) { XmlNode valueNode = parameterNode.SelectSingleNode("value"); var valueEntries = parameterNode.SelectNodes("valuelist/valueentry"); if (valueNode != null) { int no; if (int.TryParse(valueNode.InnerText, out no)) { no--; if (no < valueEntries.Count) { var styleNameNode = valueEntries[no].SelectSingleNode("name"); if (styleNameNode != null) { string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim(); italic = styleName == "italic" || styleName == "bold/italic"; bold = styleName == "bold" || styleName == "bold/italic"; } } } } } } if (text.Length > 0) { if (!text.Contains(Environment.NewLine)) { text = text.Replace("\r", Environment.NewLine); } if (bold) { text = "" + text + ""; } if (italic) { text = "" + text + ""; } subtitle.Paragraphs.Add(new Paragraph(text, Convert.ToDouble((startFrame / frameRate) * 1000), Convert.ToDouble((endFrame / frameRate) * 1000))); } } } catch { _errorCount++; } } subtitle.Renumber(); } catch { _errorCount = 1; return; } Configuration.Settings.General.CurrentFrameRate = frameRate; } } }