Skip to content

Add Plotly.NET.ImageExport project #94

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,7 @@ temp/gh-pages
/.fsdocs
/tmp/watch
/output
/src/Plotly.NET.ImageExport/.local-chromium
/src/Plotly.NET.ImageExport/testrenders
/src/Plotly.NET.Interactive/.local-chromium/
/docs/.local-chromium/
9 changes: 9 additions & 0 deletions Plotly.NET.sln
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "content", "content", "{60FB82C0-F472-494E-BCF7-7B3C54212406}"
ProjectSection(SolutionItems) = preProject
docs\0_0_basics.fsx = docs\0_0_basics.fsx
docs\0_1_image-export.fsx = docs\0_1_image-export.fsx
docs\1_0_axis-styling.fsx = docs\1_0_axis-styling.fsx
docs\1_1_errorbars.fsx = docs\1_1_errorbars.fsx
docs\1_2_multiple-charts.fsx = docs\1_2_multiple-charts.fsx
Expand Down Expand Up @@ -102,6 +103,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "img", "img", "{CDB973F2-0F6
docs\img\logo.png = docs\img\logo.png
EndProjectSection
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Plotly.NET.ImageExport", "src\Plotly.NET.ImageExport\Plotly.NET.ImageExport.fsproj", "{6CFC629E-1A0C-4EF3-8495-BA00A356A381}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -127,6 +130,12 @@ Global
{0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Dotnet|Any CPU.Build.0 = Debug|Any CPU
{0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Release|Any CPU.Build.0 = Release|Any CPU
{6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU
{6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Dotnet|Any CPU.Build.0 = Debug|Any CPU
{6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
36 changes: 18 additions & 18 deletions build.fsx.lock
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,11 @@ NUGET
FSharp.Core (>= 4.7.2)
FParsec (1.1.1)
FSharp.Core (>= 4.3.4)
FSharp.Compiler.Service (39.0)
FSharp.Core (5.0.1)
Microsoft.Build.Framework (>= 16.6)
Microsoft.Build.Tasks.Core (>= 16.6)
Microsoft.Build.Utilities.Core (>= 16.6)
FSharp.Compiler.Service (40.0)
FSharp.Core (5.0.2)
Microsoft.Build.Framework (>= 16.9)
Microsoft.Build.Tasks.Core (>= 16.9)
Microsoft.Build.Utilities.Core (>= 16.9)
System.Buffers (>= 4.5.1)
System.Collections.Immutable (>= 5.0)
System.Diagnostics.Process (>= 4.3)
Expand All @@ -173,7 +173,7 @@ NUGET
FSharp.Control.Reactive (5.0.2)
FSharp.Core (>= 4.7.2)
System.Reactive (>= 5.0)
FSharp.Core (5.0.1)
FSharp.Core (5.0.2)
Microsoft.Build (16.10)
Microsoft.Build.Framework (16.10)
System.Security.Permissions (>= 4.7)
Expand Down Expand Up @@ -213,27 +213,27 @@ NUGET
System.Security.AccessControl (>= 5.0)
System.Security.Principal.Windows (>= 5.0)
Mono.Posix.NETStandard (1.0)
MSBuild.StructuredLogger (2.1.500)
MSBuild.StructuredLogger (2.1.507)
Microsoft.Build (>= 16.4)
Microsoft.Build.Framework (>= 16.4)
Microsoft.Build.Tasks.Core (>= 16.4)
Microsoft.Build.Utilities.Core (>= 16.4)
Newtonsoft.Json (13.0.1)
NuGet.Common (5.9.1)
NuGet.Frameworks (>= 5.9.1)
NuGet.Configuration (5.9.1)
NuGet.Common (>= 5.9.1)
NuGet.Common (5.10)
NuGet.Frameworks (>= 5.10)
NuGet.Configuration (5.10)
NuGet.Common (>= 5.10)
System.Security.Cryptography.ProtectedData (>= 4.4)
NuGet.Frameworks (5.9.1)
NuGet.Packaging (5.9.1)
NuGet.Frameworks (5.10)
NuGet.Packaging (5.10)
Newtonsoft.Json (>= 9.0.1)
NuGet.Configuration (>= 5.9.1)
NuGet.Versioning (>= 5.9.1)
NuGet.Configuration (>= 5.10)
NuGet.Versioning (>= 5.10)
System.Security.Cryptography.Cng (>= 5.0)
System.Security.Cryptography.Pkcs (>= 5.0)
NuGet.Protocol (5.9.1)
NuGet.Packaging (>= 5.9.1)
NuGet.Versioning (5.9.1)
NuGet.Protocol (5.10)
NuGet.Packaging (>= 5.10)
NuGet.Versioning (5.10)
Octokit (0.50)
runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3)
runtime.debian.9-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3)
Expand Down
185 changes: 185 additions & 0 deletions docs/0_1_image-export.fsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
(**
---
title: Static image export
category: General
categoryindex: 1
index: 2
---
*)


(*** hide ***)

(*** condition: prepare ***)
#r "nuget: Newtonsoft.JSON, 12.0.3"
#r "nuget: PuppeteerSharp"
#r "../bin/Plotly.NET/netstandard2.0/Plotly.NET.dll"
#r "../bin/Plotly.NET.ImageExport/netstandard2.0/Plotly.NET.ImageExport.dll"

(*** condition: ipynb ***)
#if IPYNB
#r "nuget: Plotly.NET, {{fsdocs-package-version}}"
#r "nuget: Plotly.NET.Interactive, {{fsdocs-package-version}}"
#r "nuget: Plotly.NET.ImageExport, {{fsdocs-package-version}}"
#endif // IPYNB


(**
[![Binder]({{root}}img/badge-binder.svg)](https://mybinder.org/v2/gh/plotly/Plotly.NET/gh-pages?filepath={{fsdocs-source-basename}}.ipynb) 
[![Script]({{root}}img/badge-script.svg)]({{root}}{{fsdocs-source-basename}}.fsx) 
[![Notebook]({{root}}img/badge-notebook.svg)]({{root}}{{fsdocs-source-basename}}.ipynb)

# Static image export

### Table of contents

- [Saving static images](#Saving-static-images)
- [Generating URIs for static chart images](#Generating-URIs-for-static-chart-images)
- [Including static images in dotnet interactive notebooks](#Including-static-images-in-dotnet-interactive-notebooks)

As Plotly.NET generates static html pages that contain charts rendered by plotly.js, static image export needs a lot more overhead under the hood
than you might expect. The underlying renderer needs to execute javascript, leading to the usage of headless browsers.

The package `Plotly.NET.ImageExport` contains extensions for Plotly.NET to render static images. It is designed with extensibility in mind and
it is very easy to add a new rendering engine. The current engines are provided:

| Rendering engine | Type | Prerequisites |
|-|-|-|
| [PuppeteerSharp](https://github.com/hardkoded/puppeteer-sharp) | headless browser | [read more here](https://github.com/hardkoded/puppeteer-sharp#prerequisites) |

## Saving static images

By referencing the `Plotly.NET.ImageExport` package, you get access to:

- jpg via `Chart.SaveJPG`
- png via `Chart.SavePNG`
- svg via `Chart.SaveSVG`

(and Extensions for C# style fluent interfaces by opening the `GenericChartExtensions` namespace)

The parameters for all three functions are exactly the same.
*)

open Plotly.NET
open Plotly.NET.ImageExport

let exampleChart =
Chart.Histogram2dContour(
[1.;2.;2.;4.;5.],
[1.;2.;2.;4.;5.]
)

(***do-not-eval***)
exampleChart
|> Chart.saveJPG(
"/your/path/without/extension/here",
Width=300,
Height=300
)

(*** condition: ipynb ***)
#if IPYNB
let imgString = $"""<img
src= "{exampleChart|> Chart.toBase64JPGString(Width=300,Height=300)}"
/>"""
DisplayExtensions.DisplayAs(imgString,"text/html")
#endif // IPYNB

(***hide***)
$"""<img
src= "{exampleChart|> Chart.toBase64JPGString(Width=300,Height=300)}"
/>"""
(***include-it-raw***)

(**
## Generating URIs for static chart images

By referencing the `Plotly.NET.ImageExport` package, you get access to:

- jpg via `Chart.toBase64JPGString`
- png via `Chart.toBase64PNGString`
- svg via `Chart.toSVGString`

(and Extensions for C# style fluent interfaces by opening the `GenericChartExtensions` namespace)

*)

let base64JPG =
exampleChart
|> Chart.toBase64JPGString(
Width=300,
Height=300
)

(**
It is very easy to construct a html tag that includes this image via a base64 uri. For SVGs,
not even that is necessary and just the SVG string can be used.
*)

(***do-not-eval***)
$"""<img
src= "{base64JPG}"
/>"""

(*** condition: ipynb ***)
#if IPYNB
let imgString = $"""<img
src= "{base64JPG}"
/>"""
DisplayExtensions.DisplayAs(imgString,"text/html")
#endif // IPYNB

(***hide***)
$"""<img
src= "{base64JPG}"
/>"""

(***include-it-raw***)

(**
SVGs can be included without the image tag:
*)

let svgString =
exampleChart
|> Chart.toSVGString(
Width=300,
Height=300
)

svgString.Substring(0,300)
|> printfn "%s"

(***include-output***)

(**
In fact, the images shown on this site are included just the same way.

## Including static images in dotnet interactive notebooks

To include the images in dotnet interactive, convert them to html tags as above and include them via
dotnet interactive's `DisplayAs` function. The content type for PNG/JPB is "text/html", and "image/svg+xml" for SVG.
*)

let base64PNG =
exampleChart
|> Chart.toBase64PNGString(
Width=300,
Height=300
)

let svgString2 =
exampleChart
|> Chart.toSVGString(
Width=300,
Height=300
)

// DisplayExtensions.DisplayAs(base64PNG,"text/html")
// DisplayExtensions.DisplayAs(svgString,"image/svg+xml")

(*** condition: ipynb ***)
#if IPYNB
DisplayExtensions.DisplayAs(base64PNG,"text/html")
DisplayExtensions.DisplayAs(svgString,"image/svg+xml")
#endif // IPYNB
Loading