Initial ChatGPT code and docs.

This commit is contained in:
Wesley R. Elsberry 2025-10-04 22:29:06 -04:00
parent 93368211de
commit 96235c83db
40 changed files with 1061 additions and 0 deletions

25
.forgejo/workflows/build.yml Executable file
View File

@ -0,0 +1,25 @@
name: Build PolyPaper (Forgejo)
on: [push, pull_request, workflow_dispatch]
jobs:
build:
runs-on: docker
strategy: { fail-fast: false, matrix: { venue: [arxiv, ieee, elsevier, acm] } }
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -f Dockerfile.full -t polypaper:full .
- name: Validate metadata
run: docker run --rm -v $GITHUB_WORKSPACE:/work -w /work polypaper:full bash -lc 'chmod +x scripts/check_metadata.py && scripts/check_metadata.py'
- name: Build PDFs and HTML (${{ matrix.venue }})
run: |
docker run --rm -v $GITHUB_WORKSPACE:/work -w /work polypaper:full bash -lc 'make VENUE=${{ matrix.venue }}'
docker run --rm -v $GITHUB_WORKSPACE:/work -w /work polypaper:full bash -lc 'make supplement VENUE=${{ matrix.venue }}'
docker run --rm -v $GITHUB_WORKSPACE:/work -w /work polypaper:full bash -lc 'make camera-ready VENUE=${{ matrix.venue }}'
docker run --rm -v $GITHUB_WORKSPACE:/work -w /work polypaper:full bash -lc 'make site VENUE=${{ matrix.venue }}'
- name: Archive outputs
run: |
mkdir -p artifacts/${{ matrix.venue }}
mv paper-${{ matrix.venue }}.pdf artifacts/${{ matrix.venue }}/ || true
mv paper-supplement-${{ matrix.venue }}.pdf artifacts/${{ matrix.venue }}/ || true
mv paper-camera-ready-${{ matrix.venue }}.pdf artifacts/${{ matrix.venue }}/ || true
cp -a public artifacts/${{ matrix.venue }}/ || true

14
CITATION.cff Executable file
View File

@ -0,0 +1,14 @@
cff-version: 1.2.0
message: "If you use this project, please cite it as below."
title: "PolyPaper"
authors:
- family-names: "Elsberry"
given-names: "Wesley R."
orcid: "https://orcid.org/0000-0000-0000-0000"
identifiers:
- type: doi
value: 10.5281/zenodo.0000000
repository-code: "https://example.org/polypaper"
license: MIT
version: "0.1.0"
date-released: "2025-10-04"

3
CODE_OF_CONDUCT.md Executable file
View File

@ -0,0 +1,3 @@
# Code of Conduct
We follow the Contributor Covenant. Be kind.

3
CONTRIBUTING.md Executable file
View File

@ -0,0 +1,3 @@
# Contributing
Thanks for considering a contribution! See issues for ideas.

10
Dockerfile.full Executable file
View File

@ -0,0 +1,10 @@
# syntax=docker/dockerfile:1
FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive PYTHONUNBUFFERED=1 LC_ALL=C.UTF-8 LANG=C.UTF-8
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl git make emacs-nox python3 python3-pip python3-matplotlib \
latexmk biber texlive-full && rm -rf /var/lib/apt/lists/*
RUN useradd -m -u 1000 builder
WORKDIR /work
USER builder
CMD ["bash", "-lc", "make VENUE=arxiv"]

14
Dockerfile.tinytex Executable file
View File

@ -0,0 +1,14 @@
# syntax=docker/dockerfile:1
FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive PYTHONUNBUFFERED=1 LC_ALL=C.UTF-8 LANG=C.UTF-8 PATH=/opt/TinyTeX/bin/x86_64-linux:$PATH
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl git make emacs-nox python3 python3-pip python3-matplotlib perl && \
rm -rf /var/lib/apt/lists/*
RUN curl -sL https://yihui.org/tinytex/install-unx.sh | sh -s - --admin --no-path && \
/opt/TinyTeX/bin/*/tlmgr path add && \
tlmgr install latexmk biber latex-bin biblatex hyperref geometry microtype etoolbox enumitem xcolor listings IEEEtran acmart elsarticle && \
tlmgr path add
RUN useradd -m -u 1000 builder
WORKDIR /work
USER builder
CMD ["bash", "-lc", "make VENUE=arxiv"]

40
Makefile Executable file
View File

@ -0,0 +1,40 @@
VENUE ?= arxiv
JOB ?= paper-$(VENUE)
all: $(JOB).pdf
$(JOB).pdf: paper.org setup/venue-$(VENUE).org build.el biblio.bib
emacs -Q --batch -l build.el --eval "(setenv \"VENUE\" \"$(VENUE)\")" --funcall wes/export-pdf-cli
site:
emacs -Q --batch -l build.el --eval "(setenv \"VENUE\" \"$(VENUE)\")" --funcall wes/publish-site-cli
@echo "HTML written to public/ (paper.org → public/paper.html)"
supplement:
emacs -Q --batch -l build.el --eval "(setenv \"VENUE\" \"$(VENUE)\")" --funcall wes/export-supplement-pdf-cli
camera-ready:
emacs -Q --batch -l build.el --eval "(setenv \"VENUE\" \"$(VENUE)\")" --funcall wes/export-camera-ready-pdf-cli
clean:
latexmk -C
rm -f paper-*.pdf
# Dockerized builds
DOCKER_IMAGE ?= polypaper:full
DOCKERFILE ?= Dockerfile.full
build-image:
docker build -f $(DOCKERFILE) -t $(DOCKER_IMAGE) .
docker:
docker run --rm -u $$(id -u):$$(id -g) -v $$(pwd):/work -w /work $(DOCKER_IMAGE) bash -lc "make VENUE=$(VENUE)"
docker-supplement:
docker run --rm -u $$(id -u):$$(id -g) -v $$(pwd):/work -w /work $(DOCKER_IMAGE) bash -lc "make supplement VENUE=$(VENUE)"
docker-camera-ready:
docker run --rm -u $$(id -u):$$(id -g) -v $$(pwd):/work -w /work $(DOCKER_IMAGE) bash -lc "make camera-ready VENUE=$(VENUE)"
docker-site:
docker run --rm -u $$(id -u):$$(id -g) -v $$(pwd):/work -w /work $(DOCKER_IMAGE) bash -lc "make site VENUE=$(VENUE)"

9
biblio.bib Executable file
View File

@ -0,0 +1,9 @@
@article{demo2020,
title = {A Demo Reference for PolyPaper},
author = {Example, Ada and Example, Alan},
journal = {Journal of Demos},
year = {2020},
volume = {1},
number = {1},
pages = {1--10}
}

91
build.el Executable file
View File

@ -0,0 +1,91 @@
;; Batch export & site publish
(setq org-confirm-babel-evaluate nil)
(setq org-latex-pdf-process '("latexmk -pdf -interaction=nonstopmode -shell-escape %f"))
(defun wes/export-pdf (&optional venue)
(let* ((v (or venue (or (getenv "VENUE") "arxiv")))
(outfile (format "paper-%s.pdf" v)))
(with-current-buffer (find-file-noselect "paper.org")
(setenv "VENUE" v)
(org-latex-export-to-pdf nil nil nil t)
(when (file-exists-p "paper.pdf")
(rename-file "paper.pdf" outfile t))
(message "Wrote %s" outfile))))
(defun wes/export-pdf-cli () (wes/export-pdf))
(require 'ox) (require 'ox-publish)
(defun wes/publish-site (&optional venue)
(let* ((v (or venue (or (getenv "VENUE") "arxiv"))))
(setenv "VENUE" v)
(setq org-publish-project-alist
`(("site-html"
:base-directory "."
:publishing-directory "public"
:publishing-function org-html-publish-to-html
:with-author t :with-toc t :section-numbers t
:html-head "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
:recursive nil
:exclude "setup/\\|code/\\|figs/\\|public/\\|data/"
:include ("paper.org")
:select-tags ("export")
:exclude-tags ,(if (member v '("ieee" "acm"))
'("noexport" "appendix" "notes")
'("noexport")))))
(org-publish "site-html" t)
(message "Published HTML site for %s" v)))
(defun wes/publish-site-cli () (wes/publish-site))
(defun wes--cam-tight-latex ()
(mapconcat #'identity
'("\\usepackage{microtype}"
"\\flushbottom"
"\\setlength{\\textfloatsep}{10pt}"
"\\setlength{\\floatsep}{8pt}"
"\\setlength{\\intextsep}{8pt}"
"\\usepackage{enumitem}"
"\\setlist{nosep}"
"\\usepackage{balance}")
"\n"))
(defun wes/export-camera-ready-pdf (&optional venue)
(let* ((v (or venue (or (getenv "VENUE") "arxiv")))
(base-setup (expand-file-name (format "setup/venue-%s.org" v)))
(tmp-name (format ".tmp-venue-%s-camera" v))
(tmp-setup (expand-file-name (format "setup/%s.org" tmp-name)))
(outfile (format "paper-camera-ready-%s.pdf" v)))
(unless (file-exists-p base-setup)
(error "Base setup file not found for venue %s" v))
(with-temp-buffer
(insert-file-contents base-setup)
(goto-char (point-max))
(insert "\n# Camera-ready overrides\n#+BIND: paper-anon nil\n")
(insert (format "#+latex_header: %s\n" (wes--cam-tight-latex)))
(write-region (point-min) (point-max) tmp-setup))
(unwind-protect
(progn
(with-current-buffer (find-file-noselect "paper.org")
(setenv "VENUE" tmp-name)
(org-latex-export-to-pdf nil nil nil t)
(when (file-exists-p "paper.pdf")
(rename-file "paper.pdf" outfile t))
(message "Wrote %s" outfile)))
(ignore-errors (delete-file tmp-setup)))))
(defun wes/export-camera-ready-pdf-cli () (wes/export-camera-ready-pdf))
(defun wes/export-supplement-pdf (&optional venue)
(let* ((v (or venue (or (getenv "VENUE") "arxiv")))
(outfile (format "paper-supplement-%s.pdf" v))
(paper-short nil) (paper-anon nil) (paper-limit-figs nil)
(org-export-select-tags '("export"))
(org-export-exclude-tags '("noexport")))
(with-current-buffer (find-file-noselect "paper.org")
(setenv "VENUE" v)
(org-latex-export-to-pdf nil nil nil t)
(when (file-exists-p "paper.pdf")
(rename-file "paper.pdf" outfile t))
(message "Wrote %s" outfile))))
(defun wes/export-supplement-pdf-cli () (wes/export-supplement-pdf))

14
docker-compose.yml Executable file
View File

@ -0,0 +1,14 @@
services:
paper:
build: { context: ., dockerfile: Dockerfile.full }
image: polypaper:full
working_dir: /work
volumes: [ ".:/work" ]
command: bash -lc "make VENUE=${VENUE:-arxiv}"
paper-tiny:
build: { context: ., dockerfile: Dockerfile.tinytex }
image: polypaper:tinytex
working_dir: /work
volumes: [ ".:/work" ]
command: bash -lc "make VENUE=${VENUE:-arxiv}"

96
paper.org Executable file
View File

@ -0,0 +1,96 @@
# -*- org-export-allow-bind: t; -*-
#+TITLE: Demo Paper (PolyPaper)
#+AUTHOR: {{{NAME}}}
#+FILETAGS: :export:
#+OPTIONS: toc:2 num:t broken-links:auto
#+PROPERTY: header-args:python :session py :results file graphics :exports results :cache yes
#+PROPERTY: header-args: :mkdirp yes
#+MACRO: VENUE (eval (or (getenv "VENUE") "arxiv"))
#+SETUPFILE: setup/venue-{{{VENUE}}}.org
#+MACRO: IF-SHORT (eval (when (bound-and-true-p paper-short) ""))
#+MACRO: IF-LONG (eval (unless (bound-and-true-p paper-short) ""))
#+MACRO: IF-ANON (eval (when (bound-and-true-p paper-anon) ""))
#+MACRO: IF-LIMITFIG (eval (when (bound-and-true-p paper-limit-figs) ""))
* Abstract :export:
PolyPaper demonstrates building venue-specific PDFs from a single Org source.
* Introduction :export:
- One literate Org source.
- Venue profiles in =setup/=.
- Reproducible figures via Org Babel.
- Conditional content via tags/macros.
{{{IF-ANON}}}
Double-blind mode replaces author name and redacts URLs.
{{{end}}}
* Methods :export:
We generate a simple figure via Python/Matplotlib.
#+name: fig-core
#+begin_src python :var seed=123
import numpy as np, matplotlib.pyplot as plt
np.random.seed(seed)
x=np.linspace(0,10,200); y=np.sin(x)+0.15*np.random.randn(200)
plt.figure(); plt.plot(x,y); plt.xlabel("x"); plt.ylabel("y")
out="figs/sine-core.pdf"; plt.savefig(out, bbox_inches="tight"); out
#+end_src
#+caption: Core figure used in all venues.
#+attr_latex: :width 0.8\linewidth
#+RESULTS: fig-core
file:figs/sine-core.pdf
* Results :export:
Here is a citation [cite:@demo2020].
{{{IF-LIMITFIG}}}
** Extra figures (excluded when figure-limited) :noexport:
Text here won't export when =paper-limit-figs=t=.
{{{end}}}
{{{IF-LONG}}}
* Appendix :export:
Extended content appears only for non-short profiles.
{{{end}}}
* Data and Code Availability :export:
# This section is auto-generated from repo_meta.json.
#+name: availability
#+begin_src python :var meta_file="repo_meta.json" :results raw :exports results :cache yes
import json, textwrap
m=json.load(open(meta_file, "r", encoding="utf-8"))
lines = []
lines.append("*Data and Code Availability*")
if m.get("url"):
lines.append(f"- Code repository: [[{m['url']}][{m.get('repo_name', m['url'])}]]")
if m.get("license"):
lines.append(f"- License: {m['license']} (see LICENSE file)")
if m.get("code_archive_doi"):
lines.append(f"- Code archive DOI: {m['code_archive_doi']}")
if m.get("data_availability"):
lines.append(f"- Data availability: {m['data_availability']}")
dls = m.get("data_links", [])
if dls:
lines.append("- Data links:")
for d in dls:
if 'url' in d:
label = d.get('label', d['url'])
lines.append(f" - [[{d['url']}][{label}]]")
steps = m.get("repro_instructions", [])
if steps:
lines.append("- Reproducibility:")
for st in steps:
lines.append(f" - {st}")
if m.get("contact_email"):
lines.append(f"- Contact: {m['contact_email']}")
print("\n".join(lines))
#+end_src
#+RESULTS: availability
* References :export:

11
polypaper-logo.svg Executable file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 200">
<rect width="640" height="200" fill="#0b132b"/>
<g font-family="Segoe UI, Roboto, Helvetica, Arial, sans-serif" font-weight="700">
<text x="40" y="110" font-size="72" fill="#e0fbfc">Poly</text>
<text x="230" y="110" font-size="72" fill="#98c1d9">Paper</text>
</g>
<g font-family="Segoe UI, Roboto, Helvetica, Arial, sans-serif" font-weight="400">
<text x="42" y="160" font-size="22" fill="#e0fbfc">One manuscript, many venues — Org-mode → LaTeX, reproducibly.</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 607 B

View File

@ -0,0 +1,25 @@
name: Build PolyPaper (Forgejo)
on: [push, pull_request, workflow_dispatch]
jobs:
build:
runs-on: docker
strategy: { fail-fast: false, matrix: { venue: [arxiv, ieee, elsevier, acm] } }
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -f Dockerfile.full -t polypaper:full .
- name: Validate metadata
run: docker run --rm -v $GITHUB_WORKSPACE:/work -w /work polypaper:full bash -lc 'chmod +x scripts/check_metadata.py && scripts/check_metadata.py'
- name: Build PDFs and HTML (${{ matrix.venue }})
run: |
docker run --rm -v $GITHUB_WORKSPACE:/work -w /work polypaper:full bash -lc 'make VENUE=${{ matrix.venue }}'
docker run --rm -v $GITHUB_WORKSPACE:/work -w /work polypaper:full bash -lc 'make supplement VENUE=${{ matrix.venue }}'
docker run --rm -v $GITHUB_WORKSPACE:/work -w /work polypaper:full bash -lc 'make camera-ready VENUE=${{ matrix.venue }}'
docker run --rm -v $GITHUB_WORKSPACE:/work -w /work polypaper:full bash -lc 'make site VENUE=${{ matrix.venue }}'
- name: Archive outputs
run: |
mkdir -p artifacts/${{ matrix.venue }}
mv paper-${{ matrix.venue }}.pdf artifacts/${{ matrix.venue }}/ || true
mv paper-supplement-${{ matrix.venue }}.pdf artifacts/${{ matrix.venue }}/ || true
mv paper-camera-ready-${{ matrix.venue }}.pdf artifacts/${{ matrix.venue }}/ || true
cp -a public artifacts/${{ matrix.venue }}/ || true

31
polypaper/.github/workflows/build.yml vendored Executable file
View File

@ -0,0 +1,31 @@
name: Build PolyPaper
on: [push, pull_request, workflow_dispatch]
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: { venue: [arxiv, ieee, elsevier, acm] }
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -f Dockerfile.full -t polypaper:full .
- name: Validate metadata (in container)
run: docker run --rm -v ${{ github.workspace }}:/work -w /work polypaper:full bash -lc 'chmod +x scripts/check_metadata.py && scripts/check_metadata.py'
- name: Build PDFs and HTML (${{ matrix.venue }})
run: |
docker run --rm -v ${{ github.workspace }}:/work -w /work polypaper:full bash -lc 'make VENUE=${{ matrix.venue }}'
docker run --rm -v ${{ github.workspace }}:/work -w /work polypaper:full bash -lc 'make supplement VENUE=${{ matrix.venue }}'
docker run --rm -v ${{ github.workspace }}:/work -w /work polypaper:full bash -lc 'make camera-ready VENUE=${{ matrix.venue }}'
docker run --rm -v ${{ github.workspace }}:/work -w /work polypaper:full bash -lc 'make site VENUE=${{ matrix.venue }}'
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: polypaper-${{ matrix.venue }}
path: |
paper-${{ matrix.venue }}.pdf
paper-supplement-${{ matrix.venue }}.pdf
paper-camera-ready-${{ matrix.venue }}.pdf
public/paper.html

22
polypaper/.gitignore vendored Executable file
View File

@ -0,0 +1,22 @@
.DS_Store
*.aux
*.bbl
*.bcf
*.blg
*.fdb_latexmk
*.fls
*.log
*.out
*.run.xml
*.toc
*.synctex.gz
build/
public/
figs/
code/
__pycache__/
*.pyc
.venv/
env/
*~
\#*\#

14
polypaper/CITATION.cff Executable file
View File

@ -0,0 +1,14 @@
cff-version: 1.2.0
message: "If you use this project, please cite it as below."
title: "PolyPaper"
authors:
- family-names: "Elsberry"
given-names: "Wesley R."
orcid: "https://orcid.org/0000-0000-0000-0000"
identifiers:
- type: doi
value: 10.5281/zenodo.0000000
repository-code: "https://example.org/polypaper"
license: MIT
version: "0.1.0"
date-released: "2025-10-04"

3
polypaper/CODE_OF_CONDUCT.md Executable file
View File

@ -0,0 +1,3 @@
# Code of Conduct
We follow the Contributor Covenant. Be kind.

3
polypaper/CONTRIBUTING.md Executable file
View File

@ -0,0 +1,3 @@
# Contributing
Thanks for considering a contribution! See issues for ideas.

10
polypaper/Dockerfile.full Executable file
View File

@ -0,0 +1,10 @@
# syntax=docker/dockerfile:1
FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive PYTHONUNBUFFERED=1 LC_ALL=C.UTF-8 LANG=C.UTF-8
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl git make emacs-nox python3 python3-pip python3-matplotlib \
latexmk biber texlive-full && rm -rf /var/lib/apt/lists/*
RUN useradd -m -u 1000 builder
WORKDIR /work
USER builder
CMD ["bash", "-lc", "make VENUE=arxiv"]

14
polypaper/Dockerfile.tinytex Executable file
View File

@ -0,0 +1,14 @@
# syntax=docker/dockerfile:1
FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive PYTHONUNBUFFERED=1 LC_ALL=C.UTF-8 LANG=C.UTF-8 PATH=/opt/TinyTeX/bin/x86_64-linux:$PATH
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl git make emacs-nox python3 python3-pip python3-matplotlib perl && \
rm -rf /var/lib/apt/lists/*
RUN curl -sL https://yihui.org/tinytex/install-unx.sh | sh -s - --admin --no-path && \
/opt/TinyTeX/bin/*/tlmgr path add && \
tlmgr install latexmk biber latex-bin biblatex hyperref geometry microtype etoolbox enumitem xcolor listings IEEEtran acmart elsarticle && \
tlmgr path add
RUN useradd -m -u 1000 builder
WORKDIR /work
USER builder
CMD ["bash", "-lc", "make VENUE=arxiv"]

21
polypaper/LICENSE Executable file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Your Name
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

40
polypaper/Makefile Executable file
View File

@ -0,0 +1,40 @@
VENUE ?= arxiv
JOB ?= paper-$(VENUE)
all: $(JOB).pdf
$(JOB).pdf: paper.org setup/venue-$(VENUE).org build.el biblio.bib
emacs -Q --batch -l build.el --eval "(setenv \"VENUE\" \"$(VENUE)\")" --funcall wes/export-pdf-cli
site:
emacs -Q --batch -l build.el --eval "(setenv \"VENUE\" \"$(VENUE)\")" --funcall wes/publish-site-cli
@echo "HTML written to public/ (paper.org → public/paper.html)"
supplement:
emacs -Q --batch -l build.el --eval "(setenv \"VENUE\" \"$(VENUE)\")" --funcall wes/export-supplement-pdf-cli
camera-ready:
emacs -Q --batch -l build.el --eval "(setenv \"VENUE\" \"$(VENUE)\")" --funcall wes/export-camera-ready-pdf-cli
clean:
latexmk -C
rm -f paper-*.pdf
# Dockerized builds
DOCKER_IMAGE ?= polypaper:full
DOCKERFILE ?= Dockerfile.full
build-image:
docker build -f $(DOCKERFILE) -t $(DOCKER_IMAGE) .
docker:
docker run --rm -u $$(id -u):$$(id -g) -v $$(pwd):/work -w /work $(DOCKER_IMAGE) bash -lc "make VENUE=$(VENUE)"
docker-supplement:
docker run --rm -u $$(id -u):$$(id -g) -v $$(pwd):/work -w /work $(DOCKER_IMAGE) bash -lc "make supplement VENUE=$(VENUE)"
docker-camera-ready:
docker run --rm -u $$(id -u):$$(id -g) -v $$(pwd):/work -w /work $(DOCKER_IMAGE) bash -lc "make camera-ready VENUE=$(VENUE)"
docker-site:
docker run --rm -u $$(id -u):$$(id -g) -v $$(pwd):/work -w /work $(DOCKER_IMAGE) bash -lc "make site VENUE=$(VENUE)"

49
polypaper/README.md Executable file
View File

@ -0,0 +1,49 @@
<div align="center">
<img src="polypaper-logo.svg" alt="PolyPaper logo" width="540"/>
**PolyPaper**
*One manuscript, many venues — Org-mode → LaTeX, reproducibly.*
[![Build (GitHub Actions)](https://img.shields.io/badge/build-GitHub_Actions-blue)](#)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
</div>
---
# PolyPaper: Org → Multi-venue LaTeX Toolkit
This is a literate, *single-source* paper template that exports venue-specific PDFs and HTML.
## Requirements
- Emacs with Org-mode
- TeX toolchain (`latexmk`, `biber`)
- Python + Matplotlib
## Quick start
```bash
make VENUE=arxiv
make VENUE=ieee
make VENUE=elsevier
make VENUE=acm
make supplement VENUE=arxiv
make camera-ready VENUE=ieee
make site VENUE=arxiv
```
## Data & Code Availability Appendix
Edit `repo_meta.json` and rebuild. Validate with:
```bash
scripts/check_metadata.py
```
## Dockerized builds
```bash
make build-image DOCKERFILE=Dockerfile.full DOCKER_IMAGE=polypaper:full
make docker VENUE=arxiv
```
## Continuous Integration
- GitHub: `.github/workflows/build.yml`
- Forgejo: `.forgejo/workflows/build.yml`

9
polypaper/biblio.bib Executable file
View File

@ -0,0 +1,9 @@
@article{demo2020,
title = {A Demo Reference for PolyPaper},
author = {Example, Ada and Example, Alan},
journal = {Journal of Demos},
year = {2020},
volume = {1},
number = {1},
pages = {1--10}
}

91
polypaper/build.el Executable file
View File

@ -0,0 +1,91 @@
;; Batch export & site publish
(setq org-confirm-babel-evaluate nil)
(setq org-latex-pdf-process '("latexmk -pdf -interaction=nonstopmode -shell-escape %f"))
(defun wes/export-pdf (&optional venue)
(let* ((v (or venue (or (getenv "VENUE") "arxiv")))
(outfile (format "paper-%s.pdf" v)))
(with-current-buffer (find-file-noselect "paper.org")
(setenv "VENUE" v)
(org-latex-export-to-pdf nil nil nil t)
(when (file-exists-p "paper.pdf")
(rename-file "paper.pdf" outfile t))
(message "Wrote %s" outfile))))
(defun wes/export-pdf-cli () (wes/export-pdf))
(require 'ox) (require 'ox-publish)
(defun wes/publish-site (&optional venue)
(let* ((v (or venue (or (getenv "VENUE") "arxiv"))))
(setenv "VENUE" v)
(setq org-publish-project-alist
`(("site-html"
:base-directory "."
:publishing-directory "public"
:publishing-function org-html-publish-to-html
:with-author t :with-toc t :section-numbers t
:html-head "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
:recursive nil
:exclude "setup/\\|code/\\|figs/\\|public/\\|data/"
:include ("paper.org")
:select-tags ("export")
:exclude-tags ,(if (member v '("ieee" "acm"))
'("noexport" "appendix" "notes")
'("noexport")))))
(org-publish "site-html" t)
(message "Published HTML site for %s" v)))
(defun wes/publish-site-cli () (wes/publish-site))
(defun wes--cam-tight-latex ()
(mapconcat #'identity
'("\\usepackage{microtype}"
"\\flushbottom"
"\\setlength{\\textfloatsep}{10pt}"
"\\setlength{\\floatsep}{8pt}"
"\\setlength{\\intextsep}{8pt}"
"\\usepackage{enumitem}"
"\\setlist{nosep}"
"\\usepackage{balance}")
"\n"))
(defun wes/export-camera-ready-pdf (&optional venue)
(let* ((v (or venue (or (getenv "VENUE") "arxiv")))
(base-setup (expand-file-name (format "setup/venue-%s.org" v)))
(tmp-name (format ".tmp-venue-%s-camera" v))
(tmp-setup (expand-file-name (format "setup/%s.org" tmp-name)))
(outfile (format "paper-camera-ready-%s.pdf" v)))
(unless (file-exists-p base-setup)
(error "Base setup file not found for venue %s" v))
(with-temp-buffer
(insert-file-contents base-setup)
(goto-char (point-max))
(insert "\n# Camera-ready overrides\n#+BIND: paper-anon nil\n")
(insert (format "#+latex_header: %s\n" (wes--cam-tight-latex)))
(write-region (point-min) (point-max) tmp-setup))
(unwind-protect
(progn
(with-current-buffer (find-file-noselect "paper.org")
(setenv "VENUE" tmp-name)
(org-latex-export-to-pdf nil nil nil t)
(when (file-exists-p "paper.pdf")
(rename-file "paper.pdf" outfile t))
(message "Wrote %s" outfile)))
(ignore-errors (delete-file tmp-setup)))))
(defun wes/export-camera-ready-pdf-cli () (wes/export-camera-ready-pdf))
(defun wes/export-supplement-pdf (&optional venue)
(let* ((v (or venue (or (getenv "VENUE") "arxiv")))
(outfile (format "paper-supplement-%s.pdf" v))
(paper-short nil) (paper-anon nil) (paper-limit-figs nil)
(org-export-select-tags '("export"))
(org-export-exclude-tags '("noexport")))
(with-current-buffer (find-file-noselect "paper.org")
(setenv "VENUE" v)
(org-latex-export-to-pdf nil nil nil t)
(when (file-exists-p "paper.pdf")
(rename-file "paper.pdf" outfile t))
(message "Wrote %s" outfile))))
(defun wes/export-supplement-pdf-cli () (wes/export-supplement-pdf))

14
polypaper/docker-compose.yml Executable file
View File

@ -0,0 +1,14 @@
services:
paper:
build: { context: ., dockerfile: Dockerfile.full }
image: polypaper:full
working_dir: /work
volumes: [ ".:/work" ]
command: bash -lc "make VENUE=${VENUE:-arxiv}"
paper-tiny:
build: { context: ., dockerfile: Dockerfile.tinytex }
image: polypaper:tinytex
working_dir: /work
volumes: [ ".:/work" ]
command: bash -lc "make VENUE=${VENUE:-arxiv}"

96
polypaper/paper.org Executable file
View File

@ -0,0 +1,96 @@
# -*- org-export-allow-bind: t; -*-
#+TITLE: Demo Paper (PolyPaper)
#+AUTHOR: {{{NAME}}}
#+FILETAGS: :export:
#+OPTIONS: toc:2 num:t broken-links:auto
#+PROPERTY: header-args:python :session py :results file graphics :exports results :cache yes
#+PROPERTY: header-args: :mkdirp yes
#+MACRO: VENUE (eval (or (getenv "VENUE") "arxiv"))
#+SETUPFILE: setup/venue-{{{VENUE}}}.org
#+MACRO: IF-SHORT (eval (when (bound-and-true-p paper-short) ""))
#+MACRO: IF-LONG (eval (unless (bound-and-true-p paper-short) ""))
#+MACRO: IF-ANON (eval (when (bound-and-true-p paper-anon) ""))
#+MACRO: IF-LIMITFIG (eval (when (bound-and-true-p paper-limit-figs) ""))
* Abstract :export:
PolyPaper demonstrates building venue-specific PDFs from a single Org source.
* Introduction :export:
- One literate Org source.
- Venue profiles in =setup/=.
- Reproducible figures via Org Babel.
- Conditional content via tags/macros.
{{{IF-ANON}}}
Double-blind mode replaces author name and redacts URLs.
{{{end}}}
* Methods :export:
We generate a simple figure via Python/Matplotlib.
#+name: fig-core
#+begin_src python :var seed=123
import numpy as np, matplotlib.pyplot as plt
np.random.seed(seed)
x=np.linspace(0,10,200); y=np.sin(x)+0.15*np.random.randn(200)
plt.figure(); plt.plot(x,y); plt.xlabel("x"); plt.ylabel("y")
out="figs/sine-core.pdf"; plt.savefig(out, bbox_inches="tight"); out
#+end_src
#+caption: Core figure used in all venues.
#+attr_latex: :width 0.8\linewidth
#+RESULTS: fig-core
file:figs/sine-core.pdf
* Results :export:
Here is a citation [cite:@demo2020].
{{{IF-LIMITFIG}}}
** Extra figures (excluded when figure-limited) :noexport:
Text here won't export when =paper-limit-figs=t=.
{{{end}}}
{{{IF-LONG}}}
* Appendix :export:
Extended content appears only for non-short profiles.
{{{end}}}
* Data and Code Availability :export:
# This section is auto-generated from repo_meta.json.
#+name: availability
#+begin_src python :var meta_file="repo_meta.json" :results raw :exports results :cache yes
import json, textwrap
m=json.load(open(meta_file, "r", encoding="utf-8"))
lines = []
lines.append("*Data and Code Availability*")
if m.get("url"):
lines.append(f"- Code repository: [[{m['url']}][{m.get('repo_name', m['url'])}]]")
if m.get("license"):
lines.append(f"- License: {m['license']} (see LICENSE file)")
if m.get("code_archive_doi"):
lines.append(f"- Code archive DOI: {m['code_archive_doi']}")
if m.get("data_availability"):
lines.append(f"- Data availability: {m['data_availability']}")
dls = m.get("data_links", [])
if dls:
lines.append("- Data links:")
for d in dls:
if 'url' in d:
label = d.get('label', d['url'])
lines.append(f" - [[{d['url']}][{label}]]")
steps = m.get("repro_instructions", [])
if steps:
lines.append("- Reproducibility:")
for st in steps:
lines.append(f" - {st}")
if m.get("contact_email"):
lines.append(f"- Contact: {m['contact_email']}")
print("\n".join(lines))
#+end_src
#+RESULTS: availability
* References :export:

11
polypaper/polypaper-logo.svg Executable file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 200">
<rect width="640" height="200" fill="#0b132b"/>
<g font-family="Segoe UI, Roboto, Helvetica, Arial, sans-serif" font-weight="700">
<text x="40" y="110" font-size="72" fill="#e0fbfc">Poly</text>
<text x="230" y="110" font-size="72" fill="#98c1d9">Paper</text>
</g>
<g font-family="Segoe UI, Roboto, Helvetica, Arial, sans-serif" font-weight="400">
<text x="42" y="160" font-size="22" fill="#e0fbfc">One manuscript, many venues — Org-mode → LaTeX, reproducibly.</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 607 B

36
polypaper/repo_meta.json Executable file
View File

@ -0,0 +1,36 @@
{
"brand": "PolyPaper",
"repo_name": "PolyPaper",
"url": "https://example.org/polypaper",
"contact_email": "you@example.org",
"license": "MIT",
"code_archive_doi": "10.5281/zenodo.0000000",
"data_availability": "All data needed to evaluate the conclusions are available within the paper and its Supplementary Materials.",
"data_links": [
{
"label": "Zenodo dataset",
"url": "https://zenodo.org/record/0000000"
},
{
"label": "OSF project",
"url": "https://osf.io/xxxxx/"
}
],
"software_env": {
"python": ">=3.10",
"packages": [
"matplotlib",
"numpy"
],
"latex": [
"latexmk",
"biber"
]
},
"repro_instructions": [
"Install TeX (with latexmk and biber).",
"Ensure Python and Matplotlib are installed.",
"Run `make VENUE=arxiv` to build the paper.",
"Run `make supplement VENUE=arxiv` to build the supplement."
]
}

View File

@ -0,0 +1,10 @@
#!/usr/bin/env python3
import sys, json, os
META = "repo_meta.json"
REQ = ["url", "license"]
def fail(msg): print(f"[meta-check] {msg}", file=sys.stderr); sys.exit(1)
if not os.path.exists(META): fail(f"{META} not found.")
m = json.load(open(META, "r", encoding="utf-8"))
missing = [k for k in REQ if not m.get(k)]
if missing: fail("Missing required fields: " + ", ".join(missing))
print("[meta-check] repo_meta.json looks OK.")

31
polypaper/setup/venue-acm.org Executable file
View File

@ -0,0 +1,31 @@
#+latex_class: acmart
#+latex_class_options: [sigconf]
#+latex_header: \citestyle{acmauthoryear}
#+latex_header: \usepackage{microtype}
#+latex_header: \usepackage[hidelinks]{hyperref}
#+latex_header: \usepackage[backend=biber,style=acmauthoryear,sorting=nyt]{biblatex}
#+latex_header: \addbibresource{biblio.bib}
#+latex_header: \AtEndDocument{\printbibliography}
#+select_tags: export
#+exclude_tags: noexport appendix notes
#+BIND: paper-short t
#+BIND: paper-anon t
#+BIND: paper-limit-figs t
#+MACRO: NAME (eval (if (bound-and-true-p paper-anon) "Anonymous" "Wesley R. Elsberry"))
# ACM CCS taxonomy placeholders
#+latex_header: \begin{CCSXML}
#+latex_header: <ccs2012>
#+latex_header: <concept>
#+latex_header: <concept_id>10010147.10010257</concept_id>
#+latex_header: <concept_desc>Computing methodologies~Machine learning</concept_desc>
#+latex_header: <concept_significance>500</concept_significance>
#+latex_header: </concept>
#+latex_header: </ccs2012>
#+latex_header: \end{CCSXML}
#+latex_header: \ccsdesc[500]{Computing methodologies~Machine learning}
#+latex_header: \keywords{add, your, keywords}

18
polypaper/setup/venue-arxiv.org Executable file
View File

@ -0,0 +1,18 @@
#+latex_class: article
#+latex_class_options: [10pt]
#+latex_header: \usepackage[margin=1in]{geometry}
#+latex_header: \usepackage{microtype}
#+latex_header: \usepackage[hidelinks]{hyperref}
#+latex_header: \usepackage[backend=biber,style=authoryear,maxbibnames=99]{biblatex}
#+latex_header: \addbibresource{biblio.bib}
#+latex_header: \AtEndDocument{\printbibliography}
#+select_tags: export
#+exclude_tags: noexport
#+BIND: paper-short nil
#+BIND: paper-anon nil
#+BIND: paper-limit-figs nil
#+MACRO: NAME (eval (if (bound-and-true-p paper-anon) "Anonymous" "Wesley R. Elsberry"))

View File

@ -0,0 +1,27 @@
#+latex_class: elsarticle
#+latex_class_options: [preprint,12pt]
#+latex_header: \usepackage{microtype}
#+latex_header: \usepackage[hidelinks]{hyperref}
#+latex_header: \usepackage{lineno}
#+latex_header: \usepackage[margin=1in]{geometry}
#+latex_header: \usepackage[backend=biber,style=authoryear,maxbibnames=99]{biblatex}
#+latex_header: \addbibresource{biblio.bib}
#+latex_header: \AtEndDocument{\printbibliography}
#+select_tags: export
#+exclude_tags: noexport
#+BIND: paper-short nil
#+BIND: paper-anon nil
#+BIND: paper-limit-figs nil
#+MACRO: NAME (eval (if (bound-and-true-p paper-anon) "Anonymous" "Wesley R. Elsberry"))
# Elsevier author/affiliation placeholders
#+latex_header: \author[1]{Wesley R. Elsberry\corref{cor1}}
#+latex_header: \ead{welsberr@example.org}
#+latex_header: \author[2]{Coauthor A. Name}
#+latex_header: \address[1]{Department, University, City, Country}
#+latex_header: \address[2]{Institute, Organization, City, Country}
#+latex_header: \cortext[cor1]{Corresponding author.}

17
polypaper/setup/venue-ieee.org Executable file
View File

@ -0,0 +1,17 @@
#+latex_class: IEEEtran
#+latex_class_options: [conference]
#+latex_header: \usepackage{microtype}
#+latex_header: \usepackage[hidelinks]{hyperref}
#+latex_header: \usepackage[backend=biber,style=ieee,sorting=none]{biblatex}
#+latex_header: \addbibresource{biblio.bib}
#+latex_header: \AtEndDocument{\printbibliography}
#+select_tags: export
#+exclude_tags: noexport appendix notes
#+BIND: paper-short t
#+BIND: paper-anon t
#+BIND: paper-limit-figs t
#+MACRO: NAME (eval (if (bound-and-true-p paper-anon) "Anonymous" "Wesley R. Elsberry"))

36
repo_meta.json Executable file
View File

@ -0,0 +1,36 @@
{
"brand": "PolyPaper",
"repo_name": "PolyPaper",
"url": "https://example.org/polypaper",
"contact_email": "you@example.org",
"license": "MIT",
"code_archive_doi": "10.5281/zenodo.0000000",
"data_availability": "All data needed to evaluate the conclusions are available within the paper and its Supplementary Materials.",
"data_links": [
{
"label": "Zenodo dataset",
"url": "https://zenodo.org/record/0000000"
},
{
"label": "OSF project",
"url": "https://osf.io/xxxxx/"
}
],
"software_env": {
"python": ">=3.10",
"packages": [
"matplotlib",
"numpy"
],
"latex": [
"latexmk",
"biber"
]
},
"repro_instructions": [
"Install TeX (with latexmk and biber).",
"Ensure Python and Matplotlib are installed.",
"Run `make VENUE=arxiv` to build the paper.",
"Run `make supplement VENUE=arxiv` to build the supplement."
]
}

10
scripts/check_metadata.py Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env python3
import sys, json, os
META = "repo_meta.json"
REQ = ["url", "license"]
def fail(msg): print(f"[meta-check] {msg}", file=sys.stderr); sys.exit(1)
if not os.path.exists(META): fail(f"{META} not found.")
m = json.load(open(META, "r", encoding="utf-8"))
missing = [k for k in REQ if not m.get(k)]
if missing: fail("Missing required fields: " + ", ".join(missing))
print("[meta-check] repo_meta.json looks OK.")

31
setup/venue-acm.org Executable file
View File

@ -0,0 +1,31 @@
#+latex_class: acmart
#+latex_class_options: [sigconf]
#+latex_header: \citestyle{acmauthoryear}
#+latex_header: \usepackage{microtype}
#+latex_header: \usepackage[hidelinks]{hyperref}
#+latex_header: \usepackage[backend=biber,style=acmauthoryear,sorting=nyt]{biblatex}
#+latex_header: \addbibresource{biblio.bib}
#+latex_header: \AtEndDocument{\printbibliography}
#+select_tags: export
#+exclude_tags: noexport appendix notes
#+BIND: paper-short t
#+BIND: paper-anon t
#+BIND: paper-limit-figs t
#+MACRO: NAME (eval (if (bound-and-true-p paper-anon) "Anonymous" "Wesley R. Elsberry"))
# ACM CCS taxonomy placeholders
#+latex_header: \begin{CCSXML}
#+latex_header: <ccs2012>
#+latex_header: <concept>
#+latex_header: <concept_id>10010147.10010257</concept_id>
#+latex_header: <concept_desc>Computing methodologies~Machine learning</concept_desc>
#+latex_header: <concept_significance>500</concept_significance>
#+latex_header: </concept>
#+latex_header: </ccs2012>
#+latex_header: \end{CCSXML}
#+latex_header: \ccsdesc[500]{Computing methodologies~Machine learning}
#+latex_header: \keywords{add, your, keywords}

18
setup/venue-arxiv.org Executable file
View File

@ -0,0 +1,18 @@
#+latex_class: article
#+latex_class_options: [10pt]
#+latex_header: \usepackage[margin=1in]{geometry}
#+latex_header: \usepackage{microtype}
#+latex_header: \usepackage[hidelinks]{hyperref}
#+latex_header: \usepackage[backend=biber,style=authoryear,maxbibnames=99]{biblatex}
#+latex_header: \addbibresource{biblio.bib}
#+latex_header: \AtEndDocument{\printbibliography}
#+select_tags: export
#+exclude_tags: noexport
#+BIND: paper-short nil
#+BIND: paper-anon nil
#+BIND: paper-limit-figs nil
#+MACRO: NAME (eval (if (bound-and-true-p paper-anon) "Anonymous" "Wesley R. Elsberry"))

27
setup/venue-elsevier.org Executable file
View File

@ -0,0 +1,27 @@
#+latex_class: elsarticle
#+latex_class_options: [preprint,12pt]
#+latex_header: \usepackage{microtype}
#+latex_header: \usepackage[hidelinks]{hyperref}
#+latex_header: \usepackage{lineno}
#+latex_header: \usepackage[margin=1in]{geometry}
#+latex_header: \usepackage[backend=biber,style=authoryear,maxbibnames=99]{biblatex}
#+latex_header: \addbibresource{biblio.bib}
#+latex_header: \AtEndDocument{\printbibliography}
#+select_tags: export
#+exclude_tags: noexport
#+BIND: paper-short nil
#+BIND: paper-anon nil
#+BIND: paper-limit-figs nil
#+MACRO: NAME (eval (if (bound-and-true-p paper-anon) "Anonymous" "Wesley R. Elsberry"))
# Elsevier author/affiliation placeholders
#+latex_header: \author[1]{Wesley R. Elsberry\corref{cor1}}
#+latex_header: \ead{welsberr@example.org}
#+latex_header: \author[2]{Coauthor A. Name}
#+latex_header: \address[1]{Department, University, City, Country}
#+latex_header: \address[2]{Institute, Organization, City, Country}
#+latex_header: \cortext[cor1]{Corresponding author.}

17
setup/venue-ieee.org Executable file
View File

@ -0,0 +1,17 @@
#+latex_class: IEEEtran
#+latex_class_options: [conference]
#+latex_header: \usepackage{microtype}
#+latex_header: \usepackage[hidelinks]{hyperref}
#+latex_header: \usepackage[backend=biber,style=ieee,sorting=none]{biblatex}
#+latex_header: \addbibresource{biblio.bib}
#+latex_header: \AtEndDocument{\printbibliography}
#+select_tags: export
#+exclude_tags: noexport appendix notes
#+BIND: paper-short t
#+BIND: paper-anon t
#+BIND: paper-limit-figs t
#+MACRO: NAME (eval (if (bound-and-true-p paper-anon) "Anonymous" "Wesley R. Elsberry"))