/*
 * SDK Pullenti Unitext, version 4.33, fabruary 2026. Copyright (c) 2013-2026, Pullenti. All rights reserved. 
 * Non-Commercial Freeware and Commercial Software. 
 * This class is generated using the converter UniSharping (www.unisharping.ru) from Pullenti C# project. 
 * The latest version of the code is available on the site www.pullenti.ru
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Pullenti.Unitext.Internal.Pdf
{
    public class PdfDictionary : PdfObject
    {
        Dictionary<string, PdfObject> m_Items = new Dictionary<string, PdfObject>();
        private byte[] m_StreamData = null;
        private PdfStream m_StreamIn = null;
        private int m_StreamPos = 0;
        private int m_StreamLength = 0;
        internal int m_FilePos;
        public int PageNum;
        public byte[] GetData()
        {
            if (m_StreamData == null) 
            {
                if (m_StreamLength == 0) 
                    return null;
                m_StreamData = new byte[(int)m_StreamLength];
                m_StreamIn.Position = m_StreamPos;
                m_StreamIn.Read(m_StreamData, 0, m_StreamLength);
            }
            return m_StreamData;
        }
        public void SetData(byte[] dat)
        {
            m_StreamData = dat;
        }
        public void AddObject(string key, PdfObject o)
        {
            if (!m_Items.ContainsKey(key)) 
            {
                if (o != null) 
                    m_Items.Add(key, o);
            }
            else if (o != null) 
                m_Items[key] = o;
            else 
                m_Items.Remove(key);
        }
        public List<string> Keys
        {
            get
            {
                return new List<string>(m_Items.Keys);
            }
        }
        public override bool IsSimple(int lev)
        {
            if (m_Items.Count > 10 || lev > 10) 
                return false;
            foreach (string key in Keys) 
            {
                if (key == "Parent") 
                    continue;
                PdfObject it = this.GetObject(key, false);
                if (it != null && !it.IsSimple(lev + 1)) 
                    return false;
            }
            return true;
        }
        public override string ToStringEx(int lev)
        {
            if (lev > 5) 
                return string.Format("<<...{0}>>", m_Items.Count);
            StringBuilder res = new StringBuilder();
            PdfObject it = this.GetObject("Type", false);
            if (it != null && lev >= 0) 
                res.Append(it.ToString());
            if (PageNum > 0) 
                res.AppendFormat(" Num={0}", PageNum);
            if (Id > 0) 
                res.AppendFormat(" Id={0}", Id);
            res.Append("<<");
            bool fi = true;
            bool partial = false;
            foreach (string key in Keys) 
            {
                if (key == "Parent" || key == "Type") 
                    continue;
                it = this.GetObject(key, false);
                if (it == null) 
                {
                    PdfObject indir = m_Items[key];
                    if (!fi) 
                        res.Append(", ");
                    else 
                        fi = false;
                    res.AppendFormat("{0}={1}", key, (indir == null ? "NULL" : indir.ToString()));
                    if (res.Length > 100) 
                        break;
                    continue;
                }
                if (!it.IsSimple(lev + 1)) 
                {
                    partial = true;
                    continue;
                }
                if (!fi) 
                    res.Append(", ");
                else 
                    fi = false;
                if (res.Length > 100) 
                {
                    partial = true;
                    break;
                }
                string str = it.ToStringEx(lev + 1);
                if (str.Length > 50) 
                    str = str.Substring(0, 50) + "...";
                res.AppendFormat("{0}={1}", key, str);
            }
            if (partial) 
                res.AppendFormat("...{0}", m_Items.Count);
            res.Append(">>");
            if (m_StreamLength > 0) 
                res.AppendFormat(" DATA:{0}", m_StreamLength);
            return res.ToString();
        }
        public override void OutHtml(StringBuilder res)
        {
            if (Id > 0) 
                res.AppendFormat("Id={0} ", Id);
            res.Append("<b>&lt;&lt;</b> ");
            bool fi = true;
            foreach (string k in Keys) 
            {
                if (fi) 
                    fi = false;
                else 
                    res.Append(", <br/>");
                res.AppendFormat("{0} : ", k);
                PdfObject o = this.GetObject(k, false);
                if (o == null) 
                {
                    res.Append("null");
                    continue;
                }
                if (o.Id > 0) 
                {
                    res.AppendFormat("<a href=\"obj{0}\">", o.Id);
                    Pullenti.Util.MiscHelper.CorrectHtmlValue(res, o.ToString(), false, false, false);
                    res.Append("</a>");
                }
                else 
                    o.OutHtml(res);
            }
            res.Append("<b> &gt;&gt;</b>");
            this._outHtmlRefs(res);
            PdfPage page = null;
            if (this.IsTypeItem("Page")) 
                page = new PdfPage(this, null, true);
            else if (this.IsTypeItem("XObject") && this.GetStringItem("Subtype") == "Form") 
            {
                List<PdfObject> li = SourceFile.GetAllWhoRefObject0(Id);
                foreach (PdfObject o in li) 
                {
                    if ((o is PdfDictionary) && (o as PdfDictionary).IsTypeItem("Page")) 
                    {
                        PdfPage page0 = new PdfPage(o as PdfDictionary, null, true);
                        if (page0.Subpages != null) 
                        {
                            foreach (PdfPage p in page0.Subpages) 
                            {
                                if (p.Dic == this) 
                                {
                                    page = p;
                                    break;
                                }
                            }
                        }
                        break;
                    }
                }
            }
            if (page != null && page.Lexems != null) 
            {
                res.Append("\r\n<br/><hr/>");
                List<PdfPageOper> opers = PdfPageOper.Parse(page.Lexems);
                foreach (PdfPageOper op in opers) 
                {
                    op.OutHtml(res);
                    res.Append("<br/>");
                }
            }
            else if (this.IsTypeItem("Font")) 
            {
                res.Append("\r\n<br/><hr/>");
                PdfFont font = new PdfFont(this);
                Pullenti.Util.MiscHelper.CorrectHtmlValue(res, font.OutDebug(), false, false, false);
            }
            else if (this.IsTypeItem("Image") || ((this.IsTypeItem("XObject") && this.GetStringItem("Subtype") == "Image"))) 
            {
                res.Append("\r\n<br/><hr/>");
                PdfImage img = new PdfImage(this);
                if (img.Content != null) 
                {
                    Pullenti.Unitext.FileFormat frm = Pullenti.Util.FileFormatsHelper.AnalizeFormat(null, img.Content);
                    res.AppendFormat("<div>Image Format: <b>{0}</b></div>", frm);
                    Pullenti.Unitext.UnitextImage uimg = new Pullenti.Unitext.UnitextImage();
                    uimg.Content = img.Content;
                    if (img.Width > 0) 
                        uimg.Width = string.Format("{0}pt", (int)img.Width);
                    if (img.Height > 0) 
                        uimg.Height = string.Format("{0}pt", (int)img.Height);
                    res.Append("<div style=\"border:1pt solid red\">");
                    uimg.GetHtml(res, null);
                    res.Append("</div>");
                }
            }
            else if (HasDataStream) 
            {
                res.Append("\r\n<br/><hr/>DataStream:<br/>");
                try 
                {
                    byte[] dat = this.ExtractData();
                    if (dat == null) 
                        dat = this.GetData();
                    if (dat != null) 
                    {
                        int hasZero = 0;
                        int zind = -1;
                        for (int i = 0; i < dat.Length; i++) 
                        {
                            if (dat[i] == 0) 
                            {
                                hasZero++;
                                if (zind < 0) 
                                    zind = i;
                                break;
                            }
                        }
                        if ((hasZero < 3) || zind > 1000) 
                        {
                            int len = dat.Length;
                            if (dat.Length > 30000) 
                                len = 30000;
                            char ch0 = (char)0;
                            char ch1 = (char)1;
                            string txt = Encoding.ASCII.GetString(dat, 0, len).Replace(string.Format("{0}", ch0), "{0}").Replace(string.Format("{0}", ch1), "{1}");
                            Pullenti.Util.MiscHelper.CorrectHtmlValue(res, txt, false, false, false);
                        }
                        else 
                        {
                            res.Append("<div style=\"font-family:monospace\">");
                            for (int i = 0; (i < dat.Length) && (i < 1000); i++) 
                            {
                                if (((i & 0xF)) == 0) 
                                    res.Append("\n<br/>");
                                res.AppendFormat("{0} ", dat[i].ToString("X02"));
                                if (res.Length > 30000) 
                                {
                                    res.Append("\r\n<br/>...");
                                    break;
                                }
                            }
                            res.Append("</div>");
                        }
                    }
                }
                catch(Exception ex) 
                {
                    res.Append("ERROR: ");
                    Pullenti.Util.MiscHelper.CorrectHtmlValue(res, ex.ToString(), false, false, false);
                }
            }
        }
        public bool HasDataStream
        {
            get
            {
                return m_StreamLength > 0;
            }
        }
        internal void PostParse(PdfStream stream)
        {
            m_Items.Clear();
            m_StreamIn = null;
            m_StreamLength = 0;
            m_StreamData = null;
            int p0 = (int)stream.Position;
            byte ch;
            int i;
            string str;
            while ((((i = stream.PeekSolidByte()))) >= 0) 
            {
                ch = (byte)i;
                if (ch == '/') 
                {
                    str = PdfStringValue.GetStringByBytes(stream.ReadName());
                    if (str == null) 
                        break;
                    if (str == "Contents") 
                    {
                    }
                    PdfObject obj = stream.ParseObject(SourceFile, false);
                    if (obj == null) 
                        break;
                    if (!m_Items.ContainsKey(str)) 
                        m_Items.Add(str, obj);
                    continue;
                }
                if (ch != '>') 
                    break;
                stream.Position++;
                ch = (byte)stream.ReadByte();
                if (ch != '>') 
                    break;
                this._parseStream(stream);
                return;
            }
        }
        void _parseStream(PdfStream stream)
        {
            int p0 = (int)stream.Position;
            string str = stream.ReadWord(false);
            if (str != "stream") 
            {
                stream.Position = p0;
                return;
            }
            byte[] StreamTag = new byte[(int)6];
            byte ch = (byte)stream.ReadByte();
            if (ch != 0xD) 
                stream.Position--;
            ch = (byte)stream.ReadByte();
            if (ch != 0xA) 
                stream.Position--;
            m_StreamIn = stream;
            m_StreamPos = stream.Position;
            m_StreamLength = (int)this.GetIntItem("Length");
            if (m_StreamLength > 0 && ((m_StreamPos + m_StreamLength) < stream.Length)) 
                stream.Position = m_StreamPos + m_StreamLength;
            int len = 0;
            int i;
            while ((((i = stream.ReadByte()))) >= 0) 
            {
                ch = (byte)i;
                len++;
                if (ch != 'e') 
                    continue;
                ch = (byte)stream.ReadByte();
                len++;
                if (ch != 'n') 
                    continue;
                ch = (byte)stream.ReadByte();
                len++;
                if (ch != 'd') 
                    continue;
                p0 = stream.Position;
                stream.Read(StreamTag, 0, 6);
                if (PdfStringValue.GetStringByBytes(StreamTag) == "stream") 
                {
                    len -= 3;
                    break;
                }
                stream.Position = p0;
            }
            if (m_StreamLength == 0) 
                m_StreamLength = len;
        }
        public string GetStringItem(string Key)
        {
            PdfObject val = this.GetObject(Key, false);
            while (true) 
            {
                if (val == null) 
                    return null;
                if (val is PdfName) 
                    return (val as PdfName).Name;
                if (val is PdfBoolValue) 
                    return ((val as PdfBoolValue).Val ? "true" : "false");
                if (val is PdfStringValue) 
                    return PdfStringValue.GetStringByBytes((val as PdfStringValue).Val);
                if (val is PdfIntValue) 
                    return (val as PdfIntValue).Val.ToString();
                if (val is PdfArray) 
                {
                    val = (val as PdfArray).GetItem(0, false);
                    if (val == null) 
                        break;
                    continue;
                }
                break;
            }
            return null;
        }
        public bool IsTypeItem(string typeVal)
        {
            return this.GetStringItem("Type") == typeVal;
        }
        public int GetIntItem(string key)
        {
            PdfObject val = this.GetObject(key, false);
            if (val == null) 
                return 0;
            if (val is PdfIntValue) 
                return (val as PdfIntValue).Val;
            if (val is PdfRealValue) 
                return (int)(val as PdfRealValue).GetDouble();
            return 0;
        }
        public PdfObject GetObject(string key, bool keepRef = false)
        {
            PdfObject res = null;
            if (!m_Items.TryGetValue(key, out res)) 
                return null;
            if ((res is PdfReference) && !keepRef) 
            {
                res = SourceFile.GetObject(res.Id);
                if (res == null) 
                    return null;
                m_Items[key] = res;
            }
            return res;
        }
        public PdfDictionary GetDictionary(string key, string typVal = null)
        {
            PdfObject obj = this.GetObject(key, false);
            if (obj is PdfArray) 
                obj = (obj as PdfArray).GetItem(0, false);
            if (obj is PdfDictionary) 
            {
                PdfDictionary res = obj as PdfDictionary;
                if (typVal != null) 
                {
                    if (!res.IsTypeItem(typVal)) 
                        return null;
                }
                return res;
            }
            return null;
        }
        public void GetAllPages(List<PdfDictionary> res)
        {
            if (this.IsTypeItem("Page")) 
            {
                res.Add(this);
                return;
            }
            PdfDictionary dic = this.GetDictionary("Pages", "Pages");
            if (dic != null) 
                dic.GetAllPages(res);
            else 
            {
                PdfArray kids = this.GetObject("Kids", false) as PdfArray;
                if (kids != null) 
                {
                    for (int i = 0; i < kids.ItemsCount; i++) 
                    {
                        PdfObject it = kids.GetItem(i, false);
                        if (it is PdfDictionary) 
                            (it as PdfDictionary).GetAllPages(res);
                    }
                }
            }
        }
        public byte[] ExtractData()
        {
            byte[] dat = this.GetData();
            if (dat == null) 
                return new byte[(int)0];
            string str = this.GetStringItem("Filter");
            if (string.IsNullOrEmpty(str)) 
                return dat;
            if (dat.Length < 10) 
                return null;
            byte[] res = null;
            if (str == "FlateDecode") 
                res = Pullenti.Util.ArchiveHelper.DecompressZlib(dat);
            else if (str == "LZWDecode") 
            {
                Pullenti.Unitext.Internal.Misc.LzwDecoder lzw = new Pullenti.Unitext.Internal.Misc.LzwDecoder();
                res = lzw.Decode(dat);
            }
            else 
                return null;
            if (res == null || res.Length == 0) 
                return null;
            PdfDictionary dparms = this.GetDictionary("DecodeParms", null);
            int predict = 0;
            int N = 0;
            int columns = 0;
            if (dparms != null) 
            {
                PdfIntValue iv = dparms.GetObject("Predictor", false) as PdfIntValue;
                if (iv != null && iv.Val > 1) 
                    predict = iv.Val;
                iv = dparms.GetObject("Columns", false) as PdfIntValue;
                if (iv != null) 
                    columns = iv.Val;
                iv = dparms.GetObject("Colors", false) as PdfIntValue;
                if (iv != null) 
                    N = iv.Val;
            }
            if ((predict >= 10 && predict <= 15 && N > 0) && columns > 0) 
            {
                int rowSize = columns * N;
                if (predict == 15) 
                    rowSize += 1;
                for (int pos0 = 0; (pos0 + rowSize) < res.Length; pos0 += rowSize) 
                {
                    int cod = predict - 10;
                    int pos = pos0;
                    if (predict == 15) 
                    {
                        cod = res[pos0];
                        pos++;
                    }
                    for (int j = pos; j < (pos0 + rowSize); j++) 
                    {
                        byte valLeft = (byte)0;
                        byte valTop = (byte)0;
                        byte valCorner = (byte)0;
                        int k = j - N;
                        int kk = j - rowSize;
                        if (kk >= 0) 
                            valTop = res[kk];
                        if (k >= pos) 
                        {
                            valLeft = res[k];
                            if (kk >= 0) 
                                valCorner = res[kk - N];
                        }
                        res[j] = Pullenti.Unitext.Internal.Misc.PngWrapper.FilterByte(cod, false, res[j], valLeft, valTop, valCorner);
                    }
                }
                if (predict == 15) 
                {
                    int p = 0;
                    for (int i = 0; i < res.Length; i++) 
                    {
                        if (((i % rowSize)) == 0) 
                        {
                        }
                        else 
                        {
                            res[p] = res[i];
                            p++;
                        }
                    }
                    Array.Resize(ref res, p);
                }
            }
            return res;
        }
        public void SetDataValue(byte[] data)
        {
            PdfName filt = this.GetObject("Filter", false) as PdfName;
            if (filt != null) 
            {
                if (filt.Name == "FlateDecode" || filt.Name == "LZWDecode") 
                {
                    filt.Name = "FlateDecode";
                    data = Pullenti.Util.ArchiveHelper.CompressZlib(data);
                }
            }
            m_StreamData = data;
        }
        public byte[] GetTotalDataStream(string key)
        {
            PdfObject obj = this.GetObject(key, false);
            if (obj == null) 
                return null;
            if (obj is PdfDictionary) 
                return ((PdfDictionary)obj).ExtractData();
            PdfArray arr = obj as PdfArray;
            if (arr == null) 
                return null;
            List<byte> res = null;
            for (int i = 0; i < arr.ItemsCount; i++) 
            {
                obj = arr.GetItem(i, false);
                if (obj == null) 
                    continue;
                if (!(obj is PdfDictionary)) 
                    continue;
                byte[] tmp = ((PdfDictionary)obj).ExtractData();
                if (tmp == null) 
                    continue;
                if (tmp.Length < 1) 
                    continue;
                if (res == null) 
                    res = new List<byte>(tmp);
                else 
                {
                    res.Add(0xD);
                    res.Add(0xA);
                    res.AddRange(tmp);
                }
            }
            if (res == null) 
                return null;
            return res.ToArray();
        }
        public void ClearTotalDataStream(string key)
        {
            PdfObject obj = this.GetObject(key, false);
            if (obj == null) 
                return;
            if (obj is PdfDictionary) 
                ((PdfDictionary)obj).SetData(null);
            PdfArray arr = obj as PdfArray;
            if (arr == null) 
                return;
            for (int i = 0; i < arr.ItemsCount; i++) 
            {
                obj = arr.GetItem(i, false);
                if (obj == null) 
                    continue;
                if (!(obj is PdfDictionary)) 
                    continue;
                ((PdfDictionary)obj).SetData(null);
            }
        }
        public override void Write(Stream s)
        {
            if (Id == 18) 
            {
            }
            s.WriteByte((byte)'<');
            s.WriteByte((byte)'<');
            byte[] buf;
            bool fi = true;
            byte[] dat = this.GetData();
            bool hasSize = false;
            if (dat != null) 
            {
                if (dat.Length == 448) 
                {
                }
            }
            foreach (KeyValuePair<string, PdfObject> kp in m_Items) 
            {
                PdfObject it = kp.Value;
                if (it.Deleted) 
                    continue;
                if (kp.Key == "Length" && dat != null && (it is PdfIntValue)) 
                {
                    hasSize = true;
                    (it as PdfIntValue).Val = dat.Length;
                }
                if (fi) 
                    fi = false;
                else 
                    s.WriteByte((byte)' ');
                buf = Encoding.ASCII.GetBytes(string.Format("/{0}", kp.Key));
                s.Write(buf, 0, buf.Length);
                s.WriteByte((byte)' ');
                if (it.Id == 0 || kp.Key == "Length") 
                    it.Write(s);
                else 
                {
                    buf = Encoding.ASCII.GetBytes(string.Format("{0} 0 R", it.WriteId));
                    s.Write(buf, 0, buf.Length);
                }
            }
            if (!hasSize && dat != null) 
            {
                if (fi) 
                    fi = false;
                else 
                    s.WriteByte((byte)' ');
                buf = Encoding.ASCII.GetBytes(string.Format("/Length {0}", dat.Length));
                s.Write(buf, 0, buf.Length);
            }
            s.WriteByte((byte)'>');
            s.WriteByte((byte)'>');
            if (dat == null) 
                return;
            buf = Encoding.ASCII.GetBytes("stream\r\n");
            s.Write(buf, 0, buf.Length);
            s.Write(dat, 0, dat.Length);
            buf = Encoding.ASCII.GetBytes("endstream");
            s.Write(buf, 0, buf.Length);
        }
        public override void GetAllReferences(List<PdfReference> refs, List<int> ids)
        {
            if (Id > 0) 
            {
                if (ids.Contains(Id)) 
                    return;
                ids.Add(Id);
            }
            foreach (string k in Keys) 
            {
                PdfObject o = this.GetObject(k, true);
                if (o != null) 
                    o.GetAllReferences(refs, ids);
            }
            if (Id > 0) 
                ids.Remove(Id);
        }
        public override void GetAllWhoRefObject(int objId, PdfObject cur, List<PdfObject> res, List<int> ids)
        {
            if (Id > 0) 
            {
                if (ids.Contains(Id)) 
                    return;
                ids.Add(Id);
                cur = this;
            }
            foreach (string k in Keys) 
            {
                PdfObject o = this.GetObject(k, true);
                if (o != null && o.Id == objId) 
                {
                    if (!res.Contains(cur)) 
                        res.Add(cur);
                }
                else if (o != null && o.Id == 0) 
                    o.GetAllWhoRefObject(objId, cur, res, ids);
            }
            if (Id > 0) 
                ids.Remove(Id);
        }
        public IPdfDictionaryWrapper Obj;
        public void SetNewDataContent(byte[] data)
        {
            if (this.IsTypeItem("XObject")) 
            {
                this.SetDataValue(data);
                return;
            }
            PdfObject cnts = this.GetObject("Contents", false);
            if (cnts is PdfDictionary) 
                (cnts as PdfDictionary).SetDataValue(data);
            else if (cnts is PdfArray) 
            {
                PdfArray arr = cnts as PdfArray;
                if (arr.ItemsCount > 0) 
                {
                    PdfDictionary ddic = null;
                    int len = 0;
                    for (int i = 0; i < arr.ItemsCount; i++) 
                    {
                        PdfDictionary cc = arr.GetItem(i, false) as PdfDictionary;
                        if (cc == null) 
                            continue;
                        int le = cc.GetIntItem("Length");
                        if (le > len) 
                        {
                            len = le;
                            ddic = cc;
                        }
                    }
                    if (ddic != null) 
                    {
                        ddic.SetDataValue(data);
                        for (int i = arr.ItemsCount - 1; i >= 0; i--) 
                        {
                            PdfObject oo = arr.GetItem(i, false);
                            if (oo != null && oo != ddic) 
                                oo.Deleted = true;
                            if (oo != ddic) 
                                arr.RemoveItem(i);
                        }
                    }
                }
            }
            else if (HasDataStream) 
                this.SetDataValue(data);
            else 
            {
            }
        }
    }
}