在PDF文件创建过程中添加新页面(如果需要) [英] Add new pages (if needed) during PDF file creation

查看:250
本文介绍了在PDF文件创建过程中添加新页面(如果需要)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发Windows窗体应用程序,用于在PDF文件中生成发票.

I'm developing a windows form application for generating invoices in PDF files.

此Winform应用程序使用PDF模板来创建PDF文件.

This Winform application is using a PDF template for creating a PDF file.

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

This is the screenshot of the PDF template (this template was created using Adobe Acrobat XI Lite Portable):

我正在使用 (版本5.5.13),用于生成PDF文件:

I'm using itextsharp (version 5.5.13) in this code for generate the PDF file:

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).
        }
    }
}

这是结果(只有一些细节):

此代码面临的问题是,如果明细表中有更多行,则这些行将在生成的页面中被覆盖(并且不会再生成更多页面).

The problem I'm facing with this code is that, if the details table has more rows, the rows are overwritten in the generated page (and no more pages are generated).

这是数据被覆盖的结果:

This is the result with overwritten data:

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

I'm looking the way for generating the PDF invoice correctly (if possible) using this PDF template.

这是我尝试后到目前为止获得的最接近的有利结果:

This is the closest favorable result I got so far after tried:

  • 每次在 detail标头下修改用于添加变量的PDF模板,并为每次添加换行符更新字段的值,但是,似乎PDF中变量的大小很重要,因此,换行符不会扩展变量(并强制)以生成更多页面.
  • 使用 HTMLWorker (不建议使用)强制使用HTML模板(类似于PDF模板的结构).此方法在打印详细信息"表的行时生成多个页面时效果很好,但是未应用CSS.
  • 结合使用PdfDocument,PdfParagraph和类似的类(试图遵循我发现的指南下载iText7 以使用HtmlConverter.ConvertToPdf功能,但是在这种情况下,我m在生成旋转的PDF时遇到问题.该文档位于我不熟悉的Java 中.
  • 我在Oracle中找到了创建PDF模板-着眼于定义重复字段的组"部分,但是,即使可以在C#WinForms应用程序中使用,我也找不到如何编辑PDF模板的源代码.
  • Modifying the PDF template for add variables under the detail headers and updating the field's value for add line breaks each time, but, it seems the size of the variable in the PDF matters and so, the line breaks aren't expanding the variable (and forcing) to generate more pages.
  • Using HTMLWorker (deprecated) for force an HTML template (similar to PDF template's structure). This approach works well for generating multiple pages when printing the Details table's rows, but CSS is not applied.
  • Using the combination of PdfDocument, PdfParagraph and similar classes (tried to follow the guidelines I found here), but I honestly have no idea how set in the specific coordinates and using the correct measure points for a set each element in the resulting PDF file.
  • Download iText7 for use HtmlConverter.ConvertToPdf functionality, but in this case, I'm having problems generating the PDF rotated. The documentation is in java which I'm not familiar.
  • I found this documentation in Oracle for Create a PDF Template - focusing on "Defining Groups of Repeating Fields" section, but I can't find how to edit the source code of the PDF template neither if this can be used in a C# WinForms application.

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

All the mentioned attempts generate me more problems than solutions, hence, I want to focus one more time using the PDF template approach.

我整周忙于这项任务,但我没主意.

I'm running the whole week with this assignment and I'm out of ideas.

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

I'll edit my question for add related questions with the same problem I'm facing:

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

I'm hardly trying to avoid creating a give me the code question, but, any suggestion or any idea about how can I generate PDF files correctly are welcome.

推荐答案

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

Due to time and other work-related issues, after search during a entirely week, I get the following solution:

  • 我决定不再生成正在构建的PDF文件中的页面,而是决定生成X数量的临时PDF文件.(每个文件的名称与之相关,例如: PDF_File_0.pdf PDF_File_1.pdf PDF_File_2.pdf 等).
  • 生成临时PDF文件后,我使用在此答案中找到的代码合并所有PDF文件在一个PDF文件中.
  • Instead of keep focus in add pages in the PDF file in construction, I decided to generate X quantity of temporal PDF files (each on, with related names like: PDF_File_0.pdf, PDF_File_1.pdf, PDF_File_2.pdf, etc).
  • Once the temporal PDF files are generated, I used the code I found in this answer for merge all the PDF files in a single PDF file.

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

If anyone is interested in know how to add multiple pages in a PDF file (while is been created), you can use this code:

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();

我正在使用此答案中的代码在单个PDF文件中合并" PDF临时文件:

I'm using the code from this answer for "merge" PDF temp files in a single PDF file:

/// <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模板的发票.

The PDF file to create is actually a invoice that uses a PDF template.

在这里,我需要将存储在DataTable变量中的详细信息"划分为10个记录/行的块.

Here, I need to divide the Details "stored in a DataTable variable" in chunks of 10 records/rows.

要设置块(我需要设置10个记录/行的块),我向DataTable变量"PAGE_SEPARATOR"添加了新的DataColumn并更新了"PAGE_SEPARATOR"的值"列,其结果如下:

For set the chunks (I need set chunks of 10 records/rows), I added a new DataColumn to the DataTable variable called "PAGE_SEPARATOR" and update the value of "PAGE_SEPARATOR" column with result of this division:

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

此代码的完整说明可以在此处

The full explanation of this code can be found here

对于每条记录,我必须添加具有PDF模板结构的新页面.

For each chunk of records I have to add a new page with the PDF template's structure.

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

This is the full code for generate the PDF invoice file using the PDF template and generate the PDF files for each chunk of records; at the end of the process, call the MergePdfs method and delete the temp PDF files:

注意:您可能会发现西班牙语注释,但我希望代码足够清晰,可以根据您的目的进行理解和修改.

NOTE: You may find comments in spanish, but I hope the code is clear enough for understand and modify according to your purposes.

/// <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;
}

这是我与西班牙语中的堆栈溢出密切相关的条目.

This is my closely related entry in Stack Overflow in spanish.

这篇关于在PDF文件创建过程中添加新页面(如果需要)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆