// Copyright (C) 2003-2026, Foxit Software Inc..
// All Rights Reserved.
//
// http://www.foxitsoftware.com
//
// The following code is copyrighted and contains proprietary information and trade secrets of Foxit Software Inc..
// You cannot distribute any part of Foxit PDF SDK to any third party or general public,
// unless there is a separate license agreement with Foxit Software Inc. which explicitly grants you such rights.
//
// This file contains an example to demonstrate how to use Foxit PDF SDK to to call layout recognition related classes
// for PDF document.

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;

using foxit;
using foxit.common;
using foxit.common.fxcrt;
using foxit.pdf;
using foxit.pdf.graphics;
using foxit.pdf.objects;
using foxit.addon.layoutrecognition;

namespace layout_recognitionCS
{
    class TextDoc
    {
        private FileStream mfile_stream;
        public TextDoc(string file_name, FileMode fill_mode)
        {
            //FileStream F = new FileStream(file_name, FileMode.Open, FileAccess.Read, FileShare.Read);
            mfile_stream = new FileStream(file_name, fill_mode);
        }
        ~TextDoc()
        {
            try
            {
                mfile_stream.Flush();
                mfile_stream.Close();
            }
            catch (System.ObjectDisposedException e)
            {
                string str = e.Message;
            }

        }
        public void Write(int count, string prefix, string contents)
        {
            string prefix_all = "";
            for (int i = 0; i < count; i++)
            {
                prefix_all = prefix_all + prefix;
            }
            contents = prefix_all + contents;

            byte[] byte_data = System.Text.Encoding.Default.GetBytes(contents);
            // Move file pointer to beginning of file.
            mfile_stream.Seek(0, SeekOrigin.End);
            mfile_stream.Write(byte_data, 0, byte_data.Length);
            mfile_stream.Flush();
        }
        public void Write(string contents)
        {
            byte[] byte_data = System.Text.Encoding.Default.GetBytes(contents);
            // Move file pointer to beginning of file.
            mfile_stream.Seek(0, SeekOrigin.End);
            mfile_stream.Write(byte_data, 0, byte_data.Length);
            mfile_stream.Flush();
        }
        public string Read()
        {
            int fs_Len = (int)mfile_stream.Length;
            byte[] byte_data = new byte[fs_Len];
            int r = mfile_stream.Read(byte_data, 0, byte_data.Length);
            string contents = System.Text.Encoding.UTF8.GetString(byte_data);
            return contents;
        }
    };

    class layout_recognition
    {
        public static void GetChildFromElement(LRStructureElement pElement, List<LRElement> elemArray, LRElement.ElementType nType)
        {
            int nElemListSize = pElement.GetChildCount();
            for (int i = 0; i < nElemListSize; i++)
            {
                LRElement item = pElement.GetChild(i);
                LRElement.ElementType item_type = item.GetElementType();
                if (item_type == nType)
                    elemArray.Add(item);
                if (item.IsStructureElement())
                {
                    LRStructureElement srt_item = new LRStructureElement(item);
                    GetChildFromElement(srt_item, elemArray, nType);
                }
            }
        }

        public static String LR_Format(bool val)
        {
            return val ? "True" : "False";
        }

        public static String LR_Format(int val)
        {
            return String.Format("{0}", val);
        }

        public static String LR_Format(float val)
        {
            return String.Format("{0:0.0}", val);
        }

        public static String LR_FormatARGB(uint val)
        {
            return String.Format("#{0}{1}{2}", ((Int32)(char)(val >> 16)).ToString("X2"), ((Int32)(char)(val >> 8)).ToString("X2"), ((Int32)(char)val).ToString("X2"));
        }

        public static String LR_FormatAttributeValueEnum(LRStructureElement.AttributeValueEnum val)
        {
            return LRStructureElement.StringifyEnumVariant(val);
        }

        private static void OutputLRStructureElement(LRStructureElement element, TextDoc doc, int depth)
        {
            if (element.IsEmpty())
                return;

            LRStructureElement parentElement = element.GetParentElement();
            bool bIsEmpty = parentElement.IsEmpty();

            LRElement.ElementType elemType = element.GetElementType();
            String elementTypeStr = LRStructureElement.StringifyElementType(elemType);
            String outputStr = "< StructureElement: " + elementTypeStr + " >\r\n";
            doc.Write(depth, "\t", outputStr);

            int nSize = element.GetSupportedAttributeCount();
            for (int i = 0; i < nSize; i++)
            {
                LRStructureElement.AttributeType attrType = element.GetSupportedAttribute(i);

                LRStructureElement.AttributeValueType attrValueType = element.GetAttributeValueType(attrType);
                int nLength = 0;
                bool bIsArray = LRStructureElement.IsArrayAttributeValueType(attrValueType);
                nLength = element.GetAttributeValueCount(attrType);
                if (attrValueType == LRStructureElement.AttributeValueType.e_AttributeValueTypeEmpty)
                    continue;

                String szKey = LRStructureElement.StringifyAttributeType(attrType);
                String szVal = "";
                if (bIsArray) szVal += "[";

                for (int idx = 0; idx < nLength; idx++)
                {
                    if (idx > 0) szVal += ", ";

                    switch (attrValueType)
                    {
                        case LRStructureElement.AttributeValueType.e_AttributeValueTypeEnum:
                        case LRStructureElement.AttributeValueType.e_AttributeValueTypeEnumArray:
                            szVal += LR_FormatAttributeValueEnum(element.GetAttributeValueEnum(attrType, idx));
                            break;
                        case LRStructureElement.AttributeValueType.e_AttributeValueTypeInt32:
                        case LRStructureElement.AttributeValueType.e_AttributeValueTypeInt32Array:
                            szVal += LR_Format(element.GetAttributeValueInt32(attrType, idx));
                            break;
                        case LRStructureElement.AttributeValueType.e_AttributeValueTypeFloat:
                        case LRStructureElement.AttributeValueType.e_AttributeValueTypeFloatArray:
                            szVal += LR_Format(element.GetAttributeValueFloat(attrType, idx));
                            break;
                        case LRStructureElement.AttributeValueType.e_AttributeValueTypeARGB:
                        case LRStructureElement.AttributeValueType.e_AttributeValueTypeARGBArray:
                            szVal += LR_FormatARGB(element.GetAttributeValueARGB(attrType, idx));
                            break;
                        case LRStructureElement.AttributeValueType.e_AttributeValueTypeWStr:
                        case LRStructureElement.AttributeValueType.e_AttributeValueTypeWStrArray:
                            szVal += element.GetAttributeValueString(attrType, idx);
                            break;
                    }
                }
                if (bIsArray) szVal += "]";

                outputStr = szKey + ": " + szVal + "\r\n";
                doc.Write(depth, "\t", outputStr);
            }
        }

        private static void OutputLRStructureElement(LRGraphicsObjectElement element, TextDoc doc, int depth)
        {
            if (element.IsEmpty())
                return;

            String outputStr = "< LRGraphicsObjectElement: >\r\n";
            LRGraphicsObjectElement parentPageObj = element.GetParentGraphicsObjectElement();
            bool bIsEmpty = parentPageObj.IsEmpty();
            doc.Write(depth, "\t", outputStr);
            GraphicsObject pageObj = element.GetGraphicsObject();
            GraphicsObject.Type type = 0;
            if (pageObj != null)
                type = pageObj.GetType();
            RectF rcBox = element.GetBBox();
            PDFDictionary pDict = element.GetDict();
            int objIndex = element.GetGraphicsObjectIndex();
            Matrix2D matrix = element.GetMatrix();
            String szVal = "";
            szVal = String.Format("BBox: [{0:0.0},{1:0.0},{2:0.0},{3:0.0}]\r\n", rcBox.left, rcBox.top, rcBox.right, rcBox.bottom);
            doc.Write(depth, "\t", szVal);
            szVal = String.Format("Matrix: [{0:0.0},{1:0.0},{2:0.0},{3:0.0},{4:0.0},{5:0.0}]\r\n", matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f);
            doc.Write(depth, "\t", szVal);
            szVal = String.Format("PageObjectIndex: {0}\r\n", objIndex);
            doc.Write(depth, "\t", szVal);

            String text = (type == GraphicsObject.Type.e_TypeText) ? pageObj.GetTextObject().GetText() : "";
            if (text.Length > 0)
            {
                szVal = "Text: ";
                szVal += text;
                szVal += "\r\n";
                doc.Write(depth, "\t", szVal);
            }
        }

        private static void OutputLRStructureElement(LRContentElement element, TextDoc doc, int depth)
        {
            if (element.IsEmpty())
                return;

            LRGraphicsObjectElement pageObj = element.GetGraphicsObjectElement();
            bool bIsEmpty = pageObj.IsEmpty();
            LRStructureElement parentPageObj = element.GetParentElement();
            bIsEmpty = parentPageObj.IsEmpty();

            LRElement.ElementType elemType = element.GetElementType();
            String elementTypeStr = LRElement.StringifyElementType(elemType);
            String outputStr = "< LRContentElement: " + elementTypeStr + " >\r\n";
            doc.Write(depth, "\t", outputStr);

            RectF rcBox = element.GetBBox();
            int nStartPos = 0, nLength = 0;
            Range range = element.GetGraphicsObjectRange();
            if (!range.IsEmpty())
            {
                nStartPos = range.GetSegmentStart(0);
                nLength = range.GetSegmentEnd(0) - nStartPos + 1;
            }
            Matrix2D matrix = element.GetMatrix();

            String szVal = "";
            szVal = String.Format("BBox: [{0:0.0},{1:0.0},{2:0.0},{3:0.0}]\r\n", rcBox.left, rcBox.top, rcBox.right, rcBox.bottom);
            doc.Write(depth, "\t", szVal);
            szVal = String.Format("Matrix: [{0:0.0},{1:0.0},{2:0.0},{3:0.0},{4:0.0},{5:0.0}]\r\n", matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f);
            doc.Write(depth, "\t", szVal);
            szVal = String.Format("StartPos: {0}\r\n", nStartPos);
            doc.Write(depth, "\t", szVal);
            szVal = String.Format("Length: {0}\r\n", nLength);
            doc.Write(depth, "\t", szVal);

            if (!bIsEmpty)
                OutputLRStructureElement(pageObj, doc, depth + 1);
        }

        private static void ShowLRElementInfo(LRStructureElement element, TextDoc doc, int depth)
        {
            int nElemListSize = element.GetChildCount();
            OutputLRStructureElement(element, doc, depth);
            for (int i = 0; i < nElemListSize; i++)
            {
                LRElement item = element.GetChild(i);
                LRElement.ElementType item_type = item.GetElementType();
                if (item.IsStructureElement())
                {
                    LRStructureElement srt_item = new LRStructureElement(item);
                    ShowLRElementInfo(srt_item, doc, depth + 1);
                }
                else if (item.IsContentElement())
                {
                    LRContentElement srt_item = new LRContentElement(item);
                    OutputLRStructureElement(srt_item, doc, depth);
                }
                else if (item.IsGraphicsObjectElement())
                {
                    LRGraphicsObjectElement srt_item = new LRGraphicsObjectElement(item);
                    OutputLRStructureElement(srt_item, doc, depth);
                }
            }
        }

        private static void OutputAllLRElement(LRStructureElement element, String info_path)
        {
            TextDoc text_doc = new TextDoc(info_path, FileMode.Create);
            if (!element.IsEmpty())
            {
                ShowLRElementInfo(element, text_doc, 0);
            }
            else
            {
                text_doc.Write("No layout recognition information!\r\n");
            }
        }

        static void Main(string[] args)
        {
            string input_path = "../input_files/";
            string output_path = "../output_files/layout_recognition/";
            System.IO.Directory.CreateDirectory(output_path);

            string sn = "Rew1vB0TqoMIxX444UcpScARWdhgJpsJfzmyMLR32a0+EgrCjp2NaQ==";
            string key = "8f3g1sFttW8NAgfSNv+BvaJWy4Pp8flcm7ncNDkdvCCO4JwUFbGLyZdujReMaiplbYJwwxam3lr7uHtQaEf0xTSu8j2ZBdrZ8FS4OQkEUjOI9UEGb3Ic0GqmlP9T9OnSItOYqoZZA/OnYg5EaVP+c/ha32nUquOk+5riEdhYgINAZDdgJkPc+YSWsrfixOIROf6t34j3eRcyFnOY0MS7tSXi1AEPE0VZdyu+5bfmBSGvLI+WR5lQ+oOQR5hRGwqf990WEdBelNO7dxhZELVhEZ+/L60PpDf5y9A8qmrY6PiEU8nhqIyLpks0BxeIsYyZ3Cr82jjfbItPGyEARsIVzluBSgVMD4oGjojz8T6wGDEXUbmZH/pra94XAs8wZ28mzbmIEppWnhBqX0c6Ov8iXCfFfUZr4KNuPxMEgk4VOYTMOd70GEjGnylivw18hbn7ZpkWh2Zql+uU2HejvOgn+WHvT0HF9NhK0Xb5vH8aFkgBP92PJx8obIbxBVeeExczxvsqWufuPdXS2sPNIgMYZ2TtvqVVsiUToeGB2FQo09fzZOSH5Vi18u/siyvC3FmjeMf4+OYWPuDPNCcrb21NgcHam7XI8AAoGLTGtLLWR+fxynbBBcg2d2ZPcZ5CMFIfOBHRWCkcA0sDT3cl9nsBppHNJze/jVxHy+lREeQEA/etSIgohTGAWW3ZlpibRXqMxlmVWEPifCubbWTzQ6U54mRMpW2UghH3gvCNFNgdday3Av7EtlIJOdUuiMq3zmmd5J4Jt4C82CKnVewA0DtZ4EEtwRG9IYuLrfpuZlnjBAtoeXRV1lqUZ6BcCe0E3m9jO28sGkALuIeJQ74XG7X8lMC7lMWWQYiYD7D6G9QT6X3qV4vXJwNKRKpYc/rI+amEawTSnDYAMEaYPwtZ0rb/7BL+atsZZaI+ncebdfVfjDJZbCZAc+JnkuNlUn2xrNRuObw8h/G0lm2pNOEJ10S8R4+yplo5B1QmC2dCDhaTaW67QJPqnJPKbVmjw7i6KGBi+CDP3JnAKlOX/YUu8uk56JfeQUuGcQg/KaBMGctnchH2n1i3+oGbibhurQj22+OO66Q6mijKoCRP/QJm5WTGcO4c0iVX0B8dIlai/uv/pGU8Wps9MxysH5ViVIj56LWKuNfy1FNO9+wx4iHYnUxfW0Iccq3YJYAlOBkMv8VZIJl9aTEEFJ626LJEw/UbEBdR9CdlwnylPf4RuNW/lcGYMgq3xNVpp/bzYKwZivvQOFgMdX1tv8D33SHvAm3zrRCU+v94BJXnBi9k25fnKsc705IMwv+I6bueXA+mDnagj1oTqsWXZ0WnPAYI3WUO7sBf7XUH230wfYXnTSVhujJxiEP/2oWqi2813gtQkKwz9N2p6yT8Vsh3+BaAwwDvtOZMJKUFcabTBSL6NoouzmzwItJ4mIboT6KXaef66rs8jCU=";
            // Initialize library
            ErrorCode error_code = Library.Initialize(sn, key);
            if (error_code != ErrorCode.e_ErrSuccess)
            {
                Console.WriteLine("Library Initialize Error: {0}\n", error_code);
                return;
            }

            try
            {
                String input_file = input_path + "AboutFoxit.pdf";
                String info_file = output_path + "layout_recognition_info.txt";

                using (PDFDoc doc = new PDFDoc(input_file))
                {
                    error_code = doc.Load(null);
                    if (error_code != ErrorCode.e_ErrSuccess)
                    {
                        Console.WriteLine("The PDFDoc [{0}] Error: {1}\n", input_file, error_code);
                        Library.Release();
                        return;
                    }

                    using (PDFPage original_page = doc.GetPage(0))
                    {
                        original_page.StartParse((int)PDFPage.ParseFlags.e_ParsePageNormal, null, false);
                        using (LRContext context = new LRContext(original_page))
                        {
                            Progressive progressive = context.StartParse(null);

                            LRStructureElement root = context.GetRootElement();
                            LRElement.ElementType type = root.GetElementType();
                            int nElemListSize = root.GetChildCount();
                            Console.WriteLine("RootElement getChildCount: {0}", nElemListSize);
                            OutputAllLRElement(root, info_file);

                            List<LRElement> element_array = new List<LRElement>();
                            GetChildFromElement(root, element_array, LRElement.ElementType.e_ElementTypeSpan);
                            Console.WriteLine("LRElement::e_ElementTypeSpan count: {0}\n", element_array.Count());
                        }
                    }
                    
                    Console.WriteLine("Layout recognition test.");
                }
            }
            catch (foxit.PDFException e)
            {
                Console.WriteLine(e.Message);
            }

            Library.Release();
        }
    }
}
