@@ -0,0 +1,88 @@
1+{-
2+Copyright (C) 2006-7 John MacFarlane <jgm@berkeley.edu>
3+
4+This program is free software; you can redistribute it and/or modify
5+it under the terms of the GNU General Public License as published by
6+the Free Software Foundation; either version 2 of the License, or
7+(at your option) any later version.
8+
9+This program is distributed in the hope that it will be useful,
10+but WITHOUT ANY WARRANTY; without even the implied warranty of
11+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+GNU General Public License for more details.
13+
14+You should have received a copy of the GNU General Public License
15+along with this program; if not, write to the Free Software
16+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+-}
18+
19+{- |
20+ Module : Text.Pandoc.XML
21+ Copyright : Copyright (C) 2006-7 John MacFarlane
22+ License : GNU GPL, version 2 or above
23+
24+ Maintainer : John MacFarlane <jgm@berkeley.edu>
25+ Stability : alpha
26+ Portability : portable
27+
28+Functions for escaping and formatting XML.
29+-}
30+module Text.Pandoc.XML ( escapeCharForXML,
31+ escapeStringForXML,
32+ inTags,
33+ selfClosingTag,
34+ inTagsSimple,
35+ inTagsIndented ) where
36+import Text.PrettyPrint.HughesPJ
37+
38+-- | Escape one character as needed for XML.
39+escapeCharForXML :: Char -> String
40+escapeCharForXML x = case x of
41+ '&' -> "&"
42+ '<' -> "<"
43+ '>' -> ">"
44+ '"' -> """
45+ '\160' -> " "
46+ c -> [c]
47+
48+-- | True if the character needs to be escaped.
49+needsEscaping :: Char -> Bool
50+needsEscaping c = c `elem` "&<>\"\160"
51+
52+-- | Escape string as needed for XML. Entity references are not preserved.
53+escapeStringForXML :: String -> String
54+escapeStringForXML "" = ""
55+escapeStringForXML str =
56+ case break needsEscaping str of
57+ (okay, "") -> okay
58+ (okay, (c:cs)) -> okay ++ escapeCharForXML c ++ escapeStringForXML cs
59+
60+-- | Return a text object with a string of formatted XML attributes.
61+attributeList :: [(String, String)] -> Doc
62+attributeList = text . concatMap
63+ (\(a, b) -> " " ++ escapeStringForXML a ++ "=\"" ++
64+ escapeStringForXML b ++ "\"")
65+
66+-- | Put the supplied contents between start and end tags of tagType,
67+-- with specified attributes and (if specified) indentation.
68+inTags:: Bool -> String -> [(String, String)] -> Doc -> Doc
69+inTags isIndented tagType attribs contents =
70+ let openTag = char '<' <> text tagType <> attributeList attribs <>
71+ char '>'
72+ closeTag = text "</" <> text tagType <> char '>'
73+ in if isIndented
74+ then openTag $$ nest 2 contents $$ closeTag
75+ else openTag <> contents <> closeTag
76+
77+-- | Return a self-closing tag of tagType with specified attributes
78+selfClosingTag :: String -> [(String, String)] -> Doc
79+selfClosingTag tagType attribs =
80+ char '<' <> text tagType <> attributeList attribs <> text " />"
81+
82+-- | Put the supplied contents between start and end tags of tagType.
83+inTagsSimple :: String -> Doc -> Doc
84+inTagsSimple tagType = inTags False tagType []
85+
86+-- | Put the supplied contents in indented block btw start and end tags.
87+inTagsIndented :: String -> Doc -> Doc
88+inTagsIndented tagType = inTags True tagType []