git.iko.soy/typeable/pandoc
on master help sign in
Replace Element and makeHierarchical with makeSections.

Text.Pandoc.Shared: + Remove `Element` type [API change] + Remove `makeHierarchicalize` [API change] + Add `makeSections` [API change] + Export `deLink` [API change] Now that we have Divs, we can use them to represent the structure of sections, and we don't need a special Element type. `makeSections` reorganizes a block list, adding Divs with class `section` around sections, and adding numbering if needed. This change also fixes some longstanding issues recognizing section structure when the document contains Divs. Closes #3057, see also #997. All writers have been changed to use `makeSections`. Note that in the process we have reverted the change c1d058aeb1c6a331a2cc22786ffaab17f7118ccd made in response to #5168, which I'm not completely sure was a good idea. Lua modules have also been adjusted accordingly. Existing lua filters that use `hierarchicalize` will need to be rewritten to use `make_sections`.

commit9f984ff26ac248a27212a37ab34754a2e9261e8c authorJohn MacFarlane <jgm@berkeley.edu> dateSat 7 Sep 2019, 11:23 (7 years ago) parent1ccff33
25 files changed+479−635
doc/lua-filters.md+13−49 src/Text/Pandoc/Lua/Marshaling/AST.hs−31 src/Text/Pandoc/Lua/Module/Types.hs−5 src/Text/Pandoc/Lua/Module/Utils.hs+5−4 src/Text/Pandoc/Shared.hs+66−61 src/Text/Pandoc/Slides.hs+1−1 src/Text/Pandoc/Writers/AsciiDoc.hs+6−9 src/Text/Pandoc/Writers/ConTeXt.hs+7−11 src/Text/Pandoc/Writers/Docbook.hs+28−34 src/Text/Pandoc/Writers/Docx.hs+1−1 src/Text/Pandoc/Writers/EPUB.hs+29−25 src/Text/Pandoc/Writers/FB2.hs+16−17 src/Text/Pandoc/Writers/HTML.hs+100−133 src/Text/Pandoc/Writers/JATS.hs+16−23 src/Text/Pandoc/Writers/LaTeX.hs+131−152 src/Text/Pandoc/Writers/OPML.hs+13−22 src/Text/Pandoc/Writers/Shared.hs+18−9 src/Text/Pandoc/Writers/TEI.hs+22−26 test/command/1710.md−2 test/command/4016.md+2−6 test/command/4690.md−2 test/command/empty_paragraphs.md−2 test/lua/module/pandoc-utils.lua+5−8 test/writer.html4−1 test/writer.html5−1
doc/lua-filters.md+13−49
@@ -1314,40 +1314,6 @@ Object equality is determined via
13141314: delimiter of list numbers; one of `DefaultDelim`, `Period`,
13151315 `OneParen`, and `TwoParens` (string)
13161316
1317−## Hierarchical Element {#type-ref-Element}
1318−
1319−Hierarchical elements can be either *Sec* (sections) or *Blk*
1320−(blocks). *Blk* elements are treated like
1321−[Block](#type-ref-Block)s.
1322−
1323−### Sec {#type-ref-Sec}
1324−
1325−Section elements used to provide hierarchical information on
1326−document contents.
1327−
1328−**Objects of this type are read-only.**
1329−
1330−`level`
1331−: header level (integer)
1332−
1333−`numbering`
1334−: section numbering ([list](#module-pandoc.list) of integers)
1335−
1336−`attr`
1337−: header attributes ([Attr](#type-ref-Attr))
1338−
1339−`label`
1340−: header content ([list](#module-pandoc.list) of
1341− [Inline](#type-ref-Inline)s)
1342−
1343−`contents`
1344−: list of contents in this section
1345− ([list](#module-pandoc.list) of [hierarchical
1346− element](#Element)s)
1347−
1348−`tag`, `t`
1349−: constant `Sec` (string)
1350−
13511317## ReaderOptions {#type-ref-ReaderOptions}
13521318
13531319Pandoc reader options
@@ -2392,23 +2358,23 @@ Returns:
23922358
23932359- Whether the two objects represent the same element (boolean)
23942360
2395−### hierarchicalize {#utils-hierarchicalize}
2396−
2397−`hierarchicalize (blocks)`
2361+### make\_sections {#utils-make_sections}
23982362
2399−Convert list of [Blocks](#Blocks) into an hierarchical list. An
2400−hierarchical elements is either a normal block (but no Header),
2401−or a `Sec` element. The latter has the following fields:
2363+`make_sections (number_sections, base_level, blocks)`
24022364
2403−- level: level in the document hierarchy;
2404−- numbering: list of integers of length `level`, specifying
2405− the absolute position of the section in the document;
2406−- attr: section attributes (see [Attr](#Attr));
2407−- contents: nested list of hierarchical elements.
2365+Converst list of [Blocks](#Blocks) into sections.
2366+`Div`s will be created beginning at each `Header`
2367+and containing following content until the next `Header`
2368+of comparable level. If `number_sections` is true,
2369+a `number` attribute will be added to each `Header`
2370+containing the section number. If `base_level` is
2371+non-null, `Header` levels will be reorganized so
2372+that there are no gaps, and so that the base level
2373+is the level specified.
24082374
24092375Returns:
24102376
2411−- List of hierarchical elements.
2377+- List of [Blocks](#Blocks).
24122378
24132379Usage:
24142380
@@ -2416,9 +2382,7 @@ Usage:
24162382 pandoc.Header(2, pandoc.Str 'first'),
24172383 pandoc.Header(2, pandoc.Str 'second'),
24182384 }
2419− local elements = pandoc.utils.hierarchicalize(blocks)
2420− print(table.concat(elements[1].numbering, '.')) -- 0.1
2421− print(table.concat(elements[2].numbering, '.')) -- 0.2
2385+ local newblocks = pandoc.utils.make_sections(true, 1, blocks)
24222386
24232387### run\_json\_filter {#utils-run_json_filter}
24242388
src/Text/Pandoc/Lua/Marshaling/AST.hs−31
@@ -22,12 +22,9 @@ module Text.Pandoc.Lua.Marshaling.AST
2222import Prelude
2323import Control.Applicative ((<|>))
2424import Foreign.Lua (Lua, Peekable, Pushable, StackIndex)
25−import Foreign.Lua.Userdata ( ensureUserdataMetatable, pushAnyWithMetatable
26− , metatableName)
2725import Text.Pandoc.Definition
2826import Text.Pandoc.Lua.Util (defineHowTo, pushViaConstructor)
2927import Text.Pandoc.Lua.Marshaling.CommonState ()
30−import Text.Pandoc.Shared (Element (Blk, Sec))
3128
3229import qualified Foreign.Lua as Lua
3330import qualified Text.Pandoc.Lua.Util as LuaUtil
@@ -285,31 +282,3 @@ instance Pushable LuaListAttributes where
285282instance Peekable LuaListAttributes where
286283 peek = defineHowTo "get ListAttributes value" .
287284 fmap LuaListAttributes . Lua.peek
288−
289−--
290−-- Hierarchical elements
291−--
292−instance Pushable Element where
293− push (Blk blk) = Lua.push blk
294− push sec = pushAnyWithMetatable pushElementMetatable sec
295− where
296− pushElementMetatable = ensureUserdataMetatable (metatableName sec) $
297− LuaUtil.addFunction "__index" indexElement
298−
299−instance Peekable Element where
300− peek idx = Lua.ltype idx >>= \case
301− Lua.TypeUserdata -> Lua.peekAny idx
302− _ -> Blk <$> Lua.peek idx
303−
304−indexElement :: Element -> String -> Lua Lua.NumResults
305−indexElement = \case
306− (Blk _) -> const (1 <$ Lua.pushnil) -- this shouldn't happen
307− (Sec lvl num attr label contents) -> fmap (return 1) . \case
308− "level" -> Lua.push lvl
309− "numbering" -> Lua.push num
310− "attr" -> Lua.push (LuaAttr attr)
311− "label" -> Lua.push label
312− "contents" -> Lua.push contents
313− "tag" -> Lua.push "Sec"
314− "t" -> Lua.push "Sec"
315− _ -> Lua.pushnil
src/Text/Pandoc/Lua/Module/Types.hs−5
@@ -19,7 +19,6 @@ import Text.Pandoc.Definition
1919import Text.Pandoc.Lua.Marshaling.AST (LuaAttr, LuaListAttributes)
2020import Text.Pandoc.Lua.Marshaling.Version ()
2121import Text.Pandoc.Lua.Util (addFunction)
22−import Text.Pandoc.Shared (Element (..))
2322
2423import qualified Foreign.Lua as Lua
2524
@@ -38,7 +37,6 @@ pushCloneTable = do
3837 addFunction "Attr" cloneAttr
3938 addFunction "Block" cloneBlock
4039 addFunction "Citation" cloneCitation
41− addFunction "Element" cloneElement
4240 addFunction "Inline" cloneInline
4341 addFunction "Meta" cloneMeta
4442 addFunction "MetaValue" cloneMetaValue
@@ -55,9 +53,6 @@ cloneBlock = return
5553cloneCitation :: Citation -> Lua Citation
5654cloneCitation = return
5755
58−cloneElement :: Element -> Lua Element
59−cloneElement = return
60−
6156cloneInline :: Inline -> Lua Inline
6257cloneInline = return
6358
src/Text/Pandoc/Lua/Module/Utils.hs+5−4
@@ -38,7 +38,7 @@ pushModule mbDatadir = do
3838 Lua.newtable
3939 addFunction "blocks_to_inlines" blocksToInlines
4040 addFunction "equals" equals
41− addFunction "hierarchicalize" hierarchicalize
41+ addFunction "make_sections" makeSections
4242 addFunction "normalize_date" normalizeDate
4343 addFunction "run_json_filter" (runJSONFilter mbDatadir)
4444 addFunction "sha1" sha1
@@ -55,9 +55,10 @@ blocksToInlines blks optSep = do
5555 Nothing -> Shared.defaultBlocksSeparator
5656 return $ B.toList (Shared.blocksToInlinesWithSep sep blks)
5757
58−-- | Convert list of Pandoc blocks into (hierarchical) list of Elements.
59−hierarchicalize :: [Block] -> Lua [Shared.Element]
60−hierarchicalize = return . Shared.hierarchicalize
58+-- | Convert list of Pandoc blocks into sections using Divs.
59+makeSections :: Bool -> Lua.Optional Int -> [Block] -> Lua [Block]
60+makeSections number baselevel =
61+ return . Shared.makeSections number (Lua.fromOptional baselevel)
6162
6263-- | Parse a date and convert (if possible) to "YYYY-MM-DD" format. We
6364-- limit years to the range 1601-9999 (ISO 8601 accepts greater than
src/Text/Pandoc/Shared.hs+66−61
@@ -47,13 +47,13 @@ module Text.Pandoc.Shared (
4747 extractSpaces,
4848 removeFormatting,
4949 deNote,
50+ deLink,
5051 stringify,
5152 capitalize,
5253 compactify,
5354 compactifyDL,
5455 linesToPara,
55− Element (..),
56− hierarchicalize,
56+ makeSections,
5757 uniqueIdent,
5858 inlineListToIdentifier,
5959 isHeaderBlock,
@@ -104,11 +104,10 @@ import qualified Data.Bifunctor as Bifunctor
104104import Data.Char (isAlpha, isLower, isSpace, isUpper, toLower, isAlphaNum,
105105 generalCategory, GeneralCategory(NonSpacingMark,
106106 SpacingCombiningMark, EnclosingMark, ConnectorPunctuation))
107−import Data.Data (Data, Typeable)
108107import Data.List (find, intercalate, intersperse, stripPrefix, sortBy)
109108import Data.Ord (comparing)
110109import qualified Data.Map as M
111−import Data.Maybe (mapMaybe)
110+import Data.Maybe (mapMaybe, fromMaybe)
112111import Data.Monoid (Any (..))
113112import Data.Sequence (ViewL (..), ViewR (..), viewl, viewr)
114113import qualified Data.Set as Set
@@ -366,6 +365,10 @@ deNote :: Inline -> Inline
366365deNote (Note _) = Str ""
367366deNote x = x
368367
368+deLink :: Inline -> Inline
369+deLink (Link _ ils _) = Span nullAttr ils
370+deLink x = x
371+
369372deQuote :: Inline -> Inline
370373deQuote (Quoted SingleQuote xs) =
371374 Span ("",[],[]) (Str "\8216" : xs ++ [Str "\8217"])
@@ -449,34 +452,6 @@ isPara :: Block -> Bool
449452isPara (Para _) = True
450453isPara _ = False
451454
452−-- | Data structure for defining hierarchical Pandoc documents
453−data Element = Blk Block
454− | Sec Int [Int] Attr [Inline] [Element]
455− -- lvl num attributes label contents
456− deriving (Eq, Read, Show, Typeable, Data)
457−
458−instance Walkable Inline Element where
459− walk f (Blk x) = Blk (walk f x)
460− walk f (Sec lev nums attr ils elts) = Sec lev nums attr (walk f ils) (walk f elts)
461− walkM f (Blk x) = Blk `fmap` walkM f x
462− walkM f (Sec lev nums attr ils elts) = do
463− ils' <- walkM f ils
464− elts' <- walkM f elts
465− return $ Sec lev nums attr ils' elts'
466− query f (Blk x) = query f x
467− query f (Sec _ _ _ ils elts) = query f ils `mappend` query f elts
468−
469−instance Walkable Block Element where
470− walk f (Blk x) = Blk (walk f x)
471− walk f (Sec lev nums attr ils elts) = Sec lev nums attr (walk f ils) (walk f elts)
472− walkM f (Blk x) = Blk `fmap` walkM f x
473− walkM f (Sec lev nums attr ils elts) = do
474− ils' <- walkM f ils
475− elts' <- walkM f elts
476− return $ Sec lev nums attr ils' elts'
477− query f (Blk x) = query f x
478− query f (Sec _ _ _ ils elts) = query f ils `mappend` query f elts
479−
480455-- | Convert Pandoc inline list to plain text identifier. HTML
481456-- identifiers must start with a letter, and may contain only
482457-- letters, digits, and the characters _-.
@@ -504,37 +479,67 @@ inlineListToIdentifier exts =
504479 | otherwise = c == '_' || c == '-' || c == '.'
505480 spaceToDash = map (\c -> if isSpace c then '-' else c)
506481
507−-- | Convert list of Pandoc blocks into (hierarchical) list of Elements
508−hierarchicalize :: [Block] -> [Element]
509−hierarchicalize blocks = S.evalState (hierarchicalizeWithIds blocks) []
510−
511−hierarchicalizeWithIds :: [Block] -> S.State [Int] [Element]
512−hierarchicalizeWithIds [] = return []
513−hierarchicalizeWithIds (Header level attr@(_,classes,_) title':xs) = do
514− lastnum <- S.get
515− let lastnum' = take level lastnum
516− let newnum = case length lastnum' of
517− x | "unnumbered" `elem` classes -> []
518− | x >= level -> init lastnum' ++ [last lastnum' + 1]
519− | otherwise -> lastnum ++
520− replicate (level - length lastnum - 1) 0 ++ [1]
521− unless (null newnum) $ S.put newnum
522− let (sectionContents, rest) = break (headerLtEq level) xs
523− sectionContents' <- hierarchicalizeWithIds sectionContents
524− rest' <- hierarchicalizeWithIds rest
525− return $ Sec level newnum attr title' sectionContents' : rest'
526−hierarchicalizeWithIds (Div ("refs",classes',kvs')
527− (Header level (ident,classes,kvs) title' : xs):ys) =
528− hierarchicalizeWithIds (Header level (ident,"references":classes,kvs)
529− title' : Div ("refs",classes',kvs') xs : ys)
530−hierarchicalizeWithIds (x:rest) = do
531− rest' <- hierarchicalizeWithIds rest
532− return $ Blk x : rest'
482+
483+-- | Put a list of Pandoc blocks into a hierarchical structure:
484+-- a list of sections (each a Div with class "section" and first
485+-- element a Header). If the 'numbering' parameter is True, Header
486+-- numbers are added via the number attribute on the header.
487+-- If the baseLevel parameter is Just n, Header levels are
488+-- adjusted to be gapless starting at level n.
489+makeSections :: Bool -> Maybe Int -> [Block] -> [Block]
490+makeSections numbering mbBaseLevel bs =
491+ S.evalState (go bs) (mbBaseLevel, [])
492+ where
493+ go :: [Block] -> S.State (Maybe Int, [Int]) [Block]
494+ go (Header level (ident,classes,kvs) title':xs) = do
495+ (mbLevel, lastnum) <- S.get
496+ let level' = fromMaybe level mbLevel
497+ let lastnum' = take level' lastnum
498+ let newnum =
499+ if level' > 0
500+ then case length lastnum' of
501+ x | "unnumbered" `elem` classes -> []
502+ | x >= level' -> init lastnum' ++ [last lastnum' + 1]
503+ | otherwise -> lastnum ++
504+ replicate (level' - length lastnum - 1) 0 ++ [1]
505+ else []
506+ unless (null newnum) $ S.modify $ \(mbl, _) -> (mbl, newnum)
507+ let (sectionContents, rest) = break (headerLtEq level) xs
508+ S.modify $ \(_, ln) -> (fmap (+ 1) mbLevel, ln)
509+ sectionContents' <- go sectionContents
510+ S.modify $ \(_, ln) -> (mbLevel, ln)
511+ rest' <- go rest
512+ let divattr = (ident, ["section"], [])
513+ let attr = ("",classes,kvs ++
514+ [("number", intercalate "." (map show newnum))
515+ | numbering])
516+ return $
517+ Div divattr (Header level' attr title' : sectionContents') : rest'
518+ go (Div (dident,dclasses,dkvs)
519+ (Header level (ident,classes,kvs) title':ys) : xs) = do
520+ inner <- go (Header level (ident,classes,kvs) title':ys)
521+ let inner' =
522+ case inner of
523+ (Div (dident',dclasses',dkvs') zs@(Header{}:zs') : ws)
524+ | null dident ->
525+ Div (dident',dclasses' ++ dclasses,dkvs' ++ dkvs) zs : ws
526+ | otherwise -> -- keep id on header so we don't lose anchor
527+ Div (dident,dclasses ++ dclasses',dkvs ++ dkvs')
528+ (Header level (dident',classes,kvs) title':zs') : ws
529+ _ -> inner -- shouldn't happen
530+ rest <- go xs
531+ return $ inner' ++ rest
532+ go (Div attr xs : rest) = do
533+ xs' <- go xs
534+ rest' <- go rest
535+ return $ Div attr xs' : rest'
536+ go (x:xs) = (x :) <$> go xs
537+ go [] = return []
533538
534539headerLtEq :: Int -> Block -> Bool
535−headerLtEq level (Header l _ _) = l <= level
536−headerLtEq level (Div ("",["references"],[]) (Header l _ _ : _)) = l <= level
537−headerLtEq _ _ = False
540+headerLtEq level (Header l _ _) = l <= level
541+headerLtEq level (Div _ (b:_)) = headerLtEq level b
542+headerLtEq _ _ = False
538543
539544-- | Generate a unique identifier from a list of inlines.
540545-- Second argument is a list of already used identifiers.
src/Text/Pandoc/Slides.hs+1−1
@@ -28,7 +28,7 @@ getSlideLevel = go 6
2828 nonHOrHR HorizontalRule = False
2929 nonHOrHR _ = True
3030
31−-- | Prepare a block list to be passed to hierarchicalize.
31+-- | Prepare a block list to be passed to makeSections.
3232prepSlides :: Int -> [Block] -> [Block]
3333prepSlides slideLevel = ensureStartWithH . splitHrule . extractRefsHeader
3434 where splitHrule (HorizontalRule : Header n attr xs : ys)
src/Text/Pandoc/Writers/AsciiDoc.hs+6−9
@@ -84,7 +84,7 @@ pandocToAsciiDoc opts (Pandoc meta blocks) = do
8484 (blockListToAsciiDoc opts)
8585 (fmap chomp . inlineListToAsciiDoc opts)
8686 meta
87− main <- vcat <$> mapM (elementToAsciiDoc 1 opts) (hierarchicalize blocks)
87+ main <- blockListToAsciiDoc opts $ makeSections False (Just 1) blocks
8888 st <- get
8989 let context = defField "body" main
9090 $ defField "toc"
@@ -97,14 +97,6 @@ pandocToAsciiDoc opts (Pandoc meta blocks) = do
9797 Nothing -> main
9898 Just tpl -> renderTemplate tpl context
9999
100−elementToAsciiDoc :: PandocMonad m
101− => Int -> WriterOptions -> Element -> ADW m (Doc Text)
102−elementToAsciiDoc _ opts (Blk b) = blockToAsciiDoc opts b
103−elementToAsciiDoc nestlevel opts (Sec _lvl _num attr label children) = do
104− hdr <- blockToAsciiDoc opts (Header nestlevel attr label)
105− rest <- vcat <$> mapM (elementToAsciiDoc (nestlevel + 1) opts) children
106− return $ hdr $$ rest
107−
108100-- | Escape special characters for AsciiDoc.
109101escapeString :: String -> String
110102escapeString = escapeStringUsing escs
@@ -137,6 +129,11 @@ blockToAsciiDoc :: PandocMonad m
137129 -> Block -- ^ Block element
138130 -> ADW m (Doc Text)
139131blockToAsciiDoc _ Null = return empty
132+blockToAsciiDoc opts (Div (id',"section":_,_)
133+ (Header level (_,cls,kvs) ils : xs)) = do
134+ hdr <- blockToAsciiDoc opts (Header level (id',cls,kvs) ils)
135+ rest <- blockListToAsciiDoc opts xs
136+ return $ hdr $$ rest
140137blockToAsciiDoc opts (Plain inlines) = do
141138 contents <- inlineListToAsciiDoc opts inlines
142139 return $ contents <> blankline
src/Text/Pandoc/Writers/ConTeXt.hs+7−11
@@ -65,8 +65,7 @@ pandocToConTeXt options (Pandoc meta blocks) = do
6565 blockListToConTeXt
6666 (fmap chomp . inlineListToConTeXt)
6767 meta
68− body <- mapM (elementToConTeXt options) $ hierarchicalize blocks
69− let main = vcat body
68+ main <- blockListToConTeXt $ makeSections False Nothing blocks
7069 let layoutFromMargins = mconcat $ intersperse ("," :: Doc Text) $
7170 mapMaybe (\(x,y) ->
7271 ((x <> "=") <>) <$> getField y metadata)
@@ -147,18 +146,15 @@ toLabel z = concatMap go z
147146 | x `elem` ("\\#[]\",{}%()|=" :: String) = "ux" ++ printf "%x" (ord x)
148147 | otherwise = [x]
149148
150−-- | Convert Elements to ConTeXt
151−elementToConTeXt :: PandocMonad m => WriterOptions -> Element -> WM m (Doc Text)
152−elementToConTeXt _ (Blk block) = blockToConTeXt block
153−elementToConTeXt opts (Sec level _ attr title' elements) = do
154− header' <- sectionHeader attr level title'
155− footer' <- sectionFooter attr level
156− innerContents <- mapM (elementToConTeXt opts) elements
157− return $ header' $$ vcat innerContents $$ footer'
158−
159149-- | Convert Pandoc block element to ConTeXt.
160150blockToConTeXt :: PandocMonad m => Block -> WM m (Doc Text)
161151blockToConTeXt Null = return empty
152+blockToConTeXt (Div attr@(_,"section":_,_)
153+ (Header level _ title' : xs)) = do
154+ header' <- sectionHeader attr level title'
155+ footer' <- sectionFooter attr level
156+ innerContents <- blockListToConTeXt xs
157+ return $ header' $$ innerContents $$ footer'
162158blockToConTeXt (Plain lst) = inlineListToConTeXt lst
163159-- title beginning with fig: indicates that the image is a figure
164160blockToConTeXt (Para [Image attr txt (src,'f':'i':'g':':':_)]) = do
src/Text/Pandoc/Writers/Docbook.hs+28−34
@@ -78,7 +78,6 @@ writeDocbook5 opts d =
7878-- | Convert Pandoc document to string in Docbook format.
7979writeDocbook :: PandocMonad m => WriterOptions -> Pandoc -> DB m Text
8080writeDocbook opts (Pandoc meta blocks) = do
81− let elements = hierarchicalize blocks
8281 let colwidth = if writerWrapText opts == WrapAuto
8382 then Just $ writerColumns opts
8483 else Nothing
@@ -88,15 +87,15 @@ writeDocbook opts (Pandoc meta blocks) = do
8887 TopLevelChapter -> 0
8988 TopLevelSection -> 1
9089 TopLevelDefault -> 1
90+ let fromBlocks = blocksToDocbook opts .
91+ makeSections False (Just startLvl)
9192 auths' <- mapM (authorToDocbook opts) $ docAuthors meta
9293 let meta' = B.setMeta "author" auths' meta
9394 metadata <- metaToContext opts
94− (fmap vcat .
95− mapM (elementToDocbook opts startLvl) .
96− hierarchicalize)
95+ (fromBlocks)
9796 (inlinesToDocbook opts)
9897 meta'
99− main <- vcat <$> mapM (elementToDocbook opts startLvl) elements
98+ main <- fromBlocks blocks
10099 let context = defField "body" main
101100 $ defField "mathml" (case writerHTMLMathMethod opts of
102101 MathML -> True
@@ -107,34 +106,6 @@ writeDocbook opts (Pandoc meta blocks) = do
107106 Nothing -> main
108107 Just tpl -> renderTemplate tpl context
109108
110−-- | Convert an Element to Docbook.
111−elementToDocbook :: PandocMonad m => WriterOptions -> Int -> Element -> DB m (Doc Text)
112−elementToDocbook opts _ (Blk block) = blockToDocbook opts block
113−elementToDocbook opts lvl (Sec _ _num (id',_,_) title elements) = do
114− version <- ask
115− -- Docbook doesn't allow sections with no content, so insert some if needed
116− let elements' = if null elements
117− then [Blk (Para [])]
118− else elements
119− tag = case lvl of
120− -1 -> "part"
121− 0 -> "chapter"
122− n | n >= 1 && n <= 5 -> if version == DocBook5
123− then "section"
124− else "sect" ++ show n
125− _ -> "simplesect"
126− idName = if version == DocBook5
127− then "xml:id"
128− else "id"
129− idAttr = [(idName, writerIdentifierPrefix opts ++ id') | not (null id')]
130− nsAttr = if version == DocBook5 && lvl == 0 then [("xmlns", "http://docbook.org/ns/docbook"),("xmlns:xlink", "http://www.w3.org/1999/xlink")]
131− else []
132− attribs = nsAttr ++ idAttr
133− contents <- mapM (elementToDocbook opts (lvl + 1)) elements'
134− title' <- inlinesToDocbook opts title
135− return $ inTags True tag attribs $
136− inTagsSimple "title" title' $$ vcat contents
137−
138109-- | Convert a list of Pandoc blocks to Docbook.
139110blocksToDocbook :: PandocMonad m => WriterOptions -> [Block] -> DB m (Doc Text)
140111blocksToDocbook opts = fmap vcat . mapM (blockToDocbook opts)
@@ -184,6 +155,29 @@ blockToDocbook :: PandocMonad m => WriterOptions -> Block -> DB m (Doc Text)
184155blockToDocbook _ Null = return empty
185156-- Add ids to paragraphs in divs with ids - this is needed for
186157-- pandoc-citeproc to get link anchors in bibliographies:
158+blockToDocbook opts (Div (id',"section":_,_) (Header lvl _ ils : xs)) = do
159+ version <- ask
160+ -- Docbook doesn't allow sections with no content, so insert some if needed
161+ let bs = if null xs
162+ then [Para []]
163+ else xs
164+ tag = case lvl of
165+ -1 -> "part"
166+ 0 -> "chapter"
167+ n | n >= 1 && n <= 5 -> if version == DocBook5
168+ then "section"
169+ else "sect" ++ show n
170+ _ -> "simplesect"
171+ idName = if version == DocBook5
172+ then "xml:id"
173+ else "id"
174+ idAttr = [(idName, writerIdentifierPrefix opts ++ id') | not (null id')]
175+ nsAttr = if version == DocBook5 && lvl == 0 then [("xmlns", "http://docbook.org/ns/docbook"),("xmlns:xlink", "http://www.w3.org/1999/xlink")]
176+ else []
177+ attribs = nsAttr ++ idAttr
178+ title' <- inlinesToDocbook opts ils
179+ contents <- blocksToDocbook opts bs
180+ return $ inTags True tag attribs $ inTagsSimple "title" title' $$ contents
187181blockToDocbook opts (Div (ident,_,_) [Para lst]) =
188182 let attribs = [("id", ident) | not (null ident)] in
189183 if hasLineBreaks lst
@@ -197,7 +191,7 @@ blockToDocbook opts (Div (ident,_,_) bs) = do
197191 then mempty
198192 else selfClosingTag "anchor" [("id", ident)]) $$ contents
199193blockToDocbook _ h@Header{} = do
200− -- should not occur after hierarchicalize, except inside lists/blockquotes
194+ -- should be handled by Div section above, except inside lists/blockquotes
201195 report $ BlockNotRendered h
202196 return empty
203197blockToDocbook opts (Plain lst) = inlinesToDocbook opts lst
src/Text/Pandoc/Writers/Docx.hs+1−1
@@ -47,7 +47,7 @@ import Text.Pandoc.MIME (MimeType, extensionFromMimeType, getMimeType,
4747 getMimeTypeDef)
4848import Text.Pandoc.Options
4949import Text.Pandoc.Readers.Docx.StyleMap
50−import Text.Pandoc.Shared hiding (Element)
50+import Text.Pandoc.Shared
5151import Text.Pandoc.Walk
5252import Text.Pandoc.Writers.Math
5353import Text.Pandoc.Writers.Shared
src/Text/Pandoc/Writers/EPUB.hs+29−25
@@ -26,7 +26,7 @@ import Control.Monad.State.Strict (State, StateT, evalState, evalStateT, get,
2626import qualified Data.ByteString.Lazy as B
2727import qualified Data.ByteString.Lazy.Char8 as B8
2828import Data.Char (isAlphaNum, isAscii, isDigit, toLower)
29−import Data.List (intercalate, isInfixOf, isPrefixOf)
29+import Data.List (isInfixOf, isPrefixOf)
3030import qualified Data.Map as M
3131import Data.Maybe (fromMaybe, isNothing, mapMaybe, isJust)
3232import qualified Data.Set as Set
@@ -47,9 +47,8 @@ import Text.Pandoc.MIME (MimeType, extensionFromMimeType, getMimeType)
4747import Text.Pandoc.Options (EPUBVersion (..), HTMLMathMethod (..),
4848 ObfuscationMethod (NoObfuscation), WrapOption (..),
4949 WriterOptions (..))
50−import Text.Pandoc.Shared (hierarchicalize, normalizeDate, renderTags',
50+import Text.Pandoc.Shared (makeSections, normalizeDate, renderTags',
5151 safeRead, stringify, trim, uniqueIdent)
52−import qualified Text.Pandoc.Shared as S (Element (..))
5352import qualified Text.Pandoc.UTF8 as UTF8
5453import Text.Pandoc.UUID (getUUID)
5554import Text.Pandoc.Walk (query, walk, walkM)
@@ -712,31 +711,34 @@ pandocToEPUB version opts doc = do
712711 contentsEntry <- mkEntry "content.opf" contentsData
713712
714713 -- toc.ncx
715− let secs = hierarchicalize blocks'
714+ let secs = makeSections True (Just 1) blocks'
716715
717716 let tocLevel = writerTOCDepth opts
718717
719718 let navPointNode :: PandocMonad m
720719 => (Int -> [Inline] -> String -> [Element] -> Element)
721− -> S.Element -> StateT Int m Element
722− navPointNode formatter (S.Sec _ nums (ident,_,_) ils children) = do
723− n <- get
724− modify (+1)
725− let showNums :: [Int] -> String
726− showNums = intercalate "." . map show
727− let tit = if writerNumberSections opts && not (null nums)
728− then Span ("", ["section-header-number"], [])
729− [Str (showNums nums)] : Space : ils
730− else ils
731− src <- case lookup ident reftable of
732− Just x -> return x
733− Nothing -> throwError $ PandocSomeError $ ident ++ " not found in reftable"
734− let isSec (S.Sec lev _ _ _ _) = lev <= tocLevel
735− isSec _ = False
736− let subsecs = filter isSec children
737− subs <- mapM (navPointNode formatter) subsecs
738− return $ formatter n tit src subs
739− navPointNode _ (S.Blk _) = throwError $ PandocSomeError "navPointNode encountered Blk"
720+ -> Block -> StateT Int m [Element]
721+ navPointNode formatter (Div (ident,"section":_,_)
722+ (Header lvl (_,_,kvs) ils : children)) = do
723+ if lvl > tocLevel
724+ then return []
725+ else do
726+ n <- get
727+ modify (+1)
728+ let num = fromMaybe "" $ lookup "number" kvs
729+ let tit = if writerNumberSections opts && not (null num)
730+ then Span ("", ["section-header-number"], [])
731+ [Str num] : Space : ils
732+ else ils
733+ src <- case lookup ident reftable of
734+ Just x -> return x
735+ Nothing -> throwError $ PandocSomeError $
736+ ident ++ " not found in reftable"
737+ subs <- concat <$> mapM (navPointNode formatter) children
738+ return [formatter n tit src subs]
739+ navPointNode formatter (Div _ bs) =
740+ concat <$> mapM (navPointNode formatter) bs
741+ navPointNode _ _ = return []
740742
741743 let navMapFormatter :: Int -> [Inline] -> String -> [Element] -> Element
742744 navMapFormatter n tit src subs = unode "navPoint" !
@@ -750,7 +752,8 @@ pandocToEPUB version opts doc = do
750752 , unode "content" ! [("src", "text/title_page.xhtml")]
751753 $ () ]
752754
753− navMap <- lift $ evalStateT (mapM (navPointNode navMapFormatter) secs) 1
755+ navMap <- lift $ evalStateT
756+ (concat <$> mapM (navPointNode navMapFormatter) secs) 1
754757 let tocData = UTF8.fromStringLazy $ ppTopElement $
755758 unode "ncx" ! [("version","2005-1")
756759 ,("xmlns","http://www.daisy.org/z3986/2005/ncx/")] $
@@ -800,7 +803,8 @@ pandocToEPUB version opts doc = do
800803 clean x = x
801804
802805 let navtag = if epub3 then "nav" else "div"
803− tocBlocks <- lift $ evalStateT (mapM (navPointNode navXhtmlFormatter) secs) 1
806+ tocBlocks <- lift $ evalStateT
807+ (concat <$> mapM (navPointNode navXhtmlFormatter) secs) 1
804808 let navBlocks = [RawBlock (Format "html")
805809 $ showElement $ -- prettyprinting introduces bad spaces
806810 unode navtag ! ([("epub:type","toc") | epub3] ++
src/Text/Pandoc/Writers/FB2.hs+16−17
@@ -39,9 +39,9 @@ import qualified Text.Pandoc.Class as P
3939import Text.Pandoc.Definition
4040import Text.Pandoc.Logging
4141import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..), def)
42−import Text.Pandoc.Shared (capitalize, isURI, orderedListMarkers, hierarchicalize)
42+import Text.Pandoc.Shared (capitalize, isURI, orderedListMarkers,
43+ makeSections)
4344import Text.Pandoc.Writers.Shared (lookupMetaString)
44−import qualified Text.Pandoc.Shared as Shared (Element(Blk, Sec))
4545
4646-- | Data to be written at the end of the document:
4747-- (foot)notes, URLs, references, images.
@@ -162,28 +162,27 @@ docdate meta' = do
162162-- representation.
163163renderSections :: PandocMonad m => Int -> [Block] -> FBM m [Content]
164164renderSections level blocks = do
165− let elements = hierarchicalize blocks
166− let isSection Shared.Sec{} = True
165+ let blocks' = makeSections False Nothing blocks
166+ let isSection (Div (_,"section":_,_) (Header{}:_)) = True
167167 isSection _ = False
168− let (initialBlocks, secs) = break isSection elements
169− let elements' = if null initialBlocks
170− then secs
171− else Shared.Sec 1 [] nullAttr mempty initialBlocks : secs
172− cMapM (renderSection level) elements'
173−
174−
175−
176−renderSection :: PandocMonad m => Int -> Shared.Element -> FBM m [Content]
177−renderSection _ (Shared.Blk block) = blockToXml block
178−renderSection lvl (Shared.Sec _ _num (id',_,_) title elements) = do
179− content <- cMapM (renderSection (lvl + 1)) elements
168+ let (initialBlocks, secs) = break isSection blocks'
169+ let blocks'' = if null initialBlocks
170+ then blocks'
171+ else Div ("",["section"],[])
172+ (Header 1 nullAttr mempty : initialBlocks) : secs
173+ cMapM (renderSection level) blocks''
174+
175+renderSection :: PandocMonad m => Int -> Block -> FBM m [Content]
176+renderSection lvl (Div (id',"section":_,_) (Header _ _ title : xs)) = do
180177 title' <- if null title
181178 then return []
182179 else list . el "title" <$> formatTitle title
180+ content <- cMapM (renderSection (lvl + 1)) xs
183181 let sectionContent = if null id'
184182 then el "section" (title' ++ content)
185183 else el "section" ([uattr "id" id'], title' ++ content)
186184 return [sectionContent]
185+renderSection _ b = blockToXml b
187186
188187-- | Only <p> and <empty-line> are allowed within <title> in FB2.
189188formatTitle :: PandocMonad m => [Inline] -> FBM m [Content]
@@ -334,7 +333,7 @@ blockToXml (DefinitionList defs) =
334333 t <- wrap "strong" term
335334 return (el "p" t : items)
336335blockToXml h@Header{} = do
337− -- should not occur after hierarchicalize, except inside lists/blockquotes
336+ -- should not occur after makeSections, except inside lists/blockquotes
338337 report $ BlockNotRendered h
339338 return []
340339blockToXml HorizontalRule = return [ el "empty-line" () ]
src/Text/Pandoc/Writers/HTML.hs+100−133
@@ -32,9 +32,10 @@ import Prelude
3232import Control.Monad.State.Strict
3333import Data.Char (ord, toLower)
3434import Data.List (intercalate, intersperse, isPrefixOf, partition)
35−import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing, mapMaybe)
35+import Data.Maybe (fromMaybe, isJust, isNothing, mapMaybe)
3636import qualified Data.Set as Set
3737import Data.String (fromString)
38+import Data.List.Split (splitWhen)
3839import Data.Text (Text)
3940import qualified Data.Text as T
4041import qualified Data.Text.Lazy as TL
@@ -90,20 +91,20 @@ data WriterState = WriterState
9091 , stMath :: Bool -- ^ Math is used in document
9192 , stQuotes :: Bool -- ^ <q> tag is used
9293 , stHighlighting :: Bool -- ^ Syntax highlighting is used
93− , stSecNum :: [Int] -- ^ Number of current section
94− , stElement :: Bool -- ^ Processing an Element
9594 , stHtml5 :: Bool -- ^ Use HTML5
9695 , stEPUBVersion :: Maybe EPUBVersion -- ^ EPUB version if for epub
9796 , stSlideVariant :: HTMLSlideVariant
97+ , stSlideLevel :: Int -- ^ Slide level
9898 , stCodeBlockNum :: Int -- ^ Number of code block
9999 }
100100
101101defaultWriterState :: WriterState
102102defaultWriterState = WriterState {stNotes= [], stMath = False, stQuotes = False,
103− stHighlighting = False, stSecNum = [],
104− stElement = False, stHtml5 = False,
103+ stHighlighting = False,
104+ stHtml5 = False,
105105 stEPUBVersion = Nothing,
106106 stSlideVariant = NoSlides,
107+ stSlideLevel = 1,
107108 stCodeBlockNum = 0}
108109
109110-- Helpers to render HTML with the appropriate function.
@@ -243,6 +244,8 @@ pandocToHtml :: PandocMonad m
243244 -> Pandoc
244245 -> StateT WriterState m (Html, Context Text)
245246pandocToHtml opts (Pandoc meta blocks) = do
247+ let slideLevel = fromMaybe (getSlideLevel blocks) $ writerSlideLevel opts
248+ modify $ \st -> st{ stSlideLevel = slideLevel }
246249 metadata <- metaToContext opts
247250 (fmap renderHtml' . blockListToHtml opts)
248251 (fmap renderHtml' . inlineListToHtml opts)
@@ -250,17 +253,15 @@ pandocToHtml opts (Pandoc meta blocks) = do
250253 let stringifyHTML = escapeStringForXML . stringify
251254 let authsMeta = map stringifyHTML $ docAuthors meta
252255 let dateMeta = stringifyHTML $ docDate meta
253− let slideLevel = fromMaybe (getSlideLevel blocks) $ writerSlideLevel opts
254256 slideVariant <- gets stSlideVariant
255− let sects = hierarchicalize $
257+ let sects = makeSections (writerNumberSections opts) Nothing $
256258 if slideVariant == NoSlides
257259 then blocks
258260 else prepSlides slideLevel blocks
259261 toc <- if writerTableOfContents opts && slideVariant /= S5Slides
260262 then fmap renderHtml' <$> tableOfContents opts sects
261263 else return Nothing
262− blocks' <- liftM (mconcat . intersperse (nl opts)) $
263− mapM (elementToHtml Nothing slideLevel opts) sects
264+ blocks' <- blockListToHtml opts sects
264265 st <- get
265266 notes <- footnoteSection opts (reverse (stNotes st))
266267 let thebody = blocks' >> notes
@@ -380,130 +381,20 @@ listItemToHtml opts bls
380381 return $ constr (checkbox >> isContents) >> bsContents
381382
382383-- | Construct table of contents from list of elements.
383−tableOfContents :: PandocMonad m => WriterOptions -> [Element]
384+tableOfContents :: PandocMonad m => WriterOptions -> [Block]
384385 -> StateT WriterState m (Maybe Html)
385386tableOfContents _ [] = return Nothing
386387tableOfContents opts sects = do
387− contents <- mapM (elementToListItem opts) sects
388− let tocList = catMaybes contents
389− if null tocList
390− then return Nothing
391− else Just <$> unordList opts tocList
392−
393−-- | Convert section number to string
394−showSecNum :: [Int] -> String
395−showSecNum = intercalate "." . map show
396−
397−-- | Converts an Element to a list item for a table of contents,
398−-- retrieving the appropriate identifier from state.
399−elementToListItem :: PandocMonad m => WriterOptions -> Element
400− -> StateT WriterState m (Maybe Html)
401−-- Don't include the empty headers created in slide shows
402−-- shows when an hrule is used to separate slides without a new title:
403−elementToListItem _ (Sec _ _ _ [Str "\0"] _) = return Nothing
404−elementToListItem opts (Sec lev num (id',classes,_) headerText subsecs)
405− | lev <= writerTOCDepth opts = do
406− let num' = zipWith (+) num (writerNumberOffset opts ++ repeat 0)
407− let sectnum = if writerNumberSections opts && not (null num) &&
408− "unnumbered" `notElem` classes
409− then (H.span ! A.class_ "toc-section-number"
410− $ toHtml $ showSecNum num') >> preEscapedString " "
411− else mempty
412− txt <- liftM (sectnum >>) $
413− inlineListToHtml opts $ walk (deLink . deNote) headerText
414− subHeads <- mapM (elementToListItem opts) subsecs >>= return . catMaybes
415− subList <- if null subHeads
416− then return mempty
417− else unordList opts subHeads
418388 -- in reveal.js, we need #/apples, not #apples:
419389 slideVariant <- gets stSlideVariant
420− let revealSlash = ['/' | slideVariant== RevealJsSlides]
421− return $ Just
422− $ if null id'
423− then H.a (toHtml txt) >> subList
424− else (H.a ! A.href (toValue $ "#" ++ revealSlash ++
425− writerIdentifierPrefix opts ++ id')
426− $ toHtml txt) >> subList
427−elementToListItem _ _ = return Nothing
428−
429−deLink :: Inline -> Inline
430−deLink (Link _ ils _) = Span nullAttr ils
431−deLink x = x
432−
433−-- | Convert an Element to Html.
434−elementToHtml :: PandocMonad m => Maybe Int -> Int -> WriterOptions -> Element
435− -> StateT WriterState m Html
436−elementToHtml _ _ opts (Blk block) = blockToHtml opts block
437−elementToHtml mbparentlevel slideLevel opts
438− (Sec level num (id',classes,keyvals) title' elements)
439− = do
440− slideVariant <- gets stSlideVariant
441− let slide = slideVariant /= NoSlides &&
442− (level <= slideLevel ||
443− -- we're missing a header at slide level (see #5168)
444− maybe False (< slideLevel) mbparentlevel)
445− let num' = zipWith (+) num (writerNumberOffset opts ++ repeat 0)
446− modify $ \st -> st{stSecNum = num'} -- update section number
447− html5 <- gets stHtml5
448− let titleSlide = slide && level < slideLevel
449− header' <- if title' == [Str "\0"] -- marker for hrule
450− then return mempty
451− else do
452− modify (\st -> st{ stElement = True})
453− let level' = if level <= slideLevel &&
454− slideVariant == SlidySlides
455− then 1 -- see #3566
456− else level
457− res <- blockToHtml opts
458− (Header level' (id',classes,keyvals) title')
459− modify (\st -> st{ stElement = False})
460− return res
461−
462− let isSec Sec{} = True
463− isSec (Blk _) = False
464− let isPause (Blk x) = x == Para [Str ".",Space,Str ".",Space,Str "."]
465− isPause _ = False
466− let fragmentClass = case slideVariant of
467− RevealJsSlides -> "fragment"
468− _ -> "incremental"
469− let inDiv xs = Blk (RawBlock (Format "html") ("<div class=\""
470− ++ fragmentClass ++ "\">")) :
471− (xs ++ [Blk (RawBlock (Format "html") "</div>")])
472− let (titleBlocks, innerSecs) =
473− if titleSlide
474− -- title slides have no content of their own
475− then ([x | Blk x <- elements],
476− filter isSec elements)
477− else case splitBy isPause elements of
478− [] -> ([],[])
479− (x:xs) -> ([],x ++ concatMap inDiv xs)
480− titleContents <- blockListToHtml opts titleBlocks
481− innerContents <- mapM (elementToHtml (Just level) slideLevel opts) innerSecs
482− let inNl x = mconcat $ nl opts : intersperse (nl opts) x ++ [nl opts]
483− let classes' = ["title-slide" | titleSlide] ++ ["slide" | slide] ++
484− ["section" | (slide || writerSectionDivs opts) &&
485− not html5 ] ++
486− ["level" ++ show level | slide || writerSectionDivs opts ]
487− ++ classes
488− let secttag = if html5
489− then H5.section
490− else H.div
491− let attr = (id',classes',keyvals)
492− if titleSlide
493− then do
494− t <- addAttrs opts attr $ secttag $ header' <> titleContents
495− return $
496− (if slideVariant == RevealJsSlides && not (null innerContents)
497− -- revealjs doesn't like more than one level of section nesting:
498− && isNothing mbparentlevel
499− then H5.section
500− else id) $ mconcat $ t : innerContents
501− else if writerSectionDivs opts || slide
502− then addAttrs opts attr
503− $ secttag $ inNl $ header' : innerContents
504− else do
505− t <- addAttrs opts attr header'
506− return $ mconcat $ intersperse (nl opts) (t : innerContents)
390+ let opts' = case slideVariant of
391+ RevealJsSlides ->
392+ opts{ writerIdentifierPrefix =
393+ '/' : writerIdentifierPrefix opts }
394+ _ -> opts
395+ case toTableOfContents opts sects of
396+ bl@(BulletList (_:_)) -> Just <$> blockToHtml opts' bl
397+ _ -> return Nothing
507398
508399-- | Convert list of Note blocks to a footnote <div>.
509400-- Assumes notes are sorted.
@@ -686,6 +577,16 @@ figure opts attr txt (s,tit) = do
686577 else H.div ! A.class_ "figure" $ mconcat
687578 [nl opts, img, nl opts, capt, nl opts]
688579
580+showSecNum :: [Int] -> String
581+showSecNum = intercalate "." . map show
582+
583+getNumber :: WriterOptions -> Attr -> String
584+getNumber opts (_,_,kvs) =
585+ showSecNum $ zipWith (+) num (writerNumberOffset opts ++ repeat 0)
586+ where
587+ num = maybe [] (map (fromMaybe 0 . safeRead) . splitWhen (=='.')) $
588+ lookup "number" kvs
589+
689590-- | Convert Pandoc block element to HTML.
690591blockToHtml :: PandocMonad m => WriterOptions -> Block -> StateT WriterState m Html
691592blockToHtml _ Null = return mempty
@@ -713,6 +614,73 @@ blockToHtml opts (LineBlock lns) =
713614 else do
714615 htmlLines <- inlineListToHtml opts $ intercalate [LineBreak] lns
715616 return $ H.div ! A.class_ "line-block" $ htmlLines
617+blockToHtml opts (Div (ident, "section":dclasses, dkvs)
618+ (Header level hattr ils : xs)) = do
619+ slideVariant <- gets stSlideVariant
620+ slideLevel <- gets stSlideLevel
621+ let slide = slideVariant /= NoSlides &&
622+ level <= slideLevel {- DROPPED old fix for #5168 here -}
623+ html5 <- gets stHtml5
624+ let titleSlide = slide && level < slideLevel
625+ let level' = if level <= slideLevel && slideVariant == SlidySlides
626+ then 1 -- see #3566
627+ else level
628+ header' <- if ils == [Str "\0"] -- marker for hrule
629+ then return mempty
630+ else blockToHtml opts (Header level' hattr ils)
631+ let isSec (Div (_,"section":_,_) _) = True
632+ isSec (Div _ zs) = any isSec zs
633+ isSec _ = False
634+ let isPause (Para [Str ".",Space,Str ".",Space,Str "."]) = True
635+ isPause _ = False
636+ let fragmentClass = case slideVariant of
637+ RevealJsSlides -> "fragment"
638+ _ -> "incremental"
639+ let inDiv zs = (RawBlock (Format "html") ("<div class=\""
640+ ++ fragmentClass ++ "\">")) :
641+ (zs ++ [RawBlock (Format "html") "</div>"])
642+ let (titleBlocks, innerSecs) =
643+ if titleSlide
644+ -- title slides have no content of their own
645+ then break isSec xs
646+ else case splitBy isPause xs of
647+ [] -> ([],[])
648+ (z:zs) -> ([],z ++ concatMap inDiv zs)
649+ titleContents <- blockListToHtml opts titleBlocks
650+ innerContents <- blockListToHtml opts innerSecs
651+ let classes' = ["title-slide" | titleSlide] ++ ["slide" | slide] ++
652+ ["section" | (slide || writerSectionDivs opts) &&
653+ not html5 ] ++
654+ ["level" ++ show level | slide || writerSectionDivs opts ]
655+ ++ dclasses
656+ let secttag = if html5
657+ then H5.section
658+ else H.div
659+ let attr = (ident, classes', dkvs)
660+ if titleSlide
661+ then do
662+ t <- addAttrs opts attr $ secttag $ header' <> titleContents
663+ return $
664+ (if slideVariant == RevealJsSlides && not (null innerSecs)
665+ -- revealjs doesn't like more than one level of section nesting:
666+ {- REMOVED && isNothing mbparentlevel -}
667+ then H5.section
668+ else id) $ t <> if null innerSecs
669+ then mempty
670+ else nl opts <> innerContents
671+ else if writerSectionDivs opts || slide || not (null dclasses) ||
672+ not (null dkvs)
673+ then addAttrs opts attr
674+ $ secttag
675+ $ nl opts <> header' <> nl opts <>
676+ if null innerSecs
677+ then mempty
678+ else innerContents <> nl opts
679+ else do
680+ t <- addAttrs opts attr header'
681+ return $ t <> if null innerSecs
682+ then mempty
683+ else nl opts <> innerContents
716684blockToHtml opts (Div attr@(ident, classes, kvs') bs) = do
717685 html5 <- gets stHtml5
718686 slideVariant <- gets stSlideVariant
@@ -826,14 +794,13 @@ blockToHtml opts (BlockQuote blocks) = do
826794 return $ H.blockquote $ nl opts >> contents >> nl opts
827795blockToHtml opts (Header level attr@(_,classes,_) lst) = do
828796 contents <- inlineListToHtml opts lst
829− secnum <- liftM stSecNum get
797+ let secnum = getNumber opts attr
830798 let contents' = if writerNumberSections opts && not (null secnum)
831799 && "unnumbered" `notElem` classes
832− then (H.span ! A.class_ "header-section-number" $ toHtml
833− $ showSecNum secnum) >> strToHtml " " >> contents
800+ then (H.span ! A.class_ "header-section-number"
801+ $ toHtml secnum) >> strToHtml " " >> contents
834802 else contents
835− inElement <- gets stElement
836− (if inElement then return else addAttrs opts attr)
803+ addAttrs opts attr
837804 $ case level of
838805 1 -> H.h1 contents'
839806 2 -> H.h2 contents'
src/Text/Pandoc/Writers/JATS.hs+16−23
@@ -63,30 +63,27 @@ docToJATS opts (Pandoc meta blocks) = do
6363 let isBackBlock (Div ("refs",_,_) _) = True
6464 isBackBlock _ = False
6565 let (backblocks, bodyblocks) = partition isBackBlock blocks
66− let elements = hierarchicalize bodyblocks
67− let backElements = hierarchicalize $ backblocks
68− let colwidth = if writerWrapText opts == WrapAuto
69− then Just $ writerColumns opts
70− else Nothing
7166 -- The numbering here follows LaTeX's internal numbering
7267 let startLvl = case writerTopLevelDivision opts of
7368 TopLevelPart -> -1
7469 TopLevelChapter -> 0
7570 TopLevelSection -> 1
7671 TopLevelDefault -> 1
72+ let fromBlocks = blocksToJATS opts . makeSections False (Just startLvl)
73+ let colwidth = if writerWrapText opts == WrapAuto
74+ then Just $ writerColumns opts
75+ else Nothing
7776 metadata <- metaToContext opts
78− (fmap vcat .
79− mapM (elementToJATS opts startLvl) .
80− hierarchicalize)
77+ fromBlocks
8178 (fmap chomp . inlinesToJATS opts)
8279 meta
83− main <- vcat <$> mapM (elementToJATS opts startLvl) elements
80+ main <- fromBlocks bodyblocks
8481 notes <- reverse . map snd <$> gets jatsNotes
85− backs <- mapM (elementToJATS opts startLvl) backElements
82+ backs <- fromBlocks backblocks
8683 let fns = if null notes
8784 then mempty
8885 else inTagsIndented "fn-group" $ vcat notes
89− let back = vcat backs $$ fns
86+ let back = backs $$ fns
9087 let date =
9188 case getField "date" metadata of
9289 Nothing -> NullVal
@@ -116,18 +113,6 @@ docToJATS opts (Pandoc meta blocks) = do
116113 Nothing -> main
117114 Just tpl -> renderTemplate tpl context
118115
119−-- | Convert an Element to JATS.
120−elementToJATS :: PandocMonad m => WriterOptions -> Int -> Element -> JATS m (Doc Text)
121−elementToJATS opts _ (Blk block) = blockToJATS opts block
122−elementToJATS opts lvl (Sec _ _num (id',_,kvs) title elements) = do
123− let idAttr = [("id", writerIdentifierPrefix opts ++ id') | not (null id')]
124− let otherAttrs = ["sec-type", "specific-use"]
125− let attribs = idAttr ++ [(k,v) | (k,v) <- kvs, k `elem` otherAttrs]
126− contents <- mapM (elementToJATS opts (lvl + 1)) elements
127− title' <- inlinesToJATS opts title
128− return $ inTags True "sec" attribs $
129− inTagsSimple "title" title' $$ vcat contents
130−
131116-- | Convert a list of Pandoc blocks to JATS.
132117blocksToJATS :: PandocMonad m => WriterOptions -> [Block] -> JATS m (Doc Text)
133118blocksToJATS = wrappedBlocksToJATS (const False)
@@ -225,6 +210,14 @@ codeAttr (ident,classes,kvs) = (lang, attr)
225210-- | Convert a Pandoc block element to JATS.
226211blockToJATS :: PandocMonad m => WriterOptions -> Block -> JATS m (Doc Text)
227212blockToJATS _ Null = return empty
213+blockToJATS opts (Div (id',"section":_,kvs) (Header _lvl _ ils : xs)) = do
214+ let idAttr = [("id", writerIdentifierPrefix opts ++ id') | not (null id')]
215+ let otherAttrs = ["sec-type", "specific-use"]
216+ let attribs = idAttr ++ [(k,v) | (k,v) <- kvs, k `elem` otherAttrs]
217+ title' <- inlinesToJATS opts ils
218+ contents <- blocksToJATS opts xs
219+ return $ inTags True "sec" attribs $
220+ inTagsSimple "title" title' $$ contents
228221-- Bibliography reference:
229222blockToJATS opts (Div ('r':'e':'f':'-':_,_,_) [Para lst]) =
230223 inlinesToJATS opts lst
src/Text/Pandoc/Writers/LaTeX.hs+131−152
@@ -168,9 +168,8 @@ pandocToLaTeX options (Pandoc meta blocks) = do
168168 blocks''' <- if beamer
169169 then toSlides blocks''
170170 else return blocks''
171− body <- mapM (elementToLaTeX options) $ hierarchicalize blocks'''
171+ main <- blockListToLaTeX $ makeSections False Nothing blocks'''
172172 biblioTitle <- inlineListToLaTeX lastHeader
173− let main = vsep body
174173 st <- get
175174 titleMeta <- stringToLaTeX TextString $ stringify $ docTitle meta
176175 authorsMeta <- mapM (stringToLaTeX TextString . stringify) $ docAuthors meta
@@ -298,16 +297,6 @@ pandocToLaTeX options (Pandoc meta blocks) = do
298297 Nothing -> main
299298 Just tpl -> renderTemplate tpl context'
300299
301−-- | Convert Elements to LaTeX
302−elementToLaTeX :: PandocMonad m => WriterOptions -> Element -> LW m (Doc Text)
303−elementToLaTeX _ (Blk block) = blockToLaTeX block
304−elementToLaTeX opts (Sec level _ (id',classes,_) title' elements) = do
305− modify $ \s -> s{stInHeading = True}
306− header' <- sectionHeader ("unnumbered" `elem` classes) id' level title'
307− modify $ \s -> s{stInHeading = False}
308− innerContents <- mapM (elementToLaTeX opts) elements
309− return $ vsep (header' : innerContents)
310−
311300data StringContext = TextString
312301 | URLString
313302 | CodeString
@@ -459,68 +448,16 @@ toSlides bs = do
459448 opts <- gets stOptions
460449 let slideLevel = fromMaybe (getSlideLevel bs) $ writerSlideLevel opts
461450 let bs' = prepSlides slideLevel bs
462− concat `fmap` mapM (elementToBeamer slideLevel) (hierarchicalize bs')
451+ walkM (elementToBeamer slideLevel) (makeSections False Nothing bs')
463452
464−elementToBeamer :: PandocMonad m => Int -> Element -> LW m [Block]
465−elementToBeamer _slideLevel (Blk (Div attrs bs)) = do
466− -- make sure we support "blocks" inside divs
467− bs' <- concat `fmap` mapM (elementToBeamer 0) (hierarchicalize bs)
468− return [Div attrs bs']
469−
470−elementToBeamer _slideLevel (Blk b) = return [b]
471−elementToBeamer slideLevel (Sec lvl _num (ident,classes,kvs) tit elts)
472− | lvl > slideLevel = do
473− bs <- concat `fmap` mapM (elementToBeamer slideLevel) elts
474− return $ Para ( RawInline "latex" "\\begin{block}{"
475− : tit ++ [RawInline "latex" "}"] )
476− : bs ++ [RawBlock "latex" "\\end{block}"]
477− | lvl < slideLevel = do
478− let isSec Sec{} = True
479− isSec _ = False
480− let (contentElts, secElts) = break isSec elts
481− let elts' = if null contentElts
482− then secElts
483− else Sec slideLevel [] nullAttr tit contentElts :
484− secElts
485− bs <- concat `fmap` mapM (elementToBeamer slideLevel) elts'
486− return $ Header lvl (ident,classes,kvs) tit : bs
487− | otherwise = do -- lvl == slideLevel
488− -- note: [fragile] is required or verbatim breaks
489− let hasCodeBlock (CodeBlock _ _) = [True]
490− hasCodeBlock _ = []
491− let hasCode (Code _ _) = [True]
492− hasCode _ = []
493− let fragile = "fragile" `elem` classes ||
494− not (null $ query hasCodeBlock elts ++ query hasCode elts)
495− let frameoptions = ["allowdisplaybreaks", "allowframebreaks", "fragile",
496− "b", "c", "t", "environment",
497− "label", "plain", "shrink", "standout",
498− "noframenumbering"]
499− let optionslist = ["fragile" | fragile
500− , isNothing (lookup "fragile" kvs)
501− , "fragile" `notElem` classes] ++
502− [k | k <- classes, k `elem` frameoptions] ++
503− [k ++ "=" ++ v | (k,v) <- kvs, k `elem` frameoptions]
504− let options = if null optionslist
505− then ""
506− else "[" ++ intercalate "," optionslist ++ "]"
507− let latex = RawInline (Format "latex")
508− slideTitle <-
509− if tit == [Str "\0"] -- marker for hrule
510− then return []
511− else return $ latex "{" : tit ++ [latex "}"]
512− ref <- toLabel ident
513− let slideAnchor = if null ident
514− then []
515− else [latex ("\n\\protect\\hypertarget{" ++
516− ref ++ "}{}")]
517− let slideStart = Para $
518− RawInline "latex" ("\\begin{frame}" ++ options) :
519− slideTitle ++ slideAnchor
520− let slideEnd = RawBlock "latex" "\\end{frame}"
521− -- now carve up slide into blocks if there are sections inside
522− bs <- concat `fmap` mapM (elementToBeamer slideLevel) elts
523− return $ slideStart : bs ++ [slideEnd]
453+-- this creates section slides and marks slides with class "slide","block"
454+elementToBeamer :: PandocMonad m => Int -> Block -> LW m Block
455+elementToBeamer slideLevel d@(Div (ident,dclasses,dkvs)
456+ xs@(Header lvl _ _ : _))
457+ | lvl > slideLevel = return $ Div (ident,"block":dclasses,dkvs) xs
458+ | lvl < slideLevel = return d
459+ | otherwise = return $ Div (ident,"slide":dclasses,dkvs) xs
460+elementToBeamer _ x = return x
524461
525462isListBlock :: Block -> Bool
526463isListBlock (BulletList _) = True
@@ -533,85 +470,87 @@ blockToLaTeX :: PandocMonad m
533470 => Block -- ^ Block to convert
534471 -> LW m (Doc Text)
535472blockToLaTeX Null = return empty
536−blockToLaTeX (Div (identifier,classes,kvs) bs)
537− | "incremental" `elem` classes = do
538− let classes' = filter ("incremental"/=) classes
539− beamer <- gets stBeamer
540− if beamer
541− then do oldIncremental <- gets stIncremental
542− modify $ \s -> s{ stIncremental = True }
543− result <- blockToLaTeX $ Div (identifier,classes',kvs) bs
544− modify $ \s -> s{ stIncremental = oldIncremental }
545− return result
546− else blockToLaTeX $ Div (identifier,classes',kvs) bs
547− | "nonincremental" `elem` classes = do
548− let classes' = filter ("nonincremental"/=) classes
549− beamer <- gets stBeamer
550− if beamer
551− then do oldIncremental <- gets stIncremental
552− modify $ \s -> s{ stIncremental = False }
553− result <- blockToLaTeX $ Div (identifier,classes',kvs) bs
554− modify $ \s -> s{ stIncremental = oldIncremental }
555− return result
556− else blockToLaTeX $ Div (identifier,classes',kvs) bs
557− | identifier == "refs" = do
558− modify $ \st -> st{ stHasCslRefs = True
559− , stCslHangingIndent =
560− "hanging-indent" `elem` classes }
561− contents <- blockListToLaTeX bs
562− return $ "\\begin{cslreferences}" $$
563− contents $$
564− "\\end{cslreferences}"
565− | otherwise = do
566− beamer <- gets stBeamer
567− linkAnchor' <- hypertarget True identifier empty
568− -- see #2704 for the motivation for adding \leavevmode:
569− let linkAnchor =
570− case bs of
571− Para _ : _
572− | not (isEmpty linkAnchor')
573− -> "\\leavevmode" <> linkAnchor' <> "%"
574− _ -> linkAnchor'
575− let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir
576− lang <- toLang $ lookup "lang" kvs
577− let wrapColumns = if beamer && "columns" `elem` classes
578− then \contents ->
579− inCmd "begin" "columns" <> brackets "T"
580− $$ contents
581− $$ inCmd "end" "columns"
582− else id
583− wrapColumn = if beamer && "column" `elem` classes
584− then \contents ->
585− let w = maybe "0.48" fromPct (lookup "width" kvs)
586− in inCmd "begin" "column" <>
587− braces (text w <> "\\textwidth")
588− $$ contents
589− $$ inCmd "end" "column"
590− else id
591− fromPct xs =
592− case reverse xs of
593− '%':ds -> case safeRead (reverse ds) of
594− Just digits -> showFl (digits / 100 :: Double)
595− Nothing -> xs
596− _ -> xs
597− wrapDir = case lookup "dir" kvs of
598− Just "rtl" -> align "RTL"
599− Just "ltr" -> align "LTR"
600− _ -> id
601− wrapLang txt = case lang of
602− Just lng -> let (l, o) = toPolyglossiaEnv lng
603− ops = if null o
604− then ""
605− else brackets $ text o
606− in inCmd "begin" (text l) <> ops
607− $$ blankline <> txt <> blankline
608− $$ inCmd "end" (text l)
609− Nothing -> txt
610− wrapNotes txt = if beamer && "notes" `elem` classes
611− then "\\note" <> braces txt -- speaker notes
612− else linkAnchor $$ txt
613− (wrapColumns . wrapColumn . wrapDir . wrapLang . wrapNotes)
614− <$> blockListToLaTeX bs
473+blockToLaTeX (Div attr@(identifier,"block":_,_) (Header _ _ ils : bs)) = do
474+ ref <- toLabel identifier
475+ let anchor = if null identifier
476+ then empty
477+ else cr <> "\\protect\\hypertarget" <>
478+ braces (text ref) <> braces empty
479+ title' <- inlineListToLaTeX ils
480+ contents <- blockListToLaTeX bs
481+ wrapDiv attr $ ("\\begin{block}" <> braces title' <> anchor) $$
482+ contents $$ "\\end{block}"
483+blockToLaTeX (Div (identifier,"slide":dclasses,dkvs)
484+ (Header _ (_,hclasses,hkvs) ils : bs)) = do
485+ -- note: [fragile] is required or verbatim breaks
486+ let hasCodeBlock (CodeBlock _ _) = [True]
487+ hasCodeBlock _ = []
488+ let hasCode (Code _ _) = [True]
489+ hasCode _ = []
490+ let classes = dclasses ++ hclasses
491+ let kvs = dkvs ++ hkvs
492+ let fragile = "fragile" `elem` classes ||
493+ not (null $ query hasCodeBlock bs ++ query hasCode bs)
494+ let frameoptions = ["allowdisplaybreaks", "allowframebreaks", "fragile",
495+ "b", "c", "t", "environment",
496+ "label", "plain", "shrink", "standout",
497+ "noframenumbering"]
498+ let optionslist = ["fragile" | fragile
499+ , isNothing (lookup "fragile" kvs)
500+ , "fragile" `notElem` classes] ++
501+ [k | k <- classes, k `elem` frameoptions] ++
502+ [k ++ "=" ++ v | (k,v) <- kvs, k `elem` frameoptions]
503+ let options = if null optionslist
504+ then empty
505+ else brackets (text (intercalate "," optionslist))
506+ slideTitle <- if ils == [Str "\0"] -- marker for hrule
507+ then return empty
508+ else braces <$> inlineListToLaTeX ils
509+ ref <- toLabel identifier
510+ let slideAnchor = if null identifier
511+ then empty
512+ else cr <> "\\protect\\hypertarget" <>
513+ braces (text ref) <> braces empty
514+ contents <- blockListToLaTeX bs >>= wrapDiv (identifier,classes,kvs)
515+ return $ ("\\begin{frame}" <> options <> slideTitle <> slideAnchor) $$
516+ contents $$
517+ "\\end{frame}"
518+blockToLaTeX (Div (identifier@(_:_),dclasses,dkvs)
519+ (Header lvl ("",hclasses,hkvs) ils : bs)) = do
520+ -- move identifier from div to header
521+ blockToLaTeX (Div ("",dclasses,dkvs)
522+ (Header lvl (identifier,hclasses,hkvs) ils : bs))
523+blockToLaTeX (Div (identifier,classes,kvs) bs) = do
524+ beamer <- gets stBeamer
525+ oldIncremental <- gets stIncremental
526+ if beamer && "incremental" `elem` classes
527+ then modify $ \st -> st{ stIncremental = True }
528+ else if beamer && "nonincremental" `elem` classes
529+ then modify $ \st -> st { stIncremental = False }
530+ else return ()
531+ result <- if identifier == "refs"
532+ then do
533+ inner <- blockListToLaTeX bs
534+ modify $ \st -> st{ stHasCslRefs = True
535+ , stCslHangingIndent =
536+ "hanging-indent" `elem` classes }
537+ return $ "\\begin{cslreferences}" $$
538+ inner $$
539+ "\\end{cslreferences}"
540+ else blockListToLaTeX bs
541+ modify $ \st -> st{ stIncremental = oldIncremental }
542+ linkAnchor' <- hypertarget True identifier empty
543+ -- see #2704 for the motivation for adding \leavevmode:
544+ let linkAnchor =
545+ case bs of
546+ Para _ : _
547+ | not (isEmpty linkAnchor')
548+ -> "\\leavevmode" <> linkAnchor' <> "%"
549+ _ -> linkAnchor'
550+ wrapNotes txt = if beamer && "notes" `elem` classes
551+ then "\\note" <> braces txt -- speaker notes
552+ else linkAnchor $$ txt
553+ wrapNotes <$> wrapDiv (identifier,classes,kvs) result
615554blockToLaTeX (Plain lst) =
616555 inlineListToLaTeX lst
617556-- title beginning with fig: indicates that the image is a figure
@@ -1077,6 +1016,46 @@ sectionHeader unnumbered ident level lst = do
10771016 braces txtNoNotes
10781017 else empty
10791018
1019+wrapDiv :: PandocMonad m => Attr -> Doc Text -> LW m (Doc Text)
1020+wrapDiv (_,classes,kvs) t = do
1021+ beamer <- gets stBeamer
1022+ let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir
1023+ lang <- toLang $ lookup "lang" kvs
1024+ let wrapColumns = if beamer && "columns" `elem` classes
1025+ then \contents ->
1026+ inCmd "begin" "columns" <> brackets "T"
1027+ $$ contents
1028+ $$ inCmd "end" "columns"
1029+ else id
1030+ wrapColumn = if beamer && "column" `elem` classes
1031+ then \contents ->
1032+ let w = maybe "0.48" fromPct (lookup "width" kvs)
1033+ in inCmd "begin" "column" <>
1034+ braces (text w <> "\\textwidth")
1035+ $$ contents
1036+ $$ inCmd "end" "column"
1037+ else id
1038+ fromPct xs =
1039+ case reverse xs of
1040+ '%':ds -> case safeRead (reverse ds) of
1041+ Just digits -> showFl (digits / 100 :: Double)
1042+ Nothing -> xs
1043+ _ -> xs
1044+ wrapDir = case lookup "dir" kvs of
1045+ Just "rtl" -> align "RTL"
1046+ Just "ltr" -> align "LTR"
1047+ _ -> id
1048+ wrapLang txt = case lang of
1049+ Just lng -> let (l, o) = toPolyglossiaEnv lng
1050+ ops = if null o
1051+ then ""
1052+ else brackets $ text o
1053+ in inCmd "begin" (text l) <> ops
1054+ $$ blankline <> txt <> blankline
1055+ $$ inCmd "end" (text l)
1056+ Nothing -> txt
1057+ return $ wrapColumns . wrapColumn . wrapDir . wrapLang $ t
1058+
10801059hypertarget :: PandocMonad m => Bool -> String -> Doc Text -> LW m (Doc Text)
10811060hypertarget _ "" x = return x
10821061hypertarget addnewline ident x = do
src/Text/Pandoc/Writers/OPML.hs+13−22
@@ -13,14 +13,12 @@ Conversion of 'Pandoc' documents to OPML XML.
1313-}
1414module Text.Pandoc.Writers.OPML ( writeOPML) where
1515import Prelude
16−import Control.Monad.Except (throwError)
1716import Data.Text (Text)
1817import qualified Data.Text as T
1918import qualified Text.Pandoc.Builder as B
2019import Text.Pandoc.Class (PandocMonad)
2120import Data.Time
2221import Text.Pandoc.Definition
23−import Text.Pandoc.Error
2422import Text.Pandoc.Options
2523import Text.DocLayout
2624import Text.Pandoc.Shared
@@ -33,8 +31,7 @@ import Text.Pandoc.XML
3331-- | Convert Pandoc document to string in OPML format.
3432writeOPML :: PandocMonad m => WriterOptions -> Pandoc -> m Text
3533writeOPML opts (Pandoc meta blocks) = do
36− let elements = hierarchicalize blocks
37− colwidth = if writerWrapText opts == WrapAuto
34+ let colwidth = if writerWrapText opts == WrapAuto
3835 then Just $ writerColumns opts
3936 else Nothing
4037 meta' = B.setMeta "date" (B.str $ convertDate $ docDate meta) meta
@@ -42,7 +39,8 @@ writeOPML opts (Pandoc meta blocks) = do
4239 (writeMarkdown def . Pandoc nullMeta)
4340 (\ils -> T.stripEnd <$> writeMarkdown def (Pandoc nullMeta [Plain ils]))
4441 meta'
45− main <- (render colwidth . vcat) <$> mapM (elementToOPML opts) elements
42+ let blocks' = makeSections False (Just 1) blocks
43+ main <- (render colwidth . vcat) <$> mapM (blockToOPML opts) blocks'
4644 let context = defField "body" main metadata
4745 return $
4846 (if writerPreferAscii opts then toEntities else id) $
@@ -63,25 +61,18 @@ convertDate :: [Inline] -> String
6361convertDate ils = maybe "" showDateTimeRFC822 $
6462 parseTimeM True defaultTimeLocale "%F" =<< normalizeDate (stringify ils)
6563
66−-- | Convert an Element to OPML.
67−elementToOPML :: PandocMonad m => WriterOptions -> Element -> m (Doc Text)
68−elementToOPML _ (Blk _) = return empty
69−elementToOPML opts (Sec _ _num _ title elements) = do
70− let isBlk :: Element -> Bool
71− isBlk (Blk _) = True
72− isBlk _ = False
73−
74− fromBlk :: PandocMonad m => Element -> m Block
75− fromBlk (Blk x) = return x
76− fromBlk _ = throwError $ PandocSomeError "fromBlk called on non-block"
77−
78− (blocks, rest) = span isBlk elements
64+-- | Convert a Block to OPML.
65+blockToOPML :: PandocMonad m => WriterOptions -> Block -> m (Doc Text)
66+blockToOPML opts (Div (_,"section":_,_) (Header _ _ title : xs)) = do
67+ let isSect (Div (_,"section":_,_) (Header{}:_)) = True
68+ isSect _ = False
69+ let (blocks, rest) = break isSect xs
7970 htmlIls <- writeHtmlInlines title
8071 md <- if null blocks
8172 then return mempty
82− else do blks <- mapM fromBlk blocks
83− writeMarkdown def $ Pandoc nullMeta blks
73+ else writeMarkdown def $ Pandoc nullMeta blocks
8474 let attrs = ("text", T.unpack htmlIls) :
8575 [("_note", T.unpack $ T.stripEnd md) | not (null blocks)]
86− o <- mapM (elementToOPML opts) rest
87− return $ inTags True "outline" attrs $ vcat o
76+ rest' <- vcat <$> mapM (blockToOPML opts) rest
77+ return $ inTags True "outline" attrs rest'
78+blockToOPML _ _ = return empty
src/Text/Pandoc/Writers/Shared.hs+18−9
@@ -39,6 +39,7 @@ module Text.Pandoc.Writers.Shared (
3939where
4040import Prelude
4141import Safe (lastMay)
42+import Data.Maybe (fromMaybe)
4243import Control.Monad (zipWithM)
4344import Data.Aeson (ToJSON (..), encode)
4445import Data.Char (chr, ord, isSpace)
@@ -49,7 +50,7 @@ import qualified Text.Pandoc.Builder as Builder
4950import Text.Pandoc.Definition
5051import Text.Pandoc.Options
5152import Text.DocLayout
52−import Text.Pandoc.Shared (stringify, hierarchicalize, Element(..), deNote)
53+import Text.Pandoc.Shared (stringify, makeSections, deNote, deLink)
5354import Text.Pandoc.Walk (walk)
5455import qualified Text.Pandoc.UTF8 as UTF8
5556import Text.Pandoc.XML (escapeStringForXML)
@@ -382,20 +383,28 @@ toTableOfContents :: WriterOptions
382383 -> [Block]
383384 -> Block
384385toTableOfContents opts bs =
385− BulletList $ map (elementToListItem opts) (hierarchicalize bs)
386+ BulletList $ filter (not . null)
387+ $ map (sectionToListItem opts)
388+ $ makeSections (writerNumberSections opts) Nothing bs
386389
387390-- | Converts an Element to a list item for a table of contents,
388−elementToListItem :: WriterOptions -> Element -> [Block]
389−elementToListItem opts (Sec lev _nums (ident,_,_) headerText subsecs)
390− = Plain headerLink : [BulletList listContents | not (null subsecs)
391− , lev < writerTOCDepth opts]
391+sectionToListItem :: WriterOptions -> Block -> [Block]
392+sectionToListItem opts (Div (ident,_,_)
393+ (Header lev (_,_,kvs) ils : subsecs)) =
394+ Plain headerLink : [BulletList listContents | not (null listContents)
395+ , lev < writerTOCDepth opts]
392396 where
393− headerText' = walk deNote headerText
397+ num = fromMaybe "" $ lookup "number" kvs
398+ addNumber = if null num
399+ then id
400+ else (Span ("",["toc-section-number"],[])
401+ [Str num] :) . (Space :)
402+ headerText' = addNumber $ walk (deLink . deNote) ils
394403 headerLink = if null ident
395404 then headerText'
396405 else [Link nullAttr headerText' ('#':ident, "")]
397− listContents = map (elementToListItem opts) subsecs
398−elementToListItem _ (Blk _) = []
406+ listContents = filter (not . null) $ map (sectionToListItem opts) subsecs
407+sectionToListItem _ _ = []
399408
400409endsWithPlain :: [Block] -> Bool
401410endsWithPlain xs =
src/Text/Pandoc/Writers/TEI.hs+22−26
@@ -32,21 +32,20 @@ import Text.Pandoc.XML
3232-- | Convert Pandoc document to string in Docbook format.
3333writeTEI :: PandocMonad m => WriterOptions -> Pandoc -> m Text
3434writeTEI opts (Pandoc meta blocks) = do
35− let elements = hierarchicalize blocks
36− colwidth = if writerWrapText opts == WrapAuto
35+ let colwidth = if writerWrapText opts == WrapAuto
3736 then Just $ writerColumns opts
3837 else Nothing
39− startLvl = case writerTopLevelDivision opts of
38+ let startLvl = case writerTopLevelDivision opts of
4039 TopLevelPart -> -1
4140 TopLevelChapter -> 0
4241 TopLevelSection -> 1
4342 TopLevelDefault -> 1
43+ let fromBlocks = blocksToTEI opts . makeSections False (Just startLvl)
4444 metadata <- metaToContext opts
45− (fmap vcat .
46− mapM (elementToTEI opts startLvl) . hierarchicalize)
45+ fromBlocks
4746 (fmap chomp . inlinesToTEI opts)
4847 meta
49− main <- vcat <$> mapM (elementToTEI opts startLvl) elements
48+ main <- fromBlocks blocks
5049 let context = defField "body" main
5150 $ defField "mathml" (case writerHTMLMathMethod opts of
5251 MathML -> True
@@ -56,25 +55,6 @@ writeTEI opts (Pandoc meta blocks) = do
5655 Nothing -> main
5756 Just tpl -> renderTemplate tpl context
5857
59−-- | Convert an Element to TEI.
60−elementToTEI :: PandocMonad m => WriterOptions -> Int -> Element -> m (Doc Text)
61−elementToTEI opts _ (Blk block) = blockToTEI opts block
62−elementToTEI opts lvl (Sec _ _num attr title elements) = do
63− -- TEI doesn't allow sections with no content, so insert some if needed
64− let elements' = if null elements
65− then [Blk (Para [])]
66− else elements
67− -- level numbering correspond to LaTeX internals
68− divType = case lvl of
69− n | n == -1 -> "part"
70− | n == 0 -> "chapter"
71− | n >= 1 && n <= 5 -> "level" ++ show n
72− | otherwise -> "section"
73− contents <- vcat <$> mapM (elementToTEI opts (lvl + 1)) elements'
74− titleContents <- inlinesToTEI opts title
75− return $ inTags True "div" (("type", divType) : idFromAttr opts attr) $
76− inTagsSimple "head" titleContents $$ contents
77−
7858-- | Convert a list of Pandoc blocks to TEI.
7959blocksToTEI :: PandocMonad m => WriterOptions -> [Block] -> m (Doc Text)
8060blocksToTEI opts bs = vcat <$> mapM (blockToTEI opts) bs
@@ -121,6 +101,22 @@ imageToTEI opts attr src = return $ selfClosingTag "graphic" $
121101-- | Convert a Pandoc block element to TEI.
122102blockToTEI :: PandocMonad m => WriterOptions -> Block -> m (Doc Text)
123103blockToTEI _ Null = return empty
104+blockToTEI opts (Div attr@(_,"section":_,_) (Header lvl _ ils : xs)) =
105+ do
106+ -- TEI doesn't allow sections with no content, so insert some if needed
107+ let xs' = if null xs
108+ then [Para []]
109+ else xs
110+ -- level numbering correspond to LaTeX internals
111+ divType = case lvl of
112+ n | n == -1 -> "part"
113+ | n == 0 -> "chapter"
114+ | n >= 1 && n <= 5 -> "level" ++ show n
115+ | otherwise -> "section"
116+ titleContents <- inlinesToTEI opts ils
117+ contents <- blocksToTEI opts xs'
118+ return $ inTags True "div" (("type", divType) : idFromAttr opts attr) $
119+ inTagsSimple "head" titleContents $$ contents
124120-- Add ids to paragraphs in divs with ids - this is needed for
125121-- pandoc-citeproc to get link anchors in bibliographies:
126122blockToTEI opts (Div attr [Para lst]) = do
@@ -128,7 +124,7 @@ blockToTEI opts (Div attr [Para lst]) = do
128124 inTags False "p" attribs <$> inlinesToTEI opts lst
129125blockToTEI opts (Div _ bs) = blocksToTEI opts $ map plainToPara bs
130126blockToTEI _ h@Header{} = do
131− -- should not occur after hierarchicalize, except inside lists/blockquotes
127+ -- should not occur after makeSections, except inside lists/blockquotes
132128 report $ BlockNotRendered h
133129 return empty
134130-- For TEI simple, text must be within containing block element, so
test/command/1710.md−2
@@ -56,7 +56,6 @@ ok
5656^D
5757\begin{frame}{Slide one}
5858\protect\hypertarget{slide-one}{}
59−
6059\begin{columns}[T]
6160\begin{column}{0.4\textwidth}
6261\begin{itemize}
@@ -82,6 +81,5 @@ ok
8281ok
8382\end{column}
8483\end{columns}
85−
8684\end{frame}
8785```
test/command/4016.md+2−6
@@ -15,32 +15,28 @@ pandoc -t beamer
1515^D
1616\begin{frame}{Level 2 blocks}
1717\protect\hypertarget{level-2-blocks}{}
18−
1918\begin{columns}[T]
2019\begin{column}{0.4\textwidth}
2120\begin{block}{Block one}
22−
21+\protect\hypertarget{block-one}{}
2322\begin{itemize}
2423\tightlist
2524\item
2625 Item
2726\end{itemize}
28−
2927\end{block}
3028\end{column}
3129
3230\begin{column}{0.6\textwidth}
3331\begin{block}{Block two}
34−
32+\protect\hypertarget{block-two}{}
3533\begin{itemize}
3634\tightlist
3735\item
3836 Item
3937\end{itemize}
40−
4138\end{block}
4239\end{column}
4340\end{columns}
44−
4541\end{frame}
4642```
test/command/4690.md−2
@@ -13,7 +13,6 @@ content2
1313^D
1414\begin{frame}{title}
1515\protect\hypertarget{title}{}
16−
1716\begin{columns}[T]
1817\begin{column}{0.08\textwidth}
1918content
@@ -23,6 +22,5 @@ content
2322content2
2423\end{column}
2524\end{columns}
26−
2725\end{frame}
2826```
test/command/empty_paragraphs.md−2
@@ -37,8 +37,6 @@
3737[Para [Str "hi"], Para [], Para [], Para [Str "lo"]]
3838^D
3939<p>hi</p>
40−
41−
4240<p>lo</p>
4341```
4442
test/lua/module/pandoc-utils.lua+5−8
@@ -39,20 +39,17 @@ return {
3939 end)
4040 },
4141
42− group 'hierarchicalize' {
42+ group 'make_sections' {
4343 test('sanity check', function ()
4444 local blks = {
4545 pandoc.Header(1, {pandoc.Str 'First'}),
4646 pandoc.Header(2, {pandoc.Str 'Second'}),
4747 pandoc.Header(2, {pandoc.Str 'Third'}),
4848 }
49− local hblks = utils.hierarchicalize(blks)
50− -- cannot create Elements directly; performing only an approximate
51− -- sanity checking instead of a full equality comparison.
52− assert.are_equal('Sec', hblks[1].t)
53− assert.are_equal('Sec', hblks[1].contents[1].t)
54− assert.are_equal(1, hblks[1].contents[2].numbering[1])
55− assert.are_equal(2, hblks[1].contents[2].numbering[2])
49+ local hblks = utils.make_sections(true, 1, blks)
50+ assert.are_equal('Div', hblks[1].t)
51+ assert.are_equal('Header', hblks[1].content[1].t)
52+ assert.are_equal('1', hblks[1].content[1].attributes['number'])
5653 end)
5754 },
5855
test/writer.html4−1
@@ -442,7 +442,6 @@ Blah
442442<li>Escaped <code>$</code>: $73 <em>this should be emphasized</em> 23$.</li>
443443</ul>
444444<p>Here’s a LaTeX table:</p>
445−
446445<hr />
447446<h1 id="special-characters">Special Characters</h1>
448447<p>Here is some unicode:</p>
test/writer.html5−1
@@ -445,7 +445,6 @@ Blah
445445<li>Escaped <code>$</code>: $73 <em>this should be emphasized</em> 23$.</li>
446446</ul>
447447<p>Here’s a LaTeX table:</p>
448−
449448<hr />
450449<h1 id="special-characters">Special Characters</h1>
451450<p>Here is some unicode:</p>