示例#1
0
            private IndirectReferenceToken CopyPageNode(PageTreeNode pageNode, IndirectReferenceToken parentPagesObject, IPdfTokenScanner tokenScanner,
                                                        IDictionary <IndirectReferenceToken, IndirectReferenceToken> referencesFromDocument)
            {
                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, referencesFromDocument));
                }

                return(context.WriteToken(new DictionaryToken(pageDictionary)));
            }
示例#2
0
        private static Dictionary <IToken, IToken> ConvertToDictionary(IReadOnlyList <IToken> tokens)
        {
            var result = new Dictionary <IToken, IToken>();

            IToken key = null;

            for (var i = 0; i < tokens.Count; i++)
            {
                var token = tokens[i];

                if (key == null)
                {
                    key = token;
                    continue;
                }

                // Combine indirect references, e.g. 12 0 R
                if (token is NumericToken num && PeekNext(tokens, i) is NumericToken gen)
                {
                    var r = PeekNext(tokens, i + 1);

                    if (r == OperatorToken.R)
                    {
                        result[key] = new IndirectReferenceToken(new IndirectReference(num.Long, gen.Int));
                        i           = i + 2;
                    }
                }
示例#3
0
        private static Dictionary <NameToken, IToken> ConvertToDictionary(IReadOnlyList <IToken> tokens)
        {
            var result = new Dictionary <NameToken, IToken>();

            NameToken key = null;

            for (var i = 0; i < tokens.Count; i++)
            {
                var token = tokens[i];

                if (key == null)
                {
                    if (token is NameToken name)
                    {
                        key = name;
                        continue;
                    }

                    throw new PdfDocumentFormatException($"Expected name as dictionary key, instead got: " + token);
                }

                // Combine indirect references, e.g. 12 0 R
                if (token is NumericToken num && PeekNext(tokens, i) is NumericToken gen)
                {
                    var r = PeekNext(tokens, i + 1);

                    if (r == OperatorToken.R)
                    {
                        result[key] = new IndirectReferenceToken(new IndirectReference(num.Long, gen.Int));
                        i           = i + 2;
                    }
                }
示例#4
0
            public DocumentMerger()
            {
                var reserved = context.ReserveNumber();

                rootPagesIndirectReference = new IndirectReferenceToken(new IndirectReference(reserved, 0));

                WriteHeaderToStream();
            }
示例#5
0
        private IndirectReferenceToken AddToken(IToken token, int reservedNumber)
        {
            var reference      = new IndirectReference(reservedNumber, 0);
            var referenceToken = new IndirectReferenceToken(reference);

            tokenReferences.Add(referenceToken, token);
            return(referenceToken);
        }
示例#6
0
        public IFont GetFontDirectly(IndirectReferenceToken fontReferenceToken, bool isLenientParsing)
        {
            if (!DirectObjectFinder.TryGet(fontReferenceToken, scanner, out DictionaryToken fontDictionaryToken))
            {
                throw new PdfDocumentFormatException($"The requested font reference token {fontReferenceToken} wasn't a font.");
            }

            var font = fontFactory.Get(fontDictionaryToken, isLenientParsing);

            return(font);
        }
示例#7
0
        private static void WriteIndirectReference(IndirectReferenceToken reference, Stream outputStream)
        {
            WriteLong(reference.Data.ObjectNumber, outputStream);
            WriteWhitespace(outputStream);

            WriteInt(reference.Data.Generation, outputStream);
            WriteWhitespace(outputStream);

            outputStream.WriteByte(RByte);
            WriteWhitespace(outputStream);
        }
示例#8
0
        public virtual IndirectReferenceToken WriteToken(IToken token, IndirectReferenceToken indirectReference)
        {
            if (!Initialized)
            {
                InitializePdf(DefaultVersion);
            }

            offsets.Add(indirectReference.Data, Stream.Position);
            var obj = new ObjectToken(Stream.Position, indirectReference.Data, token);

            TokenWriter.WriteToken(obj, Stream);
            return(indirectReference);
        }
示例#9
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);
            }
示例#10
0
        public override IndirectReferenceToken WriteToken(IToken token, IndirectReferenceToken indirectReference)
        {
            if (!Initialized)
            {
                InitializePdf(DefaultVersion);
            }

            ms.SetLength(0);
            TokenWriter.WriteToken(token, ms);
            var contents = ms.ToArray();

            hashes.Add(contents, indirectReference);
            offsets.Add(indirectReference.Data, Stream.Position);
            TokenWriter.WriteObject(indirectReference.Data.ObjectNumber, indirectReference.Data.Generation, contents, Stream);
            return(indirectReference);
        }
示例#11
0
        public void Flush(decimal version, IndirectReferenceToken catalogReference)
        {
            if (catalogReference == null)
            {
                throw new ArgumentNullException(nameof(catalogReference));
            }

            WriteString($"%PDF-{version:0.0}", Stream);

            Stream.WriteText("%");
            Stream.WriteByte(169);
            Stream.WriteByte(205);
            Stream.WriteByte(196);
            Stream.WriteByte(210);
            Stream.WriteNewLine();

            var         offsets      = new Dictionary <IndirectReference, long>();
            ObjectToken catalogToken = null;

            foreach (var pair in tokenReferences)
            {
                var referenceToken = pair.Key;
                var token          = pair.Value;
                var offset         = Stream.Position;
                var obj            = new ObjectToken(offset, referenceToken.Data, token);

                TokenWriter.WriteToken(obj, Stream);

                offsets.Add(referenceToken.Data, offset);

                if (catalogToken == null && referenceToken == catalogReference)
                {
                    catalogToken = obj;
                }
            }

            if (catalogToken == null)
            {
                throw new Exception("Catalog object wasn't found");
            }

            // TODO: Support document information
            TokenWriter.WriteCrossReferenceTable(offsets, catalogToken, Stream, null);
        }
示例#12
0
        /// <summary>
        /// Adds the image previously added using <see cref="AddJpeg(byte[], PdfRectangle)"/>
        /// or <see cref="AddPng(byte[], PdfRectangle)"/> sharing the same image to prevent duplication.
        /// </summary>
        public void AddImage(AddedImage image, PdfRectangle placementRectangle)
        {
            var resources = pageDictionary.GetOrCreateDict(NameToken.Resources);
            var xObjects  = resources.GetOrCreateDict(NameToken.Xobject);

            var key = NameToken.Create($"I{imageKey++}");

            xObjects[key] = new IndirectReferenceToken(image.Reference);

            currentStream.Add(Push.Value);
            // This needs to be the placement rectangle.
            currentStream.Add(new ModifyCurrentTransformationMatrix(new[]
            {
                (decimal)placementRectangle.Width, 0,
                0, (decimal)placementRectangle.Height,
                (decimal)placementRectangle.BottomLeft.X, (decimal)placementRectangle.BottomLeft.Y
            }));
            currentStream.Add(new InvokeNamedXObject(key));
            currentStream.Add(Pop.Value);
        }
示例#13
0
        public IndirectReferenceToken WriteFont(IPdfStreamWriter writer, IndirectReferenceToken reservedIndirect = null)
        {
            var dictionary = new Dictionary <NameToken, IToken>
            {
                { NameToken.Type, NameToken.Font },
                { NameToken.Subtype, NameToken.Type1 },
                { NameToken.BaseFont, NameToken.Create(metrics.FontName) },
                { NameToken.Encoding, NameToken.MacRomanEncoding }
            };

            var token = new DictionaryToken(dictionary);

            if (reservedIndirect != null)
            {
                return(writer.WriteToken(token, reservedIndirect));
            }

            var result = writer.WriteToken(token);

            return(result);
        }
示例#14
0
        /// <summary>
        /// Builds a PDF document from the current content of this builder and its pages.
        /// </summary>
        /// <returns>The bytes of the resulting PDF document.</returns>
        public byte[] Build()
        {
            var context      = new BuilderContext();
            var fontsWritten = new Dictionary <Guid, ObjectToken>();

            using (var memory = new MemoryStream())
            {
                // Header
                WriteString("%PDF-1.7", memory);

                // Files with binary data should contain a 2nd comment line followed by 4 bytes with values > 127
                memory.WriteText("%");
                memory.WriteByte(169);
                memory.WriteByte(205);
                memory.WriteByte(196);
                memory.WriteByte(210);
                memory.WriteNewLine();

                // Body
                foreach (var font in fonts)
                {
                    var fontObj = font.Value.FontProgram.WriteFont(font.Value.FontKey.Name, memory, context);
                    fontsWritten.Add(font.Key, fontObj);
                }

                var resources = new Dictionary <NameToken, IToken>
                {
                    { NameToken.ProcSet, new ArrayToken(new [] { NameToken.Create("PDF"), NameToken.Create("Text") }) }
                };

                if (fontsWritten.Count > 0)
                {
                    var fontsDictionary = new DictionaryToken(fontsWritten.Select(x => (fonts[x.Key].FontKey.Name, (IToken) new IndirectReferenceToken(x.Value.Number)))
                                                              .ToDictionary(x => x.Item1, x => x.Item2));

                    var fontsDictionaryRef = context.WriteObject(memory, fontsDictionary);

                    resources.Add(NameToken.Font, new IndirectReferenceToken(fontsDictionaryRef.Number));
                }

                var reserved       = context.ReserveNumber();
                var parentIndirect = new IndirectReferenceToken(new IndirectReference(reserved, 0));

                var pageReferences = new List <IndirectReferenceToken>();
                foreach (var page in pages)
                {
                    var pageDictionary = new Dictionary <NameToken, IToken>
                    {
                        { NameToken.Type, NameToken.Page },
                        {
                            NameToken.Resources,
                            new DictionaryToken(resources)
                        },
                        { NameToken.MediaBox, RectangleToArray(page.Value.PageSize) },
                        { NameToken.Parent, parentIndirect }
                    };

                    if (page.Value.Operations.Count > 0)
                    {
                        var contentStream = WriteContentStream(page.Value.Operations);

                        var contentStreamObj = context.WriteObject(memory, contentStream);

                        pageDictionary[NameToken.Contents] = new IndirectReferenceToken(contentStreamObj.Number);
                    }

                    var pageRef = context.WriteObject(memory, new DictionaryToken(pageDictionary));

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

                var pagesDictionary = new DictionaryToken(new Dictionary <NameToken, IToken>
                {
                    { NameToken.Type, NameToken.Pages },
                    { NameToken.Kids, new ArrayToken(pageReferences) },
                    { NameToken.Count, new NumericToken(pageReferences.Count) }
                });

                var pagesRef = context.WriteObject(memory, pagesDictionary, reserved);

                var catalog = new DictionaryToken(new Dictionary <NameToken, IToken>
                {
                    { NameToken.Type, NameToken.Catalog },
                    { NameToken.Pages, new IndirectReferenceToken(pagesRef.Number) }
                });

                var catalogRef = context.WriteObject(memory, catalog);

                var informationReference = default(IndirectReference?);
                if (IncludeDocumentInformation)
                {
                    var informationDictionary = DocumentInformation.ToDictionary();
                    if (informationDictionary.Count > 0)
                    {
                        var dictionary = new DictionaryToken(informationDictionary);
                        informationReference = context.WriteObject(memory, dictionary).Number;
                    }
                }

                TokenWriter.WriteCrossReferenceTable(context.ObjectOffsets, catalogRef, memory, informationReference);

                return(memory.ToArray());
            }
        }
示例#15
0
 public void WriteToken(IndirectReferenceToken referenceToken, IToken token)
 {
     tokenReferences.Add(referenceToken, token);
 }
示例#16
0
        public IndirectReferenceToken  WriteFont(IPdfStreamWriter writer, IndirectReferenceToken reservedIndirect = null)
        {
            var newEncoding = new TrueTypeSubsetEncoding(characterMapping.Keys.ToList());
            var subsetBytes = TrueTypeSubsetter.Subset(fontFileBytes.ToArray(), newEncoding);

            var embeddedFile = DataCompresser.CompressToStream(subsetBytes);

            var fileRef = writer.WriteToken(embeddedFile);

            var baseFont = NameToken.Create(font.TableRegister.NameTable.GetPostscriptName());

            var postscript = font.TableRegister.PostScriptTable;
            var hhead      = font.TableRegister.HorizontalHeaderTable;

            var bbox = font.TableRegister.HeaderTable.Bounds;

            var scaling = 1000m / font.TableRegister.HeaderTable.UnitsPerEm;
            var descriptorDictionary = new Dictionary <NameToken, IToken>
            {
                { NameToken.Type, NameToken.FontDescriptor },
                { NameToken.FontName, baseFont },
                // TODO: get flags TrueTypeEmbedder.java
                { NameToken.Flags, new NumericToken((int)FontDescriptorFlags.Symbolic) },
                { NameToken.FontBbox, GetBoundingBox(bbox, scaling) },
                { NameToken.ItalicAngle, new NumericToken((decimal)postscript.ItalicAngle) },
                { NameToken.Ascent, new NumericToken(Math.Round(hhead.Ascent * scaling, 2)) },
                { NameToken.Descent, new NumericToken(Math.Round(hhead.Descent * scaling, 2)) },
                { NameToken.CapHeight, new NumericToken(90) },
                { NameToken.StemV, new NumericToken(90) },
                { NameToken.FontFile2, fileRef }
            };

            var os2 = font.TableRegister.Os2Table;

            if (os2 == null)
            {
                throw new InvalidFontFormatException("Embedding TrueType font requires OS/2 table.");
            }

            if (os2 is Os2Version2To4OpenTypeTable twoPlus)
            {
                descriptorDictionary[NameToken.CapHeight] = new NumericToken(twoPlus.CapHeight);
                descriptorDictionary[NameToken.Xheight]   = new NumericToken(twoPlus.XHeight);
            }

            descriptorDictionary[NameToken.StemV] = new NumericToken(((decimal)bbox.Width) * scaling * 0.13m);

            var lastCharacter = 0;
            var widths        = new List <NumericToken> {
                NumericToken.Zero
            };

            foreach (var kvp in characterMapping)
            {
                if (kvp.Value > lastCharacter)
                {
                    lastCharacter = kvp.Value;
                }

                var glyphId = font.WindowsUnicodeCMap.CharacterCodeToGlyphIndex(kvp.Key);
                var width   = decimal.Round(font.TableRegister.HorizontalMetricsTable.GetAdvanceWidth(glyphId) * scaling, 2);

                widths.Add(new NumericToken(width));
            }

            var descriptor = writer.WriteToken(new DictionaryToken(descriptorDictionary));

            var toUnicodeCMap   = ToUnicodeCMapBuilder.ConvertToCMapStream(characterMapping);
            var toUnicodeStream = DataCompresser.CompressToStream(toUnicodeCMap);
            var toUnicode       = writer.WriteToken(toUnicodeStream);

            var dictionary = new Dictionary <NameToken, IToken>
            {
                { NameToken.Type, NameToken.Font },
                { NameToken.Subtype, NameToken.TrueType },
                { NameToken.BaseFont, baseFont },
                { NameToken.FontDescriptor, descriptor },
                { NameToken.FirstChar, new NumericToken(0) },
                { NameToken.LastChar, new NumericToken(lastCharacter) },
                { NameToken.Widths, new ArrayToken(widths) },
                { NameToken.ToUnicode, toUnicode }
            };

            var token = new DictionaryToken(dictionary);

            if (reservedIndirect != null)
            {
                return(writer.WriteToken(token, reservedIndirect));
            }

            return(writer.WriteToken(token));
        }
示例#17
0
        public bool MoveNext()
        {
            if (isDisposed)
            {
                throw new ObjectDisposedException(nameof(PdfTokenScanner));
            }

            // Read until we find object-number generation obj, e.g. "69 420 obj".
            int tokensRead = 0;

            while (coreTokenScanner.MoveNext() && !Equals(coreTokenScanner.CurrentToken, OperatorToken.StartObject))
            {
                if (coreTokenScanner.CurrentToken is CommentToken)
                {
                    continue;
                }

                tokensRead++;

                previousTokens[0]         = previousTokens[1];
                previousTokenPositions[0] = previousTokenPositions[1];

                previousTokens[1]         = coreTokenScanner.CurrentToken;
                previousTokenPositions[1] = coreTokenScanner.CurrentTokenStart;
            }

            // We only read partial tokens.
            if (tokensRead < 2)
            {
                return(false);
            }

            var startPosition = previousTokenPositions[0];
            var objectNumber  = previousTokens[0] as NumericToken;
            var generation    = previousTokens[1] as NumericToken;

            if (objectNumber == null || generation == null)
            {
                // Handle case where the scanner correctly reads most of an object token but includes too much of the first token
                // specifically %%EOF1 0 obj where scanning starts from 'F'.
                if (generation != null && previousTokens[0] is OperatorToken op)
                {
                    var match = EndsWithNumberRegex.Match(op.Data);

                    if (match.Success && int.TryParse(match.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var number))
                    {
                        startPosition = previousTokenPositions[0] + match.Index;
                        objectNumber  = new NumericToken(number);
                    }
                    else
                    {
                        return(false);
                    }
                }
                else
                {
                    return(false);
                }
            }

            // Read all tokens between obj and endobj.
            while (coreTokenScanner.MoveNext() && !Equals(coreTokenScanner.CurrentToken, OperatorToken.EndObject))
            {
                if (coreTokenScanner.CurrentToken is CommentToken)
                {
                    continue;
                }

                if (ReferenceEquals(coreTokenScanner.CurrentToken, OperatorToken.StartObject))
                {
                    // This should never happen.
                    Debug.Assert(false, "Encountered a start object 'obj' operator before the end of the previous object.");
                    return(false);
                }

                if (ReferenceEquals(coreTokenScanner.CurrentToken, OperatorToken.StartStream))
                {
                    var streamIdentifier = new IndirectReference(objectNumber.Long, generation.Int);

                    // Prevent an infinite loop where a stream's length references the stream or the stream's offset.
                    var getLengthFromFile = !(callingObject.HasValue && callingObject.Value.Equals(streamIdentifier));

                    var outerCallingObject = callingObject;

                    try
                    {
                        callingObject = streamIdentifier;

                        // Read stream: special case.
                        if (TryReadStream(coreTokenScanner.CurrentTokenStart, getLengthFromFile, out var stream))
                        {
                            readTokens.Clear();
                            readTokens.Add(stream);
                        }
                    }
                    finally
                    {
                        callingObject = outerCallingObject;
                    }
                }
                else
                {
                    readTokens.Add(coreTokenScanner.CurrentToken);
                }

                previousTokens[0]         = previousTokens[1];
                previousTokenPositions[0] = previousTokenPositions[1];

                previousTokens[1]         = coreTokenScanner.CurrentToken;
                previousTokenPositions[1] = coreTokenScanner.CurrentPosition;
            }

            if (!ReferenceEquals(coreTokenScanner.CurrentToken, OperatorToken.EndObject))
            {
                readTokens.Clear();
                return(false);
            }

            var reference = new IndirectReference(objectNumber.Long, generation.Int);

            IToken token;

            if (readTokens.Count == 3 && readTokens[0] is NumericToken objNum &&
                readTokens[1] is NumericToken genNum &&
                ReferenceEquals(readTokens[2], OperatorToken.R))
            {
                // I have no idea if this can ever happen.
                token = new IndirectReferenceToken(new IndirectReference(objNum.Long, genNum.Int));
            }
示例#18
0
        public AcroForm GetAcroForm(Catalog catalog)
        {
            if (!catalog.CatalogDictionary.TryGet(NameToken.AcroForm, out var acroRawToken))
            {
                return(null);
            }

            if (!DirectObjectFinder.TryGet(acroRawToken, tokenScanner, out DictionaryToken acroDictionary))
            {
                var fieldsRefs = new List <IndirectReferenceToken>();

                // Invalid reference, try constructing the form from a Brute Force scan.
                foreach (var reference in crossReferenceTable.ObjectOffsets.Keys)
                {
                    var referenceToken = new IndirectReferenceToken(reference);
                    if (!DirectObjectFinder.TryGet(referenceToken, tokenScanner, out DictionaryToken dict))
                    {
                        continue;
                    }

                    if (dict.TryGet(NameToken.Kids, tokenScanner, out ArrayToken _) && dict.TryGet(NameToken.T, tokenScanner, out StringToken _))
                    {
                        fieldsRefs.Add(referenceToken);
                    }
                }

                if (fieldsRefs.Count == 0)
                {
                    return(null);
                }

                acroDictionary = new DictionaryToken(new Dictionary <NameToken, IToken>
                {
                    { NameToken.Fields, new ArrayToken(fieldsRefs) }
                });
            }

            var signatureFlags = (SignatureFlags)0;

            if (acroDictionary.TryGetOptionalTokenDirect(NameToken.SigFlags, tokenScanner, out NumericToken signatureToken))
            {
                signatureFlags = (SignatureFlags)signatureToken.Int;
            }

            var needAppearances = false;

            if (acroDictionary.TryGetOptionalTokenDirect(NameToken.NeedAppearances, tokenScanner, out BooleanToken appearancesToken))
            {
                needAppearances = appearancesToken.Data;
            }

            var calculationOrder = default(ArrayToken);

            acroDictionary.TryGetOptionalTokenDirect(NameToken.Co, tokenScanner, out calculationOrder);

            var formResources = default(DictionaryToken);

            acroDictionary.TryGetOptionalTokenDirect(NameToken.Dr, tokenScanner, out formResources);

            var da = default(string);

            if (acroDictionary.TryGetOptionalTokenDirect(NameToken.Da, tokenScanner, out StringToken daToken))
            {
                da = daToken.Data;
            }
            else if (acroDictionary.TryGetOptionalTokenDirect(NameToken.Da, tokenScanner, out HexToken daHexToken))
            {
                da = daHexToken.Data;
            }

            var q = default(int?);

            if (acroDictionary.TryGetOptionalTokenDirect(NameToken.Q, tokenScanner, out NumericToken qToken))
            {
                q = qToken.Int;
            }

            if (!acroDictionary.TryGet(NameToken.Fields, tokenScanner, out ArrayToken fieldsArray))
            {
                return(null);
            }

            var fields = new Dictionary <IndirectReference, AcroFieldBase>(fieldsArray.Length);

            foreach (var fieldToken in fieldsArray.Data)
            {
                if (!(fieldToken is IndirectReferenceToken fieldReferenceToken))
                {
                    throw new PdfDocumentFormatException($"The fields array should only contain indirect references, instead got: {fieldToken}.");
                }

                var fieldDictionary = DirectObjectFinder.Get <DictionaryToken>(fieldToken, tokenScanner);

                var field = GetAcroField(fieldDictionary, catalog, new List <DictionaryToken>(0));

                fields[fieldReferenceToken.Data] = field;
            }

            return(new AcroForm(acroDictionary, signatureFlags, needAppearances, fields));
        }
示例#19
0
 public DocumentMerger()
 {
     rootPagesReference = context.ReserveNumberToken();
 }
示例#20
0
 public CopiedContentStream(IndirectReferenceToken indirectReferenceToken)
 {
     token = indirectReferenceToken;
 }
示例#21
0
        /// <summary>
        /// Builds a PDF document from the current content of this builder and its pages.
        /// </summary>
        /// <returns>The bytes of the resulting PDF document.</returns>
        public byte[] Build()
        {
            var fontsWritten = new Dictionary <Guid, ObjectToken>();

            using (var memory = new MemoryStream())
            {
                // Header
                WriteString("%PDF-1.7", memory);

                // Files with binary data should contain a 2nd comment line followed by 4 bytes with values > 127
                memory.WriteText("%");
                memory.WriteByte(169);
                memory.WriteByte(205);
                memory.WriteByte(196);
                memory.WriteByte(210);
                memory.WriteNewLine();

                // Body
                foreach (var font in fonts)
                {
                    var fontObj = font.Value.FontProgram.WriteFont(font.Value.FontKey.Name, memory, context);
                    fontsWritten.Add(font.Key, fontObj);
                }

                foreach (var image in images)
                {
                    var streamToken = new StreamToken(image.Value.StreamDictionary, image.Value.StreamData);

                    context.WriteObject(memory, streamToken, image.Value.ObjectNumber);
                }

                var procSet = new List <NameToken>
                {
                    NameToken.Create("PDF"),
                    NameToken.Text,
                    NameToken.ImageB,
                    NameToken.ImageC,
                    NameToken.ImageI
                };

                var resources = new Dictionary <NameToken, IToken>
                {
                    { NameToken.ProcSet, new ArrayToken(procSet) }
                };

                if (fontsWritten.Count > 0)
                {
                    var fontsDictionary = new DictionaryToken(fontsWritten.Select(x => (fonts[x.Key].FontKey.Name, (IToken) new IndirectReferenceToken(x.Value.Number)))
                                                              .ToDictionary(x => x.Item1, x => x.Item2));

                    var fontsDictionaryRef = context.WriteObject(memory, fontsDictionary);

                    resources.Add(NameToken.Font, new IndirectReferenceToken(fontsDictionaryRef.Number));
                }

                var reserved       = context.ReserveNumber();
                var parentIndirect = new IndirectReferenceToken(new IndirectReference(reserved, 0));

                var pageReferences = new List <IndirectReferenceToken>();
                foreach (var page in pages)
                {
                    var individualResources = new Dictionary <NameToken, IToken>(resources);
                    var pageDictionary      = new Dictionary <NameToken, IToken>
                    {
                        { NameToken.Type, NameToken.Page },
                        { NameToken.MediaBox, RectangleToArray(page.Value.PageSize) },
                        { NameToken.Parent, parentIndirect }
                    };

                    if (page.Value.Resources.Count > 0)
                    {
                        foreach (var kvp in page.Value.Resources)
                        {
                            // TODO: combine resources if value is dictionary or array, otherwise overwrite.
                            individualResources[kvp.Key] = kvp.Value;
                        }
                    }

                    pageDictionary[NameToken.Resources] = new DictionaryToken(individualResources);

                    if (page.Value.Operations.Count > 0)
                    {
                        var contentStream = WriteContentStream(page.Value.Operations);

                        var contentStreamObj = context.WriteObject(memory, contentStream);

                        pageDictionary[NameToken.Contents] = new IndirectReferenceToken(contentStreamObj.Number);
                    }

                    var pageRef = context.WriteObject(memory, new DictionaryToken(pageDictionary));

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

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

                var pagesDictionary = new DictionaryToken(pagesDictionaryData);

                var pagesRef = context.WriteObject(memory, pagesDictionary, reserved);

                var catalogDictionary = new Dictionary <NameToken, IToken>
                {
                    { NameToken.Type, NameToken.Catalog },
                    { NameToken.Pages, new IndirectReferenceToken(pagesRef.Number) }
                };

                if (ArchiveStandard != PdfAStandard.None)
                {
                    Func <IToken, ObjectToken> writerFunc = x => context.WriteObject(memory, x);

                    PdfABaselineRuleBuilder.Obey(catalogDictionary, writerFunc, DocumentInformation, ArchiveStandard);

                    switch (ArchiveStandard)
                    {
                    case PdfAStandard.A1A:
                        PdfA1ARuleBuilder.Obey(catalogDictionary);
                        break;

                    case PdfAStandard.A2B:
                        break;

                    case PdfAStandard.A2A:
                        PdfA1ARuleBuilder.Obey(catalogDictionary);
                        break;
                    }
                }

                var catalog = new DictionaryToken(catalogDictionary);

                var catalogRef = context.WriteObject(memory, catalog);

                var informationReference = default(IndirectReference?);
                if (IncludeDocumentInformation)
                {
                    var informationDictionary = DocumentInformation.ToDictionary();
                    if (informationDictionary.Count > 0)
                    {
                        var dictionary = new DictionaryToken(informationDictionary);
                        informationReference = context.WriteObject(memory, dictionary).Number;
                    }
                }

                TokenWriter.WriteCrossReferenceTable(context.ObjectOffsets, catalogRef, memory, informationReference);

                return(memory.ToArray());
            }
        }
示例#22
0
 public void CompletePdf(IndirectReferenceToken catalogReference, IndirectReferenceToken documentInformationReference = null)
 {
     TokenWriter.WriteCrossReferenceTable(offsets, catalogReference.Data, Stream, documentInformationReference?.Data);
 }
示例#23
0
        public bool MoveNext()
        {
            // Read until we find object-number generation obj, e.g. "69 420 obj".
            int tokensRead = 0;

            while (coreTokenScanner.MoveNext() && !Equals(coreTokenScanner.CurrentToken, OperatorToken.StartObject))
            {
                if (coreTokenScanner.CurrentToken is CommentToken)
                {
                    continue;
                }

                tokensRead++;

                previousTokens[0]         = previousTokens[1];
                previousTokenPositions[0] = previousTokenPositions[1];

                previousTokens[1]         = coreTokenScanner.CurrentToken;
                previousTokenPositions[1] = coreTokenScanner.CurrentTokenStart;
            }

            // We only read partial tokens.
            if (tokensRead < 2)
            {
                return(false);
            }

            var startPosition = previousTokenPositions[0];
            var objectNumber  = previousTokens[0] as NumericToken;
            var generation    = previousTokens[1] as NumericToken;

            if (objectNumber == null || generation == null)
            {
                throw new PdfDocumentFormatException("The obj operator (start object) was not preceded by a 2 numbers." +
                                                     $"Instead got: {previousTokens[0]} {previousTokens[1]} obj");
            }

            // Read all tokens between obj and endobj.
            while (coreTokenScanner.MoveNext() && !Equals(coreTokenScanner.CurrentToken, OperatorToken.EndObject))
            {
                if (coreTokenScanner.CurrentToken is CommentToken)
                {
                    continue;
                }

                if (ReferenceEquals(coreTokenScanner.CurrentToken, OperatorToken.StartObject))
                {
                    // This should never happen.
                    Debug.Assert(false, "Encountered a start object 'obj' operator before the end of the previous object.");
                    return(false);
                }

                if (ReferenceEquals(coreTokenScanner.CurrentToken, OperatorToken.StartStream))
                {
                    // Read stream: special case.
                    if (TryReadStream(coreTokenScanner.CurrentTokenStart, out var stream))
                    {
                        readTokens.Clear();
                        readTokens.Add(stream);
                    }
                }
                else
                {
                    readTokens.Add(coreTokenScanner.CurrentToken);
                }

                previousTokens[0]         = previousTokens[1];
                previousTokenPositions[0] = previousTokenPositions[1];

                previousTokens[1]         = coreTokenScanner.CurrentToken;
                previousTokenPositions[1] = coreTokenScanner.CurrentPosition;
            }

            if (!ReferenceEquals(coreTokenScanner.CurrentToken, OperatorToken.EndObject))
            {
                readTokens.Clear();
                return(false);
            }

            var reference = new IndirectReference(objectNumber.Long, generation.Int);

            IToken token;

            if (readTokens.Count == 3 && readTokens[0] is NumericToken objNum &&
                readTokens[1] is NumericToken genNum &&
                ReferenceEquals(readTokens[2], OperatorToken.R))
            {
                // I have no idea if this can ever happen.
                token = new IndirectReferenceToken(new IndirectReference(objNum.Long, genNum.Int));
            }
示例#24
0
 public IFont GetFontDirectly(IndirectReferenceToken fontReferenceToken, bool isLenientParsing)
 {
     return(null);
 }
示例#25
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>
        internal 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:
            {
                var tokenObject = DirectObjectFinder.Get <IToken>(referenceToken.Data, tokenScanner);

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

                var newToken = CopyToken(tokenObject, tokenScanner);

                var reserved     = context.ReserveNumber();
                var newReference = new IndirectReferenceToken(new IndirectReference(reserved, 0));

                unwrittenTokens.Add(newReference, newToken);

                return(newReference);
            }

            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);
        }
示例#26
0
        /// <summary>
        /// Adds the PNG image represented by the input stream at the specified location.
        /// </summary>
        public AddedImage AddPng(Stream pngStream, PdfRectangle placementRectangle)
        {
            var png = Png.Open(pngStream);

            byte[] data;
            var    pixelBuffer = new byte[3];

            using (var memoryStream = new MemoryStream())
            {
                for (var rowIndex = 0; rowIndex < png.Height; rowIndex++)
                {
                    for (var colIndex = 0; colIndex < png.Width; colIndex++)
                    {
                        var pixel = png.GetPixel(colIndex, rowIndex);

                        pixelBuffer[0] = pixel.R;
                        pixelBuffer[1] = pixel.G;
                        pixelBuffer[2] = pixel.B;

                        memoryStream.Write(pixelBuffer, 0, pixelBuffer.Length);
                    }
                }

                data = memoryStream.ToArray();
            }

            var widthToken  = new NumericToken(png.Width);
            var heightToken = new NumericToken(png.Height);

            IndirectReferenceToken smaskReference = null;

            if (png.HasAlphaChannel && documentBuilder.ArchiveStandard != PdfAStandard.A1B && documentBuilder.ArchiveStandard != PdfAStandard.A1A)
            {
                var smaskData = new byte[data.Length / 3];
                for (var rowIndex = 0; rowIndex < png.Height; rowIndex++)
                {
                    for (var colIndex = 0; colIndex < png.Width; colIndex++)
                    {
                        var pixel = png.GetPixel(colIndex, rowIndex);

                        var index = rowIndex * png.Width + colIndex;
                        smaskData[index] = pixel.A;
                    }
                }

                var compressedSmask = DataCompresser.CompressBytes(smaskData);

                // Create a soft-mask.
                var smaskDictionary = new Dictionary <NameToken, IToken>
                {
                    { NameToken.Type, NameToken.Xobject },
                    { NameToken.Subtype, NameToken.Image },
                    { NameToken.Width, widthToken },
                    { NameToken.Height, heightToken },
                    { NameToken.ColorSpace, NameToken.Devicegray },
                    { NameToken.BitsPerComponent, new NumericToken(png.Header.BitDepth) },
                    { NameToken.Decode, new ArrayToken(new IToken[] { new NumericToken(0), new NumericToken(1) }) },
                    { NameToken.Length, new NumericToken(compressedSmask.Length) },
                    { NameToken.Filter, NameToken.FlateDecode }
                };

                smaskReference = documentBuilder.AddImage(new DictionaryToken(smaskDictionary), compressedSmask);
            }

            var compressed = DataCompresser.CompressBytes(data);

            var imgDictionary = new Dictionary <NameToken, IToken>
            {
                { NameToken.Type, NameToken.Xobject },
                { NameToken.Subtype, NameToken.Image },
                { NameToken.Width, widthToken },
                { NameToken.Height, heightToken },
                { NameToken.BitsPerComponent, new NumericToken(png.Header.BitDepth) },
                { NameToken.ColorSpace, NameToken.Devicergb },
                { NameToken.Filter, NameToken.FlateDecode },
                { NameToken.Length, new NumericToken(compressed.Length) }
            };

            if (smaskReference != null)
            {
                imgDictionary.Add(NameToken.Smask, smaskReference);
            }

            var reference = documentBuilder.AddImage(new DictionaryToken(imgDictionary), compressed);

            var resources = pageDictionary.GetOrCreateDict(NameToken.Resources);
            var xObjects  = resources.GetOrCreateDict(NameToken.Xobject);

            var key = NameToken.Create($"I{imageKey++}");

            xObjects[key] = reference;

            currentStream.Add(Push.Value);
            // This needs to be the placement rectangle.
            currentStream.Add(new ModifyCurrentTransformationMatrix(new[]
            {
                (decimal)placementRectangle.Width, 0,
                0, (decimal)placementRectangle.Height,
                (decimal)placementRectangle.BottomLeft.X, (decimal)placementRectangle.BottomLeft.Y
            }));
            currentStream.Add(new InvokeNamedXObject(key));
            currentStream.Add(Pop.Value);

            return(new AddedImage(reference.Data, png.Width, png.Height));
        }
示例#27
0
 /// <summary>
 /// Create a new <see cref="AddedFont"/>.
 /// </summary>
 internal AddedFont(Guid id, IndirectReferenceToken reference)
 {
     Id        = id;
     Reference = reference;
 }
示例#28
0
 /// <summary>
 /// Create a new <see cref="DescriptorFontFile"/>.
 /// </summary>
 public DescriptorFontFile(IndirectReferenceToken key, FontFileType fileType)
 {
     ObjectKey = key;
     FileType  = fileType;
 }
示例#29
0
 public DocumentMerger(Stream baseStream)
 {
     context            = new PdfStreamWriter(baseStream, false);
     rootPagesReference = context.ReserveNumberToken();
 }