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.28k stars 560 forks source link

When export svg, the background of part of legends is black #1470

Closed dumbnessrf closed 3 weeks ago

dumbnessrf commented 6 months ago

when i try to convert chart to svg file, i met some issues that my legends part become black, but it render well in the UI image in the ui image

public Axis[] XAxes { get; set; } = [new Axis() { 
    Name = "Minutes",
    MinLimit = 0-5, // forces the axis to start at -5
    MaxLimit = 60+5, // forces the axis to end at 65
    CustomSeparators = Enumerable.Range(0,61).Where(s=>s%5==0).Select(d=>Convert.ToDouble(d)).ToList()
}];
public Axis[] YAxes { get; set; } = [new Axis()
{ 
    Name= "Count" ,
    Labeler = (d) =>
    {
        return $"*{d:F0}";
    },
    MinLimit = 0,
    SubseparatorsPaint = new SolidColorPaint
    {
        Color = SKColors.Black.WithAlpha(50),
        StrokeThickness = 0.5f
    },
    SubseparatorsCount = 4
}];

var cartesianChart = new LiveChartsCore.SkiaSharpView.WPF.CartesianChart() { Width = 800, Height = 400};
cartesianChart.XAxes = XAxes;
cartesianChart.YAxes = YAxes;
cartesianChart.Series = Series;
cartesianChart.LegendPosition = LiveChartsCore.Measure.LegendPosition.Right;

thanks if any (๑→ܫ←)

dumbnessrf commented 6 months ago

i found reason in svg plain text, there is a tag clipPath which defines svg rect size, and there is id key

<clipPath id="cl_d">
    <rect width="800" height="400" />
</clipPath>

and there also have tag g with the same id clip-path="url(#cl_d)", when i add fill="none", the legend works fine!

<g clip-path="url(#cl_d)" fill="none">
    <path d="M649.764 270.225L649.764 129.77 ....."
</g>

all you need is to find what clipPath's id that define svg size, and find <g clip-path="url(#your id)">, add fill="none" Actually, this is a bit troublesome. I don't know why it's like this. It doesn't happen when I export the PNG

dumbnessrf commented 6 months ago

here is my script for fix this bug, and it need thrid lib call SVG, you can download it from nuget

var path = @"CartesianStraight.svg";
SvgDocument svgDocument = SvgDocument.Open(path);
var svgSizeElement = svgDocument.Children.First() as SvgRectangle;
var width = svgSizeElement.Width.Value;
var height = svgSizeElement.Height.Value;
var svgID = "";
foreach (var element in svgDocument.Children)
{
    if (element is SvgClipPath clip_path)
    {
        if (clip_path.Children.First() is SvgRectangle rect && svgID=="")
        {
            if (rect.Width.Value == width && rect.Height.Value == height)
            {
                svgID = clip_path.ID;
            }
        }
    }
    if (element is SvgGroup g)
    {
        var str = g.ClipPath.ToString();

        if (str.Contains(svgID))
        {
            g.Fill = new SvgColourServer(System.Drawing.Color.Transparent);
        }
    }
}
svgDocument.Write(@"1234Cartesian.svg");
mimeie commented 6 months ago

Hello @dumbnessrf

could you provide what's behind Series in your code cartesianChart.Series = Series;

it could help me with my issue: https://github.com/beto-rodriguez/LiveCharts2/issues/1469

Thanks Michael

dumbnessrf commented 6 months ago

Hello @dumbnessrf

could you provide what's behind Series in your code cartesianChart.Series = Series;

it could help me with my issue: https://github.com/beto-rodriguez/LiveCharts2/issues/1469

Thanks Michael

actually, it just some kind of plain data, the data type is LineSeries

Series = new LineSeries<int>[] { };
var temp = new List<LineSeries<int>>();
foreach (var d in PeriodTrendParams)
{
    temp.Add(new LineSeries<int>
    {
        Values = new ObservableCollection<int>(d.PeriodParams.Select(d => d.LogsCount)),
        Fill = new SolidColorPaint(GetColorByLevel(d.Level)), // mark
        Name = d.Level.ToString(),
        Stroke = null,
        GeometryFill = null,
        GeometryStroke = null
    });
}
Series = temp.ToArray();
mimeie commented 6 months ago

Hi @dumbnessrf

thanks very much for your fast reply. So in this case what I don't get is what's exactly inside Values? It seems it is just an observable integer. so where is the Value for the x-axis in your data? somehow in your screenshot it starts at "40 minutes"

or do you just write zeros into the collection before 40minutes?

dumbnessrf commented 6 months ago

Hi @dumbnessrf

thanks very much for your fast reply. So in this case what I don't get is what's exactly inside Values? It seems it is just an observable integer. so where is the Value for the x-axis in your data? somehow in your screenshot it starts at "40 minutes"

or do you just write zeros into the collection before 40minutes?

just write zeros into that collection in my case, i made a logs viewer in wpf, and the x-axis value just the minutes in an hour, y-axis value is the logs count in that period of minutes, and i use that to observe my logs trends

mimeie commented 6 months ago

worked fine with zeros.

Meanwhile my issue was also answered, it works also with x and y: https://livecharts.dev/docs/maui/2.0.0-rc2/samples.lines.xy#specify-both-x-and-y

beto-rodriguez commented 3 weeks ago

Thanks for your workaround @dumbnessrf, I think this is the best alternative for now, SVG is parsed by SkiaSharp, there not much this library can do there, at the level of this library, your workaround is perfect, I am closing this because that seems a good alternative, If someone really needs the perfect SVG, then the issue should be raised to SkiaSharp.