1{-# LANGUAGE OverloadedStrings #-} 2{- | 3 Module : Text.Pandoc.Writers.AsciiDoc 4 Copyright : Copyright (C) 2006-2021 John MacFarlane 5 License : GNU GPL, version 2 or above 6 7 Maintainer : John MacFarlane <jgm@berkeley.edu> 8 Stability : alpha 9 Portability : portable 10 11Conversion of 'Pandoc' documents to asciidoc. 12 13Note that some information may be lost in conversion, due to 14expressive limitations of asciidoc. Footnotes and table cells with 15paragraphs (or other block items) are not possible in asciidoc. 16If pandoc encounters one of these, it will insert a message indicating 17that it has omitted the construct. 18 19AsciiDoc: <http://www.methods.co.nz/asciidoc/> 20-} 21module Text.Pandoc.Writers.AsciiDoc (writeAsciiDoc, writeAsciiDoctor) where 22import Control.Monad.State.Strict 23import Data.Char (isPunctuation, isSpace) 24import Data.List (intercalate, intersperse) 25import Data.List.NonEmpty (NonEmpty(..)) 26import Data.Maybe (fromMaybe, isJust) 27import qualified Data.Set as Set 28import qualified Data.Text as T 29import Data.Text (Text) 30import Text.Pandoc.Class.PandocMonad (PandocMonad, report) 31import Text.Pandoc.Definition 32import Text.Pandoc.ImageSize 33import Text.Pandoc.Logging 34import Text.Pandoc.Options 35import Text.Pandoc.Parsing hiding (blankline, space) 36import Text.DocLayout 37import Text.Pandoc.Shared 38import Text.Pandoc.Templates (renderTemplate) 39import Text.Pandoc.Writers.Shared 40 41 42data WriterState = WriterState { defListMarker :: Text 43 , orderedListLevel :: Int 44 , bulletListLevel :: Int 45 , intraword :: Bool 46 , autoIds :: Set.Set Text 47 , asciidoctorVariant :: Bool 48 , inList :: Bool 49 , hasMath :: Bool 50 -- |0 is no table 51 -- 1 is top level table 52 -- 2 is a table in a table 53 , tableNestingLevel :: Int 54 } 55 56defaultWriterState :: WriterState 57defaultWriterState = WriterState { defListMarker = "::" 58 , orderedListLevel = 0 59 , bulletListLevel = 0 60 , intraword = False 61 , autoIds = Set.empty 62 , asciidoctorVariant = False 63 , inList = False 64 , hasMath = False 65 , tableNestingLevel = 0 66 } 67 68-- | Convert Pandoc to AsciiDoc. 69writeAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> m Text 70writeAsciiDoc opts document = 71 evalStateT (pandocToAsciiDoc opts document) defaultWriterState 72 73-- | Convert Pandoc to AsciiDoctor compatible AsciiDoc. 74writeAsciiDoctor :: PandocMonad m => WriterOptions -> Pandoc -> m Text 75writeAsciiDoctor opts document = 76 evalStateT (pandocToAsciiDoc opts document) 77 defaultWriterState{ asciidoctorVariant = True } 78 79type ADW = StateT WriterState 80 81-- | Return asciidoc representation of document. 82pandocToAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> ADW m Text 83pandocToAsciiDoc opts (Pandoc meta blocks) = do 84 let titleblock = not $ null (docTitle meta) && null (docAuthors meta) && 85 null (docDate meta) 86 let colwidth = if writerWrapText opts == WrapAuto 87 then Just $ writerColumns opts 88 else Nothing 89 metadata <- metaToContext opts 90 (blockListToAsciiDoc opts) 91 (fmap chomp . inlineListToAsciiDoc opts) 92 meta 93 main <- blockListToAsciiDoc opts $ makeSections False (Just 1) blocks 94 st <- get 95 let context = defField "body" main 96 $ defField "toc" 97 (writerTableOfContents opts && 98 isJust (writerTemplate opts)) 99 $ defField "math" (hasMath st) 100 $ defField "titleblock" titleblock metadata 101 return $ render colwidth $ 102 case writerTemplate opts of 103 Nothing -> main 104 Just tpl -> renderTemplate tpl context 105 106-- | Escape special characters for AsciiDoc. 107escapeString :: Text -> Text 108escapeString t 109 | T.any (== '{') t = T.concatMap escChar t 110 | otherwise = t 111 where escChar '{' = "\\{" 112 escChar c = T.singleton c 113 114-- | Ordered list start parser for use in Para below. 115olMarker :: Parser Text ParserState Char 116olMarker = do (start, style', delim) <- anyOrderedListMarker 117 if delim == Period && 118 (style' == UpperAlpha || (style' == UpperRoman && 119 start `elem` [1, 5, 10, 50, 100, 500, 1000])) 120 then spaceChar >> spaceChar 121 else spaceChar 122 123-- | True if string begins with an ordered list marker 124-- or would be interpreted as an AsciiDoc option command 125needsEscaping :: Text -> Bool 126needsEscaping s = beginsWithOrderedListMarker s || isBracketed s 127 where 128 beginsWithOrderedListMarker str = 129 case runParser olMarker defaultParserState "para start" (T.take 10 str) of 130 Left _ -> False 131 Right _ -> True 132 isBracketed t 133 | Just ('[', t') <- T.uncons t 134 , Just (_, ']') <- T.unsnoc t' 135 = True 136 | otherwise = False 137 138-- | Convert Pandoc block element to asciidoc. 139blockToAsciiDoc :: PandocMonad m 140 => WriterOptions -- ^ Options 141 -> Block -- ^ Block element 142 -> ADW m (Doc Text) 143blockToAsciiDoc _ Null = return empty 144blockToAsciiDoc opts (Div (id',"section":_,_) 145 (Header level (_,cls,kvs) ils : xs)) = do 146 hdr <- blockToAsciiDoc opts (Header level (id',cls,kvs) ils) 147 rest <- blockListToAsciiDoc opts xs 148 return $ hdr $$ rest 149blockToAsciiDoc opts (Plain inlines) = do 150 contents <- inlineListToAsciiDoc opts inlines 151 return $ contents <> blankline 152blockToAsciiDoc opts (Para [Image attr alternate (src,tgt)]) 153 -- image::images/logo.png[Company logo, title="blah"] 154 | Just tit <- T.stripPrefix "fig:" tgt 155 = (\args -> "image::" <> args <> blankline) <$> 156 imageArguments opts attr alternate src tit 157blockToAsciiDoc opts (Para inlines) = do 158 contents <- inlineListToAsciiDoc opts inlines 159 -- escape if para starts with ordered list marker 160 let esc = if needsEscaping (render Nothing contents) 161 then text "{empty}" 162 else empty 163 return $ esc <> contents <> blankline 164blockToAsciiDoc opts (LineBlock lns) = do 165 let docify line = if null line 166 then return blankline 167 else inlineListToAsciiDoc opts line 168 let joinWithLinefeeds = nowrap . mconcat . intersperse cr 169 contents <- joinWithLinefeeds <$> mapM docify lns 170 return $ "[verse]" $$ text "--" $$ contents $$ text "--" $$ blankline 171blockToAsciiDoc _ b@(RawBlock f s) 172 | f == "asciidoc" = return $ literal s 173 | otherwise = do 174 report $ BlockNotRendered b 175 return empty 176blockToAsciiDoc _ HorizontalRule = 177 return $ blankline <> text "'''''" <> blankline 178blockToAsciiDoc opts (Header level (ident,_,_) inlines) = do 179 contents <- inlineListToAsciiDoc opts inlines 180 ids <- gets autoIds 181 let autoId = uniqueIdent (writerExtensions opts) inlines ids 182 modify $ \st -> st{ autoIds = Set.insert autoId ids } 183 let identifier = if T.null ident || 184 (isEnabled Ext_auto_identifiers opts && ident == autoId) 185 then empty 186 else "[[" <> literal ident <> "]]" 187 return $ identifier $$ 188 nowrap (text (replicate (level + 1) '=') <> space <> contents) <> 189 blankline 190 191blockToAsciiDoc _ (CodeBlock (_,classes,_) str) = return $ flush ( 192 if null classes 193 then "...." $$ literal str $$ "...." 194 else attrs $$ "----" $$ literal str $$ "----") 195 <> blankline 196 where attrs = "[" <> literal (T.intercalate "," ("source" : classes)) <> "]" 197blockToAsciiDoc opts (BlockQuote blocks) = do 198 contents <- blockListToAsciiDoc opts blocks 199 let isBlock (BlockQuote _) = True 200 isBlock _ = False 201 -- if there are nested block quotes, put in an open block 202 let contents' = if any isBlock blocks 203 then "--" $$ contents $$ "--" 204 else contents 205 let bar = text "____" 206 return $ bar $$ chomp contents' $$ bar <> blankline 207blockToAsciiDoc opts block@(Table _ blkCapt specs thead tbody tfoot) = do 208 let (caption, aligns, widths, headers, rows) = 209 toLegacyTable blkCapt specs thead tbody tfoot 210 caption' <- inlineListToAsciiDoc opts caption 211 let caption'' = if null caption 212 then empty 213 else "." <> caption' <> cr 214 let isSimple = all (== 0) widths 215 let relativePercentWidths = if isSimple 216 then widths 217 else map (/ sum widths) widths 218 let widths'' :: [Integer] 219 widths'' = map (floor . (* 100)) relativePercentWidths 220 -- ensure that the widths sum to 100 221 let widths' = case widths'' of 222 _ | isSimple -> widths'' 223 (w:ws) | sum (w:ws) < 100 224 -> (100 - sum ws) : ws 225 ws -> ws 226 let totalwidth :: Integer 227 totalwidth = floor $ sum widths * 100 228 let colspec al wi = (case al of 229 AlignLeft -> "<" 230 AlignCenter -> "^" 231 AlignRight -> ">" 232 AlignDefault -> "") ++ 233 if wi == 0 then "" else show wi ++ "%" 234 let headerspec = if all null headers 235 then empty 236 else text "options=\"header\"," 237 let widthspec = if totalwidth == 0 238 then empty 239 else text "width=" 240 <> doubleQuotes (text $ show totalwidth ++ "%") 241 <> text "," 242 let tablespec = text "[" 243 <> widthspec 244 <> text "cols=" 245 <> doubleQuotes (text $ intercalate "," 246 $ zipWith colspec aligns widths') 247 <> text "," 248 <> headerspec <> text "]" 249 250 -- construct cells and recurse in case of nested tables 251 parentTableLevel <- gets tableNestingLevel 252 let currentNestingLevel = parentTableLevel + 1 253 254 modify $ \st -> st{ tableNestingLevel = currentNestingLevel } 255 256 let separator = text (if parentTableLevel == 0 257 then "|" -- top level separator 258 else "!") -- nested separator 259 260 let makeCell [Plain x] = do d <- blockListToAsciiDoc opts [Plain x] 261 return $ separator <> chomp d 262 makeCell [Para x] = makeCell [Plain x] 263 makeCell [] = return separator 264 makeCell bs = if currentNestingLevel == 2 265 then do 266 --asciidoc only supports nesting once 267 report $ BlockNotRendered block 268 return separator 269 else do 270 d <- blockListToAsciiDoc opts bs 271 return $ (text "a" <> separator) $$ d 272 273 let makeRow cells = hsep `fmap` mapM makeCell cells 274 rows' <- mapM makeRow rows 275 head' <- makeRow headers 276 modify $ \st -> st{ tableNestingLevel = parentTableLevel } 277 let head'' = if all null headers then empty else head' 278 let colwidth = if writerWrapText opts == WrapAuto 279 then writerColumns opts 280 else 100000 281 let maxwidth = maximum $ fmap offset (head' :| rows') 282 let body = if maxwidth > colwidth then vsep rows' else vcat rows' 283 let border = separator <> text "===" 284 return $ 285 caption'' $$ tablespec $$ border $$ head'' $$ body $$ border $$ blankline 286blockToAsciiDoc opts (BulletList items) = do 287 inlist <- gets inList 288 modify $ \st -> st{ inList = True } 289 contents <- mapM (bulletListItemToAsciiDoc opts) items 290 modify $ \st -> st{ inList = inlist } 291 return $ mconcat contents <> blankline 292blockToAsciiDoc opts (OrderedList (start, sty, _delim) items) = do 293 let listStyle = case sty of 294 DefaultStyle -> [] 295 Decimal -> ["arabic"] 296 Example -> [] 297 _ -> [T.toLower (tshow sty)] 298 let listStart = ["start=" <> tshow start | start /= 1] 299 let listoptions = case T.intercalate ", " (listStyle ++ listStart) of 300 "" -> empty 301 x -> brackets (literal x) 302 inlist <- gets inList 303 modify $ \st -> st{ inList = True } 304 contents <- mapM (orderedListItemToAsciiDoc opts) items 305 modify $ \st -> st{ inList = inlist } 306 return $ listoptions $$ mconcat contents <> blankline 307blockToAsciiDoc opts (DefinitionList items) = do 308 inlist <- gets inList 309 modify $ \st -> st{ inList = True } 310 contents <- mapM (definitionListItemToAsciiDoc opts) items 311 modify $ \st -> st{ inList = inlist } 312 return $ mconcat contents <> blankline 313blockToAsciiDoc opts (Div (ident,classes,_) bs) = do 314 let identifier = if T.null ident then empty else "[[" <> literal ident <> "]]" 315 let admonitions = ["attention","caution","danger","error","hint", 316 "important","note","tip","warning"] 317 contents <- 318 case classes of 319 (l:_) | l `elem` admonitions -> do 320 let (titleBs, bodyBs) = 321 case bs of 322 (Div (_,["title"],_) ts : rest) -> (ts, rest) 323 _ -> ([], bs) 324 admonitionTitle <- if null titleBs 325 then return mempty 326 else ("." <>) <$> 327 blockListToAsciiDoc opts titleBs 328 admonitionBody <- blockListToAsciiDoc opts bodyBs 329 return $ "[" <> literal (T.toUpper l) <> "]" $$ 330 chomp admonitionTitle $$ 331 "====" $$ 332 chomp admonitionBody $$ 333 "====" 334 _ -> blockListToAsciiDoc opts bs 335 return $ identifier $$ contents $$ blankline 336 337-- | Convert bullet list item (list of blocks) to asciidoc. 338bulletListItemToAsciiDoc :: PandocMonad m 339 => WriterOptions -> [Block] -> ADW m (Doc Text) 340bulletListItemToAsciiDoc opts blocks = do 341 lev <- gets bulletListLevel 342 modify $ \s -> s{ bulletListLevel = lev + 1 } 343 contents <- foldM (addBlock opts) empty blocks 344 modify $ \s -> s{ bulletListLevel = lev } 345 let marker = text (replicate (lev + 1) '*') 346 return $ marker <> text " " <> listBegin blocks <> 347 contents <> cr 348 349addBlock :: PandocMonad m 350 => WriterOptions -> Doc Text -> Block -> ADW m (Doc Text) 351addBlock opts d b = do 352 x <- chomp <$> blockToAsciiDoc opts b 353 return $ 354 case b of 355 BulletList{} -> d <> cr <> x 356 OrderedList{} -> d <> cr <> x 357 Para (Math DisplayMath _:_) -> d <> cr <> x 358 Plain (Math DisplayMath _:_) -> d <> cr <> x 359 Para{} | isEmpty d -> x 360 Plain{} | isEmpty d -> x 361 _ -> d <> cr <> text "+" <> cr <> x 362 363listBegin :: [Block] -> Doc Text 364listBegin blocks = 365 case blocks of 366 Para (Math DisplayMath _:_) : _ -> "{blank}" 367 Plain (Math DisplayMath _:_) : _ -> "{blank}" 368 Para _ : _ -> empty 369 Plain _ : _ -> empty 370 _ : _ -> "{blank}" 371 [] -> "{blank}" 372 373-- | Convert ordered list item (a list of blocks) to asciidoc. 374orderedListItemToAsciiDoc :: PandocMonad m 375 => WriterOptions -- ^ options 376 -> [Block] -- ^ list item (list of blocks) 377 -> ADW m (Doc Text) 378orderedListItemToAsciiDoc opts blocks = do 379 lev <- gets orderedListLevel 380 modify $ \s -> s{ orderedListLevel = lev + 1 } 381 contents <- foldM (addBlock opts) empty blocks 382 modify $ \s -> s{ orderedListLevel = lev } 383 let marker = text (replicate (lev + 1) '.') 384 return $ marker <> text " " <> listBegin blocks <> contents <> cr 385 386-- | Convert definition list item (label, list of blocks) to asciidoc. 387definitionListItemToAsciiDoc :: PandocMonad m 388 => WriterOptions 389 -> ([Inline],[[Block]]) 390 -> ADW m (Doc Text) 391definitionListItemToAsciiDoc opts (label, defs) = do 392 labelText <- inlineListToAsciiDoc opts label 393 marker <- gets defListMarker 394 if marker == "::" 395 then modify (\st -> st{ defListMarker = ";;"}) 396 else modify (\st -> st{ defListMarker = "::"}) 397 let divider = cr <> text "+" <> cr 398 let defsToAsciiDoc :: PandocMonad m => [Block] -> ADW m (Doc Text) 399 defsToAsciiDoc ds = (vcat . intersperse divider . map chomp) 400 `fmap` mapM (blockToAsciiDoc opts) ds 401 defs' <- mapM defsToAsciiDoc defs 402 modify (\st -> st{ defListMarker = marker }) 403 let contents = nest 2 $ vcat $ intersperse divider $ map chomp defs' 404 return $ labelText <> literal marker <> cr <> contents <> cr 405 406-- | Convert list of Pandoc block elements to asciidoc. 407blockListToAsciiDoc :: PandocMonad m 408 => WriterOptions -- ^ Options 409 -> [Block] -- ^ List of block elements 410 -> ADW m (Doc Text) 411blockListToAsciiDoc opts blocks = 412 mconcat `fmap` mapM (blockToAsciiDoc opts) blocks 413 414data SpacyLocation = End | Start 415 416-- | Convert list of Pandoc inline elements to asciidoc. 417inlineListToAsciiDoc :: PandocMonad m => 418 WriterOptions -> 419 [Inline] -> 420 ADW m (Doc Text) 421inlineListToAsciiDoc opts lst = do 422 oldIntraword <- gets intraword 423 setIntraword False 424 result <- go lst 425 setIntraword oldIntraword 426 return result 427 where go [] = return empty 428 go (y:x:xs) 429 | not (isSpacy End y) = do 430 y' <- if isSpacy Start x 431 then inlineToAsciiDoc opts y 432 else withIntraword $ inlineToAsciiDoc opts y 433 x' <- withIntraword $ inlineToAsciiDoc opts x 434 xs' <- go xs 435 return (y' <> x' <> xs') 436 | not (isSpacy Start x) = do 437 y' <- withIntraword $ inlineToAsciiDoc opts y 438 xs' <- go (x:xs) 439 return (y' <> xs') 440 go (x:xs) = do 441 x' <- inlineToAsciiDoc opts x 442 xs' <- go xs 443 return (x' <> xs') 444 isSpacy :: SpacyLocation -> Inline -> Bool 445 isSpacy _ Space = True 446 isSpacy _ LineBreak = True 447 isSpacy _ SoftBreak = True 448 -- Note that \W characters count as spacy in AsciiDoc 449 -- for purposes of determining interword: 450 isSpacy End (Str xs) = case T.unsnoc xs of 451 Just (_, c) -> isPunctuation c || isSpace c 452 _ -> False 453 isSpacy Start (Str xs) 454 | Just (c, _) <- T.uncons xs = isPunctuation c || isSpace c 455 isSpacy _ _ = False 456 457setIntraword :: PandocMonad m => Bool -> ADW m () 458setIntraword b = modify $ \st -> st{ intraword = b } 459 460withIntraword :: PandocMonad m => ADW m a -> ADW m a 461withIntraword p = setIntraword True *> p <* setIntraword False 462 463-- | Convert Pandoc inline element to asciidoc. 464inlineToAsciiDoc :: PandocMonad m => WriterOptions -> Inline -> ADW m (Doc Text) 465inlineToAsciiDoc opts (Emph [Strong xs]) = 466 inlineToAsciiDoc opts (Strong [Emph xs]) -- see #5565 467inlineToAsciiDoc opts (Emph lst) = do 468 contents <- inlineListToAsciiDoc opts lst 469 isIntraword <- gets intraword 470 let marker = if isIntraword then "__" else "_" 471 return $ marker <> contents <> marker 472inlineToAsciiDoc opts (Underline lst) = do 473 contents <- inlineListToAsciiDoc opts lst 474 return $ "+++" <> contents <> "+++" 475inlineToAsciiDoc opts (Strong lst) = do 476 contents <- inlineListToAsciiDoc opts lst 477 isIntraword <- gets intraword 478 let marker = if isIntraword then "**" else "*" 479 return $ marker <> contents <> marker 480inlineToAsciiDoc opts (Strikeout lst) = do 481 contents <- inlineListToAsciiDoc opts lst 482 return $ "[line-through]*" <> contents <> "*" 483inlineToAsciiDoc opts (Superscript lst) = do 484 contents <- inlineListToAsciiDoc opts lst 485 return $ "^" <> contents <> "^" 486inlineToAsciiDoc opts (Subscript lst) = do 487 contents <- inlineListToAsciiDoc opts lst 488 return $ "~" <> contents <> "~" 489inlineToAsciiDoc opts (SmallCaps lst) = inlineListToAsciiDoc opts lst 490inlineToAsciiDoc opts (Quoted qt lst) = do 491 isAsciidoctor <- gets asciidoctorVariant 492 inlineListToAsciiDoc opts $ 493 case qt of 494 SingleQuote 495 | isAsciidoctor -> [Str "'`"] ++ lst ++ [Str "`'"] 496 | otherwise -> [Str "`"] ++ lst ++ [Str "'"] 497 DoubleQuote 498 | isAsciidoctor -> [Str "\"`"] ++ lst ++ [Str "`\""] 499 | otherwise -> [Str "``"] ++ lst ++ [Str "''"] 500inlineToAsciiDoc _ (Code _ str) = do 501 isAsciidoctor <- gets asciidoctorVariant 502 let escChar '`' = "\\'" 503 escChar c = T.singleton c 504 let contents = literal (T.concatMap escChar str) 505 return $ 506 if isAsciidoctor 507 then text "`+" <> contents <> "+`" 508 else text "`" <> contents <> "`" 509inlineToAsciiDoc _ (Str str) = return $ literal $ escapeString str 510inlineToAsciiDoc _ (Math InlineMath str) = do 511 isAsciidoctor <- gets asciidoctorVariant 512 modify $ \st -> st{ hasMath = True } 513 let content = if isAsciidoctor 514 then literal str 515 else "$" <> literal str <> "$" 516 return $ "latexmath:[" <> content <> "]" 517inlineToAsciiDoc _ (Math DisplayMath str) = do 518 isAsciidoctor <- gets asciidoctorVariant 519 modify $ \st -> st{ hasMath = True } 520 let content = if isAsciidoctor 521 then literal str 522 else "\\[" <> literal str <> "\\]" 523 inlist <- gets inList 524 let sepline = if inlist 525 then text "+" 526 else blankline 527 return $ 528 (cr <> sepline) $$ "[latexmath]" $$ "++++" $$ 529 content $$ "++++" <> cr 530inlineToAsciiDoc _ il@(RawInline f s) 531 | f == "asciidoc" = return $ literal s 532 | otherwise = do 533 report $ InlineNotRendered il 534 return empty 535inlineToAsciiDoc _ LineBreak = return $ " +" <> cr 536inlineToAsciiDoc _ Space = return space 537inlineToAsciiDoc opts SoftBreak = 538 case writerWrapText opts of 539 WrapAuto -> return space 540 WrapPreserve -> return cr 541 WrapNone -> return space 542inlineToAsciiDoc opts (Cite _ lst) = inlineListToAsciiDoc opts lst 543inlineToAsciiDoc opts (Link _ txt (src, _tit)) = do 544-- relative: link:downloads/foo.zip[download foo.zip] 545-- abs: http://google.cod[Google] 546-- or my@email.com[email john] 547 linktext <- inlineListToAsciiDoc opts txt 548 let isRelative = T.all (/= ':') src 549 let prefix = if isRelative 550 then text "link:" 551 else empty 552 let srcSuffix = fromMaybe src (T.stripPrefix "mailto:" src) 553 let useAuto = case txt of 554 [Str s] | escapeURI s == srcSuffix -> True 555 _ -> False 556 return $ if useAuto 557 then literal srcSuffix 558 else prefix <> literal src <> "[" <> linktext <> "]" 559inlineToAsciiDoc opts (Image attr alternate (src, tit)) = 560 ("image:" <>) <$> imageArguments opts attr alternate src tit 561inlineToAsciiDoc opts (Note [Para inlines]) = 562 inlineToAsciiDoc opts (Note [Plain inlines]) 563inlineToAsciiDoc opts (Note [Plain inlines]) = do 564 contents <- inlineListToAsciiDoc opts inlines 565 return $ text "footnote:[" <> contents <> "]" 566-- asciidoc can't handle blank lines in notes 567inlineToAsciiDoc _ (Note _) = return "[multiblock footnote omitted]" 568inlineToAsciiDoc opts (Span (ident,classes,_) ils) = do 569 contents <- inlineListToAsciiDoc opts ils 570 isIntraword <- gets intraword 571 let marker = if isIntraword then "##" else "#" 572 if T.null ident && null classes 573 then return contents 574 else do 575 let modifier = brackets $ literal $ T.unwords $ 576 [ "#" <> ident | not (T.null ident)] ++ map ("." <>) classes 577 return $ modifier <> marker <> contents <> marker 578 579-- | Provides the arguments for both `image:` and `image::` 580-- e.g.: sunset.jpg[Sunset,300,200] 581imageArguments :: PandocMonad m => WriterOptions -> 582 Attr -> [Inline] -> Text -> Text -> 583 ADW m (Doc Text) 584imageArguments opts attr altText src title = do 585 let txt = if null altText || (altText == [Str ""]) 586 then [Str "image"] 587 else altText 588 linktext <- inlineListToAsciiDoc opts txt 589 let linktitle = if T.null title 590 then empty 591 else ",title=\"" <> literal title <> "\"" 592 showDim dir = case dimension dir attr of 593 Just (Percent a) -> 594 ["scaledwidth=" <> text (show (Percent a))] 595 Just dim -> 596 [text (show dir) <> "=" <> 597 literal (showInPixel opts dim)] 598 Nothing -> 599 [] 600 dimList = showDim Width ++ showDim Height 601 dims = if null dimList 602 then empty 603 else "," <> mconcat (intersperse "," dimList) 604 return $ literal src <> "[" <> linktext <> linktitle <> dims <> "]" 605