Metadata-Version: 2.4
Name: mark-my-word
Version: 0.5.0
Summary: Convert Markdown file to PDF using themes extracted directly from Word templates
Project-URL: Homepage, https://gitlab.com/tastapod/mark-my-word
Project-URL: Repository, https://gitlab.com/tastapod/mark-my-word.git
Project-URL: Issues, https://gitlab.com/tastapod/mark-my-word/-/issues
Author-email: Daniel Terhorst-North <daniel@dannorth.net>
License-Expression: MIT
Keywords: docx,markdown,pdf,theme,weasyprint,word
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Text Processing :: Markup
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: click~=8.3
Requires-Dist: markdown~=3.10
Requires-Dist: pymdown-extensions~=11.0
Requires-Dist: python-docx~=1.2
Requires-Dist: python-frontmatter~=1.1
Requires-Dist: pyyaml~=6.0
Requires-Dist: weasyprint~=68.1
Description-Content-Type: text/markdown

# mark-my-word

You have a Word template — the right fonts, colours, headers, footers. You have a Markdown file you want to print. `mark-my-word` connects the two: it reads your Word template and uses it to render your Markdown as a styled PDF.

## Is this for me?

It's a good fit if:

- You have a `.docx` or `.dotx` Word template with the styling you want
- You want your PDFs to match that template without manual CSS work
- You're happy running a command-line tool

It's probably not for you if you need pixel-perfect fidelity to complex Word layouts (tables with merged cells, tracked changes, etc.) or if you need to render Word documents directly — this tool works with Markdown input.

## Install

```shell
uv tool install mark-my-word
# or
pipx install mark-my-word
```

Requires Python 3.11+. WeasyPrint (the PDF engine) needs some system libraries — see the [WeasyPrint docs](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#installation) if the install fails.

## Basic usage

Pass your Word template directly and `mark-my-word` does the rest:

```shell
mark-my-word render document.md --template my-template.dotx
```

Output lands next to the input file by default (`document.pdf`). Use `--output` to put it somewhere else:

```shell
mark-my-word render document.md --template my-template.dotx --output /tmp/document.pdf
```

If the output file already exists, the command refuses rather than silently overwriting. Use `--force` (or `-f`) to overwrite:

```shell
mark-my-word render document.md --template my-template.dotx --force
```

### Or extract the theme separately

If you render from the same template often, extract it once and reuse the theme directory:

```shell
mark-my-word extract my-template.dotx --output themes/my-theme/
mark-my-word render document.md --theme themes/my-theme/
```

The extracted theme is plain CSS and HTML — easy to inspect or tweak. Add `--save-theme themes/my-theme/` to the one-step command if you want to keep the theme without a separate extract step.

## Document frontmatter

Headers and footers in your theme can include placeholders like `{{title}}` or `{{author}}`. You can also supply a fallback: `{{title: Untitled}}` is used when `title` is not set. Matching is case-insensitive.

Set values in your Markdown file's YAML frontmatter:

```markdown
---
title: Q1 Report
author: Daniel
---

# Introduction

...
```

Pass extra values on the command line with `--set`:

```shell
mark-my-word render document.md --theme themes/my-theme/ --set version=draft
```

## Skipping preamble

If you write notes in an app like NotePlan, there's often metadata at the top — tags, links, app-specific syntax — that you don't want in the PDF. Put `<!--more-->` on its own line to mark where your real content starts:

```markdown
@project(Quarterly Review)
[[linked-note]]

<!--more-->

# Q1 Report

...
```

Everything before `<!--more-->` is ignored. Everything after renders normally. If `<!--more-->` appears inside a code block in the body, put a second one at the top of the document to act as the separator instead.

## Heading anchors and links

Every heading gets an anchor derived from its text, so you can link between sections. The trouble is that the anchor for "Everything you wanted to know about widgets" is `everything-you-wanted-to-know-about-widgets` — correct, and miserable to type into a link.

So name it yourself. Put `{#your-id}` at the end of the heading line:

```markdown
# Everything you wanted to know about widgets {#widgets}

See [the widget section](#widgets) for the details.
```

The link resolves to the heading, and the id holds steady even when you reword the heading text. One catch: the `{#id}` has to sit at the end of the heading line. On its own line above the heading it does not attach — it renders as literal text instead.

The same braces carry CSS classes too, if your theme has a use for them: `# Heading {#widgets .callout}` adds `class="callout"`.

### Table of contents

Put `[TOC]` on its own line and it becomes a linked table of contents, one entry per heading:

```markdown
[TOC]

# Introduction

# Details
```

Newly extracted themes render it as an indented list, each entry followed by a dotted leader and the page its heading lands on. The look is set by the `.toc` rules in your theme's `style.css`; the quickest knobs are `--toc-leader` (the leader: `'.'` dots, `'-'` dashes, `' '` blank, `''` for none) and `--toc-indent` (per-level indent), which you can set in `:root`.

By default the contents lists every heading level. To limit it, set `toc_levels` in frontmatter — a single number for "up to that level", or a range:

```yaml
---
toc_levels: 2-4    # or 3 for H1–H3
---
```

A single document can override the leader with `toc_leader` (same values as `--toc-leader`) — a one-off flourish without touching the theme.

## Footnotes

A footnote renders at the foot of the page it is referenced on. Reference it with `[^label]` and define it anywhere in the document:

```markdown
The claim rests on shaky ground[^pub].

[^pub]: Source: overheard at the pub.
```

The label is yours to name — `pub`, not a number. You reference and define by the same handle, so inserting or removing a footnote never makes you renumber anything by hand. The rendered marker is a plain number, counted in the order the footnotes appear.

A footnote is defined once and referenced once. Defining the same `[^label]` twice is an error — there is no telling which definition you meant — and referencing it twice is too, since one note shared across two places is not supported. Either way, `render` stops and names the label.

## What gets extracted

From your Word template, `mark-my-word extract` pulls:

- Page margins
- Theme colours → CSS custom properties (`--color-body`, `--color-heading`, etc.)
- Body text formatting: font family, size, line height, and paragraph spacing (from the Normal style)
- Heading styles: font size, weight, colour, and spacing for H1–H6
- Header and footer content, including tab-separated left/right layouts, page numbers, and paragraph borders (top and bottom)
- Watermark images

The result is a plain directory you can inspect and edit. The stylesheet uses CSS custom properties throughout, so tweaking colours or fonts is a one-line change.

## Theme directory layout

```
themes/my-theme/
  style.css           required
  header.html         header on all pages (or cont-header.html / first-header.html)
  footer.html         footer on all pages (or cont-footer.html / first-footer.html)
  watermark.png       optional full-page background image
```

If your Word template has a different header on the first page, `extract` writes `first-header.html` and `cont-header.html` separately. `render` handles both layouts automatically.

## Tips for Word templates

### Left/right footer layouts

For a footer with content on the left and right (e.g. company name on the left, page number on the right), use a **single paragraph with a right-aligned tab stop** rather than a table:

1. In your footer, type the left content
2. Press Tab
3. Type the right content
4. Set a right-aligned tab stop at the right margin (Format → Tabs, or drag the tab marker in the ruler)

`mark-my-word` converts the tab character into a flex-row layout. Tables work too, but they carry implicit cell spacing that doesn't always translate cleanly.

### Horizontal rules

To draw a decorative line above or below footer content, add a paragraph border (Format → Borders and Shading → Borders tab) rather than an actual horizontal rule or a separate table row. Paragraph top and bottom borders are extracted and rendered faithfully.

## Options

```
mark-my-word render --help
mark-my-word extract --help
```

## Changelog

### 0.5.0

- The `[TOC]` table of contents now shows the page each heading lands on, with a dotted leader between title and number. `toc_levels` in frontmatter limits which heading levels it lists (`3` for H1–H3, or a range like `2-4`), and `toc_leader` overrides the leader character per document.

### 0.4.0

- Footnotes: `[^label]` references render at the foot of the page they appear on, with named labels you never renumber by hand and CSS-counted numbers. Reusing a label is an error.

### 0.3.0

- Headings can carry a hand-picked anchor — `# Long heading {#short}` — so you can link to `#short` instead of the auto-generated slug. The same `{...}` syntax also sets CSS classes and other attributes.
- `[TOC]` on its own line expands into a linked table of contents. Newly extracted themes style it as a plain indented list.
- The Markdown engine moved from markdown2 to Python-Markdown — this is what makes the anchor and table-of-contents support possible. Existing documents render unchanged.

### 0.2.3

- More style properties are now extracted from the Word template and applied to the PDF: body text font size, line height, and paragraph spacing (from the Normal style) join heading size, weight, colour, and spacing (H1–H6) as template-driven values rather than hardcoded defaults
- Internal cleanup: removed unused helper functions

### 0.2.2

- README updated with 0.2.1 changes (should have shipped with the release)

### 0.2.1

- Paragraph borders (top and bottom) in Word headers and footers are now extracted and rendered with correct spacing
- Empty paragraphs used as horizontal rules extract at natural line height, matching Word's visual spacing
- Footer height estimation fixed: `<p>` elements inside flex-row cells are no longer counted as separate lines, preventing over-large body margins
- Page break orphan/widow protection relaxed from 3 lines to 2

### 0.2.0

- First-page header/footer fallback: if no distinct first-page fragments exist, the running header/footer is used on all pages
- `<!--more-->` preamble separator support
- `{{variable: default}}` syntax for fragment placeholders
- H1 extracted as document title and removed from body when no `title:` frontmatter is set
- `--force` flag to overwrite existing output files
- Watermark extraction from Word VML `<w:pict>` elements

## Links

- [Repository](https://gitlab.com/tastapod/mark-my-word)
- [Issue tracker](https://gitlab.com/tastapod/mark-my-word/-/issues)
