mirror of
https://github.com/jgm/pandoc.git
synced 2026-09-21 05:43:39 +08:00
Org reader: honor tex export option
The `tex` export option can be set with `#+OPTION: tex:nil` and allows three settings: - `t` causes LaTeX fragments to be parsed as TeX or added as raw TeX, - `nil` removes all LaTeX fragments from the document, and - `verbatim` treats LaTeX as text. The default is `t`. Closes: #4070
This commit is contained in:
@@ -34,7 +34,7 @@ import Text.Pandoc.Shared (compactify, compactifyDL, safeRead)
|
||||
import Control.Monad (foldM, guard, mplus, mzero, void)
|
||||
import Data.Char (isSpace)
|
||||
import Data.Default (Default)
|
||||
import Data.List (foldl')
|
||||
import Data.List (foldl', intersperse)
|
||||
import Data.Maybe (fromMaybe, isJust, isNothing)
|
||||
import Data.Text (Text)
|
||||
|
||||
@@ -739,22 +739,26 @@ rowToContent tbl row =
|
||||
--
|
||||
-- LaTeX fragments
|
||||
--
|
||||
latexFragment :: Monad m => OrgParser m (F Blocks)
|
||||
latexFragment :: PandocMonad m => OrgParser m (F Blocks)
|
||||
latexFragment = try $ do
|
||||
envName <- latexEnvStart
|
||||
content <- mconcat <$> manyTill anyLineNewline (latexEnd envName)
|
||||
returnF $ B.rawBlock "latex" (content `inLatexEnv` envName)
|
||||
texOpt <- getExportSetting exportWithLatex
|
||||
let envStart = "\\begin{" <> envName <> "}"
|
||||
let envEnd = "\\end{" <> envName <> "}"
|
||||
envLines <- do
|
||||
content <- manyTill anyLine (latexEnd envName)
|
||||
return $ envStart : content ++ [envEnd]
|
||||
returnF $ case texOpt of
|
||||
TeXExport -> B.rawBlock "latex" . T.unlines $ envLines
|
||||
TeXIgnore -> mempty
|
||||
TeXVerbatim -> B.para . mconcat . intersperse B.softbreak $
|
||||
map B.str envLines
|
||||
where
|
||||
c `inLatexEnv` e = mconcat [ "\\begin{", e, "}\n"
|
||||
, c
|
||||
, "\\end{", e, "}\n"
|
||||
]
|
||||
|
||||
latexEnd :: Monad m => Text -> OrgParser m ()
|
||||
latexEnd envName = try $
|
||||
() <$ skipSpaces
|
||||
<* textStr ("\\end{" <> envName <> "}")
|
||||
<* blankline
|
||||
latexEnd :: Monad m => Text -> OrgParser m ()
|
||||
latexEnd envName = try . void
|
||||
$ skipSpaces
|
||||
<* textStr ("\\end{" <> envName <> "}")
|
||||
<* blankline
|
||||
|
||||
|
||||
--
|
||||
|
||||
@@ -20,7 +20,7 @@ import Text.Pandoc.Readers.Org.Parsing
|
||||
import Control.Monad (mzero, void)
|
||||
import Data.Char (toLower)
|
||||
import Data.Maybe (listToMaybe)
|
||||
import Data.Text (Text)
|
||||
import Data.Text (Text, unpack)
|
||||
|
||||
-- | Read and handle space separated org-mode export settings.
|
||||
exportSettings :: PandocMonad m => OrgParser m ()
|
||||
@@ -59,7 +59,7 @@ exportSetting = choice
|
||||
, ignoredSetting "stat"
|
||||
, booleanSetting "tags" (\val es -> es { exportWithTags = val })
|
||||
, ignoredSetting "tasks"
|
||||
, ignoredSetting "tex"
|
||||
, texSetting "tex" (\val es -> es { exportWithLatex = val })
|
||||
, ignoredSetting "timestamp"
|
||||
, ignoredSetting "title"
|
||||
, ignoredSetting "toc"
|
||||
@@ -68,6 +68,8 @@ exportSetting = choice
|
||||
, ignoreAndWarn
|
||||
] <?> "export setting"
|
||||
|
||||
-- | Generic handler for export settings. Takes a parser which converts
|
||||
-- the plain option text into a data structure.
|
||||
genericExportSetting :: Monad m
|
||||
=> OrgParser m a
|
||||
-> Text
|
||||
@@ -101,10 +103,8 @@ archivedTreeSetting :: Monad m
|
||||
archivedTreeSetting =
|
||||
genericExportSetting $ archivedTreesHeadlineSetting <|> archivedTreesBoolean
|
||||
where
|
||||
archivedTreesHeadlineSetting = try $ do
|
||||
_ <- string "headline"
|
||||
lookAhead (newline <|> spaceChar)
|
||||
return ArchivedTreesHeadlineOnly
|
||||
archivedTreesHeadlineSetting =
|
||||
ArchivedTreesHeadlineOnly <$ optionString "headline"
|
||||
|
||||
archivedTreesBoolean = try $ do
|
||||
exportBool <- elispBoolean
|
||||
@@ -143,6 +143,22 @@ complementableListSetting = genericExportSetting $ choice
|
||||
char '"'
|
||||
*> manyTillChar alphaNum (char '"')
|
||||
|
||||
-- | Parses either @t@, @nil@, or @verbatim@ into a 'TeXExport' value.
|
||||
texSetting :: Monad m
|
||||
=> Text
|
||||
-> ExportSettingSetter TeXExport
|
||||
-> OrgParser m ()
|
||||
texSetting = genericExportSetting $ texVerbatim <|> texBoolean
|
||||
where
|
||||
texVerbatim = TeXVerbatim <$ optionString "verbatim"
|
||||
|
||||
texBoolean = try $ do
|
||||
exportBool <- elispBoolean
|
||||
return $
|
||||
if exportBool
|
||||
then TeXExport
|
||||
else TeXIgnore
|
||||
|
||||
-- | Read but ignore the export setting.
|
||||
ignoredSetting :: Monad m => Text -> OrgParser m ()
|
||||
ignoredSetting s = try (() <$ textStr s <* char ':' <* many1 nonspaceChar)
|
||||
@@ -164,3 +180,11 @@ elispBoolean = try $ do
|
||||
"{}" -> False
|
||||
"()" -> False
|
||||
_ -> True
|
||||
|
||||
-- | Try to parse a literal string as the option value. Returns the
|
||||
-- string on success.
|
||||
optionString :: Monad m => Text -> OrgParser m Text
|
||||
optionString s = try $ do
|
||||
_ <- string (unpack s)
|
||||
lookAhead (newline <|> spaceChar)
|
||||
return s
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{- |
|
||||
Module : Text.Pandoc.Readers.Org.Inlines
|
||||
@@ -788,15 +789,17 @@ simpleSubOrSuperText = try $ do
|
||||
inlineLaTeX :: PandocMonad m => OrgParser m (F Inlines)
|
||||
inlineLaTeX = try $ do
|
||||
cmd <- inlineLaTeXCommand
|
||||
ils <- (lift . lift) $ parseAsInlineLaTeX cmd
|
||||
texOpt <- getExportSetting exportWithLatex
|
||||
ils <- parseAsInlineLaTeX cmd texOpt
|
||||
maybe mzero returnF $
|
||||
parseAsMathMLSym cmd `mplus` parseAsMath cmd `mplus` ils
|
||||
parseAsMathMLSym cmd `mplus` parseAsMath cmd texOpt `mplus` ils
|
||||
where
|
||||
parseAsMath :: Text -> Maybe Inlines
|
||||
parseAsMath cs = B.fromList <$> texMathToPandoc cs
|
||||
|
||||
parseAsInlineLaTeX :: PandocMonad m => Text -> m (Maybe Inlines)
|
||||
parseAsInlineLaTeX cs = maybeRight <$> runParserT inlineCommand state "" cs
|
||||
parseAsInlineLaTeX :: PandocMonad m
|
||||
=> Text -> TeXExport -> OrgParser m (Maybe Inlines)
|
||||
parseAsInlineLaTeX cs = \case
|
||||
TeXExport -> maybeRight <$> runParserT inlineCommand state "" cs
|
||||
TeXIgnore -> return (Just mempty)
|
||||
TeXVerbatim -> return (Just $ B.str cs)
|
||||
|
||||
parseAsMathMLSym :: Text -> Maybe Inlines
|
||||
parseAsMathMLSym cs = B.str <$> MathMLEntityMap.getUnicode (clean cs)
|
||||
@@ -807,8 +810,12 @@ inlineLaTeX = try $ do
|
||||
state = def{ stateOptions = def{ readerExtensions =
|
||||
enableExtension Ext_raw_tex (readerExtensions def) } }
|
||||
|
||||
texMathToPandoc :: Text -> Maybe [Inline]
|
||||
texMathToPandoc cs = maybeRight (readTeX cs) >>= writePandoc DisplayInline
|
||||
parseAsMath :: Text -> TeXExport -> Maybe Inlines
|
||||
parseAsMath cs = \case
|
||||
TeXExport -> maybeRight (readTeX cs) >>=
|
||||
fmap B.fromList . writePandoc DisplayInline
|
||||
TeXIgnore -> Just mempty
|
||||
TeXVerbatim -> Just $ B.str cs
|
||||
|
||||
maybeRight :: Either a b -> Maybe b
|
||||
maybeRight = either (const Nothing) Just
|
||||
@@ -820,7 +827,7 @@ inlineLaTeXCommand = try $ do
|
||||
parsed <- (lift . lift) $ runParserT rawLaTeXInline st "source" rest
|
||||
case parsed of
|
||||
Right cs -> do
|
||||
-- drop any trailing whitespace, those are not be part of the command as
|
||||
-- drop any trailing whitespace, those are not part of the command as
|
||||
-- far as org mode is concerned.
|
||||
let cmdNoSpc = T.dropWhileEnd isSpace cs
|
||||
let len = T.length cmdNoSpc
|
||||
|
||||
@@ -35,6 +35,7 @@ module Text.Pandoc.Readers.Org.ParserState
|
||||
, returnF
|
||||
, ExportSettings (..)
|
||||
, ArchivedTreesOption (..)
|
||||
, TeXExport (..)
|
||||
, optionsToParserState
|
||||
) where
|
||||
|
||||
@@ -231,6 +232,13 @@ data ArchivedTreesOption =
|
||||
| ArchivedTreesNoExport -- ^ Exclude archived trees from exporting
|
||||
| ArchivedTreesHeadlineOnly -- ^ Export only the headline, discard the contents
|
||||
|
||||
-- | Options for the handling of LaTeX environments and fragments.
|
||||
-- Represents allowed values of Emacs variable @org-export-with-latex@.
|
||||
data TeXExport
|
||||
= TeXExport -- ^ Include raw TeX in the output
|
||||
| TeXIgnore -- ^ Ignore raw TeX
|
||||
| TeXVerbatim -- ^ Keep everything in verbatim
|
||||
|
||||
-- | Export settings <http://orgmode.org/manual/Export-settings.html>
|
||||
-- These settings can be changed via OPTIONS statements.
|
||||
data ExportSettings = ExportSettings
|
||||
@@ -249,6 +257,7 @@ data ExportSettings = ExportSettings
|
||||
, exportWithAuthor :: Bool -- ^ Include author in final meta-data
|
||||
, exportWithCreator :: Bool -- ^ Include creator in final meta-data
|
||||
, exportWithEmail :: Bool -- ^ Include email in final meta-data
|
||||
, exportWithLatex :: TeXExport -- ^ Handling of raw TeX commands
|
||||
, exportWithPlanning :: Bool -- ^ Keep planning info after headlines
|
||||
, exportWithTags :: Bool -- ^ Keep tags as part of headlines
|
||||
, exportWithTodoKeywords :: Bool -- ^ Keep TODO keywords in headers
|
||||
@@ -270,6 +279,7 @@ defaultExportSettings = ExportSettings
|
||||
, exportWithAuthor = True
|
||||
, exportWithCreator = True
|
||||
, exportWithEmail = True
|
||||
, exportWithLatex = TeXExport
|
||||
, exportWithPlanning = False
|
||||
, exportWithTags = True
|
||||
, exportWithTodoKeywords = True
|
||||
|
||||
@@ -162,6 +162,79 @@ tests =
|
||||
] =?>
|
||||
headerWith ("headline", [], mempty) 1 "Headline"
|
||||
|
||||
, testGroup "LaTeX"
|
||||
[ testGroup "Include LaTeX fragments"
|
||||
[ "Inline command" =:
|
||||
T.unlines [ "#+OPTIONS: tex:t"
|
||||
, "Hello \\emph{Name}"
|
||||
] =?>
|
||||
para ("Hello" <> space <> emph "Name")
|
||||
|
||||
, "Alpha" =:
|
||||
T.unlines [ "#+OPTIONS: tex:t"
|
||||
, "\\alpha"
|
||||
] =?>
|
||||
para "α"
|
||||
|
||||
, "equation environment" =:
|
||||
T.unlines [ "#+OPTIONS: tex:t"
|
||||
, "\\begin{equation}"
|
||||
, "f(x) = x^2"
|
||||
, "\\end{equation}"
|
||||
] =?>
|
||||
rawBlock "latex" (T.unlines [ "\\begin{equation}"
|
||||
, "f(x) = x^2"
|
||||
, "\\end{equation}"
|
||||
])
|
||||
]
|
||||
|
||||
, testGroup "Ignore LaTeX fragments"
|
||||
[ "Inline command" =:
|
||||
T.unlines [ "#+OPTIONS: tex:nil"
|
||||
, "Hello \\emph{Emphasised}"
|
||||
] =?>
|
||||
para "Hello"
|
||||
|
||||
, "MathML symbol (alpha)" =:
|
||||
T.unlines [ "#+OPTIONS: tex:nil"
|
||||
, "\\alpha"
|
||||
] =?>
|
||||
para "α"
|
||||
|
||||
, "equation environment" =:
|
||||
T.unlines [ "#+OPTIONS: tex:nil"
|
||||
, "\\begin{equation}"
|
||||
, "f(x) = x^2"
|
||||
, "\\end{equation}"
|
||||
] =?>
|
||||
(mempty :: Blocks)
|
||||
]
|
||||
|
||||
, testGroup "Verbatim LaTeX"
|
||||
[ "Inline command" =:
|
||||
T.unlines [ "#+OPTIONS: tex:verbatim"
|
||||
, "Hello \\emph{Emphasised}"
|
||||
] =?>
|
||||
para "Hello \\emph{Emphasised}"
|
||||
|
||||
, "MathML symbol (alpha)" =:
|
||||
T.unlines [ "#+OPTIONS: tex:verbatim"
|
||||
, "\\alpha"
|
||||
] =?>
|
||||
para "α"
|
||||
|
||||
, "equation environment" =:
|
||||
T.unlines [ "#+OPTIONS: tex:verbatim"
|
||||
, "\\begin{equation}"
|
||||
, "f(x) = x^2"
|
||||
, "\\end{equation}"
|
||||
] =?>
|
||||
para (str "\\begin{equation}" <> softbreak <>
|
||||
str "f(x) = x^2" <> softbreak <>
|
||||
str "\\end{equation}")
|
||||
]
|
||||
]
|
||||
|
||||
, testGroup "planning information"
|
||||
[ "include planning info after headlines" =:
|
||||
T.unlines [ "#+OPTIONS: p:t"
|
||||
|
||||
Reference in New Issue
Block a user