【问题标题】:iText7 Document has no pages when trying to add watermark尝试添加水印时,iText7 文档没有页面
【发布时间】:2021-01-21 10:52:46
【问题描述】:

我有一个在 SSRS 中的 iText 之外呈现的 pdf。该报告以单独的 PDF 格式提供,我使用 iText 应用一些进一步的功能,例如将pdf合并为一个,添加密码加密等。

我正在尝试在 pdf 的每一页上添加水印,如下所示:

public string MergeReports(ConcurrentDictionary<int, string> base64Reports, string password = null, string watermark = null)
{
    try
    {
        var passwordBytes = password != null ? Encoding.ASCII.GetBytes(password) : null;

        // Creating new PdfDocument with smart mode on which maintains formatting and styling.
        using (var ms = new MemoryStream())
        using (var mergedPdf = new PdfDocument(new PdfWriter(ms, 
            new WriterProperties().SetStandardEncryption(passwordBytes, passwordBytes, EncryptionConstants.ALLOW_PRINTING,
                EncryptionConstants.ENCRYPTION_AES_128 | EncryptionConstants.DO_NOT_ENCRYPT_METADATA)).SetSmartMode(true)))
        {
            foreach (var report in base64Reports.OrderBy(x => x.Key))
            {
                var (_, base64String) = report;
                var bytes = Convert.FromBase64String(base64String);

                using (var memoryStream = new MemoryStream(bytes))
                {
                    using (PdfReader reader = new PdfReader(memoryStream))
                    {
                        using (var pdfDoc = new PdfDocument(reader))
                        {
                            if (watermark != null)
                                AddWatermark(watermark, pdfDoc);

                            pdfDoc.CopyPagesTo(1, pdfDoc.GetNumberOfPages(), mergedPdf);
                            pdfDoc.Close();
                        }
                    }
                }
            }
            
            mergedPdf.Close();

            return Convert.ToBase64String(ms.ToArray());
        }
    }
    catch (Exception ex)
    {
        throw new PdfException($"Unable to merge PDF reports: {ex.Message}");
    }
}
private void AddWatermark(string watermark, PdfDocument pdfDoc)
{
    float watermarkTrimmingRectangleWidth = 300;
    float watermarkTrimmingRectangleHeight = 300;

    float formWidth = 300;
    float formHeight = 300;
    float formXOffset = 0;
    float formYOffset = 0;

    float xTranslation = 50;
    float yTranslation = 25;

    double rotationInRads = Math.PI / 3;
    var fontProgram = FontProgramFactory.CreateFont(_configuration.GetSection("FontFiles")["Calibri"]);
    PdfFont font = PdfFontFactory.CreateFont(fontProgram); 
    
    float fontSize = 150;

    var numberOfPages = pdfDoc.GetNumberOfPages();
    PdfPage page = null;

    for (var i = 1; i <= numberOfPages; i++)
    {
        page = pdfDoc.GetPage(i);
        Rectangle ps = page.GetPageSize();

        //Center the annotation
        float bottomLeftX = ps.GetWidth() / 2 - watermarkTrimmingRectangleWidth / 2;
        float bottomLeftY = ps.GetHeight() / 2 - watermarkTrimmingRectangleHeight / 2;
        Rectangle watermarkTrimmingRectangle = new Rectangle(bottomLeftX, bottomLeftY, watermarkTrimmingRectangleWidth, watermarkTrimmingRectangleHeight);

        PdfWatermarkAnnotation pdfWatermark = new PdfWatermarkAnnotation(watermarkTrimmingRectangle);

        //Apply linear algebra rotation math
        //Create identity matrix
        AffineTransform transform = new AffineTransform();//No-args constructor creates the identity transform
                                                          //Apply translation
        transform.Translate(xTranslation, yTranslation);
        //Apply rotation
        transform.Rotate(rotationInRads);

        PdfFixedPrint fixedPrint = new PdfFixedPrint();
        pdfWatermark.SetFixedPrint(fixedPrint);
        //Create appearance
        Rectangle formRectangle = new Rectangle(formXOffset, formYOffset, formWidth, formHeight);

        //Observation: font XObject will be resized to fit inside the watermark rectangle
        PdfFormXObject form = new PdfFormXObject(formRectangle);
        PdfExtGState gs1 = new PdfExtGState().SetFillOpacity(0.6f);
        PdfCanvas canvas = new PdfCanvas(form, pdfDoc);

        float[] transformValues = new float[6];
        transform.GetMatrix(transformValues);
        canvas.SaveState()
            .BeginText().SetColor(ColorConstants.GRAY, true).SetExtGState(gs1)
            .SetTextMatrix(transformValues[0], transformValues[1], transformValues[2], transformValues[3], transformValues[4], transformValues[5])
            .SetFontAndSize(font, fontSize)
            .ShowText(watermark)
            .EndText()
            .RestoreState();

        canvas.Release();

        pdfWatermark.SetAppearance(PdfName.N, new PdfAnnotationAppearance(form.GetPdfObject()));
        pdfWatermark.SetFlags(PdfAnnotation.PRINT);

        page.AddAnnotation(pdfWatermark);

    }
    page?.Flush();
}

我从这个stackoverflow answer 中找到了水印代码,不得不对其进行一些修改以匹配我的实现。

调试时,单步执行似乎会在canvas.SaveState(..)上抛出异常

我得到的例外是

((iText.Kernel.PdfException)ex).Message: 文档没有页面。

堆栈跟踪

在 iText.Kernel.Pdf.PdfPagesTree.GenerateTree() 在 iText.Kernel.Pdf.PdfDocument.Close() 在 iText.Kernel.Pdf.PdfDocument.System.IDisposable.Dispose() 在 D:_src\render-service\Solutions\Render Service\Source\RenderService\Services\PDFMerger.cs:line 66 中的 RenderService.Services.PDFMerger.MergeReports(ConcurrentDictionary`2 base64Reports, String password, String watermark)

添加它是因为它可能是相关的,但我在尝试创建字体时遇到了 iText7 库的问题。错误

System,NullReferenceException 对象引用未设置为对象异常的实例

在这一行:

PdfFont font = PdfFontFactory.CreateFont(fontProgram);

我仍然遇到此错误,但在从 itext.io.dll 抛出时忽略了此异常,如问题线程的评论中所述。

link to above issue

【问题讨论】:

    标签: c# pdf itext itext7 watermark


    【解决方案1】:

    您的问题不止一个问题,但要解决添加水印的问题,您必须创建另一个 PdfWriter,因为您正在编辑 pdf:

    using (var destMemoryStream = new MemoryStream())
    {
        using (var memoryStream = new MemoryStream(bytes))
        {
            using (PdfReader reader = new PdfReader(memoryStream))
            {
                using (PdfWriter pdfWriter = new PdfWriter(destMemoryStream))
                {
                    using (var pdfDoc = new PdfDocument(reader, pdfWriter))
                    {
                        if (watermark != null)
                            AddWatermark(watermark, pdfDoc);
    
                        pdfDoc.CopyPagesTo(1, pdfDoc.GetNumberOfPages(), mergedPdf);
                        pdfDoc.Close();
                    }
                }
            }
        }
    }
    

    我不确定是否有必要在您的 AddWatermark 中调用 page?.Flush() 以及合并是否可行。您正在尝试将 pdf 页面复制到空的 pdf 文档。

    【讨论】:

    • 是否需要不同的dest内存流?我能不能只在PdfWriter(memoryStream);中使用memoryStream
    • @UsmanKhan 我只使用一个 PdfWriter 和目标内存流是有意义的,但我没有测试它。
    • 想通了,感谢您的帮助。我使用了PdfWritermergedPdf 声明的AddWatermark,并将我的呼叫移至foreach 之外的AddWatermark
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多