git.iko.soy/typeable/pandoc
on master help sign in
Split out T.P.Writers.LaTeX.Util.
commit6fbdb487bdeebbaae799687c59f816a88051c74d authorJohn MacFarlane <jgm@berkeley.edu> dateTue 2 Mar 2021, 22:36 (6 years ago) parent190765f
3 files changed+286−243
pandoc.cabal+1 src/Text/Pandoc/Writers/LaTeX.hs+11−243 src/Text/Pandoc/Writers/LaTeX/Util.hs+274
pandoc.cabal+1
@@ -667,6 +667,7 @@ library
667667 Text.Pandoc.Writers.LaTeX.Lang,
668668 Text.Pandoc.Writers.LaTeX.Types,
669669 Text.Pandoc.Writers.LaTeX.Citation,
670+ Text.Pandoc.Writers.LaTeX.Util,
670671 Text.Pandoc.Writers.Markdown.Types,
671672 Text.Pandoc.Writers.Markdown.Inline,
672673 Text.Pandoc.Writers.Roff,
src/Text/Pandoc/Writers/LaTeX.hs+11−243
@@ -18,10 +18,9 @@ module Text.Pandoc.Writers.LaTeX (
1818 writeLaTeX
1919 , writeBeamer
2020 ) where
21−import Control.Applicative ((<|>))
2221import Control.Monad.State.Strict
23−import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isSpace, ord)
24−import Data.List (intersperse, nubBy, (\\), uncons)
22+import Data.Char (isDigit)
23+import Data.List (intersperse, nubBy, (\\))
2524import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe, isNothing)
2625import qualified Data.Map as M
2726import Data.Text (Text)
@@ -33,7 +32,7 @@ import Text.Pandoc.BCP47 (Lang (..), getLang, renderLang)
3332import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang)
3433import Text.Pandoc.Definition
3534import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight,
36− styleToLaTeX, toListingsLanguage)
35+ styleToLaTeX)
3736import Text.Pandoc.ImageSize
3837import Text.Pandoc.Logging
3938import Text.Pandoc.Options
@@ -46,11 +45,12 @@ import Text.Pandoc.Writers.LaTeX.Table (tableToLaTeX)
4645import Text.Pandoc.Writers.LaTeX.Citation (citationsToNatbib,
4746 citationsToBiblatex)
4847import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState (..), startingState)
49−import Text.Pandoc.Writers.LaTeX.Lang (toPolyglossiaEnv, toPolyglossia,
50− toBabel)
48+import Text.Pandoc.Writers.LaTeX.Lang (toPolyglossia, toBabel)
49+import Text.Pandoc.Writers.LaTeX.Util (stringToLaTeX, StringContext(..),
50+ toLabel, inCmd,
51+ wrapDiv, hypertarget, labelFor,
52+ getListingsLanguage, mbBraced)
5153import Text.Pandoc.Writers.Shared
52−import Text.Printf (printf)
53−import qualified Data.Text.Normalize as Normalize
5454import qualified Text.Pandoc.Writers.AnnotatedTable as Ann
5555
5656-- | Convert Pandoc to LaTeX.
@@ -244,152 +244,6 @@ pandocToLaTeX options (Pandoc meta blocks) = do
244244 Nothing -> main
245245 Just tpl -> renderTemplate tpl context'
246246
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−
393247toSlides :: PandocMonad m => [Block] -> LW m [Block]
394248toSlides bs = do
395249 opts <- gets stOptions
@@ -854,91 +708,6 @@ sectionHeader classes ident level lst = do
854708 braces txtNoNotes
855709 else empty
856710
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−
942711-- | Convert list of inline elements to LaTeX.
943712inlineListToLaTeX :: PandocMonad m
944713 => [Inline] -- ^ Inlines to convert
@@ -963,10 +732,6 @@ inlineListToLaTeX lst = hcat <$>
963732 fixInitialLineBreaks xs
964733 fixInitialLineBreaks xs = xs
965734
966−isQuoted :: Inline -> Bool
967−isQuoted (Quoted _ _) = True
968−isQuoted _ = False
969−
970735-- | Convert inline element to LaTeX
971736inlineToLaTeX :: PandocMonad m
972737 => Inline -- ^ Inline to convert
@@ -1102,6 +867,9 @@ inlineToLaTeX (Quoted qt lst) = do
1102867 if isEnabled Ext_smart opts
1103868 then char '`' <> inner <> char '\''
1104869 else char '\x2018' <> inner <> char '\x2019'
870+ where
871+ isQuoted (Quoted _ _) = True
872+ isQuoted _ = False
1105873inlineToLaTeX (Str str) = do
1106874 setEmptyLine False
1107875 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+