diff --git a/deps.edn b/deps.edn
index 76a7bd50..de6beaaf 100644
--- a/deps.edn
+++ b/deps.edn
@@ -15,6 +15,7 @@
org.scicloj/tempfiles {:mvn/version "1-beta1"}
org.scicloj/kind-portal {:mvn/version "1-beta3"}
org.clojure/tools.reader {:mvn/version "1.5.2"}
+ com.microsoft.playwright/playwright {:mvn/version "1.47.0"}
com.nextjournal/beholder {:mvn/version "1.0.2"}
babashka/fs {:mvn/version "0.5.25"}
org.scicloj/kindly-render {:mvn/version "0.1.5-alpha"}
diff --git a/notebooks/pdf_test.clj b/notebooks/pdf_test.clj
new file mode 100644
index 00000000..cc7188ff
--- /dev/null
+++ b/notebooks/pdf_test.clj
@@ -0,0 +1,49 @@
+(ns pdf-test
+ {:clay {:format [:quarto :pdf]
+ :quarto {
+ ;; TODO: for some reason the clay.edn causes the format to be unknown, setting it explicitly for now
+ :format {:pdf {}}
+ ;;:documentclass "article"
+ ;;:classoption ["twocolumn"]
+ ;;:geometry ["top=30mm" "left=20mm" "heightrounded"]
+
+ ;; TODO: again this is just necessary because the project clay.edn puts some html in the include-in-header
+ :include-in-header {:text "\\AddToHook{env/Highlighting/begin}{\\small}"}}}}
+ (:require [tablecloth.api :as tc]
+ [scicloj.tableplot.v1.plotly :as tp]))
+
+;; this is an example pdf
+
+;; ---
+
+;; if we want a new page, use latex
+
+;; \newpage
+
+(def scatter-ds
+ (tc/dataset {:x [1 2 3 4 5]
+ :y [10 20 15 25 18]}))
+
+(-> scatter-ds
+ (tp/base {:=title "Sample Scatter Plot"})
+ (tp/layer-point {:=x :x
+ :=y :y}))
+
+(comment
+ (require '[scicloj.clay.v2.api :as clay])
+
+ ;; use playwright
+ (clay/make! {:source-path "notebooks/pdf_test.clj"
+ :kindly/options {:playwright true
+ :plotly-ext "svg"}})
+ (clay/make! {:source-path "notebooks/pdf_test.clj"
+ :kindly/options {:playwright true
+ :plotly-ext "png"}})
+
+ ;; use python
+ (clay/make! {:source-path "notebooks/pdf_test.clj"
+ :kindly/options {:plotly-ext "svg"}})
+ (clay/make! {:source-path "notebooks/pdf_test.clj"
+ :kindly/options {:plotly-ext "png"}})
+
+ )
diff --git a/src/scicloj/clay/v2/item.clj b/src/scicloj/clay/v2/item.clj
index b4638ad5..3cf1afcc 100644
--- a/src/scicloj/clay/v2/item.clj
+++ b/src/scicloj/clay/v2/item.clj
@@ -4,7 +4,8 @@
[scicloj.kind-portal.v1.prepare :as kind-portal]
[scicloj.kindly-render.shared.jso :as jso]
[scicloj.clay.v2.files :as files]
- [scicloj.clay.v2.plotly-export :as plotly-export]
+ [scicloj.clay.v2.static.plotly-python :as plotly-python]
+ [scicloj.clay.v2.static.plotly-playwright :as plotly-playwright]
[scicloj.clay.v2.util.image :as util.image]
[scicloj.clay.v2.util.meta :as meta]
[clj-commons.format.exceptions :as fe]))
@@ -324,11 +325,14 @@
config {}}} :value}]
(if (static? context)
(let [[plot-path relative-path]
- (files/next-file! context "plotly-chart" value ".png")
- exit (plotly-export/export-plot! plot-path data layout)]
- (if (zero? exit)
- (println "Clay plotly-export:" [:wrote plot-path])
- (println "Clay plotly-export failed."))
+ (files/next-file! context "plotly-chart" value
+ (str "." (or (:plotly-ext options) "svg")))
+ success (if (:playwright options)
+ (plotly-playwright/export-plot! plot-path data layout)
+ (plotly-python/export-plot! plot-path data layout))]
+ (if success
+ (println "Clay plotly export:" [:wrote plot-path])
+ (println "Clay plotly export failed."))
{:md (str "")})
{:hiccup
[:div
diff --git a/src/scicloj/clay/v2/page.clj b/src/scicloj/clay/v2/page.clj
index b30e259a..c362d715 100644
--- a/src/scicloj/clay/v2/page.clj
+++ b/src/scicloj/clay/v2/page.clj
@@ -274,11 +274,13 @@
:keys [title favicon quarto format]}]
(let [quarto-target (or (second format) :html)]
(cond-> quarto
- ;; Users may provide non-quarto specific configuration (see also html),
- ;; if so this will be added to the quarto front-matter to make them behave the same way
- title (assoc-in [:format quarto-target :title] title)
- favicon (update-in [:format quarto-target :include-in-header :text]
- str ""))))
+ ;; Users may provide non-quarto specific configuration (see also html),
+ ;; if so this will be added to the quarto front-matter to make them behave the same way
+ title (assoc-in [:format quarto-target :title] title)
+ ;; favicon only applies to html
+ (and favicon (= quarto-target :html))
+ (update-in [:format :html :include-in-header :text]
+ str ""))))
(defn md [{:as spec
:keys [items]}]
diff --git a/src/scicloj/clay/v2/static/plotly_playwright.clj b/src/scicloj/clay/v2/static/plotly_playwright.clj
new file mode 100644
index 00000000..4d6ba67d
--- /dev/null
+++ b/src/scicloj/clay/v2/static/plotly_playwright.clj
@@ -0,0 +1,57 @@
+(ns scicloj.clay.v2.static.plotly-playwright
+ (:require [scicloj.kindly-render.shared.jso :as jso]
+ [clojure.java.io :as io]
+ [clojure.string :as str]
+ [babashka.fs :as fs]
+ [hiccup.core :as hiccup])
+ (:import (com.microsoft.playwright Playwright Locator$WaitForOptions)
+ (java.net URLDecoder)
+ (java.util Base64)))
+
+(set! *warn-on-reflection* true)
+
+(defn plotly-html [data layout]
+ (hiccup/html
+ [:html
+ [:head
+ [:meta {:charset "UTF-8"}]
+ [:meta {:name "viewport" :content "width=device-width, initial-scale=1"}]
+ ;; TODO: This is duplicated from `scicloj.clay.v2.page`,
+ ;; it should be a shared def but under the current structure that causes a cyclic dependency.
+ [:script {:src "https://cdnjs.cloudflare.com/ajax/libs/plotly.js/2.20.0/plotly.min.js"}]]
+ [:body
+ [:div#myDiv]]
+ [:script
+ (str "var data = " (jso/write-json-str data) ";"
+ "var layout = " (jso/write-json-str layout) ";"
+ "Plotly.newPlot('myDiv', data, layout, {staticPlot: true});")]]))
+
+(defn export-plot! [filename data layout]
+ (let [ext (fs/extension filename)]
+ (with-open [playwright (Playwright/create)
+ browser (.launch (.chromium playwright))]
+ (let [page (.newPage browser)]
+ (.setContent page (plotly-html data layout))
+ (let [locator (.locator page "#myDiv")
+ wait-opts (doto (Locator$WaitForOptions.)
+ (.setTimeout 10000))]
+ (.waitFor locator wait-opts)
+ ;; Plotly.toImage on the rendered div returns encoded image
+ (let [img-data-uri (.evaluate page (str "Plotly.toImage(document.getElementById('myDiv'), {format: '" ext "'})"))
+ [_header payload] (str/split img-data-uri #"," 2)]
+ (io/make-parents filename)
+ (if (= ext "svg")
+ (->> (URLDecoder/decode ^String payload "UTF-8")
+ (spit filename))
+ (with-open [out (io/output-stream filename)]
+ (.write out (.decode (Base64/getDecoder) ^String payload))))
+ :success))))))
+
+(comment
+ (export-plot! "plotly_chart.svg"
+ [{:y [1 2 3 4 5] :type "bar"}]
+ {:title "Playwright Plotly"})
+ (export-plot! "plotly_chart.png"
+ [{:y [1 2 3 4 5] :type "bar"}]
+ {:title "Playwright Plotly"})
+ :-)
diff --git a/src/scicloj/clay/v2/plotly_export.clj b/src/scicloj/clay/v2/static/plotly_python.clj
similarity index 95%
rename from src/scicloj/clay/v2/plotly_export.clj
rename to src/scicloj/clay/v2/static/plotly_python.clj
index f73a39ce..eaa88a36 100644
--- a/src/scicloj/clay/v2/plotly_export.clj
+++ b/src/scicloj/clay/v2/static/plotly_python.clj
@@ -1,4 +1,4 @@
-(ns scicloj.clay.v2.plotly-export
+(ns scicloj.clay.v2.static.plotly-python
(:require [scicloj.kindly-render.shared.jso :as jso]
[clojure.java.shell :as shell]))
@@ -33,4 +33,4 @@ fig.write_image(filename)
(when (seq err)
(binding [*out* *err*]
(println err)))
- exit))
+ (zero? exit)))