Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MANUAL.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5389,7 +5389,7 @@ from being interpreted as Markdown.

### Extension: `native_divs` ###

Use native pandoc `Div` blocks for content inside `<div>` tags.
Use native pandoc `Div` blocks for content inside `<div>` tags. This extension also influences how HTML `<p>` tags with attributes are processed, by wrapping them in `Div` blocks to better preserve their attributes during conversion.
For the most part this should give the same output as
`markdown_in_html_blocks`, but it makes it easier to write pandoc
filters to manipulate groups of blocks.
Expand Down
2 changes: 2 additions & 0 deletions src/Text/Pandoc/Extensions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ data Extension =
| Ext_ntb -- ^ ConTeXt Natural Tables
| Ext_old_dashes -- ^ -- = em, - before number = en
| Ext_pandoc_title_block -- ^ Pandoc title block
| Ext_paragraph_attributes -- ^ Allow paragraph attributes in HTML
| Ext_pipe_tables -- ^ Pipe tables (as in PHP markdown extra)
| Ext_raw_attribute -- ^ Allow explicit raw blocks/inlines
| Ext_raw_html -- ^ Allow raw HTML
Expand Down Expand Up @@ -598,6 +599,7 @@ getAllExtensions f = universalExtensions <> getAll f
, Ext_literate_haskell
, Ext_epub_html_exts
, Ext_smart
, Ext_paragraph_attributes
]
getAll "html4" = getAll "html"
getAll "html5" = getAll "html"
Expand Down
33 changes: 29 additions & 4 deletions src/Text/Pandoc/Readers/HTML.hs
Original file line number Diff line number Diff line change
Expand Up @@ -623,13 +623,38 @@ pPlain = do
then return mempty
else return $ B.plain contents

pPara :: PandocMonad m => TagParser m Blocks
pPara = do
-- Helper function for pPara when significant attributes are present
pParaWithWrapper :: PandocMonad m => Attr -> TagParser m Blocks
pParaWithWrapper (ident, classes, kvs) = do
guardEnabled Ext_native_divs -- Ensure native_divs is enabled for this behavior
contents <- trimInlines <$> pInTags "p" inline
(do guardDisabled Ext_empty_paragraphs
guard (null contents)
return mempty) <|> do
let wrapperAttr = ("wrapper", "1")
let finalKVs = wrapperAttr : kvs
let finalAttrs = (ident, classes, finalKVs)
return $ B.divWith finalAttrs (B.para contents)

-- Helper function for pPara when no significant attributes are present
pParaSimple :: PandocMonad m => TagParser m Blocks
pParaSimple = do
contents <- trimInlines <$> pInTags "p" inline
(do guardDisabled Ext_empty_paragraphs
guard (null contents)
return mempty)
<|> return (B.para contents)
return mempty) <|>
return (B.para contents)

pPara :: PandocMonad m => TagParser m Blocks
pPara = do
TagOpen _ attr' <- lookAhead $ pSatisfy (matchTagOpen "p" [])
let attr@(ident, classes, kvs) = toAttr attr'
-- "Significant" attributes are any id, class, or key-value pair.
let hasSignificantAttributes = not (T.null ident) || not (null classes) || not (null kvs)

if hasSignificantAttributes
then pParaWithWrapper attr
else pParaSimple

pFigure :: PandocMonad m => TagParser m Blocks
pFigure = do
Expand Down
60 changes: 32 additions & 28 deletions src/Text/Pandoc/Writers/AsciiDoc.hs
Original file line number Diff line number Diff line change
Expand Up @@ -377,34 +377,38 @@ blockToAsciiDoc opts (DefinitionList items) = do
return $ mconcat contents <> blankline

-- convert admonition and sidebar divs to asicidoc
blockToAsciiDoc opts (Div (ident,classes,_) bs) = do
let identifier = if T.null ident then empty else "[[" <> literal ident <> "]]"
let admonition_classes = ["attention","caution","danger","error","hint",
"important","note","tip","warning"]
let sidebar_class = "sidebar"

contents <-
case classes of
(l:_) | l `elem` admonition_classes || T.toLower l == sidebar_class -> do
let (titleBs, bodyBs) =
case bs of
(Div (_,["title"],_) ts : rest) -> (ts, rest)
_ -> ([], bs)
let fence = if l == "sidebar" then "****" else "===="
elemTitle <- if null titleBs ||
-- If title matches class, omit
(T.toLower (T.strip (stringify titleBs))) == l
then return mempty
else ("." <>) <$>
blockListToAsciiDoc opts titleBs
elemBody <- blockListToAsciiDoc opts bodyBs
return $ "[" <> literal (T.toUpper l) <> "]" $$
chomp elemTitle $$
fence $$
chomp elemBody $$
fence
_ -> blockListToAsciiDoc opts bs
return $ identifier $$ contents $$ blankline
blockToAsciiDoc opts divBlock@(Div (ident,classes,_) bs) = do
-- First try to unwrap wrapper divs
case unwrapWrapperDiv divBlock of
Para inlines -> blockToAsciiDoc opts (Para inlines)
_ -> do
let identifier = if T.null ident then empty else "[[" <> literal ident <> "]]"
let admonition_classes = ["attention","caution","danger","error","hint",
"important","note","tip","warning"]
let sidebar_class = "sidebar"

contents <-
case classes of
(l:_) | l `elem` admonition_classes || T.toLower l == sidebar_class -> do
let (titleBs, bodyBs) =
case bs of
(Div (_,["title"],_) ts : rest) -> (ts, rest)
_ -> ([], bs)
let fence = if l == "sidebar" then "****" else "===="
elemTitle <- if null titleBs ||
-- If title matches class, omit
(T.toLower (T.strip (stringify titleBs))) == l
then return mempty
else ("." <>) <$>
blockListToAsciiDoc opts titleBs
elemBody <- blockListToAsciiDoc opts bodyBs
return $ "[" <> literal (T.toUpper l) <> "]" $$
chomp elemTitle $$
fence $$
chomp elemBody $$
fence
_ -> blockListToAsciiDoc opts bs
return $ identifier $$ contents $$ blankline

-- | Convert bullet list item (list of blocks) to asciidoc.
bulletListItemToAsciiDoc :: PandocMonad m
Expand Down
37 changes: 20 additions & 17 deletions src/Text/Pandoc/Writers/ConTeXt.hs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import Text.Pandoc.Shared
import Text.Pandoc.URI (isURI)
import Text.Pandoc.Templates (renderTemplate)
import Text.Pandoc.Walk (query)
import Text.Pandoc.Writers.Shared
import Text.Pandoc.Writers.Shared (unwrapWrapperDiv, defField, getField, resetField, lookupMetaString, metaToContext, getLang)
import Text.Printf (printf)

import qualified Data.List.NonEmpty as NonEmpty
Expand Down Expand Up @@ -187,37 +187,40 @@ toLabel z = T.concatMap go z

-- | Convert Pandoc block element to ConTeXt.
blockToConTeXt :: PandocMonad m => Block -> WM m (Doc Text)
blockToConTeXt (Div attr@(_,"section":_,_)
blockToConTeXt block = blockToConTeXt' (unwrapWrapperDiv block)

blockToConTeXt' :: PandocMonad m => Block -> WM m (Doc Text)
blockToConTeXt' (Div attr@(_,"section":_,_)
(Header level _ title' : xs)) = do
header' <- sectionHeader attr level title' SectionHeading
footer' <- sectionFooter attr level
innerContents <- blockListToConTeXt xs
return $ header' $$ innerContents $$ footer'
blockToConTeXt (Plain lst) = do
blockToConTeXt' (Plain lst) = do
opts <- gets stOptions
contents <- inlineListToConTeXt lst
return $
if isEnabled Ext_tagging opts
then "\\bpar{}" <> contents <> "\\epar{}"
else contents
blockToConTeXt (Para lst) = do
blockToConTeXt' (Para lst) = do
opts <- gets stOptions
contents <- inlineListToConTeXt lst
return $
if isEnabled Ext_tagging opts
then "\\bpar" $$ contents $$ "\\epar" <> blankline
else contents <> blankline
blockToConTeXt (LineBlock lns) = do
blockToConTeXt' (LineBlock lns) = do
let emptyToBlankline doc = if isEmpty doc
then blankline
else doc
doclines <- mapM inlineListToConTeXt lns
let contextLines = vcat . map emptyToBlankline $ doclines
return $ "\\startlines" $$ contextLines $$ "\\stoplines" <> blankline
blockToConTeXt (BlockQuote lst) = do
blockToConTeXt' (BlockQuote lst) = do
contents <- blockListToConTeXt lst
return $ "\\startblockquote" $$ nest 0 contents $$ "\\stopblockquote" <> blankline
blockToConTeXt (CodeBlock (_ident, classes, kv) str) = do
blockToConTeXt' (CodeBlock (_ident, classes, kv) str) = do
opts <- gets stOptions
let syntaxMap = writerSyntaxMap opts
let attr' = ("", classes, kv)
Expand All @@ -236,15 +239,15 @@ blockToConTeXt (CodeBlock (_ident, classes, kv) str) = do
if null classes || isNothing (writerHighlightStyle opts)
then pure unhighlighted
else highlighted
blockToConTeXt b@(RawBlock f str)
blockToConTeXt' b@(RawBlock f str)
| f == Format "context" || f == Format "tex" = return $ literal str <> blankline
| otherwise = empty <$ report (BlockNotRendered b)
blockToConTeXt (Div ("refs",classes,_) bs) = do
blockToConTeXt' (Div ("refs",classes,_) bs) = do
modify $ \st -> st{ stHasCslRefs = True
, stCslHangingIndent = "hanging-indent" `elem` classes }
inner <- blockListToConTeXt bs
return $ "\\startcslreferences" $$ inner $$ "\\stopcslreferences"
blockToConTeXt (Div (ident,_,kvs) bs) = do
blockToConTeXt' (Div (ident,_,kvs) bs) = do
let align dir txt = "\\startalignment[" <> dir <> "]" $$ txt $$ "\\stopalignment"
mblang <- fromBCP47 (lookup "lang" kvs)
let wrapRef txt = if T.null ident
Expand All @@ -261,13 +264,13 @@ blockToConTeXt (Div (ident,_,kvs) bs) = do
Nothing -> txt
wrapBlank txt = blankline <> txt <> blankline
wrapBlank . wrapLang . wrapDir . wrapRef <$> blockListToConTeXt bs
blockToConTeXt (BulletList lst) = do
blockToConTeXt' (BulletList lst) = do
contents <- mapM listItemToConTeXt lst
return $ ("\\startitemize" <> if isTightList lst
then brackets "packed"
else empty) $$
vcat contents $$ literal "\\stopitemize" <> blankline
blockToConTeXt (OrderedList (start, style', delim) lst) = do
blockToConTeXt' (OrderedList (start, style', delim) lst) = do
st <- get
let level = stOrderedListLevel st
put st {stOrderedListLevel = level + 1}
Expand Down Expand Up @@ -295,15 +298,15 @@ blockToConTeXt (OrderedList (start, style', delim) lst) = do
let specs = T.pack style'' <> specs2
return $ "\\startenumerate" <> literal specs $$ vcat contents $$
"\\stopenumerate" <> blankline
blockToConTeXt (DefinitionList lst) =
blockToConTeXt' (DefinitionList lst) =
liftM vcat $ mapM defListItemToConTeXt lst
blockToConTeXt HorizontalRule = return $ "\\thinrule" <> blankline
blockToConTeXt' HorizontalRule = return $ "\\thinrule" <> blankline
-- If this is ever executed, provide a default for the reference identifier.
blockToConTeXt (Header level attr lst) =
blockToConTeXt' (Header level attr lst) =
sectionHeader attr level lst NonSectionHeading
blockToConTeXt (Table attr caption colspecs thead tbody tfoot) =
blockToConTeXt' (Table attr caption colspecs thead tbody tfoot) =
tableToConTeXt (Ann.toTable attr caption colspecs thead tbody tfoot)
blockToConTeXt (Figure (ident, _, _) (Caption cshort clong) body) = do
blockToConTeXt' (Figure (ident, _, _) (Caption cshort clong) body) = do
title <- inlineListToConTeXt (blocksToInlines clong)
list <- maybe (pure empty) inlineListToConTeXt cshort
content <- blockListToConTeXt body
Expand Down
7 changes: 5 additions & 2 deletions src/Text/Pandoc/Writers/DocBook.hs
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,11 @@ blockToDocBook opts (Div (id',"section":_classes,divattrs)
title' <- inlinesToDocBook opts ils
contents <- blocksToDocBook opts bs
return $ inTags True tag attribs $ inTagsSimple "title" title' $$ contents
blockToDocBook opts (Div (ident,classes,_) bs) = do
blockToDocBook opts (Div (ident,classes,_kvs) bs) = do
version <- ask
let identAttribs = [(idName version, ident) | not (T.null ident)]
admonitions = ["caution","danger","important","note","tip","warning"]

case classes of
(l:_) | l `elem` admonitions -> do
let (mTitleBs, bodyBs) =
Expand All @@ -219,13 +220,15 @@ blockToDocBook opts (Div (ident,classes,_) bs) = do
_ -> (Nothing, bs)
admonitionTitle <- case mTitleBs of
Nothing -> return mempty
-- id will be attached to the admonition so lets pass empty identAttrs.
-- id will be attached to the admonition so let's pass empty identAttrs.
Just titleBs -> inTagsSimple "title" <$> titleBs
admonitionBody <- handleDivBody [] bodyBs
return (inTags True l identAttribs (admonitionTitle $$ admonitionBody))
_ -> handleDivBody identAttribs bs
where
handleDivBody identAttribs [Para lst] =
-- For wrapper divs with single Para, apply attributes to para
-- For normal divs with single Para, also apply attributes to para (original behavior)
if hasLineBreaks lst
then flush . nowrap . inTags False "literallayout" identAttribs
<$> inlinesToDocBook opts lst
Expand Down
4 changes: 3 additions & 1 deletion src/Text/Pandoc/Writers/Docx/OpenXML.hs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ writeOpenXML :: PandocMonad m
-> WS m (Text, [Element], [Element])
writeOpenXML opts (Pandoc meta blocks) = do
setupTranslations meta
-- Apply unwrapWrapperDiv to all blocks
let blocks' = walk unwrapWrapperDiv blocks
let includeTOC = writerTableOfContents opts || lookupMetaBool "toc" meta
let includeLOF = writerListOfFigures opts || lookupMetaBool "lof" meta
let includeLOT = writerListOfTables opts || lookupMetaBool "lot" meta
Expand All @@ -252,7 +254,7 @@ writeOpenXML opts (Pandoc meta blocks) = do
(fmap (hcat . map (literal . showContent)) . inlinesToOpenXML opts)
(docAuthors meta)

doc' <- setFirstPara >> blocksToOpenXML opts blocks
doc' <- setFirstPara >> blocksToOpenXML opts blocks'
let body = vcat $ map (literal . showContent) doc'
notes' <- gets (reverse . stFootnotes)
comments <- gets (reverse . stComments)
Expand Down
4 changes: 2 additions & 2 deletions src/Text/Pandoc/Writers/DokuWiki.hs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import Text.Pandoc.Shared (figureDiv, linesToPara, removeFormatting, trimr)
import Text.Pandoc.URI (escapeURI, isURI)
import Text.Pandoc.Templates (renderTemplate)
import Text.DocLayout (render, literal)
import Text.Pandoc.Writers.Shared (defField, metaToContext, toLegacyTable)
import Text.Pandoc.Writers.Shared (defField, metaToContext, toLegacyTable, unwrapWrapperDiv)
import Data.Maybe (fromMaybe)
import qualified Data.Map as M

Expand Down Expand Up @@ -101,7 +101,7 @@ blockToDokuWiki :: PandocMonad m
-> DokuWiki m Text

blockToDokuWiki opts (Div _attrs bs) = do
contents <- blockListToDokuWiki opts bs
contents <- blockListToDokuWiki opts (map unwrapWrapperDiv bs)
indent <- asks stIndent
return $ contents <> if T.null indent then "\n" else ""

Expand Down
4 changes: 2 additions & 2 deletions src/Text/Pandoc/Writers/EPUB.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import qualified Data.Text.Lazy as TL
import System.FilePath (takeExtension, takeFileName, makeRelative)
import Text.HTML.TagSoup (Tag (TagOpen), fromAttrib, parseTags)
import Text.Pandoc.Builder (fromList, setMeta)
import Text.Pandoc.Writers.Shared (ensureValidXmlIdentifiers)
import Text.Pandoc.Writers.Shared (ensureValidXmlIdentifiers, unwrapWrapperDiv)
import Data.Tree (Tree(..))
import Text.Pandoc.Class (PandocMonad, report)
import qualified Text.Pandoc.Class.PandocPure as P
Expand Down Expand Up @@ -1211,7 +1211,7 @@ transformBlock (RawBlock fmt raw)
let tags = parseTags raw
tags' <- mapM transformTag tags
return $ RawBlock fmt (renderTags' tags')
transformBlock b = return b
transformBlock b = return $ unwrapWrapperDiv b

transformInline :: PandocMonad m
=> WriterOptions
Expand Down
4 changes: 2 additions & 2 deletions src/Text/Pandoc/Writers/FB2.hs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import Text.Pandoc.Shared (blocksToInlines, capitalize, orderedListMarkers,
makeSections, tshow, stringify)
import Text.Pandoc.Walk (walk)
import Text.Pandoc.Writers.Shared (lookupMetaString, toLegacyTable,
ensureValidXmlIdentifiers)
ensureValidXmlIdentifiers, unwrapWrapperDiv)
import Data.Generics (everywhere, mkT)

-- | Data to be written at the end of the document:
Expand Down Expand Up @@ -315,7 +315,7 @@ blockToXml (RawBlock f str) =
Left msg -> throwError $ PandocXMLError "" msg
Right nds -> return nds
else return []
blockToXml (Div _ bs) = cMapM blockToXml bs
blockToXml (Div _ bs) = cMapM blockToXml (map unwrapWrapperDiv bs)
blockToXml (BlockQuote bs) = list . el "cite" <$> cMapM blockToXml bs
blockToXml (LineBlock lns) =
list . el "poem" <$> mapM stanza (split null lns)
Expand Down
13 changes: 13 additions & 0 deletions src/Text/Pandoc/Writers/HTML.hs
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,19 @@ blockToHtmlInner opts (Para lst) = do
blockToHtmlInner opts (LineBlock lns) = do
htmlLines <- inlineListToHtml opts $ intercalate [LineBreak] lns
return $ H.div ! A.class_ "line-block" $ htmlLines
blockToHtmlInner opts (Div (ident, classes, kvs) [Para pans]) | Just "1" <- lookup "wrapper" kvs = do
-- This is a paragraph that was wrapped in a Div by the reader
-- Unwrap it back to a <p> tag, with extension-controlled attribute handling
inner <- inlineListToHtml opts pans
if isEnabled Ext_paragraph_attributes opts
then do
-- Extension enabled: preserve all attributes except wrapper
let pKVs = filter (\(k,_) -> k /= "wrapper") kvs
let pAttr = (ident, classes, pKVs)
addAttrs opts pAttr (H.p inner)
else
-- Extension disabled: remove all attributes
return (H.p inner)
blockToHtmlInner opts (Div (ident, "section":dclasses, dkvs)
(Header level
hattr@(hident,hclasses,hkvs) ils : xs)) = do
Expand Down
Loading