Skip to content
Open
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
1 change: 1 addition & 0 deletions deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
Expand Down
49 changes: 49 additions & 0 deletions notebooks/pdf_test.clj
Original file line number Diff line number Diff line change
@@ -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"}})

)
16 changes: 10 additions & 6 deletions src/scicloj/clay/v2/item.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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]))
Expand Down Expand Up @@ -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 "![" (:caption options) "](" relative-path ")")})
{:hiccup
[:div
Expand Down
12 changes: 7 additions & 5 deletions src/scicloj/clay/v2/page.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<link rel = \"icon\" href = \"" favicon "\" />"))))
;; 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 "<link rel = \"icon\" href = \"" favicon "\" />"))))

(defn md [{:as spec
:keys [items]}]
Expand Down
57 changes: 57 additions & 0 deletions src/scicloj/clay/v2/static/plotly_playwright.clj
Original file line number Diff line number Diff line change
@@ -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"})
:-)
Original file line number Diff line number Diff line change
@@ -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]))

Expand Down Expand Up @@ -33,4 +33,4 @@ fig.write_image(filename)
(when (seq err)
(binding [*out* *err*]
(println err)))
exit))
(zero? exit)))