A Clojure library designed to produce Tex documents.
Add this to the dependencies:
[texdata "0.1.5-SNAPSHOT"]
For testing purposes, create test.tex file somewhere, let's say in the test directory of the project.
(ns texdata-demo.core
(:require [texdata.core :refer [tex compile-and-view]]))
(def test-path "test/texdata_demo/test.tex")
(compile-and-view
test-path
(tex [:documentclass "article"]
[:document "hello world"]))
This library offers a few functions that convert Clojure data to string recognizable by TeX. tex is such a function and the below are simple examples:
(tex 1)
;; => "1"
(tex "a")
;; => "a"
(tex :sin)
;; => "\\sin"
(tex [:frac 1 2])
;; => "\\frac{1}{2}"
(tex [:equation [:frac 1 2]])
;; => "\\begin{equation} \\frac{1}{2} \\end{equation}"
When it is given string or number, it just returns the string representation of the argument. In other words, tex is nothing else but clojure.core/str in this case.
tex treats a standalone keyword signifying a TeX symbol. Most keywords are converted to the corresponding TeX symbol when passed to tex, tex-> or tex->>.
(tex :sin)
;; => "\\sin"
(tex :log)
;; => "\\log"
As you see above, tex converts a keyword k to string by just adding the escape character to the result of (name k).
There are some exceptions to this rule:
(tex :amp)
;; => "&"
(tex :next)
;; => "\\\\"
When tex is given a vector beginning with a keyword that specifies a Tex command (like :frac in the last example), it returns a string corresponding to that TeX command. More precisely, a vector like
[keyword arg1 arg2 ...]
is treated in a similar way to the S-expression in Lisp:
(function arg1 arg2 ...)
In TeX, superscripts and subscripts are written using the symbols ^ and _. To get the same result, use :super and :sub keywords respectively:
(tex ["a" :super 2 :sub "n"])
;; => "a_{n}^{2}"
To put a mathematical expression in inline mode, use :dol like:
(tex [:dol ["x" :super 2] "+" ["y" :super 2] :eq ["z" :super 2]])
;; => "$ x^{2} + y^{2} = z^{2} $"
To effect the display mode, use :math for unnumbered expressions and :equation for numbered expressions:
(tex [:math "E" :eq "mc"])
;; => "\\[ E = mc \\]"
(tex [:equation "E" :eq "mc"])
;; => "\\begin{equation} E = mc \\end{equation}"
You can specify optional attributes in some commands. That is done by inserting a map that contains appropriate data.
:documentclass is one of such commands as take optional map argument:
(tex [:documentclass "article"])
;; => "\\documentclass{article}"
(tex [:documentclass {:opt "landscape"} "article"])
;; => "\\documentclass[landscape]{article}"
tex-> and tex->> are functions somewhat similar to clojure.core/-> and clojure.core/->> respectively. They are handy when composing commands. For example,
(tex-> "x" :equation :huge)
is same as
(tex [:huge [:equation "x"]])
producing the result:
"\\begin{huge} \\begin{equation} x \\end{equation} \\end{huge}"
tex->, like clojure.core/->, inserts the previous result at the second place of the next form and repeats the process.
tex->> works likewise, but the previous result is inserted at the last of the next form, like clojure.core/->>. This is necessary when working with commands like :color, which takes a string specifying the color of the subsequent part as the first argument:
(tex [:color "red" [:equation "x = 1"]])
To obtain the same result with tex->>, we write:
(tex->> "x = 1" :equation [:color "red"])
;; => "\\color{red}{\\begin{equation} x = 1 \\end{equation}}"
example is a function to obtain expected input examples for each command.
(example :int)
;; => [:int {:from 0, :to 1} "f(x)" "dx"]
There may be times when you want to add a new command. defcmd is a macro designed for that purpose. Its grammar is as follows:
(defcmd command-keyword command-type &body)
Here, command-keyword is a keyword signifying the command to be defined. command-type is one of
- :environment
- :normal
- :independent
:environment commands are such ones as :equation, namely, they are commands sandwiched between \begin{...} and \end{...}. :normal commands are other commands that takes arguments (e.g., :frac, :color). :independent commands are specified by standalone keywords that are converted to TeX symbols.
defcmd provides default implementation for :environment and :normal commands. Let's suppose you want to register a new environment command :hoge with the desired result:
(tex [:hoge "hello"])
;; => "\\begin{hoge} hello \\end{hoge}"
Then, just evaluating
(defcmd :hoge :environment :default)
will do.
Other times, you may need to have more specific structure. Let's suppose that your new command :my-color takes a string of color as the first argument, followed by any number of arguments:
(tex [:my-color "red" "hello"])
;; => "\\mycolor{red}{hello}"
Registering :my-color requires giving necessary details in defcmd:
(defcmd :my-color :normal [[_ c & args]]
(format "\\mycolor{%s}{%s}" c (tex args)))
When not using the :default keyword as explained above, the @body part of the defcmd is like that of defn. Let me note that its parameter is supposed to be a vector (not variable argument list ), whose first item is the command keyword (:my-color in the above example).
Here is a code that produces a complete TeX document:
(def dirac-delta
(tex
[:documentclass "article"]
[:usepackage "amsmath"]
[:usepackage "amssymb"]
[:usepackage {:opt ["left = 20mm" "right = 20mm"] } "geometry"]
[:document
[:huge
["The Dirac delta function" [:dol :delta "(x)"] "satisfies:"
[:enumerate
[:item
[:math :delta "(x)" :eq 0 :sp
[:text "for all" [:dol "x" :neq 0]]]]
[:item
[:math :int :sub ["-" :infty] :super :infty
:delta "(x)dx"
:eq 1]]]
"From these properties, it follows that for all function"
[:dol "f,"]
[:math
:int :sub ["-" :infty] :super :infty
:delta "(x)" "f(x)" "dx"
:eq
"f(0)."]]]]))
compile-and-view is useful when you want to see the result quickly, It takes path to a TeX file and a string, writes the string therein and TeX compiles the file and opens the resulting PDF in the system's default viewer. Let's create a file:
test/texdata/examples/out/test.tex
To see the result of the above example, evaluate:
(compile-and-view "test/texdata/examples/out/test.tex" dirac-delta )
Copyright © 2020 FIXME
This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0.
This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version, with the GNU Classpath Exception which is available at https://www.gnu.org/software/classpath/license.html.