diff --git a/doc/CODING.md b/doc/CODING.md
index ebba160ce9d07bbfb2c5d2add6d51eed373862ee..b4c0af7e1972b03f6a8c4724cc1a5fcdd4ca7e5b 100644
--- a/doc/CODING.md
+++ b/doc/CODING.md
@@ -191,6 +191,51 @@ Form save
     * delete [STORE_EXTRA][<uploadSip>]
 * Step 3: update record with final <fileDestination>    
 
+Download
+--------
+
+A download might be:
+  * a single file (any type, will be detected on the fly), 
+  * an export of several files as a ZIP archive,
+  * an export of a T3-'XML'-Page converted to Excel,
+  * a converted HTML page to PDF,
+  * a PDF file, concatenated on single PDF files and/or converted HTML page to PDF.
+    
+'api/download.php' will be called with a SIP (no other vars used). The SIP contains:
+  * DOWNLOAD_EXPORT_FILENAME - any target filename, if none given take DOWNLOAD_OUTPUT_PDF ('output.pdf'). 
+  * DONWLOAD_MODE - file / pdf / excel / zip. If not specified:
+      a) 'file' is the default if only one is given and if this is a file.
+      b) 'pdf' is the default if there are multiple TOKEN_URL, TOKEN_URL_PARAM, TOKEN_FILE in SIP_DOWNLOAD_PARAMETER found.
+  * SIP_DOWNLOAD_PARAMETER (base64 encoded) - contains all parameter to source elements. 
+    Format: <format 1>:<element 1>|<format 2>:<element 2>|...|<format n>:<element n>|
+    
+    <format>: TOKEN_URL, TOKEN_URL_PARAM, TOKEN_FILE
+    <element>: depending on the token - see below
+    
+    URL:  a) 'u:http://w3c.org', b) 'u:w3c.org/', c) 'u:w3c.org/2017/index.php?issue=23'
+    URL_PARAM:  a) 'U:id=export&r=123', b) 'U:id=export&r=123&_orientation=landscape&_page-size=a3'
+    FILE: a) 'f:fileadmin/example.png'
+      
+    * In URL_PARAM extra parameter used by `wkhtmltopdf` can be specified. All Parameter, starting with '-'
+      will be extracted from the regular URL_PARAM and instead forwarded as options to `wkhtmlpdf`
+    
+  * The base64 encoding is necessary:
+    * to deliver multiple elements with the same token (e.g. multiple PDF files to concatenate).
+    * special parameter names, like 'id', should not force the regular interpretation of 'id' during conversion to a SIP. 
+      
+During preparing and delivering the download, a popup shows a spinning gear by default. The popup itself will display an
+individual message. The popup needs some HTML code (only once per T3 page). 
+Download links might be generated in `report` as well as in `subrecords of forms`. To trigger the generation of the HTML 
+popup code, a variable DOWNLOAD_POPUP_REQUEST in STORE_SYSTEM will be set to 'true' (string) in class Link(), as soon as 
+the first download link is rendered. During internal rendering of the download link, the const text token 
+DOWNLOAD_POPUP_REPLACE_TEXT and DOWNLOAD_POPUP_REPLACE_TITLE will be replaced with individual texts, defined per download link.
+
+
+Print
+-----
+
+tbd
+
 Formelement type: DATE / DATETIME / TIME
 ----------------------------------------
  * Available Formats:
diff --git a/extension/Documentation/Manual.rst b/extension/Documentation/Manual.rst
index b568b3afbe5e75b11d049b4ee457268044ddd71d..87fca0ba632f1a1918142f94fc6739f2a200a8a5 100644
--- a/extension/Documentation/Manual.rst
+++ b/extension/Documentation/Manual.rst
@@ -2374,7 +2374,7 @@ The sum of these three columns should always be 12.
 Multiple Elements per row
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Every row is by default wrapped in a `<div class='form-group'>` and every column is wrapped in a `<div class='col-md-?>`.
+Every row is by default wrapped in a `<div class='form-group'>` and every column is wrapped in a `<div class='col-md-?'>`.
 To display multiple input elements in one row, the wrapping of the *FormElement* row and of the three columns can be
 customized via the checkboxes of `Label / Input / Note`. Every open and every close tag can be individually switched on
 or off.
@@ -3275,7 +3275,7 @@ Column: _link
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
 |x  |   |Page          |p:<pageId>                         |p:impressum                |Prepend '?' or '?id=', no hostname qualifier (automatically set by browser), default link class: internal, default value: {{pageId}}    |
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
-|x  |   |Download      |d                                  |d                          |If an image is specified, it will be rendered inside the link, default link class: internal. Link points to `api/download.php`          |
+|x  |   |Download      |d:[<exportFilename>]               |d:complete.pdf             |Link points to `api/download.php`. Additonal parameter are encoded into a SIP. 'Download' needs an enabled SIP.  See `download`_.       |
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
 |   |   |Text          |t:<text>                           |t:Firstname Lastname       |-                                                                                                                                       |
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
@@ -3303,7 +3303,7 @@ Column: _link
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
 |   |   |Tooltip       |o:<text>                           |o:More information here    |Tooltip text                                                                                                                            |
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
-|   |   |Alttext       |a:<text>                           |a:Name of person           |Alttext for images                                                                                                                      |
+|   |   |Alttext       |a:<text>                           |a:Name of person           |a) Alttext for images, b) Message text for download popup window.                                                                       |
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
 |   |   |Class         |c:[n|i|e|<text>]                   |c:i                        |CSS class for link. n:no class attribut, i:internal (ext_localconf.php)(default), e:external (ext_localconf.php), <text>: explicit named|
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
@@ -3317,6 +3317,10 @@ Column: _link
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
 |   |   |SIP           |s[:0|1]                            |s, s:0, s:1                |If 's' or 's:1' a SIP entry is generated with all non Typo 3 Parameters. The URL contains only parameter 's' and Typo 3 parameter       |
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
+|   |   |Mode          |M:file|pdf|excel|zip               |M:file, M:pdf, M:excel     |Mode. Used to specify type of download. One or more element sources needs to be configured. See `download`_.                            |
++---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
+|   |   |File          |f:<filename>                       |f:fileadmin/file.pdf       |Element source for download mode file|pdf|zip. See `download`_.                                                                         |
++---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
 |   |   |Delete record | x[:a|r|c]                         |x, x:r, x:c                |a: ajax (only QFQ internal used), r: report (default), c: close (current page, open last page)                                          |
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
 
@@ -3384,7 +3388,8 @@ Link Examples
 +-----------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
 |SELECT "U:form=Person&r=123|x|t:Delete" as _link                       |<a href="typo3conf/ext/qfq/qfq/api/delete.php?s=badcaffee1234">Delete</a>                                                               |
 +-----------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
-|SELECT "d:U:1_pageId=req&1_id=12&1_mode=html2pdf"                      |<a href="typo3conf/ext/qfq/qfq/api/download.php?s=badcaffee1234">Download</a>                                                           |
+|SELECT "s:1|d:full.pdf|M:pdf|U:id=det1&r=12|U:id=det2|f:cv.pdf|        |<a href="typo3conf/ext/qfq/qfq/api/download.php?s=badcaffee1234">Download</a>                                                           |
+|        t:Download|a:Create complete PDF - please wait" as _link       |                                                                                                                                        |
 +-----------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
 
 .. _question:
@@ -3435,75 +3440,96 @@ Examples:
 | SELECT "p:form_person|q:Edit Person:::10:0" AS _link       | The Alert will be shown 10 seconds and is not modal.                      |
 +------------------------------------------------------------+---------------------------------------------------------------------------+
 
+.. _download:
+
 Download
 ^^^^^^^^
 
-Download links can be used to offer:
+Download offers:
 
-* to download a single file, or
-* to concatenate several files and/or web pages (=HTML to PDF) into one output file, or
-* to create an `Excel` export.
+* download a single file (any type),
+* concatenate several files (uploaded) and/or web pages (=HTML to PDF) into one PDF output file,
+* create an `excel` export (based on a Typo3 page which creates XML output),
+* create a ZIP archive, filled with several files ('uploaded' or 'HTML to PDF'-converted).
 
 The downloads are SIP protected. Only the current user can use the link to download files.
 
 By using the `_link` columnname:
-* the option `d` enables the download mode
-* setting `s` (or `s=1`) is mandatory for download mode
 
-By using `_download` or `_Download` as columnname the options `d` and `s` will be set by automatically.
+* the option `d` initiate creating the download link and optional specifies an export filename,
+* the optional `M` (Mode) specifies the export type (file, pdf, excel, zip),
+* setting `s` (or `s=1`) is mandatory for the download function,
+* the alttext `a` specifies a message in the dowload popup.
+
+By using `_pdf`,  `_Pdf`, `_file`, `_File`, `_excel`, `_Excel`, `_zip`, `_Zip` as columnname the options `d`, `m` and `s`
+will be set by automatically.
 
 All files will be read by PHP - therefore the directory might be protected against direct web access. This way is the
 preferred way to offer secure downloads via QFQ.
 
 In case the download needs a persistant URL (no SIP, no user session), a regular
-link, pointing directly to a file, have to be used - the download described here won't help.
+link, pointing directly to a file, have to be used - the download functionality described here is not appropriate.
 
 .. _download-parameter-files:
 
-Specify file(s)/web page(s)
-'''''''''''''''''''''''''''
+Parameter and (element) sources
+'''''''''''''''''''''''''''''''
 
-The following parameter have to be specified as 'U' parameter. The SIP encoded parameter remains on the server and
-are not transferred to the client. This prohibits parameter manipulation.
+* *download*: `d[:<exportFilename>]`
 
-* *Link parameter 'U:.....'* :
+  * *exportFilename* = <filename for save as> - Name, offered in the 'File save as' browser dialog. Default: 'output.pdf'.
+      The user typically expect meaningfull and distinct filenames for different download links.
 
- * *exportFilename* = <filename for save as> - Name, offered in the 'File save as' browser dialog. Default: 'output.pdf'.
-      The user typically expect meaningfull and distinct filenames for different downloads.
+* *popupMessage*: `a:<text>` - will be displayed in the popup window during download. If the creating/download is fast,
+      the window might disapear quicly.
 
- * *mode* = <file | pdf | excel> - This parameter is optional and can be skipped in most situations.
+* *mode*: `m:<mode>`
 
-      * If `mode=file`, the mimetype is derived dynamically from the specified file. All others are fix (PDF or Excel).
-      * In case of multiple files and/or web pages, only `pdf` is supported.
-      * `excel` is not implemented now.
-      * The **default** depends on the number of specified `elements` (=file or web page). If only one `file` is specifed,
-        the default is `file`. If there is a) a page defined or b) multiple elements, the default is `pdf`.
+  * *mode* = <file | pdf | excel | zip> - This parameter is optional and can be skipped in most situations. Mandatory
+    for 'excel', 'zip'.
 
- * For `web page` or `excel` export, all pages have to be specified with the necessary parameters (might
-    vary per page). The preceeding number `<i>` aggregates all parameter to one element (=`file` or `page`). The ordering has to start with `1`
-      and has to be consecutive.
+      * If `m:file`, the mimetype is derived dynamically from the specified file. In this mode, only one element source
+        is allowed per download link (no concatenation).
 
-   * *<i>_id* = <Typo3 pageId> - `id` is fix and mandatory for `web page` and `excel`.
-   * *<i>_<keyname>* = <value key i> - <keyname> is free of choice except `id` and `file`.
+      * In case of multiple element sources, only `pdf` or `zip` is supported.
+      * `m:excel` is not implemented now.
+      * If `m:zip` is used together with `U:...` oder `u:..`, those HTML pages will be converted to PDF and have technical
+        filenames inside the archive.
+      * If not specified, the **default** depends on the number of specified element sources (=file or web page).
 
- * For `file`
+        * If only one `file` is specifed, the default is `file`.
+        * If there is a) a page defined or b) multiple elements, the default is `pdf`.
 
-   * *<i>_file* = <pathfilename> - `file` is fix and mandatory for direct `file` access.
+* *element sources* - for `m:pdf` or `m:zip`, all of the following three element sources might be specified multiple times.
+     Any combination and order of the three options are allowed.
 
+  * *file*: `f:<pathFilename>` - relative or absolute pathFilename offered for a) download (single), or to be concatenated
+            in a PDF or ZIP.
+  * *urlParam*: `U:id=<t3 page>&<key 1>=<value 1>&<key 2>=<value 2>&...&<key n>=<value n>
+  * *url*: `u:<url>` - any URL, pointing to an internal or external destination.
+
+
+  * *Options* for `urlParam` or `url`:
+
+    * The 'HTML to PDF' will be done via `wkhtmltopdf`.
+    * All possible options, suitable for `wkhtmltopdf`, can be submitted in the `u:...` or `U:...` element source.
+      Check https://wkhtmltopdf.org/usage/wkhtmltopdf.txt. Examples see below.
 
 Most of the other Link-Class attributes can be used to customize the link.
 
 Example: ::
 
-	# single `file`
-	SELECT "d|s|t:PDF|U:exportFilename=final.pdf&1_file=fileadmin/pdf/test.pdf" AS _link
+	# single `file`. Specifying a popup message window text is not necessary, cause a file directly accessed is fast.
+	SELECT "d:file.pdf|s|t:Download|f:fileadmin/pdf/test.pdf" AS _link
 	# single `file`, with mode
-	SELECT "d|s|t:PDF|U:exportFilename=final.pdf&mode=file&1_file=fileadmin/pdf/test.pdf" AS _link
-
-	# two pages (1_id, 2_id) and one file (3_file)
-	SELECT "d|s|t:PDF|U:exportFilename=final.pdf&1_id=exportP1&2_id=123&3_file=fileadmin/pdf/test.pdf" AS _link
-	# two pages (1_id, 2_id) and one file (3_file) with mode
-	SELECT "d|s|t:PDF|U:exportFilename=final.pdf&mode=pdf&1_id=exportP1&2_id=123&3_file=fileadmin/pdf/test.pdf" AS _link
+	SELECT "d:file.pdf|m:pdf|s|t:Download|f:fileadmin/pdf/test.pdf" AS _link
+
+	# three sources: two pages and one file
+	SELECT "d:complete.pdf|s|t:Complete PDF|U:id=detail&r=1|U:id=detail2&r=1|f:fileadmin/pdf/test.pdf" AS _link AS _link
+	# three sources: two pages and one file
+	SELECT "d:complete.pdf|s|t:Complete PDF|U:id=detail&r=1|U:id=detail2&r=1|f:fileadmin/pdf/test.pdf" AS _link AS _link
+	# three sources: two pages and one file, the second page will be in landscape and pagesize A3
+	SELECT "d:complete.pdf|s|t:Complete PDF|U:id=detail&r=1|U:id=detail2&r=1&--orientation=Landscape&--page-size=A3|f:fileadmin/pdf/test.pdf" AS _link AS _link
 
 ..
 
diff --git a/extension/qfq/api/download.php b/extension/qfq/api/download.php
index fd5b35784dff99beef5fc153198b4a1c57dc377b..07de5dd3ceb3b930ab1b5c2b058fd0cc5f987813 100644
--- a/extension/qfq/api/download.php
+++ b/extension/qfq/api/download.php
@@ -4,6 +4,8 @@
  * User: crose
  * Date: 4/17/17
  * Time: 5:51 PM
+ *
+ * Check: CODING.md > Download
  */
 
 namespace qfq;
@@ -11,19 +13,31 @@ namespace qfq;
 use qfq;
 
 require_once(__DIR__ . '/../qfq/report/Download.php');
-//require_once(__DIR__ . '/../qfq/store/Store.php');
 require_once(__DIR__ . '/../qfq/Constants.php');
+require_once(__DIR__ . '/../qfq/exceptions/DownloadException.php');
+require_once(__DIR__ . '/../qfq/exceptions/CodeException.php');
+require_once(__DIR__ . '/../qfq/exceptions/DbException.php');
+require_once(__DIR__ . '/../qfq/exceptions/ErrorHandler.php');
+
+
+set_error_handler("\\qfq\\ErrorHandler::exception_error_handler");
+
+$data = '';
 
 try {
     $download = new \qfq\Download();
 
-    // If all is fine - this function never returns! The output file is delivered and PHP is stopped after that.
+    // If all is fine - 'process()' never returns! The output file is delivered and PHP is stopped after that.
     $data = $download->process();
 
-
+} catch (qfq\CodeException $e) {
+    $data = $e->formatMessage();
+} catch (qfq\DbException $e) {
+    $data = $e->formatMessage();
+} catch (qfq\DownloadException $e) {
+    $data = $e->formatMessage();
 } catch (\Exception $e) {
     $data = "Exception: " . $e->getMessage();
 }
 
 echo $data;
-
diff --git a/extension/qfq/qfq/Constants.php b/extension/qfq/qfq/Constants.php
index 927d08c08865696d34e9170dd34b72462c88050a..18c5bb6ee7e3b15b19b97f8494e74ce2099e07dc 100644
--- a/extension/qfq/qfq/Constants.php
+++ b/extension/qfq/qfq/Constants.php
@@ -212,6 +212,7 @@ const ERROR_DOWNLOAD_CREATE_NEW_FILE = 1700;
 const ERROR_DOWNLOAD_NO_FILES = 1701;
 const ERROR_DOWNLOAD_NOTHING_TO_DO = 1702;
 const ERROR_DOWNLOAD_UNEXPECTED_MIMETYPE = 1703;
+const ERROR_DOWNLOAD_UNEXPECTED_NUMBER_OF_SOURCES = 1704;
 
 // KeyValueParser
 const ERROR_KVP_VALUE_HAS_NO_KEY = 1900;
@@ -366,6 +367,12 @@ const SYSTEM_REPORT_FULL_LEVEL = 'reportFullLevel'; // Keyname of SQL-column pro
 const SYSTEM_LDAP_1_RDN = 'LDAP_1_RDN'; // Credentials to access LDAP
 const SYSTEM_LDAP_1_PASSWORD = 'LDAP_1_PASSWORD'; // Credentials to access LDAP
 
+const SYSTEM_DOWNLOAD_POPUP = 'hasDownloadPopup'; // Marker which is set to 'true' if there is at least one Download Link rendered
+
+const DOWNLOAD_POPUP_REQUEST = 'true';
+const DOWNLOAD_POPUP_REPLACE_TEXT = '#downloadPopupReplaceText#';
+const DOWNLOAD_POPUP_REPLACE_TITLE = '#downloadPopupReplaceTitle#';
+
 // die folgenden Elemente sind vermutlich nicht noetig, wenn Store Klassen gloable Vars benutzt.
 //const SYSTEM_FORM_DEF = 'formDefinition'; // Type: SANITIZE_ALNUMX / AssocArray. Final form to process. Useful for error reporting.
 //const SYSTEM_FORM_ELEMENT_DEF = 'formElementDefinition'; // Type: SANITIZE_ALL / AssocArray. Formelement which are processed at the moment. Useful for error reporting.
@@ -387,6 +394,10 @@ const SIP_FORM = CLIENT_FORM;
 const SIP_TABLE = 'table'; // delete a record from 'table'
 const SIP_URLPARAM = 'urlparam';
 const SIP_MAKE_URLPARAM_UNIQ = '_makeUrlParamUniq'; // SIPs for 'new records' needs to be uniq per TAB! Therefore add a uniq parameter
+const SIP_DOWNLOAD_PARAMETER = '_b64_download'; // Parametername, filled in SIP, to hold all download element parameter.
+
+const SIP_PREFIX_BASE64 = '_b64';
+
 // FURTHER: all extracted params from 'urlparam
 
 const ACTION_KEYWORD_SLAVE_ID = 'slaveId';
@@ -853,15 +864,54 @@ const DOWNLOAD_MODE = 'mode';
 const DOWNLOAD_MODE_FILE = 'file';
 const DOWNLOAD_MODE_PDF = 'pdf';
 const DOWNLOAD_MODE_EXCEL = 'excel';
-const DOWNLOAD_EXPORT_FILENAME = 'exportFilename';
-const DOWNLOAD_PAGE_ID = 'id';
-const DOWNLOAD_FILE = 'file';
-const DOWNLOAD_FILE_PREFIX = 'qfq.temp';
+const DOWNLOAD_MODE_ZIP = 'zip';
+const DOWNLOAD_EXPORT_FILENAME = '_exportFilename';
+const DOWNLOAD_FILE_PREFIX = 'qfq.temp'; // temporary filename on server of single export file
 const DOWNLOAD_OUTPUT_PDF = 'output.pdf';
-const DOWNLOAD_PARAMETER_DELIMITER = '_';
+
 
 // HTML2PDF
 const HTML2PDF_PAGEID = 'id';
 const HTML2PDF_PARAM_GET = 'paramGet';
 const HTML2PDF_URL_PRINT = 'urlPrint';
 
+// Class: LINK
+const PARAM_DELIMITER = '|';
+
+const TOKEN_URL = 'u';
+const TOKEN_MAIL = 'm';
+const TOKEN_PAGE = 'p';
+const TOKEN_DOWNLOAD = 'd';
+
+const TOKEN_TEXT = 't';
+const TOKEN_ALT_TEXT = 'a';
+const TOKEN_TOOL_TIP = 'o';
+const TOKEN_PICTURE = 'P';
+const TOKEN_BULLET = 'B';
+const TOKEN_CHECK = 'C';
+const TOKEN_DELETE = 'D';
+const TOKEN_EDIT = 'E';
+const TOKEN_HELP = 'H';
+const TOKEN_INFO = 'I';
+const TOKEN_NEW = 'N';
+const TOKEN_SHOW = 'S';
+const TOKEN_RENDER = 'r';
+const TOKEN_TARGET = 'g';
+const TOKEN_CLASS = 'c';
+const TOKEN_QUESTION = 'q';
+const TOKEN_ENCRYPTION = 'e';
+const TOKEN_SIP = 's';
+const TOKEN_URL_PARAM = 'U';
+const TOKEN_RIGHT = 'R';
+const TOKEN_FILE = 'f';
+const TOKEN_DOWNLOAD_MODE = 'M';
+
+const TOKEN_ACTION_DELETE = 'x';
+const TOKEN_ACTION_DELETE_AJAX = 'a';
+const TOKEN_ACTION_DELETE_REPORT = 'r';
+const TOKEN_ACTION_DELETE_CLOSE = 'c';
+
+const TOKEN_CLASS_NONE = 'n';
+const TOKEN_CLASS_INTERNAL = 'i';
+const TOKEN_CLASS_EXTERNAL = 'e';
+
diff --git a/extension/qfq/qfq/QuickFormQuery.php b/extension/qfq/qfq/QuickFormQuery.php
index 3910f1649d235fb8f87f9cd52c0811777e773782..2c54063aa3a38852df6dc49feb9e0542e8095f32 100644
--- a/extension/qfq/qfq/QuickFormQuery.php
+++ b/extension/qfq/qfq/QuickFormQuery.php
@@ -183,6 +183,7 @@ class QuickFormQuery {
      */
     public function process() {
         $html = '';
+        $rcHasDownloadLinks = false;
 
         if ($this->store->getVar(TYPO3_DEBUG_SHOW_BODY_TEXT, STORE_TYPO3) === 'yes') {
             $htmlId = HelperFormElement::buildFormElementId($this->formSpec[F_ID], 0, 0, 0);
@@ -192,9 +193,16 @@ class QuickFormQuery {
         $html .= $this->doForm(FORM_LOAD);
         $html .= $this->doReport();
 
+        // Only needed if there are download which show a popup during rendering/downloading.
+        if ($this->store->getVar(SYSTEM_DOWNLOAD_POPUP, STORE_SYSTEM) == DOWNLOAD_POPUP_REQUEST) {
+            $html .= $this->getModalCode();
+        }
+
         $class = $this->store->getVar(SYSTEM_CSS_CLASS_QFQ_CONTAINER, STORE_SYSTEM);
-        if ($class)
+        if ($class) {
             $html = Support::wrapTag("<div class='$class'>", $html);
+        }
+
 
 //        $feUidLoggedIn = isset($GLOBALS["TSFE"]->fe_user->user["uid"]) ? $GLOBALS["TSFE"]->fe_user->user["uid"] : false;
 //        $feUidSession = $_SESSION[SESSION_NAME][SESSION_FE_USER_UID];
@@ -913,4 +921,35 @@ class QuickFormQuery {
         $this->store->setStore($tmpParam, STORE_SIP, true);
     }
 
+
+    /**
+     * @return string
+     */
+    private function getModalCode() {
+
+        $code = <<<EOF
+    <!-- Modal -->
+    <div class="modal fade" id="qfqModal101" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" id="qfqModalTitle101">Loading Document</h4>
+                </div>
+                <div class="modal-body" style="text-align: center;">
+                    <span class="glyphicon glyphicon-cog glyphicon-spin text-large-with-margin text-primary"></span>
+                    <p id="qfqModalText101">PDF Document is being generated. Please wait.</p>
+                </div>
+                <div class="modal-footer">
+                    <p>In progress..</p>
+                </div>
+            </div>
+        </div>
+    </div>
+EOF;
+
+        return $code;
+    }
+
+
 }
\ No newline at end of file
diff --git a/extension/qfq/qfq/report/Download.php b/extension/qfq/qfq/report/Download.php
index 0c0330cfb6583257335f54fe7866d677b8d09386..8023199983d9a0e1eae4265a084ec81af935c26e 100644
--- a/extension/qfq/qfq/report/Download.php
+++ b/extension/qfq/qfq/report/Download.php
@@ -4,6 +4,8 @@
  * User: crose
  * Date: 4/17/17
  * Time: 11:32 AM
+ *
+ * Check: CODING.md > Download
  */
 
 namespace qfq;
@@ -65,44 +67,6 @@ class Download {
         $this->html2pdf = new Html2Pdf($this->store->getStore(STORE_SYSTEM));
     }
 
-    /**
-     * Collect all elements, separated per element.
-     * Skip parameter which do not start with '<i>_' (i=integer).
-     * Create a numbered and sorted (by key) array, with all parameter belonging to one element in one subarray.
-     *
-     * E.g.: exportFilename=final.pdf&mode=pdf&1_id=exportP1&2_id=123&3_file=fileadmin/pdf/test.pdf&1_grId=78
-     * result [
-     *          [1] => [ 'id' => 'exportP1', 'grId' = '78' ],
-     *          [2] => [ 'id' => '123' ],
-     *          [3] => [ 'file' => 'fileadmin/pdf/test.pdf' ],
-     *          [mode] => 'pdf|file|excel',
-     *          [exportFilename] => '....'
-     *        ]
-     *
-     * @param array $vars
-     * @return array
-     */
-    private function collectElement(array $vars) {
-        $final = array();
-
-        foreach ($vars as $key => $value) {
-            $splitArr = explode(DOWNLOAD_PARAMETER_DELIMITER, $key, 2);
-            $idx = $splitArr[0];
-            if ((count($splitArr) == 2) && is_numeric($idx) && !isset($final[$idx])) {
-                $arr = OnArray::getArrayItemKeyNameStartWith($vars, $idx . DOWNLOAD_PARAMETER_DELIMITER);
-                if (count($arr) > 0) {
-                    $final[$idx] = $arr;
-                }
-
-            }
-        }
-
-        // sort array
-        ksort($final, SORT_NATURAL);
-
-        return $final;
-    }
-
     /**
      * Concatenate all named files to one PDF file. Return name of new full PDF.
      *
@@ -156,7 +120,11 @@ class Download {
 
         header("Content-type: $mimetype");
         header("Content-Length: $length");
+        // No idea if 'attachment' has disadvantages.
+//        header("Content-Disposition: attachment; filename=$outputFilename");
         header("Content-Disposition: inline; filename=$outputFilename");
+        header("Pragma: no-cache");
+        header("Expires: 0");
 
         print file_get_contents($filename);
     }
@@ -178,32 +146,70 @@ class Download {
     }
 
     /**
-     * @param $element
-     * @return mixed
+     * Interprets $element and fetches corresponding content as file.
+     *
+     * @param string $element - U:id=myExport&r=12, u:http://www.nzz.ch/issue?nr=21, f:fileadmin/sample.pdf
+     * @return string filename - already ready or fresh exported. Fresh exported needs to be deleted later.
      * @throws DownloadException
      * @throws \exception
      */
-    private function getFile($element) {
-//        $first = each($element);
-//        $key = $first['key'];
-//        $arr = explode(DOWNLOAD_PARAMETER_DELIMITER, $key, 2);
-//        $idx = $arr[0];
-
-        if (isset($element[DOWNLOAD_FILE])) {
+    private function getElement($element) {
 
-            $filename = $element[DOWNLOAD_FILE];
+        $arr = explode(':', $element, 2);
+        if (count($arr) != 2) {
+            throw new DownloadException('Missing parameter for "' . $element . '"', ERROR_MISSING_REQUIRED_PARAMETER);
+        }
 
-        } elseif (isset($element[DOWNLOAD_PAGE_ID])) {
+        $token = $arr[0];
+        $value = $arr[1];
 
-            $filename = $this->html2pdf->page2pdf($element);
+        switch ($token) {
+            case TOKEN_URL:
+            case TOKEN_URL_PARAM:
+                $filename = $this->html2pdf->page2pdf($token, $value);
+                break;
 
-        } else {
-            throw new DownloadException('Neither found: ' . DOWNLOAD_FILE . ', ' . DOWNLOAD_PAGE_ID, ERROR_MISSING_REQUIRED_PARAMETER);
+            case TOKEN_FILE:
+                $filename = $value;
+                break;
+            default:
+                throw new DownloadException('Unknown token: "' . $token . '"', ERROR_UNKNOWN_TOKEN);
+                break;
         }
 
         return $filename;
     }
 
+
+    /**
+     * Creates a ZIP Files of all given $files
+     *
+     * @param array $files
+     * @return string ZIP filename - has to be deleted later.
+     * @throws DownloadException
+     */
+    private function zipFiles(array $files) {
+
+        $zipFile = tempnam(sys_get_temp_dir(), DOWNLOAD_FILE_PREFIX);
+        if (false === $zipFile) {
+            throw new DownloadException("Error creating output file.", ERROR_DOWNLOAD_CREATE_NEW_FILE);
+        }
+
+        $zip = new \ZipArchive();
+
+        if ($zip->open($zipFile, \ZipArchive::CREATE) !== TRUE) {
+            throw new DownloadException("Error creating/opening new empty zip file: $zipFile", ERROR_IO_OPEN);
+        }
+
+        foreach ($files AS $filename) {
+            $localname = substr($filename, strrpos($filename, '/'));
+            $zip->addFile($filename, $localname);
+        }
+        $zip->close();
+
+        return $zipFile;
+    }
+
     /**
      * exportFilename=<new filename>
      * mode=file | pdf | excel - default is 'file' in case of only one or 'pdf' in case of multiple sources.
@@ -213,10 +219,11 @@ class Download {
      * Direct
      * <i>_file=<filename>
      *
-     * @param array $elements
+     * @param array $vars [ DOWNLOAD_EXPORT_FILENAME, DOWNLOAD_MODE, SIP_DOWNLOAD_PARAMETER ]
      * @throws DownloadException
+     * @internal param array $elements
      */
-    private function doElements(array $elements, array $vars) {
+    private function doElements(array $vars) {
 
         $tmpFiles = array();
 
@@ -225,31 +232,35 @@ class Download {
             throw new DownloadException ("Error chdir($workDir)", ERROR_IO_CHDIR);
         }
 
-        $exportFilename = isset($vars[DOWNLOAD_EXPORT_FILENAME]) ? $vars[DOWNLOAD_EXPORT_FILENAME] : '';
+        $exportFilename = empty($vars[DOWNLOAD_EXPORT_FILENAME]) ? DOWNLOAD_OUTPUT_PDF : $vars[DOWNLOAD_EXPORT_FILENAME];
+        $downloadMode = $vars[DOWNLOAD_MODE];
+        $elements = explode(PARAM_DELIMITER, $vars[SIP_DOWNLOAD_PARAMETER]);
 
+        // Get all files
         foreach ($elements as $element) {
-            $tmpFiles[] = $this->getFile($element);
+            $tmpFiles[] = $this->getElement($element);
         }
 
-        switch (count($tmpFiles)) {
-            case 0:
-                throw new DownloadException('Nothing to do.', ERROR_DOWNLOAD_NOTHING_TO_DO);
+        // Export, Concat File(s)
+        switch ($downloadMode) {
+            case DOWNLOAD_MODE_ZIP:
+                $filename = $this->zipFiles($tmpFiles);
+                break;
+
+            case DOWNLOAD_MODE_EXCEL:
+                throw new DownloadException("Not implemented: $downloadMode", ERROR_NOT_IMPLEMENTED);
                 break;
 
-            case 1:
+            case DOWNLOAD_MODE_FILE:
                 $filename = $tmpFiles[0];
-                if ($exportFilename == '') {
-                    $exportFilename = strrchr($filename, '/');
-                    if ($exportFilename == false) {
-                        $exportFilename = DOWNLOAD_OUTPUT_PDF;
-                    }
-                }
                 break;
-            default:
+
+            case DOWNLOAD_MODE_PDF:
                 $filename = $this->concatPdfFiles($tmpFiles);
-                if ($exportFilename == '') {
-                    $exportFilename = DOWNLOAD_OUTPUT_PDF;
-                }
+                break;
+
+            default:
+                throw new DownloadException("Unknown downloadMode: $downloadMode", ERROR_UNKNOWN_MODE);
                 break;
         }
 
@@ -267,8 +278,8 @@ class Download {
     public function process() {
 
         $vars = $this->store->getStore(STORE_SIP);
-        $elements = $this->collectElement($vars);
-        $this->doElements($elements, $vars);
+//        $elements = $this->collectElement($vars);
+        $this->doElements($vars);
     }
 }
 
diff --git a/extension/qfq/qfq/report/Html2Pdf.php b/extension/qfq/qfq/report/Html2Pdf.php
index 44c219e5d895141b318765de3d1119fa6ebfbe6a..1657dea81260a11fcb834ba403f7733d42ed649b 100644
--- a/extension/qfq/qfq/report/Html2Pdf.php
+++ b/extension/qfq/qfq/report/Html2Pdf.php
@@ -4,6 +4,9 @@
  * User: crose
  * Date: 4/17/17
  * Time: 10:17 PM
+ *
+ * Check: CODING.md > 'Print' and 'Download'
+ *
  */
 
 namespace qfq;
@@ -83,26 +86,44 @@ class Html2Pdf {
     }
 
     /**
-     * @return string
+     * Converts a Webpag (URL) to a PDF file.
+     * The URL might be a local Typo3 page or a full URL.
+     *
+     * @param string $token TOKEN_URL | TOKEN_URL_PARAM
+     * @param string $url id=exportPage&r=123, www.nzz.ch/issue?id=456
+     * @return string        rendered file - please delete later
      * @throws \exception
      */
-    public function page2pdf(array $get) {
+    public function page2pdf($token, $url) {
+        $args = array();
+        $urlParamNew = array();
 
-        if (!isset($get[HTML2PDF_PAGEID]) || $get[HTML2PDF_PAGEID] == '') {
-            throw new \exception("Missing GET Parameter '" . HTML2PDF_PAGEID . "'.");
+        if ($token == TOKEN_URL_PARAM) {
+            $url = $this->config[SYSTEM_BASE_URL_PRINT] . '/?' . $url;
         }
 
-        $urlPrint = escapeshellarg($this->config[SYSTEM_BASE_URL_PRINT] . '/?' . KeyValueStringParser::unparse($get, '=', '&'));
-        $wkhtml = $this->config[SYSTEM_WKHTMLTOPDF];
+        $urlParam = KeyValueStringParser::parse($url, '=', '&');
+        foreach ($urlParam as $key => $value) {
+            if (substr($key, 0, 1) == '-') {
+                $args[$key] = $value;
+            } else {
+                $urlParamNew[$key] = $value;
+            }
+        }
+
+        $args = OnArray::arrayEscapeshellarg($args);
+        $options = KeyValueStringParser::unparse($args, ' ', ' ');
+        $url = KeyValueStringParser::unparse($urlParamNew, '=', '&');
+
+        $urlPrint = escapeshellarg($url);
+        $wkhtmlToPdf = $this->config[SYSTEM_WKHTMLTOPDF];
 
-        $rc = 0;
         $filename = tempnam(sys_get_temp_dir(), DOWNLOAD_FILE_PREFIX);
         $filenameEscape = escapeshellarg($filename);
 
-//        $cmd = $wkhtml . " '" . $urlPrint . "' " . $filename . " > $filename.log 2>&1";
-//        $cmd = "$wkhtml '$urlPrint' $filename > $filename.log 2>&1";
-        $cmd = "$wkhtml $urlPrint $filenameEscape";
+        $cmd = "$wkhtmlToPdf $options $urlPrint $filenameEscape";
 
+        $rc = 0;
         $line = system($cmd, $rc);
 
         if ($rc != 0) {
@@ -118,9 +139,10 @@ class Html2Pdf {
     public function outputHtml2Pdf() {
 
         $get = $this->readCleanGetParam($_GET);
+        $urlParam = KeyValueStringParser::unparse($get, '=', '&');
         $pageId = Support::setIfNotSet($get, HTML2PDF_PAGEID, 0);
 
-        $filename = $this->page2pdf($get);
+        $filename = $this->page2pdf(TOKEN_URL_PARAM, $urlParam);
 
         $this->setHeader('print.' . $pageId . '.pdf');
         @readfile($filename);
diff --git a/extension/qfq/qfq/report/Link.php b/extension/qfq/qfq/report/Link.php
index b8e849dea513893303f46c9e7c27d563a8303330..05f549aed692df523430079c6cc3f3366a4f7b89 100644
--- a/extension/qfq/qfq/report/Link.php
+++ b/extension/qfq/qfq/report/Link.php
@@ -68,7 +68,9 @@ const NAME_URL = 'url';
 const NAME_MAIL = 'mail';
 const NAME_PAGE = 'page';
 const NAME_TEXT = 'text';
-const NAME_DOWNLOAD = 'download';
+const NAME_DOWNLOAD = DOWNLOAD_EXPORT_FILENAME;
+const NAME_DOWNLOAD_ELEMENTS = 'downloadElements';  // array with element sources
+const NAME_DOWNLOAD_MODE = 'mode';
 const NAME_ALT_TEXT = 'altText';
 const NAME_TOOL_TIP = 'toolTip';
 const NAME_TOOL_TIP_JS = 'toolTipJs';
@@ -88,6 +90,7 @@ const NAME_URL_PARAM = 'param';
 const NAME_RIGHT = 'picturePositionRight';
 const NAME_ACTION_DELETE = 'actionDelete';
 const NAME_EXTRA_CONTENT_WRAP = 'extraContentWrap';
+const NAME_FILE = 'file';
 
 const FINAL_HREF = 'finalHref';
 const FINAL_ANCHOR = 'finalAnchor';
@@ -97,40 +100,6 @@ const FINAL_TOOL_TIP = 'finalToolTip';
 const FINAL_CLASS = 'finalClass';
 const FINAL_QUESTION = 'finalQuestion';
 
-const TOKEN_URL = 'u';
-const TOKEN_MAIL = 'm';
-const TOKEN_PAGE = 'p';
-const TOKEN_TEXT = 't';
-const TOKEN_DOWNLOAD = 'd';
-const TOKEN_ALT_TEXT = 'a';
-const TOKEN_TOOL_TIP = 'o';
-const TOKEN_PICTURE = 'P';
-const TOKEN_BULLET = 'B';
-const TOKEN_CHECK = 'C';
-const TOKEN_DELETE = 'D';
-const TOKEN_EDIT = 'E';
-const TOKEN_HELP = 'H';
-const TOKEN_INFO = 'I';
-const TOKEN_NEW = 'N';
-const TOKEN_SHOW = 'S';
-const TOKEN_RENDER = 'r';
-const TOKEN_TARGET = 'g';
-const TOKEN_CLASS = 'c';
-const TOKEN_QUESTION = 'q';
-const TOKEN_ENCRYPTION = 'e';
-const TOKEN_SIP = 's';
-const TOKEN_URL_PARAM = 'U';
-const TOKEN_RIGHT = 'R';
-
-const TOKEN_ACTION_DELETE = 'x';
-const TOKEN_ACTION_DELETE_AJAX = 'a';
-const TOKEN_ACTION_DELETE_REPORT = 'r';
-const TOKEN_ACTION_DELETE_CLOSE = 'c';
-
-const TOKEN_CLASS_NONE = 'n';
-const TOKEN_CLASS_INTERNAL = 'i';
-const TOKEN_CLASS_EXTERNAL = 'e';
-
 const LINK_ANCHOR = 'linkAnchor';
 const LINK_PICTURE = 'linkPicture';
 
@@ -189,6 +158,7 @@ class Link {
         TOKEN_INFO => 'buildInfo',
         TOKEN_NEW => 'buildNew',
         TOKEN_SHOW => 'buildShow',
+        TOKEN_FILE => 'buildFile',
     ];
 
     private $tableVarName = [
@@ -196,6 +166,7 @@ class Link {
         TOKEN_MAIL => NAME_MAIL,
         TOKEN_PAGE => NAME_PAGE,
         TOKEN_DOWNLOAD => NAME_DOWNLOAD,
+        TOKEN_DOWNLOAD_MODE => NAME_DOWNLOAD_MODE,
         TOKEN_TEXT => NAME_TEXT,
         TOKEN_ALT_TEXT => NAME_ALT_TEXT,
         TOKEN_TOOL_TIP => NAME_TOOL_TIP,
@@ -217,6 +188,7 @@ class Link {
         TOKEN_URL_PARAM => NAME_URL_PARAM,
         TOKEN_RIGHT => NAME_RIGHT,
         TOKEN_ACTION_DELETE => NAME_ACTION_DELETE,
+        TOKEN_FILE => NAME_FILE,
     ];
 
     // Used to find double definitions.
@@ -305,11 +277,10 @@ class Link {
      * Build the whole link
      *
      * @param string $str Qualifier with params. 'report'-syntax. F.e.:  A:u:www.example.com|G:P:home.gif|t:Home"
-     * @param string $rcHtmlIdModalDownloadDialog - If the link is a download link, set it to true.
      * @return string The complete HTML encoded Link like <a href='http://example.com' class='external'><img src='iconf.gif' title='help text'>Description</a>
      * @throws UserReportException
      */
-    public function renderLink($str, &$rcHtmlIdModalDownloadDialog) {
+    public function renderLink($str) {
 
         $tokenGiven = array();
 
@@ -317,9 +288,13 @@ class Link {
             return '';
 
         $vars = $this->fillParameter($str, $tokenGiven);
-        $vars = $this->processParameter($vars);
+        $vars = $this->processParameter($vars, $tokenGiven);
         $mode = $this->getModeRender($vars);
 
+        if (isset($tokenGiven[TOKEN_DOWNLOAD]) && $tokenGiven[TOKEN_DOWNLOAD] === true) {
+            $this->store->setVar(SYSTEM_DOWNLOAD_POPUP, DOWNLOAD_POPUP_REQUEST, STORE_SYSTEM);
+        }
+
         $link = '';
 
         // 0-4 URL, plain email
@@ -354,11 +329,9 @@ class Link {
             case '3':
 //                $link = $htmlUrl . $vars[NAME_URL] . '</a>' . $vars[NAME_TOOL_TIP_JS][1];
                 $link = Support::wrapTag($vars[FINAL_ANCHOR], $vars[FINAL_HREF]);
-                $rcHtmlIdModalDownloadDialog = $this->getHtmlIdModalDownloadDialog();
                 break;
             case '13':
                 $link = Support::wrapTag($vars[FINAL_ANCHOR], $vars[FINAL_HREF]);
-                $rcHtmlIdModalDownloadDialog = $this->getHtmlIdModalDownloadDialog();
 //                $vars[NAME_TEXT] = $vars[NAME_MAIL];
 //                $link = $this->encryptMailtoJS($vars, true);
                 break;
@@ -366,11 +339,9 @@ class Link {
             // 4: <a href=url ...>Text</a>
             case '4':
                 $link = Support::wrapTag($vars[FINAL_ANCHOR], $vars[FINAL_CONTENT]);
-                $rcHtmlIdModalDownloadDialog = $this->getHtmlIdModalDownloadDialog();
                 break;
             case '14':
                 $link = Support::wrapTag($vars[FINAL_ANCHOR], $vars[FINAL_CONTENT]);
-                $rcHtmlIdModalDownloadDialog = $this->getHtmlIdModalDownloadDialog();
 //                $link = $this->encryptMailtoJS($vars, true);
                 break;
             case '21':
@@ -393,14 +364,14 @@ class Link {
      */
     private function fillParameter($str, array &$tokenGiven) {
 
-        // define all possible vars: no more isset()
+        // Define all possible vars: no more isset().
         $vars = $this->initVars();
         $flagArray = array();
 
         // str="u:http://www.example.com|c:i|t:Hello World|q:Do you really want to delete the record 25:warn:yes:no"
-        $param = explode("|", $str);
+        $param = explode(PARAM_DELIMITER, $str);
 
-        // Parse all parameter, fill variables
+        // Parse all parameter, fill variables.
         foreach ($param as $item) {
 
             // Skip empty entries
@@ -422,15 +393,23 @@ class Link {
             if (!isset($this->tableVarName[$key])) {
                 throw new UserReportException ("Unknown link qualifier: '$key' - do you forget the one character qualifier?", ERROR_UNKNOWN_LINK_QUALIFIER);
             }
+
             $keyName = $this->tableVarName[$key]; // convert token to name
 
-            if ($value === '') {
-                $value = $this->checkEmptyValue($key);
-            }
+            $value = $this->checkForEmptyValue($key, $value);
             $value = $this->checkValue($key, $value);
 
             // Store value
-            $vars[$keyName] = $value;
+            if (isset($tokenGiven[TOKEN_DOWNLOAD]) && ($key == TOKEN_URL || $key == TOKEN_URL_PARAM || $key == TOKEN_FILE)) {
+
+                $vars[NAME_DOWNLOAD_ELEMENTS][] = $key . ':' . $value;
+
+                unset($tokenGiven[$key]); // Skip Bookkeeping for TOKEN_URL_PARAM | TOKEN_FILE | TOKEN_URL.
+                continue;
+            } else {
+                $vars[$keyName] = $value;
+            }
+
 
             // Check for double anchor or picture definition
             if (isset($this->tokenMapping[$key])) {
@@ -454,7 +433,7 @@ class Link {
         }
 
         // Final Checks
-        $this->checkParam($tokenGiven);
+        $this->checkParam($tokenGiven, $vars);
 
         return $vars;
     }
@@ -483,6 +462,8 @@ class Link {
             NAME_TOOL_TIP_JS => '',
             NAME_URL_PARAM => '',
             NAME_EXTRA_CONTENT_WRAP => '',
+            NAME_DOWNLOAD_MODE => '',
+            NAME_DOWNLOAD_ELEMENTS => array(),
 
             NAME_RENDER => '0',
             NAME_RIGHT => 'l',
@@ -509,10 +490,15 @@ class Link {
      * Verify Empty values. If appropriate, set defaults, if not throw an exception.
      *
      * @param string $key
+     * @param string $value
      * @return string
      * @throws UserReportException
      */
-    private function checkEmptyValue($key) {
+    private function checkForEmptyValue($key, $value) {
+
+        if ($value !== '') {
+            return $value;
+        }
         $value = '';
 
         switch ($key) {
@@ -587,7 +573,7 @@ class Link {
      * @param array $tokenGiven
      * @throws UserReportException
      */
-    private function checkParam(array $tokenGiven) {
+    private function checkParam(array $tokenGiven, array $vars) {
         $countLinkAnchor = 0;
         $countLinkPicture = 0;
 
@@ -617,18 +603,23 @@ class Link {
         if (isset($tokenGiven[TOKEN_MAIL]) && isset($tokenGiven[TOKEN_TARGET])) {
             throw new UserReportException ("Token Mail and Target at the same time not possible'" . TOKEN_PAGE . "'", ERROR_MULTIPLE_DEFINITION);
         }
+
+        if (isset($tokenGiven[TOKEN_DOWNLOAD]) && count($vars[NAME_DOWNLOAD_ELEMENTS]) == 0) {
+            throw new UserReportException ("Missing element sources for download", ERROR_MISSING_REQUIRED_PARAMETER);
+        }
     }
 
     /**
      * Compute final link parameter.
      *
      * @param array $vars
+     * @param array $tokenGiven
      * @return string
      * @throws UserReportException
      */
-    private function processParameter(array $vars) {
+    private function processParameter(array $vars, array $tokenGiven) {
 
-        $vars[FINAL_HREF] = $this->doHref($vars); // must be called before doToolTip()
+        $vars[FINAL_HREF] = $this->doHref($vars, $tokenGiven); // must be called before doToolTip()
         $vars[FINAL_TOOL_TIP] = $this->doToolTip($vars);
         $vars[FINAL_CLASS] = $this->doCssClass($vars);
         $vars[FINAL_SYMBOL] = $this->doSymbol($vars);
@@ -639,16 +630,70 @@ class Link {
         return $vars;
     }
 
+    /**
+     * @param array $vars
+     * @return string
+     * @throws UserFormException
+     */
+    private function getDownloadModeNCheck(array $vars) {
+
+        $cnt = count($vars[NAME_DOWNLOAD_ELEMENTS]);
+        $mode = $vars[NAME_DOWNLOAD_MODE];
+
+        // Determine default.
+        if ($mode == '') {
+            if ($cnt == 1) {
+                $mode = (substr($vars[NAME_DOWNLOAD_ELEMENTS][0], 0, 1) == TOKEN_FILE) ? DOWNLOAD_MODE_FILE : DOWNLOAD_MODE_PDF;
+            } else {
+                $mode = DOWNLOAD_MODE_PDF;
+            }
+        }
+
+        // Do some checks.
+        switch ($mode) {
+            case DOWNLOAD_MODE_PDF:
+            case DOWNLOAD_MODE_ZIP:
+                break;
+            case DOWNLOAD_MODE_FILE:
+            case DOWNLOAD_MODE_EXCEL:
+                if ($cnt > 1) {
+                    throw new UserFormException("With 'downloadMode' = 'file' or 'excle', only one element source is allowed.", ERROR_DOWNLOAD_UNEXPECTED_NUMBER_OF_SOURCES);
+                }
+                break;
+            default:
+                throw new UserFormException("Unknown mode: $mode", ERROR_UNKNOWN_MODE);
+                break;
+
+        }
+
+        return $mode;
+    }
+
     /**
      * Concat final HREF string
      *
      * @param array $vars
+     * @param array $tokenGiven
      * @return string
+     * @throws CodeException
      * @throws UserReportException
      */
-    private function doHref(array &$vars) {
+    private function doHref(array &$vars, array $tokenGiven) {
         $urlNParam = '';
 
+
+        if (isset($tokenGiven[TOKEN_DOWNLOAD])) {
+            // Message in download popup.
+            $altText = ($vars[NAME_ALT_TEXT] == '') ? 'Please wait' : addslashes($vars[NAME_ALT_TEXT]);
+            $vars[NAME_EXTRA_CONTENT_WRAP] = str_replace(DOWNLOAD_POPUP_REPLACE_TEXT, $altText, $vars[NAME_EXTRA_CONTENT_WRAP]);
+            $vars[NAME_EXTRA_CONTENT_WRAP] = str_replace(DOWNLOAD_POPUP_REPLACE_TITLE, 'Download: ' . addslashes($vars[NAME_DOWNLOAD]), $vars[NAME_EXTRA_CONTENT_WRAP]);
+
+            $tmpUrlParam[DOWNLOAD_MODE] = $this->getDownloadModeNCheck($vars);
+            $tmpUrlParam[DOWNLOAD_EXPORT_FILENAME] = $vars[NAME_DOWNLOAD];
+            $tmpUrlParam[SIP_DOWNLOAD_PARAMETER] = base64_encode(implode(PARAM_DELIMITER, $vars[NAME_DOWNLOAD_ELEMENTS]));
+            $vars[NAME_URL_PARAM] = KeyValueStringParser::unparse($tmpUrlParam, '=', '&');
+        }
+
         if ($vars[NAME_ACTION_DELETE] !== '') {
             $vars[NAME_URL_PARAM] = $this->adjustDeleteParameter($vars[NAME_ACTION_DELETE], $vars[NAME_URL_PARAM]);
         }
@@ -667,7 +712,7 @@ class Link {
                 $urlNParam = $paramArray['_url'];
 
                 if ($this->store->getVar(SYSTEM_SHOW_DEBUG_INFO, STORE_SYSTEM) === 'yes') {
-                    $vars[NAME_TOOL_TIP] .= PHP_EOL . PHP_EOL . OnArray::toString($paramArray, ' = ', PHP_EOL, "'");
+                    $vars[NAME_TOOL_TIP] .= PHP_EOL . PHP_EOL . $this->sip->debugSip($paramArray);
                 }
             }
             // Link: MAILTO
@@ -1069,13 +1114,20 @@ EOF;
      */
     private function buildDownload($vars, $value) {
 
-        $htmlIdModal = $this->getHtmlIdModalDownloadDialog();
+        $text = DOWNLOAD_POPUP_REPLACE_TEXT;
+        $title = DOWNLOAD_POPUP_REPLACE_TITLE;
+
+        $attributes = 'class="btn btn-default" data-toggle="modal" data-target="#qfqModal101"  data-backdrop="static" data-keyboard="false" ';
+        $onClick = <<<EOF
+           onclick="$('#qfqModalTitle101').text($(this).data('$title')); $('#qfqModalText101').text($(this).data('$text'));"
+EOF;
 
         $vars[NAME_GLYPH] = GLYPH_ICON_FILE;
         $vars[NAME_GLYPH_TITLE] = "Download";
         $vars[NAME_URL] = API_DIR . '/' . API_DOWNLOAD_PHP;
         $vars[NAME_LINK_CLASS_DEFAULT] = NO_CLASS;
-        $vars[NAME_EXTRA_CONTENT_WRAP] = '<button type="button" class="btn btn-default" data-toggle="modal" data-target="#' . $htmlIdModal . '" data-backdrop="static" data-keyboard="false">';
+        $vars[NAME_EXTRA_CONTENT_WRAP] = '<button type="button" ' . $attributes . $onClick . '>';
+        $vars[NAME_DOWNLOAD] = ($value == '') ? DOWNLOAD_OUTPUT_PDF : $value; // Download ExportFileName
 
         return $vars;
     }
@@ -1322,11 +1374,4 @@ EOF;
 
         return $vars;
     }
-
-    /**
-     * @return string
-     */
-    private function getHtmlIdModalDownloadDialog() {
-        return 'qfqModal' . $this->ttContentUid;
-    }
 }
\ No newline at end of file
diff --git a/extension/qfq/qfq/report/Report.php b/extension/qfq/qfq/report/Report.php
index b632eb596002229a5ec3d5c67863347eb7938000..8cac9aea3655fe137342edfe6dfe257680797a07 100644
--- a/extension/qfq/qfq/report/Report.php
+++ b/extension/qfq/qfq/report/Report.php
@@ -81,11 +81,6 @@ class Report {
 
     private $showDebugInfo = false;
 
-    /**
-     * @var string - will be set as soon as there is at least one download element. All download elements will reference this id.
-     */
-    private $rcHtmlIdModalDownloadDialog = false;
-
     /**
      * Report constructor.
      *
@@ -481,43 +476,9 @@ class Report {
             }
         }
 
-        if ($this->rcHtmlIdModalDownloadDialog != '') {
-            $content .= $this->getModalCode($this->rcHtmlIdModalDownloadDialog);
-        }
-
         return $content;
     }
 
-    private function getModalCode($htmlId, $title = "Loading Document", $message = "Document is being generated. Please wait.") {
-        /*
-         *      <!-- Button trigger modal -->
-     <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#qfqModal101" data-backdrop="static" data-keyboard="false">
-         <span class="glyphicon glyphicon-search"></span>
-     </button>
- */
-
-        $code = <<<EOF
-     <!-- Modal -->
-     <div class="modal fade" id="$htmlId" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
-         <div class="modal-dialog" role="document">
-             <div class="modal-content">
-                 <div class="modal-header">
-                     <h4 class="modal-title" id="myModalLabel">$title</h4>
-                 </div>
-                 <div class="modal-body" style="text-align: center;">
-                     <span class="glyphicon glyphicon-cog glyphicon-spin text-large-with-margin text-primary"></span>
-                     <p>$message</p>
-                 </div>
-                 <div class="modal-footer">
-                     <p>In Progress..</p>
-                 </div>
-             </div>
-         </div>
-     </div>
-EOF;
-
-        return $code;
-    }
     /**
      * Determine value:
      * 1) if one specified in line: take it
@@ -608,7 +569,7 @@ EOF;
         //TODO: reserved names,not starting with '_' will be still accepted - stop this!
         switch ($columnName) {
             case "link":
-                $content .= $this->link->renderLink($columnValue, $this->rcHtmlIdModalDownloadDialog);
+                $content .= $this->link->renderLink($columnValue);
                 break;
 
             case "exec":
@@ -627,7 +588,7 @@ EOF;
                 $pageColumnName = strtolower($columnName);
                 $tokenizedValue = $this->doFixColPosPage($columnName, $columnValue);
                 $linkValue = $this->doPage($pageColumnName, $tokenizedValue);
-            $content .= $this->link->renderLink($linkValue, $dummy);
+            $content .= $this->link->renderLink($linkValue);
                 break;
 
             // Lowercase 'P'
@@ -640,18 +601,18 @@ EOF;
             case COLUMN_PAGEN:
             case COLUMN_PAGES:
                 $linkValue = $this->doPage($columnName, $columnValue);
-            $content .= $this->link->renderLink($linkValue, $dummy);
+            $content .= $this->link->renderLink($linkValue);
                 break;
 
             case COLUMN_DDOWNLOAD:
                 $tokenizedValue = $this->doFixColPosDownload($columnValue);
                 $linkValue = $this->doDownload($tokenizedValue);
-                $content .= $this->link->renderLink($linkValue, $this->rcHtmlIdModalDownloadDialog);
+                $content .= $this->link->renderLink($linkValue);
                 break;
 
             case COLUMN_DOWNLOAD:
                 $linkValue = $this->doDownload($columnValue);
-                $content .= $this->link->renderLink($linkValue, $this->rcHtmlIdModalDownloadDialog);
+                $content .= $this->link->renderLink($linkValue);
                 break;
 
             case "bullet":
@@ -661,7 +622,7 @@ EOF;
 
                 // r:3|B:
                 $linkValue = TOKEN_RENDER . ":3|" . TOKEN_BULLET . ":" . $columnValue;
-                $content .= $this->link->renderLink($linkValue, $dummy);
+                $content .= $this->link->renderLink($linkValue);
                 break;
 
             case "check":
@@ -671,7 +632,7 @@ EOF;
 
                 // "r:3|C:
                 $linkValue = TOKEN_RENDER . ":3|" . TOKEN_CHECK . ":" . $columnValue;
-                $content .= $this->link->renderLink($linkValue, $dummy);
+                $content .= $this->link->renderLink($linkValue);
                 break;
 
             case "img":
@@ -1100,9 +1061,11 @@ EOF;
                 case TOKEN_SIP:
                     $defaultSip = '';    // if any of the img token is given: no default
                     break;
+
                 case TOKEN_DOWNLOAD:
                     $defaultDownload = '';
                     break;
+
                 default:
                     break;
             }
diff --git a/extension/qfq/qfq/store/Sip.php b/extension/qfq/qfq/store/Sip.php
index dbbf3d287b37edda4ea8a2ddf9a20b20116f3dcb..c23f203236f32ab1e7496956b71f135364a7d20c 100644
--- a/extension/qfq/qfq/store/Sip.php
+++ b/extension/qfq/qfq/store/Sip.php
@@ -281,6 +281,32 @@ class Sip {
         return KeyValueStringParser::parse($sessionVar, "=", "&");
     }
 
+    /**
+     * Formats the content of sip. Per variable one line. Decode base64 encoded variables.
+     * If $vars is an array, iterate over it.
+     * If $vars is a string, than this is the SIP - retrieve parameter from SIP and process those.
+     *
+     * @param string|array $vars
+     * @return string
+     * @throws \qfq\UserFormException
+     */
+    public function debugSip($vars) {
+
+        if (!is_array($vars)) {
+            $vars = $this->getVarsFromSip($vars);
+        }
+
+        // Detect and decode base64 content
+        $len = strlen(SIP_PREFIX_BASE64);
+        foreach ($vars as $key => $value) {
+            if (substr($key, 0, $len) == SIP_PREFIX_BASE64) {
+                $vars[$key] = base64_decode($value);
+            }
+        }
+
+        return OnArray::toString($vars, ' = ', PHP_EOL, "'");
+    }
+
     /**
      * Returns the sip for the given querystring. The querystring has to be sorted.
      *
diff --git a/extension/qfq/qfq/store/Store.php b/extension/qfq/qfq/store/Store.php
index e9575e77b11526b43842e73a444df312236b2597..273031d2c88e9f565de10a440496f9f371e03c51 100644
--- a/extension/qfq/qfq/store/Store.php
+++ b/extension/qfq/qfq/store/Store.php
@@ -91,8 +91,8 @@ class Store {
 //        self::$session = Session::getInstance(self::$phpUnit);
 
         // This check is critical for some unwanted exception recursion during startup.
-        if(!function_exists('normalizer_normalize')) {
-            throw new CodeException("Function normalizer_normalize() not found - Please install 'php5-intl' / 'php7.0-intl'", ERROR_MISSING_INTL );
+        if (!function_exists('normalizer_normalize')) {
+            throw new CodeException("Function normalizer_normalize() not found - Please install 'php5-intl' / 'php7.0-intl'", ERROR_MISSING_INTL);
         }
 
         self::$sanitizeClass = [
@@ -212,7 +212,7 @@ class Store {
      * @param array $config
      * @return array
      */
-    private function doSystemPath(array $config) {
+    private static function doSystemPath(array $config) {
 
         // SYSTEM_PATH_EXT: compute only if not already defined.
         if (!isset($config[SYSTEM_PATH_EXT]) || $config[SYSTEM_PATH_EXT] === '' || $config[SYSTEM_PATH_EXT][0] !== '/') {
@@ -416,6 +416,8 @@ class Store {
             $sanitizeClass = isset(self::$sanitizeClass[$key]) ? self::$sanitizeClass[$key] : SANITIZE_DEFAULT;
         }
 
+        $len = strlen(SIP_PREFIX_BASE64);
+
         while ($useStores !== false) {
             $store = substr($useStores, 0, 1); // next store
 
@@ -454,6 +456,9 @@ class Store {
                 }
                 return \qfq\Sanitize::sanitize($rawVal, $sanitizeClass, '', SANATIZE_EMPTY_STRING);
             } else {
+                if ($store == STORE_SIP && (substr($key, 0, $len) == SIP_PREFIX_BASE64)) {
+                    $rawVal = base64_decode($rawVal);
+                }
                 return $rawVal;
             }
         }
@@ -668,18 +673,44 @@ class Store {
      * @throws \qfq\CodeException
      */
     public static function getStore($store) {
+        $vars = array();
+
         // Check valid Storename
-        if (!isset(self::$sanitizeStore[$store]))
+        if (!isset(self::$sanitizeStore[$store])) {
             throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
+        }
 
-        if ($store === STORE_ZERO)
+        if ($store === STORE_ZERO) {
             throw new CodeException("getStore() for STORE_ZERO is impossible - there are no values saved.", ERROR_GET_STORE_ZERO);
+        }
 
         if (isset(self::$raw[$store])) {
-            return self::$raw[$store];
+            $vars = self::$raw[$store];
+            if ($store == STORE_SIP) {
+                $vars = self::checkDecodeBase64Arr($vars);
+            }
         }
 
-        return array();
+        return $vars;
+    }
+
+    /**
+     * Iterate over an array and look for keynames starting with SIP_PREFIX_BASE64.
+     * Found elements will be base64_decode().
+     *
+     * @param array $vars
+     * @return array - incl. decoded base64 vars.
+     */
+    private static function checkDecodeBase64Arr(array $vars) {
+
+        $len = strlen(SIP_PREFIX_BASE64);
+
+        foreach ($vars as $key => $value) {
+            if (substr($key, 0, $len) == SIP_PREFIX_BASE64) {
+                $vars[$key] = base64_decode($value);
+            }
+        }
+        return $vars;
     }
 
     /**
@@ -739,7 +770,7 @@ class Store {
      *
      * @param string $sipTypo3Vars
      */
-    public function fillTypo3StoreFromSip($sipTypo3Vars) {
+    public static function fillTypo3StoreFromSip($sipTypo3Vars) {
         $t3vars = self::getStore(STORE_TYPO3);
 
         // Do nothing if STORE_TYPO3 is already filled