git.iko.soy/typeable/pandoc
on master help sign in
More progress on using-the-pandoc-api.md.
07bf48dJohn MacFarlane
doc/using-the-pandoc-api.md+238−32
@@ -9,77 +9,283 @@ Detailed API documentation at the level of individual functions
9and types is available at
9and types is available at
10<https://hackage.haskell.org/package/pandoc>.
10<https://hackage.haskell.org/package/pandoc>.
11
11
12−# Pandoc's architecture, and a simple example
12+# Pandoc's architecture
13
13
14−Pandoc structure, readers, writers.
14+Pandoc is structured as a set of *readers*, which translate
15+various input formats into an abstract syntax tree (the
16+Pandoc AST) representing a structured document, and a set of
17+*writers*, which render this AST into various input formats.
18+Pictorially:
15
19
16−example of using a reader.
20+```
21+[input format] ==reader==> [Pandoc AST] ==writer==> [output format]
22+```
23+
24+This architecture allows pandoc to perform $M \times n$
25+conversions with $M$ readers and $N$ writers.
26+
27+The Pandoc AST is defined in the
28+[pandoc-types](https://hackage.haskell.org/package/pandoc-types)
29+package. You should start by looking at the Haddock
30+documentation for
31+[Text.Pandoc.Definition](https://hackage.haskell.org/package/pandoc-types/docs/Text-Pandoc-Definition.html). As you'll see, a `Pandoc` is
32+composed of some metadata and a list of `Block`s. There are
33+various kinds of `Block`, including `Para` (paragraph),
34+`Header` (section heading), and `BlockQuote`. Some of the
35+`Block`s (like `BlockQuote`) contain lists of `Block`s,
36+while others (like `Para`) contain lists of `Inline`s, and
37+still others (like `CodeBlock`) contain plain text or
38+nothing. `Inline`s are the basic elements of paragraphs.
39+The distinction between `Block` and `Inline` in the type
40+system makes it impossible to represent, for example,
41+a link (`Inline`) whose link text is a block quote (`Block`).
42+This expressive limitation is mostly a help rather than a
43+hindrance, since many of the formats pandoc supports have
44+similar limitations.
45+
46+The best way to explore the pandoc AST is to use `pandoc -t
47+native`, which will display the AST correspoding to some
48+Markdown input:
49+
50+```
51+% echo -e "1. *foo*\n2. bar" | pandoc -t native
52+[OrderedList (1,Decimal,Period)
53+ [[Plain [Emph [Str "foo"]]]
54+ ,[Plain [Str "bar"]]]]
55+```
56+
57+# A simple example
58+
59+Here is a simple example of the use of a pandoc reader and
60+writer to perform a conversion inside ghci:
61+
62+```
63+import Text.Pandoc
64+import qualified Data.Text as T
65+import qualified Data.Text.IO as TIO
66+
67+main :: IO ()
68+main = do
69+ result <- runIO $ do
70+ doc <- readMarkdown def (T.pack "[testing](url)")
71+ writeRST def doc
72+ rst <- handleError result
73+ TIO.putStrLn rst
74+```
17
75
18−example of using a writer.
76+Some notes:
19
77
20−chaining them together.
78+1. The first part constructs a conversion pipeline: the input
79+ string is passed to `readMarkdown`, and the resulting Pandoc
80+ AST (`doc`) is then rendered by `writeRST`. The conversion
81+ pipeline is "run" by `runIO`---more on that below.
82+
83+2. `result` has the type `Either PandocError Text`. We could
84+ pattern-match on this manually, but it's simpler in this
85+ context to use the `handleError` function from
86+ Text.Pandoc.Error. This exits with an appropriate error
87+ code and message if the value is a `Left`, and returns the
88+ `Text` if the value is a `Right`.
21
89
22# The PandocMonad class
90# The PandocMonad class
23
91
24−Pandoc's functions define computations that can be run in
92+Let's look at the types of `readMarkdown` and `writeRST`:
25−any instance of the `PandocMonad` typeclass. Two instances
93+
26−are provided: `PandocIO` and `PandocPure`. The difference is
94+```haskell
27−that computations run in `PandocIO` are allowed to do IO
95+readMarkdown :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
28−(for example, read a file), while computations in `PandocPure`
96+
29−are free of any side effects. `PandocPure` is useful when
97+writeRST :: PandocMonad m => WriterOptions -> Pandoc -> m Text
30−you want to prevent users from doing anything malicious.
98+```
31
99
32−Here's an example of such a computation, from the module
100+The `PandocMonad m =>` part is a typeclass constraint.
33−`Text.Pandoc.Class`:
101+It says that `readMarkdown` and `writeRST` define computations
102+that can be used in any instance of the `PandocMonad`
103+type class. `PandocMonad` is defined in the module
104+Text.Pandoc.Class.
105+
106+Two instances of `PandocMonad` are provided: `PandocIO` and
107+`PandocPure`. The difference is that computations run in
108+`PandocIO` are allowed to do IO (for example, read a file),
109+while computations in `PandocPure` are free of any side effects.
110+`PandocPure` is useful for sandboxed environments, when you want
111+to prevent users from doing anything malicious. To run the
112+conversion in `PandocIO`, use `runIO` (as above). To run it in
113+`PandocPure`, use `runPure`.
114+
115+As you can see from the Haddocks,
116+[Text.Pandoc.Class](https://hackage.haskell.org/package/pandoc/docs/Text-Pandoc-Class.html)
117+exports many auxiliary functions that can be used in any
118+instance of `PandocMonad`. For example:
34
119
35```haskell
120```haskell
36-- | Get the verbosity level.
121-- | Get the verbosity level.
37getVerbosity :: PandocMonad m => m Verbosity
122getVerbosity :: PandocMonad m => m Verbosity
38−```
39
123
40−motivations
124+-- | Set the verbosity level.
41−Class.
125+setVerbosity :: PandocMonad m => Verbosity -> m ()
42
126
43−# The Pandoc structure
127+-- Get the accomulated log messages (in temporal order).
128+getLog :: PandocMonad m => m [LogMessage]
129+getLog = reverse <$> getsCommonState stLog
44
130
45−blocks/inlines
131+-- | Log a message using 'logOutput'. Note that
132+-- 'logOutput' is called only if the verbosity
133+-- level exceeds the level of the message, but
134+-- the message is added to the list of log messages
135+-- that will be retrieved by 'getLog' regardless
136+-- of its verbosity level.
137+report :: PandocMonad m => LogMessage -> m ()
46
138
47−# Readers and writers
139+-- | Fetch an image or other item from the local filesystem or the net.
140+-- Returns raw content and maybe mime type.
141+fetchItem :: PandocMonad m
142+ => String
143+ -> m (B.ByteString, Maybe MimeType)
48
144
49−getReader, getWriter
145+setResourcePath :: PandocMonad m => [FilePath] -> m ()
146+```
147+
148+If we wanted more verbose informational messages
149+during the conversion we defined in the previous
150+section, we could do this:
151+
152+```haskell
153+ result <- runIO $ do
154+ setVerbosity INFO
155+ doc <- readMarkdown def (T.pack "[testing](url)")
156+ writeRST def doc
157+```
50
158
51# Options
159# Options
52
160
53−various reader and writer options you can set.
161+The first argument of each reader or writer is for
54−templates
162+options controlling the behavior of the reader or writer:
55−extensions
163+`ReaderOptions` for readers and `WriterOptions`
164+for writers. These are defined in
165+[Text.Pandoc.Options](https://hackage.haskell.org/package/pandoc/docs/Text-Pandoc-Options.html). It is a good idea to study these
166+options to see what can be adjusted.
167+
168+`def` (from Data.Default) denotes a default value for
169+each kind of option. (You can also use `defaultWriterOptions`
170+and `defaultReaderOptions`.) Generally you'll want to use
171+the defaults and modify them only when needed, for example:
172+
173+```haskell
174+ writeRST def{ writerReferenceLinks = True }
175+```
176+
177+Some particularly important options to know about:
178+
179+1. `writerTemplate`: By default, this is `Nothing`, which
180+ means that a document fragment will be produced. If you
181+ want a full document, you need to specify `Just template`,
182+ where `template` is a String containing the template's
183+ contents (not the path).
184+
185+2. `readerExtensions` and `writerExtensions`: These specify
186+ the extensions to be used in parsing and rendering.
187+ Extensions are defined in [Text.Pandoc.Extensions](https://hackage.haskell.org/package/pandoc/docs/Text-Pandoc-Extensions.html).
56
188
57# Builder
189# Builder
58
190
191+
59Inlines vs Inline, etc.
192Inlines vs Inline, etc.
60−Monoid
61
193
62−example: report from CSV data
194+Concatenating lists is slow. So we use special types Inlines and Blocks that wrap Sequences of Inline and Block elements.
195+
196+Monoid - makes it easy to build up docs programatically.
197+
198+Example.
199+Here’s a JSON data source about CNG fueling stations in the
200+Chicago area: cng_fuel_chicago.json. Boss says: write me a
201+letter in Word listing all the stations that take the Voyager
202+card.
203+
204+```
205+[ {
206+ "state" : "IL",
207+ "city" : "Chicago",
208+ "fuel_type_code" : "CNG",
209+ "zip" : "60607",
210+ "station_name" : "Clean Energy - Yellow Cab",
211+ "cards_accepted" : "A D M V Voyager Wright_Exp CleanEnergy",
212+ "street_address" : "540 W Grenshaw"
213+}, ...
214+```
215+
216+No need to open Word for this job! fuel.hs
217+
218+```
219+{-# LANGUAGE OverloadedStrings #-}
220+import Text.Pandoc.Builder
221+import Text.Pandoc
222+import Data.Monoid ((<>), mempty, mconcat)
223+import Data.Aeson
224+import Control.Applicative
225+import Control.Monad (mzero)
226+import qualified Data.ByteString.Lazy as BL
227+import qualified Data.Text as T
228+import Data.List (intersperse)
229+
230+data Station = Station{
231+ address :: String
232+ , name :: String
233+ , cardsAccepted :: [String]
234+ } deriving Show
235+
236+instance FromJSON Station where
237+ parseJSON (Object v) = Station <$>
238+ v .: "street_address" <*>
239+ v .: "station_name" <*>
240+ (words <$> (v .:? "cards_accepted" .!= ""))
241+ parseJSON _ = mzero
242+
243+createLetter :: [Station] -> Pandoc
244+createLetter stations = doc $
245+ para "Dear Boss:" <>
246+ para "Here are the CNG stations that accept Voyager cards:" <>
247+ simpleTable [plain "Station", plain "Address", plain "Cards accepted"]
248+ (map stationToRow stations) <>
249+ para "Your loyal servant," <>
250+ plain (image "JohnHancock.png" "" mempty)
251+ where
252+ stationToRow station =
253+ [ plain (text $ name station)
254+ , plain (text $ address station)
255+ , plain (mconcat $ intersperse linebreak $ map text $ cardsAccepted station)
256+ ]
257+
258+main :: IO ()
259+main = do
260+ json <- BL.readFile "cng_fuel_chicago.json"
261+ let letter = case decode json of
262+ Just stations -> createLetter [s | s <- stations,
263+ "Voyager" `elem` cardsAccepted s]
264+ Nothing -> error "Could not decode JSON"
265+ BL.writeFile "letter.docx" =<< writeDocx def letter
266+ putStrLn "Created letter.docx"
267+```
268+
269+
63
270
64# Templates and other data files
271# Templates and other data files
65
272
273+readDataFile
274+
66# Handling errors and warnings
275# Handling errors and warnings
67
276
68# Generic transformations
277# Generic transformations
69
278
70Walk and syb for AST transformations
279Walk and syb for AST transformations
71
280
72−# Filters
281+Filters: see filters.md
73−
74−just the basic idea of toJSONFilter
75−the rest can be left to filters.md
76
282
77−# Self-contained
283+but, how do you run filters from a program?
284+need to export these functions from Text.Pandoc.App!
78
285
79
286
80# PDF
287# PDF
81
288
82−# Custom PandocMonad instances
83
289
84# Creating a front-end
290# Creating a front-end
85
291