+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper
+mail.
+
+If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for
+the specific requirements.
+
+You should also get your employer (if you work as a programmer) or
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. For more information on this, and how to apply and follow
+the GNU AGPL, see .
diff --git a/gitea-markdown/Setup.hs b/gitea-markdown/Setup.hs
new file mode 100644
index 0000000..00bfe1f
--- /dev/null
+++ b/gitea-markdown/Setup.hs
@@ -0,0 +1,4 @@
+import Distribution.Simple
+
+main :: IO ()
+main = defaultMain
diff --git a/gitea-markdown/data-gitea-markdown.cabal b/gitea-markdown/data-gitea-markdown.cabal
new file mode 100644
index 0000000..9cf10ad
--- /dev/null
+++ b/gitea-markdown/data-gitea-markdown.cabal
@@ -0,0 +1,26 @@
+cabal-version: 3.6
+name: data-gitea-markdown
+version: 0.0.1
+author: Saku Laesvuori
+license: AGPL-3.0-or-later
+license-file: COPYING.md
+build-type: Simple
+stability: alpha
+
+executable gitea-markdown
+ build-depends:
+ base,
+ containers,
+ data-default,
+ pandoc,
+ pandoc-types,
+ tagsoup,
+ text
+ main-is: Main.hs
+ other-modules:
+ Gitea.Preview.Markdown
+ Gitea.Preview.Markdown.Math
+ Gitea.Preview.Markdown.Meta
+ Gitea.Preview.Markdown.Signatures
+ hs-source-dirs: src
+ default-language: Haskell2010
diff --git a/gitea-markdown/src/Gitea/Preview/Markdown.hs b/gitea-markdown/src/Gitea/Preview/Markdown.hs
new file mode 100644
index 0000000..dbee46c
--- /dev/null
+++ b/gitea-markdown/src/Gitea/Preview/Markdown.hs
@@ -0,0 +1,31 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Gitea.Preview.Markdown where
+
+import Data.Default (def)
+import Text.Pandoc (Pandoc, PandocError, ReaderOptions(..), readMarkdown, runPure, handleError, pandocExtensions, writeHtml5String)
+import Data.Text (Text)
+
+import qualified Data.Text.IO as T
+
+import Gitea.Preview.Markdown.Math
+import Gitea.Preview.Markdown.Meta
+import Gitea.Preview.Markdown.Signatures
+
+main :: IO ()
+main = do
+ input <- T.getContents
+ pandoc <- handleError $ readMD input
+ out <- handleError $ runPure $ writeHtml5String def $ pandocFilter pandoc
+ T.putStr out
+
+pandocFilter :: Pandoc -> Pandoc
+pandocFilter = renderSignatures'
+ . katexMath'
+ . renderMeta'
+
+readMD :: Text -> Either PandocError Pandoc
+readMD = runPure . readMarkdown def
+ { readerStandalone = True
+ , readerExtensions = pandocExtensions
+ }
diff --git a/gitea-markdown/src/Gitea/Preview/Markdown/Math.hs b/gitea-markdown/src/Gitea/Preview/Markdown/Math.hs
new file mode 100644
index 0000000..224a48f
--- /dev/null
+++ b/gitea-markdown/src/Gitea/Preview/Markdown/Math.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Gitea.Preview.Markdown.Math (katexMath, katexMath') where
+
+import Text.Pandoc (Inline(..), MathType(..), Format(..), Pandoc)
+import Text.Pandoc.Walk (walk)
+import Text.HTML.TagSoup (escapeHTML)
+
+import qualified Data.Text as T
+
+katexMath' :: Pandoc -> Pandoc
+katexMath' = walk katexMath
+
+katexMath :: Inline -> Inline
+katexMath (Math InlineMath txt) = RawInline (Format "html") $
+ "" <>
+ T.pack (escapeHTML (T.unpack txt)) <>
+ "
"
+katexMath (Math DisplayMath txt) = RawInline (Format "html") $
+ "" <>
+ T.pack (escapeHTML (T.unpack txt)) <>
+ "
\n"
+katexMath x = x
diff --git a/gitea-markdown/src/Gitea/Preview/Markdown/Meta.hs b/gitea-markdown/src/Gitea/Preview/Markdown/Meta.hs
new file mode 100644
index 0000000..ebb995b
--- /dev/null
+++ b/gitea-markdown/src/Gitea/Preview/Markdown/Meta.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Gitea.Preview.Markdown.Meta (renderMeta, renderMeta') where
+
+import Data.List (singleton)
+import Data.Map (toList)
+
+import Text.Pandoc.Definition
+
+renderMeta' :: Pandoc -> Pandoc
+renderMeta' (Pandoc meta blocks) = Pandoc meta (renderMeta (MetaMap $ unMeta meta) <> blocks)
+
+renderMeta :: MetaValue -> [Block]
+renderMeta (MetaMap metaMap) = singleton $ plainTable
+ (map (singleton . Plain . singleton . Str . fst))
+ (singleton . plainRow . map (renderMeta . snd))
+ (toList metaMap)
+renderMeta (MetaList metaValues) = singleton $ plainTable
+ (const [])
+ (map (plainRow . singleton . renderMeta))
+ metaValues
+renderMeta (MetaBool bool) = [Plain [Str $ if bool then "true" else "false"]]
+renderMeta (MetaString text) = [Plain [Str text]]
+renderMeta (MetaInlines inlines) = [Plain inlines]
+renderMeta (MetaBlocks blocks) = blocks
+
+plainTable :: ([a] -> [[Block]]) -> ([a] -> [Row]) -> [a] -> Block
+plainTable mkHead mkBody xs = Table
+ nullAttr
+ (Caption Nothing [])
+ (map (const $ (AlignDefault, ColWidthDefault)) xs)
+ (plainHead $ mkHead xs)
+ [plainBody $ mkBody xs]
+ (TableFoot nullAttr [])
+
+plainCell :: [Block] -> Cell
+plainCell = Cell nullAttr AlignDefault (RowSpan 1) (ColSpan 1)
+
+plainRow :: [[Block]] -> Row
+plainRow = Row nullAttr . map plainCell
+
+plainHead :: [[Block]] -> TableHead
+plainHead = TableHead nullAttr . singleton . plainRow
+
+plainBody :: [Row] -> TableBody
+plainBody = TableBody nullAttr (RowHeadColumns 0) []
diff --git a/gitea-markdown/src/Gitea/Preview/Markdown/Signatures.hs b/gitea-markdown/src/Gitea/Preview/Markdown/Signatures.hs
new file mode 100644
index 0000000..d809454
--- /dev/null
+++ b/gitea-markdown/src/Gitea/Preview/Markdown/Signatures.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ViewPatterns #-}
+
+module Gitea.Preview.Markdown.Signatures (renderSignatures, renderSignatures') where
+
+import Text.Pandoc (Pandoc, Block(..), Inline(..))
+import Text.Pandoc.Walk (walk)
+
+renderSignatures' :: Pandoc -> Pandoc
+renderSignatures' = walk renderSignatures
+
+renderSignatures :: Block -> Block
+renderSignatures (Div attr@(_, (elem "allekirjoittajat" -> True), _) blocks) =
+ Div attr $ Plain [Str "Pöytäkirjan vakuudeksi,", LineBreak] : blocks
+renderSignatures x = x
diff --git a/gitea-markdown/src/Main.hs b/gitea-markdown/src/Main.hs
new file mode 100644
index 0000000..3dbe301
--- /dev/null
+++ b/gitea-markdown/src/Main.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+import qualified Gitea.Preview.Markdown as MD
+
+main :: IO ()
+main = MD.main