Example #1
0
        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++;
                }
            }
        }
Example #2
0
        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();
        }
Example #3
0
        /// <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;
        }
Example #4
0
        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);
        }