Book production pipeline for authors and self-publishers.
Multi-format export (PDF, EPUB, DOCX, HTML, Markdown), audiobook generation, translation, and manuscript tooling, powered by Pandoc.
pip install manuscriptaOr with Poetry:
poetry add manuscripta- Python 3.11+
- Pandoc 3.0 or newer installed and available on PATH
- For audiobook generation: internet connection (Edge TTS) or local TTS engine
Inside your book repository root:
# Export to PDF
export-pdf
# Export to EPUB with cover
export-ewc --cover assets/covers/cover.jpg
# Export all formats
export-all
# Safe export (no source modifications, good for drafts)
export-pdf-safe
# Generate audiobook
manuscripta-audiobook --engine edge --voice en-US-JennyNeural
# Initialize a new book project
manuscripta-initEach book repository should follow this layout:
my-book/
manuscript/
front-matter/
toc.md
toc-print.md
foreword.md
preface.md
chapters/
01-chapter-one.md
02-chapter-two.md
back-matter/
epilogue.md
glossary.md
acknowledgments.md
about-the-author.md
bibliography.md
imprint.md
config/
metadata.yaml
export-settings.yaml
voice-settings.yaml
assets/
covers/
images/
fonts/
templates/
output/
pyproject.toml
Images referenced from your Markdown () are resolved
against the book repository's assets/ directory. As of v0.8.0 the library
has an explicit contract about where that directory lives — the caller
passes it in, there is no cwd fallback inside the library.
Invoke from the project root (default behavior — cwd is used as the source dir at the CLI layer):
export-pdfOr pass --source-dir explicitly to build from anywhere:
export-pdf --source-dir=/path/to/my-bookMissing images fail the build by default. Opt out with --no-strict-images
to continue with warnings. Extra asset directories can be appended with
--resource-path (repeatable).
from pathlib import Path
from manuscripta.export.book import run_export
from manuscripta import ManuscriptaImageError, ManuscriptaLayoutError
try:
run_export(
Path("/abs/path/to/my-book"), # REQUIRED — no cwd fallback
formats="pdf",
resource_paths=[Path("/abs/shared/assets")],
strict_images=True, # default
)
except ManuscriptaLayoutError as e:
print(f"Bad project layout: missing {e.missing}")
except ManuscriptaImageError as e:
print(f"Unresolved images: {e.unresolved}")source_dir must contain manuscript/, config/, and assets/ — a
ManuscriptaLayoutError is raised otherwise, naming the missing pieces.
Migrating from v0.7.x? See MIGRATION.md.
Controls output formats, TOC behavior, and section ordering per book type (ebook, paperback, hardcover, audiobook).
TTS configuration: language, voice, and sections to skip during audio generation.
Pandoc metadata: title, author, date, language.
| Command | Description |
|---|---|
export-pdf / export-p |
Export PDF |
export-epub / export-e |
Export EPUB |
export-docx / export-d |
Export DOCX |
export-html / export-h |
Export HTML |
export-md |
Export Markdown |
export-all |
Export all formats |
export-all-with-cover |
Export all with cover |
export-pvp |
Print version (paperback) |
export-pvh |
Print version (hardcover) |
All export commands have a -safe variant (e.g. export-pdf-safe) that skips source preprocessing for fast,
non-destructive draft builds.
| Command | Description |
|---|---|
manuscripta-audiobook |
Generate MP3 audiobook |
Engines (--engine or engine: in config/voice-settings.yaml): edge (default, online),
google, pyttsx3 (offline), elevenlabs (needs ELEVENLABS_API_KEY) and voicestudio.
voicestudio narrates with a voice profile from the local VoiceStudio app
(cloned from a short sample or designed). The app must be running; its API listens on
http://localhost:3900 (override with VOICESTUDIO_URL). voice takes the profile id or name:
engine: voicestudio
voice: Asterios # VoiceStudio profile name or id
language: de| Command | Description |
|---|---|
translate-en-de |
English to German (DeepL) |
translate-de-en |
German to English (DeepL) |
translate-en-es |
English to Spanish (DeepL) |
translate-de-es |
German to Spanish (DeepL) |
translate-book-en-de |
English to German (LMStudio) |
translate-book-de-en |
German to English (LMStudio) |
translate-book-en-es |
English to Spanish (LMStudio) |
translate-book-en-fr |
English to French (LMStudio) |
| Command | Description |
|---|---|
fix-german-quotes |
Fix German quotation marks |
fix-english-quotes |
Fix English quotation marks |
fix-french-quotes |
Fix French quotes and spacing |
fix-spanish-quotes |
Fix Spanish quotation marks |
replace-md-bullet-points |
Replace markdown bullet points |
unbold-md-headers |
Remove bold from headers |
replace-emojis |
Replace emojis in markdown |
strip-links |
Strip links from markdown |
normalize-toc |
Normalize TOC links |
fix-german-quotes converts straight double quotes "..." and English typographic quotes
(“...”, ‘...’) to German „...“ and ‚...‘. Straight single quotes ' are left alone on
purpose: they are indistinguishable from apostrophes (geht's), so the tool never touches them.
fix-english-quotes converts straight double quotes to “...” and straight single quotes to
‘...’ and the apostrophe ’ (don’t, the students’ books, the ’90s). In English the
closing single quote and the apostrophe are the same character, so only an opening ' needs
its context: it opens a quotation at the start of a paragraph or after a space, bracket, opening
double quote, emphasis marker or dash. A paragraph whose opening quote is never closed, for
example the elision in 'tis, keeps its straight opening quotes and gets a warning. Typographic
English quotes are already the target; German „...“ are left alone.
fix-french-quotes and fix-spanish-quotes convert straight double quotes to guillemets,
French « ... » with a no-break space (U+00A0) inside, Spanish «...» without, and set that
spacing for the guillemets already in the text. Straight single quotes and apostrophes are
converted as in English (l’homme, ‘significado’). English “...” stay as they are, since
both languages use them as the second level. Only guillemets that pair up in a paragraph are
respaced: a lone one, such as the marker in ### » Title, and one next to a hard line break keep
their spacing. fix-french-quotes also puts a no-break space before ; : ! ? (Quoi ?),
except where the mark is markup or no punctuation: URLs, times (10:30), footnote definitions
([^1]:), table alignment colons (---:), HTML entities ( ), ![image] and (?). All four
commands take the same arguments (--dry-run, --pattern, a file or a directory).
| Command | Description |
|---|---|
convert-paths-to-absolute |
Convert relative paths to absolute |
convert-paths-to-relative |
Convert absolute paths to relative |
| Command | Description |
|---|---|
convert-images |
Convert image formats |
generate-images |
Generate images |
generate-images-deepai |
Generate images via DeepAI |
inject-images |
Inject images into manuscript |
| Command | Description |
|---|---|
manuscripta-init |
Initialize new book project |
create-chapters |
Create chapter files |
reorder-chapters |
Reorder and rename chapters |
update-metadata-values |
Update metadata values |
manuscripta-tag |
Generate release tag message |
| Command | Description |
|---|---|
pandoc-batch |
Batch pandoc conversion |
bulk-change-ext |
Bulk change file extensions |
clean-git-cache |
Clean git cache |
manuscripta/
export/ # PDF, EPUB, DOCX, HTML, Markdown export
audiobook/ # TTS-based audiobook generation
tts/ # Pluggable TTS backends (Edge, gTTS, pyttsx3, ElevenLabs)
translation/ # DeepL and LMStudio translation
markdown/ # Markdown processing (quotes, links, emojis, TOC)
paths/ # Path conversion (absolute/relative, image tags)
images/ # Image conversion, generation, injection
project/ # Project init, chapters, metadata, tagging
config/ # Config file loading
enums/ # Book type enum
utils/ # Pandoc batch, git cache, bulk operations
data/ # Emoji/symbol maps, JSON data files
git clone https://github.com/astrapi69/manuscripta.git
cd manuscripta
make lock-installRun make help for a full list. Key targets:
| Target | Description |
|---|---|
make install |
Install project with all dependencies |
make lock-install |
Lock and install project dependencies |
make update |
Update dependencies |
make hooks |
Install pre-commit hooks |
make test |
Run all tests |
make test-v |
Run all tests (verbose) |
make test-fast |
Run tests without coverage (faster) |
make test-cov |
Run tests with coverage report |
make lint |
Run ruff linter |
make lint-fix |
Run ruff linter with auto-fix |
make format |
Format code with black |
make format-check |
Check formatting without changes |
make typecheck |
Run MyPy type checks |
make codespell |
Run codespell |
make precommit |
Run all pre-commit hooks |
make ci |
Full CI pipeline (lint + format-check + test) |
make bump-patch |
Bump patch version (0.1.0 -> 0.1.1) |
make bump-minor |
Bump minor version (0.1.0 -> 0.2.0) |
make bump-major |
Bump major version (0.1.0 -> 1.0.0) |
make tag-message |
Generate tag message and create tag |
make build |
Build distribution package |
make publish |
Run CI, build and publish to PyPI |
make publish-test |
Run CI, build and publish to TestPyPI |
make clean |
Remove build artifacts and caches |
make clean-venv |
Remove Poetry virtualenv |
# All tests with coverage
make test
# Quick run without coverage
make test-fast
# Full CI check before committing
make ci# Test release
make publish-test
# Production release
make publishBoth targets run the full CI pipeline (lint, format-check, tests) before building and publishing.
- manuscript-tools - Validation, sanitization and metrics for Markdown manuscripts. Install separately for linting capabilities.
MIT