latextify 0.3.0__tar.gz → 0.3.2__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.
- {latextify-0.3.0 → latextify-0.3.2}/.github/workflows/ci.yml +48 -6
- {latextify-0.3.0 → latextify-0.3.2}/.github/workflows/release.yml +57 -2
- {latextify-0.3.0 → latextify-0.3.2}/PKG-INFO +69 -7
- {latextify-0.3.0 → latextify-0.3.2}/README.md +68 -6
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/body_markers.py +11 -2
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/crossref.py +3 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/plaintext.py +87 -35
- {latextify-0.3.0 → latextify-0.3.2}/latextify/cli_convert.py +23 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/compile/tectonic.py +4 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/emit/citation_resolution.py +66 -1
- {latextify-0.3.0 → latextify-0.3.2}/latextify/emit/figures_copy.py +33 -4
- {latextify-0.3.0 → latextify-0.3.2}/latextify/emit/figures_stage.py +11 -1
- latextify-0.3.2/latextify/emit/inline_supplement.py +91 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/emit/project.py +49 -23
- {latextify-0.3.0 → latextify-0.3.2}/latextify/emit/supplement.py +5 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/figures/convert.py +7 -2
- latextify-0.3.2/latextify/figures/placement.py +44 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/figures/vector.py +89 -7
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/convert_routes.py +27 -7
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/server.py +17 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/static/app.js +41 -16
- latextify-0.3.2/latextify/gui/static/figure-placement.js +115 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/static/index.html +26 -1
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/static/style.css +16 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/kit/README-OFFLINE.md +23 -3
- {latextify-0.3.0 → latextify-0.3.2}/latextify/kit/install_template.py +59 -10
- latextify-0.3.2/latextify/portable.py +160 -0
- latextify-0.3.2/packaging/README-WINDOWS-PORTABLE.txt +34 -0
- {latextify-0.3.0 → latextify-0.3.2}/pyproject.toml +6 -1
- {latextify-0.3.0 → latextify-0.3.2}/startup.bat +22 -3
- {latextify-0.3.0 → latextify-0.3.2}/startup.sh +13 -7
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_crossref.py +13 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_plaintext.py +23 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_emit.py +21 -0
- latextify-0.3.2/tests/test_figure_placement.py +74 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_figures_convert.py +116 -2
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_gui.py +149 -1
- latextify-0.3.2/tests/test_inline_citations.py +52 -0
- latextify-0.3.2/tests/test_inline_supplement.py +272 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_kit_build.py +20 -1
- latextify-0.3.2/tests/test_portable.py +52 -0
- latextify-0.3.2/tools/build_windows_portable.py +134 -0
- {latextify-0.3.0 → latextify-0.3.2}/uv.lock +93 -1
- {latextify-0.3.0 → latextify-0.3.2}/.dockerignore +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/.gitattributes +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/.github/dependabot.yml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/.github/workflows/codeql.yml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/.github/workflows/dependency-audit.yml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/.github/workflows/publish.yml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/.gitignore +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/.python-version +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/CONTRIBUTING.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/LICENSE +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/NOTICE +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/SECURITY.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/deploy/hf-space/DEPLOY.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/deploy/hf-space/Dockerfile +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/deploy/hf-space/README.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/deploy/hf-space/warm_cache.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/deploy/render/DEPLOY.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/deploy/render/Dockerfile +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/examples/01-all-embedded/.gitignore +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/examples/01-all-embedded/README.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/examples/01-all-embedded/make_manuscript.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/examples/01-all-embedded/run.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/examples/02-word-plus-figures/.gitignore +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/examples/02-word-plus-figures/README.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/examples/02-word-plus-figures/make_manuscript.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/examples/02-word-plus-figures/run.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/examples/03-multipart-refmanager/.gitignore +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/examples/03-multipart-refmanager/README.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/examples/03-multipart-refmanager/make_manuscript.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/examples/03-multipart-refmanager/run.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/examples/README.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/__init__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/__main__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/audit/__init__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/audit/equations.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/__init__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/authoryear_index.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/bib.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/bibmatch.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/bibtex_in.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/corrections.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/csl_json_in.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/endnote.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/endnote_xml_in.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/fields.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/mendeley.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/merge.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/nbib_in.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/reconcile.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/reflist_nondocx.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/refs_import.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/validate.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/wordnative.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/citations/zotero.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/cli.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/cli_batch.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/cli_equations.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/cli_export.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/cli_figures.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/cli_kit.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/cli_privacy.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/cli_review.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/compile/__init__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/compile/logs.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/compile/pdf.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/emit/__init__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/emit/alt_formats.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/emit/alt_formats_render.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/emit/anchors.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/emit/bibliography.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/emit/metadata.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/emit/output_dir.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/emit/submission.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/figures/__init__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/figures/caption_gaps.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/figures/crop.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/figures/extract.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/figures/gap_fill.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/figures/inventory.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/figures/outcome.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/figures/override.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/figures/raster.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/figures/scrub.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/__init__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/convert_inputs.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/demo.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/downloads.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/exporting.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/figures_view.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/folder_picker.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/guard.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/lifecycle.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/schemas.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/static/alt-export.js +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/static/clean.js +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/static/export.js +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/static/figures.js +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/static/lifecycle.js +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/static/results.js +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/static/review.js +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/upload_utils.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/uploads_routes.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/gui/validation_view.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/__init__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/_xml.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/archive_guard.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/citation_sentinels.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/docx_clean.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/filters.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/formats.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/frontmatter.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/headings.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/metadata_authors.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/metadata_body.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/metadata_guess.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/metadata_guess_nondocx.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/metadata_markers.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/metadata_paragraphs.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/metadata_schema.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/pandoc.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/portable_anchors.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/preflight.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/preflight_privacy.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/tables.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/ingest/tables_degraded.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/kit/__init__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/kit/build.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/kit/target.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/kit/tex_cache.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/model/__init__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/model/_compat.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/model/body.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/model/compile.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/model/emit.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/model/equations.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/model/figure.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/model/meta.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/model/preflight.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/model/reconcile.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/model/refs.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/model/validate.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/privacy/__init__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/privacy/docx_adapter.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/privacy/images.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/privacy/ole.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/privacy/opc.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/privacy/pdf.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/privacy/pdf_content.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/privacy/pptx.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/privacy/registry.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/privacy/report.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/privacy/xlsx.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/report/__init__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/report/render.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/__init__.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/authors.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/.gitkeep +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/achemso/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/achemso/metadata.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/achemso/preamble.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/aip-advances/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/aip-apl/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/aip-jap/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/aps-prapplied/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/aps-prl/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/aps-prx/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/aps-rmp/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/elsarticle/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/elsarticle/metadata.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/elsarticle/preamble.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/elsarticle/vendor/elsarticle.cls +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/ieeetran/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/ieeetran/metadata.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/ieeetran/preamble.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/iopart/VENDOR_LICENSE.txt +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/iopart/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/iopart/metadata.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/iopart/preamble.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/iopart/vendor/.gitattributes +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/iopart/vendor/iopart.cls +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/iopart/vendor/iopart10.clo +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/iopart/vendor/iopart12.clo +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/revtex4-2/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/revtex4-2/metadata.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/revtex4-2/preamble.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/sn-jnl/VENDOR_LICENSE.txt +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/sn-jnl/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/sn-jnl/metadata.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/sn-jnl/preamble.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/sn-jnl/vendor/.gitattributes +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/sn-jnl/vendor/sn-jnl.cls +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/sn-jnl/vendor/sn-mathphys-ay.bst +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/sn-jnl/vendor/sn-mathphys-num.bst +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/wiley/manifest.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/wiley/metadata.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/journals/wiley/preamble.tex.j2 +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/latextify/templates/loader.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/plans/FORMATS_AND_PRIVACY_PLAN.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/plans/GUI_MULTIFILE_PLAN.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/plans/GUI_OPTIONS_FORMATS_PLAN.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/plans/METADATA_PRIVACY_PLAN.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/plans/OFFLINE_DISTRIBUTION_PLAN.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/plans/VECTOR_FIGURES_PLAN.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/plans/archive/GUI_JOURNALS_EXPORT_PLAN.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/plans/archive/LATEXTIFY_PLAN.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/plans/archive/MANUSCRIPT_FIDELITY_PLAN.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/plans/archive/OFFLINE_PORTABILITY_PLAN.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/plans/archive/REPO_HEALTH_PLAN.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/plans/archive/SECURITY_RELIABILITY_AUDIT_PLAN.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/render.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/README.md +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/bracket_cited.docx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/clean.docx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/clean_black_figure.pdf +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/cropped_figure.docx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/endnote_cited.docx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/equations.docx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/figures.docx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/achemso_metadata.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/achemso_preamble.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/elsarticle_metadata.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/elsarticle_preamble_authoryear.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/elsarticle_preamble_numeric.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/ieeetran_metadata.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/ieeetran_preamble.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/iopart_metadata.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/iopart_preamble.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/revtex4-2_metadata.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/revtex4-2_preamble.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/sn-jnl_metadata.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/sn-jnl_preamble.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/sn-jnl_preamble_authoryear.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/wiley_metadata.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/wiley_preamble_authoryear.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/golden/wiley_preamble_numeric.tex +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/hand_cited.docx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/leaky_book.xlsx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/leaky_deck.pptx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/leaky_doc.pdf +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/leaky_files.truth.json +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/leaky_photo.jpg +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_bracket_cited.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_clean.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_cropped_figure.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_endnote_cited.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_equations.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_figures.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_hand_cited.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_leaky_files.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_metadata_titlepage.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_supplement.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_tables.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_unsupported.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_wordnative_cited.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/make_zotero_cited.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/metadata_titlepage.docx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/paper.yaml +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/supplement.docx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/tables.docx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/unsupported.docx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/wordnative_cited.docx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/fixtures/zotero_cited.docx +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_archive_guard.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_audit_equations.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citation_sentinels.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_bib.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_bibmatch.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_bibtex_in.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_bracket_refs.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_compile_stub.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_corrections.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_csl_json_in.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_degradation.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_endnote.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_endnote_compile.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_endnote_xml_in.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_extract.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_extract_endnote.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_extract_wordnative.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_fields.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_merge.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_nbib_in.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_parsers.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_reconstruct.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_refs_import.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_stress.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_validate.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_citations_wordnative.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_cli.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_cli_figures.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_cli_review.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_compat_strenum.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_compile_logs.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_compile_tectonic.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_docx_clean.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_emit_submission.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_examples.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_export_formats.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_fidelity_captions_reflist.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_figures.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_figures_caption_gaps.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_figures_crop.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_figures_inventory.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_figures_scrub.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_figures_stage2.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_figures_tiff.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_figures_wide.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_filters.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_filters_headings.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_frontmatter.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_gui_demo.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_journal_pdf_layout.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_manuscript_formats.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_metadata_guess.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_package.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_pandoc.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_pdf_combine.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_preflight.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_preflight_privacy.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_privacy.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_repo_integrity.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_report.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_supplement.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_tables.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_tectonic_integrity.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_templates.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_templates_item22.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_templates_sn_jnl.py +0 -0
- {latextify-0.3.0 → latextify-0.3.2}/tests/test_xml_hardening.py +0 -0
|
@@ -138,9 +138,8 @@ jobs:
|
|
|
138
138
|
# OLDEST supported Python (3.10, our documented floor -- see
|
|
139
139
|
# requires-python in pyproject.toml) on the platform most kit users
|
|
140
140
|
# actually run: Windows. A future dependency bump could silently break
|
|
141
|
-
# the 3.10 floor without this catching it.
|
|
142
|
-
#
|
|
143
|
-
# covered by the `offline-kit` job above.
|
|
141
|
+
# the 3.10 floor without this catching it. This job also freezes and boots
|
|
142
|
+
# the no-Python portable application while the network is poisoned.
|
|
144
143
|
runs-on: windows-latest
|
|
145
144
|
steps:
|
|
146
145
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
@@ -148,14 +147,14 @@ jobs:
|
|
|
148
147
|
with:
|
|
149
148
|
python-version: "3.13"
|
|
150
149
|
- name: Install
|
|
151
|
-
run: uv sync --locked
|
|
150
|
+
run: uv sync --locked --group portable
|
|
152
151
|
- name: Set up the TARGET Python 3.10 (what install.py will run under)
|
|
153
152
|
id: py310
|
|
154
153
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
155
154
|
with:
|
|
156
155
|
python-version: "3.10"
|
|
157
|
-
- name: Build
|
|
158
|
-
run: uv run latextify make-kit --target current --python-versions 3.10 --
|
|
156
|
+
- name: Build a GUI kit covering Python 3.10 and warm one journal (network ON)
|
|
157
|
+
run: uv run latextify make-kit --target current --python-versions 3.10 --with-gui --journals revtex4-2 --output build
|
|
159
158
|
- name: Install the kit under Python 3.10, network POISONED
|
|
160
159
|
env:
|
|
161
160
|
# Same reasoning as the Linux offline-kit job: uppercase only, one
|
|
@@ -166,6 +165,12 @@ jobs:
|
|
|
166
165
|
run: |
|
|
167
166
|
& "${{ steps.py310.outputs.python-path }}" "build/latextify-offline-windows-x64/install.py"
|
|
168
167
|
if ($LASTEXITCODE -ne 0) { throw "install.py failed under Python 3.10 (exit $LASTEXITCODE)" }
|
|
168
|
+
$kit = "build/latextify-offline-windows-x64"
|
|
169
|
+
& "$kit/.venv/Scripts/python.exe" -c "import fastapi, uvicorn, multipart; import latextify.gui.server"
|
|
170
|
+
if ($LASTEXITCODE -ne 0) { throw "offline GUI imports failed (exit $LASTEXITCODE)" }
|
|
171
|
+
if (-not (Test-Path "$kit/Start-LaTeXtify-GUI.bat")) {
|
|
172
|
+
throw "offline GUI launcher was not generated"
|
|
173
|
+
}
|
|
169
174
|
- name: Convert a fixture manuscript offline (emit-only, no --pdf)
|
|
170
175
|
env:
|
|
171
176
|
HTTPS_PROXY: http://127.0.0.1:9
|
|
@@ -178,3 +183,40 @@ jobs:
|
|
|
178
183
|
throw "Python 3.10 kit did not produce demo-out/revtex4-2/main.tex"
|
|
179
184
|
}
|
|
180
185
|
Write-Host "offline (Python 3.10, Windows): produced demo-out/revtex4-2/main.tex"
|
|
186
|
+
- name: Build the no-install portable Windows application
|
|
187
|
+
run: uv run --group portable python tools/build_windows_portable.py --kit build/latextify-offline-windows-x64 --output build/portable
|
|
188
|
+
- name: Exercise the frozen application with the network poisoned
|
|
189
|
+
env:
|
|
190
|
+
HTTPS_PROXY: http://127.0.0.1:9
|
|
191
|
+
HTTP_PROXY: http://127.0.0.1:9
|
|
192
|
+
ALL_PROXY: http://127.0.0.1:9
|
|
193
|
+
run: |
|
|
194
|
+
$app = Resolve-Path "build/portable/LaTeXtify-Windows-Portable/LaTeXtify.exe"
|
|
195
|
+
$selfTest = Start-Process -FilePath $app -ArgumentList "--self-test" -PassThru -Wait -WindowStyle Hidden
|
|
196
|
+
if ($selfTest.ExitCode -ne 0) {
|
|
197
|
+
Get-Content "build/portable/LaTeXtify-Windows-Portable/latextify-startup.log" -ErrorAction SilentlyContinue
|
|
198
|
+
throw "portable self-test failed (exit $($selfTest.ExitCode))"
|
|
199
|
+
}
|
|
200
|
+
$env:LATEXTIFY_NO_BROWSER = "1"
|
|
201
|
+
$env:LATEXTIFY_PORT = "8765"
|
|
202
|
+
$process = Start-Process -FilePath $app -PassThru -WindowStyle Hidden
|
|
203
|
+
try {
|
|
204
|
+
$ready = $false
|
|
205
|
+
foreach ($attempt in 1..30) {
|
|
206
|
+
Start-Sleep -Seconds 1
|
|
207
|
+
try {
|
|
208
|
+
$response = Invoke-WebRequest "http://127.0.0.1:8765/" -UseBasicParsing -NoProxy
|
|
209
|
+
if ($response.StatusCode -eq 200 -and $response.Content -match "LaTeXtify") {
|
|
210
|
+
$ready = $true
|
|
211
|
+
break
|
|
212
|
+
}
|
|
213
|
+
} catch {}
|
|
214
|
+
if ($process.HasExited) { break }
|
|
215
|
+
}
|
|
216
|
+
if (-not $ready) {
|
|
217
|
+
Get-Content "build/portable/LaTeXtify-Windows-Portable/latextify-startup.log" -ErrorAction SilentlyContinue
|
|
218
|
+
throw "portable GUI did not answer on loopback"
|
|
219
|
+
}
|
|
220
|
+
} finally {
|
|
221
|
+
if (-not $process.HasExited) { Stop-Process -Id $process.Id -Force }
|
|
222
|
+
}
|
|
@@ -27,7 +27,50 @@ concurrency:
|
|
|
27
27
|
cancel-in-progress: false
|
|
28
28
|
|
|
29
29
|
jobs:
|
|
30
|
+
build-windows-portable:
|
|
31
|
+
runs-on: windows-latest
|
|
32
|
+
timeout-minutes: 60
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
35
|
+
with:
|
|
36
|
+
ref: ${{ github.event.inputs.tag || github.ref_name }}
|
|
37
|
+
- uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
38
|
+
with:
|
|
39
|
+
python-version: "3.13"
|
|
40
|
+
- name: Cache Tectonic binary and package bundle
|
|
41
|
+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
|
|
42
|
+
with:
|
|
43
|
+
path: |
|
|
44
|
+
~/.cache/latextify
|
|
45
|
+
~/.cache/Tectonic
|
|
46
|
+
key: tectonic-${{ runner.os }}-v1
|
|
47
|
+
- name: Install build dependencies
|
|
48
|
+
run: uv sync --locked --no-dev --extra gui --group portable
|
|
49
|
+
- name: Build native Windows runtime assets
|
|
50
|
+
env:
|
|
51
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
52
|
+
run: uv run latextify make-kit --target current --python-versions 3.13 --with-gui -o build
|
|
53
|
+
- name: Freeze and verify the portable application
|
|
54
|
+
env:
|
|
55
|
+
HTTPS_PROXY: http://127.0.0.1:9
|
|
56
|
+
HTTP_PROXY: http://127.0.0.1:9
|
|
57
|
+
ALL_PROXY: http://127.0.0.1:9
|
|
58
|
+
run: |
|
|
59
|
+
uv run --group portable python tools/build_windows_portable.py --kit build/latextify-offline-windows-x64 --output build/portable
|
|
60
|
+
$app = Resolve-Path "build/portable/LaTeXtify-Windows-Portable/LaTeXtify.exe"
|
|
61
|
+
$selfTest = Start-Process -FilePath $app -ArgumentList "--self-test" -PassThru -Wait -WindowStyle Hidden
|
|
62
|
+
if ($selfTest.ExitCode -ne 0) {
|
|
63
|
+
Get-Content "build/portable/LaTeXtify-Windows-Portable/latextify-startup.log" -ErrorAction SilentlyContinue
|
|
64
|
+
throw "portable self-test failed (exit $($selfTest.ExitCode))"
|
|
65
|
+
}
|
|
66
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
67
|
+
with:
|
|
68
|
+
name: windows-portable
|
|
69
|
+
path: build/portable/LaTeXtify-Windows-Portable.zip
|
|
70
|
+
if-no-files-found: error
|
|
71
|
+
|
|
30
72
|
build-kits:
|
|
73
|
+
needs: build-windows-portable
|
|
31
74
|
runs-on: ubuntu-latest
|
|
32
75
|
timeout-minutes: 60
|
|
33
76
|
steps:
|
|
@@ -54,7 +97,7 @@ jobs:
|
|
|
54
97
|
- name: Build native Linux kit (warms the shared, portable TeX cache)
|
|
55
98
|
env:
|
|
56
99
|
GITHUB_TOKEN: ${{ github.token }}
|
|
57
|
-
run: uv run latextify make-kit --target linux-x64 -o build
|
|
100
|
+
run: uv run latextify make-kit --target linux-x64 --with-gui -o build
|
|
58
101
|
|
|
59
102
|
# NOTE -- native-build preference: this workflow cross-builds the
|
|
60
103
|
# win-x64 (and macos-arm64) kits from a single Linux runner, on a
|
|
@@ -80,7 +123,7 @@ jobs:
|
|
|
80
123
|
# and reuse it for the cross targets rather than re-downloading the
|
|
81
124
|
# whole TeX bundle two more times.
|
|
82
125
|
for tgt in win-x64 macos-arm64; do
|
|
83
|
-
uv run latextify make-kit --target "$tgt" --no-warm-tex -o build
|
|
126
|
+
uv run latextify make-kit --target "$tgt" --with-gui --no-warm-tex -o build
|
|
84
127
|
done
|
|
85
128
|
uv run python - <<'PY'
|
|
86
129
|
import json
|
|
@@ -116,6 +159,17 @@ jobs:
|
|
|
116
159
|
ls -lh *.zip SHA256SUMS.txt
|
|
117
160
|
cat SHA256SUMS.txt
|
|
118
161
|
|
|
162
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
|
163
|
+
with:
|
|
164
|
+
name: windows-portable
|
|
165
|
+
path: build
|
|
166
|
+
|
|
167
|
+
- name: Add the portable Windows application to checksums
|
|
168
|
+
run: |
|
|
169
|
+
cd build
|
|
170
|
+
sha256sum LaTeXtify-Windows-Portable.zip >> SHA256SUMS.txt
|
|
171
|
+
cat SHA256SUMS.txt
|
|
172
|
+
|
|
119
173
|
- name: Create release (if missing) and upload assets
|
|
120
174
|
env:
|
|
121
175
|
GH_TOKEN: ${{ github.token }}
|
|
@@ -126,6 +180,7 @@ jobs:
|
|
|
126
180
|
latextify-offline-windows-x64.zip
|
|
127
181
|
latextify-offline-linux-x64.zip
|
|
128
182
|
latextify-offline-macos-arm64.zip
|
|
183
|
+
LaTeXtify-Windows-Portable.zip
|
|
129
184
|
latextify-source.zip
|
|
130
185
|
SHA256SUMS.txt
|
|
131
186
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: latextify
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Convert Word manuscripts to journal-ready LaTeX projects and PDFs
|
|
5
5
|
Project-URL: Homepage, https://github.com/pquarterman17/LaTeXtify
|
|
6
6
|
Project-URL: Repository, https://github.com/pquarterman17/LaTeXtify
|
|
@@ -252,8 +252,9 @@ holds real vector for anything drawn:
|
|
|
252
252
|
Metafile)*. That lands as EMF, which is vector. A plain Ctrl+V usually gives
|
|
253
253
|
you a bitmap.
|
|
254
254
|
- **LaTeXtify converts EMF and WMF to PDF** when LibreOffice or Inkscape is on
|
|
255
|
-
your PATH
|
|
256
|
-
|
|
255
|
+
your PATH or in its usual Windows install directory. If neither is available,
|
|
256
|
+
it tries a 600-DPI PNG fallback and warns that the result is no longer vector;
|
|
257
|
+
a metafile it cannot safely decode is skipped with an actionable warning.
|
|
257
258
|
- **Export from the plotting tool.** Saving directly to PDF or SVG and dropping
|
|
258
259
|
it in as `figures/fig<N>.pdf` skips Word entirely and is the most reliable
|
|
259
260
|
route.
|
|
@@ -330,7 +331,34 @@ To start the web GUI without touching a command line, double-click
|
|
|
330
331
|
**`startup.bat`** (Windows) or run **`./startup.sh`** (macOS/Linux). It installs
|
|
331
332
|
dependencies on first run, opens the GUI in your browser, and — if anything
|
|
332
333
|
goes wrong — writes an easy-to-find `latextify-startup.log` next to the script
|
|
333
|
-
and prints it for you to copy.
|
|
334
|
+
and prints it for you to copy. This source-tree launcher needs internet access
|
|
335
|
+
on its first run.
|
|
336
|
+
|
|
337
|
+
### Offline / firewalled Windows computer
|
|
338
|
+
|
|
339
|
+
Do not transfer GitHub's automatically generated source-code ZIP or
|
|
340
|
+
`latextify-source.zip`: their `startup.bat` must fetch `uv` and Python packages.
|
|
341
|
+
For a locked-down work computer, download the release asset named
|
|
342
|
+
**`LaTeXtify-Windows-Portable.zip`**, transfer it using an approved method,
|
|
343
|
+
extract the whole folder to a short writable path such as `C:\LaTeXtify`, and
|
|
344
|
+
double-click **`LaTeXtify.exe`**. It carries its own Python runtime, GUI,
|
|
345
|
+
Pandoc, Tectonic, and warmed TeX cache. It performs no installation and needs
|
|
346
|
+
no administrator rights, Python, `uv`, `pip`, GitHub, Astral, or PyPI access.
|
|
347
|
+
|
|
348
|
+
The older **`latextify-offline-windows-x64.zip`** remains available for users
|
|
349
|
+
who prefer a conventional, inspectable Python environment. It requires a
|
|
350
|
+
supported 64-bit Python already present. Extract it and run:
|
|
351
|
+
|
|
352
|
+
```bat
|
|
353
|
+
py install.py
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Then double-click **`Start-LaTeXtify-GUI.bat`**. That offline package contains
|
|
357
|
+
the GUI, Pandoc, Tectonic, dependencies, and a warmed TeX cache; it does not
|
|
358
|
+
contact GitHub, Astral, PyPI, or Crossref. It requires a supported 64-bit
|
|
359
|
+
Python already present on the work computer. Embedded EMF/WMF figures work,
|
|
360
|
+
but preserving them as vectors requires an offline installation of LibreOffice
|
|
361
|
+
or Inkscape; otherwise Windows attempts a 600-DPI raster fallback.
|
|
334
362
|
|
|
335
363
|
## Usage
|
|
336
364
|
|
|
@@ -347,6 +375,18 @@ latextify convert paper.docx --journal elsarticle --citation-style authoryear
|
|
|
347
375
|
# Supplementary material: S-numbered second document sharing the bibliography
|
|
348
376
|
latextify convert paper.docx --journal revtex4-2 --supplement si.docx --pdf
|
|
349
377
|
|
|
378
|
+
# Main text + supplement in ONE Word file and ONE main.pdf. Put a heading named
|
|
379
|
+
# "Supplementary Material", "Supplementary Information", "Supplemental
|
|
380
|
+
# Material", or "Supporting Information" at the boundary.
|
|
381
|
+
latextify convert merged.docx --journal revtex4-2 --inline-supplement --pdf
|
|
382
|
+
|
|
383
|
+
# Inline supplements cannot be combined with --figures-at-end: delayed floats
|
|
384
|
+
# would cross the numbering boundary. LaTeXtify rejects that combination.
|
|
385
|
+
# If the merged file contains separate main and supplementary reference lists,
|
|
386
|
+
# LaTeXtify combines them into one bibliography at the end of the PDF. Numeric
|
|
387
|
+
# citation numbering is continuous across the whole document and a warning is
|
|
388
|
+
# written to the conversion report so this policy is never implicit.
|
|
389
|
+
|
|
350
390
|
# ...and staple the main text + supplement into one combined.pdf
|
|
351
391
|
latextify convert paper.docx --journal revtex4-2 --supplement si.docx --pdf --combine-supplement
|
|
352
392
|
|
|
@@ -366,6 +406,11 @@ latextify figures paper.docx
|
|
|
366
406
|
# Convert, reporting every figure that is still a screenshot rather than vector
|
|
367
407
|
latextify convert paper.docx --journal revtex4-2 --pdf --vector-figures
|
|
368
408
|
|
|
409
|
+
# Force selected embedded or external figures to one/two columns. Numbers are
|
|
410
|
+
# document order as shown by `latextify figures`; all others stay automatic.
|
|
411
|
+
latextify convert paper.docx --journal revtex4-2 --pdf \
|
|
412
|
+
--figure-column 1=one --figure-column 2=two
|
|
413
|
+
|
|
369
414
|
# A folder of manuscripts at once (continue-on-error + summary)
|
|
370
415
|
latextify batch drafts/ --journal revtex4-2 --pdf
|
|
371
416
|
|
|
@@ -385,9 +430,11 @@ latextify journals
|
|
|
385
430
|
`127.0.0.1` only — your uploads never leave your machine) and opens a tab.
|
|
386
431
|
Drop your whole submission in at once — **main `.docx`, supplement `.docx`,
|
|
387
432
|
figure files, and a `.bib` reference library together** — then set each
|
|
388
|
-
file's role, pick a journal from the full publisher list, choose options
|
|
389
|
-
|
|
390
|
-
|
|
433
|
+
file's role, pick a journal from the full publisher list, choose options,
|
|
434
|
+
and click **Preview**. The GUI reads the selected manuscript's figures and
|
|
435
|
+
gives every figure its own **Automatic / 1 column / 2 columns** selector;
|
|
436
|
+
separately uploaded replacements appear under the same document-order number.
|
|
437
|
+
The compiled PDFs render inline so you can
|
|
391
438
|
confirm the conversion worked. A preview is held only in **temporary local
|
|
392
439
|
storage** (a private working directory the app owns) — it is pruned
|
|
393
440
|
automatically about an hour after its last use and deleted when the app shuts
|
|
@@ -396,6 +443,21 @@ panel lets you pick a destination folder (a native "Browse…" dialog) and copy
|
|
|
396
443
|
any subset of the outputs — the LaTeX project, individual PDFs, or the `.zip` —
|
|
397
444
|
to the folder you choose to keep.
|
|
398
445
|
|
|
446
|
+
For a merged main+supplement Word file, upload it only as **Main text**, do not
|
|
447
|
+
add a separate Supplement file, and choose one **Combined main + supplement
|
|
448
|
+
Word file** layout:
|
|
449
|
+
|
|
450
|
+
- **Single-column throughout (closest to Word)**
|
|
451
|
+
- **Main two columns; supplement two columns**
|
|
452
|
+
- **Main two columns; supplement one column**
|
|
453
|
+
|
|
454
|
+
Every combined mode produces one `main.pdf`, starts the recognized
|
|
455
|
+
Supplementary Material/Information heading on a new page, and restarts figures,
|
|
456
|
+
tables, equations, and sections as S1. **Check references online**, **Strip
|
|
457
|
+
figure metadata**, and **Optimize figure placement** are independent toggles.
|
|
458
|
+
The offline launcher disables the online reference check; the other two remain
|
|
459
|
+
available. Manual per-figure column choices override automatic placement.
|
|
460
|
+
|
|
399
461
|
Output layout per conversion:
|
|
400
462
|
|
|
401
463
|
```
|
|
@@ -206,8 +206,9 @@ holds real vector for anything drawn:
|
|
|
206
206
|
Metafile)*. That lands as EMF, which is vector. A plain Ctrl+V usually gives
|
|
207
207
|
you a bitmap.
|
|
208
208
|
- **LaTeXtify converts EMF and WMF to PDF** when LibreOffice or Inkscape is on
|
|
209
|
-
your PATH
|
|
210
|
-
|
|
209
|
+
your PATH or in its usual Windows install directory. If neither is available,
|
|
210
|
+
it tries a 600-DPI PNG fallback and warns that the result is no longer vector;
|
|
211
|
+
a metafile it cannot safely decode is skipped with an actionable warning.
|
|
211
212
|
- **Export from the plotting tool.** Saving directly to PDF or SVG and dropping
|
|
212
213
|
it in as `figures/fig<N>.pdf` skips Word entirely and is the most reliable
|
|
213
214
|
route.
|
|
@@ -284,7 +285,34 @@ To start the web GUI without touching a command line, double-click
|
|
|
284
285
|
**`startup.bat`** (Windows) or run **`./startup.sh`** (macOS/Linux). It installs
|
|
285
286
|
dependencies on first run, opens the GUI in your browser, and — if anything
|
|
286
287
|
goes wrong — writes an easy-to-find `latextify-startup.log` next to the script
|
|
287
|
-
and prints it for you to copy.
|
|
288
|
+
and prints it for you to copy. This source-tree launcher needs internet access
|
|
289
|
+
on its first run.
|
|
290
|
+
|
|
291
|
+
### Offline / firewalled Windows computer
|
|
292
|
+
|
|
293
|
+
Do not transfer GitHub's automatically generated source-code ZIP or
|
|
294
|
+
`latextify-source.zip`: their `startup.bat` must fetch `uv` and Python packages.
|
|
295
|
+
For a locked-down work computer, download the release asset named
|
|
296
|
+
**`LaTeXtify-Windows-Portable.zip`**, transfer it using an approved method,
|
|
297
|
+
extract the whole folder to a short writable path such as `C:\LaTeXtify`, and
|
|
298
|
+
double-click **`LaTeXtify.exe`**. It carries its own Python runtime, GUI,
|
|
299
|
+
Pandoc, Tectonic, and warmed TeX cache. It performs no installation and needs
|
|
300
|
+
no administrator rights, Python, `uv`, `pip`, GitHub, Astral, or PyPI access.
|
|
301
|
+
|
|
302
|
+
The older **`latextify-offline-windows-x64.zip`** remains available for users
|
|
303
|
+
who prefer a conventional, inspectable Python environment. It requires a
|
|
304
|
+
supported 64-bit Python already present. Extract it and run:
|
|
305
|
+
|
|
306
|
+
```bat
|
|
307
|
+
py install.py
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Then double-click **`Start-LaTeXtify-GUI.bat`**. That offline package contains
|
|
311
|
+
the GUI, Pandoc, Tectonic, dependencies, and a warmed TeX cache; it does not
|
|
312
|
+
contact GitHub, Astral, PyPI, or Crossref. It requires a supported 64-bit
|
|
313
|
+
Python already present on the work computer. Embedded EMF/WMF figures work,
|
|
314
|
+
but preserving them as vectors requires an offline installation of LibreOffice
|
|
315
|
+
or Inkscape; otherwise Windows attempts a 600-DPI raster fallback.
|
|
288
316
|
|
|
289
317
|
## Usage
|
|
290
318
|
|
|
@@ -301,6 +329,18 @@ latextify convert paper.docx --journal elsarticle --citation-style authoryear
|
|
|
301
329
|
# Supplementary material: S-numbered second document sharing the bibliography
|
|
302
330
|
latextify convert paper.docx --journal revtex4-2 --supplement si.docx --pdf
|
|
303
331
|
|
|
332
|
+
# Main text + supplement in ONE Word file and ONE main.pdf. Put a heading named
|
|
333
|
+
# "Supplementary Material", "Supplementary Information", "Supplemental
|
|
334
|
+
# Material", or "Supporting Information" at the boundary.
|
|
335
|
+
latextify convert merged.docx --journal revtex4-2 --inline-supplement --pdf
|
|
336
|
+
|
|
337
|
+
# Inline supplements cannot be combined with --figures-at-end: delayed floats
|
|
338
|
+
# would cross the numbering boundary. LaTeXtify rejects that combination.
|
|
339
|
+
# If the merged file contains separate main and supplementary reference lists,
|
|
340
|
+
# LaTeXtify combines them into one bibliography at the end of the PDF. Numeric
|
|
341
|
+
# citation numbering is continuous across the whole document and a warning is
|
|
342
|
+
# written to the conversion report so this policy is never implicit.
|
|
343
|
+
|
|
304
344
|
# ...and staple the main text + supplement into one combined.pdf
|
|
305
345
|
latextify convert paper.docx --journal revtex4-2 --supplement si.docx --pdf --combine-supplement
|
|
306
346
|
|
|
@@ -320,6 +360,11 @@ latextify figures paper.docx
|
|
|
320
360
|
# Convert, reporting every figure that is still a screenshot rather than vector
|
|
321
361
|
latextify convert paper.docx --journal revtex4-2 --pdf --vector-figures
|
|
322
362
|
|
|
363
|
+
# Force selected embedded or external figures to one/two columns. Numbers are
|
|
364
|
+
# document order as shown by `latextify figures`; all others stay automatic.
|
|
365
|
+
latextify convert paper.docx --journal revtex4-2 --pdf \
|
|
366
|
+
--figure-column 1=one --figure-column 2=two
|
|
367
|
+
|
|
323
368
|
# A folder of manuscripts at once (continue-on-error + summary)
|
|
324
369
|
latextify batch drafts/ --journal revtex4-2 --pdf
|
|
325
370
|
|
|
@@ -339,9 +384,11 @@ latextify journals
|
|
|
339
384
|
`127.0.0.1` only — your uploads never leave your machine) and opens a tab.
|
|
340
385
|
Drop your whole submission in at once — **main `.docx`, supplement `.docx`,
|
|
341
386
|
figure files, and a `.bib` reference library together** — then set each
|
|
342
|
-
file's role, pick a journal from the full publisher list, choose options
|
|
343
|
-
|
|
344
|
-
|
|
387
|
+
file's role, pick a journal from the full publisher list, choose options,
|
|
388
|
+
and click **Preview**. The GUI reads the selected manuscript's figures and
|
|
389
|
+
gives every figure its own **Automatic / 1 column / 2 columns** selector;
|
|
390
|
+
separately uploaded replacements appear under the same document-order number.
|
|
391
|
+
The compiled PDFs render inline so you can
|
|
345
392
|
confirm the conversion worked. A preview is held only in **temporary local
|
|
346
393
|
storage** (a private working directory the app owns) — it is pruned
|
|
347
394
|
automatically about an hour after its last use and deleted when the app shuts
|
|
@@ -350,6 +397,21 @@ panel lets you pick a destination folder (a native "Browse…" dialog) and copy
|
|
|
350
397
|
any subset of the outputs — the LaTeX project, individual PDFs, or the `.zip` —
|
|
351
398
|
to the folder you choose to keep.
|
|
352
399
|
|
|
400
|
+
For a merged main+supplement Word file, upload it only as **Main text**, do not
|
|
401
|
+
add a separate Supplement file, and choose one **Combined main + supplement
|
|
402
|
+
Word file** layout:
|
|
403
|
+
|
|
404
|
+
- **Single-column throughout (closest to Word)**
|
|
405
|
+
- **Main two columns; supplement two columns**
|
|
406
|
+
- **Main two columns; supplement one column**
|
|
407
|
+
|
|
408
|
+
Every combined mode produces one `main.pdf`, starts the recognized
|
|
409
|
+
Supplementary Material/Information heading on a new page, and restarts figures,
|
|
410
|
+
tables, equations, and sections as S1. **Check references online**, **Strip
|
|
411
|
+
figure metadata**, and **Optimize figure placement** are independent toggles.
|
|
412
|
+
The offline launcher disables the online reference check; the other two remain
|
|
413
|
+
available. Manual per-figure column choices override automatic placement.
|
|
414
|
+
|
|
353
415
|
Output layout per conversion:
|
|
354
416
|
|
|
355
417
|
```
|
|
@@ -235,12 +235,21 @@ def strip_reference_section_to_eof(tex: str) -> str:
|
|
|
235
235
|
(:mod:`latextify.emit.project`) strips the reference manager's own
|
|
236
236
|
formatted bibliography, which duplicates the generated ``\\bibliography``.
|
|
237
237
|
"""
|
|
238
|
+
|
|
239
|
+
def cut(start: int) -> str:
|
|
240
|
+
# A merged main+SI manuscript can put its main reference list before
|
|
241
|
+
# the supplementary heading. Preserve that protected tail rather than
|
|
242
|
+
# applying the historical "references are always EOF" assumption.
|
|
243
|
+
marker = tex.find("%%LATEXTIFY_INLINE_SUPPLEMENT%%", start)
|
|
244
|
+
tail = strip_reference_section_to_eof(tex[marker:]) if marker >= 0 else ""
|
|
245
|
+
return tex[:start].rstrip() + "\n" + tail
|
|
246
|
+
|
|
238
247
|
for match in _REF_SECTION_RE.finditer(tex):
|
|
239
248
|
if is_reference_heading_text(match.group(1).strip()):
|
|
240
|
-
return
|
|
249
|
+
return cut(match.start())
|
|
241
250
|
offset = _find_bare_reference_heading(tex)
|
|
242
251
|
if offset is not None:
|
|
243
|
-
return
|
|
252
|
+
return cut(offset)
|
|
244
253
|
return tex
|
|
245
254
|
|
|
246
255
|
|
|
@@ -56,6 +56,7 @@ from .bib import csl_type_to_bibtex
|
|
|
56
56
|
#: Placeholder contact address; override with a real one in production use.
|
|
57
57
|
DEFAULT_MAILTO = "latextify@example.com"
|
|
58
58
|
_MAILTO_ENV = "LATEXTIFY_CROSSREF_MAILTO"
|
|
59
|
+
_OFFLINE_ENV = "LATEXTIFY_OFFLINE"
|
|
59
60
|
|
|
60
61
|
DEFAULT_BASE_URL = "https://api.crossref.org"
|
|
61
62
|
_USER_AGENT_PRODUCT = "LaTeXtify/0.1 (https://github.com/latextify/latextify)"
|
|
@@ -313,6 +314,8 @@ class CrossrefClient:
|
|
|
313
314
|
consecutive calls have failed, further calls raise immediately so a
|
|
314
315
|
full outage costs a few timeouts, not one per remaining reference.
|
|
315
316
|
"""
|
|
317
|
+
if os.environ.get(_OFFLINE_ENV, "").lower() in {"1", "true", "yes"}:
|
|
318
|
+
raise CrossrefUnavailable("Crossref disabled by LATEXTIFY_OFFLINE")
|
|
316
319
|
if self._consecutive_failures >= _BREAKER_THRESHOLD:
|
|
317
320
|
raise CrossrefUnavailable(
|
|
318
321
|
f"skipped after {self._consecutive_failures} consecutive Crossref failures"
|
|
@@ -113,6 +113,12 @@ def is_reference_heading_text(text: str) -> bool:
|
|
|
113
113
|
return _is_heading_paragraph(text)
|
|
114
114
|
|
|
115
115
|
|
|
116
|
+
_SUPPLEMENT_HEADING_RE = re.compile(
|
|
117
|
+
r"^(?:supplement(?:ary|al)?\s+(?:material|information)|supporting\s+information)\s*:?$",
|
|
118
|
+
re.IGNORECASE,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
116
122
|
def _has_list_numbering(paragraph) -> bool:
|
|
117
123
|
"""True when a paragraph carries real Word list numbering (``w:numPr``).
|
|
118
124
|
|
|
@@ -139,6 +145,7 @@ class ReferenceList:
|
|
|
139
145
|
|
|
140
146
|
heading: str | None
|
|
141
147
|
references: list[ReferenceItem] = field(default_factory=list)
|
|
148
|
+
supplement: bool = False
|
|
142
149
|
|
|
143
150
|
@property
|
|
144
151
|
def found(self) -> bool:
|
|
@@ -155,46 +162,57 @@ def segment_reference_list(docx_path: Path | str) -> ReferenceList:
|
|
|
155
162
|
``word/document.xml`` to read) dispatches to
|
|
156
163
|
:mod:`latextify.citations.reflist_nondocx` instead.
|
|
157
164
|
"""
|
|
165
|
+
lists = segment_reference_lists(docx_path)
|
|
166
|
+
return lists[0] if lists else ReferenceList(heading=None)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def segment_reference_lists(docx_path: Path | str) -> tuple[ReferenceList, ...]:
|
|
170
|
+
"""Return each typed reference list, tagged as main or supplementary."""
|
|
158
171
|
if not is_docx(docx_path):
|
|
159
172
|
from .reflist_nondocx import segment_reference_list_from_manuscript
|
|
160
173
|
|
|
161
|
-
|
|
174
|
+
found = segment_reference_list_from_manuscript(Path(docx_path))
|
|
175
|
+
return (found,) if found.heading is not None else ()
|
|
162
176
|
root = etree.fromstring(read_document_xml(docx_path))
|
|
163
177
|
paragraphs = list(root.iter(_q("p")))
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
178
|
+
texts = [_paragraph_text(paragraph).strip() for paragraph in paragraphs]
|
|
179
|
+
supplement_at = next(
|
|
180
|
+
(index for index, text in enumerate(texts) if _SUPPLEMENT_HEADING_RE.match(text)), None
|
|
181
|
+
)
|
|
182
|
+
headings = [index for index, text in enumerate(texts) if _is_heading_paragraph(text)]
|
|
183
|
+
lists: list[ReferenceList] = []
|
|
184
|
+
for position, heading_index in enumerate(headings):
|
|
185
|
+
stops = [len(paragraphs)]
|
|
186
|
+
if position + 1 < len(headings):
|
|
187
|
+
stops.append(headings[position + 1])
|
|
188
|
+
if supplement_at is not None and heading_index < supplement_at:
|
|
189
|
+
stops.append(supplement_at)
|
|
190
|
+
references: list[ReferenceItem] = []
|
|
191
|
+
auto_number = 0
|
|
192
|
+
for paragraph, text in zip(
|
|
193
|
+
paragraphs[heading_index + 1 : min(stops)],
|
|
194
|
+
texts[heading_index + 1 : min(stops)],
|
|
195
|
+
strict=True,
|
|
196
|
+
):
|
|
197
|
+
if not text:
|
|
198
|
+
continue
|
|
199
|
+
match = _LIST_NUMBER_RE.match(text)
|
|
200
|
+
if match:
|
|
201
|
+
number = int(match.group("br") or match.group("pr") or match.group("dot"))
|
|
202
|
+
references.append(ReferenceItem(text=text[match.end() :].strip(), number=number))
|
|
203
|
+
elif _has_list_numbering(paragraph):
|
|
204
|
+
auto_number += 1
|
|
205
|
+
references.append(ReferenceItem(text=text, number=auto_number))
|
|
206
|
+
else:
|
|
207
|
+
references.append(ReferenceItem(text=text, number=None))
|
|
208
|
+
lists.append(
|
|
209
|
+
ReferenceList(
|
|
210
|
+
heading=texts[heading_index].rstrip(":").strip(),
|
|
211
|
+
references=references,
|
|
212
|
+
supplement=supplement_at is not None and heading_index > supplement_at,
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
return tuple(lists)
|
|
198
216
|
|
|
199
217
|
|
|
200
218
|
# --- reconstruction ----------------------------------------------------------
|
|
@@ -217,6 +235,7 @@ class PlaintextResult:
|
|
|
217
235
|
author_year_keys: dict[tuple[str, str], list[str]] = field(default_factory=dict)
|
|
218
236
|
heading: str | None = None
|
|
219
237
|
has_reference_list: bool = False
|
|
238
|
+
supplement: bool = False
|
|
220
239
|
|
|
221
240
|
@property
|
|
222
241
|
def report(self) -> ReconciliationReport:
|
|
@@ -256,6 +275,10 @@ def reconstruct_citations(
|
|
|
256
275
|
if owns_client:
|
|
257
276
|
client.close()
|
|
258
277
|
|
|
278
|
+
return _result_from_outcome(reflist, outcome)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _result_from_outcome(reflist: ReferenceList, outcome) -> PlaintextResult:
|
|
259
282
|
keys_by_number = {
|
|
260
283
|
record.ref_number: record.key for record in outcome.records if record.ref_number is not None
|
|
261
284
|
}
|
|
@@ -266,4 +289,33 @@ def reconstruct_citations(
|
|
|
266
289
|
author_year_keys=build_author_year_index(outcome.entries),
|
|
267
290
|
heading=reflist.heading,
|
|
268
291
|
has_reference_list=True,
|
|
292
|
+
supplement=reflist.supplement,
|
|
269
293
|
)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def reconstruct_citation_lists(
|
|
297
|
+
docx_path: Path | str,
|
|
298
|
+
*,
|
|
299
|
+
mailto: str | None = None,
|
|
300
|
+
threshold: float = reconcile.DEFAULT_THRESHOLD,
|
|
301
|
+
client: crossref.CrossrefClient | None = None,
|
|
302
|
+
bib_entries: list[RefEntry] | None = None,
|
|
303
|
+
) -> tuple[PlaintextResult, ...]:
|
|
304
|
+
"""Reconstruct every typed reference list in a merged main+SI manuscript."""
|
|
305
|
+
reflists = tuple(item for item in segment_reference_lists(docx_path) if item.found)
|
|
306
|
+
if not reflists:
|
|
307
|
+
return ()
|
|
308
|
+
owns_client = client is None
|
|
309
|
+
if client is None:
|
|
310
|
+
client = crossref.CrossrefClient(mailto=mailto)
|
|
311
|
+
results: list[PlaintextResult] = []
|
|
312
|
+
try:
|
|
313
|
+
for reflist in reflists:
|
|
314
|
+
outcome = reconcile.reconcile_references(
|
|
315
|
+
reflist.references, client, threshold=threshold, bib_entries=bib_entries
|
|
316
|
+
)
|
|
317
|
+
results.append(_result_from_outcome(reflist, outcome))
|
|
318
|
+
finally:
|
|
319
|
+
if owns_client:
|
|
320
|
+
client.close()
|
|
321
|
+
return tuple(results)
|