【问题标题】:Add new pages (if needed) during PDF file creation在 PDF 文件创建期间添加新页面(如果需要)
【发布时间】:2018-09-27 17:36:52
【问题描述】:

我正在开发一个用于在 PDF 文件中生成发票的 Windows 窗体应用程序。

此 Winform 应用程序正在使用 PDF 模板来创建 PDF 文件。

这是 PDF 模板的屏幕截图(此模板是使用 Adobe Acrobat XI Lite Portable 创建的)

我在此代码中使用(版本 5.5.13) 来生成 PDF 文件:

private void GenerateInvoice(DataTable tbl_template_variables, DataTable tbl_details_invoice)
{
    using (PdfReader pdfReader = new PdfReader(plantilla__Invoice__manual))
    {
        try
        {
            PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(location_output_file, FileMode.Create));
            AcroFields pdfFormFields = pdfStamper.AcroFields;

            // Loop DataTable and set the value in the specified field.
            for (int i = 0; i < tbl_template_variables.Rows.Count; i++)
            {
                pdfFormFields.SetField(tbl_template_variables.Rows[i][0].ToString(), tbl_template_variables.Rows[i][1].ToString(), true);// set form pdfFormFields 
            }

            #region Details's table Invoice

            PdfPCell cell = null;
            PdfPTable table = null;

            table = new PdfPTable(9);
            table.HorizontalAlignment = Element.ALIGN_LEFT;
            table.SetWidths(new float[] { 22f, 22f, 22f, 22f, 22f, 22f, 22f, 22f, 22f });
            //table.SpacingBefore = 5;
            table.TotalWidth = 800f;

            for (int i = 0; i < tbl_details_invoice.Rows.Count; i++)
            {
                DataRow row = tbl_details_invoice.Rows[i];
                object Invoice_PDFColumn0_value = row.Field<string>("PROVIDER") == null ? string.Empty : row.Field<string>("PROVIDER").ToString();
                object Invoice_PDFColumn1_value = row.Field<string>("DESCRIPTION") == null ? string.Empty : row.Field<string>("DESCRIPTION").ToString();
                object Invoice_PDFColumn2_value = row.Field<string>("PPTO") == null ? string.Empty : row.Field<string>("PPTO").ToString();
                object Invoice_PDFColumn3_value = row.Field<string>("JOB_MEDIA_TYPE") == null ? string.Empty : row.Field<string>("JOB_MEDIA_TYPE").ToString();
                object Invoice_PDFColumn4_value = row.Field<string>("VEND_INV_NO") == null ? string.Empty : row.Field<string>("VEND_INV_NO").ToString();
                //object Invoice_PDFColumn5_value = row.Field<string>("ORDER_MEDIA") == null ? string.Empty : row.Field<string>("ORDER_MEDIA").ToString();
                //object Invoice_PDFColumn6_value = row.Field<string>("ACTIVITY_MONTH") == null ? string.Empty : row.Field<string>("ACTIVITY_MONTH").ToString();
                object Invoice_PDFColumn7_value = row.Field<string>("COMMISSIONABLE") == null ? string.Empty : row.Field<string>("COMMISSIONABLE").ToString();
                object Invoice_PDFColumn8_value = row.Field<string>("NON_COMMISSIONABLE") == null ? string.Empty : row.Field<string>("NON_COMMISSIONABLE").ToString();
                string Invoice_PDFColumn9_value = row.Field<string>("IVA_PROVEEDOR") == null ? string.Empty : row.Field<string>("IVA_PROVEEDOR").ToString();
                string Invoice_PDFColumn10_value = row.Field<string>("TOTAL") == null ? string.Empty : row.Field<string>("TOTAL").ToString();

                //Columns table
                cell = PhraseCell(new Phrase(Invoice_PDFColumn0.ToString(), GettypeStyle()));
                table.AddCell(cell);

                cell = PhraseCell(new Phrase(Invoice_PDFColumn1.ToString(), GettypeStyle()));
                table.AddCell(cell);

                cell = PhraseCell(new Phrase(Invoice_PDFColumn2.ToString(), GettypeStyle()));
                table.AddCell(cell);

                cell = PhraseCell(new Phrase(Invoice_PDFColumn3.ToString(), GettypeStyle()));
                table.AddCell(cell);

                cell = PhraseCell(new Phrase(Invoice_PDFColumn4.ToString(), GettypeStyle()));
                table.AddCell(cell);

                //cell = PhraseCell(new Phrase(Invoice_PDFColumn5.ToString(), GettypeStyle()));
                //table.AddCell(cell);

                //cell = PhraseCell(new Phrase(Invoice_PDFColumn6.ToString(), GettypeStyle()));
                //table.AddCell(cell);

                cell = PhraseCell(new Phrase(Invoice_PDFColumn7.ToString(), GettypeStyle()));
                table.AddCell(cell);

                cell = PhraseCell(new Phrase(Invoice_PDFColumn8.ToString(), GettypeStyle()));
                table.AddCell(cell);

                cell = PhraseCell(new Phrase(Invoice_PDFColumn9.ToString(), GettypeStyle()));
                table.AddCell(cell);

                cell = PhraseCell(new Phrase(Invoice_PDFColumn10.ToString(), GettypeStyle()));
                table.AddCell(cell);
            }

            ColumnText ct = new ColumnText(pdfStamper.GetOverContent(1));
            ct.AddElement(table);
            //iTextSharp.text.Rectangle rect = new iTextSharp.text.Rectangle(18, 370, 800, 36);
            iTextSharp.text.Rectangle rect = new iTextSharp.text.Rectangle(16, 320, 900, 16);
            rect.Border = iTextSharp.text.Rectangle.LEFT_BORDER | iTextSharp.text.Rectangle.RIGHT_BORDER;
            rect.BorderWidth = 15;
            rect.BorderColor = new BaseColor(0, 0, 0);
            rect.Border = iTextSharp.text.Rectangle.BOX;
            ct.SetSimpleColumn(rect);
            ct.Go();

            #endregion

            // flatten the form to remove editting options, set it to false
            // to leave the form open to subsequent manual edits
            pdfStamper.FormFlattening = true;
            pdfStamper.FreeTextFlattening = true;
            pdfStamper.Writer.CloseStream = true;
            pdfStamper.Close();// close the pdf
        }
        catch (Exception ex)
        {
            // No errors (yet).
        }
    }
}

这是结果(仅包含一些细节)

这段代码我面临的问题是,如果详细信息表有更多行,则这些行会在生成的页面中被覆盖(并且不会生成更多页面)

这是覆盖数据的结果:

我正在寻找使用此 PDF 模板正确生成 PDF 发票的方法(如果可能)

这是迄今为止我尝试后得到的最接近的有利结果:

  • 修改 PDF 模板以在 detail headers 下添加变量并更新字段的值以添加换行符,但是,似乎 PDF 中变量的大小很重要,因此,换行符不会扩展变量 (并强制) 以生成更多页面。
  • 使用HTMLWorker(已弃用) 强制使用 HTML 模板(类似于 PDF 模板的结构)。此方法适用于在打印 Details 表的行时生成多个页面,但未应用 CSS。
  • 使用 PdfDocument、PdfParagraph 和类似类的组合(尝试遵循我找到的指南 here,但老实说,我不知道如何在特定坐标中设置并使用正确的度量为生成的 PDF 文件中的每个元素设置点。
  • Download iText7 用于使用 HtmlConverter.ConvertToPdf 功能,但在这种情况下,我在生成旋转的 PDF 时遇到问题。文档在 java 我不熟悉
  • 我在 Oracle 中为 Create a PDF Template 找到了此文档 - 专注于“定义重复字段组”部分,但如果可以在 C# 中使用,我也找不到如何编辑 PDF 模板的源代码WinForms 应用程序。

所有提到的尝试给我带来的问题多于解决方案,因此,我想多花点时间使用 PDF 模板方法。

我整整一周都在做这个任务,但我没有想法。

我将编辑我的问题以添加与我面临的相同问题的相关问题:


我几乎不想避免创建 给我代码 问题,但是,欢迎任何关于如何正确生成 PDF 文件的建议或想法。

【问题讨论】:

    标签: itextsharp c# winforms pdf itext


    【解决方案1】:

    由于时间和其他工作相关问题,经过整整一周的搜索,我得到了以下解决方案:

    • 我决定生成 X 数量的临时 PDF 文件(每个都在,相关名称如:PDF_File_0.pdfPDF_File_1.pdfPDF_File_2.pdf 等)
    • 生成临时 PDF 文件后,我使用在 this answer 中找到的代码将所有 PDF 文件合并到一个 PDF 文件中。

    如果有人有兴趣知道如何在 PDF 文件中添加多个页面(在创建时),您可以使用以下代码:

    string PDF_filePath = @"C:\New Folder\myPdfTest.pdf";
    Document doc = new Document();
    PdfSmartCopy copy = new PdfSmartCopy(doc, new FileStream(PDF_filePath, FileMode.Create));
    doc.Open();
    
    double qtyPages = 8; // it will be added eight pages.
    
    // In each loop iteration a page will be added "which is a Rectangle, actually"
    // with the standard size of a LETTER paper format - landscape orientation.
    for (int pag = 0; pag < qtyPages; pag++)
    {
        iTextSharp.text.Rectangle rect1 = new iTextSharp.text.Rectangle(PageSize.LETTER.Rotate());
        rect1.Border = iTextSharp.text.Rectangle.BOX;
        copy.AddPage(rect1, 0);
    }
    
    // Close the document with the changes made.
    doc.Close();
    

    我正在使用code from this answer 在单个 PDF 文件中“合并”PDF 临时文件:

    /// <summary>
    /// Merge PDF's in a single PDF file.
    /// Source: https://stackoverflow.com/a/26883360/4092887
    /// </summary>
    /// <param name="fileNames">List with (filepath & filename) of PDF temp files.</param>
    /// <param name="targetPdf">Path and filename of the PDF unified file.</param>
    /// <returns>bool</returns>
    public bool MergePDFs(IEnumerable<string> fileNames, string targetPdf)
    {
        bool merged = true;
    
        try
        {
            using (FileStream stream = new FileStream(targetPdf, FileMode.Create))
            {
                Document document = new Document();
                PdfCopy pdf = new PdfCopy(document, stream);
                PdfReader reader = null;
                try
                {
                    document.Open();
                    foreach (string file in fileNames)
                    {
                        reader = new PdfReader(file);
                        pdf.AddDocument(reader);
                        reader.Close();
                    }
                }
                catch (Exception)
                {
                    merged = false;
                    if (reader != null)
                    {
                        reader.Close();
                    }
                }
                finally
                {
                    if (document != null)
                    {
                        document.Close();
                    }
                }
            }
        }
        catch (Exception ex)
        {
            // Log error in the log file - omitted here for clarity's sake.
            MessageBox.Show("An error ocurred at merginf the PDF files: " + SALTO_DE_LINEA +
                "Check the application log file for more details", TITLE, MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    
        return merged;
    }
    

    要创建的 PDF 文件实际上是使用 PDF 模板的发票。

    在这里,我需要将“存储在DataTable 变量中”的详细信息分成 10 条记录/行的块。

    为了设置块(我需要设置 10 条记录/行的块),我在名为“PAGE_SEPARATOR”的DataTable 变量中添加了一个新的DataColumn,并更新了“PAGE_SEPARATOR”的值" 包含此除法结果的列:

    // Set chunk separator.
    for (int r = 0; r < items.Rows.Count; r++)
    {
        items.Rows[r]["SEPARADOR"] = r/10;
    }
    

    这段代码的完整解释可以看here

    对于每个记录块,我必须添加一个带有 PDF 模板结构的新页面。

    这是使用 PDF 模板生成 PDF 发票文件并为每个记录块生成 PDF 文件的完整代码;在流程结束时,调用 MergePdfs 方法并删除临时 PDF 文件:

    注意:您可能会在西班牙语中找到 cmets,但我希望代码足够清晰,以便根据您的目的理解和修改。

    /// <summary>
    /// Generate PDF invoice file.
    /// It wuill create X PDF temp files "with consecutive file names" for - at the end
    /// of the process - merge those PDF temp files in a single one PDF file.
    /// Temp PDF files will be deleted after creating the PDF merged file.
    /// </summary>
    /// <param name="formFactura">DataTable with the values of the PDF template.</param>
    /// <param name="DetalleFactura">DataTable with the JSON - DataTable (known as details or detalles).</param>
    /// <param name="ruta_archivo_salida">File name and path of the PDF unified file.</param>
    /// <returns>string</returns>
    private string GenerateInvoice(DataTable formFactura, DataTable DetalleFactura, string ruta_archivo_salida)
    {
        // Inicializar variables.
        string msg = "";
        List<string> rutas_archivos = new List<string>();
    
        try
        {
            if (File.Exists(plantilla_factura_manual))
            {
                try
                {
                    // Crear X cantidad de archivos.
                    // "PAGE_SEPARATOR" es el nombre de la columna que posee los valores separados por bloques.
                    DataView view = new DataView(DetalleFactura);
                    DataTable distinctValues = view.ToTable(true, "PAGE_SEPARATOR");
                    double cantPaginas = distinctValues.Rows.Count;
                    for (int pagina = 0; pagina < cantPaginas; pagina++)
                    {
                        using (PdfReader pdfReader = new PdfReader(plantilla_factura_manual))
                        {
                            // Agregar la ruta del archivo temporal PDF a generar.
                            rutas_archivos.Add(ruta_factura_generada.Replace(".pdf", "(" + pagina + ").pdf"));
    
                            PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(ruta_factura_generada.Replace(".pdf", "(" + pagina + ").pdf"), FileMode.OpenOrCreate));
                            AcroFields pdfFormFields = pdfStamper.AcroFields;
    
                            // Llenar las variables de la plantilla en el archivo PDF en construcción.
                            for (int i = 0; i < formFactura.Rows.Count; i++)
                            {
                                pdfFormFields.SetField(formFactura.Rows[i][0].ToString(), formFactura.Rows[i][1].ToString(), true);// set form pdfFormFields
                            }
    
                            #region Diseño grid factura
    
                            PdfPCell cell = null;
                            PdfPTable table = null;
    
                            table = new PdfPTable(9);
                            table.HorizontalAlignment = Element.ALIGN_LEFT;
                            table.SetWidths(new float[] { 22f, 22f, 22f, 22f, 22f, 22f, 22f, 22f, 22f });
                            //table.SpacingBefore = 5;
                            table.TotalWidth = 800f;
    
                            DataRow[] filas_a_usar = DetalleFactura.Select("PAGE_SEPARATOR = " + pagina);
                            foreach (DataRow r in filas_a_usar)
                            {
                                DataRow row = r;
                                object valorFacturaPDFColumna0 = row.Field<string>("PROVIDER") == null ? string.Empty : row.Field<string>("PROVIDER").ToString();
                                object valorFacturaPDFColumna1 = row.Field<string>("DESCRIPTION") == null ? string.Empty : row.Field<string>("DESCRIPTION").ToString();
                                object valorFacturaPDFColumna2 = row.Field<string>("PPTO") == null ? string.Empty : row.Field<string>("PPTO").ToString();
                                object valorFacturaPDFColumna3 = row.Field<string>("JOB_MEDIA_TYPE") == null ? string.Empty : row.Field<string>("JOB_MEDIA_TYPE").ToString();
                                object valorFacturaPDFColumna4 = row.Field<string>("VEND_INV_NO") == null ? string.Empty : row.Field<string>("VEND_INV_NO").ToString();
                                //object valorFacturaPDFColumna5 = row.Field<string>("ORDER_MEDIA") == null ? string.Empty : row.Field<string>("ORDER_MEDIA").ToString();
                                //object valorFacturaPDFColumna6 = row.Field<string>("ACTIVITY_MONTH") == null ? string.Empty : row.Field<string>("ACTIVITY_MONTH").ToString();
                                object valorFacturaPDFColumna7 = row.Field<string>("COMMISSIONABLE") == null ? string.Empty : row.Field<string>("COMMISSIONABLE").ToString();
                                object valorFacturaPDFColumna8 = row.Field<string>("NON_COMMISSIONABLE") == null ? string.Empty : row.Field<string>("NON_COMMISSIONABLE").ToString();
                                string valorFacturaPDFColumna9 = row.Field<string>("IVA_PROVEEDOR") == null ? string.Empty : row.Field<string>("IVA_PROVEEDOR").ToString();
                                string valorFacturaPDFColumna10 = row.Field<string>("TOTAL") == null ? string.Empty : row.Field<string>("TOTAL").ToString();
    
                                //Columnas table
                                cell = PhraseCell(new Phrase(valorFacturaPDFColumna0.ToString(), GettypeStyle()));
                                table.AddCell(cell);
    
                                cell = PhraseCell(new Phrase(valorFacturaPDFColumna1.ToString(), GettypeStyle()));
                                table.AddCell(cell);
    
                                cell = PhraseCell(new Phrase(valorFacturaPDFColumna2.ToString(), GettypeStyle()));
                                table.AddCell(cell);
    
                                cell = PhraseCell(new Phrase(valorFacturaPDFColumna3.ToString(), GettypeStyle()));
                                table.AddCell(cell);
    
                                cell = PhraseCell(new Phrase(valorFacturaPDFColumna4.ToString(), GettypeStyle()));
                                table.AddCell(cell);
    
                                //cell = PhraseCell(new Phrase(valorFacturaPDFColumna5.ToString(), GettypeStyle()));
                                //table.AddCell(cell);
    
                                //cell = PhraseCell(new Phrase(valorFacturaPDFColumna6.ToString(), GettypeStyle()));
                                //table.AddCell(cell);
    
                                cell = PhraseCell(new Phrase(valorFacturaPDFColumna7.ToString(), GettypeStyle()));
                                table.AddCell(cell);
    
                                cell = PhraseCell(new Phrase(valorFacturaPDFColumna8.ToString(), GettypeStyle()));
                                table.AddCell(cell);
    
                                cell = PhraseCell(new Phrase(valorFacturaPDFColumna9.ToString(), GettypeStyle()));
                                table.AddCell(cell);
    
                                cell = PhraseCell(new Phrase(valorFacturaPDFColumna10.ToString(), GettypeStyle()));
                                table.AddCell(cell);
                            }
    
                            ColumnText ct = new ColumnText(pdfStamper.GetOverContent(1));
                            ct.AddElement(table);
                            //iTextSharp.text.Rectangle rect = new iTextSharp.text.Rectangle(18, 370, 800, 36);
                            iTextSharp.text.Rectangle rect = new iTextSharp.text.Rectangle(16, 320, 900, 16);
                            rect.Border = iTextSharp.text.Rectangle.LEFT_BORDER | iTextSharp.text.Rectangle.RIGHT_BORDER;
                            rect.BorderWidth = 15;
                            rect.BorderColor = new BaseColor(0, 0, 0);
                            rect.Border = iTextSharp.text.Rectangle.BOX;
                            ct.SetSimpleColumn(rect);
                            ct.Go();
    
                            #endregion
    
                            // flatten the form to remove editting options, set it to false
                            // to leave the form open to subsequent manual edits
                            pdfStamper.FormFlattening = true;
                            pdfStamper.FreeTextFlattening = true;
                            pdfStamper.Writer.CloseStream = true;
                            pdfStamper.Close();// close the pdf
                        }
                    }
    
                    // Unir los archivos PDF's en uno solo.
                    MergePDFs(rutas_archivos, ruta_archivo_salida);
    
                    #region Eliminar archivos PDF temporales.
    
                    try
                    {
                        foreach (string archivo in rutas_archivos)
                        {
                            File.Delete(archivo);
                        }
                    }
                    catch (Exception ex)
                    {
                        RegistrarEventosDelPrograma("Error al eliminar archivos PDF temporales: " + ex.ToString(), "Error al eliminar archivos PDF temporales");
                    }
    
                    #endregion
                }
                catch (Exception ex)
                {
                    msg += "- Hay un error con la plantilla. Consulte el log de eventos." + SALTO_DE_LINEA;
                    RegistrarEventosDelPrograma("Error al usar la plantilla (" + Path.GetFileName(plantilla_factura_manual) + "): " + ex.ToString(), "Error al usar la plantilla (" + Path.GetFileName(plantilla_factura_manual) + ")");
                }
            }
        }
        catch (Exception ex)
        {
            msg += "- Hubo un error inesperado al generar el archivo PDF. Consulte el log de eventos.";
            RegistrarEventosDelPrograma("Error al generar el archivo PDF. Detalles: " + ex.ToString(), "Error al generar PDF - Plantilla");
        }
    
        return msg;
    }
    

    这是我在Stack Overflow in spanish 中密切相关的条目。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-20
      • 1970-01-01
      • 1970-01-01
      • 2018-07-15
      相关资源
      最近更新 更多