How do I create a custom Invoice document with an existing template PDF file using .NET C#?

2023-04-19 14_27_53-Snipping Tool.png (42.8 KB)

I want to create a final PDF document based on this template PDF in the picture above: The template will already contain the company address and logo within it. However, the placeholders in the template such as [PURCHASECONTACT] will be replaced during runtime using the following method in C#:

TextFragmentAbsorber tfa = new TextFragmentAbsorber(placeholder.Key);
document.Pages.Accept(tfa);
TextFragmentCollection tfc = tfa.TextFragments;
if (tfc.Count > 0)
{
     TextBuilder textBuilder = new TextBuilder(document.Pages[1]);
     TextFragment tf = tfc[1];

    if (placeholder.Value != null)
    {
         switch (placeholder.Key)
    {
     default:
          tf.Text = placeholder.Value;
          break;
         }
   }
}

I want to then add custom Aspose table in the green section in the picture. Once this [ORDERLINES] table has been made, I want to then Absorb the totals table, highlighted in red border in the bottom-right hand side of the page to be dynamically moved to the end of [ORDERLINES] table. This needs to take in accounts the the number of new pages the [ORDERLINES] table will span to depending on how long it will get and then accurately placing the totals table underneath it.

I would like to know what is the best approach for these types of requirements and also the best best practices to use.

@vishv,

Pdf Documents are not flow-type(where items accommodate themself in the space) it is box-type. Which means it is restricted to rectangles.

My suggestion would be to write the whole table but with placeholders.

Then you use TableAbsorbers to manipulate the table with the information you need: Manipulate Tables in existing PDF|Aspose.PDF for .NET

Hi thanks for the suggestion.

When you mean write the whole table with placeholders, which table are you referring to?

Thank you

@vishv,

Everything but especially the one in the green section. You can add lines to an existing table, which will be easier than creating from scratch.

Now remember what I said about Pdf documents being box-types. If you have something dynamic that can have from 1 to many pages, I would forget the template idea and generate the document from scratch every time. This way, you do not have to deal with text or table writing on top of another.

Usually, when developing invoices and stuff like on boxed-type documents using a template will make your work harder than what you save from reusing.

It is code, so it won’t be writing itself every time you execute it.

Now if the template has another purpose, like having a dynamic logo or format that a client upload, then it is another story, and you will have to deal with those difficulties when coding it,

I hope this explains it a bit better.

So you suggest creating all tables using aspose tables and then adding data directly to the rows during the creation of the tables and not use placeholders to replace data?

Also if I was to manually create all the tables, how can I align these tables on certain areas of the page as I want, without odd behaviours such as cropping of table data when a table gets closer to the edge of the page etc…

@vishv,

I am confused. I thought the tables contained a dynamic amount of rows. If not, make it static. But when something is dynamic, the problems happen with the information below it.

When you say align, what do you mean? Do you mean “purchase Contact” and “delivery date” tables? I saw your picture, and that instead of 2 tables is just one, with 3 columns, having the middle column white with no borders. So you have the effect that it is one on the left side and one on the right side.

Also, you give each column a width. It won’t go beyond that limit.

Just read the multiple examples here: Create or Add Table In PDF using C#|Aspose.PDF for .NET

Whenever something needs to be “aligned”, make it the same table row instead of different tables.

You can do it in a different way, but it will be more difficult.

Going by using the template method, only dynamic table we have is the [ORDERLINES] table which is expected to expand in number of rows, since there is that assumption, we need to be careful with whatever information that comes after the [ORDERLINES] table. Like the totals table. Our idea was to draw the [ORDERLINES] table manually and then absorb the Totals table from the original template and then move it at the end of the [ORDERLINES] table once orderlines table has been finished generating. - but had problems trying to fully absorb the totals table.

Also,

instead of 2 tables is just one, with 3 columns, having the middle column white with no borders. So you have the effect that it is one on the left side and one on the right side.

was another approach we had in mind, due to the amount of alignment issues we encountered when trying to place those mini tables like “purchase Contact” and “delivery date” tables as seen in the picture.

We did have an earlier prototype where we drew the [ORDERLINES] table dynamically with aspose tables and we did manage to create the [TOTALS] table dynamically as well, and then add this underneath [ORDERLINES] table. This method did work for us, however, as we were trying to fit the [TOTALS] table towards the right side of the page as shown in the picture, odd behaviours started to show up like cropping of the the numbers within the [TOTALS] table.

Also can’t seem to understand the table.ColumnWidth property. What parameters shall we use to show a table with cells that expand automatically based on the cell text/content length and also have the table fit in the page width wise and have cells with auto width for text/content within?

@vishv,

Here is an simple example of what I described before. You can tweak the value and use it on your own code to achive those tables aligned:

private void Logic()
{
    Document doc = new Document();
    var page = doc.Pages.Add();

    doc.SetTitle("Example");
    TextState displayValueStyle = new TextState();
    displayValueStyle.ForegroundColor = Color.Black;
    displayValueStyle.Invisible = false;

    Table table_body = new Table();
    page.Paragraphs.Add(table_body);

    table_body.ColumnWidths = "30% 40% 30%"; // Cannot go beyonf 100%
    table_body.Border = new BorderInfo(BorderSide.Left | BorderSide.Right, 1, Aspose.Pdf.Color.Black);

    // Create MarginInfo object and set its left, bottom, right and top margins
    MarginInfo margin = new MarginInfo();
    margin.Left = 5f;
    margin.Right = 5f;
    margin.Top = 5f;
    margin.Bottom = 5f;

    // Set the default cell padding to the MarginInfo object
    table_body.DefaultCellPadding = margin;
    TextState textState = new TextState();
    textState.FontStyle = FontStyles.Bold;

    //Add Table Header
    var row = table_body.Rows.Add();

    var cell = row.Cells.Add("Column One");
    cell.DefaultCellTextState = textState;
    cell.BackgroundColor = Color.Teal;
    cell.Border = new BorderInfo(BorderSide.Top | BorderSide.Bottom | BorderSide.Right, 1, Aspose.Pdf.Color.Black);

    cell = row.Cells.Add("");
    cell.DefaultCellTextState = textState;


    cell = row.Cells.Add("Column Three");
    cell.BackgroundColor = Color.Teal;
    cell.Border = new BorderInfo(BorderSide.Top | BorderSide.Bottom | BorderSide.Left, 1, Aspose.Pdf.Color.Black);
    cell.DefaultCellTextState = textState;

    row = table_body.Rows.Add();
    cell = row.Cells.Add("Value One");
    cell.DefaultCellTextState = displayValueStyle;
    cell.Border = new BorderInfo(BorderSide.Right | BorderSide.Bottom, 1, Aspose.Pdf.Color.Black);
    row.Cells.Add("").DefaultCellTextState = displayValueStyle;
    cell = row.Cells.Add("Value Three");
    cell.DefaultCellTextState = displayValueStyle;
    cell.Border = new BorderInfo(BorderSide.Left | BorderSide.Bottom, 1, Aspose.Pdf.Color.Black);

    row = table_body.Rows.Add();
    
    cell = row.Cells.Add("Another value for column One");
    cell.Border = new BorderInfo(BorderSide.Right | BorderSide.Bottom , 1, Aspose.Pdf.Color.Black);
    row.Cells.Add("").DefaultCellTextState = displayValueStyle;
    cell = row.Cells.Add("Another value for column Three");
    cell.Border = new BorderInfo(BorderSide.Left | BorderSide.Bottom, 1, Aspose.Pdf.Color.Black);

    doc.Save($"{PartialPath}_output.pdf");
}

The output:
ColumnWidthSample_output.pdf (2.7 KB)

Hi thank you for the sample code, we have tired an approach with HTML to Aspose.PDF by using the following method:
HtmlLoadOptions options = new HtmlLoadOptions();
Document pdfDocument = new Document(dir + "index.html", options);

but we are faced with a huge page margin. I have tried to set the page margin to a lower value but this white space stays. Any idea how we can get rid of this default Aspose page margin when trying to convert an HTML file into a PDF?

Thank you

HTML to PDF method output: Capture.PNG (37.9 KB)

@vishv,

Whenever there is a conversion things get complicated because it is like a black box.

But can you iterate the pages and do something like this?

page.PageInfo.Margin = new MarginInfo(0, 0, 0, 0);

Hi,

Just tried page.PageInfo.Margin = new MarginInfo(0, 0, 0, 0); and it had no affect…

@vishv,

Whould you mind sharing a html file(zip it in order to upload in the forum) with me so I can try to if I can change the margins?

Yeah sure, you can find the HTML here > index.zip (1019 Bytes)

@vishv,

Use the following code the fix the excessive margins generated:

private void Logic()
{
    var loadOptions = new HtmlLoadOptions();
    loadOptions.PageInfo.Margin = new MarginInfo(0, 0, 0, 0);

    Document doc = new Document($"{PartialPath}_input.html", loadOptions);

    // Save output PDF document
    doc.Save($"{PartialPath}_output.pdf");
    
}

You can change those margins to whatever you want.

Hi thanks for the reply, we will try this!

@vishv,

Let me know how it goes, please.