Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
27 changes: 26 additions & 1 deletion src/UglyToad.PdfPig.Tests/Integration/GithubIssuesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,31 @@

public class GithubIssuesTests
{
[Fact]
public void Issues1250()
{
// Issue comes from HasFormXObjectCircularReference
var path = IntegrationHelpers.GetDocumentPath("SPE8EF26T0545.pdf");
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true }))
{
var page = document.GetPage(1);
Assert.NotNull(page);
Assert.NotEmpty(page.Letters);

page = document.GetPage(7);
Assert.NotNull(page);
Assert.NotEmpty(page.Letters);
}

// Ensure still no StackOverflowException
using (var document = PdfDocument.Open(IntegrationHelpers.GetDocumentPath("issue_671")))
{
var page = document.GetPage(1);
Assert.NotNull(page);
Assert.NotEmpty(page.Letters);
}
}

[Fact]
public void Issues1248()
{
Expand All @@ -29,7 +54,7 @@ public void Issues1248()
}
}
}

[Fact]
public void Issues1238()
{
Expand Down
57 changes: 50 additions & 7 deletions src/UglyToad.PdfPig/Graphics/BaseStreamProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,8 @@ protected virtual void ProcessFormXObject(StreamToken formStream, NameToken xObj
if (hasCircularReference)
{
if (ParsingOptions.UseLenientParsing)
{
{
// TODO - We might be removing too much, good for the moment. See Issues1250() for examples
operations = operations.Where(o => o is not InvokeNamedXObject xo || xo.Name != xObjectName)
.ToArray();
ParsingOptions.Logger.Warn(
Expand Down Expand Up @@ -618,14 +619,56 @@ protected virtual void ProcessFormXObject(StreamToken formStream, NameToken xObj
/// <param name="xObjectName">The form's name.</param>
/// <param name="operations">The form operations parsed from original form stream.</param>
protected virtual bool HasFormXObjectCircularReference(StreamToken formStream,
NameToken xObjectName,
NameToken? xObjectName,
IReadOnlyList<IGraphicsStateOperation> operations)
{
return xObjectName != null
&& operations.OfType<InvokeNamedXObject>()?.Any(o => o.Name == xObjectName) ==
true // operations contain another form with same name
&& ResourceStore.TryGetXObject(xObjectName, out var result)
&& result.Data.Span.SequenceEqual(formStream.Data.Span); // The form contained in the operations has identical data to current form
if (xObjectName is null)
{
return false;
}

if (operations.OfType<InvokeNamedXObject>()?.Any(o => o.Name == xObjectName) != true)
{
return false;
}

if (!TryGetXObjectToken(formStream, xObjectName, PdfScanner, out var t1))
{
return false;
}

if (!ResourceStore.TryGetXObject(xObjectName, out var resourceStream))
{
return false;
}

if (!TryGetXObjectToken(resourceStream, xObjectName, PdfScanner, out var t2))
{
return false;
}

if (t1 is null || t2 is null)
{
return false;
}

return t1.Equals(t2);

static bool TryGetXObjectToken(StreamToken streamToken, NameToken xObjectName, IPdfTokenScanner scanner, out IToken? token)
{
token = null;
if (!streamToken.StreamDictionary.TryGet<DictionaryToken>(NameToken.Resources, scanner, out var formResources))
{
return false;
}

if (!formResources.TryGet<DictionaryToken>(NameToken.Xobject, out var xObjectBase) || !xObjectBase.TryGet(xObjectName, out token))
{
return false;
}

return token is not null;
}
}

/// <inheritdoc/>
Expand Down
Loading