You need to enable JavaScript to run this app. 使用iText7和ASP.NET C#为PDF页脚添加页码及点线时显示异常求助
优惠活动
大模型
产品
解决方案
定价
更多

使用iText7和ASP.NET C#为PDF页脚添加页码及点线时显示异常求助

iText7 实现PDF页脚异常问题求助

基础情况

  • 当前可使用 iText7 + C# ASP.NET 正常生成总页数为9页的PDF文件
  • 开发目标:为PDF页脚添加两类内容
    • 符合 1 of 9、2 of 9... 格式的当前页/总页码标注
    • 装饰性点线分隔线

遇到的异常问题

  1. 上述格式的页码仅在PDF的第8、9页正常显示,前7页无对应页码内容
  2. 点线装饰未在PDF第一页正常渲染

恳请各位开发者提供对应问题的解决方案

问题原因与解决方法

页码仅在最后两页显示的根因

当前代码中,PdfDocumentEvent.END_PAGE 事件的注册时机远晚于页面生成时机:

  • 代码先完成了全部内容的添加,过程中生成了前7页,此时页脚事件处理器还未创建、未注册,这7页触发END_PAGE事件时无对应逻辑绘制页脚
  • 等全部内容添加完成后,才创建Footer实例、注册END_PAGE事件,只有后续新生成的第8、9页会触发页脚绘制逻辑,因此仅这两页显示页码

对应修改方案

  1. 调整事件注册时机:在完成PdfDocument、Document对象初始化后,立刻实例化Footer并注册END_PAGE事件,确保每一页完成渲染时都能触发页脚绘制
    修改后代码片段:
    // 原有初始化代码
    PdfWriter writer = new PdfWriter(filename);
    PdfDocument pdf = new PdfDocument(writer);
    Document document = new Document(pdf);
    
    // 立刻注册页脚事件,不要等所有内容加载完再注册
    Footer footerHandler = new Footer();
    pdf.AddEventHandler(PdfDocumentEvent.END_PAGE, footerHandler);
    
  2. 保留总页数占位逻辑:所有内容添加完成、执行document.Close()之前,再调用WriteTotal方法把总页数写入之前创建的占位FormXObject,这个逻辑本身是正确的,不需要调整。

第一页点线不显示的根因

当前代码中的点线是作为内容流的一部分添加的,添加逻辑绑定了“遇到Set开头的非首条数据”才会触发:

  • 第一页是首条数据的渲染载体,不满足触发条件,因此不会添加点线
  • 点线被添加在新页面的最顶部,而非设计要求的页脚位置,位置逻辑错位

对应修改方案

如果点线是页脚的装饰元素,直接在Footer的HandleEvent方法中添加贯穿页边距的点线,替代原来在内容流里加LineSeparator的逻辑,这样所有页面(包括第一页)都会在统一位置显示点线。
修改后的Footer类关键代码片段:

public virtual void HandleEvent(Event @event)
{
    PdfDocumentEvent docEvent = (PdfDocumentEvent)@event;
    PdfDocument pdf = docEvent.GetDocument();
    PdfPage page = docEvent.GetPage();
    int pageNumber = pdf.GetPageNumber(page);
    Rectangle pageSize = page.GetPageSize();

    PdfCanvas pdfCanvas = new PdfCanvas(page.NewContentStreamBefore(), page.GetResources(), pdf);
    // 页脚区域绘制贯穿边距的点线:x方向从36到页面宽度-36,y坐标和页码对齐
    float lineY = y + 5;
    pdfCanvas.SetStrokeColorGray(0.5f)
        .SetLineDash(1, 2) // 设置点线样式,和原设计保持一致
        .MoveTo(36, lineY)
        .LineTo(pageSize.GetWidth() - 36, lineY)
        .Stroke();

    Canvas canvas = new Canvas(pdfCanvas, pageSize);
    canvas.SetFontSize(10);

    Paragraph p = new Paragraph()
        .Add("Page ")
        .Add(pageNumber.ToString())
        .Add(" of");

    canvas.ShowTextAligned(p, x, y, TextAlignment.RIGHT);
    canvas.Close();

    pdfCanvas.AddXObjectAt(placeholder, x + space, y - descent);
    pdfCanvas.Release();
}

同时删除原来在内容流里加LineSeparator的逻辑,避免点线错位到页面顶部。

原始问题代码

private void mtpdfnew()
{
    DataTable dt = new DataTable();

    filename = @"C:\inetpub\wwwroot\public\pdf\" +
        DateTime.Now.ToString("yyyyMMddHHmmss") + ".pdf";

    PdfWriter writer = new PdfWriter(filename);
    PdfDocument pdf = new PdfDocument(writer);
    Document document = new Document(pdf);

    using (MySqlConnection myConnectionString =
       new MySqlConnection(ConfigurationManager.ConnectionStrings["cn"].ConnectionString))
    {
        using (MySqlCommand cmd =
            new MySqlCommand("SP", myConnectionString))
        {
            cmd.Connection.Open();
            cmd.CommandType = CommandType.StoredProcedure;

            using (MySqlDataAdapter da =
                new MySqlDataAdapter(cmd))
            {
                da.Fill(dt);

                if (dt.Rows.Count > 0)
                {
                    for (int i = 0; i < dt.Rows.Count; i++)
                    {
                        contents = new Paragraph(dt.Rows[i]["contents"].ToString())
                            .SetTextAlignment(TextAlignment.JUSTIFIED)
                            .SetFontSize(12);

                        if (dt.Rows[i]["contents"].ToString().StartsWith("Set") && i != 0)
                        {
                            document.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
                            document.Add(new LineSeparator(new DottedLine(1, 2)).SetMarginTop(4));
                        }

                        List<IElement> lst = HtmlConverter.ConvertToElements(dt.Rows[i]["contents"].ToString()).ToList();

                        for (int j = 0; j < lst.Count; j++)
                        {
                            IBlockElement element = (IBlockElement)lst[j];

                            if (dt.Rows[i]["contents"].ToString().StartsWith("Set"))
                            {
                                contents.SetFontSize(12)
                                    .SetBold()
                                    .SetFontColor(ColorConstants.BLUE);
                            }
                            else if (dt.Rows[i]["contents"].ToString().StartsWith("- "))
                            {
                                contents.SetFontSize(10)
                                    .SetBold()
                                    .SetFontColor(ColorConstants.BLACK);
                            }
                            else
                            {
                                contents.SetTextAlignment(TextAlignment.JUSTIFIED_ALL)
                                    .SetFontSize(10)
                                    .SetFontColor(ColorConstants.BLACK);
                            }

                            element.SetProperty(Property.LEADING, new Leading(Leading.MULTIPLIED, -1f));
                            document.Add(element);
                        }
                    }

                    // 原代码的事件注册时机太靠后,需要移到Document初始化之后
                    Footer footerHandler = new Footer();
                    pdf.AddEventHandler(PdfDocumentEvent.END_PAGE, footerHandler);
                    footerHandler.WriteTotal(pdf);
                    dest = filename.ToString();
                    document.Close();
                }

                Response.Clear();
                Response.ContentType = "application/pdf";
                Response.AppendHeader("Content-Disposition", "attachment; filename=" + dest);
                Response.TransmitFile(dest);
                Response.End();
            }
        }
    }
}

private class TableFooterEventHandler : IEventHandler
{
    private Table table;

    public TableFooterEventHandler(Table table)
    {
        this.table = table;
    }

    public void HandleEvent(Event currentEvent)
    {
        PdfDocumentEvent docEvent = (PdfDocumentEvent)currentEvent;
        PdfDocument pdfDoc = docEvent.GetDocument();
        PdfPage page = docEvent.GetPage();

        int pageNumber = pdfDoc.GetPageNumber(page);
        Rectangle pageSize = page.GetPageSize();

        PdfCanvas canvas = new PdfCanvas(page.NewContentStreamBefore(), page.GetResources(), pdfDoc);
        new Canvas(canvas, new Rectangle(36, 20, page.GetPageSize().GetWidth() - 72, 50))
            .Add(table)
            .Close();
    }
}

protected class Footer : IEventHandler
{
    protected PdfFormXObject placeholder;
    protected float side = 20;
    protected float x = 300;
    protected float y = 25;
    protected float space = 4.5f;
    protected float descent = 3;

    public Footer()
    {
        placeholder = new PdfFormXObject(new Rectangle(0, 0, side, side));
    }

    public virtual void HandleEvent(Event @event)
    {
        PdfDocumentEvent docEvent = (PdfDocumentEvent)@event;
        PdfDocument pdf = docEvent.GetDocument();
        PdfPage page = docEvent.GetPage();
        int pageNumber = pdf.GetPageNumber(page);
        Rectangle pageSize = page.GetPageSize();

        PdfCanvas pdfCanvas = new PdfCanvas(page);
        Canvas canvas = new Canvas(pdfCanvas, pageSize);
        canvas.SetFontSize(10);

        Paragraph p = new Paragraph()
            .Add("Page ")
            .Add(pageNumber.ToString())
            .Add(" of");

        canvas.ShowTextAligned(p, x, y, TextAlignment.RIGHT);
        canvas.Close();

        pdfCanvas.AddXObjectAt(placeholder, x + space, y - descent);
        pdfCanvas.Release();
    }

    public void WriteTotal(PdfDocument pdfDoc)
    {
        Canvas canvas = new Canvas(placeholder, pdfDoc);
        canvas.SetFontSize(10);
        canvas.ShowTextAligned(pdfDoc.GetNumberOfPages().ToString(),
            0, descent, TextAlignment.LEFT);
        canvas.Close();
    }
}

private class TableHeaderEventHandler : IEventHandler
{
    private Table table;
    private float tableHeight;
    private Document doc;

    public TableHeaderEventHandler(Document doc)
    {
        this.doc = doc;
        InitTable();

        TableRenderer renderer = (TableRenderer)table.CreateRendererSubTree();
        renderer.SetParent(new DocumentRenderer(doc));

        LayoutResult result = renderer.Layout(new LayoutContext(new LayoutArea(0, PageSize.A4)));
        tableHeight = result.GetOccupiedArea().GetBBox().GetHeight();
    }

    public void HandleEvent(Event currentEvent)
    {
        PdfDocumentEvent docEvent = (PdfDocumentEvent)currentEvent;
        PdfDocument pdfDoc = docEvent.GetDocument();
        PdfPage page = docEvent.GetPage();
        int pageNum = docEvent.GetDocument().GetPageNumber(page);
        PdfCanvas canvas = new PdfCanvas(page.NewContentStreamBefore(), page.GetResources(), pdfDoc);
        PageSize pageSize = pdfDoc.GetDefaultPageSize();
        float coordX = pageSize.GetX() + doc.GetLeftMargin();
        float coordY = pageSize.GetTop() - doc.GetTopMargin();
        float width = pageSize.GetWidth() - doc.GetRightMargin() - doc.GetLeftMargin();
        float height = GetTableHeight();

        Rectangle rect = new Rectangle(coordX, coordY, width, height);

        new Canvas(canvas, rect)
            .Add(table)
            .Close();
    }

    public float GetTableHeight()
    {
        return tableHeight;
    }

    private void InitTable()
    {

    }
}

内容的提问来源于stack exchange,提问作者Edward Sheriff Curtis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.29 09:27:06