// =========================================================================== //  ©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.Collections.Generic; using System.Diagnostics; using System.Windows.Forms; using System.IO; using System.Text; using System.Linq; using WebSupergoo.ABCpdf14; using WebSupergoo.ABCpdf14.Objects; using WebSupergoo.ABCpdf14.Atoms; using WebSupergoo.ABCpdf14.Operations; using WebSupergoo.ABCpdf14.Elements; using WebSupergoo.ABCpdf14.Operations.Accessibility; using WebSupergoo.TableDetection; namespace AccessiblePDF {   /// <summary>   /// Summary description for Form1.   /// </summary>   public class Form1 : System.Windows.Forms.Form {     private Button button1;     private Button button2;     private Button button3;     private Button button4;     private Button button5;     private Button button6;     private Button button7;     private TextBox textBox1;     private TextBox textBox3;     private TextBox textBox2;     private Button button8;     private Button button9;     private Button button10;     private Button button11;     private Button button12;         private Button button13;         /// <summary>         /// Required designer variable.         /// </summary>         private System.ComponentModel.Container components = null;     public Form1() {       //       // Required for Windows Form Designer support       //       InitializeComponent();       //       // TODO: Add any constructor code after InitializeComponent call       //     }     /// <summary>     /// Clean up any resources being used.     /// </summary>     protected override void Dispose(bool disposing) {       if (disposing) {         if (components != null) {           components.Dispose();         }       }       base.Dispose(disposing);     }     #region Windows Form Designer generated code     /// <summary>     /// Required method for Designer support - do not modify     /// the contents of this method with the code editor.     /// </summary>     private void InitializeComponent() {       System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));       this.button1 = new System.Windows.Forms.Button();       this.button3 = new System.Windows.Forms.Button();       this.button4 = new System.Windows.Forms.Button();       this.button5 = new System.Windows.Forms.Button();       this.button6 = new System.Windows.Forms.Button();       this.button7 = new System.Windows.Forms.Button();       this.textBox1 = new System.Windows.Forms.TextBox();       this.textBox3 = new System.Windows.Forms.TextBox();       this.button2 = new System.Windows.Forms.Button();       this.textBox2 = new System.Windows.Forms.TextBox();       this.button8 = new System.Windows.Forms.Button();       this.button9 = new System.Windows.Forms.Button();       this.button10 = new System.Windows.Forms.Button();       this.button11 = new System.Windows.Forms.Button();       this.button12 = new System.Windows.Forms.Button();       this.button13 = new System.Windows.Forms.Button();       this.SuspendLayout();       //       // button1       //       this.button1.Location = new System.Drawing.Point(19, 358);       this.button1.Name = "button1";       this.button1.Size = new System.Drawing.Size(256, 33);       this.button1.TabIndex = 0;       this.button1.Text = "Change Content";       this.button1.Click += new System.EventHandler(this.button1_Click);       //       // button3       //       this.button3.Location = new System.Drawing.Point(301, 120);       this.button3.Name = "button3";       this.button3.Size = new System.Drawing.Size(256, 33);       this.button3.TabIndex = 2;       this.button3.Text = "Detect Tables";       this.button3.Click += new System.EventHandler(this.button3_Click);       //       // button4       //       this.button4.Location = new System.Drawing.Point(19, 120);       this.button4.Name = "button4";       this.button4.Size = new System.Drawing.Size(256, 33);       this.button4.TabIndex = 3;       this.button4.Text = "Detect Headers";       this.button4.Click += new System.EventHandler(this.button4_Click);       //       // button5       //       this.button5.Location = new System.Drawing.Point(301, 162);       this.button5.Name = "button5";       this.button5.Size = new System.Drawing.Size(256, 34);       this.button5.TabIndex = 4;       this.button5.Text = "Detect Lists";       this.button5.Click += new System.EventHandler(this.button5_Click);       //       // button6       //       this.button6.Location = new System.Drawing.Point(19, 162);       this.button6.Name = "button6";       this.button6.Size = new System.Drawing.Size(256, 34);       this.button6.TabIndex = 5;       this.button6.Text = "Detect Sections";       this.button6.Click += new System.EventHandler(this.button6_Click);       //       // button7       //       this.button7.Location = new System.Drawing.Point(584, 120);       this.button7.Name = "button7";       this.button7.Size = new System.Drawing.Size(256, 33);       this.button7.TabIndex = 6;       this.button7.Text = "Detect Artifacts";       this.button7.Click += new System.EventHandler(this.button7_Click);       //       // textBox1       //       this.textBox1.Location = new System.Drawing.Point(17, 244);       this.textBox1.Multiline = true;       this.textBox1.Name = "textBox1";       this.textBox1.ReadOnly = true;       this.textBox1.Size = new System.Drawing.Size(823, 88);       this.textBox1.TabIndex = 7;       this.textBox1.Text = resources.GetString("textBox1.Text");       //       // textBox3       //       this.textBox3.Location = new System.Drawing.Point(19, 30);       this.textBox3.Multiline = true;       this.textBox3.Name = "textBox3";       this.textBox3.ReadOnly = true;       this.textBox3.Size = new System.Drawing.Size(821, 68);       this.textBox3.TabIndex = 9;       this.textBox3.Text = resources.GetString("textBox3.Text");       //       // button2       //       this.button2.Location = new System.Drawing.Point(19, 575);       this.button2.Name = "button2";       this.button2.Size = new System.Drawing.Size(256, 33);       this.button2.TabIndex = 10;       this.button2.Text = "Extract Structure";       this.button2.Click += new System.EventHandler(this.button2_Click);       //       // textBox2       //       this.textBox2.Location = new System.Drawing.Point(19, 481);       this.textBox2.Multiline = true;       this.textBox2.Name = "textBox2";       this.textBox2.ReadOnly = true;       this.textBox2.Size = new System.Drawing.Size(823, 67);       this.textBox2.TabIndex = 11;       this.textBox2.Text = resources.GetString("textBox2.Text");       //       // button8       //       this.button8.Location = new System.Drawing.Point(586, 575);       this.button8.Name = "button8";       this.button8.Size = new System.Drawing.Size(256, 33);       this.button8.TabIndex = 12;       this.button8.Text = "Markup Structure";       this.button8.Click += new System.EventHandler(this.button8_Click);       //       // button9       //       this.button9.Location = new System.Drawing.Point(584, 358);       this.button9.Name = "button9";       this.button9.Size = new System.Drawing.Size(256, 33);       this.button9.TabIndex = 13;       this.button9.Text = "Add Tagged Signature";       this.button9.Click += new System.EventHandler(this.button9_Click);       //       // button10       //       this.button10.Location = new System.Drawing.Point(584, 162);       this.button10.Name = "button10";       this.button10.Size = new System.Drawing.Size(256, 34);       this.button10.TabIndex = 14;       this.button10.Text = "Add Custom Tags";       this.button10.Click += new System.EventHandler(this.button10_Click);       //       // button11       //       this.button11.Location = new System.Drawing.Point(301, 358);       this.button11.Name = "button11";       this.button11.Size = new System.Drawing.Size(256, 33);       this.button11.TabIndex = 15;       this.button11.Text = "Remove Content";       this.button11.Click += new System.EventHandler(this.button11_Click);       //       // button12       //       this.button12.Location = new System.Drawing.Point(19, 404);       this.button12.Name = "button12";       this.button12.Size = new System.Drawing.Size(256, 33);       this.button12.TabIndex = 16;       this.button12.Text = "Add Tagged Content 1";       this.button12.Click += new System.EventHandler(this.button12_Click);       //       // button13       //       this.button13.Location = new System.Drawing.Point(301, 404);       this.button13.Name = "button13";       this.button13.Size = new System.Drawing.Size(256, 33);       this.button13.TabIndex = 17;       this.button13.Text = "Add Tagged Content 2";       this.button13.Click += new System.EventHandler(this.button13_Click);       //       // Form1       //       this.AutoScaleBaseSize = new System.Drawing.Size(8, 19);       this.ClientSize = new System.Drawing.Size(881, 626);       this.Controls.Add(this.button13);       this.Controls.Add(this.button12);       this.Controls.Add(this.button11);       this.Controls.Add(this.button10);       this.Controls.Add(this.button9);       this.Controls.Add(this.button8);       this.Controls.Add(this.textBox2);       this.Controls.Add(this.button2);       this.Controls.Add(this.textBox3);       this.Controls.Add(this.textBox1);       this.Controls.Add(this.button7);       this.Controls.Add(this.button6);       this.Controls.Add(this.button5);       this.Controls.Add(this.button4);       this.Controls.Add(this.button3);       this.Controls.Add(this.button1);       this.Name = "Form1";       this.Text = "AccessiblePDF";       this.ResumeLayout(false);       this.PerformLayout();     }     #endregion     /// <summary>     /// The main entry point for the application.     /// </summary>     [STAThread]     static void Main() {       Application.Run(new Form1());     }     private void button1_Click(object sender, System.EventArgs e) {       string theBase = Directory.GetCurrentDirectory();       string theRez = Directory.GetParent(theBase).Parent.FullName + "\\";       ChangeContent(Path.Combine(theRez, "_Flowers.pdf"), Path.Combine(theRez, "_Squirrel.png"), Path.Combine(theRez, "6-replace_content.pdf"));     }     private static void ChangeContent(string src, string img, string dst) {       using (Doc doc = new Doc()) {         doc.Read(src);         Structure tags = new Structure(doc);         List<StructureElementElement> images = tags.FindElementsByType("CustomImageTag");         if (images.Count > 0) {           using (XImage image = XImage.FromFile(img, null)) {             PixMap pm = PixMap.FromXImage(doc.ObjectSoup, image);             tags.ReplaceImages(images, pm);           }         }         List<StructureElementElement> texts = tags.FindElementsByType("CustomTextTag");         tags.ReplaceText(texts, "Replaced Text!");         doc.Save(dst);       }     }     private void button11_Click(object sender, EventArgs e) {       string theBase = Directory.GetCurrentDirectory();       string theRez = Directory.GetParent(theBase).Parent.FullName + "\\";       AccessibilityWithTables(Path.Combine(theRez, "_Romans.pdf"), Path.Combine(theRez, "9-subject_matter.pdf"));       PurgeUnreferencedTags(Path.Combine(theRez, "9-subject_matter.pdf"), Path.Combine(theRez, "9-purge_tags_before.pdf"), Path.Combine(theRez, "9-purge_tags_after.pdf"));       File.Delete(Path.Combine(theRez, "9-subject_matter.pdf"));     }     private static void PurgeUnreferencedTags(string src, string before, string after) {       var r = new Random(5); // arbitrary seed set to ensure same output each time       using (Doc doc = new Doc()) {         doc.Read(src);         foreach (var page in doc.ObjectSoup.Catalog.Pages.GetPageArrayAll()) {           string txt = ASCIIEncoding.ASCII.GetString(page.GetContentData());           string[] lines = txt.Split(new string[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);           var sb = new StringBuilder();           bool skipping = false;           foreach (string line in lines) {             if (line.Contains("/Span << /MCID") && (r.Next(5) == 0))               skipping = true;             else if (skipping && line.Contains("EMC"))               skipping = false;             else if (!skipping)               sb.AppendLine(line);           }           var l = page.GetLayers()[0];           l.SetData(Encoding.ASCII.GetBytes(sb.ToString()), true);         }         doc.Save(before);         Structure tags = new Structure(doc);         tags.Purge(new string[] { "P" });         tags.UpdateActualText(true, true);         doc.Save(after);         //MarkupStructure(before, before.Replace(".pdf", "_Markup.pdf"));         //MarkupStructure(after, after.Replace(".pdf", "_Markup.pdf"));       }     }     private void button2_Click(object sender, EventArgs e) {       string theBase = Directory.GetCurrentDirectory();       string theRez = Directory.GetParent(theBase).Parent.FullName + "\\";       foreach (string file in Directory.GetFiles(theRez, "*.pdf")) {         if (Path.GetFileName(file).StartsWith("_"))           continue;         // the content we extract isn't really HTML but it is similar enough that a browser will often display it         ExtractStructure(file, Path.Combine(theRez, Path.GetFileNameWithoutExtension(file) + ".htm"));       }     }     private static void ExtractStructure(string src, string dst) {       using (Doc doc = new Doc()) {         doc.Read(src);         Structure tags = new Structure(doc);         StringBuilder sb = tags.ExtractStructure();         File.WriteAllText(dst, sb.ToString());       }     }     private void button3_Click(object sender, EventArgs e) {       string theBase = Directory.GetCurrentDirectory();       string theRez = Directory.GetParent(theBase).Parent.FullName + "\\";       AccessibilityWithTables(Path.Combine(theRez, "_Romans.pdf"), Path.Combine(theRez, "3-detect_tables.pdf"));     }     private static void AccessibilityWithTables(string src, string dst) {       using (Doc doc = new Doc()) {         doc.Read(src);         AccessibilityOperation op = new AccessibilityOperation(doc);         op.PageContents.AddPages();         op.MakeAccessible();         doc.Save(dst);         Structure tags = new Structure(doc);         // reorder elements so that they go from top to bottom         StructureElementElement document = (StructureElementElement)tags.Root.EntryK[0];         KidArranger.SortTopToBottom(tags, document, null); // can pass KidArranger.GetPageLayout(doc.MediaBox, 72.0, 72.0, 2) for columns         // convert paragraphs to tables where appropriate         List<StructureElementElement> elements = tags.FindElementsByType("P");         Tables tables = new Tables();         tables.FontSensitive = false;         tables.Add(tags, elements);         foreach (Table table in tables.Items) {           if (table.Items.Count <= 1)             continue; // too small           int index = 0;           StructureElementElement parent = null;           StructureElementElement tb = new StructureElementElement(document);           tb.EntryType = "StructElem";           tb.EntryS = "Table";           foreach (Row row in table.Items) {             StructureElementElement tr = new StructureElementElement(document);             tr.EntryType = "StructElem";             tr.EntryS = "TR";             tr.SetParent(tb);             foreach (StructureElementElement item in row.Items) {               if ((parent == null) && (item != null)) {                 Debug.Assert(item.EntryP is StructureTreeRootElement == false);                 parent = item.EntryP as StructureElementElement;                 index = parent.EntryK.IndexOf(item);               }               StructureElementElement td = new StructureElementElement(document);               td.EntryType = "StructElem";               td.EntryS = "TD";               td.SetParent(tr);               if (item != null)                 item.SetParent(td);             }           }           tb.SetParent(parent, index);         }         doc.Save(dst);       }     }     private void button4_Click(object sender, EventArgs e) {       string theBase = Directory.GetCurrentDirectory();       string theRez = Directory.GetParent(theBase).Parent.FullName + "\\";       AccessibilityWithHeaders(Path.Combine(theRez, "_Romans.pdf"), Path.Combine(theRez, "1-detect_headers.pdf"));     }     private static void AccessibilityWithHeaders(string src, string dst) {       using (Doc doc = new Doc()) {         doc.Read(src);         AccessibilityOperation op = new AccessibilityOperation(doc);         op.PageContents.AddPages();         op.MakeAccessible();         doc.Save(dst);         Structure tags = new Structure(doc);         // reorder elements so that they go from top to bottom         StructureElementElement document = (StructureElementElement)tags.Root.EntryK[0];         KidArranger.SortTopToBottom(tags, document, null);         // determine what styles are used for headers and change tag type appropriately         List<StructureElementElement> elements = tags.FindElementsByType("P");         Dictionary<string, List<StructureElementElement>> styles = new Dictionary<string, List<StructureElementElement>>();         foreach (StructureElementElement element in elements) {           string weight = null;           if (!element.GetStyle().TryGetValue("font-weight", out weight))             continue; // we are only interested in bold fonts           int value = 0;           if ((!int.TryParse(weight, out value)) || (value < 700))             continue;           string size = null;           if (!element.GetStyle().TryGetValue("font-size", out size))             continue;           List<StructureElementElement> matches = null;           if (!styles.TryGetValue(size, out matches)) {             matches = new List<StructureElementElement>();             styles[size] = matches;           }           matches.Add(element);         }         List<Style> headers = new List<Style>();         foreach (KeyValuePair<string, List<StructureElementElement>> pair in styles)           headers.Add(new Style(pair.Key, pair.Value));         headers.Sort((i1, i2) => i1.Elements.Count.CompareTo(i2.Elements.Count));         for (int i = 0; i < headers.Count; i++) {           if (i > 4)             break; // limit the number of header levels           string tag = "H" + (i + 1).ToString();           foreach (StructureElementElement element in headers[i].Elements) {             element.EntryS = tag;           }         }         doc.Save(dst);       }     }     private void button5_Click(object sender, EventArgs e) {       string theBase = Directory.GetCurrentDirectory();       string theRez = Directory.GetParent(theBase).Parent.FullName + "\\";       AccessibilityWithLists(Path.Combine(theRez, "_Romans.pdf"), Path.Combine(theRez, "4-detect_lists.pdf"));     }     private static void AccessibilityWithLists(string src, string dst) {       using (Doc doc = new Doc()) {         doc.Read(src);         AccessibilityOperation op = new AccessibilityOperation(doc);         op.PageContents.AddPages();         op.MakeAccessible();         doc.Save(dst);         Structure tags = new Structure(doc);         // reorder elements so that they go from top to bottom         StructureElementElement document = (StructureElementElement)tags.Root.EntryK[0];         KidArranger.SortTopToBottom(tags, document, null);         // detect and insert list and list item structure         List<StructureElementElement> elements = tags.FindElementsByType("P");         StructureElementElement list = null;         foreach (StructureElementElement element in elements) {           bool isListItem = element.EntryActualText.TrimStart().StartsWith("\u2022");           if ((isListItem) && (list == null)) {             list = new StructureElementElement(document);             list.EntryType = "StructElem";             list.EntryS = "L";             Debug.Assert(element.EntryP is StructureTreeRootElement == false);             StructureElementElement parent = element.EntryP as StructureElementElement;             Debug.Assert(parent != null);             int index = parent.EntryK.IndexOf(element);             list.SetParent(parent, index);           }           if (isListItem) {             element.SetParent(list, int.MaxValue);             element.EntryS = "LI";           }           if ((!isListItem) && (list != null))             list = null;         }         doc.Save(dst);       }     }     private void button10_Click(object sender, EventArgs e) {       string theBase = Directory.GetCurrentDirectory();       string theRez = Directory.GetParent(theBase).Parent.FullName + "\\";       AccessibilityWithCustomTags(Path.Combine(theRez, "8-detect_custom_tags.pdf"));     }     private static void AccessibilityWithCustomTags(string dst) {       using (Doc doc = new Doc()) {         // add some content and some custom tags         if (true) {           doc.Rect.Inset(50, 50);           doc.Page = doc.AddPage();           Page page = (Page)doc.ObjectSoup[doc.Page];           page.AddLayer(new StreamObject(doc.ObjectSoup, Encoding.ASCII.GetBytes("BX\r\n")));           doc.AddTextStyled("<p tag-start=\"/H1 TAG\"><stylerun fontsize='72'><b>De Bello Gallico</b></stylerun></p>");           doc.AddTextStyled("<p tag-start=\"/H2 TAG\"><stylerun fontsize='36'>Julius Caesar</stylerun></p>");           doc.AddTextStyled("<p><stylerun fontsize='18'>&nbsp;</stylerun></p>");           doc.AddTextStyled("<p tag-start=\"/P TAG\"><stylerun fontsize='18'><b>Gallia</b> est omnis divisa in partes tres, quarum unam incolunt <b>Belgae</b>, aliam <b>Aquitani</b>, tertiam qui ipsorum lingua <b>Celtae</b>, nostra <b>Galli</b> appellantur.</stylerun></p>");           page.AddLayer(new StreamObject(doc.ObjectSoup, Encoding.ASCII.GetBytes("EX\r\n")));         }         // make accessible         AccessibilityOperation op = new AccessibilityOperation(doc);         op.PageContents.AddPages();         op.MakeAccessible();         // work out how our custom tags match up with the standard PDF tags         Dictionary<int, Dictionary<int, string>> allCustomTags = new Dictionary<int, Dictionary<int, string>>();         for (int i = 0; i < doc.PageCount; i++) {           // get page contents           doc.PageNumber = i + 1;           Dictionary<int, string> customTags = new Dictionary<int, string>();           allCustomTags[doc.Page] = customTags;           List<int> deletion = new List<int>();           Page page = (Page)doc.ObjectSoup[doc.Page];           ArrayAtom array = ArrayAtom.FromContentStream(page.GetContentData());           var items = OpAtom.Find(array, new string[] { "BDC", "TAG" });           string style = null;           // scan for tags           foreach (var pair in items) {             if (pair.Item1 == "BDC") {               NumAtom mcid = Atom.GetItem(array[pair.Item2 - 1], "MCID") as NumAtom;               if (mcid != null)                 customTags[mcid.Num] = style;             }             else {               style = Atom.GetName(array[pair.Item2 - 1]);               deletion.Add(pair.Item2 - 1);               deletion.Add(pair.Item2);             }           }           // remove the custom tags we added           for (int j = deletion.Count - 1; j >= 0; j--)             array.RemoveAt(deletion[j]);           byte[] arrayData = array.GetData();           StreamObject so = new StreamObject(doc.ObjectSoup);           so.SetData(arrayData, 1, arrayData.Length - 2);           so.CompressFlate();           doc.SetInfo(page.ID, "/Contents:Del", "");           page.AddLayer(so);         }         // determine what custom styles are used and change tag type appropriately         Structure tags = new Structure(doc);         List<StructureElementElement> elements = tags.FindElementsByType("P");         foreach (StructureElementElement element in elements) {           var page = element.EntryPg;           if (page == null)             continue; // this should never happen           Dictionary<Page, HashSet<int>> mcidMap = new Dictionary<Page, HashSet<int>>();           element.FindMcids(mcidMap);           if (mcidMap.Count != 1)             continue; // paragraph spread across more than one page           HashSet<int> mcids = null;           mcidMap.TryGetValue(page.Object as Page, out mcids);           if (mcids == null)             continue;           // match mcids with custom tags           Dictionary<int, string> customTags = allCustomTags[page.Object.ID];           Dictionary<string, int> styles = new Dictionary<string, int>();           foreach (int mcid in mcids) {             string style = null;             customTags.TryGetValue(mcid, out style);             if (!string.IsNullOrWhiteSpace(style)) {               int count = 0;               styles.TryGetValue(style, out count);               styles[style] = count + 1;             }           }           // if verdict is unanimous assign new style           if (styles.Count == 1)             element.EntryS = (new List<string>(styles.Keys))[0];         }         doc.Save(dst);       }     }     private void button6_Click(object sender, EventArgs e) {       string theBase = Directory.GetCurrentDirectory();       string theRez = Directory.GetParent(theBase).Parent.FullName + "\\";       AccessibilityWithSections(Path.Combine(theRez, "_Romans.pdf"), Path.Combine(theRez, "2-detect_sections.pdf"));     }     private static void AccessibilityWithSections(string src, string dst) {       using (Doc doc = new Doc()) {         doc.Read(src);         AccessibilityOperation op = new AccessibilityOperation(doc);         op.PageContents.AddPages();         op.MakeAccessible();         doc.Save(dst);         Structure tags = new Structure(doc);         // reorder elements so that they go from top to bottom         StructureElementElement document = (StructureElementElement)tags.Root.EntryK[0];         KidArranger.SortTopToBottom(tags, document, null);         // define structure hierarchy template         List<Heading> template = new List<Heading>();         template.Add(new Heading("Title ", "Part", 1));         template.Add(new Heading("Subheader ", "Sect", 2));         // detect and insert section item structure         List<StructureElementElement> elements = tags.FindElementsByType("P");         Stack<StructureElementElement> hierarchy = new Stack<StructureElementElement>();         foreach (StructureElementElement element in elements) {           string text = element.EntryActualText.TrimStart();           foreach (Heading item in template) {             if (text.StartsWith(item.Match)) {               while (hierarchy.Count >= item.Level)                 hierarchy.Pop();               if (hierarchy.Count == item.Level - 1) {                 StructureElementElement heading = new StructureElementElement(document);                 heading.EntryType = "StructElem";                 heading.EntryS = item.Type;                 StructureElementElement parent = (StructureElementElement)element.EntryP;                 heading.SetParent(hierarchy.Count > 0 ? hierarchy.Peek() : parent, Int32.MaxValue);                 hierarchy.Push(heading);               }               break;             }           }           if (hierarchy.Count > 0)             element.SetParent(hierarchy.Peek(), Int32.MaxValue);         }         doc.Save(dst);       }     }     private void button7_Click(object sender, EventArgs e) {       string theBase = Directory.GetCurrentDirectory();       string theRez = Directory.GetParent(theBase).Parent.FullName + "\\";       AccessibilityWithArtifacts(Path.Combine(theRez, "_Romans.pdf"), Path.Combine(theRez, "5-detect_artifacts.pdf"), false);     }     private static void AccessibilityWithArtifacts(string src, string dst, bool frame) {       using (Doc doc = new Doc()) {         doc.Read(src);         // define header and footer areas         Dictionary<int, XRect> headers = new Dictionary<int, XRect>();         Dictionary<int, XRect> footers = new Dictionary<int, XRect>();         for (int i = 0; i < doc.PageCount; i++) {           doc.PageNumber = i + 1;           XRect header = doc.CropBox.Clone();           header.Pin = XRect.Corner.TopLeft;           header.Height = 2.0 * 72;           headers[doc.Page] = header;           XRect footer = doc.CropBox.Clone();           footer.Pin = XRect.Corner.BottomLeft;           footer.Height = 2.0 * 72;           footers[doc.Page] = footer;           if (frame) { // mark red so we can see where they are             doc.Color.SetRgb(255, 0, 0, 40);             doc.Rect.String = header.ToString();             doc.FillRect();             doc.Rect.String = footer.ToString();             doc.FillRect();           }         }         // make accessible         AccessibilityOperation op = new AccessibilityOperation(doc);         op.PageContents.AddPages();         op.MakeAccessible();         doc.Save(dst);         Structure tags = new Structure(doc);         // reorder elements so that they go from top to bottom         StructureElementElement document = (StructureElementElement)tags.Root.EntryK[0];         KidArranger.SortTopToBottom(tags, document, null);         Dictionary<Page, HashSet<int>> artifacts = new Dictionary<Page, HashSet<int>>();         // find header elements and remove from structure         List<StructureElementElement> elements = tags.FindElementsByType("P");         foreach (StructureElementElement element in elements) {           var page = element.EntryPg;           if ((page != null) && (headers[page.Object.ID].Contains(element.GetBBox())) || (footers[page.Object.ID].Contains(element.GetBBox()))) {             Debug.Assert(element.Object is StreamObject == false, "This should never happen using ABCpdf. We should only ever see Pages.");             element.FindMcids(artifacts);             element.SetParent(null, int.MaxValue);           }         }         // turn header elements into artifacts         foreach (KeyValuePair<Page, HashSet<int>> pair in artifacts) {           Page page = pair.Key;           page.Flatten(true, false);           HashSet<int> mcids = pair.Value;           foreach (StreamObject layer in page.GetLayers()) {             if (!layer.Decompress())               throw new Exception("Unable to decompress stream.");             bool changed = false;             ArrayAtom array = ArrayAtom.FromContentStream(layer.GetData());             // eg "/Span << /MCID 11 >> BDC" becomes "/Artifact << /Type /Pagination /Subtype /Header>> BDC"             var items = OpAtom.Find(array, new string[] { "BDC" });             foreach (var pair2 in items) {               Atom[] args = OpAtom.GetParameters(array, pair2.Item2);               DictAtom pars = (DictAtom)args[1];               NumAtom mcid = (NumAtom)pars["MCID"];               if ((mcid != null) && (mcids.Contains(mcid.Num))) {                 NameAtom name = (NameAtom)args[0];                 name.Text = "Artifact";                 pars.Clear();                 pars["Type"] = new NameAtom("Pagination");                 pars["Subtype"] = new NameAtom("Header");                 changed = true;               }             }             if (changed) {               byte[] arrayData = array.GetData();               layer.SetData(arrayData, 1, arrayData.Length - 2);             }             layer.CompressFlate();           }         }         doc.Save(dst);       }     }     private void button8_Click(object sender, EventArgs e) {       string theBase = Directory.GetCurrentDirectory();       string theRez = Directory.GetParent(theBase).Parent.FullName + "\\";       foreach (string file in Directory.GetFiles(theRez, "*.pdf")) {         if (Path.GetFileName(file).StartsWith("_"))           continue;         MarkupStructure(file, Path.Combine(theRez, "_" + Path.GetFileNameWithoutExtension(file) + "_Markup.pdf"));       }     }     private static void MarkupStructure(string src, string dst) {       using (Doc doc = new Doc()) {         doc.Read(src);         Structure tags = new Structure(doc);         tags.MarkupStructure();         doc.Save(dst);       }     }     private void button9_Click(object sender, EventArgs e) {       string theBase = Directory.GetCurrentDirectory();       string theRez = Directory.GetParent(theBase).Parent.FullName + "\\";       AddTaggedSignature(Path.Combine(theRez, "_Flowers.pdf"), Path.Combine(theRez, "7-tagged-signature.pdf"));     }     private static void AddTaggedSignature(string src, string dst) {       using (Doc doc = new Doc()) {         doc.Read(src);         // find place in structure         Structure tags = new Structure(doc);         List<StructureElementElement> elements = tags.FindElementsByType("Span");         Debug.Assert(elements.Count == 1);         StructureElementElement signatureTag = elements[0];         doc.Page = signatureTag.GetPage(tags).ID;         // create signature and add to page         // we use a restricted set of options because it seems that eSignLive may not         // always understand all possible options 9th April 2018         CatalogElement catalog = new CatalogElement(doc.ObjectSoup.Catalog);         PageObjectElement page = new PageObjectElement(doc.ObjectSoup[doc.Page]);         int id = doc.AddObject("<</FT /Sig /Subtype /Widget /Type /Annot >>");         AnnotationElement sigAnnot = new AnnotationElement(doc.ObjectSoup[id]);         sigAnnot.EntryP = page;         XRect sigRect = XRect.FromLbwh(150, 600, 300, 100);         sigAnnot.EntryRect = new RectangleElement(ArrayAtom.FromXRect(sigRect), sigAnnot.Host);         SignatureFieldElement sigField = new SignatureFieldElement(doc.ObjectSoup[id]);         sigField.EntryT = "Signature1"; // name of signature field         sigField.EntryTU = "My Signature"; // display name of signature field         if (page.EntryAnnots == null)           page.EntryAnnots = new ArrayElement<AnnotationElement>(page);         page.EntryAnnots.Add(sigAnnot);         if (catalog.EntryAcroForm == null)           catalog.EntryAcroForm = new InteractiveFormElement(catalog);         if (catalog.EntryAcroForm.EntryFields == null)           catalog.EntryAcroForm.EntryFields = new ArrayElement<FieldElement>(catalog);         catalog.EntryAcroForm.EntrySigFlags = 1;         catalog.EntryAcroForm.EntryFields.Add(sigField);         // link into accessibility structure         StructureElementElement form = new StructureElementElement(page);         form.EntryType = "StructElem";         form.EntryT = "";         form.EntryS = "Form";         form.EntryP = signatureTag;         form.EntryK = new ArrayElement<Element>(form);         signatureTag.EntryK.Add(form);         ObjectReferenceElement objr = new ObjectReferenceElement(page);         objr.EntryType = "OBJR";         objr.EntryObj = sigAnnot;         objr.EntryPg = page;         form.EntryK.Add(objr);         tags.ParentTree.AddStructParent(sigAnnot.Object, form.Object);         doc.Save(dst);       }     }     private void button12_Click(object sender, EventArgs e) {       string theBase = Directory.GetCurrentDirectory();       string theRez = Directory.GetParent(theBase).Parent.FullName + "\\";       AddTaggedContent1(Path.Combine(theRez, "A-add-tagged-content.pdf"));     }     private static void AddTaggedContent1(string dst) {       using (var doc = new Doc()) {         doc.Font = doc.EmbedFont("Arial");         doc.TextStyle.Size = 20;         doc.Rect.Inset(50, 50);         var sb = new StringBuilder();         sb.AppendLine("<ul>");         foreach (var color in Enum.GetNames(typeof(System.Drawing.KnownColor)))           sb.AppendLine($"<li>{color}</li>");         sb.AppendLine("</ul>");         doc.TextStyle.AutoTag = true;         doc.Tag.Open("P"); // put our content inside a paragraph         int id = doc.AddTextStyled(sb.ToString());         doc.FrameRect();         while (true) {           var tl = (TextLayer)doc.ObjectSoup[id];           if (!tl.Truncated)             break;           doc.Page = doc.AddPage();           id = doc.AddTextStyled("", id);           doc.FrameRect();         }         doc.Tag.Close("P");         doc.Save(dst);       }     }     private void button13_Click(object sender, EventArgs e) {       string theBase = Directory.GetCurrentDirectory();       string theRez = Directory.GetParent(theBase).Parent.FullName + "\\";       AddTaggedContent2(Path.Combine(theRez, "_Flowers.pdf"), Path.Combine(theRez, "B-add-tagged-content.pdf"));     }     private static void AddTaggedContent2(string src, string dst) {       using (var doc = new Doc()) {         bool doRead = true, addToExistingPage = true, addToNewPage = true, addStampToExistingPage = true;         if (doRead)           doc.Read(src);         Structure structure = new Structure(doc);         structure.CreateAsRequired();         if (string.IsNullOrEmpty(structure.Title))           structure.Title = "Tagged Document"; // PDF/UA requires a title         if (structure.Root.EntryK == null || structure.Root.EntryK.Count == 0) {           var d = structure.Root.AddKid("Document");           d.EntryLang = "en-US";           addToExistingPage = false;         }         doc.Font = doc.EmbedFont("Arial", LanguageType.Unicode, false, true, false);         doc.TextStyle.Size = 24;         if (addToExistingPage) {           doc.Rect.SetRect(0, 0, doc.MediaBox.Width, 150);           doc.TextStyle.HPos = 0.5;           doc.Tag.Open("P", "Span");           doc.AddText("Some new additional content for the page.\r\n");           doc.Tag.CloseOpen("Span");           doc.AddText("Continued here as we have more content.");           doc.Tag.Close("Span", "P");         }         if (addToNewPage) {           doc.Page = doc.AddPage();           doc.Rect.String = doc.MediaBox.String;           doc.Rect.Inset(72, 72);           doc.TextStyle.HPos = 0;           doc.Tag.Open("Sect", "Div", "P");           doc.AddText("Div one paragraph one.\r\n");           doc.Tag.CloseOpen("P");           doc.AddText("Div one paragraph two.\r\n");           doc.Tag.Close("P", "Div");           doc.Tag.Open("Div", "P");           doc.AddText("Div two paragraph one.\r\n");           doc.Tag.CloseOpen("P");           doc.AddText("Div two paragraph two.\r\n");           doc.Tag.Close("P", "Div", "Sect");         }         if (addStampToExistingPage) {           doc.TextStyle.HPos = 0;           if (doc.Page == 0)             doc.Page = doc.AddPage();           doc.Tag.Roles["RubberStamp"] = "Annot";           doc.Tag.Classes["RedBorder"] = Atom.FromString("<< /O /Layout /BorderColor [1 0 0] /BorderStyle /Solid >>");           var graphic = doc.Tag.MakeTag("RubberStamp");           graphic.Attributes = new DictAtom();           graphic.Attributes["Alt"] = new StringAtom("Classification: Secret");           graphic.Attributes["C"] = new NameAtom("RedBorder");           graphic.Object = new StampAnnotation(doc, XRect.FromLbwh(200, 400, 100, 40), "SECRET", XColor.FromRgb(255, 0, 0));           doc.Tag.OpenClose(graphic);         }         doc.Save(dst);       }     }   }   class Heading {     public string Match;     public string Type;     public int Level;     public Heading(string match, string type, int level) { Match = match; Type = type; Level = level; }   }   class Style {     public string Size;     public List<StructureElementElement> Elements;     public Style(string size, List<StructureElementElement> elements) { Size = size; Elements = elements; }   }; }