mirror of
https://github.com/SubtitleEdit/subtitleedit.git
synced 2025-01-31 13:01:39 +01:00
Testing save to stream method on a few binary subtitle formats (for libse usage)
This commit is contained in:
parent
e157e90700
commit
cb4f6e7aba
9
libse/Interfaces/IBinaryPersistableSubtitle.cs
Normal file
9
libse/Interfaces/IBinaryPersistableSubtitle.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Nikse.SubtitleEdit.Core.Interfaces
|
||||
{
|
||||
public interface IBinaryPersistableSubtitle
|
||||
{
|
||||
bool Save(string fileName, Stream stream, Subtitle subtitle, bool batchMode);
|
||||
}
|
||||
}
|
@ -156,6 +156,7 @@
|
||||
<Compile Include="Forms\FixCommonErrors\FixUnneededSpaces.cs" />
|
||||
<Compile Include="Forms\FixCommonErrors\FixUppercaseIInsideWords.cs" />
|
||||
<Compile Include="Forms\FixCommonErrors\Helper.cs" />
|
||||
<Compile Include="Interfaces\IBinaryPersistableSubtitle.cs" />
|
||||
<Compile Include="Interfaces\IFixCallbacks.cs" />
|
||||
<Compile Include="Interfaces\IFixCommonError.cs" />
|
||||
<Compile Include="Forms\FixCommonErrors\RemoveSpaceBetweenNumbers.cs" />
|
||||
@ -195,8 +196,8 @@
|
||||
<Compile Include="SubtitleFormats\AribB36.cs" />
|
||||
<Compile Include="SubtitleFormats\AribB24Decoder.cs" />
|
||||
<Compile Include="SubtitleFormats\Csv4.cs" />
|
||||
<Compile Include="SubtitleFormats\MacSub.cs" />
|
||||
<Compile Include="SubtitleFormats\SubUrbia.cs" />
|
||||
<Compile Include="SubtitleFormats\MacSub.cs" />
|
||||
<Compile Include="SubtitleFormats\SubUrbia.cs" />
|
||||
<Compile Include="SubtitleFormats\NetflixTimedText.cs" />
|
||||
<Compile Include="SubtitleFormats\JsonType7.cs" />
|
||||
<Compile Include="SubtitleFormats\PhoenixSubtitle.cs" />
|
||||
|
@ -3,10 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Nikse.SubtitleEdit.Core.Interfaces;
|
||||
|
||||
namespace Nikse.SubtitleEdit.Core.SubtitleFormats
|
||||
{
|
||||
public class Cavena890 : SubtitleFormat
|
||||
public class Cavena890 : SubtitleFormat, IBinaryPersistableSubtitle
|
||||
{
|
||||
public const int LanguageIdDanish = 0x07;
|
||||
public const int LanguageIdEnglish = 0x09;
|
||||
@ -190,230 +191,236 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
|
||||
private int _languageIdLine1 = LanguageIdEnglish;
|
||||
private int _languageIdLine2 = LanguageIdEnglish;
|
||||
|
||||
public void Save(string fileName, Subtitle subtitle)
|
||||
public bool Save(string fileName, Subtitle subtitle, bool batchMode = false)
|
||||
{
|
||||
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
int russianCount = 0;
|
||||
char[] logoGrams = { '的', '是', '啊', '吧', '好', '吧', '亲', '爱', '的', '早', '上' };
|
||||
char[] russianChars = { 'я', 'д', 'й', 'л', 'щ', 'ж', 'ц', 'ф', 'ы' };
|
||||
foreach (Paragraph p in subtitle.Paragraphs)
|
||||
return Save(fileName, fs, subtitle, batchMode);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Save(string fileName, Stream stream, Subtitle subtitle, bool batchMode)
|
||||
{
|
||||
int russianCount = 0;
|
||||
char[] logoGrams = { '的', '是', '啊', '吧', '好', '吧', '亲', '爱', '的', '早', '上' };
|
||||
char[] russianChars = { 'я', 'д', 'й', 'л', 'щ', 'ж', 'ц', 'ф', 'ы' };
|
||||
foreach (Paragraph p in subtitle.Paragraphs)
|
||||
{
|
||||
if (p.Text.Contains(logoGrams))
|
||||
{
|
||||
if (p.Text.Contains(logoGrams))
|
||||
_languageIdLine1 = LanguageIdChineseSimplified;
|
||||
_languageIdLine2 = LanguageIdChineseSimplified;
|
||||
break;
|
||||
}
|
||||
if (p.Text.Contains(russianChars))
|
||||
{
|
||||
russianCount++;
|
||||
if (russianCount > 10)
|
||||
{
|
||||
_languageIdLine1 = LanguageIdRussian;
|
||||
_languageIdLine2 = LanguageIdRussian; // or 0x09?
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId > 0)
|
||||
{
|
||||
_languageIdLine1 = Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId;
|
||||
_languageIdLine2 = Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId;
|
||||
}
|
||||
else
|
||||
{
|
||||
var language = LanguageAutoDetect.AutoDetectGoogleLanguage(subtitle);
|
||||
switch (language)
|
||||
{
|
||||
case "he":
|
||||
_languageIdLine1 = LanguageIdHebrew;
|
||||
_languageIdLine2 = LanguageIdHebrew; // or 0x09
|
||||
break;
|
||||
case "ru":
|
||||
_languageIdLine1 = LanguageIdRussian;
|
||||
_languageIdLine2 = LanguageIdRussian; // or 0x09?
|
||||
break;
|
||||
case "zh":
|
||||
_languageIdLine1 = LanguageIdChineseSimplified;
|
||||
_languageIdLine2 = LanguageIdChineseSimplified;
|
||||
break;
|
||||
}
|
||||
if (p.Text.Contains(russianChars))
|
||||
{
|
||||
russianCount++;
|
||||
if (russianCount > 10)
|
||||
{
|
||||
_languageIdLine1 = LanguageIdRussian;
|
||||
_languageIdLine2 = LanguageIdRussian; // or 0x09?
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId > 0)
|
||||
{
|
||||
_languageIdLine1 = Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId;
|
||||
_languageIdLine2 = Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId;
|
||||
}
|
||||
else
|
||||
{
|
||||
var language = LanguageAutoDetect.AutoDetectGoogleLanguage(subtitle);
|
||||
switch (language)
|
||||
{
|
||||
case "he":
|
||||
_languageIdLine1 = LanguageIdHebrew;
|
||||
_languageIdLine2 = LanguageIdHebrew; // or 0x09
|
||||
break;
|
||||
case "ru":
|
||||
_languageIdLine1 = LanguageIdRussian;
|
||||
_languageIdLine2 = LanguageIdRussian; // or 0x09?
|
||||
break;
|
||||
case "zh":
|
||||
_languageIdLine1 = LanguageIdChineseSimplified;
|
||||
_languageIdLine2 = LanguageIdChineseSimplified;
|
||||
break;
|
||||
case "da":
|
||||
_languageIdLine1 = LanguageIdDanish;
|
||||
_languageIdLine2 = LanguageIdDanish;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// prompt???
|
||||
//if (Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1 >= 0)
|
||||
// _languageIdLine1 = Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1;
|
||||
//if (Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2 >= 0)
|
||||
// _languageIdLine2 = Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2;
|
||||
|
||||
// write file header (some fields are known, some are not...)
|
||||
|
||||
fs.WriteByte(0); // ?
|
||||
fs.WriteByte(0); // ?
|
||||
|
||||
// tape number (20 bytes)
|
||||
for (int i = 0; i < 20; i++)
|
||||
fs.WriteByte(0);
|
||||
|
||||
// ?
|
||||
for (int i = 0; i < 18; i++)
|
||||
fs.WriteByte(0);
|
||||
|
||||
// translated programme title (28 bytes)
|
||||
string title = Path.GetFileNameWithoutExtension(fileName) ?? string.Empty;
|
||||
if (title.Length > 28)
|
||||
title = title.Substring(0, 28);
|
||||
if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena89Title) && Configuration.Settings.SubtitleSettings.CurrentCavena89Title.Length <= 28)
|
||||
title = Configuration.Settings.SubtitleSettings.CurrentCavena89Title;
|
||||
var buffer = Encoding.ASCII.GetBytes(title);
|
||||
fs.Write(buffer, 0, buffer.Length);
|
||||
for (int i = 0; i < 28 - buffer.Length; i++)
|
||||
fs.WriteByte(0);
|
||||
|
||||
// translator (28 bytes)
|
||||
if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena890Translator) && Configuration.Settings.SubtitleSettings.CurrentCavena890Translator.Length <= 28)
|
||||
{
|
||||
buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena890Translator);
|
||||
fs.Write(buffer, 0, buffer.Length);
|
||||
for (int i = 0; i < 28 - buffer.Length; i++)
|
||||
fs.WriteByte(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 28; i++)
|
||||
fs.WriteByte(0);
|
||||
}
|
||||
|
||||
// ?
|
||||
for (int i = 0; i < 9; i++)
|
||||
fs.WriteByte(0);
|
||||
|
||||
// translated episode title (11 bytes)
|
||||
for (int i = 0; i < 11; i++)
|
||||
fs.WriteByte(0);
|
||||
|
||||
// ?
|
||||
for (int i = 0; i < 18; i++)
|
||||
fs.WriteByte(0);
|
||||
|
||||
// ? + language codes
|
||||
buffer = new byte[] { 0xA0, 0x05, 0x04, 0x03, 0x06, 0x06, 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, (byte)_languageIdLine1, (byte)_languageIdLine2 };
|
||||
fs.Write(buffer, 0, buffer.Length);
|
||||
|
||||
// comments (24 bytes)
|
||||
buffer = Encoding.ASCII.GetBytes("");
|
||||
if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena89Comment) && Configuration.Settings.SubtitleSettings.CurrentCavena89Comment.Length <= 24)
|
||||
buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena89Comment);
|
||||
|
||||
fs.Write(buffer, 0, buffer.Length);
|
||||
for (int i = 0; i < 24 - buffer.Length; i++)
|
||||
fs.WriteByte(0);
|
||||
|
||||
// ??
|
||||
buffer = new byte[] { 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
fs.Write(buffer, 0, buffer.Length);
|
||||
|
||||
// number of subtitles
|
||||
fs.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
|
||||
fs.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
|
||||
|
||||
// write font - prefix with binary zeroes
|
||||
buffer = GetFontBytesFromLanguageId(_languageIdLine1); // also TBX308VFONTL.V for english...
|
||||
for (int i = 0; i < 14 - buffer.Length; i++)
|
||||
fs.WriteByte(0);
|
||||
fs.Write(buffer, 0, buffer.Length);
|
||||
|
||||
// ?
|
||||
for (int i = 0; i < 13; i++)
|
||||
fs.WriteByte(0);
|
||||
|
||||
// number of subtitles again
|
||||
fs.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
|
||||
fs.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
|
||||
|
||||
|
||||
// number of subtitles again again
|
||||
fs.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
|
||||
fs.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
|
||||
|
||||
// ?
|
||||
for (int i = 0; i < 6; i++)
|
||||
fs.WriteByte(0);
|
||||
|
||||
// original programme title (28 chars)
|
||||
if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle) && Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle.Length <= 28)
|
||||
{
|
||||
buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle);
|
||||
fs.Write(buffer, 0, buffer.Length);
|
||||
for (int i = 0; i < 28 - buffer.Length; i++)
|
||||
fs.WriteByte(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 28; i++)
|
||||
fs.WriteByte(0);
|
||||
}
|
||||
|
||||
// write font (use same font id from line 1)
|
||||
buffer = GetFontBytesFromLanguageId(_languageIdLine1);
|
||||
fs.Write(buffer, 0, buffer.Length);
|
||||
|
||||
// ?
|
||||
fs.WriteByte(0x3d);
|
||||
fs.WriteByte(0x8d);
|
||||
|
||||
// start of message time
|
||||
string startOfMessage = "10:00:00:00";
|
||||
if (Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage != null &&
|
||||
Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage.Length == startOfMessage.Length)
|
||||
startOfMessage = Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage;
|
||||
buffer = Encoding.ASCII.GetBytes(startOfMessage);
|
||||
fs.Write(buffer, 0, buffer.Length);
|
||||
|
||||
buffer = new byte[]
|
||||
{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x54, 0x44
|
||||
};
|
||||
fs.Write(buffer, 0, buffer.Length);
|
||||
|
||||
for (int i = 0; i < 92; i++)
|
||||
fs.WriteByte(0);
|
||||
|
||||
// paragraphs
|
||||
int number = 16;
|
||||
foreach (Paragraph p in subtitle.Paragraphs)
|
||||
{
|
||||
// number
|
||||
fs.WriteByte((byte)(number / 256));
|
||||
fs.WriteByte((byte)(number % 256));
|
||||
|
||||
WriteTime(fs, p.StartTime);
|
||||
WriteTime(fs, p.EndTime);
|
||||
|
||||
if (p.Text.StartsWith("{\\an1}"))
|
||||
fs.WriteByte(0x50); // left
|
||||
else if (p.Text.StartsWith("{\\an3}"))
|
||||
fs.WriteByte(0x52); // left
|
||||
else
|
||||
fs.WriteByte(0x54); // center
|
||||
|
||||
buffer = new byte[] { 0, 0, 0, 0, 0, 0, 0 }; // 0x16 }; -- the last two bytes might be something with vertical alignment...
|
||||
fs.Write(buffer, 0, buffer.Length);
|
||||
|
||||
bool hasBox = Utilities.RemoveSsaTags(p.Text).StartsWith("<box>");
|
||||
var text = p.Text.Replace("<box>", string.Empty).Replace("</box>", string.Empty);
|
||||
text = HtmlUtil.RemoveOpenCloseTags(Utilities.RemoveSsaTags(text), HtmlUtil.TagBold, HtmlUtil.TagFont, HtmlUtil.TagBold);
|
||||
WriteText(fs, text, p == subtitle.Paragraphs[subtitle.Paragraphs.Count - 1], _languageIdLine1, hasBox);
|
||||
|
||||
number += 16;
|
||||
case "da":
|
||||
_languageIdLine1 = LanguageIdDanish;
|
||||
_languageIdLine2 = LanguageIdDanish;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// prompt???
|
||||
//if (Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1 >= 0)
|
||||
// _languageIdLine1 = Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1;
|
||||
//if (Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2 >= 0)
|
||||
// _languageIdLine2 = Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2;
|
||||
|
||||
// write file header (some fields are known, some are not...)
|
||||
|
||||
stream.WriteByte(0); // ?
|
||||
stream.WriteByte(0); // ?
|
||||
|
||||
// tape number (20 bytes)
|
||||
for (int i = 0; i < 20; i++)
|
||||
stream.WriteByte(0);
|
||||
|
||||
// ?
|
||||
for (int i = 0; i < 18; i++)
|
||||
stream.WriteByte(0);
|
||||
|
||||
// translated programme title (28 bytes)
|
||||
string title = Path.GetFileNameWithoutExtension(fileName) ?? string.Empty;
|
||||
if (title.Length > 28)
|
||||
title = title.Substring(0, 28);
|
||||
if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena89Title) && Configuration.Settings.SubtitleSettings.CurrentCavena89Title.Length <= 28)
|
||||
title = Configuration.Settings.SubtitleSettings.CurrentCavena89Title;
|
||||
var buffer = Encoding.ASCII.GetBytes(title);
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
for (int i = 0; i < 28 - buffer.Length; i++)
|
||||
stream.WriteByte(0);
|
||||
|
||||
// translator (28 bytes)
|
||||
if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena890Translator) && Configuration.Settings.SubtitleSettings.CurrentCavena890Translator.Length <= 28)
|
||||
{
|
||||
buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena890Translator);
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
for (int i = 0; i < 28 - buffer.Length; i++)
|
||||
stream.WriteByte(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 28; i++)
|
||||
stream.WriteByte(0);
|
||||
}
|
||||
|
||||
// ?
|
||||
for (int i = 0; i < 9; i++)
|
||||
stream.WriteByte(0);
|
||||
|
||||
// translated episode title (11 bytes)
|
||||
for (int i = 0; i < 11; i++)
|
||||
stream.WriteByte(0);
|
||||
|
||||
// ?
|
||||
for (int i = 0; i < 18; i++)
|
||||
stream.WriteByte(0);
|
||||
|
||||
// ? + language codes
|
||||
buffer = new byte[] { 0xA0, 0x05, 0x04, 0x03, 0x06, 0x06, 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, (byte)_languageIdLine1, (byte)_languageIdLine2 };
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
// comments (24 bytes)
|
||||
buffer = Encoding.ASCII.GetBytes("");
|
||||
if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena89Comment) && Configuration.Settings.SubtitleSettings.CurrentCavena89Comment.Length <= 24)
|
||||
buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena89Comment);
|
||||
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
for (int i = 0; i < 24 - buffer.Length; i++)
|
||||
stream.WriteByte(0);
|
||||
|
||||
// ??
|
||||
buffer = new byte[] { 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
// number of subtitles
|
||||
stream.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
|
||||
stream.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
|
||||
|
||||
// write font - prefix with binary zeroes
|
||||
buffer = GetFontBytesFromLanguageId(_languageIdLine1); // also TBX308VFONTL.V for english...
|
||||
for (int i = 0; i < 14 - buffer.Length; i++)
|
||||
stream.WriteByte(0);
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
// ?
|
||||
for (int i = 0; i < 13; i++)
|
||||
stream.WriteByte(0);
|
||||
|
||||
// number of subtitles again
|
||||
stream.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
|
||||
stream.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
|
||||
|
||||
|
||||
// number of subtitles again again
|
||||
stream.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
|
||||
stream.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
|
||||
|
||||
// ?
|
||||
for (int i = 0; i < 6; i++)
|
||||
stream.WriteByte(0);
|
||||
|
||||
// original programme title (28 chars)
|
||||
if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle) && Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle.Length <= 28)
|
||||
{
|
||||
buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle);
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
for (int i = 0; i < 28 - buffer.Length; i++)
|
||||
stream.WriteByte(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 28; i++)
|
||||
stream.WriteByte(0);
|
||||
}
|
||||
|
||||
// write font (use same font id from line 1)
|
||||
buffer = GetFontBytesFromLanguageId(_languageIdLine1);
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
// ?
|
||||
stream.WriteByte(0x3d);
|
||||
stream.WriteByte(0x8d);
|
||||
|
||||
// start of message time
|
||||
string startOfMessage = "10:00:00:00";
|
||||
if (Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage != null &&
|
||||
Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage.Length == startOfMessage.Length)
|
||||
startOfMessage = Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage;
|
||||
buffer = Encoding.ASCII.GetBytes(startOfMessage);
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
buffer = new byte[]
|
||||
{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x54, 0x44
|
||||
};
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
for (int i = 0; i < 92; i++)
|
||||
stream.WriteByte(0);
|
||||
|
||||
// paragraphs
|
||||
int number = 16;
|
||||
foreach (Paragraph p in subtitle.Paragraphs)
|
||||
{
|
||||
// number
|
||||
stream.WriteByte((byte)(number / 256));
|
||||
stream.WriteByte((byte)(number % 256));
|
||||
|
||||
WriteTime(stream, p.StartTime);
|
||||
WriteTime(stream, p.EndTime);
|
||||
|
||||
if (p.Text.StartsWith("{\\an1}"))
|
||||
stream.WriteByte(0x50); // left
|
||||
else if (p.Text.StartsWith("{\\an3}"))
|
||||
stream.WriteByte(0x52); // left
|
||||
else
|
||||
stream.WriteByte(0x54); // center
|
||||
|
||||
buffer = new byte[] { 0, 0, 0, 0, 0, 0, 0 }; // 0x16 }; -- the last two bytes might be something with vertical alignment...
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
bool hasBox = Utilities.RemoveSsaTags(p.Text).StartsWith("<box>");
|
||||
var text = p.Text.Replace("<box>", string.Empty).Replace("</box>", string.Empty);
|
||||
text = HtmlUtil.RemoveOpenCloseTags(Utilities.RemoveSsaTags(text), HtmlUtil.TagBold, HtmlUtil.TagFont, HtmlUtil.TagBold);
|
||||
WriteText(stream, text, p == subtitle.Paragraphs[subtitle.Paragraphs.Count - 1], _languageIdLine1, hasBox);
|
||||
|
||||
number += 16;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static byte[] GetFontBytesFromLanguageId(int languageId)
|
||||
@ -845,7 +852,7 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
|
||||
buffer[index] = b2;
|
||||
}
|
||||
|
||||
private static void WriteTime(FileStream fs, TimeCode timeCode)
|
||||
private static void WriteTime(Stream fs, TimeCode timeCode)
|
||||
{
|
||||
double totalMilliseconds = timeCode.TotalMilliseconds;
|
||||
int frames = (int)Math.Round(totalMilliseconds / (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate));
|
||||
|
@ -4,13 +4,14 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Nikse.SubtitleEdit.Core.Interfaces;
|
||||
|
||||
namespace Nikse.SubtitleEdit.Core.SubtitleFormats
|
||||
{
|
||||
/// <summary>
|
||||
/// EBU Subtitling data exchange format
|
||||
/// </summary>
|
||||
public class Ebu : SubtitleFormat
|
||||
public class Ebu : SubtitleFormat, IBinaryPersistableSubtitle
|
||||
{
|
||||
|
||||
private const string LanguageCodeChinese = "75";
|
||||
@ -524,12 +525,20 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public static bool Save(string fileName, Subtitle subtitle)
|
||||
public bool Save(string fileName, Subtitle subtitle)
|
||||
{
|
||||
return Save(fileName, subtitle, false);
|
||||
}
|
||||
|
||||
public static bool Save(string fileName, Subtitle subtitle, bool batchMode)
|
||||
public bool Save(string fileName, Subtitle subtitle, bool batchMode)
|
||||
{
|
||||
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
return Save(fileName, fs, subtitle, batchMode);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Save(string fileName, Stream stream, Subtitle subtitle, bool batchMode)
|
||||
{
|
||||
var header = new EbuGeneralSubtitleInformation();
|
||||
if (EbuUiHelper == null)
|
||||
@ -548,95 +557,92 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
|
||||
if (!batchMode && !EbuUiHelper.ShowDialogOk())
|
||||
return false;
|
||||
|
||||
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
|
||||
header.TotalNumberOfSubtitles = subtitle.Paragraphs.Count.ToString("D5"); // seems to be 1 higher than actual number of subtitles
|
||||
header.TotalNumberOfTextAndTimingInformationBlocks = header.TotalNumberOfSubtitles;
|
||||
|
||||
var today = string.Format("{0:yyMMdd}", DateTime.Now);
|
||||
if (today.Length == 6)
|
||||
{
|
||||
header.TotalNumberOfSubtitles = subtitle.Paragraphs.Count.ToString("D5"); // seems to be 1 higher than actual number of subtitles
|
||||
header.TotalNumberOfTextAndTimingInformationBlocks = header.TotalNumberOfSubtitles;
|
||||
header.CreationDate = today;
|
||||
header.RevisionDate = today;
|
||||
}
|
||||
|
||||
var today = string.Format("{0:yyMMdd}", DateTime.Now);
|
||||
if (today.Length == 6)
|
||||
var firstParagraph = subtitle.GetParagraphOrDefault(0);
|
||||
if (firstParagraph != null)
|
||||
{
|
||||
var tc = firstParagraph.StartTime;
|
||||
string firstTimeCode = string.Format("{0:00}{1:00}{2:00}{3:00}", tc.Hours, tc.Minutes, tc.Seconds, EbuTextTimingInformation.GetFrameFromMilliseconds(tc.Milliseconds, header.FrameRate));
|
||||
if (firstTimeCode.Length == 8)
|
||||
header.TimeCodeFirstInCue = firstTimeCode;
|
||||
}
|
||||
|
||||
byte[] buffer = Encoding.Default.GetBytes(header.ToString());
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
int subtitleNumber = 0;
|
||||
foreach (var p in subtitle.Paragraphs)
|
||||
{
|
||||
var tti = new EbuTextTimingInformation();
|
||||
|
||||
int rows;
|
||||
if (!int.TryParse(header.MaximumNumberOfDisplayableRows, out rows))
|
||||
rows = 23;
|
||||
|
||||
if (header.DisplayStandardCode == "1" || header.DisplayStandardCode == "2") // teletext
|
||||
rows = 23;
|
||||
else if (header.DisplayStandardCode == "0" && header.MaximumNumberOfDisplayableRows == "02") // open subtitling
|
||||
rows = 15;
|
||||
|
||||
var text = p.Text.Trim(Utilities.NewLineChars);
|
||||
if (text.StartsWith("{\\an7}", StringComparison.Ordinal) || text.StartsWith("{\\an8}", StringComparison.Ordinal) || text.StartsWith("{\\an9}", StringComparison.Ordinal))
|
||||
{
|
||||
header.CreationDate = today;
|
||||
header.RevisionDate = today;
|
||||
tti.VerticalPosition = 1; // top (vertical)
|
||||
}
|
||||
else if (text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an5}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal))
|
||||
{
|
||||
tti.VerticalPosition = (byte)(rows / 2); // middle (vertical)
|
||||
}
|
||||
else
|
||||
{
|
||||
int startRow = (rows - 1) - Utilities.CountTagInText(text, Environment.NewLine) * 2;
|
||||
if (startRow < 0)
|
||||
startRow = 0;
|
||||
tti.VerticalPosition = (byte)startRow; // bottom (vertical)
|
||||
}
|
||||
|
||||
Paragraph firstParagraph = subtitle.GetParagraphOrDefault(0);
|
||||
if (firstParagraph != null)
|
||||
tti.JustificationCode = EbuUiHelper.JustificationCode; // use default justification
|
||||
if (text.StartsWith("{\\an1}", StringComparison.Ordinal) || text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an7}", StringComparison.Ordinal))
|
||||
{
|
||||
TimeCode tc = firstParagraph.StartTime;
|
||||
string firstTimeCode = string.Format("{0:00}{1:00}{2:00}{3:00}", tc.Hours, tc.Minutes, tc.Seconds, EbuTextTimingInformation.GetFrameFromMilliseconds(tc.Milliseconds, header.FrameRate));
|
||||
if (firstTimeCode.Length == 8)
|
||||
header.TimeCodeFirstInCue = firstTimeCode;
|
||||
tti.JustificationCode = 1; // 01h=left-justified text
|
||||
}
|
||||
else if (text.StartsWith("{\\an3}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal) || text.StartsWith("{\\an9}", StringComparison.Ordinal))
|
||||
{
|
||||
tti.JustificationCode = 3; // 03h=right-justified
|
||||
}
|
||||
else if (text.StartsWith("{\\an2}", StringComparison.Ordinal) || text.StartsWith("{\\an5}", StringComparison.Ordinal) || text.StartsWith("{\\an8}", StringComparison.Ordinal))
|
||||
{
|
||||
tti.JustificationCode = 2; // 02h=centred text
|
||||
}
|
||||
|
||||
byte[] buffer = Encoding.Default.GetBytes(header.ToString());
|
||||
fs.Write(buffer, 0, buffer.Length);
|
||||
|
||||
int subtitleNumber = 0;
|
||||
foreach (Paragraph p in subtitle.Paragraphs)
|
||||
tti.SubtitleNumber = (ushort)subtitleNumber;
|
||||
tti.TextField = text;
|
||||
int startTag = tti.TextField.IndexOf('}');
|
||||
if (tti.TextField.StartsWith("{\\", StringComparison.Ordinal) && startTag > 0 && startTag < 10)
|
||||
{
|
||||
var tti = new EbuTextTimingInformation();
|
||||
|
||||
int rows;
|
||||
if (!int.TryParse(header.MaximumNumberOfDisplayableRows, out rows))
|
||||
rows = 23;
|
||||
|
||||
if (header.DisplayStandardCode == "1" || header.DisplayStandardCode == "2") // teletext
|
||||
rows = 23;
|
||||
else if (header.DisplayStandardCode == "0" && header.MaximumNumberOfDisplayableRows == "02") // open subtitling
|
||||
rows = 15;
|
||||
|
||||
var text = p.Text.Trim(Utilities.NewLineChars);
|
||||
if (text.StartsWith("{\\an7}", StringComparison.Ordinal) || text.StartsWith("{\\an8}", StringComparison.Ordinal) || text.StartsWith("{\\an9}", StringComparison.Ordinal))
|
||||
{
|
||||
tti.VerticalPosition = 1; // top (vertical)
|
||||
}
|
||||
else if (text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an5}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal))
|
||||
{
|
||||
tti.VerticalPosition = (byte)(rows / 2); // middle (vertical)
|
||||
}
|
||||
else
|
||||
{
|
||||
int startRow = (rows - 1) - Utilities.CountTagInText(text, Environment.NewLine) * 2;
|
||||
if (startRow < 0)
|
||||
startRow = 0;
|
||||
tti.VerticalPosition = (byte)startRow; // bottom (vertical)
|
||||
}
|
||||
|
||||
tti.JustificationCode = EbuUiHelper.JustificationCode; // use default justification
|
||||
if (text.StartsWith("{\\an1}", StringComparison.Ordinal) || text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an7}", StringComparison.Ordinal))
|
||||
{
|
||||
tti.JustificationCode = 1; // 01h=left-justified text
|
||||
}
|
||||
else if (text.StartsWith("{\\an3}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal) || text.StartsWith("{\\an9}", StringComparison.Ordinal))
|
||||
{
|
||||
tti.JustificationCode = 3; // 03h=right-justified
|
||||
}
|
||||
else if (text.StartsWith("{\\an2}", StringComparison.Ordinal) || text.StartsWith("{\\an5}", StringComparison.Ordinal) || text.StartsWith("{\\an8}", StringComparison.Ordinal))
|
||||
{
|
||||
tti.JustificationCode = 2; // 02h=centred text
|
||||
}
|
||||
|
||||
tti.SubtitleNumber = (ushort)subtitleNumber;
|
||||
tti.TextField = text;
|
||||
int startTag = tti.TextField.IndexOf('}');
|
||||
if (tti.TextField.StartsWith("{\\", StringComparison.Ordinal) && startTag > 0 && startTag < 10)
|
||||
{
|
||||
tti.TextField = tti.TextField.Remove(0, startTag + 1);
|
||||
}
|
||||
|
||||
tti.TimeCodeInHours = p.StartTime.Hours;
|
||||
tti.TimeCodeInMinutes = p.StartTime.Minutes;
|
||||
tti.TimeCodeInSeconds = p.StartTime.Seconds;
|
||||
tti.TimeCodeInMilliseconds = p.StartTime.Milliseconds;
|
||||
tti.TimeCodeOutHours = p.EndTime.Hours;
|
||||
tti.TimeCodeOutMinutes = p.EndTime.Minutes;
|
||||
tti.TimeCodeOutSeconds = p.EndTime.Seconds;
|
||||
tti.TimeCodeOutMilliseconds = p.EndTime.Milliseconds;
|
||||
buffer = tti.GetBytes(header);
|
||||
fs.Write(buffer, 0, buffer.Length);
|
||||
subtitleNumber++;
|
||||
tti.TextField = tti.TextField.Remove(0, startTag + 1);
|
||||
}
|
||||
|
||||
tti.TimeCodeInHours = p.StartTime.Hours;
|
||||
tti.TimeCodeInMinutes = p.StartTime.Minutes;
|
||||
tti.TimeCodeInSeconds = p.StartTime.Seconds;
|
||||
tti.TimeCodeInMilliseconds = p.StartTime.Milliseconds;
|
||||
tti.TimeCodeOutHours = p.EndTime.Hours;
|
||||
tti.TimeCodeOutMinutes = p.EndTime.Minutes;
|
||||
tti.TimeCodeOutSeconds = p.EndTime.Seconds;
|
||||
tti.TimeCodeOutMilliseconds = p.EndTime.Milliseconds;
|
||||
buffer = tti.GetBytes(header);
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
subtitleNumber++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -831,38 +831,44 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public void Save(string fileName, Subtitle subtitle)
|
||||
public bool Save(string fileName, Subtitle subtitle, bool batchMode = false)
|
||||
{
|
||||
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
_fileName = fileName;
|
||||
|
||||
// header
|
||||
fs.WriteByte(1);
|
||||
for (int i = 1; i < 23; i++)
|
||||
fs.WriteByte(0);
|
||||
fs.WriteByte(0x60);
|
||||
|
||||
// paragraphs
|
||||
int number = 0;
|
||||
foreach (Paragraph p in subtitle.Paragraphs)
|
||||
{
|
||||
WriteParagraph(fs, p, number, number + 1 == subtitle.Paragraphs.Count);
|
||||
number++;
|
||||
}
|
||||
|
||||
// footer
|
||||
fs.WriteByte(0xff);
|
||||
for (int i = 0; i < 11; i++)
|
||||
fs.WriteByte(0);
|
||||
fs.WriteByte(0x11);
|
||||
fs.WriteByte(0);
|
||||
byte[] footerBuffer = Encoding.ASCII.GetBytes("dummy end of file");
|
||||
fs.Write(footerBuffer, 0, footerBuffer.Length);
|
||||
return Save(fileName, fs, subtitle, batchMode);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteParagraph(FileStream fs, Paragraph p, int number, bool isLast)
|
||||
public bool Save(string fileName, Stream stream, Subtitle subtitle, bool batchMode)
|
||||
{
|
||||
_fileName = fileName;
|
||||
|
||||
// header
|
||||
stream.WriteByte(1);
|
||||
for (int i = 1; i < 23; i++)
|
||||
stream.WriteByte(0);
|
||||
stream.WriteByte(0x60);
|
||||
|
||||
// paragraphs
|
||||
int number = 0;
|
||||
foreach (Paragraph p in subtitle.Paragraphs)
|
||||
{
|
||||
WriteParagraph(stream, p, number, number + 1 == subtitle.Paragraphs.Count);
|
||||
number++;
|
||||
}
|
||||
|
||||
// footer
|
||||
stream.WriteByte(0xff);
|
||||
for (int i = 0; i < 11; i++)
|
||||
stream.WriteByte(0);
|
||||
stream.WriteByte(0x11);
|
||||
stream.WriteByte(0);
|
||||
byte[] footerBuffer = Encoding.ASCII.GetBytes("dummy end of file");
|
||||
stream.Write(footerBuffer, 0, footerBuffer.Length);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void WriteParagraph(Stream fs, Paragraph p, int number, bool isLast)
|
||||
{
|
||||
WriteTimeCode(fs, p.StartTime);
|
||||
WriteTimeCode(fs, p.EndTime);
|
||||
@ -974,7 +980,7 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats
|
||||
return sb.ToString().Trim();
|
||||
}
|
||||
|
||||
internal static void WriteTimeCode(FileStream fs, TimeCode timeCode)
|
||||
internal static void WriteTimeCode(Stream fs, TimeCode timeCode)
|
||||
{
|
||||
// write four bytes time code
|
||||
string highPart = string.Format("{0:00}", timeCode.Hours) + string.Format("{0:00}", timeCode.Minutes);
|
||||
|
@ -3338,9 +3338,10 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
|
||||
if (format != null && !format.IsTextBased)
|
||||
{
|
||||
if (format.GetType() == typeof(Ebu))
|
||||
var ebu = format as Ebu;
|
||||
if (ebu != null)
|
||||
{
|
||||
if (Ebu.Save(_fileName, sub))
|
||||
if (ebu.Save(_fileName, sub))
|
||||
{
|
||||
Configuration.Settings.RecentFiles.Add(_fileName, FirstVisibleIndex, FirstSelectedIndex, _videoFileName, _subtitleAlternateFileName, Configuration.Settings.General.CurrentVideoOffsetInMs);
|
||||
Configuration.Settings.Save();
|
||||
@ -18000,7 +18001,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
fileName = fileName.Substring(0, fileName.Length - 1);
|
||||
fileName += ebu.Extension;
|
||||
}
|
||||
Ebu.Save(fileName, GetSaveSubtitle(_subtitle));
|
||||
new Ebu().Save(fileName, GetSaveSubtitle(_subtitle));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -825,7 +825,7 @@ namespace Nikse.SubtitleEdit.Logic
|
||||
targetFormatFound = true;
|
||||
outputFileName = FormatOutputFileNameForBatchConvert(fileName, ebu.Extension, outputFolder, overwrite);
|
||||
Console.Write("{0}: {1} -> {2}...", count, Path.GetFileName(fileName), outputFileName);
|
||||
Ebu.Save(outputFileName, sub, true);
|
||||
ebu.Save(outputFileName, sub, true);
|
||||
Console.WriteLine(" done.");
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user