This commit is contained in:
BobLd
2019-11-26 12:16:43 +00:00
9 changed files with 124 additions and 21 deletions

View File

@@ -6,7 +6,5 @@
{ {
Page Create(int number, DictionaryToken dictionary, PageTreeMembers pageTreeMembers, Page Create(int number, DictionaryToken dictionary, PageTreeMembers pageTreeMembers,
bool isLenientParsing); bool isLenientParsing);
void LoadResources(DictionaryToken dictionary, bool isLenientParsing);
} }
} }

View File

@@ -7,6 +7,12 @@
{ {
void LoadResourceDictionary(DictionaryToken resourceDictionary, bool isLenientParsing); void LoadResourceDictionary(DictionaryToken resourceDictionary, bool isLenientParsing);
/// <summary>
/// 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.
/// </summary>
void UnloadResourceDictionary();
IFont GetFont(NameToken name); IFont GetFont(NameToken name);
StreamToken GetXObject(NameToken name); StreamToken GetXObject(NameToken name);

View File

@@ -1,5 +1,8 @@
namespace UglyToad.PdfPig.Content namespace UglyToad.PdfPig.Content
{ {
using System.Collections.Generic;
using Tokens;
/// <summary> /// <summary>
/// Contains the values inherited from the Page Tree for this page. /// Contains the values inherited from the Page Tree for this page.
/// </summary> /// </summary>
@@ -13,5 +16,7 @@
public MediaBox MediaBox { get; set; } public MediaBox MediaBox { get; set; }
public int Rotation { get; set; } public int Rotation { get; set; }
public Queue<DictionaryToken> ParentResources { get; } = new Queue<DictionaryToken>();
} }
} }

View File

@@ -50,7 +50,10 @@
{ {
currentNode = pageStack.Pop(); 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)) if (currentNode.NodeDictionary.TryGet(NameToken.MediaBox, pdfScanner, out ArrayToken mediaBox))
{ {

View File

@@ -7,6 +7,7 @@
using Parser.Parts; using Parser.Parts;
using Tokenization.Scanner; using Tokenization.Scanner;
using Tokens; using Tokens;
using Util;
internal class ResourceStore : IResourceStore internal class ResourceStore : IResourceStore
{ {
@@ -14,7 +15,7 @@
private readonly IFontFactory fontFactory; private readonly IFontFactory fontFactory;
private readonly Dictionary<IndirectReference, IFont> loadedFonts = new Dictionary<IndirectReference, IFont>(); private readonly Dictionary<IndirectReference, IFont> loadedFonts = new Dictionary<IndirectReference, IFont>();
private readonly Dictionary<NameToken, IndirectReference> currentResourceState = new Dictionary<NameToken, IndirectReference>(); private readonly StackDictionary<NameToken, IndirectReference> currentResourceState = new StackDictionary<NameToken, IndirectReference>();
private readonly Dictionary<NameToken, DictionaryToken> extendedGraphicsStates = new Dictionary<NameToken, DictionaryToken>(); private readonly Dictionary<NameToken, DictionaryToken> extendedGraphicsStates = new Dictionary<NameToken, DictionaryToken>();
@@ -28,6 +29,8 @@
public void LoadResourceDictionary(DictionaryToken resourceDictionary, bool isLenientParsing) public void LoadResourceDictionary(DictionaryToken resourceDictionary, bool isLenientParsing)
{ {
currentResourceState.Push();
if (resourceDictionary.TryGet(NameToken.Font, out var fontBase)) if (resourceDictionary.TryGet(NameToken.Font, out var fontBase))
{ {
var fontDictionary = DirectObjectFinder.Get<DictionaryToken>(fontBase, scanner); var fontDictionary = DirectObjectFinder.Get<DictionaryToken>(fontBase, scanner);
@@ -95,6 +98,11 @@
} }
} }
public void UnloadResourceDictionary()
{
currentResourceState.Pop();
}
private void LoadFontDictionary(DictionaryToken fontDictionary, bool isLenientParsing) private void LoadFontDictionary(DictionaryToken fontDictionary, bool isLenientParsing)
{ {
foreach (var pair in fontDictionary.Data) foreach (var pair in fontDictionary.Data)

View File

@@ -392,7 +392,9 @@
continue; continue;
} }
if (keyValuePair.Value is StringToken || keyValuePair.Value is ArrayToken || keyValuePair.Value is DictionaryToken) if (keyValuePair.Value is StringToken || keyValuePair.Value is ArrayToken
|| keyValuePair.Value is DictionaryToken
|| keyValuePair.Value is HexToken)
{ {
var inner = DecryptInternal(reference, keyValuePair.Value); var inner = DecryptInternal(reference, keyValuePair.Value);
dictionary = dictionary.With(keyValuePair.Key, inner); dictionary = dictionary.With(keyValuePair.Key, inner);

View File

@@ -336,7 +336,8 @@
* 5. Restore the saved graphics state, as if by invoking the Q operator. * 5. Restore the saved graphics state, as if by invoking the Q operator.
*/ */
if (formStream.StreamDictionary.TryGet<DictionaryToken>(NameToken.Resources, pdfScanner, out var formResources)) var hasResources = formStream.StreamDictionary.TryGet<DictionaryToken>(NameToken.Resources, pdfScanner, out var formResources);
if (hasResources)
{ {
resourceStore.LoadResourceDictionary(formResources, isLenientParsing); resourceStore.LoadResourceDictionary(formResources, isLenientParsing);
} }
@@ -368,6 +369,11 @@
// 5. Restore saved state. // 5. Restore saved state.
PopState(); PopState();
if (hasResources)
{
resourceStore.UnloadResourceDictionary();
}
} }
public void BeginSubpath() public void BeginSubpath()

View File

@@ -58,9 +58,23 @@
MediaBox mediaBox = GetMediaBox(number, dictionary, pageTreeMembers, isLenientParsing); MediaBox mediaBox = GetMediaBox(number, dictionary, pageTreeMembers, isLenientParsing);
CropBox cropBox = GetCropBox(dictionary, pageTreeMembers, mediaBox, isLenientParsing); CropBox cropBox = GetCropBox(dictionary, pageTreeMembers, mediaBox, isLenientParsing);
UserSpaceUnit userSpaceUnit = GetUserSpaceUnits(dictionary); var stackDepth = 0;
LoadResources(dictionary, isLenientParsing); 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);
PageContent content = default(PageContent); PageContent content = default(PageContent);
@@ -114,6 +128,11 @@
var page = new Page(number, dictionary, mediaBox, cropBox, rotation, content, new AnnotationProvider(pdfScanner, dictionary, isLenientParsing)); 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; return page;
} }
@@ -201,17 +220,5 @@
return mediaBox; return mediaBox;
} }
public void LoadResources(DictionaryToken dictionary, bool isLenientParsing)
{
if (!dictionary.TryGet(NameToken.Resources, out var token))
{
return;
}
var resources = DirectObjectFinder.Get<DictionaryToken>(token, pdfScanner);
resourceStore.LoadResourceDictionary(resources, isLenientParsing);
}
} }
} }

View File

@@ -0,0 +1,68 @@
// ReSharper disable InconsistentNaming
namespace UglyToad.PdfPig.Util
{
using System;
using System.Collections.Generic;
internal class StackDictionary<K, V>
{
private readonly List<Dictionary<K, V>> values = new List<Dictionary<K, V>>();
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<K, V>());
}
public void Pop()
{
if (values.Count == 0)
{
throw new InvalidOperationException("Cannot pop empty stacked dictionary.");
}
values.RemoveAt(values.Count - 1);
}
}
}