@@ -38,7 +38,7 @@ module Text.Pandoc.Readers.LaTeX ( readLaTeX,
3838import Control.Applicative (many, optional, (<|>))
3939import Control.Monad
4040import Control.Monad.Except (throwError)
41−import Data.Char (chr, isAlphaNum, isLetter, ord)
41+import Data.Char (chr, isAlphaNum, isLetter, ord, isDigit)
4242import Data.Text (Text, unpack)
4343import qualified Data.Text as T
4444import Data.List (intercalate, isPrefixOf)
@@ -54,7 +54,7 @@ import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension)
5454import Text.Pandoc.ImageSize (numUnit, showFl)
5555import Text.Pandoc.Logging
5656import Text.Pandoc.Options
57−import Text.Pandoc.Parsing hiding (many, optional,
57+import Text.Pandoc.Parsing hiding (many, optional, withRaw,
5858 space, (<|>), spaces, blankline)
5959import Text.Pandoc.Shared
6060import Text.Pandoc.Walk
@@ -288,12 +288,25 @@ bracketed parser = try $ do
288288 symbol '['
289289 mconcat <$> manyTill parser (symbol ']')
290290
291+dimenarg :: PandocMonad m => LP m Text
292+dimenarg = try $ do
293+ ch <- option False $ True <$ symbol '='
294+ Tok _ _ s <- satisfyTok isWordTok
295+ guard $ (T.take 2 (T.reverse s)) `elem`
296+ ["pt","pc","in","bp","cm","mm","dd","cc","sp"]
297+ let num = T.take (T.length s - 2) s
298+ guard $ T.length num > 0
299+ guard $ T.all isDigit num
300+ return $ T.pack ['=' | ch] <> s
301+
291302-- inline elements:
292303
293304inlineText :: PandocMonad m => LP m Inlines
294305inlineText = (str . T.unpack . untoken) <$> satisfyTok isWordTok
295− where isWordTok (Tok _ Word _) = True
296− isWordTok _ = False
306+
307+isWordTok :: Tok -> Bool
308+isWordTok (Tok _ Word _) = True
309+isWordTok _ = False
297310
298311inlineGroup :: PandocMonad m => LP m Inlines
299312inlineGroup = do
@@ -365,12 +378,294 @@ dollarsMath = do
365378 let constructor = if display then displayMath else math
366379 return $ constructor $ T.unpack $ untokenize contents
367380
381+inlineCommand' :: PandocMonad m => LP m Inlines
382+inlineCommand' = try $ do
383+ cs@(Tok _ CtrlSeq t) <- anyControlSeq
384+ let name = T.drop 1 t
385+ guard $ name /= "begin" && name /= "end"
386+ mbstar <- option Nothing (Just <$> symbol '*')
387+ let names = name : maybe [] (const [name <> "*"]) mbstar
388+ optional $ try $ symbol '{' >> symbol '}'
389+ let raw = do
390+ guard $ not (isBlockCommand name)
391+ (_, rawargs) <- withRaw
392+ (skipangles *> skipopts *> option "" dimenarg *> many braced)
393+ let rawcommand = T.unpack $ untokenize $
394+ cs : maybe [] (:[]) mbstar ++ rawargs
395+ (guardEnabled Ext_raw_tex >> return (rawInline "latex" rawcommand))
396+ <|> ignore rawcommand
397+ -- transformed <- applyMacros' rawcommand -- TODO or get rid of
398+ -- exts <- getOption readerExtensions
399+ -- if transformed /= rawcommand
400+ -- then parseFromString' inlines transformed
401+ -- else if extensionEnabled Ext_raw_tex exts
402+ -- then return $ rawInline "latex" rawcommand
403+ -- else ignore rawcommand
404+ lookupListDefault raw names inlineCommands
405+
406+tok :: PandocMonad m => LP m Inlines
407+tok = try $ grouped inline <|> inlineCommand'
408+ <|> (str . T.unpack . untoken) <$> anyTok
409+ -- note: this will swallow a whole word in cases
410+ -- where latex would only consider one letter (TODO?)
411+
412+opt :: PandocMonad m => LP m Inlines
413+opt = bracketed inline
414+
415+rawopt :: PandocMonad m => LP m Text
416+rawopt = do
417+ symbol '['
418+ inner <- untokenize <$> manyTill anyTok (symbol ']')
419+ spaces
420+ return $ "[" <> inner <> "]"
421+
422+skipopts :: PandocMonad m => LP m ()
423+skipopts = skipMany rawopt
424+
425+-- opts in angle brackets are used in beamer
426+rawangle :: PandocMonad m => LP m ()
427+rawangle = try $ do
428+ symbol '<'
429+ () <$ manyTill anyTok (symbol '>')
430+
431+skipangles :: PandocMonad m => LP m ()
432+skipangles = skipMany rawangle
433+
434+ignore :: (Monoid a, PandocMonad m) => String -> ParserT s u m a
435+ignore raw = do
436+ pos <- getPosition
437+ report $ SkippedContent raw pos
438+ return mempty
439+
440+withRaw :: PandocMonad m => LP m a -> LP m (a, [Tok])
441+withRaw parser = do
442+ pos1 <- getPosition
443+ inp <- getInput
444+ result <- parser
445+ nxt <- option (Tok (0,0) Word "") (lookAhead anyTok)
446+ let raw = takeWhile (/= nxt) inp
447+ return (result, raw)
448+
449+inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines)
450+inlineCommands = M.fromList
451+ [ ("emph", extractSpaces emph <$> tok)
452+ ]
453+
454+isBlockCommand :: Text -> Bool
455+isBlockCommand s =
456+ s `M.member` (blockCommands :: M.Map Text (LP PandocPure Blocks))
457+
458+lookupListDefault :: (Ord k) => v -> [k] -> M.Map k v -> v
459+lookupListDefault d = (fromMaybe d .) . lookupList
460+ where
461+ lookupList l m = msum $ map (`M.lookup` m) l
462+
463+{-
464+rawInlineOr :: PandocMonad m => String -> LP m Inlines -> LP m Inlines
465+rawInlineOr name' fallback = do
466+ parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions
467+ if parseRaw
468+ then rawInline "latex" <$> getRawCommand name'
469+ else fallback
470+
471+isBlockCommand :: String -> Bool
472+isBlockCommand s = s `M.member` (blockCommands :: M.Map String (LP PandocPure Blocks))
473+
474+inlineCommands :: PandocMonad m => M.Map String (LP m Inlines)
475+inlineCommands = M.fromList $
476+ [ ("emph", extractSpaces emph <$> tok)
477+ , ("textit", extractSpaces emph <$> tok)
478+ , ("textsl", extractSpaces emph <$> tok)
479+ , ("textsc", extractSpaces smallcaps <$> tok)
480+ , ("textsf", extractSpaces (spanWith ("",["sans-serif"],[])) <$> tok)
481+ , ("textmd", extractSpaces (spanWith ("",["medium"],[])) <$> tok)
482+ , ("textrm", extractSpaces (spanWith ("",["roman"],[])) <$> tok)
483+ , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok)
484+ , ("texttt", ttfamily)
485+ , ("sout", extractSpaces strikeout <$> tok)
486+ , ("textsuperscript", extractSpaces superscript <$> tok)
487+ , ("textsubscript", extractSpaces subscript <$> tok)
488+ , ("textbackslash", lit "\\")
489+ , ("backslash", lit "\\")
490+ , ("slash", lit "/")
491+ , ("textbf", extractSpaces strong <$> tok)
492+ , ("textnormal", extractSpaces (spanWith ("",["nodecor"],[])) <$> tok)
493+ , ("ldots", lit "…")
494+ , ("vdots", lit "\8942")
495+ , ("dots", lit "…")
496+ , ("mdots", lit "…")
497+ , ("sim", lit "~")
498+ , ("label", rawInlineOr "label" (inBrackets <$> tok))
499+ , ("ref", rawInlineOr "ref" (inBrackets <$> tok))
500+ , ("textgreek", tok)
501+ , ("sep", lit ",")
502+ , ("cref", rawInlineOr "cref" (inBrackets <$> tok)) -- from cleveref.sty
503+ , ("(", mathInline $ manyTill anyChar (try $ string "\\)"))
504+ , ("[", mathDisplay $ manyTill anyChar (try $ string "\\]"))
505+ , ("ensuremath", mathInline braced)
506+ , ("texorpdfstring", (\_ x -> x) <$> tok <*> tok)
507+ , ("P", lit "¶")
508+ , ("S", lit "§")
509+ , ("$", lit "$")
510+ , ("%", lit "%")
511+ , ("&", lit "&")
512+ , ("#", lit "#")
513+ , ("_", lit "_")
514+ , ("{", lit "{")
515+ , ("}", lit "}")
516+ -- old TeX commands
517+ , ("em", extractSpaces emph <$> inlines)
518+ , ("it", extractSpaces emph <$> inlines)
519+ , ("sl", extractSpaces emph <$> inlines)
520+ , ("bf", extractSpaces strong <$> inlines)
521+ , ("rm", inlines)
522+ , ("itshape", extractSpaces emph <$> inlines)
523+ , ("slshape", extractSpaces emph <$> inlines)
524+ , ("scshape", extractSpaces smallcaps <$> inlines)
525+ , ("bfseries", extractSpaces strong <$> inlines)
526+ , ("/", pure mempty) -- italic correction
527+ , ("aa", lit "å")
528+ , ("AA", lit "Å")
529+ , ("ss", lit "ß")
530+ , ("o", lit "ø")
531+ , ("O", lit "Ø")
532+ , ("L", lit "Ł")
533+ , ("l", lit "ł")
534+ , ("ae", lit "æ")
535+ , ("AE", lit "Æ")
536+ , ("oe", lit "œ")
537+ , ("OE", lit "Œ")
538+ , ("pounds", lit "£")
539+ , ("euro", lit "€")
540+ , ("copyright", lit "©")
541+ , ("textasciicircum", lit "^")
542+ , ("textasciitilde", lit "~")
543+ , ("H", try $ tok >>= accent hungarumlaut)
544+ , ("`", option (str "`") $ try $ tok >>= accent grave)
545+ , ("'", option (str "'") $ try $ tok >>= accent acute)
546+ , ("^", option (str "^") $ try $ tok >>= accent circ)
547+ , ("~", option (str "~") $ try $ tok >>= accent tilde)
548+ , ("\"", option (str "\"") $ try $ tok >>= accent umlaut)
549+ , (".", option (str ".") $ try $ tok >>= accent dot)
550+ , ("=", option (str "=") $ try $ tok >>= accent macron)
551+ , ("c", option (str "c") $ try $ tok >>= accent cedilla)
552+ , ("v", option (str "v") $ try $ tok >>= accent hacek)
553+ , ("u", option (str "u") $ try $ tok >>= accent breve)
554+ , ("i", lit "i")
555+ , ("\\", linebreak <$ (optional (bracketed inline) *> spaces'))
556+ , (",", lit "\8198")
557+ , ("@", pure mempty)
558+ , (" ", lit "\160")
559+ , ("ps", pure $ str "PS." <> space)
560+ , ("TeX", lit "TeX")
561+ , ("LaTeX", lit "LaTeX")
562+ , ("bar", lit "|")
563+ , ("textless", lit "<")
564+ , ("textgreater", lit ">")
565+ , ("thanks", note <$> grouped block)
566+ , ("footnote", note <$> grouped block)
567+ , ("verb", doverb)
568+ , ("lstinline", dolstinline)
569+ , ("Verb", doverb)
570+ , ("url", (unescapeURL <$> braced) >>= \url ->
571+ pure (link url "" (str url)))
572+ , ("href", (unescapeURL <$> braced <* optional sp) >>= \url ->
573+ tok >>= \lab ->
574+ pure (link url "" lab))
575+ , ("includegraphics", do options <- option [] keyvals
576+ src <- unescapeURL . removeDoubleQuotes <$> braced
577+ mkImage options src)
578+ , ("enquote", enquote)
579+ , ("cite", citation "cite" NormalCitation False)
580+ , ("Cite", citation "Cite" NormalCitation False)
581+ , ("citep", citation "citep" NormalCitation False)
582+ , ("citep*", citation "citep*" NormalCitation False)
583+ , ("citeal", citation "citeal" NormalCitation False)
584+ , ("citealp", citation "citealp" NormalCitation False)
585+ , ("citealp*", citation "citealp*" NormalCitation False)
586+ , ("autocite", citation "autocite" NormalCitation False)
587+ , ("smartcite", citation "smartcite" NormalCitation False)
588+ , ("footcite", inNote <$> citation "footcite" NormalCitation False)
589+ , ("parencite", citation "parencite" NormalCitation False)
590+ , ("supercite", citation "supercite" NormalCitation False)
591+ , ("footcitetext", inNote <$> citation "footcitetext" NormalCitation False)
592+ , ("citeyearpar", citation "citeyearpar" SuppressAuthor False)
593+ , ("citeyear", citation "citeyear" SuppressAuthor False)
594+ , ("autocite*", citation "autocite*" SuppressAuthor False)
595+ , ("cite*", citation "cite*" SuppressAuthor False)
596+ , ("parencite*", citation "parencite*" SuppressAuthor False)
597+ , ("textcite", citation "textcite" AuthorInText False)
598+ , ("citet", citation "citet" AuthorInText False)
599+ , ("citet*", citation "citet*" AuthorInText False)
600+ , ("citealt", citation "citealt" AuthorInText False)
601+ , ("citealt*", citation "citealt*" AuthorInText False)
602+ , ("textcites", citation "textcites" AuthorInText True)
603+ , ("cites", citation "cites" NormalCitation True)
604+ , ("autocites", citation "autocites" NormalCitation True)
605+ , ("footcites", inNote <$> citation "footcites" NormalCitation True)
606+ , ("parencites", citation "parencites" NormalCitation True)
607+ , ("supercites", citation "supercites" NormalCitation True)
608+ , ("footcitetexts", inNote <$> citation "footcitetexts" NormalCitation True)
609+ , ("Autocite", citation "Autocite" NormalCitation False)
610+ , ("Smartcite", citation "Smartcite" NormalCitation False)
611+ , ("Footcite", citation "Footcite" NormalCitation False)
612+ , ("Parencite", citation "Parencite" NormalCitation False)
613+ , ("Supercite", citation "Supercite" NormalCitation False)
614+ , ("Footcitetext", inNote <$> citation "Footcitetext" NormalCitation False)
615+ , ("Citeyearpar", citation "Citeyearpar" SuppressAuthor False)
616+ , ("Citeyear", citation "Citeyear" SuppressAuthor False)
617+ , ("Autocite*", citation "Autocite*" SuppressAuthor False)
618+ , ("Cite*", citation "Cite*" SuppressAuthor False)
619+ , ("Parencite*", citation "Parencite*" SuppressAuthor False)
620+ , ("Textcite", citation "Textcite" AuthorInText False)
621+ , ("Textcites", citation "Textcites" AuthorInText True)
622+ , ("Cites", citation "Cites" NormalCitation True)
623+ , ("Autocites", citation "Autocites" NormalCitation True)
624+ , ("Footcites", citation "Footcites" NormalCitation True)
625+ , ("Parencites", citation "Parencites" NormalCitation True)
626+ , ("Supercites", citation "Supercites" NormalCitation True)
627+ , ("Footcitetexts", inNote <$> citation "Footcitetexts" NormalCitation True)
628+ , ("citetext", complexNatbibCitation NormalCitation)
629+ , ("citeauthor", (try (tok *> optional sp *> controlSeq "citetext") *>
630+ complexNatbibCitation AuthorInText)
631+ <|> citation "citeauthor" AuthorInText False)
632+ , ("nocite", mempty <$ (citation "nocite" NormalCitation False >>=
633+ addMeta "nocite"))
634+ , ("hypertarget", braced >> tok)
635+ -- siuntix
636+ , ("SI", dosiunitx)
637+ -- hyphenat
638+ , ("bshyp", lit "\\\173")
639+ , ("fshyp", lit "/\173")
640+ , ("dothyp", lit ".\173")
641+ , ("colonhyp", lit ":\173")
642+ , ("hyp", lit "-")
643+ , ("nohyphens", tok)
644+ , ("textnhtt", ttfamily)
645+ , ("nhttfamily", ttfamily)
646+ -- fontawesome
647+ , ("faCheck", lit "\10003")
648+ , ("faClose", lit "\10007")
649+ ] ++ map ignoreInlines
650+ -- these commands will be ignored unless --parse-raw is specified,
651+ -- in which case they will appear as raw latex blocks:
652+ [ "index"
653+ , "hspace"
654+ , "vspace"
655+ , "newpage"
656+ , "clearpage"
657+ , "pagebreak"
658+ ]
659+
660+
661+-}
662+
368663inline :: PandocMonad m => LP m Inlines
369664inline = (mempty <$ comment)
370665 <|> (space <$ whitespace)
371666 <|> (softbreak <$ endline)
372667 <|> inlineText
373− -- <|> inlineCommand
668+ <|> inlineCommand'
374669 -- <|> inlineEnvironment
375670 <|> inlineGroup
376671 <|> (symbol '-' *>
@@ -424,6 +719,8 @@ maybeAddExtension ext fp =
424719 then addExtension fp ext
425720 else fp
426721
722+blockCommands :: PandocMonad m => M.Map Text (LP m Blocks)
723+blockCommands = M.empty
427724
428725block :: PandocMonad m => LP m Blocks
429726block = (mempty <$ spaces1)