var generator = new PdfGenerator();

var document = await generator.CreateDocument(doc =>
{
    doc.Page(page =>
    {
        page.Size(PageSize.A4);
        page.Margin(24);
        page.Header(header => header.Text(t =>
        {
            t.Alignment(TextAlignment.Center);
            t.Span("Quarterly Report").Bold().FontColor(PdfColor.FromRgb(70, 70, 70));
        }));
        page.Footer(footer => footer.Text(t =>
        {
            t.Alignment(TextAlignment.Center);
            t.Span("Page ");
            t.CurrentPageNumber();
            t.Span(" of ");
            t.TotalPages();
        }));
        page.Content(container =>
        {
            container.Column(column =>
            {
                column.Spacing(16);

                column.Item()
                    .Padding(16).Border(1, PdfColor.FromHex("#DDDDDD")).CornerRadius(8)
                    .Background(PdfColor.FromRgb(248, 248, 248))
                    .Shadow(new PdfBoxShadow(PdfColor.FromArgb(40, 0, 0, 0), 0, 3, 6))
                    .Text(t =>
                    {
                        t.Span("Built directly in C# - ").FontSize(14);
                        t.Span("no HTML or CSS strings involved.").FontSize(14).Italic();
                    });

                column.Item().Table(table =>
                {
                    table.Columns(columns =>
                    {
                        columns.RelativeColumn(2);
                        columns.RelativeColumn(1);
                        columns.RelativeColumn(1);
                    });
                    table.Header(header =>
                    {
                        header.Cell().Padding(4).Text(t => t.Span("Region").Bold());
                        header.Cell().Padding(4).Text(t => t.Span("Q1").Bold());
                        header.Cell().Padding(4).Text(t => t.Span("Q2").Bold());
                    });
                    foreach (var (region, q1, q2) in new[] { ("North", "$42k", "$48k"), ("South", "$31k", "$35k"), ("West", "$27k", "$30k") })
                    {
                        table.Row(row =>
                        {
                            row.Cell().Padding(4).Text(region);
                            row.Cell().Padding(4).Text(q1);
                            row.Cell().Padding(4).Text(q2);
                        });
                    }
                });

                column.Item().LineHorizontal(1, PdfColor.FromHex("#BBBBBB"), dashed: true);

                column.Item().UnorderedList(list =>
                {
                    list.Item().Text("Revenue grew across every region.");
                    list.Item().Text("West opened its first retail location.");
                }, PdfListMarkerType.Disc);

                column.Item().ClampLines(2, "… [continued]").Text(
                    "A full year in review: every region grew its revenue year over year, led by a " +
                    "particularly strong second half, and the West region opened its first physical " +
                    "retail location - a milestone the team has been working toward since the start " +
                    "of the fiscal year.");

                column.Item().Text(t =>
                {
                    // PdfTextDirection.Auto detects each span's own base direction from its text (the
                    // same first-strong-character detection dir="auto" uses on the HTML side) - no
                    // need to know ahead of time which language a given run of content is in. This
                    // English span resolves to ltr; a Hebrew or Arabic span (given a font covering
                    // those glyphs) would resolve to rtl the same way.
                    t.Span("Auto-detected direction, per span: ").Direction(PdfTextDirection.Auto);
                    t.Span("this paragraph resolves left-to-right.").Direction(PdfTextDirection.Auto);
                });

                // Standalone SVG: author-supplied markup placed directly, and a real charting
                // library's own SVG export - both go through the same Svg(string) overload.
                column.Item().Row(row =>
                {
                    row.Spacing(12);

                    row.Item().Grow().Svg(
                        """
                        <svg xmlns="http://www.w3.org/2000/svg" width="300" height="180" viewBox="0 0 100 60">
                          <circle cx="50" cy="30" r="26" fill="#FFDEE9" stroke="#B5FFFC" stroke-width="3"/>
                          <text x="50" y="34" font-size="10" text-anchor="middle" fill="#444">Hand-authored</text>
                        </svg>
                        """);

                    var scatterPlot = new Plot();
                    scatterPlot.Add.Scatter(
                        new double[] { 1, 2, 3, 4, 5, 6 },
                        new double[] { 12, 18, 15, 24, 21, 30 });
                    scatterPlot.Title("Weekly signups");
                    row.Item().Grow().Svg(scatterPlot.GetSvgXml(300, 180));
                });

                // A dynamic raster chart, generated at exactly the container's own resolved pixel
                // size once layout knows it - the reason Image(Func<PdfSize,byte[]>) exists at all.
                column.Item().Width(300).Height(160).Image(size =>
                {
                    var barPlot = new Plot();
                    barPlot.Add.Bars(new double[] { 42, 31, 27 });
                    barPlot.Title("Q1 revenue by region ($k)");
                    // 2x the container's own point size for a crisp raster at normal PDF viewer zoom
                    // (a PDF point isn't a device pixel) - the fill-by-default width:100%/height:100%
                    // still scales this to the container's actual size regardless of pixel count.
                    return barPlot.GetImageBytes((int)size.Width * 2, (int)size.Height * 2, ImageFormat.Png);
                });
            });
        });
    });
});

var stream = new MemoryStream();
document.Save(stream);