public static EbuGeneralSubtitleInformation ReadHeader(byte[] buffer) { var header = new EbuGeneralSubtitleInformation(); header.CodePageNumber = Encoding.Default.GetString(buffer, 0, 3); header.DiskFormatCode = Encoding.Default.GetString(buffer, 3, 8); header.DisplayStandardCode = Encoding.Default.GetString(buffer, 11, 1); header.CharacterCodeTableNumber = Encoding.Default.GetString(buffer, 12, 2); header.LanguageCode = Encoding.Default.GetString(buffer, 14, 2); header.OriginalProgrammeTitle = Encoding.Default.GetString(buffer, 16, 32); header.OriginalEpisodeTitle = Encoding.Default.GetString(buffer, 48, 32); header.TranslatedProgrammeTitle = Encoding.Default.GetString(buffer, 80, 32); header.TranslatedEpisodeTitle = Encoding.Default.GetString(buffer, 112, 32); header.TranslatorsName = Encoding.Default.GetString(buffer, 144, 32); header.TranslatorsContactDetails = Encoding.Default.GetString(buffer, 176, 32); header.SubtitleListReferenceCode = Encoding.Default.GetString(buffer, 208, 16); header.CreationDate = Encoding.Default.GetString(buffer, 224, 6); header.RevisionDate = Encoding.Default.GetString(buffer, 230, 6); header.RevisionNumber = Encoding.Default.GetString(buffer, 236, 2); header.TotalNumberOfTextAndTimingInformationBlocks = Encoding.Default.GetString(buffer, 238, 5); header.TotalNumberOfSubtitles = Encoding.Default.GetString(buffer, 243, 5); header.TotalNumberOfSubtitleGroups = Encoding.Default.GetString(buffer, 248, 3); header.MaximumNumberOfDisplayableCharactersInAnyTextRow = Encoding.Default.GetString(buffer, 251, 2); header.MaximumNumberOfDisplayableRows = Encoding.Default.GetString(buffer, 253, 2); header.TimeCodeStatus = Encoding.Default.GetString(buffer, 255, 1); header.TimeCodeStartOfProgramme = Encoding.Default.GetString(buffer, 256, 8); header.CountryOfOrigin = Encoding.Default.GetString(buffer, 274, 3); header.SpareBytes = Encoding.Default.GetString(buffer, 373, 75); header.UserDefinedArea = Encoding.Default.GetString(buffer, 448, 576); return header; }
private EbuGeneralSubtitleInformation ReadHeader(byte[] buffer) { EbuGeneralSubtitleInformation header = new EbuGeneralSubtitleInformation(); header.CodePageNumber = Encoding.ASCII.GetString(buffer, 0, 3); header.DiskFormatCode = Encoding.ASCII.GetString(buffer, 3, 8); header.DisplayStandardCode = Encoding.ASCII.GetString(buffer, 11, 1); header.CharacterCodeTableNumber = Encoding.ASCII.GetString(buffer, 12, 2); header.LanguageCode = Encoding.ASCII.GetString(buffer, 14, 2); header.OriginalProgrammeTitle = Encoding.ASCII.GetString(buffer, 16, 32); header.OriginalEpisodeTitle = Encoding.ASCII.GetString(buffer, 48, 32); header.TranslatedProgrammeTitle = Encoding.ASCII.GetString(buffer, 80, 32); header.TranslatedEpisodeTitle = Encoding.ASCII.GetString(buffer, 112, 32); header.TranslatorsName = Encoding.ASCII.GetString(buffer, 144, 32); header.TranslatorsContactDetails = Encoding.ASCII.GetString(buffer, 176, 32); header.SubtitleListReferenceCode = Encoding.ASCII.GetString(buffer, 208, 16); header.CreationDate = Encoding.ASCII.GetString(buffer, 224, 6); header.RevisionDate = Encoding.ASCII.GetString(buffer, 230, 6); header.RevisionNumber = Encoding.ASCII.GetString(buffer, 236, 2); header.TotalNumberOfTextAndTimingInformationBlocks = Encoding.ASCII.GetString(buffer, 238, 5); header.TotalNumberOfSubtitles = Encoding.ASCII.GetString(buffer, 243, 5); header.TotalNumberOfSubtitleGroups = Encoding.ASCII.GetString(buffer, 248, 3); header.MaximumNumberOfDisplayableCharactersInAnyTextRow = Encoding.ASCII.GetString(buffer, 251, 2); header.MaximumNumberOfDisplayableRows = Encoding.ASCII.GetString(buffer, 253, 2); header.TimeCodeStatus = Encoding.ASCII.GetString(buffer, 255, 1); header.TimeCodeStartOfProgramme = Encoding.ASCII.GetString(buffer, 256, 8); header.CountryOfOrigin = Encoding.ASCII.GetString(buffer, 274, 3); header.SpareBytes = Encoding.ASCII.GetString(buffer, 373, 75); header.UserDefinedArea = Encoding.ASCII.GetString(buffer, 448, 576); return(header); }
public override bool IsMine(List <string> lines, string fileName) { if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName)) { FileInfo fi = new FileInfo(fileName); if (fi.Length > 1024 + 128 && fi.Length < 1024000) // not too small or too big { byte[] buffer = File.ReadAllBytes(fileName); EbuGeneralSubtitleInformation header = ReadHeader(buffer); if (header.DiskFormatCode.StartsWith("STL25") || header.DiskFormatCode.StartsWith("STL30")) { return(Utilities.IsInteger(header.CodePageNumber)); } } } return(false); }
public override void LoadSubtitle(Subtitle subtitle, List <string> lines, string fileName) { subtitle.Paragraphs.Clear(); subtitle.Header = null; byte[] buffer = File.ReadAllBytes(fileName); EbuGeneralSubtitleInformation header = ReadHeader(buffer); subtitle.Header = Encoding.UTF8.GetString(buffer); foreach (EbuTextTimingInformation tti in ReadTTI(buffer, header)) { Paragraph p = new Paragraph(); p.Text = tti.TextField; p.StartTime = new TimeCode(tti.TimeCodeInHours, tti.TimeCodeInMinutes, tti.TimeCodeInSeconds, tti.TimeCodeInMilliseconds); p.EndTime = new TimeCode(tti.TimeCodeOutHours, tti.TimeCodeOutMinutes, tti.TimeCodeOutSeconds, tti.TimeCodeOutMilliseconds); subtitle.Paragraphs.Add(p); } subtitle.Renumber(1); Header = header; }
/// <summary> /// Get text with regard code page from header /// </summary> /// <param name="skipNext">Skip next character</param> /// <param name="header">EBU header</param> /// <param name="buffer">data buffer</param> /// <param name="index">index to current byte in buffer</param> /// <returns>Character at index</returns> private static string GetCharacter(out bool skipNext, EbuGeneralSubtitleInformation header, byte[] buffer, int index) { skipNext = false; if (header.LanguageCode == LanguageCodeChinese) { skipNext = true; return Encoding.GetEncoding(1200).GetString(buffer, index, 2); // 16-bit Unicode } if (header.CharacterCodeTableNumber == "00") { //note that 0xC1—0xCF combines characters - http://en.wikipedia.org/wiki/ISO/IEC_6937 var encoding = Encoding.GetEncoding(20269); string next = encoding.GetString(buffer, index + 1, 1); switch (buffer[index]) { case 0xc1: // Grave skipNext = @"AEIOUaeiou".Contains(next); switch (next) { case "A": return "À"; case "E": return "È"; case "I": return "Ì"; case "O": return "Ò"; case "U": return "Ù"; case "a": return "à"; case "e": return "è"; case "i": return "ì"; case "o": return "ò"; case "u": return "ù"; } return string.Empty; case 0xc2: // Acute skipNext = @"ACEILNORSUYZacegilnorsuyz".Contains(next); switch (next) { case "A": return "Á"; case "C": return "Ć"; case "E": return "É"; case "I": return "Í"; case "L": return "Ĺ"; case "N": return "Ń"; case "O": return "Ó"; case "R": return "Ŕ"; case "S": return "Ś"; case "U": return "Ú"; case "Y": return "Ý"; case "Z": return "Ź"; case "a": return "á"; case "c": return "ć"; case "e": return "é"; case "g": return "ģ"; case "i": return "í"; case "l": return "ĺ"; case "n": return "ń"; case "o": return "ó"; case "r": return "ŕ"; case "s": return "ś"; case "u": return "ú"; case "y": return "ý"; case "z": return "ź"; } return string.Empty; case 0xc3: // Circumflex skipNext = @"ACEGHIJOSUWYaceghijosuwy".Contains(next); switch (next) { case "A": return "Â"; case "C": return "Ĉ"; case "E": return "Ê"; case "G": return "Ĝ"; case "H": return "Ĥ"; case "I": return "Î"; case "J": return "Ĵ"; case "O": return "Ô"; case "S": return "Ŝ"; case "U": return "Û"; case "W": return "Ŵ"; case "Y": return "Ŷ"; case "a": return "â"; case "c": return "ĉ"; case "e": return "ê"; case "g": return "ĝ"; case "h": return "ĥ"; case "i": return "î"; case "j": return "ĵ"; case "o": return "ô"; case "s": return "ŝ"; case "u": return "û"; case "w": return "ŵ"; case "y": return "ŷ"; } return string.Empty; case 0xc4: // Tilde skipNext = @"AINOUainou".Contains(next); switch (next) { case "A": return "Ã"; case "I": return "Ĩ"; case "N": return "Ñ"; case "O": return "Õ"; case "U": return "Ũ"; case "a": return "ã"; case "i": return "ĩ"; case "n": return "ñ"; case "o": return "õ"; case "u": return "ũ"; } return string.Empty; case 0xc5: // Macron skipNext = @"AEIOUaeiou".Contains(next); switch (next) { case "A": return "Ā"; case "E": return "Ē"; case "I": return "Ī"; case "O": return "Ō"; case "U": return "Ū"; case "a": return "ā"; case "e": return "ē"; case "i": return "ī"; case "o": return "ō"; case "u": return "ū"; } return string.Empty; case 0xc6: // Breve skipNext = @"AGUagu".Contains(next); switch (next) { case "A": return "Ă"; case "G": return "Ğ"; case "U": return "Ŭ"; case "a": return "ă"; case "g": return "ğ"; case "u": return "ŭ"; } return string.Empty; case 0xc7: // Dot skipNext = @"CEGIZcegiz".Contains(next); switch (next) { case "C": return "Ċ"; case "E": return "Ė"; case "G": return "Ġ"; case "I": return "İ"; case "Z": return "Ż"; case "c": return "ċ"; case "e": return "ė"; case "g": return "ġ"; case "i": return "ı"; case "z": return "ż"; } return string.Empty; case 0xc8: // Umlaut or diæresis skipNext = @"AEIOUYaeiouy".Contains(next); switch (next) { case "A": return "Ä"; case "E": return "Ë"; case "I": return "Ï"; case "O": return "Ö"; case "U": return "Ü"; case "Y": return "Ÿ"; case "a": return "ä"; case "e": return "ë"; case "i": return "ï"; case "o": return "ö"; case "u": return "ü"; case "y": return "ÿ"; } return string.Empty; case 0xca: // Ring skipNext = @"AUau".Contains(next); switch (next) { case "A": return "Å"; case "U": return "Ů"; case "a": return "å"; case "u": return "ů"; } return string.Empty; case 0xcb: // Cedilla skipNext = @"CGKLNRSTcklnrst".Contains(next); switch (next) { case "C": return "Ç"; case "G": return "Ģ"; case "K": return "Ķ"; case "L": return "Ļ"; case "N": return "Ņ"; case "R": return "Ŗ"; case "S": return "Ş"; case "T": return "Ţ"; case "c": return "ç"; case "k": return "ķ"; case "l": return "ļ"; case "n": return "ņ"; case "r": return "ŗ"; case "s": return "ş"; case "t": return "ţ"; } return string.Empty; case 0xcd: // DoubleAcute skipNext = @"OUou".Contains(next); switch (next) { case "O": return "Ő"; case "U": return "Ű"; case "o": return "ő"; case "u": return "ű"; } return string.Empty; case 0xce: // Ogonek skipNext = @"AEIUaeiu".Contains(next); switch (next) { case "A": return "Ą"; case "E": return "Ę"; case "I": return "Į"; case "U": return "Ų"; case "a": return "ą"; case "e": return "ę"; case "i": return "į"; case "u": return "ų"; } return string.Empty; case 0xcf: // Caron skipNext = @"CDELNRSTZcdelnrstz".Contains(next); switch (next) { case "C": return "Č"; case "D": return "Ď"; case "E": return "Ě"; case "L": return "Ľ"; case "N": return "Ň"; case "R": return "Ř"; case "S": return "Š"; case "T": return "Ť"; case "Z": return "Ž"; case "c": return "č"; case "d": return "ď"; case "e": return "ě"; case "l": return "ľ"; case "n": return "ň"; case "r": return "ř"; case "s": return "š"; case "t": return "ť"; case "z": return "ž"; } return string.Empty; default: return encoding.GetString(buffer, index, 1); } } if (header.CharacterCodeTableNumber == "01") // Latin/Cyrillic alphabet - from ISO 8859/5-1988 { return Encoding.GetEncoding("ISO-8859-5").GetString(buffer, index, 1); } if (header.CharacterCodeTableNumber == "02") // Latin/Arabic alphabet - from ISO 8859/6-1987 { return Encoding.GetEncoding("ISO-8859-6").GetString(buffer, index, 1); } if (header.CharacterCodeTableNumber == "03") // Latin/Greek alphabet - from ISO 8859/7-1987 { return Encoding.GetEncoding("ISO-8859-7").GetString(buffer, index, 1); // or ISO-8859-1 ? } if (header.CharacterCodeTableNumber == "04") // Latin/Hebrew alphabet - from ISO 8859/8-1988 { return Encoding.GetEncoding("ISO-8859-8").GetString(buffer, index, 1); } return string.Empty; }
public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName) { subtitle.Paragraphs.Clear(); subtitle.Header = null; byte[] buffer = FileUtil.ReadAllBytesShared(fileName); EbuGeneralSubtitleInformation header = ReadHeader(buffer); subtitle.Header = Encoding.UTF8.GetString(buffer); Paragraph last = null; byte lastExtensionBlockNumber = 0xff; JustificationCodes = new List<int>(); VerticalPositions = new List<int>(); foreach (EbuTextTimingInformation tti in ReadTextAndTiming(buffer, header)) { if (tti.ExtensionBlockNumber != 0xfe) // FEh : Reserved for User Data { var p = new Paragraph(); p.Text = tti.TextField; p.StartTime = new TimeCode(tti.TimeCodeInHours, tti.TimeCodeInMinutes, tti.TimeCodeInSeconds, tti.TimeCodeInMilliseconds); p.EndTime = new TimeCode(tti.TimeCodeOutHours, tti.TimeCodeOutMinutes, tti.TimeCodeOutSeconds, tti.TimeCodeOutMilliseconds); if (lastExtensionBlockNumber != 0xff && last != null) { last.Text += p.Text; // merge text } else { subtitle.Paragraphs.Add(p); last = p; } p.Text = HtmlUtil.FixInvalidItalicTags(p.Text); lastExtensionBlockNumber = tti.ExtensionBlockNumber; } } subtitle.Renumber(); Header = header; }
public static void Save(string fileName, Subtitle subtitle, bool batchMode) { var header = new EbuGeneralSubtitleInformation(); if (EbuUiHelper == null) return; if (subtitle.Header != null && subtitle.Header.Length == 1024 && (subtitle.Header.Contains("STL24") || subtitle.Header.Contains("STL25") || subtitle.Header.Contains("STL29") || subtitle.Header.Contains("STL30"))) { header = ReadHeader(Encoding.UTF8.GetBytes(subtitle.Header)); EbuUiHelper.Initialize(header, 0, null, subtitle); } else { EbuUiHelper.Initialize(header, 0, fileName, subtitle); } if (!batchMode && !EbuUiHelper.ShowDialogOk()) return; 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.CreationDate = today; header.RevisionDate = today; } Paragraph firstParagraph = subtitle.GetParagraphOrDefault(0); if (firstParagraph != null) { 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; } byte[] buffer = Encoding.Default.GetBytes(header.ToString()); fs.Write(buffer, 0, buffer.Length); int subtitleNumber = 0; foreach (Paragraph 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; if (p.Text.StartsWith("{\\an7}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an8}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal)) { tti.VerticalPosition = 1; // top (vertical) } else if (p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal)) { tti.VerticalPosition = (byte)(rows / 2); // middle (vertical) } else { int startRow = (rows - 1) - Utilities.CountTagInText(p.Text, Environment.NewLine) * 2; if (startRow < 0) startRow = 0; tti.VerticalPosition = (byte)startRow; // bottom (vertical) } tti.JustificationCode = EbuUiHelper.JustificationCode; if (p.Text.StartsWith("{\\an1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an7}", StringComparison.Ordinal)) { tti.JustificationCode = 1; // 01h=left-justified text } else if (p.Text.StartsWith("{\\an3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal)) { tti.JustificationCode = 3; // 03h=right-justified } else // If it's not left- or right-justified, it's centred. { tti.JustificationCode = 2; // 02h=centred text } tti.SubtitleNumber = (ushort)subtitleNumber; tti.TextField = p.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++; } } }
public byte[] GetBytes(EbuGeneralSubtitleInformation header) { var buffer = new byte[128]; // Text and Timing Information (TTI) block consists of 128 bytes buffer[0] = SubtitleGroupNumber; byte[] temp = BitConverter.GetBytes(SubtitleNumber); buffer[1] = temp[0]; buffer[2] = temp[1]; buffer[3] = ExtensionBlockNumber; buffer[4] = CumulativeStatus; buffer[5] = (byte)TimeCodeInHours; buffer[6] = (byte)TimeCodeInMinutes; buffer[7] = (byte)TimeCodeInSeconds; buffer[8] = GetFrameFromMilliseconds(TimeCodeInMilliseconds, header.FrameRate); buffer[9] = (byte)TimeCodeOutHours; buffer[10] = (byte)TimeCodeOutMinutes; buffer[11] = (byte)TimeCodeOutSeconds; buffer[12] = GetFrameFromMilliseconds(TimeCodeOutMilliseconds, header.FrameRate); buffer[13] = VerticalPosition; buffer[14] = JustificationCode; buffer[15] = CommentFlag; var encoding = Encoding.Default; if (header.LanguageCode == LanguageCodeChinese) { var lines = HtmlUtil.RemoveHtmlTags(TextField, true).SplitToLines(); var byteList = new List<byte>(); encoding = Encoding.GetEncoding(1200); // 16-bit Unicode for (int i = 0; i < lines.Count(); i++) { var l = lines[i]; if (i > 0) { // new line byteList.Add(0); byteList.Add(138); } byteList.AddRange(encoding.GetBytes(l).ToArray()); } for (int i = 0; i < 112; i++) { if (i < byteList.Count) buffer[16 + i] = byteList[i]; else buffer[16 + i] = 0x8f; } return buffer; } if (header.CharacterCodeTableNumber == "00") { encoding = Encoding.GetEncoding(20269); // 0xC1—0xCF combines characters - http://en.wikipedia.org/wiki/ISO/IEC_6937 TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc1 }), "ÀÈÌÒÙàèìòù", "AEIOUaeiou"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc2 }), "ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź", "ACEILNORSUYZacegilnorsuyz"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc3 }), "ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ", "ACEGHIJOSUWYaceghijosuwy"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc4 }), "ÃĨÑÕŨãĩñõũ", "AINOUainou"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc5 }), "ĀĒĪŌŪāēīōū", "AEIOUaeiou"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc6 }), "ĂĞŬăğŭ", "AGUagu"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc7 }), "ĊĖĠİŻċėġıż", "CEGIZcegiz"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc8 }), "ÄËÏÖÜŸäëïöüÿ", "AEIOUYaeiouy"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xca }), "ÅŮåů", "AUau"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xcb }), "ÇĢĶĻŅŖŞŢçķļņŗşţ", "CGKLNRSTcklnrst"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xcd }), "ŐŰőű", "OUou"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xce }), "ĄĘĮŲąęįų", "AEIUaeiu"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xcf }), "ČĎĚĽŇŘŠŤŽčďěľňřšťž", "CDELNRSTZcdelnrstz"); } else if (header.CharacterCodeTableNumber == "01") // Latin/Cyrillic alphabet - from ISO 8859/5-1988 { encoding = Encoding.GetEncoding("ISO-8859-5"); } else if (header.CharacterCodeTableNumber == "02") // Latin/Arabic alphabet - from ISO 8859/6-1987 { encoding = Encoding.GetEncoding("ISO-8859-6"); } else if (header.CharacterCodeTableNumber == "03") // Latin/Greek alphabet - from ISO 8859/7-1987 { encoding = Encoding.GetEncoding("ISO-8859-7"); // or ISO-8859-1 ? } else if (header.CharacterCodeTableNumber == "04") // Latin/Hebrew alphabet - from ISO 8859/8-1988 { encoding = Encoding.GetEncoding("ISO-8859-8"); } // italic/underline string italicsOn = encoding.GetString(new byte[] { 0x80 }); string italicsOff = encoding.GetString(new byte[] { 0x81 }); string underlineOn = encoding.GetString(new byte[] { 0x82 }); string underlineOff = encoding.GetString(new byte[] { 0x83 }); if (Utilities.CountTagInText(TextField, "<i>") == 1 && TextField.StartsWith("<i>") && TextField.EndsWith("</i>")) // italic on all lines TextField = TextField.Replace(Environment.NewLine, Environment.NewLine + "<i>"); TextField = TextField.Replace("<i>", italicsOn); TextField = TextField.Replace("<I>", italicsOn); TextField = TextField.Replace("</i>", italicsOff); TextField = TextField.Replace("</I>", italicsOff); TextField = TextField.Replace("<u>", underlineOn); TextField = TextField.Replace("<U>", underlineOn); TextField = TextField.Replace("</u>", underlineOff); TextField = TextField.Replace("</U>", underlineOff); if (header.CharacterCodeTableNumber == "00") { TextField = TextField.Replace("©", encoding.GetString(new byte[] { 0xd3 })); TextField = TextField.Replace("™", encoding.GetString(new byte[] { 0xd4 })); TextField = TextField.Replace("♪", encoding.GetString(new byte[] { 0xd5 })); } //em-dash (–) tags // TextField = TextField.Replace("–", "Ð"); //font tags if (header.DisplayStandardCode == "0") // Open subtitling { TextField = HtmlUtil.RemoveHtmlTags(TextField, true); } else // teletext { var lines = TextField.SplitToLines(); var sb = new StringBuilder(); string veryFirstColor = null; foreach (string line in lines) { string firstColor = null; string s = line; var start = s.IndexOf("<font ", StringComparison.Ordinal); if (start >= 0) { int end = s.IndexOf('>', start); if (end > 0) { string f = s.Substring(start, end - start); if (f.Contains(" color=")) { var colorStart = f.IndexOf(" color=", StringComparison.Ordinal); if (s.IndexOf('"', colorStart + " color=".Length + 1) > 0) { int colorEnd = f.IndexOf('"', colorStart + " color=".Length + 1); if (colorStart > 1) { string color = f.Substring(colorStart + 7, colorEnd - (colorStart + 7)); color = color.Trim('\''); color = color.Trim('\"'); color = color.Trim('#'); s = s.Remove(start, end - start + 1); if (veryFirstColor == null) veryFirstColor = GetNearestEbuColorCode(color, encoding); firstColor = GetNearestEbuColorCode(color, encoding); } } } } } //byte colorByte = 0x07; // white byte colorByte = 255; if (!string.IsNullOrEmpty(veryFirstColor)) colorByte = encoding.GetBytes(veryFirstColor)[0]; if (!string.IsNullOrEmpty(firstColor)) colorByte = encoding.GetBytes(firstColor)[0]; string prefix = encoding.GetString(new byte[] { 0xd, colorByte, 0xb, 0xb }); if (colorByte != 255) sb.Append(prefix); sb.AppendLine(s); } TextField = HtmlUtil.RemoveHtmlTags(sb.ToString()).TrimEnd(); } // newline string newline = encoding.GetString(new byte[] { 0x8a, 0x8a }); if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox && Configuration.Settings.SubtitleSettings.EbuStlTeletextUseDoubleHeight) { newline = encoding.GetString(new byte[] { 0x0a, 0x0a, 0x8a, 0x8a, 0x0d, 0x0b, 0x0b }); // 0a==end box, 0d==double height, 0b==start box } else if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox) { newline = encoding.GetString(new byte[] { 0x0a, 0x0a, 0x8a, 0x8a, 0x0b, 0x0b }); // 0a==end box, 0b==start box } else if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseDoubleHeight) { newline = encoding.GetString(new byte[] { 0x8a, 0x8a, 0x0d, 0x0d }); // 0d==double height } if (header.DisplayStandardCode == "0") // 0=Open subtitling { newline = encoding.GetString(new byte[] { 0x8A }); //8Ah=CR/LF } TextField = TextField.Replace(Environment.NewLine, newline); string endOfLine = encoding.GetString(new byte[] { 0x0a, 0x0a }); //a=end box if (!Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox || header.DisplayStandardCode == "0") // DisplayStandardCode 0==Open subtitling { endOfLine = string.Empty; } TextField += endOfLine; // save em-dash indexes (–) var indexOfEmdash = new List<int>(); for (int j = 0; j < TextField.Length; j++) { if (TextField[j] == '–') indexOfEmdash.Add(j); } if (header.DisplayStandardCode != "0") // 0=Open subtitling { if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox && Configuration.Settings.SubtitleSettings.EbuStlTeletextUseDoubleHeight) { TextField = encoding.GetString(new byte[] { 0x0d, 0x0b, 0x0b }) + TextField; // d=double height, b=start box } else if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox) { TextField = encoding.GetString(new byte[] { 0x0b, 0x0b }) + TextField; // b=start box } else if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox && Configuration.Settings.SubtitleSettings.EbuStlTeletextUseDoubleHeight) { TextField = encoding.GetString(new byte[] { 0x0d }) + TextField; // d=double height } } // convert text to bytes byte[] bytes = encoding.GetBytes(TextField); // restore em-dashes (–) foreach (int index in indexOfEmdash) { bytes[index] = 0xd0; } for (int i = 0; i < 112; i++) { if (i < bytes.Length) buffer[16 + i] = bytes[i]; //else if (i == bytes.Length) // buffer[16 + i] = 0x8f; else buffer[16 + i] = 0x8f; } return buffer; }
/// <summary> /// Read TTI block /// </summary> private IEnumerable<EbuTextTimingInformation> ReadTextAndTiming(byte[] buffer, EbuGeneralSubtitleInformation header) { const int startOfTextAndTimingBlock = 1024; const int ttiSize = 128; const byte textFieldCarriageReturnLineFeed = 0x8A; const byte textFieldTerminator = 0x8F; const byte italicsOn = 0x80; const byte italicsOff = 0x81; const byte underlineOn = 0x82; const byte underlineOff = 0x83; var list = new List<EbuTextTimingInformation>(); int index = startOfTextAndTimingBlock; while (index + ttiSize <= buffer.Length) { var tti = new EbuTextTimingInformation(); tti.SubtitleGroupNumber = buffer[index]; tti.SubtitleNumber = (ushort)(buffer[index + 2] * 256 + buffer[index + 1]); tti.ExtensionBlockNumber = buffer[index + 3]; tti.CumulativeStatus = buffer[index + 4]; tti.TimeCodeInHours = buffer[index + 5 + 0]; tti.TimeCodeInMinutes = buffer[index + 5 + 1]; tti.TimeCodeInSeconds = buffer[index + 5 + 2]; tti.TimeCodeInMilliseconds = (int)(TimeCode.BaseUnit / (header.FrameRate / buffer[index + 5 + 3])); tti.TimeCodeOutHours = buffer[index + 9 + 0]; tti.TimeCodeOutMinutes = buffer[index + 9 + 1]; tti.TimeCodeOutSeconds = buffer[index + 9 + 2]; tti.TimeCodeOutMilliseconds = (int)(1000 / (header.FrameRate / buffer[index + 9 + 3])); tti.VerticalPosition = buffer[index + 13]; VerticalPositions.Add(tti.VerticalPosition); tti.JustificationCode = buffer[index + 14]; JustificationCodes.Add(tti.JustificationCode); tti.CommentFlag = buffer[index + 15]; // build text bool skipNext = false; var sb = new StringBuilder(); string endTags = string.Empty; string color = string.Empty; string lastColor = string.Empty; for (int i = 0; i < 112; i++) // skip fist byte (seems to be always 0xd/32/space - thx Iban) { byte b = buffer[index + 16 + i]; if (skipNext) { skipNext = false; } else if (header.LanguageCode == LanguageCodeChinese) { if (b == textFieldTerminator) { break; } if (index + 16 + i + 1 < buffer.Length) { byte next = buffer[index + 17 + i]; if (b == 0 && next == textFieldTerminator) break; if (b == 0 && next == 138) // new line { sb.AppendLine(); skipNext = true; } else { sb.Append(GetCharacter(out skipNext, header, buffer, index + 16 + i)); } } } else if (b <= 0xf && (i == 0 || i == 2 || i == 3)) { // not used, 0=0xd, 2=0xb, 3=0xb } else { if (b <= 0x17) { switch (b) { case 0x00: case 0x10: color = "Black"; break; case 0x01: case 0x11: color = "Red"; break; case 0x02: case 0x12: color = "Green"; break; case 0x03: case 0x13: color = "Yellow"; break; case 0x04: case 0x14: color = "Blue"; break; case 0x05: case 0x15: color = "Magenta"; break; case 0x06: case 0x16: color = "Cyan"; break; case 0x07: case 0x17: color = "White"; break; } } if (b == textFieldCarriageReturnLineFeed) sb.AppendLine(); else if (b == italicsOn && header.LanguageCode != LanguageCodeChinese) sb.Append("<i>"); else if (b == italicsOff && header.LanguageCode != LanguageCodeChinese) sb.Append("</i>"); else if (b == underlineOn && header.LanguageCode != LanguageCodeChinese) sb.Append("<u>"); else if (b == underlineOff && header.LanguageCode != LanguageCodeChinese) sb.Append("</u>"); else if (b == 0xd3 && header.CharacterCodeTableNumber == "00") // Latin { sb.Append("©"); } else if (b == 0xd4 && header.CharacterCodeTableNumber == "00") // Latin { sb.Append("™"); } else if (b == 0xd5 && header.CharacterCodeTableNumber == "00") // Latin { sb.Append("♪"); } //else if (b == 0xD0) // em-dash // sb.Append('–'); else if (b == textFieldTerminator) break; else if ((b >= 0x20 && b <= 0x7F) || b >= 0xA1 || header.LanguageCode == LanguageCodeChinese) { string ch = GetCharacter(out skipNext, header, buffer, index + 16 + i); if (ch != " ") { if (color != lastColor && color.Length > 0) { endTags = "</font>"; if (lastColor.Length > 0) sb.Append("</font>"); sb.Append("<font color=\"" + color + "\">"); } lastColor = color; } sb.Append(ch); } } } tti.TextField = sb.ToString().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine).TrimEnd() + endTags; int rows; if (!int.TryParse(header.MaximumNumberOfDisplayableRows, out rows)) rows = 23; if (tti.VerticalPosition < 3) { if (tti.JustificationCode == 1) // left tti.TextField = "{\\an7}" + tti.TextField; else if (tti.JustificationCode == 3) // right tti.TextField = "{\\an9}" + tti.TextField; else tti.TextField = "{\\an8}" + tti.TextField; } else if (tti.VerticalPosition <= rows / 2 + 1) { if (tti.JustificationCode == 1) // left tti.TextField = "{\\an4}" + tti.TextField; else if (tti.JustificationCode == 3) // right tti.TextField = "{\\an6}" + tti.TextField; else tti.TextField = "{\\an5}" + tti.TextField; } else { if (tti.JustificationCode == 1) // left tti.TextField = "{\\an1}" + tti.TextField; else if (tti.JustificationCode == 3) // right tti.TextField = "{\\an3}" + tti.TextField; } index += ttiSize; list.Add(tti); } return list; }
public bool Save(string fileName, Subtitle subtitle, bool batchMode, EbuGeneralSubtitleInformation header = null) { using (var ms = new MemoryStream()) { var ok = Save(fileName, ms, subtitle, batchMode, header); if (ok) { ms.Position = 0; using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) { ms.CopyTo(fs); } } return ok; } }
public byte[] GetBytes(EbuGeneralSubtitleInformation header) { byte[] buffer = new byte[128]; // Text and Timing Information (TTI) block consists of 128 bytes buffer[0] = SubtitleGroupNumber; byte[] temp = BitConverter.GetBytes(SubtitleNumber); buffer[1] = temp[0]; buffer[2] = temp[1]; buffer[3] = ExtensionBlockNumber; buffer[4] = CumulativeStatus; buffer[5] = (byte)TimeCodeInHours; buffer[6] = (byte)TimeCodeInMinutes; buffer[7] = (byte)TimeCodeInSeconds; buffer[8] = GetFrameFromMilliseconds(TimeCodeInMilliseconds, header.FrameRate); buffer[9] = (byte)TimeCodeOutHours; buffer[10] = (byte)TimeCodeOutMinutes; buffer[11] = (byte)TimeCodeOutSeconds; buffer[12] = GetFrameFromMilliseconds(TimeCodeOutMilliseconds, header.FrameRate); buffer[13] = VerticalPosition; buffer[14] = JustificationCode; buffer[15] = CommentFlag; Encoding encoding = Encoding.Default; if (header.CharacterCodeTableNumber == "00") { encoding = Encoding.GetEncoding(20269); // 0xC1—0xCF combines characters - http://en.wikipedia.org/wiki/ISO/IEC_6937 TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc1 }), "ÀÈÌÒÙàèìòù", "AEIOUaeiou"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc2 }), "ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź", "ACEILNORSUYZacegilnorsuyz"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc3 }), "ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ", "ACEGHIJOSUWYaceghijosuwy"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc4 }), "ÃĨÑÕŨãĩñõũ", "AINOUainou"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc5 }), "ĀĒĪŌŪāēīōū", "AEIOUaeiou"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc6 }), "ĂĞŬăğŭ", "AGUagu"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc7 }), "ĊĖĠİŻċėġıż", "CEGIZcegiz"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc8 }), "ÄËÏÖÜŸäëïöüÿ", "AEIOUYaeiouy"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xca }), "ÅŮåů", "AUau"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xcb }), "ÇĢĶĻŅŖŞŢçķļņŗşţ", "CGKLNRSTcklnrst"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xcd }), "ŐŰőű", "OUou"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xce }), "ĄĘĮŲąęįų", "AEIUaeiu"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xcf }), "ČĎĚĽŇŘŠŤŽčďěľňřšťž", "CDELNRSTZcdelnrstz"); } else if (header.CharacterCodeTableNumber == "01") // Latin/Cyrillic alphabet - from ISO 8859/5-1988 { encoding = Encoding.GetEncoding("ISO-8859-5"); } else if (header.CharacterCodeTableNumber == "02") // Latin/Arabic alphabet - from ISO 8859/6-1987 { encoding = Encoding.GetEncoding("ISO-8859-6"); } else if (header.CharacterCodeTableNumber == "03") // Latin/Greek alphabet - from ISO 8859/7-1987 { encoding = Encoding.GetEncoding("ISO-8859-7"); // or ISO-8859-1 ? } else if (header.CharacterCodeTableNumber == "04") // Latin/Hebrew alphabet - from ISO 8859/8-1988 { encoding = Encoding.GetEncoding("ISO-8859-8"); } // italic/underline string italicsOn = encoding.GetString(new byte[] { 0x80 }); string italicsOff = encoding.GetString(new byte[] { 0x81 }); string underlineOn = encoding.GetString(new byte[] { 0x82 }); string underlineOff = encoding.GetString(new byte[] { 0x83 }); TextField = TextField.Replace("<i>", italicsOn); TextField = TextField.Replace("<I>", italicsOn); TextField = TextField.Replace("</i>", italicsOff); TextField = TextField.Replace("</I>", italicsOff); TextField = TextField.Replace("<u>", underlineOn); TextField = TextField.Replace("<U>", underlineOn); TextField = TextField.Replace("</u>", underlineOff); TextField = TextField.Replace("</U>", underlineOff); //em-dash (–) tags // TextField = TextField.Replace("–", "Ð"); //font tags if (header.DisplayStandardCode == "0") // Open subtitling { TextField = Utilities.RemoveHtmlTags(TextField, true); } else // teletext { string[] lines = TextField.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); var sb = new StringBuilder(); string veryFirstColor = null; foreach (string line in lines) { string firstColor = null; string s = line; int start = s.IndexOf("<font "); if (start >= 0) { int end = s.IndexOf(">", start); if (end > 0) { string f = s.Substring(start, end - start); if (f.Contains(" color=")) { int colorStart = f.IndexOf(" color="); if (s.IndexOf("\"", colorStart + " color=".Length + 1) > 0) { int colorEnd = f.IndexOf("\"", colorStart + " color=".Length + 1); if (colorStart > 1) { string color = f.Substring(colorStart + 7, colorEnd - (colorStart + 7)); color = color.Trim('\''); color = color.Trim('\"'); color = color.Trim('#'); s = s.Remove(start, end - start + 1); if (veryFirstColor == null) veryFirstColor = GetNearestEbuColorCode(color, encoding); if (firstColor == null) firstColor = GetNearestEbuColorCode(color, encoding); else s = s.Insert(start, GetNearestEbuColorCode(color, encoding)); } } } } } //byte colorByte = 0x07; // white byte colorByte = 255; if (!string.IsNullOrEmpty(veryFirstColor)) colorByte = encoding.GetBytes(veryFirstColor)[0]; if (!string.IsNullOrEmpty(firstColor)) colorByte = encoding.GetBytes(firstColor)[0]; string prefix = encoding.GetString(new byte[] { 0xd, colorByte, 0xb, 0xb }); if (colorByte != 255) sb.Append(prefix); sb.AppendLine(s); } TextField = Utilities.RemoveHtmlTags(sb.ToString()).TrimEnd(); } // newline string newline = encoding.GetString(new byte[] { 0x0a, 0x0a, 0x8a, 0x8a }); if (header.DisplayStandardCode == "0") newline = encoding.GetString(new byte[] { 0x8A }); //8Ah=CR/LF // newline = encoding.GetString(new byte[] { 0x85, 0x8A, 0x0D, 0x84, 0x80 }); //85h=boxing off, 8Ah=CR/LF, 84h=boxing on, 80h, Italics on TextField = TextField.Replace(Environment.NewLine, newline); string endOfLine = encoding.GetString(new byte[] { 0x0a, 0x0a, 0x8a }); if (header.DisplayStandardCode == "0") { endOfLine = string.Empty; } TextField += endOfLine; // save em-dash indexes (–) List<int> indexOfEmdash = new List<int>(); for (int j=0; j<TextField.Length; j++) { if (TextField.Substring(j, 1) == "–") indexOfEmdash.Add(j); } // convert text to bytes byte[] bytes = encoding.GetBytes(TextField); // restore em-dashes (–) foreach (int index in indexOfEmdash) { bytes[index] = 0xd0; } for (int i = 0; i < 112; i++) { if (i < bytes.Length) buffer[16 + i] = bytes[i]; //else if (i == bytes.Length) // buffer[16 + i] = 0x8f; else buffer[16 + i] = 0x8f; } return buffer; }
/// <summary> /// Get text with regard code page from header /// </summary> /// <param name="skipNext">Skip next character</param> /// <param name="header">EBU header</param> /// <param name="buffer">data buffer</param> /// <param name="index">index to current byte in buffer</param> /// <returns>Character at index</returns> private string GetCharacter(out bool skipNext, EbuGeneralSubtitleInformation header, byte[] buffer, int index) { skipNext = false; if (header.CharacterCodeTableNumber == "00") { //note that 0xC1—0xCF combines characters - http://en.wikipedia.org/wiki/ISO/IEC_6937 Encoding encoding = Encoding.GetEncoding(20269); string next = encoding.GetString(buffer, index + 1, 1); switch (buffer[index]) { case 0xc1: // Grave skipNext = "AEIOUaeiou".Contains(next); switch (next) { case "A": return("À"); case "E": return("È"); case "I": return("Ì"); case "O": return("Ò"); case "U": return("Ù"); case "a": return("à"); case "e": return("è"); case "i": return("ì"); case "o": return("ò"); case "u": return("ù"); } return(string.Empty); case 0xc2: // Acute skipNext = "ACEILNORSUYZacegilnorsuyz".Contains(next); switch (next) { case "A": return("Á"); case "C": return("Ć"); case "E": return("É"); case "I": return("Í"); case "L": return("Ĺ"); case "N": return("Ń"); case "O": return("Ó"); case "R": return("Ŕ"); case "S": return("Ś"); case "U": return("Ú"); case "Y": return("Ý"); case "Z": return("Ź"); case "a": return("á"); case "c": return("ć"); case "e": return("é"); case "g": return("ģ"); case "i": return("í"); case "l": return("ĺ"); case "n": return("ń"); case "o": return("ó"); case "r": return("ŕ"); case "s": return("ś"); case "u": return("ú"); case "y": return("ý"); case "z": return("ź"); } return(string.Empty); case 0xc3: // Circumflex skipNext = "ACEGHIJOSUWYaceghijosuwy".Contains(next); switch (next) { case "A": return("Â"); case "C": return("Ĉ"); case "E": return("Ê"); case "G": return("Ĝ"); case "H": return("Ĥ"); case "I": return("Î"); case "J": return("Ĵ"); case "O": return("Ô"); case "S": return("Ŝ"); case "U": return("Û"); case "W": return("Ŵ"); case "Y": return("Ŷ"); case "a": return("â"); case "c": return("ĉ"); case "e": return("ê"); case "g": return("ĝ"); case "h": return("ĥ"); case "i": return("î"); case "j": return("ĵ"); case "o": return("ô"); case "s": return("ŝ"); case "u": return("û"); case "w": return("ŵ"); case "y": return("ŷ"); } return(string.Empty); case 0xc4: // Tilde skipNext = "AINOUainou".Contains(next); switch (next) { case "A": return("Ã"); case "I": return("Ĩ"); case "N": return("Ñ"); case "O": return("Õ"); case "U": return("Ũ"); case "a": return("ã"); case "i": return("ĩ"); case "n": return("ñ"); case "o": return("õ"); case "u": return("ũ"); } return(string.Empty); case 0xc5: // Macron skipNext = "AEIOUaeiou".Contains(next); switch (next) { case "A": return("Ā"); case "E": return("Ē"); case "I": return("Ī"); case "O": return("Ō"); case "U": return("Ū"); case "a": return("ā"); case "e": return("ē"); case "i": return("ī"); case "o": return("ō"); case "u": return("ū"); } return(string.Empty); case 0xc6: // Breve skipNext = "AGUagu".Contains(next); switch (next) { case "A": return("Ă"); case "G": return("Ğ"); case "U": return("Ŭ"); case "a": return("ă"); case "g": return("ğ"); case "u": return("ŭ"); } return(string.Empty); case 0xc7: // Dot skipNext = "CEGIZcegiz".Contains(next); switch (next) { case "C": return("Ċ"); case "E": return("Ė"); case "G": return("Ġ"); case "I": return("İ"); case "Z": return("Ż"); case "c": return("ċ"); case "e": return("ė"); case "g": return("ġ"); case "i": return("ı"); case "z": return("ż"); } return(string.Empty); case 0xc8: // Umlaut or diæresis skipNext = "AEIOUYaeiouy".Contains(next); switch (next) { case "A": return("Ä"); case "E": return("Ë"); case "I": return("Ï"); case "O": return("Ö"); case "U": return("Ü"); case "Y": return("Ÿ"); case "a": return("ä"); case "e": return("ë"); case "i": return("ï"); case "o": return("ö"); case "u": return("ü"); case "y": return("ÿ"); } return(string.Empty); case 0xca: // Ring skipNext = "AUau".Contains(next); switch (next) { case "A": return("Å"); case "U": return("Ů"); case "a": return("å"); case "u": return("ů"); } return(string.Empty); case 0xcb: // Cedilla skipNext = "CGKLNRSTcklnrst".Contains(next); switch (next) { case "C": return("Ç"); case "G": return("Ģ"); case "K": return("Ķ"); case "L": return("Ļ"); case "N": return("Ņ"); case "R": return("Ŗ"); case "S": return("Ş"); case "T": return("Ţ"); case "c": return("ç"); case "k": return("ķ"); case "l": return("ļ"); case "n": return("ņ"); case "r": return("ŗ"); case "s": return("ş"); case "t": return("ţ"); } return(string.Empty); case 0xcd: // DoubleAcute skipNext = "OUou".Contains(next); switch (next) { case "O": return("Ő"); case "U": return("Ű"); case "o": return("ő"); case "u": return("ű"); } return(string.Empty); case 0xce: // Ogonek skipNext = "AEIUaeiu".Contains(next); switch (next) { case "A": return("Ą"); case "E": return("Ę"); case "I": return("Į"); case "U": return("Ų"); case "a": return("ą"); case "e": return("ę"); case "i": return("į"); case "u": return("ų"); } return(string.Empty); case 0xcf: // Caron skipNext = "CDELNRSTZcdelnrstz".Contains(next); switch (next) { case "C": return("Č"); case "D": return("Ď"); case "E": return("Ě"); case "L": return("Ľ"); case "N": return("Ň"); case "R": return("Ř"); case "S": return("Š"); case "T": return("Ť"); case "Z": return("Ž"); case "c": return("č"); case "d": return("ď"); case "e": return("ě"); case "l": return("ľ"); case "n": return("ň"); case "r": return("ř"); case "s": return("š"); case "t": return("ť"); case "z": return("ž"); } return(string.Empty); default: return(encoding.GetString(buffer, index, 1)); } } else if (header.CharacterCodeTableNumber == "01") // Latin/Cyrillic alphabet - from ISO 8859/5-1988 { Encoding encoding = Encoding.GetEncoding("ISO-8859-5"); return(encoding.GetString(buffer, index, 1)); } else if (header.CharacterCodeTableNumber == "02") // Latin/Arabic alphabet - from ISO 8859/6-1987 { Encoding encoding = Encoding.GetEncoding("ISO-8859-6"); return(encoding.GetString(buffer, index, 1)); } else if (header.CharacterCodeTableNumber == "03") // Latin/Greek alphabet - from ISO 8859/7-1987 { Encoding encoding = Encoding.GetEncoding("ISO-8859-7"); // or ISO-8859-1 ? return(encoding.GetString(buffer, index, 1)); } else if (header.CharacterCodeTableNumber == "04") // Latin/Hebrew alphabet - from ISO 8859/8-1988 { Encoding encoding = Encoding.GetEncoding("ISO-8859-8"); return(encoding.GetString(buffer, index, 1)); } return(string.Empty); }
public void Save(string fileName, Subtitle subtitle) { EbuGeneralSubtitleInformation header = new EbuGeneralSubtitleInformation(); FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write); header.TotalNumberOfSubtitles = ((subtitle.Paragraphs.Count + 1).ToString()).PadLeft(5, '0'); // seems to be 1 higher than actual number of subtitles header.TotalNumberOfTextAndTimingInformationBlocks = header.TotalNumberOfSubtitles; string today = string.Format("{0:00}{1:00}{2:00}", DateTime.Now.Year.ToString().Remove(0, 2), DateTime.Now.Month, DateTime.Now.Day); if (today.Length == 6) { header.CreationDate = today; header.RevisionDate = today; } Paragraph firstParagraph = subtitle.GetParagraphOrDefault(0); if (firstParagraph != null) { 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; } } byte[] buffer = ASCIIEncoding.ASCII.GetBytes(header.ToString()); fs.Write(buffer, 0, buffer.Length); int subtitleNumber = 0; foreach (Paragraph p in subtitle.Paragraphs) { EbuTextTimingInformation tti = new EbuTextTimingInformation(); if (p.Text.Contains(Environment.NewLine)) { tti.VerticalPosition = 0x14; } else { tti.VerticalPosition = 0x16; } tti.JustificationCode = (byte)0; tti.SubtitleNumber = (ushort)subtitleNumber; tti.TextField = p.Text; 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++; } fs.Close(); }
internal byte[] GetBytes(EbuGeneralSubtitleInformation header) { byte[] buffer = new byte[128]; // Text and Timing Information (TTI) block consists of 128 bytes buffer[0] = SubtitleGroupNumber; byte[] temp = BitConverter.GetBytes(SubtitleNumber); buffer[1] = temp[0]; buffer[2] = temp[1]; buffer[3] = ExtensionBlockNumber; buffer[4] = CumulativeStatus; buffer[5] = (byte)TimeCodeInHours; buffer[6] = (byte)TimeCodeInMinutes; buffer[7] = (byte)TimeCodeInSeconds; buffer[8] = GetFrameFromMilliseconds(TimeCodeInMilliseconds, header.FrameRate); buffer[9] = (byte)TimeCodeOutHours; buffer[10] = (byte)TimeCodeOutMinutes; buffer[11] = (byte)TimeCodeOutSeconds; buffer[12] = GetFrameFromMilliseconds(TimeCodeOutMilliseconds, header.FrameRate); buffer[13] = VerticalPosition; buffer[14] = JustificationCode; buffer[15] = CommentFlag; Encoding encoding = Encoding.Default; if (header.CharacterCodeTableNumber == "00") { encoding = Encoding.GetEncoding(20269); // 0xC1—0xCF combines characters - http://en.wikipedia.org/wiki/ISO/IEC_6937 TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc1 }), "ÀÈÌÒÙàèìòù", "AEIOUaeiou"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc2 }), "ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź", "ACEILNORSUYZacegilnorsuyz"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc3 }), "ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ", "ACEGHIJOSUWYaceghijosuwy"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc4 }), "ÃĨÑÕŨãĩñõũ", "AINOUainou"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc5 }), "ĀĒĪŌŪāēīōū", "AEIOUaeiou"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc6 }), "ĂĞŬăğŭ", "AGUagu"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc7 }), "ĊĖĠİŻċėġıż", "CEGIZcegiz"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xc8 }), "ÄËÏÖÜŸäëïöüÿ", "AEIOUYaeiouy"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xca }), "ÅŮåů", "AUau"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xcb }), "ÇĢĶĻŅŖŞŢçķļņŗşţ", "CGKLNRSTcklnrst"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xcd }), "ŐŰőű", "OUou"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xce }), "ĄĘĮŲąęįų", "AEIUaeiu"); TextField = ReplaceSpecialCharactersWithTwoByteEncoding(TextField, encoding.GetString(new byte[] { 0xcf }), "ČĎĚĽŇŘŠŤŽčďěľňřšťž", "CDELNRSTZcdelnrstz"); } else if (header.CharacterCodeTableNumber == "01") // Latin/Cyrillic alphabet - from ISO 8859/5-1988 { encoding = Encoding.GetEncoding("ISO-8859-5"); } else if (header.CharacterCodeTableNumber == "02") // Latin/Arabic alphabet - from ISO 8859/6-1987 { encoding = Encoding.GetEncoding("ISO-8859-6"); } else if (header.CharacterCodeTableNumber == "03") // Latin/Greek alphabet - from ISO 8859/7-1987 { encoding = Encoding.GetEncoding("ISO-8859-7"); // or ISO-8859-1 ? } else if (header.CharacterCodeTableNumber == "04") // Latin/Hebrew alphabet - from ISO 8859/8-1988 { encoding = Encoding.GetEncoding("ISO-8859-8"); } // italic/underline string italicsOn = encoding.GetString(new byte[] { 0x80 }); string italicsOff = encoding.GetString(new byte[] { 0x81 }); string underlineOn = encoding.GetString(new byte[] { 0x82 }); string underlineOff = encoding.GetString(new byte[] { 0x83 }); TextField = TextField.Replace("<i>", italicsOn); TextField = TextField.Replace("<I>", italicsOn); TextField = TextField.Replace("</i>", italicsOff); TextField = TextField.Replace("</I>", italicsOff); TextField = TextField.Replace("<u>", underlineOn); TextField = TextField.Replace("<U>", underlineOn); TextField = TextField.Replace("</u>", underlineOff); TextField = TextField.Replace("</U>", underlineOff); //font tags string[] lines = TextField.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); StringBuilder sb = new StringBuilder(); string veryFirstColor = null; foreach (string line in lines) { string firstColor = null; string s = line; int start = s.IndexOf("<font "); if (start >= 0) { int end = s.IndexOf(">", start); if (end > 0) { string f = s.Substring(start, end - start); if (f.Contains(" color=")) { int colorStart = f.IndexOf(" color="); if (s.IndexOf("\"", colorStart + " color=".Length + 1) > 0) { int colorEnd = f.IndexOf("\"", colorStart + " color=".Length + 1); if (colorStart > 1) { string color = f.Substring(colorStart + 7, colorEnd - (colorStart + 7)); color = color.Trim('\''); color = color.Trim('\"'); color = color.Trim('#'); s = s.Remove(start, end - start + 1); if (veryFirstColor == null) { veryFirstColor = GetNearestEbuColorCode(color, encoding); } if (firstColor == null) { firstColor = GetNearestEbuColorCode(color, encoding); } else { s = s.Insert(start, GetNearestEbuColorCode(color, encoding)); } } } } } } //byte colorByte = 0x07; // white byte colorByte = 255; if (!string.IsNullOrEmpty(veryFirstColor)) { colorByte = encoding.GetBytes(veryFirstColor)[0]; } if (!string.IsNullOrEmpty(firstColor)) { colorByte = encoding.GetBytes(firstColor)[0]; } string prefix = encoding.GetString(new byte[] { 0xd, colorByte, 0xb, 0xb }); if (colorByte != 255) { sb.Append(prefix); } sb.AppendLine(s); } TextField = Utilities.RemoveHtmlTags(sb.ToString()).TrimEnd(); // newline string newline = encoding.GetString(new byte[] { 0x0a, 0x0a, 0x8a, 0x8a }); TextField = TextField.Replace(Environment.NewLine, newline); string endOfLine = encoding.GetString(new byte[] { 0x0a, 0x0a, 0x8a }); TextField += endOfLine; // convert text to bytes byte[] bytes = encoding.GetBytes(TextField); for (int i = 0; i < 112; i++) { if (i < bytes.Length) { buffer[16 + i] = bytes[i]; } //else if (i == bytes.Length) // buffer[16 + i] = 0x8f; else { buffer[16 + i] = 0x8f; } } return(buffer); }
private IEnumerable <EbuTextTimingInformation> ReadTTI(byte[] buffer, EbuGeneralSubtitleInformation header) { const int StartOfTTI = 1024; const int TTISize = 128; const byte TextFieldCRLF = 0x8A; const byte TextFieldTerminator = 0x8F; const byte ItalicsOn = 0x80; const byte ItalicsOff = 0x81; const byte UnderlineOn = 0x82; const byte UnderlineOff = 0x83; List <EbuTextTimingInformation> list = new List <EbuTextTimingInformation>(); int index = StartOfTTI; while (index + TTISize <= buffer.Length) { var tti = new EbuTextTimingInformation(); tti.SubtitleGroupNumber = buffer[index]; tti.SubtitleNumber = (ushort)(buffer[index + 2] * 256 + buffer[index + 1]); tti.ExtensionBlockNumber = buffer[index + 3]; tti.CumulativeStatus = buffer[index + 4]; tti.TimeCodeInHours = buffer[index + 5 + 0]; tti.TimeCodeInMinutes = buffer[index + 5 + 1]; tti.TimeCodeInSeconds = buffer[index + 5 + 2]; tti.TimeCodeInMilliseconds = (int)(1000.0 / (header.FrameRate / buffer[index + 5 + 3])); tti.TimeCodeOutHours = buffer[index + 9 + 0]; tti.TimeCodeOutMinutes = buffer[index + 9 + 1]; tti.TimeCodeOutSeconds = buffer[index + 9 + 2]; tti.TimeCodeOutMilliseconds = (int)(1000 / (header.FrameRate / buffer[index + 9 + 3])); tti.VerticalPosition = buffer[index + 13]; tti.JustificationCode = buffer[index + 14]; tti.CommentFlag = buffer[index + 15]; // build text bool skipNext = false; StringBuilder sb = new StringBuilder(); string endTags = string.Empty; string color = string.Empty; string lastColor = string.Empty; for (int i = 0; i < 112; i++) // skip fist byte (seems to be always 0xd/32/space - thx Iban) { byte b = buffer[index + 16 + i]; if (b <= 0xf && (i == 0 || i == 2 || i == 3)) { // not used, 0=0xd, 2=0xb, 3=0xb } else if (skipNext) { skipNext = false; } else { if (b >= 0 && b <= 0x17) { switch (b) { case 0x00: case 0x10: color = "Black"; break; case 0x01: case 0x11: color = "Red"; break; case 0x02: case 0x12: color = "Green"; break; case 0x03: case 0x13: color = "Yellow"; break; case 0x04: case 0x14: color = "Blue"; break; case 0x05: case 0x15: color = "Magenta"; break; case 0x06: case 0x16: color = "Cyan"; break; case 0x07: case 0x17: color = "White"; break; } } if (b == TextFieldCRLF) { sb.AppendLine(); } else if (b == ItalicsOn) { sb.Append("<i>"); } else if (b == ItalicsOff) { sb.Append("</i>"); } else if (b == UnderlineOn) { sb.Append("<u>"); } else if (b == UnderlineOff) { sb.Append("</u>"); } else if (b == TextFieldTerminator) { break; } else if ((b >= 0x20 && b <= 0x7F) || b >= 0xA1) { string ch = GetCharacter(out skipNext, header, buffer, index + 16 + i); if (ch != " ") { if (color != lastColor && color.Length > 0) { endTags = "</font>"; if (lastColor.Length > 0) { sb.Append("</font>"); } sb.Append("<font color=\"" + color + "\">"); } lastColor = color; } sb.Append(ch); } } } tti.TextField = sb.ToString().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine).TrimEnd() + endTags; index += TTISize; list.Add(tti); } return(list); }