git.iko.soy/typeable/pandoc
on master help sign in
Split out T.P.Writers.LaTeX.Util.
e8e5ffeJohn MacFarlane
pandoc.cabal+1
@@ -667,6 +667,7 @@ library
667 Text.Pandoc.Writers.LaTeX.Lang,
667 Text.Pandoc.Writers.LaTeX.Lang,
668 Text.Pandoc.Writers.LaTeX.Types,
668 Text.Pandoc.Writers.LaTeX.Types,
669 Text.Pandoc.Writers.LaTeX.Citation,
669 Text.Pandoc.Writers.LaTeX.Citation,
670+ Text.Pandoc.Writers.LaTeX.Util,
670 Text.Pandoc.Writers.Markdown.Types,
671 Text.Pandoc.Writers.Markdown.Types,
671 Text.Pandoc.Writers.Markdown.Inline,
672 Text.Pandoc.Writers.Markdown.Inline,
672 Text.Pandoc.Writers.Roff,
673 Text.Pandoc.Writers.Roff,
src/Text/Pandoc/Writers/LaTeX.hs+11−243
@@ -18,10 +18,9 @@ module Text.Pandoc.Writers.LaTeX (
18 writeLaTeX
18 writeLaTeX
19 , writeBeamer
19 , writeBeamer
20 ) where
20 ) where
21−import Control.Applicative ((<|>))
22import Control.Monad.State.Strict
21import Control.Monad.State.Strict
23−import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isSpace, ord)
22+import Data.Char (isDigit)
24−import Data.List (intersperse, nubBy, (\\), uncons)
23+import Data.List (intersperse, nubBy, (\\))
25import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe, isNothing)
24import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe, isNothing)
26import qualified Data.Map as M
25import qualified Data.Map as M
27import Data.Text (Text)
26import Data.Text (Text)
@@ -33,7 +32,7 @@ import Text.Pandoc.BCP47 (Lang (..), getLang, renderLang)
33import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang)
32import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang)
34import Text.Pandoc.Definition
33import Text.Pandoc.Definition
35import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight,
34import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight,
36− styleToLaTeX, toListingsLanguage)
35+ styleToLaTeX)
37import Text.Pandoc.ImageSize
36import Text.Pandoc.ImageSize
38import Text.Pandoc.Logging
37import Text.Pandoc.Logging
39import Text.Pandoc.Options
38import Text.Pandoc.Options
@@ -46,11 +45,12 @@ import Text.Pandoc.Writers.LaTeX.Table (tableToLaTeX)
46import Text.Pandoc.Writers.LaTeX.Citation (citationsToNatbib,
45import Text.Pandoc.Writers.LaTeX.Citation (citationsToNatbib,
47 citationsToBiblatex)
46 citationsToBiblatex)
48import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState (..), startingState)
47import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState (..), startingState)
49−import Text.Pandoc.Writers.LaTeX.Lang (toPolyglossiaEnv, toPolyglossia,
48+import Text.Pandoc.Writers.LaTeX.Lang (toPolyglossia, toBabel)
50− toBabel)
49+import Text.Pandoc.Writers.LaTeX.Util (stringToLaTeX, StringContext(..),
50+ toLabel, inCmd,
51+ wrapDiv, hypertarget, labelFor,
52+ getListingsLanguage, mbBraced)
51import Text.Pandoc.Writers.Shared
53import Text.Pandoc.Writers.Shared
52−import Text.Printf (printf)
53−import qualified Data.Text.Normalize as Normalize
54import qualified Text.Pandoc.Writers.AnnotatedTable as Ann
54import qualified Text.Pandoc.Writers.AnnotatedTable as Ann
55
55
56-- | Convert Pandoc to LaTeX.
56-- | Convert Pandoc to LaTeX.
@@ -244,152 +244,6 @@ pandocToLaTeX options (Pandoc meta blocks) = do
244 Nothing -> main
244 Nothing -> main
245 Just tpl -> renderTemplate tpl context'
245 Just tpl -> renderTemplate tpl context'
246
246
247−data StringContext = TextString
248− | URLString
249− | CodeString
250− deriving (Eq)
251−
252−-- escape things as needed for LaTeX
253−stringToLaTeX :: PandocMonad m => StringContext -> Text -> LW m Text
254−stringToLaTeX context zs = do
255− opts <- gets stOptions
256− return $ T.pack $
257− foldr (go opts context) mempty $ T.unpack $
258− if writerPreferAscii opts
259− then Normalize.normalize Normalize.NFD zs
260− else zs
261− where
262− go :: WriterOptions -> StringContext -> Char -> String -> String
263− go opts ctx x xs =
264− let ligatures = isEnabled Ext_smart opts && ctx == TextString
265− isUrl = ctx == URLString
266− mbAccentCmd =
267− if writerPreferAscii opts && ctx == TextString
268− then uncons xs >>= \(c,_) -> lookupAccent c
269− else Nothing
270− emits s =
271− case mbAccentCmd of
272− Just cmd ->
273− cmd <> "{" <> s <> "}" <> drop 1 xs -- drop combining accent
274− Nothing -> s <> xs
275− emitc c =
276− case mbAccentCmd of
277− Just cmd ->
278− cmd <> "{" <> [c] <> "}" <> drop 1 xs -- drop combining accent
279− Nothing -> c : xs
280− emitcseq cs =
281− case xs of
282− c:_ | isLetter c
283− , ctx == TextString
284− -> cs <> " " <> xs
285− | isSpace c -> cs <> "{}" <> xs
286− | ctx == TextString
287− -> cs <> xs
288− _ -> cs <> "{}" <> xs
289− emitquote cs =
290− case xs of
291− '`':_ -> cs <> "\\," <> xs -- add thin space
292− '\'':_ -> cs <> "\\," <> xs -- add thin space
293− _ -> cs <> xs
294− in case x of
295− '?' | ligatures -> -- avoid ?` ligature
296− case xs of
297− '`':_ -> emits "?{}"
298− _ -> emitc x
299− '!' | ligatures -> -- avoid !` ligature
300− case xs of
301− '`':_ -> emits "!{}"
302− _ -> emitc x
303− '{' -> emits "\\{"
304− '}' -> emits "\\}"
305− '`' | ctx == CodeString -> emitcseq "\\textasciigrave"
306− '$' | not isUrl -> emits "\\$"
307− '%' -> emits "\\%"
308− '&' -> emits "\\&"
309− '_' | not isUrl -> emits "\\_"
310− '#' -> emits "\\#"
311− '-' | not isUrl -> case xs of
312− -- prevent adjacent hyphens from forming ligatures
313− ('-':_) -> emits "-\\/"
314− _ -> emitc '-'
315− '~' | not isUrl -> emitcseq "\\textasciitilde"
316− '^' -> emits "\\^{}"
317− '\\'| isUrl -> emitc '/' -- NB. / works as path sep even on Windows
318− | otherwise -> emitcseq "\\textbackslash"
319− '|' | not isUrl -> emitcseq "\\textbar"
320− '<' -> emitcseq "\\textless"
321− '>' -> emitcseq "\\textgreater"
322− '[' -> emits "{[}" -- to avoid interpretation as
323− ']' -> emits "{]}" -- optional arguments
324− '\'' | ctx == CodeString -> emitcseq "\\textquotesingle"
325− '\160' -> emits "~"
326− '\x200B' -> emits "\\hspace{0pt}" -- zero-width space
327− '\x202F' -> emits "\\,"
328− '\x2026' -> emitcseq "\\ldots"
329− '\x2018' | ligatures -> emitquote "`"
330− '\x2019' | ligatures -> emitquote "'"
331− '\x201C' | ligatures -> emitquote "``"
332− '\x201D' | ligatures -> emitquote "''"
333− '\x2014' | ligatures -> emits "---"
334− '\x2013' | ligatures -> emits "--"
335− _ | writerPreferAscii opts
336− -> case x of
337− 'ı' -> emitcseq "\\i"
338− 'ȷ' -> emitcseq "\\j"
339− 'å' -> emitcseq "\\aa"
340− 'Å' -> emitcseq "\\AA"
341− 'ß' -> emitcseq "\\ss"
342− 'ø' -> emitcseq "\\o"
343− 'Ø' -> emitcseq "\\O"
344− 'Ł' -> emitcseq "\\L"
345− 'ł' -> emitcseq "\\l"
346− 'æ' -> emitcseq "\\ae"
347− 'Æ' -> emitcseq "\\AE"
348− 'œ' -> emitcseq "\\oe"
349− 'Œ' -> emitcseq "\\OE"
350− '£' -> emitcseq "\\pounds"
351− '€' -> emitcseq "\\euro"
352− '©' -> emitcseq "\\copyright"
353− _ -> emitc x
354− | otherwise -> emitc x
355−
356−lookupAccent :: Char -> Maybe String
357−lookupAccent '\779' = Just "\\H"
358−lookupAccent '\768' = Just "\\`"
359−lookupAccent '\769' = Just "\\'"
360−lookupAccent '\770' = Just "\\^"
361−lookupAccent '\771' = Just "\\~"
362−lookupAccent '\776' = Just "\\\""
363−lookupAccent '\775' = Just "\\."
364−lookupAccent '\772' = Just "\\="
365−lookupAccent '\781' = Just "\\|"
366−lookupAccent '\817' = Just "\\b"
367−lookupAccent '\807' = Just "\\c"
368−lookupAccent '\783' = Just "\\G"
369−lookupAccent '\777' = Just "\\h"
370−lookupAccent '\803' = Just "\\d"
371−lookupAccent '\785' = Just "\\f"
372−lookupAccent '\778' = Just "\\r"
373−lookupAccent '\865' = Just "\\t"
374−lookupAccent '\782' = Just "\\U"
375−lookupAccent '\780' = Just "\\v"
376−lookupAccent '\774' = Just "\\u"
377−lookupAccent '\808' = Just "\\k"
378−lookupAccent '\8413' = Just "\\textcircled"
379−lookupAccent _ = Nothing
380−
381−toLabel :: PandocMonad m => Text -> LW m Text
382−toLabel z = go `fmap` stringToLaTeX URLString z
383− where
384− go = T.concatMap $ \x -> case x of
385− _ | (isLetter x || isDigit x) && isAscii x -> T.singleton x
386− | x `elemText` "_-+=:;." -> T.singleton x
387− | otherwise -> T.pack $ "ux" <> printf "%x" (ord x)
388−
389−-- | Puts contents into LaTeX command.
390−inCmd :: Text -> Doc Text -> Doc Text
391−inCmd cmd contents = char '\\' <> literal cmd <> braces contents
392−
393toSlides :: PandocMonad m => [Block] -> LW m [Block]
247toSlides :: PandocMonad m => [Block] -> LW m [Block]
394toSlides bs = do
248toSlides bs = do
395 opts <- gets stOptions
249 opts <- gets stOptions
@@ -854,91 +708,6 @@ sectionHeader classes ident level lst = do
854 braces txtNoNotes
708 braces txtNoNotes
855 else empty
709 else empty
856
710
857−mapAlignment :: Text -> Text
858−mapAlignment a = case a of
859− "top" -> "T"
860− "top-baseline" -> "t"
861− "bottom" -> "b"
862− "center" -> "c"
863− _ -> a
864−
865−wrapDiv :: PandocMonad m => Attr -> Doc Text -> LW m (Doc Text)
866−wrapDiv (_,classes,kvs) t = do
867− beamer <- gets stBeamer
868− let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir
869− lang <- toLang $ lookup "lang" kvs
870− let wrapColumns = if beamer && "columns" `elem` classes
871− then \contents ->
872− let valign = maybe "T" mapAlignment (lookup "align" kvs)
873− totalwidth = maybe [] (\x -> ["totalwidth=" <> x])
874− (lookup "totalwidth" kvs)
875− onlytextwidth = filter ("onlytextwidth" ==) classes
876− options = text $ T.unpack $ T.intercalate "," $
877− valign : totalwidth ++ onlytextwidth
878− in inCmd "begin" "columns" <> brackets options
879− $$ contents
880− $$ inCmd "end" "columns"
881− else id
882− wrapColumn = if beamer && "column" `elem` classes
883− then \contents ->
884− let valign =
885− maybe ""
886− (brackets . text . T.unpack . mapAlignment)
887− (lookup "align" kvs)
888− w = maybe "0.48" fromPct (lookup "width" kvs)
889− in inCmd "begin" "column" <>
890− valign <>
891− braces (literal w <> "\\textwidth")
892− $$ contents
893− $$ inCmd "end" "column"
894− else id
895− fromPct xs =
896− case T.unsnoc xs of
897− Just (ds, '%') -> case safeRead ds of
898− Just digits -> showFl (digits / 100 :: Double)
899− Nothing -> xs
900− _ -> xs
901− wrapDir = case lookup "dir" kvs of
902− Just "rtl" -> align "RTL"
903− Just "ltr" -> align "LTR"
904− _ -> id
905− wrapLang txt = case lang of
906− Just lng -> let (l, o) = toPolyglossiaEnv lng
907− ops = if T.null o
908− then ""
909− else brackets $ literal o
910− in inCmd "begin" (literal l) <> ops
911− $$ blankline <> txt <> blankline
912− $$ inCmd "end" (literal l)
913− Nothing -> txt
914− return $ wrapColumns . wrapColumn . wrapDir . wrapLang $ t
915−
916−hypertarget :: PandocMonad m => Bool -> Text -> Doc Text -> LW m (Doc Text)
917−hypertarget _ "" x = return x
918−hypertarget addnewline ident x = do
919− ref <- literal `fmap` toLabel ident
920− return $ text "\\hypertarget"
921− <> braces ref
922− <> braces ((if addnewline && not (isEmpty x)
923− then "%" <> cr
924− else empty) <> x)
925−
926−labelFor :: PandocMonad m => Text -> LW m (Doc Text)
927−labelFor "" = return empty
928−labelFor ident = do
929− ref <- literal `fmap` toLabel ident
930− return $ text "\\label" <> braces ref
931−
932−-- Determine listings language from list of class attributes.
933−getListingsLanguage :: [Text] -> Maybe Text
934−getListingsLanguage xs
935− = foldr ((<|>) . toListingsLanguage) Nothing xs
936−
937−mbBraced :: Text -> Text
938−mbBraced x = if not (T.all isAlphaNum x)
939− then "{" <> x <> "}"
940− else x
941−
942-- | Convert list of inline elements to LaTeX.
711-- | Convert list of inline elements to LaTeX.
943inlineListToLaTeX :: PandocMonad m
712inlineListToLaTeX :: PandocMonad m
944 => [Inline] -- ^ Inlines to convert
713 => [Inline] -- ^ Inlines to convert
@@ -963,10 +732,6 @@ inlineListToLaTeX lst = hcat <$>
963 fixInitialLineBreaks xs
732 fixInitialLineBreaks xs
964 fixInitialLineBreaks xs = xs
733 fixInitialLineBreaks xs = xs
965
734
966−isQuoted :: Inline -> Bool
967−isQuoted (Quoted _ _) = True
968−isQuoted _ = False
969−
970-- | Convert inline element to LaTeX
735-- | Convert inline element to LaTeX
971inlineToLaTeX :: PandocMonad m
736inlineToLaTeX :: PandocMonad m
972 => Inline -- ^ Inline to convert
737 => Inline -- ^ Inline to convert
@@ -1102,6 +867,9 @@ inlineToLaTeX (Quoted qt lst) = do
1102 if isEnabled Ext_smart opts
867 if isEnabled Ext_smart opts
1103 then char '`' <> inner <> char '\''
868 then char '`' <> inner <> char '\''
1104 else char '\x2018' <> inner <> char '\x2019'
869 else char '\x2018' <> inner <> char '\x2019'
870+ where
871+ isQuoted (Quoted _ _) = True
872+ isQuoted _ = False
1105inlineToLaTeX (Str str) = do
873inlineToLaTeX (Str str) = do
1106 setEmptyLine False
874 setEmptyLine False
1107 liftM literal $ stringToLaTeX TextString str
875 liftM literal $ stringToLaTeX TextString str
src/Text/Pandoc/Writers/LaTeX/Util.hsnew file+274
@@ -0,0 +1,274 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{- |
3+ Module : Text.Pandoc.Writers.LaTeX.Util
4+ Copyright : Copyright (C) 2006-2021 John MacFarlane
5+ License : GNU GPL, version 2 or above
6+
7+ Maintainer : John MacFarlane <jgm@berkeley.edu>
8+ Stability : alpha
9+ Portability : portable
10+-}
11+module Text.Pandoc.Writers.LaTeX.Util (
12+ stringToLaTeX
13+ , StringContext(..)
14+ , toLabel
15+ , inCmd
16+ , wrapDiv
17+ , hypertarget
18+ , labelFor
19+ , getListingsLanguage
20+ , mbBraced
21+ )
22+where
23+
24+import Control.Applicative ((<|>))
25+import Text.Pandoc.Class (PandocMonad, toLang)
26+import Text.Pandoc.Options (WriterOptions(..), isEnabled)
27+import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState(..))
28+import Text.Pandoc.Writers.LaTeX.Lang (toPolyglossiaEnv)
29+import Text.Pandoc.Highlighting (toListingsLanguage)
30+import Text.DocLayout
31+import Text.Pandoc.Definition
32+import Text.Pandoc.ImageSize (showFl)
33+import Control.Monad.State.Strict (gets)
34+import Data.Text (Text)
35+import qualified Data.Text as T
36+import Text.Pandoc.Extensions (Extension(Ext_smart))
37+import Data.Char (isLetter, isSpace, isDigit, isAscii, ord, isAlphaNum)
38+import Text.Printf (printf)
39+import Text.Pandoc.Shared (safeRead, elemText)
40+import qualified Data.Text.Normalize as Normalize
41+import Data.List (uncons)
42+
43+data StringContext = TextString
44+ | URLString
45+ | CodeString
46+ deriving (Eq)
47+
48+-- escape things as needed for LaTeX
49+stringToLaTeX :: PandocMonad m => StringContext -> Text -> LW m Text
50+stringToLaTeX context zs = do
51+ opts <- gets stOptions
52+ return $ T.pack $
53+ foldr (go opts context) mempty $ T.unpack $
54+ if writerPreferAscii opts
55+ then Normalize.normalize Normalize.NFD zs
56+ else zs
57+ where
58+ go :: WriterOptions -> StringContext -> Char -> String -> String
59+ go opts ctx x xs =
60+ let ligatures = isEnabled Ext_smart opts && ctx == TextString
61+ isUrl = ctx == URLString
62+ mbAccentCmd =
63+ if writerPreferAscii opts && ctx == TextString
64+ then uncons xs >>= \(c,_) -> lookupAccent c
65+ else Nothing
66+ emits s =
67+ case mbAccentCmd of
68+ Just cmd ->
69+ cmd <> "{" <> s <> "}" <> drop 1 xs -- drop combining accent
70+ Nothing -> s <> xs
71+ emitc c =
72+ case mbAccentCmd of
73+ Just cmd ->
74+ cmd <> "{" <> [c] <> "}" <> drop 1 xs -- drop combining accent
75+ Nothing -> c : xs
76+ emitcseq cs =
77+ case xs of
78+ c:_ | isLetter c
79+ , ctx == TextString
80+ -> cs <> " " <> xs
81+ | isSpace c -> cs <> "{}" <> xs
82+ | ctx == TextString
83+ -> cs <> xs
84+ _ -> cs <> "{}" <> xs
85+ emitquote cs =
86+ case xs of
87+ '`':_ -> cs <> "\\," <> xs -- add thin space
88+ '\'':_ -> cs <> "\\," <> xs -- add thin space
89+ _ -> cs <> xs
90+ in case x of
91+ '?' | ligatures -> -- avoid ?` ligature
92+ case xs of
93+ '`':_ -> emits "?{}"
94+ _ -> emitc x
95+ '!' | ligatures -> -- avoid !` ligature
96+ case xs of
97+ '`':_ -> emits "!{}"
98+ _ -> emitc x
99+ '{' -> emits "\\{"
100+ '}' -> emits "\\}"
101+ '`' | ctx == CodeString -> emitcseq "\\textasciigrave"
102+ '$' | not isUrl -> emits "\\$"
103+ '%' -> emits "\\%"
104+ '&' -> emits "\\&"
105+ '_' | not isUrl -> emits "\\_"
106+ '#' -> emits "\\#"
107+ '-' | not isUrl -> case xs of
108+ -- prevent adjacent hyphens from forming ligatures
109+ ('-':_) -> emits "-\\/"
110+ _ -> emitc '-'
111+ '~' | not isUrl -> emitcseq "\\textasciitilde"
112+ '^' -> emits "\\^{}"
113+ '\\'| isUrl -> emitc '/' -- NB. / works as path sep even on Windows
114+ | otherwise -> emitcseq "\\textbackslash"
115+ '|' | not isUrl -> emitcseq "\\textbar"
116+ '<' -> emitcseq "\\textless"
117+ '>' -> emitcseq "\\textgreater"
118+ '[' -> emits "{[}" -- to avoid interpretation as
119+ ']' -> emits "{]}" -- optional arguments
120+ '\'' | ctx == CodeString -> emitcseq "\\textquotesingle"
121+ '\160' -> emits "~"
122+ '\x200B' -> emits "\\hspace{0pt}" -- zero-width space
123+ '\x202F' -> emits "\\,"
124+ '\x2026' -> emitcseq "\\ldots"
125+ '\x2018' | ligatures -> emitquote "`"
126+ '\x2019' | ligatures -> emitquote "'"
127+ '\x201C' | ligatures -> emitquote "``"
128+ '\x201D' | ligatures -> emitquote "''"
129+ '\x2014' | ligatures -> emits "---"
130+ '\x2013' | ligatures -> emits "--"
131+ _ | writerPreferAscii opts
132+ -> case x of
133+ 'ı' -> emitcseq "\\i"
134+ 'ȷ' -> emitcseq "\\j"
135+ 'å' -> emitcseq "\\aa"
136+ 'Å' -> emitcseq "\\AA"
137+ 'ß' -> emitcseq "\\ss"
138+ 'ø' -> emitcseq "\\o"
139+ 'Ø' -> emitcseq "\\O"
140+ 'Ł' -> emitcseq "\\L"
141+ 'ł' -> emitcseq "\\l"
142+ 'æ' -> emitcseq "\\ae"
143+ 'Æ' -> emitcseq "\\AE"
144+ 'œ' -> emitcseq "\\oe"
145+ 'Œ' -> emitcseq "\\OE"
146+ '£' -> emitcseq "\\pounds"
147+ '€' -> emitcseq "\\euro"
148+ '©' -> emitcseq "\\copyright"
149+ _ -> emitc x
150+ | otherwise -> emitc x
151+
152+lookupAccent :: Char -> Maybe String
153+lookupAccent '\779' = Just "\\H"
154+lookupAccent '\768' = Just "\\`"
155+lookupAccent '\769' = Just "\\'"
156+lookupAccent '\770' = Just "\\^"
157+lookupAccent '\771' = Just "\\~"
158+lookupAccent '\776' = Just "\\\""
159+lookupAccent '\775' = Just "\\."
160+lookupAccent '\772' = Just "\\="
161+lookupAccent '\781' = Just "\\|"
162+lookupAccent '\817' = Just "\\b"
163+lookupAccent '\807' = Just "\\c"
164+lookupAccent '\783' = Just "\\G"
165+lookupAccent '\777' = Just "\\h"
166+lookupAccent '\803' = Just "\\d"
167+lookupAccent '\785' = Just "\\f"
168+lookupAccent '\778' = Just "\\r"
169+lookupAccent '\865' = Just "\\t"
170+lookupAccent '\782' = Just "\\U"
171+lookupAccent '\780' = Just "\\v"
172+lookupAccent '\774' = Just "\\u"
173+lookupAccent '\808' = Just "\\k"
174+lookupAccent '\8413' = Just "\\textcircled"
175+lookupAccent _ = Nothing
176+
177+toLabel :: PandocMonad m => Text -> LW m Text
178+toLabel z = go `fmap` stringToLaTeX URLString z
179+ where
180+ go = T.concatMap $ \x -> case x of
181+ _ | (isLetter x || isDigit x) && isAscii x -> T.singleton x
182+ | x `elemText` "_-+=:;." -> T.singleton x
183+ | otherwise -> T.pack $ "ux" <> printf "%x" (ord x)
184+
185+-- | Puts contents into LaTeX command.
186+inCmd :: Text -> Doc Text -> Doc Text
187+inCmd cmd contents = char '\\' <> literal cmd <> braces contents
188+
189+mapAlignment :: Text -> Text
190+mapAlignment a = case a of
191+ "top" -> "T"
192+ "top-baseline" -> "t"
193+ "bottom" -> "b"
194+ "center" -> "c"
195+ _ -> a
196+
197+wrapDiv :: PandocMonad m => Attr -> Doc Text -> LW m (Doc Text)
198+wrapDiv (_,classes,kvs) t = do
199+ beamer <- gets stBeamer
200+ let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir
201+ lang <- toLang $ lookup "lang" kvs
202+ let wrapColumns = if beamer && "columns" `elem` classes
203+ then \contents ->
204+ let valign = maybe "T" mapAlignment (lookup "align" kvs)
205+ totalwidth = maybe [] (\x -> ["totalwidth=" <> x])
206+ (lookup "totalwidth" kvs)
207+ onlytextwidth = filter ("onlytextwidth" ==) classes
208+ options = text $ T.unpack $ T.intercalate "," $
209+ valign : totalwidth ++ onlytextwidth
210+ in inCmd "begin" "columns" <> brackets options
211+ $$ contents
212+ $$ inCmd "end" "columns"
213+ else id
214+ wrapColumn = if beamer && "column" `elem` classes
215+ then \contents ->
216+ let valign =
217+ maybe ""
218+ (brackets . text . T.unpack . mapAlignment)
219+ (lookup "align" kvs)
220+ w = maybe "0.48" fromPct (lookup "width" kvs)
221+ in inCmd "begin" "column" <>
222+ valign <>
223+ braces (literal w <> "\\textwidth")
224+ $$ contents
225+ $$ inCmd "end" "column"
226+ else id
227+ fromPct xs =
228+ case T.unsnoc xs of
229+ Just (ds, '%') -> case safeRead ds of
230+ Just digits -> showFl (digits / 100 :: Double)
231+ Nothing -> xs
232+ _ -> xs
233+ wrapDir = case lookup "dir" kvs of
234+ Just "rtl" -> align "RTL"
235+ Just "ltr" -> align "LTR"
236+ _ -> id
237+ wrapLang txt = case lang of
238+ Just lng -> let (l, o) = toPolyglossiaEnv lng
239+ ops = if T.null o
240+ then ""
241+ else brackets $ literal o
242+ in inCmd "begin" (literal l) <> ops
243+ $$ blankline <> txt <> blankline
244+ $$ inCmd "end" (literal l)
245+ Nothing -> txt
246+ return $ wrapColumns . wrapColumn . wrapDir . wrapLang $ t
247+
248+hypertarget :: PandocMonad m => Bool -> Text -> Doc Text -> LW m (Doc Text)
249+hypertarget _ "" x = return x
250+hypertarget addnewline ident x = do
251+ ref <- literal `fmap` toLabel ident
252+ return $ text "\\hypertarget"
253+ <> braces ref
254+ <> braces ((if addnewline && not (isEmpty x)
255+ then "%" <> cr
256+ else empty) <> x)
257+
258+labelFor :: PandocMonad m => Text -> LW m (Doc Text)
259+labelFor "" = return empty
260+labelFor ident = do
261+ ref <- literal `fmap` toLabel ident
262+ return $ text "\\label" <> braces ref
263+
264+-- Determine listings language from list of class attributes.
265+getListingsLanguage :: [Text] -> Maybe Text
266+getListingsLanguage xs
267+ = foldr ((<|>) . toListingsLanguage) Nothing xs
268+
269+mbBraced :: Text -> Text
270+mbBraced x = if not (T.all isAlphaNum x)
271+ then "{" <> x <> "}"
272+ else x
273+
274+