/// <summary> /// Formats content of <see cref="DictionaryToken"/>. /// </summary> /// <param name="token">Token to be formatted.</param> public void Format(DictionaryToken token) { _output.Write('['); var elements = token.Elements.ToArray(); int allButLast = elements.Length - 1; for (int i = 0; i < allButLast; i++) { _output.Write("{\"key\": "); elements[i].Key.Render(this); _output.Write(", \"value\": "); elements[i].Value.Render(this); _output.Write("}, "); } if (elements.Length > 0) { var last = elements[elements.Length - 1]; _output.Write("{\"key\": "); last.Key.Render(this); _output.Write(", \"value\": "); last.Value.Render(this); _output.Write('}'); } _output.Write(']'); }
public IFont Generate(DictionaryToken dictionary, bool isLenientParsing) { var baseFont = dictionary.GetNameOrDefault(NameToken.BaseFont); var cMap = ReadEncoding(dictionary, out var isCMapPredefined); ICidFont cidFont; if (TryGetFirstDescendant(dictionary, out var descendantObject)) { DictionaryToken descendantFontDictionary; if (descendantObject is IndirectReferenceToken obj) { var parsed = DirectObjectFinder.Get<DictionaryToken>(obj, scanner); descendantFontDictionary = parsed; } else { descendantFontDictionary = (DictionaryToken) descendantObject; } cidFont = ParseDescendant(descendantFontDictionary, isLenientParsing); } else { throw new InvalidFontFormatException("No descendant font dictionary was declared for this Type 0 font. This dictionary should contain the CIDFont for the Type 0 font. " + dictionary); } var (ucs2CMap, isChineseJapaneseOrKorean) = GetUcs2CMap(dictionary, isCMapPredefined, cidFont); CMap toUnicodeCMap = null; if (dictionary.ContainsKey(NameToken.ToUnicode)) { var toUnicodeValue = dictionary.Data[NameToken.ToUnicode]; if (DirectObjectFinder.TryGet<StreamToken>(toUnicodeValue, scanner, out var toUnicodeStream)) { var decodedUnicodeCMap = toUnicodeStream?.Decode(filterProvider); if (decodedUnicodeCMap != null) { toUnicodeCMap = CMapCache.Parse(new ByteArrayInputBytes(decodedUnicodeCMap), isLenientParsing); } } else if (DirectObjectFinder.TryGet<NameToken>(toUnicodeValue, scanner, out var toUnicodeName)) { toUnicodeCMap = CMapCache.Get(toUnicodeName.Data); } else { throw new PdfDocumentFormatException($"Invalid type of toUnicode CMap encountered. Got: {toUnicodeValue}."); } } var font = new Type0Font(baseFont, cidFont, cMap, toUnicodeCMap, ucs2CMap, isChineseJapaneseOrKorean); return font; }
private static DescriptorFontFile GetFontFile(DictionaryToken dictionary) { if (dictionary.TryGet(NameToken.FontFile, out var value)) { if (!(value is IndirectReferenceToken obj)) { throw new NotSupportedException("We currently expect the FontFile to be an object reference."); } return(new DescriptorFontFile(obj, DescriptorFontFile.FontFileType.Type1)); } if (dictionary.TryGet(NameToken.FontFile2, out value)) { if (!(value is IndirectReferenceToken obj)) { throw new NotSupportedException("We currently expect the FontFile2 to be an object reference."); } return(new DescriptorFontFile(obj, DescriptorFontFile.FontFileType.TrueType)); } if (dictionary.TryGet(NameToken.FontFile3, out value)) { if (!(value is IndirectReferenceToken obj)) { throw new NotSupportedException("We currently expect the FontFile3 to be an object reference."); } return(new DescriptorFontFile(obj, DescriptorFontFile.FontFileType.FromSubtype)); } return(null); }
public IFont Generate(DictionaryToken dictionary) { var boundingBox = GetBoundingBox(dictionary); var fontMatrix = GetFontMatrix(dictionary); var firstCharacter = FontDictionaryAccessHelper.GetFirstCharacter(dictionary); var lastCharacter = FontDictionaryAccessHelper.GetLastCharacter(dictionary); var widths = FontDictionaryAccessHelper.GetWidths(scanner, dictionary); Encoding encoding = encodingReader.Read(dictionary); CMap toUnicodeCMap = null; if (dictionary.TryGet(NameToken.ToUnicode, out var toUnicodeObj)) { var toUnicode = DirectObjectFinder.Get <StreamToken>(toUnicodeObj, scanner); var decodedUnicodeCMap = toUnicode?.Decode(filterProvider, scanner); if (decodedUnicodeCMap != null) { toUnicodeCMap = CMapCache.Parse(new ByteArrayInputBytes(decodedUnicodeCMap)); } } var name = GetFontName(dictionary); return(new Type3Font(name, boundingBox, fontMatrix, encoding, firstCharacter, lastCharacter, widths, toUnicodeCMap)); }
public MarkedContentElement(int markedContentIdentifier, NameToken tag, DictionaryToken properties, string language, string actualText, string alternateDescription, string expandedForm, bool isArtifact, IReadOnlyList <MarkedContentElement> children, IReadOnlyList <Letter> letters, IReadOnlyList <PdfPath> paths, IReadOnlyList <IPdfImage> images, int index) { MarkedContentIdentifier = markedContentIdentifier; Tag = tag; Language = language; ActualText = actualText; AlternateDescription = alternateDescription; ExpandedForm = expandedForm; Properties = properties ?? new DictionaryToken(new Dictionary <NameToken, IToken>()); IsArtifact = isArtifact; Children = children ?? throw new ArgumentNullException(nameof(children)); Letters = letters ?? throw new ArgumentNullException(nameof(letters)); Paths = paths ?? throw new ArgumentNullException(nameof(paths)); Images = images ?? throw new ArgumentNullException(nameof(images)); Index = index; }
public IFont Get(DictionaryToken dictionary, bool isLenientParsing) { var type = dictionary.GetNameOrDefault(NameToken.Type); if (type != null && !type.Equals(NameToken.Font)) { var message = "The font dictionary did not have type 'Font'. " + dictionary; if (isLenientParsing) { log?.Error(message); } else { throw new InvalidFontFormatException(message); } } var subtype = dictionary.GetNameOrDefault(NameToken.Subtype); if (handlers.TryGetValue(subtype, out var handler)) { return(handler.Generate(dictionary, isLenientParsing)); } throw new NotImplementedException($"Parsing not implemented for fonts of type: {subtype}, please submit a pull request or an issue."); }
/// <summary> /// Creates a new <see cref="XObjectImage"/>. /// </summary> internal XObjectImage(PdfRectangle bounds, int widthInSamples, int heightInSamples, int bitsPerComponent, ColorSpace?colorSpace, bool isJpxEncoded, bool isImageMask, RenderingIntent renderingIntent, bool interpolate, IReadOnlyList <decimal> decode, DictionaryToken imageDictionary, IReadOnlyList <byte> rawBytes, Lazy <IReadOnlyList <byte> > bytes) { Bounds = bounds; WidthInSamples = widthInSamples; HeightInSamples = heightInSamples; BitsPerComponent = bitsPerComponent; ColorSpace = colorSpace; IsJpxEncoded = isJpxEncoded; IsImageMask = isImageMask; RenderingIntent = renderingIntent; Interpolate = interpolate; Decode = decode; ImageDictionary = imageDictionary ?? throw new ArgumentNullException(nameof(imageDictionary)); RawBytes = rawBytes; this.bytes = bytes ?? throw new ArgumentNullException(nameof(bytes)); }
private CMap ReadEncoding(DictionaryToken dictionary, out bool isCMapPredefined) { isCMapPredefined = false; CMap result; if (dictionary.TryGet(NameToken.Encoding, scanner, out NameToken encodingName)) { if (!CMapCache.TryGet(encodingName.Data, out var cmap)) { throw new InvalidOperationException($"Missing CMap named {encodingName.Data}."); } result = cmap ?? throw new InvalidOperationException($"Missing CMap named {encodingName.Data}."); isCMapPredefined = true; } else if (dictionary.TryGet(NameToken.Encoding, scanner, out StreamToken stream)) { var decoded = stream.Decode(filterProvider, scanner); var cmap = CMapCache.Parse(new ByteArrayInputBytes(decoded)); result = cmap ?? throw new InvalidOperationException($"Could not read CMap from stream in the dictionary: {dictionary}"); } else { throw new InvalidOperationException( $"Could not read the encoding, expected a name or a stream but it was not found in the dictionary: {dictionary}"); } return(result); }
public static Dictionary <string, IToken> GetOrCreateDict(this Dictionary <string, IToken> dict, string key) { if (dict.TryGetValue(key, out var item)) { if (!(item is DictionaryToken dt)) { throw new ApplicationException("Expected dictionary token, got " + item.GetType()); } if (dt.Data is Dictionary <string, IToken> mutable) { return(mutable); } mutable = dt.Data. ToDictionary(x => x.Key, x => x.Value); dict[key] = DictionaryToken.With(mutable); return(mutable); } var created = new Dictionary <string, IToken>(); dict[key] = DictionaryToken.With(created); return(created); }
private static IEnumerable <long> GetObjectNumbers(DictionaryToken dictionary) { // The number one greater than the highest object number used in this section or in any section for which this is an update. if (!dictionary.TryGet(NameToken.Size, out var sizeToken) || !(sizeToken is NumericToken sizeNumeric)) { throw new PdfDocumentFormatException($"The stream dictionary must contain a numeric size value: {dictionary}."); } var objNums = new List <long>(); if (dictionary.TryGet(NameToken.Index, out var indexToken) && indexToken is ArrayToken indexArrayToken) { // An array containing a pair of integers for each subsection in this section. // Pair[0] is the first object number in the subsection; Pair[1] is the number of entries in the subsection. for (var i = 0; i < indexArrayToken.Length; i += 2) { var firstObjectNumber = indexArrayToken.GetNumeric(i).Int; var size = indexArrayToken.GetNumeric(i + 1).Int; for (var j = 0; j < size; j++) { objNums.Add(firstObjectNumber + j); } } } else { for (var i = 0; i < sizeNumeric.Int; i++) { objNums.Add(i); } } return(objNums); }
private Encoding ReadEncodingDictionary(DictionaryToken encodingDictionary, Encoding fontEncoding) { if (encodingDictionary == null) { return(null); } Encoding baseEncoding; if (encodingDictionary.TryGet(NameToken.BaseEncoding, out var baseEncodingToken) && baseEncodingToken is NameToken baseEncodingName) { if (!Encoding.TryGetNamedEncoding(baseEncodingName, out baseEncoding)) { throw new InvalidFontFormatException($"No encoding found with name {baseEncodingName} to use as base encoding."); } } else { // TODO: This isn't true for non-symbolic fonts or latin fonts (based on OS?) see section 5.5.5 baseEncoding = fontEncoding ?? StandardEncoding.Instance; } if (!encodingDictionary.TryGet(NameToken.Differences, out var differencesBase)) { return(baseEncoding); } var differenceArray = DirectObjectFinder.Get <ArrayToken>(differencesBase, pdfScanner); var differences = ProcessDifferences(differenceArray); var newEncoding = new DifferenceBasedEncoding(baseEncoding, differences); return(newEncoding); }
private CropBox GetCropBox(DictionaryToken dictionary, PageTreeMembers pageTreeMembers, MediaBox mediaBox, bool isLenientParsing) { CropBox cropBox; if (dictionary.TryGet(NameToken.CropBox, out var cropBoxObject) && DirectObjectFinder.TryGet(cropBoxObject, pdfScanner, out ArrayToken cropBoxArray)) { if (cropBoxArray.Length != 4 && isLenientParsing) { log.Error($"The CropBox was the wrong length in the dictionary: {dictionary}. Array was: {cropBoxArray}."); cropBox = new CropBox(mediaBox.Bounds); return(cropBox); } cropBox = new CropBox(cropBoxArray.ToIntRectangle()); } else { cropBox = pageTreeMembers.GetCropBox() ?? new CropBox(mediaBox.Bounds); } return(cropBox); }
/// <inheritdoc /> public IReadOnlyList <IFilter> GetFilters(DictionaryToken dictionary) { if (dictionary == null) { throw new ArgumentNullException(nameof(dictionary)); } var token = dictionary.GetObjectOrDefault(NameToken.Filter, NameToken.F); if (token == null) { return(EmptyArray <IFilter> .Instance); } switch (token) { case ArrayToken filters: var result = new IFilter[filters.Data.Count]; for (var i = 0; i < filters.Data.Count; i++) { var filterToken = filters.Data[i]; var filterName = ((NameToken)filterToken).Data; result[i] = GetFilterStrict(filterName); } return(result); case NameToken name: return(new[] { GetFilterStrict(name.Data) }); default: throw new PdfDocumentFormatException($"The filter for the stream was not a valid object. Expected name or array, instead got: {token}."); } }
private MediaBox GetMediaBox(int number, DictionaryToken dictionary, PageTreeMembers pageTreeMembers) { MediaBox mediaBox; if (dictionary.TryGet(NameToken.MediaBox, out var mediaboxObject) && DirectObjectFinder.TryGet(mediaboxObject, pdfScanner, out ArrayToken mediaboxArray)) { if (mediaboxArray.Length != 4) { log.Error($"The MediaBox was the wrong length in the dictionary: {dictionary}. Array was: {mediaboxArray}. Defaulting to US Letter."); mediaBox = MediaBox.Letter; return(mediaBox); } mediaBox = new MediaBox(mediaboxArray.ToIntRectangle(pdfScanner)); } else { mediaBox = pageTreeMembers.MediaBox; if (mediaBox == null) { log.Error($"The MediaBox was the wrong missing for page {number}. Using US Letter."); // PDFBox defaults to US Letter. mediaBox = MediaBox.Letter; } } return(mediaBox); }
/// <inheritdoc /> /// <summary> /// Create a new <see cref="T:UglyToad.PdfPig.AcroForms.Fields.AcroSignatureField" />. /// </summary> public AcroSignatureField(DictionaryToken dictionary, string fieldType, uint fieldFlags, AcroFieldCommonInformation information, int?pageNumber, PdfRectangle?bounds) : base(dictionary, fieldType, fieldFlags, AcroFieldType.Signature, information, pageNumber, bounds) { }
public IReadOnlyList <IFilter> GetFilters(DictionaryToken dictionary) { if (dictionary == null) { throw new ArgumentNullException(nameof(dictionary)); } if (!dictionary.TryGet(NameToken.Filter, out var token)) { return(new IFilter[0]); } switch (token) { case ArrayToken filters: // TODO: presumably this may be invalid... return(filters.Data.Select(x => GetFilterStrict(((NameToken)x).Data)).ToList()); case NameToken name: return(new[] { GetFilterStrict(name.Data) }); default: throw new PdfDocumentFormatException($"The filter for the stream was not a valid object. Expected name or array, instead got: {token}."); } }
/// <summary> /// Create a new <see cref="AcroFieldBase"/>. /// </summary> /// <param name="dictionary">The dictionary for this field.</param> /// <param name="fieldType">The type of this field.</param> /// <param name="fieldFlags">The flags specifying behaviour for this field.</param> /// <param name="information">Additional information for this field.</param> protected AcroFieldBase(DictionaryToken dictionary, string fieldType, uint fieldFlags, AcroFieldCommonInformation information) { Dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); FieldType = fieldType ?? throw new ArgumentNullException(nameof(fieldType)); FieldFlags = fieldFlags; Information = information ?? new AcroFieldCommonInformation(null, null, null, null); }
public void Build() { if (pagesTokenReferences.Count < 1) { throw new PdfDocumentFormatException("Empty document"); } var pagesDictionary = new DictionaryToken(new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Pages }, { NameToken.Kids, new ArrayToken(pagesTokenReferences) }, { NameToken.Count, new NumericToken(pageCount) } }); var pagesRef = context.WriteToken(pagesDictionary, (int)rootPagesReference.Data.ObjectNumber); var catalog = new DictionaryToken(new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Catalog }, { NameToken.Pages, pagesRef } }); var catalogRef = context.WriteToken(catalog); context.Flush(currentVersion, catalogRef); Close(); }
public static decimal[] GetWidths(IPdfTokenScanner pdfScanner, DictionaryToken dictionary, bool isLenientParsing) { if (!dictionary.TryGet(NameToken.Widths, out var token)) { throw new InvalidFontFormatException($"No widths array found for the font: {dictionary}."); } var widthArray = DirectObjectFinder.Get <ArrayToken>(token, pdfScanner); var result = new decimal[widthArray.Data.Count]; for (int i = 0; i < widthArray.Data.Count; i++) { var arrayToken = widthArray.Data[i]; if (!(arrayToken is NumericToken number)) { throw new InvalidFontFormatException($"Token which was not a number found in the widths array: {arrayToken}."); } result[i] = number.Data; } return(result); }
private string SafeKeyAccess(DictionaryToken dictionary, NameToken keyName) { if (!dictionary.TryGet(keyName, out var token)) { return(string.Empty); } if (token is StringToken str) { return(str.Data); } if (token is HexToken hex) { return(hex.Data); } if (token is IndirectReferenceToken obj) { if (DirectObjectFinder.TryGet(obj, pdfScanner, out StringToken stringToken)) { return(stringToken.Data); } if (DirectObjectFinder.TryGet(obj, pdfScanner, out HexToken hexToken)) { return(hexToken.Data); } throw new PdfDocumentFormatException($"Could not get key for name: {keyName} in {dictionary}."); } return(string.Empty); }
private static List <long> GetObjectNumbers(DictionaryToken dictionary) { if (!dictionary.TryGet(NameToken.Size, out var sizeToken) || !(sizeToken is NumericToken sizeNumeric)) { throw new PdfDocumentFormatException($"The stream dictionary must contain a numeric size value: {dictionary}."); } var indexArray = new[] { 0, sizeNumeric.Int }; if (dictionary.TryGet(NameToken.Index, out var indexToken) && indexToken is ArrayToken indexArrayToken) { indexArray = new[] { indexArrayToken.GetNumeric(0).Int, indexArrayToken.GetNumeric(1).Int }; } List <long> objNums = new List <long>(); var firstObjectNumber = indexArray[0]; var size = indexArray[1]; for (var i = 0; i < size; i++) { objNums.Add(firstObjectNumber + i); } return(objNums); }
private void LoadFontDictionary(DictionaryToken fontDictionary, bool isLenientParsing) { foreach (var pair in fontDictionary.Data) { if (!(pair.Value is IndirectReferenceToken objectKey)) { if (isLenientParsing) { continue; } throw new InvalidOperationException($"The font with name {pair.Key} did not link to an object key. Value was: {pair.Value}."); } var reference = objectKey.Data; currentResourceState[NameToken.Create(pair.Key)] = reference; if (loadedFonts.ContainsKey(reference)) { continue; } var fontObject = DirectObjectFinder.Get <DictionaryToken>(objectKey, scanner); if (fontObject == null) { throw new InvalidOperationException($"Could not retrieve the font with name: {pair.Key} which should have been object {objectKey}"); } loadedFonts[reference] = fontFactory.Get(fontObject, isLenientParsing); } }
public ImageStored(DictionaryToken streamDictionary, byte[] streamData, int objectNumber) { Id = Guid.NewGuid(); StreamDictionary = streamDictionary; StreamData = streamData; ObjectNumber = objectNumber; }
/// <summary> /// Create a new <see cref="InlineImage"/>. /// </summary> internal InlineImage(PdfRectangle bounds, int widthInSamples, int heightInSamples, int bitsPerComponent, bool isImageMask, RenderingIntent renderingIntent, bool interpolate, ColorSpace?colorSpace, IReadOnlyList <decimal> decode, IReadOnlyList <byte> bytes, IReadOnlyList <IFilter> filters, DictionaryToken streamDictionary) { Bounds = bounds; WidthInSamples = widthInSamples; HeightInSamples = heightInSamples; ColorSpace = colorSpace; Decode = decode; BitsPerComponent = bitsPerComponent; IsImageMask = isImageMask; RenderingIntent = renderingIntent; Interpolate = interpolate; RawBytes = bytes; bytesFactory = new Lazy <IReadOnlyList <byte> >(() => { var b = bytes.ToArray(); for (var i = 0; i < filters.Count; i++) { var filter = filters[i]; b = filter.Decode(b, streamDictionary, i); } return(b); }); }
private CMap ReadEncoding(DictionaryToken dictionary, out bool isCMapPredefined) { isCMapPredefined = false; CMap result = default(CMap); if (dictionary.TryGet(NameToken.Encoding, out var value)) { if (value is NameToken encodingName) { var cmap = CMapCache.Get(encodingName.Data); result = cmap ?? throw new InvalidOperationException("Missing CMap for " + encodingName.Data); isCMapPredefined = true; } else if (value is StreamToken stream) { var decoded = stream.Decode(filterProvider); var cmap = CMapCache.Parse(new ByteArrayInputBytes(decoded), false); result = cmap ?? throw new InvalidOperationException("Could not read CMap for " + dictionary); } else { throw new InvalidOperationException("Could not read the encoding, expected a name or a stream but got a: " + value.GetType().Name); } } return result; }
public IFont Generate(DictionaryToken dictionary, bool isLenientParsing) { var firstCharacter = FontDictionaryAccessHelper.GetFirstCharacter(dictionary); var widths = FontDictionaryAccessHelper.GetWidths(pdfScanner, dictionary, isLenientParsing); var descriptor = FontDictionaryAccessHelper.GetFontDescriptor(pdfScanner, fontDescriptorFactory, dictionary, isLenientParsing); // TODO: use the parsed font fully. var font = ParseTrueTypeFont(descriptor); var name = FontDictionaryAccessHelper.GetName(pdfScanner, dictionary, descriptor, isLenientParsing); CMap toUnicodeCMap = null; if (dictionary.TryGet(NameToken.ToUnicode, out var toUnicodeObj)) { var toUnicode = DirectObjectFinder.Get <StreamToken>(toUnicodeObj, pdfScanner); var decodedUnicodeCMap = toUnicode.Decode(filterProvider); if (decodedUnicodeCMap != null) { toUnicodeCMap = cMapCache.Parse(new ByteArrayInputBytes(decodedUnicodeCMap), isLenientParsing); } } Encoding encoding = encodingReader.Read(dictionary, isLenientParsing, descriptor); return(new TrueTypeSimpleFont(name, descriptor, toUnicodeCMap, encoding, font, firstCharacter, widths)); }
private static bool TryGetFirstDescendant(DictionaryToken dictionary, out IToken descendant) { descendant = null; if (!dictionary.TryGet(NameToken.DescendantFonts, out var value)) { return false; } if (value is IndirectReferenceToken obj) { descendant = obj; return true; } if (value is ArrayToken array && array.Data.Count > 0) { if (array.Data[0] is IndirectReferenceToken objArr) { descendant = objArr; } else if (array.Data[0] is DictionaryToken dict) { descendant = dict; } else { return false; } return true; } return false; }
internal Page(int number, DictionaryToken dictionary, MediaBox mediaBox, CropBox cropBox, PageRotationDegrees rotation, PageContent content, AnnotationProvider annotationProvider, IPdfTokenScanner pdfScanner) { if (number <= 0) { throw new ArgumentOutOfRangeException(nameof(number), "Page number cannot be 0 or negative."); } Dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); Number = number; MediaBox = mediaBox; CropBox = cropBox; Rotation = rotation; Content = content; textLazy = new Lazy <string>(() => GetText(Content)); Width = mediaBox.Bounds.Width; Height = mediaBox.Bounds.Height; Size = mediaBox.Bounds.GetPageSize(); ExperimentalAccess = new Experimental(this, annotationProvider); this.annotationProvider = annotationProvider; this.pdfScanner = pdfScanner ?? throw new ArgumentNullException(nameof(pdfScanner)); }
private static CMap GetUcs2CMap(DictionaryToken dictionary, bool isCMapPredefined, bool usesDescendantAdobeFont) { if (!isCMapPredefined) { return(null); } /* * If the font is a composite font that uses one of the predefined CMaps except Identity–H and Identity–V or whose descendant * CIDFont uses the Adobe-GB1, Adobe-CNS1, Adobe-Japan1, or Adobe-Korea1 character collection use a UCS2 CMap. */ var encodingName = dictionary.GetNameOrDefault(NameToken.Encoding); if (encodingName == null) { return(null); } var isPredefinedIdentityMap = encodingName.Equals(NameToken.IdentityH) || encodingName.Equals(NameToken.IdentityV); if (isPredefinedIdentityMap && !usesDescendantAdobeFont) { return(null); } throw new NotSupportedException("Support for UCS2 CMaps are not implemented yet. Please raise an issue."); }
public bool FindPage(DictionaryToken currentPageDictionary, int soughtPageNumber, List <int> pageNumbersObserved, PageTreeMembers pageTreeMembers) { var type = currentPageDictionary.GetNameOrDefault(NameToken.Type); if (type?.Equals(NameToken.Page) == true) { var pageNumber = GetNextPageNumber(pageNumbersObserved); bool found = pageNumber == soughtPageNumber; locatedPages[pageNumber] = currentPageDictionary; pageNumbersObserved.Add(pageNumber); return(found); } if (type?.Equals(NameToken.Pages) != true) { log.Warn("Did not find the expected type (Page or Pages) in dictionary: " + currentPageDictionary); return(false); } if (currentPageDictionary.TryGet(NameToken.MediaBox, out var token)) { var mediaBox = DirectObjectFinder.Get <ArrayToken>(token, pdfScanner); pageTreeMembers.MediaBox = new MediaBox(new PdfRectangle(mediaBox.GetNumeric(0).Data, mediaBox.GetNumeric(1).Data, mediaBox.GetNumeric(2).Data, mediaBox.GetNumeric(3).Data)); } if (!currentPageDictionary.TryGet(NameToken.Kids, out var kids) || !(kids is ArrayToken kidsArray)) { return(false); } pageFactory.LoadResources(currentPageDictionary, isLenientParsing); bool childFound = false; foreach (var kid in kidsArray.Data) { // todo: exit early var child = DirectObjectFinder.Get <DictionaryToken>(kid, pdfScanner); var thisPageMatches = FindPage(child, soughtPageNumber, pageNumbersObserved, pageTreeMembers); if (thisPageMatches) { childFound = true; break; } } return(childFound); }