diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 0b1a680f..6329e25e 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,15 +1,22 @@ -name: Docker +name: Deploy Docker image on: - push: - branches: [ "master" ] + workflow_run: + workflows: ["Python package"] + branches: ["master"] + types: + - completed + + workflow_dispatch: env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} + VERSION: latest jobs: - build_publish: + build: + name: Build Docker image runs-on: ubuntu-latest permissions: contents: read @@ -29,7 +36,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Login against Container registry + - name: Login to container registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} @@ -42,7 +49,7 @@ jobs: with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - name: Build and push Docker image + - name: Build Docker image uses: docker/build-push-action@v5 with: context: . @@ -50,3 +57,70 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} platforms: linux/amd64, linux/arm64 + + test: + name: Test Docker image + needs: build + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + strategy: + matrix: + build-command: ["", "--fancy --tex-template --feats-by-type --spells-by-level", "--output-format=epub"] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Login to container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Run Tests + run: | + docker run -v ${{ github.workspace }}/examples:/build ${{ steps.meta.outputs.tags }} ${{ matrix.build-command }} + + push: + name: Deploy Docker image + needs: [build, test] + if: ${{ github.ref == 'refs/heads/master' }} + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + + - name: Login to container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Pull Docker image from GHCR + run: docker pull ${{ steps.meta.outputs.tags }} + + - name: Lowercase reponame + run: echo "IMAGE_NAME_LC=${GITHUB_REPOSITORY@L}" >> "${GITHUB_ENV}" + + - name: Tag and push Docker image to GHCR (final) + run: | + docker tag ${{ steps.meta.outputs.tags }} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:${{ env.VERSION }} + docker push ${{ steps.meta.outputs.tags }} + docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:${{ env.VERSION }} diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml deleted file mode 100644 index c4a1b71b..00000000 --- a/.github/workflows/python-ci.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Python package - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest] - python-version: ['3.9', '3.10', '3.11', '3.12'] - - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Set up required system dependencies - run: | - sudo apt-get update - sudo apt-get -y install pdftk texlive-latex-base texlive-latex-extra texlive-fonts-recommended texlive-fonts-extra - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - cache: pip - - name: Install dependencies and do a local pip install - run: | - python -m pip install --upgrade pip - pip install -e ".[dev]" - - name: Run flake - run: flake8 dungeonsheets/ --exit-zero - - name: Run tests - run: > - cd examples/; - makesheets --debug; - makesheets --debug --fancy; - makesheets --debug --output-format=epub; - cd ../; - pytest --cov=dungeonsheets tests/ diff --git a/.github/workflows/python_ci.yml b/.github/workflows/python_ci.yml new file mode 100644 index 00000000..06d04b2d --- /dev/null +++ b/.github/workflows/python_ci.yml @@ -0,0 +1,84 @@ +name: Python package + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + name: "Run test suite" + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + python-version: ['3.9', '3.10', '3.11'] + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up required system dependencies + run: | + sudo apt-get update + sudo apt-get -y install pdftk texlive-latex-base texlive-latex-extra texlive-fonts-recommended texlive-fonts-extra texlive-luatex texlive-pstricks + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: pip + + - name: Install dependencies and do a local pip install + run: | + python -m pip install --upgrade pip + pip install -e ".[dev]" + + - name: Run flake + run: flake8 dungeonsheets/ --exit-zero + + - name: Run tests + run: pytest --cov=dungeonsheets tests/ + + render_examples: + name: "Test python-3.12, render all examples" + runs-on: ubuntu-latest + needs: [build] + strategy: + matrix: + build-command: ["", "--fancy --tex-template --spells-by-level --feats-by-type", "--output-format=epub"] + + steps: + - uses: actions/checkout@v4 + name: Checkout Repo + with: + submodules: recursive + + - name: Set up required system dependencies + run: | + sudo apt-get update + sudo apt-get -y install pdftk texlive-latex-base texlive-latex-extra texlive-fonts-recommended texlive-fonts-extra texlive-luatex texlive-pstricks + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: 3.12 + cache: pip + + - name: Install dependencies and do a local pip install + run: | + python -m pip install --upgrade pip + pip install -e ".[dev]" + + - name: Run flake + run: flake8 dungeonsheets/ --exit-zero + + - name: Run tests + run: pytest --cov=dungeonsheets tests/ + + - name: Render examples with ${{ matrix.build-command }} + run: | + cd examples + makesheets --debug ${{ matrix.build-command }} diff --git a/.gitmodules b/.gitmodules index 5bd8ab31..807489d4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "dungeonsheets/modules/DND-5e-LaTeX-Template"] path = dungeonsheets/modules/DND-5e-LaTeX-Template url = https://github.com/rpgtex/DND-5e-LaTeX-Template.git +[submodule "dungeonsheets/modules/DND-5e-LaTeX-Character-Sheet-Template"] + path = dungeonsheets/modules/DND-5e-LaTeX-Character-Sheet-Template + url = https://github.com/matsavage/DND-5e-LaTeX-Character-Sheet-Template diff --git a/Dockerfile b/Dockerfile index 38ac0bd2..c0d3aee6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM python:latest -RUN apt-get update && apt-get install -y pdftk texlive-latex-base texlive-latex-extra texlive-fonts-recommended texlive-fonts-extra +RUN apt-get update && apt-get install -y pdftk texlive-latex-base texlive-latex-extra texlive-fonts-recommended texlive-fonts-extra texlive-luatex texlive-pstricks WORKDIR /app diff --git a/README.rst b/README.rst index 2de2a927..4ec0c09e 100644 --- a/README.rst +++ b/README.rst @@ -36,7 +36,7 @@ Run the following in a directory with valid character files (such as the example .. code:: bash - $ docker run -it -v $(pwd):/build ghcr.io/canismarko/dungeon-sheets:master + $ docker run -it -v $(pwd):/build ghcr.io/canismarko/dungeon-sheets:latest Installation @@ -109,7 +109,16 @@ so attack bonuses and damage can be calculated automatically. Consider using the ``-F`` option to include the excellent D&D 5e template for rendering spellbooks, druid wild forms and features -pages (https://github.com/rpgtex/DND-5e-LaTeX-Template). +pages (https://github.com/rpgtex/DND-5e-LaTeX-Template). dungeon- +sheets includes its own version of the template, but will use a +local one if it is installed. + +Consider using the ``-T`` option to use the beautiful latex character +sheet +(https://github.com/matsavage/DND-5e-LaTeX-Character-Sheet-Template). +This does require lualatex as well as a fairly recent version of +texlive. dungeon-sheets includes its own version of the latex character +template, but will use a local one if it is installed. By default, your character's spells are ordered alphabetically. If you would like your spellbook to be ordered by level, you can use the ``-S`` diff --git a/dungeonsheets/forms/MSavage_template.tex b/dungeonsheets/forms/character_sheet_template.tex similarity index 99% rename from dungeonsheets/forms/MSavage_template.tex rename to dungeonsheets/forms/character_sheet_template.tex index 35d3e427..cd378eae 100644 --- a/dungeonsheets/forms/MSavage_template.tex +++ b/dungeonsheets/forms/character_sheet_template.tex @@ -5,8 +5,6 @@ \usepackage{ifthen} \usepackage{pstricks} -\usepackage[UKenglish]{babel} - \usepackage{dndtemplate} \usepackage{bookmark} diff --git a/dungeonsheets/latex.py b/dungeonsheets/latex.py index 124aa407..e74be676 100644 --- a/dungeonsheets/latex.py +++ b/dungeonsheets/latex.py @@ -4,6 +4,7 @@ import re import subprocess import logging +import warnings from docutils import core from docutils.writers.latex2e import Writer, Table, LaTeXTranslator @@ -50,7 +51,8 @@ def create_latex_pdf( basename: str, keep_temp_files: bool = False, use_dnd_decorations: bool = False, - comm1: str = "pdflatex" + use_tex_template: bool = False, + comm1: str = "pdflatex", ): # Create tex document tex_file = f"{basename}.tex" @@ -69,14 +71,39 @@ def create_latex_pdf( str(tex_file), ] - environment = os.environ + # Deal with TEXINPUTS and add paths to latex modules + environment = os.environ.copy() tex_env = environment.get('TEXINPUTS', '') module_root = Path(__file__).parent / "modules/" - module_dirs = [module_root / mdir for mdir in ["DND-5e-LaTeX-Template"]] + module_dirs = [] + + # Load locally installed latex packages if they exist, to allow for + # local latex customisation + for module in ["dnd.sty", "dndtemplate.sty"]: + kpsewhich_command = [ + "kpsewhich", + module, + ] + try: + module_check = subprocess.run(kpsewhich_command, capture_output=True, env=environment, text=True) + if module in module_check.stdout: + module_dirs.append(Path(module_check.stdout).parent) + except FileNotFoundError: + msg = ('Could not run kpsewhich. Something seems strange with your latex installation.') + warnings.warn(msg) + + module_dirs.extend( + [module_root / mdir for mdir in ["DND-5e-LaTeX-Template", "DND-5e-LaTeX-Character-Sheet-Template"]] + ) log.debug(f"Loading additional modules from {module_dirs}.") texinputs = ['.', *module_dirs, module_root, tex_env] - separator = ';' if isinstance(module_root, pathlib.WindowsPath) else ':' + # Two (back-)slashes at the end of each path to recursively add all subdirectories + separator = '\\\\;' if isinstance(module_root, pathlib.WindowsPath) else '//:' environment['TEXINPUTS'] = separator.join(str(path) for path in texinputs) + if use_tex_template: + environment['TTFONTS'] = environment['TEXINPUTS'] + + # Prepare the latex subprocess passes = 2 if use_dnd_decorations else 1 log.debug(tex_command_line) log.debug("LaTeX command: %s" % " ".join(tex_command_line)) @@ -264,8 +291,8 @@ def rst_to_boxlatex(rst): return tex -def msavage_spell_info(char): - """Generates the spellsheet for msavage template.""" +def latex_character_spell_info(char): + """Generates the spellsheet for the latex character template.""" headinfo = char.spell_casting_info["head"] font_options = {1:"", 2:r"\Large ", 3:r"\large "} selector = min(len(char.spellcasting_classes), 3) diff --git a/dungeonsheets/make_sheets.py b/dungeonsheets/make_sheets.py index 623c8207..a1ad8c15 100644 --- a/dungeonsheets/make_sheets.py +++ b/dungeonsheets/make_sheets.py @@ -56,7 +56,7 @@ jinja_env.filters["rst_to_html"] = epub.rst_to_html jinja_env.filters["to_heading_id"] = epub.to_heading_id jinja_env.filters["boxed"] = latex.rst_to_boxlatex -jinja_env.filters["spellsheetparser"] = latex.msavage_spell_info +jinja_env.filters["spellsheetparser"] = latex.latex_character_spell_info jinja_env.filters["monsterdoc"] = latex.RPGtex_monster_info # Custom types @@ -512,7 +512,7 @@ def make_character_content( return content -def msavage_sheet(character, basename, debug=False): +def latex_character_sheet(character, basename, debug=False): """Another adaption. All changes can be easily included as options in the orignal functions, though.""" @@ -533,7 +533,7 @@ def msavage_sheet(character, basename, debug=False): character.images = [(character.symbol, 1, 488, 564, 145, 112)] + character.images break - tex = jinja_env.get_template("MSavage_template.tex").render( + tex = jinja_env.get_template("character_sheet_template.tex").render( char=character, portrait=portrait_command ) latex.create_latex_pdf( @@ -541,7 +541,8 @@ def msavage_sheet(character, basename, debug=False): basename=basename, keep_temp_files=debug, use_dnd_decorations=True, - comm1="xelatex", + comm1="lualatex", + use_tex_template=True, ) @@ -598,7 +599,7 @@ def make_character_sheet( # Typeset combined LaTeX file if output_format == "pdf": if use_tex_template: - msavage_sheet( + latex_character_sheet( character=character, basename=char_base, debug=debug, diff --git a/dungeonsheets/modules/DND-5e-LaTeX-Character-Sheet-Template b/dungeonsheets/modules/DND-5e-LaTeX-Character-Sheet-Template new file mode 160000 index 00000000..ab3b0417 --- /dev/null +++ b/dungeonsheets/modules/DND-5e-LaTeX-Character-Sheet-Template @@ -0,0 +1 @@ +Subproject commit ab3b041769dfbd42c0fb50cc42f50363cdab07c6 diff --git a/pyproject.toml b/pyproject.toml index 86f20669..5ca5e1c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,13 @@ dungeonsheets = [ "modules/DND-5e-LaTeX-Template/*", "modules/DND-5e-LaTeX-Template/lib/*", "modules/DND-5e-LaTeX-Template/img/*", + "modules/DND-5e-LaTeX-Character-Sheet-Template/*", + "modules/DND-5e-LaTeX-Character-Sheet-Template/template/*", + "modules/DND-5e-LaTeX-Character-Sheet-Template/template/fonts/*", + "modules/DND-5e-LaTeX-Character-Sheet-Template/template/character-sheet/*", + "modules/DND-5e-LaTeX-Character-Sheet-Template/template/background-sheet/*", + "modules/DND-5e-LaTeX-Character-Sheet-Template/template/half-spell-sheet/*", + "modules/DND-5e-LaTeX-Character-Sheet-Template/template/spell-sheet/*", ] [tool.setuptools_scm]