diff --git a/simplex.cabal b/simplex.cabal index c0710fa..5caa818 100644 --- a/simplex.cabal +++ b/simplex.cabal @@ -77,5 +77,5 @@ Executable simplex , mtl >= 2.0.1 , time >= 1.4 , random >= 1.0 - - + , text >= 1.0 + , MissingH >= 1.5.0 diff --git a/src/Simplex/CmdLineOpts.hs b/src/Simplex/CmdLineOpts.hs index 033a5b4..eb091c9 100644 --- a/src/Simplex/CmdLineOpts.hs +++ b/src/Simplex/CmdLineOpts.hs @@ -13,6 +13,8 @@ import Data.Maybe data Flag = Help | Verbose | Print | NoClean | Pdflatex String | Pdfcrop String | Graphviz String | Gnuplot String + | Ditaa String | PlantUML String + | Mermaid String | Java String | Watch Int | DryRun | Type String | Density Int | Quality Int | Crop | Convert String | Force | Version @@ -38,7 +40,11 @@ data Opts = Opts { optPdflatex :: String, optPdfcrop :: String, optGraphviz :: String, + optJava :: String, optGnuplot :: String, + optDitaa :: String, + optPlantUML :: String, + optMermaid :: String, optConvert :: String } @@ -62,6 +68,10 @@ defOpts = Opts { optPdfcrop = "pdfcrop", optGraphviz = "dot", optGnuplot = "gnuplot", + optJava = "java", + optDitaa = "ditaa.jar", + optPlantUML = "plantuml.jar", + optMermaid = "mmdc", optConvert = "convert" } @@ -74,12 +84,16 @@ cmdOpts = [ Option "p" ["print"] (NoArg Print) "Print processed tex to stdout.", Option "c" ["crop"] (NoArg Crop) "Crops the document so that no margins are left.", Option "f" ["force"] (NoArg Force) "Forces the creation of output files.", - Option "tT" ["type"] (ReqArg Type "") "Specify type of output (pdf, png, tex)", + Option "tT" ["type"] (ReqArg Type "") "Specify type of output (pdf, png, tex, html)", Option "x" ["pdflatex"] (ReqArg Pdflatex "") "Path to `pdflatex' executable", Option "k" ["pdfcrop"] (ReqArg Pdfcrop "") "Path to `pdfcrop'", Option "z" ["graphviz"] (ReqArg Graphviz "") "Path to `dot' (graphviz)", Option "g" ["gnuplot"] (ReqArg Gnuplot "") "Path to `gnuplot'", Option "m" ["convert"] (ReqArg Convert "") "Path to `convert' (ImageMagick)", + Option "" ["java"] (ReqArg Java "") "Path to `java'", + Option "" ["mmdc"] (ReqArg Mermaid "") "Path to `mmdc'", + Option "" ["ditaa"] (ReqArg Ditaa "") "Path to ditaai.jar", + Option "" ["plantuml"] (ReqArg PlantUML "") "Path to plantuml.jar", Option "w" ["watch"] (OptArg (Watch . read . fromMaybe "2000") "") "Watch files or folder (optionally amount of time in ms)", Option "3" ["three-times"] (NoArg ThreeTimes) "Execute `pdflatex' three times instead of the default two times.", Option "s" ["symbols"] (OptArg (ListSymbols . fromMaybe "\\%s\n") "") "Show a list of symboles known to simplex.", @@ -115,6 +129,10 @@ parseArgs = do Pdflatex c -> opts { optPdflatex = c } Pdfcrop c -> opts { optPdfcrop = c } Graphviz c -> opts { optGraphviz = c } + Java c -> opts { optJava = c } + Mermaid c -> opts { optMermaid = c } + Ditaa c -> opts { optDitaa = c } + PlantUML c -> opts { optPlantUML = c } Gnuplot c -> opts { optGnuplot = c } Convert c -> opts { optConvert = c } parseOpts opts _ = opts diff --git a/src/Simplex/Parser.hs b/src/Simplex/Parser.hs index dc47364..64fe022 100644 --- a/src/Simplex/Parser.hs +++ b/src/Simplex/Parser.hs @@ -82,6 +82,16 @@ data State = SStart | SNewline | SControl | SControlAfter | SSymbol | SSpace | S isBlock (TBlock _) = True isBlock _ = False +convertAlias :: String -> String +convertAlias s = + case s of + ".chapter" -> "!!" + ".part" -> "!!!" + ".section" -> "=" + ".subsection" -> "==" + ".subsubsection" -> "===" + _ -> s + parse :: [Token] -> Document parse = parse' $ Document [] [] @@ -314,7 +324,7 @@ lex' l c m SStart s@(x:xs) | otherwise = lex' l (c+1) [x] SControl xs lex' l c m SNewline s@(x:xs) - | x == '\n' = mkBlock m : lex' (l+1) 0 m SStart xs + | x == '\n' = mkBlock m : lex' (l+1) 0 [] SStart xs | x == ' ' = lex' l (c+1) m SSpace xs | x == '\t' = lex' l (c+1) ('\n':m) SSymbol xs | isAlpha x = mkBlock m : lex' l (c+1) [x] SCommand xs @@ -327,10 +337,10 @@ lex' l c m SCommand s@(x:xs) | otherwise = lex' l (c+1) (x:m) SCommand xs lex' l c m SControl s@(x:xs) - | x == ' ' && c < 4 = TControl (reverse m) : lex' l (c+1) [] SControlAfter xs - | x == ' ' = TControl (reverse m) : lex' l (c+1) [] SSymbol xs - | x == '\t' = TControl (reverse m) : lex' l (c+1) [] SSymbol xs - | x == '\n' = TControl (reverse m) : lex' (l+1) 0 [] SNewline xs + | x == ' ' && c < 4 = TControl (convertAlias (reverse m)) : lex' l (c+1) [] SControlAfter xs + | x == ' ' = TControl (convertAlias (reverse m)) : lex' l (c+1) [] SSymbol xs + | x == '\t' = TControl (convertAlias (reverse m)) : lex' l (c+1) [] SSymbol xs + | x == '\n' = TControl (convertAlias (reverse m)) : lex' (l+1) 0 [] SNewline xs | otherwise = lex' l (c+1) (x:m) SControl xs lex' l c m SControlAfter s@(x:xs) diff --git a/src/Simplex/Specials.hs b/src/Simplex/Specials.hs index 0e42012..2dc5d9a 100644 --- a/src/Simplex/Specials.hs +++ b/src/Simplex/Specials.hs @@ -35,32 +35,60 @@ processSpecials o s (Document b m) = do processSpecials' :: Opts -> Spec -> [Block] -> IO (Spec, [Block]) processSpecials' opts spec (BVerbatim "digraph" b : xs) = do - (spec', pdf) <- mkGraph "dot" "digraph" opts spec b + (spec', g) <- mkGraph "dot" "digraph" opts spec b (spec'', rest) <- processSpecials' opts spec' xs - return (spec'', (if null pdf + return (spec'', (if null g then BVerbatim "error" "Graphviz .digraph failed" - else BCommand "image" [pdf]) : rest) + else BCommand "image" [g]) : rest) processSpecials' opts spec (BVerbatim "graph" b : xs) = do - (spec', pdf) <- mkGraph "neato" "graph" opts spec b + (spec', g) <- mkGraph "neato" "graph" opts spec b (spec'', rest) <- processSpecials' opts spec' xs - return (spec'', (if null pdf + return (spec'', (if null g then BVerbatim "error" "Graphviz .graph failed" - else BCommand "image" [pdf]) : rest) + else BCommand "image" [g]) : rest) processSpecials' opts spec (BVerbatim "neato" b : xs) = do - (spec', pdf) <- mkGraph "neato" "" opts spec b + (spec', g) <- mkGraph "neato" "" opts spec b (spec'', rest) <- processSpecials' opts spec' xs - return (spec'', (if null pdf + return (spec'', (if null g then BVerbatim "error" "Graphviz .neato failed" - else BCommand "image" [pdf]) : rest) + else BCommand "image" [g]) : rest) processSpecials' opts spec (BVerbatim "dot" b : xs) = do - (spec', pdf) <- mkGraph "dot" "" opts spec b + (spec', g) <- mkGraph "dot" "" opts spec b (spec'', rest) <- processSpecials' opts spec' xs - return (spec'', (if null pdf + return (spec'', (if null g then BVerbatim "error" "Graphviz .dot failed" - else BCommand "image" [pdf]) : rest) + else BCommand "image" [g]) : rest) + +processSpecials' opts spec (BVerbatim "gnuplot" b : xs) = do + (spec', g) <- mkGraph "gnuplot" "" opts spec b + (spec'', rest) <- processSpecials' opts spec' xs + return (spec'', (if null g + then BVerbatim "error" "Gnuplot .gp failed" + else BCommand "image" [g]) : rest) + +processSpecials' opts spec (BVerbatim "mermaid" b : xs) = do + (spec', g) <- mkGraph "mermaid" "" opts spec b + (spec'', rest) <- processSpecials' opts spec' xs + return (spec'', (if null g + then BVerbatim "error" "Mermaid .mmd failed" + else BCommand "image" [g]) : rest ) + +processSpecials' opts spec (BVerbatim "ditaa" b : xs) = do + (spec', png) <- mkGraph "ditaa" "" opts spec b + (spec'', rest) <- processSpecials' opts spec' xs + return (spec'', (if null png + then BVerbatim "error" "ditaa .ditaa failed" + else BCommand "image" [png]) : rest ) + +processSpecials' opts spec (BVerbatim "plantuml" b : xs) = do + (spec', g) <- mkGraph "plantuml" "" opts spec b + (spec'', rest) <- processSpecials' opts spec' xs + return (spec'', (if null g + then BVerbatim "error" "plantuml .plantuml failed" + else BCommand "image" [g]) : rest ) processSpecials' opts spec (x : xs) = do (spec', rest) <- processSpecials' opts spec xs @@ -78,10 +106,60 @@ randomString n = do mkGraph e g opts spec c = do file <- randomString 10 - - let spec' = spec { sRemoveFiles = (file ++ ".pdf") : (file ++ ".dot") : sRemoveFiles spec } + case e of + "dot" -> mkGraphDot e file g opts spec c + "neato" -> mkGraphDot e file g opts spec c + "gnuplot" -> mkGraphGnuPlot e file g opts spec c + "mermaid" -> mkGraphMermaid e file g opts spec c + "ditaa" -> mkGraphDitaa e file g opts spec c + "plantuml" -> mkGraphPlantUML e file g opts spec c + _ -> return (spec, []) + + +mkGraphDot e file g opts spec c = do + let spec' = if (optType opts == "html") + then spec { sRemoveFiles = (file ++ ".dot") : sRemoveFiles spec } + else spec { sRemoveFiles = (file ++ ".jpeg") : (file ++ ".dot") : sRemoveFiles spec} writeFile (file ++ ".dot") (if null g then c else g ++ " G {\n" ++ c ++ "\n}\n") - r <- exec (optVerbose opts) (optGraphviz opts) ["-Tpdf", "-K" ++ e, file ++ ".dot", "-o" ++ file ++ ".pdf"] - return (spec', (either (const "") (const $ file ++ ".pdf") r)) + r <- exec (optVerbose opts) (optGraphviz opts) ["-Tjpeg", "-K" ++ e, file ++ ".dot", "-o" ++ file ++ ".jpeg"] + return (spec', (either (const "") (const $ file ++ ".jpeg") r)) + +mkGraphGnuPlot e file g opts spec c = do + let spec' = if (optType opts == "html" ) + then spec { sRemoveFiles = (file ++ ".gp") : sRemoveFiles spec } + else spec { sRemoveFiles = (file ++ ".jpeg") : (file ++ ".gp") : sRemoveFiles spec } + writeFile (file ++ ".gp") ("set terminal jpeg\n" ++ "set output \"" ++ file ++ ".jpeg" ++ "\"\n" ++c ++ "\n") + + r <- exec (optVerbose opts) (optGnuplot opts) [file ++ ".gp"] + return (spec', (either (const "") (const $ file ++ ".jpeg") r)) + +mkGraphMermaid e file g opts spec c = do + let spec' = if (optType opts == "html") + then spec { sRemoveFiles = (file ++ ".mmd") : sRemoveFiles spec} + else spec { sRemoveFiles = (file ++ ".png") : (file ++ ".mmd") : sRemoveFiles spec} + writeFile (file ++ ".mmd") (c ++ "\n") + + r <- exec (optVerbose opts) (optMermaid opts) ["-i", file ++ ".mmd" , "-o", file ++ ".png"] + + return (spec', (either (const "") (const $ file ++ ".png") r)) + +mkGraphDitaa e file g opts spec c = do + let spec' = if (optType opts == "html") + then spec { sRemoveFiles = (file ++ ".ditaa") : sRemoveFiles spec} + else spec { sRemoveFiles = (file ++ ".png") : (file ++ ".ditaa") : sRemoveFiles spec} + writeFile (file ++ ".ditaa") (c ++ "\n") + + r <- exec (optVerbose opts) (optJava opts) ["-jar", (optDitaa opts),file ++ ".ditaa", file ++ ".png"] + + return (spec', (either (const "") (const $ file ++ ".png") r)) + +mkGraphPlantUML e file g opts spec c = do + let spec' = if (optType opts == "html") + then spec { sRemoveFiles = (file ++ ".plantuml") : sRemoveFiles spec } + else spec { sRemoveFiles = (file ++ ".png") : (file ++ ".plantuml") : sRemoveFiles spec } + writeFile (file ++ ".plantuml") ("@startuml\n" ++ c ++ "\n@enduml\n") + r <- exec (optVerbose opts) (optJava opts) ["-jar", (optPlantUML opts), file ++ ".plantuml"] + + return (spec', (either (const "") (const $ file ++ ".png") r)) diff --git a/src/Simplex/ToTeX.hs b/src/Simplex/ToTeX.hs index 49de622..3abb2e9 100644 --- a/src/Simplex/ToTeX.hs +++ b/src/Simplex/ToTeX.hs @@ -1,6 +1,6 @@ {-# LANGUAGE Haskell2010 #-} -module Simplex.ToTeX (toTeX) where +module Simplex.ToTeX (toTeX, docProps) where import Simplex.Parser import Simplex.Config @@ -115,13 +115,45 @@ packages = [("inputenc", "\\usepackage[utf8]{inputenc}\n"), ] +docProps doc@(Document blocks props) = props + toTeX cfg doc@(Document blocks props) = concat $ preamble $ toTeX' cfg' $ blocks where cfg' = config cfg doc preamble xs = documentClass cfg' props + : "\\usepackage{iftex}\n" + : "\\ifPDFTeX" + : "\\usepackage{lmodern} % pdflatex or dvi latex\n" + : "\\usepackage[T1]{fontenc}\n" : "\\usepackage[utf8]{inputenc}\n" + : "\\else\n" + : "\\usepackage{fontspec} % XeLaTeX or LuaLaTeX\n" + : "\\fi\n" + : maybe + "" + (\x -> "\\usepackage[heading = true ]{ctex}\n") + (lookup "cjk" props) + : "\\usepackage[\n" + : "latexmk\n" + : "]{lwarp}\n" + : maybe + "" + (\x -> "\\setcounter{FileDepth}{" ++ x ++ "}\n") + (lookup "filedepth" props) + : maybe + "" + (\x -> "\\setcounter{SideTOCDepth}{" ++ x ++ "}\n") + (lookup "sidetocdepth" props) + : maybe + "" + (\x -> "\\boolfalse{FileSectionNames}\n") + (lookup "nofilesectionnames" props) + : maybe + "" + (\x -> "\\CSSFilename{" ++ x ++ "}\n") + (lookup "css" props) : maybe "" (\x -> "\\usepackage[" ++ x ++ "]{babel}\n") @@ -130,28 +162,31 @@ toTeX cfg doc@(Document blocks props) = concat $ preamble $ toTeX' cfg' $ blocks : "\\usepackage{fancyhdr}\n" : "\\usepackage{tabularx}\n" - : "\\usepackage{eurosym}\n" - : "\\DeclareUnicodeCharacter{20AC}{\\euro{}}\n" + : "%% \\usepackage{eurosym}\n" + : maybe + "%% \\DeclareUnicodeCharacter{20AC}{\\euro{}}\n" + (\x -> "") + (lookup "cjk" props) : "\\usepackage{amsmath}\n" : "\\usepackage{amsfonts}\n" : "\\usepackage{amssymb}\n" : "\\usepackage{stmaryrd}\n" - : "\\usepackage{wasysym}\n" + : "%% \\usepackage{wasysym}\n" - : "\\let\\EUR\\undefined\n" - : "\\usepackage{marvosym}\n" + : "%% \\let\\EUR\\undefined\n" + : "%% \\usepackage{marvosym}\n" : "\\usepackage{verbatim}\n" : "\\usepackage{listings}\n" : "\\usepackage{multicol}\n" : "\\usepackage[usenames,dvipsnames]{color}\n" - : "\\usepackage[table]{xcolor}\n" - : "\\usepackage{multirow}\n" + : "%% \\usepackage[table]{xcolor}\n" + : "%% \\usepackage{multirow}\n" - : "\\usepackage{lastpage}\n" + : "%% \\usepackage{lastpage}\n" : "\\usepackage{graphicx}\n" : maybe "\\usepackage[section]{placeins}\n" @@ -251,6 +286,10 @@ toTeX cfg doc@(Document blocks props) = concat $ preamble $ toTeX' cfg' $ blocks (\x -> "\\setcounter{tocdepth}{" ++ x ++ "}\n") (lookup "tocdepth" props) + : maybe + "\\newcommand{\\setkomavar}[2]{} %% FIXME: later\n" + (\x -> "") + (lookup "letter" props) : maybe "" (\x -> "\\setkomavar{fromaddress}{" ++ escapeTeX' "}\n" x) diff --git a/src/Simplex/Util.hs b/src/Simplex/Util.hs index 00a8343..f21881f 100644 --- a/src/Simplex/Util.hs +++ b/src/Simplex/Util.hs @@ -70,6 +70,7 @@ skipOneSpace s = s removeIfExists :: String -> IO () removeIfExists file = do + print $ "removing file " ++ file exists <- doesFileExist file when exists (removeFile file) diff --git a/src/simplex.hs b/src/simplex.hs index bb54bf3..87c11e2 100644 --- a/src/simplex.hs +++ b/src/simplex.hs @@ -15,6 +15,8 @@ import Text.Printf import Data.List (sort) import Data.Maybe import Data.Time +import Data.Text (pack, unpack) +import Data.List.Utils (replace) import System.Console.GetOpt (usageInfo) import System.Directory @@ -119,6 +121,9 @@ process opts file exit = do pdflatex = optPdflatex opts pdfcrop = optPdfcrop opts convert = optConvert opts + xelatex = replace "pdflatex" "xelatex" pdflatex + lwarpmk = replace "pdflatex" "lwarpmk" pdflatex + latexmk = replace "pdflatex" "latexmk" pdflatex pdfopts = ["-interaction=nonstopmode", "-file-line-error"] @@ -156,27 +161,45 @@ process opts file exit = do else (processSpecials opts newSpec $ parse tok) let tex = toTeX cfg tok' + + let latexopt = maybe + "-latex" + (\x -> "-xelatex") + (lookup "cjk" (docProps tok')) + let latex = maybe + pdflatex + (\x -> "xelatex") + (lookup "cjk" (docProps tok')) print' "." - when (optType opts == "tex" || not (optDryRun opts)) $ do + when (optType opts == "tex" || optType opts == "html" || not (optDryRun opts)) $ do -- write tex-file r <- liftIO $ try (writeFile (filename ++ ".tex") tex) _ <- either (throw . Exc) (return . const Ok) r print' "." - unless (optDryRun opts || optType opts == "tex") $ do - -- run pdflatex - r <- liftIO $ exec verbose pdflatex (pdfopts ++ [filename ++ ".tex"]) - _ <- either (throw' spec . Err . snd) (return . const Ok) r - print' "." - - -- run pdflatex a second time - _ <- liftIO $ exec verbose pdflatex (pdfopts ++ [filename ++ ".tex"]) - print' "." - - -- run pdflatex a third time if desired - when (optThreeTimes opts) $ do { liftIO $ exec verbose pdflatex (pdfopts ++ [filename ++ ".tex"]) - ; print' "." } + when ( optType opts == "html" ) $ do + r <- liftIO $ exec verbose latexmk ["-pdf", latexopt, filename ++ ".tex"] + _ <- either (throw' spec . Err . snd) (return . const Ok) r + print' "." + + r <- liftIO $ exec verbose lwarpmk ["html1"] + _ <- either (throw' spec . Err . snd) (return . const Ok) r + print' "." + + unless ( optType opts == "tex" || optType opts == "html" ) $ do + -- run pdflatex/xelatex + r <- liftIO $ exec verbose latex (pdfopts ++ [filename ++ ".tex"]) + _ <- either (throw' spec . Err . snd) (return . const Ok) r + print' "." + + -- run pdflatex/xelatex a second time + _ <- liftIO $ exec verbose latex (pdfopts ++ [filename ++ ".tex"]) + print' "." + + -- run pdflatex/xelatex a third time if desired + when (optThreeTimes opts) $ do { liftIO $ exec verbose latex (pdfopts ++ [filename ++ ".tex"]) + ; print' "." } -- clean files unless (optNoClean opts) (liftIO $ do { mapM_ removeIfExists (prepend dirtyExts) @@ -199,6 +222,9 @@ process opts file exit = do _ <- either (throw . Err . snd) (return . const Ok) r return () + when (filetype == "html") $ do + return () + print' " OK\n" when (optPrint opts) $ do