Testing save to stream method on a few binary subtitle formats (for libse usage)

This commit is contained in:
Nikolaj Olsson 2016-09-29 20:37:49 +02:00
parent e157e90700
commit cb4f6e7aba
7 changed files with 361 additions and 331 deletions

View 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);
}
}

View File

@ -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" />

View File

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

View File

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

View File

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

View File

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

View File

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