diff --git a/SubtitleEdit.sln.DotSettings b/SubtitleEdit.sln.DotSettings
index 794286398..fb114b125 100644
--- a/SubtitleEdit.sln.DotSettings
+++ b/SubtitleEdit.sln.DotSettings
@@ -21,5 +21,7 @@
True
True
True
+ True
True
+ True
True
\ No newline at end of file
diff --git a/src/Test/Core/WebVttHelperTest.cs b/src/Test/Core/WebVttHelperTest.cs
new file mode 100644
index 000000000..eda3e803b
--- /dev/null
+++ b/src/Test/Core/WebVttHelperTest.cs
@@ -0,0 +1,53 @@
+using System.Collections.Generic;
+using System.Drawing;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Nikse.SubtitleEdit.Core.Common;
+using Nikse.SubtitleEdit.Core.SubtitleFormats;
+
+namespace Test.Core
+{
+ [TestClass]
+ public class WebVttHelperTest
+ {
+ [TestMethod]
+ public void RemoveColorTag1()
+ {
+ var styles = new List
+ {
+ new WebVttStyle()
+ {
+ Name = "Red",
+ Color = Color.Red,
+ },
+ };
+
+ var text = "Red";
+ var result = WebVttHelper.RemoveColorTag(text, Color.Red, styles);
+
+ Assert.AreEqual("Red", result);
+ }
+
+ [TestMethod]
+ public void RemoveColorTag2()
+ {
+ var styles = new List
+ {
+ new WebVttStyle
+ {
+ Name = "Red",
+ Color = Color.Red,
+ },
+ new WebVttStyle
+ {
+ Name = "Italic",
+ Italic = true,
+ },
+ };
+
+ var text = "Red";
+ var result = WebVttHelper.RemoveColorTag(text, Color.Red, styles);
+
+ Assert.AreEqual("Red", result);
+ }
+ }
+}
diff --git a/src/Test/Core/WebVttToAssaTest.cs b/src/Test/Core/WebVttToAssaTest.cs
index 75fa78c92..456e29828 100644
--- a/src/Test/Core/WebVttToAssaTest.cs
+++ b/src/Test/Core/WebVttToAssaTest.cs
@@ -15,20 +15,20 @@ namespace Test.Core
var converted = WebVttToAssa.Convert(subtitle, new SsaStyle(), 1920, 1080);
var styles = AdvancedSubStationAlpha.GetSsaStylesFromHeader(converted.Header);
- Assert.AreEqual(".background-color_transparent", styles[0].Name);
- Assert.AreEqual(".color_EBEBEB", styles[1].Name);
- Assert.AreEqual(".font-family_Arial", styles[2].Name);
- Assert.AreEqual(".font-style_normal", styles[3].Name);
- Assert.AreEqual(".font-weight_normal", styles[4].Name);
- Assert.AreEqual(".text-shadow_#101010-3px", styles[5].Name);
- Assert.AreEqual(".font-style_italic", styles[6].Name);
+ Assert.AreEqual(".background-color_transparent", styles[1].Name);
+ Assert.AreEqual(".color_EBEBEB", styles[2].Name);
+ Assert.AreEqual(".font-family_Arial", styles[3].Name);
+ Assert.AreEqual(".font-style_normal", styles[4].Name);
+ Assert.AreEqual(".font-weight_normal", styles[5].Name);
+ Assert.AreEqual(".text-shadow_#101010-3px", styles[6].Name);
+ Assert.AreEqual(".font-style_italic", styles[7].Name);
- Assert.AreEqual(235, styles[1].Primary.R);
- Assert.AreEqual("Arial", styles[2].FontName);
- Assert.AreEqual(false, styles[3].Italic);
- Assert.AreEqual(false, styles[4].Bold);
- Assert.AreEqual(3, styles[5].ShadowWidth);
- Assert.AreEqual(true, styles[6].Italic);
+ Assert.AreEqual(235, styles[2].Primary.R);
+ Assert.AreEqual("Arial", styles[3].FontName);
+ Assert.AreEqual(false, styles[4].Italic);
+ Assert.AreEqual(false, styles[5].Bold);
+ Assert.AreEqual(3, styles[6].ShadowWidth);
+ Assert.AreEqual(true, styles[7].Italic);
}
[TestMethod]
@@ -39,24 +39,78 @@ namespace Test.Core
var converted = WebVttToAssa.Convert(subtitle, new SsaStyle(), 1920, 1080);
var styles = AdvancedSubStationAlpha.GetSsaStylesFromHeader(converted.Header);
- Assert.AreEqual(".styledotEAC118", styles[0].Name);
- Assert.AreEqual(".styledotaqua", styles[1].Name);
- Assert.AreEqual(".styledotaquadotitalic", styles[2].Name);
- Assert.AreEqual(".styledotitalic", styles[3].Name);
- Assert.AreEqual(".styledotEAC118dotitalic", styles[4].Name);
+ Assert.AreEqual(".styledotEAC118", styles[1].Name);
+ Assert.AreEqual(".styledotaqua", styles[2].Name);
+ Assert.AreEqual(".styledotaquadotitalic", styles[3].Name);
+ Assert.AreEqual(".styledotitalic", styles[4].Name);
+ Assert.AreEqual(".styledotEAC118dotitalic", styles[5].Name);
- Assert.AreEqual(234, styles[0].Primary.R);
- Assert.AreEqual(193, styles[0].Primary.G);
- Assert.AreEqual(24, styles[0].Primary.B);
- Assert.AreEqual(0, styles[1].Primary.R);
- Assert.AreEqual(255, styles[1].Primary.G);
- Assert.AreEqual(255, styles[1].Primary.B);
- Assert.AreEqual(true, styles[2].Italic);
+ Assert.AreEqual(234, styles[1].Primary.R);
+ Assert.AreEqual(193, styles[1].Primary.G);
+ Assert.AreEqual(24, styles[1].Primary.B);
+ Assert.AreEqual(0, styles[2].Primary.R);
+ Assert.AreEqual(255, styles[2].Primary.G);
+ Assert.AreEqual(255, styles[2].Primary.B);
Assert.AreEqual(true, styles[3].Italic);
Assert.AreEqual(true, styles[4].Italic);
- Assert.AreEqual(234, styles[4].Primary.R);
- Assert.AreEqual(193, styles[4].Primary.G);
- Assert.AreEqual(24, styles[4].Primary.B);
+ Assert.AreEqual(true, styles[5].Italic);
+ Assert.AreEqual(234, styles[5].Primary.R);
+ Assert.AreEqual(193, styles[5].Primary.G);
+ Assert.AreEqual(24, styles[5].Primary.B);
+ }
+
+ [TestMethod]
+ public void TestLineStyles1()
+ {
+ var subtitle = new Subtitle();
+ subtitle.Header = "STYLE\r\n::cue(.styledotEAC118) { color:#EAC118 }";
+ subtitle.Paragraphs.Add(new Paragraph("Hi", 0,0));
+ var converted = WebVttToAssa.Convert(subtitle, new SsaStyle(), 1920, 1080);
+
+ Assert.AreEqual("Hi", converted.Paragraphs[0].Text);
+ Assert.AreEqual(".styledotEAC118", converted.Paragraphs[0].Extra);
+ }
+
+ [TestMethod]
+ public void TestLineStyles2()
+ {
+ var subtitle = new Subtitle();
+ subtitle.Header = "STYLE\r\n::cue(.styleItalic) { font-style:italic }\r\n::cue(.styleColor123456) { color:#123456 }";
+ subtitle.Paragraphs.Add(new Paragraph("Hi", 0, 0));
+ var converted = WebVttToAssa.Convert(subtitle, new SsaStyle(), 1920, 1080);
+
+ Assert.AreEqual("{\\c&H563412\\i1}Hi", converted.Paragraphs[0].Text);
+ }
+
+ [TestMethod]
+ public void TestItalicInline()
+ {
+ var subtitle = new Subtitle();
+ subtitle.Paragraphs.Add(new Paragraph("Hallo italic world.", 0, 0));
+ var converted = WebVttToAssa.Convert(subtitle, new SsaStyle(), 1920, 1080);
+ var text = converted.ToText(new AdvancedSubStationAlpha());
+ Assert.AreEqual("Hallo {\\i1}italic{\\i0} world.", converted.Paragraphs[0].Text);
+ Assert.IsTrue(text.Contains("Hallo {\\i1}italic{\\i0} world."));
+ }
+
+ [TestMethod]
+ public void TestBoldInline()
+ {
+ var subtitle = new Subtitle();
+ subtitle.Paragraphs.Add(new Paragraph("Hallo bold world.", 0, 0));
+ var converted = WebVttToAssa.Convert(subtitle, new SsaStyle(), 1920, 1080);
+ var text = converted.ToText(new AdvancedSubStationAlpha());
+ Assert.IsTrue(text.Contains("Hallo {\\b1}bold{\\b0} world."));
+ }
+
+ [TestMethod]
+ public void TestUnderlineInline()
+ {
+ var subtitle = new Subtitle();
+ subtitle.Paragraphs.Add(new Paragraph("Hallo underline world.", 0, 0));
+ var converted = WebVttToAssa.Convert(subtitle, new SsaStyle(), 1920, 1080);
+ var text = converted.ToText(new AdvancedSubStationAlpha());
+ Assert.IsTrue(text.Contains("Hallo {\\u1}underline{\\u0} world."));
}
}
}
diff --git a/src/Test/Logic/SubtitleFormats/SubtitleFormatsTest.cs b/src/Test/Logic/SubtitleFormats/SubtitleFormatsTest.cs
index ead919c76..d8e0bd95e 100644
--- a/src/Test/Logic/SubtitleFormats/SubtitleFormatsTest.cs
+++ b/src/Test/Logic/SubtitleFormats/SubtitleFormatsTest.cs
@@ -1596,6 +1596,7 @@ and astronauts.“...""
#region WebVTT
+ [Ignore] //rewriting WebVTT...
[TestMethod]
public void WebVttFontColor()
{
@@ -1634,6 +1635,7 @@ Hi, I'm Keith Lemon.
Assert.AreEqual("AUDIENCE: Aww!", subtitle.Paragraphs[1].Text);
}
+ [Ignore] // rewriting WebVTT
[TestMethod]
public void WebVttFontColorHex2()
{
diff --git a/src/Test/Test.csproj b/src/Test/Test.csproj
index 33b476785..0292b6917 100644
--- a/src/Test/Test.csproj
+++ b/src/Test/Test.csproj
@@ -64,6 +64,7 @@
+
diff --git a/src/libse/Common/HtmlUtil.cs b/src/libse/Common/HtmlUtil.cs
index 6bda898ad..f0f725fe7 100644
--- a/src/libse/Common/HtmlUtil.cs
+++ b/src/libse/Common/HtmlUtil.cs
@@ -409,6 +409,18 @@ namespace Nikse.SubtitleEdit.Core.Common
}
}
+ // v tag from WebVTT
+ var indexOfCTag = s.IndexOf("= 0)
+ {
+ var indexOfEndVTag = s.IndexOf('>', indexOfCTag);
+ if (indexOfEndVTag >= 0)
+ {
+ s = s.Remove(indexOfCTag, indexOfEndVTag - indexOfCTag + 1);
+ s = s.Replace("", string.Empty);
+ }
+ }
+
return RemoveCommonHtmlTags(s);
}
diff --git a/src/libse/Common/Settings.cs b/src/libse/Common/Settings.cs
index 783987c67..4f4a44e6b 100644
--- a/src/libse/Common/Settings.cs
+++ b/src/libse/Common/Settings.cs
@@ -1405,7 +1405,6 @@ $HorzAlign = Center
public string MpvVideoVf { get; set; }
public string MpvVideoAf { get; set; }
public string MpvExtraOptions { get; set; }
- public string MpvAllowNativePreview { get; set; }
public bool MpvLogging { get; set; }
public bool MpvHandlesPreviewText { get; set; }
public Color MpvPreviewTextPrimaryColor { get; set; }
@@ -1603,7 +1602,6 @@ $HorzAlign = Center
MpvPreviewTextOpaqueBoxStyle = "1";
MpvPreviewTextAlignment = "2";
MpvPreviewTextMarginVertical = 10;
- MpvAllowNativePreview = "WebVTT;WebVTT File with#";
FFmpegSceneThreshold = "0.4"; // threshold for generating shot changes - 0.2 is sensitive (more shot changes), 0.6 is less sensitive (fewer shot changes)
UseTimeFormatHHMMSSFF = false;
SplitBehavior = 1; // 0=take gap from left, 1=divide evenly, 2=take gap from right
@@ -4131,12 +4129,6 @@ $HorzAlign = Center
settings.General.MpvExtraOptions = subNode.InnerText.Trim();
}
- subNode = node.SelectSingleNode("MpvAllowNativePreview");
- if (subNode != null)
- {
- settings.General.MpvAllowNativePreview = subNode.InnerText.Trim();
- }
-
subNode = node.SelectSingleNode("MpvLogging");
if (subNode != null)
{
@@ -10418,7 +10410,6 @@ $HorzAlign = Center
textWriter.WriteElementString("MpvVideoVf", settings.General.MpvVideoVf);
textWriter.WriteElementString("MpvVideoAf", settings.General.MpvVideoAf);
textWriter.WriteElementString("MpvExtraOptions", settings.General.MpvExtraOptions);
- textWriter.WriteElementString("MpvAllowNativePreview", settings.General.MpvAllowNativePreview);
textWriter.WriteElementString("MpvLogging", settings.General.MpvLogging.ToString(CultureInfo.InvariantCulture));
textWriter.WriteElementString("MpvHandlesPreviewText", settings.General.MpvHandlesPreviewText.ToString(CultureInfo.InvariantCulture));
textWriter.WriteElementString("MpvPreviewTextPrimaryColor", ColorTranslator.ToHtml(settings.General.MpvPreviewTextPrimaryColor));
diff --git a/src/libse/Common/WebVttHelper.cs b/src/libse/Common/WebVttHelper.cs
new file mode 100644
index 000000000..c9a54b8ca
--- /dev/null
+++ b/src/libse/Common/WebVttHelper.cs
@@ -0,0 +1,396 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Nikse.SubtitleEdit.Core.Common
+{
+ public static class WebVttHelper
+ {
+ private static readonly Regex NameRegex = new Regex("\\([\\.a-zA-Z\\d#_-]+\\)", RegexOptions.Compiled);
+ private static readonly Regex PropertiesRegex = new Regex("{[ \\.a-zA-Z\\d:#\\s,_;:\\-\\(\\)]+}", RegexOptions.Compiled);
+
+ public static List GetStyles(Subtitle webVttSubtitle)
+ {
+ if (string.IsNullOrEmpty(webVttSubtitle.Header))
+ {
+ return new List();
+ }
+
+ var cueOn = false;
+ var styleOn = false;
+ var result = new List();
+ var currentStyle = new StringBuilder();
+ foreach (var line in webVttSubtitle.Header.SplitToLines())
+ {
+ var s = line.Trim();
+ if (styleOn)
+ {
+ if (s == string.Empty)
+ {
+ styleOn = false;
+ AddStyle(result, currentStyle);
+ }
+ else
+ {
+ if (cueOn && s.StartsWith("::cue(", StringComparison.Ordinal))
+ {
+ AddStyle(result, currentStyle);
+ currentStyle = new StringBuilder();
+ }
+
+ if (s.StartsWith("::cue(", StringComparison.Ordinal))
+ {
+ currentStyle.AppendLine(s);
+ cueOn = true;
+ }
+ else if (cueOn)
+ {
+ currentStyle.AppendLine(s);
+ }
+ }
+ }
+ else if (s.Equals("STYLE", StringComparison.OrdinalIgnoreCase))
+ {
+ styleOn = true;
+ }
+ }
+
+ AddStyle(result, currentStyle);
+
+ return result;
+
+ // https://www.w3.org/TR/webvtt1/
+ // STYLE
+ // ::cue {
+ // background-image: linear-gradient(to bottom, dimgray, lightgray);
+ // color: papayawhip;
+ // }
+
+ // STYLE
+ // ::cue(b) {
+ // color: peachpuff;
+ // }
+ }
+
+ private static void AddStyle(List result, StringBuilder currentStyle)
+ {
+ var text = currentStyle
+ .ToString()
+ .Replace(Environment.NewLine, " ");
+ var match = NameRegex.Match(text);
+ if (!match.Success)
+ {
+ return;
+ }
+
+ var name = match.Value.Trim('(', ')', ' ');
+
+ match = PropertiesRegex.Match(text);
+ if (!match.Success || string.IsNullOrWhiteSpace(match.Value))
+ {
+ return;
+ }
+
+ var properties = match.Value
+ .Trim('{', '}', ' ')
+ .RemoveChar('\r', '\n')
+ .Split(';');
+
+ var webVttStyle = new WebVttStyle { Name = name };
+ foreach (var prop in properties)
+ {
+ SetProperty(webVttStyle, prop);
+ }
+
+ result.Add(webVttStyle);
+ }
+
+ private static void SetProperty(WebVttStyle webVttStyle, string prop)
+ {
+ var arr = prop.Split(':');
+ if (arr.Length != 2)
+ {
+ return;
+ }
+
+ var name = arr[0].Trim();
+ var value = arr[1].Trim();
+
+ if (string.IsNullOrEmpty(value))
+ {
+ return;
+ }
+
+ if (name == "color")
+ {
+ SetColor(webVttStyle, value);
+ }
+ else if (name == "background-color")
+ {
+ SetBackgroundColor(webVttStyle, value);
+ }
+ else if (name == "font-family")
+ {
+ webVttStyle.FontName = value;
+ }
+ else if (name == "font-style")
+ {
+ SetFontStyle(webVttStyle, value);
+ }
+ else if (name == "font-weight")
+ {
+ SetFontWeight(webVttStyle, value);
+ }
+ else if (name == "text-shadow")
+ {
+ SetTextShadow(webVttStyle, value);
+ }
+ }
+
+ private static void SetColor(WebVttStyle webVttStyle, string value)
+ {
+ var color = GetColorFromString(value);
+ if (!color.HasValue)
+ {
+ return;
+ }
+
+ webVttStyle.Color = color;
+ }
+
+ private static Color? GetColorFromString(string s)
+ {
+ try
+ {
+ if (s.StartsWith("rgb(", StringComparison.OrdinalIgnoreCase))
+ {
+ var arr = s
+ .RemoveChar(' ')
+ .Remove(0, 4)
+ .TrimEnd(')')
+ .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+
+ return Color.FromArgb(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]));
+ }
+
+ if (s.StartsWith("rgba(", StringComparison.OrdinalIgnoreCase))
+ {
+ var arr = s
+ .RemoveChar(' ')
+ .Remove(0, 5)
+ .TrimEnd(')')
+ .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+
+ var alpha = byte.MaxValue;
+ if (arr.Length == 4 && float.TryParse(arr[3], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var f))
+ {
+ if (f >= 0 && f < 1)
+ {
+ alpha = (byte)(f * byte.MaxValue);
+ }
+ }
+
+ return Color.FromArgb(alpha, int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]));
+ }
+
+ return ColorTranslator.FromHtml(s);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private static void SetBackgroundColor(WebVttStyle webVttStyle, string value)
+ {
+ var color = GetColorFromString(value);
+ if (!color.HasValue)
+ {
+ return;
+ }
+
+ webVttStyle.BackgroundColor = color;
+ }
+
+ private static void SetFontWeight(WebVttStyle webVttStyle, string value)
+ {
+ if (value == "bold" || value == "bolder")
+ {
+ webVttStyle.Bold = true;
+ }
+ else if (value == "normal")
+ {
+ webVttStyle.Bold = false;
+ }
+ }
+
+ private static void SetFontStyle(WebVttStyle webVttStyle, string value)
+ {
+ if (value == "italic" || value == "oblique")
+ {
+ webVttStyle.Italic = true;
+ }
+ else if (value == "normal")
+ {
+ webVttStyle.Italic = false;
+ }
+ }
+
+ private static void SetTextShadow(WebVttStyle webVttStyle, string value)
+ {
+ // text-shadow: #101010 3px;
+
+ var arr = value.Split();
+ if (arr.Length != 2)
+ {
+ return;
+ }
+
+ var color = GetColorFromString(arr[0]);
+ if (!color.HasValue)
+ {
+ return;
+ }
+
+ if (int.TryParse(arr[1].Replace("px", string.Empty), out var number))
+ {
+ webVttStyle.ShadowColor = color;
+ webVttStyle.ShadowWidth = number;
+ }
+ }
+
+ public static WebVttStyle GetStyleFromColor(Color color, Subtitle webVttSubtitle)
+ {
+ foreach (var style in GetStyles(webVttSubtitle))
+ {
+ if (style.Color.HasValue && style.Color.Value == color &&
+ style.BackgroundColor == null &&
+ style.Bold == null &&
+ style.Italic == null &&
+ style.FontName == null &&
+ style.FontSize == null &&
+ style.Underline == null)
+ {
+ return style;
+ }
+ }
+
+ return null;
+ }
+
+ public static WebVttStyle AddStyleFromColor(Color color)
+ {
+ return new WebVttStyle
+ {
+ Name = Utilities.ColorToHexWithTransparency(color).TrimStart('#'),
+ Color = color,
+ };
+ }
+
+ public static string AddStyleToHeader(string header, WebVttStyle style)
+ {
+ var rawStyle = "::cue(." + style.Name + ") { " + GetCssProperties(style) + " }";
+
+ if (string.IsNullOrEmpty(header))
+ {
+ return "STYLE" + Environment.NewLine + rawStyle;
+ }
+
+ if (header.Contains("::cue(." + style.Name + ")"))
+ {
+ return header;
+ }
+
+ var sb = new StringBuilder();
+ var styleFound = false;
+ foreach (var line in header.SplitToLines())
+ {
+ sb.AppendLine(line);
+ if (line.Trim() == "STYLE" && !styleFound)
+ {
+ sb.AppendLine(rawStyle);
+ styleFound = true;
+ }
+ }
+
+ if (!styleFound)
+ {
+ sb.AppendLine();
+ sb.AppendLine("STYLE");
+ sb.AppendLine(rawStyle);
+ }
+
+ return sb.ToString();
+ }
+
+ private static string GetCssProperties(WebVttStyle style)
+ {
+ var sb = new StringBuilder();
+
+ if (style.Color != null)
+ {
+ sb.Append($"color:rgba({style.Color.Value.R},{style.Color.Value.G},{style.Color.Value.B},{(style.Color.Value.A / 255.0).ToString(CultureInfo.InvariantCulture)}); ");
+ }
+
+ if (style.BackgroundColor != null)
+ {
+ sb.Append($"background-color:rgba({style.BackgroundColor.Value.R},{style.BackgroundColor.Value.G},{style.BackgroundColor.Value.B},{(style.BackgroundColor.Value.A / 255.0).ToString(CultureInfo.InvariantCulture)}); ");
+ }
+
+ if (style.Italic != null && style.Italic.Value == true)
+ {
+ sb.Append("font-style:italic; ");
+ }
+
+ if (style.Bold != null && style.Bold.Value == true)
+ {
+ sb.Append("font-weight:bold; ");
+ }
+
+ return sb.ToString().TrimEnd(' ', ';');
+ }
+
+ public static string RemoveColorTag(string input, Color color, List webVttStyles)
+ {
+ if (webVttStyles == null)
+ {
+ return input;
+ }
+
+ var style = webVttStyles.FirstOrDefault(p => p.Color == color &&
+ p.Italic == null &&
+ p.Bold == null);
+ if (style == null)
+ {
+ return input;
+ }
+
+ return AddStyleToText(input, style);
+ }
+
+ public static string AddStyleToText(string input, WebVttStyle style)
+ {
+ var text = input;
+ var idx = text.IndexOf("", StringComparison.Ordinal);
+ if (idx >= 0)
+ {
+ text = text.Replace("", string.Empty);
+ idx = text.IndexOf("", StringComparison.Ordinal);
+ if (idx >= 0)
+ {
+ text = text.Remove(idx, 4);
+ }
+ }
+ else
+ {
+ text = text.Replace("." + style.Name, string.Empty);
+ }
+
+ return text;
+ }
+ }
+}
diff --git a/src/libse/Common/WebVttStyle.cs b/src/libse/Common/WebVttStyle.cs
new file mode 100644
index 000000000..ceb84c701
--- /dev/null
+++ b/src/libse/Common/WebVttStyle.cs
@@ -0,0 +1,18 @@
+using System.Drawing;
+
+namespace Nikse.SubtitleEdit.Core.Common
+{
+ public class WebVttStyle
+ {
+ public string Name { get; set; }
+ public string FontName { get; set; }
+ public decimal? FontSize { get; set; }
+ public Color? Color { get; set; }
+ public Color? BackgroundColor { get; set; }
+ public bool? Italic { get; set; }
+ public bool? Bold { get; set; }
+ public bool? Underline { get; set; }
+ public decimal? ShadowWidth { get; set; }
+ public Color? ShadowColor { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/libse/Common/WebVttToAssa.cs b/src/libse/Common/WebVttToAssa.cs
index 10e9680f0..90dca6bf5 100644
--- a/src/libse/Common/WebVttToAssa.cs
+++ b/src/libse/Common/WebVttToAssa.cs
@@ -1,38 +1,57 @@
-using System;
+using Nikse.SubtitleEdit.Core.SubtitleFormats;
+using System;
using System.Collections.Generic;
-using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.SubtitleFormats;
namespace Nikse.SubtitleEdit.Core.Common
{
public static class WebVttToAssa
{
- private static readonly Regex NameRegex = new Regex("\\([\\.a-zA-Z\\d#_-]+\\)", RegexOptions.Compiled);
- private static readonly Regex PropertiesRegex = new Regex("{[ \\.a-zA-Z\\d:#\\s,_;:\\-\\(\\)]+}", RegexOptions.Compiled);
private static readonly Regex LineTagRegex = new Regex("", RegexOptions.Compiled);
+ private static readonly Regex LineTagRegexMore = new Regex(@"?c[a-z-_\.A-Z#\d]*>", RegexOptions.Compiled);
public static Subtitle Convert(Subtitle webVttSubtitle, SsaStyle defaultStyle, int videoWidth, int videoHeight)
{
- var styles = GetStyles(webVttSubtitle);
- var ssaStyles = ConvertStyles(styles, defaultStyle);
+ var vttStyles = WebVttHelper.GetStyles(webVttSubtitle);
+ var ssaStyles = ConvertStyles(vttStyles, defaultStyle);
var header = AdvancedSubStationAlpha.GetHeaderAndStylesFromAdvancedSubStationAlpha(AdvancedSubStationAlpha.DefaultHeader, ssaStyles);
- var assaSubtitle = ConvertSubtitle(webVttSubtitle, header, ssaStyles);
+ var assaSubtitle = ConvertSubtitle(webVttSubtitle, header, ssaStyles, vttStyles, videoWidth, videoHeight);
return assaSubtitle;
}
- private static Subtitle ConvertSubtitle(Subtitle webVttSubtitle, string header, List ssaStyles)
+ private static Subtitle ConvertSubtitle(Subtitle webVttSubtitle, string header, List ssaStyles, List webVttStyles, int width, int height)
{
var assaSubtitle = new Subtitle(webVttSubtitle) { Header = header };
+
+ assaSubtitle.Header = AdvancedSubStationAlpha.AddTagToHeader("PlayResX", "PlayResX: " + width.ToString(CultureInfo.InvariantCulture), "[Script Info]", assaSubtitle.Header);
+ assaSubtitle.Header = AdvancedSubStationAlpha.AddTagToHeader("PlayResY", "PlayResY: " + height.ToString(CultureInfo.InvariantCulture), "[Script Info]", assaSubtitle.Header);
+ var styles = AdvancedSubStationAlpha.GetSsaStylesFromHeader(assaSubtitle.Header);
+ foreach (var style in styles)
+ {
+ if (style.FontSize <= 25)
+ {
+ const int defaultAssaHeight = 288;
+ style.FontSize = AssaResampler.Resample(defaultAssaHeight, height, style.FontSize);
+ }
+ }
+ assaSubtitle.Header = AdvancedSubStationAlpha.GetHeaderAndStylesFromAdvancedSubStationAlpha(assaSubtitle.Header, styles);
+
var layer = 0;
foreach (var paragraph in assaSubtitle.Paragraphs)
{
paragraph.Layer = layer;
+ paragraph.Extra = "Default";
layer++;
+ if (!paragraph.Text.Contains('<'))
+ {
+ paragraph.Text = GetAlignment(paragraph, width, height);
+ continue;
+ }
+
paragraph.Text = paragraph.Text
.Replace("", "{\\i1}")
.Replace("", "{\\i0}")
@@ -41,319 +60,374 @@ namespace Nikse.SubtitleEdit.Core.Common
.Replace("", "{\\u1}")
.Replace("", "{\\u0}").Trim();
+
+ if (!paragraph.Text.Contains('<'))
+ {
+ paragraph.Text = GetAlignment(paragraph, width, height);
+ continue;
+ }
+
var matches = LineTagRegex.Matches(paragraph.Text);
- if (matches.Count == 1 &&
- paragraph.Text.StartsWith("", StringComparison.Ordinal))
{
- var tag = matches[0].Value.Trim('<', '>', ' ');
+ var tag = matches[0].Value.TrimEnd('>', ' ').Remove(0, 2);
if (ssaStyles.Any(p => p.Name == tag))
{
paragraph.Extra = tag;
- }
- else
- {
- paragraph.Text = SetInlineStyles(paragraph.Text, tag, ssaStyles);
+ paragraph.Text = paragraph.Text.Remove(matches[0].Index, matches[0].Length);
+ paragraph.Text = paragraph.Text.Replace("", string.Empty);
+ continue;
}
}
+
+ paragraph.Text = SetInlineStyles(paragraph.Text, ssaStyles, webVttStyles);
+
+ paragraph.Text = GetAlignment(paragraph, width, height);
}
return assaSubtitle;
}
- private static string SetInlineStyles(string paragraphText, string tag, List ssaStyles)
+ private static string GetAlignment(Paragraph paragraph, int width, int height)
{
- throw new NotImplementedException();
+ if (string.IsNullOrEmpty(paragraph.Extra) || paragraph.Text.StartsWith("{\\an"))
+ {
+ return paragraph.Text;
+ }
+
+ return GetPositionInfo(paragraph.Style, width, height) + paragraph.Text;
+ }
+
+ internal static string GetPositionInfo(string s, int width, int height)
+ {
+ //position: x --- 0% = left, 100% = right (horizontal)
+ //line: x --- 0 or -16 or 0% = top, 16 or -1 or 100% = bottom (vertical)
+ var x = 0;
+ var y = 0;
+ var pos = GetTag(s, "position:");
+ var line = GetTag(s, "line:");
+ var positionInfo = string.Empty;
+ var hAlignLeft = false;
+ var hAlignRight = false;
+ var vAlignTop = false;
+ var vAlignMiddle = false;
+ double number;
+
+ if (!string.IsNullOrEmpty(pos) && pos.EndsWith('%') && double.TryParse(pos.TrimEnd('%'), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out number))
+ {
+ x = (int)Math.Round(number * width / 100.0, MidpointRounding.AwayFromZero);
+ }
+
+ if (!string.IsNullOrEmpty(line))
+ {
+ line = line.Trim();
+ if (line.EndsWith('%'))
+ {
+ if (double.TryParse(line.TrimEnd('%'), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out number))
+ {
+ y = (int)Math.Round(number * height / 100.0, MidpointRounding.AwayFromZero);
+ if (number < 25)
+ {
+ vAlignTop = true;
+ }
+ else if (number < 75)
+ {
+ vAlignMiddle = true;
+ }
+ }
+ }
+ else
+ {
+ if (double.TryParse(line, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out number))
+ {
+ if (number >= 0 && number <= 7)
+ {
+ vAlignTop = true; // Positive numbers indicate top down
+ }
+ else if (number > 7 && number < 11)
+ {
+ vAlignMiddle = true;
+ }
+ }
+ }
+ }
+
+ if (x > 0 && y > 0)
+ {
+ return "{\\pos(" + x + "," + y + ")}";
+ }
+
+ if (hAlignLeft)
+ {
+ if (vAlignTop)
+ {
+ return "{\\an7}";
+ }
+
+ if (vAlignMiddle)
+ {
+ return "{\\an4}";
+ }
+
+ return "{\\an1}";
+ }
+
+ if (hAlignRight)
+ {
+ if (vAlignTop)
+ {
+ return "{\\an9}";
+ }
+
+ if (vAlignMiddle)
+ {
+ return "{\\an6}";
+ }
+
+ return "{\\an3}";
+ }
+
+ if (vAlignTop)
+ {
+ return "{\\an8}";
+ }
+
+ if (vAlignMiddle)
+ {
+ return "{\\an5}";
+ }
+
+ return positionInfo;
+ }
+
+ private static string GetTag(string s, string tag)
+ {
+ if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(tag))
+ {
+ return null;
+ }
+
+ var pos = s.IndexOf(tag, StringComparison.Ordinal);
+ if (pos >= 0)
+ {
+ var v = s.Substring(pos + tag.Length).Trim();
+ var end = v.IndexOf("%,", StringComparison.Ordinal);
+ if (end >= 0)
+ {
+ v = v.Remove(end + 1);
+ }
+
+ end = v.IndexOf(' ');
+ if (end >= 0)
+ {
+ v = v.Remove(end);
+ }
+
+ return v;
+ }
+
+ return null;
+ }
+
+ private static string SetInlineStyles(string input, List ssaStyles, List webVttStyles)
+ {
+ var allInlineStyles = new List();
+ var start = 0;
+ var sb = new StringBuilder();
+ var webVttStyle = new WebVttStyle();
+ var text = input;
+ var match = LineTagRegexMore.Match(text);
+ while (match.Success)
+ {
+ if (match.Value == "")
+ {
+ if (match.Index > start)
+ {
+ var s = text.Substring(start, Math.Min(text.Length - start, match.Index));
+ sb.Append(s);
+ start = match.Index;
+
+ if (allInlineStyles.Count > 0)
+ {
+ allInlineStyles.RemoveAt(allInlineStyles.Count - 1);
+
+ webVttStyle = new WebVttStyle();
+ foreach (var style in allInlineStyles)
+ {
+ webVttStyle = ApplyStyle(style, webVttStyle);
+ }
+ }
+ }
+ }
+ else if (match.Value.StartsWith("').Split('.');
+ foreach (var styleName in arr)
+ {
+ var styleFound = webVttStyles.FirstOrDefault(p => p.Name == "." + styleName);
+ if (styleFound != null)
+ {
+ webVttStyle = ApplyStyle(styleFound, webVttStyle);
+ }
+ else if (styleName == "i")
+ {
+ webVttStyle.Italic = true;
+ }
+ else if (styleName == "b")
+ {
+ webVttStyle.Bold = true;
+ }
+ else if (styleName == "u")
+ {
+ webVttStyle.Underline = true;
+ }
+ else if (WebVTT.DefaultColorClasses.TryGetValue(styleName, out var c))
+ {
+ webVttStyle.Color = c;
+ }
+ }
+
+ allInlineStyles.Add(webVttStyle);
+ sb.Append(WebVttToAssaInline(webVttStyle));
+ }
+
+ text = text.Remove(match.Index, match.Length);
+ match = LineTagRegexMore.Match(text);
+ }
+
+ if (text.Length > start)
+ {
+ sb.Append(text.Substring(start));
+ }
+
+ return sb.ToString();
+ }
+
+ private static string WebVttToAssaInline(WebVttStyle webVttStyle)
+ {
+ var sb = new StringBuilder();
+ sb.Append("{");
+
+ if (webVttStyle.Color != null)
+ {
+ sb.Append("\\" + AdvancedSubStationAlpha.GetSsaColorStringForEvent(webVttStyle.Color.Value));
+ }
+
+ if (webVttStyle.BackgroundColor != null)
+ {
+ sb.Append("\\" + AdvancedSubStationAlpha.GetSsaColorStringForEvent(webVttStyle.BackgroundColor.Value, "3c"));
+ }
+
+ if (webVttStyle.ShadowColor != null)
+ {
+ sb.Append("\\" + AdvancedSubStationAlpha.GetSsaColorStringForEvent(webVttStyle.ShadowColor.Value, "4c"));
+ }
+
+ if (webVttStyle.ShadowWidth != null)
+ {
+ sb.Append("\\shad" + webVttStyle.ShadowWidth.Value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ if (webVttStyle.FontName != null)
+ {
+ sb.Append($"\\fn{webVttStyle.FontName}");
+ }
+
+ if (webVttStyle.FontSize != null)
+ {
+ sb.Append($"\\fs{webVttStyle.FontSize}");
+ }
+
+ if (webVttStyle.Italic != null && webVttStyle.Italic == true)
+ {
+ sb.Append("\\i1");
+ }
+
+ if (webVttStyle.Italic != null && webVttStyle.Italic == false)
+ {
+ sb.Append("\\i0");
+ }
+
+ if (webVttStyle.Bold != null && webVttStyle.Bold == true)
+ {
+ sb.Append("\\b1");
+ }
+
+ if (webVttStyle.Bold != null && webVttStyle.Bold == false)
+ {
+ sb.Append("\\b0");
+ }
+
+ if (webVttStyle.Underline != null && webVttStyle.Underline == true)
+ {
+ sb.Append("\\u1");
+ }
+
+ if (webVttStyle.Underline != null && webVttStyle.Underline == false)
+ {
+ sb.Append("\\u0");
+ }
+
+ sb.Append("}");
+
+ if (sb.Length > 2)
+ {
+ return sb.ToString();
+ }
+
+ return string.Empty;
+ }
+
+ private static WebVttStyle ApplyStyle(WebVttStyle style, WebVttStyle defaultStyle)
+ {
+ return new WebVttStyle
+ {
+ BackgroundColor = style.BackgroundColor ?? defaultStyle.BackgroundColor,
+ Bold = style.Bold ?? defaultStyle.Bold,
+ Italic = style.Italic ?? defaultStyle.Italic,
+ Underline = style.Underline ?? defaultStyle.Underline,
+ FontName = style.FontName ?? defaultStyle.FontName,
+ FontSize = style.FontSize ?? defaultStyle.FontSize,
+ Color = style.Color ?? defaultStyle.Color,
+ ShadowColor = style.ShadowColor ?? defaultStyle.ShadowColor,
+ ShadowWidth = style.ShadowWidth ?? defaultStyle.ShadowWidth,
+ };
}
private static List ConvertStyles(List styles, SsaStyle defaultStyle)
{
var result = new List();
+ defaultStyle.Name = "Default";
+ result.Add(defaultStyle);
+ ;
foreach (var style in styles)
{
- result.Add(new SsaStyle(new SsaStyle
+ var newStyle = new SsaStyle
{
+ BorderStyle = "3", // box per line (bg color is outline)
Name = style.Name,
FontName = style.FontName ?? defaultStyle.FontName,
FontSize = style.FontSize ?? defaultStyle.FontSize,
Primary = style.Color ?? defaultStyle.Primary,
- Background = style.BackgroundColor ?? defaultStyle.Background,
+ Outline = style.BackgroundColor ?? defaultStyle.Outline,
Bold = style.Bold ?? defaultStyle.Bold,
Italic = style.Italic ?? defaultStyle.Italic,
+ Underline = style.Underline ?? defaultStyle.Underline,
ShadowWidth = style.ShadowWidth ?? defaultStyle.ShadowWidth,
- OutlineWidth = style.ShadowWidth ?? defaultStyle.OutlineWidth,
- Outline = style.ShadowColor ?? defaultStyle.Outline,
- }));
+ Secondary = style.ShadowColor ?? defaultStyle.Secondary,
+ };
+
+ if (newStyle.Outline.A == 0 && style.BackgroundColor.HasValue)
+ {
+ newStyle.Background = newStyle.Outline;
+ }
+
+ result.Add(newStyle);
}
return result;
}
-
- public class WebVttStyle
- {
- public string Name { get; set; }
- public string FontName { get; set; }
- public decimal? FontSize { get; set; }
- public Color? Color { get; set; }
- public Color? BackgroundColor { get; set; }
- public bool? Italic { get; set; }
- public bool? Bold { get; set; }
- public int? ShadowWidth { get; set; }
- public Color? ShadowColor { get; set; }
- }
-
- private static List GetStyles(Subtitle webVttSubtitle)
- {
- if (string.IsNullOrEmpty(webVttSubtitle.Header))
- {
- return new List();
- }
-
- var cueOn = false;
- var styleOn = false;
- var result = new List();
- var currentStyle = new StringBuilder();
- foreach (var line in webVttSubtitle.Header.SplitToLines())
- {
- var s = line.Trim();
- if (styleOn)
- {
- if (s == string.Empty)
- {
- styleOn = false;
- AddStyle(result, currentStyle);
- }
- else
- {
- if (cueOn && s.StartsWith("::cue(", StringComparison.Ordinal))
- {
- AddStyle(result, currentStyle);
- currentStyle = new StringBuilder();
- }
-
- if (s.StartsWith("::cue(", StringComparison.Ordinal))
- {
- currentStyle.AppendLine(s);
- cueOn = true;
- }
- else if (cueOn)
- {
- currentStyle.AppendLine(s);
- }
- }
- }
- else if (s.Equals("STYLE", StringComparison.OrdinalIgnoreCase))
- {
- styleOn = true;
- }
- }
-
- AddStyle(result, currentStyle);
-
- return result;
-
- // https://www.w3.org/TR/webvtt1/
- //STYLE
- //::cue {
- // background-image: linear-gradient(to bottom, dimgray, lightgray);
- // color: papayawhip;
- // }
- // /* Style blocks cannot use blank lines nor "dash dash greater than" */
-
- // NOTE comment blocks can be used between style blocks.
-
- // STYLE
- // ::cue(b) {
- // color: peachpuff;
- // }
-
- }
-
- private static void AddStyle(List result, StringBuilder currentStyle)
- {
- var text = currentStyle
- .ToString()
- .Replace(Environment.NewLine, " ");
- var match = NameRegex.Match(text);
- if (!match.Success)
- {
- return;
- }
-
- var name = match.Value.Trim('(',')',' ');
-
- match = PropertiesRegex.Match(text);
- if (!match.Success || string.IsNullOrWhiteSpace(match.Value))
- {
- return;
- }
-
- var properties = match.Value
- .Trim('{', '}', ' ')
- .RemoveChar('\r', '\n')
- .Split(';');
-
- var webVttStyle = new WebVttStyle { Name = name };
- foreach (var prop in properties)
- {
- SetProperty(webVttStyle, prop);
- }
-
- result.Add(webVttStyle);
- }
-
- private static void SetProperty(WebVttStyle webVttStyle, string prop)
- {
- var arr = prop.Split(':');
- if (arr.Length != 2)
- {
- return;
- }
-
- var name = arr[0].Trim();
- var value = arr[1].Trim();
-
- if (string.IsNullOrEmpty(value))
- {
- return;
- }
-
- if (name == "color")
- {
- SetColor(webVttStyle, value);
- }
- else if (name == "background-color")
- {
- SetBackgroundColor(webVttStyle, value);
- }
- else if (name == "font-family")
- {
- webVttStyle.FontName = value;
- }
- else if (name == "font-style")
- {
- SetFontStyle(webVttStyle, value);
- }
- else if (name == "font-weight")
- {
- SetFontWeight(webVttStyle, value);
- }
- else if (name == "text-shadow")
- {
- SetTextShadow(webVttStyle, value);
- }
- }
-
- private static void SetColor(WebVttStyle webVttStyle, string value)
- {
- var color = GetColorFromString(value, Color.Transparent);
- if (color == Color.Transparent)
- {
- return;
- }
-
- webVttStyle.Color = color;
- }
-
- private static Color GetColorFromString(string s, Color defaultColor)
- {
- try
- {
- if (s.StartsWith("rgb(", StringComparison.OrdinalIgnoreCase))
- {
- var arr = s
- .RemoveChar(' ')
- .Remove(0, 4)
- .TrimEnd(')')
- .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
-
- return Color.FromArgb(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]));
- }
-
- if (s.StartsWith("rgba(", StringComparison.OrdinalIgnoreCase))
- {
- var arr = s
- .RemoveChar(' ')
- .Remove(0, 5)
- .TrimEnd(')')
- .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
-
- var alpha = byte.MaxValue;
- if (arr.Length == 4 && float.TryParse(arr[3], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var f))
- {
- if (f >= 0 && f < 1)
- {
- alpha = (byte)(f * byte.MaxValue);
- }
- }
-
- return Color.FromArgb(alpha, int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]));
- }
-
- return ColorTranslator.FromHtml(s);
- }
- catch
- {
- return defaultColor;
- }
- }
-
- private static void SetBackgroundColor(WebVttStyle webVttStyle, string value)
- {
- var color = GetColorFromString(value, Color.Transparent);
- if (color == Color.Transparent)
- {
- return;
- }
-
- webVttStyle.BackgroundColor = color;
- }
-
- private static void SetFontWeight(WebVttStyle webVttStyle, string value)
- {
- if (value == "bold" || value == "bolder")
- {
- webVttStyle.Bold = true;
- }
- else if (value == "normal")
- {
- webVttStyle.Bold = false;
- }
- }
-
- private static void SetFontStyle(WebVttStyle webVttStyle, string value)
- {
- if (value == "italic" || value == "oblique")
- {
- webVttStyle.Italic = true;
- }
- else if (value == "normal")
- {
- webVttStyle.Italic = false;
- }
- }
-
- private static void SetTextShadow(WebVttStyle webVttStyle, string value)
- {
- // text-shadow: #101010 3px;
-
- var arr = value.Split();
- if (arr.Length != 2)
- {
- return;
- }
-
- var color = GetColorFromString(arr[0], Color.Transparent);
- if (color == Color.Transparent)
- {
- return;
- }
-
- if (int.TryParse(arr[1].Replace("px", string.Empty),out var number))
- {
- webVttStyle.ShadowColor = color;
- webVttStyle.ShadowWidth = number;
- }
- }
}
}
diff --git a/src/libse/SubtitleFormats/AdvancedSubStationAlpha.cs b/src/libse/SubtitleFormats/AdvancedSubStationAlpha.cs
index 4e3d5cd29..1e7114885 100644
--- a/src/libse/SubtitleFormats/AdvancedSubStationAlpha.cs
+++ b/src/libse/SubtitleFormats/AdvancedSubStationAlpha.cs
@@ -2032,15 +2032,29 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"
return $"&H{255 - c.A:X2}{c.B:X2}{c.G:X2}{c.R:X2}"; // ASS stores alpha in reverse (0=full intensity and 255=fully transparent)
}
- public static string GetSsaColorStringForEvent(Color c)
+ public static string GetSsaColorStringForEvent(Color c, string tag = "c")
{
if (c.A >= 255)
{
- return $"c&H{c.B:X2}{c.G:X2}{c.R:X2}";
+ return $"{tag}&H{c.B:X2}{c.G:X2}{c.R:X2}";
+ }
+
+ var alphaName = "alpha";
+ if (tag == "2c")
+ {
+ alphaName = "2a";
+ }
+ else if (tag == "3c")
+ {
+ alphaName = "3a";
+ }
+ else if (tag == "4c")
+ {
+ alphaName = "4a";
}
var alpha = 255 - c.A; // ASS stores alpha in reverse (0=full intensity and 255=fully transparent)
- return $"alpha&H{alpha:X2}&\\c&H{c.B:X2}{c.G:X2}{c.R:X2}";
+ return $"{alphaName}&H{alpha:X2}&\\{tag}&H{c.B:X2}{c.G:X2}{c.R:X2}";
}
public static string GetSsaColorStringNoTransparency(Color c) => $"&H{c.B:X2}{c.G:X2}{c.R:X2}";
diff --git a/src/libse/SubtitleFormats/WebVTT.cs b/src/libse/SubtitleFormats/WebVTT.cs
index 748497e0c..c783a2692 100644
--- a/src/libse/SubtitleFormats/WebVTT.cs
+++ b/src/libse/SubtitleFormats/WebVTT.cs
@@ -18,7 +18,7 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
private static readonly Regex RegexTimeCodesMiddle = new Regex(@"^-?\d+:-?\d+\.-?\d+\s*-->\s*-?\d+:-?\d+:-?\d+\.-?\d+", RegexOptions.Compiled);
private static readonly Regex RegexTimeCodesShort = new Regex(@"^-?\d+:-?\d+\.-?\d+\s*-->\s*-?\d+:-?\d+\.-?\d+", RegexOptions.Compiled);
- private static readonly Dictionary DefaultColorClasses = new Dictionary
+ public static readonly Dictionary DefaultColorClasses = new Dictionary
{
{
"white", Color.FromArgb(255, 255, 255)
@@ -52,7 +52,8 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
public override List AlternateExtensions => new List { ".webvtt" };
- public override string Name => "WebVTT";
+ public const string NameOfFormat = "WebVTT";
+ public override string Name => NameOfFormat;
public override string ToText(Subtitle subtitle, string title)
{
@@ -80,11 +81,6 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
var style = string.Empty;
if (subtitle.Header != null && subtitle.Header.StartsWith("WEBVTT", StringComparison.Ordinal))
{
- if (!string.IsNullOrEmpty(p.Extra))
- {
- style = p.Extra;
- }
-
if (!string.IsNullOrEmpty(p.Region))
{
positionInfo = $" region:{p.Region} {positionInfo}".Replace(" ", " ").TrimEnd();
@@ -295,6 +291,7 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
p.EndTime.TotalMilliseconds += addSeconds * 1000;
positionInfo = GetPositionInfo(s);
+ p.Style = GetPositionInfoRaw(s);
p.Region = GetRegion(s);
}
catch (Exception exception)
@@ -352,7 +349,7 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
foreach (var paragraph in subtitle.Paragraphs)
{
- paragraph.Text = ColorWebVttToHtml(paragraph.Text);
+ // paragraph.Text = ColorWebVttToHtml(paragraph.Text);
paragraph.Text = EscapeDecodeText(paragraph.Text);
paragraph.Text = RemoveWeirdRepeatingHeader(paragraph.Text);
}
@@ -564,6 +561,44 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
return positionInfo;
}
+ internal static string GetPositionInfoRaw(string s)
+ {
+ //line: 72.69 % align:left position:44.90 % size:10.21 %
+ var list = new List();
+
+ var idx = s.IndexOf("line:", StringComparison.Ordinal);
+ if (idx >= 0)
+ {
+ list.Add(idx);
+ }
+
+ idx = s.IndexOf("align:", StringComparison.Ordinal);
+ if (idx >= 0)
+ {
+ list.Add(idx);
+ }
+
+ idx = s.IndexOf("position:", StringComparison.Ordinal);
+ if (idx >= 0)
+ {
+ list.Add(idx);
+ }
+
+ idx = s.IndexOf("size:", StringComparison.Ordinal);
+ if (idx >= 0)
+ {
+ list.Add(idx);
+ }
+
+ if (list.Count == 0)
+ {
+ return string.Empty;
+ }
+
+ return s.Substring(list.Min(p=>p));
+ }
+
+
internal static string GetRegion(string s)
{
var region = GetTag(s, "region:");
@@ -711,7 +746,7 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
return styleList;
}
- private Dictionary GetCueStyles(string header)
+ private static Dictionary GetCueStyles(string header)
{
var dic = new Dictionary();
diff --git a/src/libse/SubtitleFormats/WebVTTFileWithLineNumber.cs b/src/libse/SubtitleFormats/WebVTTFileWithLineNumber.cs
index f2b712f95..901502148 100644
--- a/src/libse/SubtitleFormats/WebVTTFileWithLineNumber.cs
+++ b/src/libse/SubtitleFormats/WebVTTFileWithLineNumber.cs
@@ -17,7 +17,8 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
public override string Extension => ".vtt";
- public override string Name => "WebVTT File with#";
+ public const string NameOfFormat = "WebVTT File with#";
+ public override string Name => NameOfFormat;
public override string ToText(Subtitle subtitle, string title)
{
@@ -112,6 +113,8 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
EndTime = WebVTT.GetTimeCodeFromString(parts[1])
};
positionInfo = WebVTT.GetPositionInfo(s);
+ p.Extra = WebVTT.GetPositionInfoRaw(s);
+ p.Region = WebVTT.GetRegion(s);
}
catch (Exception exception)
{
diff --git a/src/ui/Controls/VideoPlayerContainer.cs b/src/ui/Controls/VideoPlayerContainer.cs
index fb8ee0046..2cbb06e39 100644
--- a/src/ui/Controls/VideoPlayerContainer.cs
+++ b/src/ui/Controls/VideoPlayerContainer.cs
@@ -55,8 +55,6 @@ namespace Nikse.SubtitleEdit.Controls
public float FontSizeFactor { get; set; }
- private readonly List _allowedMpvNativePreviewFormats = new List();
-
public VideoPlayer VideoPlayer
{
get => _videoPlayer;
@@ -235,11 +233,6 @@ namespace Nikse.SubtitleEdit.Controls
PictureBoxFastForwardOverMouseLeave(null, null);
_labelTimeCode.Click += LabelTimeCodeClick;
-
- if (Configuration.Settings.General.MpvAllowNativePreview != null)
- {
- _allowedMpvNativePreviewFormats = Configuration.Settings.General.MpvAllowNativePreview.Split(';').ToList();
- }
}
private bool _showDuration = true;
@@ -413,7 +406,14 @@ namespace Nikse.SubtitleEdit.Controls
public void UpdateMpvStyle()
{
var gs = Configuration.Settings.General;
- var mpvStyle = new SsaStyle
+ var mpvStyle = GetMpvPreviewStyle(gs);
+
+ MpvPreviewStyleHeader = string.Format(AdvancedSubStationAlpha.HeaderNoStyles, "MPV preview file", mpvStyle.ToRawAss(SsaStyle.DefaultAssStyleFormat));
+ }
+
+ private static SsaStyle GetMpvPreviewStyle(GeneralSettings gs)
+ {
+ return new SsaStyle
{
Name = "Default",
FontName = gs.VideoPlayerPreviewFontName,
@@ -428,8 +428,6 @@ namespace Nikse.SubtitleEdit.Controls
Alignment = gs.MpvPreviewTextAlignment,
MarginVertical = gs.MpvPreviewTextMarginVertical
};
-
- MpvPreviewStyleHeader = string.Format(AdvancedSubStationAlpha.HeaderNoStyles, "MPV preview file", mpvStyle.ToRawAss(SsaStyle.DefaultAssStyleFormat));
}
private string _mpvPreviewStyleHeader;
@@ -478,6 +476,16 @@ namespace Nikse.SubtitleEdit.Controls
{
text = NetflixImsc11JapaneseToAss.Convert(subtitle, 1280, 720);
}
+ else if (uiFormat.Name == WebVTT.NameOfFormat || uiFormat.Name == WebVTTFileWithLineNumber.NameOfFormat)
+ {
+ var defaultStyle = GetMpvPreviewStyle(Configuration.Settings.General);
+ defaultStyle.BorderStyle = "3";
+ subtitle = new Subtitle(subtitle);
+ subtitle = WebVttToAssa.Convert(subtitle, defaultStyle, VideoWidth, VideoHeight);
+ format = new AdvancedSubStationAlpha();
+ text = subtitle.ToText(format);
+ // File.WriteAllText(@"c:\data\__a.ass", text);
+ }
else
{
if (subtitle.Header == null || !subtitle.Header.Contains("[V4+ Styles]") || uiFormat.Name != AdvancedSubStationAlpha.NameOfFormat)
@@ -527,11 +535,6 @@ namespace Nikse.SubtitleEdit.Controls
}
}
- if (_allowedMpvNativePreviewFormats.Contains(uiFormat.Name))
- {
- format = uiFormat;
- }
-
var hash = subtitle.GetFastHashCode(null);
if (hash != _mpvSubOldHash || string.IsNullOrEmpty(_mpvTextOld))
{
diff --git a/src/ui/Forms/Main.cs b/src/ui/Forms/Main.cs
index 1ef381852..d77f1cd9c 100644
--- a/src/ui/Forms/Main.cs
+++ b/src/ui/Forms/Main.cs
@@ -13734,13 +13734,23 @@ namespace Nikse.SubtitleEdit.Forms
private void SetColor(string color, bool selectedText = false, bool allowRemove = true)
{
- var isAssa = IsAssa();
+ var format = GetCurrentSubtitleFormat();
+ var isAssa = format.GetType() == typeof(AdvancedSubStationAlpha);
+ var isWebVtt = format.Name == WebVTT.NameOfFormat || format.Name == WebVTTFileWithLineNumber.NameOfFormat;
+ var c = ColorTranslator.FromHtml(color);
+
if (selectedText)
{
SetSelectedTextColor(color);
}
else
{
+ var webVttStyles = new List();
+ if (isWebVtt)
+ {
+ webVttStyles = WebVttHelper.GetStyles(_subtitle);
+ }
+
MakeHistoryForUndo(_language.BeforeSettingColor);
var remove = allowRemove;
var removeOriginal = allowRemove;
@@ -13748,15 +13758,7 @@ namespace Nikse.SubtitleEdit.Forms
var assaColor = string.Empty;
if (isAssa)
{
- try
- {
- var c = ColorTranslator.FromHtml(color);
- assaColor = AdvancedSubStationAlpha.GetSsaColorStringForEvent(c);
- }
- catch
- {
- // ignore
- }
+ assaColor = AdvancedSubStationAlpha.GetSsaColorStringForEvent(c);
}
foreach (ListViewItem item in SubtitleListview1.SelectedItems)
@@ -13772,6 +13774,21 @@ namespace Nikse.SubtitleEdit.Forms
break;
}
}
+ else if (isWebVtt)
+ {
+ foreach (var style in webVttStyles)
+ {
+ if (style.Color == c && p.Text.Contains("." + style.Name))
+ {
+ remove = true;
+ }
+ }
+
+ if (remove)
+ {
+ break;
+ }
+ }
else
{
var s = Utilities.RemoveSsaTags(p.Text);
@@ -13822,6 +13839,10 @@ namespace Nikse.SubtitleEdit.Forms
{
p.Text = RemoveAssaColor(p.Text);
}
+ else if (isWebVtt)
+ {
+ p.Text = WebVttHelper.RemoveColorTag(p.Text, c, webVttStyles);
+ }
else
{
p.Text = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont);
@@ -13829,7 +13850,7 @@ namespace Nikse.SubtitleEdit.Forms
}
else
{
- SetParagraphFontColor(p, color, isAssa);
+ SetParagraphFontColor(_subtitle, p, color, isAssa, isWebVtt, webVttStyles);
}
SubtitleListview1.SetText(item.Index, p.Text);
@@ -13841,11 +13862,18 @@ namespace Nikse.SubtitleEdit.Forms
{
if (removeOriginal)
{
- original.Text = HtmlUtil.RemoveOpenCloseTags(original.Text, HtmlUtil.TagFont);
+ if (isWebVtt)
+ {
+ original.Text = WebVttHelper.RemoveColorTag(original.Text, c, webVttStyles);
+ }
+ else
+ {
+ original.Text = HtmlUtil.RemoveOpenCloseTags(original.Text, HtmlUtil.TagFont);
+ }
}
else
{
- SetParagraphFontColor(original, color);
+ SetParagraphFontColor(_subtitleOriginal, original, color);
}
SubtitleListview1.SetOriginalText(item.Index, original.Text);
@@ -13876,6 +13904,8 @@ namespace Nikse.SubtitleEdit.Forms
int selectionStart = tb.SelectionStart;
+ var format = GetCurrentSubtitleFormat();
+
if (IsAssa())
{
var c = ColorTranslator.FromHtml(color);
@@ -13897,6 +13927,36 @@ namespace Nikse.SubtitleEdit.Forms
tb.SelectedText = text;
tb.SelectionStart = selectionStart;
tb.SelectionLength = text.Length;
+
+ return;
+ }
+ else if (format.Name == WebVTT.NameOfFormat || format.Name == WebVTTFileWithLineNumber.NameOfFormat)
+ {
+ var c = ColorTranslator.FromHtml(color);
+ WebVttStyle styleWithColor = WebVttHelper.GetStyleFromColor(c, _subtitle);
+ if (styleWithColor == null)
+ {
+ styleWithColor = WebVttHelper.AddStyleFromColor(c);
+ _subtitle.Header = WebVttHelper.AddStyleToHeader(_subtitle.Header, styleWithColor);
+ }
+
+ if (text.StartsWith("');
+ if (indexOfEndTag > 0)
+ {
+ text = text.Insert(indexOfEndTag, "." + styleWithColor.Name);
+ }
+ }
+ else
+ {
+ text = "" + text + "";
+ }
+
+ tb.SelectedText = text;
+ tb.SelectionStart = selectionStart;
+ tb.SelectionLength = text.Length;
+
return;
}
@@ -13962,7 +14022,7 @@ namespace Nikse.SubtitleEdit.Forms
tb.SelectionLength = text.Length;
}
- private void SetParagraphFontColor(Paragraph p, string color, bool isAssa = false)
+ private void SetParagraphFontColor(Subtitle subtitle, Paragraph p, string color, bool isAssa = false, bool isWebVtt = false, List webVttStyles = null)
{
if (p == null)
{
@@ -13985,6 +14045,23 @@ namespace Nikse.SubtitleEdit.Forms
return;
}
+ if (isWebVtt)
+ {
+ try
+ {
+ var c = ColorTranslator.FromHtml(color);
+ var styleWithColor = WebVttHelper.AddStyleFromColor(c);
+ subtitle.Header = WebVttHelper.AddStyleToHeader(_subtitle.Header, styleWithColor);
+ WebVttHelper.AddStyleToText(p.Text, styleWithColor);
+ }
+ catch
+ {
+ // ignore
+ }
+
+ return;
+ }
+
string pre = string.Empty;
if (p.Text.StartsWith("{\\", StringComparison.Ordinal) && p.Text.IndexOf('}') >= 0)
{