// =========================================================================== //  ©2013-2026 WebSupergoo. All rights reserved. // //  This source code is for use exclusively with the ABCpdf product with //  which it is distributed, under the terms of the license for that //  product. Details can be found at // //    http://www.websupergoo.com/ // //  This copyright notice must not be deleted and must be reproduced alongside //  any sections of code extracted from this module. // =========================================================================== using System; using System.ComponentModel; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; using System.Runtime.InteropServices; using System.Globalization; using WebSupergoo.ABCpdf14; using WebSupergoo.ABCpdf14.Objects; using WebSupergoo.ABCpdf14.Atoms; using WebSupergoo.ABCpdf14.Elements; namespace WebSupergoo.Annotations {   public class TreeUtilities {     /// <summary>Embeds a file and adds it to the name tree.</summary>     /// <param name="key">A key that uniquely identifies an embedded file in the name tree.</param>     /// <param name="filePath">A file path.</param>     /// <param name="description">A description.</param>     public static FullFileSpecificationElement EmbedFile(string filePath, string description, ObjectSoup soup) {       FileInfo info = new FileInfo(filePath);       EmbeddedFileStreamElement stream = EmbedFileStream(info, soup);       CatalogElement cat = new CatalogElement(soup.Catalog);       FullFileSpecificationElement file = new FullFileSpecificationElement(cat);       file.EntryType = "Filespec";       file.EntryF = info.Name;       file.EntryUF = info.Name;       file.EntryEF = new EmbeddedFileSetElement(file);       file.EntryEF.EntryF = stream;       file.EntryDesc = description;       return file;     }     /// <summary>Embeds a file.</summary>     /// <param name="info">A file info.</param>     /// <returns>The ID of the embedded file stream.</returns>     public static EmbeddedFileStreamElement EmbedFileStream(FileInfo info, ObjectSoup soup) {       string filePath = info.FullName;       StreamObject obj = new StreamObject(soup, filePath);       if (obj.Length > 64)         obj.CompressFlate();       EmbeddedFileStreamElement fs = new EmbeddedFileStreamElement(obj);       fs.EntryType = "EmbeddedFile";       fs.EntrySubtype = GetContentType(filePath);       fs.EntryParams = new EmbeddedFileParameterElement(fs);       fs.EntryParams.EntrySize = (int)info.Length;       fs.EntryParams.EntryModDate = StringAtom.DateToString(info.LastWriteTimeUtc);       fs.EntryParams.EntryCreationDate = StringAtom.DateToString(info.CreationTimeUtc);       return fs;     }     /// <summary>Gets the content type of a file.</summary>     /// <param name="fileName">A file name or path.</param>     /// <returns>The content type.</returns>     protected static string GetContentType(string fileName) {       if (fileName.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))         return "application/pdf";       if (fileName.EndsWith(".swf", StringComparison.OrdinalIgnoreCase))         return "application/x-shockwave-flash";       if (fileName.EndsWith(".wmv", StringComparison.OrdinalIgnoreCase))         return "video/x-ms-wmv";       if (fileName.EndsWith(".mpg", StringComparison.OrdinalIgnoreCase))         return "video/mpeg";       if (fileName.EndsWith(".avi", StringComparison.OrdinalIgnoreCase))         return "video/avi";       return null;     }   }   /// <summary>The name tree for embedded file streams.</summary>   public class EmbeddedFileTree : NameOrNumberTree<string, FileSpecificationElement> {     /// <summary>EmbeddedFileTree constructor.</summary>     /// <param name="doc">A document.</param>     public EmbeddedFileTree(Doc doc) {       CatalogElement cat = new CatalogElement(doc.ObjectSoup.Catalog);       if (cat.EntryNames == null)         cat.EntryNames = new NameDictionaryElement(cat);       if (cat.EntryNames.EntryEmbeddedFiles == null) {         cat.EntryNames.EntryEmbeddedFiles = new NameTreeNodeElement<FileSpecificationElement>(cat);         cat.EntryNames.EntryEmbeddedFiles.EntryNames = new ArrayElement<Element>(cat);       }       Root = cat.EntryNames.EntryEmbeddedFiles;     }     /// <summary>Embeds a file and adds it to the name tree.</summary>     /// <param name="key">A key that uniquely identifies an embedded file in the name tree.</param>     /// <param name="filePath">A file path.</param>     /// <param name="description">A description.</param>     public void EmbedFile(string key, string filePath, string description) {       FullFileSpecificationElement file = TreeUtilities.EmbedFile(filePath, description, Root.Host.Soup);       Add(key, file);     }   }   /// <summary>The name tree for document-level JavaScript actions.</summary>   public class JavaScriptTree : NameOrNumberTree<string, JavaScriptActionElement> {     /// <summary>JavaScriptTree constructor.</summary>     /// <param name="doc">A document.</param>     public JavaScriptTree(Doc doc)  {       CatalogElement cat = new CatalogElement(doc.ObjectSoup.Catalog);       if (cat.EntryNames == null)         cat.EntryNames = new NameDictionaryElement(cat);       if (cat.EntryNames.EntryJavaScript == null) {         cat.EntryNames.EntryJavaScript = new NameTreeNodeElement<JavaScriptActionElement>(cat);         cat.EntryNames.EntryJavaScript.EntryNames = new ArrayElement<Element>(cat);       }       Root = cat.EntryNames.EntryJavaScript;     }     /// <summary>Adds a JavaScript action to the name tree.</summary>     /// <param name="key">A key that uniquely identifies a JavaScript action in the name tree.</param>     /// <param name="script">JavaScript code.</param>     public void AddScript(string key, string script) {       AddScript(key, script, script.Length > 128);     }     /// <summary>Adds a JavaScript action to the name tree.</summary>     /// <param name="key">A key that uniquely identifies a JavaScript action in the name tree.</param>     /// <param name="script">JavaScript code.</param>     /// <param name="useStream">Whether to use a stream (otherwise, a string) to store JavaScript code.</param>     public void AddScript(string key, string script, bool useStream) {       JavaScriptActionElement action = new JavaScriptActionElement(Root);       action.EntryType = "Action";       action.EntryS = "JavaScript";       if (!useStream)         action.EntryJS = new Element(new StringAtom(script), action.Host);       else {         StreamObject stream = new StreamObject(action.Host.Soup);         stream.SetText(script);         if (stream.Length > 64)           stream.CompressFlate();         action.EntryJS = new Element(stream);       }       Add(key, action);     }   } }