private static void SetStyle(Attr attr, SmiStatus status)
 {
     attr.b      = status.b > 0;
     attr.i      = status.i > 0;
     attr.u      = status.u > 0;
     attr.fs     = (status.fs.Count > 0) ? status.fs[status.fs.Count - 1] : 0;
     attr.fn     = (status.fn.Count > 0) ? status.fn[status.fn.Count - 1] : "";
     attr.fc     = (status.fc.Count > 0) ? status.fc[status.fc.Count - 1] : "";
     attr.fade   = (status.fade.Count > 0) ? status.fade[status.fade.Count - 1] : 0;
     attr.typing = (status.typing.Count > 0) ? status.typing[status.typing.Count - 1] : null;
 }
        public Ass FromAttr(List <Attr> attrs)
        {
            string          text      = "";
            List <string[]> lastAttrs = new List <string[]>();

            Attr last = new Attr();

            foreach (Attr attr in attrs)
            {
                if (!last.b && attr.b)
                {
                    text += "{\\b1}";
                }
                else if (last.b && !attr.b)
                {
                    text += "{\\b}";
                }

                if (!last.i && attr.i)
                {
                    text += "{\\i1}";
                }
                else if (last.i && !attr.i)
                {
                    text += "{\\i}";
                }

                if (!last.u && attr.u)
                {
                    text += "{\\u1}";
                }
                else if (last.u && !attr.u)
                {
                    text += "{\\u}";
                }

                if (!last.fn.Equals(attr.fn))
                {
                    text += "{\\fn" + attr.fn + "}";
                }

                if (!last.fc.Equals(attr.fc))
                {
                    text += "{\\c" + ColorFromAttr(attr.fc) + "}";
                }

                text += attr.text;
                last  = attr;
            }

            this.text = text.Replace("}{", "").Replace("\n", "\\N");
            return(this);
        }
Exemple #3
0
 public Attr(Attr old)
 {
     text   = "";
     b      = old.b;
     i      = old.i;
     u      = old.u;
     fs     = old.fs;
     fn     = old.fn;
     fc     = old.fc;
     fade   = old.fade;
     typing = old.typing;
 }
        public List <Attr> ToAttr()
        {
            List <Attr> result = new List <Attr>();

            int  index = 0;
            int  pos   = 0;
            Attr last  = new Attr();

            result.Add(last);

            while ((pos = text.IndexOf('{', index)) >= 0)
            {
                last.text += text.Substring(index, pos - index).Replace("\\N", "\n");

                int    endPos     = text.IndexOf('}', pos);
                string attrString = text.Substring(pos + 1, endPos - pos - 1);

                int    mode = -1;
                int    tagStart = 0, tagEnd = 0;
                string tag = null;
                for (pos = 0; pos < attrString.Length; pos++)
                {
                    switch (mode)
                    {
                    case -1:     // 태그 시작 전
                        while (pos < attrString.Length && attrString[pos] != '\\')
                        {
                            pos++;
                        }
                        mode     = 0;
                        tagStart = pos + 1;
                        pos--;
                        break;

                    case 0:     // 태그
                        for (tagEnd = tagStart; tagEnd < attrString.Length; tagEnd++)
                        {
                            if (attrString[tagEnd] == '\\')
                            {
                                break;
                            }
                            else if (attrString[tagEnd] == '(')
                            {
                                mode++;
                                pos = tagEnd + 1;
                                break;
                            }
                        }
                        if (mode > 0)
                        {
                            break;
                        }
                        if (tagStart == tagEnd)
                        {
                            break;
                        }

                        tag = attrString.Substring(tagStart, tagEnd - tagStart);

                        if (tag.StartsWith("c") || tag.StartsWith("1c"))
                        {
                            if (last.text.Length > 0)
                            {
                                result.Add((last = new Attr(last)));
                            }

                            if (tag.StartsWith("c"))
                            {
                                tag = "1" + tag;
                            }
                            if (tag.Length >= 11 && tag[2] == '&' && tag[3] == 'H' && tag[10] == '&')
                            {
                                last.fc = ColorToAttr(tag.Substring(2, 9));
                            }
                            else
                            {
                                last.fc = "";
                            }
                        }
                        else if (tag.StartsWith("fn"))
                        {
                            if (last.text.Length > 0)
                            {
                                result.Add((last = new Attr(last)));
                            }
                            last.fn = (tag.Length > 2) ? tag.Substring(2) : "";
                        }
                        else if (tag.StartsWith("b"))
                        {
                            if (last.text.Length > 0)
                            {
                                result.Add((last = new Attr(last)));
                            }
                            last.b = (tag.Length >= 2 && tag[1] == '1');
                        }
                        else if (tag.StartsWith("u"))
                        {
                            if (last.text.Length > 0)
                            {
                                result.Add((last = new Attr(last)));
                            }
                            last.u = (tag.Length >= 2 && tag[1] == '1');
                        }
                        else if (tag.StartsWith("i"))
                        {
                            if (last.text.Length > 0)
                            {
                                result.Add((last = new Attr(last)));
                            }
                            last.i = (tag.Length >= 2 && tag[1] == '1');
                        }

                        mode     = 0;
                        pos      = tagEnd;
                        tagStart = tagEnd + 1;
                        break;

                    default:     // 괄호
                        for (; pos < attrString.Length; pos++)
                        {
                            if (attrString[pos] == ')')
                            {
                                mode--;
                                break;
                            }
                        }
                        break;
                    }
                }

                index = endPos + 1;
            }
            last.text += text.Substring(index).Replace("\n", "\\n");

            return(result);
        }
        public static void Normalize(List <Smi> smis)
        {
            int startIndex = -1;

            for (int i = 1; i < smis.Count; i++)
            {
                if (smis[i].syncType == SyncType.inner)
                {
                    if (startIndex < 0)
                    {
                        startIndex = i - 1;
                    }
                }
                else
                {
                    if (startIndex >= 0)
                    {
                        int endIndex = i;
                        if (smis[startIndex].syncType == smis[endIndex].syncType)
                        {
                            int startSync = smis[startIndex].start;
                            int endSync   = smis[endIndex].start;
                            int count     = endIndex - startIndex;

                            for (int j = 1; j < count; j++)
                            {
                                smis[startIndex + j].start = ((count - j) * startSync + j * endSync) / count;
                            }
                        }
                        startIndex = -1;
                    }
                }
            }

            for (int i = 0; i < smis.Count - 1; i++)
            {
                Smi smi = smis[i];
                if (smi.syncType != smis[i + 1].syncType)
                {
                    // 전후 싱크 타입이 맞을 때만 안전함
                    continue;
                }

                string lower = smi.text.ToLower();
                if (lower.IndexOf(" fade=") > 0)
                {
                    List <Color> fadeColors = new List <Color>();
                    List <Attr>  attrs      = smi.ToAttr();
                    for (int j = 0; j < attrs.Count; j++)
                    {
                        if (attrs[j].fade != 0)
                        {
                            string color = (attrs[j].fc.Length == 6) ? attrs[j].fc : "ffffff";
                            fadeColors.Add(new Color(j, attrs[j].fade > 0, color));
                            attrs[j].fade = 0;
                        }
                    }
                    if (fadeColors.Count == 0)
                    {
                        continue;
                    }

                    int start = smi.start, end = smis[i + 1].start;
                    int frames = (int)Math.Round((end - start) * 24 / 1001.0);

                    foreach (Color color in fadeColors)
                    {
                        Attr attr = attrs[color.index];
                        if (color.isIn)
                        {
                            attr.fc = color.Get(1, 2 * frames);
                        }
                        else
                        {
                            attr.fc = color.Get(2 * frames - 1, 2 * frames);
                        }
                    }
                    smi.FromAttr(attrs);
                    for (int j = 1; j < frames; j++)
                    {
                        foreach (Color color in fadeColors)
                        {
                            Attr attr = attrs[color.index];
                            if (color.isIn)
                            {
                                attr.fc = color.Get(1 + 2 * j, 2 * frames);
                            }
                            else
                            {
                                attr.fc = color.Get(2 * frames - (1 + 2 * j), 2 * frames);
                            }
                        }
                        smis.Insert(i + j, new Smi()
                        {
                            start = (start * (frames - j) + end * j) / frames,
                            //syncType = 2,
                            syncType = SyncType.inner
                        }.FromAttr(attrs));
                    }
                    i += frames - 1;
                }
                else if (lower.IndexOf(" typing=") > 0)
                {
                    // 타이핑은 한 싱크에 하나만 가능
                    int         attrIndex  = -1;
                    Attr        attr       = null;
                    List <Attr> attrs      = smi.ToAttr();
                    bool        isLastAttr = false;
                    for (int j = 0; j < attrs.Count; j++)
                    {
                        if (attrs[j].typing != null)
                        {
                            string color = (attrs[j].fc.Length == 6) ? attrs[j].fc : "ffffff";
                            attr = attrs[(attrIndex = j)];
                            string remains = "";
                            for (int k = j + 1; k < attrs.Count; k++)
                            {
                                remains += attrs[k].text;
                            }
                            isLastAttr = remains.Length == 0 || remains.StartsWith("\n");
                            if (!isLastAttr)
                            {
                                int length = 0;
                                for (int k = j + 1; k < attrs.Count; k++)
                                {
                                    length += attrs[k].text.Length;
                                }
                                isLastAttr = (length == 0);
                            }
                            break;
                        }
                    }
                    if (attr == null)
                    {
                        continue;
                    }

                    List <string> types = Typing.ToType(attr.text, attr.typing.mode, attr.typing.cursor);
                    float         width = GetLineWidth(attr.text);

                    int start = smi.start, end = smis[i + 1].start;
                    int count = types.Count - attr.typing.end - attr.typing.start;
                    if (count < 1)
                    {
                        continue;
                    }

                    int typingStart = attr.typing.start;
                    attr.typing = null;
                    smis.RemoveAt(i);
                    for (int j = 0; j < count; j++)
                    {
                        string text = types[j + typingStart];
                        attr.text = Width.GetAppend(GetLineWidth(text), width) + (isLastAttr ? "​" : "");

                        List <Attr> tAttrs = new List <Attr>();
                        tAttrs.AddRange(attrs.GetRange(0, attrIndex));
                        tAttrs.AddRange(new Smi()
                        {
                            text = text
                        }.ToAttr());
                        tAttrs.Add(attr);
                        if (!isLastAttr)
                        {
                            tAttrs.AddRange(attrs.GetRange(attrIndex + 1, attrs.Count - attrIndex - 1));
                        }

                        smis.Insert(i + j, new Smi()
                        {
                            start    = (start * (count - j) + end * (j)) / count,
                            syncType = j == 0 ? smi.syncType : SyncType.inner
                        }.FromAttr(tAttrs));
                    }
                    i += count - 1;
                }
            }
        }
        public Smi FromAttr(List <Attr> attrs)
        {
            string          text      = "";
            List <string[]> lastAttrs = new List <string[]>();

            Attr last = new Attr();

            foreach (Attr attr in attrs)
            {
                if (!last.b && attr.b)
                {
                    text += "<B>";
                }
                else if (last.b && !attr.b)
                {
                    text += "</B>";
                }

                if (!last.i && attr.i)
                {
                    text += "<I>";
                }
                else if (last.i && !attr.i)
                {
                    text += "</I>";
                }

                if (!last.u && attr.u)
                {
                    text += "<U>";
                }
                else if (last.u && !attr.u)
                {
                    text += "</U>";
                }

                if (last.fs != attr.fs ||
                    !last.fn.Equals(attr.fn) ||
                    !last.fc.Equals(attr.fc) ||
                    !last.fade.Equals(attr.fade) ||
                    (last.typing == null && attr.typing != null) ||
                    (last.typing != null && attr.typing == null)
                    )
                {
                    // 기존에 속성이 있었을 때만 닫는 태그
                    if (last.fs > 0 || !last.fn.Equals("") || !last.fc.Equals("") || last.fade > 0 || last.typing != null)
                    {
                        text += "</FONT>";
                    }

                    // 신규 속성이 있을 때만 여는 태그
                    if (attr.fs > 0 || !attr.fn.Equals("") || !attr.fc.Equals("") || attr.fade > 0 || attr.typing != null)
                    {
                        text += "<FONT";
                        if (attr.fs > 0)
                        {
                            text += " size=\"" + attr.fs + "\"";
                        }
                        if (!attr.fn.Equals(""))
                        {
                            text += " face=\"" + attr.fn + "\"";
                        }
                        if (!attr.fc.Equals(""))
                        {
                            text += " color=\"" + ColorFromAttr(attr.fc) + "\"";
                        }
                        if (attr.fade != 0)
                        {
                            text += " fade=\"" + (attr.fade > 0 ? "in" : "out") + "\"";
                        }
                        if (attr.typing != null)
                        {
                            text += " typing=\"" + attr.typing.mode.ToString() + "(" + attr.typing.start + "," + attr.typing.end + ") " + attr.typing.cursor.ToString() + "\"";
                        }
                        text += ">";
                    }
                }

                text += WebUtility.HtmlEncode(attr.text);
                last  = attr;
            }

            this.text = text.Replace("\n", "<br>");
            return(this);
        }
        public static List <Attr> ToAttr(string text)
        {
            List <Attr> result = new List <Attr>();

            int       index  = 0;
            int       pos    = 0;
            SmiStatus status = new SmiStatus();
            Attr      last   = new Attr();

            result.Add(last);

            while ((pos = text.IndexOf('<', index)) >= 0)
            {
                #region 태그명
                int tagNameLength = 0;
                while (pos + 1 + tagNameLength < text.Length &&
                       text[pos + 1 + tagNameLength] != ' ' &&
                       text[pos + 1 + tagNameLength] != '>' &&
                       text[pos + 1 + tagNameLength] != '\r' &&
                       text[pos + 1 + tagNameLength] != '\n' &&
                       text[pos + 1 + tagNameLength] != '\t'
                       )
                {
                    tagNameLength++;
                }

                string tagName = text.Substring(pos + 1, tagNameLength).ToUpper();
                int    attrPos = pos + 1 + tagNameLength;
                #endregion

                #region 태그 속성
                List <string[]> attrs = new List <string[]>();
                if (text[attrPos] == '>')
                {
                    attrPos++;
                }
                else
                {
                    if (tagName[0].Equals('/')) // 종료 태그
                    {
                        attrPos = text.IndexOf('>', attrPos) + 1;
                    }
                    else
                    {
                        int    mode = 0;
                        int    attrNameStart = attrPos, attrValueStart = 0;
                        string name = "";

                        while (mode >= 0)
                        {
                            switch (mode)
                            {
                            case 0:     // 속성 이름
                            {
                                for (; mode == 0 && attrPos < text.Length; attrPos++)
                                {
                                    switch (text[attrPos])
                                    {
                                    case '>':
                                        if (attrPos - attrNameStart > 0)
                                        {
                                            // 이름만 있는 속성
                                            attrs.Add(new string[] {
                                                    text.Substring(attrNameStart, attrPos - attrNameStart).ToLower(),
                                                    null
                                                });
                                        }
                                        mode = -1;
                                        break;

                                    case ' ':
                                    case '\t':
                                    case '\r':
                                    case '\n':
                                        if (attrPos - attrNameStart > 0)
                                        {
                                            // 이름만 있는 속성
                                            attrs.Add(new string[] {
                                                    text.Substring(attrNameStart, attrPos - attrNameStart).ToLower(),
                                                    null
                                                });
                                        }
                                        attrNameStart = attrPos + 1;
                                        break;

                                    case '=':
                                        // 이름 끝, 값 시작
                                        name           = text.Substring(attrNameStart, attrPos - attrNameStart).ToLower();
                                        mode           = 1;
                                        attrValueStart = attrPos + 1;
                                        break;
                                    }
                                }
                                break;
                            }

                            case 1:     // 속성 값
                            {
                                for (; mode == 1 && attrPos < text.Length; attrPos++)
                                {
                                    switch (text[attrPos])
                                    {
                                    case '>':
                                        // 값 끝
                                        attrs.Add(new string[] {
                                                name,
                                                text.Substring(attrNameStart, attrPos - attrNameStart).ToLower()
                                            });
                                        mode = -1;
                                        attrPos++;
                                        break;

                                    case ' ':
                                    case '\t':
                                    case '\r':
                                    case '\n':
                                        // 값 끝
                                        attrs.Add(new string[] {
                                                name,
                                                text.Substring(attrValueStart, attrPos - attrValueStart).ToLower()
                                            });
                                        mode          = 0;
                                        attrNameStart = attrPos + 1;
                                        break;

                                    case '\'':
                                        if (attrPos == attrValueStart)
                                        {
                                            mode = 2;
                                            attrValueStart++;
                                        }
                                        break;

                                    case '"':
                                        if (attrPos == attrValueStart)
                                        {
                                            mode = 3;
                                            attrValueStart++;
                                        }
                                        break;
                                    }
                                }
                                break;
                            }

                            case 2:     // 속성 값 (작은 따옴표)
                            {
                                for (; mode == 2 && attrPos < text.Length; attrPos++)
                                {
                                    switch (text[attrPos])
                                    {
                                    case '\\':
                                        attrPos++;
                                        break;

                                    case '\'':
                                        // 값 끝
                                        attrs.Add(new string[] {
                                                name,
                                                text.Substring(attrValueStart, attrPos - attrValueStart).ToLower()
                                            });
                                        mode          = 0;
                                        attrNameStart = attrPos + 1;
                                        break;
                                    }
                                }
                                break;
                            }

                            case 3:     // 속성 값 (큰 따옴표)
                            {
                                for (; mode == 3 && attrPos < text.Length; attrPos++)
                                {
                                    switch (text[attrPos])
                                    {
                                    case '\\':
                                        attrPos++;
                                        break;

                                    case '"':
                                        // 값 끝
                                        attrs.Add(new string[] {
                                                name,
                                                text.Substring(attrValueStart, attrPos - attrValueStart).ToLower()
                                            });
                                        mode          = 0;
                                        attrNameStart = attrPos + 1;
                                        break;
                                    }
                                }
                                break;
                            }
                            }
                        }
                    }
                }
                #endregion

                if (tagName[0].Equals('/')) // 종료 태그
                {
                    tagName = tagName.Substring(1);
                    switch (tagName)
                    {
                    case "B":
                        last.text += WebUtility.HtmlDecode(text.Substring(index, pos - index).Replace("\n", ""));
                        if (last.text.Length > 0)
                        {
                            result.Add((last = new Attr()));
                        }
                        SetStyle(last, status.SetB(false));
                        break;

                    case "I":
                        last.text += WebUtility.HtmlDecode(text.Substring(index, pos - index).Replace("\n", ""));
                        if (last.text.Length > 0)
                        {
                            result.Add((last = new Attr()));
                        }
                        SetStyle(last, status.SetI(false));
                        break;

                    case "U":
                        last.text += WebUtility.HtmlDecode(text.Substring(index, pos - index).Replace("\n", ""));
                        if (last.text.Length > 0)
                        {
                            result.Add((last = new Attr()));
                        }
                        SetStyle(last, status.SetU(false));
                        break;

                    case "FONT":
                        last.text += WebUtility.HtmlDecode(text.Substring(index, pos - index).Replace("\n", ""));
                        if (last.text.Length > 0)
                        {
                            result.Add((last = new Attr()));
                        }
                        SetStyle(last, status.SetFont(null));
                        break;

                    default:
                        break;
                    }
                }
                else
                {
                    switch (tagName)
                    {
                    case "B":
                        last.text += WebUtility.HtmlDecode(text.Substring(index, pos - index).Replace("\n", ""));
                        if (last.text.Length > 0)
                        {
                            result.Add((last = new Attr()));
                        }
                        SetStyle(last, status.SetB(true));
                        break;

                    case "I":
                        last.text += WebUtility.HtmlDecode(text.Substring(index, pos - index).Replace("\n", ""));
                        if (last.text.Length > 0)
                        {
                            result.Add((last = new Attr()));
                        }
                        SetStyle(last, status.SetI(true));
                        break;

                    case "U":
                        last.text += WebUtility.HtmlDecode(text.Substring(index, pos - index).Replace("\n", ""));
                        if (last.text.Length > 0)
                        {
                            result.Add((last = new Attr()));
                        }
                        SetStyle(last, status.SetU(true));
                        break;

                    case "FONT":
                        last.text += WebUtility.HtmlDecode(text.Substring(index, pos - index).Replace("\n", ""));
                        if (last.text.Length > 0)
                        {
                            result.Add((last = new Attr()));
                        }
                        SetStyle(last, status.SetFont(attrs));
                        break;

                    case "BR":
                        last.text += WebUtility.HtmlDecode(text.Substring(index, pos - index).Replace("\n", "")) + "\n";
                        break;

                    default:
                        last.text += WebUtility.HtmlDecode(text.Substring(index, attrPos - index).Replace("\n", ""));
                        break;
                    }
                }

                index = attrPos;
            }
            last.text += WebUtility.HtmlDecode(text.Substring(index));

            return(result);
        }