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.el
exports Org files in Leanpub’s Markua format, the default and recommended format for Leanpub books and coursesox-leanpub-markdown.el
exports Org files in Leanpub Flavored Markdown (LFM), the original markup format for Leanpub books.ox-leanpub-book.el
exports an Org file in multiple files and directories in the structure required by Leanpub, including the necessarymanuscript/
directory and theBook.txt
,Sample.txt
andSubset.txt
files. 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.git
ox-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_SUBSET
setting, see below) by pressingC-s
in 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
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
manuscript
folder if needed, under which all other files are stored. - A
resources/images
directory 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
images
directory are created both from the top-level directory, and from themanuscript
directory, to allow referencing the same image files both from the Org file and from the exported Markua files. - Exports one
.markua
or.md
file for each top-level header (chapter) in your book. - Creates the
Book.txt
file with the filenames corresponding to the chapters of your book.- Depending on the exporter settings (see below), the
Subset.txt
andSample.txt
files may also be created.
- Depending on the exporter settings (see below), the
The book files are created inside manuscript
and populated as follows:
Book.txt
with all chapters, except those tagged withnoexport
.Sample.txt
with all chapters tagged withsample
. Note: this file is only created when exporting LFM. In Markua output, all headings tagged withsample
are given thesample: true
attribute as documented in the Markua manual.Subset.txt
with chapters depending on the value of the#+LEANPUB_WRITE_SUBSET
file 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-s
in 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_NAME
property, it is used, unless the#+LEANPUB_BOOK_RECOMPUTE_FILENAMES
file 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_FILENAME
is set and the heading has aNAME
,CUSTOM_ID
orID
property, it is used as the base filename, and used to construct the filename insidemanuscript
. The resulting final filename is stored in theEXPORT_FILE_NAME
property. - 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.
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
,ID
orCUSTOM_ID
, if specified, are used for theid
attribute. - An element’s
#+CAPTION
, if specified, is used for thecaption
attribute in Markua and thetitle
attribute in LFM (see Block Captions for details of how captions are produced in block elements). - Other attributes can be specified in an
#+ATTR_LEANPUB
line 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_LEANPUB
override 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"}
![Architecture diagram](images/diagram.png)
And in LFM as:
{width="30%", id="system-diagram", title="Architecture diagram"}
![Architecture diagram](images/diagram.png)
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:
blockname
is 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-markdown
andorg-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. |
#+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.el
was written by Juan Reyero asox-leanpub.el
and 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.el
is 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.el
was based originally on code by Lakshmi Narasimhan, but also heavily modified.ox-leanpub-markua.el
delegates 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.