在PDF文件创建过程中添加新页面(如果需要)

时间:2018-09-27 17:36:53

标签: c# winforms pdf itext

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

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

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

PDF Template

我在此代码中使用 (版本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 result sample

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

>

这是数据被覆盖的结果:

Result with overwritten data

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

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

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

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

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

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


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

1 个答案:

答案 0 :(得分:0)

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

  • 我决定不再生成正在构建的PDF文件中的页面,而是决定生成X数量的临时PDF文件(每个文件,其相关名称如下: PDF_File_0.pdf PDF_File_1.pdf PDF_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“的DataColumn变量添加了一个新的DataTable,并更新了“ 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文件:

  

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

/// <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中密切相关的条目。