2018-01-21 22:35:11 +08:00
# Preprocessors
A *preprocessor* is simply a bit of code which gets run immediately after the
book is loaded and before it gets rendered, allowing you to update and mutate
the book. Possible use cases are:
2018-01-23 21:10:52 +08:00
- Creating custom helpers like `\{{#include /path/to/file.md}}`
2018-02-24 11:14:52 +01:00
- Substituting in latex-style expressions (`$$ \frac{1}{3} $$` ) with their
2018-01-21 22:35:11 +08:00
mathjax equivalents
2021-12-19 20:26:37 -08:00
See [Configuring Preprocessors ](../format/configuration/preprocessors.md ) for more information about using preprocessors.
2018-01-21 22:35:11 +08:00
2018-09-25 19:41:38 +08:00
## Hooking Into MDBook
MDBook uses a fairly simple mechanism for discovering third party plugins.
2021-12-19 20:26:37 -08:00
A new table is added to `book.toml` (e.g. `[preprocessor.foo]` for the `foo`
2018-09-25 19:41:38 +08:00
preprocessor) and then `mdbook` will try to invoke the `mdbook-foo` program as
part of the build process.
2021-03-08 20:39:39 +01:00
Once the preprocessor has been defined and the build process starts, mdBook executes the command defined in the `preprocessor.foo.command` key twice.
The first time it runs the preprocessor to determine if it supports the given renderer.
mdBook passes two arguments to the process: the first argument is the string `supports` and the second argument is the renderer name.
The preprocessor should exit with a status code 0 if it supports the given renderer, or return a non-zero exit code if it does not.
If the preprocessor supports the renderer, then mdbook runs it a second time, passing JSON data into stdin.
The JSON consists of an array of `[context, book]` where `context` is the serialized object [`PreprocessorContext` ] and `book` is a [`Book` ] object containing the content of the book.
The preprocessor should return the JSON format of the [`Book` ] object to stdout, with any modifications it wishes to perform.
2018-01-21 22:35:11 +08:00
2018-09-25 19:41:38 +08:00
The easiest way to get started is by creating your own implementation of the
`Preprocessor` trait (e.g. in `lib.rs` ) and then creating a shell binary which
translates inputs to the correct `Preprocessor` method. For convenience, there
is [an example no-op preprocessor] in the `examples/` directory which can easily
be adapted for other preprocessors.
2018-01-21 22:35:11 +08:00
2018-09-25 19:41:38 +08:00
< details >
< summary > Example no-op preprocessor< / summary >
2018-01-21 22:35:11 +08:00
```rust
2018-09-25 19:41:38 +08:00
// nop-preprocessors.rs
2018-09-10 20:58:55 +10:00
2018-09-25 19:41:38 +08:00
{{#include ../../../examples/nop-preprocessor.rs}}
```
< / details >
2018-02-24 11:14:52 +01:00
2018-09-25 19:41:38 +08:00
## Hints For Implementing A Preprocessor
2018-02-24 11:14:52 +01:00
2025-07-23 16:32:49 -07:00
By pulling in `mdbook-preprocessor` as a library, preprocessors can have access to the
2018-09-25 19:41:38 +08:00
existing infrastructure for dealing with books.
2018-02-24 11:14:52 +01:00
2018-09-25 19:41:38 +08:00
For example, a custom preprocessor could use the
[`CmdPreprocessor::parse_input()` ] function to deserialize the JSON written to
`stdin` . Then each chapter of the `Book` can be mutated in-place via
[`Book::for_each_mut()` ], and then written to `stdout` with the `serde_json`
crate.
2018-02-24 11:14:52 +01:00
2018-09-25 19:41:38 +08:00
Chapters can be accessed either directly (by recursively iterating over
chapters) or via the `Book::for_each_mut()` convenience method.
2018-02-24 11:14:52 +01:00
2018-09-25 19:41:38 +08:00
The `chapter.content` is just a string which happens to be markdown. While it's
entirely possible to use regular expressions or do a manual find & replace,
you'll probably want to process the input into something more computer-friendly.
2025-07-23 16:32:49 -07:00
The [`mdbook-markdown` ] crate exposes the [`pulldown-cmark` ][pc] crate used by mdBook to parse Markdown. The [`pulldown-cmark-to-cmark` ][pctc] crate can be used to translate events back into markdown text.
2018-02-24 11:14:52 +01:00
2018-09-25 19:41:38 +08:00
The following code block shows how to remove all emphasis from markdown,
without accidentally breaking the document.
2018-02-24 11:14:52 +01:00
```rust
2024-11-02 15:41:55 -07:00
{{#rustdoc_include ../../../examples/remove-emphasis/mdbook-remove-emphasis/src/main.rs:remove_emphasis}}
2018-02-24 11:14:52 +01:00
```
2024-11-02 15:41:55 -07:00
Take a look at the [full example source][emphasis-example] for more details.
2018-02-24 11:14:52 +01:00
2021-09-05 18:45:37 -07:00
## Implementing a preprocessor with a different language
2021-03-08 20:39:39 +01:00
The fact that mdBook utilizes stdin and stdout to communicate with the preprocessors makes it easy to implement them in a language other than Rust.
2021-09-04 23:44:27 +02:00
The following code shows how to implement a simple preprocessor in Python, which will modify the content of the first chapter.
The example below follows the configuration shown above with `preprocessor.foo.command` actually pointing to a Python script.
2021-03-05 21:11:29 +01:00
```python
import json
import sys
if __name__ == '__main__':
if len(sys.argv) > 1: # we check if we received any argument
if sys.argv[1] == "supports":
# then we are good to return an exit status code of 0, since the other argument will just be the renderer's name
sys.exit(0)
2021-09-04 23:44:27 +02:00
# load both the context and the book representations from stdin
context, book = json.load(sys.stdin)
2021-03-05 21:11:29 +01:00
# and now, we can just modify the content of the first chapter
2021-03-08 20:39:39 +01:00
book['sections'][0]['Chapter']['content'] = '# Hello'
2021-03-05 21:11:29 +01:00
# we are done with the book's modification, we can just print it to stdout,
print(json.dumps(book))
```
2024-11-02 15:41:55 -07:00
[emphasis-example]: https://github.com/rust-lang/mdBook/tree/master/examples/remove-emphasis/
2018-02-24 11:14:52 +01:00
[pc]: https://crates.io/crates/pulldown-cmark
[pctc]: https://crates.io/crates/pulldown-cmark-to-cmark
2019-10-29 08:04:16 -05:00
[an example no-op preprocessor]: https://github.com/rust-lang/mdBook/blob/master/examples/nop-preprocessor.rs
2025-07-23 16:32:49 -07:00
[`CmdPreprocessor::parse_input()` ]: https://docs.rs/mdbook-preprocessor/latest/mdbook_preprocessor/trait.Preprocessor.html#method .parse_input
[`Book::for_each_mut()` ]: https://docs.rs/mdbook-preprocessor/latest/mdbook_preprocessor/book/struct.Book.html#method .for_each_mut
[`PreprocessorContext` ]: https://docs.rs/mdbook-preprocessor/latest/mdbook_preprocessor/struct.PreprocessorContext.html
[`Book` ]: https://docs.rs/mdbook-preprocessor/latest/mdbook_preprocessor/book/struct.Book.html
[`mdbook-markdown` ]: https://docs.rs/mdbook-markdown/latest/mdbook_markdown/