@@ -67,6 +67,14 @@ renderFragment opts = if writerWrapText opts
6767stringToHtml :: String -> Html
6868stringToHtml = primHtml . escapeStringForXML
6969
70+-- Note: href and src, unmodified, incorrectly escape high
71+-- characters in URIs using entities. So we use these replacements:
72+href' :: String -> HtmlAttr
73+href' = href . stringToURI
74+
75+src' :: String -> HtmlAttr
76+src' = src . stringToURI
77+
7078-- | Convert Pandoc document to Html string.
7179writeHtmlString :: WriterOptions -> Pandoc -> String
7280writeHtmlString opts d =
@@ -112,13 +120,13 @@ pandocToHtml opts (Pandoc (Meta title' authors' date') blocks) = do
112120 then case writerHTMLMathMethod opts of
113121 LaTeXMathML (Just url) ->
114122 script !
115− [src url, thetype "text/javascript"] $ noHtml
123+ [src' url, thetype "text/javascript"] $ noHtml
116124 MathML (Just url) ->
117125 script !
118− [src url, thetype "text/javascript"] $ noHtml
126+ [src' url, thetype "text/javascript"] $ noHtml
119127 JsMath (Just url) ->
120128 script !
121− [src url, thetype "text/javascript"] $ noHtml
129+ [src' url, thetype "text/javascript"] $ noHtml
122130 _ -> case lookup "mathml-script" (writerVariables opts) of
123131 Just s ->
124132 script ! [thetype "text/javascript"] <<
@@ -188,7 +196,7 @@ elementToListItem opts (Sec _ num id' headerText subsecs) = do
188196 let subList = if null subHeads
189197 then noHtml
190198 else unordList subHeads
191− return $ Just $ (anchor ! [href ("#" ++ writerIdentifierPrefix opts ++ id')] $ txt) +++ subList
199+ return $ Just $ (anchor ! [href' ("#" ++ writerIdentifierPrefix opts ++ id')] $ txt) +++ subList
192200
193201-- | Convert an Element to Html.
194202elementToHtml :: WriterOptions -> Element -> State WriterState Html
@@ -222,7 +230,7 @@ parseMailto _ = Nothing
222230-- | Obfuscate a "mailto:" link.
223231obfuscateLink :: WriterOptions -> String -> String -> Html
224232obfuscateLink opts txt s | writerEmailObfuscation opts == NoObfuscation =
225− anchor ! [href s] << txt
233+ anchor ! [href' s] << txt
226234obfuscateLink opts txt s =
227235 let meth = writerEmailObfuscation opts
228236 s' = map toLower s
@@ -249,7 +257,7 @@ obfuscateLink opts txt s =
249257 linkText ++ "+'<\\/'+'a'+'>');\n// -->\n")) +++
250258 noscript (primHtml $ obfuscateString altText)
251259 _ -> error $ "Unknown obfuscation method: " ++ show meth
252− _ -> anchor ! [href s] $ stringToHtml txt -- malformed email
260+ _ -> anchor ! [href' s] $ stringToHtml txt -- malformed email
253261
254262-- | Obfuscate character as entity.
255263obfuscateChar :: Char -> String
@@ -312,7 +320,7 @@ blockToHtml opts (Header level lst) = do
312320 stringToHtml " " +++ contents
313321 else contents
314322 let contents'' = if writerTableOfContents opts
315− then anchor ! [href $ "#" ++ writerIdentifierPrefix opts ++ "TOC"] $ contents'
323+ then anchor ! [href' $ "#" ++ writerIdentifierPrefix opts ++ "TOC"] $ contents'
316324 else contents'
317325 return $ case level of
318326 1 -> h1 contents''
@@ -452,7 +460,7 @@ inlineToHtml opts inline =
452460 then thespan ! [theclass "math"] $ primHtml str
453461 else thediv ! [theclass "math"] $ primHtml str
454462 MimeTeX url ->
455− return $ image ! [src (url ++ "?" ++ str),
463+ return $ image ! [src' (url ++ "?" ++ str),
456464 alt str, title str]
457465 GladTeX ->
458466 return $ primHtml $ "<EQ>" ++ str ++ "</EQ>"
@@ -484,13 +492,13 @@ inlineToHtml opts inline =
484492 return $ obfuscateLink opts (show linkText) s
485493 (Link txt (s,tit)) -> do
486494 linkText <- inlineListToHtml opts txt
487− return $ anchor ! ([href s] ++
495+ return $ anchor ! ([href' s] ++
488496 if null tit then [] else [title tit]) $
489497 linkText
490498 (Image txt (s,tit)) -> do
491499 alternate <- inlineListToHtml opts txt
492500 let alternate' = renderFragment opts alternate
493− let attributes = [src s] ++
501+ let attributes = [src' s] ++
494502 (if null tit
495503 then []
496504 else [title tit]) ++
@@ -508,7 +516,7 @@ inlineToHtml opts inline =
508516 -- push contents onto front of notes
509517 put $ st {stNotes = (htmlContents:notes)}
510518 return $ sup <<
511− anchor ! [href ("#" ++ writerIdentifierPrefix opts ++ "fn" ++ ref),
519+ anchor ! [href' ("#" ++ writerIdentifierPrefix opts ++ "fn" ++ ref),
512520 theclass "footnoteRef",
513521 prefixedId opts ("fnref" ++ ref)] << ref
514522 (Cite _ il) -> inlineListToHtml opts il
@@ -517,7 +525,7 @@ blockListToNote :: WriterOptions -> String -> [Block] -> State WriterState Html
517525blockListToNote opts ref blocks =
518526 -- If last block is Para or Plain, include the backlink at the end of
519527 -- that block. Otherwise, insert a new Plain block with the backlink.
520− let backlink = [HtmlInline $ " <a href=\"#" ++ writerIdentifierPrefix opts ++ "fnref" ++ ref ++
528+ let backlink = [HtmlInline $ " <a href=\"#" ++ stringToURI (writerIdentifierPrefix opts ++ "fnref" ++ ref) ++
521529 "\" class=\"footnoteBackLink\"" ++
522530 " title=\"Jump back to footnote " ++ ref ++ "\">↩</a>"]
523531 blocks' = if null blocks