plotly / Plotly.NET

interactive graphing library for .NET programming languages :chart_with_upwards_trend:
https://plotly.net
MIT License
651 stars 84 forks source link

Text "newline" not working (c#) #454

Closed jaguarxii closed 3 months ago

jaguarxii commented 4 months ago

Description

Trying to set a text that has "newline" characters does not work on Titles or Markers Text

Repro steps

Initialize a Title with new line character

  1. Title.init( Text: "Simple\nbar charts" ) image

  2. Title.init( Text: "Simple<br>bar charts" ) image

Expected behavior

Either option should display the title in two different lines.

The option 2 works with plotly.js image

Actual behavior

Neither of the options work

Known workarounds

Any workaround

Related information

kMutagene commented 4 months ago

Hey @jaguarxii, can you add a minimal reproducible example please?

I cannot reproduce this error with the most barebone usage of the API, In fact, the second approach using <br> elements works as expected:

Chart2D.Chart.Point<double,double,string>(
    x: new double[] { 1, 2, 3, 4 },
    y: new double[] { 10, 11, 12, 13 }
)
.WithTitle("Some<br>line<br>breaks")
.Show();

image

You do not seem to use the default template, maybe you do something to the layout/config on the way that prevents the expected bahvior?

jaguarxii commented 4 months ago

Hi @kMutagene, thanks for reviewing this issue, here is the code for the sample chart

            string chartName = $"simple_{DateTime.Now.ToString("yyyyMMdd_HHmm")}";

            Font titleFont = Font.init(FontFamily.NewCustom("Nunito-SemiBold"), 16);
            //Font titleFont = Font.init(FontFamily.Arial, 16);
            Font defaultFont = Font.init(FontFamily.NewCustom("Nunito-SemiBold"), 10);

            Layout chartLayout = Layout.init<string>(
                Title.init(
                    Text: "Simple<br>bar charts"
                    , XRef: "paper"
                    , XAnchor: XAnchorPosition.Center
                    , X: 0.5
                    , Font: titleFont
                    )
                , Width: 800
                , Height: 450
                , PaperBGColor: Plotly.NET.Color.fromARGB(0, 0, 0, 0)
                , PlotBGColor: Plotly.NET.Color.fromARGB(80, 255, 255, 255)
                //, Font: Plotly.NET.Font.init(FontFamily.Open_Sans, 10)
                , Font: defaultFont
                , UniformText: UniformText.init(10, UniformTextMode.Show)
            //, Colorway: Plotly.NET.Color.fromColors(pdf_const_styles.md_pastel_colors_hex_plotly)
            );

            Random randNum = new Random();
            double[] sampleData = Enumerable
                .Repeat(0, 9)
                .Select(i => Convert.ToDouble(randNum.Next(100, 1000)) / 100)
                .ToArray();

            GenericChart.GenericChart barA = Chart.Column<double, string, string>(
                values: sampleData
                , Keys: sampleData.Select(x=>Array.IndexOf(sampleData, x).ToString()).ToArray()
                , MultiText: sampleData.Select(x=>"label A<br>" + x.ToString()).ToArray()
                , TextPosition: TextPosition.Outside
                //, TextAngle: 45
                , Name: "Barra A"
                , MarkerColor: Plotly.NET.Color.fromHex(pdf_const_styles.md_pastel_colors_hex[2])
            )
            //.WithLineStyle(Color: Plotly.NET.Color.fromHex(pdf_const_styles.pdf_table_border_color))

            .WithXAxis(LinearAxis.init<IConvertible, IConvertible, IConvertible, IConvertible, IConvertible, IConvertible>(
                Visible: true
                , LineColor: Plotly.NET.Color.fromHex(pdf_const_styles.pdf_chart_axis_color)
                , Title: Title.init("Categoria X")
                , TicksOn: CategoryTickAnchor.Boundaries
                , ShowGrid: true
                , GridColor: Plotly.NET.Color.fromHex(pdf_const_styles.pdf_chart_grid_color)
                , GridWidth: 0.1
                ))
            .WithYAxis(LinearAxis.init<IConvertible, IConvertible, IConvertible, IConvertible, IConvertible, IConvertible>(
                Visible: true
                , LineColor: Plotly.NET.Color.fromHex(pdf_const_styles.pdf_chart_axis_color)
                , Title: Plotly.NET.Title.init("Valor Y")
                , ShowGrid: true
                , GridColor: Plotly.NET.Color.fromHex(pdf_const_styles.pdf_chart_grid_color)
                , GridWidth: 0.1
                ))
            .WithMarker(Marker.init(
                //Color: Plotly.NET.Color.fromColors(pdf_const_styles.md_pastel_colors_hex_plotly)
                Outline: Line.init(
                    Color: Plotly.NET.Color.fromHex(pdf_const_styles.pdf_chart_border_color),
                    Width: 0.02
                    )
                ))
            .WithLayout(chartLayout);

            sampleData = Enumerable
                .Repeat(0, 9)
                .Select(i => Convert.ToDouble(randNum.Next(100, 1000)) / 100)
                .ToArray();

            GenericChart.GenericChart barB = Chart.Column<double, string, string>(
                values: sampleData
                , Keys: sampleData.Select(x=>Array.IndexOf(sampleData, x).ToString()).ToArray()
                , MultiText: sampleData.Select(x=>"label B<br>" + x.ToString()).ToArray()
                , TextPosition: TextPosition.Outside
                //, TextAngle: 45
                , Name: "Barra B"
                , MarkerColor: Plotly.NET.Color.fromHex(pdf_const_styles.md_pastel_colors_hex[4])
            )
            //.WithLineStyle(Color: Plotly.NET.Color.fromHex(pdf_const_styles.pdf_table_border_color))

            .WithXAxis(LinearAxis.init<IConvertible, IConvertible, IConvertible, IConvertible, IConvertible, IConvertible>(
                Visible: true
                , LineColor: Plotly.NET.Color.fromHex(pdf_const_styles.pdf_chart_axis_color)
                , Title: Title.init("Categoria X")
                , TicksOn: CategoryTickAnchor.Boundaries
                , ShowGrid: true
                , GridColor: Plotly.NET.Color.fromHex(pdf_const_styles.pdf_chart_grid_color)
                , GridWidth: 0.1
                ))
            .WithYAxis(LinearAxis.init<IConvertible, IConvertible, IConvertible, IConvertible, IConvertible, IConvertible>(
                Visible: true
                , LineColor: Plotly.NET.Color.fromHex(pdf_const_styles.pdf_chart_axis_color)
                , Title: Plotly.NET.Title.init("Valor Y")
                , ShowGrid: true
                , GridColor: Plotly.NET.Color.fromHex(pdf_const_styles.pdf_chart_grid_color)
                , GridWidth: 0.1
                ))
            .WithMarker(Marker.init(
                //Color: Plotly.NET.Color.fromColors(pdf_const_styles.md_pastel_colors_hex_plotly)
                Outline: Line.init(
                    Color: Plotly.NET.Color.fromHex(pdf_const_styles.pdf_chart_border_color),
                    Width: 0.02
                    )
                ))
            .WithLayout(chartLayout);

            GenericChart.GenericChart barCombined = Chart.Combine(new List<GenericChart.GenericChart> { barA, barB });
            string tmpSvgChart = barCombined.ToSVGString(Width:800, Height:450)
jaguarxii commented 4 months ago

I also created a Pie Chart with a similar problem

  GenericChart.GenericChart pieA = Chart.Pie<double, string, string>(
      values: sampleData
      , Labels: sampleData.Select(x => "label A - " + x.ToString()).ToArray()
      , MultiText: sampleData.Select(x => "label A - " + x.ToString()).ToArray()
      , Hole: 0.5
      , TextPosition: TextPosition.Outside
      , SectionOutlineColor: Plotly.NET.Color.fromHex(pdf_const_styles.pdf_chart_border_color)
      , SectionOutlineWidth: 0.1
      //, TextAngle: 45
      , Name: "Pie A"
      , ShowLegend: false
  );
  string svgPieChart = pieA.ToSVGString(Width: 800, Height: 800);

image

jaguarxii commented 3 months ago

OffTopic: I also tried to create a notebook to test this issue but could not do it

image

kMutagene commented 3 months ago

OffTopic: I also tried to create a notebook to test this issue but could not do it

image

You seem to use the .NET(C#) kernel instead of just .NET interactive kernel maybe that is relevant. I work with both libs daily in vscode jupyter notebooks so this looks to me like a config issue.

I'll have a look at your pie example soon

jaguarxii commented 3 months ago

Hi, I am closing this, because I found that QuestPDF is not embedding the SVG file correctly into the PDFs I am generating (the images with errors come from the resultting PDF).

Sorry to waste your time.

I also managed to create a notebook to speed my local tests, thanks for the tips.

jaguarxii commented 3 months ago

Hi, indeed it seems that relative units (em) are not quite supported by Skia SVG.

Using the Example Units SVG from the W3 SVG Recommendation, that should look like this:

image

After embedding into my test PDF document, looks like this:

image

Plotly.Net SVGs use relative units

image

kMutagene commented 3 months ago

Hi, indeed it seems that relative units (em) are not quite supported by Skia SVG.

Sorry to hear that, but thanks for the detailed info, might come in handy to reference for other issues with pdf creation in the future. No way we can change this though, as the svgs are rendered by plotly.js