diff --git a/doerg/src/net/deertopia/doerg/render.clj b/doerg/src/net/deertopia/doerg/render.clj index 0619019..b989bc2 100644 --- a/doerg/src/net/deertopia/doerg/render.clj +++ b/doerg/src/net/deertopia/doerg/render.clj @@ -9,10 +9,10 @@ [net.deertopia.doerg.html :as doerg-html] [hiccup2.core :as hiccup] [clojure.pprint] - #_ + ;; #_ [net.deertopia.doerg.tex :as tex] - [net.deertopia.doerg.tex.native :as tex-native] - [net.deertopia.doerg.tex.temml :as tex-temml] + ;; [net.deertopia.doerg.tex.native :as tex-native] + ;; [net.deertopia.doerg.tex.temml :as tex-temml] [clojure.zip :as z] [babashka.fs :as fs])) @@ -143,72 +143,28 @@ #(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'\?>\n?" "") - (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 "Traverse doc, adorning each LaTeX node with a promise resolving to, optimistically, Hiccup-rendered SVG or MathML code." [doc] - (let [promises (atom []) + (let [snippet-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})) + (swap! snippet-promises #(conj % [(:value node) p])) (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-native/render svg-dir)))] - (doseq [{:keys [promise node]} @promises] - (try (let [{:keys [value]} node - temml (tex-temml/render value)] - (if (tex-temml/erroneous-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 - (lr/error e) - (throw e))))))) - fut (future-call (bound-fn* f))] + fut (-> #(tex/render-snippets @snippet-promises) + bound-fn* future-call)] ;; Time out after eight seconds. With all the LaTeX and IPC, there ;; are so many opportunities for things to go wrong (slurp file) + (str/replace-first #"<\?xml version='1.0' encoding='UTF-8'\?>\n?" "") + (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-snippets [snippet-promises] + (fs/with-temp-dir [svg-dir {:prefix "doerg-svg-"}] + (let [rendered-snippets + (delay (->> snippet-promises + (map first) + (apply native/render svg-dir)))] + (doseq [[snippet p] snippet-promises] + (try (let [temml (temml/render snippet)] + (if (temml/erroneous-output? temml) + (let [tex (get @rendered-snippets snippet)] + (if (:errors tex) + (deliver p (hiccup/raw temml)) + (->> tex + read-and-patch-generated-svg + hiccup/raw + (deliver p)))) + (deliver p (hiccup/raw temml)))) + (catch Exception e + (l/error e "Error in TeX thread") + (throw e))))))) + +(comment + (let [snippets (for [x ["\\(\\ifxetex blah \\fi\\)" + "\\(\\sqrt{x^2 + y^2}\\)"]] + [x (promise)])] + (temml/binding-worker + (render-snippets snippets) + (map #(-> % second deref) snippets)))) diff --git a/doerg/src/net/deertopia/doerg/tex/temml.clj b/doerg/src/net/deertopia/doerg/tex/temml.clj index 18aacdf..9beefa0 100644 --- a/doerg/src/net/deertopia/doerg/tex/temml.clj +++ b/doerg/src/net/deertopia/doerg/tex/temml.clj @@ -61,7 +61,9 @@ (render-display s) (if-let [[_ inner] (re-matches #"(?s)\\\((.*)\\\)" s)] (render-inline inner) - (throw (ex-info "weird" {:snippet s})))))) + (throw (IllegalArgumentException. + (str "`net.deertopia.doerg.tex.temml` argument should" + " be enclosed in math delimiters."))))))) ;; hackky.... (defn erroneous-output? [s]