git.iko.soy/typeable/pandoc
on master help sign in
Added Textile writer module.
commitd073b168924a8391c771ecf47af3448bdfa027c6 authorJohn MacFarlane <jgm@berkeley.edu> dateSat 10 Apr 2010, 12:38 (17 years ago) parentb5bda75
10 files changed+1346−14
README+7−3 man/man1/pandoc.1.md+10−10 pandoc.cabal+5−1 src/Text/Pandoc.hs+2 src/Text/Pandoc/Writers/Textile.hs+413 src/pandoc.hs+1 templates/textile.template+12 tests/RunTests.hs+1 tests/tables.textile+212 tests/writer.textile+683
README+7−3
@@ -7,7 +7,7 @@ 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], [groff man] pages, and [S5] HTML slide shows.
10+[MediaWiki markup], [Textile], [groff man] pages, and [S5] HTML slide shows.
1111Pandoc's enhanced version of markdown includes syntax for footnotes,
1212tables, flexible ordered lists, definition lists, delimited code blocks,
1313superscript, subscript, strikeout, title blocks, automatic tables of
@@ -22,6 +22,7 @@ representation of the document, and a set of writers, which convert
2222this native representation into a target format. Thus, adding an input
2323or output format requires only adding a reader or writer.
2424
25+[Textile]: http://thresholdstate.com/articles/4312/the-textile-reference-manual
2526[markdown]: http://daringfireball.net/projects/markdown/
2627[reStructuredText]: http://docutils.sourceforge.net/docs/ref/rst/introduction.html
2728[S5]: http://meyerweb.com/eric/tools/s5/
@@ -90,8 +91,9 @@ Supported output formats include `markdown`, `latex`, `context`
9091(ConTeXt), `html`, `rtf` (rich text format), `rst`
9192(reStructuredText), `docbook` (DocBook XML), `opendocument`
9293(OpenDocument XML), `odt` (OpenOffice text document), `texinfo`, (GNU
93−Texinfo), `mediawiki` (MediaWiki markup), `man` (groff man), and `s5`
94−(which produces an HTML file that acts like powerpoint).
94+Texinfo), `mediawiki` (MediaWiki markup), `textile` (Textile), `man`
95+(groff man), and `s5` (which produces an HTML file that acts like
96+powerpoint).
9597
9698Supported input formats include `markdown`, `html`, `latex`, and `rst`.
9799Note that the `rst` reader only parses a subset of reStructuredText
@@ -1122,6 +1124,8 @@ In groff man output, it will be rendered verbatim without $'s.
11221124
11231125In MediaWiki output, it will be rendered inside `<math>` tags.
11241126
1127+In Textile output, it will be rendered inside `<span class="math">` tags.
1128+
11251129In RTF, Docbook, and OpenDocument output, it will be rendered, as far as
11261130possible, using unicode characters, and will otherwise appear verbatim.
11271131Unknown commands and symbols, and commands that cannot be dealt with
man/man1/pandoc.1.md+10−10
@@ -15,8 +15,8 @@ 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, and S5 HTML slide shows.
18+ConTeXt, Texinfo, groff man, MediaWiki markup, Textile, RTF,
19+OpenDocument XML, ODT, DocBook XML, and S5 HTML slide shows.
2020
2121If no *input-file* is specified, input is read from *stdin*.
2222Otherwise, the *input-files* are concatenated (with a blank
@@ -72,14 +72,14 @@ should pipe input and output through `iconv`:
7272: Specify output format. *FORMAT* can be `native` (native Haskell),
7373 `plain` (plain text), `markdown` (markdown), `rst` (reStructuredText),
7474 `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), `s5` (S5 HTML and javascript slide
78− show), or `rtf` (rich text format). Note that `odt` output will not
79− be directed to *stdout*; an output filename must be specified using
80− the `-o/--output` option. If `+lhs` is appended to `markdown`,
81− `rst`, `latex`, or `html`, the output will be rendered as literate
82− Haskell source.
75+ `mediawiki` (MediaWiki markup), `textile` (Textile), `texinfo` (GNU
76+ Texinfo), `docbook` (DocBook XML), `opendocument` (OpenDocument
77+ XML), `odt` (OpenOffice text document), `s5` (S5 HTML and javascript
78+ slide show), or `rtf` (rich text format). Note that `odt` output
79+ will not be directed to *stdout*; an output filename must be
80+ specified using the `-o/--output` option. If `+lhs` is appended to
81+ `markdown`, `rst`, `latex`, or `html`, the output will be rendered
82+ as literate Haskell source.
8383
8484-s, \--standalone
8585: Produce output with an appropriate header and footer (e.g. a
pandoc.cabal+5−1
@@ -18,7 +18,7 @@ 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, and
21+ OpenDocument, ODT, RTF, MediaWiki, Textile, groff man pages, and
2222 S5 HTML slide shows.
2323 .
2424 Pandoc extends standard markdown syntax with footnotes,
@@ -42,6 +42,7 @@ Data-Files:
4242 templates/man.template, templates/markdown.template,
4343 templates/rst.template, templates/plain.template,
4444 templates/mediawiki.template, templates/rtf.template,
45+ templates/textile.template
4546 -- data for ODT writer
4647 reference.odt,
4748 -- data for LaTeXMathML writer
@@ -87,6 +88,7 @@ Extra-Source-Files:
8788 tests/tables.plain,
8889 tests/tables.markdown,
8990 tests/tables.mediawiki,
91+ tests/tables.textile,
9092 tests/tables.native,
9193 tests/tables.opendocument,
9294 tests/tables.texinfo,
@@ -104,6 +106,7 @@ Extra-Source-Files:
104106 tests/writer.markdown,
105107 tests/writer.plain,
106108 tests/writer.mediawiki,
109+ tests/writer.textile,
107110 tests/writer.native,
108111 tests/writer.opendocument,
109112 tests/writer.rst,
@@ -180,6 +183,7 @@ Library
180183 Text.Pandoc.Writers.Man,
181184 Text.Pandoc.Writers.Markdown,
182185 Text.Pandoc.Writers.RST,
186+ Text.Pandoc.Writers.Textile,
183187 Text.Pandoc.Writers.MediaWiki,
184188 Text.Pandoc.Writers.RTF,
185189 Text.Pandoc.Writers.S5,
src/Text/Pandoc.hs+2
@@ -85,6 +85,7 @@ module Text.Pandoc
8585 , writeOpenDocument
8686 , writeMan
8787 , writeMediaWiki
88+ , writeTextile
8889 , writeRTF
8990 , prettyPandoc
9091 -- * Writer options used in writers
@@ -114,6 +115,7 @@ import Text.Pandoc.Writers.OpenDocument
114115import Text.Pandoc.Writers.Man
115116import Text.Pandoc.Writers.RTF
116117import Text.Pandoc.Writers.MediaWiki
118+import Text.Pandoc.Writers.Textile
117119import Text.Pandoc.Templates
118120import Text.Pandoc.Shared
119121import Data.Version (showVersion)
src/Text/Pandoc/Writers/Textile.hsnew file+413
@@ -0,0 +1,413 @@
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+ contents <- mapM (listItemToTextile opts) items
180+ modify $ \s -> s { stListLevel = init (stListLevel s) }
181+ return $ vcat contents ++ "\n"
182+
183+blockToTextile opts x@(OrderedList attribs items) = do
184+ oldUseTags <- liftM stUseTags get
185+ let useTags = oldUseTags || not (isSimpleList x)
186+ if useTags
187+ then do
188+ contents <- withUseTags $ mapM (listItemToTextile opts) items
189+ return $ " <ol" ++ listAttribsToString attribs ++ ">\n" ++ vcat contents ++
190+ " </ol>\n"
191+ else do
192+ modify $ \s -> s { stListLevel = stListLevel s ++ "#" }
193+ contents <- mapM (listItemToTextile opts) items
194+ modify $ \s -> s { stListLevel = init (stListLevel s) }
195+ return $ vcat contents ++ "\n"
196+
197+blockToTextile opts (DefinitionList items) = do
198+ contents <- withUseTags $ mapM (definitionListItemToTextile opts) items
199+ return $ " <dl>\n" ++ vcat contents ++ " </dl>\n"
200+
201+-- Auxiliary functions for lists:
202+
203+-- | Convert ordered list attributes to HTML attribute string
204+listAttribsToString :: ListAttributes -> String
205+listAttribsToString (startnum, numstyle, _) =
206+ let numstyle' = camelCaseToHyphenated $ show numstyle
207+ in (if startnum /= 1
208+ then " start=\"" ++ show startnum ++ "\""
209+ else "") ++
210+ (if numstyle /= DefaultStyle
211+ then " style=\"list-style-type: " ++ numstyle' ++ ";\""
212+ else "")
213+
214+-- | Convert bullet or ordered list item (list of blocks) to Textile.
215+listItemToTextile :: WriterOptions -> [Block] -> State WriterState String
216+listItemToTextile opts items = do
217+ contents <- blockListToTextile opts items
218+ useTags <- get >>= return . stUseTags
219+ if useTags
220+ then return $ " <li>" ++ contents ++ "</li>"
221+ else do
222+ marker <- get >>= return . stListLevel
223+ return $ marker ++ " " ++ contents
224+
225+-- | Convert definition list item (label, list of blocks) to Textile.
226+definitionListItemToTextile :: WriterOptions
227+ -> ([Inline],[[Block]])
228+ -> State WriterState String
229+definitionListItemToTextile opts (label, items) = do
230+ labelText <- inlineListToTextile opts label
231+ contents <- mapM (blockListToTextile opts) items
232+ return $ " <dt>" ++ labelText ++ "</dt>\n" ++
233+ (intercalate "\n" $ map (\d -> " <dd>" ++ d ++ "</dd>") contents)
234+
235+-- | True if the list can be handled by simple wiki markup, False if HTML tags will be needed.
236+isSimpleList :: Block -> Bool
237+isSimpleList x =
238+ case x of
239+ BulletList items -> all isSimpleListItem items
240+ OrderedList (num, sty, _) items -> all isSimpleListItem items &&
241+ num == 1 && sty `elem` [DefaultStyle, Decimal]
242+ _ -> False
243+
244+-- | True if list item can be handled with the simple wiki syntax. False if
245+-- HTML tags will be needed.
246+isSimpleListItem :: [Block] -> Bool
247+isSimpleListItem [] = True
248+isSimpleListItem [x] =
249+ case x of
250+ Plain _ -> True
251+ Para _ -> True
252+ BulletList _ -> isSimpleList x
253+ OrderedList _ _ -> isSimpleList x
254+ _ -> False
255+isSimpleListItem [x, y] | isPlainOrPara x =
256+ case y of
257+ BulletList _ -> isSimpleList y
258+ OrderedList _ _ -> isSimpleList y
259+ _ -> False
260+isSimpleListItem _ = False
261+
262+isPlainOrPara :: Block -> Bool
263+isPlainOrPara (Plain _) = True
264+isPlainOrPara (Para _) = True
265+isPlainOrPara _ = False
266+
267+-- | Concatenates strings with line breaks between them.
268+vcat :: [String] -> String
269+vcat = intercalate "\n"
270+
271+-- Auxiliary functions for tables. (TODO: these are common to HTML, MediaWiki,
272+-- and Textile writers, and should be abstracted out.)
273+
274+tableRowToTextile :: WriterOptions
275+ -> [String]
276+ -> Int
277+ -> [[Block]]
278+ -> State WriterState String
279+tableRowToTextile opts alignStrings rownum cols' = do
280+ let celltype = if rownum == 0 then "th" else "td"
281+ let rowclass = case rownum of
282+ 0 -> "header"
283+ x | x `rem` 2 == 1 -> "odd"
284+ _ -> "even"
285+ cols'' <- sequence $ zipWith
286+ (\alignment item -> tableItemToTextile opts celltype alignment item)
287+ alignStrings cols'
288+ return $ "<tr class=\"" ++ rowclass ++ "\">\n" ++ unlines cols'' ++ "</tr>"
289+
290+alignmentToString :: Alignment -> [Char]
291+alignmentToString alignment = case alignment of
292+ AlignLeft -> "left"
293+ AlignRight -> "right"
294+ AlignCenter -> "center"
295+ AlignDefault -> "left"
296+
297+tableItemToTextile :: WriterOptions
298+ -> String
299+ -> String
300+ -> [Block]
301+ -> State WriterState String
302+tableItemToTextile opts celltype align' item = do
303+ let mkcell x = "<" ++ celltype ++ " align=\"" ++ align' ++ "\">" ++
304+ x ++ "</" ++ celltype ++ ">"
305+ contents <- blockListToTextile opts item
306+ return $ mkcell contents
307+
308+-- | Convert list of Pandoc block elements to Textile.
309+blockListToTextile :: WriterOptions -- ^ Options
310+ -> [Block] -- ^ List of block elements
311+ -> State WriterState String
312+blockListToTextile opts blocks =
313+ mapM (blockToTextile opts) blocks >>= return . vcat
314+
315+-- | Convert list of Pandoc inline elements to Textile.
316+inlineListToTextile :: WriterOptions -> [Inline] -> State WriterState String
317+inlineListToTextile opts lst =
318+ mapM (inlineToTextile opts) lst >>= return . concat
319+
320+-- | Convert Pandoc inline element to Textile.
321+inlineToTextile :: WriterOptions -> Inline -> State WriterState String
322+
323+inlineToTextile opts (Emph lst) = do
324+ contents <- inlineListToTextile opts lst
325+ return $ if '_' `elem` contents
326+ then "<em>" ++ contents ++ "</em>"
327+ else "_" ++ contents ++ "_"
328+
329+inlineToTextile opts (Strong lst) = do
330+ contents <- inlineListToTextile opts lst
331+ return $ if '*' `elem` contents
332+ then "<strong>" ++ contents ++ "</strong>"
333+ else "*" ++ contents ++ "*"
334+
335+inlineToTextile opts (Strikeout lst) = do
336+ contents <- inlineListToTextile opts lst
337+ return $ if '-' `elem` contents
338+ then "<del>" ++ contents ++ "</del>"
339+ else "-" ++ contents ++ "-"
340+
341+inlineToTextile opts (Superscript lst) = do
342+ contents <- inlineListToTextile opts lst
343+ return $ if '^' `elem` contents
344+ then "<sup>" ++ contents ++ "</sup>"
345+ else "^" ++ contents ++ "^"
346+
347+inlineToTextile opts (Subscript lst) = do
348+ contents <- inlineListToTextile opts lst
349+ return $ if '~' `elem` contents
350+ then "<sub>" ++ contents ++ "</sub>"
351+ else "~" ++ contents ++ "~"
352+
353+inlineToTextile opts (SmallCaps lst) = inlineListToTextile opts lst
354+
355+inlineToTextile opts (Quoted SingleQuote lst) = do
356+ contents <- inlineListToTextile opts lst
357+ return $ "'" ++ contents ++ "'"
358+
359+inlineToTextile opts (Quoted DoubleQuote lst) = do
360+ contents <- inlineListToTextile opts lst
361+ return $ "\"" ++ contents ++ "\""
362+
363+inlineToTextile opts (Cite _ lst) = inlineListToTextile opts lst
364+
365+inlineToTextile _ EmDash = return " -- "
366+
367+inlineToTextile _ EnDash = return " - "
368+
369+inlineToTextile _ Apostrophe = return "'"
370+
371+inlineToTextile _ Ellipses = return "..."
372+
373+inlineToTextile _ (Code str) =
374+ return $ if '@' `elem` str
375+ then "<tt>" ++ escapeStringForXML str ++ "</tt>"
376+ else "@" ++ escapeStringForXML str ++ "@"
377+
378+inlineToTextile _ (Str str) = return $ escapeStringForTextile str
379+
380+inlineToTextile _ (Math _ str) =
381+ return $ "<span class=\"math\">" ++ escapeStringForXML str ++ "</math>"
382+
383+inlineToTextile _ (TeX _) = return ""
384+
385+inlineToTextile _ (HtmlInline str) = return str
386+
387+inlineToTextile _ (LineBreak) = return "\n"
388+
389+inlineToTextile _ Space = return " "
390+
391+inlineToTextile opts (Link txt (src, _)) = do
392+ label <- case txt of
393+ [Code s] -> return s
394+ _ -> inlineListToTextile opts txt
395+ return $ "\"" ++ label ++ "\":" ++ src
396+
397+inlineToTextile opts (Image alt (source, tit)) = do
398+ alt' <- inlineListToTextile opts alt
399+ let txt = if null tit
400+ then if null alt'
401+ then ""
402+ else "(" ++ alt' ++ ")"
403+ else "(" ++ tit ++ ")"
404+ return $ "!" ++ source ++ txt ++ "!"
405+
406+inlineToTextile opts (Note contents) = do
407+ curNotes <- liftM stNotes get
408+ let newnum = length curNotes + 1
409+ contents' <- blockListToTextile opts contents
410+ let thisnote = "fn" ++ show newnum ++ ". " ++ contents' ++ "\n"
411+ modify $ \s -> s { stNotes = thisnote : curNotes }
412+ return $ "[" ++ show newnum ++ "]"
413+ -- note - may not work for notes with multiple blocks
src/pandoc.hs+1
@@ -120,6 +120,7 @@ writers = [("native" , writeDoc)
120120 ,("rst" , writeRST)
121121 ,("rst+lhs" , writeRST)
122122 ,("mediawiki" , writeMediaWiki)
123+ ,("textile" , writeTextile)
123124 ,("rtf" , writeRTF)
124125 ]
125126
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+683
@@ -0,0 +1,683 @@
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+
176+
177+Here's another:
178+
179+# First
180+# Second:
181+#* Fee
182+#* Fie
183+#* Foe
184+
185+# Third
186+
187+Same thing but with paragraphs:
188+
189+# First
190+# Second:
191+#* Fee
192+#* Fie
193+#* Foe
194+
195+# Third
196+
197+h2. Tabs and spaces
198+
199+* this is a list item indented with tabs
200+* this is a list item indented with spaces
201+** this is an example list item indented with tabs
202+** this is an example list item indented with spaces
203+
204+
205+h2. Fancy list markers
206+
207+ <ol start="2" style="list-style-type: decimal;">
208+ <li>begins with 2</li>
209+ <li> <p>and now 3</p>
210+ <p>with a continuation</p>
211+ <ol start="4" style="list-style-type: lower-roman;">
212+ <li>sublist with roman numerals, starting with 4</li>
213+ <li>more items
214+ <ol style="list-style-type: upper-alpha;">
215+ <li>a subsublist</li>
216+ <li>a subsublist</li> </ol>
217+</li> </ol>
218+</li> </ol>
219+
220+Nesting:
221+
222+ <ol style="list-style-type: upper-alpha;">
223+ <li>Upper Alpha
224+ <ol style="list-style-type: upper-roman;">
225+ <li>Upper Roman.
226+ <ol start="6" style="list-style-type: decimal;">
227+ <li>Decimal start with 6
228+ <ol start="3" style="list-style-type: lower-alpha;">
229+ <li>Lower alpha with paren</li> </ol>
230+</li> </ol>
231+</li> </ol>
232+</li> </ol>
233+
234+Autonumbering:
235+
236+# Autonumber.
237+# More.
238+## Nested.
239+
240+
241+Should not be a list item:
242+
243+M.A. 2007
244+
245+B. Williams
246+
247+<hr />
248+
249+h1. Definition Lists
250+
251+Tight using spaces:
252+
253+ <dl>
254+ <dt>apple</dt>
255+ <dd>red fruit</dd>
256+ <dt>orange</dt>
257+ <dd>orange fruit</dd>
258+ <dt>banana</dt>
259+ <dd>yellow fruit</dd> </dl>
260+
261+Tight using tabs:
262+
263+ <dl>
264+ <dt>apple</dt>
265+ <dd>red fruit</dd>
266+ <dt>orange</dt>
267+ <dd>orange fruit</dd>
268+ <dt>banana</dt>
269+ <dd>yellow fruit</dd> </dl>
270+
271+Loose:
272+
273+ <dl>
274+ <dt>apple</dt>
275+ <dd> <p>red fruit</p></dd>
276+ <dt>orange</dt>
277+ <dd> <p>orange fruit</p></dd>
278+ <dt>banana</dt>
279+ <dd> <p>yellow fruit</p></dd> </dl>
280+
281+Multiple blocks with italics:
282+
283+ <dl>
284+ <dt>_apple_</dt>
285+ <dd> <p>red fruit</p>
286+ <p>contains seeds, crisp, pleasant to taste</p></dd>
287+ <dt>_orange_</dt>
288+ <dd> <p>orange fruit</p>
289+bc. { orange code block }
290+
291+bq. <p>orange block quote</p></dd> </dl>
292+
293+Multiple definitions, tight:
294+
295+ <dl>
296+ <dt>apple</dt>
297+ <dd>red fruit</dd>
298+ <dd>computer</dd>
299+ <dt>orange</dt>
300+ <dd>orange fruit</dd>
301+ <dd>bank</dd> </dl>
302+
303+Multiple definitions, loose:
304+
305+ <dl>
306+ <dt>apple</dt>
307+ <dd> <p>red fruit</p></dd>
308+ <dd> <p>computer</p></dd>
309+ <dt>orange</dt>
310+ <dd> <p>orange fruit</p></dd>
311+ <dd> <p>bank</p></dd> </dl>
312+
313+Blank line after term, indented marker, alternate markers:
314+
315+ <dl>
316+ <dt>apple</dt>
317+ <dd> <p>red fruit</p></dd>
318+ <dd> <p>computer</p></dd>
319+ <dt>orange</dt>
320+ <dd> <p>orange fruit</p>
321+ <ol style="list-style-type: decimal;">
322+ <li>sublist</li>
323+ <li>sublist</li> </ol>
324+</dd> </dl>
325+
326+h1. HTML Blocks
327+
328+Simple block on one line:
329+
330+<div>
331+foo
332+</div>
333+
334+And nested without indentation:
335+
336+<div>
337+<div>
338+<div>
339+foo
340+</div>
341+</div>
342+<div>
343+bar
344+</div>
345+</div>
346+
347+Interpreted markdown in a table:
348+
349+<table>
350+<tr>
351+<td>
352+This is _emphasized_
353+</td>
354+<td>
355+And this is *strong*
356+</td>
357+</tr>
358+</table>
359+
360+<script type="text/javascript">document.write('This *should not* be interpreted as markdown');</script>
361+
362+Here's a simple block:
363+
364+<div>
365+
366+foo
367+</div>
368+
369+This should be a code block, though:
370+
371+bc. &lt;div&gt;
372+ foo
373+&lt;/div&gt;
374+
375+As should this:
376+
377+bc. &lt;div&gt;foo&lt;/div&gt;
378+
379+Now, nested:
380+
381+<div>
382+ <div>
383+ <div>
384+
385+foo
386+</div>
387+ </div>
388+</div>
389+
390+This should just be an HTML comment:
391+
392+<!-- Comment -->
393+
394+Multiline:
395+
396+<!--
397+Blah
398+Blah
399+-->
400+
401+<!--
402+ This is another comment.
403+-->
404+
405+Code block:
406+
407+bc. &lt;!-- Comment --&gt;
408+
409+Just plain comment, with trailing spaces on the line:
410+
411+<!-- foo -->
412+
413+Code:
414+
415+bc. &lt;hr /&gt;
416+
417+Hr's:
418+
419+<hr>
420+
421+<hr />
422+
423+<hr />
424+
425+<hr>
426+
427+<hr />
428+
429+<hr />
430+
431+<hr class="foo" id="bar" />
432+
433+<hr class="foo" id="bar" />
434+
435+<hr class="foo" id="bar">
436+
437+<hr />
438+
439+h1. Inline Markup
440+
441+This is _emphasized_, and so _is this_.
442+
443+This is *strong*, and so *is this*.
444+
445+An _"emphasized link":/url_.
446+
447+*_This is strong and em._*
448+
449+So is *_this_* word.
450+
451+*_This is strong and em._*
452+
453+So is *_this_* word.
454+
455+This is code: @&gt;@, @$@, @\@, @\$@, @&lt;html&gt;@.
456+
457+-This is _strikeout_.-
458+
459+Superscripts: a^bc^d a^_hello_^ a^hello there^.
460+
461+Subscripts: H~2~O, H~23~O, H~many of them~O.
462+
463+These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.
464+
465+<hr />
466+
467+h1. Smart quotes, ellipses, dashes
468+
469+"Hello," said the spider. "'Shelob' is my name."
470+
471+'A', 'B', and 'C' are letters.
472+
473+'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.'
474+
475+'He said, "I want to go."' Were you alive in the 70's?
476+
477+Here is some quoted '@code@' and a ""quoted link":http://example.com/?foo=1&bar=2".
478+
479+Some dashes: one -- two -- three -- four -- five.
480+
481+Dashes between numbers: 5 - 7, 255 - 66, 1987 - 1999.
482+
483+Ellipses...and...and....
484+
485+<hr />
486+
487+h1. LaTeX
488+
489+*
490+* <span class="math">2+2=4</math>
491+* <span class="math">x \in y</math>
492+* <span class="math">\alpha \wedge \omega</math>
493+* <span class="math">223</math>
494+* <span class="math">p</math>-Tree
495+* 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>
496+* Here's one that has a line break in it: <span class="math">\alpha + \omega \times x^2</math>.
497+
498+These shouldn't be math:
499+
500+* To get the famous equation, write @$e = mc^2$@.
501+* $22,000 is a _lot_ of money. So is $34,000. (It worked if "lot" is emphasized.)
502+* Shoes ($20) and socks ($5).
503+* Escaped @$@: $73 _this should be emphasized_ 23$.
504+
505+Here's a LaTeX table:
506+
507+
508+
509+<hr />
510+
511+h1. Special Characters
512+
513+Here is some unicode:
514+
515+* I hat: Î
516+* o umlaut: ö
517+* section: §
518+* set membership: ∈
519+* copyright: ©
520+
521+AT&amp;T has an ampersand in their name.
522+
523+AT&amp;T is another way to write it.
524+
525+This &amp; that.
526+
527+4 &lt; 5.
528+
529+6 &gt; 5.
530+
531+Backslash: \
532+
533+Backtick: `
534+
535+Asterisk: &#42;
536+
537+Underscore: &#95;
538+
539+Left brace: {
540+
541+Right brace: }
542+
543+Left bracket: [
544+
545+Right bracket: ]
546+
547+Left paren: (
548+
549+Right paren: )
550+
551+Greater-than: &gt;
552+
553+Hash: #
554+
555+Period: .
556+
557+Bang: !
558+
559+Plus: +
560+
561+Minus: -
562+
563+<hr />
564+
565+h1. Links
566+
567+h2. Explicit
568+
569+Just a "URL":/url/.
570+
571+"URL and title":/url/.
572+
573+"URL and title":/url/.
574+
575+"URL and title":/url/.
576+
577+"URL and title":/url/
578+
579+"URL and title":/url/
580+
581+"with&#95;underscore":/url/with_underscore
582+
583+"Email link":mailto:nobody@nowhere.net
584+
585+"Empty":.
586+
587+h2. Reference
588+
589+Foo "bar":/url/.
590+
591+Foo "bar":/url/.
592+
593+Foo "bar":/url/.
594+
595+With "embedded [brackets]":/url/.
596+
597+"b":/url/ by itself should be a link.
598+
599+Indented "once":/url.
600+
601+Indented "twice":/url.
602+
603+Indented "thrice":/url.
604+
605+This should [not][] be a link.
606+
607+bc. [not]: /url
608+
609+Foo "bar":/url/.
610+
611+Foo "biz":/url/.
612+
613+h2. With ampersands
614+
615+Here's a "link with an ampersand in the URL":http://example.com/?foo=1&bar=2.
616+
617+Here's a link with an amersand in the link text: "AT&amp;T":http://att.com/.
618+
619+Here's an "inline link":/script?foo=1&bar=2.
620+
621+Here's an "inline link in pointy braces":/script?foo=1&bar=2.
622+
623+h2. Autolinks
624+
625+With an ampersand: "http://example.com/?foo=1&bar=2":http://example.com/?foo=1&bar=2
626+
627+* In a list?
628+* "http://example.com/":http://example.com/
629+* It should.
630+
631+An e-mail address: "nobody@nowhere.net":mailto:nobody@nowhere.net
632+
633+bq. Blockquoted: "http://example.com/":http://example.com/
634+
635+Auto-links should not occur here: @&lt;http://example.com/&gt;@
636+
637+bc. or here: &lt;http://example.com/&gt;
638+
639+<hr />
640+
641+h1. Images
642+
643+From "Voyage dans la Lune" by Georges Melies (1902):
644+
645+!lalune.jpg(Voyage dans la Lune)!
646+lalune
647+
648+Here is a movie !movie.jpg(movie)! icon.
649+
650+<hr />
651+
652+h1. Footnotes
653+
654+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]
655+
656+bq. Notes can go in quotes.[4]
657+
658+# And in list items.[5]
659+
660+This paragraph should not be part of the note, as it is not indented.
661+
662+
663+fn1. Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.
664+
665+
666+fn2. Here's the long note. This one contains multiple blocks.
667+
668+Subsequent blocks are indented to show that they belong to the footnote (as with list items).
669+
670+bc. { &lt;code&gt; }
671+
672+If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.
673+
674+
675+fn3. This is _easier_ to type. Inline notes may contain "links":http://google.com and @]@ verbatim characters, as well as [bracketed text].
676+
677+
678+fn4. In quote.
679+
680+
681+fn5. In list.
682+
683+