Compare commits
10 Commits
efd4c4cdbc
...
71163b9bb5
| Author | SHA1 | Date | |
|---|---|---|---|
| 71163b9bb5 | |||
| 7d12f7d90e | |||
| d5ccadb326 | |||
| ee86f0d643 | |||
| e67f62e5b0 | |||
| b765f71b55 | |||
| 47b201fd03 | |||
| 8ec78c040a | |||
| d15f9988c2 | |||
| b3ca176ee2 |
6
doerg/doerg-tex/deps.edn
Normal file
6
doerg/doerg-tex/deps.edn
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{:deps {babashka/fs {:mvn/version "0.5.24"}
|
||||||
|
cheshire/cheshire {:mvn/version "6.1.0"}
|
||||||
|
com.rpl/specter {:mvn/version "1.1.6"}
|
||||||
|
mvxcvi/clj-cbor {:mvn/version "1.1.1"}
|
||||||
|
babashka/process {:mvn/version "0.6.25"}}
|
||||||
|
:paths ["." "classes"]}
|
||||||
5
doerg/doerg-tex/deserialise.clj
Normal file
5
doerg/doerg-tex/deserialise.clj
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
(ns deserialise
|
||||||
|
(:require [clj-cbor.core :as cbor]
|
||||||
|
[clojure.string :as str]))
|
||||||
|
|
||||||
|
(prn (cbor/decode cbor/default-codec System/in :eof))
|
||||||
@@ -8,8 +8,11 @@
|
|||||||
(defn c [x]
|
(defn c [x]
|
||||||
(->> x cbor/encode (map #(format "%02x" %)) (str/join " ")))
|
(->> x cbor/encode (map #(format "%02x" %)) (str/join " ")))
|
||||||
|
|
||||||
|
#_
|
||||||
(w "\\naturalto")
|
(w "\\naturalto")
|
||||||
|
|
||||||
|
(w "\\ifxetex blah \\fi")
|
||||||
|
|
||||||
#_#_#_
|
#_#_#_
|
||||||
(w "c = \\sqrt{a^2 + y^2}")
|
(w "c = \\sqrt{a^2 + y^2}")
|
||||||
(w "c = \\sqrt{a^ + y^2")
|
(w "c = \\sqrt{a^ + y^2")
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
\usepackage{amsmath}
|
\usepackage{amsmath}
|
||||||
\usepackage[active,tightpage,auctex,dvips]{preview}
|
\usepackage[active,tightpage,auctex,dvips]{preview}
|
||||||
\usepackage{fontspec}
|
\usepackage{fontspec}
|
||||||
%% \usepackage{syd-plex}
|
\usepackage{ifxetex}
|
||||||
|
\usepackage{syd-plex}
|
||||||
|
|
||||||
\begin{document}
|
\begin{document}
|
||||||
\setlength\abovedisplayskip{0pt}
|
\setlength\abovedisplayskip{0pt}
|
||||||
|
|||||||
@@ -542,3 +542,7 @@ figure.fullwidth figcaption {
|
|||||||
; max-width: 55%
|
; max-width: 55%
|
||||||
; font-size: 1.5rem
|
; font-size: 1.5rem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.latex-fragment
|
||||||
|
{ fill: currentColor
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
[hiccup2.core :as hiccup]
|
[hiccup2.core :as hiccup]
|
||||||
[clojure.pprint]
|
[clojure.pprint]
|
||||||
[net.deertopia.doerg.tex :as tex]
|
[net.deertopia.doerg.tex :as tex]
|
||||||
[clojure.zip :as z]))
|
[clojure.zip :as z]
|
||||||
|
[babashka.fs :as fs]))
|
||||||
|
|
||||||
;;; Top-level API
|
;;; Top-level API
|
||||||
|
|
||||||
@@ -40,33 +41,33 @@
|
|||||||
(def ^:dynamic ^:private *document-info*)
|
(def ^:dynamic ^:private *document-info*)
|
||||||
|
|
||||||
(declare ^:private gather-footnotes render-renderer-error
|
(declare ^:private gather-footnotes render-renderer-error
|
||||||
view-children-as-seq)
|
view-children-as-seq render-tex-snippets)
|
||||||
|
|
||||||
(defn org-element-recursive
|
(defn org-element-recursive
|
||||||
"Recursively render an Org-mode element to Hiccup."
|
"Recursively render an Org-mode element to Hiccup."
|
||||||
[e]
|
[e]
|
||||||
(tex/binding-tex-worker
|
|
||||||
(->> e
|
(->> e
|
||||||
;; gather-footnotes
|
|
||||||
(sp/transform
|
(sp/transform
|
||||||
[element/postorder-walker view-children-as-seq]
|
[element/postorder-walker view-children-as-seq]
|
||||||
(fn [node]
|
(fn [node]
|
||||||
(try (org-element node)
|
(try (org-element node)
|
||||||
(catch Throwable e
|
(catch Throwable e
|
||||||
(lr/error e "Error in renderer" {:node node})
|
(lr/error e "Error in renderer" {:node node})
|
||||||
(render-renderer-error e))))))))
|
(render-renderer-error e)))))))
|
||||||
|
|
||||||
(defn org-document
|
(defn org-document
|
||||||
"Recursively render an Org-mode document to Hiccup."
|
"Recursively render an Org-mode document to Hiccup."
|
||||||
[doc]
|
[doc]
|
||||||
(let [rendered (org-element-recursive (gather-footnotes doc))]
|
(tex/binding-temml-worker
|
||||||
|
(let [rendered (-> doc gather-footnotes render-tex-snippets
|
||||||
|
org-element-recursive)]
|
||||||
[:html
|
[:html
|
||||||
[:head
|
[:head
|
||||||
[:title "org document"]
|
[:title "org document"]
|
||||||
doerg-html/head]
|
doerg-html/head]
|
||||||
[:body
|
[:body
|
||||||
[:article
|
[:article
|
||||||
rendered]]]))
|
rendered]]])))
|
||||||
|
|
||||||
|
|
||||||
;;; Further dispatching on `org-element`
|
;;; Further dispatching on `org-element`
|
||||||
@@ -126,6 +127,79 @@
|
|||||||
element/footnotes-section?]
|
element/footnotes-section?]
|
||||||
sp/NONE))))
|
sp/NONE))))
|
||||||
|
|
||||||
|
(defn- collect-latex-headers [doc]
|
||||||
|
(->> doc
|
||||||
|
(sp/select
|
||||||
|
[element/postorder-walker
|
||||||
|
#(element/of-keyword-type? % "LATEX_HEADER")
|
||||||
|
(sp/view :value)])))
|
||||||
|
|
||||||
|
(defn- read-and-patch-generated-svg [{:keys [file height depth]}]
|
||||||
|
;; dvisvgm writes standalone SVG files, to which we need to make a
|
||||||
|
;; few changes to use them inline within our HTML.
|
||||||
|
;; • XML header: Bad syntax when embedded in an HTML doc. Remove
|
||||||
|
;; it.
|
||||||
|
;; • Width and height: We override these with our own values
|
||||||
|
;; computed by `net.deertopia.doerg.tex` to ensure correct
|
||||||
|
;; positioning relative to the surrounding text. More
|
||||||
|
;; accurately, we remove the height and width attributes from
|
||||||
|
;; the SVG tag, and set the new values for height and
|
||||||
|
;; vertical-align in the style attribute
|
||||||
|
;; • Viewbox: Must be removed entirely for correct positioning.
|
||||||
|
(-> (slurp file)
|
||||||
|
(str/replace-first "<?xml version='1.0' encoding='UTF-8'?>" "")
|
||||||
|
(str/replace-first #" height=['\"][^\"']+[\"']" "")
|
||||||
|
(str/replace-first #" width=['\"][^\"']+[\"']" "")
|
||||||
|
(str/replace-first
|
||||||
|
#"viewBox=['\"][^\"']+[\"']"
|
||||||
|
(fn [s]
|
||||||
|
(format "%s style=\"%s\""
|
||||||
|
s
|
||||||
|
(format "height:%.4fem;vertical-align:%.4fem;display:inline-block"
|
||||||
|
height (- depth)))))))
|
||||||
|
|
||||||
|
(defn- render-tex-snippets [doc]
|
||||||
|
(let [promises (atom [])
|
||||||
|
r (->> doc (sp/transform
|
||||||
|
[element/postorder-walker
|
||||||
|
#(element/of-type?
|
||||||
|
% "latex-fragment" "latex-environment")]
|
||||||
|
(fn [node]
|
||||||
|
(let [p (promise)]
|
||||||
|
(swap! promises #(conj % {:promise p :node node}))
|
||||||
|
(assoc node ::rendered p)))))
|
||||||
|
f (fn []
|
||||||
|
(fs/with-temp-dir [svg-dir {:prefix "doerg-svg"}]
|
||||||
|
(let [rendered-snippets
|
||||||
|
(delay (->> @promises
|
||||||
|
(map #(-> % :node :value))
|
||||||
|
(apply tex/render-xelatex svg-dir)))]
|
||||||
|
(def the-rendered-snippets rendered-snippets)
|
||||||
|
(doseq [{:keys [promise node]} @promises]
|
||||||
|
(try (let [{:keys [value contents]} node
|
||||||
|
temml (tex/render-temml (or contents value))]
|
||||||
|
(if (tex/erroneous-temml-output? temml)
|
||||||
|
(let [tex (get @rendered-snippets value)]
|
||||||
|
(if (:errors tex)
|
||||||
|
(deliver promise (hiccup/raw temml))
|
||||||
|
(->> tex
|
||||||
|
read-and-patch-generated-svg
|
||||||
|
hiccup/raw
|
||||||
|
(deliver promise))))
|
||||||
|
(deliver promise (hiccup/raw temml))))
|
||||||
|
(catch Exception e
|
||||||
|
(prn e)
|
||||||
|
(flush)
|
||||||
|
(throw e))))
|
||||||
|
(when (fs/exists? "/tmp/doerg-svgs")
|
||||||
|
(fs/delete-tree "/tmp/doerg-svgs"))
|
||||||
|
(fs/copy-tree svg-dir "/tmp/doerg-svgs"))))]
|
||||||
|
(future-call (bound-fn* f))
|
||||||
|
r))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(render-tex-snippets doc))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defn- render-pprint
|
(defn- render-pprint
|
||||||
@@ -269,15 +343,12 @@
|
|||||||
(str "@" key))
|
(str "@" key))
|
||||||
|
|
||||||
(defmethod org-element "latex-fragment" [{:keys [contents value] :as e}]
|
(defmethod org-element "latex-fragment" [{:keys [contents value] :as e}]
|
||||||
(let [display? (str/starts-with? value "\\[")]
|
|
||||||
#_
|
|
||||||
(render-pprint (assoc e :display? display?))
|
|
||||||
[:span.latex-fragment
|
[:span.latex-fragment
|
||||||
(hiccup/raw (tex/render contents :display? display?))]))
|
(-> e ::rendered (deref #_#_ 2000 "«timed out»"))])
|
||||||
|
|
||||||
(defmethod org-element "latex-environment" [{:keys [value]}]
|
(defmethod org-element "latex-environment" [{:keys [value] :as e}]
|
||||||
[:span.latex-fragment
|
[:span.latex-fragment
|
||||||
(hiccup/raw (tex/render value :display? true))])
|
(-> e ::rendered (deref #_#_ 2000 "«timed out»"))])
|
||||||
|
|
||||||
(defmethod org-element "example-block" [{:keys [value]}]
|
(defmethod org-element "example-block" [{:keys [value]}]
|
||||||
[:pre value])
|
[:pre value])
|
||||||
@@ -296,7 +367,6 @@
|
|||||||
;; Completely ignore the LATEX_COMPILER keyword.
|
;; Completely ignore the LATEX_COMPILER keyword.
|
||||||
(defmethod org-keyword "LATEX_COMPILER" [_] nil)
|
(defmethod org-keyword "LATEX_COMPILER" [_] nil)
|
||||||
|
|
||||||
;; TODO: Real LatEx support.
|
|
||||||
(defmethod org-keyword "LATEX_HEADER" [_] nil)
|
(defmethod org-keyword "LATEX_HEADER" [_] nil)
|
||||||
|
|
||||||
;; Not sure how to deal with this one yet.
|
;; Not sure how to deal with this one yet.
|
||||||
@@ -319,4 +389,3 @@
|
|||||||
[:span.org-link.external
|
[:span.org-link.external
|
||||||
[:a {:href raw-link}
|
[:a {:href raw-link}
|
||||||
(or (seq children) raw-link)]])
|
(or (seq children) raw-link)]])
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,11 @@
|
|||||||
"/home/msyds/org/20250919114912-homepage.org"
|
"/home/msyds/org/20250919114912-homepage.org"
|
||||||
#_
|
#_
|
||||||
"/home/msyds/org/20251111182118-path_induction.org"
|
"/home/msyds/org/20251111182118-path_induction.org"
|
||||||
"/home/msyds/org/20250512144715-natural_transformation_category_theory.org")
|
#_
|
||||||
|
"/home/msyds/org/20250512144715-natural_transformation_category_theory.org"
|
||||||
|
#_
|
||||||
|
"/home/msyds/org/20251021155921-path_action.org"
|
||||||
|
"/tmp/t.org")
|
||||||
|
|
||||||
(defn- force-create-sym-link [path target]
|
(defn- force-create-sym-link [path target]
|
||||||
(fs/delete-if-exists path)
|
(fs/delete-if-exists path)
|
||||||
|
|||||||
@@ -116,7 +116,13 @@
|
|||||||
slurp
|
slurp
|
||||||
(str/replace-first "% {{contents}}" contents))))
|
(str/replace-first "% {{contents}}" contents))))
|
||||||
|
|
||||||
(defn render-xelatex [output-dir & snippets]
|
(defn render-xelatex
|
||||||
|
"Render a collection of `snippets` to SVGs in `output-dir` using
|
||||||
|
XeLaTeX and dvisvgm. Returns a map whose keys are `snippets` and
|
||||||
|
whose values are maps containing dimensional info, a string of
|
||||||
|
errors output by XeLaTeX, and the path to the generated SVG
|
||||||
|
file. Math delimiters are *not* implicitly added to each snippet."
|
||||||
|
[output-dir & snippets]
|
||||||
(fs/with-temp-dir [dir {:prefix "doerg-xelatex"}]
|
(fs/with-temp-dir [dir {:prefix "doerg-xelatex"}]
|
||||||
(let [preview-tex (fs/file dir "preview.tex")
|
(let [preview-tex (fs/file dir "preview.tex")
|
||||||
preview-xdv (fs/file dir "preview.xdv")
|
preview-xdv (fs/file dir "preview.xdv")
|
||||||
@@ -135,7 +141,8 @@
|
|||||||
{snippet
|
{snippet
|
||||||
(-> dimensions
|
(-> dimensions
|
||||||
(assoc
|
(assoc
|
||||||
:file (fs/file dir (format "%09d.svg" (inc ix)))))})
|
:file (fs/file output-dir
|
||||||
|
(format "%09d.svg" (inc ix)))))})
|
||||||
(range)
|
(range)
|
||||||
distinct-snippets
|
distinct-snippets
|
||||||
dimensions)
|
dimensions)
|
||||||
@@ -154,46 +161,50 @@
|
|||||||
|
|
||||||
;;; Temml
|
;;; Temml
|
||||||
|
|
||||||
(def ^:dynamic *tex-worker-timeout-duration*
|
(def ^:dynamic *temml-worker-timeout-duration*
|
||||||
"Number of milliseconds to wait before killing the external Uniorg
|
"Number of milliseconds to wait before killing the external Uniorg
|
||||||
process."
|
process."
|
||||||
(* 10 1000))
|
(* 10 1000))
|
||||||
|
|
||||||
(def ^:dynamic *worker*)
|
(def ^:dynamic *temml-worker*)
|
||||||
|
|
||||||
(defn tex-worker [& {:keys [preamble]}]
|
(defn temml-worker [& {:keys [preamble]}]
|
||||||
(p/process
|
(p/process
|
||||||
{:shutdown p/destroy-tree
|
{:shutdown p/destroy-tree
|
||||||
:err :inherit}
|
:err (l/log-stream :info "temml/err")}
|
||||||
#_"doerg-tex"
|
#_"doerg-tex"
|
||||||
"./doerg-tex/index.js"
|
"./doerg-tex/index.js"
|
||||||
"--preamble"
|
"--preamble"
|
||||||
"resources/net/deertopia/doerg/prelude.tex"))
|
"resources/net/deertopia/doerg/prelude.tex"))
|
||||||
|
|
||||||
(defn finish [tw]
|
(defn close-temml-worker [tw]
|
||||||
(.close (:in tw)))
|
(.close (:in tw)))
|
||||||
|
|
||||||
(defmacro with-tex-worker [tw & body]
|
(defmacro with-temml-worker [tw & body]
|
||||||
`(let [~tw (tex-worker)]
|
`(let [~tw (temml-worker)]
|
||||||
(try
|
(try
|
||||||
(do ~@body)
|
(do ~@body)
|
||||||
(finally
|
(finally
|
||||||
(finish ~tw)
|
(close-temml-worker ~tw)
|
||||||
(p/destroy-tree ~tw)))))
|
(p/destroy-tree ~tw)))))
|
||||||
|
|
||||||
(defmacro binding-tex-worker [& body]
|
(defmacro binding-temml-worker [& body]
|
||||||
`(binding [*worker* (tex-worker)]
|
`(binding [*temml-worker* (temml-worker)]
|
||||||
(try
|
(try
|
||||||
~@body
|
~@body
|
||||||
(finally
|
(finally
|
||||||
(finish *worker*)))))
|
(close-temml-worker *temml-worker*)))))
|
||||||
|
|
||||||
(defn command [x]
|
(defn command-temml-worker [x]
|
||||||
(cbor/encode cbor/default-codec (:in *worker*) x)
|
(cbor/encode cbor/default-codec (:in *temml-worker*) x)
|
||||||
(.flush (:in *worker*))
|
(.flush (:in *temml-worker*))
|
||||||
(cbor/decode cbor/default-codec (:out *worker*)))
|
(cbor/decode cbor/default-codec (:out *temml-worker*)))
|
||||||
|
|
||||||
(defn render [s & {:keys [display?]}]
|
(defn render-temml [s & {:keys [display?]}]
|
||||||
(if display?
|
(if display?
|
||||||
(command [s])
|
(command-temml-worker [s])
|
||||||
(command s)))
|
(command-temml-worker s)))
|
||||||
|
|
||||||
|
;; hackky....
|
||||||
|
(defn erroneous-temml-output? [s]
|
||||||
|
(re-find #"(#b22222|temml-error)" s))
|
||||||
|
|||||||
Reference in New Issue
Block a user