markdown-docx 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- markdown_docx-0.1.0/.github/workflows/ci.yml +125 -0
- markdown_docx-0.1.0/.github/workflows/publish-pypi.yml +109 -0
- markdown_docx-0.1.0/.gitignore +12 -0
- markdown_docx-0.1.0/AGENTS.md +41 -0
- markdown_docx-0.1.0/CHANGELOG.md +9 -0
- markdown_docx-0.1.0/LICENSE +21 -0
- markdown_docx-0.1.0/PKG-INFO +372 -0
- markdown_docx-0.1.0/PLAN.md +840 -0
- markdown_docx-0.1.0/README.md +341 -0
- markdown_docx-0.1.0/docs/public-api-capabilities.md +29 -0
- markdown_docx-0.1.0/pyproject.toml +72 -0
- markdown_docx-0.1.0/sample/assets/dog-lunch-chase.png +0 -0
- markdown_docx-0.1.0/sample/assets/dog-run-finish.png +0 -0
- markdown_docx-0.1.0/sample/assets/dog-run-start.png +0 -0
- markdown_docx-0.1.0/sample/assets/dog-trio-cameo.png +0 -0
- markdown_docx-0.1.0/sample/assets/dog-trio-inline.png +0 -0
- markdown_docx-0.1.0/sample/assets/dog-wagon-rescue.png +0 -0
- markdown_docx-0.1.0/sample/assets/illustration-prompts.md +31 -0
- markdown_docx-0.1.0/sample/assets/word-icon.png +0 -0
- markdown_docx-0.1.0/sample/assets/word-workflow.png +0 -0
- markdown_docx-0.1.0/sample/showcase.docx +0 -0
- markdown_docx-0.1.0/sample/showcase.md +310 -0
- markdown_docx-0.1.0/scripts/Export-DocxPdf.ps1 +34 -0
- markdown_docx-0.1.0/scripts/build_default_template.py +94 -0
- markdown_docx-0.1.0/scripts/build_qa_templates.py +38 -0
- markdown_docx-0.1.0/scripts/build_showcase_assets.py +60 -0
- markdown_docx-0.1.0/src/markdown_docx/__init__.py +1 -0
- markdown_docx-0.1.0/src/markdown_docx/assets/default.docx +0 -0
- markdown_docx-0.1.0/src/markdown_docx/assets/syntax.json +47 -0
- markdown_docx-0.1.0/src/markdown_docx/assets.py +15 -0
- markdown_docx-0.1.0/src/markdown_docx/cli.py +318 -0
- markdown_docx-0.1.0/src/markdown_docx/errors.py +118 -0
- markdown_docx-0.1.0/src/markdown_docx/images.py +161 -0
- markdown_docx-0.1.0/src/markdown_docx/markdown_body.py +121 -0
- markdown_docx-0.1.0/src/markdown_docx/metadata.py +349 -0
- markdown_docx-0.1.0/src/markdown_docx/models.py +180 -0
- markdown_docx-0.1.0/src/markdown_docx/parser.py +456 -0
- markdown_docx-0.1.0/src/markdown_docx/renderer.py +285 -0
- markdown_docx-0.1.0/src/markdown_docx/skill.py +123 -0
- markdown_docx-0.1.0/src/markdown_docx/styles.py +50 -0
- markdown_docx-0.1.0/src/markdown_docx/template.py +121 -0
- markdown_docx-0.1.0/tests/conftest.py +33 -0
- markdown_docx-0.1.0/tests/test_cli.py +163 -0
- markdown_docx-0.1.0/tests/test_metadata.py +152 -0
- markdown_docx-0.1.0/tests/test_parser.py +179 -0
- markdown_docx-0.1.0/tests/test_public_api_boundary.py +21 -0
- markdown_docx-0.1.0/tests/test_public_api_capabilities.py +82 -0
- markdown_docx-0.1.0/tests/test_renderer_images.py +85 -0
- markdown_docx-0.1.0/tests/test_renderer_lists.py +39 -0
- markdown_docx-0.1.0/tests/test_renderer_sections.py +49 -0
- markdown_docx-0.1.0/tests/test_renderer_tables.py +59 -0
- markdown_docx-0.1.0/tests/test_renderer_text.py +76 -0
- markdown_docx-0.1.0/tests/test_showcase.py +136 -0
- markdown_docx-0.1.0/tests/test_template.py +89 -0
- markdown_docx-0.1.0/uv.lock +1314 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- name: Check out repository
|
|
18
|
+
uses: actions/checkout@v7.0.1
|
|
19
|
+
|
|
20
|
+
- name: Set up uv
|
|
21
|
+
uses: astral-sh/setup-uv@v10.0.1
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.13"
|
|
24
|
+
enable-cache: true
|
|
25
|
+
|
|
26
|
+
- name: Install locked dependencies
|
|
27
|
+
run: uv sync --locked --all-groups
|
|
28
|
+
|
|
29
|
+
- name: Lint
|
|
30
|
+
run: uv run ruff check .
|
|
31
|
+
|
|
32
|
+
- name: Check formatting
|
|
33
|
+
run: uv run ruff format --check .
|
|
34
|
+
|
|
35
|
+
- name: Type check
|
|
36
|
+
run: uv run mypy
|
|
37
|
+
|
|
38
|
+
test:
|
|
39
|
+
strategy:
|
|
40
|
+
fail-fast: false
|
|
41
|
+
matrix:
|
|
42
|
+
os: [ubuntu-latest, windows-latest]
|
|
43
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
44
|
+
runs-on: ${{ matrix.os }}
|
|
45
|
+
env:
|
|
46
|
+
UV_LINK_MODE: copy
|
|
47
|
+
steps:
|
|
48
|
+
- name: Check out repository
|
|
49
|
+
uses: actions/checkout@v7.0.1
|
|
50
|
+
|
|
51
|
+
- name: Set up uv
|
|
52
|
+
uses: astral-sh/setup-uv@v10.0.1
|
|
53
|
+
with:
|
|
54
|
+
python-version: ${{ matrix.python-version }}
|
|
55
|
+
enable-cache: true
|
|
56
|
+
|
|
57
|
+
- name: Install locked dependencies
|
|
58
|
+
run: uv sync --locked --all-groups
|
|
59
|
+
|
|
60
|
+
- name: Run tests
|
|
61
|
+
run: uv run pytest
|
|
62
|
+
|
|
63
|
+
visual-smoke:
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
steps:
|
|
66
|
+
- name: Check out repository
|
|
67
|
+
uses: actions/checkout@v7.0.1
|
|
68
|
+
|
|
69
|
+
- name: Set up uv
|
|
70
|
+
uses: astral-sh/setup-uv@v10.0.1
|
|
71
|
+
with:
|
|
72
|
+
python-version: "3.13"
|
|
73
|
+
enable-cache: true
|
|
74
|
+
|
|
75
|
+
- name: Install LibreOffice and Poppler
|
|
76
|
+
run: sudo apt-get update && sudo apt-get install -y libreoffice-writer poppler-utils
|
|
77
|
+
|
|
78
|
+
- name: Render showcase DOCX
|
|
79
|
+
run: |
|
|
80
|
+
mkdir -p .visual
|
|
81
|
+
uvx --from . markdown-docx sample/showcase.md .visual/showcase.docx
|
|
82
|
+
|
|
83
|
+
- name: Convert showcase to PDF
|
|
84
|
+
run: libreoffice --headless --convert-to pdf --outdir .visual .visual/showcase.docx
|
|
85
|
+
|
|
86
|
+
- name: Verify rendered PDF
|
|
87
|
+
run: pdfinfo .visual/showcase.pdf
|
|
88
|
+
|
|
89
|
+
package:
|
|
90
|
+
needs: [lint, test, visual-smoke]
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
steps:
|
|
93
|
+
- name: Check out repository
|
|
94
|
+
uses: actions/checkout@v7.0.1
|
|
95
|
+
|
|
96
|
+
- name: Set up uv
|
|
97
|
+
uses: astral-sh/setup-uv@v10.0.1
|
|
98
|
+
with:
|
|
99
|
+
python-version: "3.13"
|
|
100
|
+
enable-cache: true
|
|
101
|
+
|
|
102
|
+
- name: Install locked dependencies
|
|
103
|
+
run: uv sync --locked --all-groups
|
|
104
|
+
|
|
105
|
+
- name: Build distributions
|
|
106
|
+
run: uv build
|
|
107
|
+
|
|
108
|
+
- name: Check distribution metadata
|
|
109
|
+
run: uv run twine check dist/*
|
|
110
|
+
|
|
111
|
+
- name: Smoke-test the wheel
|
|
112
|
+
shell: bash
|
|
113
|
+
run: |
|
|
114
|
+
python -m venv .wheel-smoke
|
|
115
|
+
.wheel-smoke/bin/python -m pip install dist/*.whl
|
|
116
|
+
.wheel-smoke/bin/markdown-docx --version
|
|
117
|
+
.wheel-smoke/bin/markdown-docx --syntax --json
|
|
118
|
+
echo '# Wheel smoke test' > smoke.md
|
|
119
|
+
.wheel-smoke/bin/markdown-docx smoke.md smoke.docx
|
|
120
|
+
|
|
121
|
+
- name: Upload distributions
|
|
122
|
+
uses: actions/upload-artifact@v7.0.1
|
|
123
|
+
with:
|
|
124
|
+
name: distributions
|
|
125
|
+
path: dist/
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types:
|
|
6
|
+
- published
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
quality:
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
os: [ubuntu-latest, windows-latest]
|
|
18
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
19
|
+
runs-on: ${{ matrix.os }}
|
|
20
|
+
env:
|
|
21
|
+
UV_LINK_MODE: copy
|
|
22
|
+
steps:
|
|
23
|
+
- name: Check out repository
|
|
24
|
+
uses: actions/checkout@v7.0.1
|
|
25
|
+
|
|
26
|
+
- name: Set up uv
|
|
27
|
+
uses: astral-sh/setup-uv@v10.0.1
|
|
28
|
+
with:
|
|
29
|
+
python-version: ${{ matrix.python-version }}
|
|
30
|
+
enable-cache: true
|
|
31
|
+
|
|
32
|
+
- name: Install locked dependencies
|
|
33
|
+
run: uv sync --locked --all-groups
|
|
34
|
+
|
|
35
|
+
- name: Lint, format, and type check
|
|
36
|
+
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13'
|
|
37
|
+
run: |
|
|
38
|
+
uv run ruff check .
|
|
39
|
+
uv run ruff format --check .
|
|
40
|
+
uv run mypy
|
|
41
|
+
|
|
42
|
+
- name: Run tests
|
|
43
|
+
run: uv run pytest
|
|
44
|
+
|
|
45
|
+
- name: Verify release tag matches package version
|
|
46
|
+
if: github.event_name == 'release' && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13'
|
|
47
|
+
shell: bash
|
|
48
|
+
run: |
|
|
49
|
+
version_output="$(uv run markdown-docx --version)"
|
|
50
|
+
package_version="${version_output#markdown-docx }"
|
|
51
|
+
tag_version="${GITHUB_REF_NAME#v}"
|
|
52
|
+
test "$package_version" = "$tag_version" || {
|
|
53
|
+
echo "Package version $package_version does not match release tag $GITHUB_REF_NAME."
|
|
54
|
+
exit 1
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
build:
|
|
58
|
+
needs: quality
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
steps:
|
|
61
|
+
- name: Check out repository
|
|
62
|
+
uses: actions/checkout@v7.0.1
|
|
63
|
+
|
|
64
|
+
- name: Set up uv
|
|
65
|
+
uses: astral-sh/setup-uv@v10.0.1
|
|
66
|
+
with:
|
|
67
|
+
python-version: "3.13"
|
|
68
|
+
enable-cache: true
|
|
69
|
+
|
|
70
|
+
- name: Install locked dependencies
|
|
71
|
+
run: uv sync --locked --all-groups
|
|
72
|
+
|
|
73
|
+
- name: Build distributions
|
|
74
|
+
run: uv build
|
|
75
|
+
|
|
76
|
+
- name: Check distribution metadata
|
|
77
|
+
run: uv run twine check dist/*
|
|
78
|
+
|
|
79
|
+
- name: Smoke-test the wheel
|
|
80
|
+
shell: bash
|
|
81
|
+
run: |
|
|
82
|
+
python -m venv .wheel-smoke
|
|
83
|
+
.wheel-smoke/bin/python -m pip install dist/*.whl
|
|
84
|
+
.wheel-smoke/bin/markdown-docx --version
|
|
85
|
+
.wheel-smoke/bin/markdown-docx --inspect-template --json
|
|
86
|
+
|
|
87
|
+
- name: Upload distributions
|
|
88
|
+
uses: actions/upload-artifact@v7.0.1
|
|
89
|
+
with:
|
|
90
|
+
name: distributions
|
|
91
|
+
path: dist/
|
|
92
|
+
|
|
93
|
+
publish:
|
|
94
|
+
needs: build
|
|
95
|
+
runs-on: ubuntu-latest
|
|
96
|
+
environment:
|
|
97
|
+
name: pypi
|
|
98
|
+
url: https://pypi.org/p/markdown-docx
|
|
99
|
+
permissions:
|
|
100
|
+
id-token: write
|
|
101
|
+
steps:
|
|
102
|
+
- name: Download distributions
|
|
103
|
+
uses: actions/download-artifact@v8.0.1
|
|
104
|
+
with:
|
|
105
|
+
name: distributions
|
|
106
|
+
path: dist/
|
|
107
|
+
|
|
108
|
+
- name: Publish to PyPI
|
|
109
|
+
uses: pypa/gh-action-pypi-publish@v1.14.2
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This repository contains `markdown-docx`, a strict Python CLI that converts constrained Markdown into editable Word `.docx` files.
|
|
4
|
+
|
|
5
|
+
## Project identity
|
|
6
|
+
|
|
7
|
+
- Published package: `markdown-docx`
|
|
8
|
+
- CLI command: `markdown-docx`
|
|
9
|
+
- Python package: `markdown_docx`
|
|
10
|
+
|
|
11
|
+
## Core rules
|
|
12
|
+
|
|
13
|
+
- Use only supported public `python-docx` APIs in production code.
|
|
14
|
+
- Never edit OOXML parts directly in production code.
|
|
15
|
+
- Preserve normal Markdown meaning and keep Word metadata in invisible reserved HTML comments.
|
|
16
|
+
- Reject unsupported behavior with stable, line-aware diagnostics.
|
|
17
|
+
- Treat Word sections as layout boundaries. Headings never create sections.
|
|
18
|
+
- Never report a stable page count without a Word-compatible layout engine.
|
|
19
|
+
- Keep parser models independent of `python-docx` objects.
|
|
20
|
+
- Add or update tests whenever behavior changes.
|
|
21
|
+
- Do not use em dashes or semicolons in documentation, messages, or comments.
|
|
22
|
+
|
|
23
|
+
Tests may inspect generated OOXML read-only for precise assertions.
|
|
24
|
+
|
|
25
|
+
## Commands
|
|
26
|
+
|
|
27
|
+
```powershell
|
|
28
|
+
$env:UV_LINK_MODE="copy"
|
|
29
|
+
uv sync --locked --all-groups
|
|
30
|
+
uv run pytest
|
|
31
|
+
uv run ruff check .
|
|
32
|
+
uv run ruff format --check .
|
|
33
|
+
uv run mypy
|
|
34
|
+
uv build
|
|
35
|
+
uv run twine check dist/*
|
|
36
|
+
uvx --refresh --from . markdown-docx sample\showcase.md sample\showcase.docx --force
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Contract files
|
|
40
|
+
|
|
41
|
+
When the input format changes, update the parser, tests, `README.md`, `src/markdown_docx/assets/syntax.json`, the managed skill text, and the showcase together.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Add strict Markdown parsing with invisible YAML directives and line-aware diagnostics.
|
|
6
|
+
- Add editable Word rendering for text, headings, blockquotes, code, mixed nested lists, tables, images, page breaks, and sections.
|
|
7
|
+
- Add blank `.docx` templates, semantic style mapping, font overrides, template inspection, and a packaged default template.
|
|
8
|
+
- Add safe local and remote image handling, JSON automation output, overwrite protection, syntax discovery, and managed agent skill commands.
|
|
9
|
+
- Add a complete test suite, showcase document, CI, package validation, wheel smoke tests, and trusted PyPI publishing.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 John Paul Ellis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: markdown-docx
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Convert constrained Markdown documents into editable Word files.
|
|
5
|
+
Project-URL: Homepage, https://github.com/pseudosavant/markdown-docx
|
|
6
|
+
Project-URL: Repository, https://github.com/pseudosavant/markdown-docx
|
|
7
|
+
Project-URL: Issues, https://github.com/pseudosavant/markdown-docx/issues
|
|
8
|
+
Project-URL: Releases, https://github.com/pseudosavant/markdown-docx/releases
|
|
9
|
+
Author: John Paul Ellis
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: cli,document,docx,markdown,word
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Office/Business :: Office Suites
|
|
22
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
23
|
+
Classifier: Topic :: Text Processing :: Markup
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: httpx<1,>=0.27
|
|
26
|
+
Requires-Dist: markdown-it-py<5,>=3.0
|
|
27
|
+
Requires-Dist: pillow<13,>=10.0
|
|
28
|
+
Requires-Dist: python-docx==1.2.0
|
|
29
|
+
Requires-Dist: pyyaml<7,>=6.0
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# markdown-docx
|
|
33
|
+
|
|
34
|
+
`markdown-docx` turns constrained Markdown into predictable, editable Word `.docx` files. The Markdown stays readable in normal renderers. Word-specific layout and style settings live in invisible HTML comments.
|
|
35
|
+
|
|
36
|
+
The format is strict by design. Unsupported input produces a stable, line-aware error instead of an approximate document.
|
|
37
|
+
|
|
38
|
+
## Prerequisite
|
|
39
|
+
|
|
40
|
+
The documented workflow uses [`uv`](https://docs.astral.sh/uv/getting-started/installation/). Install `uv` before continuing.
|
|
41
|
+
|
|
42
|
+
## Quick start with an agent
|
|
43
|
+
|
|
44
|
+
Install the managed agent skill:
|
|
45
|
+
|
|
46
|
+
```powershell
|
|
47
|
+
uvx markdown-docx skill install
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then ask an agent to create both the Markdown source and the editable Word output:
|
|
51
|
+
|
|
52
|
+
> Use $markdown-docx to create a project brief. Keep the source readable as normal Markdown and save the editable DOCX beside it.
|
|
53
|
+
|
|
54
|
+
The skill teaches the agent how to inspect the format, inspect blank Word templates, render safely, and handle structured results.
|
|
55
|
+
|
|
56
|
+
## Use the CLI directly
|
|
57
|
+
|
|
58
|
+
```powershell
|
|
59
|
+
uvx markdown-docx document.md document.docx
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
When the output path is omitted, the tool writes a `.docx` beside the input Markdown file.
|
|
63
|
+
|
|
64
|
+
```powershell
|
|
65
|
+
uvx markdown-docx document.md
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Inspect the supported syntax:
|
|
69
|
+
|
|
70
|
+
```powershell
|
|
71
|
+
uvx markdown-docx --syntax
|
|
72
|
+
uvx markdown-docx --syntax --json
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Supported Markdown
|
|
76
|
+
|
|
77
|
+
Version 0.1.0 supports:
|
|
78
|
+
|
|
79
|
+
- ATX headings from `#` through `######`
|
|
80
|
+
- Paragraphs and standard soft or hard line breaks
|
|
81
|
+
- Emphasis, strong emphasis, and inline backtick code
|
|
82
|
+
- Fenced code blocks
|
|
83
|
+
- Blockquotes containing paragraphs
|
|
84
|
+
- Ordered and unordered lists, including mixed nesting
|
|
85
|
+
- Pipe tables with inline text formatting
|
|
86
|
+
- Local and remote inline images
|
|
87
|
+
- Standalone images with width and alignment metadata
|
|
88
|
+
|
|
89
|
+
Links are rejected in 0.1.0. `python-docx` 1.2.0 can read hyperlinks but has no supported public API for creating them. The source alt text for images remains meaningful Markdown content, but the same library release has no public API for embedding it in a Word drawing. A rendered document containing images reports `image_alt_text_not_embedded` in its warning list.
|
|
90
|
+
|
|
91
|
+
The following syntax is intentionally unsupported:
|
|
92
|
+
|
|
93
|
+
- Raw HTML and non-reserved HTML comments
|
|
94
|
+
- Setext headings and horizontal rules
|
|
95
|
+
- Indented code blocks
|
|
96
|
+
- Task lists and footnotes
|
|
97
|
+
- Multi-paragraph list items
|
|
98
|
+
- Tables, images, code blocks, or blockquotes nested inside list items
|
|
99
|
+
- Images inside table cells or blockquotes
|
|
100
|
+
- Arbitrary Markdown extensions
|
|
101
|
+
|
|
102
|
+
## Invisible Word metadata
|
|
103
|
+
|
|
104
|
+
Only HTML comments beginning with `markdown-docx` are accepted. All other HTML is an error.
|
|
105
|
+
|
|
106
|
+
Document metadata may appear once. It must be the first non-whitespace content:
|
|
107
|
+
|
|
108
|
+
```markdown
|
|
109
|
+
<!-- markdown-docx
|
|
110
|
+
document:
|
|
111
|
+
page_size: letter
|
|
112
|
+
orientation: portrait
|
|
113
|
+
margins:
|
|
114
|
+
top: 1in
|
|
115
|
+
right: 1in
|
|
116
|
+
bottom: 1in
|
|
117
|
+
left: 1in
|
|
118
|
+
styles:
|
|
119
|
+
paragraph: Normal
|
|
120
|
+
headings:
|
|
121
|
+
1: Heading 1
|
|
122
|
+
2: Heading 2
|
|
123
|
+
3: Heading 3
|
|
124
|
+
4: Heading 4
|
|
125
|
+
5: Heading 5
|
|
126
|
+
6: Heading 6
|
|
127
|
+
blockquote: Quote
|
|
128
|
+
code_block: Code Block
|
|
129
|
+
ordered_list:
|
|
130
|
+
- List Number
|
|
131
|
+
- List Number 2
|
|
132
|
+
- List Number 3
|
|
133
|
+
unordered_list:
|
|
134
|
+
- List Bullet
|
|
135
|
+
- List Bullet 2
|
|
136
|
+
- List Bullet 3
|
|
137
|
+
table: Table Grid
|
|
138
|
+
fonts:
|
|
139
|
+
body: Calibri
|
|
140
|
+
headings: Calibri
|
|
141
|
+
monospace: Consolas
|
|
142
|
+
-->
|
|
143
|
+
|
|
144
|
+
# Project brief
|
|
145
|
+
|
|
146
|
+
This remains ordinary Markdown.
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Metadata uses strict YAML. Duplicate keys, unknown keys, invalid types, unitless lengths, and misplaced comments are errors.
|
|
150
|
+
|
|
151
|
+
### Page sizes and margins
|
|
152
|
+
|
|
153
|
+
Named page sizes are `letter`, `legal`, and `a4`. Custom page sizes use nominal portrait dimensions. The width must not exceed the height.
|
|
154
|
+
|
|
155
|
+
```markdown
|
|
156
|
+
<!-- markdown-docx
|
|
157
|
+
document:
|
|
158
|
+
page_size:
|
|
159
|
+
width: 7in
|
|
160
|
+
height: 10in
|
|
161
|
+
orientation: portrait
|
|
162
|
+
margins:
|
|
163
|
+
top: 0.75in
|
|
164
|
+
right: 0.75in
|
|
165
|
+
bottom: 0.75in
|
|
166
|
+
left: 0.75in
|
|
167
|
+
-->
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Lengths accept `in`, `cm`, `mm`, and `pt`. Margins must leave a positive usable page area.
|
|
171
|
+
|
|
172
|
+
### Sections
|
|
173
|
+
|
|
174
|
+
A section directive starts a next-page Word section before the following content block:
|
|
175
|
+
|
|
176
|
+
```markdown
|
|
177
|
+
<!-- markdown-docx
|
|
178
|
+
section:
|
|
179
|
+
page_size: letter
|
|
180
|
+
orientation: landscape
|
|
181
|
+
margins:
|
|
182
|
+
top: 0.75in
|
|
183
|
+
right: 0.75in
|
|
184
|
+
bottom: 0.75in
|
|
185
|
+
left: 0.75in
|
|
186
|
+
-->
|
|
187
|
+
|
|
188
|
+
## Landscape analysis
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Each section starts from the document defaults and applies its own overrides. It does not inherit omitted values from the preceding section. Reset to the document defaults with:
|
|
192
|
+
|
|
193
|
+
```markdown
|
|
194
|
+
<!-- markdown-docx
|
|
195
|
+
section: default
|
|
196
|
+
-->
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Headings never create sections.
|
|
200
|
+
|
|
201
|
+
### Page breaks
|
|
202
|
+
|
|
203
|
+
Insert a page break before the next content block:
|
|
204
|
+
|
|
205
|
+
```markdown
|
|
206
|
+
<!-- markdown-docx: page-break -->
|
|
207
|
+
|
|
208
|
+
## Appendix
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
A page break does not create a new section.
|
|
212
|
+
|
|
213
|
+
## Templates and styles
|
|
214
|
+
|
|
215
|
+
A custom template must be a blank `.docx` formatting template. `.dotx` is not supported. The template may define styles, theme data, fonts, numbering definitions, and section defaults. It must not contain:
|
|
216
|
+
|
|
217
|
+
- Non-whitespace body text
|
|
218
|
+
- Body tables
|
|
219
|
+
- Body images or drawings
|
|
220
|
+
- Nonempty headers or footers
|
|
221
|
+
|
|
222
|
+
Inspect a template before writing Markdown that refers to its style names:
|
|
223
|
+
|
|
224
|
+
```powershell
|
|
225
|
+
uvx markdown-docx --inspect-template --template formatting.docx
|
|
226
|
+
uvx markdown-docx --list-styles --template formatting.docx
|
|
227
|
+
uvx markdown-docx --list-table-styles --template formatting.docx
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Render with the template:
|
|
231
|
+
|
|
232
|
+
```powershell
|
|
233
|
+
uvx markdown-docx report.md report.docx --template formatting.docx
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
The template remains unchanged. When no template is supplied, the packaged blank template provides every default style.
|
|
237
|
+
|
|
238
|
+
The Markdown metadata maps semantic constructs to paragraph and table style names. Each configured style must exist and must have the correct Word style type. Body and heading font overrides modify the mapped paragraph styles. The monospace override applies to code blocks and inline code. Sizes, colors, spacing, borders, and other typography remain owned by the template.
|
|
239
|
+
|
|
240
|
+
## Lists
|
|
241
|
+
|
|
242
|
+
Apply one Word paragraph style per list type and nesting depth:
|
|
243
|
+
|
|
244
|
+
```yaml
|
|
245
|
+
styles:
|
|
246
|
+
ordered_list:
|
|
247
|
+
- List Number
|
|
248
|
+
- List Number 2
|
|
249
|
+
- List Number 3
|
|
250
|
+
unordered_list:
|
|
251
|
+
- List Bullet
|
|
252
|
+
- List Bullet 2
|
|
253
|
+
- List Bullet 3
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Mixed nested lists select ordered or unordered styles independently at every depth. A list deeper than the configured style array is an error. Ordered lists must begin with `1`. Restart controls and arbitrary start values are not supported.
|
|
257
|
+
|
|
258
|
+
## Tables
|
|
259
|
+
|
|
260
|
+
Standard pipe tables become editable Word tables. Put optional table metadata immediately before the table:
|
|
261
|
+
|
|
262
|
+
```markdown
|
|
263
|
+
<!-- markdown-docx
|
|
264
|
+
table:
|
|
265
|
+
style: Table Grid
|
|
266
|
+
alignment: center
|
|
267
|
+
width: page
|
|
268
|
+
column_widths: [3, 1, 1]
|
|
269
|
+
-->
|
|
270
|
+
|
|
271
|
+
| Item | Count | Price |
|
|
272
|
+
| --- | ---: | ---: |
|
|
273
|
+
| Widget | 2 | $10 |
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The first Markdown row is the semantic header row. Cell alignment follows the Markdown delimiter row. `column_widths` contains positive ratios and must match the column count. `width: page` uses the active section's usable width. `width: auto` lets Word size the table unless ratios are supplied.
|
|
277
|
+
|
|
278
|
+
Merged cells, nested tables, fixed row heights, repeated-header controls, and per-cell border or fill metadata are not supported.
|
|
279
|
+
|
|
280
|
+
## Images
|
|
281
|
+
|
|
282
|
+
Relative paths resolve from the Markdown file's directory:
|
|
283
|
+
|
|
284
|
+
```markdown
|
|
285
|
+
Text before  text after.
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Put metadata immediately before a standalone image:
|
|
289
|
+
|
|
290
|
+
```markdown
|
|
291
|
+
<!-- markdown-docx
|
|
292
|
+
image:
|
|
293
|
+
width: 40%
|
|
294
|
+
alignment: center
|
|
295
|
+
-->
|
|
296
|
+

|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Widths accept `in`, `cm`, `mm`, `pt`, or a percentage of the active section's usable width. Images preserve aspect ratio. Natural-size images are clamped to the usable width. An explicit physical width that exceeds the usable width is an error.
|
|
300
|
+
|
|
301
|
+
HTTP and HTTPS images use timeouts, a 25 MiB download limit, content-type validation, a 50 megapixel decode limit, and one download per unique URL. Reject remote images for offline builds or untrusted input:
|
|
302
|
+
|
|
303
|
+
```powershell
|
|
304
|
+
uvx markdown-docx report.md --no-remote-images
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Automation and safety
|
|
308
|
+
|
|
309
|
+
Use `--json` for one complete machine-readable result:
|
|
310
|
+
|
|
311
|
+
```powershell
|
|
312
|
+
uvx markdown-docx report.md report.docx --json
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
A successful result contains the input path, output path, template identifier, section count, and warnings. It does not report a page count. DOCX files do not have a reliable intrinsic page count until a compatible layout engine paginates them.
|
|
316
|
+
|
|
317
|
+
The CLI refuses to overwrite an existing output. Add `--force` only when replacement is intended:
|
|
318
|
+
|
|
319
|
+
```powershell
|
|
320
|
+
uvx markdown-docx report.md report.docx --force --json
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
For stdin, provide both an output path and a base directory:
|
|
324
|
+
|
|
325
|
+
```powershell
|
|
326
|
+
Get-Content report.md | uvx markdown-docx --input - --output report.docx --base-dir .
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Exit codes:
|
|
330
|
+
|
|
331
|
+
| Code | Meaning |
|
|
332
|
+
| ---: | --- |
|
|
333
|
+
| `0` | Success |
|
|
334
|
+
| `2` | Usage or input error |
|
|
335
|
+
| `3` | Markdown or metadata parse error |
|
|
336
|
+
| `4` | Template or style error |
|
|
337
|
+
| `5` | Image or asset error |
|
|
338
|
+
| `6` | Unsupported Markdown or feature |
|
|
339
|
+
| `7` | DOCX rendering error |
|
|
340
|
+
| `8` | Unexpected internal error |
|
|
341
|
+
|
|
342
|
+
## Complete example
|
|
343
|
+
|
|
344
|
+
See [the showcase Markdown](sample/showcase.md). It opens with an illustrated three-dog story, then exercises supported text, list, table, image, page-break, and section behavior in a dedicated capability lab. Regenerate it from a repository checkout:
|
|
345
|
+
|
|
346
|
+
```powershell
|
|
347
|
+
uvx --refresh --from . markdown-docx sample\showcase.md sample\showcase.docx --force
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## Public API boundary
|
|
351
|
+
|
|
352
|
+
Production code uses only documented public `python-docx` APIs. It does not write OOXML directly and does not call private library members. Tests may inspect generated package XML read-only. The complete capability decision record is in [docs/public-api-capabilities.md](docs/public-api-capabilities.md).
|
|
353
|
+
|
|
354
|
+
Word is the primary compatibility target. LibreOffice Writer is used as a visual smoke-test engine. Differences in pagination or font metrics can occur between layout engines.
|
|
355
|
+
|
|
356
|
+
## Development
|
|
357
|
+
|
|
358
|
+
```powershell
|
|
359
|
+
$env:UV_LINK_MODE="copy"
|
|
360
|
+
uv sync --locked --all-groups
|
|
361
|
+
uv run pytest
|
|
362
|
+
uv run ruff check .
|
|
363
|
+
uv run ruff format --check .
|
|
364
|
+
uv run mypy
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Build and validate distributions:
|
|
368
|
+
|
|
369
|
+
```powershell
|
|
370
|
+
uv build
|
|
371
|
+
uv run twine check dist/*
|
|
372
|
+
```
|