Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# - itextSharp subtotal on every page

Tags:

c#

itext

subtotal

The solution for C# you can find in the Edit-Part of the Question. Special Thanks to Bruno Lowagie

I'm trying to create an invoice via iTextSharp in C#. It works very well but im gettin problems while i try to print the subtotal on every page.

Here is a part of the code im using to create the table for the products:

PdfPTable table = new PdfPTable(5);
        table.WidthPercentage = 100;
        float[] widths = new float[] { 10f, 30f, 120f, 30f, 30f };
        table.SetWidths(widths);
        table.SkipLastFooter = true;
        table.SpacingAfter = 10;                    

        //Cells to Write Header & Footer
        AddCell(table, "Pos.", PdfPCell.ALIGN_RIGHT, BORDER_LTB, 0);
        AddCell(table, "Menge", PdfPCell.ALIGN_RIGHT, BORDER_TB);
        AddCell(table, "Text", PdfPCell.ALIGN_LEFT, BORDER_TB);
        AddCell(table, "Einzelpreis ", PdfPCell.ALIGN_RIGHT, BORDER_TB);
        AddCell(table, "Summe", PdfPCell.ALIGN_RIGHT, BORDER_RTB);

        AddCell(table, "SUBTOTAL", PdfPCell.ALIGN_LEFT, BORDER_LTB, 7, 4);
        AddCell(table, "", PdfPCell.ALIGN_LEFT, BORDER_RTB);

        table.HeaderRows = 2;
        table.FooterRows = 1;

        //Cells with positions of invoice
        for (int i = 0; i < beleg.Einzel.Count; i++)
        {
            AddCell(table, beleg.Einzel[i].be_lfd_nr.ToString(), PdfPCell.ALIGN_LEFT, BORDER_LTB);
            AddCell(table, beleg.Einzel[i].be_art_menge.ToString("N", _gbVars.NFI3D), PdfPCell.ALIGN_RIGHT, BORDER_TB);
            AddCell(table, (beleg.Einzel[i].be_art_bez.ToString() + "\n" + beleg.Einzel[i].be_pos_text.ToString()).Trim(), PdfPCell.ALIGN_LEFT, BORDER_TB);
            AddCell(table, beleg.Einzel[i].be_summe_preis.ToString("N", _gbVars.NFI2D), PdfPCell.ALIGN_RIGHT, BORDER_TB);
            AddCell(table, beleg.Einzel[i].be_summe_netto.ToString("N", _gbVars.NFI2D), PdfPCell.ALIGN_RIGHT, BORDER_RTB);
        }

        table.SplitLate = false;
        document.Add(table);

Border_RTB and so on are Constants for my Border-Styles.

This code generates a table witch looks like this:

+------------------------------------------+
| Pos | Menge | Text | Einzelpreis | Summe |
+------------------------------------------+
|  1  |   2   | Text |       10.00 | 20.00 |
+------------------------------------------+
|  2  |   1   | Text |       10.00 | 10.00 |
+------------------------------------------+
|  3  |   4   | Text |       10.00 | 40.00 |
+------------------------------------------+
|  4  |   2   | Text |       10.00 | 20.00 |
+------------------------------------------+
|SUBTOTAL                          |       |
+------------------------------------------+

The Table can flow to the next page an i whant to write the subtotal of every page at the footer. The event "OnEndPage" does not help me because this event does not "know" where in the table the page is broken.

Can someone tell me how i get the subtotal on every page? Is there any solution how i can sum up something on a page an print it out on the pdf-file?

Sorry if my description is not that good. Having Problems with my english. :-/

EDIT:

Here is the Solution. Special Thanks to Bruno. He has shown me the way. In my Version, the Subtotal does NOT Reset on every Page. I guess this was a misstake in my description of the problem.

The new Clases:

public class Totals
    {
        public double subtotal = 0;
        public double total = 0;
    }


    public class SubTotalEvent : IPdfPCellEvent
    {
        Double price;   
        Totals totals;
        bool printTotals = false;       //If we just whant to print out the Subtotal, this will be true.
        bool CellWrittenOnce = false;   //Bool to SUM price just once (if row flows to a second page)

        public SubTotalEvent(Totals totals, double price) {
            printTotals = false;
            this.totals = totals;
            this.price = price;
        }

        public SubTotalEvent(Totals totals) {
            this.totals = totals;
            printTotals = true;
        }

        public void CellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {
            if (printTotals)
            {
                PdfContentByte canvas = canvases[PdfPTable.TEXTCANVAS];
                ColumnText.ShowTextAligned(canvas, Element.ALIGN_LEFT, new Phrase(totals.subtotal.ToString()), position.GetLeft(0) + 2, position.GetBottom(0) + 2, 0);
                return;
            }      
            if (!CellWrittenOnce) { 
                totals.subtotal += price;
                totals.total += price;
            }
            CellWrittenOnce = true;
        }
    }

Code to create the Table:

PdfPTable table = new PdfPTable(5);
        table.WidthPercentage = 100;
        float[] widths = new float[] { 10f, 30f, 120f, 30f, 30f };
        table.SetWidths(widths);
        table.SkipLastFooter = true;
        table.SpacingAfter = 10;                    

        AddCell(new PdfPCell(new Phrase("item")));
        AddCell(new PdfPCell(new Phrase("amount")));
        AddCell(new PdfPCell(new Phrase("text")));
        AddCell(new PdfPCell(new Phrase("price")));
        AddCell(new PdfPCell(new Phrase("sum")));

        Totals totals = new Totals();
        PdfPCell cel = new PdfPCell(new Phrase("Subtotal"));
        cel.Colspan = 4;
        table.AddCell(cel);
        cel = new PdfPCell();
        cel.CellEvent = new SubTotalEvent(totals);
        table.AddCell(cel);


        table.HeaderRows = 2;
        table.FooterRows = 1;

        for (int i = 0; i < beleg.Einzel.Count; i++)
        {
            AddCell(new PdfPCell(new Phrase(i.toString())));
            AddCell(new PdfPCell(new Phrase("2")));
            AddCell(new PdfPCell(new Phrase("ItemText")));
            AddCell(new PdfPCell(new Phrase("10.00")));

            cell = new PdfPCell(new Phrase("20.00"));
            cell.CellEvent = new SubTotalEvent(totals, 20.00);
            table.AddCell(cell);
        }

        table.SplitLate = false;
        document.Add(table);
like image 362
Martin S. Avatar asked Sep 26 '16 12:09

Martin S.


1 Answers

Please take a look at the SubTotal example.

First we create a class to keep track of the subtotal and the total:

class Totals {
    double subtotal = 0;
    double total = 0;
}

Then we implement the PdfPCellEvent interface that we will use on column 5:

class SubTotalEvent implements PdfPCellEvent {

    Double price;
    Totals totals;

    public SubTotalEvent(Totals totals, double price) {
        this.totals = totals;
        this.price = price;
    }

    public SubTotalEvent(Totals totals) {
        this.totals = totals;
        price = null;
    }

    @Override
    public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {
        if (price == null) {
            PdfContentByte canvas = canvases[PdfPTable.TEXTCANVAS];
            ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT,
                    new Phrase(String.valueOf(totals.subtotal)),
                    position.getLeft() + 2, position.getBottom() + 2, 0);
            totals.subtotal = 0;
            return;
        }
        totals.subtotal += price;
        totals.total += price;
    }

}

We have defined two constructors: one for a normal cell containing a price. One for a footer cell containing a subtotal (in this case we don't pass a price).

This is how we use this cell event:

public void createPdf(String dest) throws IOException, DocumentException {

    Totals totals = new Totals();

    Document document = new Document();
    PdfWriter.getInstance(document, new FileOutputStream(dest));
    document.open();
    PdfPTable table = new PdfPTable(5);
    table.setWidths(new int[]{1, 1, 1, 3, 3});
    // header
    table.addCell("Pos");
    table.addCell("Menge");
    table.addCell("Text");
    table.addCell("Einzerpreis");
    table.addCell("Summe");
    // footer
    PdfPCell cell = new PdfPCell(new Phrase("Subtotal"));
    cell.setColspan(4);
    table.addCell(cell);
    cell = new PdfPCell();
    cell.setCellEvent(new SubTotalEvent(totals));
    table.addCell(cell);
    // definitions
    table.setHeaderRows(2);
    table.setFooterRows(1);
    // table body
    for(int r = 0; r < 50; ){
        table.addCell(String.valueOf(++r));
        table.addCell("1");
        table.addCell("text");
        table.addCell("10.0");
        cell = new PdfPCell(new Phrase("10.0"));
        cell.setCellEvent(new SubTotalEvent(totals, 10));
        table.addCell(cell);
    }
    document.add(table);
    // extra footer
    table = new PdfPTable(5);
    table.setWidths(new int[]{1, 1, 1, 3, 3});
    cell = new PdfPCell(new Phrase("Grand total"));
    cell.setColspan(4);
    table.addCell(cell);
    table.addCell(String.valueOf(totals.total));
    document.add(table);
    document.close();
}

The result looks like this:

enter image description here

On the first page, we have 46 body rows each for an item with price 10. In the footer, we have a subtotal of 460. On the second page, we have 4 body rows each for an item with price 10. In the footer, we have a subtotal of 40. We added an extra table to mimic an extra footer for the Grand Total: 500.

like image 166
Bruno Lowagie Avatar answered Nov 11 '22 09:11

Bruno Lowagie