summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2015-11-07 19:24:34 +0000
committerGregor Kleen <gkleen@yggdrasil.li>2015-11-07 19:24:34 +0000
commitbb296bb319a1d9a0050577dfed96e30298390db7 (patch)
tree44b60ff682669c718e7431aef39f0fcbd1bd3b90
parenta1068fbdeea74a12e4f33069cf091302f87e8d17 (diff)
downloaddirty-haskell.org-bb296bb319a1d9a0050577dfed96e30298390db7.tar
dirty-haskell.org-bb296bb319a1d9a0050577dfed96e30298390db7.tar.gz
dirty-haskell.org-bb296bb319a1d9a0050577dfed96e30298390db7.tar.bz2
dirty-haskell.org-bb296bb319a1d9a0050577dfed96e30298390db7.tar.xz
dirty-haskell.org-bb296bb319a1d9a0050577dfed96e30298390db7.zip
Working math support
-rw-r--r--blog.cabal2
-rw-r--r--blog.nix6
-rw-r--r--default.nix6
-rw-r--r--provider/css/default.css2
-rw-r--r--provider/css/math.css49
-rw-r--r--provider/posts/tex-support.md235
-rw-r--r--provider/templates/default.html1
-rw-r--r--provider/templates/math.tex2
-rw-r--r--shell.nix5
-rw-r--r--src/Math.hs63
-rw-r--r--src/Site.hs53
-rw-r--r--tex/preamble.tex2
12 files changed, 376 insertions, 50 deletions
diff --git a/blog.cabal b/blog.cabal
index 7f79183..e6aba5f 100644
--- a/blog.cabal
+++ b/blog.cabal
@@ -34,3 +34,5 @@ executable site
34 , process >=1.2 && <2 34 , process >=1.2 && <2
35 , directory >=1.2 && <2 35 , directory >=1.2 && <2
36 , deepseq >=1.4 && <2 36 , deepseq >=1.4 && <2
37 , regex-tdfa >=1.2 && <2
38 , mtl >=2.2 && <3
diff --git a/blog.nix b/blog.nix
index 1ef9585..ca9a83f 100644
--- a/blog.nix
+++ b/blog.nix
@@ -1,7 +1,8 @@
1# This file was auto-generated by cabal2nix. Please do NOT edit manually! 1# This file was auto-generated by cabal2nix. Please do NOT edit manually!
2 2
3{ mkDerivation, stdenv 3{ mkDerivation, stdenv
4, hakyll, containers, pandoc, data-default, filepath, hex, cryptohash, process, temporary, directory, deepseq 4, hakyll, containers, pandoc, data-default, filepath, hex, cryptohash
5, process, temporary, directory, deepseq, regex-tdfa, mtl
5}: 6}:
6 7
7mkDerivation { 8mkDerivation {
@@ -11,7 +12,8 @@ mkDerivation {
11 isExecutable = true; 12 isExecutable = true;
12 isLibrary = false; 13 isLibrary = false;
13 buildDepends = [ 14 buildDepends = [
14 hakyll containers pandoc data-default filepath hex cryptohash process temporary directory deepseq 15 hakyll containers pandoc data-default filepath hex cryptohash
16 process temporary directory deepseq regex-tdfa mtl
15 ]; 17 ];
16 license = stdenv.lib.licenses.publicDomain; 18 license = stdenv.lib.licenses.publicDomain;
17} 19}
diff --git a/default.nix b/default.nix
index 73581f5..d6e481e 100644
--- a/default.nix
+++ b/default.nix
@@ -10,8 +10,8 @@ rec {
10 ''; 10 '';
11 } 11 }
12 ); 12 );
13 texEnv = with pkgs; texLiveAggregationFun { 13 texEnv = with pkgs; texlive.combine {
14 paths = [ texLive texLiveExtra lmodern libertine tipa texLiveContext texLiveCMSuper ]; 14 inherit (texlive) scheme-small standalone dvisvgm amsmath;
15 }; 15 };
16 dirty-haskell-wrapper = pkgs.stdenv.mkDerivation rec { 16 dirty-haskell-wrapper = pkgs.stdenv.mkDerivation rec {
17 name = "dirty-haskell-wrapper"; 17 name = "dirty-haskell-wrapper";
@@ -19,7 +19,7 @@ rec {
19 buildCommand = '' 19 buildCommand = ''
20 mkdir -p $out/bin 20 mkdir -p $out/bin
21 makeWrapper ${dirty-haskell}/bin/site $out/bin/dirty-haskell \ 21 makeWrapper ${dirty-haskell}/bin/site $out/bin/dirty-haskell \
22 --append PATH : ${texEnv}/bin 22 --suffix PATH : ${texEnv}/bin
23 ''; 23 '';
24 }; 24 };
25} 25}
diff --git a/provider/css/default.css b/provider/css/default.css
index 8f796e0..2d2a777 100644
--- a/provider/css/default.css
+++ b/provider/css/default.css
@@ -25,5 +25,5 @@ pre {
25} 25}
26 26
27p code { 27p code {
28 font-style:italic; 28 font-style:italic;
29} \ No newline at end of file 29} \ No newline at end of file
diff --git a/provider/css/math.css b/provider/css/math.css
new file mode 100644
index 0000000..4dc0dd7
--- /dev/null
+++ b/provider/css/math.css
@@ -0,0 +1,49 @@
1span.inline-math {
2 display:inline;
3}
4
5span.display-math {
6 display:block;
7 text-align:center;
8 margin-top:0.2em;
9 margin-bottom:0.2em;
10}
11
12div.theorem, div.lemma, div.definition, div.corollary, div.proof {
13 margin-left: 2em;
14 margin-top: 1em;
15 margin-bottom: 1em;
16}
17
18div.theorem > p:first-child:before {
19 content: "Theorem. ";
20 font-weight: bold;
21}
22
23div.lemma > p:first-child:before {
24 content: "Lemma. ";
25 font-weight: bold;
26}
27
28div.definition > p:first-child:before {
29 content: "Definition. ";
30 font-weight: bold;
31}
32
33div.corollary > p:first-child:before {
34 content: "Corollary. ";
35 font-weight: bold;
36}
37
38div.proof > p:first-child:before {
39 content: "Proof. ";
40 font-style: italic;
41}
42
43div.proof > p:last-child:after {
44 content: " ∎";
45}
46
47div.theorem + div.proof {
48 margin-top: -1em;
49} \ No newline at end of file
diff --git a/provider/posts/tex-support.md b/provider/posts/tex-support.md
index 16468ca..0beade1 100644
--- a/provider/posts/tex-support.md
+++ b/provider/posts/tex-support.md
@@ -1,14 +1,243 @@
1--- 1---
2title: Cursory LaTeX-Support 2title: Cursory Math-Support
3published: 2015-11-05 3published: 2015-11-05
4tags: Blog Software 4tags: Blog Software
5--- 5---
6 6
7I added some cursory support for LaTeX as shown below: 7## Demonstration
8
9I added some cursory support for math as shown below:
8 10
9<div class="theorem"> 11<div class="theorem">
12
13Formulae are rendered with $\text{\LaTeX}$ and included as [SVG](https://en.wikipedia.org/wiki/SVG).
14
15<div class="proof">
10$$e^{ix} =\text{cos}(x) + i \text{sin} (x)$$ 16$$e^{ix} =\text{cos}(x) + i \text{sin} (x)$$
11</div> 17</div>
18<div class="lemma">
19
20Inline formulae get correctly aligned to match the baseline of the surrounding text.
21
22<div class="proof">
23$\text{tan}(\phi) = \frac{\text{sin}(\phi)}{\text{cos}(\phi)}$
24</div>
25</div>
26</div>
27
28## Implementation
29
30Theorem environments are written using [pandoc](http://pandoc.org)s support for block environments:
31
32~~~ {.markdown .numberLines}
12<div class="theorem"> 33<div class="theorem">
13$$\text{tan}(\phi) = \frac{\text{sin}(\phi)}{\text{cos}(\phi)}$$ 34
35Formulae are rendered with $\text{\LaTeX}$ and included as [SVG](https://en.wikipedia.org/wiki/SVG).
36
37<div class="proof">
38$$e^{ix} =\text{cos}(x) + i \text{sin} (x)$$
39</div>
40<div class="lemma">
41
42Inline formulae get correctly aligned to match the baseline of the surrounding text.
43
44<div class="proof">
45$\text{tan}(\phi) = \frac{\text{sin}(\phi)}{\text{cos}(\phi)}$
46</div>
47</div>
14</div> 48</div>
49~~~
50
51Combined with a smattering of CSS this works nicely.
52$\text{\LaTeX}$ support is, however, lacking as I opted not to patch pandoc ([math.kleen.org](https://math.kleen.org) did).
53
54### `Math.hs`
55
56The actual compilation happens in a new module I named `Math.hs`. We´ll start there.
57For your reading pleasure I added some comments to the reproduction below.
58
59~~~ {.haskell .numberLines}
60module Math
61 ( compileMath
62 ) where
63
64import System.IO (stdout, stderr, hPutStrLn, writeFile, readFile)
65import System.IO.Temp (withSystemTempDirectory)
66import System.Process (callProcess, readProcessWithExitCode)
67import System.Directory (copyFile, getCurrentDirectory, setCurrentDirectory)
68import System.FilePath (takeFileName, FilePath(..), (</>))
69import System.Exit (ExitCode(..))
70
71import Control.Monad (when)
72import Control.Exception (bracket, throwIO)
73import Data.Maybe (fromMaybe, listToMaybe)
74
75import Control.Monad.Writer.Strict (WriterT(..), execWriterT, tell)
76import Control.Monad.Trans (liftIO)
77
78import Control.DeepSeq (($!!))
79
80import Text.Regex.TDFA ((=~))
81
82-- We built a monoid instance for `ExitCode` so we can easily collect failure using a `MonadWriter`
83instance Monoid ExitCode where
84 mempty = ExitSuccess
85 (ExitFailure a) `mappend` _ = ExitFailure a
86 ExitSuccess `mappend` x@(ExitFailure _) = x
87 ExitSuccess `mappend` ExitSuccess = ExitSuccess
88
89
90compileMath :: String -> IO (String, String)
91compileMath = withSystemTempDirectory "math" . compileMath' -- Create a temporary directory, run `compileMath'`, and make sure the directory get's deleted
92
93compileMath' :: String -> FilePath -> IO (String, String)
94compileMath' input tmpDir = do
95 mapM_ (copyToTmp . ("tex" </>)) [ "preamble.tex"
96 , "preview.dtx"
97 , "preview.ins"
98 ]
99 (exitCode, out, err) <- withCurrentDirectory tmpDir $ execWriterT $ do -- Collect stdout, stderr, and exitCode of all subprocesses (stdout and stderr simply get appended to one another)
100 run "latex" [ "-interaction=batchmode"
101 , "preview.ins"
102 ] ""
103 liftIO $ writeFile (tmpDir </> "image.tex") input
104 run "latex" [ "-interaction=batchmode"
105 , "image.tex"
106 ] ""
107 run "dvisvgm" [ "--exact"
108 , "--no-fonts"
109 , tmpDir </> "image.dvi"
110 ] ""
111 when (exitCode /= ExitSuccess) $ do -- Fail with maximum noise if any of the latex passes fail -- otherwise be silent
112 hPutStrLn stdout out
113 hPutStrLn stderr err
114 throwIO exitCode
115 (\x -> return $!! (x, extractAlignment err)) =<< (readFile $ tmpDir </> "image.svg") -- Note the call to `($!!)` -- since we'll be deleting `tmpDir` we need to make sure the entire generated output resides in memory before we leave this block
116 where
117 copyToTmp fp = copyFile fp (tmpDir </> takeFileName fp)
118 run :: String -> [String] -> String -> WriterT (ExitCode, String, String) IO ()
119 run bin args stdin = tell =<< liftIO (readProcessWithExitCode bin args stdin)
120
121withCurrentDirectory :: FilePath -- ^ Directory to execute in
122 -> IO a -- ^ Action to be executed
123 -> IO a
124-- ^ This is provided in newer versions of temporary
125withCurrentDirectory dir action =
126 bracket getCurrentDirectory setCurrentDirectory $ \ _ -> do
127 setCurrentDirectory dir
128 action
129
130extractAlignment :: String -> String
131extractAlignment = fromMaybe "0pt" . extract . (=~ "depth=([^\\s]+)") -- One of the few places where regular expressions really prove usefull
132 where
133 extract :: (String, String, String, [String]) -> Maybe String
134 extract (_, _, _, xs) = listToMaybe xs
135~~~
136
137### `Site.hs`
138
139The more trick part proved to be integration into the framework as provided by [Hakyll](http://jaspervdj.be/hakyll/).
140
141~~~ {.haskell .numberLines}
142
143
144import qualified Crypto.Hash.SHA256 as SHA256 (hash)
145import qualified Data.ByteString.Char8 as CBS
146import Data.Hex (hex)
147import Data.Char (toLower)
148
149import Math (compileMath)
150import Text.Printf (printf)
151
152main :: IO ()
153main = hakyllWith config $ do
154
155
156 math <- getMath "posts/*" mathTranslation'
157 forM_ math $ \(_, mathStr) -> create [mathTranslation' mathStr] $ do
158 route idRoute
159 compile $ do
160 item <- makeItem mathStr
161 >>= loadAndApplyTemplate "templates/math.tex" defaultContext
162 >>= withItemBody (unsafeCompiler . compileMath) -- unsafeCompiler :: IO a -> Compiler a
163 saveSnapshot "alignment" $ fmap snd item
164 return $ fmap fst item
165
166 match "posts/*" $ do
167 route $ setExtension ".html"
168 compile $ do
169 getResourceBody >>= saveSnapshot "content"
170 pandocCompilerWithTransformM defaultHakyllReaderOptions defaultHakyllWriterOptions mathTransform -- pandocCompilerWithTransformM :: ReaderOptions -> WriterOptions -> (Pandoc -> Compiler Pandoc) -> Item String
171 >>= loadAndApplyTemplate "templates/default.html" defaultContext
172 >>= relativizeUrls
173
174
175
176
177mathTranslation' :: String -> Identifier
178-- ^ This generates the filename for a svg file given the TeX-source
179mathTranslation' = fromCapture "math/*.svg" . map toLower . CBS.unpack . hex . SHA256.hash . CBS.pack
180
181getMath :: Pattern -> (String -> Identifier) -> Rules [([Identifier], String)]
182-- ^ We scrape all posts for math, calls `readPandoc'`
183getMath pattern makeId = do
184 ids <- getMatches pattern
185 mathStrs <- concat `liftM` mapM (\id -> map ((,) [id]) `liftM` getMath' (toFilePath' id)) ids
186 return $ mergeGroups $ groupBy ((==) `on` snd) $ mathStrs
187 where
188 getMath' :: FilePath -> Rules [String]
189 getMath' path = preprocess (query extractMath `liftM` readPandoc' path)
190 extractMath :: Inline -> [String]
191 extractMath (Math _ str) = [str]
192 extractMath _ = []
193 mergeGroups :: [[([Identifier], String)]] -> [([Identifier], String)]
194 mergeGroups = map mergeGroups' . filter (not . null)
195 mergeGroups' :: [([Identifier], String)] -> ([Identifier], String)
196 mergeGroups' xs@((_, str):_) = (concatMap fst xs, str)
197
198readPandoc' :: FilePath -> IO Pandoc
199-- ^ This is copied, almost verbatim, from Hakyll source -- Does what it says on the tin
200readPandoc' path = readFile path >>= either fail return . result'
201 where
202 result' str = case result str of
203 Left (ParseFailure err) -> Left $
204 "parse failed: " ++ err
205 Left (ParsecError _ err) -> Left $
206 "parse failed: " ++ show err
207 Right item' -> Right item'
208 result str = reader defaultHakyllReaderOptions (fileType path) str
209 reader ro t = case t of
210 DocBook -> readDocBook ro
211 Html -> readHtml ro
212 LaTeX -> readLaTeX ro
213 LiterateHaskell t' -> reader (addExt ro Ext_literate_haskell) t'
214 Markdown -> readMarkdown ro
215 MediaWiki -> readMediaWiki ro
216 OrgMode -> readOrg ro
217 Rst -> readRST ro
218 Textile -> readTextile ro
219 _ -> error $
220 "I don't know how to read a file of " ++
221 "the type " ++ show t ++ " for: " ++ path
222
223 addExt ro e = ro {readerExtensions = Set.insert e $ readerExtensions ro}
224
225mathTransform :: Pandoc -> Compiler Pandoc
226-- ^ We replace math by raw html includes of the respective svg files here
227mathTransform = walkM mathTransform'
228 where
229 mathTransform' :: Inline -> Compiler Inline
230 mathTransform' (Math mathType tex) = do
231 alignment <- loadSnapshotBody texId "alignment"
232 let
233 html = printf "<object data=\"/%s\" type=\"image/svg+xml\" style=\"vertical-align:-%s\">%s</object>"
234 (toFilePath texId) (alignment :: String) tex
235 return $ Span ("", [classOf mathType], []) [RawInline (Format "html") html]
236 where
237 texId = mathTranslation' tex
238 classOf DisplayMath = "display-math"
239 classOf InlineMath = "inline-math"
240 mathTransform' x = return x
241
242
243~~~
diff --git a/provider/templates/default.html b/provider/templates/default.html
index 5bc0e9f..ba412e6 100644
--- a/provider/templates/default.html
+++ b/provider/templates/default.html
@@ -10,6 +10,7 @@
10 <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> 10 <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
11 <![endif]--> 11 <![endif]-->
12 <link rel="stylesheet" href="/css/default.css"> 12 <link rel="stylesheet" href="/css/default.css">
13 <link rel="stylesheet" href="/css/math.css">
13 <link rel="stylesheet" href="/css/syntax.css"> 14 <link rel="stylesheet" href="/css/syntax.css">
14 $if(rss)$<link rel="alternate" type="application/atom+xml" href="$rss$" title="Atom 1.0">$endif$ 15 $if(rss)$<link rel="alternate" type="application/atom+xml" href="$rss$" title="Atom 1.0">$endif$
15</head> 16</head>
diff --git a/provider/templates/math.tex b/provider/templates/math.tex
index 4d5455f..23774bf 100644
--- a/provider/templates/math.tex
+++ b/provider/templates/math.tex
@@ -1,5 +1,5 @@
1\documentclass[14pt,preview,border=1pt,class=extarticle]{standalone} 1\documentclass[14pt,preview,border=1pt,class=extarticle]{standalone}
2\include{preamble.tex} 2\include{preamble}
3\begin{document} 3\begin{document}
4\begin{preview} 4\begin{preview}
5\( 5\(
diff --git a/shell.nix b/shell.nix
index e17c3e6..9e363eb 100644
--- a/shell.nix
+++ b/shell.nix
@@ -3,8 +3,9 @@
3 3
4pkgs.stdenv.mkDerivation rec { 4pkgs.stdenv.mkDerivation rec {
5 name = "dirty-haskell"; 5 name = "dirty-haskell";
6 buildInputs = [ (import ./default.nix {}).dirty-haskell-wrapper 6 buildInputs = with (import ./default.nix {}); [ dirty-haskell-wrapper
7 ]; 7 (pkgs.haskellPackages.ghcWithPackages (p: with p; [ regex-tdfa ]))
8 ];
8 shellHook = '' 9 shellHook = ''
9 export PROMPT_INFO=${name} 10 export PROMPT_INFO=${name}
10 ''; 11 '';
diff --git a/src/Math.hs b/src/Math.hs
index e927fdd..db01f75 100644
--- a/src/Math.hs
+++ b/src/Math.hs
@@ -2,40 +2,61 @@ module Math
2 ( compileMath 2 ( compileMath
3 ) where 3 ) where
4 4
5import System.IO (stdout, stderr, hPutStrLn, writeFile, readFile)
5import System.IO.Temp (withSystemTempDirectory) 6import System.IO.Temp (withSystemTempDirectory)
6import System.Process (callProcess) 7import System.Process (callProcess, readProcessWithExitCode)
7import System.Directory (copyFile, getCurrentDirectory, setCurrentDirectory) 8import System.Directory (copyFile, getCurrentDirectory, setCurrentDirectory)
8import System.FilePath (takeFileName, FilePath(..), (</>)) 9import System.FilePath (takeFileName, FilePath(..), (</>))
10import System.Exit (ExitCode(..))
9 11
10import Control.Monad 12import Control.Monad (when)
11import Control.Exception (bracket) 13import Control.Exception (bracket, throwIO)
14import Data.Maybe (fromMaybe, listToMaybe)
15
16import Control.Monad.Writer.Strict (WriterT(..), execWriterT, tell)
17import Control.Monad.Trans (liftIO)
12 18
13import Control.DeepSeq (($!!)) 19import Control.DeepSeq (($!!))
14 20
15compileMath :: String -> IO String 21import Text.Regex.TDFA ((=~))
22
23instance Monoid ExitCode where
24 mempty = ExitSuccess
25 (ExitFailure a) `mappend` _ = ExitFailure a
26 ExitSuccess `mappend` x@(ExitFailure _) = x
27 ExitSuccess `mappend` ExitSuccess = ExitSuccess
28
29
30compileMath :: String -> IO (String, String)
16compileMath = withSystemTempDirectory "math" . compileMath' 31compileMath = withSystemTempDirectory "math" . compileMath'
17 32
18compileMath' :: String -> FilePath -> IO String 33compileMath' :: String -> FilePath -> IO (String, String)
19compileMath' input tmpDir = do 34compileMath' input tmpDir = do
20 mapM_ (copyToTmp . ("tex" </>)) [ "preamble.tex" 35 mapM_ (copyToTmp . ("tex" </>)) [ "preamble.tex"
21 , "preview.dtx" 36 , "preview.dtx"
22 , "preview.ins" 37 , "preview.ins"
23 ] 38 ]
24 withCurrentDirectory tmpDir $ do 39 (exitCode, out, err) <- withCurrentDirectory tmpDir $ execWriterT $ do
25 callProcess "latex" [ "-interaction=batchmode" 40 run "latex" [ "-interaction=batchmode"
26 , "preview.ins" 41 , "preview.ins"
27 ] 42 ] ""
28 writeFile (tmpDir </> "image.tex") input 43 liftIO $ writeFile (tmpDir </> "image.tex") input
29 callProcess "latex" [ "-interaction=batchmode" 44 run "latex" [ "-interaction=batchmode"
30 , "image.tex" 45 , "image.tex"
31 ] 46 ] ""
32 callProcess "dvisvgm" [ "--exact" 47 run "dvisvgm" [ "--exact"
33 , "--no-fonts" 48 , "--no-fonts"
34 , tmpDir </> "image.dvi" 49 , tmpDir </> "image.dvi"
35 ] 50 ] ""
36 (\x -> return $!! x) =<< (readFile $ tmpDir </> "image.svg") 51 when (exitCode /= ExitSuccess) $ do
52 hPutStrLn stdout out
53 hPutStrLn stderr err
54 throwIO exitCode
55 (\x -> return $!! (x, extractAlignment err)) =<< (readFile $ tmpDir </> "image.svg")
37 where 56 where
38 copyToTmp fp = copyFile fp (tmpDir </> takeFileName fp) 57 copyToTmp fp = copyFile fp (tmpDir </> takeFileName fp)
58 run :: String -> [String] -> String -> WriterT (ExitCode, String, String) IO ()
59 run bin args stdin = tell =<< liftIO (readProcessWithExitCode bin args stdin)
39 60
40withCurrentDirectory :: FilePath -- ^ Directory to execute in 61withCurrentDirectory :: FilePath -- ^ Directory to execute in
41 -> IO a -- ^ Action to be executed 62 -> IO a -- ^ Action to be executed
@@ -44,3 +65,9 @@ withCurrentDirectory dir action =
44 bracket getCurrentDirectory setCurrentDirectory $ \ _ -> do 65 bracket getCurrentDirectory setCurrentDirectory $ \ _ -> do
45 setCurrentDirectory dir 66 setCurrentDirectory dir
46 action 67 action
68
69extractAlignment :: String -> String
70extractAlignment = fromMaybe "0pt" . extract . (=~ "depth=([^\\s]+)")
71 where
72 extract :: (String, String, String, [String]) -> Maybe String
73 extract (_, _, _, xs) = listToMaybe xs
diff --git a/src/Site.hs b/src/Site.hs
index e821322..dcb9a7d 100644
--- a/src/Site.hs
+++ b/src/Site.hs
@@ -13,18 +13,19 @@ import Data.List (take, reverse, nub, groupBy, concatMap)
13import Data.Function (on) 13import Data.Function (on)
14import Data.Default 14import Data.Default
15import Text.Pandoc 15import Text.Pandoc
16import Text.Pandoc.Walk (query) 16import Text.Pandoc.Walk (query, walkM)
17import Text.Pandoc.Error 17import Text.Pandoc.Error
18import Control.Applicative (Alternative(..), Applicative(..)) 18import Control.Applicative (Alternative(..), Applicative(..))
19 19
20import System.FilePath (takeBaseName, (</>), (<.>))
21
20import qualified Crypto.Hash.SHA256 as SHA256 (hash) 22import qualified Crypto.Hash.SHA256 as SHA256 (hash)
21import qualified Data.ByteString.Char8 as CBS 23import qualified Data.ByteString.Char8 as CBS
22import Data.Hex 24import Data.Hex (hex)
23import Data.Char (toLower) 25import Data.Char (toLower)
24 26
25import System.FilePath (takeBaseName, (</>), (<.>))
26
27import Math (compileMath) 27import Math (compileMath)
28import Text.Printf (printf)
28 29
29main :: IO () 30main :: IO ()
30main = hakyllWith config $ do 31main = hakyllWith config $ do
@@ -34,24 +35,24 @@ main = hakyllWith config $ do
34 route idRoute 35 route idRoute
35 compile copyFileCompiler 36 compile copyFileCompiler
36 37
38 math <- getMath "posts/*" mathTranslation'
39 forM_ math $ \(_, mathStr) -> create [mathTranslation' mathStr] $ do
40 route idRoute
41 compile $ do
42 item <- makeItem mathStr
43 >>= loadAndApplyTemplate "templates/math.tex" defaultContext
44 >>= withItemBody (unsafeCompiler . compileMath)
45 saveSnapshot "alignment" $ fmap snd item
46 return $ fmap fst item
47
37 match "posts/*" $ do 48 match "posts/*" $ do
38 route $ setExtension ".html" 49 route $ setExtension ".html"
39 compile $ do 50 compile $ do
40 getResourceBody >>= saveSnapshot "content" 51 getResourceBody >>= saveSnapshot "content"
41 pandocCompiler 52 pandocCompilerWithTransformM defaultHakyllReaderOptions defaultHakyllWriterOptions mathTransform
42 >>= loadAndApplyTemplate "templates/default.html" defaultContext 53 >>= loadAndApplyTemplate "templates/default.html" defaultContext
43 >>= relativizeUrls 54 >>= relativizeUrls
44 55
45 math <- getMath "posts/*" mathTranslation'
46 forM_ math $ \(deps, mathStr) ->
47 rulesExtraDependencies (map IdentifierDependency deps) $
48 create [mathTranslation' mathStr] $ do
49 route idRoute
50 compile $ do
51 makeItem mathStr
52 >>= loadAndApplyTemplate "templates/math.tex" defaultContext
53 >>= withItemBody (unsafeCompiler . compileMath)
54
55 tags <- buildTags "posts/*" tagTranslation' >>= addTag "All Posts" "posts/*" 56 tags <- buildTags "posts/*" tagTranslation' >>= addTag "All Posts" "posts/*"
56 57
57 tagsRules tags $ \tag pattern -> do 58 tagsRules tags $ \tag pattern -> do
@@ -143,14 +144,14 @@ tagTranslation = mapMaybe charTrans
143 | isAlphaNum c = Just $ toLower c 144 | isAlphaNum c = Just $ toLower c
144 | otherwise = Nothing 145 | otherwise = Nothing
145 146
146mathTranslation' :: String -> Identifier
147mathTranslation' = fromCapture "math/*.svg" . map toLower . CBS.unpack . hex . SHA256.hash . CBS.pack
148
149addTag :: MonadMetadata m => String -> Pattern -> Tags -> m Tags 147addTag :: MonadMetadata m => String -> Pattern -> Tags -> m Tags
150addTag name pattern tags = do 148addTag name pattern tags = do
151 ids <- getMatches pattern 149 ids <- getMatches pattern
152 return $ tags { tagsMap = tagsMap tags ++ [(name, ids)] } 150 return $ tags { tagsMap = tagsMap tags ++ [(name, ids)] }
153 151
152mathTranslation' :: String -> Identifier
153mathTranslation' = fromCapture "math/*.svg" . map toLower . CBS.unpack . hex . SHA256.hash . CBS.pack
154
154getMath :: Pattern -> (String -> Identifier) -> Rules [([Identifier], String)] 155getMath :: Pattern -> (String -> Identifier) -> Rules [([Identifier], String)]
155getMath pattern makeId = do 156getMath pattern makeId = do
156 ids <- getMatches pattern 157 ids <- getMatches pattern
@@ -193,6 +194,22 @@ readPandoc' path = readFile path >>= either fail return . result'
193 194
194 addExt ro e = ro {readerExtensions = Set.insert e $ readerExtensions ro} 195 addExt ro e = ro {readerExtensions = Set.insert e $ readerExtensions ro}
195 196
197mathTransform :: Pandoc -> Compiler Pandoc
198mathTransform = walkM mathTransform'
199 where
200 mathTransform' :: Inline -> Compiler Inline
201 mathTransform' (Math mathType tex) = do
202 alignment <- loadSnapshotBody texId "alignment"
203 let
204 html = printf "<object data=\"/%s\" type=\"image/svg+xml\" style=\"vertical-align:-%s\">%s</object>"
205 (toFilePath texId) (alignment :: String) tex
206 return $ Span ("", [classOf mathType], []) [RawInline (Format "html") html]
207 where
208 texId = mathTranslation' tex
209 classOf DisplayMath = "display-math"
210 classOf InlineMath = "inline-math"
211 mathTransform' x = return x
212
196toFilePath' :: Identifier -> FilePath 213toFilePath' :: Identifier -> FilePath
197toFilePath' = (providerDirectory config </>) . toFilePath 214toFilePath' = (providerDirectory config </>) . toFilePath
198 215
diff --git a/tex/preamble.tex b/tex/preamble.tex
index 531506a..b916cf1 100644
--- a/tex/preamble.tex
+++ b/tex/preamble.tex
@@ -2,5 +2,3 @@
2 2
3\usepackage{amssymb} 3\usepackage{amssymb}
4\usepackage{amsmath} 4\usepackage{amsmath}
5\usepackage{amsthm}
6\usepackage{thmtools}