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 { get { return ".xml"; } } public override string Name { get { return "Final Cut Pro Test2 Xml"; } } public override bool IsTimeBased { get { return true; } } public override bool IsMine(List lines, string fileName) { var subtitle = new Subtitle(); LoadSubtitle(subtitle, lines, fileName); return subtitle.Paragraphs.Count > 0; } 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 < 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 < 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"; //if (Configuration.Settings.General.CurrentFrameRate < 29) // return "FALSE"; //if (Configuration.Settings.General.CurrentFrameRate < 29) // return "FALSE"; //if (Configuration.Settings.General.CurrentFrameRate < 30) // return "TRUE"; // ntsc 29.97 //if (Configuration.Settings.General.CurrentFrameRate < 40) // return "TRUE"; //if (Configuration.Settings.General.CurrentFrameRate < 40) // return "TRUE"; //if (Configuration.Settings.General.CurrentFrameRate < 60) // return "TRUE"; // ntsc 59.94 //return "FALSE"; } 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().ToUpper(); 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("xmeml/sequence/media/video/track")) { try { foreach (XmlNode generatorItemNode in node.SelectNodes("generatoritem")) { XmlNode rate = generatorItemNode.SelectSingleNode("rate"); if (rate != null) { 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.ToLower().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.ToLower().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; } } }