{-# LANGUAGE ViewPatterns, PatternGuards, PatternSynonyms #-} import Text.Pandoc.JSON import Text.Pandoc.Walk (walk) import Text.Pandoc.Options (def) import Text.Pandoc.Shared (stringify) import qualified Data.Map as M import Data.String.Utils (replace) #if MIN_VERSION_pandoc(2,0,0) import Text.Pandoc.Writers (Writer (..), getWriter) import Text.Pandoc.Class (runPure) import qualified Data.ByteString.Lazy.Char8 as C8L import qualified Data.Text as T import Control.Exception (displayException) #define MBPLAIN Plain #else import Text.Pandoc (Writer (..), getWriter) #define MBPLAIN Para #endif #if MIN_TOOL_VERSION_ghc(7,10,1) pattern Style :: String -> Inline #endif pattern Style x <- Math InlineMath x #if MIN_TOOL_VERSION_ghc(7,10,1) pattern Subst :: String -> Inline #endif pattern Subst x = Math InlineMath x #if MIN_TOOL_VERSION_ghc(7,10,1) pattern SubstVerbatim :: String -> Inline #endif pattern SubstVerbatim x <- Math DisplayMath x #if MIN_TOOL_VERSION_ghc(7,10,1) pattern Alt :: [Inline] -> [Inline] #endif pattern Alt x <- (dropWhile (== Space) -> x) type MMap = M.Map String MetaValue type PureInlineParams = ([Inline], Target) -- style:(alt, target) type InlineParams = (Inline, [Inline], Target) -- (style:alt, target) type InlineCons = [Inline] -> Target -> Inline -- Image or Link styleFromMeta :: Maybe Format -> Pandoc -> IO Pandoc styleFromMeta (Just fm) (Pandoc m bs) = do let b = unMeta m return $ Pandoc m $ walk (substInlineStyle fm b) $ -- apply styles for links and images walk (substBlockStyle fm b) bs -- apply para_style to paragraphs styleFromMeta _ p = return p substBlockStyle :: Format -> MMap -> Block -> Block substBlockStyle _ _ b@(Para is@(Image {} : _)) | all isImage is = b -- do not apply para_style to standalone images where isImage Image {} = True isImage _ = False substBlockStyle (Format fmt) m (Para cnt) | Just (MetaMap mm) <- M.lookup "para_style" m , Just (MetaBlocks [MBPLAIN [Span attr _]]) <- M.lookup fmt mm = Plain [Span attr cnt] substBlockStyle _ _ b = b substInlineStyle :: Format -> MMap -> Inline -> Inline substInlineStyle fm@(Format fmt) m i@(toInlineParams -> Just ((Style style, alt, tgt), cons)) | Just (MetaMap mm) <- M.lookup style m = let params = (alt, tgt) substPlainParams = Span nullAttr . map (substParams fm params) substInlineStyle' (Just (MetaBlocks [CodeBlock _ vb])) = RawInline fm $ substParamsInRawBlock fm params vb substInlineStyle' (Just (MetaBlocks mbs)) = RawInline fm $ renderInlines fm $ map substInlineStyle'' mbs where substInlineStyle'' mb = case mb of Plain is -> substPlainParams is Para is -> substPlainParams is d@Div {} -> RawInline fm $ substParamsInRawBlock fm params $ renderBlocks fm [d] RawBlock bfm b -> RawInline bfm $ substParamsInRawBlock bfm params b _ -> i substInlineStyle' Nothing = cons alt tgt substInlineStyle' _ = i in substInlineStyle' $ M.lookup fmt mm substInlineStyle _ _ i = i toInlineParams :: Inline -> Maybe (InlineParams, InlineCons) toInlineParams (Image attr (style@(Style _) : Alt alt) tgt) = Just ((style, alt, tgt), Image attr) toInlineParams (Link attr (style@(Style _) : Alt alt) tgt) = Just ((style, alt, tgt), Link attr) toInlineParams _ = Nothing substParams :: Format -> PureInlineParams -> Inline -> Inline substParams _ (alt, _) (Subst "ALT") = Span nullAttr alt substParams fm params (SubstVerbatim "ALT") = RawInline fm $ stringify $ substParams fm params $ Subst "ALT" substParams _ (_, (src, _)) (Subst "SRC") = Str src substParams fm (_, (src, _)) (SubstVerbatim "SRC") = RawInline fm src substParams _ (_, (_, title)) (Subst "TITLE") = Str title substParams fm (_, (_, title)) (SubstVerbatim "TITLE") = RawInline fm title substParams _ params (RawInline fm s) = RawInline fm $ substParamsInRawBlock fm params s substParams _ _ i = i substParamsInRawBlock :: Format -> PureInlineParams -> String -> String substParamsInRawBlock fm (alt, (src, title)) s = foldr (\(p, is) -> replace p $ renderInlines fm is) s [("$ALT$", alt ) ,("$SRC$", [Str src] ) ,("$TITLE$", [Str title] ) ,("$$ALT$$", [RawInline fm $ stringify $ Span nullAttr alt]) ,("$$SRC$$", [RawInline fm src] ) ,("$$TITLE$$", [RawInline fm title] ) ] renderBlocks :: Format -> [Block] -> String renderBlocks fm p = let fmt = toWriterFormat fm writer = getWriter fmt doc = Pandoc (Meta M.empty) p in case writer of Left _ -> error $ "Unknown format " ++ fmt #if MIN_VERSION_pandoc(2,0,0) Right (TextWriter w, _) -> case runPure $ w def doc of Left e -> displayException e Right r -> T.unpack r Right (ByteStringWriter w, _) -> case runPure $ w def doc of Left e -> displayException e Right r -> C8L.unpack r #else Right (PureStringWriter w) -> w def doc _ -> error $ "Unsupported format " ++ fmt ++ ", try Pandoc 2.0!" #endif renderInlines :: Format -> [Inline] -> String renderInlines fm p = renderBlocks fm [Plain p] toWriterFormat :: Format -> String toWriterFormat (Format "tex") = "latex" toWriterFormat (Format fmt) = fmt main :: IO () main = toJSONFilter styleFromMeta