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.
Files changed (43) hide show
  1. texmark-0.0.0/.gitattributes +4 -0
  2. texmark-0.0.0/.github/workflows/pypi.yml +35 -0
  3. texmark-0.0.0/.gitignore +174 -0
  4. texmark-0.0.0/LICENSE +21 -0
  5. texmark-0.0.0/PKG-INFO +68 -0
  6. texmark-0.0.0/README.md +52 -0
  7. texmark-0.0.0/example.md +59 -0
  8. texmark-0.0.0/images/eof_mean.png +3 -0
  9. texmark-0.0.0/pyproject.toml +34 -0
  10. texmark-0.0.0/references.bib +13 -0
  11. texmark-0.0.0/requirements.txt +5 -0
  12. texmark-0.0.0/setup.cfg +4 -0
  13. texmark-0.0.0/texmark/__init__.py +0 -0
  14. texmark-0.0.0/texmark/build.py +178 -0
  15. texmark-0.0.0/texmark/filters.py +114 -0
  16. texmark-0.0.0/texmark/logs.py +3 -0
  17. texmark-0.0.0/texmark/sectiontracker.py +136 -0
  18. texmark-0.0.0/texmark/shared.py +83 -0
  19. texmark-0.0.0/texmark/templates/body.tex +1 -0
  20. texmark-0.0.0/texmark/templates/copernicus/README_copernicus_package_7_11.txt +22 -0
  21. texmark-0.0.0/texmark/templates/copernicus/copernicus.bst +1726 -0
  22. texmark-0.0.0/texmark/templates/copernicus/copernicus.cfg +1158 -0
  23. texmark-0.0.0/texmark/templates/copernicus/copernicus.cls +3131 -0
  24. texmark-0.0.0/texmark/templates/copernicus/pdfscreen.sty +1260 -0
  25. texmark-0.0.0/texmark/templates/copernicus/pdfscreencop.sty +1260 -0
  26. texmark-0.0.0/texmark/templates/copernicus/template.tex +447 -0
  27. texmark-0.0.0/texmark/templates/default/template.tex +54 -0
  28. texmark-0.0.0/texmark/templates/science/.gitignore +1 -0
  29. texmark-0.0.0/texmark/templates/science/example_figure.eps +3 -0
  30. texmark-0.0.0/texmark/templates/science/example_figure.png +3 -0
  31. texmark-0.0.0/texmark/templates/science/example_figure.ps +3 -0
  32. texmark-0.0.0/texmark/templates/science/readme.txt +26 -0
  33. texmark-0.0.0/texmark/templates/science/scicite.sty +512 -0
  34. texmark-0.0.0/texmark/templates/science/science_template.bib +64 -0
  35. texmark-0.0.0/texmark/templates/science/science_template.tex +496 -0
  36. texmark-0.0.0/texmark/templates/science/sciencemag.bst +1334 -0
  37. texmark-0.0.0/texmark/templates/science/template.tex +347 -0
  38. texmark-0.0.0/texmark.egg-info/PKG-INFO +68 -0
  39. texmark-0.0.0/texmark.egg-info/SOURCES.txt +41 -0
  40. texmark-0.0.0/texmark.egg-info/dependency_links.txt +1 -0
  41. texmark-0.0.0/texmark.egg-info/entry_points.txt +3 -0
  42. texmark-0.0.0/texmark.egg-info/requires.txt +5 -0
  43. texmark-0.0.0/texmark.egg-info/top_level.txt +1 -0
@@ -0,0 +1,4 @@
1
+ *.pdf filter=lfs diff=lfs merge=lfs -text
2
+ *.png filter=lfs diff=lfs merge=lfs -text
3
+ *.eps filter=lfs diff=lfs merge=lfs -text
4
+ *.ps filter=lfs diff=lfs merge=lfs -text
@@ -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
@@ -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.
@@ -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.
@@ -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
+ ![My figure \label{fig:eofmean}. The leading / in images and links is removed, but it is convenient for proper formatting in github.](/images/eof_mean.png)
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,3 @@
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a80a00876ad992bd64774a3d2996695e4d09298a074134666f9d0bff2b90addd
3
+ size 80641
@@ -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
+ }
@@ -0,0 +1,5 @@
1
+ pypandoc
2
+ jinja2
3
+ pyyaml
4
+ panflute
5
+ python-frontmatter
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
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()