mathfmt 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.
- mathfmt-0.1.0/CHANGELOG.md +12 -0
- mathfmt-0.1.0/CODE_OF_CONDUCT.md +11 -0
- mathfmt-0.1.0/CONTRIBUTING.md +12 -0
- mathfmt-0.1.0/LICENSE +21 -0
- mathfmt-0.1.0/MANIFEST.in +2 -0
- mathfmt-0.1.0/PKG-INFO +128 -0
- mathfmt-0.1.0/README.md +96 -0
- mathfmt-0.1.0/SECURITY.md +14 -0
- mathfmt-0.1.0/pyproject.toml +57 -0
- mathfmt-0.1.0/setup.cfg +4 -0
- mathfmt-0.1.0/skills/mathfmt/SKILL.md +27 -0
- mathfmt-0.1.0/skills/mathfmt/agents/openai.yaml +4 -0
- mathfmt-0.1.0/skills/mathfmt/references/paper-notation.md +11 -0
- mathfmt-0.1.0/src/mathfmt/__init__.py +6 -0
- mathfmt-0.1.0/src/mathfmt/__main__.py +3 -0
- mathfmt-0.1.0/src/mathfmt/cli.py +136 -0
- mathfmt-0.1.0/src/mathfmt/core.py +779 -0
- mathfmt-0.1.0/src/mathfmt.egg-info/PKG-INFO +128 -0
- mathfmt-0.1.0/src/mathfmt.egg-info/SOURCES.txt +24 -0
- mathfmt-0.1.0/src/mathfmt.egg-info/dependency_links.txt +1 -0
- mathfmt-0.1.0/src/mathfmt.egg-info/entry_points.txt +2 -0
- mathfmt-0.1.0/src/mathfmt.egg-info/requires.txt +6 -0
- mathfmt-0.1.0/src/mathfmt.egg-info/top_level.txt +1 -0
- mathfmt-0.1.0/tests/test_docx.py +85 -0
- mathfmt-0.1.0/tests/test_formula.py +36 -0
- mathfmt-0.1.0/tests/test_skill.py +15 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to MathFmt are documented here.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.1.0] - 2026-06-21
|
|
8
|
+
|
|
9
|
+
- Added review-first `scan` and `apply` commands.
|
|
10
|
+
- Added conservative `convert` and environment `doctor` commands.
|
|
11
|
+
- Added native Word equation output for DOCX body text, tables, headers, and footers.
|
|
12
|
+
- Added Codex Skill, tests, bilingual documentation, and release automation.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
MathFmt welcomes contributors of every background and experience level.
|
|
4
|
+
|
|
5
|
+
- Be respectful, constructive, and specific.
|
|
6
|
+
- Critique ideas and code, never people.
|
|
7
|
+
- Do not harass, threaten, discriminate, or publish private information.
|
|
8
|
+
- Respect document privacy and intellectual property.
|
|
9
|
+
|
|
10
|
+
Project maintainers may edit or remove unacceptable contributions and may temporarily or permanently
|
|
11
|
+
restrict participation. Report conduct concerns privately to the repository owner through GitHub.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thank you for helping improve MathFmt.
|
|
4
|
+
|
|
5
|
+
1. Open an issue before large behavioral changes.
|
|
6
|
+
2. Create a focused branch and keep source documents private.
|
|
7
|
+
3. Install development dependencies with `python -m pip install -e ".[dev]"`.
|
|
8
|
+
4. Run `ruff check .` and `pytest` before submitting a pull request.
|
|
9
|
+
5. Add synthetic fixtures and tests for new notation or DOCX structures.
|
|
10
|
+
|
|
11
|
+
Do not commit copyrighted, confidential, or personally identifying documents. Reproduce document
|
|
12
|
+
problems with the smallest synthetic fixture possible.
|
mathfmt-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Leo
|
|
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.
|
mathfmt-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mathfmt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Typeset plain-text formulas in DOCX files as native Word equations.
|
|
5
|
+
Author: Leo
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/gml853503962-creator/mathfmt
|
|
8
|
+
Project-URL: Repository, https://github.com/gml853503962-creator/mathfmt
|
|
9
|
+
Project-URL: Issues, https://github.com/gml853503962-creator/mathfmt/issues
|
|
10
|
+
Keywords: docx,word,math,omml,equation,typesetting
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Education
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Office/Business :: Office Suites
|
|
22
|
+
Classifier: Topic :: Text Processing :: Markup :: XML
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: lxml>=5.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# MathFmt
|
|
34
|
+
|
|
35
|
+
[中文](#中文) | [English](#english)
|
|
36
|
+
|
|
37
|
+
MathFmt turns awkward plain-text formulas in Word documents into native Word equations suitable for
|
|
38
|
+
textbooks, exams, and technical reports. Processing stays on your computer.
|
|
39
|
+
|
|
40
|
+
## 中文
|
|
41
|
+
|
|
42
|
+
MathFmt 将 DOCX 文档中的普通文本公式转换为 Word 原生 OMML 公式,例如把 `ds(t)/dt`、
|
|
43
|
+
`sqrt(x^2+1)` 和 `p1` 排版为纸质教材中常见的分式、根号与上下标。
|
|
44
|
+
|
|
45
|
+
### 特点
|
|
46
|
+
|
|
47
|
+
- 保留原始 DOCX,始终写入新文件。
|
|
48
|
+
- 支持正文、表格、页眉、页脚和混合文本公式。
|
|
49
|
+
- 自动跳过代码、图片公式和已有 Word 原生公式。
|
|
50
|
+
- 提供可审核流程和保守的一键转换。
|
|
51
|
+
- 完全本地运行,不上传文档。
|
|
52
|
+
|
|
53
|
+
### 环境要求
|
|
54
|
+
|
|
55
|
+
- Windows 10/11
|
|
56
|
+
- Python 3.10 或更高版本
|
|
57
|
+
- Microsoft Word/Office,并包含 `MML2OMML.XSL`
|
|
58
|
+
|
|
59
|
+
MathFmt 不分发微软的 XSL 文件。它会自动检查常见 Office 安装目录,也可使用 `--xsl` 指定路径。
|
|
60
|
+
|
|
61
|
+
### 安装
|
|
62
|
+
|
|
63
|
+
```powershell
|
|
64
|
+
pip install mathfmt
|
|
65
|
+
mathfmt doctor
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
从源码开发安装:
|
|
69
|
+
|
|
70
|
+
```powershell
|
|
71
|
+
git clone https://github.com/gml853503962-creator/mathfmt.git
|
|
72
|
+
cd mathfmt
|
|
73
|
+
python -m pip install -e ".[dev]"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 使用
|
|
77
|
+
|
|
78
|
+
```powershell
|
|
79
|
+
# 保守的一键转换
|
|
80
|
+
mathfmt convert input.docx
|
|
81
|
+
|
|
82
|
+
# 审核后转换
|
|
83
|
+
mathfmt scan input.docx --report candidates.json
|
|
84
|
+
mathfmt apply input.docx --review candidates.json --output output.docx --report result.json
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
一键转换默认生成 `input.mathfmt.docx` 和 `input.mathfmt.report.json`,不会覆盖原文件。编辑
|
|
88
|
+
`candidates.json` 中的 `selected`、`source` 或 `linear` 后再执行 `apply`。如果 Office 安装在
|
|
89
|
+
非标准目录,为 `apply`、`convert` 或 `doctor` 添加 `--xsl C:\path\to\MML2OMML.XSL`。
|
|
90
|
+
|
|
91
|
+
### Codex Skill
|
|
92
|
+
|
|
93
|
+
仓库中的 `skills/mathfmt` 可复制到 Codex 技能目录。技能依赖已安装的 `mathfmt` 命令。
|
|
94
|
+
|
|
95
|
+
## English
|
|
96
|
+
|
|
97
|
+
MathFmt converts plain-text formulas in DOCX files into native Word OMML equations with stacked
|
|
98
|
+
fractions, radicals, superscripts, subscripts, derivatives, and standard mathematical operators.
|
|
99
|
+
|
|
100
|
+
### Highlights
|
|
101
|
+
|
|
102
|
+
- Never overwrites the source DOCX.
|
|
103
|
+
- Handles body text, tables, headers, footers, and formulas mixed with prose.
|
|
104
|
+
- Skips likely code, image formulas, and existing Word equations.
|
|
105
|
+
- Offers both a review-first workflow and conservative one-step conversion.
|
|
106
|
+
- Processes documents locally without uploading them.
|
|
107
|
+
|
|
108
|
+
### Requirements and installation
|
|
109
|
+
|
|
110
|
+
MathFmt 0.1 supports Windows, Python 3.10+, and a Microsoft Office installation containing
|
|
111
|
+
`MML2OMML.XSL`. The Microsoft stylesheet is detected locally and is not distributed with MathFmt.
|
|
112
|
+
|
|
113
|
+
```powershell
|
|
114
|
+
pip install mathfmt
|
|
115
|
+
mathfmt doctor
|
|
116
|
+
mathfmt convert input.docx
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
For review-first conversion, run `scan`, edit the generated JSON, then run `apply` as shown above.
|
|
120
|
+
|
|
121
|
+
## Contributing
|
|
122
|
+
|
|
123
|
+
Issues and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md),
|
|
124
|
+
[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md), and [SECURITY.md](SECURITY.md).
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT License. Copyright (c) 2026 Leo.
|
mathfmt-0.1.0/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# MathFmt
|
|
2
|
+
|
|
3
|
+
[中文](#中文) | [English](#english)
|
|
4
|
+
|
|
5
|
+
MathFmt turns awkward plain-text formulas in Word documents into native Word equations suitable for
|
|
6
|
+
textbooks, exams, and technical reports. Processing stays on your computer.
|
|
7
|
+
|
|
8
|
+
## 中文
|
|
9
|
+
|
|
10
|
+
MathFmt 将 DOCX 文档中的普通文本公式转换为 Word 原生 OMML 公式,例如把 `ds(t)/dt`、
|
|
11
|
+
`sqrt(x^2+1)` 和 `p1` 排版为纸质教材中常见的分式、根号与上下标。
|
|
12
|
+
|
|
13
|
+
### 特点
|
|
14
|
+
|
|
15
|
+
- 保留原始 DOCX,始终写入新文件。
|
|
16
|
+
- 支持正文、表格、页眉、页脚和混合文本公式。
|
|
17
|
+
- 自动跳过代码、图片公式和已有 Word 原生公式。
|
|
18
|
+
- 提供可审核流程和保守的一键转换。
|
|
19
|
+
- 完全本地运行,不上传文档。
|
|
20
|
+
|
|
21
|
+
### 环境要求
|
|
22
|
+
|
|
23
|
+
- Windows 10/11
|
|
24
|
+
- Python 3.10 或更高版本
|
|
25
|
+
- Microsoft Word/Office,并包含 `MML2OMML.XSL`
|
|
26
|
+
|
|
27
|
+
MathFmt 不分发微软的 XSL 文件。它会自动检查常见 Office 安装目录,也可使用 `--xsl` 指定路径。
|
|
28
|
+
|
|
29
|
+
### 安装
|
|
30
|
+
|
|
31
|
+
```powershell
|
|
32
|
+
pip install mathfmt
|
|
33
|
+
mathfmt doctor
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
从源码开发安装:
|
|
37
|
+
|
|
38
|
+
```powershell
|
|
39
|
+
git clone https://github.com/gml853503962-creator/mathfmt.git
|
|
40
|
+
cd mathfmt
|
|
41
|
+
python -m pip install -e ".[dev]"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 使用
|
|
45
|
+
|
|
46
|
+
```powershell
|
|
47
|
+
# 保守的一键转换
|
|
48
|
+
mathfmt convert input.docx
|
|
49
|
+
|
|
50
|
+
# 审核后转换
|
|
51
|
+
mathfmt scan input.docx --report candidates.json
|
|
52
|
+
mathfmt apply input.docx --review candidates.json --output output.docx --report result.json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
一键转换默认生成 `input.mathfmt.docx` 和 `input.mathfmt.report.json`,不会覆盖原文件。编辑
|
|
56
|
+
`candidates.json` 中的 `selected`、`source` 或 `linear` 后再执行 `apply`。如果 Office 安装在
|
|
57
|
+
非标准目录,为 `apply`、`convert` 或 `doctor` 添加 `--xsl C:\path\to\MML2OMML.XSL`。
|
|
58
|
+
|
|
59
|
+
### Codex Skill
|
|
60
|
+
|
|
61
|
+
仓库中的 `skills/mathfmt` 可复制到 Codex 技能目录。技能依赖已安装的 `mathfmt` 命令。
|
|
62
|
+
|
|
63
|
+
## English
|
|
64
|
+
|
|
65
|
+
MathFmt converts plain-text formulas in DOCX files into native Word OMML equations with stacked
|
|
66
|
+
fractions, radicals, superscripts, subscripts, derivatives, and standard mathematical operators.
|
|
67
|
+
|
|
68
|
+
### Highlights
|
|
69
|
+
|
|
70
|
+
- Never overwrites the source DOCX.
|
|
71
|
+
- Handles body text, tables, headers, footers, and formulas mixed with prose.
|
|
72
|
+
- Skips likely code, image formulas, and existing Word equations.
|
|
73
|
+
- Offers both a review-first workflow and conservative one-step conversion.
|
|
74
|
+
- Processes documents locally without uploading them.
|
|
75
|
+
|
|
76
|
+
### Requirements and installation
|
|
77
|
+
|
|
78
|
+
MathFmt 0.1 supports Windows, Python 3.10+, and a Microsoft Office installation containing
|
|
79
|
+
`MML2OMML.XSL`. The Microsoft stylesheet is detected locally and is not distributed with MathFmt.
|
|
80
|
+
|
|
81
|
+
```powershell
|
|
82
|
+
pip install mathfmt
|
|
83
|
+
mathfmt doctor
|
|
84
|
+
mathfmt convert input.docx
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
For review-first conversion, run `scan`, edit the generated JSON, then run `apply` as shown above.
|
|
88
|
+
|
|
89
|
+
## Contributing
|
|
90
|
+
|
|
91
|
+
Issues and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md),
|
|
92
|
+
[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md), and [SECURITY.md](SECURITY.md).
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
MIT License. Copyright (c) 2026 Leo.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Security fixes are provided for the latest released version.
|
|
6
|
+
|
|
7
|
+
## Reporting a vulnerability
|
|
8
|
+
|
|
9
|
+
Use GitHub private vulnerability reporting for the MathFmt repository. Do not include confidential
|
|
10
|
+
DOCX files; provide a synthetic reproducer instead. Please allow the maintainer reasonable time to
|
|
11
|
+
investigate before public disclosure.
|
|
12
|
+
|
|
13
|
+
MathFmt processes files locally. Treat untrusted DOCX files as untrusted ZIP/XML input and run the
|
|
14
|
+
tool with ordinary user privileges.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mathfmt"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Typeset plain-text formulas in DOCX files as native Word equations."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Leo" }]
|
|
14
|
+
keywords = ["docx", "word", "math", "omml", "equation", "typesetting"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Intended Audience :: Education",
|
|
19
|
+
"Intended Audience :: Science/Research",
|
|
20
|
+
"Operating System :: Microsoft :: Windows",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Topic :: Office/Business :: Office Suites",
|
|
27
|
+
"Topic :: Text Processing :: Markup :: XML",
|
|
28
|
+
]
|
|
29
|
+
dependencies = ["lxml>=5.0"]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
dev = ["build>=1.2", "pytest>=8.0", "ruff>=0.6"]
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
mathfmt = "mathfmt.cli:main"
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://github.com/gml853503962-creator/mathfmt"
|
|
39
|
+
Repository = "https://github.com/gml853503962-creator/mathfmt"
|
|
40
|
+
Issues = "https://github.com/gml853503962-creator/mathfmt/issues"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools]
|
|
43
|
+
package-dir = { "" = "src" }
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.packages.find]
|
|
46
|
+
where = ["src"]
|
|
47
|
+
|
|
48
|
+
[tool.pytest.ini_options]
|
|
49
|
+
addopts = "-ra"
|
|
50
|
+
testpaths = ["tests"]
|
|
51
|
+
|
|
52
|
+
[tool.ruff]
|
|
53
|
+
line-length = 110
|
|
54
|
+
target-version = "py310"
|
|
55
|
+
|
|
56
|
+
[tool.ruff.lint]
|
|
57
|
+
select = ["F", "I", "UP"]
|
mathfmt-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mathfmt
|
|
3
|
+
description: Typeset plain-text formulas in Word DOCX documents as native paper-style Word equations. Use for requests to fix awkward formula symbols, convert typed math into textbook or exam formatting, normalize derivatives and control-system notation, or review formula candidates in DOCX files.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# MathFmt
|
|
7
|
+
|
|
8
|
+
Convert plain-text formulas to native Word OMML equations with the installed `mathfmt` CLI. Preserve
|
|
9
|
+
the source document and inspect the rendered result.
|
|
10
|
+
|
|
11
|
+
## Workflow
|
|
12
|
+
|
|
13
|
+
1. Run `mathfmt doctor` and resolve a missing `MML2OMML.XSL` with `--xsl PATH`.
|
|
14
|
+
2. Use `mathfmt convert input.docx` only when conservative automatic selection is appropriate.
|
|
15
|
+
3. For mixed technical prose, run `mathfmt scan input.docx --report candidates.json`, review the JSON,
|
|
16
|
+
then run `mathfmt apply input.docx --review candidates.json --output output.docx --report result.json`.
|
|
17
|
+
4. Keep code, image formulas, and existing native Word equations unchanged.
|
|
18
|
+
5. Inspect the result report and render every output page before delivery.
|
|
19
|
+
|
|
20
|
+
Read `references/paper-notation.md` when reviewing notation or correcting a candidate's `linear` value.
|
|
21
|
+
|
|
22
|
+
## Safety
|
|
23
|
+
|
|
24
|
+
- Never use the input path as the output path.
|
|
25
|
+
- Prefer the review-first flow when prose may resemble formulas.
|
|
26
|
+
- Treat skipped candidates as unresolved work and report them.
|
|
27
|
+
- Deliver only the final DOCX unless QA artifacts are requested.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Paper-Style Notation
|
|
2
|
+
|
|
3
|
+
- Use stacked fractions for mathematical division.
|
|
4
|
+
- Use radical bars for `sqrt(...)` or `√(...)`.
|
|
5
|
+
- Render powers and indexes as true superscripts and subscripts.
|
|
6
|
+
- Prefer fraction derivatives such as `ds(t)/dt` and `d²s(t)/dt²`.
|
|
7
|
+
- Use `u(t)` for the unit-step function.
|
|
8
|
+
- Preserve the source Laplace variable: commonly `p` in French material and `s` elsewhere.
|
|
9
|
+
- Use standard symbols such as `±`, `≠`, `≤`, `≥`, `→`, `∞`, `Δ`, and `π`.
|
|
10
|
+
- Prefer invisible multiplication for coefficient-variable products.
|
|
11
|
+
- Split long table formulas at top-level addition or subtraction operators when necessary.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Command-line interface for MathFmt."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import platform
|
|
9
|
+
import sys
|
|
10
|
+
import tempfile
|
|
11
|
+
from collections.abc import Sequence
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from lxml import etree
|
|
15
|
+
|
|
16
|
+
from . import __version__
|
|
17
|
+
from .core import apply_docx, find_xsl, scan_docx
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def default_output(input_path: Path) -> Path:
|
|
21
|
+
return input_path.with_name(f"{input_path.stem}.mathfmt.docx")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def default_result_report(output_path: Path) -> Path:
|
|
25
|
+
return output_path.with_name(f"{output_path.stem}.report.json")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def doctor_data(explicit_xsl: Path | None = None) -> dict[str, object]:
|
|
29
|
+
data: dict[str, object] = {
|
|
30
|
+
"mathfmt": __version__,
|
|
31
|
+
"python": platform.python_version(),
|
|
32
|
+
"platform": platform.platform(),
|
|
33
|
+
"windows": os.name == "nt",
|
|
34
|
+
"lxml": etree.LXML_VERSION,
|
|
35
|
+
"xsl": None,
|
|
36
|
+
"ready": False,
|
|
37
|
+
}
|
|
38
|
+
try:
|
|
39
|
+
data["xsl"] = str(find_xsl(explicit_xsl).resolve())
|
|
40
|
+
data["ready"] = True
|
|
41
|
+
except FileNotFoundError as exc:
|
|
42
|
+
data["error"] = str(exc)
|
|
43
|
+
return data
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
47
|
+
parser = argparse.ArgumentParser(
|
|
48
|
+
prog="mathfmt",
|
|
49
|
+
description="Typeset plain-text DOCX formulas as native Word equations.",
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument("--version", action="version", version=f"MathFmt {__version__}")
|
|
52
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
53
|
+
|
|
54
|
+
scan = subparsers.add_parser("scan", help="create a reviewable formula candidate report")
|
|
55
|
+
scan.add_argument("input", type=Path)
|
|
56
|
+
scan.add_argument("--report", type=Path, required=True)
|
|
57
|
+
|
|
58
|
+
apply = subparsers.add_parser("apply", help="apply a reviewed candidate report")
|
|
59
|
+
apply.add_argument("input", type=Path)
|
|
60
|
+
apply.add_argument("--review", type=Path, required=True)
|
|
61
|
+
apply.add_argument("--output", "--out", dest="output", type=Path, required=True)
|
|
62
|
+
apply.add_argument("--report", type=Path, required=True)
|
|
63
|
+
apply.add_argument("--xsl", type=Path)
|
|
64
|
+
|
|
65
|
+
convert = subparsers.add_parser("convert", help="conservatively convert detected formulas in one step")
|
|
66
|
+
convert.add_argument("input", type=Path)
|
|
67
|
+
convert.add_argument("--output", "--out", dest="output", type=Path)
|
|
68
|
+
convert.add_argument("--report", type=Path)
|
|
69
|
+
convert.add_argument("--xsl", type=Path)
|
|
70
|
+
|
|
71
|
+
doctor = subparsers.add_parser("doctor", help="check the local MathFmt environment")
|
|
72
|
+
doctor.add_argument("--xsl", type=Path)
|
|
73
|
+
doctor.add_argument("--json", action="store_true", dest="as_json")
|
|
74
|
+
return parser
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def run_convert(args: argparse.Namespace) -> int:
|
|
78
|
+
output = args.output or default_output(args.input)
|
|
79
|
+
report_path = args.report or default_result_report(output)
|
|
80
|
+
xsl = find_xsl(args.xsl)
|
|
81
|
+
with tempfile.TemporaryDirectory(prefix="mathfmt-") as temp_dir:
|
|
82
|
+
review_path = Path(temp_dir) / "candidates.json"
|
|
83
|
+
scan = scan_docx(args.input, review_path)
|
|
84
|
+
result = apply_docx(args.input, review_path, output, report_path, xsl)
|
|
85
|
+
print(f"Candidates: {scan['summary']['candidates']}")
|
|
86
|
+
print(f"Converted: {result['converted_count']}")
|
|
87
|
+
print(f"Skipped: {result['skipped_count']}")
|
|
88
|
+
print(f"Output: {output}")
|
|
89
|
+
print(f"Report: {report_path}")
|
|
90
|
+
return 0 if result["skipped_count"] == 0 else 2
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
94
|
+
args = build_parser().parse_args(argv)
|
|
95
|
+
try:
|
|
96
|
+
if args.command == "scan":
|
|
97
|
+
report = scan_docx(args.input, args.report)
|
|
98
|
+
print(f"Candidates: {report['summary']['candidates']}")
|
|
99
|
+
print(f"Report: {args.report}")
|
|
100
|
+
return 0
|
|
101
|
+
if args.command == "apply":
|
|
102
|
+
result = apply_docx(
|
|
103
|
+
args.input,
|
|
104
|
+
args.review,
|
|
105
|
+
args.output,
|
|
106
|
+
args.report,
|
|
107
|
+
find_xsl(args.xsl),
|
|
108
|
+
)
|
|
109
|
+
print(f"Converted: {result['converted_count']}")
|
|
110
|
+
print(f"Skipped: {result['skipped_count']}")
|
|
111
|
+
print(f"Output: {args.output}")
|
|
112
|
+
print(f"Report: {args.report}")
|
|
113
|
+
return 0 if result["skipped_count"] == 0 else 2
|
|
114
|
+
if args.command == "convert":
|
|
115
|
+
return run_convert(args)
|
|
116
|
+
if args.command == "doctor":
|
|
117
|
+
data = doctor_data(args.xsl)
|
|
118
|
+
if args.as_json:
|
|
119
|
+
print(json.dumps(data, ensure_ascii=False, indent=2))
|
|
120
|
+
else:
|
|
121
|
+
print(f"MathFmt: {data['mathfmt']}")
|
|
122
|
+
print(f"Python: {data['python']}")
|
|
123
|
+
print(f"Platform: {data['platform']}")
|
|
124
|
+
print(f"MML2OMML.XSL: {data['xsl'] or 'not found'}")
|
|
125
|
+
print(f"Ready: {'yes' if data['ready'] else 'no'}")
|
|
126
|
+
if data.get("error"):
|
|
127
|
+
print(f"Hint: {data['error']}")
|
|
128
|
+
return 0 if data["ready"] else 1
|
|
129
|
+
except (FileNotFoundError, ValueError, json.JSONDecodeError, etree.XMLSyntaxError) as exc:
|
|
130
|
+
print(f"mathfmt: error: {exc}", file=sys.stderr)
|
|
131
|
+
return 1
|
|
132
|
+
return 1
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
if __name__ == "__main__":
|
|
136
|
+
raise SystemExit(main())
|