diff options
Diffstat (limited to 'src/Text/Pandoc/Writers')
-rw-r--r-- | src/Text/Pandoc/Writers/AsciiDoc.hs | 1 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/ConTeXt.hs | 84 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/Custom.hs | 1 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/Docbook.hs | 1 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/Docx.hs | 21 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/DokuWiki.hs | 1 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/EPUB.hs | 27 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/HTML.hs | 35 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/Haddock.hs | 4 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/ICML.hs | 5 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/LaTeX.hs | 345 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/Man.hs | 2 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/Markdown.hs | 24 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/Native.hs | 2 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/ODT.hs | 1 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/OPML.hs | 13 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/OpenDocument.hs | 4 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/Org.hs | 1 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/RST.hs | 17 | ||||
-rw-r--r-- | src/Text/Pandoc/Writers/Textile.hs | 28 |
20 files changed, 475 insertions, 142 deletions
diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index 4e8c96907..174b00dac 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -51,7 +51,6 @@ import Control.Monad.State import qualified Data.Map as M import Data.Aeson (Value(String), fromJSON, toJSON, Result(..)) import qualified Data.Text as T -import Control.Applicative ((<*), (*>)) data WriterState = WriterState { defListMarker :: String , orderedListLevel :: Int diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 56fcd4b0b..bbc5f7132 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -81,16 +81,21 @@ pandocToConTeXt options (Pandoc meta blocks) = do "subsubsubsection","subsubsubsubsection"]) $ defField "body" main $ defField "number-sections" (writerNumberSections options) - $ defField "mainlang" (maybe "" - (reverse . takeWhile (/=',') . reverse) - (lookup "lang" $ writerVariables options)) $ metadata + let context' = defField "context-lang" (maybe "" (fromBcp47 . splitBy (=='-')) $ + getField "lang" context) + $ defField "context-dir" (toContextDir $ getField "dir" context) + $ context return $ if writerStandalone options - then renderTemplate' (writerTemplate options) context + then renderTemplate' (writerTemplate options) context' else main --- escape things as needed for ConTeXt +toContextDir :: Maybe String -> String +toContextDir (Just "rtl") = "r2l" +toContextDir (Just "ltr") = "l2r" +toContextDir _ = "" +-- | escape things as needed for ConTeXt escapeCharForConTeXt :: WriterOptions -> Char -> String escapeCharForConTeXt opts ch = let ligatures = writerTeXLigatures opts in @@ -156,13 +161,22 @@ blockToConTeXt (CodeBlock _ str) = -- blankline because \stoptyping can't have anything after it, inc. '}' blockToConTeXt (RawBlock "context" str) = return $ text str <> blankline blockToConTeXt (RawBlock _ _ ) = return empty -blockToConTeXt (Div (ident,_,_) bs) = do - contents <- blockListToConTeXt bs - if null ident - then return contents - else return $ - ("\\reference" <> brackets (text $ toLabel ident) <> braces empty <> - "%") $$ contents +blockToConTeXt (Div (ident,_,kvs) bs) = do + let align dir txt = "\\startalignment[" <> dir <> "]" $$ txt $$ "\\stopalignment" + let wrapRef txt = if null ident + then txt + else ("\\reference" <> brackets (text $ toLabel ident) <> + braces empty <> "%") $$ txt + wrapDir = case lookup "dir" kvs of + Just "rtl" -> align "righttoleft" + Just "ltr" -> align "lefttoright" + _ -> id + wrapLang txt = case lookup "lang" kvs of + Just lng -> "\\start\\language[" + <> text (fromBcp47' lng) <> "]" $$ txt $$ "\\stop" + Nothing -> txt + wrapBlank txt = blankline <> txt <> blankline + fmap (wrapBlank . wrapLang . wrapDir . wrapRef) $ blockListToConTeXt bs blockToConTeXt (BulletList lst) = do contents <- mapM listItemToConTeXt lst return $ ("\\startitemize" <> if isTightList lst @@ -358,7 +372,16 @@ inlineToConTeXt (Note contents) = do then text "\\footnote{" <> nest 2 contents' <> char '}' else text "\\startbuffer " <> nest 2 contents' <> text "\\stopbuffer\\footnote{\\getbuffer}" -inlineToConTeXt (Span _ ils) = inlineListToConTeXt ils +inlineToConTeXt (Span (_,_,kvs) ils) = do + let wrapDir txt = case lookup "dir" kvs of + Just "rtl" -> braces $ "\\righttoleft " <> txt + Just "ltr" -> braces $ "\\lefttoright " <> txt + _ -> txt + wrapLang txt = case lookup "lang" kvs of + Just lng -> "\\start\\language[" <> text (fromBcp47' lng) + <> "]" <> txt <> "\\stop " + Nothing -> txt + fmap (wrapLang . wrapDir) $ inlineListToConTeXt ils -- | Craft the section header, inserting the secton reference, if supplied. sectionHeader :: Attr @@ -385,3 +408,38 @@ sectionHeader (ident,classes,_) hdrLevel lst = do then char '\\' <> chapter <> braces contents else contents <> blankline +fromBcp47' :: String -> String +fromBcp47' = fromBcp47 . splitBy (=='-') + +-- Takes a list of the constituents of a BCP 47 language code +-- and irons out ConTeXt's exceptions +-- https://tools.ietf.org/html/bcp47#section-2.1 +-- http://wiki.contextgarden.net/Language_Codes +fromBcp47 :: [String] -> String +fromBcp47 [] = "" +fromBcp47 ("ar":"SY":_) = "ar-sy" +fromBcp47 ("ar":"IQ":_) = "ar-iq" +fromBcp47 ("ar":"JO":_) = "ar-jo" +fromBcp47 ("ar":"LB":_) = "ar-lb" +fromBcp47 ("ar":"DZ":_) = "ar-dz" +fromBcp47 ("ar":"MA":_) = "ar-ma" +fromBcp47 ("de":"1901":_) = "deo" +fromBcp47 ("de":"DE":_) = "de-de" +fromBcp47 ("de":"AT":_) = "de-at" +fromBcp47 ("de":"CH":_) = "de-ch" +fromBcp47 ("el":"poly":_) = "agr" +fromBcp47 ("en":"US":_) = "en-us" +fromBcp47 ("en":"GB":_) = "en-gb" +fromBcp47 ("grc":_) = "agr" +fromBcp47 x = fromIso $ head x + where + fromIso "cz" = "cs" + fromIso "el" = "gr" + fromIso "eu" = "ba" + fromIso "he" = "il" + fromIso "jp" = "ja" + fromIso "uk" = "ua" + fromIso "vi" = "vn" + fromIso "zh" = "cn" + fromIso l = l + diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 8f2810932..e89828911 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -44,7 +44,6 @@ import Scripting.Lua (LuaState, StackValue, callfunc) import Text.Pandoc.Writers.Shared import qualified Scripting.Lua as Lua import qualified Text.Pandoc.UTF8 as UTF8 -import Data.Monoid import Control.Monad (when) import Control.Exception import qualified Data.Map as M diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index e3444d257..d2c39e3b9 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -39,7 +39,6 @@ import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Readers.TeXMath import Data.List ( stripPrefix, isPrefixOf, intercalate, isSuffixOf ) import Data.Char ( toLower ) -import Control.Applicative ((<$>)) import Data.Monoid ( Any(..) ) import Text.Pandoc.Highlighting ( languages, languagesByExtension ) import Text.Pandoc.Pretty diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index e9f256210..dd4a4b258 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -35,13 +35,11 @@ import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BL8 import qualified Data.Map as M import qualified Text.Pandoc.UTF8 as UTF8 -import Text.Pandoc.Compat.Monoid ((<>)) import Codec.Archive.Zip import Data.Time.Clock.POSIX import Data.Time.Clock -import Data.Time.Format import System.Environment -import Text.Pandoc.Compat.Locale (defaultTimeLocale) +import Text.Pandoc.Compat.Time import Text.Pandoc.Definition import Text.Pandoc.Generic import Text.Pandoc.ImageSize @@ -62,9 +60,10 @@ import Data.Unique (hashUnique, newUnique) import System.Random (randomRIO) import Text.Printf (printf) import qualified Control.Exception as E +import Text.Pandoc.Compat.Monoid ((<>)) import Text.Pandoc.MIME (MimeType, getMimeType, getMimeTypeDef, extensionFromMimeType) -import Control.Applicative ((<$>), (<|>), (<*>)) +import Control.Applicative ((<|>)) import Data.Maybe (fromMaybe, mapMaybe, maybeToList) import Data.Char (ord) @@ -181,8 +180,8 @@ renumIds f renumMap = map (renumId f renumMap) -- | Certain characters are invalid in XML even if escaped. -- See #1992 -stripInvalidChars :: Pandoc -> Pandoc -stripInvalidChars = bottomUp (filter isValidChar) +stripInvalidChars :: String -> String +stripInvalidChars = filter isValidChar -- | See XML reference isValidChar :: Char -> Bool @@ -208,10 +207,10 @@ writeDocx :: WriterOptions -- ^ Writer options -> IO BL.ByteString writeDocx opts doc@(Pandoc meta _) = do let datadir = writerUserDataDir opts - let doc' = stripInvalidChars . walk fixDisplayMath $ doc + let doc' = walk fixDisplayMath $ doc username <- lookup "USERNAME" <$> getEnvironment utctime <- getCurrentTime - distArchive <- getDefaultReferenceDocx Nothing + distArchive <- getDefaultReferenceDocx datadir refArchive <- case writerReferenceDocx opts of Just f -> liftM (toArchive . toLazy) $ B.readFile f Nothing -> getDefaultReferenceDocx datadir @@ -973,7 +972,7 @@ formattedString str = do return [ mknode "w:r" [] $ props ++ [ mknode (if inDel then "w:delText" else "w:t") - [("xml:space","preserve")] str ] ] + [("xml:space","preserve")] (stripInvalidChars str) ] ] setFirstPara :: WS () setFirstPara = modify $ \s -> s { stFirstPara = True } @@ -1070,8 +1069,8 @@ inlineToOpenXML opts (Note bs) = do [ mknode "w:rPr" [] footnoteStyle , mknode "w:footnoteRef" [] () ] let notemarkerXml = RawInline (Format "openxml") $ ppElement notemarker - let insertNoteRef (Plain ils : xs) = Plain (notemarkerXml : ils) : xs - insertNoteRef (Para ils : xs) = Para (notemarkerXml : ils) : xs + let insertNoteRef (Plain ils : xs) = Plain (notemarkerXml : Space : ils) : xs + insertNoteRef (Para ils : xs) = Para (notemarkerXml : Space : ils) : xs insertNoteRef xs = Para [notemarkerXml] : xs oldListLevel <- gets stListLevel oldParaProperties <- gets stParaProperties diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index ebd5f8d70..730b31fe8 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -55,7 +55,6 @@ import Network.URI ( isURI ) import Control.Monad ( zipWithM ) import Control.Monad.State ( modify, State, get, evalState ) import Control.Monad.Reader ( ReaderT, runReaderT, ask, local ) -import Control.Applicative ( (<$>) ) data WriterState = WriterState { stNotes :: Bool -- True if there are notes diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index c3e295c8f..f4989c8ea 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -37,16 +37,14 @@ import System.Environment ( getEnv ) import Text.Printf (printf) import System.FilePath ( takeExtension, takeFileName ) import System.FilePath.Glob ( namesMatching ) +import Network.HTTP ( urlEncode ) import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy.Char8 as B8 import qualified Text.Pandoc.UTF8 as UTF8 -import Text.Pandoc.SelfContained ( makeSelfContained ) import Codec.Archive.Zip ( emptyArchive, addEntryToArchive, eRelativePath, fromEntry , Entry, toEntry, fromArchive) -import Control.Applicative ((<$>)) import Data.Time.Clock.POSIX ( getPOSIXTime ) -import Data.Time (getCurrentTime,UTCTime, formatTime) -import Text.Pandoc.Compat.Locale ( defaultTimeLocale ) -import Text.Pandoc.Shared ( trimr, renderTags', safeRead, uniqueIdent, trim +import Text.Pandoc.Compat.Time +import Text.Pandoc.Shared ( renderTags', safeRead, uniqueIdent, trim , normalizeDate, readDataFile, stringify, warn , hierarchicalize, fetchItem' ) import qualified Text.Pandoc.Shared as S (Element(..)) @@ -65,7 +63,7 @@ import Text.XML.Light ( unode, Element(..), unqual, Attr(..), add_attrs , strContent, lookupAttr, Node(..), QName(..), parseXML , onlyElems, node, ppElement) import Text.Pandoc.UUID (getRandomUUID) -import Text.Pandoc.Writers.HTML (writeHtmlString, writeHtml) +import Text.Pandoc.Writers.HTML ( writeHtml ) import Data.Char ( toLower, isDigit, isAlphaNum ) import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) import qualified Control.Exception as E @@ -818,7 +816,8 @@ transformTag :: WriterOptions -> Tag String -> IO (Tag String) transformTag opts mediaRef tag@(TagOpen name attr) - | name `elem` ["video", "source", "img", "audio"] = do + | name `elem` ["video", "source", "img", "audio"] && + lookup "data-external" attr == Nothing = do let src = fromAttrib "src" tag let poster = fromAttrib "poster" tag newsrc <- modifyMediaRef opts mediaRef src @@ -874,10 +873,11 @@ transformInline :: WriterOptions transformInline opts mediaRef (Image attr lab (src,tit)) = do newsrc <- modifyMediaRef opts mediaRef src return $ Image attr lab (newsrc, tit) -transformInline opts _ (x@(Math _ _)) - | WebTeX _ <- writerHTMLMathMethod opts = do - raw <- makeSelfContained opts $ writeHtmlInline opts x - return $ RawInline (Format "html") raw +transformInline opts mediaRef (x@(Math t m)) + | WebTeX url <- writerHTMLMathMethod opts = do + newsrc <- modifyMediaRef opts mediaRef (url ++ urlEncode m) + let mathclass = if t == DisplayMath then "display" else "inline" + return $ Span ("",["math",mathclass],[]) [Image nullAttr [x] (newsrc, "")] transformInline opts mediaRef (RawInline fmt raw) | fmt == Format "html" = do let tags = parseTags raw @@ -885,11 +885,6 @@ transformInline opts mediaRef (RawInline fmt raw) return $ RawInline fmt (renderTags' tags') transformInline _ _ x = return x -writeHtmlInline :: WriterOptions -> Inline -> String -writeHtmlInline opts z = trimr $ - writeHtmlString opts{ writerStandalone = False } - $ Pandoc nullMeta [Plain [z]] - (!) :: Node t => (t -> Element) -> [(String, String)] -> t -> Element (!) f attrs n = add_attrs (map (\(k,v) -> Attr (unqual k) v) attrs) (f n) diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index ab158b38d..67d398a4d 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -31,6 +31,7 @@ Conversion of 'Pandoc' documents to HTML. -} module Text.Pandoc.Writers.HTML ( writeHtml , writeHtmlString ) where import Text.Pandoc.Definition +import Text.Pandoc.Compat.Monoid ((<>)) import Text.Pandoc.Shared import Text.Pandoc.Writers.Shared import Text.Pandoc.Options @@ -67,9 +68,7 @@ import Text.XML.Light.Output import Text.XML.Light (unode, elChildren, unqual) import qualified Text.XML.Light as XML import System.FilePath (takeExtension) -import Data.Monoid import Data.Aeson (Value) -import Control.Applicative ((<$>)) data WriterState = WriterState { stNotes :: [Html] -- ^ List of notes @@ -195,9 +194,6 @@ pandocToHtml opts (Pandoc meta blocks) = do defField "revealjs-url" ("reveal.js" :: String) $ defField "s5-url" ("s5/default" :: String) $ defField "html5" (writerHtml5 opts) $ - defField "center" (case lookupMeta "center" meta of - Just (MetaBool False) -> False - _ -> True) $ metadata return (thebody, context) @@ -310,11 +306,9 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen $ if titleSlide -- title slides have no content of their own then filter isSec elements - else if slide - then case splitBy isPause elements of - [] -> [] - (x:xs) -> x ++ concatMap inDiv xs - else elements + else case splitBy isPause elements of + [] -> [] + (x:xs) -> x ++ concatMap inDiv xs let inNl x = mconcat $ nl opts : intersperse (nl opts) x ++ [nl opts] let classes' = ["titleslide" | titleSlide] ++ ["slide" | slide] ++ ["section" | (slide || writerSectionDivs opts) && @@ -471,12 +465,15 @@ blockToHtml opts (Para [Image attr txt (s,'f':'i':'g':':':tit)]) = do blockToHtml opts (Para lst) = do contents <- inlineListToHtml opts lst return $ H.p contents -blockToHtml opts (Div attr@(_,classes,_) bs) = do +blockToHtml opts (Div attr@(ident, classes, kvs) bs) = do let speakerNotes = "notes" `elem` classes -- we don't want incremental output inside speaker notes, see #1394 let opts' = if speakerNotes then opts{ writerIncremental = False } else opts contents <- blockListToHtml opts' bs let contents' = nl opts >> contents >> nl opts + let (divtag, classes') = if writerHtml5 opts && "section" `elem` classes + then (H5.section, filter (/= "section") classes) + else (H.div, classes) return $ if speakerNotes then case writerSlideVariant opts of @@ -485,7 +482,7 @@ blockToHtml opts (Div attr@(_,classes,_) bs) = do ! (H5.customAttribute "role" "note") NoSlides -> addAttrs opts' attr $ H.div $ contents' _ -> mempty - else addAttrs opts attr $ H.div $ contents' + else addAttrs opts (ident, classes', kvs) $ divtag $ contents' blockToHtml opts (RawBlock f str) | f == Format "html" = return $ preEscapedString str | f == Format "latex" = @@ -565,6 +562,9 @@ blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do let attribs = (if startnum /= 1 then [A.start $ toValue startnum] else []) ++ + (if numstyle == Example + then [A.class_ "example"] + else []) ++ (if numstyle /= DefaultStyle then if writerHtml5 opts then [A.type_ $ @@ -615,8 +615,15 @@ blockToHtml opts (Table capt aligns widths headers rows') = do return $ H.thead (nl opts >> contents) >> nl opts body' <- liftM (\x -> H.tbody (nl opts >> mconcat x)) $ zipWithM (tableRowToHtml opts aligns) [1..] rows' - return $ H.table $ nl opts >> captionDoc >> coltags >> head' >> - body' >> nl opts + let tbl = H.table $ + nl opts >> captionDoc >> coltags >> head' >> body' >> nl opts + let totalWidth = sum widths + -- When widths of columns are < 100%, we need to set width for the whole + -- table, or some browsers give us skinny columns with lots of space between: + return $ if totalWidth == 0 || totalWidth == 1 + then tbl + else tbl ! A.style (toValue $ "width:" ++ + show (round (totalWidth * 100) :: Int) ++ "%;") tableRowToHtml :: WriterOptions -> [Alignment] diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index a3188c647..118d42d7d 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -327,8 +327,8 @@ inlineToHaddock _ (RawInline f str) inlineToHaddock _ (LineBreak) = return cr inlineToHaddock _ Space = return space inlineToHaddock opts (Cite _ lst) = inlineListToHaddock opts lst -inlineToHaddock opts (Link _ txt (src, _)) = do - linktext <- inlineListToHaddock opts txt +inlineToHaddock _ (Link _ txt (src, _)) = do + let linktext = text $ escapeString $ stringify txt let useAuto = isURI src && case txt of [Str s] | escapeURI s == src -> True diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 2bbd3b44f..eb6d135ca 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -16,6 +16,7 @@ into InDesign with File -> Place. module Text.Pandoc.Writers.ICML (writeICML) where import Text.Pandoc.Definition import Text.Pandoc.XML +import Text.Pandoc.Readers.TeXMath (texMathToInlines) import Text.Pandoc.Writers.Shared import Text.Pandoc.Shared (splitBy, fetchItem, warn) import Text.Pandoc.Options @@ -24,7 +25,6 @@ import Text.Pandoc.Pretty import Text.Pandoc.ImageSize import Data.List (isPrefixOf, isInfixOf, stripPrefix) import Data.Text as Text (breakOnAll, pack) -import Data.Monoid (mappend) import Control.Monad.State import Network.URI (isURI) import System.FilePath (pathSeparator) @@ -415,7 +415,8 @@ inlineToICML opts style (Cite _ lst) = inlinesToICML opts (citeName:style) lst inlineToICML _ style (Code _ str) = charStyle (codeName:style) $ text $ escapeStringForXML str inlineToICML _ style Space = charStyle style space inlineToICML _ style LineBreak = charStyle style $ text lineSeparator -inlineToICML _ style (Math _ str) = charStyle style $ text $ escapeStringForXML str --InDesign doesn't really do math +inlineToICML opts style (Math mt str) = + cat <$> mapM (inlineToICML opts style) (texMathToInlines mt str) inlineToICML _ _ (RawInline f str) | f == Format "icml" = return $ text str | otherwise = return empty diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 5857723a6..9e15e0be7 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -38,10 +38,11 @@ import Text.Pandoc.Options import Text.Pandoc.Templates import Text.Printf ( printf ) import Network.URI ( isURI, unEscapeString ) -import Data.List ( (\\), isInfixOf, stripPrefix, intercalate, intersperse ) +import Data.Aeson (object, (.=)) +import Data.List ( (\\), isInfixOf, stripPrefix, intercalate, intersperse, nub, nubBy ) import Data.Char ( toLower, isPunctuation, isAscii, isLetter, isDigit, ord ) -import Data.Maybe ( fromMaybe ) -import Data.Aeson.Types ( (.:), parseMaybe, withObject ) +import Data.Maybe ( fromMaybe, isJust ) +import qualified Data.Text as T import Control.Applicative ((<|>)) import Control.Monad.State import qualified Text.Parsec as P @@ -121,7 +122,7 @@ pandocToLaTeX options (Pandoc meta blocks) = do Right r -> r Left _ -> "" case lookup "documentclass" (writerVariables options) `mplus` - parseMaybe (withObject "object" (.: "documentclass")) metadata of + fmap stringify (lookupMeta "documentclass" meta) of Just x | x `elem` bookClasses -> modify $ \s -> s{stBook = True} | otherwise -> return () Nothing | documentClass `elem` bookClasses @@ -145,11 +146,8 @@ pandocToLaTeX options (Pandoc meta blocks) = do st <- get titleMeta <- stringToLaTeX TextString $ stringify $ docTitle meta authorsMeta <- mapM (stringToLaTeX TextString . stringify) $ docAuthors meta - let (mainlang, otherlang) = - case (reverse . splitBy (==',') . filter (/=' ')) `fmap` - getField "lang" metadata of - Just (m:os) -> (m, reverse os) - _ -> ("", []) + let docLangs = nub $ query (extract "lang") blocks + let hasStringValue x = isJust (getField x metadata :: Maybe String) let context = defField "toc" (writerTableOfContents options) $ defField "toc-depth" (show (writerTOCDepth options - if stBook st @@ -174,8 +172,6 @@ pandocToLaTeX options (Pandoc meta blocks) = do defField "euro" (stUsesEuro st) $ defField "listings" (writerListings options || stLHS st) $ defField "beamer" (writerBeamer options) $ - defField "mainlang" mainlang $ - defField "otherlang" otherlang $ (if stHighlighting st then defField "highlighting-macros" (styleToLaTeX $ writerHighlightStyle options ) @@ -186,9 +182,56 @@ pandocToLaTeX options (Pandoc meta blocks) = do Biblatex -> defField "biblio-title" biblioTitle . defField "biblatex" True _ -> id) $ + -- set lang to something so polyglossia/babel is included + defField "lang" (if null docLangs then ""::String else "en") $ + defField "otherlangs" docLangs $ + defField "colorlinks" (any hasStringValue + ["citecolor", "urlcolor", "linkcolor", "toccolor"]) $ + defField "dir" (if (null $ query (extract "dir") blocks) + then ""::String + else "ltr") $ metadata + let toPolyObj lang = object [ "name" .= T.pack name + , "options" .= T.pack opts ] + where + (name, opts) = toPolyglossia lang + let lang = maybe [] (splitBy (=='-')) $ getField "lang" context + otherlangs = maybe [] (map $ splitBy (=='-')) $ getField "otherlangs" context + let context' = + defField "babel-lang" (toBabel lang) + $ defField "babel-otherlangs" (map toBabel otherlangs) + $ defField "babel-newcommands" (concatMap (\(poly, babel) -> + -- \textspanish and \textgalician are already used by babel + -- save them as \oritext... and let babel use that + if poly `elem` ["spanish", "galician"] + then "\\let\\oritext" ++ poly ++ "\\text" ++ poly ++ "\n" ++ + "\\AddBabelHook{" ++ poly ++ "}{beforeextras}" ++ + "{\\renewcommand{\\text" ++ poly ++ "}{\\oritext" + ++ poly ++ "}}\n" ++ + "\\AddBabelHook{" ++ poly ++ "}{afterextras}" ++ + "{\\renewcommand{\\text" ++ poly ++ "}[2][]{\\foreignlanguage{" + ++ poly ++ "}{##2}}}\n" + else "\\newcommand{\\text" ++ poly ++ "}[2][]{\\foreignlanguage{" + ++ babel ++ "}{#2}}\n" ++ + "\\newenvironment{" ++ poly ++ "}[1]{\\begin{otherlanguage}{" + ++ babel ++ "}}{\\end{otherlanguage}}\n" + ) + -- eliminate duplicates that have same polyglossia name + $ nubBy (\a b -> fst a == fst b) + -- find polyglossia and babel names of languages used in the document + $ map (\l -> + let lng = splitBy (=='-') l + in (fst $ toPolyglossia lng, toBabel lng) + ) + docLangs ) + $ defField "polyglossia-lang" (toPolyObj lang) + $ defField "polyglossia-otherlangs" (map toPolyObj otherlangs) + $ defField "latex-dir-rtl" (case (getField "dir" context)::Maybe String of + Just "rtl" -> True + _ -> False) + $ context return $ if writerStandalone options - then renderTemplate' template context + then renderTemplate' template context' else main -- | Convert Elements to LaTeX @@ -234,7 +277,7 @@ stringToLaTeX ctx (x:xs) = do '^' -> "\\^{}" ++ rest '\\'| isUrl -> '/' : rest -- NB. / works as path sep even on Windows | otherwise -> "\\textbackslash{}" ++ rest - '|' -> "\\textbar{}" ++ rest + '|' | not isUrl -> "\\textbar{}" ++ rest '<' -> "\\textless{}" ++ rest '>' -> "\\textgreater{}" ++ rest '[' -> "{[}" ++ rest -- to avoid interpretation as @@ -292,9 +335,12 @@ elementToBeamer slideLevel (Sec lvl _num (ident,classes,kvs) tit elts) if writerListings opts then query hasCode elts else []) - let allowframebreaks = "allowframebreaks" `elem` classes + let frameoptions = ["allowdisplaybreaks", "allowframebreaks", + "b", "c", "t", "environment", + "label", "plain", "shrink"] let optionslist = ["fragile" | fragile] ++ - ["allowframebreaks" | allowframebreaks] + [k | k <- classes, k `elem` frameoptions] ++ + [k ++ "=" ++ v | (k,v) <- kvs, k `elem` frameoptions] let options = if null optionslist then "" else "[" ++ intercalate "," optionslist ++ "]" @@ -322,34 +368,53 @@ isLineBreakOrSpace _ = False blockToLaTeX :: Block -- ^ Block to convert -> State WriterState Doc blockToLaTeX Null = return empty -blockToLaTeX (Div (identifier,classes,_) bs) = do +blockToLaTeX (Div (identifier,classes,kvs) bs) = do beamer <- writerBeamer `fmap` gets stOptions ref <- toLabel identifier let linkAnchor = if null identifier then empty - else "\\hyperdef{}" <> braces (text ref) <> - braces ("\\label" <> braces (text ref)) - contents <- blockListToLaTeX bs - if beamer && "notes" `elem` classes -- speaker notes - then return $ "\\note" <> braces contents - else return (linkAnchor $$ contents) + else "\\hypertarget" <> braces (text ref) <> + braces empty + let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir + let wrapDir = case lookup "dir" kvs of + Just "rtl" -> align "RTL" + Just "ltr" -> align "LTR" + _ -> id + wrapLang txt = case lookup "lang" kvs of + Just lng -> let (l, o) = toPolyglossiaEnv lng + ops = if null o + then "" + else brackets $ text o + in inCmd "begin" (text l) <> ops + $$ blankline <> txt <> blankline + $$ inCmd "end" (text l) + Nothing -> txt + wrapNotes txt = if beamer && "notes" `elem` classes + then "\\note" <> braces txt -- speaker notes + else linkAnchor $$ txt + fmap (wrapDir . wrapLang . wrapNotes) $ blockListToLaTeX bs blockToLaTeX (Plain lst) = inlineListToLaTeX $ dropWhile isLineBreakOrSpace lst -- title beginning with fig: indicates that the image is a figure blockToLaTeX (Para [Image attr txt (src,'f':'i':'g':':':tit)]) = do inNote <- gets stInNote + modify $ \st -> st{ stInMinipage = True, stNotes = [] } capt <- inlineListToLaTeX txt + notes <- gets stNotes + modify $ \st -> st{ stInMinipage = False, stNotes = [] } + -- We can't have footnotes in the list of figures, so remove them: + captForLof <- if null notes + then return empty + else brackets <$> inlineListToLaTeX (walk deNote txt) img <- inlineToLaTeX (Image attr txt (src,tit)) - let (ident, _, _) = attr - idn <- toLabel ident - let label = if null ident - then empty - else "\\label" <> braces (text idn) + let footnotes = notesToLaTeX notes return $ if inNote -- can't have figures in notes then "\\begin{center}" $$ img $+$ capt $$ "\\end{center}" else "\\begin{figure}[htbp]" $$ "\\centering" $$ img $$ - ("\\caption" <> braces capt) $$ label $$ "\\end{figure}" + ("\\caption" <> captForLof <> braces capt) $$ + "\\end{figure}" $$ + footnotes -- . . . indicates pause in beamer slides blockToLaTeX (Para [Str ".",Space,Str ".",Space,Str "."]) = do beamer <- writerBeamer `fmap` gets stOptions @@ -378,7 +443,7 @@ blockToLaTeX (CodeBlock (identifier,classes,keyvalAttr) str) = do ref <- toLabel identifier let linkAnchor = if null identifier then empty - else "\\hyperdef{}" <> braces (text ref) <> + else "\\hypertarget" <> braces (text ref) <> braces ("\\label" <> braces (text ref)) let lhsCodeBlock = do modify $ \s -> s{ stLHS = True } @@ -591,19 +656,21 @@ tableCellToLaTeX header (width, align, blocks) = do return $ ("\\begin{minipage}" <> valign <> braces (text (printf "%.2f\\columnwidth" width)) <> (halign <> "\\strut" <> cr <> cellContents <> cr) <> - "\\strut\\end{minipage}") - $$ case notes of - [] -> empty - ns -> (case length ns of + "\\strut\\end{minipage}") $$ + notesToLaTeX notes + +notesToLaTeX :: [Doc] -> Doc +notesToLaTeX [] = empty +notesToLaTeX ns = (case length ns of n | n > 1 -> "\\addtocounter" <> braces "footnote" <> braces (text $ show $ 1 - n) | otherwise -> empty) - $$ - vcat (intersperse - ("\\addtocounter" <> braces "footnote" <> braces "1") - $ map (\x -> "\\footnotetext" <> braces x) - $ reverse ns) + $$ + vcat (intersperse + ("\\addtocounter" <> braces "footnote" <> braces "1") + $ map (\x -> "\\footnotetext" <> braces x) + $ reverse ns) listItemToLaTeX :: [Block] -> State WriterState Doc listItemToLaTeX lst @@ -665,8 +732,7 @@ sectionHeader unnumbered ref level lst = do let level' = if book || writerChapters opts then level - 1 else level internalLinks <- gets stInternalLinks let refLabel x = (if ref `elem` internalLinks - then text "\\hyperdef" - <> braces empty + then text "\\hypertarget" <> braces lab <> braces x else x) @@ -731,22 +797,29 @@ isQuoted _ = False -- | Convert inline element to LaTeX inlineToLaTeX :: Inline -- ^ Inline to convert -> State WriterState Doc -inlineToLaTeX (Span (id',classes,_) ils) = do +inlineToLaTeX (Span (id',classes,kvs) ils) = do let noEmph = "csl-no-emph" `elem` classes let noStrong = "csl-no-strong" `elem` classes let noSmallCaps = "csl-no-smallcaps" `elem` classes + let rtl = ("dir","rtl") `elem` kvs + let ltr = ("dir","ltr") `elem` kvs ref <- toLabel id' let linkAnchor = if null id' then empty - else "\\hyperdef{}" <> braces (text ref) <> - braces ("\\label" <> braces (text ref)) + else "\\protect\\hypertarget" <> braces (text ref) <> + braces empty fmap (linkAnchor <>) ((if noEmph then inCmd "textup" else id) . (if noStrong then inCmd "textnormal" else id) . (if noSmallCaps then inCmd "textnormal" else id) . - (if not (noEmph || noStrong || noSmallCaps) - then braces - else id)) `fmap` inlineListToLaTeX ils + (if rtl then inCmd "RL" else id) . + (if ltr then inCmd "LR" else id) . + (case lookup "lang" kvs of + Just lng -> let (l, o) = toPolyglossia $ splitBy (=='-') lng + ops = if null o then "" else brackets (text o) + in \c -> char '\\' <> "text" <> text l <> ops <> braces c + Nothing -> id) + ) `fmap` inlineListToLaTeX ils inlineToLaTeX (Emph lst) = inlineListToLaTeX lst >>= return . inCmd "emph" inlineToLaTeX (Strong lst) = @@ -831,22 +904,22 @@ inlineToLaTeX Space = return space inlineToLaTeX (Link _ txt ('#':ident, _)) = do contents <- inlineListToLaTeX txt lab <- toLabel ident - return $ text "\\hyperref" <> brackets (text lab) <> braces contents + return $ text "\\protect\\hyperlink" <> braces (text lab) <> braces contents inlineToLaTeX (Link _ txt (src, _)) = case txt of [Str x] | escapeURI x == src -> -- autolink do modify $ \s -> s{ stUrl = True } - src' <- stringToLaTeX URLString src + src' <- stringToLaTeX URLString (escapeURI src) return $ text $ "\\url{" ++ src' ++ "}" [Str x] | Just rest <- stripPrefix "mailto:" src, escapeURI x == rest -> -- email autolink do modify $ \s -> s{ stUrl = True } - src' <- stringToLaTeX URLString src + src' <- stringToLaTeX URLString (escapeURI src) contents <- inlineListToLaTeX txt return $ "\\href" <> braces (text src') <> braces ("\\nolinkurl" <> braces contents) _ -> do contents <- inlineListToLaTeX txt - src' <- stringToLaTeX URLString src + src' <- stringToLaTeX URLString (escapeURI src) return $ text ("\\href{" ++ src' ++ "}{") <> contents <> char '}' inlineToLaTeX (Image attr _ (source, _)) = do @@ -869,7 +942,7 @@ inlineToLaTeX (Image attr _ (source, _)) = do source' = if isURI source then source else unEscapeString source - source'' <- stringToLaTeX URLString source' + source'' <- stringToLaTeX URLString (escapeURI source') inHeading <- gets stInHeading return $ (if inHeading then "\\protect\\includegraphics" else "\\includegraphics") <> @@ -1001,3 +1074,173 @@ citationsToBiblatex _ = return empty getListingsLanguage :: [String] -> Maybe String getListingsLanguage [] = Nothing getListingsLanguage (x:xs) = toListingsLanguage x <|> getListingsLanguage xs + +-- Extract a key from divs and spans +extract :: String -> Block -> [String] +extract key (Div attr _) = lookKey key attr +extract key (Plain ils) = concatMap (extractInline key) ils +extract key (Para ils) = concatMap (extractInline key) ils +extract key (Header _ _ ils) = concatMap (extractInline key) ils +extract _ _ = [] + +-- Extract a key from spans +extractInline :: String -> Inline -> [String] +extractInline key (Span attr _) = lookKey key attr +extractInline _ _ = [] + +-- Look up a key in an attribute and give a list of its values +lookKey :: String -> Attr -> [String] +lookKey key (_,_,kvs) = maybe [] words $ lookup key kvs + +-- In environments \Arabic instead of \arabic is used +toPolyglossiaEnv :: String -> (String, String) +toPolyglossiaEnv l = + case toPolyglossia $ (splitBy (=='-')) l of + ("arabic", o) -> ("Arabic", o) + x -> x + +-- Takes a list of the constituents of a BCP 47 language code and +-- converts it to a Polyglossia (language, options) tuple +-- http://mirrors.concertpass.com/tex-archive/macros/latex/contrib/polyglossia/polyglossia.pdf +toPolyglossia :: [String] -> (String, String) +toPolyglossia ("ar":"DZ":_) = ("arabic", "locale=algeria") +toPolyglossia ("ar":"IQ":_) = ("arabic", "locale=mashriq") +toPolyglossia ("ar":"JO":_) = ("arabic", "locale=mashriq") +toPolyglossia ("ar":"LB":_) = ("arabic", "locale=mashriq") +toPolyglossia ("ar":"LY":_) = ("arabic", "locale=libya") +toPolyglossia ("ar":"MA":_) = ("arabic", "locale=morocco") +toPolyglossia ("ar":"MR":_) = ("arabic", "locale=mauritania") +toPolyglossia ("ar":"PS":_) = ("arabic", "locale=mashriq") +toPolyglossia ("ar":"SY":_) = ("arabic", "locale=mashriq") +toPolyglossia ("ar":"TN":_) = ("arabic", "locale=tunisia") +toPolyglossia ("de":"1901":_) = ("german", "spelling=old") +toPolyglossia ("de":"AT":"1901":_) = ("german", "variant=austrian, spelling=old") +toPolyglossia ("de":"AT":_) = ("german", "variant=austrian") +toPolyglossia ("de":"CH":"1901":_) = ("german", "variant=swiss, spelling=old") +toPolyglossia ("de":"CH":_) = ("german", "variant=swiss") +toPolyglossia ("de":_) = ("german", "") +toPolyglossia ("dsb":_) = ("lsorbian", "") +toPolyglossia ("el":"polyton":_) = ("greek", "variant=poly") +toPolyglossia ("en":"AU":_) = ("english", "variant=australian") +toPolyglossia ("en":"CA":_) = ("english", "variant=canadian") +toPolyglossia ("en":"GB":_) = ("english", "variant=british") +toPolyglossia ("en":"NZ":_) = ("english", "variant=newzealand") +toPolyglossia ("en":"UK":_) = ("english", "variant=british") +toPolyglossia ("en":"US":_) = ("english", "variant=american") +toPolyglossia ("grc":_) = ("greek", "variant=ancient") +toPolyglossia ("hsb":_) = ("usorbian", "") +toPolyglossia ("sl":_) = ("slovenian", "") +toPolyglossia x = (commonFromBcp47 x, "") + +-- Takes a list of the constituents of a BCP 47 language code and +-- converts it to a Babel language string. +-- http://mirrors.concertpass.com/tex-archive/macros/latex/required/babel/base/babel.pdf +-- Note that the PDF unfortunately does not contain a complete list of supported languages. +toBabel :: [String] -> String +toBabel ("de":"1901":_) = "german" +toBabel ("de":"AT":"1901":_) = "austrian" +toBabel ("de":"AT":_) = "naustrian" +toBabel ("de":_) = "ngerman" +toBabel ("dsb":_) = "lowersorbian" +toBabel ("el":"polyton":_) = "polutonikogreek" +toBabel ("en":"AU":_) = "australian" +toBabel ("en":"CA":_) = "canadian" +toBabel ("en":"GB":_) = "british" +toBabel ("en":"NZ":_) = "newzealand" +toBabel ("en":"UK":_) = "british" +toBabel ("en":"US":_) = "american" +toBabel ("fr":"CA":_) = "canadien" +toBabel ("fra":"aca":_) = "acadian" +toBabel ("grc":_) = "polutonikogreek" +toBabel ("hsb":_) = "uppersorbian" +toBabel ("sl":_) = "slovene" +toBabel x = commonFromBcp47 x + +-- Takes a list of the constituents of a BCP 47 language code +-- and converts it to a string shared by Babel and Polyglossia. +-- https://tools.ietf.org/html/bcp47#section-2.1 +commonFromBcp47 :: [String] -> String +commonFromBcp47 [] = "" +commonFromBcp47 ("pt":"BR":_) = "brazilian" +commonFromBcp47 x = fromIso $ head x + where + fromIso "af" = "afrikaans" + fromIso "am" = "amharic" + fromIso "ar" = "arabic" + fromIso "ast" = "asturian" + fromIso "bg" = "bulgarian" + fromIso "bn" = "bengali" + fromIso "bo" = "tibetan" + fromIso "br" = "breton" + fromIso "ca" = "catalan" + fromIso "cy" = "welsh" + fromIso "cz" = "czech" + fromIso "cop" = "coptic" + fromIso "da" = "danish" + fromIso "dv" = "divehi" + fromIso "el" = "greek" + fromIso "en" = "english" + fromIso "eo" = "esperanto" + fromIso "es" = "spanish" + fromIso "et" = "estonian" + fromIso "eu" = "basque" + fromIso "fa" = "farsi" + fromIso "fi" = "finnish" + fromIso "fr" = "french" + fromIso "fur" = "friulan" + fromIso "ga" = "irish" + fromIso "gd" = "scottish" + fromIso "gl" = "galician" + fromIso "he" = "hebrew" + fromIso "hi" = "hindi" + fromIso "hr" = "croatian" + fromIso "hy" = "armenian" + fromIso "hu" = "magyar" + fromIso "ia" = "interlingua" + fromIso "id" = "indonesian" + fromIso "ie" = "interlingua" + fromIso "is" = "icelandic" + fromIso "it" = "italian" + fromIso "jp" = "japanese" + fromIso "km" = "khmer" + fromIso "kn" = "kannada" + fromIso "ko" = "korean" + fromIso "la" = "latin" + fromIso "lo" = "lao" + fromIso "lt" = "lithuanian" + fromIso "lv" = "latvian" + fromIso "ml" = "malayalam" + fromIso "mn" = "mongolian" + fromIso "mr" = "marathi" + fromIso "nb" = "norsk" + fromIso "nl" = "dutch" + fromIso "nn" = "nynorsk" + fromIso "no" = "norsk" + fromIso "nqo" = "nko" + fromIso "oc" = "occitan" + fromIso "pl" = "polish" + fromIso "pms" = "piedmontese" + fromIso "pt" = "portuguese" + fromIso "rm" = "romansh" + fromIso "ro" = "romanian" + fromIso "ru" = "russian" + fromIso "sa" = "sanskrit" + fromIso "se" = "samin" + fromIso "sk" = "slovak" + fromIso "sq" = "albanian" + fromIso "sr" = "serbian" + fromIso "sv" = "swedish" + fromIso "syr" = "syriac" + fromIso "ta" = "tamil" + fromIso "te" = "telugu" + fromIso "th" = "thai" + fromIso "tk" = "turkmen" + fromIso "tr" = "turkish" + fromIso "uk" = "ukrainian" + fromIso "ur" = "urdu" + fromIso "vi" = "vietnamese" + fromIso _ = "" + +deNote :: Inline -> Inline +deNote (Note _) = RawInline (Format "latex") "" +deNote x = x diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 71fd145e2..b8b1c1fdd 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -85,6 +85,8 @@ pandocToMan opts (Pandoc meta blocks) = do let context = defField "body" main $ setFieldsFromTitle $ defField "has-tables" hasTables + $ defField "hyphenate" True + $ defField "pandoc-version" pandocVersion $ metadata if writerStandalone opts then return $ renderTemplate' (writerTemplate opts) context diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 019a0e272..898e6c32d 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -40,7 +40,7 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (blankline, blanklines, char, space) import Data.Maybe (fromMaybe) import Data.List ( group, stripPrefix, find, intersperse, transpose, sortBy ) -import Data.Char ( isSpace, isPunctuation ) +import Data.Char ( isSpace, isPunctuation, ord, chr ) import Data.Ord ( comparing ) import Text.Pandoc.Pretty import Control.Monad.State @@ -260,10 +260,13 @@ tableOfContents opts headers = -- | Converts an Element to a list item for a table of contents, elementToListItem :: WriterOptions -> Element -> [Block] -elementToListItem opts (Sec lev _ _ headerText subsecs) - = Plain headerText : +elementToListItem opts (Sec lev _nums (ident,_,_) headerText subsecs) + = Plain headerLink : [ BulletList (map (elementToListItem opts) subsecs) | not (null subsecs) && lev < writerTOCDepth opts ] + where headerLink = if null ident + then headerText + else [Link nullAttr headerText ('#':ident, "")] elementToListItem _ (Blk _) = [] attrsToMarkdown :: Attr -> Doc @@ -780,14 +783,25 @@ inlineToMarkdown opts (Superscript lst) = do then "^" <> contents <> "^" else if isEnabled Ext_raw_html opts then "<sup>" <> contents <> "</sup>" - else contents + else case (render Nothing contents) of + ds | all (\d -> d >= '0' && d <= '9') ds + -> text (map toSuperscript ds) + _ -> contents + where toSuperscript '1' = '\x00B9' + toSuperscript '2' = '\x00B2' + toSuperscript '3' = '\x00B3' + toSuperscript c = chr (0x2070 + (ord c - 48)) inlineToMarkdown opts (Subscript lst) = do contents <- inlineListToMarkdown opts $ walk escapeSpaces lst return $ if isEnabled Ext_subscript opts then "~" <> contents <> "~" else if isEnabled Ext_raw_html opts then "<sub>" <> contents <> "</sub>" - else contents + else case (render Nothing contents) of + ds | all (\d -> d >= '0' && d <= '9') ds + -> text (map toSubscript ds) + _ -> contents + where toSubscript c = chr (0x2080 + (ord c - 48)) inlineToMarkdown opts (SmallCaps lst) = do plain <- gets stPlain if not plain && diff --git a/src/Text/Pandoc/Writers/Native.hs b/src/Text/Pandoc/Writers/Native.hs index f342dc4f5..2343ff1a8 100644 --- a/src/Text/Pandoc/Writers/Native.hs +++ b/src/Text/Pandoc/Writers/Native.hs @@ -63,6 +63,8 @@ prettyBlock (Table caption aligns widths header rows) = prettyRow header $$ prettyList (map prettyRow rows) where prettyRow cols = prettyList (map (prettyList . map prettyBlock) cols) +prettyBlock (Div attr blocks) = + text ("Div " <> show attr) $$ prettyList (map prettyBlock blocks) prettyBlock block = text $ show block -- | Prettyprint Pandoc document. diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index f7df74246..835e79ce7 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -37,7 +37,6 @@ import Text.TeXMath import qualified Data.ByteString.Lazy as B import Text.Pandoc.UTF8 ( fromStringLazy ) import Codec.Archive.Zip -import Control.Applicative ((<$>)) import Text.Pandoc.Options ( WriterOptions(..) ) import Text.Pandoc.Shared ( stringify, fetchItem', warn, getDefaultReferenceODT ) diff --git a/src/Text/Pandoc/Writers/OPML.hs b/src/Text/Pandoc/Writers/OPML.hs index c7563d751..519136861 100644 --- a/src/Text/Pandoc/Writers/OPML.hs +++ b/src/Text/Pandoc/Writers/OPML.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE CPP #-} {- Copyright (C) 2013-2015 John MacFarlane <jgm@berkeley.edu> @@ -37,8 +38,7 @@ import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.HTML (writeHtmlString) import Text.Pandoc.Writers.Markdown (writeMarkdown) import Text.Pandoc.Pretty -import Data.Time -import Text.Pandoc.Compat.Locale (defaultTimeLocale) +import Text.Pandoc.Compat.Time import qualified Text.Pandoc.Builder as B -- | Convert Pandoc document to string in OPML format. @@ -69,8 +69,13 @@ showDateTimeRFC822 :: UTCTime -> String showDateTimeRFC822 = formatTime defaultTimeLocale "%a, %d %b %Y %X %Z" convertDate :: [Inline] -> String -convertDate ils = maybe "" showDateTimeRFC822 - $ parseTime defaultTimeLocale "%F" =<< (normalizeDate $ stringify ils) +convertDate ils = maybe "" showDateTimeRFC822 $ +#if MIN_VERSION_time(1,5,0) + parseTimeM True +#else + parseTime +#endif + defaultTimeLocale "%F" =<< (normalizeDate $ stringify ils) -- | Convert an Element to OPML. elementToOPML :: WriterOptions -> Element -> Doc diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 7b964e2d2..dad6b431e 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -37,7 +37,6 @@ import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Readers.TeXMath import Text.Pandoc.Pretty import Text.Printf ( printf ) -import Control.Applicative ( (<$>) ) import Control.Arrow ( (***), (>>>) ) import Control.Monad.State hiding ( when ) import Data.Char (chr, isDigit) @@ -192,8 +191,7 @@ writeOpenDocument opts (Pandoc meta blocks) = listStyle (n,l) = inTags True "text:list-style" [("style:name", "L" ++ show n)] (vcat l) listStyles = map listStyle (stListStyles s) - automaticStyles = inTagsIndented "office:automatic-styles" $ vcat $ - reverse $ styles ++ listStyles + automaticStyles = vcat $ reverse $ styles ++ listStyles context = defField "body" body $ defField "automatic-styles" (render' automaticStyles) $ metadata diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 24da7b9e1..75967fa2a 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -40,7 +40,6 @@ import Text.Pandoc.Pretty import Text.Pandoc.Templates (renderTemplate') import Data.List ( intersect, intersperse, transpose ) import Control.Monad.State -import Control.Applicative ( (<$>) ) data WriterState = WriterState { stNotes :: [[Block]] diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index a65d6f8bb..94c54c250 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -43,7 +43,6 @@ import Data.List ( isPrefixOf, stripPrefix, intersperse, transpose ) import Network.URI (isURI) import Text.Pandoc.Pretty import Control.Monad.State -import Control.Applicative ( (<$>) ) import Data.Char (isSpace, toLower) type Refs = [([Inline], Target)] @@ -82,7 +81,9 @@ pandocToRST (Pandoc meta blocks) = do (fmap (render colwidth) . blockListToRST) (fmap (trimr . render colwidth) . inlineListToRST) $ deleteMeta "title" $ deleteMeta "subtitle" meta - body <- blockListToRST' True $ normalizeHeadings 1 blocks + body <- blockListToRST' True $ if writerStandalone opts + then normalizeHeadings 1 blocks + else blocks notes <- liftM (reverse . stNotes) get >>= notesToRST -- note that the notes may contain refs, so we do them first refs <- liftM (reverse . stLinks) get >>= refsToRST @@ -102,7 +103,8 @@ pandocToRST (Pandoc meta blocks) = do then return $ renderTemplate' (writerTemplate opts) context else return main where - normalizeHeadings lev (Header l a i:bs) = Header lev a i:normalizeHeadings (lev+1) cont ++ normalizeHeadings lev bs' + normalizeHeadings lev (Header l a i:bs) = + Header lev a i:normalizeHeadings (lev+1) cont ++ normalizeHeadings lev bs' where (cont,bs') = break (headerLtEq l) bs headerLtEq level (Header l' _ _) = l' <= level headerLtEq _ _ = False @@ -344,7 +346,8 @@ blockListToRST = blockListToRST' False -- | Convert list of Pandoc inline elements to RST. inlineListToRST :: [Inline] -> State WriterState Doc inlineListToRST lst = - mapM inlineToRST (removeSpaceAfterDisplayMath $ insertBS lst) >>= return . hcat + mapM inlineToRST (removeSpaceAfterDisplayMath $ insertBS lst) >>= + return . hcat where -- remove spaces after displaymath, as they screw up indentation: removeSpaceAfterDisplayMath (Math DisplayMath x : zs) = Math DisplayMath x : dropWhile (==Space) zs @@ -352,8 +355,8 @@ inlineListToRST lst = removeSpaceAfterDisplayMath [] = [] insertBS :: [Inline] -> [Inline] -- insert '\ ' where needed insertBS (x:y:z:zs) - | isComplex y && surroundComplex x z = - x : y : RawInline "rst" "\\ " : insertBS (z:zs) + | isComplex y && (surroundComplex x z) = + x : y : insertBS (z : zs) insertBS (x:y:zs) | isComplex x && not (okAfterComplex y) = x : RawInline "rst" "\\ " : insertBS (y : zs) @@ -394,6 +397,8 @@ inlineListToRST lst = isComplex (Image _ _ _) = True isComplex (Code _ _) = True isComplex (Math _ _) = True + isComplex (Cite _ (x:_)) = isComplex x + isComplex (Span _ (x:_)) = isComplex x isComplex _ = False -- | Convert Pandoc inline element to RST. diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index 456bf19c9..1d5734c96 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -40,12 +40,12 @@ import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.XML ( escapeStringForXML ) import Data.List ( intercalate ) import Control.Monad.State -import Control.Applicative ((<$>)) import Data.Char ( isSpace ) data WriterState = WriterState { stNotes :: [String] -- Footnotes , stListLevel :: [Char] -- String at beginning of list items, e.g. "**" + , stStartNum :: Maybe Int -- Start number if first list item , stUseTags :: Bool -- True if we should use HTML tags because we're in a complex list } @@ -53,7 +53,8 @@ data WriterState = WriterState { writeTextile :: WriterOptions -> Pandoc -> String writeTextile opts document = evalState (pandocToTextile opts document) - WriterState { stNotes = [], stListLevel = [], stUseTags = False } + WriterState { stNotes = [], stListLevel = [], stStartNum = Nothing, + stUseTags = False } -- | Return Textile representation of document. pandocToTextile :: WriterOptions -> Pandoc -> State WriterState String @@ -220,7 +221,7 @@ blockToTextile opts x@(BulletList items) = do modify $ \s -> s { stListLevel = init (stListLevel s) } return $ vcat contents ++ (if level > 1 then "" else "\n") -blockToTextile opts x@(OrderedList attribs items) = do +blockToTextile opts x@(OrderedList attribs@(start, _, _) items) = do oldUseTags <- liftM stUseTags get let useTags = oldUseTags || not (isSimpleList x) if useTags @@ -229,10 +230,14 @@ blockToTextile opts x@(OrderedList attribs items) = do return $ "<ol" ++ listAttribsToString attribs ++ ">\n" ++ vcat contents ++ "\n</ol>\n" else do - modify $ \s -> s { stListLevel = stListLevel s ++ "#" } + modify $ \s -> s { stListLevel = stListLevel s ++ "#" + , stStartNum = if start > 1 + then Just start + else Nothing } level <- get >>= return . length . stListLevel contents <- mapM (listItemToTextile opts) items - modify $ \s -> s { stListLevel = init (stListLevel s) } + modify $ \s -> s { stListLevel = init (stListLevel s), + stStartNum = Nothing } return $ vcat contents ++ (if level > 1 then "" else "\n") blockToTextile opts (DefinitionList items) = do @@ -260,8 +265,13 @@ listItemToTextile opts items = do if useTags then return $ "<li>" ++ contents ++ "</li>" else do - marker <- get >>= return . stListLevel - return $ marker ++ " " ++ contents + marker <- gets stListLevel + mbstart <- gets stStartNum + case mbstart of + Just n -> do + modify $ \s -> s{ stStartNum = Nothing } + return $ marker ++ show n ++ " " ++ contents + Nothing -> return $ marker ++ " " ++ contents -- | Convert definition list item (label, list of blocks) to Textile. definitionListItemToTextile :: WriterOptions @@ -278,8 +288,8 @@ isSimpleList :: Block -> Bool isSimpleList x = case x of BulletList items -> all isSimpleListItem items - OrderedList (num, sty, _) items -> all isSimpleListItem items && - num == 1 && sty `elem` [DefaultStyle, Decimal] + OrderedList (_, sty, _) items -> all isSimpleListItem items && + sty `elem` [DefaultStyle, Decimal] _ -> False -- | True if list item can be handled with the simple wiki syntax. False if |