diff --git a/src/UglyToad.PdfPig/Content/IPageFactory.cs b/src/UglyToad.PdfPig/Content/IPageFactory.cs
index 8ca300ed..9076cce1 100644
--- a/src/UglyToad.PdfPig/Content/IPageFactory.cs
+++ b/src/UglyToad.PdfPig/Content/IPageFactory.cs
@@ -6,7 +6,5 @@
{
Page Create(int number, DictionaryToken dictionary, PageTreeMembers pageTreeMembers,
bool isLenientParsing);
-
- void LoadResources(DictionaryToken dictionary, bool isLenientParsing);
}
}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig/Content/IResourceStore.cs b/src/UglyToad.PdfPig/Content/IResourceStore.cs
index 66b03752..a5f33767 100644
--- a/src/UglyToad.PdfPig/Content/IResourceStore.cs
+++ b/src/UglyToad.PdfPig/Content/IResourceStore.cs
@@ -7,6 +7,12 @@
{
void LoadResourceDictionary(DictionaryToken resourceDictionary, bool isLenientParsing);
+ ///
+ /// Remove any named resources and associated state for the last resource dictionary loaded.
+ /// Does not affect the cached resources, just the labels associated with them.
+ ///
+ void UnloadResourceDictionary();
+
IFont GetFont(NameToken name);
StreamToken GetXObject(NameToken name);
diff --git a/src/UglyToad.PdfPig/Content/PageTreeMembers.cs b/src/UglyToad.PdfPig/Content/PageTreeMembers.cs
index 9f14c999..20ae0744 100644
--- a/src/UglyToad.PdfPig/Content/PageTreeMembers.cs
+++ b/src/UglyToad.PdfPig/Content/PageTreeMembers.cs
@@ -1,5 +1,8 @@
namespace UglyToad.PdfPig.Content
{
+ using System.Collections.Generic;
+ using Tokens;
+
///
/// Contains the values inherited from the Page Tree for this page.
///
@@ -13,5 +16,7 @@
public MediaBox MediaBox { get; set; }
public int Rotation { get; set; }
+
+ public Queue ParentResources { get; } = new Queue();
}
}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig/Content/Pages.cs b/src/UglyToad.PdfPig/Content/Pages.cs
index 7304802e..bf9cdac3 100644
--- a/src/UglyToad.PdfPig/Content/Pages.cs
+++ b/src/UglyToad.PdfPig/Content/Pages.cs
@@ -45,12 +45,15 @@
}
var pageTreeMembers = new PageTreeMembers();
-
+
while (pageStack.Count > 0)
{
currentNode = pageStack.Pop();
- pageFactory.LoadResources(currentNode.NodeDictionary, isLenientParsing);
+ if (currentNode.NodeDictionary.TryGet(NameToken.Resources, pdfScanner, out DictionaryToken resourcesDictionary))
+ {
+ pageTreeMembers.ParentResources.Enqueue(resourcesDictionary);
+ }
if (currentNode.NodeDictionary.TryGet(NameToken.MediaBox, pdfScanner, out ArrayToken mediaBox))
{
diff --git a/src/UglyToad.PdfPig/Content/ResourceStore.cs b/src/UglyToad.PdfPig/Content/ResourceStore.cs
index e8a70ce9..92ca2fe8 100644
--- a/src/UglyToad.PdfPig/Content/ResourceStore.cs
+++ b/src/UglyToad.PdfPig/Content/ResourceStore.cs
@@ -7,6 +7,7 @@
using Parser.Parts;
using Tokenization.Scanner;
using Tokens;
+ using Util;
internal class ResourceStore : IResourceStore
{
@@ -14,7 +15,7 @@
private readonly IFontFactory fontFactory;
private readonly Dictionary loadedFonts = new Dictionary();
- private readonly Dictionary currentResourceState = new Dictionary();
+ private readonly StackDictionary currentResourceState = new StackDictionary();
private readonly Dictionary extendedGraphicsStates = new Dictionary();
@@ -28,6 +29,8 @@
public void LoadResourceDictionary(DictionaryToken resourceDictionary, bool isLenientParsing)
{
+ currentResourceState.Push();
+
if (resourceDictionary.TryGet(NameToken.Font, out var fontBase))
{
var fontDictionary = DirectObjectFinder.Get(fontBase, scanner);
@@ -95,6 +98,11 @@
}
}
+ public void UnloadResourceDictionary()
+ {
+ currentResourceState.Pop();
+ }
+
private void LoadFontDictionary(DictionaryToken fontDictionary, bool isLenientParsing)
{
foreach (var pair in fontDictionary.Data)
diff --git a/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs b/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs
index 430498cb..4346e371 100644
--- a/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs
+++ b/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs
@@ -336,7 +336,8 @@
* 5. Restore the saved graphics state, as if by invoking the Q operator.
*/
- if (formStream.StreamDictionary.TryGet(NameToken.Resources, pdfScanner, out var formResources))
+ var hasResources = formStream.StreamDictionary.TryGet(NameToken.Resources, pdfScanner, out var formResources);
+ if (hasResources)
{
resourceStore.LoadResourceDictionary(formResources, isLenientParsing);
}
@@ -368,6 +369,11 @@
// 5. Restore saved state.
PopState();
+
+ if (hasResources)
+ {
+ resourceStore.UnloadResourceDictionary();
+ }
}
public void BeginSubpath()
diff --git a/src/UglyToad.PdfPig/Parser/PageFactory.cs b/src/UglyToad.PdfPig/Parser/PageFactory.cs
index b182b530..e3dc71c5 100644
--- a/src/UglyToad.PdfPig/Parser/PageFactory.cs
+++ b/src/UglyToad.PdfPig/Parser/PageFactory.cs
@@ -57,11 +57,25 @@
MediaBox mediaBox = GetMediaBox(number, dictionary, pageTreeMembers, isLenientParsing);
CropBox cropBox = GetCropBox(dictionary, pageTreeMembers, mediaBox, isLenientParsing);
+
+ var stackDepth = 0;
+
+ while (pageTreeMembers.ParentResources.Count > 0)
+ {
+ var resource = pageTreeMembers.ParentResources.Dequeue();
+
+ resourceStore.LoadResourceDictionary(resource, isLenientParsing);
+ stackDepth++;
+ }
+
+ if (dictionary.TryGet(NameToken.Resources, pdfScanner, out DictionaryToken resources))
+ {
+ resourceStore.LoadResourceDictionary(resources, isLenientParsing);
+ stackDepth++;
+ }
UserSpaceUnit userSpaceUnit = GetUserSpaceUnits(dictionary);
- LoadResources(dictionary, isLenientParsing);
-
PageContent content = default(PageContent);
if (!dictionary.TryGet(NameToken.Contents, out var contents))
@@ -114,6 +128,11 @@
var page = new Page(number, dictionary, mediaBox, cropBox, rotation, content, new AnnotationProvider(pdfScanner, dictionary, isLenientParsing));
+ for (var i = 0; i < stackDepth; i++)
+ {
+ resourceStore.UnloadResourceDictionary();
+ }
+
return page;
}
@@ -201,17 +220,5 @@
return mediaBox;
}
-
- public void LoadResources(DictionaryToken dictionary, bool isLenientParsing)
- {
- if (!dictionary.TryGet(NameToken.Resources, out var token))
- {
- return;
- }
-
- var resources = DirectObjectFinder.Get(token, pdfScanner);
-
- resourceStore.LoadResourceDictionary(resources, isLenientParsing);
- }
}
}
diff --git a/src/UglyToad.PdfPig/Util/StackDictionary.cs b/src/UglyToad.PdfPig/Util/StackDictionary.cs
new file mode 100644
index 00000000..c3a3408b
--- /dev/null
+++ b/src/UglyToad.PdfPig/Util/StackDictionary.cs
@@ -0,0 +1,68 @@
+// ReSharper disable InconsistentNaming
+namespace UglyToad.PdfPig.Util
+{
+ using System;
+ using System.Collections.Generic;
+
+ internal class StackDictionary
+ {
+ private readonly List> values = new List>();
+
+ public V this[K key]
+ {
+ get
+ {
+ if (TryGetValue(key, out var result))
+ {
+ return result;
+ }
+
+ throw new KeyNotFoundException($"No item with key {key} in stack.");
+ }
+ set
+ {
+ if (values.Count == 0)
+ {
+ throw new InvalidOperationException($"Cannot set item in empty stack, call {nameof(Push)} before use.");
+ }
+
+ values[values.Count - 1][key] = value;
+ }
+ }
+
+ public bool TryGetValue(K key, out V result)
+ {
+ if (values.Count == 0)
+ {
+ throw new InvalidOperationException($"Cannot get item from empty stack, call {nameof(Push)} before use.");
+ }
+
+ for (var i = values.Count - 1; i >= 0; i--)
+ {
+ if (values[i].TryGetValue(key, out result))
+ {
+ return true;
+ }
+ }
+
+ result = default(V);
+
+ return false;
+ }
+
+ public void Push()
+ {
+ values.Add(new Dictionary());
+ }
+
+ public void Pop()
+ {
+ if (values.Count == 0)
+ {
+ throw new InvalidOperationException("Cannot pop empty stacked dictionary.");
+ }
+
+ values.RemoveAt(values.Count - 1);
+ }
+ }
+}