From 9849ba7fd744f529f063e0802a18fa18c8433eeb Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 16 Jun 2017 23:29:37 +0200 Subject: Use Control.Monad.State.Strict throughout. This gives 20-30% speedup and reduction of memory usage in most of the writers. --- src/Text/Pandoc/Writers/OpenDocument.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text/Pandoc/Writers/OpenDocument.hs') diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 58295684e..fd9a13f3e 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -33,7 +33,7 @@ Conversion of 'Pandoc' documents to OpenDocument XML. -} module Text.Pandoc.Writers.OpenDocument ( writeOpenDocument ) where import Control.Arrow ((***), (>>>)) -import Control.Monad.State hiding (when) +import Control.Monad.State.Strict hiding (when) import Data.Char (chr) import Data.List (sortBy) import Data.Text (Text) -- cgit v1.2.3 From c349f0b0baef5866041b6668fff5bbb16f0002f9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 20 Jun 2017 22:43:48 +0200 Subject: Writers: adjusted for renderTemplate' changes. Now we raise a proper error on template failure. --- src/Text/Pandoc/Writers/AsciiDoc.hs | 2 +- src/Text/Pandoc/Writers/CommonMark.hs | 6 +++--- src/Text/Pandoc/Writers/ConTeXt.hs | 6 +++--- src/Text/Pandoc/Writers/Custom.hs | 7 +++++-- src/Text/Pandoc/Writers/Docbook.hs | 6 +++--- src/Text/Pandoc/Writers/DokuWiki.hs | 2 +- src/Text/Pandoc/Writers/HTML.hs | 2 +- src/Text/Pandoc/Writers/Haddock.hs | 2 +- src/Text/Pandoc/Writers/ICML.hs | 6 +++--- src/Text/Pandoc/Writers/JATS.hs | 6 +++--- src/Text/Pandoc/Writers/LaTeX.hs | 6 +++--- src/Text/Pandoc/Writers/Man.hs | 2 +- src/Text/Pandoc/Writers/Markdown.hs | 2 +- src/Text/Pandoc/Writers/MediaWiki.hs | 5 ++--- src/Text/Pandoc/Writers/Ms.hs | 5 +++-- src/Text/Pandoc/Writers/Muse.hs | 2 +- src/Text/Pandoc/Writers/OPML.hs | 6 +++--- src/Text/Pandoc/Writers/OpenDocument.hs | 6 +++--- src/Text/Pandoc/Writers/Org.hs | 2 +- src/Text/Pandoc/Writers/RST.hs | 2 +- src/Text/Pandoc/Writers/RTF.hs | 7 ++++--- src/Text/Pandoc/Writers/TEI.hs | 2 +- src/Text/Pandoc/Writers/Texinfo.hs | 2 +- src/Text/Pandoc/Writers/Textile.hs | 2 +- src/Text/Pandoc/Writers/ZimWiki.hs | 2 +- 25 files changed, 51 insertions(+), 47 deletions(-) (limited to 'src/Text/Pandoc/Writers/OpenDocument.hs') diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index ee977f90b..112f8b657 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -105,7 +105,7 @@ pandocToAsciiDoc opts (Pandoc meta blocks) = do $ metadata' case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Escape special characters for AsciiDoc. escapeString :: String -> String diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 93cc0b53a..63249a7ce 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -58,9 +58,9 @@ writeCommonMark opts (Pandoc meta blocks) = do (inlinesToCommonMark opts) meta let context = defField "body" main $ metadata - return $ case writerTemplate opts of - Nothing -> main - Just tpl -> renderTemplate' tpl context + case writerTemplate opts of + Nothing -> return main + Just tpl -> renderTemplate' tpl context processNotes :: Inline -> State [[Block]] Inline processNotes (Note bs) = do diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 571c55b19..5a81aa8a0 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -105,9 +105,9 @@ pandocToConTeXt options (Pandoc meta blocks) = do getField "lang" context) $ defField "context-dir" (toContextDir $ getField "dir" context) $ context - return $ case writerTemplate options of - Nothing -> main - Just tpl -> renderTemplate' tpl context' + case writerTemplate options of + Nothing -> return main + Just tpl -> renderTemplate' tpl context' toContextDir :: Maybe String -> String toContextDir (Just "rtl") = "r2l" diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 1314ef844..363bad99b 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -46,6 +46,7 @@ import Data.Typeable import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) import Scripting.Lua (LuaState, StackValue, callfunc) import qualified Scripting.Lua as Lua +import Text.Pandoc.Error import Text.Pandoc.Lua.Compat ( loadstring ) import Text.Pandoc.Lua.Util ( addValue ) import Text.Pandoc.Lua.SharedInstances () @@ -141,8 +142,10 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do let body = rendered case writerTemplate opts of Nothing -> return $ pack body - Just tpl -> return $ pack $ - renderTemplate' tpl $ setField "body" body context + Just tpl -> + case applyTemplate (pack tpl) $ setField "body" body context of + Left e -> throw (PandocTemplateError e) + Right r -> return (pack r) docToCustom :: LuaState -> WriterOptions -> Pandoc -> IO String docToCustom lua opts (Pandoc (Meta metamap) blocks) = do diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index a0e69ffb4..9db9a0102 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -124,9 +124,9 @@ writeDocbook opts (Pandoc meta blocks) = do MathML -> True _ -> False) $ metadata - return $ case writerTemplate opts of - Nothing -> main - Just tpl -> renderTemplate' tpl context + case writerTemplate opts of + Nothing -> return main + Just tpl -> renderTemplate' tpl context -- | Convert an Element to Docbook. elementToDocbook :: PandocMonad m => WriterOptions -> Int -> Element -> DB m Doc diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index dc227cfa9..ad8689e8c 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -103,7 +103,7 @@ pandocToDokuWiki opts (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Escape special characters for DokuWiki. escapeString :: String -> String diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 43c098866..3687ca53b 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -210,7 +210,7 @@ writeHtmlString' st opts d = do lookup "sourcefile" (writerVariables opts) report $ NoTitleElement fallback return $ resetField "pagetitle" fallback context - return $ renderTemplate' tpl $ + renderTemplate' tpl $ defField "body" (renderHtml' body) context' writeHtml' :: PandocMonad m => WriterState -> WriterOptions -> Pandoc -> m Html diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index 7965ebfae..d1146ca73 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -80,7 +80,7 @@ pandocToHaddock opts (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Return haddock representation of notes. notesToHaddock :: PandocMonad m diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index e564f94fe..37df58e65 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -147,9 +147,9 @@ writeICML opts (Pandoc meta blocks) = do $ defField "parStyles" (render' $ parStylesToDoc st) $ defField "hyperlinks" (render' $ hyperlinksToDoc $ links st) $ metadata - return $ case writerTemplate opts of - Nothing -> main - Just tpl -> renderTemplate' tpl context + case writerTemplate opts of + Nothing -> return main + Just tpl -> renderTemplate' tpl context -- | Auxilary functions for parStylesToDoc and charStylesToDoc. contains :: String -> (String, (String, String)) -> [(String, String)] diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 11f3b0c22..012ff8416 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -128,9 +128,9 @@ docToJATS opts (Pandoc meta blocks) = do MathML -> True _ -> False) $ metadata - return $ case writerTemplate opts of - Nothing -> main - Just tpl -> renderTemplate' tpl context + case writerTemplate opts of + Nothing -> return main + Just tpl -> renderTemplate' tpl context -- | Convert an Element to JATS. elementToJATS :: PandocMonad m => WriterOptions -> Int -> Element -> DB m Doc diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 88ff454ce..53a67a27a 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -285,9 +285,9 @@ pandocToLaTeX options (Pandoc meta blocks) = do Just "rtl" -> True _ -> False) $ context - return $ case writerTemplate options of - Nothing -> main - Just tpl -> renderTemplate' tpl context' + case writerTemplate options of + Nothing -> return main + Just tpl -> renderTemplate' tpl context' -- | Convert Elements to LaTeX elementToLaTeX :: PandocMonad m => WriterOptions -> Element -> LW m Doc diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index d96342fb5..4e756c419 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -110,7 +110,7 @@ pandocToMan opts (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Return man representation of notes. notesToMan :: PandocMonad m => WriterOptions -> [[Block]] -> StateT WriterState m Doc diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 4449bb5ce..8433f648f 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -228,7 +228,7 @@ pandocToMarkdown opts (Pandoc meta blocks) = do $ addVariablesToJSON opts metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Return markdown representation of reference key table. refsToMarkdown :: PandocMonad m => WriterOptions -> Refs -> MD m Doc diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index 3825a4e73..58d1b0707 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -82,9 +82,8 @@ pandocToMediaWiki (Pandoc meta blocks) = do let main = body ++ notes let context = defField "body" main $ defField "toc" (writerTableOfContents opts) metadata - return $ pack - $ case writerTemplate opts of - Nothing -> main + pack <$> case writerTemplate opts of + Nothing -> return main Just tpl -> renderTemplate' tpl context -- | Escape special characters for MediaWiki. diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 0999d13db..493da1545 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -125,7 +125,7 @@ pandocToMs opts (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Association list of characters to escape. msEscapes :: Map.Map Char String @@ -411,7 +411,8 @@ definitionListItemToMs opts (label, defs) = do let (first, rest) = case blocks of ((Para x):y) -> (Plain x,y) (x:y) -> (x,y) - [] -> error "blocks is null" + [] -> (Plain [], []) + -- should not happen rest' <- liftM vcat $ mapM (\item -> blockToMs opts item) rest first' <- blockToMs opts first diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 286bd1431..3d9e232ae 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -101,7 +101,7 @@ pandocToMuse (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Convert list of Pandoc block elements to Muse. blockListToMuse :: PandocMonad m diff --git a/src/Text/Pandoc/Writers/OPML.hs b/src/Text/Pandoc/Writers/OPML.hs index 4a0a317fa..52577ac17 100644 --- a/src/Text/Pandoc/Writers/OPML.hs +++ b/src/Text/Pandoc/Writers/OPML.hs @@ -60,9 +60,9 @@ writeOPML opts (Pandoc meta blocks) = do meta' main <- (render colwidth . vcat) <$> (mapM (elementToOPML opts) elements) let context = defField "body" main metadata - return $ case writerTemplate opts of - Nothing -> main - Just tpl -> renderTemplate' tpl context + case writerTemplate opts of + Nothing -> return main + Just tpl -> renderTemplate' tpl context writeHtmlInlines :: PandocMonad m => [Inline] -> m Text diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index fd9a13f3e..95a800c94 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -221,9 +221,9 @@ writeOpenDocument opts (Pandoc meta blocks) = do let context = defField "body" body $ defField "automatic-styles" (render' automaticStyles) $ metadata - return $ case writerTemplate opts of - Nothing -> body - Just tpl -> renderTemplate' tpl context + case writerTemplate opts of + Nothing -> return body + Just tpl -> renderTemplate' tpl context withParagraphStyle :: PandocMonad m => WriterOptions -> String -> [Block] -> OD m Doc diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 8524c441d..48f17c4fb 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -86,7 +86,7 @@ pandocToOrg (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Return Org representation of notes. notesToOrg :: PandocMonad m => [[Block]] -> Org m Doc diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 9c0693b0f..019c8335d 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -108,7 +108,7 @@ pandocToRST (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context where normalizeHeadings lev (Header l a i:bs) = Header lev a i:normalizeHeadings (lev+1) cont ++ normalizeHeadings lev bs' diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 5c990f324..6666f6549 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -125,10 +125,11 @@ writeRTF options doc = do then defField "toc" toc else id) $ metadata - return $ T.pack - $ case writerTemplate options of + T.pack <$> + case writerTemplate options of Just tpl -> renderTemplate' tpl context - Nothing -> case reverse body of + Nothing -> return $ + case reverse body of ('\n':_) -> body _ -> body ++ "\n" diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index 86a7415cf..26070966e 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -85,7 +85,7 @@ writeTEI opts (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Convert an Element to TEI. elementToTEI :: PandocMonad m => WriterOptions -> Int -> Element -> m Doc diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index fd489786d..549d4f3d9 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -106,7 +106,7 @@ pandocToTexinfo options (Pandoc meta blocks) = do $ metadata case writerTemplate options of Nothing -> return body - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Escape things as needed for Texinfo. stringToTexinfo :: String -> String diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index 432c055b8..acc9eaa0f 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -75,7 +75,7 @@ pandocToTextile opts (Pandoc meta blocks) = do let context = defField "body" main metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context withUseTags :: PandocMonad m => TW m a -> TW m a withUseTags action = do diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index ba51acfce..ced02d4be 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -78,7 +78,7 @@ pandocToZimWiki opts (Pandoc meta blocks) = do $ defField "toc" (writerTableOfContents opts) $ metadata case writerTemplate opts of - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context Nothing -> return main -- | Escape special characters for ZimWiki. -- cgit v1.2.3 From 083a224d1e3d791c459a998d18aa9975b8816c15 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 25 Jun 2017 10:38:11 +0200 Subject: Support `lang` attribute in OpenDocument and ODT writers. This adds the required attributes to the temporary styles, and also replaces existing language attributes in styles.xml. Support for lang attributes on Div and Span has also been added. Closes #1667. --- src/Text/Pandoc/Writers/Docx.hs | 7 ++--- src/Text/Pandoc/Writers/ODT.hs | 48 ++++++++++++++++++++++++++++----- src/Text/Pandoc/Writers/OpenDocument.hs | 35 +++++++++++++++++++----- 3 files changed, 72 insertions(+), 18 deletions(-) (limited to 'src/Text/Pandoc/Writers/OpenDocument.hs') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index b488f2479..d93b99486 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -67,7 +67,7 @@ import Text.Pandoc.Shared hiding (Element) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk import Text.Pandoc.Writers.Math -import Text.Pandoc.Writers.Shared (fixDisplayMath) +import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang) import Text.Printf (printf) import Text.TeXMath import Text.XML.Light as XML @@ -257,10 +257,7 @@ writeDocx opts doc@(Pandoc meta _) = do ) -- styles - let lang = case lookupMeta "lang" meta of - Just (MetaInlines [Str s]) -> Just s - Just (MetaString s) -> Just s - _ -> Nothing + let lang = getLang opts meta let addLang :: Element -> Element addLang e = case lang >>= \l -> (return . XMLC.toTree . go l . XMLC.fromElement) e of Just (Elem e') -> e' diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index c9a7de642..dff4f8fcf 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -33,6 +33,7 @@ import Codec.Archive.Zip import Control.Monad.Except (catchError) import Control.Monad.State.Strict import qualified Data.ByteString.Lazy as B +import Data.Generics (everywhere', mkT) import Data.List (isPrefixOf) import Data.Maybe (fromMaybe) import qualified Data.Text.Lazy as TL @@ -46,13 +47,13 @@ import Text.Pandoc.MIME (extensionFromMimeType, getMimeType) import Text.Pandoc.Options (WrapOption (..), WriterOptions (..)) import Text.Pandoc.Pretty import Text.Pandoc.Shared (stringify) -import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy) +import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) -import Text.Pandoc.Writers.Shared (fixDisplayMath) +import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang, splitLang) import Text.Pandoc.XML import Text.TeXMath -import Text.XML.Light.Output +import Text.XML.Light data ODTState = ODTState { stEntries :: [Entry] } @@ -78,6 +79,7 @@ pandocToODT :: PandocMonad m pandocToODT opts doc@(Pandoc meta _) = do let datadir = writerUserDataDir opts let title = docTitle meta + let lang = getLang opts meta refArchive <- case writerReferenceDoc opts of Just f -> liftM toArchive $ lift $ P.readFileLazy f @@ -132,18 +134,50 @@ pandocToODT opts doc@(Pandoc meta _) = do ,("xmlns:ooo","http://openoffice.org/2004/office") ,("xmlns:grddl","http://www.w3.org/2003/g/data-view#") ,("office:version","1.2")] - $ ( inTagsSimple "office:meta" - $ ( inTagsSimple "dc:title" (text $ escapeStringForXML (stringify title)) - ) + $ ( inTagsSimple "office:meta" $ + ( inTagsSimple "dc:title" + (text $ escapeStringForXML (stringify title)) + $$ + case lang of + Just l -> inTagsSimple "dc:language" + (text (escapeStringForXML l)) + Nothing -> empty + ) ) ) -- make sure mimetype is first let mimetypeEntry = toEntry "mimetype" epochtime $ fromStringLazy "application/vnd.oasis.opendocument.text" - let archive'' = addEntryToArchive mimetypeEntry + archive'' <- updateStyleWithLang lang + $ addEntryToArchive mimetypeEntry $ addEntryToArchive metaEntry archive' return $ fromArchive archive'' +updateStyleWithLang :: PandocMonad m => Maybe String -> Archive -> O m Archive +updateStyleWithLang Nothing arch = return arch +updateStyleWithLang (Just l) arch = do + (mblang, mbcountry) <- splitLang l + epochtime <- floor `fmap` (lift P.getPOSIXTime) + return arch{ zEntries = [if eRelativePath e == "styles.xml" + then case parseXMLDoc + (toStringLazy (fromEntry e)) of + Nothing -> e + Just d -> + toEntry "styles.xml" epochtime + ( fromStringLazy + . ppTopElement + . addLang mblang mbcountry $ d ) + else e + | e <- zEntries arch] } + +addLang :: Maybe String -> Maybe String -> Element -> Element +addLang mblang mbcountry = everywhere' (mkT updateLangAttr) + where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) l) + = Attr n (maybe l id mblang) + updateLangAttr (Attr n@(QName "country" _ (Just "fo")) c) + = Attr n (maybe c id mbcountry) + updateLangAttr x = x + -- | transform both Image and Math elements transformPicMath :: PandocMonad m => WriterOptions ->Inline -> O m Inline transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = catchError diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 95a800c94..a4c9e0ef2 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -45,7 +45,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty -import Text.Pandoc.Shared (linesToPara) +import Text.Pandoc.Shared (linesToPara, splitBy) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared @@ -75,6 +75,8 @@ data WriterState = , stTight :: Bool , stFirstPara :: Bool , stImageId :: Int + , stLang :: Maybe String + , stCountry :: Maybe String } defaultWriterState :: WriterState @@ -90,6 +92,8 @@ defaultWriterState = , stTight = False , stFirstPara = False , stImageId = 1 + , stLang = Nothing + , stCountry = Nothing } when :: Bool -> Doc -> Doc @@ -155,6 +159,10 @@ withTextStyle s f = do inTextStyle :: PandocMonad m => Doc -> OD m Doc inTextStyle d = do at <- gets stTextStyleAttr + mblang <- gets stLang + mbcountry <- gets stCountry + let langat = maybe [] (\la -> [("fo:language", la)]) mblang + let countryat = maybe [] (\co -> [("fo:country", co)]) mbcountry if Set.null at then return d else do @@ -168,8 +176,9 @@ inTextStyle d = do inTags False "style:style" [("style:name", styleName) ,("style:family", "text")] - $ selfClosingTag "style:text-properties" - (concatMap textStyleAttr (Set.toList at))) + $ selfClosingTag "style:text-properties" + (langat ++ countryat ++ + concatMap textStyleAttr (Set.toList at))) return $ inTags False "text:span" [("text:style-name",styleName)] d @@ -203,8 +212,10 @@ writeOpenDocument opts (Pandoc meta blocks) = do else Nothing let render' :: Doc -> Text render' = render colwidth + let lang = getLang opts meta + (mblang, mbcountry) <- maybe (return (Nothing, Nothing)) splitLang lang ((body, metadata),s) <- flip runStateT - defaultWriterState $ do + defaultWriterState{ stLang = mblang, stCountry = mbcountry } $ do m <- metaToJSON opts (fmap render' . blocksToOpenDocument opts) (fmap render' . inlinesToOpenDocument opts) @@ -326,7 +337,8 @@ blockToOpenDocument o bs then return empty else inParagraphTags =<< inlinesToOpenDocument o b | LineBlock b <- bs = blockToOpenDocument o $ linesToPara b - | Div _ xs <- bs = blocksToOpenDocument o xs + | Div attr xs <- bs = withLangFromAttr attr + (blocksToOpenDocument o xs) | Header i _ b <- bs = setFirstPara >> (inHeaderTags i =<< inlinesToOpenDocument o b) | BlockQuote b <- bs = setFirstPara >> mkBlockQuote b @@ -444,7 +456,7 @@ inlineToOpenDocument o ils | writerWrapText o == WrapPreserve -> return $ preformatted "\n" | otherwise -> return $ space - Span _ xs -> inlinesToOpenDocument o xs + Span attr xs -> withLangFromAttr attr (inlinesToOpenDocument o xs) LineBreak -> return $ selfClosingTag "text:line-break" [] Str s -> return $ handleSpaces $ escapeStringForXML s Emph l -> withTextStyle Italic $ inlinesToOpenDocument o l @@ -625,3 +637,14 @@ textStyleAttr s ,("style:font-name-asian" ,"Courier New") ,("style:font-name-complex" ,"Courier New")] | otherwise = [] + +withLangFromAttr :: PandocMonad m => Attr -> OD m a -> OD m a +withLangFromAttr (_,_,kvs) action = do + oldlang <- gets stLang + case lookup "lang" kvs of + Nothing -> action + Just l -> do + modify (\st -> st{ stLang = Just l}) + result <- action + modify (\st -> st{ stLang = oldlang}) + return result -- cgit v1.2.3 From 3ae4105d143dbec44afa713f6c3fa28f7a8c1d1f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 25 Jun 2017 10:38:11 +0200 Subject: Fixed support for `lang` attribute in OpenDocument and ODT writers. This improves on the last commit, which didn't work in some important ways. See #1667. --- src/Text/Pandoc/Writers/OpenDocument.hs | 35 ++++++++++++++------------------- 1 file changed, 15 insertions(+), 20 deletions(-) (limited to 'src/Text/Pandoc/Writers/OpenDocument.hs') diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index a4c9e0ef2..3a720acdc 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -36,6 +36,7 @@ import Control.Arrow ((***), (>>>)) import Control.Monad.State.Strict hiding (when) import Data.Char (chr) import Data.List (sortBy) +import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Map as Map import Data.Ord (comparing) @@ -45,7 +46,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty -import Text.Pandoc.Shared (linesToPara, splitBy) +import Text.Pandoc.Shared (linesToPara) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared @@ -75,8 +76,6 @@ data WriterState = , stTight :: Bool , stFirstPara :: Bool , stImageId :: Int - , stLang :: Maybe String - , stCountry :: Maybe String } defaultWriterState :: WriterState @@ -92,8 +91,6 @@ defaultWriterState = , stTight = False , stFirstPara = False , stImageId = 1 - , stLang = Nothing - , stCountry = Nothing } when :: Bool -> Doc -> Doc @@ -159,10 +156,6 @@ withTextStyle s f = do inTextStyle :: PandocMonad m => Doc -> OD m Doc inTextStyle d = do at <- gets stTextStyleAttr - mblang <- gets stLang - mbcountry <- gets stCountry - let langat = maybe [] (\la -> [("fo:language", la)]) mblang - let countryat = maybe [] (\co -> [("fo:country", co)]) mbcountry if Set.null at then return d else do @@ -177,8 +170,7 @@ inTextStyle d = do [("style:name", styleName) ,("style:family", "text")] $ selfClosingTag "style:text-properties" - (langat ++ countryat ++ - concatMap textStyleAttr (Set.toList at))) + (concatMap textStyleAttr (Set.toList at))) return $ inTags False "text:span" [("text:style-name",styleName)] d @@ -212,10 +204,8 @@ writeOpenDocument opts (Pandoc meta blocks) = do else Nothing let render' :: Doc -> Text render' = render colwidth - let lang = getLang opts meta - (mblang, mbcountry) <- maybe (return (Nothing, Nothing)) splitLang lang ((body, metadata),s) <- flip runStateT - defaultWriterState{ stLang = mblang, stCountry = mbcountry } $ do + defaultWriterState $ do m <- metaToJSON opts (fmap render' . blocksToOpenDocument opts) (fmap render' . inlinesToOpenDocument opts) @@ -619,6 +609,7 @@ paraTableStyles t s (a:xs) , ("style:justify-single-word", "false")] data TextStyle = Italic | Bold | Strike | Sub | Sup | SmallC | Pre + | Lang String String deriving ( Eq,Ord ) textStyleAttr :: TextStyle -> [(String,String)] @@ -636,15 +627,19 @@ textStyleAttr s | Pre <- s = [("style:font-name" ,"Courier New") ,("style:font-name-asian" ,"Courier New") ,("style:font-name-complex" ,"Courier New")] + | Lang lang country <- s + = [("fo:language" ,lang) + ,("fo:country" ,country)] | otherwise = [] withLangFromAttr :: PandocMonad m => Attr -> OD m a -> OD m a -withLangFromAttr (_,_,kvs) action = do - oldlang <- gets stLang +withLangFromAttr (_,_,kvs) action = case lookup "lang" kvs of Nothing -> action Just l -> do - modify (\st -> st{ stLang = Just l}) - result <- action - modify (\st -> st{ stLang = oldlang}) - return result + (mblang, mbcountry) <- splitLang l + case (mblang, mbcountry) of + (Just lang, _) -> withTextStyle + (Lang lang (fromMaybe "" mbcountry)) + action + _ -> action -- cgit v1.2.3 From e7cd3cb4668b119b61eb69eed857b0254a614ad9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 25 Jun 2017 15:36:30 +0200 Subject: Writers.Shared: refactored getLang, splitLang... into `Lang(..)`, `getLang`, `parceBCP47`. --- src/Text/Pandoc/Writers/Docx.hs | 8 +++++--- src/Text/Pandoc/Writers/ODT.hs | 26 ++++++++++++------------ src/Text/Pandoc/Writers/OpenDocument.hs | 22 ++++++++++++--------- src/Text/Pandoc/Writers/Shared.hs | 35 ++++++++++++++++++++++----------- 4 files changed, 55 insertions(+), 36 deletions(-) (limited to 'src/Text/Pandoc/Writers/OpenDocument.hs') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index d93b99486..52ababb14 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -67,7 +67,7 @@ import Text.Pandoc.Shared hiding (Element) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk import Text.Pandoc.Writers.Math -import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang) +import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang, renderLang) import Text.Printf (printf) import Text.TeXMath import Text.XML.Light as XML @@ -257,9 +257,11 @@ writeDocx opts doc@(Pandoc meta _) = do ) -- styles - let lang = getLang opts meta + lang <- getLang opts meta let addLang :: Element -> Element - addLang e = case lang >>= \l -> (return . XMLC.toTree . go l . XMLC.fromElement) e of + addLang e = case lang >>= \l -> + (return . XMLC.toTree . go (renderLang l) + . XMLC.fromElement) e of Just (Elem e') -> e' _ -> e -- return original where go :: String -> Cursor -> Cursor diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index dff4f8fcf..8573f5719 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -50,7 +50,8 @@ import Text.Pandoc.Shared (stringify) import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) -import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang, splitLang) +import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang, Lang(..), + renderLang) import Text.Pandoc.XML import Text.TeXMath import Text.XML.Light @@ -79,7 +80,7 @@ pandocToODT :: PandocMonad m pandocToODT opts doc@(Pandoc meta _) = do let datadir = writerUserDataDir opts let title = docTitle meta - let lang = getLang opts meta + lang <- getLang opts meta refArchive <- case writerReferenceDoc opts of Just f -> liftM toArchive $ lift $ P.readFileLazy f @@ -140,7 +141,7 @@ pandocToODT opts doc@(Pandoc meta _) = do $$ case lang of Just l -> inTagsSimple "dc:language" - (text (escapeStringForXML l)) + (text (escapeStringForXML (renderLang l))) Nothing -> empty ) ) @@ -153,10 +154,9 @@ pandocToODT opts doc@(Pandoc meta _) = do $ addEntryToArchive metaEntry archive' return $ fromArchive archive'' -updateStyleWithLang :: PandocMonad m => Maybe String -> Archive -> O m Archive +updateStyleWithLang :: PandocMonad m => Maybe Lang -> Archive -> O m Archive updateStyleWithLang Nothing arch = return arch -updateStyleWithLang (Just l) arch = do - (mblang, mbcountry) <- splitLang l +updateStyleWithLang (Just lang) arch = do epochtime <- floor `fmap` (lift P.getPOSIXTime) return arch{ zEntries = [if eRelativePath e == "styles.xml" then case parseXMLDoc @@ -166,16 +166,16 @@ updateStyleWithLang (Just l) arch = do toEntry "styles.xml" epochtime ( fromStringLazy . ppTopElement - . addLang mblang mbcountry $ d ) + . addLang lang $ d ) else e | e <- zEntries arch] } -addLang :: Maybe String -> Maybe String -> Element -> Element -addLang mblang mbcountry = everywhere' (mkT updateLangAttr) - where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) l) - = Attr n (maybe l id mblang) - updateLangAttr (Attr n@(QName "country" _ (Just "fo")) c) - = Attr n (maybe c id mbcountry) +addLang :: Lang -> Element -> Element +addLang (Lang lang country) = everywhere' (mkT updateLangAttr) + where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) _) + = Attr n lang + updateLangAttr (Attr n@(QName "country" _ (Just "fo")) _) + = Attr n country updateLangAttr x = x -- | transform both Image and Math elements diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 3a720acdc..57f3c1194 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -36,7 +36,6 @@ import Control.Arrow ((***), (>>>)) import Control.Monad.State.Strict hiding (when) import Data.Char (chr) import Data.List (sortBy) -import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Map as Map import Data.Ord (comparing) @@ -608,8 +607,14 @@ paraTableStyles t s (a:xs) [ ("fo:text-align", x) , ("style:justify-single-word", "false")] -data TextStyle = Italic | Bold | Strike | Sub | Sup | SmallC | Pre - | Lang String String +data TextStyle = Italic + | Bold + | Strike + | Sub + | Sup + | SmallC + | Pre + | Language String String deriving ( Eq,Ord ) textStyleAttr :: TextStyle -> [(String,String)] @@ -627,7 +632,7 @@ textStyleAttr s | Pre <- s = [("style:font-name" ,"Courier New") ,("style:font-name-asian" ,"Courier New") ,("style:font-name-complex" ,"Courier New")] - | Lang lang country <- s + | Language lang country <- s = [("fo:language" ,lang) ,("fo:country" ,country)] | otherwise = [] @@ -637,9 +642,8 @@ withLangFromAttr (_,_,kvs) action = case lookup "lang" kvs of Nothing -> action Just l -> do - (mblang, mbcountry) <- splitLang l - case (mblang, mbcountry) of - (Just lang, _) -> withTextStyle - (Lang lang (fromMaybe "" mbcountry)) - action + mblang <- parseBCP47 l + case mblang of + Just (Lang lang country) -> withTextStyle + (Language lang country) action _ -> action diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 0b35d27f6..efb553ac2 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -30,7 +30,9 @@ Shared utility functions for pandoc writers. -} module Text.Pandoc.Writers.Shared ( getLang - , splitLang + , parseBCP47 + , Lang(..) + , renderLang , metaToJSON , metaToJSON' , addVariablesToJSON @@ -62,30 +64,41 @@ import Text.Pandoc.Shared (splitBy) import Text.Pandoc.UTF8 (toStringLazy) import Text.Pandoc.XML (escapeStringForXML) +-- | Represents BCP 47 language/country code. +data Lang = Lang String String + +-- | Render a Lang as BCP 47. +renderLang :: Lang -> String +renderLang (Lang la co) = la ++ if null co + then "" + else '-':co + -- | Get the contents of the `lang` metadata field or variable. -getLang :: WriterOptions -> Meta -> Maybe String -getLang opts meta = - lookup "lang" (writerVariables opts) +getLang :: PandocMonad m => WriterOptions -> Meta -> m (Maybe Lang) +getLang opts meta = maybe (return Nothing) parseBCP47 $ + case lookup "lang" (writerVariables opts) of + Just s -> Just s + _ -> Nothing `mplus` case lookupMeta "lang" meta of Just (MetaInlines [Str s]) -> Just s Just (MetaString s) -> Just s _ -> Nothing --- | Split `lang` field into lang and country, issuing warning --- if it doesn't look valid. -splitLang :: PandocMonad m => String -> m (Maybe String, Maybe String) -splitLang lang = +-- | Parse a BCP 47 string as a Lang, issuing a warning if there +-- are issues. +parseBCP47 :: PandocMonad m => String -> m (Maybe Lang) +parseBCP47 lang = case splitBy (== '-') lang of [la,co] | length la == 2 && length co == 2 - -> return (Just la, Just co) + -> return $ Just $ Lang la co [la] | length la == 2 - -> return (Just la, Nothing) + -> return $ Just $ Lang la "" _ -> do report $ InvalidLang lang - return (Nothing, Nothing) + return Nothing -- | Create JSON value for template from a 'Meta' and an association list -- of variables, specified at the command line or in the writer. -- cgit v1.2.3 From 643cbdf1044623475cb6ade9c35de85148d0dff6 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 25 Jun 2017 18:31:59 +0200 Subject: Writers.Shared: improve type of Lang and bcp47 parser. Use a real parsec parser for BCP47, include variants. --- src/Text/Pandoc/Writers/ODT.hs | 6 +-- src/Text/Pandoc/Writers/OpenDocument.hs | 18 +++---- src/Text/Pandoc/Writers/Shared.hs | 96 +++++++++++++++++++++++---------- 3 files changed, 79 insertions(+), 41 deletions(-) (limited to 'src/Text/Pandoc/Writers/OpenDocument.hs') diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 8573f5719..54873efb2 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -171,11 +171,11 @@ updateStyleWithLang (Just lang) arch = do | e <- zEntries arch] } addLang :: Lang -> Element -> Element -addLang (Lang lang country) = everywhere' (mkT updateLangAttr) +addLang lang = everywhere' (mkT updateLangAttr) where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) _) - = Attr n lang + = Attr n (langLanguage lang) updateLangAttr (Attr n@(QName "country" _ (Just "fo")) _) - = Attr n country + = Attr n (langRegion lang) updateLangAttr x = x -- | transform both Image and Math elements diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 57f3c1194..763cea5ad 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -614,7 +614,7 @@ data TextStyle = Italic | Sup | SmallC | Pre - | Language String String + | Language Lang deriving ( Eq,Ord ) textStyleAttr :: TextStyle -> [(String,String)] @@ -632,9 +632,9 @@ textStyleAttr s | Pre <- s = [("style:font-name" ,"Courier New") ,("style:font-name-asian" ,"Courier New") ,("style:font-name-complex" ,"Courier New")] - | Language lang country <- s - = [("fo:language" ,lang) - ,("fo:country" ,country)] + | Language lang <- s + = [("fo:language" ,langLanguage lang) + ,("fo:country" ,langRegion lang)] | otherwise = [] withLangFromAttr :: PandocMonad m => Attr -> OD m a -> OD m a @@ -642,8 +642,8 @@ withLangFromAttr (_,_,kvs) action = case lookup "lang" kvs of Nothing -> action Just l -> do - mblang <- parseBCP47 l - case mblang of - Just (Lang lang country) -> withTextStyle - (Language lang country) action - _ -> action + case parseBCP47 l of + Right lang -> withTextStyle (Language lang) action + Left _ -> do + report $ InvalidLang l + action diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index efb553ac2..b56f2d468 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -46,11 +46,12 @@ module Text.Pandoc.Writers.Shared ( , gridTable ) where -import Control.Monad (liftM, zipWithM, mplus) +import Control.Monad (liftM, zipWithM, guard) import Data.Aeson (FromJSON (..), Result (..), ToJSON (..), Value (Object), encode, fromJSON) +import Data.Char (isAscii, isLetter, isUpper, isLower) import qualified Data.HashMap.Strict as H -import Data.List (groupBy, intersperse, transpose) +import Data.List (groupBy, intersperse, transpose, intercalate) import qualified Data.Map as M import Data.Maybe (isJust) import qualified Data.Text as T @@ -60,45 +61,82 @@ import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty -import Text.Pandoc.Shared (splitBy) import Text.Pandoc.UTF8 (toStringLazy) import Text.Pandoc.XML (escapeStringForXML) +import qualified Text.Parsec as P -- | Represents BCP 47 language/country code. -data Lang = Lang String String +data Lang = Lang{ langLanguage :: String + , langScript :: String + , langRegion :: String + , langVariants :: [String] } + deriving (Eq, Ord, Show) -- | Render a Lang as BCP 47. renderLang :: Lang -> String -renderLang (Lang la co) = la ++ if null co - then "" - else '-':co +renderLang lang = intercalate "-" (langLanguage lang : filter (not . null) + ([langScript lang, langRegion lang] ++ langVariants lang)) -- | Get the contents of the `lang` metadata field or variable. getLang :: PandocMonad m => WriterOptions -> Meta -> m (Maybe Lang) -getLang opts meta = maybe (return Nothing) parseBCP47 $ - case lookup "lang" (writerVariables opts) of - Just s -> Just s - _ -> Nothing - `mplus` - case lookupMeta "lang" meta of - Just (MetaInlines [Str s]) -> Just s - Just (MetaString s) -> Just s - _ -> Nothing +getLang opts meta = case + (case lookup "lang" (writerVariables opts) of + Just s -> Just s + _ -> + case lookupMeta "lang" meta of + Just (MetaInlines [Str s]) -> Just s + Just (MetaString s) -> Just s + _ -> Nothing) of + Nothing -> return Nothing + Just s -> case parseBCP47 s of + Left _ -> do + report $ InvalidLang s + return Nothing + Right l -> return (Just l) --- | Parse a BCP 47 string as a Lang, issuing a warning if there --- are issues. -parseBCP47 :: PandocMonad m => String -> m (Maybe Lang) +-- | Parse a BCP 47 string as a Lang. +parseBCP47 :: String -> Either String Lang parseBCP47 lang = - case splitBy (== '-') lang of - [la,co] - | length la == 2 && length co == 2 - -> return $ Just $ Lang la co - [la] - | length la == 2 - -> return $ Just $ Lang la "" - _ -> do - report $ InvalidLang lang - return Nothing + case P.parse bcp47 "lang" lang of + Right r -> Right r + Left e -> Left $ show e + where bcp47 = do + language <- pLanguage + script <- P.option "" pScript + region <- P.option "" pRegion + variants <- P.many pVariant + () <$ P.char '-' P.<|> P.eof + return $ Lang{ langLanguage = language + , langScript = script + , langRegion = region + , langVariants = variants } + asciiLetter = P.satisfy (\c -> isAscii c && isLetter c) + pLanguage = do + cs <- P.many1 asciiLetter + let lcs = length cs + guard $ lcs == 2 || lcs == 3 + return cs + pScript = P.try $ do + P.char '-' + x <- P.satisfy (\c -> isAscii c && isLetter c && isUpper c) + xs <- P.count 3 + (P.satisfy (\c -> isAscii c && isLetter c && isLower c)) + return (x:xs) + pRegion = P.try $ do + P.char '-' + cs <- P.many1 asciiLetter + let lcs = length cs + guard $ lcs == 2 || lcs == 3 + return cs + pVariant = P.try $ do + P.char '-' + ds <- P.option "" (P.count 1 P.digit) + cs <- P.many1 asciiLetter + let var = ds ++ cs + guard $ if null ds + then length var >= 5 && length var <= 8 + else length var == 4 + return var -- | Create JSON value for template from a 'Meta' and an association list -- of variables, specified at the command line or in the writer. -- cgit v1.2.3 From ac9423eccc76005f996a10a545594247ac753e02 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 25 Jun 2017 21:00:35 +0200 Subject: Moved BCP47 specific functions from Writers.Shared to new module. Text.Pandoc.BCP47 (unexported, internal module). `getLang`, `Lang(..)`, `parseBCP47`. --- pandoc.cabal | 1 + src/Text/Pandoc/BCP47.hs | 117 ++++++++++++++++++++++++++++++++ src/Text/Pandoc/Writers/Docx.hs | 3 +- src/Text/Pandoc/Writers/ODT.hs | 4 +- src/Text/Pandoc/Writers/OpenDocument.hs | 1 + src/Text/Pandoc/Writers/Shared.hs | 87 +----------------------- 6 files changed, 126 insertions(+), 87 deletions(-) create mode 100644 src/Text/Pandoc/BCP47.hs (limited to 'src/Text/Pandoc/Writers/OpenDocument.hs') diff --git a/pandoc.cabal b/pandoc.cabal index 3b644c7d0..5ae255284 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -463,6 +463,7 @@ Library Text.Pandoc.Lua.Util, Text.Pandoc.CSS, Text.Pandoc.UUID, + Text.Pandoc.BCP47 Text.Pandoc.Slides, Text.Pandoc.Compat.Time, Paths_pandoc diff --git a/src/Text/Pandoc/BCP47.hs b/src/Text/Pandoc/BCP47.hs new file mode 100644 index 000000000..ae7f54473 --- /dev/null +++ b/src/Text/Pandoc/BCP47.hs @@ -0,0 +1,117 @@ +{- +Copyright (C) 2017 John MacFarlane + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +-} + +{- | + Module : Text.Pandoc.BCP47 + Copyright : Copyright (C) 2017 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane + Stability : alpha + Portability : portable + +Functions for parsing and rendering BCP47 language identifiers. +-} +module Text.Pandoc.BCP47 ( + getLang + , parseBCP47 + , Lang(..) + , renderLang + ) +where +import Control.Monad (guard) +import Data.Char (isAscii, isLetter, isUpper, isLower) +import Data.List (intercalate) +import Text.Pandoc.Definition +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Logging +import Text.Pandoc.Options +import qualified Text.Parsec as P + +-- | Represents BCP 47 language/country code. +data Lang = Lang{ langLanguage :: String + , langScript :: String + , langRegion :: String + , langVariants :: [String] } + deriving (Eq, Ord, Show) + +-- | Render a Lang as BCP 47. +renderLang :: Lang -> String +renderLang lang = intercalate "-" (langLanguage lang : filter (not . null) + ([langScript lang, langRegion lang] ++ langVariants lang)) + +-- | Get the contents of the `lang` metadata field or variable. +getLang :: PandocMonad m => WriterOptions -> Meta -> m (Maybe Lang) +getLang opts meta = case + (case lookup "lang" (writerVariables opts) of + Just s -> Just s + _ -> + case lookupMeta "lang" meta of + Just (MetaInlines [Str s]) -> Just s + Just (MetaString s) -> Just s + _ -> Nothing) of + Nothing -> return Nothing + Just s -> case parseBCP47 s of + Left _ -> do + report $ InvalidLang s + return Nothing + Right l -> return (Just l) + +-- | Parse a BCP 47 string as a Lang. +parseBCP47 :: String -> Either String Lang +parseBCP47 lang = + case P.parse bcp47 "lang" lang of + Right r -> Right r + Left e -> Left $ show e + where bcp47 = do + language <- pLanguage + script <- P.option "" pScript + region <- P.option "" pRegion + variants <- P.many pVariant + () <$ P.char '-' P.<|> P.eof + return $ Lang{ langLanguage = language + , langScript = script + , langRegion = region + , langVariants = variants } + asciiLetter = P.satisfy (\c -> isAscii c && isLetter c) + pLanguage = do + cs <- P.many1 asciiLetter + let lcs = length cs + guard $ lcs == 2 || lcs == 3 + return cs + pScript = P.try $ do + P.char '-' + x <- P.satisfy (\c -> isAscii c && isLetter c && isUpper c) + xs <- P.count 3 + (P.satisfy (\c -> isAscii c && isLetter c && isLower c)) + return (x:xs) + pRegion = P.try $ do + P.char '-' + cs <- P.many1 asciiLetter + let lcs = length cs + guard $ lcs == 2 || lcs == 3 + return cs + pVariant = P.try $ do + P.char '-' + ds <- P.option "" (P.count 1 P.digit) + cs <- P.many1 asciiLetter + let var = ds ++ cs + guard $ if null ds + then length var >= 5 && length var <= 8 + else length var == 4 + return var diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 52ababb14..bc8568cd1 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -67,7 +67,8 @@ import Text.Pandoc.Shared hiding (Element) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk import Text.Pandoc.Writers.Math -import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang, renderLang) +import Text.Pandoc.Writers.Shared (fixDisplayMath) +import Text.Pandoc.BCP47 (getLang, renderLang) import Text.Printf (printf) import Text.TeXMath import Text.XML.Light as XML diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 54873efb2..98aa3b30b 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -50,8 +50,8 @@ import Text.Pandoc.Shared (stringify) import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) -import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang, Lang(..), - renderLang) +import Text.Pandoc.Writers.Shared (fixDisplayMath) +import Text.Pandoc.BCP47 (getLang, Lang(..), renderLang) import Text.Pandoc.XML import Text.TeXMath import Text.XML.Light diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 763cea5ad..6c53ab4ab 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -50,6 +50,7 @@ import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import Text.Pandoc.XML +import Text.Pandoc.BCP47 (parseBCP47, Lang(..)) import Text.Printf (printf) -- | Auxiliary function to convert Plain block to Para. diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index b56f2d468..2047285eb 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -29,11 +29,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Shared utility functions for pandoc writers. -} module Text.Pandoc.Writers.Shared ( - getLang - , parseBCP47 - , Lang(..) - , renderLang - , metaToJSON + metaToJSON , metaToJSON' , addVariablesToJSON , getField @@ -46,97 +42,20 @@ module Text.Pandoc.Writers.Shared ( , gridTable ) where -import Control.Monad (liftM, zipWithM, guard) +import Control.Monad (liftM, zipWithM) import Data.Aeson (FromJSON (..), Result (..), ToJSON (..), Value (Object), encode, fromJSON) -import Data.Char (isAscii, isLetter, isUpper, isLower) import qualified Data.HashMap.Strict as H -import Data.List (groupBy, intersperse, transpose, intercalate) +import Data.List (groupBy, intersperse, transpose) import qualified Data.Map as M import Data.Maybe (isJust) import qualified Data.Text as T import qualified Data.Traversable as Traversable import Text.Pandoc.Definition -import Text.Pandoc.Class (PandocMonad, report) -import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty import Text.Pandoc.UTF8 (toStringLazy) import Text.Pandoc.XML (escapeStringForXML) -import qualified Text.Parsec as P - --- | Represents BCP 47 language/country code. -data Lang = Lang{ langLanguage :: String - , langScript :: String - , langRegion :: String - , langVariants :: [String] } - deriving (Eq, Ord, Show) - --- | Render a Lang as BCP 47. -renderLang :: Lang -> String -renderLang lang = intercalate "-" (langLanguage lang : filter (not . null) - ([langScript lang, langRegion lang] ++ langVariants lang)) - --- | Get the contents of the `lang` metadata field or variable. -getLang :: PandocMonad m => WriterOptions -> Meta -> m (Maybe Lang) -getLang opts meta = case - (case lookup "lang" (writerVariables opts) of - Just s -> Just s - _ -> - case lookupMeta "lang" meta of - Just (MetaInlines [Str s]) -> Just s - Just (MetaString s) -> Just s - _ -> Nothing) of - Nothing -> return Nothing - Just s -> case parseBCP47 s of - Left _ -> do - report $ InvalidLang s - return Nothing - Right l -> return (Just l) - --- | Parse a BCP 47 string as a Lang. -parseBCP47 :: String -> Either String Lang -parseBCP47 lang = - case P.parse bcp47 "lang" lang of - Right r -> Right r - Left e -> Left $ show e - where bcp47 = do - language <- pLanguage - script <- P.option "" pScript - region <- P.option "" pRegion - variants <- P.many pVariant - () <$ P.char '-' P.<|> P.eof - return $ Lang{ langLanguage = language - , langScript = script - , langRegion = region - , langVariants = variants } - asciiLetter = P.satisfy (\c -> isAscii c && isLetter c) - pLanguage = do - cs <- P.many1 asciiLetter - let lcs = length cs - guard $ lcs == 2 || lcs == 3 - return cs - pScript = P.try $ do - P.char '-' - x <- P.satisfy (\c -> isAscii c && isLetter c && isUpper c) - xs <- P.count 3 - (P.satisfy (\c -> isAscii c && isLetter c && isLower c)) - return (x:xs) - pRegion = P.try $ do - P.char '-' - cs <- P.many1 asciiLetter - let lcs = length cs - guard $ lcs == 2 || lcs == 3 - return cs - pVariant = P.try $ do - P.char '-' - ds <- P.option "" (P.count 1 P.digit) - cs <- P.many1 asciiLetter - let var = ds ++ cs - guard $ if null ds - then length var >= 5 && length var <= 8 - else length var == 4 - return var -- | Create JSON value for template from a 'Meta' and an association list -- of variables, specified at the command line or in the writer. -- cgit v1.2.3 From 19d9482fc400cf486547b6a670c946d3634401cf Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 26 Jun 2017 16:46:56 +0200 Subject: OpenDocument/ODT writer: Added support for table of contents. Closes #2836. Thanks to @anayrat. --- MANUAL.txt | 3 ++- src/Text/Pandoc/Writers/OpenDocument.hs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text/Pandoc/Writers/OpenDocument.hs') diff --git a/MANUAL.txt b/MANUAL.txt index 6499426e1..b5cea779e 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1228,7 +1228,8 @@ as the following: : non-null value if `--toc/--table-of-contents` was specified `toc-title` -: title of table of contents (works only with EPUB and docx) +: title of table of contents (works only with EPUB, + opendocument, odt, docx) `include-before` : contents specified by `-B/--include-before-body` (may have diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 6c53ab4ab..ed3dabb87 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -220,6 +220,7 @@ writeOpenDocument opts (Pandoc meta blocks) = do let listStyles = map listStyle (stListStyles s) let automaticStyles = vcat $ reverse $ styles ++ listStyles let context = defField "body" body + $ defField "toc" (writerTableOfContents opts) $ defField "automatic-styles" (render' automaticStyles) $ metadata case writerTemplate opts of -- cgit v1.2.3