Skip to content

Commit e845ba2

Browse files
authored
Merge pull request #94 from plotly/image-export
Add Plotly.NET.ImageExport project
2 parents f1d8dc6 + 7d1bc36 commit e845ba2

22 files changed

+3006
-133
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,7 @@ temp/gh-pages
187187
/.fsdocs
188188
/tmp/watch
189189
/output
190+
/src/Plotly.NET.ImageExport/.local-chromium
191+
/src/Plotly.NET.ImageExport/testrenders
192+
/src/Plotly.NET.Interactive/.local-chromium/
193+
/docs/.local-chromium/

Plotly.NET.sln

+9
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ EndProject
5151
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "content", "content", "{60FB82C0-F472-494E-BCF7-7B3C54212406}"
5252
ProjectSection(SolutionItems) = preProject
5353
docs\0_0_basics.fsx = docs\0_0_basics.fsx
54+
docs\0_1_image-export.fsx = docs\0_1_image-export.fsx
5455
docs\1_0_axis-styling.fsx = docs\1_0_axis-styling.fsx
5556
docs\1_1_errorbars.fsx = docs\1_1_errorbars.fsx
5657
docs\1_2_multiple-charts.fsx = docs\1_2_multiple-charts.fsx
@@ -102,6 +103,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "img", "img", "{CDB973F2-0F6
102103
docs\img\logo.png = docs\img\logo.png
103104
EndProjectSection
104105
EndProject
106+
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Plotly.NET.ImageExport", "src\Plotly.NET.ImageExport\Plotly.NET.ImageExport.fsproj", "{6CFC629E-1A0C-4EF3-8495-BA00A356A381}"
107+
EndProject
105108
Global
106109
GlobalSection(SolutionConfigurationPlatforms) = preSolution
107110
Debug|Any CPU = Debug|Any CPU
@@ -127,6 +130,12 @@ Global
127130
{0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Dotnet|Any CPU.Build.0 = Debug|Any CPU
128131
{0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
129132
{0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Release|Any CPU.Build.0 = Release|Any CPU
133+
{6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
134+
{6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Debug|Any CPU.Build.0 = Debug|Any CPU
135+
{6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU
136+
{6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Dotnet|Any CPU.Build.0 = Debug|Any CPU
137+
{6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Release|Any CPU.ActiveCfg = Release|Any CPU
138+
{6CFC629E-1A0C-4EF3-8495-BA00A356A381}.Release|Any CPU.Build.0 = Release|Any CPU
130139
EndGlobalSection
131140
GlobalSection(SolutionProperties) = preSolution
132141
HideSolutionNode = FALSE

build.fsx.lock

+18-18
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,11 @@ NUGET
144144
FSharp.Core (>= 4.7.2)
145145
FParsec (1.1.1)
146146
FSharp.Core (>= 4.3.4)
147-
FSharp.Compiler.Service (39.0)
148-
FSharp.Core (5.0.1)
149-
Microsoft.Build.Framework (>= 16.6)
150-
Microsoft.Build.Tasks.Core (>= 16.6)
151-
Microsoft.Build.Utilities.Core (>= 16.6)
147+
FSharp.Compiler.Service (40.0)
148+
FSharp.Core (5.0.2)
149+
Microsoft.Build.Framework (>= 16.9)
150+
Microsoft.Build.Tasks.Core (>= 16.9)
151+
Microsoft.Build.Utilities.Core (>= 16.9)
152152
System.Buffers (>= 4.5.1)
153153
System.Collections.Immutable (>= 5.0)
154154
System.Diagnostics.Process (>= 4.3)
@@ -173,7 +173,7 @@ NUGET
173173
FSharp.Control.Reactive (5.0.2)
174174
FSharp.Core (>= 4.7.2)
175175
System.Reactive (>= 5.0)
176-
FSharp.Core (5.0.1)
176+
FSharp.Core (5.0.2)
177177
Microsoft.Build (16.10)
178178
Microsoft.Build.Framework (16.10)
179179
System.Security.Permissions (>= 4.7)
@@ -213,27 +213,27 @@ NUGET
213213
System.Security.AccessControl (>= 5.0)
214214
System.Security.Principal.Windows (>= 5.0)
215215
Mono.Posix.NETStandard (1.0)
216-
MSBuild.StructuredLogger (2.1.500)
216+
MSBuild.StructuredLogger (2.1.507)
217217
Microsoft.Build (>= 16.4)
218218
Microsoft.Build.Framework (>= 16.4)
219219
Microsoft.Build.Tasks.Core (>= 16.4)
220220
Microsoft.Build.Utilities.Core (>= 16.4)
221221
Newtonsoft.Json (13.0.1)
222-
NuGet.Common (5.9.1)
223-
NuGet.Frameworks (>= 5.9.1)
224-
NuGet.Configuration (5.9.1)
225-
NuGet.Common (>= 5.9.1)
222+
NuGet.Common (5.10)
223+
NuGet.Frameworks (>= 5.10)
224+
NuGet.Configuration (5.10)
225+
NuGet.Common (>= 5.10)
226226
System.Security.Cryptography.ProtectedData (>= 4.4)
227-
NuGet.Frameworks (5.9.1)
228-
NuGet.Packaging (5.9.1)
227+
NuGet.Frameworks (5.10)
228+
NuGet.Packaging (5.10)
229229
Newtonsoft.Json (>= 9.0.1)
230-
NuGet.Configuration (>= 5.9.1)
231-
NuGet.Versioning (>= 5.9.1)
230+
NuGet.Configuration (>= 5.10)
231+
NuGet.Versioning (>= 5.10)
232232
System.Security.Cryptography.Cng (>= 5.0)
233233
System.Security.Cryptography.Pkcs (>= 5.0)
234-
NuGet.Protocol (5.9.1)
235-
NuGet.Packaging (>= 5.9.1)
236-
NuGet.Versioning (5.9.1)
234+
NuGet.Protocol (5.10)
235+
NuGet.Packaging (>= 5.10)
236+
NuGet.Versioning (5.10)
237237
Octokit (0.50)
238238
runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3)
239239
runtime.debian.9-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3)

docs/0_1_image-export.fsx

+185
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
(**
2+
---
3+
title: Static image export
4+
category: General
5+
categoryindex: 1
6+
index: 2
7+
---
8+
*)
9+
10+
11+
(*** hide ***)
12+
13+
(*** condition: prepare ***)
14+
#r "nuget: Newtonsoft.JSON, 12.0.3"
15+
#r "nuget: PuppeteerSharp"
16+
#r "../bin/Plotly.NET/netstandard2.0/Plotly.NET.dll"
17+
#r "../bin/Plotly.NET.ImageExport/netstandard2.0/Plotly.NET.ImageExport.dll"
18+
19+
(*** condition: ipynb ***)
20+
#if IPYNB
21+
#r "nuget: Plotly.NET, {{fsdocs-package-version}}"
22+
#r "nuget: Plotly.NET.Interactive, {{fsdocs-package-version}}"
23+
#r "nuget: Plotly.NET.ImageExport, {{fsdocs-package-version}}"
24+
#endif // IPYNB
25+
26+
27+
(**
28+
[![Binder]({{root}}img/badge-binder.svg)](https://mybinder.org/v2/gh/plotly/Plotly.NET/gh-pages?filepath={{fsdocs-source-basename}}.ipynb) 
29+
[![Script]({{root}}img/badge-script.svg)]({{root}}{{fsdocs-source-basename}}.fsx) 
30+
[![Notebook]({{root}}img/badge-notebook.svg)]({{root}}{{fsdocs-source-basename}}.ipynb)
31+
32+
# Static image export
33+
34+
### Table of contents
35+
36+
- [Saving static images](#Saving-static-images)
37+
- [Generating URIs for static chart images](#Generating-URIs-for-static-chart-images)
38+
- [Including static images in dotnet interactive notebooks](#Including-static-images-in-dotnet-interactive-notebooks)
39+
40+
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
41+
than you might expect. The underlying renderer needs to execute javascript, leading to the usage of headless browsers.
42+
43+
The package `Plotly.NET.ImageExport` contains extensions for Plotly.NET to render static images. It is designed with extensibility in mind and
44+
it is very easy to add a new rendering engine. The current engines are provided:
45+
46+
| Rendering engine | Type | Prerequisites |
47+
|-|-|-|
48+
| [PuppeteerSharp](https://github.com/hardkoded/puppeteer-sharp) | headless browser | [read more here](https://github.com/hardkoded/puppeteer-sharp#prerequisites) |
49+
50+
## Saving static images
51+
52+
By referencing the `Plotly.NET.ImageExport` package, you get access to:
53+
54+
- jpg via `Chart.SaveJPG`
55+
- png via `Chart.SavePNG`
56+
- svg via `Chart.SaveSVG`
57+
58+
(and Extensions for C# style fluent interfaces by opening the `GenericChartExtensions` namespace)
59+
60+
The parameters for all three functions are exactly the same.
61+
*)
62+
63+
open Plotly.NET
64+
open Plotly.NET.ImageExport
65+
66+
let exampleChart =
67+
Chart.Histogram2dContour(
68+
[1.;2.;2.;4.;5.],
69+
[1.;2.;2.;4.;5.]
70+
)
71+
72+
(***do-not-eval***)
73+
exampleChart
74+
|> Chart.saveJPG(
75+
"/your/path/without/extension/here",
76+
Width=300,
77+
Height=300
78+
)
79+
80+
(*** condition: ipynb ***)
81+
#if IPYNB
82+
let imgString = $"""<img
83+
src= "{exampleChart|> Chart.toBase64JPGString(Width=300,Height=300)}"
84+
/>"""
85+
DisplayExtensions.DisplayAs(imgString,"text/html")
86+
#endif // IPYNB
87+
88+
(***hide***)
89+
$"""<img
90+
src= "{exampleChart|> Chart.toBase64JPGString(Width=300,Height=300)}"
91+
/>"""
92+
(***include-it-raw***)
93+
94+
(**
95+
## Generating URIs for static chart images
96+
97+
By referencing the `Plotly.NET.ImageExport` package, you get access to:
98+
99+
- jpg via `Chart.toBase64JPGString`
100+
- png via `Chart.toBase64PNGString`
101+
- svg via `Chart.toSVGString`
102+
103+
(and Extensions for C# style fluent interfaces by opening the `GenericChartExtensions` namespace)
104+
105+
*)
106+
107+
let base64JPG =
108+
exampleChart
109+
|> Chart.toBase64JPGString(
110+
Width=300,
111+
Height=300
112+
)
113+
114+
(**
115+
It is very easy to construct a html tag that includes this image via a base64 uri. For SVGs,
116+
not even that is necessary and just the SVG string can be used.
117+
*)
118+
119+
(***do-not-eval***)
120+
$"""<img
121+
src= "{base64JPG}"
122+
/>"""
123+
124+
(*** condition: ipynb ***)
125+
#if IPYNB
126+
let imgString = $"""<img
127+
src= "{base64JPG}"
128+
/>"""
129+
DisplayExtensions.DisplayAs(imgString,"text/html")
130+
#endif // IPYNB
131+
132+
(***hide***)
133+
$"""<img
134+
src= "{base64JPG}"
135+
/>"""
136+
137+
(***include-it-raw***)
138+
139+
(**
140+
SVGs can be included without the image tag:
141+
*)
142+
143+
let svgString =
144+
exampleChart
145+
|> Chart.toSVGString(
146+
Width=300,
147+
Height=300
148+
)
149+
150+
svgString.Substring(0,300)
151+
|> printfn "%s"
152+
153+
(***include-output***)
154+
155+
(**
156+
In fact, the images shown on this site are included just the same way.
157+
158+
## Including static images in dotnet interactive notebooks
159+
160+
To include the images in dotnet interactive, convert them to html tags as above and include them via
161+
dotnet interactive's `DisplayAs` function. The content type for PNG/JPB is "text/html", and "image/svg+xml" for SVG.
162+
*)
163+
164+
let base64PNG =
165+
exampleChart
166+
|> Chart.toBase64PNGString(
167+
Width=300,
168+
Height=300
169+
)
170+
171+
let svgString2 =
172+
exampleChart
173+
|> Chart.toSVGString(
174+
Width=300,
175+
Height=300
176+
)
177+
178+
// DisplayExtensions.DisplayAs(base64PNG,"text/html")
179+
// DisplayExtensions.DisplayAs(svgString,"image/svg+xml")
180+
181+
(*** condition: ipynb ***)
182+
#if IPYNB
183+
DisplayExtensions.DisplayAs(base64PNG,"text/html")
184+
DisplayExtensions.DisplayAs(svgString,"image/svg+xml")
185+
#endif // IPYNB

0 commit comments

Comments
 (0)