Merge pull request #629 from thinkbeforecoding/fix/copyfrom

Fix: copyfrom resources collisions
This commit is contained in:
Eliot Jones 2023-05-22 19:59:14 +01:00 committed by GitHub
commit 98be67d93b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 116 additions and 15 deletions

View File

@ -1,6 +1,7 @@
namespace UglyToad.PdfPig.Tests.Functions.Type4
{
using System;
using System.Globalization;
using UglyToad.PdfPig.Functions.Type4;
using Xunit;
@ -60,7 +61,7 @@
/// <returns>this instance</returns>
public Type4Tester PopReal(double expected, double delta)
{
double value = Convert.ToDouble(context.Stack.Pop());
double value = Convert.ToDouble(context.Stack.Pop(), CultureInfo.InvariantCulture);
DoubleComparer doubleComparer = new DoubleComparer(delta);
Assert.True(doubleComparer.Equals(expected, value));//expected, value, delta);
return this;
@ -98,7 +99,7 @@
{
object value = context.PopNumber();
DoubleComparer doubleComparer = new DoubleComparer(delta);
Assert.True(doubleComparer.Equals(expected, Convert.ToDouble(value)));
Assert.True(doubleComparer.Equals(expected, Convert.ToDouble(value, CultureInfo.InvariantCulture)));
return this;
}

View File

@ -1,6 +1,7 @@
namespace UglyToad.PdfPig.Functions.Type4
{
using System.Collections.Generic;
using System.Globalization;
/// <summary>
/// Basic parser for Type 4 functions which is used to build up instruction sequences.
@ -62,13 +63,13 @@
}
else
{
if (int.TryParse(token, out int tokenInt))
if (int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out int tokenInt))
{
GetCurrentSequence().AddInteger(tokenInt);
return;
}
if (double.TryParse(token, out double tokenFloat))
if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out double tokenFloat))
{
GetCurrentSequence().AddReal(tokenFloat);
return;

View File

@ -23,6 +23,59 @@
using Images.Png;
using UglyToad.PdfPig.Actions;
internal class NameConflictSolver
{
private string prefix;
private int key = 0;
private HashSet<string> xobjectNamesUsed = new HashSet<string>();
public NameConflictSolver(string prefix)
{
this.prefix = prefix;
}
private string ExtractPrefix(string name)
{
if (name == null)
return prefix;
var i = 0;
while (i < name.Length && (name[i] < '0' || name[i] > '9'))
{
i++;
}
return i != 0 ? name.Substring(0,i) : prefix;
}
public string NewName(string orginalName = null)
{
var newPrefix = ExtractPrefix(orginalName);
var name = $"{newPrefix}{key}";
while (xobjectNamesUsed.Contains(name))
{
name = $"{newPrefix}{++key}";
}
xobjectNamesUsed.Add(name);
return name;
}
public string FixName(string name)
{
if (xobjectNamesUsed.Contains(name))
{
return NewName(name);
}
else
{
xobjectNamesUsed.Add(name);
return name;
}
}
}
/// <summary>
/// A builder used to add construct a page in a PDF document.
/// </summary>
@ -48,7 +101,9 @@
//a sequence number of ShowText operation to determine whether letters belong to same operation or not (letters that belong to different operations have less changes to belong to same word)
private int textSequence;
private int imageKey = 1;
private NameConflictSolver xobjectsNames = new NameConflictSolver("I");
private NameConflictSolver gStateNames = new NameConflictSolver("GS");
internal int? rotation;
@ -522,6 +577,7 @@
}
}
/// <summary>
/// Adds the JPEG image represented by the input stream at the specified location.
/// </summary>
@ -557,7 +613,7 @@
var resources = pageDictionary.GetOrCreateDict(NameToken.Resources);
var xObjects = resources.GetOrCreateDict(NameToken.Xobject);
var key = NameToken.Create($"I{imageKey++}");
var key = NameToken.Create( xobjectsNames.NewName());
xObjects[key] = reference;
currentStream.Add(Push.Value);
@ -591,7 +647,7 @@
var resources = pageDictionary.GetOrCreateDict(NameToken.Resources);
var xObjects = resources.GetOrCreateDict(NameToken.Xobject);
var key = NameToken.Create($"I{imageKey++}");
var key = NameToken.Create(xobjectsNames.NewName());
xObjects[key] = new IndirectReferenceToken(image.Reference);
currentStream.Add(Push.Value);
@ -710,7 +766,7 @@
var resources = pageDictionary.GetOrCreateDict(NameToken.Resources);
var xObjects = resources.GetOrCreateDict(NameToken.Xobject);
var key = NameToken.Create($"I{imageKey++}");
var key = NameToken.Create(xobjectsNames.NewName());
xObjects[key] = reference;
@ -832,15 +888,15 @@
{
var pageXobjectsDictionary = resources.GetOrCreateDict(NameToken.Xobject);
var xobjectNamesUsed = Enumerable.Range(0, imageKey).Select(i => $"I{i}");
foreach (var xobjectSet in xobjectsDictionary.Data)
{
var xobjectName = xobjectSet.Key;
if (xobjectName[0] == 'I' && xobjectNamesUsed.Any(s => s == xobjectName))
{
// This would mean that the imported xobject collide with one of the added image. so we have to rename it
var newName = $"I{imageKey++}";
// This would mean that the imported xobject collide with one of the added image. so we have to rename it
var newName = xobjectsNames.FixName(xobjectName);
if (xobjectName != newName)
{
// Set all the pertinent SetFontAndSize operations with the new name
operations = operations.Select(op =>
{
@ -856,10 +912,10 @@
return op;
}).ToList();
xobjectName = newName;
}
xobjectName = newName;
if (!(xobjectSet.Value is IndirectReferenceToken fontReferenceToken))
{
throw new PdfDocumentFormatException($"Expected a IndirectReferenceToken for the XObject, got a {xobjectSet.Value.GetType().Name}");
@ -869,6 +925,49 @@
}
}
// Since we don't directly add xobjects's to the pages resources, we have to go look at the document's xobjects
if (srcResourceDictionary.TryGet(NameToken.ExtGState, srcPage.pdfScanner, out DictionaryToken gsDictionary))
{
var pageGstateDictionary = resources.GetOrCreateDict(NameToken.ExtGState);
foreach (var gstate in gsDictionary.Data)
{
var gstateName = gstate.Key;
var newName = gStateNames.FixName(gstateName);
if (newName != gstateName)
{
// This would mean that the imported xobject collide with one of the added image. so we have to rename it
// Set all the pertinent SetFontAndSize operations with the new name
operations = operations.Select(op =>
{
if (!(op is SetGraphicsStateParametersFromDictionary invokeNamedOperation))
{
return op;
}
if (invokeNamedOperation.Name.Data == gstateName)
{
return new SetGraphicsStateParametersFromDictionary(NameToken.Create(newName));
}
return op;
}).ToList();
gstateName = newName;
}
if (!(gstate.Value is IndirectReferenceToken fontReferenceToken))
{
throw new PdfDocumentFormatException($"Expected a IndirectReferenceToken for the XObject, got a {gstate.Value.GetType().Name}");
}
pageGstateDictionary[gstateName] = documentBuilder.CopyToken(srcPage.pdfScanner, fontReferenceToken);
}
}
destinationStream.Operations.AddRange(operations);
return this;