/// <summary>
        /// Adds a watermark on all pages of the specified document.
        /// </summary>
        /// <param name="doc">Document to process.</param>
        /// <param name="imageDataStream">Stream containing image data to be used as watermark. Caller is reposible for closing it.</param>
        /// <param name="outputStream">Output stream, optional. If not set, incremental save will be performed. Caller is responsible for closing it.</param>
        public static void Watermark(this FixedDocument doc, Stream imageDataStream, Stream outputStream = null)
        {
            if (doc == null)
            {
                throw new ArgumentNullException(nameof(doc));
            }

            if (imageDataStream == null)
            {
                throw new ArgumentNullException(nameof(imageDataStream));
            }

            // create and register image resource
            string imageResourceId = Guid.NewGuid().ToString("N");
            Image  imageResource   = new Image(imageResourceId, imageDataStream);

            doc.ResourceManager.RegisterResource(imageResource);

            // register watermark XObject it will be referenced in all watermark annotations
            FixedContent watermarkContent = new FixedContent(Guid.NewGuid().ToString("N"),
                                                             new Boundary(imageResource.Width, imageResource.Height));

            watermarkContent.Content.AppendImage(imageResourceId, 0, 0, imageResource.Width, imageResource.Height);
            doc.ResourceManager.RegisterResource(watermarkContent);

            // add annotations to every page
            foreach (Page page in doc.Pages)
            {
                WatermarkAnnotation watermarkAnnotation = CreateWatermarkAnnotation(page.Boundary.MediaBox, watermarkContent);
                doc.ResourceManager.RegisterResource(watermarkAnnotation.Watermark);
                page.Annotations.Add(watermarkAnnotation);
            }

            // save to specified file or do an incremental update
            if (outputStream != null)
            {
                doc.Save(outputStream);
            }
            else
            {
                doc.Save();
            }
        }
        /// <summary>
        /// Signs the range of document pages using given certificate and signature image.
        /// </summary>
        /// <param name="doc">Document to sign.</param>
        /// <param name="signingCertificate">Signing certificate's data stream.</param>
        /// <param name="certPassword">Certificate's password.</param>
        /// <param name="signatureImage">Image stream that will represent the signature visually on page.</param>
        /// <param name="signatureBoundary">Visual signature boundaries.</param>
        /// <param name="signaturePageIndexStart">The index of the first page to sign.</param>
        /// <param name="signaturePageIndexEnd">The index of the last page to sign.</param>
        /// <param name="outputStream">Output stream, optional. If not set, incremental save will be performed.</param>
        /// <returns>Identifier assigned to the created signature field. Using this id you can find this field in doc's AcroForm dictionary.</returns>
        public static string Sign(this FixedDocument doc, Stream signingCertificate,
                                  string certPassword, Stream signatureImage, Boundary signatureBoundary,
                                  int signaturePageIndexStart = 0, int signaturePageIndexEnd = 0, Stream outputStream = null)
        {
            if (doc == null)
            {
                throw new ArgumentNullException(nameof(doc));
            }

            if (signingCertificate == null)
            {
                throw new ArgumentNullException(nameof(signingCertificate));
            }

            if (certPassword == null)
            {
                throw new ArgumentNullException(nameof(certPassword));
            }

            if (signatureImage == null)
            {
                throw new ArgumentNullException(nameof(signatureImage));
            }

            if (signatureBoundary == null)
            {
                throw new ArgumentNullException(nameof(signatureBoundary));
            }

            if (signaturePageIndexStart < 0 || signaturePageIndexStart > doc.Pages.Count - 1)
            {
                throw new ArgumentOutOfRangeException(nameof(signaturePageIndexStart));
            }

            if (signaturePageIndexEnd < signaturePageIndexStart || signaturePageIndexEnd > doc.Pages.Count - 1)
            {
                throw new ArgumentOutOfRangeException(nameof(signaturePageIndexEnd));
            }

            string imageId          = Guid.NewGuid().ToString("N");
            string signatureFieldId = Guid.NewGuid().ToString("N");

            // register signature image resource
            doc.ResourceManager.RegisterResource(new Image(imageId, signatureImage));

            // create signature field and initialize it using a stored
            // password protected certificate
            SignatureField signatureField = new SignatureField(signatureFieldId);

            signatureField.Signature = Signature.Create(new Pkcs12Store(signingCertificate, certPassword));

            // add signature field to a document
            doc.AcroForm.Fields.Add(signatureField);

            // create signature view using the image resource
            SignatureFieldView signatureView = new SignatureFieldView(signatureField, signatureBoundary);

            signatureView.ViewSettings.Graphic           = Graphic.Image;
            signatureView.ViewSettings.GraphicResourceID = imageId;
            signatureView.ViewSettings.Description       = Description.None;

            // add view to pages' annotations
            for (int i = signaturePageIndexStart; i <= signaturePageIndexEnd; ++i)
            {
                doc.Pages[i].Annotations.Add(signatureView);
            }

            // save to specified file or do an incremental update
            if (outputStream != null)
            {
                doc.Save(outputStream);
            }
            else
            {
                doc.Save();
            }

            return(signatureFieldId);
        }
        /// <summary>
        /// Adds a watermark to all pages of the specified document.
        /// </summary>
        /// <param name="doc">Document to process.</param>
        /// <param name="watermarkText">Watermark text.</param>
        /// <param name="outputStream">Output stream, optional. If not set, incremental save will be performed.</param>
        public static void WatermarkText(this FixedDocument doc, string watermarkText, Stream outputStream = null)
        {
            if (doc == null)
            {
                throw new ArgumentNullException(nameof(doc));
            }

            if (string.IsNullOrEmpty(watermarkText))
            {
                throw new ArgumentException("Value cannot be null or empty.", nameof(watermarkText));
            }


            // register graphics state that sets transparency level for content
            GraphicsState gsTransparency = new GraphicsState("gsTransparency");

            gsTransparency.CurrentNonStrokingAlpha = 0.3;
            gsTransparency.CurrentStrokingAlpha    = 0.3;
            doc.ResourceManager.RegisterResource(gsTransparency);

            // create watermark content template using given text
            double fontSizeInPoints = 20;
            double padding          = 10;
            double borderThickness  = 2;

            double totalAddedSpace = (padding + borderThickness) * 2;

            TextBlock watermarkTextBlock = new TextBlock(watermarkText)
            {
                Font         = new Font("Times New Roman", fontSizeInPoints),
                Color        = RgbColors.Red,
                Padding      = new Thickness(padding),
                BorderColor  = RgbColors.Red,
                Border       = new Border(borderThickness),
                BorderRadius = 5,
                Background   = RgbColors.Pink
            };

            double textBlockWidth  = watermarkTextBlock.Measure(doc.ResourceManager) + totalAddedSpace;
            double textBlockHeight = fontSizeInPoints + totalAddedSpace + 10;

            FixedContent watermarkContent = new FixedContent(Guid.NewGuid().ToString("N"), new Boundary(textBlockWidth, textBlockHeight));

            watermarkContent.Content.SetGraphicsState(gsTransparency.ID);
            watermarkContent.Content.AppendContentElement(watermarkTextBlock, textBlockWidth, textBlockHeight);

            // register watermark XObject it will be referenced in all watermark annotations
            doc.ResourceManager.RegisterResource(watermarkContent);

            // add annotations to every page
            foreach (Page page in doc.Pages)
            {
                WatermarkAnnotation watermarkAnnotation = CreateWatermarkAnnotation(page.Boundary.MediaBox, watermarkContent, true);
                doc.ResourceManager.RegisterResource(watermarkAnnotation.Watermark);
                page.Annotations.Add(watermarkAnnotation);
            }

            // save to specified file or do an incremental update
            if (outputStream != null)
            {
                doc.Save(outputStream);
            }
            else
            {
                doc.Save();
            }
        }
        /// <summary>
        /// Signs the range of document pages using given certificate and signature image.
        /// </summary>
        /// <param name="doc">Document to sign.</param>
        /// <param name="signingCertificate">Signing certificate's data stream.</param>
        /// <param name="certPassword">Certificate's password.</param>
        /// <param name="signatureText">The text of the signature.</param>
        /// <param name="signatureBoundary">Visual signature boundaries.</param>
        /// <param name="signaturePageIndexStart">The index of the first page to sign.</param>
        /// <param name="signaturePageIndexEnd">The index of the last page to sign.</param>
        /// <param name="outputStream">Output stream, optional. If not set, incremental save will be performed.</param>
        /// <returns>Identifier assigned to the created signature field. Using this id you can find this field in doc's AcroForm dictionary.</returns>
        public static string Sign(this FixedDocument doc, Stream signingCertificate,
                                  string certPassword, string signatureText, Boundary signatureBoundary,
                                  int signaturePageIndexStart = 0, int signaturePageIndexEnd = 0, Stream outputStream = null)
        {
            if (doc == null)
            {
                throw new ArgumentNullException(nameof(doc));
            }

            if (signingCertificate == null)
            {
                throw new ArgumentNullException(nameof(signingCertificate));
            }

            if (certPassword == null)
            {
                throw new ArgumentNullException(nameof(certPassword));
            }

            if (signatureBoundary == null)
            {
                throw new ArgumentNullException(nameof(signatureBoundary));
            }

            if (signaturePageIndexStart < 0 || signaturePageIndexStart > doc.Pages.Count - 1)
            {
                throw new ArgumentOutOfRangeException(nameof(signaturePageIndexStart));
            }

            if (signaturePageIndexEnd < signaturePageIndexStart || signaturePageIndexEnd > doc.Pages.Count - 1)
            {
                throw new ArgumentOutOfRangeException(nameof(signaturePageIndexEnd));
            }

            // create textual resource
            FixedContent signatureTextXObject = new FixedContent(Guid.NewGuid().ToString("N"), new Boundary(0, 0, signatureBoundary.Width, signatureBoundary.Height));

            Section section = new Section();

            if (!string.IsNullOrEmpty(signatureText))
            {
                var newLineString = "<br/>";
                signatureText = signatureText.Replace("\r\n", newLineString)
                                .Replace("\n", newLineString)
                                .Replace("\r", newLineString);

                foreach (ContentElement contentElement in ContentElement.FromMarkup(signatureText))
                {
                    contentElement.Font = new Font("TimesNewRoman", 12);
                    section.Add(contentElement);
                }

                signatureTextXObject.Content.AppendContentElement(section, signatureBoundary.Width, signatureBoundary.Height);
            }

            doc.ResourceManager.RegisterResource(signatureTextXObject);

            string signatureFieldId = Guid.NewGuid().ToString("N");

            // create signature field and initialize it using a stored
            // password protected certificate
            SignatureField signatureField = new SignatureField(signatureFieldId);

            signatureField.Signature = Signature.Create(new Pkcs12Store(signingCertificate, certPassword));

            // add signature field to a document
            doc.AcroForm.Fields.Add(signatureField);

            // create signature view using the image resource
            SignatureFieldView signatureView = new SignatureFieldView(signatureField, signatureBoundary);

            signatureView.ViewSettings.Graphic           = Graphic.XObject;
            signatureView.ViewSettings.GraphicResourceID = signatureTextXObject.ID;
            signatureView.ViewSettings.Description       = Description.None;

            // add view to pages' annotations
            for (int i = signaturePageIndexStart; i <= signaturePageIndexEnd; ++i)
            {
                doc.Pages[i].Annotations.Add(signatureView);
            }

            // save to specified file or do an incremental update
            if (outputStream != null)
            {
                doc.Save(outputStream);
            }
            else
            {
                doc.Save();
            }

            return(signatureFieldId);
        }