git.iko.soy/typeable/pandoc
on master help sign in
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

commit9e6e9a72218e8c408e151bf8b169f44a8c55eb40 authorAlbert Krewinkel <albert@zeitkraut.de> dateThu 25 Jun 2020, 20:04 (6 years ago) parentf1c678a
5 files changed+148−30
src/Text/Pandoc/Readers/Org/Blocks.hs+18−14 src/Text/Pandoc/Readers/Org/ExportSettings.hs+30−6 src/Text/Pandoc/Readers/Org/Inlines.hs+17−10 src/Text/Pandoc/Readers/Org/ParserState.hs+10 test/Tests/Readers/Org/Directive.hs+73
src/Text/Pandoc/Readers/Org/Blocks.hs+18−14
@@ -34,7 +34,7 @@ import Text.Pandoc.Shared (compactify, compactifyDL, safeRead)
3434import Control.Monad (foldM, guard, mplus, mzero, void)
3535import Data.Char (isSpace)
3636import Data.Default (Default)
37−import Data.List (foldl')
37+import Data.List (foldl', intersperse)
3838import Data.Maybe (fromMaybe, isJust, isNothing)
3939import Data.Text (Text)
4040
@@ -739,22 +739,26 @@ rowToContent tbl row =
739739--
740740-- LaTeX fragments
741741--
742−latexFragment :: Monad m => OrgParser m (F Blocks)
742+latexFragment :: PandocMonad m => OrgParser m (F Blocks)
743743latexFragment = try $ do
744744 envName <- latexEnvStart
745− content <- mconcat <$> manyTill anyLineNewline (latexEnd envName)
746− returnF $ B.rawBlock "latex" (content `inLatexEnv` envName)
745+ texOpt <- getExportSetting exportWithLatex
746+ let envStart = "\\begin{" <> envName <> "}"
747+ let envEnd = "\\end{" <> envName <> "}"
748+ envLines <- do
749+ content <- manyTill anyLine (latexEnd envName)
750+ return $ envStart : content ++ [envEnd]
751+ returnF $ case texOpt of
752+ TeXExport -> B.rawBlock "latex" . T.unlines $ envLines
753+ TeXIgnore -> mempty
754+ TeXVerbatim -> B.para . mconcat . intersperse B.softbreak $
755+ map B.str envLines
747756 where
748− c `inLatexEnv` e = mconcat [ "\\begin{", e, "}\n"
749− , c
750− , "\\end{", e, "}\n"
751− ]
752−
753−latexEnd :: Monad m => Text -> OrgParser m ()
754−latexEnd envName = try $
755− () <$ skipSpaces
756− <* textStr ("\\end{" <> envName <> "}")
757− <* blankline
757+ latexEnd :: Monad m => Text -> OrgParser m ()
758+ latexEnd envName = try . void
759+ $ skipSpaces
760+ <* textStr ("\\end{" <> envName <> "}")
761+ <* blankline
758762
759763
760764--
src/Text/Pandoc/Readers/Org/ExportSettings.hs+30−6
@@ -20,7 +20,7 @@ import Text.Pandoc.Readers.Org.Parsing
2020import Control.Monad (mzero, void)
2121import Data.Char (toLower)
2222import Data.Maybe (listToMaybe)
23−import Data.Text (Text)
23+import Data.Text (Text, unpack)
2424
2525-- | Read and handle space separated org-mode export settings.
2626exportSettings :: PandocMonad m => OrgParser m ()
@@ -59,7 +59,7 @@ exportSetting = choice
5959 , ignoredSetting "stat"
6060 , booleanSetting "tags" (\val es -> es { exportWithTags = val })
6161 , ignoredSetting "tasks"
62− , ignoredSetting "tex"
62+ , texSetting "tex" (\val es -> es { exportWithLatex = val })
6363 , ignoredSetting "timestamp"
6464 , ignoredSetting "title"
6565 , ignoredSetting "toc"
@@ -68,6 +68,8 @@ exportSetting = choice
6868 , ignoreAndWarn
6969 ] <?> "export setting"
7070
71+-- | Generic handler for export settings. Takes a parser which converts
72+-- the plain option text into a data structure.
7173genericExportSetting :: Monad m
7274 => OrgParser m a
7375 -> Text
@@ -101,10 +103,8 @@ archivedTreeSetting :: Monad m
101103archivedTreeSetting =
102104 genericExportSetting $ archivedTreesHeadlineSetting <|> archivedTreesBoolean
103105 where
104− archivedTreesHeadlineSetting = try $ do
105− _ <- string "headline"
106− lookAhead (newline <|> spaceChar)
107− return ArchivedTreesHeadlineOnly
106+ archivedTreesHeadlineSetting =
107+ ArchivedTreesHeadlineOnly <$ optionString "headline"
108108
109109 archivedTreesBoolean = try $ do
110110 exportBool <- elispBoolean
@@ -143,6 +143,22 @@ complementableListSetting = genericExportSetting $ choice
143143 char '"'
144144 *> manyTillChar alphaNum (char '"')
145145
146+-- | Parses either @t@, @nil@, or @verbatim@ into a 'TeXExport' value.
147+texSetting :: Monad m
148+ => Text
149+ -> ExportSettingSetter TeXExport
150+ -> OrgParser m ()
151+texSetting = genericExportSetting $ texVerbatim <|> texBoolean
152+ where
153+ texVerbatim = TeXVerbatim <$ optionString "verbatim"
154+
155+ texBoolean = try $ do
156+ exportBool <- elispBoolean
157+ return $
158+ if exportBool
159+ then TeXExport
160+ else TeXIgnore
161+
146162-- | Read but ignore the export setting.
147163ignoredSetting :: Monad m => Text -> OrgParser m ()
148164ignoredSetting s = try (() <$ textStr s <* char ':' <* many1 nonspaceChar)
@@ -164,3 +180,11 @@ elispBoolean = try $ do
164180 "{}" -> False
165181 "()" -> False
166182 _ -> True
183+
184+-- | Try to parse a literal string as the option value. Returns the
185+-- string on success.
186+optionString :: Monad m => Text -> OrgParser m Text
187+optionString s = try $ do
188+ _ <- string (unpack s)
189+ lookAhead (newline <|> spaceChar)
190+ return s
src/Text/Pandoc/Readers/Org/Inlines.hs+17−10
@@ -1,3 +1,4 @@
1+{-# LANGUAGE LambdaCase #-}
12{-# LANGUAGE OverloadedStrings #-}
23{- |
34 Module : Text.Pandoc.Readers.Org.Inlines
@@ -788,15 +789,17 @@ simpleSubOrSuperText = try $ do
788789inlineLaTeX :: PandocMonad m => OrgParser m (F Inlines)
789790inlineLaTeX = try $ do
790791 cmd <- inlineLaTeXCommand
791− ils <- (lift . lift) $ parseAsInlineLaTeX cmd
792+ texOpt <- getExportSetting exportWithLatex
793+ ils <- parseAsInlineLaTeX cmd texOpt
792794 maybe mzero returnF $
793− parseAsMathMLSym cmd `mplus` parseAsMath cmd `mplus` ils
795+ parseAsMathMLSym cmd `mplus` parseAsMath cmd texOpt `mplus` ils
794796 where
795− parseAsMath :: Text -> Maybe Inlines
796− parseAsMath cs = B.fromList <$> texMathToPandoc cs
797−
798− parseAsInlineLaTeX :: PandocMonad m => Text -> m (Maybe Inlines)
799− parseAsInlineLaTeX cs = maybeRight <$> runParserT inlineCommand state "" cs
797+ parseAsInlineLaTeX :: PandocMonad m
798+ => Text -> TeXExport -> OrgParser m (Maybe Inlines)
799+ parseAsInlineLaTeX cs = \case
800+ TeXExport -> maybeRight <$> runParserT inlineCommand state "" cs
801+ TeXIgnore -> return (Just mempty)
802+ TeXVerbatim -> return (Just $ B.str cs)
800803
801804 parseAsMathMLSym :: Text -> Maybe Inlines
802805 parseAsMathMLSym cs = B.str <$> MathMLEntityMap.getUnicode (clean cs)
@@ -807,8 +810,12 @@ inlineLaTeX = try $ do
807810 state = def{ stateOptions = def{ readerExtensions =
808811 enableExtension Ext_raw_tex (readerExtensions def) } }
809812
810− texMathToPandoc :: Text -> Maybe [Inline]
811− texMathToPandoc cs = maybeRight (readTeX cs) >>= writePandoc DisplayInline
813+ parseAsMath :: Text -> TeXExport -> Maybe Inlines
814+ parseAsMath cs = \case
815+ TeXExport -> maybeRight (readTeX cs) >>=
816+ fmap B.fromList . writePandoc DisplayInline
817+ TeXIgnore -> Just mempty
818+ TeXVerbatim -> Just $ B.str cs
812819
813820maybeRight :: Either a b -> Maybe b
814821maybeRight = either (const Nothing) Just
@@ -820,7 +827,7 @@ inlineLaTeXCommand = try $ do
820827 parsed <- (lift . lift) $ runParserT rawLaTeXInline st "source" rest
821828 case parsed of
822829 Right cs -> do
823− -- drop any trailing whitespace, those are not be part of the command as
830+ -- drop any trailing whitespace, those are not part of the command as
824831 -- far as org mode is concerned.
825832 let cmdNoSpc = T.dropWhileEnd isSpace cs
826833 let len = T.length cmdNoSpc
src/Text/Pandoc/Readers/Org/ParserState.hs+10
@@ -35,6 +35,7 @@ module Text.Pandoc.Readers.Org.ParserState
3535 , returnF
3636 , ExportSettings (..)
3737 , ArchivedTreesOption (..)
38+ , TeXExport (..)
3839 , optionsToParserState
3940 ) where
4041
@@ -231,6 +232,13 @@ data ArchivedTreesOption =
231232 | ArchivedTreesNoExport -- ^ Exclude archived trees from exporting
232233 | ArchivedTreesHeadlineOnly -- ^ Export only the headline, discard the contents
233234
235+-- | Options for the handling of LaTeX environments and fragments.
236+-- Represents allowed values of Emacs variable @org-export-with-latex@.
237+data TeXExport
238+ = TeXExport -- ^ Include raw TeX in the output
239+ | TeXIgnore -- ^ Ignore raw TeX
240+ | TeXVerbatim -- ^ Keep everything in verbatim
241+
234242-- | Export settings <http://orgmode.org/manual/Export-settings.html>
235243-- These settings can be changed via OPTIONS statements.
236244data ExportSettings = ExportSettings
@@ -249,6 +257,7 @@ data ExportSettings = ExportSettings
249257 , exportWithAuthor :: Bool -- ^ Include author in final meta-data
250258 , exportWithCreator :: Bool -- ^ Include creator in final meta-data
251259 , exportWithEmail :: Bool -- ^ Include email in final meta-data
260+ , exportWithLatex :: TeXExport -- ^ Handling of raw TeX commands
252261 , exportWithPlanning :: Bool -- ^ Keep planning info after headlines
253262 , exportWithTags :: Bool -- ^ Keep tags as part of headlines
254263 , exportWithTodoKeywords :: Bool -- ^ Keep TODO keywords in headers
@@ -270,6 +279,7 @@ defaultExportSettings = ExportSettings
270279 , exportWithAuthor = True
271280 , exportWithCreator = True
272281 , exportWithEmail = True
282+ , exportWithLatex = TeXExport
273283 , exportWithPlanning = False
274284 , exportWithTags = True
275285 , exportWithTodoKeywords = True
test/Tests/Readers/Org/Directive.hs+73
@@ -162,6 +162,79 @@ tests =
162162 ] =?>
163163 headerWith ("headline", [], mempty) 1 "Headline"
164164
165+ , testGroup "LaTeX"
166+ [ testGroup "Include LaTeX fragments"
167+ [ "Inline command" =:
168+ T.unlines [ "#+OPTIONS: tex:t"
169+ , "Hello \\emph{Name}"
170+ ] =?>
171+ para ("Hello" <> space <> emph "Name")
172+
173+ , "Alpha" =:
174+ T.unlines [ "#+OPTIONS: tex:t"
175+ , "\\alpha"
176+ ] =?>
177+ para "α"
178+
179+ , "equation environment" =:
180+ T.unlines [ "#+OPTIONS: tex:t"
181+ , "\\begin{equation}"
182+ , "f(x) = x^2"
183+ , "\\end{equation}"
184+ ] =?>
185+ rawBlock "latex" (T.unlines [ "\\begin{equation}"
186+ , "f(x) = x^2"
187+ , "\\end{equation}"
188+ ])
189+ ]
190+
191+ , testGroup "Ignore LaTeX fragments"
192+ [ "Inline command" =:
193+ T.unlines [ "#+OPTIONS: tex:nil"
194+ , "Hello \\emph{Emphasised}"
195+ ] =?>
196+ para "Hello"
197+
198+ , "MathML symbol (alpha)" =:
199+ T.unlines [ "#+OPTIONS: tex:nil"
200+ , "\\alpha"
201+ ] =?>
202+ para "α"
203+
204+ , "equation environment" =:
205+ T.unlines [ "#+OPTIONS: tex:nil"
206+ , "\\begin{equation}"
207+ , "f(x) = x^2"
208+ , "\\end{equation}"
209+ ] =?>
210+ (mempty :: Blocks)
211+ ]
212+
213+ , testGroup "Verbatim LaTeX"
214+ [ "Inline command" =:
215+ T.unlines [ "#+OPTIONS: tex:verbatim"
216+ , "Hello \\emph{Emphasised}"
217+ ] =?>
218+ para "Hello \\emph{Emphasised}"
219+
220+ , "MathML symbol (alpha)" =:
221+ T.unlines [ "#+OPTIONS: tex:verbatim"
222+ , "\\alpha"
223+ ] =?>
224+ para "α"
225+
226+ , "equation environment" =:
227+ T.unlines [ "#+OPTIONS: tex:verbatim"
228+ , "\\begin{equation}"
229+ , "f(x) = x^2"
230+ , "\\end{equation}"
231+ ] =?>
232+ para (str "\\begin{equation}" <> softbreak <>
233+ str "f(x) = x^2" <> softbreak <>
234+ str "\\end{equation}")
235+ ]
236+ ]
237+
165238 , testGroup "planning information"
166239 [ "include planning info after headlines" =:
167240 T.unlines [ "#+OPTIONS: p:t"