diff --git a/.gitignore b/.gitignore
index 49984f1f98db5cb0c0b5d00f30a2500374ea313f..858b1e1da5fd77e0e517d662c54481c20a8c7d0e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@ documents/
 test/
 *.sql
 pg_to_sqlite.sh
+local-templates/
diff --git a/config.py.example b/config.py.example
index fbb680399656338b2a03321fe8320bfe19f2f73f..94d34a12cf1f761c88fd2fce9f7e4e2af53a599e 100644
--- a/config.py.example
+++ b/config.py.example
@@ -146,3 +146,17 @@ LATEX_BULLETPOINTS = [
     r"\textasteriskcentered",
     r"\textperiodcentered"
 ]
+
+# optional: path to additional jinja-templates
+#LATEX_LOCAL_TEMPLATES = "local-templates"
+# optional: the template to include at the top of protocol.tex
+#LATEX_LOGO_TEMPLATE = "asta-logo.tex"
+# optional: custom protocol page geometry
+#LATEX_GEOMETRY = "bottom=1.6cm,top=1.6cm,inner=2.5cm,outer=1.0cm,footskip=1.0cm,headsep=0.6cm"
+# optional: custom protocol pagestyle
+#LATEX_PAGESTYLE = "fancy"
+# optional: additional latex packages
+#LATEX_ADDITIONAL_PACKAGES = ["[absolute]{textpos}", "{fancyheadings}"]
+# optional: include header and footer in asta-style, not just a page number on top
+#LATEX_HEADER_FOOTER = True
+
diff --git a/parser.py b/parser.py
index 9be2c3e8b82ce8dc8d25a7ca4d460346cfdf5abf..6ac375715396105177bd03a21b08e6ee8383d2ad 100644
--- a/parser.py
+++ b/parser.py
@@ -32,6 +32,7 @@ class RenderType(Enum):
     latex = 0
     wikitext = 1
     plaintext = 2
+    html = 3
 
 def _not_implemented(self, render_type):
     return NotImplementedError("The rendertype {} has not been implemented for {}.".format(render_type.name, self.__class__.__name__))
@@ -175,6 +176,8 @@ class Text:
             return self.text
         elif render_type == RenderType.plaintext:
             return self.text
+        elif render_type == RenderType.html:
+            return self.text
         else:
             raise _not_implemented(self, render_type)
 
@@ -235,6 +238,24 @@ class Tag:
                     return ""
                 return self.todo.render_wikitext(current_protocol=protocol)
             return "'''{}:''' {}".format(self.name.capitalize(), ";".join(self.values))
+        elif render_type == RenderType.html:
+            if self.name == "url":
+                return "<a href=\"{0}\">{0}</a>".format(self.values[0])
+            elif self.name == "todo":
+                if not show_private:
+                    return ""
+                if getattr(self, "todo", None) is not None:
+                    return self.todo.render_html(current_protocol=protocol)
+                else:
+                    return "<b>Todo:</b> {}".format(";".join(self.values))
+            elif self.name == "beschluss":
+                if getattr(self, "decision", None) is not None:
+                    parts = ["<b>Beschluss:</b>", self.decision.content]
+                    if self.decision.category is not None:
+                        parts.append("<i>{}</i>".format(self.decision.category.name))
+                    return " ".join(parts)
+                else:
+                    return "<b>Beschluss:</b> {}".format(self.values[0])
         else:
             raise _not_implemented(self, render_type)
 
@@ -295,6 +316,11 @@ class Remark(Element):
             return "{}: {}".format(self.name, self.value)
         elif render_type == RenderType.plaintext:
             return "{}: {}".format(RenderType.plaintex)
+        elif render_type == RenderType.html:
+            return "<p>{}: {}</p>".format(self.name, self.value)
+        else:
+            raise _not_implemented(self, render_type)
+            
 
     def dump(self, level=None):
         if level is None:
@@ -399,6 +425,19 @@ class Fork(Element):
                 return ""
             else:
                 return content_lines
+        elif render_type == RenderType.html:
+            title_line = "<h{depth}>{content}</h{depth}>".format(depth=level+1, content=name_line)
+            content_parts = []
+            for child in self.children:
+                part = child.render(render_type, show_private, level=level+1, protocol=protocol)
+                if len(part.strip()) == 0:
+                    continue
+                content_parts.append("<p>{}</p>".format(part))
+            content_lines = "{}\n\n{}".format(title_line, "\n".join(content_parts))
+            if self.test_private(self.name) and not show_private:
+                return ""
+            else:
+                return content_lines
         else:
             raise _not_implemented(self, render_type)
 
diff --git a/server.py b/server.py
index eeb3fdb8ee0d6796cf4e23d57631002f56b8778e..de2fbe8ae40c9e3cfff567ac184b2a0d128a5603 100755
--- a/server.py
+++ b/server.py
@@ -63,6 +63,12 @@ app.jinja_env.filters["fancy_join"] = fancy_join
 app.jinja_env.tests["auth_valid"] = security_manager.check_user
 app.jinja_env.tests["needs_date"] = needs_date_test
 
+additional_templates = getattr(config, "LATEX_LOCAL_TEMPLATES", None)
+if additional_templates is not None and os.path.isdir(additional_templates):
+    if additional_templates not in app.jinja_loader.searchpath:
+        app.jinja_loader.searchpath.append(additional_templates)
+    
+
 import tasks
 
 app.jinja_env.globals.update(check_login=check_login)
diff --git a/tasks.py b/tasks.py
index 269483289b8a9ebbe9da4795c99f5c92455936c9..d1eff0d51a5f66106919392f340b410f278d5a43 100644
--- a/tasks.py
+++ b/tasks.py
@@ -38,6 +38,26 @@ texenv.filters["datify_short"] = date_filter_short
 texenv.filters["datetimify"] = datetime_filter
 texenv.filters["timify"] = time_filter
 texenv.filters["class"] = class_filter
+logo_template = getattr(config, "LATEX_LOGO_TEMPLATE", None)
+if logo_template is not None:
+    texenv.globals["logo_template"] = logo_template
+latex_geometry = getattr(config, "LATEX_GEOMETRY", "vmargin=1.5cm,hmargin={1.5cm,1.2cm},bindingoffset=8mm")
+texenv.globals["latex_geometry"] = latex_geometry
+raw_additional_packages = getattr(config, "LATEX_ADDITIONAL_PACKAGES", None)
+additional_packages = []
+if raw_additional_packages is not None:
+    for package in raw_additional_packages:
+        if "{" not in package:
+            package = "{{{}}}".format(package)
+        additional_packages.append(package)
+print(raw_additional_packages)
+print(additional_packages)
+texenv.globals["additional_packages"] = additional_packages
+latex_pagestyle = getattr(config, "LATEX_PAGESTYLE", None)
+if latex_pagestyle is not None:
+    texenv.globals["latex_pagestyle"] = latex_pagestyle
+latex_header_footer = getattr(config, "LATEX_HEADER_FOOTER", False)
+texenv.globals["latex_header_footer"] = latex_header_footer
 
 mailenv = app.create_jinja_environment()
 mailenv.trim_blocks = True
@@ -403,11 +423,12 @@ def compile_async(content, protocol_id, show_private=False, use_decision=False,
             current = os.getcwd()
             protocol_source_filename = "protocol.tex"
             protocol_target_filename = "protocol.pdf"
+            protocol_class_filename = "protokoll2.cls"
             log_filename = "protocol.log"
             with open(os.path.join(compile_dir, protocol_source_filename), "w") as source_file:
                 source_file.write(content)
-            protocol2_class_source = texenv.get_template("protokoll2.cls").render(fonts=config.FONTS, maxdepth=maxdepth, bulletpoints=config.LATEX_BULLETPOINTS)
-            with open(os.path.join(compile_dir, "protokoll2.cls"), "w") as protocol2_class_file:
+            protocol2_class_source = texenv.get_template(protocol_class_filename).render(fonts=config.FONTS, maxdepth=maxdepth, bulletpoints=config.LATEX_BULLETPOINTS)
+            with open(os.path.join(compile_dir, protocol_class_filename), "w") as protocol2_class_file:
                 protocol2_class_file.write(protocol2_class_source)
             os.chdir(compile_dir)
             command = [
@@ -460,6 +481,10 @@ def compile_async(content, protocol_id, show_private=False, use_decision=False,
             if os.path.isfile(total_source_filename):
                 with open(total_source_filename, "r") as source_file:
                     log += "\n\nSource:\n\n" + add_line_numbers(source_file.read())
+            total_class_filename = os.path.join(compile_dir, protocol_class_filename)
+            if os.path.isfile(total_class_filename):
+                with open(total_class_filename, "r") as class_file:
+                    log += "\n\nClass:\n\n" + add_line_numbers(class_file.read())
             error = protocol.create_error("Compiling", "Compiling LaTeX failed", log)
             db.session.add(error)
             db.session.commit()
diff --git a/templates/decision.tex b/templates/decision.tex
index 323f818fecec9e2b433aebbee808a87aafb600d0..3c7b9956eb35ed170ad32be5e85be599aa1ae5ff 100644
--- a/templates/decision.tex
+++ b/templates/decision.tex
@@ -18,6 +18,9 @@
 
 \begin{document}
 %\thispagestyle{plain}   %ggf kommentarzeichen entfernen
+\ENV{if logo_template is defined}
+    \ENV{include logo_template}
+\ENV{endif}
 \Titel{
 \large Protokoll: \VAR{protocol.protocoltype.name|escape_tex}
 \\\normalsize \VAR{protocol.protocoltype.organization|escape_tex}
diff --git a/templates/protocol.tex b/templates/protocol.tex
index 41a759ec41cbccf499b1dda103cb415d1773f6fc..f29e5a2eee34f739ad40e7119963c1609f28a02b 100644
--- a/templates/protocol.tex
+++ b/templates/protocol.tex
@@ -18,6 +18,9 @@
 
 \begin{document}
 %\thispagestyle{plain}   %ggf kommentarzeichen entfernen
+\ENV{if logo_template is defined}
+    \ENV{include logo_template}
+\ENV{endif}
 \Titel{
 \large \ENV{if show_private}Internes \ENV{endif} Protokoll: \VAR{protocol.protocoltype.name|escape_tex}
 \\\normalsize \VAR{protocol.protocoltype.organization|escape_tex}
diff --git a/templates/protokoll2.cls b/templates/protokoll2.cls
index a1b5467828373bb7cdc44806c50b507fb49fa55d..c732b57f0bf95c5ddb4059c5fba79b30b267dfb7 100644
--- a/templates/protokoll2.cls
+++ b/templates/protokoll2.cls
@@ -15,7 +15,7 @@
 
 % so ein paar sachen die wir eh immer brauchen
 \RequirePackage[ngerman]{babel}
-\RequirePackage[vmargin=1.5cm,hmargin={1.5cm,1.2cm},bindingoffset=8mm]{geometry}
+\RequirePackage[\VAR{latex_geometry}]{geometry}
 %\RequirePackage{lineno}
 \RequirePackage{longtable}
 \RequirePackage{framed}
@@ -27,6 +27,9 @@
 \RequirePackage{xcolor}
 \RequirePackage[breakable]{tcolorbox}
 \RequirePackage{linegoal}
+\ENV{for package in additional_packages}
+    \RequirePackage\VAR{package}
+\ENV{endfor}
 
 \setlistdepth{\VAR{maxdepth}}
 \renewlist{itemize}{itemize}{\VAR{maxdepth}}
@@ -88,7 +91,19 @@
 % nicht einrücken und benutzerinnendefinierte kopfzeile
 \setlength{\parindent}{0cm}
 \setlength{\parskip}{1ex}
-\pagestyle{myheadings}
+\pagestyle{\VAR{latex_pagestyle|default("myheadings")}}
+
+\ENV{if latex_header_footer}
+    \renewcommand{\headrulewidth}{0pt}
+    \renewcommand{\footrulewidth}{0.5pt}
+    \def\footerprotocoltext{\relax}
+    \rfoot[]{\rmfamily\small\footerprotocoltext~~|~~\textbf{\thepage}}
+    \lfoot[\rmfamily\small\textbf{\thepage}~~|~~\footerprotocoltext]{}
+    \cfoot{}
+    \chead{}
+    \lhead[]{}
+    \rhead[]{}
+\ENV{endif}
 
 %\renewcommand*{\rmdefault}{phv}
 %\renewcommand*{\sfdefault}{phv}
@@ -96,9 +111,10 @@
 % Titel
 \newcommand{\Kopf}[1]{\markboth{#1}{#1}}
 \newcommand{\Titel}[2]{
-  \markboth{#2}{#2}
-  \markright{#2}
-  \thispagestyle{plain}
+  \def\footerprotocoltext{#2}
+  %\markboth{#2}{#2}
+  %\markright{#2}
+  %\thispagestyle{plain}
   \begin{center}
     \textbf{#1}
   \end{center}