From 1fb6ec41d1db51dcf2a1caff862ba5e47adccd46 Mon Sep 17 00:00:00 2001 From: Eliot Jones Date: Sun, 14 Jan 2018 15:01:18 +0000 Subject: [PATCH] remove cos object key completely and test indirect reference --- .../ContentStream/IndirectReferenceTests.cs | 89 +++++ .../Cos/CosObjectKeyTests.cs | 35 -- .../DictionaryValueAccessorExtensions.cs | 13 +- src/UglyToad.PdfPig/Cos/CosObject.cs | 5 - src/UglyToad.PdfPig/Cos/CosObjectKey.cs | 92 ----- src/UglyToad.PdfPig/Parser/DynamicParser.cs | 9 +- .../Parser/XrefTrailerResolver.cs | 334 ------------------ 7 files changed, 91 insertions(+), 486 deletions(-) create mode 100644 src/UglyToad.PdfPig.Tests/ContentStream/IndirectReferenceTests.cs delete mode 100644 src/UglyToad.PdfPig.Tests/Cos/CosObjectKeyTests.cs delete mode 100644 src/UglyToad.PdfPig/Cos/CosObjectKey.cs delete mode 100644 src/UglyToad.PdfPig/Parser/XrefTrailerResolver.cs diff --git a/src/UglyToad.PdfPig.Tests/ContentStream/IndirectReferenceTests.cs b/src/UglyToad.PdfPig.Tests/ContentStream/IndirectReferenceTests.cs new file mode 100644 index 00000000..d5c02d75 --- /dev/null +++ b/src/UglyToad.PdfPig.Tests/ContentStream/IndirectReferenceTests.cs @@ -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)); + } + } +} diff --git a/src/UglyToad.PdfPig.Tests/Cos/CosObjectKeyTests.cs b/src/UglyToad.PdfPig.Tests/Cos/CosObjectKeyTests.cs deleted file mode 100644 index 19540c3b..00000000 --- a/src/UglyToad.PdfPig.Tests/Cos/CosObjectKeyTests.cs +++ /dev/null @@ -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(action); - } - - [Fact] - public void CanLookupInDictionary() - { - var key1 = new CosObjectKey(3, 0); - var key2 = new CosObjectKey(3, 0); - - var dictionary = new Dictionary - { - {key1, 5} - }; - - var result = dictionary[key2]; - - Assert.Equal(5, result); - } - } -} diff --git a/src/UglyToad.PdfPig/ContentStream/TypedAccessors/DictionaryValueAccessorExtensions.cs b/src/UglyToad.PdfPig/ContentStream/TypedAccessors/DictionaryValueAccessorExtensions.cs index d32e3928..530862eb 100644 --- a/src/UglyToad.PdfPig/ContentStream/TypedAccessors/DictionaryValueAccessorExtensions.cs +++ b/src/UglyToad.PdfPig/ContentStream/TypedAccessors/DictionaryValueAccessorExtensions.cs @@ -140,18 +140,7 @@ 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) diff --git a/src/UglyToad.PdfPig/Cos/CosObject.cs b/src/UglyToad.PdfPig/Cos/CosObject.cs index 67dec765..a5526105 100644 --- a/src/UglyToad.PdfPig/Cos/CosObject.cs +++ b/src/UglyToad.PdfPig/Cos/CosObject.cs @@ -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. diff --git a/src/UglyToad.PdfPig/Cos/CosObjectKey.cs b/src/UglyToad.PdfPig/Cos/CosObjectKey.cs deleted file mode 100644 index 63e4fdd2..00000000 --- a/src/UglyToad.PdfPig/Cos/CosObjectKey.cs +++ /dev/null @@ -1,92 +0,0 @@ -namespace UglyToad.PdfPig.Cos -{ - using System; - - internal class CosObjectKey : IComparable - { - 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; - } - } - } - - } - -} diff --git a/src/UglyToad.PdfPig/Parser/DynamicParser.cs b/src/UglyToad.PdfPig/Parser/DynamicParser.cs index 103ccf71..ec4b0284 100644 --- a/src/UglyToad.PdfPig/Parser/DynamicParser.cs +++ b/src/UglyToad.PdfPig/Parser/DynamicParser.cs @@ -32,14 +32,7 @@ arguments.IsLenientParsing, 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) { diff --git a/src/UglyToad.PdfPig/Parser/XrefTrailerResolver.cs b/src/UglyToad.PdfPig/Parser/XrefTrailerResolver.cs deleted file mode 100644 index 2d1d3a9d..00000000 --- a/src/UglyToad.PdfPig/Parser/XrefTrailerResolver.cs +++ /dev/null @@ -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 xrefTable = new Dictionary(); - - /** - * 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 bytePosToXrefMap = new Dictionary(); - 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 xrefSeqBytePos = new List(); - - 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 null 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 null in case - * {@link #setStartxref(long)} was not called before. - * - * @return the xrefTable if available - */ - public Dictionary 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 null if {@link #setStartxref(long)} was not - * called before so that no resolved xref table exists - */ - public ISet getContainedObjectNumbers(int objstmObjNr) - { - if (resolvedXrefTrailer == null) - { - return null; - } - ISet refObjNrs = new HashSet(); - 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; - } - } -}