{-# LANGUAGE OverloadedStrings #-}
module Text.Pandoc.Writers.Roff (
WriterState(..)
, defaultWriterState
, MS
, Note
, EscapeMode(..)
, escapeString
, withFontFeature
) where
import Data.Char (ord, isAscii)
import Control.Monad.State.Strict
import qualified Data.Map as Map
import Data.Text (Text)
import qualified Data.Text as Text
import Data.String
import Data.Maybe (fromMaybe, isJust, catMaybes)
import Text.Pandoc.Class.PandocMonad (PandocMonad)
import Text.Pandoc.Definition
import Text.DocLayout
import Text.Printf (printf)
import Text.Pandoc.RoffChar (standardEscapes,
characterCodes, combiningAccents)
data WriterState = WriterState { WriterState -> Bool
stHasInlineMath :: Bool
, WriterState -> Bool
stFirstPara :: Bool
, WriterState -> [Note]
stNotes :: [Note]
, WriterState -> Bool
stSmallCaps :: Bool
, WriterState -> Bool
stHighlighting :: Bool
, :: Bool
, WriterState -> Map Char Bool
stFontFeatures :: Map.Map Char Bool
, WriterState -> Bool
stHasTables :: Bool
}
defaultWriterState :: WriterState
defaultWriterState :: WriterState
defaultWriterState = WriterState{ stHasInlineMath :: Bool
stHasInlineMath = Bool
False
, stFirstPara :: Bool
stFirstPara = Bool
True
, stNotes :: [Note]
stNotes = []
, stSmallCaps :: Bool
stSmallCaps = Bool
False
, stHighlighting :: Bool
stHighlighting = Bool
False
, stInHeader :: Bool
stInHeader = Bool
False
, stFontFeatures :: Map Char Bool
stFontFeatures = [(Char, Bool)] -> Map Char Bool
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [
(Char
'I',Bool
False)
, (Char
'B',Bool
False)
, (Char
'C',Bool
False)
, (Char
'V',Bool
False)
]
, stHasTables :: Bool
stHasTables = Bool
False
}
type Note = [Block]
type MS = StateT WriterState
data EscapeMode = AllowUTF8
| AsciiOnly
deriving Int -> EscapeMode -> ShowS
[EscapeMode] -> ShowS
EscapeMode -> String
(Int -> EscapeMode -> ShowS)
-> (EscapeMode -> String)
-> ([EscapeMode] -> ShowS)
-> Show EscapeMode
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> EscapeMode -> ShowS
showsPrec :: Int -> EscapeMode -> ShowS
$cshow :: EscapeMode -> String
show :: EscapeMode -> String
$cshowList :: [EscapeMode] -> ShowS
showList :: [EscapeMode] -> ShowS
Show
combiningAccentsMap :: Map.Map Char Text
combiningAccentsMap :: Map Char Text
combiningAccentsMap = [(Char, Text)] -> Map Char Text
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Char, Text)]
combiningAccents
essentialEscapes :: Map.Map Char Text
essentialEscapes :: Map Char Text
essentialEscapes = [(Char, Text)] -> Map Char Text
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Char, Text)]
standardEscapes
escapeString :: EscapeMode -> Text -> Text
escapeString :: EscapeMode -> Text -> Text
escapeString EscapeMode
e = [Text] -> Text
Text.concat ([Text] -> Text) -> (Text -> [Text]) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EscapeMode -> String -> [Text]
escapeString' EscapeMode
e (String -> [Text]) -> (Text -> String) -> Text -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
Text.unpack
where
escapeString' :: EscapeMode -> String -> [Text]
escapeString' EscapeMode
_ [] = []
escapeString' EscapeMode
escapeMode (Char
'\n':Char
'.':String
xs) =
Text
"\n\\&." Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: EscapeMode -> String -> [Text]
escapeString' EscapeMode
escapeMode String
xs
escapeString' EscapeMode
escapeMode (Char
x:String
xs) =
case Char -> Map Char Text -> Maybe Text
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Char
x Map Char Text
essentialEscapes of
Just Text
s -> Text
s Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: EscapeMode -> String -> [Text]
escapeString' EscapeMode
escapeMode String
xs
Maybe Text
Nothing
| Char -> Bool
isAscii Char
x -> Char -> Text
Text.singleton Char
x Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: EscapeMode -> String -> [Text]
escapeString' EscapeMode
escapeMode String
xs
| Bool
otherwise ->
case EscapeMode
escapeMode of
EscapeMode
AllowUTF8 -> Char -> Text
Text.singleton Char
x Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: EscapeMode -> String -> [Text]
escapeString' EscapeMode
escapeMode String
xs
EscapeMode
AsciiOnly ->
let accents :: [Text]
accents = [Maybe Text] -> [Text]
forall a. [Maybe a] -> [a]
catMaybes ([Maybe Text] -> [Text]) -> [Maybe Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ (Maybe Text -> Bool) -> [Maybe Text] -> [Maybe Text]
forall a. (a -> Bool) -> [a] -> [a]
takeWhile Maybe Text -> Bool
forall a. Maybe a -> Bool
isJust
((Char -> Maybe Text) -> String -> [Maybe Text]
forall a b. (a -> b) -> [a] -> [b]
map (Char -> Map Char Text -> Maybe Text
forall k a. Ord k => k -> Map k a -> Maybe a
`Map.lookup` Map Char Text
combiningAccentsMap) String
xs)
rest :: String
rest = Int -> ShowS
forall a. Int -> [a] -> [a]
drop ([Text] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Text]
accents) String
xs
s :: Text
s = case Char -> Map Char Text -> Maybe Text
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Char
x Map Char Text
characterCodeMap of
Just Text
t -> Text
"\\[" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [Text] -> Text
Text.unwords (Text
tText -> [Text] -> [Text]
forall a. a -> [a] -> [a]
:[Text]
accents) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"]"
Maybe Text
Nothing -> Text
"\\[" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [Text] -> Text
Text.unwords
(String -> Text
Text.pack (String -> Int -> String
forall r. PrintfType r => String -> r
printf String
"u%04X" (Char -> Int
ord Char
x)) Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: [Text]
accents) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"]"
in Text
s Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: EscapeMode -> String -> [Text]
escapeString' EscapeMode
escapeMode String
rest
characterCodeMap :: Map.Map Char Text
characterCodeMap :: Map Char Text
characterCodeMap = [(Char, Text)] -> Map Char Text
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Char, Text)]
characterCodes
fontChange :: (HasChars a, IsString a, PandocMonad m) => MS m (Doc a)
fontChange :: forall a (m :: * -> *).
(HasChars a, IsString a, PandocMonad m) =>
MS m (Doc a)
fontChange = do
Map Char Bool
features <- (WriterState -> Map Char Bool)
-> StateT WriterState m (Map Char Bool)
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets WriterState -> Map Char Bool
stFontFeatures
Bool
inHeader <- (WriterState -> Bool) -> StateT WriterState m Bool
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets WriterState -> Bool
stInHeader
let filling :: String
filling = [Char
'C' | Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Char -> Map Char Bool -> Maybe Bool
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Char
'C' Map Char Bool
features] String -> ShowS
forall a. [a] -> [a] -> [a]
++
[Char
'V' | Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Char -> Map Char Bool -> Maybe Bool
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Char
'V' Map Char Bool
features] String -> ShowS
forall a. [a] -> [a] -> [a]
++
[Char
'B' | Bool
inHeader Bool -> Bool -> Bool
||
Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Char -> Map Char Bool -> Maybe Bool
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Char
'B' Map Char Bool
features)] String -> ShowS
forall a. [a] -> [a] -> [a]
++
[Char
'I' | Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Char -> Map Char Bool -> Maybe Bool
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Char
'I' Map Char Bool
features]
Doc a -> MS m (Doc a)
forall a. a -> StateT WriterState m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Doc a -> MS m (Doc a)) -> Doc a -> MS m (Doc a)
forall a b. (a -> b) -> a -> b
$
case String
filling of
[] -> String -> Doc a
forall a. HasChars a => String -> Doc a
text String
"\\f[R]"
[Char
'C'] -> String -> Doc a
forall a. HasChars a => String -> Doc a
text String
"\\f[CR]"
String
_ -> String -> Doc a
forall a. HasChars a => String -> Doc a
text (String -> Doc a) -> String -> Doc a
forall a b. (a -> b) -> a -> b
$ String
"\\f[" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
filling String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"]"
withFontFeature :: (HasChars a, IsString a, PandocMonad m)
=> Char -> MS m (Doc a) -> MS m (Doc a)
withFontFeature :: forall a (m :: * -> *).
(HasChars a, IsString a, PandocMonad m) =>
Char -> MS m (Doc a) -> MS m (Doc a)
withFontFeature Char
c MS m (Doc a)
action = do
(WriterState -> WriterState) -> StateT WriterState m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((WriterState -> WriterState) -> StateT WriterState m ())
-> (WriterState -> WriterState) -> StateT WriterState m ()
forall a b. (a -> b) -> a -> b
$ \WriterState
st -> WriterState
st{ stFontFeatures = Map.adjust not c $ stFontFeatures st }
Doc a
begin <- MS m (Doc a)
forall a (m :: * -> *).
(HasChars a, IsString a, PandocMonad m) =>
MS m (Doc a)
fontChange
Doc a
d <- MS m (Doc a)
action
(WriterState -> WriterState) -> StateT WriterState m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((WriterState -> WriterState) -> StateT WriterState m ())
-> (WriterState -> WriterState) -> StateT WriterState m ()
forall a b. (a -> b) -> a -> b
$ \WriterState
st -> WriterState
st{ stFontFeatures = Map.adjust not c $ stFontFeatures st }
Doc a
end <- MS m (Doc a)
forall a (m :: * -> *).
(HasChars a, IsString a, PandocMonad m) =>
MS m (Doc a)
fontChange
Doc a -> MS m (Doc a)
forall a. a -> StateT WriterState m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Doc a -> MS m (Doc a)) -> Doc a -> MS m (Doc a)
forall a b. (a -> b) -> a -> b
$ Doc a
begin Doc a -> Doc a -> Doc a
forall a. Semigroup a => a -> a -> a
<> Doc a
d Doc a -> Doc a -> Doc a
forall a. Semigroup a => a -> a -> a
<> Doc a
end