Добавить новые страницы (при необходимости) во время создания файла PDF - PullRequest
0 голосов
/ 27 сентября 2018

Я разрабатываю приложение формы Windows для создания счетов в файлах PDF.

Это приложение Winform использует шаблон PDF для создания файла PDF.

Это снимок экранаШаблон PDF (этот шаблон был создан с использованием Adobe Acrobat XI Lite Portable ) :

PDF Template

I 'm, используя (версия 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.

Это закрытыйsest благоприятный результат, который я получил до сих пор после попытки:

  • Изменение шаблона PDF для добавления переменных в подробных заголовках и обновление значения поля для строки добавленияразрывы каждый раз, но, кажется, размер переменной в PDF имеет значение, и поэтому разрывы строк не расширяют переменную (и заставляют) , чтобы генерировать больше страниц.
  • Использование HTMLWorker (устарело) для принудительного применения HTML-шаблона (аналогично структуре шаблона PDF) .Этот подход хорошо работает для генерации нескольких страниц при печати строк таблицы Details, но CSS не применяется.
  • Использование комбинации PdfDocument, PdfParagraph и аналогичных классов (пытался следовать рекомендациям, которые я нашел здесь ) , но я, честно говоря, понятия не имею, как установить конкретные координаты и использовать правильные точки измерения для каждого элемента в результирующем PDF-файле.
  • Загрузить iText7 для использования HtmlConverter.ConvertToPdf функциональности, но в этом случае у меня возникают проблемы при генерации повернутого PDF.Документация находится в java , с которой я не знаком .
  • Я нашел эту документацию в Oracle для Создайте шаблон PDF - с акцентом на "Определение групп повторяющихся полей"", но я не могу найти, как редактировать исходный код шаблона PDF, если это не может быть использовано в приложении C # WinForms.

Все упомянутые попытки вызывают у меня больше проблем, чем решенийследовательно, я хочу сфокусироваться еще раз, используя шаблонный подход PDF.

Я выполняю целую неделю с этим заданием, и у меня нет идей.

Я буду редактироватьмой вопрос для добавления связанных вопросов с той же проблемой, с которой я сталкиваюсь:


Я вряд ли пытаюсь избежать создания , дайте мне вопрос кода, но любые предложения или идеи о том, как правильно генерировать PDF-файлы, приветствуются.

1 Ответ

0 голосов
/ 02 октября 2018

Из-за нехватки времени и других проблем, связанных с работой, после поиска в течение целой недели я получаю следующее решение:

  • Вместо того, чтобы сосредоточиться на добавлении страниц в файл PDF в процессе разработки, ярешил сгенерировать X количество временных файлов PDF (каждый со связанными именами, например: PDF_File_0.pdf , PDF_File_1.pdf , PDF_File_2.pdf и т. д.) .
  • После создания временных PDF-файлов я использовал код, обнаруженный в в этом ответе , для объединения всех 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();

Я использую код из этого ответа для «слияния» временных файлов 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.template.

Здесь мне нужно разделить детали, «хранящиеся в переменной DataTable», на куски по 10записи / строки.

Для установки чанков (мне нужно установить чанки из 10 записей / строк) , я добавил новый DataColumn в переменную DataTable с именем "PAGE_SEPARATOR" иобновите значение столбца «PAGE_SEPARATOR» с результатом этого деления:

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

Полное объяснение этого кода можно найти здесь

Для каждого куска записейМне нужно добавить новую страницу со структурой шаблона 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;
}

Это моя тесно связанная запись в Переполнение стека на испанском .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...