texmark 0.0.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.
- texmark-0.0.0/.gitattributes +4 -0
- texmark-0.0.0/.github/workflows/pypi.yml +35 -0
- texmark-0.0.0/.gitignore +174 -0
- texmark-0.0.0/LICENSE +21 -0
- texmark-0.0.0/PKG-INFO +68 -0
- texmark-0.0.0/README.md +52 -0
- texmark-0.0.0/example.md +59 -0
- texmark-0.0.0/images/eof_mean.png +3 -0
- texmark-0.0.0/pyproject.toml +34 -0
- texmark-0.0.0/references.bib +13 -0
- texmark-0.0.0/requirements.txt +5 -0
- texmark-0.0.0/setup.cfg +4 -0
- texmark-0.0.0/texmark/__init__.py +0 -0
- texmark-0.0.0/texmark/build.py +178 -0
- texmark-0.0.0/texmark/filters.py +114 -0
- texmark-0.0.0/texmark/logs.py +3 -0
- texmark-0.0.0/texmark/sectiontracker.py +136 -0
- texmark-0.0.0/texmark/shared.py +83 -0
- texmark-0.0.0/texmark/templates/body.tex +1 -0
- texmark-0.0.0/texmark/templates/copernicus/README_copernicus_package_7_11.txt +22 -0
- texmark-0.0.0/texmark/templates/copernicus/copernicus.bst +1726 -0
- texmark-0.0.0/texmark/templates/copernicus/copernicus.cfg +1158 -0
- texmark-0.0.0/texmark/templates/copernicus/copernicus.cls +3131 -0
- texmark-0.0.0/texmark/templates/copernicus/pdfscreen.sty +1260 -0
- texmark-0.0.0/texmark/templates/copernicus/pdfscreencop.sty +1260 -0
- texmark-0.0.0/texmark/templates/copernicus/template.tex +447 -0
- texmark-0.0.0/texmark/templates/default/template.tex +54 -0
- texmark-0.0.0/texmark/templates/science/.gitignore +1 -0
- texmark-0.0.0/texmark/templates/science/example_figure.eps +3 -0
- texmark-0.0.0/texmark/templates/science/example_figure.png +3 -0
- texmark-0.0.0/texmark/templates/science/example_figure.ps +3 -0
- texmark-0.0.0/texmark/templates/science/readme.txt +26 -0
- texmark-0.0.0/texmark/templates/science/scicite.sty +512 -0
- texmark-0.0.0/texmark/templates/science/science_template.bib +64 -0
- texmark-0.0.0/texmark/templates/science/science_template.tex +496 -0
- texmark-0.0.0/texmark/templates/science/sciencemag.bst +1334 -0
- texmark-0.0.0/texmark/templates/science/template.tex +347 -0
- texmark-0.0.0/texmark.egg-info/PKG-INFO +68 -0
- texmark-0.0.0/texmark.egg-info/SOURCES.txt +41 -0
- texmark-0.0.0/texmark.egg-info/dependency_links.txt +1 -0
- texmark-0.0.0/texmark.egg-info/entry_points.txt +3 -0
- texmark-0.0.0/texmark.egg-info/requires.txt +5 -0
- texmark-0.0.0/texmark.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- '*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
environment:
|
|
12
|
+
name: pypi
|
|
13
|
+
permissions:
|
|
14
|
+
id-token: write # This is required for OIDC
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout code
|
|
19
|
+
uses: actions/checkout@v2
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
uses: actions/setup-python@v2
|
|
23
|
+
with:
|
|
24
|
+
python-version: '3.x'
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install --upgrade pip
|
|
29
|
+
pip install setuptools setuptools-scm[toml] wheel build
|
|
30
|
+
|
|
31
|
+
- name: Build distribution
|
|
32
|
+
run: python -m build
|
|
33
|
+
|
|
34
|
+
- name: Publish to PyPI
|
|
35
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
texmark-0.0.0/.gitignore
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
#uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
#poetry.lock
|
|
109
|
+
|
|
110
|
+
# pdm
|
|
111
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
112
|
+
#pdm.lock
|
|
113
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
114
|
+
# in version control.
|
|
115
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
116
|
+
.pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
121
|
+
__pypackages__/
|
|
122
|
+
|
|
123
|
+
# Celery stuff
|
|
124
|
+
celerybeat-schedule
|
|
125
|
+
celerybeat.pid
|
|
126
|
+
|
|
127
|
+
# SageMath parsed files
|
|
128
|
+
*.sage.py
|
|
129
|
+
|
|
130
|
+
# Environments
|
|
131
|
+
.env
|
|
132
|
+
.venv
|
|
133
|
+
env/
|
|
134
|
+
venv/
|
|
135
|
+
ENV/
|
|
136
|
+
env.bak/
|
|
137
|
+
venv.bak/
|
|
138
|
+
|
|
139
|
+
# Spyder project settings
|
|
140
|
+
.spyderproject
|
|
141
|
+
.spyproject
|
|
142
|
+
|
|
143
|
+
# Rope project settings
|
|
144
|
+
.ropeproject
|
|
145
|
+
|
|
146
|
+
# mkdocs documentation
|
|
147
|
+
/site
|
|
148
|
+
|
|
149
|
+
# mypy
|
|
150
|
+
.mypy_cache/
|
|
151
|
+
.dmypy.json
|
|
152
|
+
dmypy.json
|
|
153
|
+
|
|
154
|
+
# Pyre type checker
|
|
155
|
+
.pyre/
|
|
156
|
+
|
|
157
|
+
# pytype static type analyzer
|
|
158
|
+
.pytype/
|
|
159
|
+
|
|
160
|
+
# Cython debug symbols
|
|
161
|
+
cython_debug/
|
|
162
|
+
|
|
163
|
+
# PyCharm
|
|
164
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
165
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
166
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
167
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
168
|
+
#.idea/
|
|
169
|
+
|
|
170
|
+
# Ruff stuff:
|
|
171
|
+
.ruff_cache/
|
|
172
|
+
|
|
173
|
+
# PyPI configuration file
|
|
174
|
+
.pypirc
|
texmark-0.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mahé Perrette
|
|
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.
|
texmark-0.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: texmark
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Write scientific articles in markdown
|
|
5
|
+
Author-email: Mahé Perrette <mahe.perrette@gmail.com>
|
|
6
|
+
Project-URL: homepage, https://github.com/perrette/texmark
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: pypandoc
|
|
11
|
+
Requires-Dist: jinja2
|
|
12
|
+
Requires-Dist: pyyaml
|
|
13
|
+
Requires-Dist: panflute
|
|
14
|
+
Requires-Dist: python-frontmatter
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# texmark
|
|
18
|
+
|
|
19
|
+
Write scientific articles in markdown
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
for development, after cloning:
|
|
25
|
+
|
|
26
|
+
pip install -e .
|
|
27
|
+
|
|
28
|
+
and soon:
|
|
29
|
+
|
|
30
|
+
pip install texmark
|
|
31
|
+
|
|
32
|
+
## Example
|
|
33
|
+
|
|
34
|
+
See [example.md](example.md) for a sample markdown file with yaml metadata in the header.
|
|
35
|
+
|
|
36
|
+
The command to convert the markdow to tex is:
|
|
37
|
+
|
|
38
|
+
texmark example.md
|
|
39
|
+
|
|
40
|
+
And to convert to PDF
|
|
41
|
+
|
|
42
|
+
texmark example.md --pdf
|
|
43
|
+
|
|
44
|
+
For another journal, it is enough to change the `journal -> template' field in the yaml metadata.
|
|
45
|
+
For testing it is also possible to pass `-j` for `--journal-template`:
|
|
46
|
+
|
|
47
|
+
texmark example.md --pdf -j science -o build/example-science.pdf --tex build/example-science.tex
|
|
48
|
+
|
|
49
|
+
See the example tex and pdf results in [build](/build)
|
|
50
|
+
|
|
51
|
+
For now only `copernicus` and `science` template are available.
|
|
52
|
+
Only partial support is provided. Upon submission you'll most likely need
|
|
53
|
+
to rework the final latex version, especially to handle things like appendix or special sections.
|
|
54
|
+
Alternatively, you may write your custom template (see the advanced section)
|
|
55
|
+
|
|
56
|
+
## Advanced: latex template
|
|
57
|
+
|
|
58
|
+
The templates are written in [jinja2](https://jinja.palletsprojects.com).
|
|
59
|
+
|
|
60
|
+
Just copy from e.g. texmark/templates/science/template.tex to your own, e.g. custom_template.tex
|
|
61
|
+
And run again with:
|
|
62
|
+
|
|
63
|
+
texmark example.md --pdf -j science -o build/example-science.pdf --tex build/example-science.tex --template custom_template.tex
|
|
64
|
+
|
|
65
|
+
The -j journal template option (here `science`) is still used to set custom filters (e.g. only `\cite` for Science, no `\citet` ; extract specific sections as metadata to be injected as `{{section}}` instead of `{{body}}` etc). The machinery is defined in [texmark/filters.py](/texmark/filters.py) and can in principle be extended or copied.
|
|
66
|
+
Two approaches are possible:
|
|
67
|
+
- just add more filters via the `--filters` command or in the yaml metadata.
|
|
68
|
+
- extend the existing filters in a module, e.g. custom_filter.py, that extends the `filters` dict from the `texmark.filters` module (see the source code to check the details). And then pass it via `--filters-module custom_filter` parameter (or `custom_filter` in the metadata) to prompt the texmark filter to load that module and make it available via `-j your-custom-name`. Note that will require you to explicitly pass `--template` as well. Unless you overwrite an existing filter.
|
texmark-0.0.0/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# texmark
|
|
2
|
+
|
|
3
|
+
Write scientific articles in markdown
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
for development, after cloning:
|
|
9
|
+
|
|
10
|
+
pip install -e .
|
|
11
|
+
|
|
12
|
+
and soon:
|
|
13
|
+
|
|
14
|
+
pip install texmark
|
|
15
|
+
|
|
16
|
+
## Example
|
|
17
|
+
|
|
18
|
+
See [example.md](example.md) for a sample markdown file with yaml metadata in the header.
|
|
19
|
+
|
|
20
|
+
The command to convert the markdow to tex is:
|
|
21
|
+
|
|
22
|
+
texmark example.md
|
|
23
|
+
|
|
24
|
+
And to convert to PDF
|
|
25
|
+
|
|
26
|
+
texmark example.md --pdf
|
|
27
|
+
|
|
28
|
+
For another journal, it is enough to change the `journal -> template' field in the yaml metadata.
|
|
29
|
+
For testing it is also possible to pass `-j` for `--journal-template`:
|
|
30
|
+
|
|
31
|
+
texmark example.md --pdf -j science -o build/example-science.pdf --tex build/example-science.tex
|
|
32
|
+
|
|
33
|
+
See the example tex and pdf results in [build](/build)
|
|
34
|
+
|
|
35
|
+
For now only `copernicus` and `science` template are available.
|
|
36
|
+
Only partial support is provided. Upon submission you'll most likely need
|
|
37
|
+
to rework the final latex version, especially to handle things like appendix or special sections.
|
|
38
|
+
Alternatively, you may write your custom template (see the advanced section)
|
|
39
|
+
|
|
40
|
+
## Advanced: latex template
|
|
41
|
+
|
|
42
|
+
The templates are written in [jinja2](https://jinja.palletsprojects.com).
|
|
43
|
+
|
|
44
|
+
Just copy from e.g. texmark/templates/science/template.tex to your own, e.g. custom_template.tex
|
|
45
|
+
And run again with:
|
|
46
|
+
|
|
47
|
+
texmark example.md --pdf -j science -o build/example-science.pdf --tex build/example-science.tex --template custom_template.tex
|
|
48
|
+
|
|
49
|
+
The -j journal template option (here `science`) is still used to set custom filters (e.g. only `\cite` for Science, no `\citet` ; extract specific sections as metadata to be injected as `{{section}}` instead of `{{body}}` etc). The machinery is defined in [texmark/filters.py](/texmark/filters.py) and can in principle be extended or copied.
|
|
50
|
+
Two approaches are possible:
|
|
51
|
+
- just add more filters via the `--filters` command or in the yaml metadata.
|
|
52
|
+
- extend the existing filters in a module, e.g. custom_filter.py, that extends the `filters` dict from the `texmark.filters` module (see the source code to check the details). And then pass it via `--filters-module custom_filter` parameter (or `custom_filter` in the metadata) to prompt the texmark filter to load that module and make it available via `-j your-custom-name`. Note that will require you to explicitly pass `--template` as well. Unless you overwrite an existing filter.
|
texmark-0.0.0/example.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "This is an example title"
|
|
3
|
+
authors:
|
|
4
|
+
- firstname: Mahé
|
|
5
|
+
lastname: Perrette
|
|
6
|
+
affiliation: 1
|
|
7
|
+
email: mahe.perrette@gmail.com
|
|
8
|
+
- firstname: Another
|
|
9
|
+
lastname: Author
|
|
10
|
+
affiliation: 2
|
|
11
|
+
affiliations:
|
|
12
|
+
- "Alfred Wegener Institut fur Meeres und Polarforschung (AWI)"
|
|
13
|
+
- "Another Institution in a Remote Country"
|
|
14
|
+
running:
|
|
15
|
+
title: LGM data assimilation
|
|
16
|
+
author: Perrette et al
|
|
17
|
+
date: "2025-07-15"
|
|
18
|
+
bibliography: references.bib
|
|
19
|
+
journal:
|
|
20
|
+
short: cp
|
|
21
|
+
template: copernicus
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Abstract
|
|
25
|
+
|
|
26
|
+
The abstract will be identified and moved to the metadata,
|
|
27
|
+
so that it can be used as in the journal templates.
|
|
28
|
+
|
|
29
|
+
Each journal has its own template. For now only Copernicus and Science are covered.
|
|
30
|
+
|
|
31
|
+
# Introduction
|
|
32
|
+
|
|
33
|
+
The journal templates can be edited in [texmark/templates](/texmark/templates)
|
|
34
|
+
and journal-specific filters can be added such as for [copernicus](/texmark/copernicus.py).
|
|
35
|
+
|
|
36
|
+
Lots of research has been done on that topic.
|
|
37
|
+
We focus mostly on @tierney_zhu2020
|
|
38
|
+
but also on \cite{tierney_zhu2020}
|
|
39
|
+
|
|
40
|
+

|
|
41
|
+
|
|
42
|
+
That's an inline equation $a + 1 = 3$.
|
|
43
|
+
And a block equation
|
|
44
|
+
$$
|
|
45
|
+
a + \Sigma_i i^2
|
|
46
|
+
$$
|
|
47
|
+
and an align environment
|
|
48
|
+
\begin{align} \label{eq:aligneq}
|
|
49
|
+
a &= 9 + 2 \\
|
|
50
|
+
b &= 3
|
|
51
|
+
\end{align}
|
|
52
|
+
|
|
53
|
+
Now i can cite Eq. \ref{eq:aligneq} and my figure \ref{fig:eofmean}
|
|
54
|
+
|
|
55
|
+
Both latex and markdown commands are supported.
|
|
56
|
+
|
|
57
|
+
# Conclusions
|
|
58
|
+
|
|
59
|
+
This is also a special section.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "setuptools_scm[toml]>=6.2"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "texmark"
|
|
7
|
+
authors = [
|
|
8
|
+
{name = "Mahé Perrette", email = "mahe.perrette@gmail.com"},
|
|
9
|
+
]
|
|
10
|
+
description='Write scientific articles in markdown'
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
requires-python = ">=3.9"
|
|
13
|
+
# license = {text = "MIT"}
|
|
14
|
+
|
|
15
|
+
dynamic = ["version", "dependencies"]
|
|
16
|
+
|
|
17
|
+
[tool.setuptools.dynamic]
|
|
18
|
+
dependencies = {file = ["requirements.txt"]}
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
homepage = "https://github.com/perrette/texmark"
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.packages.find]
|
|
24
|
+
where = ["."]
|
|
25
|
+
include = [
|
|
26
|
+
"texmark",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[ tool.setuptools.package-data ]
|
|
30
|
+
texmark = ["templates/**/*"]
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
texmark = "texmark.build:main"
|
|
34
|
+
texmark-filter = "texmark.filters:main"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
@article{tierney_zhu2020,
|
|
3
|
+
author = {Tierney, Jessica E. and Zhu, Jiang and King, Jonathan and Malevich, Steven B. and Hakim, Gregory J. and Poulsen, Christopher J.},
|
|
4
|
+
doi = {10.1038/s41586-020-2617-x},
|
|
5
|
+
file = {:../../Documents/papers/lgmdataassim/tierney_et_al_2020_glacial-cooling-and-climate-sensitivity-revisited.pdf:pdf},
|
|
6
|
+
journal = {Nature},
|
|
7
|
+
number = {7822},
|
|
8
|
+
pages = {569-573},
|
|
9
|
+
title = {Glacial cooling and climate sensitivity revisited},
|
|
10
|
+
url = {https://doi.org/10.1038/s41586-020-2617-x},
|
|
11
|
+
volume = {584},
|
|
12
|
+
year = {2020}
|
|
13
|
+
}
|
texmark-0.0.0/setup.cfg
ADDED
|
File without changes
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import pypandoc
|
|
7
|
+
import json
|
|
8
|
+
import yaml
|
|
9
|
+
import jinja2
|
|
10
|
+
import frontmatter
|
|
11
|
+
import argparse
|
|
12
|
+
import texmark
|
|
13
|
+
import json
|
|
14
|
+
import panflute as pf
|
|
15
|
+
import io
|
|
16
|
+
from texmark.logs import logger
|
|
17
|
+
|
|
18
|
+
rootpath = Path(texmark.__file__).resolve().parent
|
|
19
|
+
|
|
20
|
+
def run(cmd, shell=False, check=True, **kwargs):
|
|
21
|
+
print(cmd if shell else ' '.join(cmd))
|
|
22
|
+
return subprocess.run(cmd, shell=shell, check=check, **kwargs)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def normalize_metadata(meta):
|
|
26
|
+
"""
|
|
27
|
+
Recursively convert panflute metadata into plain JSON-serializable Python dict.
|
|
28
|
+
(Plain strings, lists, dicts, no MetaInlines etc.)
|
|
29
|
+
"""
|
|
30
|
+
if isinstance(meta, pf.MetaInlines) or isinstance(meta, pf.MetaBlocks):
|
|
31
|
+
return pf.stringify(meta)
|
|
32
|
+
elif isinstance(meta, pf.MetaString):
|
|
33
|
+
return meta.text
|
|
34
|
+
elif isinstance(meta, pf.MetaBool):
|
|
35
|
+
return bool(meta)
|
|
36
|
+
elif isinstance(meta, pf.MetaList):
|
|
37
|
+
return [normalize_metadata(item) for item in meta]
|
|
38
|
+
elif isinstance(meta, pf.MetaMap):
|
|
39
|
+
return {key: normalize_metadata(value) for key, value in meta.items()}
|
|
40
|
+
else:
|
|
41
|
+
# Primitive types (str, int, etc.) or unknown - return as is
|
|
42
|
+
return meta
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def build_tex(input_md, output_tex, template='', bib_file='', build_dir='build', filters=None, journal_template=None, filters_module=None):
|
|
46
|
+
# 1. Parse Markdown
|
|
47
|
+
input_text = open(input_md).read()
|
|
48
|
+
post = frontmatter.loads(input_text)
|
|
49
|
+
metadata = post.metadata
|
|
50
|
+
content = post.content
|
|
51
|
+
|
|
52
|
+
if not journal_template:
|
|
53
|
+
journal_template = metadata.get('journal', {}).get('template', 'default')
|
|
54
|
+
if not journal_template:
|
|
55
|
+
journal_template = "default"
|
|
56
|
+
|
|
57
|
+
metadata.setdefault('journal', {})['template'] = journal_template
|
|
58
|
+
|
|
59
|
+
if filters_module:
|
|
60
|
+
metadata['filters_module'] = filters_module
|
|
61
|
+
|
|
62
|
+
if not template:
|
|
63
|
+
template = metadata.get('template')
|
|
64
|
+
if not template:
|
|
65
|
+
template = f'templates/{journal_template}/template.tex'
|
|
66
|
+
|
|
67
|
+
template_folder = Path(template).parent
|
|
68
|
+
template_name = Path(template).name
|
|
69
|
+
resource_path = rootpath / template_folder
|
|
70
|
+
|
|
71
|
+
if not bib_file:
|
|
72
|
+
bib_file = metadata.get('bibliography', None)
|
|
73
|
+
if bib_file:
|
|
74
|
+
bib_args = ['--bibliography', bib_file]
|
|
75
|
+
args = bib_args + metadata.get('pandoc_args', []) + [
|
|
76
|
+
"--natbib",
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
filters = [
|
|
80
|
+
"texmark-filter",
|
|
81
|
+
] + (filters or metadata.get('filters', []))
|
|
82
|
+
|
|
83
|
+
# Step 1: Run pandoc to get JSON AST with filters applied, and updated metadata
|
|
84
|
+
cmd_json = []
|
|
85
|
+
for f in filters:
|
|
86
|
+
cmd_json.extend(['--filter', f])
|
|
87
|
+
cmd_json.extend(args)
|
|
88
|
+
|
|
89
|
+
post.metadata = metadata
|
|
90
|
+
|
|
91
|
+
ast_json_str = pypandoc.convert_text(
|
|
92
|
+
frontmatter.dumps(post),
|
|
93
|
+
format="markdown+footnotes",
|
|
94
|
+
to="json",
|
|
95
|
+
extra_args=cmd_json,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
doc = pf.load(io.StringIO(ast_json_str)) # <-- no input_format argument
|
|
99
|
+
metadata.update(normalize_metadata(doc.metadata))
|
|
100
|
+
|
|
101
|
+
# Step 2. Render Jinja2 Template
|
|
102
|
+
env = jinja2.Environment(loader=jinja2.FileSystemLoader(resource_path))
|
|
103
|
+
template = env.get_template(template_name)
|
|
104
|
+
|
|
105
|
+
build_dir = Path(build_dir)
|
|
106
|
+
build_dir.mkdir(parents=True, exist_ok=True)
|
|
107
|
+
|
|
108
|
+
Path(output_tex).parent.mkdir(parents=True, exist_ok=True)
|
|
109
|
+
|
|
110
|
+
# Step 3: Render AST to LaTeX (filters not needed again)
|
|
111
|
+
body = pypandoc.convert_text(
|
|
112
|
+
ast_json_str,
|
|
113
|
+
format="json",
|
|
114
|
+
to="latex",
|
|
115
|
+
extra_args=['--template', rootpath / "templates" / "body.tex"] + args,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
with open(output_tex, "w") as f:
|
|
119
|
+
f.write(template.render(body=body, **metadata)) # Includes authors/abstract
|
|
120
|
+
|
|
121
|
+
metadata["resource_path"] = str(resource_path)
|
|
122
|
+
return metadata
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def compile_pdf(input_tex, output_pdf, engine='pdflatex', build_dir='build', images_dir='images', bib_file='references.bib', resource_path=''):
|
|
126
|
+
"""
|
|
127
|
+
Step 2: Compile LaTeX source into PDF.
|
|
128
|
+
"""
|
|
129
|
+
if resource_path:
|
|
130
|
+
print(f"Resource path: {resource_path}")
|
|
131
|
+
run(f"rsync -r {resource_path}/ {build_dir}/", shell=True)
|
|
132
|
+
# os.environ['TEXINPUTS'] = f"{resource_path}:" + os.environ.get('TEXINPUTS', '')
|
|
133
|
+
|
|
134
|
+
run(f"rsync -r {Path(images_dir)} {build_dir}/", shell=True)
|
|
135
|
+
run(f"rsync {input_tex} {build_dir}/", shell=True)
|
|
136
|
+
run(f"rsync {bib_file} {build_dir}/", shell=True)
|
|
137
|
+
cmd = [engine, '-interaction=nonstopmode', Path(input_tex).name]
|
|
138
|
+
run(cmd, cwd=build_dir, check=False)
|
|
139
|
+
bibcmd = ["bibtex", Path(input_tex).with_suffix(".aux").name]
|
|
140
|
+
run(bibcmd, cwd=build_dir, check=False)
|
|
141
|
+
run(cmd, cwd=build_dir, check=False)
|
|
142
|
+
run(cmd, cwd=build_dir, check=False)
|
|
143
|
+
# Rename/move the generated PDF if needed
|
|
144
|
+
actual_pdf = Path(build_dir) / Path(input_tex).with_suffix(".pdf").name
|
|
145
|
+
if Path(output_pdf) != actual_pdf:
|
|
146
|
+
run(['mv', str(actual_pdf), output_pdf])
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def main():
|
|
150
|
+
|
|
151
|
+
parser = argparse.ArgumentParser(description='Two-step build: Markdown → LaTeX → PDF')
|
|
152
|
+
parser.add_argument('input', help='Input markdown file')
|
|
153
|
+
parser.add_argument('-j', '--journal-template', help='Pandoc LaTeX + filter template family. Update journal -> template yaml field)')
|
|
154
|
+
parser.add_argument('-t', '--template', help='Pandoc LaTeX template. Update template yaml field)')
|
|
155
|
+
parser.add_argument('-f', '--filters', nargs='*', help='Additional, custom filters. By default the pre-defined, custom filters for the journal are used via the `texmark-filter` utility.')
|
|
156
|
+
parser.add_argument('--filters-module', help='Load a custom filter module. This is a Python module that may extend the filters dict defined in the `texmark.shared` module.')
|
|
157
|
+
parser.add_argument('-o', '--output', help='Final PDF output filename')
|
|
158
|
+
parser.add_argument('-e', '--engine', default='pdflatex', help='LaTeX engine (e.g. pdflatex, xelatex)')
|
|
159
|
+
parser.add_argument('-d', '--build', default='build', help='build directory')
|
|
160
|
+
parser.add_argument('--bib', help='bibliography file')
|
|
161
|
+
parser.add_argument('--tex', help='LaTeX output filename')
|
|
162
|
+
parser.add_argument('--pdf', action="store_true")
|
|
163
|
+
parser.add_argument('--images', default='images', help='images directory')
|
|
164
|
+
args = parser.parse_args()
|
|
165
|
+
|
|
166
|
+
# Derive filenames
|
|
167
|
+
build_dir = Path(args.build)
|
|
168
|
+
tex_file = args.tex or build_dir / Path(args.input).with_suffix(".tex").name
|
|
169
|
+
pdf_file = args.output or build_dir / Path(args.input).with_suffix(".pdf").name
|
|
170
|
+
|
|
171
|
+
metadata = build_tex(args.input, tex_file, template=args.template, bib_file=args.bib, filters=args.filters, journal_template=args.journal_template, filters_module=args.filters_module)
|
|
172
|
+
|
|
173
|
+
if args.pdf:
|
|
174
|
+
compile_pdf(tex_file, pdf_file, args.engine, args.build, args.images, bib_file=metadata.get('bibliography'), resource_path=metadata.get('resource_path'))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if __name__ == '__main__':
|
|
178
|
+
main()
|