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