Example #1
0
        private static bool CheckIfIsPage(DictionaryToken nodeDictionary, IndirectReference parentReference, bool isRoot, IPdfTokenScanner pdfTokenScanner, bool isLenientParsing)
        {
            var isPage = false;

            if (!nodeDictionary.TryGet(NameToken.Type, pdfTokenScanner, out NameToken type))
            {
                if (!isLenientParsing)
                {
                    throw new PdfDocumentFormatException($"Node in the document pages tree did not define a type: {nodeDictionary}.");
                }

                if (!nodeDictionary.TryGet(NameToken.Kids, pdfTokenScanner, out ArrayToken _))
                {
                    isPage = true;
                }
            }
            else
            {
                isPage = type.Equals(NameToken.Page);

                if (!isPage && !type.Equals(NameToken.Pages) && !isLenientParsing)
                {
                    throw new PdfDocumentFormatException($"Node in the document pages tree defined invalid type: {nodeDictionary}.");
                }
            }

            if (!isLenientParsing && !isRoot)
            {
                if (!nodeDictionary.TryGet(NameToken.Parent, pdfTokenScanner, out IndirectReferenceToken parentReferenceToken))
                {
                    throw new PdfDocumentFormatException($"Could not find parent indirect reference token on pages tree node: {nodeDictionary}.");
                }

                if (!parentReferenceToken.Data.Equals(parentReference))
                {
                    throw new PdfDocumentFormatException($"Pages tree node parent reference {parentReferenceToken.Data} did not match actual parent {parentReference}.");
                }
            }

            return(isPage);
        }
Example #2
0
        private static (IndirectReference, DictionaryToken) ParseTrailer(CrossReferenceTable crossReferenceTable, bool isLenientParsing, IPdfTokenScanner pdfTokenScanner,
                                                                         out EncryptionDictionary encryptionDictionary)
        {
            encryptionDictionary = null;

            if (crossReferenceTable.Trailer.EncryptionToken != null)
            {
                if (!DirectObjectFinder.TryGet(crossReferenceTable.Trailer.EncryptionToken, pdfTokenScanner, out DictionaryToken encryptionDictionaryToken))
                {
                    throw new PdfDocumentFormatException($"Unrecognized encryption token in trailer: {crossReferenceTable.Trailer.EncryptionToken}.");
                }

                encryptionDictionary = EncryptionDictionaryFactory.Read(encryptionDictionaryToken, pdfTokenScanner);
            }

            var rootDictionary = DirectObjectFinder.Get <DictionaryToken>(crossReferenceTable.Trailer.Root, pdfTokenScanner);

            if (!rootDictionary.ContainsKey(NameToken.Type) && isLenientParsing)
            {
                rootDictionary = rootDictionary.With(NameToken.Type, NameToken.Catalog);
            }

            return(crossReferenceTable.Trailer.Root, rootDictionary);
        }
Example #3
0
            private (ObjectToken, int) CopyPagesTree(PageTreeNode treeNode, IndirectReferenceToken treeParentReference, IPdfTokenScanner tokenScanner)
            {
                Debug.Assert(!treeNode.IsPage);

                var currentNodeReserved  = context.ReserveNumber();
                var currentNodeReference = new IndirectReferenceToken(new IndirectReference(currentNodeReserved, 0));

                var pageReferences = new List <IndirectReferenceToken>();
                var nodeCount      = 0;

                foreach (var pageNode in treeNode.Children)
                {
                    ObjectToken newEntry;
                    if (!pageNode.IsPage)
                    {
                        var count = 0;
                        (newEntry, count) = CopyPagesTree(pageNode, currentNodeReference, tokenScanner);
                        nodeCount        += count;
                    }
                    else
                    {
                        newEntry = CopyPageNode(pageNode, currentNodeReference, tokenScanner);
                        ++nodeCount;
                    }

                    pageReferences.Add(new IndirectReferenceToken(newEntry.Number));
                }

                var newPagesNode = new Dictionary <NameToken, IToken>
                {
                    { NameToken.Type, NameToken.Pages },
                    { NameToken.Kids, new ArrayToken(pageReferences) },
                    { NameToken.Count, new NumericToken(nodeCount) },
                    { NameToken.Parent, treeParentReference }
                };

                foreach (var pair in treeNode.NodeDictionary.Data)
                {
                    if (IgnoreKeyForPagesNode(pair))
                    {
                        continue;
                    }

                    newPagesNode[NameToken.Create(pair.Key)] = CopyToken(pair.Value, tokenScanner);
                }

                var pagesDictionary = new DictionaryToken(newPagesNode);

                return(context.WriteObject(memory, pagesDictionary, currentNodeReserved), nodeCount);
            }
Example #4
0
 private static bool TryGetAction(DictionaryToken actionDictionary, Catalog catalog, IPdfTokenScanner pdfScanner,
                                  IReadOnlyDictionary <string, ExplicitDestination> namedDestinations,
                                  ILog log,
                                  out (bool isExternal, string externalFileName, ExplicitDestination destination) result)
Example #5
0
 public IReadOnlyList <IFilter> GetFilters(DictionaryToken dictionary, IPdfTokenScanner scanner)
 {
     return(new List <IFilter>());
 }
Example #6
0
        public InlineImage CreateInlineImage(TransformationMatrix transformationMatrix, IFilterProvider filterProvider,
                                             IPdfTokenScanner tokenScanner,
                                             RenderingIntent defaultRenderingIntent,
                                             IResourceStore resourceStore)
        {
            if (Properties == null || Bytes == null)
            {
                throw new InvalidOperationException($"Inline image builder not completely defined before calling {nameof(CreateInlineImage)}.");
            }

            bool TryMapColorSpace(NameToken name, out ColorSpace colorSpaceResult)
            {
                if (name.TryMapToColorSpace(out colorSpaceResult))
                {
                    return(true);
                }

                if (TryExtendedColorSpaceNameMapping(name, out colorSpaceResult))
                {
                    return(true);
                }

                if (!resourceStore.TryGetNamedColorSpace(name, out var colorSpaceNamedToken))
                {
                    return(false);
                }

                if (colorSpaceNamedToken.Name.TryMapToColorSpace(out colorSpaceResult))
                {
                    return(true);
                }

                if (TryExtendedColorSpaceNameMapping(colorSpaceNamedToken.Name, out colorSpaceResult))
                {
                    return(true);
                }

                return(false);
            }

            var bounds = transformationMatrix.Transform(new PdfRectangle(new PdfPoint(1, 1),
                                                                         new PdfPoint(0, 0)));

            var width = GetByKeys <NumericToken>(NameToken.Width, NameToken.W, true).Int;

            var height = GetByKeys <NumericToken>(NameToken.Height, NameToken.H, true).Int;

            var maskToken = GetByKeys <BooleanToken>(NameToken.ImageMask, NameToken.Im, false);

            var isMask = maskToken?.Data == true;

            var bitsPerComponent = GetByKeys <NumericToken>(NameToken.BitsPerComponent, NameToken.Bpc, !isMask)?.Int ?? 1;

            var colorSpace = default(ColorSpace?);

            if (!isMask)
            {
                var colorSpaceName = GetByKeys <NameToken>(NameToken.ColorSpace, NameToken.Cs, false);

                if (colorSpaceName == null)
                {
                    var colorSpaceArray = GetByKeys <ArrayToken>(NameToken.ColorSpace, NameToken.Cs, true);

                    if (colorSpaceArray.Length == 0)
                    {
                        throw new PdfDocumentFormatException("Empty ColorSpace array defined for inline image.");
                    }

                    if (!(colorSpaceArray.Data[0] is NameToken firstColorSpaceName))
                    {
                        throw new PdfDocumentFormatException($"Invalid ColorSpace array defined for inline image: {colorSpaceArray}.");
                    }

                    if (!TryMapColorSpace(firstColorSpaceName, out var colorSpaceMapped))
                    {
                        throw new PdfDocumentFormatException($"Invalid ColorSpace defined for inline image: {firstColorSpaceName}.");
                    }

                    colorSpace = colorSpaceMapped;
                }
                else
                {
                    if (!TryMapColorSpace(colorSpaceName, out var colorSpaceMapped))
                    {
                        throw new PdfDocumentFormatException($"Invalid ColorSpace defined for inline image: {colorSpaceName}.");
                    }

                    colorSpace = colorSpaceMapped;
                }
            }

            var renderingIntent = GetByKeys <NameToken>(NameToken.Intent, null, false)?.Data?.ToRenderingIntent() ?? defaultRenderingIntent;

            var filterNames = new List <NameToken>();

            var filterName = GetByKeys <NameToken>(NameToken.Filter, NameToken.F, false);

            if (filterName == null)
            {
                var filterArray = GetByKeys <ArrayToken>(NameToken.Filter, NameToken.F, false);

                if (filterArray != null)
                {
                    filterNames.AddRange(filterArray.Data.OfType <NameToken>());
                }
            }
            else
            {
                filterNames.Add(filterName);
            }

            var filters = filterProvider.GetNamedFilters(filterNames);

            var decodeRaw = GetByKeys <ArrayToken>(NameToken.Decode, NameToken.D, false) ?? new ArrayToken(EmptyArray <IToken> .Instance);

            var decode = decodeRaw.Data.OfType <NumericToken>().Select(x => x.Data).ToArray();

            var filterDictionaryEntries = new Dictionary <NameToken, IToken>();
            var decodeParamsDict        = GetByKeys <DictionaryToken>(NameToken.DecodeParms, NameToken.Dp, false);

            if (decodeParamsDict == null)
            {
                var decodeParamsArray = GetByKeys <ArrayToken>(NameToken.DecodeParms, NameToken.Dp, false);

                if (decodeParamsArray != null)
                {
                    filterDictionaryEntries[NameToken.DecodeParms] = decodeParamsArray;
                }
            }
            else
            {
                filterDictionaryEntries[NameToken.DecodeParms] = decodeParamsDict;
            }

            var streamDictionary = new DictionaryToken(filterDictionaryEntries);

            var interpolate = GetByKeys <BooleanToken>(NameToken.Interpolate, NameToken.I, false)?.Data ?? false;

            return(new InlineImage(bounds, width, height, bitsPerComponent, isMask, renderingIntent, interpolate, colorSpace, decode, Bytes,
                                   filters,
                                   streamDictionary));
        }
Example #7
0
 public BookmarksProvider(ILog log, IPdfTokenScanner pdfScanner)
 {
     this.log        = log;
     this.pdfScanner = pdfScanner;
 }
        public static EncryptionDictionary Read(DictionaryToken encryptionDictionary, IPdfTokenScanner tokenScanner)
        {
            if (encryptionDictionary == null)
            {
                throw new ArgumentNullException(nameof(encryptionDictionary));
            }

            var filter = encryptionDictionary.Get <NameToken>(NameToken.Filter, tokenScanner);

            var code = EncryptionAlgorithmCode.Unrecognized;

            if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.V, tokenScanner, out NumericToken vNum))
            {
                code = (EncryptionAlgorithmCode)vNum.Int;
            }

            var length = default(int?);

            if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.Length, tokenScanner, out NumericToken lengthToken))
            {
                length = lengthToken.Int;
            }

            var revision = default(int);

            if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.R, tokenScanner, out NumericToken revisionToken))
            {
                revision = revisionToken.Int;
            }

            byte[] ownerBytes = null;
            if (encryptionDictionary.TryGet(NameToken.O, out IToken ownerToken))
            {
                if (ownerToken is StringToken ownerString)
                {
                    ownerBytes = ownerString.GetBytes();
                }
                else if (ownerToken is HexToken ownerHex)
                {
                    ownerBytes = ownerHex.Bytes.ToArray();
                }
            }

            byte[] userBytes = null;
            if (encryptionDictionary.TryGet(NameToken.U, out IToken userToken))
            {
                if (userToken is StringToken userString)
                {
                    userBytes = userString.GetBytes();
                }
                else if (userToken is HexToken userHex)
                {
                    userBytes = userHex.Bytes.ToArray();
                }
            }

            var access = default(UserAccessPermissions);

            if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.P, tokenScanner, out NumericToken accessToken))
            {
                access = (UserAccessPermissions)accessToken.Int;
            }

            byte[] userEncryptionBytes = null, ownerEncryptionBytes = null;
            if (revision >= 5)
            {
                ownerEncryptionBytes = GetEncryptionBytesOrDefault(encryptionDictionary, tokenScanner, false);
                userEncryptionBytes  = GetEncryptionBytesOrDefault(encryptionDictionary, tokenScanner, true);
            }

            encryptionDictionary.TryGetOptionalTokenDirect(NameToken.EncryptMetaData, tokenScanner, out BooleanToken encryptMetadata);

            return(new EncryptionDictionary(filter.Data, code, length, revision, ownerBytes, userBytes,
                                            ownerEncryptionBytes,
                                            userEncryptionBytes,
                                            access,
                                            encryptionDictionary,
                                            encryptMetadata?.Data ?? true));
        }
        private static byte[] GetEncryptionBytesOrDefault(DictionaryToken encryptionDictionary, IPdfTokenScanner tokenScanner, bool isUser)
        {
            var name = isUser ? NameToken.Ue : NameToken.Oe;

            if (encryptionDictionary.TryGet(name, tokenScanner, out StringToken stringToken))
            {
                return(OtherEncodings.StringAsLatin1Bytes(stringToken.Data));
            }

            if (encryptionDictionary.TryGet(name, tokenScanner, out HexToken hexToken))
            {
                return(hexToken.Bytes.ToArray());
            }

            return(null);
        }
Example #10
0
        public static bool TryGetOptionalStringDirect(this DictionaryToken token, NameToken name, IPdfTokenScanner scanner, out string result)
        {
            result = default(string);
            if (token.TryGetOptionalTokenDirect(name, scanner, out StringToken stringToken))
            {
                result = stringToken.Data;
                return(true);
            }

            if (token.TryGetOptionalTokenDirect(name, scanner, out HexToken hexToken))
            {
                result = hexToken.Data;
                return(true);
            }

            return(false);
        }
Example #11
0
        internal Pages(ILog log, Catalog catalog, IPageFactory pageFactory, bool isLenientParsing, IPdfTokenScanner pdfScanner)
        {
            if (catalog == null)
            {
                throw new ArgumentNullException(nameof(catalog));
            }

            rootPageDictionary = catalog.PagesDictionary;

            Count = rootPageDictionary.GetIntOrDefault(NameToken.Count);

            this.log              = log;
            this.catalog          = catalog;
            this.pageFactory      = pageFactory;
            this.isLenientParsing = isLenientParsing;
            this.pdfScanner       = pdfScanner;
        }
Example #12
0
        public static bool TryGetOptionalTokenDirect <T>(this DictionaryToken token, NameToken name, IPdfTokenScanner scanner, out T result) where T : IToken
        {
            result = default(T);
            if (token.TryGet(name, out var appearancesToken) && DirectObjectFinder.TryGet(appearancesToken, scanner, out T innerResult))
            {
                result = innerResult;
                return(true);
            }

            return(false);
        }
Example #13
0
        public static IReadOnlyDictionary <string, TResult> FlattenNameTreeToDictionary <TResult>(DictionaryToken nameTreeNodeDictionary,
                                                                                                  IPdfTokenScanner pdfScanner,
                                                                                                  bool isLenientParsing,
                                                                                                  Func <IToken, TResult> valuesFactory) where TResult : class
        {
            var result = new Dictionary <string, TResult>();

            FlattenNameTree(nameTreeNodeDictionary, pdfScanner, isLenientParsing, valuesFactory, result);

            return(result);
        }
Example #14
0
        private static PageTreeNode ProcessPagesNode(IndirectReference referenceInput,
                                                     DictionaryToken nodeDictionaryInput,
                                                     IndirectReference parentReferenceInput,
                                                     bool isRoot,
                                                     IPdfTokenScanner pdfTokenScanner,
                                                     bool isLenientParsing,
                                                     PageCounter pageNumber)
        {
            bool isPage = CheckIfIsPage(nodeDictionaryInput, parentReferenceInput, isRoot, pdfTokenScanner, isLenientParsing);

            if (isPage)
            {
                pageNumber.Increment();

                return(new PageTreeNode(nodeDictionaryInput, referenceInput, true, pageNumber.PageCount).WithChildren(EmptyArray <PageTreeNode> .Instance));
            }

            //If we got here, we have to iterate till we manage to exit

            var toProcess =
                new Queue <(PageTreeNode thisPage, IndirectReference reference, DictionaryToken nodeDictionary, IndirectReference parentReference,
                            List <PageTreeNode> nodeChildren)>();
            var firstPage         = new PageTreeNode(nodeDictionaryInput, referenceInput, false, null);
            var setChildren       = new List <Action>();
            var firstPageChildren = new List <PageTreeNode>();

            setChildren.Add(() => firstPage.WithChildren(firstPageChildren));

            toProcess.Enqueue(
                (thisPage: firstPage, reference: referenceInput, nodeDictionary: nodeDictionaryInput, parentReference: parentReferenceInput,
                 nodeChildren: firstPageChildren));

            do
            {
                var current = toProcess.Dequeue();

                if (!current.nodeDictionary.TryGet(NameToken.Kids, pdfTokenScanner, out ArrayToken kids))
                {
                    if (!isLenientParsing)
                    {
                        throw new PdfDocumentFormatException($"Pages node in the document pages tree did not define a kids array: {current.nodeDictionary}.");
                    }

                    kids = new ArrayToken(EmptyArray <IToken> .Instance);
                }

                foreach (var kid in kids.Data)
                {
                    if (!(kid is IndirectReferenceToken kidRef))
                    {
                        throw new PdfDocumentFormatException($"Kids array contained invalid entry (must be indirect reference): {kid}.");
                    }

                    if (!DirectObjectFinder.TryGet(kidRef, pdfTokenScanner, out DictionaryToken kidDictionaryToken))
                    {
                        throw new PdfDocumentFormatException($"Could not find dictionary associated with reference in pages kids array: {kidRef}.");
                    }

                    bool isChildPage = CheckIfIsPage(kidDictionaryToken, current.reference, false, pdfTokenScanner, isLenientParsing);

                    if (isChildPage)
                    {
                        pageNumber.Increment();

                        var kidPageNode =
                            new PageTreeNode(kidDictionaryToken, kidRef.Data, true, pageNumber.PageCount).WithChildren(EmptyArray <PageTreeNode> .Instance);
                        current.nodeChildren.Add(kidPageNode);
                    }
                    else
                    {
                        var kidChildNode = new PageTreeNode(kidDictionaryToken, kidRef.Data, false, null);
                        var kidChildren  = new List <PageTreeNode>();
                        toProcess.Enqueue(
                            (thisPage: kidChildNode, reference: kidRef.Data, nodeDictionary: kidDictionaryToken, parentReference: current.reference,
                             nodeChildren: kidChildren));

                        setChildren.Add(() => kidChildNode.WithChildren(kidChildren));

                        current.nodeChildren.Add(kidChildNode);
                    }
                }
            } while (toProcess.Count > 0);

            foreach (var action in setChildren)
            {
                action();
            }

            return(firstPage);
        }
Example #15
0
        private static DictionaryToken ParseTrailer(CrossReferenceTable crossReferenceTable, bool isLenientParsing, IPdfTokenScanner pdfTokenScanner)
        {
            if (crossReferenceTable.Dictionary.ContainsKey(NameToken.Encrypt))
            {
                throw new NotSupportedException("Cannot currently parse a document using encryption: " + crossReferenceTable.Dictionary);
            }

            if (!crossReferenceTable.Dictionary.TryGet(NameToken.Root, out var rootToken))
            {
                throw new PdfDocumentFormatException($"Missing root object specification in trailer: {crossReferenceTable.Dictionary}.");
            }

            var rootDictionary = DirectObjectFinder.Get <DictionaryToken>(rootToken, pdfTokenScanner);

            if (!rootDictionary.ContainsKey(NameToken.Type) && isLenientParsing)
            {
                rootDictionary = rootDictionary.With(NameToken.Type, NameToken.Catalog);
            }

            return(rootDictionary);
        }
Example #16
0
            public MarkedContentElement Build(IPdfTokenScanner pdfScanner)
            {
                var mcid = -1;

                if (properties.TryGet(NameToken.Mcid, pdfScanner, out NumericToken mcidToken))
                {
                    mcid = mcidToken.Int;
                }

                var language             = GetOptional(NameToken.Lang, pdfScanner);
                var actualText           = GetOptional(NameToken.ActualText, pdfScanner);
                var alternateDescription = GetOptional(NameToken.Alternate, pdfScanner);
                var expandedForm         = GetOptional(NameToken.E, pdfScanner);

                if (name != NameToken.Artifact)
                {
                    return(new MarkedContentElement(mcid, name, properties,
                                                    language,
                                                    actualText,
                                                    alternateDescription,
                                                    expandedForm,
                                                    false,
                                                    Children,
                                                    letters,
                                                    paths,
                                                    images,
                                                    number));
                }

                var artifactType = ArtifactMarkedContentElement.ArtifactType.Unknown;

                if (properties.TryGet(NameToken.Type, pdfScanner, out IDataToken <string> typeToken) &&
                    Enum.TryParse(typeToken.Data, true, out ArtifactMarkedContentElement.ArtifactType parsedType))
                {
                    artifactType = parsedType;
                }

                var subType         = GetOptional(NameToken.Subtype, pdfScanner);
                var attributeOwners = GetOptional(NameToken.O, pdfScanner);

                var boundingBox = default(PdfRectangle?);

                if (properties.TryGet(NameToken.Bbox, pdfScanner, out ArrayToken arrayToken) &&
                    arrayToken.Length == 6)
                {
                    var left   = arrayToken[2] as NumericToken;
                    var bottom = arrayToken[3] as NumericToken;
                    var right  = arrayToken[4] as NumericToken;
                    var top    = arrayToken[5] as NumericToken;

                    if (left != null && bottom != null && right != null && top != null)
                    {
                        boundingBox = new PdfRectangle(left.Double, bottom.Double, right.Double, top.Double);
                    }
                }

                var attached = new List <NameToken>();

                if (properties.TryGet(NameToken.Attached, out ArrayToken attachedToken))
                {
                    foreach (var token in attachedToken.Data)
                    {
                        if (token is NameToken aName)
                        {
                            attached.Add(aName);
                        }
                    }
                }

                return(new ArtifactMarkedContentElement(mcid, name, properties, language,
                                                        actualText,
                                                        alternateDescription,
                                                        expandedForm,
                                                        artifactType,
                                                        subType,
                                                        attributeOwners,
                                                        boundingBox,
                                                        attached,
                                                        Children,
                                                        letters,
                                                        paths,
                                                        images,
                                                        number));
            }
Example #17
0
        public CrossReferenceTable Parse(IInputBytes bytes, bool isLenientParsing, long crossReferenceLocation,
                                         long offsetCorrection,
                                         IPdfTokenScanner pdfScanner,
                                         ISeekableTokenScanner tokenScanner)
        {
            long fixedOffset = offsetValidator.CheckXRefOffset(crossReferenceLocation, tokenScanner, bytes, isLenientParsing);

            if (fixedOffset > -1)
            {
                crossReferenceLocation = fixedOffset;

                log.Debug($"Found the first cross reference table or stream at {fixedOffset}.");
            }

            var table = new CrossReferenceTableBuilder();

            var  prevSet = new HashSet <long>();
            long previousCrossReferenceLocation = crossReferenceLocation;

            var missedAttempts = 0;

            // Parse all cross reference tables and streams.
            while (previousCrossReferenceLocation > 0 && missedAttempts < 100)
            {
                log.Debug($"Reading cross reference table or stream at {previousCrossReferenceLocation}.");

                if (previousCrossReferenceLocation >= bytes.Length)
                {
                    break;
                }

                // seek to xref table
                tokenScanner.Seek(previousCrossReferenceLocation);

                tokenScanner.MoveNext();

                if (tokenScanner.CurrentToken is OperatorToken tableToken && tableToken.Data == "xref")
                {
                    missedAttempts = 0;
                    log.Debug("Element was cross reference table.");

                    CrossReferenceTablePart tablePart = crossReferenceTableParser.Parse(tokenScanner,
                                                                                        previousCrossReferenceLocation, isLenientParsing);

                    var nextOffset = tablePart.GetPreviousOffset();

                    if (nextOffset >= 0)
                    {
                        nextOffset += offsetCorrection;
                    }

                    previousCrossReferenceLocation = nextOffset;

                    DictionaryToken tableDictionary = tablePart.Dictionary;

                    CrossReferenceTablePart streamPart = null;

                    // check for a XRef stream, it may contain some object ids of compressed objects
                    if (tableDictionary.ContainsKey(NameToken.XrefStm))
                    {
                        log.Debug("Cross reference table contained referenced to stream. Reading the stream.");

                        int streamOffset = ((NumericToken)tableDictionary.Data[NameToken.XrefStm]).Int;

                        // check the xref stream reference
                        fixedOffset = offsetValidator.CheckXRefOffset(streamOffset, tokenScanner, bytes, isLenientParsing);
                        if (fixedOffset > -1 && fixedOffset != streamOffset)
                        {
                            log.Warn($"/XRefStm offset {streamOffset} is incorrect, corrected to {fixedOffset}");

                            streamOffset = (int)fixedOffset;

                            // Update the cross reference table to be a stream instead.
                            tableDictionary = tableDictionary.With(NameToken.XrefStm, new NumericToken(streamOffset));
                            tablePart       = new CrossReferenceTablePart(tablePart.ObjectOffsets, streamOffset,
                                                                          tablePart.Previous, tableDictionary, tablePart.Type);
                        }

                        // Read the stream from the table.
                        if (streamOffset > 0)
                        {
                            try
                            {
                                TryParseCrossReferenceStream(streamOffset, pdfScanner, out streamPart);
                            }
                            catch (InvalidOperationException ex)
                            {
                                if (isLenientParsing)
                                {
                                    log.Error("Failed to parse /XRefStm at offset " + streamOffset, ex);
                                }
                                else
                                {
                                    throw;
                                }
                            }
                        }
                        else
                        {
                            if (isLenientParsing)
                            {
                                log.Error("Skipped XRef stream due to a corrupt offset:" + streamOffset);
                            }
                            else
                            {
                                throw new PdfDocumentFormatException("Skipped XRef stream due to a corrupt offset:" + streamOffset);
                            }
                        }
                    }

                    table.Add(tablePart);

                    if (streamPart != null)
                    {
                        table.Add(streamPart);
                    }
                }
Example #18
0
        internal OptionalContentGroupElement(MarkedContentElement markedContentElement, IPdfTokenScanner pdfTokenScanner)
        {
            MarkedContent = markedContentElement;

            // Type - Required
            if (markedContentElement.Properties.TryGet(NameToken.Type, pdfTokenScanner, out NameToken type))
            {
                Type = type.Data;
            }
            else if (markedContentElement.Properties.TryGet(NameToken.Type, pdfTokenScanner, out StringToken typeStr))
            {
                Type = typeStr.Data;
            }
            else
            {
                throw new ArgumentException($"Cannot parse optional content's {nameof(Type)} from {nameof(markedContentElement.Properties)}. This is a required field.", nameof(markedContentElement.Properties));
            }

            switch (Type)
            {
            case "OCG":     // Optional content group dictionary
                // Name - Required
                if (markedContentElement.Properties.TryGet(NameToken.Name, pdfTokenScanner, out NameToken name))
                {
                    Name = name.Data;
                }
                else if (markedContentElement.Properties.TryGet(NameToken.Name, pdfTokenScanner, out StringToken nameStr))
                {
                    Name = nameStr.Data;
                }
                else
                {
                    throw new ArgumentException($"Cannot parse optional content's {nameof(Name)} from {nameof(markedContentElement.Properties)}. This is a required field.", nameof(markedContentElement.Properties));
                }

                // Intent - Optional
                if (markedContentElement.Properties.TryGet(NameToken.Intent, pdfTokenScanner, out NameToken intentName))
                {
                    Intent = new string[] { intentName.Data };
                }
                else if (markedContentElement.Properties.TryGet(NameToken.Intent, pdfTokenScanner, out StringToken intentStr))
                {
                    Intent = new string[] { intentStr.Data };
                }
                else if (markedContentElement.Properties.TryGet(NameToken.Intent, pdfTokenScanner, out ArrayToken intentArray))
                {
                    List <string> intentList = new List <string>();
                    foreach (var token in intentArray.Data)
                    {
                        if (token is NameToken nameA)
                        {
                            intentList.Add(nameA.Data);
                        }
                        else if (token is StringToken strA)
                        {
                            intentList.Add(strA.Data);
                        }
                        else
                        {
                            throw new NotImplementedException();
                        }
                    }
                    Intent = intentList;
                }
                else
                {
                    // Default value is 'View'.
                    Intent = new string[] { "View" };
                }

                // Usage - Optional
                if (markedContentElement.Properties.TryGet(NameToken.Usage, pdfTokenScanner, out DictionaryToken usage))
                {
                    this.Usage = usage.Data;
                }
                break;

            case "OCMD":
                // OCGs - Optional
                if (markedContentElement.Properties.TryGet(NameToken.Ocgs, pdfTokenScanner, out DictionaryToken ocgsD))
                {
                    // dictionary or array
                    throw new NotImplementedException($"{NameToken.Ocgs}");
                }
                else if (markedContentElement.Properties.TryGet(NameToken.Ocgs, pdfTokenScanner, out ArrayToken ocgsA))
                {
                    // dictionary or array
                    throw new NotImplementedException($"{NameToken.Ocgs}");
                }

                // P - Optional
                if (markedContentElement.Properties.TryGet(NameToken.P, pdfTokenScanner, out NameToken p))
                {
                    throw new NotImplementedException($"{NameToken.P}");
                }

                // VE - Optional
                if (markedContentElement.Properties.TryGet(NameToken.VE, pdfTokenScanner, out ArrayToken ve))
                {
                    throw new NotImplementedException($"{NameToken.VE}");
                }
                break;

            default:
                throw new ArgumentException($"Unknown Optional Content of type '{Type}' not known.", nameof(Type));
            }
        }
Example #19
0
        private static IReadOnlyDictionary <string, ExplicitDestination> ReadNamedDestinations(Catalog catalog, IPdfTokenScanner pdfScanner,
                                                                                               ILog log)
        {
            var result = new Dictionary <string, ExplicitDestination>();

            if (catalog.CatalogDictionary.TryGet(NameToken.Dests, pdfScanner, out DictionaryToken dests))
            {
                /*
                 * In PDF 1.1, the correspondence between name objects and destinations is defined by the /Dests entry in the document catalog.
                 * The value of this entry is a dictionary in which each key is a destination name and the corresponding value is either an array
                 * defining the destination, using the explicit destination syntax, or a dictionary with a /D entry whose value is such an array.
                 */
                foreach (var kvp in dests.Data)
                {
                    var value = kvp.Value;

                    if (TryReadExplicitDestination(value, catalog, pdfScanner, log, out var destination))
                    {
                        result[kvp.Key] = destination;
                    }
                }
            }
            else if (catalog.CatalogDictionary.TryGet(NameToken.Names, pdfScanner, out DictionaryToken names) &&
                     names.TryGet(NameToken.Dests, pdfScanner, out dests))
            {
                /*
                 * In PDF 1.2, the correspondence between strings and destinations is defined by the /Dests entry in the document's name dictionary.
                 * The value of the /Dests entry is a name tree mapping name strings to destinations.
                 * The keys in the name tree may be treated as text strings for display purposes.
                 * The destination value associated with a key in the name tree may be either an array or a dictionary.
                 */
                NameTreeParser.FlattenNameTree(dests, pdfScanner, value =>
                {
                    if (TryReadExplicitDestination(value, catalog, pdfScanner, log, out var destination))
                    {
                        return(destination);
                    }

                    return(null);
                }, result);
            }

            return(result);
        }
Example #20
0
        public static ColorSpaceDetails GetColorSpaceDetails(ColorSpace?colorSpace,
                                                             DictionaryToken imageDictionary,
                                                             IPdfTokenScanner scanner,
                                                             IResourceStore resourceStore,
                                                             IFilterProvider filterProvider,
                                                             bool cannotRecurse = false)
        {
            if (!colorSpace.HasValue)
            {
                return(UnsupportedColorSpaceDetails.Instance);
            }

            switch (colorSpace.Value)
            {
            case ColorSpace.DeviceGray:
                return(DeviceGrayColorSpaceDetails.Instance);

            case ColorSpace.DeviceRGB:
                return(DeviceRgbColorSpaceDetails.Instance);

            case ColorSpace.DeviceCMYK:
                return(DeviceCmykColorSpaceDetails.Instance);

            case ColorSpace.CalGray:
                return(UnsupportedColorSpaceDetails.Instance);

            case ColorSpace.CalRGB:
                return(UnsupportedColorSpaceDetails.Instance);

            case ColorSpace.Lab:
                return(UnsupportedColorSpaceDetails.Instance);

            case ColorSpace.ICCBased:
                return(UnsupportedColorSpaceDetails.Instance);

            case ColorSpace.Indexed:
            {
                if (cannotRecurse)
                {
                    return(UnsupportedColorSpaceDetails.Instance);
                }

                if (!imageDictionary.TryGet(NameToken.ColorSpace, scanner, out ArrayToken colorSpaceArray) ||
                    colorSpaceArray.Length != 4)
                {
                    // Error instead?
                    return(UnsupportedColorSpaceDetails.Instance);
                }

                var first = colorSpaceArray[0] as NameToken;

                if (first == null || !ColorSpaceMapper.TryMap(first, resourceStore, out var innerColorSpace) ||
                    innerColorSpace != ColorSpace.Indexed)
                {
                    return(UnsupportedColorSpaceDetails.Instance);
                }

                var second = colorSpaceArray[1];

                ColorSpaceDetails baseDetails;

                if (DirectObjectFinder.TryGet(second, scanner, out NameToken baseColorSpaceNameToken) &&
                    ColorSpaceMapper.TryMap(baseColorSpaceNameToken, resourceStore, out var baseColorSpaceName))
                {
                    baseDetails = GetColorSpaceDetails(
                        baseColorSpaceName,
                        imageDictionary,
                        scanner,
                        resourceStore,
                        filterProvider,
                        true);
                }
                else if (DirectObjectFinder.TryGet(second, scanner, out ArrayToken baseColorSpaceArrayToken) &&
                         baseColorSpaceArrayToken.Length > 0 && baseColorSpaceArrayToken[0] is NameToken baseColorSpaceArrayNameToken &&
                         ColorSpaceMapper.TryMap(baseColorSpaceArrayNameToken, resourceStore, out var baseColorSpaceArrayColorSpace))
                {
                    var pseudoImageDictionary = new DictionaryToken(
                        new Dictionary <NameToken, IToken>
                        {
                            { NameToken.ColorSpace, baseColorSpaceArrayToken }
                        });

                    baseDetails = GetColorSpaceDetails(
                        baseColorSpaceArrayColorSpace,
                        pseudoImageDictionary,
                        scanner,
                        resourceStore,
                        filterProvider,
                        true);
                }
Example #21
0
        private static bool TryReadExplicitDestination(IToken value, Catalog catalog, IPdfTokenScanner pdfScanner,
                                                       ILog log, out ExplicitDestination destination)
        {
            destination = null;

            if (DirectObjectFinder.TryGet(value, pdfScanner, out ArrayToken valueArray) &&
                TryGetExplicitDestination(valueArray, catalog, log, out destination))
            {
                return(true);
            }

            if (DirectObjectFinder.TryGet(value, pdfScanner, out DictionaryToken valueDictionary) &&
                valueDictionary.TryGet(NameToken.D, pdfScanner, out valueArray) &&
                TryGetExplicitDestination(valueArray, catalog, log, out destination))
            {
                return(true);
            }

            return(false);
        }
Example #22
0
        /// <summary>
        /// Try and get the entry with a given name and type or look-up the object if it's an indirect reference.
        /// </summary>
        internal static bool TryGet <T>(this DictionaryToken dictionary, NameToken name, IPdfTokenScanner tokenScanner, out T token) where T : IToken
        {
            token = default(T);
            if (!dictionary.TryGet(name, out var t) || !(t is T typedToken))
            {
                if (t is IndirectReferenceToken reference)
                {
                    return(DirectObjectFinder.TryGet(reference, tokenScanner, out token));
                }

                return(false);
            }

            token = typedToken;
            return(true);
        }
Example #23
0
 public ResourceContainer(IPdfTokenScanner scanner, IFontFactory fontFactory)
 {
     this.scanner     = scanner;
     this.fontFactory = fontFactory;
 }
Example #24
0
        internal static IReadOnlyList <byte> Decode(this StreamToken stream, ILookupFilterProvider filterProvider, IPdfTokenScanner scanner)
        {
            var filters = filterProvider.GetFilters(stream.StreamDictionary, scanner);

            var transform = stream.Data;

            for (var i = 0; i < filters.Count; i++)
            {
                transform = filters[i].Decode(transform, stream.StreamDictionary, i);
            }

            return(transform);
        }
Example #25
0
 public AcroFormFactory(IPdfTokenScanner tokenScanner, IFilterProvider filterProvider, CrossReferenceTable crossReferenceTable)
 {
     this.tokenScanner        = tokenScanner ?? throw new ArgumentNullException(nameof(tokenScanner));
     this.filterProvider      = filterProvider ?? throw new ArgumentNullException(nameof(filterProvider));
     this.crossReferenceTable = crossReferenceTable ?? throw new ArgumentNullException(nameof(crossReferenceTable));
 }
Example #26
0
            /// <summary>
            /// The purpose of this method is to resolve indirect reference. That mean copy the reference's content to the new document's stream
            /// and replace the indirect reference with the correct/new one
            /// </summary>
            /// <param name="tokenToCopy">Token to inspect for reference</param>
            /// <param name="tokenScanner">scanner get the content from the original document</param>
            /// <returns>A reference of the token that was copied. With all the reference updated</returns>
            private IToken CopyToken(IToken tokenToCopy, IPdfTokenScanner tokenScanner)
            {
                // This token need to be deep copied, because they could contain reference. So we have to update them.
                switch (tokenToCopy)
                {
                case DictionaryToken dictionaryToken:
                {
                    var newContent = new Dictionary <NameToken, IToken>();
                    foreach (var setPair in dictionaryToken.Data)
                    {
                        var name  = setPair.Key;
                        var token = setPair.Value;
                        newContent.Add(NameToken.Create(name), CopyToken(token, tokenScanner));
                    }

                    return(new DictionaryToken(newContent));
                }

                case ArrayToken arrayToken:
                {
                    var newArray = new List <IToken>(arrayToken.Length);
                    foreach (var token in arrayToken.Data)
                    {
                        newArray.Add(CopyToken(token, tokenScanner));
                    }

                    return(new ArrayToken(newArray));
                }

                case IndirectReferenceToken referenceToken:
                {
                    if (referencesFromDocument.TryGetValue(referenceToken, out var newReferenceToken))
                    {
                        return(newReferenceToken);
                    }

                    var tokenObject = DirectObjectFinder.Get <IToken>(referenceToken.Data, tokenScanner);

                    Debug.Assert(!(tokenObject is IndirectReferenceToken));

                    var newToken = CopyToken(tokenObject, tokenScanner);
                    newReferenceToken = context.WriteToken(newToken);

                    referencesFromDocument.Add(referenceToken, newReferenceToken);

                    return(newReferenceToken);
                }

                case StreamToken streamToken:
                {
                    var properties = CopyToken(streamToken.StreamDictionary, tokenScanner) as DictionaryToken;
                    Debug.Assert(properties != null);

                    var bytes = streamToken.Data;
                    return(new StreamToken(properties, bytes));
                }

                case ObjectToken _:
                {
                    // Since we don't write token directly to the stream.
                    // We can't know the offset. Therefore the token would be invalid
                    throw new NotSupportedException("Copying a Object token is not supported");
                }
                }

                return(tokenToCopy);
            }
Example #27
0
 public CidFontFactory(IPdfTokenScanner pdfScanner, IFilterProvider filterProvider)
 {
     this.pdfScanner     = pdfScanner;
     this.filterProvider = filterProvider;
 }
Example #28
0
        public static XObjectImage ReadImage(XObjectContentRecord xObject, IPdfTokenScanner pdfScanner,
                                             IFilterProvider filterProvider,
                                             IResourceStore resourceStore)
        {
            if (xObject == null)
            {
                throw new ArgumentNullException(nameof(xObject));
            }

            if (xObject.Type != XObjectType.Image)
            {
                throw new InvalidOperationException($"Cannot create an image from an XObject with type: {xObject.Type}.");
            }

            var dictionary = xObject.Stream.StreamDictionary;

            var bounds = xObject.AppliedTransformation.Transform(new PdfRectangle(new PdfPoint(0, 0), new PdfPoint(1, 1)));

            var width  = dictionary.Get <NumericToken>(NameToken.Width, pdfScanner).Int;
            var height = dictionary.Get <NumericToken>(NameToken.Height, pdfScanner).Int;

            var isImageMask = dictionary.TryGet(NameToken.ImageMask, pdfScanner, out BooleanToken isMaskToken) &&
                              isMaskToken.Data;

            var isJpxDecode = dictionary.TryGet(NameToken.Filter, out var token) &&
                              token is NameToken filterName &&
                              filterName.Equals(NameToken.JpxDecode);

            int bitsPerComponent = 0;

            if (!isImageMask && !isJpxDecode)
            {
                if (!dictionary.TryGet(NameToken.BitsPerComponent, pdfScanner, out NumericToken bitsPerComponentToken))
                {
                    throw new PdfDocumentFormatException($"No bits per component defined for image: {dictionary}.");
                }

                bitsPerComponent = bitsPerComponentToken.Int;
            }
            else if (isImageMask)
            {
                bitsPerComponent = 1;
            }

            var intent = xObject.DefaultRenderingIntent;

            if (dictionary.TryGet(NameToken.Intent, out NameToken renderingIntentToken))
            {
                intent = renderingIntentToken.Data.ToRenderingIntent();
            }

            var interpolate = dictionary.TryGet(NameToken.Interpolate, pdfScanner, out BooleanToken interpolateToken) &&
                              interpolateToken.Data;

            DictionaryToken filterDictionary = xObject.Stream.StreamDictionary;

            if (xObject.Stream.StreamDictionary.TryGet(NameToken.Filter, out var filterToken) &&
                filterToken is IndirectReferenceToken)
            {
                if (filterDictionary.TryGet(NameToken.Filter, pdfScanner, out ArrayToken filterArray))
                {
                    filterDictionary = filterDictionary.With(NameToken.Filter, filterArray);
                }
                else if (filterDictionary.TryGet(NameToken.Filter, pdfScanner, out NameToken filterNameToken))
                {
                    filterDictionary = filterDictionary.With(NameToken.Filter, filterNameToken);
                }
                else
                {
                    filterDictionary = null;
                }
            }

            var supportsFilters = filterDictionary != null;

            if (filterDictionary != null)
            {
                var filters = filterProvider.GetFilters(filterDictionary);
                foreach (var filter in filters)
                {
                    if (!filter.IsSupported)
                    {
                        supportsFilters = false;
                        break;
                    }
                }
            }

            var decodedBytes = supportsFilters ? new Lazy <IReadOnlyList <byte> >(() => xObject.Stream.Decode(filterProvider))
                : null;

            var decode = EmptyArray <decimal> .Instance;

            if (dictionary.TryGet(NameToken.Decode, pdfScanner, out ArrayToken decodeArrayToken))
            {
                decode = decodeArrayToken.Data.OfType <NumericToken>()
                         .Select(x => x.Data)
                         .ToArray();
            }

            var colorSpace = default(ColorSpace?);

            if (!isImageMask)
            {
                if (dictionary.TryGet(NameToken.ColorSpace, pdfScanner, out NameToken colorSpaceNameToken) &&
                    TryMapColorSpace(colorSpaceNameToken, resourceStore, out var colorSpaceResult))
                {
                    colorSpace = colorSpaceResult;
                }
                else if (dictionary.TryGet(NameToken.ColorSpace, pdfScanner, out ArrayToken colorSpaceArrayToken) &&
                         colorSpaceArrayToken.Length > 0)
                {
                    var first = colorSpaceArrayToken.Data[0];

                    if ((first is NameToken firstColorSpaceName) && TryMapColorSpace(firstColorSpaceName, resourceStore, out colorSpaceResult))
                    {
                        colorSpace = colorSpaceResult;
                    }
                }
                else if (!isJpxDecode)
                {
                    colorSpace = xObject.DefaultColorSpace;
                }
            }

            return(new XObjectImage(bounds, width, height, bitsPerComponent, colorSpace, isJpxDecode, isImageMask, intent, interpolate, decode,
                                    dictionary, xObject.Stream.Data, decodedBytes));
        }
Example #29
0
            private ObjectToken CopyPageNode(PageTreeNode pageNode, IndirectReferenceToken parentPagesObject, IPdfTokenScanner tokenScanner)
            {
                Debug.Assert(pageNode.IsPage);

                var pageDictionary = new Dictionary <NameToken, IToken>
                {
                    { NameToken.Parent, parentPagesObject },
                };

                foreach (var setPair in pageNode.NodeDictionary.Data)
                {
                    var name  = setPair.Key;
                    var token = setPair.Value;

                    if (name == NameToken.Parent)
                    {
                        // Skip Parent token, since we have to reassign it
                        continue;
                    }

                    pageDictionary.Add(NameToken.Create(name), CopyToken(token, tokenScanner));
                }

                return(context.WriteObject(memory, new DictionaryToken(pageDictionary)));
            }
Example #30
0
            public void AppendDocument(Catalog catalog, decimal version, IPdfTokenScanner tokenScanner, IReadOnlyList <int> pages)
            {
                IEnumerable <int> pageIndices;

                if (pages == null)
                {
                    var pagesCount = catalog.PagesDictionary.GetIntOrDefault(NameToken.Count);
                    if (pagesCount < 1)
                    {
                        return;
                    }

                    pageIndices = Enumerable.Range(1, pagesCount);
                }
                else if (pages.Count < 1)
                {
                    return;
                }
                else
                {
                    pageIndices = pages;
                }

                currentVersion = Math.Max(version, currentVersion);

                var referencesFromDocument = new Dictionary <IndirectReference, IndirectReferenceToken>();

                var currentNodeReference = context.ReserveNumberToken();
                var pagesReferences      = new List <IndirectReferenceToken>();
                var resources            = new Dictionary <string, IToken>();

                bool DoesAEntryCollide(PageTreeNode node)
                {
                    while (node != null)
                    {
                        var dictionary = node.NodeDictionary;
                        if (dictionary.TryGet(NameToken.Resources, tokenScanner, out DictionaryToken resourcesDictionary))
                        {
                            var nonCollidingResources = resourcesDictionary.Data.Keys.Except(resources.Keys);
                            if (nonCollidingResources.Count() != resourcesDictionary.Data.Count)
                            {
                                // This means that at least one of the resources collided
                                return(true);
                            }
                        }

                        /* TODO: How to handle?
                         *  `Rotate`
                         *  `CropBox`
                         *  `MediaBox`
                         */

                        // No colliding entry was found, in this node
                        // Keep walking up into the tree
                        node = node.Parent;
                    }

                    return(false);
                }

                void CopyEntries(PageTreeNode node)
                {
                    while (node != null)
                    {
                        var dictionary = node.NodeDictionary;
                        if (dictionary.TryGet(NameToken.Resources, tokenScanner, out DictionaryToken resourcesDictionary))
                        {
                            foreach (var pair in resourcesDictionary.Data)
                            {
                                resources.Add(pair.Key, CopyToken(pair.Value, tokenScanner, referencesFromDocument));
                            }
                        }

                        /* TODO: How to handle?
                         *  `Rotate`
                         *  `CropBox`
                         *  `MediaBox`
                         */

                        // Keep walking up into the tree
                        node = node.Parent;
                    }
                }

                void CreateTree()
                {
                    if (pagesReferences.Count < 1)
                    {
                        throw new InvalidOperationException("Pages reference should always be more than 1 when executing this function");
                    }

                    var newPagesNode = new Dictionary <NameToken, IToken>
                    {
                        { NameToken.Type, NameToken.Pages },
                        { NameToken.Kids, new ArrayToken(pagesReferences) },
                        { NameToken.Count, new NumericToken(pagesReferences.Count) },
                        { NameToken.Parent, rootPagesReference }
                    };

                    if (resources.Count > 0)
                    {
                        newPagesNode.Add(NameToken.Resources, DictionaryToken.With(resources));
                    }

                    var pagesDictionary = new DictionaryToken(newPagesNode);

                    pagesTokenReferences.Add(context.WriteToken(pagesDictionary, (int)currentNodeReference.Data.ObjectNumber));

                    pageCount += pagesReferences.Count;
                };

                foreach (var pageIndex in pageIndices)
                {
                    var pageNode = catalog.GetPageNode(pageIndex);
                    if (pagesReferences.Count >= ARTIFICIAL_NODE_LIMIT || DoesAEntryCollide(pageNode))
                    {
                        CreateTree();

                        currentNodeReference = context.ReserveNumberToken();
                        pagesReferences      = new List <IndirectReferenceToken>();
                        resources            = new Dictionary <string, IToken>();
                    }

                    CopyEntries(pageNode.Parent);
                    pagesReferences.Add(CopyPageNode(pageNode, currentNodeReference, tokenScanner, referencesFromDocument));
                }

                if (pagesReferences.Count < 1)
                {
                    throw new InvalidOperationException("Pages reference couldn't be less than 1 because we have reserved a indirect reference token");
                }

                CreateTree();
            }