public AssDocument(string filePath, List <AssStyleOptions> styleOptions = null)
        {
            RegisterTagHandlers();

            Dictionary <string, AssDocumentSection> fileSections = ReadDocument(filePath);

            AssDocumentSection infoSection = fileSections["Script Info"];

            VideoDimensions = new Size(infoSection.GetItemInt("PlayResX", 384), infoSection.GetItemInt("PlayResY", 288));

            _styles = fileSections["V4+ Styles"].MapItems("Style", i => new AssStyle(i))
                      .ToDictionary(s => s.Name);

            if (styleOptions != null)
            {
                _styleOptions = styleOptions.ToDictionary(o => o.Name);
            }

            foreach (AssDialogue dialogue in fileSections["Events"].MapItems("Dialogue", i => new AssDialogue(i)))
            {
                AssStyle style = GetStyle(dialogue.Style);
                if (style == null)
                {
                    throw new Exception($"Line \"{dialogue.Text}\" refers to style \"{dialogue.Style}\" which doesn't exist.");
                }

                AssStyleOptions options = GetStyleOptions(dialogue.Style);

                List <AssLine> lines = ParseLine(dialogue, style, options);
                Lines.AddRange(lines.SelectMany(ExpandLine));
            }

            foreach (AssLine line in Lines)
            {
                MergeIdenticallyFormattedSections(line);
                line.NormalizeAlpha();
            }
        }
Example #2
0
        private List <AssLine> ParseLine(AssDialogue dialogue, AssStyle style, AssStyleOptions styleOptions)
        {
            AssLine line = new AssLine(dialogue.Start, dialogue.End)
            {
                AnchorPoint = style.AnchorPoint, KaraokeType = SimpleKaraokeType
            };

            AssTagContext context = new AssTagContext
            {
                Document            = this,
                InitialStyle        = style,
                InitialStyleOptions = styleOptions,
                Style        = style,
                StyleOptions = styleOptions,
                Line         = line,
                Section      = new AssSection(null)
            };

            ApplyStyle(context.Section, style, styleOptions);
            CreateTagSections(line, dialogue.Text, context);
            CreateRubySections(line);

            List <AssLine> lines = new List <AssLine> {
                line
            };

            foreach (AssTagContext.PostProcessor postProcessor in context.PostProcessors)
            {
                List <AssLine> extraLines = postProcessor();
                if (extraLines != null)
                {
                    lines.AddRange(extraLines);
                }
            }

            return(lines);
        }
Example #3
0
        private void WriteLineMetadata(AssLine line, AssStyle style, StreamWriter writer)
        {
            string effects = !line.AndroidDarkTextHackAllowed ? EffectNames.NoAndroidDarkTextHack : string.Empty;

            writer.Write($"Dialogue: 0,{line.Start:H:mm:ss.ff},{line.End:H:mm:ss.ff},{style.Name},,0,0,0,{effects},");
        }
Example #4
0
        protected virtual void WriteLine(AssLine line, StreamWriter writer)
        {
            if (line.Sections.Count == 0)
            {
                return;
            }

            AssStyle style = GetStyleMatchingStructure((AssSection)line.Sections[0]);

            WriteLineMetadata(line, style, writer);

            AssSection prevSection = new AssSection();
            AssStyle   prevStyle   = style;

            ApplyStyle(prevSection, prevStyle);

            AssLineContentBuilder lineContent = new AssLineContentBuilder();

            AppendLineTags(line, style, lineContent);

            RubyPart currentRubyPosition = RubyPart.None;

            for (int i = 0; i < line.Sections.Count; i++)
            {
                AssSection section = (AssSection)line.Sections[i];
                style = GetStyleMatchingStructure(section);
                if (style != prevStyle || (currentRubyPosition != RubyPart.None && section.RubyPart == RubyPart.None))
                {
                    lineContent.AppendTag("r", style);
                    currentRubyPosition = RubyPart.None;
                    prevSection         = (AssSection)prevSection.Clone();
                    ApplyStyle(prevSection, style);
                }

                AppendSectionTags(section, prevSection, lineContent);

                if (section.RubyPart == RubyPart.Text)
                {
                    RubyPart rubyPart;
                    if (i + 4 > line.Sections.Count || ((rubyPart = line.Sections[i + 2].RubyPart) != RubyPart.RubyAbove && rubyPart != RubyPart.RubyBelow))
                    {
                        throw new InvalidDataException("Invalid ruby sequence");
                    }

                    if (rubyPart != currentRubyPosition)
                    {
                        lineContent.AppendTag("ytruby", rubyPart == RubyPart.RubyAbove ? 8 : 2);
                        currentRubyPosition = rubyPart;
                    }
                    lineContent.AppendText($"[{section.Text}/{line.Sections[i + 2].Text}]");
                    i += 3;
                }
                else
                {
                    lineContent.AppendText(section.Text);
                }

                prevSection = section;
                prevStyle   = style;
            }

            writer.WriteLine(lineContent);
        }
Example #5
0
 internal void ApplyStyle(AssSection section, AssStyle style)
 {
     ApplyStyle(section, style, GetStyleOptions(style.Name));
 }
Example #6
0
 public AssStyleOptions GetStyleOptions(AssStyle style)
 {
     return(GetStyleOptions(style.Name));
 }
        private List <AssLine> ParseLine(AssDialogue dialogue, AssStyle style, AssStyleOptions styleOptions)
        {
            DateTime startTime = TimeUtil.SnapTimeToFrame(dialogue.Start.AddMilliseconds(32));
            DateTime endTime   = TimeUtil.SnapTimeToFrame(dialogue.End).AddMilliseconds(32);
            AssLine  line      = new AssLine(startTime, endTime)
            {
                AnchorPoint = style.AnchorPoint
            };

            AssTagContext context = new AssTagContext
            {
                Document            = this,
                Dialogue            = dialogue,
                InitialStyle        = style,
                InitialStyleOptions = styleOptions,
                Style        = style,
                StyleOptions = styleOptions,
                Line         = line,
                Section      = new AssSection(null)
            };

            ApplyStyle(context.Section, style, styleOptions);

            string text  = Regex.Replace(dialogue.Text, @"(?:\\N)+$", "");
            int    start = 0;

            foreach (Match match in Regex.Matches(text, @"\{(?:\\(?<tag>fn|\d?[a-z]+)(?<arg>\([^\{\}\(\)]*\)|[^\{\}\(\)\\]*))+\}"))
            {
                int end = match.Index;

                if (end > start)
                {
                    context.Section.Text = text.Substring(start, end - start).Replace("\\N", "\r\n");
                    line.Sections.Add(context.Section);

                    context.Section          = (AssSection)context.Section.Clone();
                    context.Section.Text     = null;
                    context.Section.Duration = TimeSpan.Zero;
                }

                CaptureCollection tags      = match.Groups["tag"].Captures;
                CaptureCollection arguments = match.Groups["arg"].Captures;
                for (int i = 0; i < tags.Count; i++)
                {
                    if (_tagHandlers.TryGetValue(tags[i].Value, out AssTagHandlerBase handler))
                    {
                        handler.Handle(context, arguments[i].Value);
                    }
                }

                start = match.Index + match.Length;
            }

            if (start < text.Length)
            {
                context.Section.Text = text.Substring(start, text.Length - start).Replace("\\N", "\r\n");
                line.Sections.Add(context.Section);
            }

            if (line.RubyPosition != RubyPosition.None)
            {
                CreateRubySections(line);
            }

            List <AssLine> lines = new List <AssLine> {
                line
            };

            foreach (AssTagContext.PostProcessor postProcessor in context.PostProcessors)
            {
                List <AssLine> extraLines = postProcessor();
                if (extraLines != null)
                {
                    lines.AddRange(extraLines);
                }
            }

            return(lines);
        }