From 38117cc055b41a29ca6d5a43b21b438bd5666114 Mon Sep 17 00:00:00 2001 From: Joseph Argento Date: Mon, 6 Oct 2025 12:08:31 -0500 Subject: [PATCH 1/2] Sample update and enhancement. Customer was asking for an example of creating nested Layers and I noticed the existing sample with some modifications could be used. I think the enhancements are straight-forward enough that they lend themselves to a more useful sample in general. Also some simplifications of sample as well. --- .../CreateLayer/CreateLayer.cs | 162 ++++++++++++------ 1 file changed, 109 insertions(+), 53 deletions(-) diff --git a/ContentModification/CreateLayer/CreateLayer.cs b/ContentModification/CreateLayer/CreateLayer.cs index 289f1b4..963eebb 100644 --- a/ContentModification/CreateLayer/CreateLayer.cs +++ b/ContentModification/CreateLayer/CreateLayer.cs @@ -1,18 +1,15 @@ +using Datalogics.PDFL; using System; using System.Collections.Generic; -using System.Text; -using Datalogics.PDFL; /* - * This sample adds an Optional Content Group (a layer) to a PDF document and - * then adds an image to that layer. + * This sample adds Optional Content Groups (layers) to a PDF document and + * then adds Content to those layers. * * The related ChangeLayerConfiguration program makes layers visible or invisible. * - * You can toggle back and forth to make the layer (the duck image) visible or invisible - * in the PDF file. Open the output PDF document in a PDF Viewer, click View and select - * Show/Hide. Select Navigation Panes and Layers to display the layers in the PDF file. - * Click on the box next to the name of the layer. + * You can toggle back and forth to make a layer visible or invisible + * in a PDF Viewer. * */ namespace CreateLayer @@ -38,60 +35,120 @@ static void Main(string[] args) Console.WriteLine("Input file: " + sInput + ", writing to " + sOutput); - Document doc = new Document(sInput); - - Console.WriteLine("Opened a document."); - - Page pg = doc.GetPage(0); - Image img = (pg.Content.GetElement(0) as Image); - - // Containers, Forms and Annotations can be attached to an - // OptionalContentGroup; other content (like Image) can - // be made optional by placing it inside a Container - Container container = new Container(); - container.Content = new Content(); - container.Content.AddElement(img); + using (Document doc = new Document(sInput)) + { + Console.WriteLine("Opened a document."); + + using (Page pg = doc.GetPage(0)) + { + Image image = (Image)pg.Content.GetElement(0); + image.Matrix = new Matrix(image.Matrix.A * .5, 0, 0, image.Matrix.D * .5, image.Matrix.H, image.Matrix.V); + + Image image2 = new Image(Library.ResourceDirectory + "Sample_Input/image.png"); + + Text text = new Text(); + Matrix matrix = new Matrix(); + Font font = new Font("Helvetica"); + GraphicState graphicState = new GraphicState(); + TextState textState = new TextState(); + + matrix.A = 42; + matrix.D = 22; + matrix.H = 72; + matrix.V = 72; + + TextRun textRun = new TextRun("sample text", font, graphicState, textState, matrix); + text.AddRun(textRun); + + Text text2 = new Text(); + + matrix.A = 30; + matrix.D = 30; + matrix.H = 72; + matrix.V = 288; + + TextRun textRun2 = new TextRun("Text definition provided here", font, graphicState, textState, matrix); + text2.AddRun(textRun2); + + // Containers, Forms and Annotations can be attached to an + // OptionalContentGroup; other content (like Image) can + // be made optional by placing it inside a Container + Container imageContainer = new Container(); + imageContainer.Content = new Content(); + imageContainer.Content.AddElement(image); + + Container imageContainer2 = new Container(); + imageContainer2.Content = new Content(); + imageContainer2.Content.AddElement(image2); + + Container textContainer = new Container(); + textContainer.Content = new Content(); + textContainer.Content.AddElement(text); + + Container textContainer2 = new Container(); + textContainer2.Content = new Content(); + textContainer2.Content.AddElement(text2); + + using (Document newDoc = new Document()) + { + using (Page newPage = newDoc.CreatePage(Document.BeforeFirstPage, pg.MediaBox)) + { + newPage.Content.AddElement(imageContainer); + newPage.Content.AddElement(imageContainer2); + newPage.Content.AddElement(textContainer); + newPage.Content.AddElement(textContainer2); + + // We create new OptionalContentGroups and place them in the OptionalContentConfig.Order array + List ocgs = CreateNewOptionalContentGroups(newDoc, new List { "Rubber Ducky", "PNG Logo", "Example Text", "Text Definition" }); + + AssociateOCGWithContainer(newDoc, ocgs[0], imageContainer); + AssociateOCGWithContainer(newDoc, ocgs[1], imageContainer2); + AssociateOCGWithContainer(newDoc, ocgs[2], textContainer); + AssociateOCGWithContainer(newDoc, ocgs[3], textContainer2); + + newPage.UpdateContent(); + + newDoc.Save(SaveFlags.Full, sOutput); + } + } + } + } + } + } - // We replace the Image with the Container - // (which now holds the image) - pg.Content.RemoveElement(0); - pg.UpdateContent(); + public static List CreateNewOptionalContentGroups(Document doc, List names) + { + List ocgs = new List(); - pg.Content.AddElement(container); - pg.UpdateContent(); + OptionalContentGroup ocg = new OptionalContentGroup(doc, names[0]); + OptionalContentGroup ocg2 = new OptionalContentGroup(doc, names[1]); + OptionalContentGroup ocg3 = new OptionalContentGroup(doc, names[2]); + OptionalContentGroup ocg4 = new OptionalContentGroup(doc, names[3]); - // We create a new OptionalContentGroup and place it in the - // OptionalContentConfig.Order array - OptionalContentGroup ocg = CreateNewOptionalContentGroup(doc, "Rubber Ducky"); + ocgs.Add(ocg); + ocgs.Add(ocg2); + ocgs.Add(ocg3); + ocgs.Add(ocg4); - // Now we associate the Container with the OptionalContentGroup - // via an OptionalContentMembershipDict. Note that we MUST - // update the Page's content afterwards. - AssociateOCGWithContainer(doc, ocg, container); - pg.UpdateContent(); + // Add it to the Order array -- this is required so that it will appear in the 'Layers' panel in a PDF Viewer. + OptionalContentOrderArray order_list = doc.DefaultOptionalContentConfig.Order; - doc.Save(SaveFlags.Full, sOutput); - } - } + OptionalContentOrderArray grouping = new OptionalContentOrderArray(doc, "Image Grouping"); + grouping.Add(new OptionalContentOrderLeaf(ocg)); + grouping.Add(new OptionalContentOrderLeaf(ocg2)); - // Create an OptionalContentGroup with a given name, and add it to the - // default OptionalContentConfig's Order array. - public static OptionalContentGroup CreateNewOptionalContentGroup(Document doc, string name) - { - // Create an OptionalContentGroup - OptionalContentGroup ocg = new OptionalContentGroup(doc, name); + OptionalContentOrderArray grouping2 = new OptionalContentOrderArray(doc, "Text Grouping"); + grouping2.Add(new OptionalContentOrderLeaf(ocg3)); + grouping2.Add(new OptionalContentOrderLeaf(ocg4)); - // Add it to the Order array -- this is required so that the OptionalContentGroup - // will appear in the 'Layers' control panel in a PDF Viewer. It will appear in - // the control panel with the name given in the OptionalContentGroup constructor. - OptionalContentOrderArray order_list = doc.DefaultOptionalContentConfig.Order; - order_list.Insert(order_list.Length, new OptionalContentOrderLeaf(ocg)); + order_list.Insert(order_list.Length, grouping); + order_list.Insert(order_list.Length, grouping2); - return ocg; + return ocgs; } // Associate a Container with an OptionalContentGroup via an OptionalContentMembershipDict. - // This function associates a Containter with a single OptionalContentGroup and uses + // This function associates a Container with a single OptionalContentGroup and uses // a VisibilityPolicy of AnyOn. public static void AssociateOCGWithContainer(Document doc, OptionalContentGroup ocg, Container cont) { @@ -99,9 +156,8 @@ public static void AssociateOCGWithContainer(Document doc, OptionalContentGroup // 'typical' usage; other options can be used to create an 'inverting' layer // (i.e. 'Display this content when the layer is turned OFF'), or to make the // Container's visibility depend on several OptionalContentGroups - OptionalContentMembershipDict ocmd = new OptionalContentMembershipDict(doc, new OptionalContentGroup[] {ocg}, VisibilityPolicy.AnyOn); + OptionalContentMembershipDict ocmd = new OptionalContentMembershipDict(doc, new OptionalContentGroup[] { ocg }, VisibilityPolicy.AnyOn); - // Associate the Container with the OptionalContentMembershipDict cont.OptionalContentMembershipDict = ocmd; } } From b3e0034d9cb86eb8c595c76fc3cdda22b3025678 Mon Sep 17 00:00:00 2001 From: Joseph Argento Date: Mon, 6 Oct 2025 12:58:41 -0500 Subject: [PATCH 2/2] Update to windows-2022 as 2019 has been retired. --- .github/workflows/test-net-framework-samples.yml | 2 +- ContentModification/CreateLayer/CreateLayer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-net-framework-samples.yml b/.github/workflows/test-net-framework-samples.yml index 7bd74bc..6d87bd6 100644 --- a/.github/workflows/test-net-framework-samples.yml +++ b/.github/workflows/test-net-framework-samples.yml @@ -12,7 +12,7 @@ on: jobs: test-samples: - runs-on: windows-2019 + runs-on: windows-2022 defaults: run: shell: bash diff --git a/ContentModification/CreateLayer/CreateLayer.cs b/ContentModification/CreateLayer/CreateLayer.cs index 963eebb..8a7dfe4 100644 --- a/ContentModification/CreateLayer/CreateLayer.cs +++ b/ContentModification/CreateLayer/CreateLayer.cs @@ -44,7 +44,7 @@ static void Main(string[] args) Image image = (Image)pg.Content.GetElement(0); image.Matrix = new Matrix(image.Matrix.A * .5, 0, 0, image.Matrix.D * .5, image.Matrix.H, image.Matrix.V); - Image image2 = new Image(Library.ResourceDirectory + "Sample_Input/image.png"); + Image image2 = new Image(Library.ResourceDirectory + "Sample_Input/Image.png"); Text text = new Text(); Matrix matrix = new Matrix();