@@ -27,69 +27,78 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2727
2828Conversion of 'Pandoc' documents to HTML.
2929-}
30−module Text.Pandoc.Writers.HTML (
31− writeHtml,
32− ) where
30+module Text.Pandoc.Writers.HTML ( writeHtml, writeHtmlString ) where
3331import Text.Pandoc.Definition
3432import Text.Pandoc.Shared
35−import Text.Pandoc.Entities ( escapeSGMLString )
3633import Text.Regex ( mkRegex, matchRegex )
3734import Numeric ( showHex )
3835import Data.Char ( ord, toLower )
3936import Data.List ( isPrefixOf, partition )
40−import Text.PrettyPrint.HughesPJ hiding ( Str )
37+import Text.XHtml.Strict
4138
42−-- | Convert Pandoc document to string in HTML format.
43−writeHtml :: WriterOptions -> Pandoc -> String
44−writeHtml opts (Pandoc (Meta title authors date) blocks) =
45− let titlePrefix = writerTitlePrefix opts in
46− let topTitle = if not (null titlePrefix)
47− then [Str titlePrefix] ++ (if not (null title)
48− then [Str " - "] ++ title
49− else [])
50− else title in
51− let head = if (writerStandalone opts)
52− then htmlHeader opts (Meta topTitle authors date)
53− else empty
54− titleBlocks = if (writerStandalone opts) && (not (null title)) &&
39+-- | Convert Pandoc document to Html string.
40+writeHtmlString :: WriterOptions -> Pandoc -> String
41+writeHtmlString opts =
42+ if writerStandalone opts
43+ then renderHtml . (writeHtml opts)
44+ else renderHtmlFragment . (writeHtml opts)
45+
46+-- | Convert Pandoc document to Html structure.
47+writeHtml :: WriterOptions -> Pandoc -> Html
48+writeHtml opts (Pandoc (Meta tit authors date) blocks) =
49+ let titlePrefix = writerTitlePrefix opts
50+ topTitle = inlineListToHtml opts tit
51+ topTitle' = if not (null titlePrefix)
52+ then stringToHtml titlePrefix +++
53+ if not (null tit)
54+ then '-' +++ topTitle
55+ else noHtml
56+ else topTitle
57+ head = header $ thetitle topTitle' +++
58+ meta ! [httpequiv "Content-Type",
59+ content "text/html; charset=UTF-8"] +++
60+ meta ! [name "generator", content "pandoc"] +++
61+ (toHtmlFromList $
62+ map (\a -> meta ! [name "author", content a]) authors) +++
63+ (if null date
64+ then noHtml
65+ else meta ! [name "date", content date]) +++
66+ primHtml (writerHeader opts)
67+ titleHeader = if (writerStandalone opts) && (not (null tit)) &&
5568 (not (writerS5 opts))
56− then [RawHtml "<h1 class=\"title\">", Plain title,
57− RawHtml "</h1>"]
58− else []
59− foot = if (writerStandalone opts)
60− then text "</body>\n</html>"
61− else empty
62− blocks' = replaceReferenceLinks (titleBlocks ++ blocks)
69+ then h1 ! [theclass "title"] $ topTitle
70+ else noHtml
71+ blocks' = replaceReferenceLinks blocks
6372 (noteBlocks, blocks'') = partition isNoteBlock blocks'
64− before = writerIncludeBefore opts
65− after = writerIncludeAfter opts
66− body = (if null before then empty else text before) $$
67− vcat (map (blockToHtml opts) blocks'') $$
68− footnoteSection opts noteBlocks $$
69− (if null after then empty else text after) in
70− render $ head $$ body $$ foot $$ text ""
73+ before = primHtml $ writerIncludeBefore opts
74+ after = primHtml $ writerIncludeAfter opts
75+ thebody = before +++ titleHeader +++
76+ toHtmlFromList (map (blockToHtml opts) blocks'') +++
77+ footnoteSection opts noteBlocks +++ after
78+ in if writerStandalone opts
79+ then head +++ (body thebody)
80+ else thebody
7181
7282-- | Convert list of Note blocks to a footnote <div>.
7383-- Assumes notes are sorted.
74−footnoteSection :: WriterOptions -> [Block] -> Doc
84+footnoteSection :: WriterOptions -> [Block] -> Html
7585footnoteSection opts notes =
7686 if null notes
77− then empty
78− else inTags True "div" [("class","footnotes")] $
79− selfClosingTag "hr" [] $$ (inTagsIndented "ol"
80− (vcat $ map (blockToHtml opts) notes))
87+ then noHtml
88+ else thediv ! [theclass "footnotes"] $
89+ hr +++ (olist $ toHtmlFromList $ map (blockToHtml opts) notes)
8190
8291-- | Obfuscate a "mailto:" link using Javascript.
83−obfuscateLink :: WriterOptions -> [Inline] -> String -> Doc
92+obfuscateLink :: WriterOptions -> [Inline] -> String -> Html
8493obfuscateLink opts txt src =
8594 let emailRegex = mkRegex "mailto:*([^@]*)@(.*)"
86− text' = render $ inlineListToHtml opts txt
87− src' = map toLower src in
95+ text' = show $ inlineListToHtml opts txt
96+ src' = map toLower src in
8897 case (matchRegex emailRegex src') of
8998 (Just [name, domain]) ->
90− let domain' = substitute "." " dot " domain
91− at' = obfuscateChar '@' in
92− let linkText = if src' == ("mailto:" ++ text')
99+ let domain' = substitute "." " dot " domain
100+ at' = obfuscateChar '@'
101+ linkText = if src' == ("mailto:" ++ text')
93102 then "e"
94103 else "'" ++ text' ++ "'"
95104 altText = if src' == ("mailto:" ++ text')
@@ -97,16 +106,16 @@ obfuscateLink opts txt src =
97106 else text' ++ " (" ++ name ++ " at " ++
98107 domain' ++ ")" in
99108 if writerStrictMarkdown opts
100− then inTags False "a" [("href", obfuscateString src')] $
101− text $ obfuscateString text'
102− else inTags False "script" [("type", "text/javascript")]
103− (text ("\n<!--\nh='" ++
109+ then anchor ! [href $ obfuscateString src'] $
110+ stringToHtml $ obfuscateString text'
111+ else (script ! [thetype "text/javascript"] $
112+ primHtml ("\n<!--\nh='" ++
104113 obfuscateString domain ++ "';a='" ++ at' ++ "';n='" ++
105114 obfuscateString name ++ "';e=n+a+h;\n" ++
106115 "document.write('<a h'+'ref'+'=\"ma'+'ilto'+':'+e+'\">'+" ++
107− linkText ++ "+'<\\/'+'a'+'>');\n// -->\n")) <>
108− inTagsSimple "noscript" (text (obfuscateString altText))
109− _ -> inTags False "a" [("href", src)] (text text') -- malformed email
116+ linkText ++ "+'<\\/'+'a'+'>');\n// -->\n")) +++
117+ noscript (stringToHtml $ obfuscateString altText)
118+ _ -> anchor ! [href src] $ inlineListToHtml opts txt -- malformed email
110119
111120-- | Obfuscate character as entity.
112121obfuscateChar :: Char -> String
@@ -119,32 +128,12 @@ obfuscateChar char =
119128obfuscateString :: String -> String
120129obfuscateString = concatMap obfuscateChar
121130
122−-- | Return an HTML header with appropriate bibliographic information.
123−htmlHeader :: WriterOptions -> Meta -> Doc
124−htmlHeader opts (Meta title authors date) =
125− let titletext = inTagsSimple "title" (wrap opts title)
126− authortext = if (null authors)
127− then empty
128− else selfClosingTag "meta" [("name", "author"),
129− ("content",
130− joinWithSep ", " (map escapeSGMLString authors))]
131− datetext = if (date == "")
132− then empty
133− else selfClosingTag "meta" [("name", "date"),
134− ("content", escapeSGMLString date)] in
135− text (writerHeader opts) $$ authortext $$ datetext $$ titletext $$
136− text "</head>\n<body>"
137−
138−-- | Take list of inline elements and return wrapped doc.
139−wrap :: WriterOptions -> [Inline] -> Doc
140−wrap opts lst = fsep $ map (inlineListToHtml opts) (splitBy Space lst)
141−
142131-- | Convert Pandoc block element to HTML.
143−blockToHtml :: WriterOptions -> Block -> Doc
144−blockToHtml opts Blank = text ""
145−blockToHtml opts Null = empty
146−blockToHtml opts (Plain lst) = wrap opts lst
147−blockToHtml opts (Para lst) = inTagsIndented "p" $ wrap opts lst
132+blockToHtml :: WriterOptions -> Block -> Html
133+blockToHtml opts Blank = noHtml
134+blockToHtml opts Null = noHtml
135+blockToHtml opts (Plain lst) = inlineListToHtml opts lst
136+blockToHtml opts (Para lst) = paragraph $ inlineListToHtml opts lst
148137blockToHtml opts (BlockQuote blocks) =
149138 if (writerS5 opts)
150139 then -- in S5, treat list in blockquote specially
@@ -152,120 +141,120 @@ blockToHtml opts (BlockQuote blocks) =
152141 -- otherwise incremental
153142 let inc = not (writerIncremental opts) in
154143 case blocks of
155− [BulletList lst] -> blockToHtml (opts {writerIncremental =
144+ [BulletList lst] -> blockToHtml (opts {writerIncremental =
156145 inc}) (BulletList lst)
157146 [OrderedList lst] -> blockToHtml (opts {writerIncremental =
158147 inc}) (OrderedList lst)
159− otherwise -> inTagsIndented "blockquote" $
160− vcat $ map (blockToHtml opts) blocks
161− else inTagsIndented "blockquote" $ vcat $ map (blockToHtml opts) blocks
148+ otherwise -> blockquote $ toHtmlFromList $
149+ map (blockToHtml opts) blocks
150+ else blockquote $ toHtmlFromList $ map (blockToHtml opts) blocks
162151blockToHtml opts (Note ref lst) =
163− let contents = (vcat $ map (blockToHtml opts) lst) in
164− inTags True "li" [("id", "fn" ++ ref)] $
165− contents <> inTags False "a" [("href", "#fnref" ++ ref),
166− ("class", "footnoteBacklink"),
167− ("title", "Jump back to footnote " ++ ref)]
168− (text "↩")
169−blockToHtml opts (Key _ _) = empty
152+ let contents = toHtmlFromList $ map (blockToHtml opts) lst
153+ backlink = anchor ! [href ("#fnref" ++ ref), theclass "footnoteBacklink",
154+ title ("Jump back to footnote " ++ ref)] $
155+ (primHtmlChar "#8617") in
156+ li ! [identifier ("fn" ++ ref)] $ contents +++ backlink
157+blockToHtml opts (Key _ _) = noHtml
170158blockToHtml opts (CodeBlock str) =
171− text "<pre><code>" <> text (escapeSGMLString str) <> text "\n</code></pre>"
172−blockToHtml opts (RawHtml str) = text str
159+ pre $ thecode $ stringToHtml (str ++ "\n") -- the final \n for consistency with Markdown.pl
160+blockToHtml opts (RawHtml str) = primHtml str
173161blockToHtml opts (BulletList lst) =
174− let attribs = if (writerIncremental opts)
175− then [("class","incremental")]
162+ let attribs = if writerIncremental opts
163+ then [theclass "incremental"]
176164 else [] in
177− inTags True "ul" attribs $ vcat $ map (listItemToHtml opts) lst
165+ ulist ! attribs $ toHtmlFromList $ map (listItemToHtml opts) lst
178166blockToHtml opts (OrderedList lst) =
179− let attribs = if (writerIncremental opts)
180− then [("class","incremental")]
167+ let attribs = if writerIncremental opts
168+ then [theclass "incremental"]
181169 else [] in
182− inTags True "ol" attribs $ vcat $ map (listItemToHtml opts) lst
183−blockToHtml opts HorizontalRule = selfClosingTag "hr" []
170+ olist ! attribs $ toHtmlFromList $ map (listItemToHtml opts) lst
171+blockToHtml opts HorizontalRule = hr
184172blockToHtml opts (Header level lst) =
185− let contents = wrap opts lst in
186− if ((level > 0) && (level <= 6))
187− then inTagsSimple ("h" ++ show level) contents
188− else inTagsSimple "p" contents
189−blockToHtml opts (Table caption aligns widths headers rows) =
173+ let contents = inlineListToHtml opts lst in
174+ case level of
175+ 1 -> h1 contents
176+ 2 -> h2 contents
177+ 3 -> h3 contents
178+ 4 -> h4 contents
179+ 5 -> h5 contents
180+ 6 -> h6 contents
181+ _ -> paragraph contents
182+blockToHtml opts (Table capt aligns widths headers rows) =
190183 let alignStrings = map alignmentToString aligns
191− captionDoc = if null caption
192− then empty
193− else inTagsSimple "caption"
194− (inlineListToHtml opts caption) in
195− inTagsIndented "table" $ captionDoc $$
196− (colHeadsToHtml opts alignStrings widths headers) $$
197− (vcat $ map (tableRowToHtml opts alignStrings) rows)
184+ captionDoc = if null capt
185+ then noHtml
186+ else caption $ inlineListToHtml opts capt in
187+ table $ captionDoc +++
188+ (colHeadsToHtml opts alignStrings widths headers) +++
189+ (toHtmlFromList $ map (tableRowToHtml opts alignStrings) rows)
198190
199191colHeadsToHtml opts alignStrings widths headers =
200192 let heads = zipWith3
201− (\align width item -> tableItemToHtml opts "th" align width item)
193+ (\align width item -> tableItemToHtml opts th align width item)
202194 alignStrings widths headers in
203− inTagsIndented "tr" $ vcat heads
195+ tr $ toHtmlFromList heads
204196
205197alignmentToString alignment = case alignment of
206− AlignLeft -> "left"
207− AlignRight -> "right"
208− AlignCenter -> "center"
198+ AlignLeft -> "left"
199+ AlignRight -> "right"
200+ AlignCenter -> "center"
209201 AlignDefault -> "left"
210−
211202tableRowToHtml opts aligns cols =
212− inTagsIndented "tr" $ vcat $ zipWith3 (tableItemToHtml opts "td") aligns (repeat 0) cols
203+ tr $ toHtmlFromList $ zipWith3 (tableItemToHtml opts td) aligns (repeat 0) cols
213204
214−tableItemToHtml opts tag align width item =
215− let attrib = [("align", align)] ++
205+tableItemToHtml opts tag align' width item =
206+ let attrib = [align align'] ++
216207 if (width /= 0)
217− then [("style", "{width: " ++
218− show (truncate (100*width)) ++ "%;}")]
208+ then [thestyle ("{width: " ++ show (truncate (100*width)) ++ "%;}")]
219209 else [] in
220− inTags False tag attrib $ vcat $ map (blockToHtml opts) item
210+ tag ! attrib $ toHtmlFromList $ map (blockToHtml opts) item
221211
222−listItemToHtml :: WriterOptions -> [Block] -> Doc
212+listItemToHtml :: WriterOptions -> [Block] -> Html
223213listItemToHtml opts list =
224− inTagsSimple "li" $ vcat $ map (blockToHtml opts) list
214+ li $ toHtmlFromList $ map (blockToHtml opts) list
225215
226216-- | Convert list of Pandoc inline elements to HTML.
227−inlineListToHtml :: WriterOptions -> [Inline] -> Doc
228−inlineListToHtml opts lst = hcat (map (inlineToHtml opts) lst)
217+inlineListToHtml :: WriterOptions -> [Inline] -> Html
218+inlineListToHtml opts lst = toHtmlFromList $ map (inlineToHtml opts) lst
229219
230220-- | Convert Pandoc inline element to HTML.
231−inlineToHtml :: WriterOptions -> Inline -> Doc
221+inlineToHtml :: WriterOptions -> Inline -> Html
232222inlineToHtml opts (Emph lst) =
233− inTagsSimple "em" (inlineListToHtml opts lst)
223+ emphasize $ inlineListToHtml opts lst
234224inlineToHtml opts (Strong lst) =
235− inTagsSimple "strong" (inlineListToHtml opts lst)
225+ strong $ inlineListToHtml opts lst
236226inlineToHtml opts (Code str) =
237− inTagsSimple "code" $ text (escapeSGMLString str)
227+ thecode $ stringToHtml $ str
238228inlineToHtml opts (Quoted SingleQuote lst) =
239− text "‘" <> (inlineListToHtml opts lst) <> text "’"
229+ primHtmlChar "lsquo" +++ inlineListToHtml opts lst +++ primHtmlChar "rsquo"
240230inlineToHtml opts (Quoted DoubleQuote lst) =
241− text "“" <> (inlineListToHtml opts lst) <> text "”"
242−inlineToHtml opts EmDash = text "—"
243−inlineToHtml opts EnDash = text "–"
244−inlineToHtml opts Ellipses = text "…"
245−inlineToHtml opts Apostrophe = text "’"
246−inlineToHtml opts (Str str) = text $ escapeSGMLString str
247−inlineToHtml opts (TeX str) = text $ escapeSGMLString str
248−inlineToHtml opts (HtmlInline str) = text str
249−inlineToHtml opts (LineBreak) = selfClosingTag "br" []
250−inlineToHtml opts Space = space
251−inlineToHtml opts (Link txt (Src src title)) =
231+ primHtmlChar "ldquo" +++ inlineListToHtml opts lst +++ primHtmlChar "rdquo"
232+inlineToHtml opts EmDash = primHtmlChar "mdash"
233+inlineToHtml opts EnDash = primHtmlChar "ndash"
234+inlineToHtml opts Ellipses = primHtmlChar "hellip"
235+inlineToHtml opts Apostrophe = primHtmlChar "rsquo"
236+inlineToHtml opts (Str str) = stringToHtml str
237+inlineToHtml opts (TeX str) = stringToHtml str
238+inlineToHtml opts (HtmlInline str) = primHtml str
239+inlineToHtml opts (LineBreak) = br
240+inlineToHtml opts Space = stringToHtml " "
241+inlineToHtml opts (Link txt (Src src tit)) =
252242 if (isPrefixOf "mailto:" src)
253243 then obfuscateLink opts txt src
254− else inTags False "a" ([("href", src)] ++
255− if null title then [] else [("title", title)])
256− (inlineListToHtml opts txt)
244+ else anchor ! ([href src] ++ if null tit then [] else [title tit]) $
245+ inlineListToHtml opts txt
257246inlineToHtml opts (Link txt (Ref ref)) =
258− char '[' <> (inlineListToHtml opts txt) <> text "][" <>
259− (inlineListToHtml opts ref) <> char ']'
247+ '[' +++ (inlineListToHtml opts txt) +++
248+ ']' +++ '[' +++ (inlineListToHtml opts ref) +++
249+ ']'
260250 -- this is what markdown does, for better or worse
261−inlineToHtml opts (Image alt (Src source title)) =
262− let alternate = render $ inlineListToHtml opts alt in
263− selfClosingTag "img" $ [("src", source)] ++
264− (if null alternate then [] else [("alt", alternate)]) ++
265− [("title", title)] -- note: null title is included, as in Markdown.pl
251+inlineToHtml opts (Image alttext (Src source tit)) =
252+ let alternate = renderHtml $ inlineListToHtml opts alttext in
253+ image ! ([src source, title tit] ++ if null alttext then [] else [alt alternate])
254+ -- note: null title is included, as in Markdown.pl
266255inlineToHtml opts (Image alternate (Ref ref)) =
267− text "![" <> (inlineListToHtml opts alternate) <> text "][" <>
268− (inlineListToHtml opts ref) <> char ']'
256+ '!' +++ inlineToHtml opts (Link alternate (Ref ref))
269257inlineToHtml opts (NoteRef ref) =
270− inTags False "sup" [("class", "footnoteRef"), ("id", "fnref" ++ ref)]
271− (inTags False "a" [("href", "#fn" ++ ref)] $ text ref)
258+ anchor ! [href ("#fn" ++ ref), theclass "footnoteRef", identifier ("fnref" ++ ref)] $
259+ sup (stringToHtml ref)
260+