Ox-leanpub includes Org Mode export backends to publish books and courses with Leanpub. ox-leanpub allows you to write your material entirely in Org mode, and manages the production of the files and directories needed for Leanpub to render your book. I use this package to publish my books.
This package contains three libraries:
ox-leanpub-markua.elexports Org files in Leanpub’s Markua format, the default and recommended format for Leanpub books and coursesox-leanpub-markdown.elexports Org files in Leanpub Flavored Markdown (LFM), the original markup format for Leanpub books.ox-leanpub-book.elexports an Org file in multiple files and directories in the structure required by Leanpub, including the necessarymanuscript/directory and theBook.txt,Sample.txtandSubset.txtfiles. It can use either Markua or LFM as the export backend.
Note: you should use the Markua exporter, as it’s more mature, complete and actively developed by me. Some Org constructs might not be exported correctly to Markdown.
If you have any feedback or bug reports, please open an issue at https://gitlab.com/zzamboni/ox-leanpub/-/issues.
If you want to see a real-world example of how to use it, you can find at https://github.com/zzamboni/emacs-org-leanpub the source of my book Publishing with Emacs, Org mode and Leanpub.
You can install the package directly from MELPA. For example, using use-package:
(use-package ox-leanpub
:after org)Note: installing the ox-leanpub package will also install ox-gfm, which is used for exporting tables in Markua format.
By default, the ox-leanpub module sets things up for exporting books in Markua format. If you want to export your books in LFM format, you need to additionally load the ox-leanpub-markdown exporter and tell ox-leanpub-book to set up the corresponding menu entries, as follows:
(use-package ox-leanpub
:after org
:config
(require 'ox-leanpub-markdown)
(org-leanpub-book-setup-menu-markdown))If you use Doom Emacs, add the following line to your packages.el file:
(package! ox-leanpub)And the following to your config.el file:
(use-package! ox-leanpub
:after org)If you need to export to Markdown, add the :config section exactly as shown before.
If you want to install from the source in GitLab, you can clone this repository using git. For example:
cd ~/.emacs.d/lisp
git clone https://gitlab.com/zzamboni/ox-leanpub.gitox-leanpub-markua depends on ox-gfm for exporting tables, so you need to install it as well. Easiest is to install it from MELPA using M-x package-install, or use-package:
(use-package ox-gfm
:after org)Finally, you need to tell Emacs to load the module from the path where you installed it. For example:
(use-package ox-leanpub
:path "~/.emacs.d/lisp/ox-leanpub"
:after org
:config
(require 'ox-leanpub-markdown)
(org-leanpub-book-setup-menu-markdown))Depending on whether you load the Markua or Markdown exporter, you will see the corresponding new sections in Org’s export menu (C-c C-e), called “Export to Leanpub Markua” and “Export to Leanpub Markdown”:
[M] Export to Leanpub Markua
[M] To temporary buffer [m] To file
[o] To file and open
[b] Book: Whole book [s] Book: Subset
[L] Export to Leanpub Markdown
[L] To temporary buffer [l] To file
[o] To file and open
[b] Book: Whole book [s] Book: Subset
The “buffer” and “file” options export the whole file to the corresponding format, but without any further structuring. You can use these if you want to convert a whole book for using with Leanpub’s in-browser editor, for example.
The “Book” options do whole-book export in the structure required by Leanpub:
- “Book: Whole book” exports the whole book as one-file-per-chapter;
- “Book: Subset” exports only the chapters that should be included in
Subset.txt(if any), according to the rules listed below, to be able to quickly preview them using LeanPub’s subset-preview feature;- The subset export can be temporarily restricted to the current chapter (regardless of the
#+LEANPUB_BOOK_WRITE_SUBSETsetting, see below) by pressingC-sin the Org Mode Export screen to set “Export scope” to “Subtree”.
- The subset export can be temporarily restricted to the current chapter (regardless of the
manuscript directory and export everything into there. However if you’re creating multiple books that share reference material or resources, you’ll need multiple output directories. Set the #+LEANPUB_BOOK_OUTPUT_DIR in your file. It’s best to point this to the root of a separate git repo which you’ll likely upload to GitHub and point LeanPub at. For example, if your book is at ~/my_cool_book/cool_book.org you might create ~/my_cool_book_export/ and add the following to the top of your org file.
#+leanpub_book_output_dir: ~/my_cool_book_export/
The first time you do a Book export, the following directory and symlink structure will be created:
.
├── images -> manuscript/resources/images
└── manuscript
├── images -> resources/images
└── resources
└── images
In short, this is what the Book export operation does:
- Creates a
manuscriptfolder if needed, under which all other files are stored. - A
resources/imagesdirectory is created insidemanuscript, as required by the Leanpub Markua processor (this is not required by the LFM processor, but the same structure is used). - Symlinks to the
imagesdirectory are created both from the top-level directory, and from themanuscriptdirectory, to allow referencing the same image files both from the Org file and from the exported Markua files. - Exports one
.markuaor.mdfile for each top-level header (chapter) in your book. - Creates the
Book.txtfile with the filenames corresponding to the chapters of your book.- Depending on the exporter settings (see below), the
Subset.txtandSample.txtfiles may also be created.
- Depending on the exporter settings (see below), the
The book files are created inside manuscript and populated as follows:
Book.txtwith all chapters, except those tagged withnoexport.Sample.txtwith all chapters tagged withsample. Note: this file is only created when exporting LFM. In Markua output, all headings tagged withsampleare given thesample: trueattribute as documented in the Markua manual.Subset.txtwith chapters depending on the value of the#+LEANPUB_WRITE_SUBSETfile property (see Configuration below):- Default or
none: not created. tagged: use all chapters taggedsubset.all: use the same chapters asBook.txt.sample: use same chapters asSample.txt.current: export the current chapter (where the cursor is at the moment of the export) as the contents ofSubset.txt. This can be set temporarily (for a single export) by pressingC-sin the Export screen to set “Export scope” to “Subtree”.
- Default or
The exported chapter files are named as follows:
- If the heading has an
EXPORT_FILE_NAMEproperty, it is used, unless the#+LEANPUB_BOOK_RECOMPUTE_FILENAMESfile property is set.- Note: this filename should already specify the output directory and extension, e.g.
manuscript/chapter.markua
- Note: this filename should already specify the output directory and extension, e.g.
- If the
#+LEANPUB_BOOK_ID_AS_FILENAMEis set and the heading has aNAME,CUSTOM_IDorIDproperty, it is used as the base filename, and used to construct the filename insidemanuscript. The resulting final filename is stored in theEXPORT_FILE_NAMEproperty. - Otherwise, the filename is generated based on the heading title by lowercasing it and replacing all non-alphanumeric characters with hyphens. The resulting final filename is likewise stored in
EXPORT_FILE_NAME.
The last-used filename is stored in the EXPORT_FILE_NAME property of the corresponding heading. By default, once this property is set it is not modified on future exports. If you set the #+LEANPUB_BOOK_RECOMPUTE_FILENAMES attribute in your file, the EXPORT_FILE_NAME property will be updated every time the book is exported. This can be useful to keep the filenames in sync when you change the heading titles in your document, but be aware that the file exported with the old name will not be removed automatically.
#+LEANPUB_BOOK_OUTPUT_DIR the created structure will not include a top level symlink into <LEANPUB_BOOK_OUTPUT_DIR>/resources/images Instead it will look like this:
.
└── manuscript
├── images -> resources/images
└── resources
└── images
The top level symlink is omitted because it is assumed that you’ll be exporting multiple books from this directory and it would be problematic if the images symlink just pointed to whichever one you exported last.
If a heading has the frontmatter, mainmatter or backmatter tags, the corresponding directive (they work in both Markdown and Markup modes) is inserted in the output, before the headline. This way, you only need to tag the first chapter of the front, main, and backmatter, respectively.
If a level-1 heading has the part tag, it is exported as a part heading (“# Title #” in Markua, “-# Title” in LFM).
If a heading has the sample tag in a Markua export, the conditional attribute {sample: true} is inserted before the heading in the output, to indicate that the section should be included in the book sample generated by Leanpub. If a heading has the sample tag in a Markdown export, the corresponding chapter is added to the Sample.txt file.
If a heading has the nobook tag, the conditional attribute {book: false} is inserted before the heading in the output, to indicate that the section should not be included in the book. You can specify both the nobook and sample tags to flag a section which should only be included in the sample. The nobook tag has no effect in Markdown exports.
Note: noexport and nobook are similar but have different semantics. noexport is interpreted by Org when exporting your file, and it completely omits the corresponding headings from the output, whereas nobook includes the text, but flags it accordingly for Leanpub to ignore it when rendering the final book.
Both LFM and Leanpub support specifying attributes for different elements using attribute lines. Both ox-leanpub-markua and ox-leanpub-markdown support specifying attributes as follows:
- An element’s
#+NAME,IDorCUSTOM_ID, if specified, are used for theidattribute. - An element’s
#+CAPTION, if specified, is used for thecaptionattribute in Markua and thetitleattribute in LFM (see Block Captions for details of how captions are produced in block elements). - Other attributes can be specified in an
#+ATTR_LEANPUBline before the corresponding element. The syntax is the same as for Org header arguments. These are merged with the previous one if specified. Attributes specified in#+ATTR_LEANPUBoverride those specified through other mechanisms.
Example:
#+name: system-diagram
#+caption: Architecture diagram
#+attr_leanpub: :width 30%
[[file:images/diagram.png]]
Gets exported in Markua as:
{width: "30%", id: "system-diagram", caption: "Architecture diagram"}

And in LFM as:
{width="30%", id="system-diagram", title="Architecture diagram"}

ox-leanpub supports all Leanpub block elements in Markua export:
| Block type | Gets exported as |
|---|---|
#+begin/end_aside | {aside} |
#+begin/end_blockquote | {blockquote} |
#+begin/end_blurb | {blurb} |
#+begin/end_center | {blurb, class: "center"} |
#+begin/end_discussion | {blurb, class: "discussion"} |
#+begin/end_error | {blurb, class: "error"} |
#+begin/end_exercise | {blurb, class: "exercise"} |
#+begin/end_information | {blurb, class: "information"} |
#+begin/end_note | {blurb, class: "information"} |
#+begin/end_question | {blurb, class: "question"} |
#+begin/end_quote | {blockquote} |
#+begin/end_tip | {blurb, class: "tip"} |
#+begin/end_warning | {blurb, class: "warning"} |
You can specify a custom icon for a block using the :icon attribute in an #+ATTR_LEANPUB line. For example:
#+ATTR_LEANPUB: :icon github
#+begin_tip
Tip with a GitHub icon instead of the default.
#+end_tip
You can change the default icon for a block for the whole document, or you can even define your own block types, by using #+MARKUA_BLOCK lines. The syntax is:
#+MARKUA_BLOCK: blockname [:class classname] [:icon iconname]
Where blockname and at least one of :class or :icon needs to be specified:
blocknameis the name of the block to define. Can be one of the existing block names (to redefine it) or a new one.classname(optional) is the name of an existing supported Markua block class (as listed in the table above). It can be omitted to use a plain{blurb}block.iconname(optional) is a valid icon name to use for the block.
You can define multiple block types, each on their own #+MARKUA_BLOCK line. For example, you can change the default icon of tip blocks to be a lightbulb instead of the default key icon:
#+MARKUA_BLOCK: tip :class tip :icon lightbulb
#+begin_tip
Tip with a lightbulb!
#+end_tip
You can also define completely new block types:
#+MARKUA_BLOCK: leanpub :icon leanpub
#+begin_leanpub
Leanpub block!
#+end_leanpub
If a #+CAPTION is specified for a block, it is exported as a headline at the top of the block. By default, the level of the headline is one below the current level (e.g. if the block is under a level-2 headline, its caption will be produced as a level-3 headline). You can configure this for the whole document by setting the #+MARKUA_BLOCK_CAPTION_LEVEL option, or for individual blocks by specifying the :caption-level option in the #+ATTR_LEANPUB line. Valid values for this option are:
same: the caption will be produced as a same-level headline;- A number 1-9: the caption will be produced as a headline of the specified level;
below(or anything else): default behavior, caption will be produced at one level below the current one.
Leanpub Markua supports exporting both books and courses. The results are largely the same, currently with one exception:
- Org blocks of type
exercise(#+begin_exercise/#+end_exercise) are exported as “X>” blurbs in books, and as {exercise} blocks in courses.
You can tell ox-leanpub-markua how your buffer should be exported by setting the #+MARKUA_EXPORT_TYPE option. Its default value is "book". If you are exporting a course, set it as follows:
#+MARKUA_EXPORT_TYPE: course
You can also set this parameter for an individual block by specifying the :export-type argument in #+ATTR_LEANPUB, as follows:
#+ATTR_LEANPUB: :export-type course
#+begin_exercise
...
#+end_exercise
Normally, a caption for a code block is specified using the standard #+CAPTION attribute, like this:
#+caption: My code block
#+begin_src bash
echo "Hi"
#+end_src
You can configure ox-leanpub-markua to automatically generate the caption using the :tangle or :noweb-ref attributes, if present, using the #+MARKUA_TANGLE_CAPTION and #+MARKUA_NOWEB_REF_CAPTION options. Either or both of them can be specified. The format of the generated captions can be configured, see Configuration below for the details. Note: generating captions based on :tangle or :noweb-ref only works if the org-export-use-babel variable is set to nil. This is due to a limitation in org-export (the code block headers are not visible to the exporter if this variable is t, since they are processed before).
Even when these options are enabled, a manually specified #+CAPTION will always take precedence.
Leanpub supports producing indices for books using Markua 0.30, so ox-leanpub-markua exports Org-mode index entries using the {i:...} syntax used by Markua. The value given to #+INDEX will be passed as-is into the {i:...} attributes (including any formatting markup, which needs to be provided in Markua format), with one exception: for see and seealso entries, the format should be see=otherentry or seealso=otherentry, and it will be converted to the correct syntax on export. The value given to #+INDEX must not be enclosed in quotes, but the value passed to see or seealso may be enclosed in quotes. Separators such as ! and | must be escaped with a backslash.
| Org-mode source | Exported Markua |
#+INDEX: "hello" | error |
#+INDEX: hello | {i: "hello"} |
#+INDEX: Zamboni, Diego | {i: "Zamboni, Diego"} |
#+INDEX: Yahoo\! | {i: "Yahoo\!"} |
#+INDEX: *hello* | {i: "*hello*"} |
#+INDEX: **hello** | {i: "**hello**"} |
#+INDEX: hello!Diego | {i: "hello!Diego"} |
#+INDEX: hello!*Diego* | {i: "hello!*Diego*"} |
#+INDEX: hello!**Diego** | {i: "hello!**Diego**"} |
#+INDEX: Diego¦see=hello | {i: "Diego¦see{i:'hello'}"} |
There are multiple endpoints which can be useful when calling from Emacs LISP, for example from hooks to automatically export the book under certain conditions. Some of the most useful are:
org-leanpub-book-export-markdownandorg-leanpub-book-export-markua: both can be called without arguments, and export the whole book in the corresponding format.
The modules provide reasonable defaults, but you can configure some parameters by specifying keywords at the top of your Org file. The following are recognized:
| Keyword | Default value | Description |
#+LEANPUB_BOOK_ID_AS_FILENAME | nil | If set (regardless of its value), use a heading’s NAME, CUSTOM_ID or ID properties (if it has them) to construct the output filename, instead of always using the title. |
#+LEANPUB_BOOK_OUTPUT_DIR | “manuscript” | Subdirectory where the exported files will be created. See Multiple books in one repo? and Caveats when you specify an output directory if you use this. |
#+LEANPUB_BOOK_RECOMPUTE_FILENAMES | nil | If set (regardless of its value), update EXPORT_FILE_NAME for all headings on each export, based on the title. Note that if a chapter title has changed since the last export, it will be exported to a new filename, but the old file will not be deleted, you need to do this manually. |
#+LEANPUB_BOOK_WRITE_SUBSET | “none” | What to write to the Subset.txt file. Possible values: none, tagged, all, sample, current. |
#+MARKUA_BLOCK | nil | Redefine or define a new block type. See Block Elements for the syntax details. |
#+MARKUA_EXPORT_TYPE | “book” | (only for Markua export) Determines the type of export being done. Valid values are “book” and “course”. |
#+MARKUA_NOWEB_REF_CAPTION | nil | (only for Markua export) If set (regardless of its value), use the value of the :noweb-ref header argument for the caption of source code blocks. For this to work, the org-export-use-babel variable must be set to nil. |
#+MARKUA_NOWEB_REF_CAPTION_FMT | “«%s»≡” | Format to use for captions generated from the :noweb-ref attribute. The string %s is replaced by the :noweb-ref value. The default value can be used (depending on the formatting of your book) to emulate the default output format produced by noweb. |
#+MARKUA_TANGLE_CAPTION | nil | (only for Markua export) If set (regardless of its value), use the value of the :tangle header argument for the caption of source code blocks. For this to work, the org-export-use-babel variable must be set to nil. |
#+MARKUA_TANGLE_CAPTION_FMT | ”[%s]” | Format to use for captions generated from the :tangle attribute. The string %s is replaced by the :tangle value. |
#+MARKUA_TANGLE_NOWEB_CAPTION_FMT | ”[%1$s] «%2$s»≡” | Format to use when both :noweb-ref and :tangle are used to generate the caption. The string %1$s is replaced by the value of :tangle, and %2$s by the value of :noweb-ref. |
This is controlled by the Org-mode “H” export option. Its default value is 3, which causes all lower-level headlines to be exported as lists instead. To fix this, you have to increase the value of this option.
This can be done in each file with a line like this:
#+options: h:9
You can also change its default by setting the org-export-headline-levels variable.
- The original version of
ox-leanpub-markdown.elwas written by Juan Reyero asox-leanpub.eland is still available at https://github.com/juanre/ox-leanpub. I made many changes to fix some bugs and process additional markup elements, andox-leanpub-markua.elis also derived from it. This repository started as a fork of the original, but given the amount of changes I have recreated it as a standalone repo, to avoid confusion. ox-leanpub-book.elwas based originally on code by Lakshmi Narasimhan, but also heavily modified.ox-leanpub-markua.eldelegates the work of exporting tables to ox-gfm.
If you find this package useful, consider supporting me by purchasing my book Publishing with Emacs, Org Mode and Leanpub, or any of my other books at Leanpub!
I am not associated with Leanpub other than being a happy author. Leanpub is not responsible for this code.