remove cos object key completely and test indirect reference

This commit is contained in:
Eliot Jones
2018-01-14 15:01:18 +00:00
parent 36c0eedd7c
commit 1fb6ec41d1
7 changed files with 91 additions and 486 deletions

View File

@@ -0,0 +1,89 @@
namespace UglyToad.PdfPig.Tests.ContentStream
{
using PdfPig.ContentStream;
using Xunit;
public class IndirectReferenceTests
{
[Fact]
public void SetsProperties()
{
var reference = new IndirectReference(129, 45);
Assert.Equal(129, reference.ObjectNumber);
Assert.Equal(45, reference.Generation);
}
[Fact]
public void ToStringCorrect()
{
var reference = new IndirectReference(130, 70);
Assert.Equal("130 70", reference.ToString());
}
[Fact]
public void TwoIndirectReferenceEqual()
{
var reference1 = new IndirectReference(1574, 690);
var reference2 = new IndirectReference(1574, 690);
Assert.True(reference1.Equals(reference2));
}
[Fact]
public void TwoIndirectReferenceNotEqual()
{
var reference1 = new IndirectReference(1574, 690);
var reference2 = new IndirectReference(12, 0);
Assert.False(reference1.Equals(reference2));
}
[Fact]
public void TwoIndirectHashCodeEqual()
{
var reference1 = new IndirectReference(1267775544, 690);
var reference2 = new IndirectReference(1267775544, 690);
Assert.Equal(reference1.GetHashCode(), reference2.GetHashCode());
}
[Fact]
public void TwoIndirectHashCodeNotEqual()
{
var reference1 = new IndirectReference(1267775544, 690);
var reference2 = new IndirectReference(1267775544, 12);
Assert.NotEqual(reference1.GetHashCode(), reference2.GetHashCode());
}
[Fact]
public void TwoIndirectHashCodeSimilarValuesNotEqual()
{
var reference1 = new IndirectReference(12, 1);
var reference2 = new IndirectReference(1, 12);
Assert.NotEqual(reference1.GetHashCode(), reference2.GetHashCode());
}
[Fact]
public void OtherObjectNotEqual()
{
var reference = new IndirectReference(1267775544, 690);
var obj = "test";
// ReSharper disable once SuspiciousTypeConversion.Global
Assert.False(reference.Equals(obj));
}
[Fact]
public void NullNotEqual()
{
var reference = new IndirectReference(1267775544, 690);
// ReSharper disable once SuspiciousTypeConversion.Global
Assert.False(reference.Equals(null));
}
}
}

View File

@@ -1,35 +0,0 @@
// ReSharper disable ObjectCreationAsStatement
namespace UglyToad.PdfPig.Tests.Cos
{
using System;
using System.Collections.Generic;
using PdfPig.Cos;
using Xunit;
public class CosObjectKeyTests
{
[Fact]
public void NullObjectThrows()
{
Action action = () => new CosObjectKey(null);
Assert.Throws<ArgumentNullException>(action);
}
[Fact]
public void CanLookupInDictionary()
{
var key1 = new CosObjectKey(3, 0);
var key2 = new CosObjectKey(3, 0);
var dictionary = new Dictionary<CosObjectKey, long>
{
{key1, 5}
};
var result = dictionary[key2];
Assert.Equal(5, result);
}
}
}

View File

@@ -141,17 +141,6 @@
return result;
}
[CanBeNull]
public static CosObjectKey GetObjectKey(this PdfDictionary dictionary, CosName key)
{
if (!dictionary.TryGetValue(key, out var value) || !(value is CosObject obj))
{
return null;
}
return new CosObjectKey(obj);
}
[CanBeNull]
public static PdfDictionary GetDictionaryOrDefault(this PdfDictionary dictionary,
CosName key)

View File

@@ -19,11 +19,6 @@
SetObject(obj);
}
public CosObjectKey GetObjectKey()
{
return new CosObjectKey(objectNumber, generationNumber);
}
/**
* This will get the dictionary object in this object that has the name key and
* if it is a pdfobjref then it will dereference that and return it.

View File

@@ -1,92 +0,0 @@
namespace UglyToad.PdfPig.Cos
{
using System;
internal class CosObjectKey : IComparable<CosObjectKey>
{
public long Number { get; }
public long Generation { get; }
/**
* PDFObjectKey constructor comment.
*
* @param object The object that this key will represent.
*/
public CosObjectKey(CosObject obj) : this(GetNumber(obj), obj.GetGenerationNumber())
{
}
private static long GetNumber(CosObject obj)
{
if (obj == null)
{
throw new ArgumentNullException(nameof(obj));
}
return obj.GetObjectNumber();
}
public CosObjectKey(long num, int gen)
{
Number = num;
Generation = gen;
}
public override bool Equals(object obj)
{
CosObjectKey objToBeCompared = obj is CosObjectKey ? (CosObjectKey)obj : null;
return objToBeCompared != null &&
objToBeCompared.Number == Number &&
objToBeCompared.Generation == Generation;
}
protected bool Equals(CosObjectKey other)
{
return Number == other.Number && Generation == other.Generation;
}
public override int GetHashCode()
{
unchecked
{
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
return (int)((Number.GetHashCode() * 397) ^ Generation);
}
}
public override string ToString()
{
return $"{Number} {Generation} R";
}
public int CompareTo(CosObjectKey other)
{
if (Number < other.Number)
{
return -1;
}
else if (Number > other.Number)
{
return 1;
}
else
{
if (Generation < other.Generation)
{
return -1;
}
else if (Generation > other.Generation)
{
return 1;
}
else
{
return 0;
}
}
}
}
}

View File

@@ -33,13 +33,6 @@
requiresExistingObject);
}
public CosBase Parse(ParsingArguments arguments, CosObjectKey key, bool requiresExistingObject)
{
return Parse(arguments.Reader, key.Number, (int)key.Generation, arguments.CachingProviders.ObjectPool,
arguments.CrossReferenceTable, arguments.CachingProviders.BruteForceSearcher,
arguments.IsLenientParsing, requiresExistingObject);
}
public CosBase Parse(IRandomAccessRead reader, CosObject obj, CosObjectPool pool,
CrossReferenceTable crossReferenceTable, BruteForceSearcher bruteForceSearcher, bool isLenient, bool requireExistingObject)
{

View File

@@ -1,334 +0,0 @@
namespace UglyToad.PdfPig.Parser
{
using System.Collections.Generic;
using System.Linq;
using Cos;
internal class XrefTrailerResolver
{
/**
* A class which represents a xref/trailer object.
*/
private class XrefTrailerObj
{
public CosDictionary trailer = null;
public XRefType xrefType;
public readonly Dictionary<CosObjectKey, long> xrefTable = new Dictionary<CosObjectKey, long>();
/**
* Default constructor.
*/
public XrefTrailerObj()
{
xrefType = XRefType.TABLE;
}
public void reset()
{
xrefTable.Clear();
}
}
/**
* The XRefType of a trailer.
*/
public enum XRefType
{
/**
* XRef table type.
*/
TABLE,
/**
* XRef stream type.
*/
STREAM
}
private readonly Dictionary<long, XrefTrailerObj> bytePosToXrefMap = new Dictionary<long, XrefTrailerObj>();
private XrefTrailerObj curXrefTrailerObj = null;
private XrefTrailerObj resolvedXrefTrailer = null;
/**
* Returns the first trailer if at least one exists.
*
* @return the first trailer or null
*/
public CosDictionary getFirstTrailer()
{
return getPositionalTrailer(Position.Last);
}
/**
* Returns the last trailer if at least one exists.
*
* @return the last trailer ir null
*/
public CosDictionary getLastTrailer()
{
return getPositionalTrailer(Position.Last);
}
private enum Position
{
First,
Last
}
private CosDictionary getPositionalTrailer(Position position)
{
if (bytePosToXrefMap.Count == 0)
{
return null;
}
var ordered = bytePosToXrefMap.Keys.OrderBy(x => x);
var key = position == Position.First ? ordered.First() : ordered.Last();
if (!bytePosToXrefMap.TryGetValue(key, out XrefTrailerObj result))
{
return null;
}
return result.trailer;
}
/**
* Returns the count of trailers.
*
* @return the count of trailers.
*/
public int getTrailerCount()
{
return bytePosToXrefMap.Count;
}
/**
* Signals that a new XRef object (table or stream) starts.
* @param startBytePos the offset to start at
* @param type the type of the Xref object
*/
public void nextXrefObj(long startBytePos, XRefType type)
{
bytePosToXrefMap[startBytePos] = curXrefTrailerObj = new XrefTrailerObj();
curXrefTrailerObj.xrefType = type;
}
/**
* Returns the XRefTxpe of the resolved trailer.
*
* @return the XRefType or null.
*/
public XRefType? getXrefType()
{
return resolvedXrefTrailer?.xrefType;
}
/**
* Populate XRef HashMap of current XRef object.
* Will add an Xreftable entry that maps ObjectKeys to byte offsets in the file.
* @param objKey The objkey, with id and gen numbers
* @param offset The byte offset in this file
*/
public void setXRef(CosObjectKey objKey, long offset)
{
if (curXrefTrailerObj == null)
{
// should not happen...
// LOG.warn("Cannot add XRef entry for '" + objKey.getNumber() + "' because XRef start was not signalled.");
return;
}
// PDFBOX-3506 check before adding to the map, to avoid entries from the table being
// overwritten by obsolete entries in hybrid files (/XRefStm entry)
if (!curXrefTrailerObj.xrefTable.ContainsKey(objKey))
{
curXrefTrailerObj.xrefTable[objKey] = offset;
}
}
/**
* Adds trailer information for current XRef object.
*
* @param trailer the current document trailer dictionary
*/
public void setTrailer(CosDictionary trailer)
{
if (curXrefTrailerObj == null)
{
// should not happen...
//LOG.warn("Cannot add trailer because XRef start was not signalled.");
return;
}
curXrefTrailerObj.trailer = trailer;
}
/**
* Returns the trailer last set by {@link #setTrailer(COSDictionary)}.
*
* @return the current trailer.
*
*/
public CosDictionary getCurrentTrailer()
{
return curXrefTrailerObj.trailer;
}
/**
* Sets the byte position of the first XRef
* (has to be called after very last startxref was read).
* This is used to resolve chain of active XRef/trailer.
*
* In case startxref position is not found we output a
* warning and use all XRef/trailer objects combined
* in byte position order.
* Thus for incomplete PDF documents with missing
* startxref one could call this method with parameter value -1.
*
* @param startxrefBytePosValue starting position of the first XRef
*
*/
public void setStartxref(long startxrefBytePosValue)
{
if (resolvedXrefTrailer != null)
{
//LOG.warn("Method must be called only ones with last startxref value.");
return;
}
resolvedXrefTrailer = new XrefTrailerObj { trailer = new CosDictionary() };
bytePosToXrefMap.TryGetValue(startxrefBytePosValue, out XrefTrailerObj curObj);
List<long> xrefSeqBytePos = new List<long>();
if (curObj == null)
{
// no XRef at given position
//LOG.warn("Did not found XRef object at specified startxref position " + startxrefBytePosValue);
// use all objects in byte position order (last entries overwrite previous ones)
xrefSeqBytePos.AddRange(bytePosToXrefMap.Keys);
xrefSeqBytePos.Sort();
}
else
{
// copy xref type
resolvedXrefTrailer.xrefType = curObj.xrefType;
// found starting Xref object
// add this and follow chain defined by 'Prev' keys
xrefSeqBytePos.Add(startxrefBytePosValue);
while (curObj.trailer != null)
{
long prevBytePos = curObj.trailer.getLong(CosName.PREV, -1L);
if (prevBytePos == -1)
{
break;
}
bytePosToXrefMap.TryGetValue(prevBytePos, out curObj);
if (curObj == null)
{
//LOG.warn("Did not found XRef object pointed to by 'Prev' key at position " + prevBytePos);
break;
}
xrefSeqBytePos.Add(prevBytePos);
// sanity check to prevent infinite loops
if (xrefSeqBytePos.Count >= bytePosToXrefMap.Count)
{
break;
}
}
// have to reverse order so that later XRefs will overwrite previous ones
xrefSeqBytePos.Reverse();
}
// merge used and sorted XRef/trailer
foreach (long bPos in xrefSeqBytePos)
{
bytePosToXrefMap.TryGetValue(bPos, out curObj);
if (curObj.trailer != null)
{
resolvedXrefTrailer.trailer.addAll(curObj.trailer);
}
foreach (var item in curObj.xrefTable)
{
resolvedXrefTrailer.xrefTable[item.Key] = item.Value;
}
}
}
/**
* Gets the resolved trailer. Might return <code>null</code> in case
* {@link #setStartxref(long)} was not called before.
*
* @return the trailer if available
*/
public CosDictionary getTrailer()
{
return (resolvedXrefTrailer == null) ? null : resolvedXrefTrailer.trailer;
}
/**
* Gets the resolved xref table. Might return <code>null</code> in case
* {@link #setStartxref(long)} was not called before.
*
* @return the xrefTable if available
*/
public Dictionary<CosObjectKey, long> getXrefTable()
{
return (resolvedXrefTrailer == null) ? null : resolvedXrefTrailer.xrefTable;
}
/** Returns object numbers which are referenced as contained
* in object stream with specified object number.
*
* This will scan resolved xref table for all entries having negated
* stream object number as value.
*
* @param objstmObjNr object number of object stream for which contained object numbers
* should be returned
*
* @return set of object numbers referenced for given object stream
* or <code>null</code> if {@link #setStartxref(long)} was not
* called before so that no resolved xref table exists
*/
public ISet<long> getContainedObjectNumbers(int objstmObjNr)
{
if (resolvedXrefTrailer == null)
{
return null;
}
ISet<long> refObjNrs = new HashSet<long>();
long cmpVal = -objstmObjNr;
foreach (var xrefEntry in resolvedXrefTrailer.xrefTable)
{
if (xrefEntry.Value == cmpVal)
{
refObjNrs.Add(xrefEntry.Key.Number);
}
}
return refObjNrs;
}
/**
* Reset all data so that it can be used to rebuild the trailer.
*
*/
public void reset()
{
foreach (XrefTrailerObj trailerObj in bytePosToXrefMap.Values)
{
trailerObj.reset();
}
curXrefTrailerObj = null;
resolvedXrefTrailer = null;
}
}
}