QuestPDF / QuestPDF

QuestPDF is a modern open-source .NET library for PDF document generation. Offering comprehensive layout engine powered by concise and discoverable C# Fluent API. Easily generate PDF reports, invoices, exports, etc.
https://www.questpdf.com
Other
12k stars 629 forks source link

Declare items in column to be of relative height #330

Open wbijker opened 2 years ago

wbijker commented 2 years ago

Describe the solution you'd like When creating a row there is an option to specify the columns's width as relative items taking a unitless parameter that serves as a proportional unit.

container.ExtendHorizontal().Row(row =>
{
    row.RelativeItem(1).Text("12.5% total width");
    row.RelativeItem(2).Text("25% total width");
    row.RelativeItem(5).Text("62.5% of total width");
});

I would like to have the same feature for columns (vertical) containers. Something like:

container.ExtendVertical().Column(col => 
{
    col.RelativeItem(1).Text("12.5% total height");
    col.RelativeItem(2).Text("25% total height");
    col.RelativeItem(5).Text("62.5% of total height");
});

Describe alternatives you've considered Currently I need to specify the height explicit and do the maths manually.

container.ExtendVertical().Column(col => 
{
    // the total page height - in this case A4 landscape. in mm
    var height = 210;
    var items = (float) (condition1 ? 1 : 0) + (condition2 ? 1 : 0) + (condition3 ? 1 : 0);

    if (condition1)
        col.Item().Height(height / items, Unit.Millimetre).Element(BuildCondition1);
    if (condition2)
        col.Item().Height(height / items, Unit.Millimetre).Element(BuildCondition2);
    if (condition3)
        col.Item().Height(height / items, Unit.Millimetre).Element(BuildCondition3);
});

Possible in current API? More elegant way to achieve this?

wbijker commented 2 years ago

I've used IDynamicComponent to achieve this. I've build a extensive API to be used as above, but the basic idea is this:

    public class DynamicHeightComponent : IDynamicComponent<int>
    {

        public DynamicComponentComposeResult Compose(DynamicContext context)
        {
            var content = context.CreateElement(container =>
            {
                var heightPerUnit = context.AvailableSize.Height / 6;
                container.Column(col =>
                {
                    col.Item().Height(heightPerUnit * 1)...
                    col.Item().Height(heightPerUnit * 2)...
                    col.Item().Height(heightPerUnit * 3)...
                });

            });

            return new DynamicComponentComposeResult()
            {
                Content = content,
                HasMoreContent = false
            };
        }
    }

Usage:

 container.Dynamic(new DynamicHeightComponent(...));

Gist container my extensive API with relative, constant and auto heights: https://gist.github.com/wbijker/62fb02a2cf822ab013c33324ac095b85

It works ok, but

MarcinZiabek commented 2 years ago

@wbijker I am working on a very similar feature. Would you like to take a look and provide your thoughts? Link to PR: https://github.com/QuestPDF/QuestPDF/pull/357


Also, I was thinking about an alternative to your solution. It uses the RotateLeft / RotateRight elements:

.Padding(20)
.AlignLeft()
.RotateRight() // rotate, so Row acts as Column
.Row(row =>
{
    row.Spacing(20);

    row.RelativeItem(1).Element(Content);
    row.RelativeItem(2).Element(Content);
    row.RelativeItem(3).Element(Content);

    void Content(IContainer container)
    {
        container
            .RotateLeft() // rotate back each child
            .Border(1)
            .Background(Placeholders.BackgroundColor())
            .Padding(5)
            .Text(Placeholders.Label());
    }
});

The code produces the following result:

wbijker commented 2 years ago

@MarcinZiabek, #357 is exactly wat I was looking for. It also caters for more advanced layout. While #357 is open I will use proposed rotate trick as mentioned above.