using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
namespace Nikse.SubtitleEdit.Core.SubtitleFormats
{
public class BelleNuitSubtitler : SubtitleFormat
{
///tc 00:00:35:09 00:00:38:05
private static readonly Regex RegexTimeCode = new Regex(@"^\/tc \d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d", RegexOptions.Compiled);
private static readonly Regex RegexFileNum = new Regex(@"^\/file\s+\d+$", RegexOptions.Compiled);
public override string Extension => ".stp";
public override string Name => "Belle Nuit Subtitler";
public override string ToText(Subtitle subtitle, string title)
{
const string paragraphWriteFormat = "/tc {0} {1}{2}{3}{2}";
var sb = new StringBuilder();
foreach (Paragraph p in subtitle.Paragraphs)
{
sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), Environment.NewLine, EncodeText(p.Text)));
}
var doc = new XmlDocument { XmlResolver = null };
doc.LoadXml("" + Environment.NewLine + @"
document
creator
SICT
type
STLI
version
1.4
applicationversion
Belle Nuit Subtitler 1.7.8
creationdate
2012-03-13 16:30:32
modificationdate
2012-03-13 16:30:32
mainleft
40
maintop
48
mainwidth
825
mainheight
886
styledt
exportdt
previewdt
moviedt
exportformat
TIFF
style
font
Geneva
size
26
spacing
1
leading
7
bold
italic
underline
vertical
486
halin
1
valign
2
standard
PAL
height
576
width
720
widthreal
768
antialiasing
4
left
40
right
680
wrapmethod
2
interlaced
textcolor
#FBFFF2
textalpha
1
textsoft
0
bordercolor
#F0F10
borderalpha
1
bordersoft
0
borderwidth
6
rectcolor
#0
rectalpha
0
rectsoft
0
rectform
1
shadowcolor
#7F7F7F
shadowalpha
0
shadowsoft
0
shadowx
2
shadowy
2
framerate
25
folderpath
prefix
moviepath
movieoffset
00:00:00:00
moviesyncoption
pagesetup
titlelist
");
XmlNode node = doc.CreateElement("string");
node.InnerText = sb.ToString().Trim() + Environment.NewLine + Environment.NewLine;
doc.DocumentElement.AppendChild(node);
return ToUtf8XmlString(doc).Replace("\r\n", "\n");
}
public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
{
_errorCount = 0;
var sb = new StringBuilder();
foreach (var line in lines)
{
sb.AppendLine(line);
}
var doc = new XmlDocument { XmlResolver = null };
try
{
doc.LoadXml(sb.ToString());
if (doc.DocumentElement == null || doc.DocumentElement.Name != "xmldict" || doc.DocumentElement.SelectSingleNode("string") == null)
{
return;
}
}
catch (Exception)
{
_errorCount = 1;
return;
}
string text = null;
string keyName = string.Empty;
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
{
if (node.Name == "key")
{
keyName = node.InnerText;
}
else if (node.Name == "string" && keyName == "titlelist")
{
text = node.InnerText;
break;
}
}
if (text == null)
{
return;
}
subtitle.Paragraphs.Clear();
Paragraph paragraph = null;
sb.Clear();
foreach (string line in text.Split(Utilities.NewLineChars))
{
if (RegexTimeCode.IsMatch(line))
{
string[] parts = line.Substring(4, 11).Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 4)
{
try
{
if (paragraph != null && !string.IsNullOrWhiteSpace(sb.ToString()))
{
paragraph.Text = DecodeText(sb);
}
var start = DecodeTimeCodeFramesFourParts(parts);
parts = line.Substring(16, 11).Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
var end = DecodeTimeCodeFramesFourParts(parts);
paragraph = new Paragraph { StartTime = start, EndTime = end };
subtitle.Paragraphs.Add(paragraph);
sb.Clear();
}
catch
{
_errorCount++;
}
}
}
else if (RegexFileNum.IsMatch(line))
{
continue; // skip Belle-Nuit's numbering lines ("/file 0001")
}
else if (paragraph != null)
{
sb.AppendLine(line);
}
else
{
_errorCount++;
}
}
if (paragraph != null && !string.IsNullOrWhiteSpace(sb.ToString()))
{
paragraph.Text = DecodeText(sb);
}
subtitle.Renumber();
}
private static string EncodeText(string s)
{
s = HtmlUtil.RemoveOpenCloseTags(s, HtmlUtil.TagBold, HtmlUtil.TagUnderline, HtmlUtil.TagFont);
if (s.StartsWith("{\\an3}", StringComparison.Ordinal) || s.StartsWith("{\\an6}", StringComparison.Ordinal))
{
s = "/STYLE RIGHT" + Environment.NewLine + s.Remove(0, 6).Trim();
}
if (s.StartsWith("{\\an1}", StringComparison.Ordinal) || s.StartsWith("{\\an4}", StringComparison.Ordinal))
{
s = "/STYLE LEFT" + Environment.NewLine + s.Remove(0, 6).Trim();
}
if (s.StartsWith("{\\an7}", StringComparison.Ordinal) || s.StartsWith("{\\an8}", StringComparison.Ordinal) || s.StartsWith("{\\an9}", StringComparison.Ordinal))
{
s = "/STYLE VERTICAL(-25)" + Environment.NewLine + s.Remove(0, 6).Trim();
}
if (s.StartsWith("{\\an2}", StringComparison.Ordinal) || s.StartsWith("{\\an5}", StringComparison.Ordinal))
{
s = s.Remove(0, 6).Trim();
}
return s;
}
private static string DecodeText(StringBuilder sb)
{
var s = sb.ToString().Trim();
s = s.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine).Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
if (s.StartsWith("/STYLE RIGHT" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an3}" + s.Remove(0, 12).Trim();
}
if (s.StartsWith("/STYLE LEFT" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an1}" + s.Remove(0, 11).Trim();
}
if (s.StartsWith("/STYLE TOP" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an8}" + s.Remove(0, 10).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-25)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an8}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-24)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an8}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-23)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an8}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-22)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an8}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-21)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an8}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-20)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an8}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-19)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an8}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-18)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an5}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-17)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an5}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-16)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an5}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-15)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an5}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-14)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an5}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-13)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an5}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-12)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an5}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-11)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an5}" + s.Remove(0, 20).Trim();
}
if (s.StartsWith("/STYLE VERTICAL(-10)" + Environment.NewLine, StringComparison.Ordinal))
{
s = "{\\an5}" + s.Remove(0, 20).Trim();
}
s = HtmlUtil.FixInvalidItalicTags(s);
return s;
}
private static string EncodeTimeCode(TimeCode time)
{
return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
}
}
}