oxyplot / oxyplot

A cross-platform plotting library for .NET
https://oxyplot.github.io/
MIT License
3.23k stars 949 forks source link

Legend titles truncated when plot exported to svg #1737

Open nathanbennett opened 3 years ago

nathanbennett commented 3 years ago

Steps to reproduce

  1. Export a plot with a legend to an svg file using OxyPlot.SvgExporter. I used the following C# code to recreate this issue:
            var plotModel = new PlotModel()
            {
                DefaultFont = "Arial",
                TitleFontWeight = FontWeights.Normal,
            };

            plotModel.Legends.Add(new Legend
            {
                LegendPlacement = LegendPlacement.Outside,
                LegendPosition = LegendPosition.TopCenter,
                LegendOrientation = LegendOrientation.Horizontal,
            });

            plotModel.Series.Add(new LineSeries { Title = "Series One" });
            plotModel.Series.Add(new LineSeries { Title = "Series Two" });

            File.WriteAllText(
                Path.Combine(Path.GetTempPath(), this.GetType().FullName, $"plotLegendIssue.svg"), 
                SvgExporter.ExportToString(plotModel, 800, 500, false)
            );

Platform: Windows .NET version: .NET Framework 4.6.2

Expected behaviour

Series titles appear in legend in full.

Actual behaviour

Series titles get truncated at end of legend.

image

Additional Info

Seems to have been introduced between 2.1.0-unstable.1434 and 2.1.0-unstable.1438. It it still present in the latest version 2.1.0-unstable.1455.

Looking at the svg produced, the text element for the titles are now within a g element using a clip path of insufficient width. In version 1434, the text elements were not within a g element. The following zip contains the output from 1434 and 1455 for comparison plotLegendIssue_versionComparison.zip

VisualMelon commented 3 years ago

This is likely an issue with the text measurement being inaccurate in the 'default' SVG exporter: can you use one of the SVG exporters available in OxyPlot.WindowsForms, OxyPlot.WPF, or OxyPlot.SkiaSharp (the last of which works completely differently, but doesn't support dashed lines at the moment)?

nathanbennett commented 3 years ago

@VisualMelon Thank you. The legend text is not truncated by any of those alternative svg exporters (though the WPF / Windows Forms legends do not vertically center the line).

image

We are avoiding using OxyPlot.SkiaSharp.SvgExporter due to the dashed line issue. It looks like we can use OxyPlot.WindowsForms.SvgExporter which hasn't broken any of our charts, though it has changed the position of text and increased the outer padding (likely due to the different text measurement results).

lopango commented 2 years ago

I think all this just come from the transformation on the font size inside the MeasureText in the PdfRendererContext: "fontSize / 96 * 72"

to test this, I just created a class where i inverted the transformation:

public class CustomPdfRenderContext : PdfRenderContext
{
    public CustomPdfRenderContext(double width, double height, OxyColor background) : base(width, height, background)
    {
    }

    public override OxySize MeasureText(string text, string fontFamily, double fontSize, double fontWeight)
    {
        return base.MeasureText(text, fontFamily, fontSize * 96 / 72, fontWeight);
    }
}

then used it like this:

var exporter = new SvgExporter { Width = width, Height = height, TextMeasurer = new CustomPdfRenderContext(width, height, model.Background) };
exporter.Export(model, stream);

And all was back to normal (including some overlaping in axes title).

edisonhuayin commented 2 years ago

Thank you i have solved my problem.

plotModel.Legends.Add(new Legend { LegendPlacement = LegendPlacement.Outside, LegendPosition = LegendPosition.TopCenter, LegendOrientation = LegendOrientation.Horizontal, });

This is right to use. The 2.0.0 below is wrong to use. PlotModel1.LegendPosition = LegendPosition.LeftTop; PlotModel1.LegendPosition = LegendPosition.LeftTop; PlotModel1.LegendBackground = OxyColor.FromArgb(255, 190, 190, 190);//DarkGrey