git.iko.soy/typeable/pandoc
on master help sign in
Refactored SGML escaping functions and "in tag" functions to
030d94efiddlosopher
debian/changelog+2
@@ -186,6 +186,8 @@ pandoc (0.3) unstable; urgency=low
186 + Renamed 'Text/Pandoc/HtmlEntities' module to
186 + Renamed 'Text/Pandoc/HtmlEntities' module to
187 'Text/Pandoc/Entities'. Also changed function names so as
187 'Text/Pandoc/Entities'. Also changed function names so as
188 not to be HTML-specific.
188 not to be HTML-specific.
189+ + Refactored SGML string escaping functions from HTML and Docbook
190+ writers into Text/Pandoc/Shared. (escapeSGML, stringToSGML)
189 + Removed 'BlockQuoteContext' from ParserContext, as it isn't
191 + Removed 'BlockQuoteContext' from ParserContext, as it isn't
190 used anywhere.
192 used anywhere.
191 + Removed splitBySpace and replaced it with a general, polymorphic
193 + Removed splitBySpace and replaced it with a general, polymorphic
src/Text/Pandoc/Shared.hs+95−3
@@ -28,6 +28,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28Utility functions and definitions used by the various Pandoc modules.
28Utility functions and definitions used by the various Pandoc modules.
29-}
29-}
30module Text.Pandoc.Shared (
30module Text.Pandoc.Shared (
31+ -- * List processing
32+ splitBy,
31 -- * Text processing
33 -- * Text processing
32 gsub,
34 gsub,
33 joinWithSep,
35 joinWithSep,
@@ -52,7 +54,6 @@ module Text.Pandoc.Shared (
52 -- * Pandoc block list processing
54 -- * Pandoc block list processing
53 consolidateList,
55 consolidateList,
54 isNoteBlock,
56 isNoteBlock,
55− splitBy,
56 normalizeSpaces,
57 normalizeSpaces,
57 compactify,
58 compactify,
58 generateReference,
59 generateReference,
@@ -62,12 +63,21 @@ module Text.Pandoc.Shared (
62 lookupKeySrc,
63 lookupKeySrc,
63 refsMatch,
64 refsMatch,
64 replaceReferenceLinks,
65 replaceReferenceLinks,
65− replaceRefLinksBlockList
66+ replaceRefLinksBlockList,
67+ -- * SGML
68+ escapeSGML,
69+ stringToSGML,
70+ inTags,
71+ selfClosingTag,
72+ inTagsSimple,
73+ inTagsIndented
66 ) where
74 ) where
67import Text.Pandoc.Definition
75import Text.Pandoc.Definition
68import Text.ParserCombinators.Parsec
76import Text.ParserCombinators.Parsec
69−import Text.Pandoc.Entities ( decodeEntities )
77+import Text.Pandoc.Entities ( decodeEntities, encodeEntities )
70import Text.Regex ( matchRegexAll, mkRegex, subRegex, Regex )
78import Text.Regex ( matchRegexAll, mkRegex, subRegex, Regex )
79+import Text.PrettyPrint.HughesPJ as PP ( text, char, (<>), ($$), nest, Doc,
80+ isEmpty )
71import Char ( toLower )
81import Char ( toLower )
72import List ( find, groupBy )
82import List ( find, groupBy )
73
83
@@ -507,3 +517,85 @@ replaceRefLinksInline keytable (Emph lst) =
507replaceRefLinksInline keytable (Strong lst) =
517replaceRefLinksInline keytable (Strong lst) =
508 Strong (map (replaceRefLinksInline keytable) lst)
518 Strong (map (replaceRefLinksInline keytable) lst)
509replaceRefLinksInline keytable other = other
519replaceRefLinksInline keytable other = other
520+
521+-- | Escape string, preserving character entities and quote, and adding
522+-- smart typography if specified.
523+stringToSGML :: WriterOptions -> String -> String
524+stringToSGML options =
525+ let escapeDoubleQuotes =
526+ gsub "(\"|&quot;)" "&rdquo;" . -- rest are right quotes
527+ gsub "(\"|&quot;)(&r[sd]quo;)" "&rdquo;\\2" .
528+ -- never left quo before right quo
529+ gsub "(&l[sd]quo;)(\"|&quot;)" "\\2&ldquo;" .
530+ -- never right quo after left quo
531+ gsub "([ \t])(\"|&quot;)" "\\1&ldquo;" .
532+ -- never right quo after space
533+ gsub "(\"|&quot;)([^,.;:!?^) \t-])" "&ldquo;\\2" . -- "word left
534+ gsub "(\"|&quot;)('|`|&lsquo;)" "&rdquo;&rsquo;" .
535+ -- right if it got through last filter
536+ gsub "(\"|&quot;)('|`|&lsquo;)([^,.;:!?^) \t-])" "&ldquo;&lsquo;\\3" .
537+ -- "'word left
538+ gsub "``" "&ldquo;" .
539+ gsub "''" "&rdquo;"
540+ escapeSingleQuotes =
541+ gsub "'" "&rsquo;" . -- otherwise right
542+ gsub "'(&r[sd]quo;)" "&rsquo;\\1" . -- never left quo before right quo
543+ gsub "(&l[sd]quo;)'" "\\1&lsquo;" . -- never right quo after left quo
544+ gsub "([ \t])'" "\\1&lsquo;" . -- never right quo after space
545+ gsub "`" "&lsquo;" . -- ` is left
546+ gsub "([^,.;:!?^) \t-])'" "\\1&rsquo;" . -- word' right
547+ gsub "^('|`)([^,.;:!?^) \t-])" "&lsquo;\\2" . -- 'word left
548+ gsub "('|`)(\"|&quot;|&ldquo;|``)" "&lsquo;&ldquo;" . -- '"word left
549+ gsub "([^,.;:!?^) \t-])'(s|S)" "\\1&rsquo;\\2" . -- possessive
550+ gsub "([[:space:]])'([^,.;:!?^) \t-])" "\\1&lsquo;\\2" . -- 'word left
551+ gsub "'([0-9][0-9](s|S))" "&rsquo;\\1" -- '80s - decade abbrevs.
552+ escapeDashes =
553+ gsub " ?-- ?" "&mdash;" .
554+ gsub " ?--- ?" "&mdash;" .
555+ gsub "([0-9])--?([0-9])" "\\1&ndash;\\2"
556+ escapeEllipses = gsub "\\.\\.\\.|\\. \\. \\." "&hellip;"
557+ smartFilter = escapeSingleQuotes . escapeDoubleQuotes . escapeDashes .
558+ escapeEllipses in
559+ encodeEntities . (if (writerSmart options) then smartFilter else id) .
560+ (escapePreservingRegex escapeSGML (mkRegex "&[[:alnum:]]*;"))
561+
562+-- | Escape string as needed for HTML. Entity references are not preserved.
563+escapeSGML :: String -> String
564+escapeSGML [] = []
565+escapeSGML (x:xs) = case x of
566+ '&' -> "&amp;" ++ escapeSGML xs
567+ '<' -> "&lt;" ++ escapeSGML xs
568+ '>' -> "&gt;" ++ escapeSGML xs
569+ '"' -> "&quot;" ++ escapeSGML xs
570+ _ -> x:(escapeSGML xs)
571+
572+-- | Return a text object with a string of formatted SGML attributes.
573+attributeList :: WriterOptions -> [(String, String)] -> Doc
574+attributeList options =
575+ text . concatMap (\(a, b) -> " " ++ stringToSGML options a ++ "=\"" ++
576+ stringToSGML options b ++ "\"")
577+
578+-- | Put the supplied contents between start and end tags of tagType,
579+-- with specified attributes and (if specified) indentation.
580+inTags:: Bool -> WriterOptions -> String -> [(String, String)] -> Doc -> Doc
581+inTags isIndented options tagType attribs contents =
582+ let openTag = PP.char '<' <> text tagType <> attributeList options attribs <>
583+ PP.char '>'
584+ closeTag = text "</" <> text tagType <> PP.char '>' in
585+ if isIndented
586+ then openTag $$ nest 2 contents $$ closeTag
587+ else openTag <> contents <> closeTag
588+
589+-- | Return a self-closing tag of tagType with specified attributes
590+selfClosingTag :: WriterOptions -> String -> [(String, String)] -> Doc
591+selfClosingTag options tagType attribs =
592+ PP.char '<' <> text tagType <> attributeList options attribs <> text " />"
593+
594+-- | Put the supplied contents between start and end tags of tagType.
595+inTagsSimple :: WriterOptions -> String -> Doc -> Doc
596+inTagsSimple options tagType = inTags False options tagType []
597+
598+-- | Put the supplied contents in indented block btw start and end tags.
599+inTagsIndented :: WriterOptions -> String -> Doc -> Doc
600+inTagsIndented options tagType = inTags True options tagType []
601+
src/Text/Pandoc/Writers/Docbook.hs+81−115
@@ -32,9 +32,7 @@ module Text.Pandoc.Writers.Docbook (
32 ) where
32 ) where
33import Text.Pandoc.Definition
33import Text.Pandoc.Definition
34import Text.Pandoc.Shared
34import Text.Pandoc.Shared
35−import Text.Pandoc.Writers.HTML ( stringToSmartHtml, stringToHtml )
36import Text.Pandoc.Entities ( encodeEntities )
35import Text.Pandoc.Entities ( encodeEntities )
37−import Text.Html ( stringToHtmlString )
38import Text.Regex ( mkRegex, matchRegex )
36import Text.Regex ( mkRegex, matchRegex )
39import Data.Char ( toLower, ord )
37import Data.Char ( toLower, ord )
40import Data.List ( isPrefixOf, partition )
38import Data.List ( isPrefixOf, partition )
@@ -54,19 +52,21 @@ hierarchicalize :: [Block] -> [Element]
54hierarchicalize [] = []
52hierarchicalize [] = []
55hierarchicalize (block:rest) =
53hierarchicalize (block:rest) =
56 case block of
54 case block of
57− (Header level title) -> let (thisSection, rest') = break (headerAtLeast level) rest in
55+ (Header level title) -> let (thisSection, rest') = break (headerAtLeast
56+ level) rest in
58− (Sec title (hierarchicalize thisSection)):(hierarchicalize rest')
57+ (Sec title (hierarchicalize thisSection)):
58+ (hierarchicalize rest')
59 x -> (Blk x):(hierarchicalize rest)
59 x -> (Blk x):(hierarchicalize rest)
60
60
61-- | Convert list of authors to a docbook <author> section
61-- | Convert list of authors to a docbook <author> section
62authorToDocbook :: WriterOptions -> [Char] -> Doc
62authorToDocbook :: WriterOptions -> [Char] -> Doc
63−authorToDocbook options name = indentedInTags "author" $
63+authorToDocbook opts name = inTagsIndented opts "author" $
64 if ',' `elem` name
64 if ',' `elem` name
65 then -- last name first
65 then -- last name first
66 let (lastname, rest) = break (==',') name
66 let (lastname, rest) = break (==',') name
67 firstname = removeLeadingSpace rest in
67 firstname = removeLeadingSpace rest in
68− inTags "firstname" (text $ stringToXML options firstname) <>
68+ inTagsSimple opts "firstname" (text $ stringToSGML opts firstname) <>
69− inTags "surname" (text $ stringToXML options lastname)
69+ inTagsSimple opts "surname" (text $ stringToSGML opts lastname)
70 else -- last name last
70 else -- last name last
71 let namewords = words name
71 let namewords = words name
72 lengthname = length namewords
72 lengthname = length namewords
@@ -74,101 +74,82 @@ authorToDocbook options name = indentedInTags "author" $
74 0 -> ("","")
74 0 -> ("","")
75 1 -> ("", name)
75 1 -> ("", name)
76 n -> (joinWithSep " " (take (n-1) namewords), last namewords) in
76 n -> (joinWithSep " " (take (n-1) namewords), last namewords) in
77− inTags "firstname" (text $ stringToXML options firstname) $$
77+ inTagsSimple opts "firstname" (text $ stringToSGML opts firstname) $$
78− inTags "surname" (text $ stringToXML options lastname)
78+ inTagsSimple opts "surname" (text $ stringToSGML opts lastname)
79
79
80-- | Convert Pandoc document to string in Docbook format.
80-- | Convert Pandoc document to string in Docbook format.
81writeDocbook :: WriterOptions -> Pandoc -> String
81writeDocbook :: WriterOptions -> Pandoc -> String
82−writeDocbook options (Pandoc (Meta title authors date) blocks) =
82+writeDocbook opts (Pandoc (Meta title authors date) blocks) =
83− let head = if (writerStandalone options)
83+ let head = if (writerStandalone opts)
84− then text (writerHeader options)
84+ then text (writerHeader opts)
85 else empty
85 else empty
86− meta = if (writerStandalone options)
86+ meta = if (writerStandalone opts)
87− then indentedInTags "articleinfo" $
87+ then inTagsIndented opts "articleinfo" $
88− (inTags "title" (inlinesToDocbook options title)) $$
88+ (inTagsSimple opts "title" (inlinesToDocbook opts title)) $$
89− (vcat (map (authorToDocbook options) authors)) $$
89+ (vcat (map (authorToDocbook opts) authors)) $$
90− (inTags "date" (text date))
90+ (inTagsSimple opts "date" (text $ stringToSGML opts date))
91 else empty
91 else empty
92 blocks' = replaceReferenceLinks blocks
92 blocks' = replaceReferenceLinks blocks
93 (noteBlocks, blocks'') = partition isNoteBlock blocks'
93 (noteBlocks, blocks'') = partition isNoteBlock blocks'
94− options' = options {writerNotes = noteBlocks}
94+ opts' = opts {writerNotes = noteBlocks}
95 elements = hierarchicalize blocks''
95 elements = hierarchicalize blocks''
96− body = text (writerIncludeBefore options') <>
96+ body = text (writerIncludeBefore opts') <>
97− vcat (map (elementToDocbook options') elements) $$
97+ vcat (map (elementToDocbook opts') elements) $$
98− text (writerIncludeAfter options')
98+ text (writerIncludeAfter opts')
99− body' = if writerStandalone options'
99+ body' = if writerStandalone opts'
100− then indentedInTags "article" (meta $$ body)
100+ then inTagsIndented opts "article" (meta $$ body)
101 else body in
101 else body in
102 render $ head $$ body' <> text "\n"
102 render $ head $$ body' <> text "\n"
103
103
104−-- | Put the supplied contents between start and end tags of tagType,
105−-- with specified attributes.
106−inTagsWithAttrib :: String -> [(String, String)] -> Doc -> Doc
107−inTagsWithAttrib tagType attribs contents = text ("<" ++ tagType ++
108− (concatMap (\(a, b) -> " " ++ attributeStringToXML a ++
109− "=\"" ++ attributeStringToXML b ++ "\"") attribs)) <>
110− if isEmpty contents
111− then text " />" -- self-closing tag
112− else text ">" <> contents <> text ("</" ++ tagType ++ ">")
113−
114−-- | Put the supplied contents between start and end tags of tagType.
115−inTags :: String -> Doc -> Doc
116−inTags tagType contents = inTagsWithAttrib tagType [] contents
117−
118−-- | Put the supplied contents in indented block btw start and end tags.
119−indentedInTags :: [Char] -> Doc -> Doc
120−indentedInTags tagType contents = text ("<" ++ tagType ++ ">") $$
121− nest 2 contents $$ text ("</" ++ tagType ++ ">")
122−
123-- | Convert an Element to Docbook.
104-- | Convert an Element to Docbook.
124elementToDocbook :: WriterOptions -> Element -> Doc
105elementToDocbook :: WriterOptions -> Element -> Doc
125−elementToDocbook options (Blk block) = blockToDocbook options block
106+elementToDocbook opts (Blk block) = blockToDocbook opts block
126−elementToDocbook options (Sec title elements) =
107+elementToDocbook opts (Sec title elements) =
127 -- Docbook doesn't allow sections with no content, so insert some if needed
108 -- Docbook doesn't allow sections with no content, so insert some if needed
128 let elements' = if null elements
109 let elements' = if null elements
129 then [Blk (Para [])]
110 then [Blk (Para [])]
130 else elements in
111 else elements in
131− indentedInTags "section" $
112+ inTagsIndented opts "section" $
132− inTags "title" (wrap options title) $$
113+ inTagsSimple opts "title" (wrap opts title) $$
133− vcat (map (elementToDocbook options) elements')
114+ vcat (map (elementToDocbook opts) elements')
134
115
135-- | Convert a list of Pandoc blocks to Docbook.
116-- | Convert a list of Pandoc blocks to Docbook.
136blocksToDocbook :: WriterOptions -> [Block] -> Doc
117blocksToDocbook :: WriterOptions -> [Block] -> Doc
137−blocksToDocbook options = vcat . map (blockToDocbook options)
118+blocksToDocbook opts = vcat . map (blockToDocbook opts)
138
119
139-- | Convert a list of lists of blocks to a list of Docbook list items.
120-- | Convert a list of lists of blocks to a list of Docbook list items.
140listItemsToDocbook :: WriterOptions -> [[Block]] -> Doc
121listItemsToDocbook :: WriterOptions -> [[Block]] -> Doc
141−listItemsToDocbook options items =
122+listItemsToDocbook opts items =
142− vcat $ map (listItemToDocbook options) items
123+ vcat $ map (listItemToDocbook opts) items
143
124
144-- | Convert a list of blocks into a Docbook list item.
125-- | Convert a list of blocks into a Docbook list item.
145listItemToDocbook :: WriterOptions -> [Block] -> Doc
126listItemToDocbook :: WriterOptions -> [Block] -> Doc
146−listItemToDocbook options item =
127+listItemToDocbook opts item =
147 let plainToPara (Plain x) = Para x
128 let plainToPara (Plain x) = Para x
148 plainToPara y = y in
129 plainToPara y = y in
149 let item' = map plainToPara item in
130 let item' = map plainToPara item in
150− indentedInTags "listitem" (blocksToDocbook options item')
131+ inTagsIndented opts "listitem" (blocksToDocbook opts item')
151
132
152-- | Convert a Pandoc block element to Docbook.
133-- | Convert a Pandoc block element to Docbook.
153blockToDocbook :: WriterOptions -> Block -> Doc
134blockToDocbook :: WriterOptions -> Block -> Doc
154−blockToDocbook options Blank = text ""
135+blockToDocbook opts Blank = text ""
155−blockToDocbook options Null = empty
136+blockToDocbook opts Null = empty
156−blockToDocbook options (Plain lst) = wrap options lst
137+blockToDocbook opts (Plain lst) = wrap opts lst
157−blockToDocbook options (Para lst) =
138+blockToDocbook opts (Para lst) =
158− indentedInTags "para" (wrap options lst)
139+ inTagsIndented opts "para" (wrap opts lst)
159−blockToDocbook options (BlockQuote blocks) =
140+blockToDocbook opts (BlockQuote blocks) =
160− indentedInTags "blockquote" (blocksToDocbook options blocks)
141+ inTagsIndented opts "blockquote" (blocksToDocbook opts blocks)
161−blockToDocbook options (CodeBlock str) =
142+blockToDocbook opts (CodeBlock str) =
162− text "<screen>\n" <> text (codeStringToXML str) <> text "\n</screen>"
143+ text "<screen>\n" <> text (escapeSGML str) <> text "\n</screen>"
163−blockToDocbook options (BulletList lst) =
144+blockToDocbook opts (BulletList lst) =
164− indentedInTags "itemizedlist" $ listItemsToDocbook options lst
145+ inTagsIndented opts "itemizedlist" $ listItemsToDocbook opts lst
165−blockToDocbook options (OrderedList lst) =
146+blockToDocbook opts (OrderedList lst) =
166− indentedInTags "orderedlist" $ listItemsToDocbook options lst
147+ inTagsIndented opts "orderedlist" $ listItemsToDocbook opts lst
167−blockToDocbook options (RawHtml str) = text str -- raw XML block
148+blockToDocbook opts (RawHtml str) = text str -- raw XML block
168−blockToDocbook options HorizontalRule = empty -- not semantic
149+blockToDocbook opts HorizontalRule = empty -- not semantic
169−blockToDocbook options (Note _ _) = empty -- shouldn't occur
150+blockToDocbook opts (Note _ _) = empty -- shouldn't occur
170−blockToDocbook options (Key _ _) = empty -- shouldn't occur
151+blockToDocbook opts (Key _ _) = empty -- shouldn't occur
171−blockToDocbook options _ = indentedInTags "para" (text "Unknown block type")
152+blockToDocbook opts _ = inTagsIndented opts "para" (text "Unknown block type")
172
153
173-- | Put string in CDATA section
154-- | Put string in CDATA section
174cdata :: String -> Doc
155cdata :: String -> Doc
@@ -176,62 +157,47 @@ cdata str = text $ "<![CDATA[" ++ str ++ "]]>"
176
157
177-- | Take list of inline elements and return wrapped doc.
158-- | Take list of inline elements and return wrapped doc.
178wrap :: WriterOptions -> [Inline] -> Doc
159wrap :: WriterOptions -> [Inline] -> Doc
179−wrap options lst = fsep $ map (inlinesToDocbook options) (splitBy Space lst)
160+wrap opts lst = fsep $ map (inlinesToDocbook opts) (splitBy Space lst)
180−
181−-- | Escape a string for XML (with "smart" option if specified).
182−stringToXML :: WriterOptions -> String -> String
183−stringToXML options = encodeEntities .
184− (if writerSmart options
185− then stringToSmartHtml
186− else stringToHtml)
187−
188−-- | Escape string to XML appropriate for attributes
189−attributeStringToXML :: String -> String
190−attributeStringToXML = gsub "\"" "&quot;" . codeStringToXML
191−
192−-- | Escape a literal string for XML.
193−codeStringToXML :: String -> String
194−codeStringToXML = encodeEntities . gsub "<" "&lt;" . gsub "&" "&amp;"
195
161
196-- | Convert a list of inline elements to Docbook.
162-- | Convert a list of inline elements to Docbook.
197inlinesToDocbook :: WriterOptions -> [Inline] -> Doc
163inlinesToDocbook :: WriterOptions -> [Inline] -> Doc
198−inlinesToDocbook options lst = hcat (map (inlineToDocbook options) lst)
164+inlinesToDocbook opts lst = hcat (map (inlineToDocbook opts) lst)
199
165
200-- | Convert an inline element to Docbook.
166-- | Convert an inline element to Docbook.
201inlineToDocbook :: WriterOptions -> Inline -> Doc
167inlineToDocbook :: WriterOptions -> Inline -> Doc
202−inlineToDocbook options (Str str) = text $ stringToXML options str
168+inlineToDocbook opts (Str str) = text $ stringToSGML opts str
203−inlineToDocbook options (Emph lst) =
169+inlineToDocbook opts (Emph lst) =
204− inTags "emphasis" (inlinesToDocbook options lst)
170+ inTagsSimple opts "emphasis" (inlinesToDocbook opts lst)
205−inlineToDocbook options (Strong lst) =
171+inlineToDocbook opts (Strong lst) =
206− inTagsWithAttrib "emphasis" [("role", "strong")]
172+ inTags False opts "emphasis" [("role", "strong")]
207− (inlinesToDocbook options lst)
173+ (inlinesToDocbook opts lst)
208−inlineToDocbook options (Code str) =
174+inlineToDocbook opts (Code str) =
209− inTags "literal" $ text (codeStringToXML str)
175+ inTagsSimple opts "literal" $ text (escapeSGML str)
210−inlineToDocbook options (TeX str) = inlineToDocbook options (Code str)
176+inlineToDocbook opts (TeX str) = inlineToDocbook opts (Code str)
211−inlineToDocbook options (HtmlInline str) = empty
177+inlineToDocbook opts (HtmlInline str) = empty
212−inlineToDocbook options LineBreak =
178+inlineToDocbook opts LineBreak =
213 text $ "<literallayout></literallayout>"
179 text $ "<literallayout></literallayout>"
214−inlineToDocbook options Space = char ' '
180+inlineToDocbook opts Space = char ' '
215−inlineToDocbook options (Link txt (Src src tit)) =
181+inlineToDocbook opts (Link txt (Src src tit)) =
216 case (matchRegex (mkRegex "mailto:(.*)") src) of
182 case (matchRegex (mkRegex "mailto:(.*)") src) of
217− Just [addr] -> inTags "email" $ text (codeStringToXML addr)
183+ Just [addr] -> inTagsSimple opts "email" $ text (escapeSGML addr)
218− Nothing -> inTagsWithAttrib "ulink" [("url", src)] $
184+ Nothing -> inTags False opts "ulink" [("url", src)] $
219− inlinesToDocbook options txt
185+ inlinesToDocbook opts txt
220−inlineToDocbook options (Link text (Ref ref)) = empty -- shouldn't occur
186+inlineToDocbook opts (Link text (Ref ref)) = empty -- shouldn't occur
221−inlineToDocbook options (Image alt (Src src tit)) =
187+inlineToDocbook opts (Image alt (Src src tit)) =
222 let titleDoc = if null tit
188 let titleDoc = if null tit
223 then empty
189 then empty
224− else indentedInTags "objectinfo" $
190+ else inTagsIndented opts "objectinfo" $
225− indentedInTags "title"
191+ inTagsIndented opts "title"
226− (text $ stringToXML options tit) in
192+ (text $ stringToSGML opts tit) in
227− indentedInTags "inlinemediaobject" $
193+ inTagsIndented opts "inlinemediaobject" $
228− indentedInTags "imageobject" $
194+ inTagsIndented opts "imageobject" $
229− titleDoc $$ inTagsWithAttrib "imagedata" [("fileref", src)] empty
195+ titleDoc $$ selfClosingTag opts "imagedata" [("fileref", src)]
230−inlineToDocbook options (Image alternate (Ref ref)) = empty --shouldn't occur
196+inlineToDocbook opts (Image alternate (Ref ref)) = empty --shouldn't occur
231−inlineToDocbook options (NoteRef ref) =
197+inlineToDocbook opts (NoteRef ref) =
232− let notes = writerNotes options
198+ let notes = writerNotes opts
233 hits = filter (\(Note r _) -> r == ref) notes in
199 hits = filter (\(Note r _) -> r == ref) notes in
234 if null hits
200 if null hits
235 then empty
201 then empty
236 else let (Note _ contents) = head hits in
202 else let (Note _ contents) = head hits in
237− indentedInTags "footnote" $ blocksToDocbook options contents
203+ inTagsIndented opts "footnote" $ blocksToDocbook opts contents
src/Text/Pandoc/Writers/HTML.hs+10−68
@@ -29,12 +29,10 @@ Conversion of 'Pandoc' documents to HTML.
29-}
29-}
30module Text.Pandoc.Writers.HTML (
30module Text.Pandoc.Writers.HTML (
31 writeHtml,
31 writeHtml,
32− stringToSmartHtml,
33− stringToHtml
34 ) where
32 ) where
35import Text.Pandoc.Definition
33import Text.Pandoc.Definition
36import Text.Pandoc.Shared
34import Text.Pandoc.Shared
37−import Text.Html ( stringToHtmlString )
35+import Text.Pandoc.Entities ( encodeEntities )
38import Text.Regex ( mkRegex, matchRegex )
36import Text.Regex ( mkRegex, matchRegex )
39import Numeric ( showHex )
37import Numeric ( showHex )
40import Data.Char ( ord, toLower )
38import Data.Char ( ord, toLower )
@@ -115,61 +113,6 @@ obfuscateChar char =
115obfuscateString :: String -> String
113obfuscateString :: String -> String
116obfuscateString = concatMap obfuscateChar
114obfuscateString = concatMap obfuscateChar
117
115
118−-- | Escape string, preserving character entities and quote.
119−stringToHtml :: String -> String
120−stringToHtml str = escapePreservingRegex stringToHtmlString
121− (mkRegex "\"|(&[[:alnum:]]*;)") str
122−
123−-- | Escape string as in 'stringToHtml' but add smart typography filter.
124−stringToSmartHtml :: String -> String
125−stringToSmartHtml =
126− let escapeDoubleQuotes =
127− gsub "(\"|&quot;)" "&rdquo;" . -- rest are right quotes
128− gsub "(\"|&quot;)(&r[sd]quo;)" "&rdquo;\\2" .
129− -- never left quo before right quo
130− gsub "(&l[sd]quo;)(\"|&quot;)" "\\2&ldquo;" .
131− -- never right quo after left quo
132− gsub "([ \t])(\"|&quot;)" "\\1&ldquo;" .
133− -- never right quo after space
134− gsub "(\"|&quot;)([^,.;:!?^) \t-])" "&ldquo;\\2" . -- "word left
135− gsub "(\"|&quot;)('|`|&lsquo;)" "&rdquo;&rsquo;" .
136− -- right if it got through last filter
137− gsub "(\"|&quot;)('|`|&lsquo;)([^,.;:!?^) \t-])" "&ldquo;&lsquo;\\3" .
138− -- "'word left
139− gsub "``" "&ldquo;" .
140− gsub "''" "&rdquo;"
141− escapeSingleQuotes =
142− gsub "'" "&rsquo;" . -- otherwise right
143− gsub "'(&r[sd]quo;)" "&rsquo;\\1" . -- never left quo before right quo
144− gsub "(&l[sd]quo;)'" "\\1&lsquo;" . -- never right quo after left quo
145− gsub "([ \t])'" "\\1&lsquo;" . -- never right quo after space
146− gsub "`" "&lsquo;" . -- ` is left
147− gsub "([^,.;:!?^) \t-])'" "\\1&rsquo;" . -- word' right
148− gsub "^('|`)([^,.;:!?^) \t-])" "&lsquo;\\2" . -- 'word left
149− gsub "('|`)(\"|&quot;|&ldquo;|``)" "&lsquo;&ldquo;" . -- '"word left
150− gsub "([^,.;:!?^) \t-])'(s|S)" "\\1&rsquo;\\2" . -- possessive
151− gsub "([[:space:]])'([^,.;:!?^) \t-])" "\\1&lsquo;\\2" . -- 'word left
152− gsub "'([0-9][0-9](s|S))" "&rsquo;\\1" -- '80s - decade abbrevs.
153− escapeDashes =
154− gsub " ?-- ?" "&mdash;" .
155− gsub " ?--- ?" "&mdash;" .
156− gsub "([0-9])--?([0-9])" "\\1&ndash;\\2"
157− escapeEllipses = gsub "\\.\\.\\.|\\. \\. \\." "&hellip;" in
158− escapeSingleQuotes . escapeDoubleQuotes . escapeDashes .
159− escapeEllipses . stringToHtml
160−
161−-- | Escape code string as needed for HTML.
162−codeStringToHtml :: String -> String
163−codeStringToHtml [] = []
164−codeStringToHtml (x:xs) = case x of
165− '&' -> "&amp;" ++ codeStringToHtml xs
166− '<' -> "&lt;" ++ codeStringToHtml xs
167− _ -> x:(codeStringToHtml xs)
168−
169−-- | Escape string to HTML appropriate for attributes
170−attributeStringToHtml :: String -> String
171−attributeStringToHtml = gsub "\"" "&quot;"
172−
173-- | Returns an HTML header with appropriate bibliographic information.
116-- | Returns an HTML header with appropriate bibliographic information.
174htmlHeader :: WriterOptions -> Meta -> String
117htmlHeader :: WriterOptions -> Meta -> String
175htmlHeader options (Meta title authors date) =
118htmlHeader options (Meta title authors date) =
@@ -178,12 +121,12 @@ htmlHeader options (Meta title authors date) =
178 authortext = if (null authors)
121 authortext = if (null authors)
179 then ""
122 then ""
180 else "<meta name=\"author\" content=\"" ++
123 else "<meta name=\"author\" content=\"" ++
181− (joinWithSep ", " (map stringToHtml authors)) ++
124+ (joinWithSep ", " (map (stringToSGML options) authors)) ++
182 "\" />\n"
125 "\" />\n"
183 datetext = if (date == "")
126 datetext = if (date == "")
184 then ""
127 then ""
185 else "<meta name=\"date\" content=\"" ++
128 else "<meta name=\"date\" content=\"" ++
186− (stringToHtml date) ++ "\" />\n" in
129+ (stringToSGML options date) ++ "\" />\n" in
187 (writerHeader options) ++ authortext ++ datetext ++ titletext ++
130 (writerHeader options) ++ authortext ++ datetext ++ titletext ++
188 "</head>\n<body>\n"
131 "</head>\n<body>\n"
189
132
@@ -216,7 +159,7 @@ blockToHtml options (Note ref lst) =
216 "\">&#8617;</a></li>\n"
159 "\">&#8617;</a></li>\n"
217blockToHtml options (Key _ _) = ""
160blockToHtml options (Key _ _) = ""
218blockToHtml options (CodeBlock str) =
161blockToHtml options (CodeBlock str) =
219− "<pre><code>" ++ (codeStringToHtml str) ++ "\n</code></pre>\n"
162+ "<pre><code>" ++ (escapeSGML str) ++ "\n</code></pre>\n"
220blockToHtml options (RawHtml str) = str
163blockToHtml options (RawHtml str) = str
221blockToHtml options (BulletList lst) =
164blockToHtml options (BulletList lst) =
222 let attribs = if (writerIncremental options)
165 let attribs = if (writerIncremental options)
@@ -255,18 +198,17 @@ inlineToHtml options (Emph lst) =
255inlineToHtml options (Strong lst) =
198inlineToHtml options (Strong lst) =
256 "<strong>" ++ (inlineListToHtml options lst) ++ "</strong>"
199 "<strong>" ++ (inlineListToHtml options lst) ++ "</strong>"
257inlineToHtml options (Code str) =
200inlineToHtml options (Code str) =
258− "<code>" ++ (codeStringToHtml str) ++ "</code>"
201+ "<code>" ++ (escapeSGML str) ++ "</code>"
259−inlineToHtml options (Str str) =
202+inlineToHtml options (Str str) = stringToSGML options str
260− if (writerSmart options) then stringToSmartHtml str else stringToHtml str
261−inlineToHtml options (TeX str) = (codeStringToHtml str)
203+inlineToHtml options (TeX str) = (escapeSGML str)
262inlineToHtml options (HtmlInline str) = str
204inlineToHtml options (HtmlInline str) = str
263inlineToHtml options (LineBreak) = "<br />\n"
205inlineToHtml options (LineBreak) = "<br />\n"
264inlineToHtml options Space = " "
206inlineToHtml options Space = " "
265inlineToHtml options (Link text (Src src tit)) =
207inlineToHtml options (Link text (Src src tit)) =
266− let title = attributeStringToHtml tit in
208+ let title = stringToSGML options tit in
267 if (isPrefixOf "mailto:" src)
209 if (isPrefixOf "mailto:" src)
268 then obfuscateLink options text src
210 then obfuscateLink options text src
269− else "<a href=\"" ++ (codeStringToHtml src) ++ "\"" ++
211+ else "<a href=\"" ++ (escapeSGML src) ++ "\"" ++
270 (if tit /= "" then " title=\"" ++ title ++ "\">" else ">") ++
212 (if tit /= "" then " title=\"" ++ title ++ "\">" else ">") ++
271 (inlineListToHtml options text) ++ "</a>"
213 (inlineListToHtml options text) ++ "</a>"
272inlineToHtml options (Link text (Ref ref)) =
214inlineToHtml options (Link text (Ref ref)) =
@@ -274,7 +216,7 @@ inlineToHtml options (Link text (Ref ref)) =
274 (inlineListToHtml options ref) ++ "]"
216 (inlineListToHtml options ref) ++ "]"
275 -- this is what markdown does, for better or worse
217 -- this is what markdown does, for better or worse
276inlineToHtml options (Image alt (Src source tit)) =
218inlineToHtml options (Image alt (Src source tit)) =
277− let title = attributeStringToHtml tit
219+ let title = stringToSGML options tit
278 alternate = inlineListToHtml options alt in
220 alternate = inlineListToHtml options alt in
279 "<img src=\"" ++ source ++ "\"" ++
221 "<img src=\"" ++ source ++ "\"" ++
280 (if tit /= "" then " title=\"" ++ title ++ "\"" else "") ++
222 (if tit /= "" then " title=\"" ++ title ++ "\"" else "") ++
tests/s5.basic.html+1−1
@@ -768,7 +768,7 @@ window.onresize = function(){setTimeout('fontScale()', 50);}</script>
768<div class="slide">
768<div class="slide">
769<h1>Smarty</h1>
769<h1>Smarty</h1>
770<ul class="incremental">
770<ul class="incremental">
771−<li>"Hello there"</li>
771+<li>&quot;Hello there&quot;</li>
772<li>Here's a -- dash</li>
772<li>Here's a -- dash</li>
773<li>And 'ellipses'...</li>
773<li>And 'ellipses'...</li>
774</ul>
774</ul>
tests/s5.fragment.html+1−1
@@ -6,7 +6,7 @@
6<h1>Smarty</h1>
6<h1>Smarty</h1>
7<blockquote>
7<blockquote>
8<ul>
8<ul>
9−<li>"Hello there"</li>
9+<li>&quot;Hello there&quot;</li>
10<li>Here's a -- dash</li>
10<li>Here's a -- dash</li>
11<li>And 'ellipses'...</li>
11<li>And 'ellipses'...</li>
12</ul>
12</ul>
tests/s5.inserts.html+1−1
@@ -21,7 +21,7 @@ STUFF INSERTED
21<h1>Smarty</h1>
21<h1>Smarty</h1>
22<blockquote>
22<blockquote>
23<ul>
23<ul>
24−<li>"Hello there"</li>
24+<li>&quot;Hello there&quot;</li>
25<li>Here's a -- dash</li>
25<li>Here's a -- dash</li>
26<li>And 'ellipses'...</li>
26<li>And 'ellipses'...</li>
27</ul>
27</ul>
tests/writer.docbook+15−14
@@ -89,7 +89,7 @@
89 </para>
89 </para>
90 <screen>
90 <screen>
91sub status {
91sub status {
92− print "working";
92+ print &quot;working&quot;;
93}
93}
94</screen>
94</screen>
95 <para>
95 <para>
@@ -133,7 +133,7 @@ sub status {
133 </para>
133 </para>
134 <screen>
134 <screen>
135sub status {
135sub status {
136− print "working";
136+ print &quot;working&quot;;
137}
137}
138</screen>
138</screen>
139 </blockquote>
139 </blockquote>
@@ -177,7 +177,7 @@ sub status {
177---- (should be four hyphens)
177---- (should be four hyphens)
178
178
179sub status {
179sub status {
180− print "working";
180+ print &quot;working&quot;;
181}
181}
182
182
183this code block is indented by one tab
183this code block is indented by one tab
@@ -188,7 +188,7 @@ this code block is indented by one tab
188 <screen>
188 <screen>
189 this code block is indented by two tabs
189 this code block is indented by two tabs
190
190
191−These should not be escaped: \$ \\ \> \[ \{
191+These should not be escaped: \$ \\ \&gt; \[ \{
192</screen>
192</screen>
193 </section>
193 </section>
194 <section>
194 <section>
@@ -577,15 +577,16 @@ These should not be escaped: \$ \\ \> \[ \{
577 word.
577 word.
578 </para>
578 </para>
579 <para>
579 <para>
580− This is code: <literal>></literal>, <literal>$</literal>,
580+ This is code: <literal>&gt;</literal>, <literal>$</literal>,
581 <literal>\</literal>, <literal>\$</literal>,
581 <literal>\</literal>, <literal>\$</literal>,
582− <literal>&lt;html></literal>.
582+ <literal>&lt;html&gt;</literal>.
583 </para>
583 </para>
584 </section>
584 </section>
585 <section>
585 <section>
586 <title>Smart quotes, ellipses, dashes</title>
586 <title>Smart quotes, ellipses, dashes</title>
587 <para>
587 <para>
588− "Hello," said the spider. "'Shelob' is my name."
588+ &quot;Hello,&quot; said the spider. &quot;'Shelob' is my
589+ name.&quot;
589 </para>
590 </para>
590 <para>
591 <para>
591 'A', 'B', and 'C' are letters.
592 'A', 'B', and 'C' are letters.
@@ -594,11 +595,11 @@ These should not be escaped: \$ \\ \> \[ \{
594 'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.'
595 'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.'
595 </para>
596 </para>
596 <para>
597 <para>
597− 'He said, "I want to go."' Were you alive in the 70's?
598+ 'He said, &quot;I want to go.&quot;' Were you alive in the 70's?
598 </para>
599 </para>
599 <para>
600 <para>
600 Here is some quoted '<literal>code</literal>' and a
601 Here is some quoted '<literal>code</literal>' and a
601− "<ulink url="http://example.com/?foo=1&amp;bar=2">quoted link</ulink>".
602+ &quot;<ulink url="http://example.com/?foo=1&amp;bar=2">quoted link</ulink>&quot;.
602 </para>
603 </para>
603 <para>
604 <para>
604 Some dashes: one---two --- three--four -- five.
605 Some dashes: one---two --- three--four -- five.
@@ -672,7 +673,7 @@ These should not be escaped: \$ \\ \> \[ \{
672 <listitem>
673 <listitem>
673 <para>
674 <para>
674 $22,000 is a <emphasis>lot</emphasis> of money. So is $34,000. (It
675 $22,000 is a <emphasis>lot</emphasis> of money. So is $34,000. (It
675− worked if "lot" is emphasized.)
676+ worked if &quot;lot&quot; is emphasized.)
676 </para>
677 </para>
677 </listitem>
678 </listitem>
678 <listitem>
679 <listitem>
@@ -909,17 +910,17 @@ Cat &amp; 1 \\ \hline
909 </blockquote>
910 </blockquote>
910 <para>
911 <para>
911 Auto-links should not occur here:
912 Auto-links should not occur here:
912− <literal>&lt;http://example.com/></literal>
913+ <literal>&lt;http://example.com/&gt;</literal>
913 </para>
914 </para>
914 <screen>
915 <screen>
915−or here: &lt;http://example.com/>
916+or here: &lt;http://example.com/&gt;
916</screen>
917</screen>
917 </section>
918 </section>
918 </section>
919 </section>
919 <section>
920 <section>
920 <title>Images</title>
921 <title>Images</title>
921 <para>
922 <para>
922− From "Voyage dans la Lune" by Georges Melies (1902):
923+ From &quot;Voyage dans la Lune&quot; by Georges Melies (1902):
923 </para>
924 </para>
924 <para>
925 <para>
925 <inlinemediaobject>
926 <inlinemediaobject>
@@ -963,7 +964,7 @@ or here: &lt;http://example.com/>
963 footnote (as with list items).
964 footnote (as with list items).
964 </para>
965 </para>
965 <screen>
966 <screen>
966− { &lt;code> }
967+ { &lt;code&gt; }
967</screen>
968</screen>
968 <para>
969 <para>
969 If you want, you can indent every line, but you can also be lazy
970 If you want, you can indent every line, but you can also be lazy
tests/writer.html+24−24
@@ -39,7 +39,7 @@ here.</p>
39<blockquote>
39<blockquote>
40<p>Code in a block quote:</p>
40<p>Code in a block quote:</p>
41<pre><code>sub status {
41<pre><code>sub status {
42− print "working";
42+ print &quot;working&quot;;
43}
43}
44</code></pre>
44</code></pre>
45<p>A list:</p>
45<p>A list:</p>
@@ -60,7 +60,7 @@ here.</p>
60<blockquote>
60<blockquote>
61<p>Example:</p>
61<p>Example:</p>
62<pre><code>sub status {
62<pre><code>sub status {
63− print "working";
63+ print &quot;working&quot;;
64}
64}
65</code></pre>
65</code></pre>
66</blockquote>
66</blockquote>
@@ -84,7 +84,7 @@ here.</p>
84<pre><code>---- (should be four hyphens)
84<pre><code>---- (should be four hyphens)
85
85
86sub status {
86sub status {
87− print "working";
87+ print &quot;working&quot;;
88}
88}
89
89
90this code block is indented by one tab
90this code block is indented by one tab
@@ -92,7 +92,7 @@ this code block is indented by one tab
92<p>And:</p>
92<p>And:</p>
93<pre><code> this code block is indented by two tabs
93<pre><code> this code block is indented by two tabs
94
94
95−These should not be escaped: \$ \\ \> \[ \{
95+These should not be escaped: \$ \\ \&gt; \[ \{
96</code></pre>
96</code></pre>
97<hr />
97<hr />
98<h1>Lists</h1>
98<h1>Lists</h1>
@@ -255,12 +255,12 @@ These should not be escaped: \$ \\ \> \[ \{
255<div>
255<div>
256 foo</div>
256 foo</div>
257<p>This should be a code block, though:</p>
257<p>This should be a code block, though:</p>
258−<pre><code>&lt;div>
258+<pre><code>&lt;div&gt;
259 foo
259 foo
260−&lt;/div>
260+&lt;/div&gt;
261</code></pre>
261</code></pre>
262<p>As should this:</p>
262<p>As should this:</p>
263−<pre><code>&lt;div>foo&lt;/div>
263+<pre><code>&lt;div&gt;foo&lt;/div&gt;
264</code></pre>
264</code></pre>
265<p>Now, nested:</p>
265<p>Now, nested:</p>
266<div>
266<div>
@@ -281,12 +281,12 @@ Blah
281 This is another comment.
281 This is another comment.
282-->
282-->
283<p>Code block:</p>
283<p>Code block:</p>
284−<pre><code>&lt;!-- Comment -->
284+<pre><code>&lt;!-- Comment --&gt;
285</code></pre>
285</code></pre>
286<p>Just plain comment, with trailing spaces on the line:</p>
286<p>Just plain comment, with trailing spaces on the line:</p>
287<!-- foo -->
287<!-- foo -->
288<p>Code:</p>
288<p>Code:</p>
289−<pre><code>&lt;hr />
289+<pre><code>&lt;hr /&gt;
290</code></pre>
290</code></pre>
291<p>Hr's:</p>
291<p>Hr's:</p>
292<hr>
292<hr>
@@ -315,14 +315,14 @@ Blah
315<p>So is <strong><em>this</em></strong> word.</p>
315<p>So is <strong><em>this</em></strong> word.</p>
316<p><strong><em>This is strong and em.</em></strong></p>
316<p><strong><em>This is strong and em.</em></strong></p>
317<p>So is <strong><em>this</em></strong> word.</p>
317<p>So is <strong><em>this</em></strong> word.</p>
318−<p>This is code: <code>></code>, <code>$</code>, <code>\</code>, <code>\$</code>, <code>&lt;html></code>.</p>
318+<p>This is code: <code>&gt;</code>, <code>$</code>, <code>\</code>, <code>\$</code>, <code>&lt;html&gt;</code>.</p>
319<hr />
319<hr />
320<h1>Smart quotes, ellipses, dashes</h1>
320<h1>Smart quotes, ellipses, dashes</h1>
321−<p>"Hello," said the spider. "'Shelob' is my name."</p>
321+<p>&quot;Hello,&quot; said the spider. &quot;'Shelob' is my name.&quot;</p>
322<p>'A', 'B', and 'C' are letters.</p>
322<p>'A', 'B', and 'C' are letters.</p>
323<p>'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.'</p>
323<p>'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.'</p>
324−<p>'He said, "I want to go."' Were you alive in the 70's?</p>
324+<p>'He said, &quot;I want to go.&quot;' Were you alive in the 70's?</p>
325−<p>Here is some quoted '<code>code</code>' and a "<a href="http://example.com/?foo=1&amp;bar=2">quoted link</a>".</p>
325+<p>Here is some quoted '<code>code</code>' and a &quot;<a href="http://example.com/?foo=1&amp;bar=2">quoted link</a>&quot;.</p>
326<p>Some dashes: one---two --- three--four -- five.</p>
326<p>Some dashes: one---two --- three--four -- five.</p>
327<p>Dashes between numbers: 5-7, 255-66, 1987-1999.</p>
327<p>Dashes between numbers: 5-7, 255-66, 1987-1999.</p>
328<p>Ellipses...and. . .and . . . .</p>
328<p>Ellipses...and. . .and . . . .</p>
@@ -342,7 +342,7 @@ Blah
342<p>These shouldn't be math:</p>
342<p>These shouldn't be math:</p>
343<ul>
343<ul>
344<li>To get the famous equation, write <code>$e = mc^2$</code>.</li>
344<li>To get the famous equation, write <code>$e = mc^2$</code>.</li>
345−<li>$22,000 is a <em>lot</em> of money. So is $34,000. (It worked if "lot" is emphasized.)</li>
345+<li>$22,000 is a <em>lot</em> of money. So is $34,000. (It worked if &quot;lot&quot; is emphasized.)</li>
346<li>Escaped <code>$</code>: $73 <em>this should be emphasized</em> 23$.</li>
346<li>Escaped <code>$</code>: $73 <em>this should be emphasized</em> 23$.</li>
347</ul>
347</ul>
348<p>Here's a LaTeX table:</p>
348<p>Here's a LaTeX table:</p>
@@ -355,11 +355,11 @@ Cat &amp; 1 \\ \hline
355<h1>Special Characters</h1>
355<h1>Special Characters</h1>
356<p>Here is some unicode:</p>
356<p>Here is some unicode:</p>
357<ul>
357<ul>
358−<li>I hat: Î</li>
358+<li>I hat: &Icirc;</li>
359−<li>o umlaut: ö</li>
359+<li>o umlaut: &ouml;</li>
360−<li>section: §</li>
360+<li>section: &sect;</li>
361−<li>set membership: ∈</li>
361+<li>set membership: &isin;</li>
362−<li>copyright: ©</li>
362+<li>copyright: &copy;</li>
363</ul>
363</ul>
364<p>AT&amp;T has an ampersand in their name.</p>
364<p>AT&amp;T has an ampersand in their name.</p>
365<p>AT&amp;T is another way to write it.</p>
365<p>AT&amp;T is another way to write it.</p>
@@ -414,7 +414,7 @@ document.write('<a h'+'ref'+'="ma'+'ilto'+':'+e+'">'+'Email link'+'<\/'+'a'+'>')
414<p>Foo <a href="/url/" title="Title with &quot;quote&quot; inside">biz</a>.</p>
414<p>Foo <a href="/url/" title="Title with &quot;quote&quot; inside">biz</a>.</p>
415<h2>With ampersands</h2>
415<h2>With ampersands</h2>
416<p>Here's a <a href="http://example.com/?foo=1&amp;bar=2">link with an ampersand in the URL</a>.</p>
416<p>Here's a <a href="http://example.com/?foo=1&amp;bar=2">link with an ampersand in the URL</a>.</p>
417−<p>Here's a link with an amersand in the link text: <a href="http://att.com/" title="AT&T">AT&amp;T</a>.</p>
417+<p>Here's a link with an amersand in the link text: <a href="http://att.com/" title="AT&amp;T">AT&amp;T</a>.</p>
418<p>Here's an <a href="/script?foo=1&amp;bar=2">inline link</a>.</p>
418<p>Here's an <a href="/script?foo=1&amp;bar=2">inline link</a>.</p>
419<p>Here's an <a href="/script?foo=1&amp;bar=2">inline link in pointy braces</a>.</p>
419<p>Here's an <a href="/script?foo=1&amp;bar=2">inline link in pointy braces</a>.</p>
420<h2>Autolinks</h2>
420<h2>Autolinks</h2>
@@ -433,12 +433,12 @@ document.write('<a h'+'ref'+'="ma'+'ilto'+':'+e+'">'+e+'<\/'+'a'+'>');
433<blockquote>
433<blockquote>
434<p>Blockquoted: <a href="http://example.com/">http://example.com/</a></p>
434<p>Blockquoted: <a href="http://example.com/">http://example.com/</a></p>
435</blockquote>
435</blockquote>
436−<p>Auto-links should not occur here: <code>&lt;http://example.com/></code></p>
436+<p>Auto-links should not occur here: <code>&lt;http://example.com/&gt;</code></p>
437−<pre><code>or here: &lt;http://example.com/>
437+<pre><code>or here: &lt;http://example.com/&gt;
438</code></pre>
438</code></pre>
439<hr />
439<hr />
440<h1>Images</h1>
440<h1>Images</h1>
441−<p>From "Voyage dans la Lune" by Georges Melies (1902):</p>
441+<p>From &quot;Voyage dans la Lune&quot; by Georges Melies (1902):</p>
442<p><img src="lalune.jpg" title="Voyage dans la Lune" alt="lalune"></p>
442<p><img src="lalune.jpg" title="Voyage dans la Lune" alt="lalune"></p>
443<p>Here is a movie <img src="movie.jpg" alt="movie"> icon.</p>
443<p>Here is a movie <img src="movie.jpg" alt="movie"> icon.</p>
444<hr />
444<hr />
@@ -458,7 +458,7 @@ document.write('<a h'+'ref'+'="ma'+'ilto'+':'+e+'">'+e+'<\/'+'a'+'>');
458 <a href="#fnref1" class="footnoteBacklink" title="Jump back to footnote 1">&#8617;</a></li>
458 <a href="#fnref1" class="footnoteBacklink" title="Jump back to footnote 1">&#8617;</a></li>
459<li id="fn2"><p>Here's the long note. This one contains multiple blocks.</p>
459<li id="fn2"><p>Here's the long note. This one contains multiple blocks.</p>
460<p>Subsequent blocks are indented to show that they belong to the footnote (as with list items).</p>
460<p>Subsequent blocks are indented to show that they belong to the footnote (as with list items).</p>
461−<pre><code> { &lt;code> }
461+<pre><code> { &lt;code&gt; }
462</code></pre>
462</code></pre>
463<p>If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.</p>
463<p>If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.</p>
464 <a href="#fnref2" class="footnoteBacklink" title="Jump back to footnote 2">&#8617;</a></li>
464 <a href="#fnref2" class="footnoteBacklink" title="Jump back to footnote 2">&#8617;</a></li>
tests/writer.smart.html+22−22
@@ -39,7 +39,7 @@ here.</p>
39<blockquote>
39<blockquote>
40<p>Code in a block quote:</p>
40<p>Code in a block quote:</p>
41<pre><code>sub status {
41<pre><code>sub status {
42− print "working";
42+ print &quot;working&quot;;
43}
43}
44</code></pre>
44</code></pre>
45<p>A list:</p>
45<p>A list:</p>
@@ -60,7 +60,7 @@ here.</p>
60<blockquote>
60<blockquote>
61<p>Example:</p>
61<p>Example:</p>
62<pre><code>sub status {
62<pre><code>sub status {
63− print "working";
63+ print &quot;working&quot;;
64}
64}
65</code></pre>
65</code></pre>
66</blockquote>
66</blockquote>
@@ -84,7 +84,7 @@ here.</p>
84<pre><code>---- (should be four hyphens)
84<pre><code>---- (should be four hyphens)
85
85
86sub status {
86sub status {
87− print "working";
87+ print &quot;working&quot;;
88}
88}
89
89
90this code block is indented by one tab
90this code block is indented by one tab
@@ -92,7 +92,7 @@ this code block is indented by one tab
92<p>And:</p>
92<p>And:</p>
93<pre><code> this code block is indented by two tabs
93<pre><code> this code block is indented by two tabs
94
94
95−These should not be escaped: \$ \\ \> \[ \{
95+These should not be escaped: \$ \\ \&gt; \[ \{
96</code></pre>
96</code></pre>
97<hr />
97<hr />
98<h1>Lists</h1>
98<h1>Lists</h1>
@@ -255,12 +255,12 @@ These should not be escaped: \$ \\ \> \[ \{
255<div>
255<div>
256 foo</div>
256 foo</div>
257<p>This should be a code block, though:</p>
257<p>This should be a code block, though:</p>
258−<pre><code>&lt;div>
258+<pre><code>&lt;div&gt;
259 foo
259 foo
260−&lt;/div>
260+&lt;/div&gt;
261</code></pre>
261</code></pre>
262<p>As should this:</p>
262<p>As should this:</p>
263−<pre><code>&lt;div>foo&lt;/div>
263+<pre><code>&lt;div&gt;foo&lt;/div&gt;
264</code></pre>
264</code></pre>
265<p>Now, nested:</p>
265<p>Now, nested:</p>
266<div>
266<div>
@@ -281,12 +281,12 @@ Blah
281 This is another comment.
281 This is another comment.
282-->
282-->
283<p>Code block:</p>
283<p>Code block:</p>
284−<pre><code>&lt;!-- Comment -->
284+<pre><code>&lt;!-- Comment --&gt;
285</code></pre>
285</code></pre>
286<p>Just plain comment, with trailing spaces on the line:</p>
286<p>Just plain comment, with trailing spaces on the line:</p>
287<!-- foo -->
287<!-- foo -->
288<p>Code:</p>
288<p>Code:</p>
289−<pre><code>&lt;hr />
289+<pre><code>&lt;hr /&gt;
290</code></pre>
290</code></pre>
291<p>Hr&rsquo;s:</p>
291<p>Hr&rsquo;s:</p>
292<hr>
292<hr>
@@ -315,7 +315,7 @@ Blah
315<p>So is <strong><em>this</em></strong> word.</p>
315<p>So is <strong><em>this</em></strong> word.</p>
316<p><strong><em>This is strong and em.</em></strong></p>
316<p><strong><em>This is strong and em.</em></strong></p>
317<p>So is <strong><em>this</em></strong> word.</p>
317<p>So is <strong><em>this</em></strong> word.</p>
318−<p>This is code: <code>></code>, <code>$</code>, <code>\</code>, <code>\$</code>, <code>&lt;html></code>.</p>
318+<p>This is code: <code>&gt;</code>, <code>$</code>, <code>\</code>, <code>\$</code>, <code>&lt;html&gt;</code>.</p>
319<hr />
319<hr />
320<h1>Smart quotes, ellipses, dashes</h1>
320<h1>Smart quotes, ellipses, dashes</h1>
321<p>&ldquo;Hello,&rdquo; said the spider. &ldquo;&lsquo;Shelob&rsquo; is my name.&rdquo;</p>
321<p>&ldquo;Hello,&rdquo; said the spider. &ldquo;&lsquo;Shelob&rsquo; is my name.&rdquo;</p>
@@ -355,11 +355,11 @@ Cat &amp; 1 \\ \hline
355<h1>Special Characters</h1>
355<h1>Special Characters</h1>
356<p>Here is some unicode:</p>
356<p>Here is some unicode:</p>
357<ul>
357<ul>
358−<li>I hat: Î</li>
358+<li>I hat: &Icirc;</li>
359−<li>o umlaut: ö</li>
359+<li>o umlaut: &ouml;</li>
360−<li>section: §</li>
360+<li>section: &sect;</li>
361−<li>set membership: ∈</li>
361+<li>set membership: &isin;</li>
362−<li>copyright: ©</li>
362+<li>copyright: &copy;</li>
363</ul>
363</ul>
364<p>AT&amp;T has an ampersand in their name.</p>
364<p>AT&amp;T has an ampersand in their name.</p>
365<p>AT&amp;T is another way to write it.</p>
365<p>AT&amp;T is another way to write it.</p>
@@ -389,7 +389,7 @@ Cat &amp; 1 \\ \hline
389<p><a href="/url/" title="title">URL and title</a>.</p>
389<p><a href="/url/" title="title">URL and title</a>.</p>
390<p><a href="/url/" title="title preceded by two spaces">URL and title</a>.</p>
390<p><a href="/url/" title="title preceded by two spaces">URL and title</a>.</p>
391<p><a href="/url/" title="title preceded by a tab">URL and title</a>.</p>
391<p><a href="/url/" title="title preceded by a tab">URL and title</a>.</p>
392−<p><a href="/url/" title="title with &quot;quotes&quot; in it">URL and title</a></p>
392+<p><a href="/url/" title="title with &ldquo;quotes&rdquo; in it">URL and title</a></p>
393<p><a href="/url/" title="title with single quotes">URL and title</a></p>
393<p><a href="/url/" title="title with single quotes">URL and title</a></p>
394<p><script type="text/javascript">
394<p><script type="text/javascript">
395<!--
395<!--
@@ -410,11 +410,11 @@ document.write('<a h'+'ref'+'="ma'+'ilto'+':'+e+'">'+'Email link'+'<\/'+'a'+'>')
410<p>This should [not][] be a link.</p>
410<p>This should [not][] be a link.</p>
411<pre><code>[not]: /url
411<pre><code>[not]: /url
412</code></pre>
412</code></pre>
413−<p>Foo <a href="/url/" title="Title with &quot;quotes&quot; inside">bar</a>.</p>
413+<p>Foo <a href="/url/" title="Title with &ldquo;quotes&rdquo; inside">bar</a>.</p>
414−<p>Foo <a href="/url/" title="Title with &quot;quote&quot; inside">biz</a>.</p>
414+<p>Foo <a href="/url/" title="Title with &ldquo;quote&rdquo; inside">biz</a>.</p>
415<h2>With ampersands</h2>
415<h2>With ampersands</h2>
416<p>Here&rsquo;s a <a href="http://example.com/?foo=1&amp;bar=2">link with an ampersand in the URL</a>.</p>
416<p>Here&rsquo;s a <a href="http://example.com/?foo=1&amp;bar=2">link with an ampersand in the URL</a>.</p>
417−<p>Here&rsquo;s a link with an amersand in the link text: <a href="http://att.com/" title="AT&T">AT&amp;T</a>.</p>
417+<p>Here&rsquo;s a link with an amersand in the link text: <a href="http://att.com/" title="AT&amp;T">AT&amp;T</a>.</p>
418<p>Here&rsquo;s an <a href="/script?foo=1&amp;bar=2">inline link</a>.</p>
418<p>Here&rsquo;s an <a href="/script?foo=1&amp;bar=2">inline link</a>.</p>
419<p>Here&rsquo;s an <a href="/script?foo=1&amp;bar=2">inline link in pointy braces</a>.</p>
419<p>Here&rsquo;s an <a href="/script?foo=1&amp;bar=2">inline link in pointy braces</a>.</p>
420<h2>Autolinks</h2>
420<h2>Autolinks</h2>
@@ -433,8 +433,8 @@ document.write('<a h'+'ref'+'="ma'+'ilto'+':'+e+'">'+e+'<\/'+'a'+'>');
433<blockquote>
433<blockquote>
434<p>Blockquoted: <a href="http://example.com/">http://example.com/</a></p>
434<p>Blockquoted: <a href="http://example.com/">http://example.com/</a></p>
435</blockquote>
435</blockquote>
436−<p>Auto-links should not occur here: <code>&lt;http://example.com/></code></p>
436+<p>Auto-links should not occur here: <code>&lt;http://example.com/&gt;</code></p>
437−<pre><code>or here: &lt;http://example.com/>
437+<pre><code>or here: &lt;http://example.com/&gt;
438</code></pre>
438</code></pre>
439<hr />
439<hr />
440<h1>Images</h1>
440<h1>Images</h1>
@@ -458,7 +458,7 @@ document.write('<a h'+'ref'+'="ma'+'ilto'+':'+e+'">'+e+'<\/'+'a'+'>');
458 <a href="#fnref1" class="footnoteBacklink" title="Jump back to footnote 1">&#8617;</a></li>
458 <a href="#fnref1" class="footnoteBacklink" title="Jump back to footnote 1">&#8617;</a></li>
459<li id="fn2"><p>Here&rsquo;s the long note. This one contains multiple blocks.</p>
459<li id="fn2"><p>Here&rsquo;s the long note. This one contains multiple blocks.</p>
460<p>Subsequent blocks are indented to show that they belong to the footnote (as with list items).</p>
460<p>Subsequent blocks are indented to show that they belong to the footnote (as with list items).</p>
461−<pre><code> { &lt;code> }
461+<pre><code> { &lt;code&gt; }
462</code></pre>
462</code></pre>
463<p>If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.</p>
463<p>If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.</p>
464 <a href="#fnref2" class="footnoteBacklink" title="Jump back to footnote 2">&#8617;</a></li>
464 <a href="#fnref2" class="footnoteBacklink" title="Jump back to footnote 2">&#8617;</a></li>