git.iko.soy/typeable/pandoc
on master help sign in
Merge branch 'textile'

Conflicts: README man/man1/pandoc.1.md pandoc.cabal

commitc989bf028fa226acb7b03039d0d77ef1e6a4700e authorJohn MacFarlane <jgm@berkeley.edu> dateSat 27 Nov 2010, 10:52 (16 years ago) parentf0b794571c9316
10 files changed+1341−13
README+8−5 man/man1/pandoc.1.md+7−6 pandoc.cabal+6−2 src/Text/Pandoc.hs+2 src/Text/Pandoc/Writers/Textile.hs+415 src/pandoc.hs+1 templates/textile.template+12 tests/RunTests.hs+1 tests/tables.textile+212 tests/writer.textile+677
README+8−5
@@ -7,8 +7,8 @@ another, and a command-line tool that uses this library. It can read
77[markdown] and (subsets of) [reStructuredText], [HTML], and [LaTeX]; and
88it can write plain text, [markdown], [reStructuredText], [HTML], [LaTeX],
99[ConTeXt], [RTF], [DocBook XML], [OpenDocument XML], [ODT], [GNU Texinfo],
10−[MediaWiki markup], [EPUB], [groff man] pages, and [Slidy] or [S5]
11−HTML slide shows.
10+[MediaWiki markup], [EPUB], [Textile], [groff man] pages, and [Slidy]
11+or [S5] HTML slide shows.
1212
1313Pandoc's enhanced version of markdown includes syntax for footnotes,
1414tables, flexible ordered lists, definition lists, delimited code blocks,
@@ -75,9 +75,9 @@ Supported output formats include `markdown`, `latex`, `context`
7575(ConTeXt), `html`, `rtf` (rich text format), `rst`
7676(reStructuredText), `docbook` (DocBook XML), `opendocument`
7777(OpenDocument XML), `odt` (OpenOffice text document), `texinfo`, (GNU
78−Texinfo), `mediawiki` (MediaWiki markup), `epub` (EPUB ebook),
79−`man` (groff man), `slidy` (slidy HTML and javascript slide show), or
80−`s5` (S5 HTML and javascript slide show).
78+Texinfo), `mediawiki` (MediaWiki markup), `textile` (Textile),
79+`epub` (EPUB ebook), `man` (groff man), `slidy` (slidy HTML and
80+javascript slide show), or `s5` (S5 HTML and javascript slide show).
8181
8282Supported input formats include `markdown`, `html`, `latex`, and `rst`.
8383Note that the `rst` reader only parses a subset of reStructuredText
@@ -1202,6 +1202,8 @@ In groff man output, it will be rendered verbatim without $'s.
12021202
12031203In MediaWiki output, it will be rendered inside `<math>` tags.
12041204
1205+In Textile output, it will be rendered inside `<span class="math">` tags.
1206+
12051207In RTF, Docbook, and OpenDocument output, it will be rendered, as far as
12061208possible, using unicode characters, and will otherwise appear verbatim.
12071209Unknown commands and symbols, and commands that cannot be dealt with
@@ -1412,6 +1414,7 @@ and pasted as literate Haskell source.
14121414[DocBook XML]: http://www.docbook.org/
14131415[OpenDocument XML]: http://opendocument.xml.org/
14141416[ODT]: http://en.wikipedia.org/wiki/OpenDocument
1417+[Textile]: http://redcloth.org/textile
14151418[MediaWiki markup]: http://www.mediawiki.org/wiki/Help:Formatting
14161419[groff man]: http://developer.apple.com/DOCUMENTATION/Darwin/Reference/ManPages/man7/groff_man.7.html
14171420[Haskell]: http://www.haskell.org/
man/man1/pandoc.1.md+7−6
@@ -15,8 +15,9 @@ pandoc [*options*] [*input-file*]...
1515Pandoc converts files from one markup format to another. It can
1616read markdown and (subsets of) reStructuredText, HTML, and LaTeX, and
1717it can write plain text, markdown, reStructuredText, HTML, LaTeX,
18−ConTeXt, Texinfo, groff man, MediaWiki markup, RTF, OpenDocument XML,
19−ODT, DocBook XML, EPUB, and Slidy or S5 HTML slide shows.
18+ConTeXt, Texinfo, groff man, MediaWiki markup, Textile, RTF,
19+OpenDocument XML, ODT, DocBook XML, EPUB, and Slidy or S5 HTML slide
20+shows.
2021
2122If no *input-file* is specified, input is read from *stdin*.
2223Otherwise, the *input-files* are concatenated (with a blank
@@ -72,10 +73,10 @@ should pipe input and output through `iconv`:
7273: Specify output format. *FORMAT* can be `native` (native Haskell),
7374 `plain` (plain text), `markdown` (markdown), `rst` (reStructuredText),
7475 `html` (HTML), `latex` (LaTeX), `context` (ConTeXt), `man` (groff man),
75− `mediawiki` (MediaWiki markup), `texinfo` (GNU Texinfo),
76− `docbook` (DocBook XML), `opendocument` (OpenDocument XML),
77− `odt` (OpenOffice text document), `epub` (EPUB book),
78− `slidy` (Slidy HTML and javascript slide show),
76+ `mediawiki` (MediaWiki markup), `textile` (Textile),
77+ `texinfo` (GNU Texinfo), `docbook` (DocBook XML),
78+ `opendocument` (OpenDocument XML), `odt` (OpenOffice text document),
79+ `epub` (EPUB book), `slidy` (Slidy HTML and javascript slide show),
7980 `s5` (S5 HTML and javascript slide show), or `rtf` (rich text
8081 format). Note that `odt` and `epub` output will not be directed to
8182 *stdout*; an output filename must be specified using the `-o/--output`
pandoc.cabal+6−2
@@ -18,8 +18,8 @@ Description: Pandoc is a Haskell library for converting from one markup
1818 this library. It can read markdown and (subsets of)
1919 reStructuredText, HTML, and LaTeX, and it can write
2020 markdown, reStructuredText, HTML, LaTeX, ConTeXt, Docbook,
21− OpenDocument, ODT, RTF, MediaWiki, groff man pages, EPUB,
22− and S5 and Slidy HTML slide shows.
21+ OpenDocument, ODT, RTF, MediaWiki, Textile, groff man pages,
22+ EPUB, and S5 and Slidy HTML slide shows.
2323 .
2424 Pandoc extends standard markdown syntax with footnotes,
2525 embedded LaTeX, definition lists, tables, and other
@@ -43,6 +43,7 @@ Data-Files:
4343 templates/rst.template, templates/plain.template,
4444 templates/mediawiki.template, templates/rtf.template,
4545 templates/s5.template, templates/slidy.template,
46+ templates/textile.template
4647 -- data for ODT writer
4748 reference.odt,
4849 -- stylesheet for EPUB writer
@@ -93,6 +94,7 @@ Extra-Source-Files:
9394 tests/tables.plain,
9495 tests/tables.markdown,
9596 tests/tables.mediawiki,
97+ tests/tables.textile,
9698 tests/tables.native,
9799 tests/tables.opendocument,
98100 tests/tables.texinfo,
@@ -110,6 +112,7 @@ Extra-Source-Files:
110112 tests/writer.markdown,
111113 tests/writer.plain,
112114 tests/writer.mediawiki,
115+ tests/writer.textile,
113116 tests/writer.native,
114117 tests/writer.opendocument,
115118 tests/writer.rst,
@@ -200,6 +203,7 @@ Library
200203 Text.Pandoc.Writers.Man,
201204 Text.Pandoc.Writers.Markdown,
202205 Text.Pandoc.Writers.RST,
206+ Text.Pandoc.Writers.Textile,
203207 Text.Pandoc.Writers.MediaWiki,
204208 Text.Pandoc.Writers.RTF,
205209 Text.Pandoc.Writers.ODT,
src/Text/Pandoc.hs+2
@@ -84,6 +84,7 @@ module Text.Pandoc
8484 , writeOpenDocument
8585 , writeMan
8686 , writeMediaWiki
87+ , writeTextile
8788 , writeRTF
8889 , writeODT
8990 , writeEPUB
@@ -117,6 +118,7 @@ import Text.Pandoc.Writers.OpenDocument
117118import Text.Pandoc.Writers.Man
118119import Text.Pandoc.Writers.RTF
119120import Text.Pandoc.Writers.MediaWiki
121+import Text.Pandoc.Writers.Textile
120122import Text.Pandoc.Templates
121123import Text.Pandoc.Parsing
122124import Text.Pandoc.Shared
src/Text/Pandoc/Writers/Textile.hsnew file+415
@@ -0,0 +1,415 @@
1+{-
2+Copyright (C) 2010 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.Writers.Textile
21+ Copyright : Copyright (C) 2010 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+Conversion of 'Pandoc' documents to Textile markup.
29+
30+Textile: <http://thresholdstate.com/articles/4312/the-textile-reference-manual>
31+-}
32+module Text.Pandoc.Writers.Textile ( writeTextile ) where
33+import Text.Pandoc.Definition
34+import Text.Pandoc.Shared
35+import Text.Pandoc.Templates (renderTemplate)
36+import Text.Pandoc.XML ( escapeStringForXML )
37+import Data.List ( intercalate )
38+import Control.Monad.State
39+import Data.Char ( isSpace )
40+
41+data WriterState = WriterState {
42+ stNotes :: [String] -- Footnotes
43+ , stListLevel :: [Char] -- String at beginning of list items, e.g. "**"
44+ , stUseTags :: Bool -- True if we should use HTML tags because we're in a complex list
45+ }
46+
47+-- | Convert Pandoc to Textile.
48+writeTextile :: WriterOptions -> Pandoc -> String
49+writeTextile opts document =
50+ evalState (pandocToTextile opts document)
51+ (WriterState { stNotes = [], stListLevel = [], stUseTags = False })
52+
53+-- | Return Textile representation of document.
54+pandocToTextile :: WriterOptions -> Pandoc -> State WriterState String
55+pandocToTextile opts (Pandoc _ blocks) = do
56+ body <- blockListToTextile opts blocks
57+ notes <- liftM (unlines . reverse . stNotes) get
58+ let main = body ++ if null notes then "" else ("\n\n" ++ notes)
59+ let context = writerVariables opts ++ [ ("body", main) ]
60+ if writerStandalone opts
61+ then return $ renderTemplate context $ writerTemplate opts
62+ else return main
63+
64+withUseTags :: State WriterState a -> State WriterState a
65+withUseTags action = do
66+ oldUseTags <- liftM stUseTags get
67+ modify $ \s -> s { stUseTags = True }
68+ result <- action
69+ modify $ \s -> s { stUseTags = oldUseTags }
70+ return result
71+
72+-- | Escape one character as needed for Textile.
73+escapeCharForTextile :: Char -> String
74+escapeCharForTextile x = case x of
75+ '&' -> "&amp;"
76+ '<' -> "&lt;"
77+ '>' -> "&gt;"
78+ '"' -> "&quot;"
79+ '*' -> "&#42;"
80+ '_' -> "&#95;"
81+ '@' -> "&#64;"
82+ '|' -> "&#124;"
83+ c -> [c]
84+
85+-- | Escape string as needed for Textile.
86+escapeStringForTextile :: String -> String
87+escapeStringForTextile = concatMap escapeCharForTextile
88+
89+-- | Convert Pandoc block element to Textile.
90+blockToTextile :: WriterOptions -- ^ Options
91+ -> Block -- ^ Block element
92+ -> State WriterState String
93+
94+blockToTextile _ Null = return ""
95+
96+blockToTextile opts (Plain inlines) =
97+ inlineListToTextile opts inlines
98+
99+blockToTextile opts (Para [Image txt (src,tit)]) = do
100+ capt <- blockToTextile opts (Para txt)
101+ im <- inlineToTextile opts (Image txt (src,tit))
102+ return $ im ++ "\n" ++ capt
103+
104+blockToTextile opts (Para inlines) = do
105+ useTags <- liftM stUseTags get
106+ listLevel <- liftM stListLevel get
107+ contents <- inlineListToTextile opts inlines
108+ return $ if useTags
109+ then " <p>" ++ contents ++ "</p>"
110+ else contents ++ if null listLevel then "\n" else ""
111+
112+blockToTextile _ (RawHtml str) = return str
113+
114+blockToTextile _ HorizontalRule = return "<hr />\n"
115+
116+blockToTextile opts (Header level inlines) = do
117+ contents <- inlineListToTextile opts inlines
118+ let prefix = 'h' : (show level ++ ". ")
119+ return $ prefix ++ contents ++ "\n"
120+
121+blockToTextile _ (CodeBlock (_,classes,_) str) =
122+ return $ "bc" ++ classes' ++ dots ++ escapeStringForXML str ++ "\n"
123+ where classes' = if null classes
124+ then ""
125+ else "(" ++ unwords classes ++ ")"
126+ dots = if any isBlank (lines str)
127+ then ".. "
128+ else ". "
129+ isBlank = all isSpace
130+
131+blockToTextile opts (BlockQuote bs@[Para _]) = do
132+ contents <- blockListToTextile opts bs
133+ return $ "bq. " ++ contents
134+
135+blockToTextile opts (BlockQuote blocks) = do
136+ contents <- blockListToTextile opts blocks
137+ return $ "<blockquote>\n\n" ++ contents ++ "\n</blockquote>\n"
138+
139+blockToTextile opts (Table [] aligns widths headers rows') |
140+ all (==0) widths && all (`elem` [AlignLeft,AlignDefault]) aligns = do
141+ hs <- mapM (liftM (("_. " ++) . stripTrailingNewlines) . blockListToTextile opts) headers
142+ let cellsToRow cells = "|" ++ intercalate "|" cells ++ "|"
143+ let header = if all null headers then "" else cellsToRow hs
144+ let rowToCells = mapM (liftM stripTrailingNewlines . blockListToTextile opts)
145+ bs <- mapM rowToCells rows'
146+ let body = unlines $ map cellsToRow bs
147+ return $ header ++ "\n" ++ body ++ "\n"
148+
149+blockToTextile opts (Table capt aligns widths headers rows') = do
150+ let alignStrings = map alignmentToString aligns
151+ captionDoc <- if null capt
152+ then return ""
153+ else do
154+ c <- inlineListToTextile opts capt
155+ return $ " <caption>" ++ c ++ "</caption>\n"
156+ let percent w = show (truncate (100*w) :: Integer) ++ "%"
157+ let coltags = if all (== 0.0) widths
158+ then ""
159+ else unlines $ map
160+ (\w -> " <col width=\"" ++ percent w ++ "\" />") widths
161+ head' <- if all null headers
162+ then return ""
163+ else do
164+ hs <- tableRowToTextile opts alignStrings 0 headers
165+ return $ " <thead>\n" ++ hs ++ "\n </thead>\n"
166+ body' <- zipWithM (tableRowToTextile opts alignStrings) [1..] rows'
167+ return $ " <table>\n" ++ captionDoc ++ coltags ++ head' ++
168+ " <tbody>\n" ++ unlines body' ++ " </tbody>\n </table>\n"
169+
170+blockToTextile opts x@(BulletList items) = do
171+ oldUseTags <- liftM stUseTags get
172+ let useTags = oldUseTags || not (isSimpleList x)
173+ if useTags
174+ then do
175+ contents <- withUseTags $ mapM (listItemToTextile opts) items
176+ return $ " <ul>\n" ++ vcat contents ++ " </ul>\n"
177+ else do
178+ modify $ \s -> s { stListLevel = stListLevel s ++ "*" }
179+ level <- get >>= return . length . stListLevel
180+ contents <- mapM (listItemToTextile opts) items
181+ modify $ \s -> s { stListLevel = init (stListLevel s) }
182+ return $ vcat contents ++ (if level > 1 then "" else "\n")
183+
184+blockToTextile opts x@(OrderedList attribs items) = do
185+ oldUseTags <- liftM stUseTags get
186+ let useTags = oldUseTags || not (isSimpleList x)
187+ if useTags
188+ then do
189+ contents <- withUseTags $ mapM (listItemToTextile opts) items
190+ return $ " <ol" ++ listAttribsToString attribs ++ ">\n" ++ vcat contents ++
191+ " </ol>\n"
192+ else do
193+ modify $ \s -> s { stListLevel = stListLevel s ++ "#" }
194+ level <- get >>= return . length . stListLevel
195+ contents <- mapM (listItemToTextile opts) items
196+ modify $ \s -> s { stListLevel = init (stListLevel s) }
197+ return $ vcat contents ++ (if level > 1 then "" else "\n")
198+
199+blockToTextile opts (DefinitionList items) = do
200+ contents <- withUseTags $ mapM (definitionListItemToTextile opts) items
201+ return $ " <dl>\n" ++ vcat contents ++ " </dl>\n"
202+
203+-- Auxiliary functions for lists:
204+
205+-- | Convert ordered list attributes to HTML attribute string
206+listAttribsToString :: ListAttributes -> String
207+listAttribsToString (startnum, numstyle, _) =
208+ let numstyle' = camelCaseToHyphenated $ show numstyle
209+ in (if startnum /= 1
210+ then " start=\"" ++ show startnum ++ "\""
211+ else "") ++
212+ (if numstyle /= DefaultStyle
213+ then " style=\"list-style-type: " ++ numstyle' ++ ";\""
214+ else "")
215+
216+-- | Convert bullet or ordered list item (list of blocks) to Textile.
217+listItemToTextile :: WriterOptions -> [Block] -> State WriterState String
218+listItemToTextile opts items = do
219+ contents <- blockListToTextile opts items
220+ useTags <- get >>= return . stUseTags
221+ if useTags
222+ then return $ " <li>" ++ contents ++ "</li>"
223+ else do
224+ marker <- get >>= return . stListLevel
225+ return $ marker ++ " " ++ contents
226+
227+-- | Convert definition list item (label, list of blocks) to Textile.
228+definitionListItemToTextile :: WriterOptions
229+ -> ([Inline],[[Block]])
230+ -> State WriterState String
231+definitionListItemToTextile opts (label, items) = do
232+ labelText <- inlineListToTextile opts label
233+ contents <- mapM (blockListToTextile opts) items
234+ return $ " <dt>" ++ labelText ++ "</dt>\n" ++
235+ (intercalate "\n" $ map (\d -> " <dd>" ++ d ++ "</dd>") contents)
236+
237+-- | True if the list can be handled by simple wiki markup, False if HTML tags will be needed.
238+isSimpleList :: Block -> Bool
239+isSimpleList x =
240+ case x of
241+ BulletList items -> all isSimpleListItem items
242+ OrderedList (num, sty, _) items -> all isSimpleListItem items &&
243+ num == 1 && sty `elem` [DefaultStyle, Decimal]
244+ _ -> False
245+
246+-- | True if list item can be handled with the simple wiki syntax. False if
247+-- HTML tags will be needed.
248+isSimpleListItem :: [Block] -> Bool
249+isSimpleListItem [] = True
250+isSimpleListItem [x] =
251+ case x of
252+ Plain _ -> True
253+ Para _ -> True
254+ BulletList _ -> isSimpleList x
255+ OrderedList _ _ -> isSimpleList x
256+ _ -> False
257+isSimpleListItem [x, y] | isPlainOrPara x =
258+ case y of
259+ BulletList _ -> isSimpleList y
260+ OrderedList _ _ -> isSimpleList y
261+ _ -> False
262+isSimpleListItem _ = False
263+
264+isPlainOrPara :: Block -> Bool
265+isPlainOrPara (Plain _) = True
266+isPlainOrPara (Para _) = True
267+isPlainOrPara _ = False
268+
269+-- | Concatenates strings with line breaks between them.
270+vcat :: [String] -> String
271+vcat = intercalate "\n"
272+
273+-- Auxiliary functions for tables. (TODO: these are common to HTML, MediaWiki,
274+-- and Textile writers, and should be abstracted out.)
275+
276+tableRowToTextile :: WriterOptions
277+ -> [String]
278+ -> Int
279+ -> [[Block]]
280+ -> State WriterState String
281+tableRowToTextile opts alignStrings rownum cols' = do
282+ let celltype = if rownum == 0 then "th" else "td"
283+ let rowclass = case rownum of
284+ 0 -> "header"
285+ x | x `rem` 2 == 1 -> "odd"
286+ _ -> "even"
287+ cols'' <- sequence $ zipWith
288+ (\alignment item -> tableItemToTextile opts celltype alignment item)
289+ alignStrings cols'
290+ return $ "<tr class=\"" ++ rowclass ++ "\">\n" ++ unlines cols'' ++ "</tr>"
291+
292+alignmentToString :: Alignment -> [Char]
293+alignmentToString alignment = case alignment of
294+ AlignLeft -> "left"
295+ AlignRight -> "right"
296+ AlignCenter -> "center"
297+ AlignDefault -> "left"
298+
299+tableItemToTextile :: WriterOptions
300+ -> String
301+ -> String
302+ -> [Block]
303+ -> State WriterState String
304+tableItemToTextile opts celltype align' item = do
305+ let mkcell x = "<" ++ celltype ++ " align=\"" ++ align' ++ "\">" ++
306+ x ++ "</" ++ celltype ++ ">"
307+ contents <- blockListToTextile opts item
308+ return $ mkcell contents
309+
310+-- | Convert list of Pandoc block elements to Textile.
311+blockListToTextile :: WriterOptions -- ^ Options
312+ -> [Block] -- ^ List of block elements
313+ -> State WriterState String
314+blockListToTextile opts blocks =
315+ mapM (blockToTextile opts) blocks >>= return . vcat
316+
317+-- | Convert list of Pandoc inline elements to Textile.
318+inlineListToTextile :: WriterOptions -> [Inline] -> State WriterState String
319+inlineListToTextile opts lst =
320+ mapM (inlineToTextile opts) lst >>= return . concat
321+
322+-- | Convert Pandoc inline element to Textile.
323+inlineToTextile :: WriterOptions -> Inline -> State WriterState String
324+
325+inlineToTextile opts (Emph lst) = do
326+ contents <- inlineListToTextile opts lst
327+ return $ if '_' `elem` contents
328+ then "<em>" ++ contents ++ "</em>"
329+ else "_" ++ contents ++ "_"
330+
331+inlineToTextile opts (Strong lst) = do
332+ contents <- inlineListToTextile opts lst
333+ return $ if '*' `elem` contents
334+ then "<strong>" ++ contents ++ "</strong>"
335+ else "*" ++ contents ++ "*"
336+
337+inlineToTextile opts (Strikeout lst) = do
338+ contents <- inlineListToTextile opts lst
339+ return $ if '-' `elem` contents
340+ then "<del>" ++ contents ++ "</del>"
341+ else "-" ++ contents ++ "-"
342+
343+inlineToTextile opts (Superscript lst) = do
344+ contents <- inlineListToTextile opts lst
345+ return $ if '^' `elem` contents
346+ then "<sup>" ++ contents ++ "</sup>"
347+ else "[^" ++ contents ++ "^]"
348+
349+inlineToTextile opts (Subscript lst) = do
350+ contents <- inlineListToTextile opts lst
351+ return $ if '~' `elem` contents
352+ then "<sub>" ++ contents ++ "</sub>"
353+ else "[~" ++ contents ++ "~]"
354+
355+inlineToTextile opts (SmallCaps lst) = inlineListToTextile opts lst
356+
357+inlineToTextile opts (Quoted SingleQuote lst) = do
358+ contents <- inlineListToTextile opts lst
359+ return $ "'" ++ contents ++ "'"
360+
361+inlineToTextile opts (Quoted DoubleQuote lst) = do
362+ contents <- inlineListToTextile opts lst
363+ return $ "\"" ++ contents ++ "\""
364+
365+inlineToTextile opts (Cite _ lst) = inlineListToTextile opts lst
366+
367+inlineToTextile _ EmDash = return " -- "
368+
369+inlineToTextile _ EnDash = return " - "
370+
371+inlineToTextile _ Apostrophe = return "'"
372+
373+inlineToTextile _ Ellipses = return "..."
374+
375+inlineToTextile _ (Code str) =
376+ return $ if '@' `elem` str
377+ then "<tt>" ++ escapeStringForXML str ++ "</tt>"
378+ else "@" ++ escapeStringForXML str ++ "@"
379+
380+inlineToTextile _ (Str str) = return $ escapeStringForTextile str
381+
382+inlineToTextile _ (Math _ str) =
383+ return $ "<span class=\"math\">" ++ escapeStringForXML str ++ "</math>"
384+
385+inlineToTextile _ (TeX _) = return ""
386+
387+inlineToTextile _ (HtmlInline str) = return str
388+
389+inlineToTextile _ (LineBreak) = return "\n"
390+
391+inlineToTextile _ Space = return " "
392+
393+inlineToTextile opts (Link txt (src, _)) = do
394+ label <- case txt of
395+ [Code s] -> return s
396+ _ -> inlineListToTextile opts txt
397+ return $ "\"" ++ label ++ "\":" ++ src
398+
399+inlineToTextile opts (Image alt (source, tit)) = do
400+ alt' <- inlineListToTextile opts alt
401+ let txt = if null tit
402+ then if null alt'
403+ then ""
404+ else "(" ++ alt' ++ ")"
405+ else "(" ++ tit ++ ")"
406+ return $ "!" ++ source ++ txt ++ "!"
407+
408+inlineToTextile opts (Note contents) = do
409+ curNotes <- liftM stNotes get
410+ let newnum = length curNotes + 1
411+ contents' <- blockListToTextile opts contents
412+ let thisnote = "fn" ++ show newnum ++ ". " ++ contents' ++ "\n"
413+ modify $ \s -> s { stNotes = thisnote : curNotes }
414+ return $ "[" ++ show newnum ++ "]"
415+ -- note - may not work for notes with multiple blocks
src/pandoc.hs+1
@@ -122,6 +122,7 @@ writers = [("native" , writeNative)
122122 ,("rst" , writeRST)
123123 ,("rst+lhs" , writeRST)
124124 ,("mediawiki" , writeMediaWiki)
125+ ,("textile" , writeTextile)
125126 ,("rtf" , writeRTF)
126127 ]
127128
templates/textile.templatenew file+12
@@ -0,0 +1,12 @@
1+$if(legacy-header)$
2+$legacy-header$
3+$endif$
4+$for(include-before)$
5+$include-before$
6+
7+$endfor$
8+$body$
9+$for(include-after)$
10+
11+$include-after$
12+$endfor$
tests/RunTests.hs+1
@@ -60,6 +60,7 @@ writerFormats = [ "native"
6060 , "markdown"
6161 , "rst"
6262 , "mediawiki"
63+ , "textile"
6364 , "rtf"
6465 ]
6566
tests/tables.textilenew file+212
@@ -0,0 +1,212 @@
1+Simple table with caption:
2+
3+ <table>
4+ <caption>Demonstration of simple table syntax.</caption>
5+ <thead>
6+<tr class="header">
7+<th align="right">Right</th>
8+<th align="left">Left</th>
9+<th align="center">Center</th>
10+<th align="left">Default</th>
11+</tr>
12+ </thead>
13+ <tbody>
14+<tr class="odd">
15+<td align="right">12</td>
16+<td align="left">12</td>
17+<td align="center">12</td>
18+<td align="left">12</td>
19+</tr>
20+<tr class="even">
21+<td align="right">123</td>
22+<td align="left">123</td>
23+<td align="center">123</td>
24+<td align="left">123</td>
25+</tr>
26+<tr class="odd">
27+<td align="right">1</td>
28+<td align="left">1</td>
29+<td align="center">1</td>
30+<td align="left">1</td>
31+</tr>
32+ </tbody>
33+ </table>
34+
35+Simple table without caption:
36+
37+ <table>
38+ <thead>
39+<tr class="header">
40+<th align="right">Right</th>
41+<th align="left">Left</th>
42+<th align="center">Center</th>
43+<th align="left">Default</th>
44+</tr>
45+ </thead>
46+ <tbody>
47+<tr class="odd">
48+<td align="right">12</td>
49+<td align="left">12</td>
50+<td align="center">12</td>
51+<td align="left">12</td>
52+</tr>
53+<tr class="even">
54+<td align="right">123</td>
55+<td align="left">123</td>
56+<td align="center">123</td>
57+<td align="left">123</td>
58+</tr>
59+<tr class="odd">
60+<td align="right">1</td>
61+<td align="left">1</td>
62+<td align="center">1</td>
63+<td align="left">1</td>
64+</tr>
65+ </tbody>
66+ </table>
67+
68+Simple table indented two spaces:
69+
70+ <table>
71+ <caption>Demonstration of simple table syntax.</caption>
72+ <thead>
73+<tr class="header">
74+<th align="right">Right</th>
75+<th align="left">Left</th>
76+<th align="center">Center</th>
77+<th align="left">Default</th>
78+</tr>
79+ </thead>
80+ <tbody>
81+<tr class="odd">
82+<td align="right">12</td>
83+<td align="left">12</td>
84+<td align="center">12</td>
85+<td align="left">12</td>
86+</tr>
87+<tr class="even">
88+<td align="right">123</td>
89+<td align="left">123</td>
90+<td align="center">123</td>
91+<td align="left">123</td>
92+</tr>
93+<tr class="odd">
94+<td align="right">1</td>
95+<td align="left">1</td>
96+<td align="center">1</td>
97+<td align="left">1</td>
98+</tr>
99+ </tbody>
100+ </table>
101+
102+Multiline table with caption:
103+
104+ <table>
105+ <caption>Here's the caption. It may span multiple lines.</caption>
106+ <col width="15%" />
107+ <col width="13%" />
108+ <col width="16%" />
109+ <col width="33%" />
110+ <thead>
111+<tr class="header">
112+<th align="center">Centered Header</th>
113+<th align="left">Left Aligned</th>
114+<th align="right">Right Aligned</th>
115+<th align="left">Default aligned</th>
116+</tr>
117+ </thead>
118+ <tbody>
119+<tr class="odd">
120+<td align="center">First</td>
121+<td align="left">row</td>
122+<td align="right">12.0</td>
123+<td align="left">Example of a row that spans multiple lines.</td>
124+</tr>
125+<tr class="even">
126+<td align="center">Second</td>
127+<td align="left">row</td>
128+<td align="right">5.0</td>
129+<td align="left">Here's another one. Note the blank line between rows.</td>
130+</tr>
131+ </tbody>
132+ </table>
133+
134+Multiline table without caption:
135+
136+ <table>
137+ <col width="15%" />
138+ <col width="13%" />
139+ <col width="16%" />
140+ <col width="33%" />
141+ <thead>
142+<tr class="header">
143+<th align="center">Centered Header</th>
144+<th align="left">Left Aligned</th>
145+<th align="right">Right Aligned</th>
146+<th align="left">Default aligned</th>
147+</tr>
148+ </thead>
149+ <tbody>
150+<tr class="odd">
151+<td align="center">First</td>
152+<td align="left">row</td>
153+<td align="right">12.0</td>
154+<td align="left">Example of a row that spans multiple lines.</td>
155+</tr>
156+<tr class="even">
157+<td align="center">Second</td>
158+<td align="left">row</td>
159+<td align="right">5.0</td>
160+<td align="left">Here's another one. Note the blank line between rows.</td>
161+</tr>
162+ </tbody>
163+ </table>
164+
165+Table without column headers:
166+
167+ <table>
168+ <tbody>
169+<tr class="odd">
170+<td align="right">12</td>
171+<td align="left">12</td>
172+<td align="center">12</td>
173+<td align="right">12</td>
174+</tr>
175+<tr class="even">
176+<td align="right">123</td>
177+<td align="left">123</td>
178+<td align="center">123</td>
179+<td align="right">123</td>
180+</tr>
181+<tr class="odd">
182+<td align="right">1</td>
183+<td align="left">1</td>
184+<td align="center">1</td>
185+<td align="right">1</td>
186+</tr>
187+ </tbody>
188+ </table>
189+
190+Multiline table without column headers:
191+
192+ <table>
193+ <col width="15%" />
194+ <col width="13%" />
195+ <col width="16%" />
196+ <col width="33%" />
197+ <tbody>
198+<tr class="odd">
199+<td align="center">First</td>
200+<td align="left">row</td>
201+<td align="right">12.0</td>
202+<td align="left">Example of a row that spans multiple lines.</td>
203+</tr>
204+<tr class="even">
205+<td align="center">Second</td>
206+<td align="left">row</td>
207+<td align="right">5.0</td>
208+<td align="left">Here's another one. Note the blank line between rows.</td>
209+</tr>
210+ </tbody>
211+ </table>
212+
tests/writer.textilenew file+677
@@ -0,0 +1,677 @@
1+This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite.
2+
3+<hr />
4+
5+h1. Headers
6+
7+h2. Level 2 with an "embedded link":/url
8+
9+h3. Level 3 with _emphasis_
10+
11+h4. Level 4
12+
13+h5. Level 5
14+
15+h1. Level 1
16+
17+h2. Level 2 with _emphasis_
18+
19+h3. Level 3
20+
21+with no blank line
22+
23+h2. Level 2
24+
25+with no blank line
26+
27+<hr />
28+
29+h1. Paragraphs
30+
31+Here's a regular paragraph.
32+
33+In Markdown 1.0.0 and earlier. Version 8. This line turns into a list item. Because a hard-wrapped line in the middle of a paragraph looked like a list item.
34+
35+Here's one with a bullet. &#42; criminey.
36+
37+There should be a hard line break
38+here.
39+
40+<hr />
41+
42+h1. Block Quotes
43+
44+E-mail style:
45+
46+bq. This is a block quote. It is pretty short.
47+
48+<blockquote>
49+
50+Code in a block quote:
51+
52+bc. sub status {
53+ print &quot;working&quot;;
54+}
55+
56+A list:
57+
58+# item one
59+# item two
60+
61+Nested block quotes:
62+
63+bq. nested
64+
65+bq. nested
66+
67+</blockquote>
68+
69+This should not be a block quote: 2 &gt; 1.
70+
71+And a following paragraph.
72+
73+<hr />
74+
75+h1. Code Blocks
76+
77+Code:
78+
79+bc.. ---- (should be four hyphens)
80+
81+sub status {
82+ print &quot;working&quot;;
83+}
84+
85+this code block is indented by one tab
86+
87+And:
88+
89+bc.. this code block is indented by two tabs
90+
91+These should not be escaped: \$ \\ \&gt; \[ \{
92+
93+<hr />
94+
95+h1. Lists
96+
97+h2. Unordered
98+
99+Asterisks tight:
100+
101+* asterisk 1
102+* asterisk 2
103+* asterisk 3
104+
105+Asterisks loose:
106+
107+* asterisk 1
108+* asterisk 2
109+* asterisk 3
110+
111+Pluses tight:
112+
113+* Plus 1
114+* Plus 2
115+* Plus 3
116+
117+Pluses loose:
118+
119+* Plus 1
120+* Plus 2
121+* Plus 3
122+
123+Minuses tight:
124+
125+* Minus 1
126+* Minus 2
127+* Minus 3
128+
129+Minuses loose:
130+
131+* Minus 1
132+* Minus 2
133+* Minus 3
134+
135+h2. Ordered
136+
137+Tight:
138+
139+# First
140+# Second
141+# Third
142+
143+and:
144+
145+# One
146+# Two
147+# Three
148+
149+Loose using tabs:
150+
151+# First
152+# Second
153+# Third
154+
155+and using spaces:
156+
157+# One
158+# Two
159+# Three
160+
161+Multiple paragraphs:
162+
163+ <ol style="list-style-type: decimal;">
164+ <li> <p>Item 1, graf one.</p>
165+ <p>Item 1. graf two. The quick brown fox jumped over the lazy dog's back.</p></li>
166+ <li> <p>Item 2.</p></li>
167+ <li> <p>Item 3.</p></li> </ol>
168+
169+h2. Nested
170+
171+* Tab
172+** Tab
173+*** Tab
174+
175+Here's another:
176+
177+# First
178+# Second:
179+#* Fee
180+#* Fie
181+#* Foe
182+# Third
183+
184+Same thing but with paragraphs:
185+
186+# First
187+# Second:
188+#* Fee
189+#* Fie
190+#* Foe
191+# Third
192+
193+h2. Tabs and spaces
194+
195+* this is a list item indented with tabs
196+* this is a list item indented with spaces
197+** this is an example list item indented with tabs
198+** this is an example list item indented with spaces
199+
200+h2. Fancy list markers
201+
202+ <ol start="2" style="list-style-type: decimal;">
203+ <li>begins with 2</li>
204+ <li> <p>and now 3</p>
205+ <p>with a continuation</p>
206+ <ol start="4" style="list-style-type: lower-roman;">
207+ <li>sublist with roman numerals, starting with 4</li>
208+ <li>more items
209+ <ol style="list-style-type: upper-alpha;">
210+ <li>a subsublist</li>
211+ <li>a subsublist</li> </ol>
212+</li> </ol>
213+</li> </ol>
214+
215+Nesting:
216+
217+ <ol style="list-style-type: upper-alpha;">
218+ <li>Upper Alpha
219+ <ol style="list-style-type: upper-roman;">
220+ <li>Upper Roman.
221+ <ol start="6" style="list-style-type: decimal;">
222+ <li>Decimal start with 6
223+ <ol start="3" style="list-style-type: lower-alpha;">
224+ <li>Lower alpha with paren</li> </ol>
225+</li> </ol>
226+</li> </ol>
227+</li> </ol>
228+
229+Autonumbering:
230+
231+# Autonumber.
232+# More.
233+## Nested.
234+
235+Should not be a list item:
236+
237+M.A. 2007
238+
239+B. Williams
240+
241+<hr />
242+
243+h1. Definition Lists
244+
245+Tight using spaces:
246+
247+ <dl>
248+ <dt>apple</dt>
249+ <dd>red fruit</dd>
250+ <dt>orange</dt>
251+ <dd>orange fruit</dd>
252+ <dt>banana</dt>
253+ <dd>yellow fruit</dd> </dl>
254+
255+Tight using tabs:
256+
257+ <dl>
258+ <dt>apple</dt>
259+ <dd>red fruit</dd>
260+ <dt>orange</dt>
261+ <dd>orange fruit</dd>
262+ <dt>banana</dt>
263+ <dd>yellow fruit</dd> </dl>
264+
265+Loose:
266+
267+ <dl>
268+ <dt>apple</dt>
269+ <dd> <p>red fruit</p></dd>
270+ <dt>orange</dt>
271+ <dd> <p>orange fruit</p></dd>
272+ <dt>banana</dt>
273+ <dd> <p>yellow fruit</p></dd> </dl>
274+
275+Multiple blocks with italics:
276+
277+ <dl>
278+ <dt>_apple_</dt>
279+ <dd> <p>red fruit</p>
280+ <p>contains seeds, crisp, pleasant to taste</p></dd>
281+ <dt>_orange_</dt>
282+ <dd> <p>orange fruit</p>
283+bc. { orange code block }
284+
285+bq. <p>orange block quote</p></dd> </dl>
286+
287+Multiple definitions, tight:
288+
289+ <dl>
290+ <dt>apple</dt>
291+ <dd>red fruit</dd>
292+ <dd>computer</dd>
293+ <dt>orange</dt>
294+ <dd>orange fruit</dd>
295+ <dd>bank</dd> </dl>
296+
297+Multiple definitions, loose:
298+
299+ <dl>
300+ <dt>apple</dt>
301+ <dd> <p>red fruit</p></dd>
302+ <dd> <p>computer</p></dd>
303+ <dt>orange</dt>
304+ <dd> <p>orange fruit</p></dd>
305+ <dd> <p>bank</p></dd> </dl>
306+
307+Blank line after term, indented marker, alternate markers:
308+
309+ <dl>
310+ <dt>apple</dt>
311+ <dd> <p>red fruit</p></dd>
312+ <dd> <p>computer</p></dd>
313+ <dt>orange</dt>
314+ <dd> <p>orange fruit</p>
315+ <ol style="list-style-type: decimal;">
316+ <li>sublist</li>
317+ <li>sublist</li> </ol>
318+</dd> </dl>
319+
320+h1. HTML Blocks
321+
322+Simple block on one line:
323+
324+<div>
325+foo
326+</div>
327+
328+And nested without indentation:
329+
330+<div>
331+<div>
332+<div>
333+foo
334+</div>
335+</div>
336+<div>
337+bar
338+</div>
339+</div>
340+
341+Interpreted markdown in a table:
342+
343+<table>
344+<tr>
345+<td>
346+This is _emphasized_
347+</td>
348+<td>
349+And this is *strong*
350+</td>
351+</tr>
352+</table>
353+
354+<script type="text/javascript">document.write('This *should not* be interpreted as markdown');</script>
355+
356+Here's a simple block:
357+
358+<div>
359+
360+foo
361+</div>
362+
363+This should be a code block, though:
364+
365+bc. &lt;div&gt;
366+ foo
367+&lt;/div&gt;
368+
369+As should this:
370+
371+bc. &lt;div&gt;foo&lt;/div&gt;
372+
373+Now, nested:
374+
375+<div>
376+ <div>
377+ <div>
378+
379+foo
380+</div>
381+ </div>
382+</div>
383+
384+This should just be an HTML comment:
385+
386+<!-- Comment -->
387+
388+Multiline:
389+
390+<!--
391+Blah
392+Blah
393+-->
394+
395+<!--
396+ This is another comment.
397+-->
398+
399+Code block:
400+
401+bc. &lt;!-- Comment --&gt;
402+
403+Just plain comment, with trailing spaces on the line:
404+
405+<!-- foo -->
406+
407+Code:
408+
409+bc. &lt;hr /&gt;
410+
411+Hr's:
412+
413+<hr>
414+
415+<hr />
416+
417+<hr />
418+
419+<hr>
420+
421+<hr />
422+
423+<hr />
424+
425+<hr class="foo" id="bar" />
426+
427+<hr class="foo" id="bar" />
428+
429+<hr class="foo" id="bar">
430+
431+<hr />
432+
433+h1. Inline Markup
434+
435+This is _emphasized_, and so _is this_.
436+
437+This is *strong*, and so *is this*.
438+
439+An _"emphasized link":/url_.
440+
441+*_This is strong and em._*
442+
443+So is *_this_* word.
444+
445+*_This is strong and em._*
446+
447+So is *_this_* word.
448+
449+This is code: @&gt;@, @$@, @\@, @\$@, @&lt;html&gt;@.
450+
451+-This is _strikeout_.-
452+
453+Superscripts: a[^bc^]d a[^_hello_^] a[^hello there^].
454+
455+Subscripts: H[~2~]O, H[~23~]O, H[~many of them~]O.
456+
457+These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.
458+
459+<hr />
460+
461+h1. Smart quotes, ellipses, dashes
462+
463+"Hello," said the spider. "'Shelob' is my name."
464+
465+'A', 'B', and 'C' are letters.
466+
467+'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.'
468+
469+'He said, "I want to go."' Were you alive in the 70's?
470+
471+Here is some quoted '@code@' and a ""quoted link":http://example.com/?foo=1&bar=2".
472+
473+Some dashes: one -- two -- three -- four -- five.
474+
475+Dashes between numbers: 5 - 7, 255 - 66, 1987 - 1999.
476+
477+Ellipses...and...and....
478+
479+<hr />
480+
481+h1. LaTeX
482+
483+*
484+* <span class="math">2+2=4</math>
485+* <span class="math">x \in y</math>
486+* <span class="math">\alpha \wedge \omega</math>
487+* <span class="math">223</math>
488+* <span class="math">p</math>-Tree
489+* Here's some display math: <span class="math">\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}</math>
490+* Here's one that has a line break in it: <span class="math">\alpha + \omega \times x^2</math>.
491+
492+These shouldn't be math:
493+
494+* To get the famous equation, write @$e = mc^2$@.
495+* $22,000 is a _lot_ of money. So is $34,000. (It worked if "lot" is emphasized.)
496+* Shoes ($20) and socks ($5).
497+* Escaped @$@: $73 _this should be emphasized_ 23$.
498+
499+Here's a LaTeX table:
500+
501+
502+
503+<hr />
504+
505+h1. Special Characters
506+
507+Here is some unicode:
508+
509+* I hat: Î
510+* o umlaut: ö
511+* section: §
512+* set membership: ∈
513+* copyright: ©
514+
515+AT&amp;T has an ampersand in their name.
516+
517+AT&amp;T is another way to write it.
518+
519+This &amp; that.
520+
521+4 &lt; 5.
522+
523+6 &gt; 5.
524+
525+Backslash: \
526+
527+Backtick: `
528+
529+Asterisk: &#42;
530+
531+Underscore: &#95;
532+
533+Left brace: {
534+
535+Right brace: }
536+
537+Left bracket: [
538+
539+Right bracket: ]
540+
541+Left paren: (
542+
543+Right paren: )
544+
545+Greater-than: &gt;
546+
547+Hash: #
548+
549+Period: .
550+
551+Bang: !
552+
553+Plus: +
554+
555+Minus: -
556+
557+<hr />
558+
559+h1. Links
560+
561+h2. Explicit
562+
563+Just a "URL":/url/.
564+
565+"URL and title":/url/.
566+
567+"URL and title":/url/.
568+
569+"URL and title":/url/.
570+
571+"URL and title":/url/
572+
573+"URL and title":/url/
574+
575+"with&#95;underscore":/url/with_underscore
576+
577+"Email link":mailto:nobody@nowhere.net
578+
579+"Empty":.
580+
581+h2. Reference
582+
583+Foo "bar":/url/.
584+
585+Foo "bar":/url/.
586+
587+Foo "bar":/url/.
588+
589+With "embedded [brackets]":/url/.
590+
591+"b":/url/ by itself should be a link.
592+
593+Indented "once":/url.
594+
595+Indented "twice":/url.
596+
597+Indented "thrice":/url.
598+
599+This should [not][] be a link.
600+
601+bc. [not]: /url
602+
603+Foo "bar":/url/.
604+
605+Foo "biz":/url/.
606+
607+h2. With ampersands
608+
609+Here's a "link with an ampersand in the URL":http://example.com/?foo=1&bar=2.
610+
611+Here's a link with an amersand in the link text: "AT&amp;T":http://att.com/.
612+
613+Here's an "inline link":/script?foo=1&bar=2.
614+
615+Here's an "inline link in pointy braces":/script?foo=1&bar=2.
616+
617+h2. Autolinks
618+
619+With an ampersand: "http://example.com/?foo=1&bar=2":http://example.com/?foo=1&bar=2
620+
621+* In a list?
622+* "http://example.com/":http://example.com/
623+* It should.
624+
625+An e-mail address: "nobody@nowhere.net":mailto:nobody@nowhere.net
626+
627+bq. Blockquoted: "http://example.com/":http://example.com/
628+
629+Auto-links should not occur here: @&lt;http://example.com/&gt;@
630+
631+bc. or here: &lt;http://example.com/&gt;
632+
633+<hr />
634+
635+h1. Images
636+
637+From "Voyage dans la Lune" by Georges Melies (1902):
638+
639+!lalune.jpg(Voyage dans la Lune)!
640+lalune
641+
642+Here is a movie !movie.jpg(movie)! icon.
643+
644+<hr />
645+
646+h1. Footnotes
647+
648+Here is a footnote reference,[1] and another.[2] This should _not_ be a footnote reference, because it contains a space.[^my note] Here is an inline note.[3]
649+
650+bq. Notes can go in quotes.[4]
651+
652+# And in list items.[5]
653+
654+This paragraph should not be part of the note, as it is not indented.
655+
656+
657+fn1. Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.
658+
659+
660+fn2. Here's the long note. This one contains multiple blocks.
661+
662+Subsequent blocks are indented to show that they belong to the footnote (as with list items).
663+
664+bc. { &lt;code&gt; }
665+
666+If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.
667+
668+
669+fn3. This is _easier_ to type. Inline notes may contain "links":http://google.com and @]@ verbatim characters, as well as [bracketed text].
670+
671+
672+fn4. In quote.
673+
674+
675+fn5. In list.
676+
677+