diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/Layer pdf - 322_High_Holborn_building_Brochure.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/Layer pdf - 322_High_Holborn_building_Brochure.pdf
new file mode 100644
index 00000000..5363c286
Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/Layer pdf - 322_High_Holborn_building_Brochure.pdf differ
diff --git a/src/UglyToad.PdfPig.Tests/Integration/OptionalContentTests.cs b/src/UglyToad.PdfPig.Tests/Integration/OptionalContentTests.cs
index 78719aa3..de7dcbf4 100644
--- a/src/UglyToad.PdfPig.Tests/Integration/OptionalContentTests.cs
+++ b/src/UglyToad.PdfPig.Tests/Integration/OptionalContentTests.cs
@@ -1,4 +1,6 @@
-using Xunit;
+using System;
+using UglyToad.PdfPig.Tokens;
+using Xunit;
namespace UglyToad.PdfPig.Tests.Integration
{
@@ -10,7 +12,7 @@ namespace UglyToad.PdfPig.Tests.Integration
using (var document = PdfDocument.Open(IntegrationHelpers.GetDocumentPath("odwriteex.pdf")))
{
var page = document.GetPage(1);
- var oc = page.GetOptionalContents();
+ var oc = page.ExperimentalAccess.GetOptionalContents();
Assert.Equal(3, oc.Count);
@@ -23,5 +25,30 @@ namespace UglyToad.PdfPig.Tests.Integration
Assert.Equal(1, oc["Text"].Count);
}
}
+
+ [Fact]
+ public void MarkedOptionalContentRecursion()
+ {
+ using (var document = PdfDocument.Open(IntegrationHelpers.GetDocumentPath("Layer pdf - 322_High_Holborn_building_Brochure.pdf")))
+ {
+ var page1 = document.GetPage(1);
+ var oc1 = page1.ExperimentalAccess.GetOptionalContents();
+ Assert.Equal(16, oc1.Count);
+ Assert.Contains("NEW ARRANGEMENT", oc1);
+
+ var page2 = document.GetPage(2);
+ var oc2 = page2.ExperimentalAccess.GetOptionalContents();
+ Assert.Equal(15, oc2.Count);
+ Assert.DoesNotContain("NEW ARRANGEMENT", oc2);
+ Assert.Contains("WDL Shell text", oc2);
+ Assert.Equal(2, oc2["WDL Shell text"].Count);
+
+ var page3 = document.GetPage(3);
+ var oc3 = page3.ExperimentalAccess.GetOptionalContents();
+ Assert.Equal(15, oc3.Count);
+ Assert.Contains("WDL Shell text", oc3);
+ Assert.Equal(2, oc3["WDL Shell text"].Count);
+ }
+ }
}
}
diff --git a/src/UglyToad.PdfPig/Content/OptionalContentGroupElement.cs b/src/UglyToad.PdfPig/Content/OptionalContentGroupElement.cs
index 587b7d22..2732bcbd 100644
--- a/src/UglyToad.PdfPig/Content/OptionalContentGroupElement.cs
+++ b/src/UglyToad.PdfPig/Content/OptionalContentGroupElement.cs
@@ -30,7 +30,7 @@ namespace UglyToad.PdfPig.Content
///
/// A usage dictionary describing the nature of the content controlled by the group.
///
- public IDictionary Usage { get; }
+ public IReadOnlyDictionary Usage { get; }
///
/// Underlying .
@@ -110,7 +110,7 @@ namespace UglyToad.PdfPig.Content
// Usage - Optional
if (markedContentElement.Properties.TryGet(NameToken.Usage, out DictionaryToken usage))
{
- throw new NotImplementedException();
+ this.Usage = usage.Data;
}
break;
@@ -119,29 +119,29 @@ namespace UglyToad.PdfPig.Content
if (markedContentElement.Properties.TryGet(NameToken.Ocgs, out DictionaryToken ocgsD))
{
// dictionary or array
- throw new NotImplementedException();
+ throw new NotImplementedException($"{NameToken.Ocgs}");
}
else if (markedContentElement.Properties.TryGet(NameToken.Ocgs, out ArrayToken ocgsA))
{
// dictionary or array
- throw new NotImplementedException();
+ throw new NotImplementedException($"{NameToken.Ocgs}");
}
// P - Optional
if (markedContentElement.Properties.TryGet(NameToken.P, out NameToken p))
{
- throw new NotImplementedException();
+ throw new NotImplementedException($"{NameToken.P}");
}
// VE - Optional
if (markedContentElement.Properties.TryGet(NameToken.VE, out ArrayToken ve))
{
- throw new NotImplementedException();
+ throw new NotImplementedException($"{NameToken.VE}");
}
break;
default:
- throw new ArgumentException($"Unknown Optional Content of type '{Type}'.", nameof(Type));
+ throw new ArgumentException($"Unknown Optional Content of type '{Type}' not known.", nameof(Type));
}
}
diff --git a/src/UglyToad.PdfPig/Content/Page.cs b/src/UglyToad.PdfPig/Content/Page.cs
index f27bae0d..9ce53278 100644
--- a/src/UglyToad.PdfPig/Content/Page.cs
+++ b/src/UglyToad.PdfPig/Content/Page.cs
@@ -10,6 +10,7 @@
using Util.JetBrains.Annotations;
using Tokenization.Scanner;
using Graphics;
+ using System.Linq;
///
/// Contains the content and provides access to methods of a single page in the .
@@ -166,11 +167,6 @@
///
public IReadOnlyList GetMarkedContents() => Content.GetMarkedContents();
- ///
- /// Gets any optional content on the page.
- ///
- public IDictionary> GetOptionalContents() => Content.GetOptionalContents();
-
///
/// Provides access to useful members which will change in future releases.
///
@@ -198,6 +194,42 @@
{
return annotationProvider.GetAnnotations();
}
+
+ ///
+ /// Gets any optional content on the page.
+ /// Does not handle XObjects and annotations for the time being.
+ ///
+ public IDictionary> GetOptionalContents()
+ {
+ // 4.10.2
+ // Optional content in content stream
+ var mc = page.Content?.GetMarkedContents();
+
+ List mcesOptional = new List();
+ GetOptionalContentsRecursively(mc, ref mcesOptional);
+
+ // Optional content in XObjects and annotations
+ // TO DO
+ //var annots = GetAnnotations().ToList();
+
+ return mcesOptional.GroupBy(oc => oc.Name).ToDictionary(g => g.Key, g => g.ToList() as IReadOnlyList);
+ }
+
+ private void GetOptionalContentsRecursively(IReadOnlyList markedContentElements, ref List mcesOptional)
+ {
+ foreach (var mce in markedContentElements)
+ {
+ if (mce.Tag == "OC")
+ {
+ mcesOptional.Add(new OptionalContentGroupElement(mce));
+ // we don't recurse
+ }
+ else if (mce.Children?.Count > 0)
+ {
+ GetOptionalContentsRecursively(mce.Children, ref mcesOptional);
+ }
+ }
+ }
}
}
}
diff --git a/src/UglyToad.PdfPig/Content/PageContent.cs b/src/UglyToad.PdfPig/Content/PageContent.cs
index 065a764c..0e645df3 100644
--- a/src/UglyToad.PdfPig/Content/PageContent.cs
+++ b/src/UglyToad.PdfPig/Content/PageContent.cs
@@ -67,24 +67,5 @@
}
public IReadOnlyList GetMarkedContents() => markedContents;
-
- public IDictionary> GetOptionalContents()
- {
- const string ocTag = "OC";
-
- List optionalContent = new List();
-
- // 4.10.2
- // Optional content in content stream
- foreach (var omc in GetMarkedContents().Where(mc => mc.Tag == ocTag))
- {
- optionalContent.Add(new OptionalContentGroupElement(omc));
- }
-
- // Optional content in XObjects and annotations
- // TO DO
-
- return optionalContent.GroupBy(oc => oc.Name).ToDictionary(g => g.Key, g => g.ToList() as IReadOnlyList);
- }
}
}