diff --git a/config.py.example b/config.py.example
index 98a011257b0794b1ab142a06a28be3616743044d..5c443f4da4d37864609fbb8607c3b80271f9ef63 100644
--- a/config.py.example
+++ b/config.py.example
@@ -25,6 +25,14 @@ LDAP_PROVIDER_URL = "ldaps://auth.example.com:389"
 LDAP_BASE = "dc=example,dc=example,dc=com"
 LDAP_PROTOCOL_VERSION = 3 # do not change
 
+PRINTING_ACTIVE = True
+PRINTING_SERVER = "printsrv.example.com:631"
+PRINTING_USER = "protocols"
+PRINTING_PRINTERS = [
+    "example_printer": ["Duplex=DuplexNoTumble", "option2=value"],
+    "other_printer": ["list", "of", "options"]
+]
+
 ETHERPAD_URL = "https://fachschaften.rwth-aachen.de/etherpad"
 EMPTY_ETHERPAD = """Welcome to Etherpad!
 
diff --git a/migrations/versions/f91d760158dc_.py b/migrations/versions/f91d760158dc_.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a868a58746b47eb43b2370bbdac7a15c3c56b1c
--- /dev/null
+++ b/migrations/versions/f91d760158dc_.py
@@ -0,0 +1,28 @@
+"""empty message
+
+Revision ID: f91d760158dc
+Revises: d8c0c74b88bd
+Create Date: 2017-02-25 21:52:07.654276
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'f91d760158dc'
+down_revision = 'd8c0c74b88bd'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.add_column('protocoltypes', sa.Column('printer', sa.String(), nullable=True))
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_column('protocoltypes', 'printer')
+    # ### end Alembic commands ###
diff --git a/models/database.py b/models/database.py
index 89d0921cbe8bb33b544e676af572a82a4a459a2e..5c180270fb6dc1df0e571b0d9b437fb8e605745d 100644
--- a/models/database.py
+++ b/models/database.py
@@ -29,6 +29,7 @@ class ProtocolType(db.Model):
     use_wiki = db.Column(db.Boolean)
     wiki_category = db.Column(db.String)
     wiki_only_public = db.Column(db.Boolean)
+    printer = db.Column(db.String)
 
     protocols = relationship("Protocol", backref=backref("protocoltype"), cascade="all, delete-orphan", order_by="Protocol.id")
     default_tops = relationship("DefaultTOP", backref=backref("protocoltype"), cascade="all, delete-orphan", order_by="DefaultTOP.number")
@@ -37,7 +38,7 @@ class ProtocolType(db.Model):
 
     def __init__(self, name, short_name, organization,
             is_public, private_group, public_group, private_mail, public_mail,
-            use_wiki, wiki_category, wiki_only_public):
+            use_wiki, wiki_category, wiki_only_public, printer):
         self.name = name
         self.short_name = short_name
         self.organization = organization
@@ -49,16 +50,17 @@ class ProtocolType(db.Model):
         self.use_wiki = use_wiki
         self.wiki_category = wiki_category
         self.wiki_only_public = wiki_only_public
+        self.printer = printer
 
     def __repr__(self):
         return ("<ProtocolType(id={}, short_name={}, name={}, "
                 "organization={}, is_public={}, private_group={}, "
                 "public_group={}, use_wiki={}, wiki_category='{}', "
-                "wiki_only_public={})>".format(
+                "wiki_only_public={}, printer={})>".format(
             self.id, self.short_name, self.name,
             self.organization, self.is_public, self.private_group,
             self.public_group, self.use_wiki, self.wiki_category,
-            self.wiki_only_public))
+            self.wiki_only_public, self.printer))
 
     def get_latest_protocol(self):
         candidates = sorted([protocol for protocol in self.protocols if protocol.is_done()], key=lambda p: p.date, reverse=True)
diff --git a/server.py b/server.py
index 289cdcd3e3e1eda33d889c442d94e59f59563b10..f5c8cb1a2e1796e10724662691657847d471eef8 100755
--- a/server.py
+++ b/server.py
@@ -90,7 +90,7 @@ def new_type():
                 form.private_group.data, form.public_group.data,
                 form.private_mail.data, form.public_mail.data,
                 form.use_wiki.data, form.wiki_category.data,
-                form.wiki_only_public.data)
+                form.wiki_only_public.data, form.printer.data)
             db.session.add(protocoltype)
             db.session.commit()
             flash("Der Protokolltyp {} wurde angelegt.".format(protocoltype.name), "alert-success")
@@ -688,6 +688,17 @@ def delete_document(document_id):
     flash("Das Dokument {} wurde gelöscht.".format(name), "alert-success")
     return redirect(request.args.get("next") or url_for("show_protocol", protocol_id=protocol.id))
 
+@app.route("/document/print/<int:document_id>")
+@login_required
+def print_document(document_id):
+    user = current_user()
+    document = Document.query.filter_by(id=document_id).first()
+    if document is None or not document.protocol.protocoltype.has_modify_right(user):
+        flash("Invalides Protokoll oder keine Berechtigung.", "alert-error")
+        return redirect(request.args.get("next") or url_for("index"))
+    tasks.print_file(document.get_filename(), document.protocol)
+    flash("Das Dokument {} wird gedruckt.".format(document.name), "alert-success")
+    return redirect(request.args.get("next") or url_for("show_protocol", protocol_id=document.protocol.id))
 
 @app.route("/login", methods=["GET", "POST"])
 def login():
diff --git a/tasks.py b/tasks.py
index 0beb9491b30cf845d65312798add25ca07cbd64d..88fdebfd5359541ae9f729c750b816b675d9fb44 100644
--- a/tasks.py
+++ b/tasks.py
@@ -281,6 +281,33 @@ def compile_async(content, protocol_id, show_private):
         finally:
             os.chdir(current)
 
+def print_file(filename, protocol):
+    if config.PRINTING_ACTIVE:
+        print_file_async.delay(filename, protocol.id)
+
+@celery.task
+def print_file_async(filename, protocol_id):
+    with app.app_context():
+        protocol = Protocol.query.filter_by(id=protocol_id).first()
+        if protocol.protocoltype.printer is None:
+            error = protocol.create_error("Printing", "No printer configured.", "You don't have any printer configured for the protocoltype {}. Please do so before printing a protocol.".format(protocol.protocoltype.name))
+        try:
+            command = [
+                "/usr/bin/lpr",
+                "-H", config.PRINTING_SERVER,
+                "-P", protocol.protocoltype.printer,
+                "-U", config.PRINTING_USER,
+                "-T", protocol.get_identifier(),
+            ]
+            for option in config.PRINTING_PRINTERS[protocol.protocoltype.printer]:
+                command.extend(["-o", '"{}"'.format(option) if " " in option else option])
+            command.append(filename)
+            subprocess.check_call(command, universal_newlines=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+        except subprocess.SubprocessError:
+            error = protocol.create_error("Printing", "Printing {} failed.".format(protocol.get_identifier()), "")
+            db.session.add(error)
+            db.session.commit()
+
 def send_mail(mail):
     send_mail_async.delay(mail.id)
 
diff --git a/utils.py b/utils.py
index 2c78e153cb36d00aa1f266e1c3acb14374ff6a8c..6d818bc921a1d59084ac33e27c86a0fa30109944 100644
--- a/utils.py
+++ b/utils.py
@@ -124,4 +124,3 @@ def set_etherpad_text(pad, text, only_if_default=True):
     req = requests.post(get_etherpad_import_url(pad), files=files)
     return req.status_code == 200
     
-
diff --git a/views/forms.py b/views/forms.py
index 6abc73b0e60a502d53c2210850802e9f180f2916..10ba631f53292059ec115bf744613ba0b89e93ca 100644
--- a/views/forms.py
+++ b/views/forms.py
@@ -2,6 +2,8 @@ from flask_wtf import FlaskForm
 from wtforms import StringField, PasswordField, BooleanField, DateField, HiddenField, IntegerField, SelectField, FileField, DateTimeField
 from wtforms.validators import InputRequired, Optional
 
+import config
+
 class LoginForm(FlaskForm):
     username = StringField("Benutzer", validators=[InputRequired("Bitte gib deinen Benutzernamen ein.")])
     password = PasswordField("Passwort", validators=[InputRequired("Bitte gib dein Passwort ein.")])
@@ -18,6 +20,7 @@ class ProtocolTypeForm(FlaskForm):
     wiki_category = StringField("Wiki-Kategorie")
     use_wiki = BooleanField("Wiki benutzen")
     wiki_only_public = BooleanField("Wiki ist öffentlich")
+    printer = SelectField("Drucker", choices=list(zip(config.PRINTING_PRINTERS, config.PRINTING_PRINTERS)))
 
 class DefaultTopForm(FlaskForm):
     name = StringField("Name", validators=[InputRequired("Du musst einen Namen angeben.")])
diff --git a/views/tables.py b/views/tables.py
index c7372a935d31b8ae6f31811f125182fe00e2266d..778e4235e3c9ae73ab4748a366e6e8e47c869bab 100644
--- a/views/tables.py
+++ b/views/tables.py
@@ -98,7 +98,7 @@ class ProtocolTypeTable(SingleValueTable):
         headers = ["Name", "Abkürzung", "Organisation", "Öffentlich",
             "Interne Gruppe", "Öffentliche Gruppe",
             "Interner Verteiler", "Öffentlicher Verteiler",
-            "Wiki"]
+            "Drucker", "Wiki"]
         if self.value.use_wiki:
             headers.append("Wiki-Kategorie")
         return headers
@@ -113,7 +113,8 @@ class ProtocolTypeTable(SingleValueTable):
             self.value.public_group,
             self.value.private_mail,
             self.value.public_mail,
-            Table.bool(self.value.use_wiki) + (", " + ("Öffentlich" if self.value.wiki_only_public else "Intern")) if self.value.use_wiki else ""
+            self.value.printer,
+            (Table.bool(self.value.use_wiki) + ((", " + ("Öffentlich" if self.value.wiki_only_public else "Intern")) if self.value.use_wiki else ""))
         ]
         if self.value.use_wiki:
             row.append(self.value.wiki_category)
@@ -221,7 +222,10 @@ class DocumentsTable(Table):
         return [
             document.id,
             Table.link(url_for("download_document", document_id=document.id), document.name),
-            (Table.link(url_for("delete_document", document_id=document.id), "Löschen", confirm="Bist du dir sicher, dass du das Dokument {} löschen willst?".format(document.name))
-                if document.protocol.protocoltype.has_modify_right(user)
-                else "")
+            Table.concat([
+                Table.link(url_for("delete_document", document_id=document.id), "Löschen", confirm="Bist du dir sicher, dass du das Dokument {} löschen willst?".format(document.name)),
+                Table.link(url_for("print_document", document_id=document.id), "Drucken")
+            ])
+            if document.protocol.protocoltype.has_modify_right(user)
+            else ""
         ]