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.

Home Page:https://www.questpdf.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Help] Minimum cell height based on rotated text

rverberne-carapax opened this issue · comments

Hello,

The table has rotated text in the header using 'RotateLeft()'. But now the height of the cell is very big.

I could set a Height() on the header cell and that would break it in 2 lines. But i would rather have a dynamic height.

Is it possible to let the height of the cell grow, and stop growing when the text fits in all available space? I want the lowest height while still showing all text.

Thanks.

image

I am trying a dynamic component, but a big problem seems to be that if the available space is too small, you get a LayoutException. Instead i would expect the whole component to be moved to the next page. But maybe i misunderstand something.

When i try ShowEntire() it works fine for non dynamic components. But when a dynamic component is used, and it does not fit i get a DocumentLayoutException.

Ignore this, i have solved it. This way:

bool isTooLarge = table.Size.Height > context.AvailableSize.Height;
return new DynamicComponentComposeResult
{
    Content = isTooLarge
        ? context.CreateElement(e => { })
        : table,
    HasMoreContent = isTooLarge,
};

I wonder if this is the correct way of doing it. Mainly context.CreateElement(e => { }). The Compose method is called 10+ times. I hope this is normal.

I think i have solved the main problem. I use a incrementing AttemptedHeaderHeight value to get the first attempt that returns a non zero Height. My assumption is that zero Height means failure. Please comment if this is risky, or if there is a better way.

public DynamicComponentComposeResult Compose(DynamicContext context)
{
    var bestAttempt = Enumerable
        .Range(0, 1000)
        .Select(i => new
        {
            AttemptedHeaderHeight = i,
            AttemptedTable = CreateTable(context, i),
        })
        .FirstOrDefault(a => a.AttemptedTable.Size.Height != 0);

    if (bestAttempt is null)
    {
        throw new Exception("Unexpected error while rendering table.");
    }

    var table = bestAttempt.AttemptedTable;
    bool isTooLarge = table.Size.Height > context.AvailableSize.Height;

    return new DynamicComponentComposeResult
    {
        Content = isTooLarge
            ? context.CreateElement(e => { })
            : table,
        HasMoreContent = isTooLarge,
    };
}

I am sorry for the late response 😄

I suggest using a combination of HeightMax (sets an upper bound for how big the cell can be) and ScaleToFit (resolves corner cases when text is very long by scaling it down) elements.

Hi, thanks for your answer,

MaxHeight and ScaleToFit does something a little different. The text is only broken in multiple lines after MaxHeight would be exceeded. Also, mixing font sizes does not look as good.

This is the result of my dynamic component: It breaks the lines and uses minimal height to fit them. Is there anything wrong with this approach?

image

Absolutely not; your implementation is completely okay 😄 It is just a bit complex, and many developers may prefer a simpler approach, even if it does not produce perfect results.

Ok, thank you. I will close the issue.