@@ -28,7 +28,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29Conversion of 'Pandoc' documents to HTML.
29Conversion of 'Pandoc' documents to HTML.
31−module Text.Pandoc.Writers.HTML ( writeHtml , writeHtmlString ) where
31+module Text.Pandoc.Writers.HTML (
32+ writeHtml4, writeHtml4String,
33+ writeHtml5, writeHtml5String ) where
32import Text.Pandoc.Definition
34import Text.Pandoc.Definition
33import Text.Pandoc.Walk
35import Text.Pandoc.Walk
34import Data.Monoid ((<>))
36import Data.Monoid ((<>))
@@ -80,12 +82,13 @@ data WriterState = WriterState
80 , stHighlighting :: Bool -- ^ Syntax highlighting is used
82 , stHighlighting :: Bool -- ^ Syntax highlighting is used
81 , stSecNum :: [Int] -- ^ Number of current section
83 , stSecNum :: [Int] -- ^ Number of current section
82 , stElement :: Bool -- ^ Processing an Element
84 , stElement :: Bool -- ^ Processing an Element
85+ , stHtml5 :: Bool -- ^ Use HTML5
85defaultWriterState :: WriterState
88defaultWriterState :: WriterState
86defaultWriterState = WriterState {stNotes= [], stMath = False, stQuotes = False,
89defaultWriterState = WriterState {stNotes= [], stMath = False, stQuotes = False,
87 stHighlighting = False, stSecNum = [],
90 stHighlighting = False, stSecNum = [],
88− stElement = False}
91+ stElement = False, stHtml5 = False}
90-- Helpers to render HTML with the appropriate function.
93-- Helpers to render HTML with the appropriate function.
@@ -102,19 +105,35 @@ nl opts = if writerWrapText opts == WrapNone
102 then mempty
105 then mempty
103 else preEscapedString "\n"
106 else preEscapedString "\n"
105−-- | Convert Pandoc document to Html string.
108+-- | Convert Pandoc document to Html 5 string.
109+writeHtml5String :: PandocMonad m => WriterOptions -> Pandoc -> m String
110+writeHtml5String = writeHtmlString' True
112+-- | Convert Pandoc document to Html 5 structure.
106−writeHtmlString :: PandocMonad m => WriterOptions -> Pandoc -> m String
113+writeHtml5 :: PandocMonad m => WriterOptions -> Pandoc -> m Html
114+writeHtml5 = writeHtml' True
116+-- | Convert Pandoc document to Html 4 string.
117+writeHtml4String :: PandocMonad m => WriterOptions -> Pandoc -> m String
107−writeHtmlString opts d = do
118+writeHtml4String = writeHtmlString' False
120+-- | Convert Pandoc document to Html 4 structure.
121+writeHtml4 :: PandocMonad m => WriterOptions -> Pandoc -> m Html
122+writeHtml4 = writeHtml' False
124+writeHtmlString' :: PandocMonad m => Bool -> WriterOptions -> Pandoc -> m String
125+writeHtmlString' html5 opts d = do
108− (body, context) <- evalStateT (pandocToHtml opts d) defaultWriterState
126+ (body, context) <- evalStateT (pandocToHtml opts d)
127+ defaultWriterState{ stHtml5 = html5 }
109 return $ case writerTemplate opts of
128 return $ case writerTemplate opts of
110 Nothing -> renderHtml body
129 Nothing -> renderHtml body
111 Just tpl -> renderTemplate' tpl $
130 Just tpl -> renderTemplate' tpl $
112 defField "body" (renderHtml body) context
131 defField "body" (renderHtml body) context
114−-- | Convert Pandoc document to Html structure.
115−writeHtml :: PandocMonad m => WriterOptions -> Pandoc -> m Html
133+writeHtml' :: PandocMonad m => Bool -> WriterOptions -> Pandoc -> m Html
116−writeHtml opts d = do
134+writeHtml' html5 opts d = do
117− (body, context) <- evalStateT (pandocToHtml opts d) defaultWriterState
135+ (body, context) <- evalStateT (pandocToHtml opts d)
136+ defaultWriterState{ stHtml5 = html5 }
118 return $ case writerTemplate opts of
137 return $ case writerTemplate opts of
119 Nothing -> body
138 Nothing -> body
120 Just tpl -> renderTemplate' tpl $
139 Just tpl -> renderTemplate' tpl $
@@ -144,8 +163,8 @@ pandocToHtml opts (Pandoc meta blocks) = do
144 blocks' <- liftM (mconcat . intersperse (nl opts)) $
163 blocks' <- liftM (mconcat . intersperse (nl opts)) $
145 mapM (elementToHtml slideLevel opts) sects
164 mapM (elementToHtml slideLevel opts) sects
146 st <- get
165 st <- get
147− let notes = reverse (stNotes st)
166+ notes <- footnoteSection opts (reverse (stNotes st))
148− let thebody = blocks' >> footnoteSection opts notes
167+ let thebody = blocks' >> notes
149 let math = case writerHTMLMathMethod opts of
168 let math = case writerHTMLMathMethod opts of
150 LaTeXMathML (Just url) ->
169 LaTeXMathML (Just url) ->
151 H.script ! A.src (toValue url)
170 H.script ! A.src (toValue url)
@@ -172,7 +191,7 @@ pandocToHtml opts (Pandoc meta blocks) = do
172 (H.link ! A.rel "stylesheet" ! A.href (toValue css)) <>
191 (H.link ! A.rel "stylesheet" ! A.href (toValue css)) <>
173 (H.script ! A.type_ "text/javascript" $ toHtml renderKaTeX)
192 (H.script ! A.type_ "text/javascript" $ toHtml renderKaTeX)
174 _ -> case lookup "mathml-script" (writerVariables opts) of
193 _ -> case lookup "mathml-script" (writerVariables opts) of
175− Just s | not (writerHtml5 opts) ->
194+ Just s | not (stHtml5 st) ->
176 H.script ! A.type_ "text/javascript"
195 H.script ! A.type_ "text/javascript"
177 $ preEscapedString
196 $ preEscapedString
178 ("/*<![CDATA[*/\n" ++ s ++ "/*]]>*/\n")
197 ("/*<![CDATA[*/\n" ++ s ++ "/*]]>*/\n")
@@ -199,7 +218,7 @@ pandocToHtml opts (Pandoc meta blocks) = do
199 defField "slideous-url" ("slideous" :: String) $
218 defField "slideous-url" ("slideous" :: String) $
200 defField "revealjs-url" ("reveal.js" :: String) $
219 defField "revealjs-url" ("reveal.js" :: String) $
201 defField "s5-url" ("s5/default" :: String) $
220 defField "s5-url" ("s5/default" :: String) $
202− defField "html5" (writerHtml5 opts) $
221+ defField "html5" (stHtml5 st) $
204 return (thebody, context)
223 return (thebody, context)
@@ -277,6 +296,7 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen
277 let slide = writerSlideVariant opts /= NoSlides && level <= slideLevel
296 let slide = writerSlideVariant opts /= NoSlides && level <= slideLevel
278 let num' = zipWith (+) num (writerNumberOffset opts ++ repeat 0)
297 let num' = zipWith (+) num (writerNumberOffset opts ++ repeat 0)
279 modify $ \st -> st{stSecNum = num'} -- update section number
298 modify $ \st -> st{stSecNum = num'} -- update section number
299+ html5 <- gets stHtml5
280 let titleSlide = slide && level < slideLevel
300 let titleSlide = slide && level < slideLevel
281 header' <- if title' == [Str "\0"] -- marker for hrule
301 header' <- if title' == [Str "\0"] -- marker for hrule
282 then return mempty
302 then return mempty
@@ -307,10 +327,10 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen
307 let inNl x = mconcat $ nl opts : intersperse (nl opts) x ++ [nl opts]
327 let inNl x = mconcat $ nl opts : intersperse (nl opts) x ++ [nl opts]
308 let classes' = ["titleslide" | titleSlide] ++ ["slide" | slide] ++
328 let classes' = ["titleslide" | titleSlide] ++ ["slide" | slide] ++
309 ["section" | (slide || writerSectionDivs opts) &&
329 ["section" | (slide || writerSectionDivs opts) &&
310− not (writerHtml5 opts) ] ++
330+ not html5 ] ++
311 ["level" ++ show level | slide || writerSectionDivs opts ]
331 ["level" ++ show level | slide || writerSectionDivs opts ]
312 ++ classes
332 ++ classes
313− let secttag = if writerHtml5 opts
333+ let secttag = if html5
314 then H5.section
334 then H5.section
315 else H.div
335 else H.div
316 let attr = (id',classes',keyvals)
336 let attr = (id',classes',keyvals)
@@ -327,19 +347,22 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen
328-- | Convert list of Note blocks to a footnote <div>.
348-- | Convert list of Note blocks to a footnote <div>.
329-- Assumes notes are sorted.
349-- Assumes notes are sorted.
350+footnoteSection :: PandocMonad m
330−footnoteSection :: WriterOptions -> [Html] -> Html
351+ => WriterOptions -> [Html] -> StateT WriterState m Html
331−footnoteSection opts notes =
352+footnoteSection opts notes = do
332− if null notes
353+ html5 <- gets stHtml5
333− then mempty
354+ let hrtag = if html5 then H5.hr else H.hr
334− else nl opts >> (container
335− $ nl opts >> hrtag >> nl opts >>
336− H.ol (mconcat notes >> nl opts) >> nl opts)
337− where container x = if writerHtml5 opts
355+ let container x = if html5
338− then H5.section ! A.class_ "footnotes" $ x
356+ then H5.section ! A.class_ "footnotes" $ x
339− else if writerSlideVariant opts /= NoSlides
357+ else if writerSlideVariant opts /= NoSlides
340− then H.div ! A.class_ "footnotes slide" $ x
358+ then H.div ! A.class_ "footnotes slide" $ x
341− else H.div ! A.class_ "footnotes" $ x
359+ else H.div ! A.class_ "footnotes" $ x
342− hrtag = if writerHtml5 opts then H5.hr else H.hr
360+ return $
363+ else nl opts >> (container
364+ $ nl opts >> hrtag >> nl opts >>
365+ H.ol (mconcat notes >> nl opts) >> nl opts)
344-- | Parse a mailto link; return Just (name, domain) or Nothing.
367-- | Parse a mailto link; return Just (name, domain) or Nothing.
345parseMailto :: String -> Maybe (String, String)
368parseMailto :: String -> Maybe (String, String)
@@ -448,13 +471,14 @@ blockToHtml opts (Plain lst) = inlineListToHtml opts lst
448-- title beginning with fig: indicates that the image is a figure
471-- title beginning with fig: indicates that the image is a figure
449blockToHtml opts (Para [Image attr txt (s,'f':'i':'g':':':tit)]) = do
472blockToHtml opts (Para [Image attr txt (s,'f':'i':'g':':':tit)]) = do
450 img <- inlineToHtml opts (Image attr txt (s,tit))
473 img <- inlineToHtml opts (Image attr txt (s,tit))
474+ html5 <- gets stHtml5
451− let tocapt = if writerHtml5 opts
475+ let tocapt = if html5
452 then H5.figcaption
476 then H5.figcaption
453 else H.p ! A.class_ "caption"
477 else H.p ! A.class_ "caption"
454 capt <- if null txt
478 capt <- if null txt
455 then return mempty
479 then return mempty
456 else tocapt `fmap` inlineListToHtml opts txt
480 else tocapt `fmap` inlineListToHtml opts txt
457− return $ if writerHtml5 opts
481+ return $ if html5
458 then H5.figure $ mconcat
482 then H5.figure $ mconcat
459 [nl opts, img, capt, nl opts]
483 [nl opts, img, capt, nl opts]
460 else H.div ! A.class_ "figure" $ mconcat
484 else H.div ! A.class_ "figure" $ mconcat
@@ -475,12 +499,13 @@ blockToHtml opts (LineBlock lns) =
475 htmlLines <- mconcat . intersperse lf <$> mapM (inlineListToHtml opts) lns
499 htmlLines <- mconcat . intersperse lf <$> mapM (inlineListToHtml opts) lns
476 return $ H.div ! A.style "white-space: pre-line;" $ htmlLines
500 return $ H.div ! A.style "white-space: pre-line;" $ htmlLines
477blockToHtml opts (Div attr@(ident, classes, kvs) bs) = do
501blockToHtml opts (Div attr@(ident, classes, kvs) bs) = do
502+ html5 <- gets stHtml5
478 let speakerNotes = "notes" `elem` classes
503 let speakerNotes = "notes" `elem` classes
479 -- we don't want incremental output inside speaker notes, see #1394
504 -- we don't want incremental output inside speaker notes, see #1394
480 let opts' = if speakerNotes then opts{ writerIncremental = False } else opts
505 let opts' = if speakerNotes then opts{ writerIncremental = False } else opts
481 contents <- blockListToHtml opts' bs
506 contents <- blockListToHtml opts' bs
482 let contents' = nl opts >> contents >> nl opts
507 let contents' = nl opts >> contents >> nl opts
483− let (divtag, classes') = if writerHtml5 opts && "section" `elem` classes
508+ let (divtag, classes') = if html5 && "section" `elem` classes
484 then (H5.section, filter (/= "section") classes)
509 then (H5.section, filter (/= "section") classes)
485 else (H.div, classes)
510 else (H.div, classes)
@@ -498,7 +523,9 @@ blockToHtml opts (RawBlock f str)
498 allowsMathEnvironments (writerHTMLMathMethod opts) &&
523 allowsMathEnvironments (writerHTMLMathMethod opts) &&
499 isMathEnvironment str = blockToHtml opts $ Plain [Math DisplayMath str]
524 isMathEnvironment str = blockToHtml opts $ Plain [Math DisplayMath str]
500 | otherwise = return mempty
525 | otherwise = return mempty
501−blockToHtml opts (HorizontalRule) = return $ if writerHtml5 opts then H5.hr else H.hr
526+blockToHtml _ (HorizontalRule) = do
527+ html5 <- gets stHtml5
528+ return $ if html5 then H5.hr else H.hr
502blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do
529blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do
503 let tolhs = isEnabled Ext_literate_haskell opts &&
530 let tolhs = isEnabled Ext_literate_haskell opts &&
504 any (\c -> map toLower c == "haskell") classes &&
531 any (\c -> map toLower c == "haskell") classes &&
@@ -564,6 +591,7 @@ blockToHtml opts (BulletList lst) = do
564 return $ unordList opts contents
591 return $ unordList opts contents
565blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do
592blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do
566 contents <- mapM (blockListToHtml opts) lst
593 contents <- mapM (blockListToHtml opts) lst
594+ html5 <- gets stHtml5
567 let numstyle' = case numstyle of
595 let numstyle' = case numstyle of
568 Example -> "decimal"
596 Example -> "decimal"
569 _ -> camelCaseToHyphenated $ show numstyle
597 _ -> camelCaseToHyphenated $ show numstyle
@@ -574,7 +602,7 @@ blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do
574 then [A.class_ "example"]
602 then [A.class_ "example"]
575 else []) ++
603 else []) ++
576 (if numstyle /= DefaultStyle
604 (if numstyle /= DefaultStyle
577− then if writerHtml5 opts
605+ then if html5
578 then [A.type_ $
606 then [A.type_ $
579 case numstyle of
607 case numstyle of
580 Decimal -> "1"
608 Decimal -> "1"
@@ -603,6 +631,7 @@ blockToHtml opts (Table capt aligns widths headers rows') = do
604 cs <- inlineListToHtml opts capt
632 cs <- inlineListToHtml opts capt
605 return $ H.caption cs >> nl opts
633 return $ H.caption cs >> nl opts
634+ html5 <- gets stHtml5
606 let percent w = show (truncate (100*w) :: Integer) ++ "%"
635 let percent w = show (truncate (100*w) :: Integer) ++ "%"
607 let coltags = if all (== 0.0) widths
636 let coltags = if all (== 0.0) widths
608 then mempty
637 then mempty
@@ -610,7 +639,7 @@ blockToHtml opts (Table capt aligns widths headers rows') = do
610 H.colgroup $ do
639 H.colgroup $ do
612 mapM_ (\w -> do
641 mapM_ (\w -> do
613− if writerHtml5 opts
642+ if html5
614 then H.col ! A.style (toValue $ "width: " ++
643 then H.col ! A.style (toValue $ "width: " ++
615 percent w)
644 percent w)
616 else H.col ! A.width (toValue $ percent w)
645 else H.col ! A.width (toValue $ percent w)
@@ -666,8 +695,9 @@ tableItemToHtml :: PandocMonad m
666 -> StateT WriterState m Html
695 -> StateT WriterState m Html
667tableItemToHtml opts tag' align' item = do
696tableItemToHtml opts tag' align' item = do
668 contents <- blockListToHtml opts item
697 contents <- blockListToHtml opts item
698+ html5 <- gets stHtml5
669 let alignStr = alignmentToString align'
699 let alignStr = alignmentToString align'
670− let attribs = if writerHtml5 opts
700+ let attribs = if html5
671 then A.style (toValue $ "text-align: " ++ alignStr ++ ";")
701 then A.style (toValue $ "text-align: " ++ alignStr ++ ";")
672 else A.align (toValue alignStr)
702 else A.align (toValue alignStr)
673 let tag'' = if null alignStr
703 let tag'' = if null alignStr
@@ -707,7 +737,8 @@ annotateMML e tex = math (unode "semantics" [cs, unode "annotation" (annotAttrs,
707-- | Convert Pandoc inline element to HTML.
737-- | Convert Pandoc inline element to HTML.
708inlineToHtml :: PandocMonad m
738inlineToHtml :: PandocMonad m
709 => WriterOptions -> Inline -> StateT WriterState m Html
739 => WriterOptions -> Inline -> StateT WriterState m Html
710−inlineToHtml opts inline =
740+inlineToHtml opts inline = do
741+ html5 <- gets stHtml5
711 case inline of
742 case inline of
712 (Str str) -> return $ strToHtml str
743 (Str str) -> return $ strToHtml str
713 (Space) -> return $ strToHtml " "
744 (Space) -> return $ strToHtml " "
@@ -715,7 +746,7 @@ inlineToHtml opts inline =
715 WrapNone -> preEscapedString " "
746 WrapNone -> preEscapedString " "
716 WrapAuto -> preEscapedString " "
747 WrapAuto -> preEscapedString " "
717 WrapPreserve -> preEscapedString "\n"
748 WrapPreserve -> preEscapedString "\n"
718− (LineBreak) -> return $ (if writerHtml5 opts then H5.br else H.br)
749+ (LineBreak) -> return $ (if html5 then H5.br else H.br)
719 <> strToHtml "\n"
750 <> strToHtml "\n"
720 (Span (id',classes,kvs) ils)
751 (Span (id',classes,kvs) ils)
721 -> inlineListToHtml opts ils >>=
752 -> inlineListToHtml opts ils >>=
@@ -784,12 +815,12 @@ inlineToHtml opts inline =
784 InlineMath -> H.span ! A.class_ mathClass $ m
815 InlineMath -> H.span ! A.class_ mathClass $ m
785 DisplayMath -> H.div ! A.class_ mathClass $ m
816 DisplayMath -> H.div ! A.class_ mathClass $ m
786 WebTeX url -> do
817 WebTeX url -> do
787− let imtag = if writerHtml5 opts then H5.img else H.img
818+ let imtag = if html5 then H5.img else H.img
788 let m = imtag ! A.style "vertical-align:middle"
819 let m = imtag ! A.style "vertical-align:middle"
789 ! A.src (toValue $ url ++ urlEncode str)
820 ! A.src (toValue $ url ++ urlEncode str)
790 ! A.alt (toValue str)
821 ! A.alt (toValue str)
791 ! A.title (toValue str)
822 ! A.title (toValue str)
792− let brtag = if writerHtml5 opts then H5.br else H.br
823+ let brtag = if html5 then H5.br else H.br
793 return $ case t of
824 return $ case t of
794 InlineMath -> m
825 InlineMath -> m
795 DisplayMath -> brtag >> m >> brtag
826 DisplayMath -> brtag >> m >> brtag
@@ -817,7 +848,7 @@ inlineToHtml opts inline =
817 PlainMath -> do
848 PlainMath -> do
818 x <- lift (texMathToInlines t str) >>= inlineListToHtml opts
849 x <- lift (texMathToInlines t str) >>= inlineListToHtml opts
819 let m = H.span ! A.class_ mathClass $ x
850 let m = H.span ! A.class_ mathClass $ x
820− let brtag = if writerHtml5 opts then H5.br else H.br
851+ let brtag = if html5 then H5.br else H.br
821 return $ case t of
852 return $ case t of
822 InlineMath -> m
853 InlineMath -> m
823 DisplayMath -> brtag >> m >> brtag
854 DisplayMath -> brtag >> m >> brtag
@@ -847,7 +878,7 @@ inlineToHtml opts inline =
847 [A.title $ toValue tit | not (null tit)] ++
878 [A.title $ toValue tit | not (null tit)] ++
848 [A.alt $ toValue alternate' | not (null txt)] ++
879 [A.alt $ toValue alternate' | not (null txt)] ++
849 imgAttrsToHtml opts attr
880 imgAttrsToHtml opts attr
850− let tag = if writerHtml5 opts then H5.img else H.img
881+ let tag = if html5 then H5.img else H.img
851 return $ foldl (!) tag attributes
882 return $ foldl (!) tag attributes
852 -- note: null title included, as in Markdown.pl
883 -- note: null title included, as in Markdown.pl
853 (Image attr _ (s,tit)) -> do
884 (Image attr _ (s,tit)) -> do
@@ -880,7 +911,7 @@ inlineToHtml opts inline =
880 (Cite cits il)-> do contents <- inlineListToHtml opts il
911 (Cite cits il)-> do contents <- inlineListToHtml opts il
881 let citationIds = unwords $ map citationId cits
912 let citationIds = unwords $ map citationId cits
882 let result = H.span ! A.class_ "citation" $ contents
913 let result = H.span ! A.class_ "citation" $ contents
883− return $ if writerHtml5 opts
914+ return $ if html5
884 then result ! customAttribute "data-cites" (toValue citationIds)
915 then result ! customAttribute "data-cites" (toValue citationIds)
885 else result
916 else result