diff --git a/campaigns/migrations/0007_campaign_document.py b/campaigns/migrations/0007_campaign_document.py new file mode 100644 index 0000000..2093e7c --- /dev/null +++ b/campaigns/migrations/0007_campaign_document.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.1 on 2023-05-28 13:54 + +from django.db import migrations, models +import utils.random_filename + + +class Migration(migrations.Migration): + dependencies = [ + ("campaigns", "0006_alter_campaign_created_alter_campaign_language_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="campaign", + name="document", + field=models.FileField( + blank=True, + editable=False, + null=True, + upload_to=utils.random_filename.get_file_path, + verbose_name="Document", + ), + ), + ] diff --git a/campaigns/models.py b/campaigns/models.py index 2ec1a30..064fff2 100644 --- a/campaigns/models.py +++ b/campaigns/models.py @@ -5,12 +5,14 @@ from django_tenants.models import DomainMixin from tenant_users.tenants.models import TenantBase from rpg_notes.secrets import DEBUG -from utils.languages import full_text_languages, full_text_languages_choice +from utils.languages import full_text_languages_choice +from utils.random_filename import get_file_path class Campaign(TenantBase): name = models.CharField(_("Name"), max_length=1000, unique=True) language = models.CharField(_("Language"), max_length=100, choices=full_text_languages_choice) + document = models.FileField(_("Document"), upload_to=get_file_path, blank=True, null=True, editable=False) auto_create_schema = True diff --git a/pdf/__init__.py b/pdf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pdf/apps.py b/pdf/apps.py new file mode 100644 index 0000000..a4d893c --- /dev/null +++ b/pdf/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class PdfConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "pdf" diff --git a/pdf/management/commands/create_document.py b/pdf/management/commands/create_document.py new file mode 100644 index 0000000..bb9f1e4 --- /dev/null +++ b/pdf/management/commands/create_document.py @@ -0,0 +1,11 @@ +from django.utils import translation +from django.core.management.base import BaseCommand + +from pdf.utils import create_document + + +class Command(BaseCommand): + def handle(self, *args, **kwargs): + translation.activate("de") + create_document() + print("test") diff --git a/pdf/urls.py b/pdf/urls.py new file mode 100644 index 0000000..071cbab --- /dev/null +++ b/pdf/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from pdf import views + +urlpatterns=[ + path("", views.pdf, name="document"), +] diff --git a/pdf/utils.py b/pdf/utils.py new file mode 100644 index 0000000..f87b262 --- /dev/null +++ b/pdf/utils.py @@ -0,0 +1,78 @@ +import subprocess +import webbrowser +from pathlib import Path +from tempfile import TemporaryDirectory + +import jinja2 +from django.core.files import File + +from campaigns.models import Campaign +from days.models import IngameDay +from rpg_notes.settings import TEMPLATES_DIR + +latex_replacements = {} + +REPLACE = { + '\\': '\\textbackslash ', + '*': '\\textasteriskcentered ', + '_': '\\_', + '#': '\\#', + '$': '\\$', + '%': '\\%', + '{': '\\{', + '}': '\\}', + '&': '\\&', + '…': '\\dots ', + '~': '\\~{}', + '^': '\\^{}' +} + + +def latex_escape(text: str) -> str: + text = text.translate(str.maketrans(REPLACE)) + return text + + +def md_to_latex(text: str) -> str: + return latex_escape(text) + + +env = jinja2.Environment( + block_start_string='\BLOCK{', + block_end_string='}', + variable_start_string='\VAR{', + variable_end_string='}', + comment_start_string='\#{', + comment_end_string='}', + line_statement_prefix='%#', + line_comment_prefix='%%', + trim_blocks=True, + autoescape=False, + loader=jinja2.FileSystemLoader(TEMPLATES_DIR) +) + +env.filters['latex-escape'] = env.filters['l'] = latex_escape +env.filters['md'] = md_to_latex + + +def generate_pdf(data, tenant: Campaign): + with TemporaryDirectory() as dirname: + dir = Path(dirname) + print(dir) + template = env.get_template('template.tex') + latex = template.render(data) + print(latex) + (dir / "document.tex").write_text(latex) + out = subprocess.run(["latexmk", "-pdf", "-interaction=batchmode", "document.tex"], + cwd=dir, check=True, capture_output=True) + print(out.stdout.decode()) + pdf_file = dir / "document.pdf" + webbrowser.open(str(pdf_file)) + with pdf_file.open("rb") as f: + tenant.document.save("document.pdf", File(f)) + + +def create_document(tenant: Campaign): + data = {"days": IngameDay.objects.all()} + + generate_pdf(data, tenant) diff --git a/pdf/views.py b/pdf/views.py new file mode 100644 index 0000000..eb4be23 --- /dev/null +++ b/pdf/views.py @@ -0,0 +1,13 @@ +from django.http import HttpRequest, HttpResponse + +from campaigns.models import Campaign +from pdf.utils import create_document + + +def pdf(request: HttpRequest) -> HttpResponse: + tenant: Campaign = request.tenant + create_document(tenant) + + response = HttpResponse(tenant.document.file, content_type='application/pdf') + response['Content-Disposition'] = f'inline; filename={"document.pdf"}' + return response diff --git a/rpg_notes/settings.py b/rpg_notes/settings.py index 512d0b4..d835a72 100644 --- a/rpg_notes/settings.py +++ b/rpg_notes/settings.py @@ -74,6 +74,7 @@ TENANT_APPS = ( 'notes', 'search', 'graph', + 'pdf', 'common', 'simple_history', diff --git a/rpg_notes/urls.py b/rpg_notes/urls.py index 4887603..d92b243 100644 --- a/rpg_notes/urls.py +++ b/rpg_notes/urls.py @@ -20,6 +20,7 @@ urlpatterns = [ path('loot/', include("loot.urls")), path('search/', include("search.urls")), path('graph/', include("graph.urls")), + path('document/', include("pdf.urls")), path('', include("common.urls")), path('', include("campaigns.urls")) ] diff --git a/templates/template.tex b/templates/template.tex new file mode 100644 index 0000000..62a4565 --- /dev/null +++ b/templates/template.tex @@ -0,0 +1,50 @@ +\RequirePackage[l2tabu, orthodox]{nag} % complain about outdated options + +\documentclass[ +a4paper, +% draft, %% produce only a draft version (mark lines that need manual edition and don't show graphics) +bibliography=totoc, % add bibilography to table of contents +10pt, %% set default font size to 10 point +DIV=12, % replace this with a larger number to get less padding around all text (or use calc for the ideal border) +parskip=half, % how much space between paragraphs. If you prefer indention instead of space between paragraphs remove it +twoside, % replace with twoside before printing +BCOR=5mm, %% amount of paper lost in the middle due to binding a book +british, % language of the document +%appendixprefix=true % in case you want Appendix A written in front of appendix headings +listof=totoc +]{scrbook} + +\usepackage[ngerman]{babel} + +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} + +\pagestyle{headings} + +\usepackage[ % +colorlinks=true, %% turn on colored links (true is better for on-screen reading, false is better for printout versions) +linkcolor = blue, +allcolors=blue, +bookmarks=true, %% if true, generate PDF bookmarks (requires two passes of pdflatex) +bookmarksopen=false, %% if true, show all PDF bookmarks expanded +bookmarksnumbered=false, %% if true, add the section numbers to the bookmarks +%pdfstartpage={1}, %% determines, on which page the PDF file is opened +% pdfpagemode=None %% None, UseOutlines (=show bookmarks), UseThumbs (show thumbnails), FullScreen +draft=false +]{hyperref} + + +\begin{document} + +\chapter{Timeline} + +\BLOCK{for day in days} + +\section*{\VAR{day.__str__()|l}} + +\VAR{day.description_md|md} + +\BLOCK{endfor} + + +\end{document}