beto-rodriguez / LiveCharts2

Simple, flexible, interactive & powerful charts, maps and gauges for .Net, LiveCharts2 can now practically run everywhere Maui, Uno Platform, Blazor-wasm, WPF, WinForms, Xamarin, Avalonia, WinUI, UWP.
https://livecharts.dev
MIT License
4.27k stars 552 forks source link

Rendering into an existing SkiaSharp Canvas #526

Closed girlpunk closed 2 years ago

girlpunk commented 2 years ago

Is your feature request related to a problem? Please describe. I'm currently working on a project that renders PDF documents using the QuestPDF library. This uses SkiaSharp for rendering, and has a built-in function for rendering custom content into an Skia canvas.

Describe the solution you'd like Is it possible to render a chart into the canvas provided by QuestPDF?

Describe alternatives you've considered A partial workaround is to use LiveCharts2 to render into it's own canvas, then convert the canvas to an image, encode that image as a PNG, then re-import that back into the PDF's Skia instance. This isn't ideal for memory use, output scaling (only a fixed resolution is possible, the chart can't be re-scaled if the output PDF preview is resized), and doesn't appear to support some features such as legends.

Additional context For reference, this is the QuestPDF function to get a canvas. It also provides the expected size of the chart, as the callback run after layout compositing. https://www.questpdf.com/documentation/api-reference.html#canvas

beto-rodriguez commented 2 years ago

Interesting, it should not be hard to implement this.

I will keep this issue open to track this implementation and also to build a sample for the web site.

beto-rodriguez commented 2 years ago

I added a sample to solve this case, it is actually supported already, I will add this to the docs.

using System.Diagnostics;
using System.Reflection;
using LiveChartsCore;
using LiveChartsCore.SkiaSharpView;
using LiveChartsCore.SkiaSharpView.SKCharts;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SkiaSharp;

Document.Create(container =>
{
    _ = container.Page(page =>
    {
        page.Size(PageSizes.A4);
        page.Margin(2, Unit.Centimetre);
        page.PageColor(Colors.White);
        page.DefaultTextStyle(x => x.FontSize(20));

        page.Header()
            .Text("Hello PDF!")
            .SemiBold().FontSize(36).FontColor(Colors.Blue.Medium);

        page.Content()
            .Canvas((canvas, size) =>
            {
                var cartesianChart = new SKCartesianChart
                {
                    Width = (int)size.Width,
                    Height = (int)size.Width,
                    Series = new ISeries[]
                    {
                        new LineSeries<int> { Values = new int[] { 1, 5, 4, 6 } },
                        new ColumnSeries<int> { Values = new int[] { 4, 8, 2, 4 } }
                    }
                };

                canvas.DrawImage(cartesianChart.GetImage(), new SKPoint(0, 0));
            });

        page.Footer()
            .AlignCenter()
            .Text(x =>
            {
                x.Span("Page ");
                x.CurrentPageNumber();
            });
    });
})
.GeneratePdf("hello.pdf");

var path = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!, "hello.pdf");
Console.WriteLine($"PDF generated at {path}");
Process.Start(new ProcessStartInfo { FileName = $"file:///{path}", UseShellExecute = true });

Thanks for the report!