{-# LANGUAGE ExtendedDefaultRules, FlexibleContexts, LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses, NoMonomorphismRestriction #-} {-# LANGUAGE OverloadedStrings, PatternGuards, QuasiQuotes, ViewPatterns #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} module Main where import Control.Applicative ((<*)) import Control.Applicative ((<$>)) import Control.Effect (EffectState, evalState, get, modify) import Control.Effect (runEffect, runWriter, tell) import Control.Monad (forM_) import Control.Monad (void) import Control.Monad.Effect (Effect) import qualified Data.Text as T import Filesystem.Path.CurrentOS (encodeString) import Shelly (cd, cp, findWhen, hasExt, mkdir_p) import Shelly (pwd, shelly, withTmpDir, writefile) import Shelly ((<.>), ()) import Shelly (cmd) import Shelly (silently) import Shelly (liftIO) import Text.LaTeX (document, documentclass) import Text.LaTeX (execLaTeXM, render, usepackage) import Text.LaTeX (execLaTeXT) import Text.LaTeX.Base.Class (fromLaTeX) import Text.LaTeX.Base.Parser (parseLaTeX) import Text.LaTeX.Base.Syntax (LaTeX (..)) import Text.LaTeX.Base.Syntax (matchCommand) import Text.LaTeX.Base.Syntax (TeXArg (..)) import Text.LaTeX.QQ (hat, hat') import Text.LaTeX.Utils (stripTeX) import Text.Pandoc (Inline (..), MathType (..)) import Text.Pandoc (Pandoc (..), bottomUpM) import Text.Pandoc (Block (..)) import Text.Pandoc.JSON (toJSONFilter) default (T.Text, Double) main :: IO () main = toJSONFilter processMath newIdent :: EffectState Integer r => Effect r Integer newIdent = get <* modify succ toSVGName :: Integer -> String toSVGName n = encodeString $ "maths" ("math-" ++ show n) <.> "svg" splitDisplayMath :: Block -> [Block] splitDisplayMath = undefined data MathSetting = MathSetting { mathMode :: MathType , mathBody :: String } deriving (Read, Show, Eq, Ord) b2i :: Num a => Bool -> a b2i False = 0 b2i True = 1 mathToTag :: MathType -> Integer -> String -> Inline mathToTag mode n src = let labeled = either (const False) (not . null . matchCommand (`elem` ["tag", "label"])) $ parseLaTeX $ T.pack src indented = "&" `T.isInfixOf` T.pack src offset = b2i (labeled || indented) + b2i (mode == DisplayMath) linum = T.count "\\\\" (T.pack src) + 1 + offset in Span ("", [show mode], []) [RawInline "html" $ ""] data Math = Equation LaTeX | FitchProof LaTeX | GentzenProof LaTeX deriving (Show, Eq) processMath :: Pandoc -> IO Pandoc processMath pan = do let (pan', maths) = runEffect $ runWriter $ evalState 1 $ flip bottomUpM pan $ \case RawInline "latex" src | Right [hat'|\pboxy{\hask{lat}}|] <- parseLaTeX $ T.pack src , [hat'|$\hask{math}$|] <- stripTeX lat -> do n <- newIdent tell [(n, MathSetting InlineMath $ T.unpack $ render math)] return $ mathToTag InlineMath n src Math mode math -> do n <- newIdent tell [(n, MathSetting mode math)] return $ mathToTag mode n math i -> return i shelly $ do cwd <- pwd let dist = cwd "maths" mkdir_p dist src <- render <$> (execLaTeXT $ do documentclass ["leqno"] "bxjsarticle" usepackage ["active", "xetex", "tightpage"] "preview" usepackage [] "mymacros" usepackage [] "amsmath,amssymb" usepackage ["inline"] "enumitem" usepackage [] "bm" usepackage [] "zxjatype" usepackage ["kozuka4"] "zxjafont" usepackage [] "zxotf" usepackage [] "cases" usepackage [] "fitch" usepackage [] "picins" fromLaTeX [hat|\def\fCenter{\ \vdash\ }|] document $ forM_ maths $ \(_, MathSetting mode cont) -> do liftIO $ print cont let Right lat = parseLaTeX $ T.pack cont labeled = not (null $ matchCommand (`elem` ["tag", "label"]) lat) math | mode == InlineMath = [hat|$\hask{lat}$|] | TeXEnv "aligned" [] body <- lat = TeXEnv "minipage" [FixArg [hat|25 \jsZw|]] $ TeXEnv (if labeled then "align" else "align*") [] body | labeled = TeXEnv "minipage" [FixArg [hat|25 \jsZw|]] $ TeXEnv "align" [] lat | labeled = TeXEnv "minipage" [FixArg [hat|25 \jsZw|]] $ TeXEnv "align*" [] lat | otherwise = TeXEnv "minipage" [FixArg [hat|25 \jsZw|]] $ [hat|\[ \hask{lat} \]|] fromLaTeX $ TeXEnv "preview" [] math) let tmp = cwd "tmp" mkdir_p tmp withTmpDir $ \_tmp -> do let texfile = tmp "math.tex" cd tmp writefile texfile src void $ silently $ cmd "xelatex" "-shell-escape" "-halt-on-error" "-file-line-error" "-interaction=nonstopmode" texfile void $ cmd "pdf2svg" (tmp "math.pdf") (tmp "math-%d.svg") "all" mapM_ (flip cp dist) =<< findWhen (return . hasExt "svg") tmp return () return pan'