Work on WebVTT

This commit is contained in:
niksedk 2023-06-13 20:54:48 +02:00
parent 48db0c9b71
commit 69330fe43a
15 changed files with 1104 additions and 369 deletions

View File

@ -21,5 +21,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=assa/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Downloader/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Matroska/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nikse/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -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<WebVttStyle>
{
new WebVttStyle()
{
Name = "Red",
Color = Color.Red,
},
};
var text = "<c.Red>Red</c>";
var result = WebVttHelper.RemoveColorTag(text, Color.Red, styles);
Assert.AreEqual("Red", result);
}
[TestMethod]
public void RemoveColorTag2()
{
var styles = new List<WebVttStyle>
{
new WebVttStyle
{
Name = "Red",
Color = Color.Red,
},
new WebVttStyle
{
Name = "Italic",
Italic = true,
},
};
var text = "<c.Red.Italic>Red</c>";
var result = WebVttHelper.RemoveColorTag(text, Color.Red, styles);
Assert.AreEqual("<c.Italic>Red</c>", result);
}
}
}

View File

@ -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("<c.styledotEAC118>Hi</c>", 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("<c.styleItalic.styleColor123456>Hi</c>", 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 <i>italic</i> 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 <b>bold</b> 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 <u>underline</u> 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."));
}
}
}

View File

@ -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("<font color=\"#r008000\">AUDIENCE: Aww!</font>", subtitle.Paragraphs[1].Text);
}
[Ignore] // rewriting WebVTT
[TestMethod]
public void WebVttFontColorHex2()
{

View File

@ -64,6 +64,7 @@
<Compile Include="Assa\AssaTimeCodes.cs" />
<Compile Include="Core\AudioToTextTest.cs" />
<Compile Include="Core\CsvUtilTest.cs" />
<Compile Include="Core\WebVttHelperTest.cs" />
<Compile Include="Core\WebVttToAssaTest.cs" />
<Compile Include="Dictionaries\StringWithoutSpaceSplitToWordsTest.cs" />
<Compile Include="GoogleCloudVision\GoogleCloudVisionJsonToLinesTest.cs" />

View File

@ -409,6 +409,18 @@ namespace Nikse.SubtitleEdit.Core.Common
}
}
// v tag from WebVTT
var indexOfCTag = s.IndexOf("<c.", StringComparison.Ordinal);
if (indexOfCTag >= 0)
{
var indexOfEndVTag = s.IndexOf('>', indexOfCTag);
if (indexOfEndVTag >= 0)
{
s = s.Remove(indexOfCTag, indexOfEndVTag - indexOfCTag + 1);
s = s.Replace("</c>", string.Empty);
}
}
return RemoveCommonHtmlTags(s);
}

View File

@ -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));

View File

@ -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<WebVttStyle> GetStyles(Subtitle webVttSubtitle)
{
if (string.IsNullOrEmpty(webVttSubtitle.Header))
{
return new List<WebVttStyle>();
}
var cueOn = false;
var styleOn = false;
var result = new List<WebVttStyle>();
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<WebVttStyle> 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<WebVttStyle> 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("<c." + style.Name + ">", StringComparison.Ordinal);
if (idx >= 0)
{
text = text.Replace("<c." + style.Name + ">", string.Empty);
idx = text.IndexOf("</c>", StringComparison.Ordinal);
if (idx >= 0)
{
text = text.Remove(idx, 4);
}
}
else
{
text = text.Replace("." + style.Name, string.Empty);
}
return text;
}
}
}

View File

@ -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; }
}
}

View File

@ -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("<c\\.[a-z-_\\.A-Z#\\d]+>", 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<SsaStyle> ssaStyles)
private static Subtitle ConvertSubtitle(Subtitle webVttSubtitle, string header, List<SsaStyle> ssaStyles, List<WebVttStyle> 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("<i>", "{\\i1}")
.Replace("</i>", "{\\i0}")
@ -41,319 +60,374 @@ namespace Nikse.SubtitleEdit.Core.Common
.Replace("<u>", "{\\u1}")
.Replace("</u>", "{\\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("<c.", StringComparison.Ordinal) &&
if (matches.Count == 1 &&
paragraph.Text.StartsWith("<c.", StringComparison.Ordinal) &&
paragraph.Text.EndsWith("</c>", 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("</c>", 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<SsaStyle> 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<SsaStyle> ssaStyles, List<WebVttStyle> webVttStyles)
{
var allInlineStyles = new List<WebVttStyle>();
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 == "</c>")
{
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("<c.", StringComparison.Ordinal))
{
var arr = match.Value.Remove(0, 3).TrimEnd('>').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<SsaStyle> ConvertStyles(List<WebVttStyle> styles, SsaStyle defaultStyle)
{
var result = new List<SsaStyle>();
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<WebVttStyle> GetStyles(Subtitle webVttSubtitle)
{
if (string.IsNullOrEmpty(webVttSubtitle.Header))
{
return new List<WebVttStyle>();
}
var cueOn = false;
var styleOn = false;
var result = new List<WebVttStyle>();
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<WebVttStyle> 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;
}
}
}
}

View File

@ -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}";

View File

@ -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<string, Color> DefaultColorClasses = new Dictionary<string, Color>
public static readonly Dictionary<string, Color> DefaultColorClasses = new Dictionary<string, Color>
{
{
"white", Color.FromArgb(255, 255, 255)
@ -52,7 +52,8 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
public override List<string> AlternateExtensions => new List<string> { ".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<int>();
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<string, string> GetCueStyles(string header)
private static Dictionary<string, string> GetCueStyles(string header)
{
var dic = new Dictionary<string, string>();

View File

@ -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)
{

View File

@ -55,8 +55,6 @@ namespace Nikse.SubtitleEdit.Controls
public float FontSizeFactor { get; set; }
private readonly List<string> _allowedMpvNativePreviewFormats = new List<string>();
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))
{

View File

@ -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<WebVttStyle>();
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("<c.", StringComparison.Ordinal))
{
var indexOfEndTag = text.IndexOf('>');
if (indexOfEndTag > 0)
{
text = text.Insert(indexOfEndTag, "." + styleWithColor.Name);
}
}
else
{
text = "<c." + styleWithColor.Name + ">" + text + "</c>";
}
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<WebVttStyle> 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)
{