CVE-2022-23475: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: daloRADIUS is an open source RADIUS web management application. daloRadius 1.3 and prior are vulnerable to a combination cross site scripting (XSS) and cross site request forgery (CSRF) vulnerability which leads to account takeover in the mng-del.php file because of an unescaped variable reflected in the DOM on line 116. This issue has been addressed in commit `ec3b4a419e`. Users are advised to manually apply the commit in order to mitigate this issue. Users may also mitigate this issue with in two parts 1) The CSRF vulnerability can be mitigated  by making the daloRadius session cookie to samesite=Lax or by the implimentation of a CSRF token in all forms. 2) The XSS vulnerability may be mitigated by escaping it or by introducing a Content-Security policy.



CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 8.8
Published: 2022-12-06T20:15Z
Repository: https://github.com/lirantal/daloradius
Language: PHP
Files changed: bill-payments-list.php, bill-pos-list.php, bill-pos-new.php, include/common/notificationsBatchDetails.php, include/common/notificationsUserDetailsInvoice.php, include/common/notificationsUserInvoice.php, include/common/notificationsWelcome.php, include/common/printTickets.php, library/checklogin.php, mng-batch-add.php, mng-del.php, mng-edit.php, mng-list-all.php, mng-rad-profiles-list.php, mng-search.php, notifications/common.php, notifications/dompdf/dompdf_config.inc.php, notifications/dompdf/lib/php-font-lib/AdobeFontMetrics.php, notifications/dompdf/lib/php-font-lib/Autoloader.php, notifications/dompdf/lib/php-font-lib/BinaryStream.php, notifications/dompdf/lib/php-font-lib/EOT/File.php, notifications/dompdf/lib/php-font-lib/EOT/Header.php, notifications/dompdf/lib/php-font-lib/EncodingMap.php, notifications/dompdf/lib/php-font-lib/Exception/FontNotFoundException.php, notifications/dompdf/lib/php-font-lib/Font.php, notifications/dompdf/lib/php-font-lib/Glyph/Outline.php, notifications/dompdf/lib/php-font-lib/Glyph/OutlineComponent.php, notifications/dompdf/lib/php-font-lib/Glyph/OutlineComposite.php, notifications/dompdf/lib/php-font-lib/Glyph/OutlineSimple.php, notifications/dompdf/lib/php-font-lib/Header.php, notifications/dompdf/lib/php-font-lib/OpenType/File.php, notifications/dompdf/lib/php-font-lib/OpenType/TableDirectoryEntry.php, notifications/dompdf/lib/php-font-lib/Table/DirectoryEntry.php, notifications/dompdf/lib/php-font-lib/Table/Table.php, notifications/dompdf/lib/php-font-lib/Table/Type/cmap.php, notifications/dompdf/lib/php-font-lib/Table/Type/glyf.php, notifications/dompdf/lib/php-font-lib/Table/Type/head.php, notifications/dompdf/lib/php-font-lib/Table/Type/hhea.php, notifications/dompdf/lib/php-font-lib/Table/Type/hmtx.php, notifications/dompdf/lib/php-font-lib/Table/Type/kern.php, notifications/dompdf/lib/php-font-lib/Table/Type/loca.php, notifications/dompdf/lib/php-font-lib/Table/Type/maxp.php, notifications/dompdf/lib/php-font-lib/Table/Type/name.php, notifications/dompdf/lib/php-font-lib/Table/Type/nameRecord.php, notifications/dompdf/lib/php-font-lib/Table/Type/os2.php, notifications/dompdf/lib/php-font-lib/Table/Type/post.php, notifications/dompdf/lib/php-font-lib/TrueType/Collection.php, notifications/dompdf/lib/php-font-lib/TrueType/File.php, notifications/dompdf/lib/php-font-lib/TrueType/Header.php, notifications/dompdf/lib/php-font-lib/TrueType/TableDirectoryEntry.php, notifications/dompdf/lib/php-font-lib/WOFF/File.php, notifications/dompdf/lib/php-font-lib/WOFF/Header.php, notifications/dompdf/lib/php-font-lib/WOFF/TableDirectoryEntry.php, notifications/processNotificationBatchDetails.php, notifications/processNotificationUserDetailsInvoice.php, notifications/processNotificationUserInvoice.php, notifications/processNotificationWelcome.php, rep-batch-details.php, rep-online.php, rep-username.php
Commit date: 2022-11-28 16:12:03 +0100
Commit message: Improvements to PDF ticket printing and security issues fix/mitigation (#297)

* Improved notification and ticket printing features

* minor template fixes

* fixed redirection on checklogin

* addressed some security issues
Diff: diff --git a/bill-payments-list.php b/bill-payments-list.php
index 14dd1c7d..16a95d59 100644
--- a/bill-payments-list.php
+++ b/bill-payments-list.php
@@ -250,28 +250,16 @@
         $failureMsg = "Nothing to display";
         include_once("include/management/actionMessages.php");
     }
     
     include('library/closedb.php');
-?>
-                
-        </div><!-- #contentnorightbar -->
-        
-        <div id="footer">
-<?php
     include('include/config/logging.php');
-    include('page-footer.php');
+    
+    $inline_extra_js = "
+var tooltipObj = new DHTMLgoodies_formTooltip();
+tooltipObj.setTooltipPosition('right');
+tooltipObj.setPageBgColor('#EEEEEE');
+tooltipObj.setTooltipCornerSize(15);
+tooltipObj.initFormFieldTooltip()";
+    
+    print_footer_and_html_epilogue($inline_extra_js);
 ?>
-        </div><!-- #footer -->
-    </div>
-</div>
-
-<script>
-    var tooltipObj = new DHTMLgoodies_formTooltip();
-    tooltipObj.setTooltipPosition('right');
-    tooltipObj.setPageBgColor('#EEEEEE');
-    tooltipObj.setTooltipCornerSize(15);
-    tooltipObj.initFormFieldTooltip();
-</script>
-
-</body>
-</html>
diff --git a/bill-pos-list.php b/bill-pos-list.php
index 61b421a4..c0fd0d5e 100644
--- a/bill-pos-list.php
+++ b/bill-pos-list.php
@@ -243,10 +243,12 @@
         $links = setupLinks_str($pageNum, $maxPage, $orderBy, $orderType, $partial_query_string);
         printTableFoot($per_page_numrows, $numrows, $colspan, $drawNumberLinks, $links);
 ?>
     </table>
 
+    <input type="hidden" name="csrf_token" value="<?= dalo_csrf_token() ?>">
+
 </form>
 
 <?php
     } else {
         $failureMsg = "Nothing to display";
diff --git a/bill-pos-new.php b/bill-pos-new.php
index 58e9f2fc..bd4b4bb7 100644
--- a/bill-pos-new.php
+++ b/bill-pos-new.php
@@ -41,17 +41,18 @@
     // we remove Cleartext-Password from the $valid_passwordTypes array
     if (isset($configValues['CONFIG_DB_PASSWORD_ENCRYPTION']) &&
         strtolower($configValues['CONFIG_DB_PASSWORD_ENCRYPTION']) !== 'cleartext') {
         $valid_passwordTypes = array_diff($valid_passwordTypes, array("Cleartext-Password"));
     }
+    $passwordType = (array_key_exists('passwordType', $_POST) && isset($_POST['passwordType']) &&
+                     in_array($_POST['passwordType'], $valid_passwordTypes)) ? $_POST['passwordType'] : "";
+
 
     isset($_POST['username']) ? $username = $_POST['username'] : $username = "";
     isset($_POST['password']) ? $password = $_POST['password'] : $password = "";
     isset($_POST['planName']) ? $planName = $_POST['planName'] : $planName = "";
     isset($_POST['profiles']) ? $profiles = $_POST['profiles'] : $profiles = "";
-    $passwordType = (array_key_exists('passwordType', $_POST) && isset($_POST['passwordType']) &&
-                         in_array($_POST['passwordType'], $valid_passwordTypes)) ? $_POST['passwordType'] : "";
     isset($_POST['notificationWelcome']) ? $notificationWelcome = $_POST['notificationWelcome'] : $notificationWelcome = "";
     
 
     isset($_POST['bi_contactperson']) ? $bi_contactperson = $_POST['bi_contactperson'] : $bi_contactperson = "";
     isset($_POST['bi_company']) ? $bi_company = $_POST['bi_company'] : $bi_company = "";
diff --git a/include/common/notificationsBatchDetails.php b/include/common/notificationsBatchDetails.php
index 55bf09bd..77b9b6b3 100644
--- a/include/common/notificationsBatchDetails.php
+++ b/include/common/notificationsBatchDetails.php
@@ -1,6 +1,6 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
  * Copyright (C) 2007 - Liran Tal <liran@enginx.com> All Rights Reserved.
  *
@@ -13,355 +13,265 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
 
-  include ("library/checklogin.php");
-  $operator = $_SESSION['operator_user'];
-
-	require_once(dirname(__FILE__)."/../../notifications/processNotificationBatchDetails.php");
-	require_once(dirname(__FILE__)."/../../library/config_read.php");
-
-	isset($_GET['batch_name']) ? $batch_name = $_GET['batch_name'] : $batch_name = "";
-	isset($_GET['destination']) ? $destination = $_GET['destination'] : $destination = "download";
-
-	if ($batch_name != "") {
-		$customerInfo = @getBatchDetails($batch_name);
-
-		$smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
-		$smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
-		$smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
-		$from = $configValues['CONFIG_MAIL_SMTPFROM'];
-
-		$pdfDocument = @createBatchDetailsNotification($customerInfo);
-
-		if ($destination == "download") {
-
-			header("Content-type: application/pdf");
-			header("Content-Disposition: attachment; filename=batch_notification_invoice_" . date("Ymd") . ".pdf; size=" . strlen($pdfDocument));
-			print $pdfDocument;
-
-		} else if ($destination == "email") {
-
-			@emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
-			header("Location: ".$_SERVER['HTTP_REFERER']);
-		}
-
-	}
-
-
-	function getBatchDetails($batch_name = NULL) {
-
-		require(dirname(__FILE__)."/../../library/opendb.php");
-		require_once(dirname(__FILE__)."/../../lang/main.php");
-
-		global $configValues;
-
-		if ($batch_name == NULL || empty($batch_name))
-			exit;
-
-
-
-		$tableTags = "width='580px' ";
-		$tableTrTags = "bgcolor='#ECE5B6'";
-
-		$customerInfo = array();
-
-		$sql = "SELECT ".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_description,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_status,".
-
-			"COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id)) as total_users,".
-			"COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_RADACCT'].".username)) as active_users,".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname,".
-			$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancost,".
-			$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancurrency,".
-			$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".name as HotspotName,".
-
-
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationdate,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationby,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updatedate,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updateby ".
-			" FROM ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id = ".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id) ".
-
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".planname = ".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname) ".
-
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".hotspot_id = ".
-			$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".id) ".
-
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_RADACCT'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_RADACCT'].".username = ".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username) ".
-
-			" WHERE ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name = '".$dbSocket->escapeSimple($batch_name)."' ".
-			" GROUP by ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ";
-
-		$res = $dbSocket->query($sql);
-
-		$batch_details = "";
-
-		$batch_details .= "<table $tableTags><tr $tableTrTags>
-					<td>
-			".t('all','BatchName')."
-			</td>
-
-			<td>
-			".t('all','HotSpot')."
-			</td>
-
-			<td>
-			".t('all','BatchStatus')."
-			</td>
-
-			<td>
-			".t('all','TotalUsers')."
-			</td>
-
-			<td>
-			".t('all','ActiveUsers')."
-			</td>
-
-			<td>
-			".t('all','PlanName')."
-			</td>
-
-			<td>
-			".t('all','PlanCost')."
-			</td>
-
-			<td>
-			".t('all','BatchCost')."
-			</td>
-
-			<td>
-			".t('all','CreationDate')."
-			</td>
-
-			<td>
-			".t('all','CreationBy')."
-			</td>
-
-			</tr>";
-
-		$active_users_per = 0;
-		$total_users = 0;
-		$active_users = 0;
-		$batch_cost = 0;
-
-		$hotspot_name = "";
-		$batch_id = "";
-		$planname = "";
-
-		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
-
-			$batch_id = $row['id'];
-			$hotspot_name = $row['HotspotName'];
-			$batch_status = $row['batch_status'];
-			$plancost = $row['plancost'];
-			$total_users = $row['total_users'];
-			$active_users = $row['active_users'];
-			$batch_cost = ($active_users * $plancost);
-			$plan_currency = $row['plancurrency'];
-			$planname = $row['planname'];
-
-
-			$batch_details .= "
-					<tr>
-
-					<td>".$row['batch_name']."
-
-					</td>
-
-					<td>".$hotspot_name."
-
-					</td>
-
-					<td>".$batch_status."
-
-					</td>
-
-					<td>".$total_users."
-
-					</td>
-
-					<td>".$active_users."
-
-					</td>
-
-					<td>".
-						$row['planname']."
-					</td>
-
-					<td>".$plancost."
-					</td>
-
-					<td>".$batch_cost."
-					</td>
-
-					<td>".
-						$row['creationdate']."
-					</td>
-
-					<td>".
-						$row['creationby']."
-					</td>
-
-				</tr>
-			";
-
-
-
-		}
-
-		$batch_details .= "</table>";
-
-		$customerInfo['batch_details'] = $batch_details;
-
-
-
-
-
-
-
-
-
-
-		$sql = "SELECT planId, planName, planRecurringPeriod, planCost, planSetupCost, planTax, planCurrency FROM ".
-				$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
-				" WHERE planName = '".$planname."'";
-		$res = $dbSocket->query($sql);
-		$row = $res->fetchRow(DB_FETCHMODE_ASSOC);
-
-		$service_plan_info = "";
-		$service_plan_info = "<table $tableTags>";
-
-		foreach($row as $rowName => $rowValue) {
-
-			$service_plan_info .= "<tr $tableTrTags'>
-						<td>$rowName</td>
-						<td>$rowValue</td>
-						</tr>";
-
-		}
-
-		$service_plan_info .= "</table>";
-		$customerInfo['service_plan_info'] = $service_plan_info;
-
-
-		$sql = "SELECT id, name, owner, address, companyphone, companyemail, companywebsite FROM ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
-					" WHERE name='".$hotspot_name."'";
-		$res = $dbSocket->query($sql);
-		$row = $res->fetchRow(DB_FETCHMODE_ASSOC);
-
-		$customerInfo['business_name'] = $row['name'];
-		$customerInfo['business_owner_name'] = $row['owner'];
-		$customerInfo['business_address'] = $row['address'];
-		$customerInfo['business_phone'] = $row['companyphone'];
-		$customerInfo['business_email'] = $row['companyemail'];
-		$customerInfo['business_web'] = $row['companywebsite'];
-
-
-
-
-
-
-		$batch_active_users = "";
-
-		$sql = "SELECT ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id,".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username,".
-				$configValues['CONFIG_DB_TBL_RADACCT'].".acctstarttime,".
-				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ".
-
-				" FROM ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].",".
-				$configValues['CONFIG_DB_TBL_RADACCT'].",".
-				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
-
-				" WHERE ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = ".
-				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id".
-				" AND ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = '$batch_id' ".
-				" AND ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username = ".
-				$configValues['CONFIG_DB_TBL_RADACCT'].".username".
-
-				" GROUP by ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username ".
-				" ORDER BY id ,".$configValues['CONFIG_DB_TBL_RADACCT'].".radacctid ASC ";
-
-		$res = $dbSocket->query($sql);
-
-		$batch_active_users = "<table $tableTags><tr $tableTrTags'>
-			<td>
-			".t('all','BatchName')."
-			</td>
-
-			<td>
-			".t('all','Username')."
-			</td>
-
-			<td>
-			".t('all','StartTime')."
-			</td>
-
-			</tr>";
-
-
-		$active_users_per = 0;
-		$total_users = 0;
-		$active_users = 0;
-		$batch_cost = 0;
-		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
-
-			$username = $row['username'];
-			$acctstarttime = $row['acctstarttime'];
-			$batch_name = $row['batch_name'];
-
-			$batch_active_users .= "
-					<tr>
-
-					<td>".$batch_name."
-					</td>
-
-					<td>".$username."
-					</td>
-
-					<td>".$acctstarttime."
-					</td>
-
-				</tr>
-			";
-
-		}
-
-
-		$batch_active_users .= "</table>";
-		$customerInfo['batch_active_users'] = $batch_active_users;
-
-
-		require(dirname(__FILE__)."/../../library/closedb.php");
-
-		return $customerInfo;
-
-
-	}
+    include("../../library/checklogin.php");
+    $operator = $_SESSION['operator_user'];
+
+    $batch_name = (array_key_exists('batch_name', $_GET) && !empty(str_replace("%", "", trim($_GET['batch_name']))))
+                ? str_replace("%", "", trim($_GET['batch_name'])) : "";
+
+    $destination = (array_key_exists('destination', $_GET) && !empty(trim($_GET['destination'])) &&
+                    in_array(strtolower(trim($_GET['destination'])), array( "download", "email" )))
+                 ? strtolower($_GET['destination']) : "download";
+
+    if (empty($batch_name)) {
+        die("you should provide a valid batch name");
+    }
+    
+    include_once("../../notifications/processNotificationBatchDetails.php");
+    include_once("../../library/config_read.php");
+    
+    
+    function getBatchDetails($batch_name = NULL) {
+        global $configValues;
+
+        if ($batch_name == NULL || empty(trim($batch_name))) {
+            return false;
+        }
+
+        include("../../library/opendb.php");
+        include_once("../../lang/main.php");
+
+        $tableTags = 'style="width: 580px"';
+        $tableTrTags = 'style="background-color: #ECE5B6"';
+
+        $customerInfo = array();
+
+        $ths = array(
+                        t('all','BatchName'),
+                        t('all','HotSpot'),
+                        t('all','BatchStatus'),
+                        t('all','TotalUsers'),
+                        t('all','ActiveUsers'),
+                        t('all','PlanName'),
+                        t('all','PlanCost'),
+                        t('all','BatchCost'),
+                        t('all','CreationDate'),
+                        t('all','CreationBy'),
+                    );
+
+        // start filling in batch details
+        $batch_details = "<table $tableTags><tr $tableTrTags>";
+        
+        foreach ($ths as $th) {
+            $batch_details .= sprintf("<th>%s</th>", $th);
+        }
+        
+        $batch_details .= "</tr>";
+
+        $sql = sprintf("SELECT dbh.id AS batch_id, dbh.batch_name, dbh.batch_description, dbh.batch_status,
+                               COUNT(DISTINCT(ubi.id)) AS total_users, COUNT(DISTINCT(ra.username)) AS active_users,
+                               ubi.planname, dbp.plancost, dbp.plancurrency, dhs.name AS hotspot_name,
+                               dbh.creationdate, dbh.creationby, dbh.updatedate, dbh.updateby
+                          FROM %s AS dbh LEFT JOIN %s AS ubi ON dbh.id=ubi.batch_id
+                                        LEFT JOIN %s AS dbp ON dbp.planname=ubi.planname
+                                        LEFT JOIN %s AS dhs ON dbh.hotspot_id=dhs.id
+                                        LEFT JOIN %s AS ra ON ra.username=ubi.username
+                         WHERE dbh.batch_name='%s'
+                         GROUP BY dbh.batch_name", $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'],
+                                                   $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'],
+                                                   $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'],
+                                                   $configValues['CONFIG_DB_TBL_DALOHOTSPOTS'],
+                                                   $configValues['CONFIG_DB_TBL_RADACCT'],
+                                                   $dbSocket->escapeSimple($batch_name));
+        $res = $dbSocket->query($sql);
+
+        
+        $active_users_per = 0;
+        $total_users = 0;
+        $active_users = 0;
+        $batch_cost = 0;
+
+        $hotspot_name = "";
+        $batch_id = "";
+        $planname = "";
+
+        while($row = $res->fetchRow()) {
+
+            foreach ($row as $i => $value) {
+                $row[$i] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
+            }
+
+            list(
+                    $batch_id, $batch_name, $batch_description, $batch_status, $total_users, $active_users, $planname,
+                    $plancost, $plancurrency, $hotspot_name, $creationdate, $creationby, $updatedate, $updateby
+                ) = $row;
+
+            
+            $batch_cost = (intval($active_users) * intval($plancost));
+
+            $tds = array(
+                            $batch_name,
+                            $hotspot_name,
+                            $batch_status,
+                            $total_users,
+                            $active_users,
+                            $planname,
+                            $plancost,
+                            $batch_cost,
+                            $creationdate,
+                            $creationby
+                        );
+                        
+            $batch_details .= "<tr>";
+            foreach ($tds as $td) {
+                $batch_details .= sprintf("<td>%s</td>", $td);
+            }
+            $batch_details .= "</tr>";
+
+        }
+
+        $batch_details .= "</table>";
+
+        $customerInfo['batch_details'] = $batch_details;
+
+
+        // filling in plan info
+        if (!empty($planname)) {
+        
+            $sql = sprintf("SELECT planId, planName, planRecurringPeriod, planCost, planSetupCost, planTax, planCurrency
+                              FROM %s WHERE planName='%s'", $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'],
+                                                            $dbSocket->escapeSimple($planname));
+            $res = $dbSocket->query($sql);
+            $row = $res->fetchRow(DB_FETCHMODE_ASSOC);
+            
+            echo $sql;
+            echo $row;
+            exit;
+
+            $service_plan_info = "<table $tableTags>";
+
+            foreach ($row as $rowName => $rowValue) {
+                $rowName = htmlspecialchars($rowName, ENT_QUOTES, 'UTF-8');
+                $rowValue = htmlspecialchars($rowValue, ENT_QUOTES, 'UTF-8');
+
+                $service_plan_info .= "<tr $tableTrTags>"
+                                    . sprintf("<th>%s</th>", $rowName)
+                                    . sprintf("<td>%s</td>", $rowValue)
+                                    . "</tr>";
+            }
+
+            $service_plan_info .= "</table>";
+            $customerInfo['service_plan_info'] = $service_plan_info;
+        }
+
+        // filling in business info
+        if (!empty($hotspot_name)) {
+            $sql = sprintf("SELECT id, name, owner, address, companyphone, companyemail, companywebsite
+                              FROM %s WHERE name='%s'", $configValues['CONFIG_DB_TBL_DALOHOTSPOTS'],
+                                                        $dbSocket->escapeSimple($hotspot_name));
+            $res = $dbSocket->query($sql);
+            $row = $res->fetchRow(DB_FETCHMODE_ASSOC);
+
+            $customerInfo['business_name'] = $row['name'];
+            $customerInfo['business_owner_name'] = $row['owner'];
+            $customerInfo['business_address'] = $row['address'];
+            $customerInfo['business_phone'] = $row['companyphone'];
+            $customerInfo['business_email'] = $row['companyemail'];
+            $customerInfo['business_web'] = $row['companywebsite'];
+        }
+
+        // active users details
+        $sql = sprintf("SELECT ubi.id, ubi.username, ra.acctstarttime, dbh.batch_name
+                          FROM %s AS ubi, %s AS ra, %s AS dbh
+                         WHERE ubi.batch_id=dbh.id
+                           AND ubi.batch_id='%s'
+                           AND ubi.username=ra.username
+                         GROUP BY ubi.username
+                         ORDER BY id, ra.radacctid ASC", $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'],
+                                                         $configValues['CONFIG_DB_TBL_RADACCT'],
+                                                         $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'],
+                                                         $dbSocket->escapeSimple($batch_id));
+        $res = $dbSocket->query($sql);
+
+        $ths = array(
+                        t('all','BatchName'),
+                        t('all','Username'),
+                        t('all','StartTime'),
+                    );
+
+        $batch_active_users = "<table $tableTags><tr $tableTrTags>";
+        foreach ($ths as $th) {
+            $batch_active_users .= sprintf("<th>%s</th>", $th);
+        }
+        $batch_active_users .= "</tr>";
+        
+        $active_users_per = 0;
+        $total_users = 0;
+        $active_users = 0;
+        $batch_cost = 0;
+        while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
+            foreach ($row as $i => $value) {
+                $row[$i] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
+            }
+
+            list($id, $username, $acctstarttime, $batch_name) = $row;
+
+            $tds = array( $username, $acctstarttime, $batch_name );
+
+            $batch_active_users .= "<tr>";
+            foreach ($tds as $td) {
+                $batch_active_users .= sprintf("<td>%s</td>", $td);
+            }
+            $batch_active_users .= "</tr>";
+        }
+        
+        $batch_active_users .= "</table>";
+        $customerInfo['batch_active_users'] = $batch_active_users;
+
+
+        require("../../library/closedb.php");
+
+        return $customerInfo;
+    }
+    
+    $customerInfo = getBatchDetails($batch_name);
+    
+    if ($customerInfo === false) {
+        die("error when loading batch details");
+    }
+
+    $smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
+    $smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
+    $smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
+    $from = $configValues['CONFIG_MAIL_SMTPFROM'];
+
+    $pdfDocument = @createBatchDetailsNotification($customerInfo);
+
+    if ($destination == "download") {
+        $filename = sprintf('batch_notification_invoice_%s.pdf', date("Ymd"));
+        $size = strlen($pdfDocument);
+        
+        header("Content-type: application/pdf");
+        header(sprintf("Content-Disposition: attachment; filename=%s; size=%d", $filename, $size));
+        print $pdfDocument;
+        
+    } else if ($destination == "email") {
+        
+        @emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
+        
+        $redirect = (array_key_exists('PREV_LIST_PAGE', $_SESSION) && !empty(trim($_SESSION['PREV_LIST_PAGE'])))
+                  ? trim($_SESSION['PREV_LIST_PAGE']) : "/mng-batch.php";
+        header("Location: " . $redirect);
+        
+    }
+    
 
 ?>
diff --git a/include/common/notificationsUserDetailsInvoice.php b/include/common/notificationsUserDetailsInvoice.php
index 356e979a..0b5f48f2 100644
--- a/include/common/notificationsUserDetailsInvoice.php
+++ b/include/common/notificationsUserDetailsInvoice.php
@@ -1,6 +1,6 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
  * Copyright (C) 2007 - Liran Tal <liran@enginx.com> All Rights Reserved.
  *
@@ -13,475 +13,482 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
 
-	require_once(dirname(__FILE__)."/../../notifications/processNotificationUserDetailsInvoice.php");
-	//require_once(dirname(__FILE__)."/../../library/config_read.php");
+    // prevent this file to be directly accessed
+    if (strpos($_SERVER['PHP_SELF'], '/include/common/notificationsUserDetailsInvoice.php') !== false) {
+        header("Location: ../../index.php");
+        exit;
+    }
 
-	/*
-	isset($_GET['batch_name']) ? $batch_name = $_GET['batch_name'] : $batch_name = "";
-	isset($_GET['destination']) ? $destination = $_GET['destination'] : $destination = "download";
-	
-	//test
-	$batch_name = "batch_test6"; 
-	
-	if ($batch_name != "") {
-		$customerInfo = @getBatchDetails($batch_name);
-		
-		$smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
-		$smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
-		$smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
-		$from = $configValues['CONFIG_MAIL_SMTPFROM'];
-		
-		$pdfDocument = @createBatchDetailsNotification($customerInfo);
-		
-		if ($destination == "download") {
-			
-			header("Content-type: application/pdf");
-			header("Content-Disposition: attachment; filename=batch_notification_invoice_" . date("Ymd") . ".pdf; size=" . strlen($pdfDocument));
-			print $pdfDocument;
-			
-		} else if ($destination == "email") {
-			
-			@emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
-			header("Location: ".$_SERVER['HTTP_REFERER']);
-		}
-		
-	}
-	*/
-	
-	function getCustomerInfo($row) {
-	
-		//global $configValues;
-		
-		$customerInfo = array();
-		
+    require_once(dirname(__FILE__)."/../../notifications/processNotificationUserDetailsInvoice.php");
+    //require_once(dirname(__FILE__)."/../../library/config_read.php");
 
-		getCustomerInfo_customer_info($row, $customerInfo);
-		getCustomerInfo_service_plan($row, $customerInfo);
-		
-		return $customerInfo;
-		
-	}
-	
-	
-	
-	function getCustomerInfo_customer_info($row, &$customerInfo) {
-				
-		global $configValues;
-		require(dirname(__FILE__)."/../../lang/main.php");
-		
-		$tableTags = "width='580px' ";
-		$tableTrTags = "bgcolor='#ECE5B6'";
-		
-		
-		if (!empty($row['email1']))
-			$invoice_email = $row['email1'];
-		else if (!empty($row['email2']))
-			$invoice_email = $row['email2'];
-		else if (!empty($row['email3']))
-			$invoice_email = $row['email3'];
-		else
-			$invoice_email = "";
-		
-		if (!empty($row['mobilephone']))
-			$invoice_phone = $row['mobilephone'];
-		else if (!empty($row['workphone']))
-			$invoice_phone = $row['mobilephone'];
-		else if (!empty($row['homephone']))
-			$invoice_phone = $row['homephone'];
-		else
-			$invoice_phone = "Unavailable";
-			
-		$invoice_address = "";
-		if (!empty($row['address']))
-			$invoice_address = $row['address'];
-		
-		if (!empty($row['city']))
-			$invoice_address .= ", ".$row['city'];
-		
-		if (!empty($row['state']))
-			$invoice_address .= "<br/>".$row['state'];
-		
-		if (!empty($row['zip']))
-			$invoice_address .= " ".$row['zip'];
-		
-		if (empty($invoice_address))
-			$invoice_address = "Unavailable";
-		
-		$customerInfo['business_name'] = $row['firstname']. " " .$row['lastname'];
-		$customerInfo['business_address'] = $invoice_address;
-		$customerInfo['business_phone'] = $invoice_phone;
-		$customerInfo['business_email'] = $invoice_email;
+    /*
+    isset($_GET['batch_name']) ? $batch_name = $_GET['batch_name'] : $batch_name = "";
+    isset($_GET['destination']) ? $destination = $_GET['destination'] : $destination = "download";
+    
+    //test
+    $batch_name = "batch_test6"; 
+    
+    if ($batch_name != "") {
+        $customerInfo = @getBatchDetails($batch_name);
+        
+        $smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
+        $smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
+        $smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
+        $from = $configValues['CONFIG_MAIL_SMTPFROM'];
+        
+        $pdfDocument = @createBatchDetailsNotification($customerInfo);
+        
+        if ($destination == "download") {
+            
+            header("Content-type: application/pdf");
+            header("Content-Disposition: attachment; filename=batch_notification_invoice_" . date("Ymd") . ".pdf; size=" . strlen($pdfDocument));
+            print $pdfDocument;
+            
+        } else if ($destination == "email") {
+            
+            @emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
+            header("Location: ".$_SERVER['HTTP_REFERER']);
+        }
+        
+    }
+    */
+    
+    function getCustomerInfo($row) {
+    
+        //global $configValues;
+        
+        $customerInfo = array();
+        
 
+        getCustomerInfo_customer_info($row, $customerInfo);
+        getCustomerInfo_service_plan($row, $customerInfo);
+        
+        return $customerInfo;
+        
+    }
+    
+    
+    
+    function getCustomerInfo_customer_info($row, &$customerInfo) {
+                
+        global $configValues;
+        require(dirname(__FILE__)."/../../lang/main.php");
+        
+        $tableTags = "width='580px' ";
+        $tableTrTags = "bgcolor='#ECE5B6'";
+        
+        
+        if (!empty($row['email1']))
+            $invoice_email = $row['email1'];
+        else if (!empty($row['email2']))
+            $invoice_email = $row['email2'];
+        else if (!empty($row['email3']))
+            $invoice_email = $row['email3'];
+        else
+            $invoice_email = "";
+        
+        if (!empty($row['mobilephone']))
+            $invoice_phone = $row['mobilephone'];
+        else if (!empty($row['workphone']))
+            $invoice_phone = $row['mobilephone'];
+        else if (!empty($row['homephone']))
+            $invoice_phone = $row['homephone'];
+        else
+            $invoice_phone = "Unavailable";
+            
+        $invoice_address = "";
+        if (!empty($row['address']))
+            $invoice_address = $row['address'];
+        
+        if (!empty($row['city']))
+            $invoice_address .= ", ".$row['city'];
+        
+        if (!empty($row['state']))
+            $invoice_address .= "<br/>".$row['state'];
+        
+        if (!empty($row['zip']))
+            $invoice_address .= " ".$row['zip'];
+        
+        if (empty($invoice_address))
+            $invoice_address = "Unavailable";
+        
+        $customerInfo['business_name'] = $row['firstname']. " " .$row['lastname'];
+        $customerInfo['business_address'] = $invoice_address;
+        $customerInfo['business_phone'] = $invoice_phone;
+        $customerInfo['business_email'] = $invoice_email;
 
-	}
-	
-	
-	function getCustomerInfo_service_plan($row, &$customerInfo) {
-				
-		global $configValues;
-		require(dirname(__FILE__)."/../../lang/main.php");
-		
-		$tableTags = "width='580px' ";
-		$tableTrTags = "bgcolor='#ECE5B6'";
-		
-		$service_plan_info = "";
-		$service_plan_info = "<table $tableTags>";
-		
-		$service_plan_info .= "".
 
-					"<tr $tableTrTags'>
-					<td>".t('all','Username')."</td>
-					<td>".$row['username']."</td>
-					</tr>".
-		
-					"<tr $tableTrTags'>
-					<td>".t('all','PlanName')."</td>
-					<td>".$row['planname']."</td>
-					</tr>".
-					"<tr $tableTrTags'>
-					<td>".t('all','PlanRecurring')."</td>
-					<td>".$row['planRecurring']."</td>
-					</tr>".
-					"<tr $tableTrTags'>
-					<td>".t('all','PlanRecurringPeriod')."</td>
-					<td>".$row['planRecurringPeriod']."</td>
-					</tr>".
-					"<tr $tableTrTags'>
-					<td>".t('all','PlanCost')."</td>
-					<td>".$row['planCost']."</td>
-					</tr>".
-					"<tr $tableTrTags'>
-					<td>".t('all','NextBill')."</td>
-					<td>".$row['nextbill']."</td>
-					</tr>".
-					"<tr $tableTrTags'>
-					<td>".t('all','BillDue')."</td>
-					<td>".$row['billdue']."</td>
-					</tr>".
-					"";
-		
-		$service_plan_info .= "</table>";
-		$customerInfo['service_plan_info'] = $service_plan_info;	
-		
-	}
-	
-	
-	/*
-	function getBatchDetails($batch_name = NULL) {
-		
-		require(dirname(__FILE__)."/../../library/opendb.php");
-		require_once(dirname(__FILE__)."/../../lang/main.php");
-		
-		global $configValues;
-		
-		if ($batch_name == NULL || empty($batch_name))
-			exit;
-			
+    }
+    
+    
+    function getCustomerInfo_service_plan($row, &$customerInfo) {
+                
+        global $configValues;
+        require(dirname(__FILE__)."/../../lang/main.php");
+        
+        $tableTags = "width='580px' ";
+        $tableTrTags = "bgcolor='#ECE5B6'";
+        
+        $service_plan_info = "";
+        $service_plan_info = "<table $tableTags>";
+        
+        $service_plan_info .= "".
 
-			
-		$tableTags = "width='580px' ";
-		$tableTrTags = "bgcolor='#ECE5B6'";
-		
-		$customerInfo = array();
-		
-		$sql = "SELECT ".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_description,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_status,".
-			
-			"COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id)) as total_users,".
-			"COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_RADACCT'].".username)) as active_users,".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname,".
-			$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancost,".
-			$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancurrency,".
-			$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".name as HotspotName,".
-			
-			
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationdate,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationby,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updatedate,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updateby ".
-			" FROM ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id = ".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id) ".
+                    "<tr $tableTrTags'>
+                    <td>".t('all','Username')."</td>
+                    <td>".$row['username']."</td>
+                    </tr>".
+        
+                    "<tr $tableTrTags'>
+                    <td>".t('all','PlanName')."</td>
+                    <td>".$row['planname']."</td>
+                    </tr>".
+                    "<tr $tableTrTags'>
+                    <td>".t('all','PlanRecurring')."</td>
+                    <td>".$row['planRecurring']."</td>
+                    </tr>".
+                    "<tr $tableTrTags'>
+                    <td>".t('all','PlanRecurringPeriod')."</td>
+                    <td>".$row['planRecurringPeriod']."</td>
+                    </tr>".
+                    "<tr $tableTrTags'>
+                    <td>".t('all','PlanCost')."</td>
+                    <td>".$row['planCost']."</td>
+                    </tr>".
+                    "<tr $tableTrTags'>
+                    <td>".t('all','NextBill')."</td>
+                    <td>".$row['nextbill']."</td>
+                    </tr>".
+                    "<tr $tableTrTags'>
+                    <td>".t('all','BillDue')."</td>
+                    <td>".$row['billdue']."</td>
+                    </tr>".
+                    "";
+        
+        $service_plan_info .= "</table>";
+        $customerInfo['service_plan_info'] = $service_plan_info;    
+        
+    }
+    
+    
+    /*
+    function getBatchDetails($batch_name = NULL) {
+        
+        require(dirname(__FILE__)."/../../library/opendb.php");
+        require_once(dirname(__FILE__)."/../../lang/main.php");
+        
+        global $configValues;
+        
+        if ($batch_name == NULL || empty($batch_name))
+            exit;
+            
 
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".planname = ".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname) ".
+            
+        $tableTags = "width='580px' ";
+        $tableTrTags = "bgcolor='#ECE5B6'";
+        
+        $customerInfo = array();
+        
+        $sql = "SELECT ".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id,".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name,".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_description,".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_status,".
+            
+            "COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id)) as total_users,".
+            "COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_RADACCT'].".username)) as active_users,".
+            $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname,".
+            $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancost,".
+            $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancurrency,".
+            $configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".name as HotspotName,".
+            
+            
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationdate,".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationby,".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updatedate,".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updateby ".
+            " FROM ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
+            " LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].
+            " ON ".
+            "(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id = ".
+            $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id) ".
 
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".hotspot_id = ".
-			$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".id) ".
-			
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_RADACCT'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_RADACCT'].".username = ".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username) ".
-			
-			" WHERE ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name = '$batch_name' ".
-			" GROUP by ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ";
-			
-		$res = $dbSocket->query($sql);
+            " LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
+            " ON ".
+            "(".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".planname = ".
+            $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname) ".
 
-		$batch_details = "";
-		
-		$batch_details .= "<table $tableTags><tr $tableTrTags>
-					<td> 
-			".t('all','BatchName')."
-			</td>
-	
-			<td> 
-			".t('all','HotSpot')."
-			</td>
-	
-			<td> 
-			".t('all','BatchStatus')."
-			</td>
-			
-			<td> 
-			".t('all','TotalUsers')."
-			</td>
-	
-			<td> 
-			".t('all','ActiveUsers')."
-			</td>
-	
-			<td> 
-			".t('all','PlanName')."
-			</td>
-	
-			<td> 
-			".t('all','PlanCost')."
-			</td>
-	
-			<td> 
-			".t('all','BatchCost')."
-			</td>
-	
-			<td> 
-			".t('all','CreationDate')."
-			</td>
-	
-			<td> 
-			".t('all','CreationBy')."
-			</td>
-	
-			</tr>";
-		
-		$active_users_per = 0;
-		$total_users = 0;
-		$active_users = 0;
-		$batch_cost = 0;
-		
-		$hotspot_name = "";
-		$batch_id = "";
-		$planname = "";
-		
-		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
-			
-			$batch_id = $row['id'];
-			$hotspot_name = $row['HotspotName'];
-			$batch_status = $row['batch_status'];
-			$plancost = $row['plancost'];
-			$total_users = $row['total_users'];
-			$active_users = $row['active_users'];
-			$batch_cost = ($active_users * $plancost);
-			$plan_currency = $row['plancurrency'];
-			$planname = $row['planname'];
-			
-	
-			$batch_details .= "
-					<tr>
-					
-					<td>".$row['batch_name']."
-						
-					</td>
-					
-					<td>".$hotspot_name."
-						
-					</td>
-			
-					<td>".$batch_status."
-						
-					</td>
-					
-					<td>".$total_users."
-						
-					</td>
-	
-					<td>".$active_users."
-						
-					</td>
-	
-					<td>".
-						$row['planname']."
-					</td>
-	
-					<td>".$plancost."
-					</td>
-	
-					<td>".$batch_cost."
-					</td>
-					
-					<td>".
-						$row['creationdate']."
-					</td>
-	
-					<td>".
-						$row['creationby']."
-					</td>
-	
-				</tr>
-			";
-			
+            " LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
+            " ON ".
+            "(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".hotspot_id = ".
+            $configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".id) ".
+            
+            " LEFT JOIN ".$configValues['CONFIG_DB_TBL_RADACCT'].
+            " ON ".
+            "(".$configValues['CONFIG_DB_TBL_RADACCT'].".username = ".
+            $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username) ".
+            
+            " WHERE ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name = '$batch_name' ".
+            " GROUP by ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ";
+            
+        $res = $dbSocket->query($sql);
 
-			
-		}
-		
-		$batch_details .= "</table>";
-		
-		$customerInfo['batch_details'] = $batch_details;
-		
-		
-		
-		
-		
-		
-		
-		
-		
-		
-		$sql = "SELECT planId, planName, planRecurringPeriod, planCost, planSetupCost, planTax, planCurrency FROM ".
-				$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
-				" WHERE planName = '".$planname."'";
-		$res = $dbSocket->query($sql);
-		$row = $res->fetchRow(DB_FETCHMODE_ASSOC);
-		
-		$service_plan_info = "";
-		$service_plan_info = "<table $tableTags>";
-		
-		foreach($row as $rowName => $rowValue) {
-		
-			$service_plan_info .= "<tr $tableTrTags'>
-						<td>$rowName</td>
-						<td>$rowValue</td>
-						</tr>";
-		
-		}
-		
-		$service_plan_info .= "</table>";
-		$customerInfo['service_plan_info'] = $service_plan_info;
-		
-		
-		$sql = "SELECT id, name, owner, address, companyphone, companyemail, companywebsite FROM ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
-					" WHERE name='".$hotspot_name."'";
-		$res = $dbSocket->query($sql);
-		$row = $res->fetchRow(DB_FETCHMODE_ASSOC);
-		
-		$customerInfo['business_name'] = $row['name'];
-		$customerInfo['business_owner_name'] = $row['owner'];
-		$customerInfo['business_address'] = $row['address'];
-		$customerInfo['business_phone'] = $row['companyphone'];
-		$customerInfo['business_email'] = $row['companyemail'];
-		$customerInfo['business_web'] = $row['companywebsite'];
-				
-		
-		
-		
-		
-		
-		$batch_active_users = "";
-		
-		$sql = "SELECT ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id,".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username,".
-				$configValues['CONFIG_DB_TBL_RADACCT'].".acctstarttime,".
-				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ".
-				
-				" FROM ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].",".
-				$configValues['CONFIG_DB_TBL_RADACCT'].",".
-				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
-				
-				" WHERE ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = ".
-				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id".
-				" AND ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = '$batch_id' ".
-				" AND ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username = ".
-				$configValues['CONFIG_DB_TBL_RADACCT'].".username".
-	
-				" GROUP by ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username ".
-				" ORDER BY id ,".$configValues['CONFIG_DB_TBL_RADACCT'].".radacctid ASC ";
-				
-		$res = $dbSocket->query($sql);
-		
-		$batch_active_users = "<table $tableTags><tr $tableTrTags'>
-			<td> 
-			".t('all','BatchName')."
-			</td>
-	
-			<td> 
-			".t('all','Username')."
-			</td>
-	
-			<td> 
-			".t('all','StartTime')."
-			</td>
-	
-			</tr>";
+        $batch_details = "";
+        
+        $batch_details .= "<table $tableTags><tr $tableTrTags>
+                    <td> 
+            ".t('all','BatchName')."
+            </td>
+    
+            <td> 
+            ".t('all','HotSpot')."
+            </td>
+    
+            <td> 
+            ".t('all','BatchStatus')."
+            </td>
+            
+            <td> 
+            ".t('all','TotalUsers')."
+            </td>
+    
+            <td> 
+            ".t('all','ActiveUsers')."
+            </td>
+    
+            <td> 
+            ".t('all','PlanName')."
+            </td>
+    
+            <td> 
+            ".t('all','PlanCost')."
+            </td>
+    
+            <td> 
+            ".t('all','BatchCost')."
+            </td>
+    
+            <td> 
+            ".t('all','CreationDate')."
+            </td>
+    
+            <td> 
+            ".t('all','CreationBy')."
+            </td>
+    
+            </tr>";
+        
+        $active_users_per = 0;
+        $total_users = 0;
+        $active_users = 0;
+        $batch_cost = 0;
+        
+        $hotspot_name = "";
+        $batch_id = "";
+        $planname = "";
+        
+        while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
+            
+            $batch_id = $row['id'];
+            $hotspot_name = $row['HotspotName'];
+            $batch_status = $row['batch_status'];
+            $plancost = $row['plancost'];
+            $total_users = $row['total_users'];
+            $active_users = $row['active_users'];
+            $batch_cost = ($active_users * $plancost);
+            $plan_currency = $row['plancurrency'];
+            $planname = $row['planname'];
+            
+    
+            $batch_details .= "
+                    <tr>
+                    
+                    <td>".$row['batch_name']."
+                        
+                    </td>
+                    
+                    <td>".$hotspot_name."
+                        
+                    </td>
+            
+                    <td>".$batch_status."
+                        
+                    </td>
+                    
+                    <td>".$total_users."
+                        
+                    </td>
+    
+                    <td>".$active_users."
+                        
+                    </td>
+    
+                    <td>".
+                        $row['planname']."
+                    </td>
+    
+                    <td>".$plancost."
+                    </td>
+    
+                    <td>".$batch_cost."
+                    </td>
+                    
+                    <td>".
+                        $row['creationdate']."
+                    </td>
+    
+                    <td>".
+                        $row['creationby']."
+                    </td>
+    
+                </tr>
+            ";
+            
 
-			
-		$active_users_per = 0;
-		$total_users = 0;
-		$active_users = 0;
-		$batch_cost = 0;
-		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
-	
-			$username = $row['username'];
-			$acctstarttime = $row['acctstarttime'];
-			$batch_name = $row['batch_name'];
-			
-			$batch_active_users .= "
-					<tr>
-					
-					<td>".$batch_name."
-					</td>
-	
-					<td>".$username."
-					</td>
-	
-					<td>".$acctstarttime."
-					</td>
-	
-				</tr>
-			";
+            
+        }
+        
+        $batch_details .= "</table>";
+        
+        $customerInfo['batch_details'] = $batch_details;
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        $sql = "SELECT planId, planName, planRecurringPeriod, planCost, planSetupCost, planTax, planCurrency FROM ".
+                $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
+                " WHERE planName = '".$planname."'";
+        $res = $dbSocket->query($sql);
+        $row = $res->fetchRow(DB_FETCHMODE_ASSOC);
+        
+        $service_plan_info = "";
+        $service_plan_info = "<table $tableTags>";
+        
+        foreach($row as $rowName => $rowValue) {
+        
+            $service_plan_info .= "<tr $tableTrTags'>
+                        <td>$rowName</td>
+                        <td>$rowValue</td>
+                        </tr>";
+        
+        }
+        
+        $service_plan_info .= "</table>";
+        $customerInfo['service_plan_info'] = $service_plan_info;
+        
+        
+        $sql = "SELECT id, name, owner, address, companyphone, companyemail, companywebsite FROM ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
+                    " WHERE name='".$hotspot_name."'";
+        $res = $dbSocket->query($sql);
+        $row = $res->fetchRow(DB_FETCHMODE_ASSOC);
+        
+        $customerInfo['business_name'] = $row['name'];
+        $customerInfo['business_owner_name'] = $row['owner'];
+        $customerInfo['business_address'] = $row['address'];
+        $customerInfo['business_phone'] = $row['companyphone'];
+        $customerInfo['business_email'] = $row['companyemail'];
+        $customerInfo['business_web'] = $row['companywebsite'];
+                
+        
+        
+        
+        
+        
+        $batch_active_users = "";
+        
+        $sql = "SELECT ".
+                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id,".
+                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username,".
+                $configValues['CONFIG_DB_TBL_RADACCT'].".acctstarttime,".
+                $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ".
+                
+                " FROM ".
+                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].",".
+                $configValues['CONFIG_DB_TBL_RADACCT'].",".
+                $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
+                
+                " WHERE ".
+                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = ".
+                $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id".
+                " AND ".
+                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = '$batch_id' ".
+                " AND ".
+                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username = ".
+                $configValues['CONFIG_DB_TBL_RADACCT'].".username".
+    
+                " GROUP by ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username ".
+                " ORDER BY id ,".$configValues['CONFIG_DB_TBL_RADACCT'].".radacctid ASC ";
+                
+        $res = $dbSocket->query($sql);
+        
+        $batch_active_users = "<table $tableTags><tr $tableTrTags'>
+            <td> 
+            ".t('all','BatchName')."
+            </td>
+    
+            <td> 
+            ".t('all','Username')."
+            </td>
+    
+            <td> 
+            ".t('all','StartTime')."
+            </td>
+    
+            </tr>";
 
-		}
-		
+            
+        $active_users_per = 0;
+        $total_users = 0;
+        $active_users = 0;
+        $batch_cost = 0;
+        while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
+    
+            $username = $row['username'];
+            $acctstarttime = $row['acctstarttime'];
+            $batch_name = $row['batch_name'];
+            
+            $batch_active_users .= "
+                    <tr>
+                    
+                    <td>".$batch_name."
+                    </td>
+    
+                    <td>".$username."
+                    </td>
+    
+                    <td>".$acctstarttime."
+                    </td>
+    
+                </tr>
+            ";
 
-		$batch_active_users .= "</table>";
-		$customerInfo['batch_active_users'] = $batch_active_users;
-		
-		
-		require(dirname(__FILE__)."/../../library/closedb.php");
-		
-		return $customerInfo;
-		
-		
-	}
-	*/
-?>
\ No newline at end of file
+        }
+        
+
+        $batch_active_users .= "</table>";
+        $customerInfo['batch_active_users'] = $batch_active_users;
+        
+        
+        require(dirname(__FILE__)."/../../library/closedb.php");
+        
+        return $customerInfo;
+        
+        
+    }
+    */
+?>
diff --git a/include/common/notificationsUserInvoice.php b/include/common/notificationsUserInvoice.php
index 1fde5a00..1ac7be06 100644
--- a/include/common/notificationsUserInvoice.php
+++ b/include/common/notificationsUserInvoice.php
@@ -1,6 +1,6 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
  * Copyright (C) 2007 - Liran Tal <liran@enginx.com> All Rights Reserved.
  *
@@ -13,192 +13,234 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
 
-	require_once(dirname(__FILE__)."/../../notifications/processNotificationUserInvoice.php");
-	require_once(dirname(__FILE__)."/../../library/config_read.php");
-	
-	isset($_GET['invoice_id']) ? $invoice_id = $_GET['invoice_id'] : $invoice_id = "";
-	isset($_GET['destination']) ? $destination = $_GET['destination'] : $destination = "preview";
-	
-	if ($invoice_id != "") {
-		$customerInfo = @getInvoiceDetails($invoice_id);
-		$document = @createNotification($customerInfo, $destination == "preview");
-		
-		if ($destination == "download") {
-			
-			$pdfDocument = $document;
-			
-			header("Content-type: application/pdf");
-			header("Content-Disposition: attachment; filename=notification_user_invoice_" . date("Ymd") . ".pdf; size=" . strlen($pdfDocument));
-			print $pdfDocument;
-			
-		} else if ($destination == "email") {
-			
-			$pdfDocument = $document;
-			
-			$smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
-			$smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
-			$smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
-			$from = $configValues['CONFIG_MAIL_SMTPFROM'];
-		
-			@emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
-			header("Location: ".$_SERVER['HTTP_REFERER']);
-			
-		} else if ($destination == 'preview') {
+    include("../../library/checklogin.php");
+    $operator = $_SESSION['operator_user'];
 
-			$htmlDocument = $document;
+    $invoice_id = (array_key_exists('invoice_id', $_GET) && !empty(str_replace("%", "", trim($_GET['invoice_id']))))
+                ? str_replace("%", "", trim($_GET['invoice_id'])) : "";
 
-			$result = file_put_contents(dirname(__FILE__).'/../../notifications/templates/invoice_preview.html', $htmlDocument);
-			header('Location: ../../notifications/templates/invoice_preview.html');
-		}
-	}
-	
-	
-	function getInvoiceDetails($invoice_id = NULL) {
-		
-		require(dirname(__FILE__)."/../../library/opendb.php");
-		require_once(dirname(__FILE__)."/../../lang/main.php");
-		
-		global $configValues;
-		
-		if ($invoice_id == NULL || empty($invoice_id))
-			exit;
-			
-		$tableTags = "width='580px' ";
-		$tableTrTags = "bgcolor='#ECE5B6'";
-		
-		// get invoice details
-		$sql = "SELECT a.id, a.date, a.status_id, a.type_id, a.user_id, a.notes, b.contactperson, b.username, b.company, ".
-				" b.city, b.state, b.country, b.zip, b.address, b.email, b.emailinvoice, b.phone, f.value as type, ".
-				" c.value AS status, COALESCE(e2.totalpayed, 0) as totalpayed, COALESCE(d2.totalbilled, 0) as totalbilled ".
-				" FROM ".$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICE']." AS a".
-				" INNER JOIN ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO']." AS b ON (a.user_id = b.id) ".
-				" INNER JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICESTATUS']." AS c ON (a.status_id = c.id) ".
-				" INNER JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICETYPE']." AS f ON (a.type_id = f.id) ".
-				" LEFT JOIN (SELECT SUM(d.amount + d.tax_amount) ".
-					" as totalbilled, invoice_id, amount, tax_amount, notes, plan_id FROM ".$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICEITEMS']." AS d ".
-					" GROUP BY d.invoice_id) AS d2 ON (d2.invoice_id = a.id) ".
-				" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS']." AS bp2 ON (bp2.id = d2.plan_id) ".
-				" LEFT JOIN (SELECT SUM(e.amount) as totalpayed, invoice_id FROM ". 
-				$configValues['CONFIG_DB_TBL_DALOPAYMENTS']." AS e GROUP BY e.invoice_id) AS e2 ON (e2.invoice_id = a.id) ".
-				" WHERE a.id = '".$dbSocket->escapeSimple($invoice_id)."'".
-				" GROUP BY a.id ";
-		$res = $dbSocket->query($sql);	
-		$invoiceDetails = $res->fetchRow(DB_FETCHMODE_ASSOC);
+    $destination = (array_key_exists('destination', $_GET) && !empty(trim($_GET['destination'])) &&
+                    in_array(strtolower(trim($_GET['destination'])), array( "download", "email", "preview" )))
+                 ? strtolower($_GET['destination']) : "preview";
+                 
+    if (empty($invoice_id)) {
+        die("you should provide a valid invoice id");
+    }
 
-		if (empty($invoiceDetails['email']))
-			$customer_email = $invoiceDetails['emailinvoice'];
-		else
-			$customer_email = $invoiceDetails['email'];
-			
-		// populate user contact informatin
-		$customerInfo['customer_name'] = $invoiceDetails['contactperson'];
-		$customerInfo['customer_address'] = $invoiceDetails['address']. " " . $invoiceDetails['city']. " " . $invoiceDetails['state'];
-		$customerInfo['customer_email'] = $customer_email;
-		$customerInfo['customer_phone'] = $invoiceDetails['phone'];
-		
-		// populate user invoice details
-		$balance = (float) ($invoiceDetails['totalpayed'] - $invoiceDetails['totalbilled']);
-		$invoice_details = "";
-		$invoice_details .= "".
-		"<b>".t('all','ClientName')."</b>: ".$invoiceDetails['contactperson']."<br/>".
-		"<b>".t('all','Invoice')."</b>: ".$invoice_id."<br/>".
-		"<b>".t('all','Date')."</b>: ".$invoiceDetails['date']."<br/>".
-		"<b>".t('all','TotalBilled')."</b>: ".$invoiceDetails['totalbilled']."<br/>".
-		"<b>".t('all','TotalPayed')."</b>: ".$invoiceDetails['totalpayed']."<br/>".
-		"<b>".t('all','Balance')."</b>: ".$balance."<br/>".
-		"<b>".t('all','Status')."</b>: ".$invoiceDetails['status']."<br/>".
-		"<b>".t('ContactInfo','Notes')."</b>: ".$invoiceDetails['notes']."<br/><br/><br/>";
-		
-		$customerInfo['invoice_details'] = $invoice_details;
-		
-		// populate customer data - NEW STYLE
-		$customerInfo['customerId'] = $invoiceDetails['user_id'];
-		$customerInfo['customerName'] = (isset($invoiceDetails['company']) ? $invoiceDetails['company'] : $invoiceDetails['contactperson']);
-		$customerInfo['customerAddress'] = $invoiceDetails['address'];
-		$customerInfo['customerAddress2'] = $invoiceDetails['zip'] . ' '. $invoiceDetails['city'] . ' ' .
-		                                    $invoiceDetails['state'] . ' ' . $invoiceDetails['country'];
-		$customerInfo['customerEmail'] = $invoiceDetails['email'];
-		$customerInfo['customerPhone'] = $invoiceDetails['phone'];
-		$customerInfo['customerContact'] = $invoiceDetails['contactperson'];
-		
-		$customerInfo['invoiceNumber'] = $invoice_id;
-		$customerInfo['invoiceDate'] = date('Y-m-d', strtotime($invoiceDetails['date']));
-		$customerInfo['invoiceStatus'] = strtoupper($invoiceDetails['status']);
-		$customerInfo['invoiceTotalBilled'] = $invoiceDetails['totalbilled'];
-		$customerInfo['invoicePaid'] = $invoiceDetails['totalpayed'];
-		$customerInfo['invoiceDue'] = $balance;
-		$customerInfo['invoiceNotes'] = $invoiceDetails['notes'];
-		
-		// populate user invoice items
-		$invoice_items = "";
-		$invoice_items .= "<table $tableTags><tr $tableTrTags>
-			<th>Plan</th>
-			<th>Item Amount</th>
-			<th>Item Tax</th>
-			<th>Notes</th>
-			</tr>
-			";
+    require_once("../../notifications/processNotificationUserInvoice.php");
+    require_once("../../library/config_read.php");
+    
+    $customerInfo = @getInvoiceDetails($invoice_id);
+    
+    if ($customerInfo === false) {
+        die("error when loading invoice");
+    }
+    
+    $document = @createNotification($customerInfo, $destination == "preview");
+        
+    if ($destination == "download") {
+        $filename = sprintf('notification_invoice_%s.pdf', date("Ymd"));
+        $size = strlen($document);
+        
+        header("Content-type: application/pdf");
+        header(sprintf("Content-Disposition: attachment; filename=%s; size=%d", $filename, $size));
+            
+        print $document;
 
-		// get all invoice items
-		$sql = 'SELECT a.id, a.plan_id, a.amount, a.tax_amount, a.notes, b.planName '.
-				' FROM '.$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICEITEMS'].' a '.
-				' LEFT JOIN '.$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].' b ON a.plan_id = b.id '.
-				' WHERE a.invoice_id = '.$invoice_id.' ORDER BY a.id ASC';
-		$res = $dbSocket->query($sql);
-		$logDebugSQL .= $sql . "\n";
-		
-		// initialize invoice items - NEW STYLE
-		$invoiceItems = array();
-		$invoiceItemsNumber = 1;
-		$invoiceItemsTotalAmount = 0;
-		$invoiceItemsTotalTax = 0;
-		
-		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
+    } else if ($destination == "email") {
+        $smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
+        $smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
+        $smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
+        
+        $from = $configValues['CONFIG_MAIL_SMTPFROM'];
+        
+        @emailNotification($document, $customerInfo, $smtpInfo, $from);
+        $redirect = (array_key_exists('PREV_LIST_PAGE', $_SESSION) && !empty(trim($_SESSION['PREV_LIST_PAGE'])))
+                  ? trim($_SESSION['PREV_LIST_PAGE']) : "/bill-invoice.php";
+        header("Location: " . $redirect);
+            
+    } else /*if ($destination == "preview")*/ {
+        print $document;
+        //~ $result = file_put_contents(dirname(__FILE__).'/../../notifications/templates/invoice_preview.html', $htmlDocument);
+        //~ header('Location: ../../notifications/templates/invoice_preview.html');
+    }
+    
+    
+    
+    function getInvoiceDetails($invoice_id = NULL) {
+        global $configValues;
+        
+        if ($invoice_id == NULL || empty($invoice_id)) {
+            exit;
+        }
+        
+        include("../../library/opendb.php");
+        include_once("../../lang/main.php");
+            
+        $tableTags = 'style="width: 580px"';
+        $tableTrTags = 'style="background-color: #ECE5B6"';
+        
+        // get invoice details
+        $sql = sprintf("SELECT a.id, a.date, a.status_id, a.type_id, a.user_id, a.notes, b.contactperson, b.username,
+                               b.company, b.city, b.state, b.country, b.zip, b.address, b.email, b.emailinvoice, b.phone,
+                               f.value AS type, c.value AS status, COALESCE(e2.totalpayed, 0) AS totalpayed,
+                               COALESCE(d2.totalbilled, 0) AS totalbilled
+                          FROM %s AS a INNER JOIN %s AS b ON a.user_id=b.id
+                                       INNER JOIN %s AS c ON a.status_id=c.id
+                                       INNER JOIN %s AS f ON a.type_id=f.id
+                                       LEFT JOIN (
+                                                    SELECT SUM(d.amount + d.tax_amount) AS totalbilled, invoice_id,
+                                                           amount, tax_amount, notes, plan_id
+                                                      FROM %s AS d
+                                                     GROUP BY d.invoice_id
+                                                 ) AS d2 ON d2.invoice_id=a.id
+                                       LEFT JOIN %s AS bp2 ON bp2.id=d2.plan_id
+                                       LEFT JOIN (
+                                                    SELECT SUM(e.amount) AS totalpayed, invoice_id
+                                                      FROM %s AS e
+                                                     GROUP BY e.invoice_id
+                                                 ) AS e2 ON e2.invoice_id=a.id
+                         WHERE a.id=%d
+                         GROUP BY a.id", $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICE'],
+                                         $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'],
+                                         $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICESTATUS'],
+                                         $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICETYPE'],
+                                         $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICEITEMS'],
+                                         $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'],
+                                         $configValues['CONFIG_DB_TBL_DALOPAYMENTS'],
+                                         $dbSocket->escapeSimple($invoice_id));
+        $res = $dbSocket->query($sql);    
+        $invoiceDetails = $res->fetchRow(DB_FETCHMODE_ASSOC);
 
-			$invoice_items .= "". 
-				"<tr>".
-					"<td>".$row['planName']."</td>".
-					"<td>".$row['amount']."</td>".
-					"<td>".$row['tax_amount']."</td>".
-					"<td>".$row['notes']."</td>".
-				"</tr>";
-				
-			// populate invoice items - NEW STYLE
-			$invoiceItem = array();
-			
-			$invoiceItem['invoiceItemNumber'] = sprintf('%02d', $invoiceItemsNumber);
-			$invoiceItem['invoiceItemPlan'] = $row['planName'];
-			$invoiceItem['invoiceItemNotes'] = $row['notes'];
-			$invoiceItem['invoiceItemAmount'] = $row['amount'];
-			$invoiceItem['invoiceItemTaxAmount'] = $row['tax_amount'];
-			$invoiceItem['invoiceItemTotalAmount'] = $row['amount'] + $row['tax_amount'];
-			
-			$invoiceItems[] = $invoiceItem;
-			$invoiceItemsTotalAmount += $row['amount'];
-			$invoiceItemsTotalTax += $row['tax_amount'];
-			
-			++$invoiceItemsNumber;
-		}
+        $customer_email = (empty($invoiceDetails['email'])) ? $invoiceDetails['emailinvoice'] : $invoiceDetails['email'];
+            
+        // populate user contact informatin
+        $customerInfo['customer_name'] = $invoiceDetails['contactperson'];
+        $customerInfo['customer_address'] = $invoiceDetails['address'] . " " . $invoiceDetails['city'] . " " . $invoiceDetails['state'];
+        $customerInfo['customer_email'] = $customer_email;
+        $customerInfo['customer_phone'] = $invoiceDetails['phone'];
+        
+        // populate user invoice details
+        $balance = floatval($invoiceDetails['totalpayed'] - $invoiceDetails['totalbilled']);
+        
+        $details = array(
+                            array( t('all','ClientName'), $invoiceDetails['contactperson'] ),
+                            array( t('all','Invoice'), $invoice_id ), 
+                            array( t('all','Date'), $invoiceDetails['date'] ), 
+                            array( t('all','TotalBilled'), $invoiceDetails['totalbilled'] ), 
+                            array( t('all','TotalPayed'), $invoiceDetails['totalpayed'] ), 
+                            array( t('all','Balance'), $balance ), 
+                            array( t('all','Status'), $invoiceDetails['status'] ), 
+                            array( t('ContactInfo','Notes'), $invoiceDetails['notes'] ), 
+                        );
+        
+        $invoice_details = "";
+        foreach ($details as $detail) {
+            list( $caption, $data ) = $detail;
+            $invoice_details .= sprintf("<b>%s</b>; %s<br>", $caption, $data);
+        }
+        $invoice_details .= "<br><br>";
 
-		$invoice_items .= "</table>";
-		
-		$customerInfo['invoice_items'] = $invoice_items;
-		
-		// populate invoice items - NEW STYLE
-		$customerInfo['invoiceItems'] = $invoiceItems;
-		$customerInfo['invoiceTotalAmount'] = $invoiceItemsTotalAmount;
-		$customerInfo['invoiceTotalTax'] = $invoiceItemsTotalTax;
-		
-		require(dirname(__FILE__)."/../../library/closedb.php");
+        $customerInfo['invoice_details'] = $invoice_details;
+        
+        // populate customer data - NEW STYLE
+        $customerInfo['customerId'] = $invoiceDetails['user_id'];
+        $customerInfo['customerName'] = (isset($invoiceDetails['company']) ? $invoiceDetails['company'] : $invoiceDetails['contactperson']);
+        $customerInfo['customerAddress'] = $invoiceDetails['address'];
+        $customerInfo['customerAddress2'] = $invoiceDetails['zip'] . ' '. $invoiceDetails['city'] . ' ' .
+                                            $invoiceDetails['state'] . ' ' . $invoiceDetails['country'];
+        $customerInfo['customerEmail'] = $invoiceDetails['email'];
+        $customerInfo['customerPhone'] = $invoiceDetails['phone'];
+        $customerInfo['customerContact'] = $invoiceDetails['contactperson'];
+        
+        $customerInfo['invoiceNumber'] = $invoice_id;
+        $customerInfo['invoiceDate'] = date('Y-m-d', strtotime($invoiceDetails['date']));
+        $customerInfo['invoiceStatus'] = strtoupper($invoiceDetails['status']);
+        $customerInfo['invoiceTotalBilled'] = $invoiceDetails['totalbilled'];
+        $customerInfo['invoicePaid'] = $invoiceDetails['totalpayed'];
+        $customerInfo['invoiceDue'] = $balance;
+        $customerInfo['invoiceNotes'] = $invoiceDetails['notes'];
 
-		return $customerInfo;
-	}
-	
+        $ths = array(
+                        t('title','Plan']),
+                        t('all','Tax']),
+                        t('all','Amount']),
+                        t('ContactInfo','Notes']),
+                    );
+        // populate user invoice items
+        $invoice_items = "<table $tableTags><tr $tableTrTags>";
+        foreach ($ths as $th) {
+            $invoice_items .= sprintf("<th>%s</th>", $th);
+        }
+        $invoice_items .= "</tr>";
+        
+        // get all invoice items
+        $sql = sprintf("SELECT a.id, a.plan_id, a.amount, a.tax_amount, a.notes, b.planName
+                          FROM %s a LEFT JOIN %s b ON a.plan_id=b.id
+                         WHERE a.invoice_id=%d
+                         ORDER BY a.id ASC", $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICEITEMS'],
+                                             $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'],
+                                             $dbSocket->escapeSimple($invoice_id));
+        $res = $dbSocket->query($sql);
+        
+        // initialize invoice items - NEW STYLE
+        $invoiceItems = array();
+        $invoiceItemsNumber = 1;
+        $invoiceItemsTotalAmount = 0;
+        $invoiceItemsTotalTax = 0;
+        
+        while ($row = $res->fetchRow()) {
+            foreach ($row as $i => $value) {
+                $row[$i] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
+            }
+
+            list($id, $plan_id, $amount, $tax_amount, $notes, $planName) = $row;
+
+            $tds = array( $planName, $amount, $tax_amount, $notes );
+            
+            $invoice_items .= "<tr>";
+            foreach ($tds as $td) {
+                $invoice_items .= sprintf("<td>%s</td>", $td);
+            }
+            $invoice_items .= "</tr>";
+
+            // populate invoice items - NEW STYLE
+            $invoiceItem = array();
+            
+            $invoiceItem['invoiceItemNumber'] = sprintf('%02d', $invoiceItemsNumber);
+            $invoiceItem['invoiceItemPlan'] = $planName;
+            $invoiceItem['invoiceItemNotes'] = $notes;
+            $invoiceItem['invoiceItemAmount'] = $amount;
+            $invoiceItem['invoiceItemTaxAmount'] = $tax_amount;
+            $invoiceItem['invoiceItemTotalAmount'] = intval($amount) + intval($tax_amount);
+            
+            $invoiceItems[] = $invoiceItem;
+            $invoiceItemsTotalAmount += intval($amount);
+            $invoiceItemsTotalTax += intval($tax_amount);
+            
+            ++$invoiceItemsNumber;
+        }
+
+        $invoice_items .= "</table>";
+        
+        $customerInfo['invoice_items'] = $invoice_items;
+        
+        // populate invoice items - NEW STYLE
+        $customerInfo['invoiceItems'] = $invoiceItems;
+        $customerInfo['invoiceTotalAmount'] = $invoiceItemsTotalAmount;
+        $customerInfo['invoiceTotalTax'] = $invoiceItemsTotalTax;
+        
+        include("../../library/closedb.php");
+
+        return $customerInfo;
+    }
+    
 ?>
diff --git a/include/common/notificationsWelcome.php b/include/common/notificationsWelcome.php
index c0ea1a68..727926b0 100644
--- a/include/common/notificationsWelcome.php
+++ b/include/common/notificationsWelcome.php
@@ -1,6 +1,6 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
  * Copyright (C) 2007 - Liran Tal <liran@enginx.com> All Rights Reserved.
  *
@@ -13,62 +13,84 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
+ 
+    // prevent this file to be directly accessed
+    if (strpos($_SERVER['PHP_SELF'], '/include/common/notificationWelcome.php') !== false) {
+        header("Location: ../../index.php");
+        exit;
+    }
+ 
+    include_once("notifications/processNotificationWelcome.php");
+    
+    // we (try to) init the email
+    if (isset($email) && !empty($email)) {
+        $invoice_email = $email;
+    } else if (isset($bi_emailinvoice) && !empty($bi_emailinvoice)) {
+        $invoice_email = $bi_emailinvoice;
+    } else if (isset($bi_email) && !empty($bi_email)) {
+        $invoice_email = $bi_email;
+    } else {
+        $invoice_email = "";
+    }
+    
+    
+    // we (try to) init the phone
+    if (isset($mobilephone) && !empty($mobilephone)) {
+        $invoice_phone = $mobilephone;
+    } else if (isset($workphone) && !empty($workphone)) {
+        $invoice_phone = $workphone;
+    } else if (isset($homephone) && !empty($homephone)) {
+        $invoice_phone = $homephone;
+    } else {
+        $invoice_phone = "(n/a)";
+    }
+    
+    // we also (try to) init the address
+    $invoice_address = "";
+    if (isset($ui_address) &&!empty($ui_address)) {
+        $invoice_address = $ui_address;
+    }
+    
+    if (isset($ui_city) && !empty($ui_city)) {
+        $invoice_address .= ", " . $ui_city;
+    }
+    
+    if (isset($ui_state) && !empty($ui_state)) {
+        $invoice_address .= "<br>" . $ui_state;
+    }
+    
+    if (isset($ui_zip) && !empty($ui_zip)) {
+        $invoice_address .= " " . $ui_zip;
+    }
+    
+    if (empty($invoice_address)) {
+        $invoice_address = "(n/a)";
+    }
+    
+    // set SMTP server info
+    $smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
+    $smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
+    $smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
+    
+    // set the from field
+    $from = $configValues['CONFIG_MAIL_SMTPFROM'];
+    
+    // set customer info
+    $customerInfo = array();
+    $customerInfo['customer_name'] = sprintf("%s %s", $firstname, $lastname);
+    $customerInfo['customer_address'] = $invoice_address;
+    $customerInfo['customer_phone'] = $invoice_phone;
+    $customerInfo['customer_email'] = $invoice_email;
+    
+    $customerInfo['service_plan_name'] = $planName;
+    
+    @sendWelcomeNotification($customerInfo, $smtpInfo, $from);
 
-	require_once(dirname(__FILE__)."/../../notifications/processNotificationWelcome.php");
-	
-	if (!empty($email))
-		$invoice_email = $email;
-	else if (!empty($bi_emailinvoice))
-		$invoice_email = $bi_emailinvoice;
-	else if (!empty($bi_email))
-		$invoice_email = $bi_email;
-	else
-		$invoice_email = "";
-	
-	if (!empty($mobilephone))
-		$invoice_phone = $mobilephone;
-	else if (!empty($workphone))
-		$invoice_phone = $workphone;
-	else if (!empty($homephone))
-		$invoice_phone = $homephone;
-	else
-		$invoice_phone = "Unavailable";
-		
-	$invoice_address = "";
-	if (!empty($ui_address))
-		$invoice_address = $ui_address;
-	
-	if (!empty($ui_city))
-		$invoice_address .= ", ".$ui_city;
-	
-	if (!empty($ui_state))
-		$invoice_address .= "<br/>".$ui_state;
-	
-	if (!empty($ui_zip))
-		$invoice_address .= " ".$ui_zip;
-	
-	if (empty($invoice_address))
-		$invoice_address = "Unavailable";
-	
-	$customerInfo = array();
-	$customerInfo['customer_name'] = $firstname ." ".$lastname;
-	$customerInfo['customer_address'] = $invoice_address;
-	
-	$customerInfo['customer_phone'] = $invoice_phone;
-	$customerInfo['customer_email'] = $invoice_email;
-	$customerInfo['service_plan_name'] = $planName;
-	
-	$smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
-	$smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
-	$smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
-	$from = $configValues['CONFIG_MAIL_SMTPFROM'];
-	
-	@sendWelcomeNotification($customerInfo, $smtpInfo, $from);
-
-?>
\ No newline at end of file
+?>
diff --git a/include/common/printTickets.php b/include/common/printTickets.php
index d9e447ac..7791ac2c 100644
--- a/include/common/printTickets.php
+++ b/include/common/printTickets.php
@@ -143,30 +143,35 @@
         </body></html>
     ";
 
 }
 
-    $ticketInformation = "Information: To use this card, please connect your device to the nearest ssid."
-                       . "<br>"
+    $ticketInformation = "<strong>Information</strong>:<br>to use this card, please connect your device to the nearest ssid."
                        . "Open your web browser and enter each needed field.";
     $ticketLogoFile = "/images/daloradius_small.png";
 
 
     if ($_SERVER['REQUEST_METHOD'] === 'POST') {
         if (array_key_exists('accounts', $_POST) && !empty($_POST['accounts']) && is_array($_POST['accounts']) &&
             array_key_exists('type', $_POST) && $_POST['type'] == "batch") {
             
+            $batch_name = (array_key_exists('batch_name', $_POST) && !empty(trim($_POST['batch_name'])))
+                        ? htmlspecialchars(trim($_POST['batch_name']), ENT_QUOTES, 'UTF-8') : "";
+            
             $accounts = $_POST['accounts'];
             
             if (array_key_exists('ticketInformation', $_POST) && !empty(trim($_POST['ticketInformation']))) {
-                $ticketInformation = htmlspecialchars(trim($_POST['ticketInformation']), ENT_QUOTES, 'UTF-8');
+                $ticketInformation = "<strong>Information</strong>:<br>" . htmlspecialchars(trim($_POST['ticketInformation']), ENT_QUOTES, 'UTF-8');
                 $ticketInformation = str_replace("\n", "<br>", $ticketInformation);
             }
             
             $plan = (array_key_exists('plan', $_POST) && !empty(trim($_POST['plan'])))
                   ? trim($_POST['plan']) : "";
-                  
+            
+            $ticketCost = "";
+            $ticketTime = "";
+            
             if (!empty($plan)) {
                 include_once('../../library/opendb.php');
                 include_once('../management/pages_common.php');
 
                 $sql = sprintf("SELECT dbp.planCost AS planCost, dbp.planTimeBank AS planTimeBank, dbp.planCurrency AS planCurrency
@@ -177,23 +182,42 @@
 
                 $ticketCost = "$ticketCost $ticketCurrency";
                 $ticketTime = time2str($ticketTime);
 
                 include_once('../../library/closedb.php');
-            } else {
-                $ticketCost = $ticketTime = $ticketCurrency = "(n/a)";
+            }
+            
+            $card_body_height = 10;
+            $card_foot_height = 30;
+            if (!empty($ticketCost)) {
+                $card_foot_height -= 5;
+                $card_body_height += 5;
+            }
+            if (!empty($ticketTime)) {
+                $card_foot_height -= 5;
+                $card_body_height += 5;
             }
             
 ?>
 <!DOCTYPE html>
 <html lang="en">
     <head>
         <meta charset="utf-8" />
-        <title>untitled</title>
+        <title><?= (!empty($batch_name)) ? $batch_name : "user cards" ?></title>
         
         <style>
 
+@page {
+    size: 21cm 29.7cm;
+    margin: 0;
+}
+
+body {
+    font-family: Tahoma;
+    padding: 1cm;
+}
+
 .container:first-child .card {
     border-top: 1px dotted gray;
 }
 
 .card {
@@ -227,40 +251,55 @@
     right: 0;
     bottom: 0;
 }
 
 .card-body {
-    height: 18mm;
-    width: 100%;
+    height: <?= $card_body_height ?>mm;
+    width: 42mm;
     margin: 0;
     padding: 1mm;
 }
 
 .card-body table {
     border: 0;
     margin: 0;
     width: 100%;
     text-align: center;
-    font-size: 9pt;
+    font-size: 8pt;
     padding: 0;
 }
 
-.card-foot {
-    height: 24mm;
+.card-body table tr {
+    border: 0;
+    margin: 0;
     width: 100%;
+    height: 5mm;
+    padding: 0;
+}
+
+.card-body table th {
+    text-align: right;
+}
+
+.card-body table td {
+    text-align: left;
+}
+
+.card-foot {
+    height: <?= $card_foot_height ?>mm;
     margin: 0;
 }
 
 .card-foot p {
     margin: 0;
-    padding: 2mm;
-    font-size: 7pt;
+    width: 42mm;
+    padding: 1mm;
+    font-size: 8pt;
     font-weight: normal;
     text-align: justify;
 }
 
-
 .container {
     text-align: center;
     padding: 0;
     margin: 0;
 }
@@ -289,31 +328,33 @@
             
             if ($i != 0 && $i % 4 == 0) {
                 echo '</div><div class="container">';
             }
             
+            $trs = array(
+                            "User" => $username,
+                            "Pass" => $password
+                        );
+            
+            if (!empty($ticketTime)) {
+                $trs["Validity"] = $ticketTime;
+            }
+            
+            if (!empty($ticketCost)) {
+                $trs["Price"] = $ticketCost;
+            }
+            
             echo '<div class="card">';
             printf('<div class="card-head"><img src="%s"></div>', $ticketLogoFile); 
             echo '<div class="card-body">'
-               . '<table>'
-               . '<tr>'
-               . '<th>Login</th>';
-            printf('<td>%s</td>', htmlspecialchars($username, ENT_QUOTES, 'UTF-8'));
-            echo '</tr>'
-               . '<tr>'
-               . '<th>Password</th>';
-            printf('<td>%s</td>', htmlspecialchars($password, ENT_QUOTES, 'UTF-8'));
-            echo '</tr>'
-               . '<tr>'
-               . '<th>Validity</th>';
-            printf('<td>%s</td>', htmlspecialchars($ticketTime, ENT_QUOTES, 'UTF-8'));
-            echo '</tr>'
-               . '<tr>'
-               . '<th>Price</th>';
-            printf('<td>%s</td>', htmlspecialchars($ticketCost, ENT_QUOTES, 'UTF-8'));
-            echo '</tr>'
-               . '</table>'
+               . '<table>';
+
+            foreach ($trs as $label => $value) {
+                printf('<tr><th>%s:</th><td>%s</td></tr>', $label, htmlspecialchars($value, ENT_QUOTES, 'UTF-8'));
+            }
+
+            echo '</table>'
                . '</div>'
                . '<div class="card-foot">';
             printf('<p>%s</p>', $ticketInformation);
             echo '</div>'
                . '</div>';
diff --git a/library/checklogin.php b/library/checklogin.php
index 383814b6..aa5f614f 100644
--- a/library/checklogin.php
+++ b/library/checklogin.php
@@ -31,12 +31,18 @@ if (strpos($_SERVER['PHP_SELF'], '/library/checklogin.php') !== false) {
 }
 
 include('sessions.php');
 dalo_session_start();
 
+
 if (!array_key_exists('daloradius_logged_in', $_SESSION) || $_SESSION['daloradius_logged_in'] !== true) {
     $_SESSION['daloradius_logged_in'] = false;
-    header('Location: /login.php');
+
+    // implement a sort of "dynamic redirect finder" based on the number of "/" in the PHP_SELF value
+    $count = substr_count($_SERVER['PHP_SELF'], "/", 1);
+    $location = str_repeat("../", $count) . "login.php";
+
+    header("Location: " . $location);
     exit;
 }
 
 ?>
diff --git a/mng-batch-add.php b/mng-batch-add.php
index 0cd83fd3..b85ef7ef 100644
--- a/mng-batch-add.php
+++ b/mng-batch-add.php
@@ -396,48 +396,43 @@
                         $exportCSV .= "$username,$password||";
                         
                     } // end for
                     
                     $form_id = "export-users-form";
-                    $exportForm .= sprintf('<form target="_blank" style="display: none" id="%s" ', $form_id)
+                    $exportForm .= sprintf('<form target="_blank" id="%s" ', $form_id)
                                  . 'method="POST">'
-                                 . sprintf('<input type="hidden" name="batch_name" value="%s">',
+                                 . sprintf('<input style="display: none" type="hidden" name="batch_name" value="%s">',
                                            htmlspecialchars($batch_name, ENT_QUOTES, 'UTF-8'))
-                                 . '<input type="hidden" name="type" value="batch">';
+                                 . '<input style="display: none" type="hidden" name="type" value="batch">';
                                  
                     if (!empty($planName)) {
                         $exportForm .= sprintf('<input type="hidden" name="plan" value="%s">',
                                                htmlspecialchars($planName, ENT_QUOTES, 'UTF-8'));
                     }
                     
                     for ($i = 0; $i < count($inserted_usernames); $i++) {
                         $u = $inserted_usernames[$i];
                         $p = $inserted_passwords[$i];
-                        $exportForm .= sprintf('<input type="hidden" name="accounts[%d][0]" value="%s">', $i, htmlspecialchars($u, ENT_QUOTES, 'UTF-8'))
-                                     . sprintf('<input type="hidden" name="accounts[%d][1]" value="%s">', $i, htmlspecialchars($p, ENT_QUOTES, 'UTF-8'));
+                        $exportForm .= sprintf('<input style="display: none" type="hidden" name="accounts[%d][0]" value="%s">', $i, htmlspecialchars($u, ENT_QUOTES, 'UTF-8'))
+                                     . sprintf('<input style="display: none" type="hidden" name="accounts[%d][1]" value="%s">', $i, htmlspecialchars($p, ENT_QUOTES, 'UTF-8'));
                     }
-                    $exportForm .= '</form>';
+                    
+                    
+                    $exportForm .= '<fieldset><label for="ticketInformation" class="form">Description</label>'
+                                 . '<textarea class="form" id="ticketInformation" name="ticketInformation">'
+                                 . 'to use this card, please connect your device to the nearest ssid.' . "\n"
+                                 . 'Open your web browser and enter each needed field.</textarea>';
+                    $exportForm .= '</fieldset></form>';
                     $onclick = "batch_export('include/common/fileExportCSV.php')";
-                    $exportForm .= sprintf('<input type="button" onclick="%s" value="CSV Download">', $onclick);
+                    $exportForm .= sprintf('<input style="margin: 10px" type="button" onclick="%s" value="CSV Download">', $onclick);
                     $onclick = "batch_export('include/common/printTickets.php')";
-                    $exportForm .= sprintf('<input type="button" onclick="%s" value="Printable Tickets">', $onclick);
+                    $exportForm .= sprintf('<input style="margin: 10px" type="button" onclick="%s" value="Printable Tickets">', $onclick);
                             
-                            // if batch_history record was created successfuly
-                            //~ if ($sql_batch_id != 0) {
-                                //~ // remove the last || chars to sanitize it for proper format
-                                //~ $exportCSV = substr($exportCSV, 0, -2);
-                                //~ $successMsg = "Exported Usernames - ".
-                                                    //~ "<a href='include/common/fileExportCSV.php?csv_output=$exportCSV'>download</a><br/>".
-                                                //~ "Printable Tickets - ".
-                                                    //~ "<a href='include/common/printTickets.php?type=batch&plan=$planName&accounts=$exportCSV'>view</a><br/>".
-                                                //~ "Added to database new user(s): <b> $actionMsgGoodUsernames </b><br/>";
                     $successMsg = sprintf("Created %d user(s) (batch name: <strong>%s</strong>)", count($inserted_usernames)-1, $batch_name);
                     $logAction .= sprintf("Successfully added to database new users [%s] with prefix [%s] on page: ",
                                           implode(", ", $inserted_usernames), $username_prefix);
-                            //~ }
-                
-                
+
                 } else {// $number > 0
                     $failureMsg = "specify a valid number of accounts";
                     $logAction = "specified an invalid number of accounts on page: ";
                 }
             }
diff --git a/mng-del.php b/mng-del.php
index 49ac1408..144fb206 100644
--- a/mng-del.php
+++ b/mng-del.php
@@ -30,171 +30,203 @@
     // init logging variables
     $log = "visited page: ";
     $logAction = "";
     $logDebugSQL = "";
 
-    isset($_GET['username']) ? $username = $_GET['username'] : $username = "";
-    isset($_GET['attribute']) ? $attribute = $_GET['attribute'] : $attribute = "";
-    isset($_GET['tablename']) ? $tablename = $_GET['tablename'] : $tablename = "";
-    isset($_GET['delradacct']) ? $delradacct = $_GET['delradacct'] : $delradacct = "";
-    isset($_GET['clearSessionsUsers']) ? $clearSessionsUsers = $_GET['clearSessionsUsers'] : $clearSessionsUsers = "";
-
-    $logAction = "";
-    $logDebugSQL = "";
-
-    $showRemoveDiv = "block";
-
-    if ( (isset($_GET['username'])) && (!(isset($_GET['attribute']))) && (!(isset($_GET['tablename']))) ) {
-
-        $allUsernames = "";
-        $isSuccessful = 0;
-
-        /* since the foreach loop will report an error/notice of undefined variable $value because
-           it is possible that the $username is not an array, but rather a simple GET request
-           with just some value, in this case we check if it's not an array and convert it to one with
-           a NULL 2nd element
-        */
-        if (!is_array($username))
-            $username = array($username, NULL);
-
-        foreach ($username as $variable=>$value) {
-
-            if (trim($variable) != "") {
-
-                $username = $value;
-                $allUsernames .= $username . ", ";
-
-                include 'library/opendb.php';
-
-                // setting table-related parameters first
-                switch($configValues['FREERADIUS_VERSION']) {
-                    case '1' :
-                        $tableSetting['postauth']['user'] = 'user';
-                        $tableSetting['postauth']['date'] = 'date';
-                        break;
-                    case '2' :
-                        // down
-                    case '3' :
-                        // down
-                    default  :
-                        $tableSetting['postauth']['user'] = 'username';
-                        $tableSetting['postauth']['date'] = 'authdate';
-                        break;
+    $valid_tablenames = array(
+                                $configValues['CONFIG_DB_TBL_RADCHECK'],
+                                $configValues['CONFIG_DB_TBL_RADREPLY'],
+                                $configValues['CONFIG_DB_TBL_RADGROUPREPLY'],
+                                $configValues['CONFIG_DB_TBL_RADGROUPCHECK']
+                             );
+
+    include('library/opendb.php');
+
+    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+        if (array_key_exists('csrf_token', $_POST) && isset($_POST['csrf_token']) && dalo_check_csrf_token($_POST['csrf_token'])) {
+                
+            $id__attribute = (array_key_exists('attribute', $_POST) && !empty(trim($_POST['attribute'])) &&
+                              preg_match("/__/", trim($_POST['attribute'])) !== false) ? trim($_POST['attribute']) : "";
+            
+            $tablename = (!empty($id__attribute) && array_key_exists('tablename', $_POST) &&
+                          !empty(trim($_POST['tablename'])) && in_array(trim($_POST['tablename']), $valid_tablenames))
+                       ? trim($_POST['tablename']) : "";
+
+            $delradacct = (array_key_exists('delradacct', $_POST) && strtolower(trim($_POST['delradacct'])) == 'yes');
+
+
+            // validate values
+            $usernames = array();
+            
+            if (array_key_exists('username', $_POST) && !empty($_POST['username'])) {
+                
+                $tmp = (!is_array($_POST['username'])) ? array($_POST['username']) : $_POST['username'];
+                foreach ($tmp as $value) {
+                    
+                    $value = trim(str_replace("%", "", $value));
+                    
+                    if (!in_array($value, $usernames)) {
+                        $usernames[] = $value;
+                    }
                 }
-
-                // delete all attributes associated with a username
-                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADCHECK']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
-                $res = $dbSocket->query($sql);
-                $logDebugSQL .= $sql . "\n";
-
-                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADREPLY']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
-                $res = $dbSocket->query($sql);
-                $logDebugSQL .= $sql . "\n";
-
-                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_DALOUSERINFO']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
-                $res = $dbSocket->query($sql);
-                $logDebugSQL .= $sql . "\n";
-
-                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
-                $res = $dbSocket->query($sql);
-                $logDebugSQL .= $sql . "\n";
-
-                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADUSERGROUP']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
-                $res = $dbSocket->query($sql);
-                $logDebugSQL .= $sql . "\n";
-
-                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADPOSTAUTH']." WHERE ".
-                    $tableSetting['postauth']['user']."='".$dbSocket->escapeSimple($username)."'";
-                $res = $dbSocket->query($sql);
-                $logDebugSQL .= $sql . "\n";
-
-                if (strtolower($delradacct) == "yes") {
-                    $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADACCT']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
-                    $res = $dbSocket->query($sql);
-                    $logDebugSQL .= $sql . "\n";
+                
+                if (count($usernames) > 0) {
+                
+                    if (!empty($id__attribute) && !empty($tablename)) {
+                        
+                        $sql = sprintf("SELECT COUNT(id) FROM %s WHERE username='%s'",
+                                       $configValues['CONFIG_DB_TBL_RADCHECK'], $dbSocket->escapeSimple($usernames[0]));
+                        $res = $dbSocket->query($sql);
+                        $logDebugSQL .= "$sql;\n";
+                        
+                        $check_attr_count = intval($res->fetchrow()[0]);
+                        
+                        $sql = sprintf("SELECT COUNT(id) FROM %s WHERE username='%s' AND attribute='Auth-Type' OR attribute LIKE '%%-Password'",
+                                       $configValues['CONFIG_DB_TBL_RADCHECK'], $dbSocket->escapeSimple($usernames[0]));
+                        $res = $dbSocket->query($sql);
+                        $logDebugSQL .= "$sql;\n";
+                        
+                        $check_auth_attr_count = intval($res->fetchrow()[0]);
+                        
+                        list($columnId, $attribute) = explode("__", $id__attribute);
+                        $attribute = trim($attribute);
+                        $columnId = intval(trim($columnId));
+                        
+                        if ($tablename == 'radcheck' && $check_attr_count == 1 ||
+                            (($attribute == 'Auth-Type' || preg_match("/-Password$/", $attribute) !== false) && $check_auth_attr_count == 1)) {
+                            // if operator wants to remove the last check attribute
+                            // or the last "password-like" check attribute
+                            // they should delete all user related info stored in the db
+                            
+                            $format = "Cannot delete the last check (password like?) attribute for the selected user (<strong>%s</strong>)";
+                            $failureMsg = sprintf($format,
+                                                  htmlspecialchars($usernames[0], ENT_QUOTES, 'UTF-8'));
+                            $logAction = sprintf("$format on page: ", $username[0]);
+                        } else {
+                        
+                            $sql = sprintf("DELETE FROM %s WHERE username='%s' AND attribute='%s' AND id=%s",
+                                           $dbSocket->escapeSimple($tablename), $dbSocket->escapeSimple($usernames[0]),
+                                           $dbSocket->escapeSimple($attribute), $dbSocket->escapeSimple($columnId));
+                            $res = $dbSocket->query($sql);
+                            $logDebugSQL .= "$sql;\n";
+                            
+                            $format = "Deleted attribute <strong>%s</strong> for user <strong>%s</strong>";
+                            $successMsg = sprintf($format, htmlspecialchars($attribute, ENT_QUOTES, 'UTF-8'),
+                                                           htmlspecialchars($usernames[0], ENT_QUOTES, 'UTF-8'));
+                            $logAction = sprintf("$format on page: ", $attribute, $usernames[0]);
+                        }
+                    } else {
+                        $dbusers = array();
+                        
+                        foreach ($usernames as $u) {
+                            if (!empty($dbSocket->escapeSimple($u))) {
+                                $dbusers[] = $dbSocket->escapeSimple($u);
+                            }
+                        }
+                        
+                        $dbusersLen = count($dbusers);
+                        if ($dbusersLen > 0) {
+                            // setting table-related parameters first                
+                            switch($configValues['FREERADIUS_VERSION']) {
+                                case '1' :
+                                    $tableSetting['postauth']['user'] = 'user';
+                                    $tableSetting['postauth']['date'] = 'date';
+                                    break;
+                                case '2' :
+                                    // down
+                                case '3' :
+                                    // down
+                                default  :
+                                    $tableSetting['postauth']['user'] = 'username';
+                                    $tableSetting['postauth']['date'] = 'authdate';
+                                    break;
+                            }
+                            
+                            $sql_format = "DELETE FROM %s WHERE %s IN ('" . implode("', '", $dbusers) . "')";
+                            
+                            $sql = sprintf($sql_format, $configValues['CONFIG_DB_TBL_RADPOSTAUTH'],
+                                                        $tableSetting['postauth']['user']);
+                            $res = $dbSocket->query($sql);
+                            $logDebugSQL .= "$sql;\n";
+                            
+                            $tables = array(
+                                                $configValues['CONFIG_DB_TBL_RADCHECK'],
+                                                $configValues['CONFIG_DB_TBL_RADREPLY'],
+                                                $configValues['CONFIG_DB_TBL_DALOUSERINFO'],
+                                                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'],
+                                                $configValues['CONFIG_DB_TBL_RADUSERGROUP'],
+                                           );
+
+                            if ($delradacct) {
+                                $tables[] = $configValues['CONFIG_DB_TBL_RADACCT'];
+                            }
+                            
+                            foreach ($tables as $table) {
+                                $sql = sprintf($sql_format, $table, 'username');
+                                $res = $dbSocket->query($sql);
+                                $logDebugSQL .= "$sql;\n";
+                            }
+                            
+                            $format = "<strong>%d user(s)</strong> have been deleted";
+                            $successMsg = sprintf($format, $dbusersLen);
+                            $logAction = sprintf("$format on page: ", $dbusersLen);
+                            
+                        } else {
+                            $failureMsg = "You have provided an empty or invalid username list";
+                            $logAction = "Provided an empty or invalid username list (user(s) deletion) on page: ";
+                        }
+                    }
+                } else {
+                    $failureMsg = "You have provided an empty or invalid username list";
+                    $logAction = "Provided an empty or invalid username list (user(s) deletion) on page: ";
+                }
+            } else if (array_key_exists('clearSessionsUsers', $_POST) && !empty($_POST['clearSessionsUsers'])) {
+                
+                $username__starttimes = array();
+                
+                $tmp = (!is_array($_POST['clearSessionsUsers'])) ? array($_POST['clearSessionsUsers']) : $_POST['clearSessionsUsers'];
+                foreach ($tmp as $value) {
+                    
+                    $value = trim(str_replace("%", "", $value));
+                    
+                    if (!in_array($value, $username__starttimes)) {
+                        $username__starttimes[] = $value;
+                    }
+                }
+                
+                
+                $userstimesLen = count($username__starttimes);
+                if ($userstimesLen > 0) {
+                    
+                    foreach ($username__starttimes as $username__starttime) {
+                        list($username, $starttime) = explode('||', $username__starttime);
+                        $sql = sprintf("DELETE FROM %s
+                                         WHERE username='%s' AND AcctStartTime='%s'
+                                           AND (AcctStopTime='0000-00-00 00:00:00' OR AcctStopTime IS NULL)",
+                                       $configValues['CONFIG_DB_TBL_RADACCT'], $dbSocket->escapeSimple($username),
+                                       $dbSocket->escapeSimple($starttime));
+                        $res = $dbSocket->query($sql);
+                        $logDebugSQL .= "$sql;\n";
+                    }
+                    
+                    $format = "<strong>%d user' session(s)</strong> have been cleaned";
+                    $successMsg = sprintf($format, $userstimesLen);
+                    $logAction = sprintf("$format on page: ", $userstimesLen);
+                    
+                } else {
+                    $failureMsg = "You have provided an empty or invalid username list";
+                    $logAction = "Provided an empty or invalid username list (session cleaning) on page: ";
                 }
-
-                $successMsg = "Deleted user(s): <b> $allUsernames </b>";
-                $logAction .= "Successfully deleted user(s) [$allUsernames] on page: ";
-
-                include 'library/closedb.php';
-
-            }  else {
-                $failureMsg = "no user was entered, please specify a username to remove from database";
-                $logAction .= "Failed deleting user(s) [$allUsernames] on page: ";
             }
-
-
-        $showRemoveDiv = "none";
-
-        } //foreach
-
-
-    } else     if ( (isset($_GET['username'])) && (isset($_GET['attribute'])) && (isset($_GET['tablename'])) ) {
-
-        /* this section of the deletion process only deletes the username record with the specified attribute
-         * variable from $tablename, this is in order to support just removing a single attribute for the user
-         */
-
-        include 'library/opendb.php';
-
-        if (isset($attribute)) {
-            if (preg_match('/__/', $attribute))
-                list($columnId, $attribute) = explode("__", $attribute);
-            else
-                $attribute = $attribute;
+        } else {
+            $failureMsg = sprintf("CSRF token error");
+            $logAction .= sprintf("CSRF token error on page: ");
         }
-
-        $sql = "DELETE FROM ".$dbSocket->escapeSimple($tablename)." WHERE Username='".$dbSocket->escapeSimple($username)."' ".
-                " AND Attribute='".$dbSocket->escapeSimple($attribute)."' AND id=".$dbSocket->escapeSimple($columnId);
-        $res = $dbSocket->query($sql);
-        $logDebugSQL .= $sql . "\n";
-
-        $successMsg = "Deleted attribute: <b> $attribute </b> for user(s): <b> $username </b> from database";
-        $logAction .= "Successfully deleted attribute [$attribute] for user [$username] on page: ";
-
-        include 'library/closedb.php';
-
-        $showRemoveDiv = "none";
-
-    } else if ( (isset($clearSessionsUsers)) && ($clearSessionsUsers != "") ) {
-
-        /* this is used to remove stale user sessions from the accounting table
-        */
-        $allUsernames = "";
-
-        if (!is_array($clearSessionsUsers))
-            $clearSessionsUsers = array($clearSessionsUsers, NULL);
-
-            foreach ($clearSessionsUsers as $variable=>$value) {
-
-                if (trim($value) != "") {
-
-                    list($userSessions,$acctStartTime) = preg_split('/\\|\\|/', $value);
-
-                    $allUsernames .= $userSessions . ", ";
-
-                    include 'library/opendb.php';
-
-                    $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADACCT'].
-                        " WHERE Username='$userSessions' AND AcctStartTime='$acctStartTime' ".
-                        " AND (AcctStopTime='0000-00-00 00:00:00' OR AcctStopTime IS NULL)";
-                    $res = $dbSocket->query($sql);
-                    $logDebugSQL .= $sql . "\n";
-
-                    $successMsg = "Deleted stale accounting sessions for user: <b> $allUsernames </b> from database";
-                    $logAction .= "Successfully deleted stale accounting sessions for user [$allUsernames] on page: ";
-
-                    include 'library/closedb.php';
-            } // if trim
-
-        } // foreach
-
-        $showRemoveDiv = "none";
-
     }
 
+    include('library/closedb.php');
+
     include_once("lang/main.php");
     include("library/layout.php");
 
     // print HTML prologue
     $title = t('Intro','mngdel.php');
@@ -211,52 +243,77 @@
     echo '<div id="contentnorightbar">';
     print_title_and_help($title, $help);
 
     include_once('include/management/actionMessages.php');
 
+    include('library/opendb.php');
+    
+    $sql = sprintf("SELECT DISTINCT(username) FROM %s", $configValues['CONFIG_DB_TBL_RADCHECK']);
+    $res = $dbSocket->query($sql);
+    $logDebugSQL .= "$sql;\n";
+    
+    $options = array();
+    while ($row = $res->fetchrow()) {
+        $options[] = $row[0];
+    }
+    
+    include('library/closedb.php');
+
+    $input_descriptors1 = array();
+
+    $input_descriptors1[] = array(
+                                'name' => 'username[]',
+                                'id' => 'username',
+                                'type' => 'select',
+                                'caption' => t('all','Username'),
+                                'options' => $options,
+                                'multiple' => true,
+                                'size' => 5
+                             );
+
+    $input_descriptors1[] = array(
+                                'name' => 'delradacct',
+                                'type' => 'select',
+                                'caption' => t('all','RemoveRadacctRecords'),
+                                'options' => array("", "yes", "no"),
+                             );
+
+    $input_descriptors1[] = array(
+                                    "name" => "csrf_token",
+                                    "type" => "hidden",
+                                    "value" => dalo_csrf_token(),
+                                 );
+
+    $input_descriptors1[] = array(
+                                    'type' => 'submit',
+                                    'name' => 'submit',
+                                    'value' => t('buttons','apply')
+                                 );
 ?>
 
-    <div id="removeDiv" style="display:<?php echo $showRemoveDiv ?>;visibility:visible" >
-    <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="get">
-
+<form method="POST">
     <fieldset>
+        <h302><?= t('title','AccountRemoval') ?></h302>
+        <ul style="margin: 10px auto">
+<?php
+        foreach ($input_descriptors1 as $input_descriptor) {
+            print_form_component($input_descriptor);
+        }
+?>
 
-        <h302> <?php echo t('title','AccountRemoval') ?> </h302>
-        <br/>
-
-        <label for='username' class='form'><?php echo t('all','Username')?></label>
-        <input name='username[]' type='text' id='username' value='<?php echo $username ?>' tabindex=100 />
-        <br />
-
-        <label for='delradacct' class='form'><?php echo t('all','RemoveRadacctRecords')?></label>
-        <select class='form' tabindex=102 name='delradacct' tabindex=101>
-            <option value='no'>no</option>
-            <option value='yes'>yes</option>
-        </select>
-        <br />
-
-        <br/><br/>
-        <hr><br/>
-        <input type="submit" name="submit" value="<?php echo t('buttons','apply') ?>" tabindex=1000
-            class='button' />
-
+        </ul>
     </fieldset>
+</form>
+<?php
 
-    </form>
-    </div>
+    if (array_key_exists('PREV_LIST_PAGE', $_SESSION) && !empty(trim($_SESSION['PREV_LIST_PAGE']))) {
+        echo '<div style="float: right; text-align: right; margin: 0; font-size: small">';
+        printf('<a href="%s" title="Back to Previous Page">Back to Previous Page</a>', trim($_SESSION['PREV_LIST_PAGE']));
+        echo '</div>';
+        
+        unset($_SESSION['PREV_LIST_PAGE']);
+    }
 
-<?php
     include('include/config/logging.php');
-    
-    include_once("include/management/autocomplete.php");
-    
-    if ($autoComplete) {
-         $inline_extra_js = "
-autoComEdit = new DHTMLSuite.autoComplete();
-autoComEdit.add('username','include/management/dynamicAutocomplete.php','_small','getAjaxAutocompleteUsernames');";
-    } else {
-        $inline_extra_js = "";
-    }
-    
-    print_footer_and_html_epilogue($inline_extra_js);
+    print_footer_and_html_epilogue();
 ?>
 
diff --git a/mng-edit.php b/mng-edit.php
index 54c9a83f..87235d18 100644
--- a/mng-edit.php
+++ b/mng-edit.php
@@ -471,10 +471,16 @@ window.onload = function(){
     include('include/management/populate_selectbox.php');
     
     if (!empty($username)) {
         $input_descriptors1 = array();
         
+        $input_descriptors1[] = array(
+                                        "type" => "hidden",
+                                        "value" => dalo_csrf_token(),
+                                        "name" => "csrf_token"
+                                     );
+        
         $input_descriptors1[] = array(
                                         "type" => "hidden",
                                         "value" => $username_enc,
                                         "name" => "username"
                                      );
@@ -608,14 +614,14 @@ window.onload = function(){
             }
 
             $id__attribute = sprintf('%s__%s', $row[5], $row[0]);
             $name = sprintf('editValues%s[]', $editCounter);
             $type = (preg_match("/-Password$/", $row[0])) ? $hiddenPassword : "text";
-
+            $onclick = sprintf("document.getElementById('form-%d-radcheck').submit()", $editCounter);
+            
             echo '<li>';
-            printf('<a class="tablenovisit" href="mng-del.php?username=%s&attribute=%s&tablename=radcheck">',
-                   urlencode($username_enc), urlencode($id__attribute));
+            printf('<a class="tablenovisit" href="#" onclick="%s">', $onclick);
             echo '<img src="images/icons/delete.png" border="0" alt="Remove"></a>';
             
             printf('<label for="attribute" class="attributes">%s</label>', $row[0]);
 
             printf('<input type="hidden" name="%s" value="%s">', $name, $id__attribute);            
@@ -628,11 +634,11 @@ window.onload = function(){
 
             printf('<input type="hidden" name="%s" value="radcheck">', $name);
 
 
             if (!empty($row[3]) || !empty($row[4])) {
-                $divId = sprintf("%s-Tooltip-%d-check", $row[0], $editCounter);
+                $divId = sprintf("%s-Tooltip-%d-radcheck", $row[0], $editCounter);
                 $onclick = sprintf("toggleShowDiv('%s')", $divId);
                 printf('<img src="images/icons/comment.png" alt="Tip" border="0" onClick="%s">', $onclick);
                 printf('<div id="%s" style="display:none;visibility:visible" class="ToolTip2">', $divId);
                 
                 if (!empty($row[3])) {
@@ -705,14 +711,14 @@ window.onload = function(){
             }
 
             $id__attribute = sprintf('%s__%s', $row[5], $row[0]);
             $name = sprintf('editValues%s[]', $editCounter);
             $type = (preg_match("/-Password$/", $row[0])) ? $hiddenPassword : "text";
-    
+            $onclick = sprintf("document.getElementById('form-%d-radreply').submit()", $editCounter);
+            
             echo '<li>';
-            printf('<a class="tablenovisit" href="mng-del.php?username=%s&attribute=%s&tablename=radreply">',
-                   urlencode($username_enc), urlencode($id__attribute));
+            printf('<a class="tablenovisit" href="#" onclick="%s">', $onclick);
             echo '<img src="images/icons/delete.png" border="0" alt="Remove"></a>';
 
             printf('<label for="attribute" class="attributes">%s</label>', $row[0]);
 
             printf('<input type="hidden" name="%s" value="%s">', $name, $id__attribute);            
@@ -724,11 +730,11 @@ window.onload = function(){
             echo '</select>';
 
             printf('<input type="hidden" name="%s" value="radreply">', $name);
 
             if (!empty($row[3]) || !empty($row[4])) {
-                $divId = sprintf("%s-Tooltip-%d-reply", $row[0], $editCounter);
+                $divId = sprintf("%s-Tooltip-%d-radreply", $row[0], $editCounter);
                 $onclick = sprintf("toggleShowDiv('%s')", $divId);
                 printf('<img src="images/icons/comment.png" alt="Tip" border="0" onClick="%s">', $onclick);
                 printf('<div id="%s" style="display:none;visibility:visible" class="ToolTip2">', $divId);
                 
                 if (!empty($row[3])) {
@@ -841,10 +847,48 @@ window.onload = function(){
 
      </div>
 
 </form>
 
+<?php
+    include('library/opendb.php');
+
+    $tables = array(
+                        'radcheck' => $configValues['CONFIG_DB_TBL_RADCHECK'],
+                        'radreply' => $configValues['CONFIG_DB_TBL_RADREPLY']
+                   );
+
+    $csrf_token = dalo_csrf_token();
+
+    foreach ($tables as $table_value => $table) {
+
+        $sql = sprintf("SELECT id, attribute, value FROM %s WHERE username='%s'",
+                       $table, $dbSocket->escapeSimple($username));
+        $res = $dbSocket->query($sql);
+        $logDebugSQL .= "$sql;\n";
+
+        if ($res->numRows() > 0) {
+            $counter = 0;
+            while ($row = $res->fetchrow()) {
+                list($id, $attribute, $value) = $row;
+                
+                $formId = sprintf("form-%d-%s", $counter, $table_value);
+                $id__attribute = sprintf("%d__%s", intval($id), htmlspecialchars($attribute, ENT_QUOTES, 'UTF-8'));
+                
+                printf('<form id="%s" style="display: none" method="POST" action="mng-del.php">', $formId);
+                printf('<input type="hidden" name="username" value="%s">', $username_enc);
+                printf('<input type="hidden" name="attribute" value="%s">', $id__attribute);
+                printf('<input type="hidden" name="csrf_token" value="%s">', $csrf_token);
+                printf('<input type="hidden" name="tablename" value="%s">', $table_value);
+                echo '</form>';
+                
+                $counter++;
+            }
+        }
+    }
+    include('library/closedb.php');
+?>
     <div id="OtherInfo-tab" class="tabcontent" title="Other Info">
 <?php
         include_once('include/management/userReports.php');
         userPlanInformation($username, 1);
         userSubscriptionAnalysis($username, 1);                 // userSubscriptionAnalysis with argument set to 1 for drawing the table
diff --git a/mng-list-all.php b/mng-list-all.php
index 586b19e2..272e49c7 100644
--- a/mng-list-all.php
+++ b/mng-list-all.php
@@ -193,11 +193,11 @@
         // this can be passed as form attribute and 
         // printTableFormControls function parameter
         $action = "mng-del.php";
 ?>
 
-<form name="listall" method="GET" action="<?= $action ?>">
+<form name="listall" method="POST" action="<?= $action ?>">
 
     <table border="0" class="table1">
         <thead>
             <tr style="background-color: white">
 <?php
@@ -287,10 +287,13 @@
         $links = setupLinks_str($pageNum, $maxPage, $orderBy, $orderType);
         printTableFoot($per_page_numrows, $numrows, $colspan, $drawNumberLinks, $links);
 ?>
 
     </table>
+
+    <input type="hidden" name="csrf_token" value="<?= dalo_csrf_token() ?>">
+    
 </form>
 
 <?php
     } else {
         $failureMsg = "Nothing to display";
diff --git a/mng-rad-profiles-list.php b/mng-rad-profiles-list.php
index 97666e1c..7bda33df 100644
--- a/mng-rad-profiles-list.php
+++ b/mng-rad-profiles-list.php
@@ -182,13 +182,13 @@
     
     
     include('include/config/logging.php');
     
     $inline_extra_js = "
-    var tooltipObj = new DHTMLgoodies_formTooltip();
-    tooltipObj.setTooltipPosition('right');
-    tooltipObj.setPageBgColor('#EEEEEE');
-    tooltipObj.setTooltipCornerSize(15);
-    tooltipObj.initFormFieldTooltip();";
+var tooltipObj = new DHTMLgoodies_formTooltip();
+tooltipObj.setTooltipPosition('right');
+tooltipObj.setPageBgColor('#EEEEEE');
+tooltipObj.setTooltipCornerSize(15);
+tooltipObj.initFormFieldTooltip();";
     
     print_footer_and_html_epilogue($inline_extra_js);
 ?>
diff --git a/mng-search.php b/mng-search.php
index 4569a4a1..25d03688 100644
--- a/mng-search.php
+++ b/mng-search.php
@@ -48,10 +48,20 @@
     
     $username_enc = (!empty($username))
                   ? htmlspecialchars($username, ENT_QUOTES, 'UTF-8')
                   : "";
 
+    // init logging variables
+    $log = "visited page: ";
+    $logQuery = "performed query for ";
+    if (!empty($username)) {
+         $logQuery .= "username(s) starting with [$username] ";
+    } else {
+        $logQuery .= "all usernames ";
+    }
+    $logQuery .= "on page: ";
+
     //feed the sidebar variables
     $search_username = $username_enc;
 
     if (!empty($username_enc)) {
         $title .=  " :: " . $username_enc;
@@ -140,13 +150,17 @@
         $per_page_numrows = $res->numRows();
         
         // the partial query is built starting from user input
         // and for being passed to setupNumbering and setupLinks functions
         $partial_query_string = (!empty($username_enc) ? "&usernameOnline=" . urlencode($username_enc) : "");
+        
+        // this can be passed as form attribute and 
+        // printTableFormControls function parameter
+        $action = "mng-del.php";
 ?>
         
-<form name="searchusers" method="GET" action="mng-del.php">
+<form name="searchusers" method="POST" action="<?= $action ?>">
     <table border="0" class="table1">
         <thead>
             <tr style="background-color: white">
 <?php
         if ($drawNumberLinks) {
@@ -162,11 +176,11 @@
             </tr>
             
             <tr>
                 <th style="text-align: left" colspan="<?= $colspan ?>">
 <?php
-        printTableFormControls('username[]', 'mng-del.php', 'searchusers');
+        printTableFormControls('username[]', $action, 'searchusers');
 ?>
                 </th>
             </tr>
             
             <tr>
@@ -233,49 +247,29 @@
         $links = setupLinks_str($pageNum, $maxPage, $orderBy, $orderType, $partial_query_string);
         printTableFoot($per_page_numrows, $numrows, $colspan, $drawNumberLinks, $links);
 ?>
 
     </table>
+    
+    <input type="hidden" name="csrf_token" value="<?= dalo_csrf_token() ?>">
+    
 </form>
 
 <?php
     } else {
         $failureMsg = "Nothing to display";
         include_once("include/management/actionMessages.php");
     }
     
     include('library/closedb.php');
-?>
-
-        </div><!-- #contentnorightbar -->
-
-        <div id="footer">
-
-<?php
-    $log = "visited page: ";
-    $logQuery = "performed query for ";
-    if (!empty($username)) {
-         $logQuery .= "username(s) starting with [$username] ";
-    } else {
-        $logQuery .= "all usernames ";
-    }
-    $logQuery .= "on page: ";
 
     include('include/config/logging.php');
-    include('page-footer.php');
-?>
-
-
-        </div><!-- #footer -->
-    </div>
-</div>
-
-<script>
-    var tooltipObj = new DHTMLgoodies_formTooltip();
-    tooltipObj.setTooltipPosition('right');
-    tooltipObj.setPageBgColor('#EEEEEE');
-    tooltipObj.setTooltipCornerSize(15);
-    tooltipObj.initFormFieldTooltip();
-</script>
+    
+    $inline_extra_js = "
+var tooltipObj = new DHTMLgoodies_formTooltip();
+tooltipObj.setTooltipPosition('right');
+tooltipObj.setPageBgColor('#EEEEEE');
+tooltipObj.setTooltipCornerSize(15);
+tooltipObj.initFormFieldTooltip()";
 
-</body>
-</html>
+    print_footer_and_html_epilogue($inline_extra_js);
+?>
diff --git a/notifications/common.php b/notifications/common.php
new file mode 100644
index 00000000..2d0617c9
--- /dev/null
+++ b/notifications/common.php
@@ -0,0 +1,92 @@
+<?php 
+/*
+ *********************************************************************************************************
+ * daloRADIUS - RADIUS Web Platform
+ * Copyright (C) 2007 - Liran Tal <liran@enginx.com> All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *********************************************************************************************************
+ *
+ * Authors:    Filippo Lauria <filippo.lauria@iit.cnr.it>
+ *
+ *********************************************************************************************************
+ */
+
+// prevent this file to be directly accessed
+if (strpos($_SERVER['PHP_SELF'], '/notifications/common.php') !== false) {
+    header("Location: ../index.php");
+    exit;
+}
+
+//include the dompdf class
+require_once("dompdf/dompdf_config.inc.php");
+
+//include the Pear Mail classes for sending out emails
+@require_once('Mail.php');
+@require_once('Mail/mime.php');
+
+/**
+ * createPDF()
+ * creates a PDF document for a given html file
+ * 
+ * @param         string        the html file to convert to pdf
+ * 
+ * @return        string        returns the pdf in binary/string stream
+ */
+function createPDF($html, $base_path) {
+    // this fix has been set in place according to this thread:
+    // https://stackoverflow.com/questions/37521775/dompdf-error-no-block-level-parent-found-not-good
+    $html = str_replace("\n", "", $html);
+    
+    // instansiate the pdf document
+    $dompdf = new DOMPDF();
+    $dompdf->set_base_path($base_path);
+    $dompdf->load_html($html);
+    $dompdf->render();
+    return $dompdf->output();
+}
+
+
+/**
+ * send_notification_via_email()
+ * creates an email message with the pdf and sends it
+ * 
+ * @param        string        pdf binary/string stream
+ * @param        array         smtp server information
+ * @param        string        destination email address
+ * @param        string        from email address of the sender identity
+ * @param        string        email subject
+ * @param        string        email body
+ * @param        string        pdf attachment name
+ * 
+ */
+function send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName) {
+    if (filter_var($sendTo, FILTER_VALIDATE_EMAIL) === false) {
+        return false;
+    }
+    
+    $headers = array(
+                       "From"     => $from, 
+                       "Subject"  => $subject,
+                       "Reply-To" => $from
+                    );
+
+    $mime = new Mail_mime();
+    $mime->setTXTBody($body); 
+    $mime->addAttachment($pdfDocument, "application/pdf", $attachmentName, false, 'base64');
+    $body = $mime->get();
+    $headers = $mime->headers($headers);
+    $mail =& Mail::factory("smtp", $smtpInfo);
+    $mail->send($sendTo, $headers, $body);
+
+    return true;
+}
+?>
diff --git a/notifications/dompdf/dompdf_config.inc.php b/notifications/dompdf/dompdf_config.inc.php
index 811a66e4..b038f973 100644
--- a/notifications/dompdf/dompdf_config.inc.php
+++ b/notifications/dompdf/dompdf_config.inc.php
@@ -329,11 +329,11 @@ require_once(DOMPDF_LIB_DIR . "/html5lib/Parser.php");
 /**
  * Load autoloader
  */
 if (DOMPDF_ENABLE_AUTOLOAD) {
   require_once(DOMPDF_INC_DIR . "/autoload.inc.php");
-  require_once(DOMPDF_LIB_DIR . "/php-font-lib/classes/Font.php");
+  require_once(DOMPDF_LIB_DIR . "/php-font-lib/Font.php");
 }
 
 /**
  * Ensure that PHP is working with text internally using UTF8 character encoding.
  */
diff --git a/notifications/dompdf/lib/php-font-lib/AdobeFontMetrics.php b/notifications/dompdf/lib/php-font-lib/AdobeFontMetrics.php
new file mode 100644
index 00000000..e75385f5
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/AdobeFontMetrics.php
@@ -0,0 +1,217 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib;
+
+use FontLib\Table\Type\name;
+use FontLib\TrueType\File;
+
+/**
+ * Adobe Font Metrics file creation utility class.
+ *
+ * @package php-font-lib
+ */
+class AdobeFontMetrics {
+  private $f;
+
+  /**
+   * @var File
+   */
+  private $font;
+
+  function __construct(File $font) {
+    $this->font = $font;
+  }
+
+  function write($file, $encoding = null) {
+    $map_data = array();
+
+    if ($encoding) {
+      $encoding = preg_replace("/[^a-z0-9-_]/", "", $encoding);
+      $map_file = dirname(__FILE__) . "/../maps/$encoding.map";
+      if (!file_exists($map_file)) {
+        throw new \Exception("Unknown encoding ($encoding)");
+      }
+
+      $map      = new EncodingMap($map_file);
+      $map_data = $map->parse();
+    }
+
+    $this->f = fopen($file, "w+");
+
+    $font = $this->font;
+
+    $this->startSection("FontMetrics", 4.1);
+    $this->addPair("Notice", "Converted by PHP-font-lib");
+    $this->addPair("Comment", "https://github.com/PhenX/php-font-lib");
+
+    $encoding_scheme = ($encoding ? $encoding : "FontSpecific");
+    $this->addPair("EncodingScheme", $encoding_scheme);
+
+    $records = $font->getData("name", "records");
+    foreach ($records as $id => $record) {
+      if (!isset(name::$nameIdCodes[$id]) || preg_match("/[\r\n]/", $record->string)) {
+        continue;
+      }
+
+      $this->addPair(name::$nameIdCodes[$id], $record->string);
+    }
+
+    $os2 = $font->getData("OS/2");
+    $this->addPair("Weight", ($os2["usWeightClass"] > 400 ? "Bold" : "Medium"));
+
+    $post = $font->getData("post");
+    $this->addPair("ItalicAngle", $post["italicAngle"]);
+    $this->addPair("IsFixedPitch", ($post["isFixedPitch"] ? "true" : "false"));
+    $this->addPair("UnderlineThickness", $font->normalizeFUnit($post["underlineThickness"]));
+    $this->addPair("UnderlinePosition", $font->normalizeFUnit($post["underlinePosition"]));
+
+    $hhea = $font->getData("hhea");
+
+    if (isset($hhea["ascent"])) {
+      $this->addPair("FontHeightOffset", $font->normalizeFUnit($hhea["lineGap"]));
+      $this->addPair("Ascender", $font->normalizeFUnit($hhea["ascent"]));
+      $this->addPair("Descender", $font->normalizeFUnit($hhea["descent"]));
+    }
+    else {
+      $this->addPair("FontHeightOffset", $font->normalizeFUnit($os2["typoLineGap"]));
+      $this->addPair("Ascender", $font->normalizeFUnit($os2["typoAscender"]));
+      $this->addPair("Descender", -abs($font->normalizeFUnit($os2["typoDescender"])));
+    }
+
+    $head = $font->getData("head");
+    $this->addArray("FontBBox", array(
+      $font->normalizeFUnit($head["xMin"]),
+      $font->normalizeFUnit($head["yMin"]),
+      $font->normalizeFUnit($head["xMax"]),
+      $font->normalizeFUnit($head["yMax"]),
+    ));
+
+    $glyphIndexArray = $font->getUnicodeCharMap();
+
+    if ($glyphIndexArray) {
+      $hmtx  = $font->getData("hmtx");
+      $names = $font->getData("post", "names");
+
+      $this->startSection("CharMetrics", count($hmtx));
+
+      if ($encoding) {
+        foreach ($map_data as $code => $value) {
+          list($c, $name) = $value;
+
+          if (!isset($glyphIndexArray[$c])) {
+            continue;
+          }
+
+          $g = $glyphIndexArray[$c];
+
+          if (!isset($hmtx[$g])) {
+            $hmtx[$g] = $hmtx[0];
+          }
+
+          $this->addMetric(array(
+            "C"  => ($code > 255 ? -1 : $code),
+            "WX" => $font->normalizeFUnit($hmtx[$g][0]),
+            "N"  => $name,
+          ));
+        }
+      }
+      else {
+        foreach ($glyphIndexArray as $c => $g) {
+          if (!isset($hmtx[$g])) {
+            $hmtx[$g] = $hmtx[0];
+          }
+
+          $this->addMetric(array(
+            "U"  => $c,
+            "WX" => $font->normalizeFUnit($hmtx[$g][0]),
+            "N"  => (isset($names[$g]) ? $names[$g] : sprintf("uni%04x", $c)),
+            "G"  => $g,
+          ));
+        }
+      }
+
+      $this->endSection("CharMetrics");
+
+      $kern = $font->getData("kern", "subtable");
+      $tree = is_array($kern) ? $kern["tree"] : null;
+
+      if (!$encoding && is_array($tree)) {
+        $this->startSection("KernData");
+        $this->startSection("KernPairs", count($tree, COUNT_RECURSIVE) - count($tree));
+
+        foreach ($tree as $left => $values) {
+          if (!is_array($values)) {
+            continue;
+          }
+          if (!isset($glyphIndexArray[$left])) {
+            continue;
+          }
+
+          $left_gid = $glyphIndexArray[$left];
+
+          if (!isset($names[$left_gid])) {
+            continue;
+          }
+
+          $left_name = $names[$left_gid];
+
+          $this->addLine("");
+
+          foreach ($values as $right => $value) {
+            if (!isset($glyphIndexArray[$right])) {
+              continue;
+            }
+
+            $right_gid = $glyphIndexArray[$right];
+
+            if (!isset($names[$right_gid])) {
+              continue;
+            }
+
+            $right_name = $names[$right_gid];
+            $this->addPair("KPX", "$left_name $right_name $value");
+          }
+        }
+
+        $this->endSection("KernPairs");
+        $this->endSection("KernData");
+      }
+    }
+
+    $this->endSection("FontMetrics");
+  }
+
+  function addLine($line) {
+    fwrite($this->f, "$line\n");
+  }
+
+  function addPair($key, $value) {
+    $this->addLine("$key $value");
+  }
+
+  function addArray($key, $array) {
+    $this->addLine("$key " . implode(" ", $array));
+  }
+
+  function addMetric($data) {
+    $array = array();
+    foreach ($data as $key => $value) {
+      $array[] = "$key $value";
+    }
+    $this->addLine(implode(" ; ", $array));
+  }
+
+  function startSection($name, $value = "") {
+    $this->addLine("Start$name $value");
+  }
+
+  function endSection($name) {
+    $this->addLine("End$name");
+  }
+}
diff --git a/notifications/dompdf/lib/php-font-lib/Autoloader.php b/notifications/dompdf/lib/php-font-lib/Autoloader.php
new file mode 100644
index 00000000..cd305453
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Autoloader.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib;
+
+/**
+ * Autoloads FontLib classes
+ *
+ * @package php-font-lib
+ */
+class Autoloader {
+  const PREFIX = 'FontLib';
+
+  /**
+   * Register the autoloader
+   */
+  public static function register() {
+    spl_autoload_register(array(new self, 'autoload'));
+  }
+
+  /**
+   * Autoloader
+   *
+   * @param string
+   */
+  public static function autoload($class) {
+    $prefixLength = strlen(self::PREFIX);
+    if (0 === strncmp(self::PREFIX, $class, $prefixLength)) {
+      $file = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, $prefixLength));
+      $file = realpath(__DIR__ . (empty($file) ? '' : DIRECTORY_SEPARATOR) . $file . '.php');
+      if (file_exists($file)) {
+        require_once $file;
+      }
+    }
+  }
+}
+
+Autoloader::register();
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/BinaryStream.php b/notifications/dompdf/lib/php-font-lib/BinaryStream.php
new file mode 100644
index 00000000..c7eb52f8
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/BinaryStream.php
@@ -0,0 +1,449 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib;
+
+/**
+ * Generic font file binary stream.
+ *
+ * @package php-font-lib
+ */
+class BinaryStream {
+  /**
+   * @var resource The file pointer
+   */
+  protected $f;
+
+  const uint8        = 1;
+  const  int8        = 2;
+  const uint16       = 3;
+  const  int16       = 4;
+  const uint32       = 5;
+  const  int32       = 6;
+  const shortFrac    = 7;
+  const Fixed        = 8;
+  const  FWord       = 9;
+  const uFWord       = 10;
+  const F2Dot14      = 11;
+  const longDateTime = 12;
+  const char         = 13;
+
+  const modeRead      = "rb";
+  const modeWrite     = "wb";
+  const modeReadWrite = "rb+";
+
+  static function backtrace() {
+    var_dump(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
+  }
+
+  /**
+   * Open a font file in read mode
+   *
+   * @param string $filename The file name of the font to open
+   *
+   * @return bool
+   */
+  public function load($filename) {
+    return $this->open($filename, self::modeRead);
+  }
+
+  /**
+   * Open a font file in a chosen mode
+   *
+   * @param string $filename The file name of the font to open
+   * @param string $mode     The opening mode
+   *
+   * @throws \Exception
+   * @return bool
+   */
+  public function open($filename, $mode = self::modeRead) {
+    if (!in_array($mode, array(self::modeRead, self::modeWrite, self::modeReadWrite))) {
+      throw new \Exception("Unknown file open mode");
+    }
+
+    $this->f = fopen($filename, $mode);
+
+    return $this->f != false;
+  }
+
+  /**
+   * Close the internal file pointer
+   */
+  public function close() {
+    return fclose($this->f) != false;
+  }
+
+  /**
+   * Change the internal file pointer
+   *
+   * @param resource $fp
+   *
+   * @throws \Exception
+   */
+  public function setFile($fp) {
+    if (!is_resource($fp)) {
+      throw new \Exception('$fp is not a valid resource');
+    }
+
+    $this->f = $fp;
+  }
+
+  /**
+   * Create a temporary file in write mode
+   *
+   * @param bool $allow_memory Allow in-memory files
+   *
+   * @return resource the temporary file pointer resource
+   */
+  public static function getTempFile($allow_memory = true) {
+    $f = null;
+
+    if ($allow_memory) {
+      $f = fopen("php://temp", "rb+");
+    }
+    else {
+      $f = fopen(tempnam(sys_get_temp_dir(), "fnt"), "rb+");
+    }
+
+    return $f;
+  }
+
+  /**
+   * Move the internal file pinter to $offset bytes
+   *
+   * @param int $offset
+   *
+   * @return bool True if the $offset position exists in the file
+   */
+  public function seek($offset) {
+    return fseek($this->f, $offset, SEEK_SET) == 0;
+  }
+
+  /**
+   * Gives the current position in the file
+   *
+   * @return int The current position
+   */
+  public function pos() {
+    return ftell($this->f);
+  }
+
+  public function skip($n) {
+    fseek($this->f, $n, SEEK_CUR);
+  }
+
+  /**
+   * @param int $n The number of bytes to read
+   *
+   * @return string
+   */
+  public function read($n) {
+    if ($n < 1) {
+      return "";
+    }
+
+    return (string) fread($this->f, $n);
+  }
+
+  public function write($data, $length = null) {
+    if ($data === null || $data === "" || $data === false) {
+      return 0;
+    }
+
+    return fwrite($this->f, $data, $length);
+  }
+
+  public function readUInt8() {
+    return ord($this->read(1));
+  }
+
+  public function readUInt8Many($count) {
+    return array_values(unpack("C*", $this->read($count)));
+  }
+
+  public function writeUInt8($data) {
+    return $this->write(chr($data), 1);
+  }
+
+  public function readInt8() {
+    $v = $this->readUInt8();
+
+    if ($v >= 0x80) {
+      $v -= 0x100;
+    }
+
+    return $v;
+  }
+
+  public function readInt8Many($count) {
+    return array_values(unpack("c*", $this->read($count)));
+  }
+
+  public function writeInt8($data) {
+    if ($data < 0) {
+      $data += 0x100;
+    }
+
+    return $this->writeUInt8($data);
+  }
+
+  public function readUInt16() {
+    $a = unpack("nn", $this->read(2));
+
+    return $a["n"];
+  }
+
+  public function readUInt16Many($count) {
+    return array_values(unpack("n*", $this->read($count * 2)));
+  }
+
+  public function readUFWord() {
+    return $this->readUInt16();
+  }
+
+  public function writeUInt16($data) {
+    return $this->write(pack("n", $data), 2);
+  }
+
+  public function writeUFWord($data) {
+    return $this->writeUInt16($data);
+  }
+
+  public function readInt16() {
+    $a = unpack("nn", $this->read(2));
+    $v = $a["n"];
+
+    if ($v >= 0x8000) {
+      $v -= 0x10000;
+    }
+
+    return $v;
+  }
+
+  public function readInt16Many($count) {
+    $vals = array_values(unpack("n*", $this->read($count * 2)));
+    foreach ($vals as &$v) {
+      if ($v >= 0x8000) {
+        $v -= 0x10000;
+      }
+    }
+
+    return $vals;
+  }
+
+  public function readFWord() {
+    return $this->readInt16();
+  }
+
+  public function writeInt16($data) {
+    if ($data < 0) {
+      $data += 0x10000;
+    }
+
+    return $this->writeUInt16($data);
+  }
+
+  public function writeFWord($data) {
+    return $this->writeInt16($data);
+  }
+
+  public function readUInt32() {
+    $a = unpack("NN", $this->read(4));
+
+    return $a["N"];
+  }
+
+  public function writeUInt32($data) {
+    return $this->write(pack("N", $data), 4);
+  }
+
+  public function readFixed() {
+    $d  = $this->readInt16();
+    $d2 = $this->readUInt16();
+
+    return round($d + $d2 / 0x10000, 4);
+  }
+
+  public function writeFixed($data) {
+    $left  = floor($data);
+    $right = ($data - $left) * 0x10000;
+
+    return $this->writeInt16($left) + $this->writeUInt16($right);
+  }
+
+  public function readLongDateTime() {
+    $this->readUInt32(); // ignored
+    $date = $this->readUInt32() - 2082844800;
+    
+    # PHP_INT_MIN isn't defined in PHP < 7.0
+    $php_int_min = defined("PHP_INT_MIN") ? PHP_INT_MIN : ~PHP_INT_MAX;
+
+    if (is_string($date) || $date > PHP_INT_MAX || $date < $php_int_min) {
+      $date = 0;
+    }
+
+    return date("Y-m-d H:i:s", $date);
+  }
+
+  public function writeLongDateTime($data) {
+    $date = strtotime($data);
+    $date += 2082844800;
+
+    return $this->writeUInt32(0) + $this->writeUInt32($date);
+  }
+
+  public function unpack($def) {
+    $d = array();
+    foreach ($def as $name => $type) {
+      $d[$name] = $this->r($type);
+    }
+
+    return $d;
+  }
+
+  public function pack($def, $data) {
+    $bytes = 0;
+    foreach ($def as $name => $type) {
+      $bytes += $this->w($type, $data[$name]);
+    }
+
+    return $bytes;
+  }
+
+  /**
+   * Read a data of type $type in the file from the current position
+   *
+   * @param mixed $type The data type to read
+   *
+   * @return mixed The data that was read
+   */
+  public function r($type) {
+    switch ($type) {
+      case self::uint8:
+        return $this->readUInt8();
+      case self::int8:
+        return $this->readInt8();
+      case self::uint16:
+        return $this->readUInt16();
+      case self::int16:
+        return $this->readInt16();
+      case self::uint32:
+        return $this->readUInt32();
+      case self::int32:
+        return $this->readUInt32();
+      case self::shortFrac:
+        return $this->readFixed();
+      case self::Fixed:
+        return $this->readFixed();
+      case self::FWord:
+        return $this->readInt16();
+      case self::uFWord:
+        return $this->readUInt16();
+      case self::F2Dot14:
+        return $this->readInt16();
+      case self::longDateTime:
+        return $this->readLongDateTime();
+      case self::char:
+        return $this->read(1);
+      default:
+        if (is_array($type)) {
+          if ($type[0] == self::char) {
+            return $this->read($type[1]);
+          }
+          if ($type[0] == self::uint16) {
+            return $this->readUInt16Many($type[1]);
+          }
+          if ($type[0] == self::int16) {
+            return $this->readInt16Many($type[1]);
+          }
+          if ($type[0] == self::uint8) {
+            return $this->readUInt8Many($type[1]);
+          }
+          if ($type[0] == self::int8) {
+            return $this->readInt8Many($type[1]);
+          }
+
+          $ret = array();
+          for ($i = 0; $i < $type[1]; $i++) {
+            $ret[] = $this->r($type[0]);
+          }
+
+          return $ret;
+        }
+
+        return null;
+    }
+  }
+
+  /**
+   * Write $data of type $type in the file from the current position
+   *
+   * @param mixed $type The data type to write
+   * @param mixed $data The data to write
+   *
+   * @return int The number of bytes read
+   */
+  public function w($type, $data) {
+    switch ($type) {
+      case self::uint8:
+        return $this->writeUInt8($data);
+      case self::int8:
+        return $this->writeInt8($data);
+      case self::uint16:
+        return $this->writeUInt16($data);
+      case self::int16:
+        return $this->writeInt16($data);
+      case self::uint32:
+        return $this->writeUInt32($data);
+      case self::int32:
+        return $this->writeUInt32($data);
+      case self::shortFrac:
+        return $this->writeFixed($data);
+      case self::Fixed:
+        return $this->writeFixed($data);
+      case self::FWord:
+        return $this->writeInt16($data);
+      case self::uFWord:
+        return $this->writeUInt16($data);
+      case self::F2Dot14:
+        return $this->writeInt16($data);
+      case self::longDateTime:
+        return $this->writeLongDateTime($data);
+      case self::char:
+        return $this->write($data, 1);
+      default:
+        if (is_array($type)) {
+          if ($type[0] == self::char) {
+            return $this->write($data, $type[1]);
+          }
+
+          $ret = 0;
+          for ($i = 0; $i < $type[1]; $i++) {
+            if (isset($data[$i])) {
+              $ret += $this->w($type[0], $data[$i]);
+            }
+          }
+
+          return $ret;
+        }
+
+        return null;
+    }
+  }
+
+  /**
+   * Converts a Uint32 value to string
+   *
+   * @param int $uint32
+   *
+   * @return string The string
+   */
+  public function convertUInt32ToStr($uint32) {
+    return chr(($uint32 >> 24) & 0xFF) . chr(($uint32 >> 16) & 0xFF) . chr(($uint32 >> 8) & 0xFF) . chr($uint32 & 0xFF);
+  }
+}
diff --git a/notifications/dompdf/lib/php-font-lib/EOT/File.php b/notifications/dompdf/lib/php-font-lib/EOT/File.php
new file mode 100644
index 00000000..f51d876c
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/EOT/File.php
@@ -0,0 +1,159 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\EOT;
+
+/**
+ * EOT font file.
+ *
+ * @package php-font-lib
+ */
+class File extends \FontLib\TrueType\File {
+  const TTEMBED_SUBSET                   = 0x00000001;
+  const TTEMBED_TTCOMPRESSED             = 0x00000004;
+  const TTEMBED_FAILIFVARIATIONSIMULATED = 0x00000010;
+  const TTMBED_EMBEDEUDC                 = 0x00000020;
+  const TTEMBED_VALIDATIONTESTS          = 0x00000040; // Deprecated
+  const TTEMBED_WEBOBJECT      = 0x00000080;
+  const TTEMBED_XORENCRYPTDATA = 0x10000000;
+
+  /**
+   * @var Header
+   */
+  public $header;
+
+  function parseHeader() {
+    if (!empty($this->header)) {
+      return;
+    }
+
+    $this->header = new Header($this);
+    $this->header->parse();
+  }
+
+  function parse() {
+    $this->parseHeader();
+
+    $flags = $this->header->data["Flags"];
+
+    if ($flags & self::TTEMBED_TTCOMPRESSED) {
+      $mtx_version    = $this->readUInt8();
+      $mtx_copy_limit = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8();
+      $mtx_offset_1   = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8();
+      $mtx_offset_2   = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8();
+      /*
+      var_dump("$mtx_version $mtx_copy_limit $mtx_offset_1 $mtx_offset_2");
+
+      $pos = $this->pos();
+      $size = $mtx_offset_1 - $pos;
+      var_dump("pos: $pos");
+      var_dump("size: $size");*/
+    }
+
+    if ($flags & self::TTEMBED_XORENCRYPTDATA) {
+      // Process XOR
+    }
+    // TODO Read font data ...
+  }
+
+  /**
+   * Little endian version of the read method
+   *
+   * @param int $n The number of bytes to read
+   *
+   * @return string
+   */
+  public function read($n) {
+    if ($n < 1) {
+      return "";
+    }
+
+    $string = (string) fread($this->f, $n);
+    $chunks = mb_str_split($string, 2, '8bit');
+    $chunks = array_map("strrev", $chunks);
+    return implode("", $chunks);
+  }
+
+  public function readUInt32() {
+    $uint32 = parent::readUInt32();
+
+    return $uint32 >> 16 & 0x0000FFFF | $uint32 << 16 & 0xFFFF0000;
+  }
+
+  /**
+   * Get font copyright
+   *
+   * @return string|null
+   */
+  function getFontCopyright() {
+    return null;
+  }
+
+  /**
+   * Get font name
+   *
+   * @return string|null
+   */
+  function getFontName() {
+    return $this->header->data["FamilyName"];
+  }
+
+  /**
+   * Get font subfamily
+   *
+   * @return string|null
+   */
+  function getFontSubfamily() {
+    return $this->header->data["StyleName"];
+  }
+
+  /**
+   * Get font subfamily ID
+   *
+   * @return string|null
+   */
+  function getFontSubfamilyID() {
+    return $this->header->data["StyleName"];
+  }
+
+  /**
+   * Get font full name
+   *
+   * @return string|null
+   */
+  function getFontFullName() {
+    return $this->header->data["FullName"];
+  }
+
+  /**
+   * Get font version
+   *
+   * @return string|null
+   */
+  function getFontVersion() {
+    return $this->header->data["VersionName"];
+  }
+
+  /**
+   * Get font weight
+   *
+   * @return string|null
+   */
+  function getFontWeight() {
+    return $this->header->data["Weight"];
+  }
+
+  /**
+   * Get font Postscript name
+   *
+   * @return string|null
+   */
+  function getFontPostscriptName() {
+    return null;
+  }
+}
diff --git a/notifications/dompdf/lib/php-font-lib/EOT/Header.php b/notifications/dompdf/lib/php-font-lib/EOT/Header.php
new file mode 100644
index 00000000..960e36ad
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/EOT/Header.php
@@ -0,0 +1,113 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\EOT;
+
+use Exception;
+use FontLib\Font;
+
+/**
+ * TrueType font file header.
+ *
+ * @package php-font-lib
+ *
+ * @property File $font
+ */
+class Header extends \FontLib\Header {
+  protected $def = array(
+    "format"        => self::uint32,
+    "numTables"     => self::uint16,
+    "searchRange"   => self::uint16,
+    "entrySelector" => self::uint16,
+    "rangeShift"    => self::uint16,
+  );
+
+  public function parse() {
+    $font = $this->font;
+
+    $this->data = $font->unpack(array(
+      "EOTSize"            => self::uint32,
+      "FontDataSize"       => self::uint32,
+      "Version"            => self::uint32,
+      "Flags"              => self::uint32,
+      "FontPANOSE"         => array(self::uint8, 10),
+      "Charset"            => self::uint8,
+      "Italic"             => self::uint8,
+      "Weight"             => self::uint32,
+      "fsType"             => self::uint16,
+      "MagicNumber"        => self::uint16,
+      "UnicodeRange1"      => self::uint32,
+      "UnicodeRange2"      => self::uint32,
+      "UnicodeRange3"      => self::uint32,
+      "UnicodeRange4"      => self::uint32,
+      "CodePageRange1"     => self::uint32,
+      "CodePageRange2"     => self::uint32,
+      "CheckSumAdjustment" => self::uint32,
+      "Reserved1"          => self::uint32,
+      "Reserved2"          => self::uint32,
+      "Reserved3"          => self::uint32,
+      "Reserved4"          => self::uint32,
+    ));
+
+    $this->data["Padding1"] = $font->readUInt16();
+    $this->readString("FamilyName");
+
+    $this->data["Padding2"] = $font->readUInt16();
+    $this->readString("StyleName");
+
+    $this->data["Padding3"] = $font->readUInt16();
+    $this->readString("VersionName");
+
+    $this->data["Padding4"] = $font->readUInt16();
+    $this->readString("FullName");
+
+    switch ($this->data["Version"]) {
+      default:
+        throw new Exception("Unknown EOT version " . $this->data["Version"]);
+
+      case 0x00010000:
+        // Nothing to do more
+        break;
+
+      case 0x00020001:
+        $this->data["Padding5"] = $font->readUInt16();
+        $this->readString("RootString");
+        break;
+
+      case 0x00020002:
+        $this->data["Padding5"] = $font->readUInt16();
+        $this->readString("RootString");
+
+        $this->data["RootStringCheckSum"] = $font->readUInt32();
+        $this->data["EUDCCodePage"]       = $font->readUInt32();
+
+        $this->data["Padding6"] = $font->readUInt16();
+        $this->readString("Signature");
+
+        $this->data["EUDCFlags"]    = $font->readUInt32();
+        $this->data["EUDCFontSize"] = $font->readUInt32();
+        break;
+    }
+
+    if (!empty($this->data["RootString"])) {
+      $this->data["RootString"] = explode("\0", $this->data["RootString"]);
+    }
+  }
+
+  private function readString($name) {
+    $font = $this->font;
+    $size = $font->readUInt16();
+
+    $this->data["{$name}Size"] = $size;
+    $this->data[$name]         = Font::UTF16ToUTF8($font->read($size));
+  }
+
+  public function encode() {
+    //return $this->font->pack($this->def, $this->data);
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/EncodingMap.php b/notifications/dompdf/lib/php-font-lib/EncodingMap.php
new file mode 100644
index 00000000..2acdebc5
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/EncodingMap.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib;
+
+/**
+ * Encoding map used to map a code point to a Unicode char.
+ *
+ * @package php-font-lib
+ */
+class EncodingMap {
+  private $f;
+
+  function __construct($file) {
+    $this->f = fopen($file, "r");
+  }
+
+  function parse() {
+    $map = array();
+
+    while ($line = fgets($this->f)) {
+      if (preg_match('/^[\!\=]([0-9A-F]{2,})\s+U\+([0-9A-F]{2})([0-9A-F]{2})\s+([^\s]+)/', $line, $matches)) {
+        $unicode = (hexdec($matches[2]) << 8) + hexdec($matches[3]);
+        $map[hexdec($matches[1])] = array($unicode, $matches[4]);
+      }
+    }
+
+    ksort($map);
+
+    return $map;
+  }
+}
diff --git a/notifications/dompdf/lib/php-font-lib/Exception/FontNotFoundException.php b/notifications/dompdf/lib/php-font-lib/Exception/FontNotFoundException.php
new file mode 100644
index 00000000..d97f2523
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Exception/FontNotFoundException.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace FontLib\Exception;
+
+class FontNotFoundException extends \Exception
+{
+    public function __construct($fontPath)
+    {
+        $this->message = 'Font not found in: ' . $fontPath;
+    }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/Font.php b/notifications/dompdf/lib/php-font-lib/Font.php
new file mode 100644
index 00000000..e13a6533
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Font.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib;
+
+use FontLib\Exception\FontNotFoundException;
+
+/**
+ * Generic font file.
+ *
+ * @package php-font-lib
+ */
+class Font {
+  static $debug = false;
+
+  /**
+   * @param string $file The font file
+   *
+   * @return TrueType\File|null $file
+   */
+  public static function load($file) {
+      if(!file_exists($file)){
+          throw new FontNotFoundException($file);
+      }
+
+    $header = file_get_contents($file, false, null, 0, 4);
+    $class  = null;
+
+    switch ($header) {
+      case "\x00\x01\x00\x00":
+      case "true":
+      case "typ1":
+        $class = "TrueType\\File";
+        break;
+
+      case "OTTO":
+        $class = "OpenType\\File";
+        break;
+
+      case "wOFF":
+        $class = "WOFF\\File";
+        break;
+
+      case "ttcf":
+        $class = "TrueType\\Collection";
+        break;
+
+      // Unknown type or EOT
+      default:
+        $magicNumber = file_get_contents($file, false, null, 34, 2);
+
+        if ($magicNumber === "LP") {
+          $class = "EOT\\File";
+        }
+    }
+
+    if ($class) {
+      $class = "FontLib\\$class";
+
+      /** @var TrueType\File $obj */
+      $obj = new $class;
+      $obj->load($file);
+
+      return $obj;
+    }
+
+    return null;
+  }
+
+  static function d($str) {
+    if (!self::$debug) {
+      return;
+    }
+    echo "$str\n";
+  }
+
+  static function UTF16ToUTF8($str) {
+    return mb_convert_encoding($str, "utf-8", "utf-16");
+  }
+
+  static function UTF8ToUTF16($str) {
+    return mb_convert_encoding($str, "utf-16", "utf-8");
+  }
+}
diff --git a/notifications/dompdf/lib/php-font-lib/Glyph/Outline.php b/notifications/dompdf/lib/php-font-lib/Glyph/Outline.php
new file mode 100644
index 00000000..639ff60a
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Glyph/Outline.php
@@ -0,0 +1,109 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
+ */
+namespace FontLib\Glyph;
+
+use FontLib\Table\Type\glyf;
+use FontLib\TrueType\File;
+use FontLib\BinaryStream;
+
+/**
+ * `glyf` font table.
+ *
+ * @package php-font-lib
+ */
+class Outline extends BinaryStream {
+  /**
+   * @var \FontLib\Table\Type\glyf
+   */
+  protected $table;
+
+  protected $offset;
+  protected $size;
+
+  // Data
+  public $numberOfContours;
+  public $xMin;
+  public $yMin;
+  public $xMax;
+  public $yMax;
+
+  /**
+   * @var string|null
+   */
+  public $raw;
+
+  /**
+   * @param glyf $table
+   * @param                 $offset
+   * @param                 $size
+   *
+   * @return Outline
+   */
+  static function init(glyf $table, $offset, $size, BinaryStream $font) {
+    $font->seek($offset);
+
+    if ($font->readInt16() > -1) {
+      /** @var OutlineSimple $glyph */
+      $glyph = new OutlineSimple($table, $offset, $size);
+    }
+    else {
+      /** @var OutlineComposite $glyph */
+      $glyph = new OutlineComposite($table, $offset, $size);
+    }
+
+    $glyph->parse($font);
+
+    return $glyph;
+  }
+
+  /**
+   * @return File
+   */
+  function getFont() {
+    return $this->table->getFont();
+  }
+
+  function __construct(glyf $table, $offset = null, $size = null) {
+    $this->table  = $table;
+    $this->offset = $offset;
+    $this->size   = $size;
+  }
+
+  function parse(BinaryStream $font) {
+    $font->seek($this->offset);
+
+      $this->raw = $font->read($this->size);
+  }
+
+  function parseData() {
+    $font = $this->getFont();
+    $font->seek($this->offset);
+
+    $this->numberOfContours = $font->readInt16();
+    $this->xMin             = $font->readFWord();
+    $this->yMin             = $font->readFWord();
+    $this->xMax             = $font->readFWord();
+    $this->yMax             = $font->readFWord();
+  }
+
+  function encode() {
+    $font = $this->getFont();
+
+    return $font->write($this->raw, mb_strlen((string) $this->raw, '8bit'));
+  }
+
+  function getSVGContours() {
+    // Inherit
+  }
+
+  function getGlyphIDs() {
+    return array();
+  }
+}
+
diff --git a/notifications/dompdf/lib/php-font-lib/Glyph/OutlineComponent.php b/notifications/dompdf/lib/php-font-lib/Glyph/OutlineComponent.php
new file mode 100644
index 00000000..9cafaf4d
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Glyph/OutlineComponent.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
+ */
+
+namespace FontLib\Glyph;
+/**
+ * Glyph outline component
+ *
+ * @package php-font-lib
+ */
+class OutlineComponent {
+  public $flags;
+  public $glyphIndex;
+  public $a, $b, $c, $d, $e, $f;
+  public $point_compound;
+  public $point_component;
+  public $instructions;
+
+  function getMatrix() {
+    return array(
+      $this->a, $this->b,
+      $this->c, $this->d,
+      $this->e, $this->f,
+    );
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/Glyph/OutlineComposite.php b/notifications/dompdf/lib/php-font-lib/Glyph/OutlineComposite.php
new file mode 100644
index 00000000..8ab0d2c6
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Glyph/OutlineComposite.php
@@ -0,0 +1,242 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
+ */
+
+namespace FontLib\Glyph;
+
+/**
+ * Composite glyph outline
+ *
+ * @package php-font-lib
+ */
+class OutlineComposite extends Outline {
+  const ARG_1_AND_2_ARE_WORDS    = 0x0001;
+  const ARGS_ARE_XY_VALUES       = 0x0002;
+  const ROUND_XY_TO_GRID         = 0x0004;
+  const WE_HAVE_A_SCALE          = 0x0008;
+  const MORE_COMPONENTS          = 0x0020;
+  const WE_HAVE_AN_X_AND_Y_SCALE = 0x0040;
+  const WE_HAVE_A_TWO_BY_TWO     = 0x0080;
+  const WE_HAVE_INSTRUCTIONS     = 0x0100;
+  const USE_MY_METRICS           = 0x0200;
+  const OVERLAP_COMPOUND         = 0x0400;
+
+  /**
+   * @var OutlineComponent[]
+   */
+  public $components = array();
+
+  function getGlyphIDs() {
+    if (empty($this->components)) {
+      $this->parseData();
+    }
+
+    $glyphIDs = array();
+    foreach ($this->components as $_component) {
+      $glyphIDs[] = $_component->glyphIndex;
+
+      $_glyph   = $this->table->data[$_component->glyphIndex];
+
+      if ($_glyph !== $this) {
+        $glyphIDs = array_merge($glyphIDs, $_glyph->getGlyphIDs());
+      }
+    }
+
+    return $glyphIDs;
+  }
+
+  /*function parse() {
+    //$this->parseData();
+  }*/
+
+  function parseData() {
+    parent::parseData();
+
+    $font = $this->getFont();
+
+    do {
+      $flags      = $font->readUInt16();
+      $glyphIndex = $font->readUInt16();
+
+      $a = 1.0;
+      $b = 0.0;
+      $c = 0.0;
+      $d = 1.0;
+      $e = 0.0;
+      $f = 0.0;
+
+      $point_compound  = null;
+      $point_component = null;
+
+      $instructions = null;
+
+      if ($flags & self::ARG_1_AND_2_ARE_WORDS) {
+        if ($flags & self::ARGS_ARE_XY_VALUES) {
+          $e = $font->readInt16();
+          $f = $font->readInt16();
+        }
+        else {
+          $point_compound  = $font->readUInt16();
+          $point_component = $font->readUInt16();
+        }
+      }
+      else {
+        if ($flags & self::ARGS_ARE_XY_VALUES) {
+          $e = $font->readInt8();
+          $f = $font->readInt8();
+        }
+        else {
+          $point_compound  = $font->readUInt8();
+          $point_component = $font->readUInt8();
+        }
+      }
+
+      if ($flags & self::WE_HAVE_A_SCALE) {
+        $a = $d = $font->readInt16();
+      }
+      elseif ($flags & self::WE_HAVE_AN_X_AND_Y_SCALE) {
+        $a = $font->readInt16();
+        $d = $font->readInt16();
+      }
+      elseif ($flags & self::WE_HAVE_A_TWO_BY_TWO) {
+        $a = $font->readInt16();
+        $b = $font->readInt16();
+        $c = $font->readInt16();
+        $d = $font->readInt16();
+      }
+
+      //if ($flags & self::WE_HAVE_INSTRUCTIONS) {
+      //
+      //}
+
+      $component                  = new OutlineComponent();
+      $component->flags           = $flags;
+      $component->glyphIndex      = $glyphIndex;
+      $component->a               = $a;
+      $component->b               = $b;
+      $component->c               = $c;
+      $component->d               = $d;
+      $component->e               = $e;
+      $component->f               = $f;
+      $component->point_compound  = $point_compound;
+      $component->point_component = $point_component;
+      $component->instructions    = $instructions;
+
+      $this->components[] = $component;
+    } while ($flags & self::MORE_COMPONENTS);
+  }
+
+  function encode() {
+    $font = $this->getFont();
+
+    $gids = $font->getSubset();
+
+    $size = $font->writeInt16(-1);
+    $size += $font->writeFWord($this->xMin);
+    $size += $font->writeFWord($this->yMin);
+    $size += $font->writeFWord($this->xMax);
+    $size += $font->writeFWord($this->yMax);
+
+    foreach ($this->components as $_i => $_component) {
+      $flags = 0;
+      if ($_component->point_component === null && $_component->point_compound === null) {
+        $flags |= self::ARGS_ARE_XY_VALUES;
+
+        if (abs($_component->e) > 0x7F || abs($_component->f) > 0x7F) {
+          $flags |= self::ARG_1_AND_2_ARE_WORDS;
+        }
+      }
+      elseif ($_component->point_component > 0xFF || $_component->point_compound > 0xFF) {
+        $flags |= self::ARG_1_AND_2_ARE_WORDS;
+      }
+
+      if ($_component->b == 0 && $_component->c == 0) {
+        if ($_component->a == $_component->d) {
+          if ($_component->a != 1.0) {
+            $flags |= self::WE_HAVE_A_SCALE;
+          }
+        }
+        else {
+          $flags |= self::WE_HAVE_AN_X_AND_Y_SCALE;
+        }
+      }
+      else {
+        $flags |= self::WE_HAVE_A_TWO_BY_TWO;
+      }
+
+      if ($_i < count($this->components) - 1) {
+        $flags |= self::MORE_COMPONENTS;
+      }
+
+      $size += $font->writeUInt16($flags);
+
+      $new_gid = array_search($_component->glyphIndex, $gids);
+      $size += $font->writeUInt16($new_gid);
+
+      if ($flags & self::ARG_1_AND_2_ARE_WORDS) {
+        if ($flags & self::ARGS_ARE_XY_VALUES) {
+          $size += $font->writeInt16($_component->e);
+          $size += $font->writeInt16($_component->f);
+        }
+        else {
+          $size += $font->writeUInt16($_component->point_compound);
+          $size += $font->writeUInt16($_component->point_component);
+        }
+      }
+      else {
+        if ($flags & self::ARGS_ARE_XY_VALUES) {
+          $size += $font->writeInt8($_component->e);
+          $size += $font->writeInt8($_component->f);
+        }
+        else {
+          $size += $font->writeUInt8($_component->point_compound);
+          $size += $font->writeUInt8($_component->point_component);
+        }
+      }
+
+      if ($flags & self::WE_HAVE_A_SCALE) {
+        $size += $font->writeInt16($_component->a);
+      }
+      elseif ($flags & self::WE_HAVE_AN_X_AND_Y_SCALE) {
+        $size += $font->writeInt16($_component->a);
+        $size += $font->writeInt16($_component->d);
+      }
+      elseif ($flags & self::WE_HAVE_A_TWO_BY_TWO) {
+        $size += $font->writeInt16($_component->a);
+        $size += $font->writeInt16($_component->b);
+        $size += $font->writeInt16($_component->c);
+        $size += $font->writeInt16($_component->d);
+      }
+    }
+
+    return $size;
+  }
+
+  public function getSVGContours() {
+    $contours = array();
+
+    /** @var \FontLib\Table\Type\glyf $glyph_data */
+    $glyph_data = $this->getFont()->getTableObject("glyf");
+
+    /** @var Outline[] $glyphs */
+    $glyphs = $glyph_data->data;
+
+    foreach ($this->components as $component) {
+      $_glyph = $glyphs[$component->glyphIndex];
+
+      if ($_glyph !== $this) {
+        $contours[] = array(
+          "contours"  => $_glyph->getSVGContours(),
+          "transform" => $component->getMatrix(),
+        );
+      }
+    }
+
+    return $contours;
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/Glyph/OutlineSimple.php b/notifications/dompdf/lib/php-font-lib/Glyph/OutlineSimple.php
new file mode 100644
index 00000000..56b2fb49
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Glyph/OutlineSimple.php
@@ -0,0 +1,335 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
+ */
+
+namespace FontLib\Glyph;
+
+/**
+ * `glyf` font table.
+ *
+ * @package php-font-lib
+ */
+class OutlineSimple extends Outline {
+  const ON_CURVE       = 0x01;
+  const X_SHORT_VECTOR = 0x02;
+  const Y_SHORT_VECTOR = 0x04;
+  const REPEAT         = 0x08;
+  const THIS_X_IS_SAME = 0x10;
+  const THIS_Y_IS_SAME = 0x20;
+
+  public $instructions;
+  public $points;
+
+  function parseData() {
+    parent::parseData();
+
+    if (!$this->size) {
+      return;
+    }
+
+    $font = $this->getFont();
+
+    $noc = $this->numberOfContours;
+
+    if ($noc == 0) {
+      return;
+    }
+
+    $endPtsOfContours = $font->r(array(self::uint16, $noc));
+
+    $instructionLength  = $font->readUInt16();
+    $this->instructions = $font->r(array(self::uint8, $instructionLength));
+
+    $count = $endPtsOfContours[$noc - 1] + 1;
+
+    // Flags
+    $flags = array();
+    for ($index = 0; $index < $count; $index++) {
+      $flags[$index] = $font->readUInt8();
+
+      if ($flags[$index] & self::REPEAT) {
+        $repeats = $font->readUInt8();
+
+        for ($i = 1; $i <= $repeats; $i++) {
+          $flags[$index + $i] = $flags[$index];
+        }
+
+        $index += $repeats;
+      }
+    }
+
+    $points = array();
+    foreach ($flags as $i => $flag) {
+      $points[$i]["onCurve"]      = $flag & self::ON_CURVE;
+      $points[$i]["endOfContour"] = in_array($i, $endPtsOfContours);
+    }
+
+    // X Coords
+    $x = 0;
+    for ($i = 0; $i < $count; $i++) {
+      $flag = $flags[$i];
+
+      if ($flag & self::THIS_X_IS_SAME) {
+        if ($flag & self::X_SHORT_VECTOR) {
+          $x += $font->readUInt8();
+        }
+      }
+      else {
+        if ($flag & self::X_SHORT_VECTOR) {
+          $x -= $font->readUInt8();
+        }
+        else {
+          $x += $font->readInt16();
+        }
+      }
+
+      $points[$i]["x"] = $x;
+    }
+
+    // Y Coords
+    $y = 0;
+    for ($i = 0; $i < $count; $i++) {
+      $flag = $flags[$i];
+
+      if ($flag & self::THIS_Y_IS_SAME) {
+        if ($flag & self::Y_SHORT_VECTOR) {
+          $y += $font->readUInt8();
+        }
+      }
+      else {
+        if ($flag & self::Y_SHORT_VECTOR) {
+          $y -= $font->readUInt8();
+        }
+        else {
+          $y += $font->readInt16();
+        }
+      }
+
+      $points[$i]["y"] = $y;
+    }
+
+    $this->points = $points;
+  }
+
+  public function splitSVGPath($path) {
+    preg_match_all('/([a-z])|(-?\d+(?:\.\d+)?)/i', $path, $matches, PREG_PATTERN_ORDER);
+
+    return $matches[0];
+  }
+
+  public function makePoints($path) {
+    $path = $this->splitSVGPath($path);
+    $l    = count($path);
+    $i    = 0;
+
+    $points = array();
+
+    while ($i < $l) {
+      switch ($path[$i]) {
+        // moveTo
+        case "M":
+          $points[] = array(
+            "onCurve"      => true,
+            "x"            => $path[++$i],
+            "y"            => $path[++$i],
+            "endOfContour" => false,
+          );
+          break;
+
+        // lineTo
+        case "L":
+          $points[] = array(
+            "onCurve"      => true,
+            "x"            => $path[++$i],
+            "y"            => $path[++$i],
+            "endOfContour" => false,
+          );
+          break;
+
+        // quadraticCurveTo
+        case "Q":
+          $points[] = array(
+            "onCurve"      => false,
+            "x"            => $path[++$i],
+            "y"            => $path[++$i],
+            "endOfContour" => false,
+          );
+          $points[] = array(
+            "onCurve"      => true,
+            "x"            => $path[++$i],
+            "y"            => $path[++$i],
+            "endOfContour" => false,
+          );
+          break;
+
+        // closePath
+        /** @noinspection PhpMissingBreakStatementInspection */
+        case "z":
+          $points[count($points) - 1]["endOfContour"] = true;
+
+        default:
+          $i++;
+          break;
+      }
+    }
+
+    return $points;
+  }
+
+  function encode() {
+    if (empty($this->points)) {
+      return parent::encode();
+    }
+
+    return $this->size = $this->encodePoints($this->points);
+  }
+
+  public function encodePoints($points) {
+    $endPtsOfContours = array();
+    $flags            = array();
+    $coords_x         = array();
+    $coords_y         = array();
+
+    $last_x = 0;
+    $last_y = 0;
+    $xMin   = $yMin = 0xFFFF;
+    $xMax   = $yMax = -0xFFFF;
+    foreach ($points as $i => $point) {
+      $flag = 0;
+      if ($point["onCurve"]) {
+        $flag |= self::ON_CURVE;
+      }
+
+      if ($point["endOfContour"]) {
+        $endPtsOfContours[] = $i;
+      }
+
+      // Simplified, we could do some optimizations
+      if ($point["x"] == $last_x) {
+        $flag |= self::THIS_X_IS_SAME;
+      }
+      else {
+        $x          = intval($point["x"]);
+        $xMin       = min($x, $xMin);
+        $xMax       = max($x, $xMax);
+        $coords_x[] = $x - $last_x; // int16
+      }
+
+      // Simplified, we could do some optimizations
+      if ($point["y"] == $last_y) {
+        $flag |= self::THIS_Y_IS_SAME;
+      }
+      else {
+        $y          = intval($point["y"]);
+        $yMin       = min($y, $yMin);
+        $yMax       = max($y, $yMax);
+        $coords_y[] = $y - $last_y; // int16
+      }
+
+      $flags[] = $flag;
+      $last_x  = $point["x"];
+      $last_y  = $point["y"];
+    }
+
+    $font = $this->getFont();
+
+    $l = 0;
+    $l += $font->writeInt16(count($endPtsOfContours)); // endPtsOfContours
+    $l += $font->writeFWord(isset($this->xMin) ? $this->xMin : $xMin); // xMin
+    $l += $font->writeFWord(isset($this->yMin) ? $this->yMin : $yMin); // yMin
+    $l += $font->writeFWord(isset($this->xMax) ? $this->xMax : $xMax); // xMax
+    $l += $font->writeFWord(isset($this->yMax) ? $this->yMax : $yMax); // yMax
+
+    // Simple glyf
+    $l += $font->w(array(self::uint16, count($endPtsOfContours)), $endPtsOfContours); // endPtsOfContours
+    $l += $font->writeUInt16(0); // instructionLength
+    $l += $font->w(array(self::uint8, count($flags)), $flags); // flags
+    $l += $font->w(array(self::int16, count($coords_x)), $coords_x); // xCoordinates
+    $l += $font->w(array(self::int16, count($coords_y)), $coords_y); // yCoordinates
+    return $l;
+  }
+
+  public function getSVGContours($points = null) {
+    $path = "";
+
+    if (!$points) {
+      if (empty($this->points)) {
+        $this->parseData();
+      }
+
+      $points = $this->points;
+    }
+
+    $length     = (empty($points) ? 0 : count($points));
+    $firstIndex = 0;
+    $count      = 0;
+
+    for ($i = 0; $i < $length; $i++) {
+      $count++;
+
+      if ($points[$i]["endOfContour"]) {
+        $path .= $this->getSVGPath($points, $firstIndex, $count);
+        $firstIndex = $i + 1;
+        $count      = 0;
+      }
+    }
+
+    return $path;
+  }
+
+  protected function getSVGPath($points, $startIndex, $count) {
+    $offset = 0;
+    $path   = "";
+
+    while ($offset < $count) {
+      $point    = $points[$startIndex + $offset % $count];
+      $point_p1 = $points[$startIndex + ($offset + 1) % $count];
+
+      if ($offset == 0) {
+        $path .= "M{$point['x']},{$point['y']} ";
+      }
+
+      if ($point["onCurve"]) {
+        if ($point_p1["onCurve"]) {
+          $path .= "L{$point_p1['x']},{$point_p1['y']} ";
+          $offset++;
+        }
+        else {
+          $point_p2 = $points[$startIndex + ($offset + 2) % $count];
+
+          if ($point_p2["onCurve"]) {
+            $path .= "Q{$point_p1['x']},{$point_p1['y']},{$point_p2['x']},{$point_p2['y']} ";
+          }
+          else {
+            $path .= "Q{$point_p1['x']},{$point_p1['y']}," . $this->midValue($point_p1['x'], $point_p2['x']) . "," . $this->midValue($point_p1['y'], $point_p2['y']) . " ";
+          }
+
+          $offset += 2;
+        }
+      }
+      else {
+        if ($point_p1["onCurve"]) {
+          $path .= "Q{$point['x']},{$point['y']},{$point_p1['x']},{$point_p1['y']} ";
+        }
+        else {
+          $path .= "Q{$point['x']},{$point['y']}," . $this->midValue($point['x'], $point_p1['x']) . "," . $this->midValue($point['y'], $point_p1['y']) . " ";
+        }
+
+        $offset++;
+      }
+    }
+
+    $path .= "z ";
+
+    return $path;
+  }
+
+  function midValue($a, $b) {
+    return $a + ($b - $a) / 2;
+  }
+}
diff --git a/notifications/dompdf/lib/php-font-lib/Header.php b/notifications/dompdf/lib/php-font-lib/Header.php
new file mode 100644
index 00000000..cbf137ed
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Header.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+namespace FontLib;
+
+use FontLib\TrueType\File;
+
+/**
+ * Font header container.
+ *
+ * @package php-font-lib
+ */
+abstract class Header extends BinaryStream {
+  /**
+   * @var File
+   */
+  protected $font;
+  protected $def = array();
+
+  public $data;
+
+  public function __construct(File $font) {
+    $this->font = $font;
+  }
+
+  public function encode() {
+    return $this->font->pack($this->def, $this->data);
+  }
+
+  public function parse() {
+    $this->data = $this->font->unpack($this->def);
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/OpenType/File.php b/notifications/dompdf/lib/php-font-lib/OpenType/File.php
new file mode 100644
index 00000000..9c6df96f
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/OpenType/File.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\OpenType;
+
+/**
+ * Open Type font, the same as a TrueType one.
+ *
+ * @package php-font-lib
+ */
+class File extends \FontLib\TrueType\File {
+  //
+}
diff --git a/notifications/dompdf/lib/php-font-lib/OpenType/TableDirectoryEntry.php b/notifications/dompdf/lib/php-font-lib/OpenType/TableDirectoryEntry.php
new file mode 100644
index 00000000..dd75a3e1
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/OpenType/TableDirectoryEntry.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\OpenType;
+
+/**
+ * Open Type Table directory entry, the same as a TrueType one.
+ *
+ * @package php-font-lib
+ */
+class TableDirectoryEntry extends \FontLib\TrueType\TableDirectoryEntry {
+
+}
diff --git a/notifications/dompdf/lib/php-font-lib/Table/DirectoryEntry.php b/notifications/dompdf/lib/php-font-lib/Table/DirectoryEntry.php
new file mode 100644
index 00000000..54a67af3
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/DirectoryEntry.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+namespace FontLib\Table;
+
+use FontLib\TrueType\File;
+use FontLib\Font;
+use FontLib\BinaryStream;
+
+/**
+ * Generic Font table directory entry.
+ *
+ * @package php-font-lib
+ */
+class DirectoryEntry extends BinaryStream {
+  /**
+   * @var File
+   */
+  protected $font;
+
+  /**
+   * @var Table
+   */
+  protected $font_table;
+
+  public $entryLength = 4;
+
+  public $tag;
+  public $checksum;
+  public $offset;
+  public $length;
+
+  protected $origF;
+
+  /**
+   * @param string $data
+   *
+   * @return int
+   */
+  static function computeChecksum($data) {
+    $len = mb_strlen($data, '8bit');
+    $mod = $len % 4;
+
+    if ($mod) {
+      $data = str_pad($data, $len + (4 - $mod), "\0");
+    }
+
+    $len = mb_strlen($data, '8bit');
+
+    $hi = 0x0000;
+    $lo = 0x0000;
+
+    for ($i = 0; $i < $len; $i += 4) {
+      $hi += (ord($data[$i]) << 8) + ord($data[$i + 1]);
+      $lo += (ord($data[$i + 2]) << 8) + ord($data[$i + 3]);
+      $hi += $lo >> 16;
+      $lo = $lo & 0xFFFF;
+      $hi = $hi & 0xFFFF;
+    }
+
+    return ($hi << 8) + $lo;
+  }
+
+  function __construct(File $font) {
+    $this->font = $font;
+    $this->f    = $font->f;
+  }
+
+  function parse() {
+    $this->tag = $this->font->read(4);
+  }
+
+  function open($filename, $mode = self::modeRead) {
+    // void
+  }
+
+  function setTable(Table $font_table) {
+    $this->font_table = $font_table;
+  }
+
+  function encode($entry_offset) {
+    Font::d("\n==== $this->tag ====");
+    //Font::d("Entry offset  = $entry_offset");
+
+    $data = $this->font_table;
+    $font = $this->font;
+
+    $table_offset = $font->pos();
+    $this->offset = $table_offset;
+    $table_length = $data->encode();
+
+    $font->seek($table_offset);
+    $table_data = $font->read($table_length);
+
+    $font->seek($entry_offset);
+
+    $font->write($this->tag, 4);
+    $font->writeUInt32(self::computeChecksum($table_data));
+    $font->writeUInt32($table_offset);
+    $font->writeUInt32($table_length);
+
+    Font::d("Bytes written = $table_length");
+
+    $font->seek($table_offset + $table_length);
+  }
+
+  /**
+   * @return File
+   */
+  function getFont() {
+    return $this->font;
+  }
+
+  function startRead() {
+    $this->font->seek($this->offset);
+  }
+
+  function endRead() {
+    //
+  }
+
+  function startWrite() {
+    $this->font->seek($this->offset);
+  }
+
+  function endWrite() {
+    //
+  }
+}
+
diff --git a/notifications/dompdf/lib/php-font-lib/Table/Table.php b/notifications/dompdf/lib/php-font-lib/Table/Table.php
new file mode 100644
index 00000000..b1271123
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/Table.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+namespace FontLib\Table;
+
+use FontLib\TrueType\File;
+use FontLib\Font;
+use FontLib\BinaryStream;
+
+/**
+ * Generic font table.
+ *
+ * @package php-font-lib
+ */
+class Table extends BinaryStream {
+  /**
+   * @var DirectoryEntry
+   */
+  protected $entry;
+  protected $def = array();
+
+  public $data;
+
+  final public function __construct(DirectoryEntry $entry) {
+    $this->entry = $entry;
+    $entry->setTable($this);
+  }
+
+  /**
+   * @return File
+   */
+  public function getFont() {
+    return $this->entry->getFont();
+  }
+
+  protected function _encode() {
+    if (empty($this->data)) {
+      Font::d("  >> Table is empty");
+
+      return 0;
+    }
+
+    return $this->getFont()->pack($this->def, $this->data);
+  }
+
+  protected function _parse() {
+    $this->data = $this->getFont()->unpack($this->def);
+  }
+
+  protected function _parseRaw() {
+    $this->data = $this->getFont()->read($this->entry->length);
+  }
+
+  protected function _encodeRaw() {
+    return $this->getFont()->write($this->data, $this->entry->length);
+  }
+
+  public function toHTML() {
+    return "<pre>" . var_export($this->data, true) . "</pre>";
+  }
+
+  final public function encode() {
+    $this->entry->startWrite();
+
+    if (false && empty($this->def)) {
+      $length = $this->_encodeRaw();
+    }
+    else {
+      $length = $this->_encode();
+    }
+
+    $this->entry->endWrite();
+
+    return $length;
+  }
+
+  final public function parse() {
+    $this->entry->startRead();
+
+    if (false && empty($this->def)) {
+      $this->_parseRaw();
+    }
+    else {
+      $this->_parse();
+    }
+
+    $this->entry->endRead();
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/Table/Type/cmap.php b/notifications/dompdf/lib/php-font-lib/Table/Type/cmap.php
new file mode 100644
index 00000000..7db77e1a
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/Type/cmap.php
@@ -0,0 +1,298 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `cmap` font table.
+ *
+ * @package php-font-lib
+ */
+class cmap extends Table {
+  private static $header_format = array(
+    "version"         => self::uint16,
+    "numberSubtables" => self::uint16,
+  );
+
+  private static $subtable_header_format = array(
+    "platformID"         => self::uint16,
+    "platformSpecificID" => self::uint16,
+    "offset"             => self::uint32,
+  );
+
+  private static $subtable_v4_format = array(
+    "length"        => self::uint16,
+    "language"      => self::uint16,
+    "segCountX2"    => self::uint16,
+    "searchRange"   => self::uint16,
+    "entrySelector" => self::uint16,
+    "rangeShift"    => self::uint16,
+  );
+
+  private static $subtable_v12_format = array(
+    "length"        => self::uint32,
+    "language"      => self::uint32,
+    "ngroups"    => self::uint32
+  );
+
+  protected function _parse() {
+    $font = $this->getFont();
+
+    $cmap_offset = $font->pos();
+
+    $data = $font->unpack(self::$header_format);
+
+    $subtables = array();
+    for ($i = 0; $i < $data["numberSubtables"]; $i++) {
+      $subtables[] = $font->unpack(self::$subtable_header_format);
+    }
+
+    $data["subtables"] = $subtables;
+
+    foreach ($data["subtables"] as $i => &$subtable) {
+      $font->seek($cmap_offset + $subtable["offset"]);
+
+      $subtable["format"] = $font->readUInt16();
+
+      // @todo Only CMAP version 4 and 12
+      if (($subtable["format"] != 4) && ($subtable["format"] != 12)) {
+        unset($data["subtables"][$i]);
+        $data["numberSubtables"]--;
+        continue;
+      }
+
+      if ($subtable["format"] == 12) {
+
+        $font->readUInt16();
+
+        $subtable += $font->unpack(self::$subtable_v12_format);
+
+        $glyphIndexArray = array();
+        $endCodes = array();
+        $startCodes = array();
+
+        for ($p = 0; $p < $subtable['ngroups']; $p++) {
+
+          $startCode = $startCodes[] = $font->readUInt32();
+          $endCode = $endCodes[] = $font->readUInt32();
+          $startGlyphCode = $font->readUInt32();
+
+          for ($c = $startCode; $c <= $endCode; $c++) {
+            $glyphIndexArray[$c] = $startGlyphCode;
+            $startGlyphCode++;
+          }
+        }
+
+        $subtable += array(
+          "startCode" => $startCodes,
+          "endCode" => $endCodes,
+          "glyphIndexArray" => $glyphIndexArray,
+        );
+
+      }
+      else if ($subtable["format"] == 4) {
+
+        $subtable += $font->unpack(self::$subtable_v4_format);
+
+        $segCount             = $subtable["segCountX2"] / 2;
+        $subtable["segCount"] = $segCount;
+
+        $endCode = $font->readUInt16Many($segCount);
+
+        $font->readUInt16(); // reservedPad
+
+        $startCode = $font->readUInt16Many($segCount);
+        $idDelta   = $font->readInt16Many($segCount);
+
+        $ro_start      = $font->pos();
+        $idRangeOffset = $font->readUInt16Many($segCount);
+
+        $glyphIndexArray = array();
+        for ($i = 0; $i < $segCount; $i++) {
+          $c1 = $startCode[$i];
+          $c2 = $endCode[$i];
+          $d  = $idDelta[$i];
+          $ro = $idRangeOffset[$i];
+
+          if ($ro > 0) {
+            $font->seek($subtable["offset"] + 2 * $i + $ro);
+          }
+
+          for ($c = $c1; $c <= $c2; $c++) {
+            if ($ro == 0) {
+              $gid = ($c + $d) & 0xFFFF;
+            }
+            else {
+              $offset = ($c - $c1) * 2 + $ro;
+              $offset = $ro_start + 2 * $i + $offset;
+
+              $font->seek($offset);
+              $gid = $font->readUInt16();
+
+              if ($gid != 0) {
+                $gid = ($gid + $d) & 0xFFFF;
+              }
+            }
+
+            if ($gid > 0) {
+              $glyphIndexArray[$c] = $gid;
+            }
+          }
+        }
+
+        $subtable += array(
+          "endCode"         => $endCode,
+          "startCode"       => $startCode,
+          "idDelta"         => $idDelta,
+          "idRangeOffset"   => $idRangeOffset,
+          "glyphIndexArray" => $glyphIndexArray,
+        );
+      }
+    }
+
+    $this->data = $data;
+  }
+
+  function _encode() {
+    $font = $this->getFont();
+
+    $subset          = $font->getSubset();
+    $glyphIndexArray = $font->getUnicodeCharMap();
+
+    $newGlyphIndexArray = array();
+    foreach ($glyphIndexArray as $code => $gid) {
+      $new_gid = array_search($gid, $subset);
+      if ($new_gid !== false) {
+        $newGlyphIndexArray[$code] = $new_gid;
+      }
+    }
+
+    ksort($newGlyphIndexArray); // Sort by char code
+
+    $segments = array();
+
+    $i        = -1;
+    $prevCode = 0xFFFF;
+    $prevGid  = 0xFFFF;
+
+    foreach ($newGlyphIndexArray as $code => $gid) {
+      if (
+        $prevCode + 1 != $code ||
+        $prevGid + 1 != $gid
+      ) {
+        $i++;
+        $segments[$i] = array();
+      }
+
+      $segments[$i][] = array($code, $gid);
+
+      $prevCode = $code;
+      $prevGid  = $gid;
+    }
+
+    $segments[][] = array(0xFFFF, 0xFFFF);
+
+    $startCode = array();
+    $endCode   = array();
+    $idDelta   = array();
+
+    foreach ($segments as $codes) {
+      $start = reset($codes);
+      $end   = end($codes);
+
+      $startCode[] = $start[0];
+      $endCode[]   = $end[0];
+      $idDelta[]   = $start[1] - $start[0];
+    }
+
+    $segCount      = count($startCode);
+    $idRangeOffset = array_fill(0, $segCount, 0);
+
+    $searchRange   = 1;
+    $entrySelector = 0;
+    while ($searchRange * 2 <= $segCount) {
+      $searchRange *= 2;
+      $entrySelector++;
+    }
+    $searchRange *= 2;
+    $rangeShift = $segCount * 2 - $searchRange;
+
+    $subtables = array(
+      array(
+        // header
+        "platformID"         => 3, // Unicode
+        "platformSpecificID" => 1,
+        "offset"             => null,
+
+        // subtable
+        "format"             => 4,
+        "length"             => null,
+        "language"           => 0,
+        "segCount"           => $segCount,
+        "segCountX2"         => $segCount * 2,
+        "searchRange"        => $searchRange,
+        "entrySelector"      => $entrySelector,
+        "rangeShift"         => $rangeShift,
+        "startCode"          => $startCode,
+        "endCode"            => $endCode,
+        "idDelta"            => $idDelta,
+        "idRangeOffset"      => $idRangeOffset,
+        "glyphIndexArray"    => $newGlyphIndexArray,
+      )
+    );
+
+    $data = array(
+      "version"         => 0,
+      "numberSubtables" => count($subtables),
+      "subtables"       => $subtables,
+    );
+
+    $length = $font->pack(self::$header_format, $data);
+
+    $subtable_headers_size   = $data["numberSubtables"] * 8; // size of self::$subtable_header_format
+    $subtable_headers_offset = $font->pos();
+
+    $length += $font->write(str_repeat("\0", $subtable_headers_size), $subtable_headers_size);
+
+    // write subtables data
+    foreach ($data["subtables"] as $i => $subtable) {
+      $length_before                   = $length;
+      $data["subtables"][$i]["offset"] = $length;
+
+      $length += $font->writeUInt16($subtable["format"]);
+
+      $before_subheader = $font->pos();
+      $length += $font->pack(self::$subtable_v4_format, $subtable);
+
+      $segCount = $subtable["segCount"];
+      $length += $font->w(array(self::uint16, $segCount), $subtable["endCode"]);
+      $length += $font->writeUInt16(0); // reservedPad
+      $length += $font->w(array(self::uint16, $segCount), $subtable["startCode"]);
+      $length += $font->w(array(self::int16, $segCount), $subtable["idDelta"]);
+      $length += $font->w(array(self::uint16, $segCount), $subtable["idRangeOffset"]);
+      $length += $font->w(array(self::uint16, $segCount), array_values($subtable["glyphIndexArray"]));
+
+      $after_subtable = $font->pos();
+
+      $subtable["length"] = $length - $length_before;
+      $font->seek($before_subheader);
+      $length += $font->pack(self::$subtable_v4_format, $subtable);
+
+      $font->seek($after_subtable);
+    }
+
+    // write subtables headers
+    $font->seek($subtable_headers_offset);
+    foreach ($data["subtables"] as $subtable) {
+      $font->pack(self::$subtable_header_format, $subtable);
+    }
+
+    return $length;
+  }
+}
diff --git a/notifications/dompdf/lib/php-font-lib/Table/Type/glyf.php b/notifications/dompdf/lib/php-font-lib/Table/Type/glyf.php
new file mode 100644
index 00000000..1fbec3fd
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/Type/glyf.php
@@ -0,0 +1,154 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+
+use FontLib\Table\Table;
+use FontLib\Glyph\Outline;
+use FontLib\Glyph\OutlineSimple;
+
+/**
+ * `glyf` font table.
+ *
+ * @package php-font-lib
+ * @property Outline[] $data
+ */
+class glyf extends Table {
+  protected function _parse() {
+    $font   = $this->getFont();
+    $offset = $font->pos();
+
+    $loca      = $font->getData("loca");
+    $real_loca = array_slice($loca, 0, -1); // Not the last dummy loca entry
+
+    $data = array();
+
+    foreach ($real_loca as $gid => $location) {
+      $_offset    = $offset + $loca[$gid];
+      $_size      = $loca[$gid + 1] - $loca[$gid];
+      $data[$gid] = Outline::init($this, $_offset, $_size, $font);
+    }
+
+    $this->data = $data;
+  }
+
+  public function getGlyphIDs($gids = array()) {
+    $glyphIDs = array();
+
+    foreach ($gids as $_gid) {
+      $_glyph   = $this->data[$_gid];
+      $glyphIDs = array_merge($glyphIDs, $_glyph->getGlyphIDs());
+    }
+
+    return array_unique(array_merge($gids, $glyphIDs));
+  }
+
+  public function toHTML() {
+    $max  = 160;
+    $font = $this->getFont();
+
+    $head      = $font->getData("head");
+    $head_json = json_encode($head);
+
+    $os2      = $font->getData("OS/2");
+    $os2_json = json_encode($os2);
+
+    $hmtx      = $font->getData("hmtx");
+    $hmtx_json = json_encode($hmtx);
+
+    $names           = $font->getData("post", "names");
+    $glyphIndexArray = array_flip($font->getUnicodeCharMap());
+
+    $width  = (abs($head["xMin"]) + $head["xMax"]);
+    $height = (abs($head["yMin"]) + $head["yMax"]);
+
+    $ratio = 1;
+    if ($width > $max || $height > $max) {
+      $ratio  = max($width, $height) / $max;
+      $width  = round($width / $ratio);
+      $height = round($height / $ratio);
+    }
+
+    $n = 500;
+
+    $s = "<h3>" . "Only the first $n simple glyphs are shown (" . count($this->data) . " total)
+    <div class='glyph-view simple'>Simple glyph</div>
+    <div class='glyph-view composite'>Composite glyph</div>
+    Zoom: <input type='range' value='100' max='400' onchange='Glyph.resize(this.value)' />
+    </h3>
+    <script>
+      Glyph.ratio  = $ratio;
+      Glyph.head   = $head_json;
+      Glyph.os2    = $os2_json;
+      Glyph.hmtx   = $hmtx_json;
+      Glyph.width  = $width;
+      Glyph.height = $height;
+    </script>";
+
+    foreach ($this->data as $g => $glyph) {
+      if ($n-- <= 0) {
+        break;
+      }
+
+      $glyph->parseData();
+
+      $shape      = array(
+        "SVGContours" => $glyph->getSVGContours(),
+        "xMin"        => $glyph->xMin,
+        "yMin"        => $glyph->yMin,
+        "xMax"        => $glyph->xMax,
+        "yMax"        => $glyph->yMax,
+      );
+      $shape_json = json_encode($shape);
+
+      $type = ($glyph instanceof OutlineSimple ? "simple" : "composite");
+      $char = isset($glyphIndexArray[$g]) ? $glyphIndexArray[$g] : 0;
+      $name = isset($names[$g]) ? $names[$g] : sprintf("uni%04x", $char);
+      $char = $char ? "&#{$glyphIndexArray[$g]};" : "";
+
+      $s .= "<div class='glyph-view $type' id='glyph-$g'>
+              <span class='glyph-id'>$g</span>
+              <span class='char'>$char</span>
+              <span class='char-name'>$name</span>
+              ";
+
+      if ($type == "composite") {
+        foreach ($glyph->getGlyphIDs() as $_id) {
+          $s .= "<a href='#glyph-$_id' class='glyph-component-id'>$_id</a> ";
+        }
+      }
+
+      $s .= "<br />
+            <canvas width='$width' height='$height' id='glyph-canvas-$g'></canvas>
+            </div>
+            <script>Glyph.glyphs.push([$g,$shape_json]);</script>";
+    }
+
+    return $s;
+  }
+
+
+  protected function _encode() {
+    $font   = $this->getFont();
+    $subset = $font->getSubset();
+    $data   = $this->data;
+
+    $loca = array();
+
+    $length = 0;
+    foreach ($subset as $gid) {
+      $loca[] = $length;
+      $length += $data[$gid]->encode();
+    }
+
+    $loca[]                             = $length; // dummy loca
+    $font->getTableObject("loca")->data = $loca;
+
+    return $length;
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/Table/Type/head.php b/notifications/dompdf/lib/php-font-lib/Table/Type/head.php
new file mode 100644
index 00000000..6349f145
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/Type/head.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+use Exception;
+
+/**
+ * `head` font table.
+ *
+ * @package php-font-lib
+ */
+class head extends Table {
+  protected $def = array(
+    "tableVersion"       => self::Fixed,
+    "fontRevision"       => self::Fixed,
+    "checkSumAdjustment" => self::uint32,
+    "magicNumber"        => self::uint32,
+    "flags"              => self::uint16,
+    "unitsPerEm"         => self::uint16,
+    "created"            => self::longDateTime,
+    "modified"           => self::longDateTime,
+    "xMin"               => self::FWord,
+    "yMin"               => self::FWord,
+    "xMax"               => self::FWord,
+    "yMax"               => self::FWord,
+    "macStyle"           => self::uint16,
+    "lowestRecPPEM"      => self::uint16,
+    "fontDirectionHint"  => self::int16,
+    "indexToLocFormat"   => self::int16,
+    "glyphDataFormat"    => self::int16,
+  );
+
+  protected function _parse() {
+    parent::_parse();
+
+    if ($this->data["magicNumber"] != 0x5F0F3CF5) {
+      throw new Exception("Incorrect magic number (" . dechex($this->data["magicNumber"]) . ")");
+    }
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/Table/Type/hhea.php b/notifications/dompdf/lib/php-font-lib/Table/Type/hhea.php
new file mode 100644
index 00000000..dc60a14b
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/Type/hhea.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `hhea` font table.
+ *
+ * @package php-font-lib
+ */
+class hhea extends Table {
+  protected $def = array(
+    "version"             => self::Fixed,
+    "ascent"              => self::FWord,
+    "descent"             => self::FWord,
+    "lineGap"             => self::FWord,
+    "advanceWidthMax"     => self::uFWord,
+    "minLeftSideBearing"  => self::FWord,
+    "minRightSideBearing" => self::FWord,
+    "xMaxExtent"          => self::FWord,
+    "caretSlopeRise"      => self::int16,
+    "caretSlopeRun"       => self::int16,
+    "caretOffset"         => self::FWord,
+    self::int16,
+    self::int16,
+    self::int16,
+    self::int16,
+    "metricDataFormat"    => self::int16,
+    "numOfLongHorMetrics" => self::uint16,
+  );
+
+  function _encode() {
+    $font                              = $this->getFont();
+    $this->data["numOfLongHorMetrics"] = count($font->getSubset());
+
+    return parent::_encode();
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/Table/Type/hmtx.php b/notifications/dompdf/lib/php-font-lib/Table/Type/hmtx.php
new file mode 100644
index 00000000..76e3307e
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/Type/hmtx.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `hmtx` font table.
+ *
+ * @package php-font-lib
+ */
+class hmtx extends Table {
+  protected function _parse() {
+    $font   = $this->getFont();
+    $offset = $font->pos();
+
+    $numOfLongHorMetrics = $font->getData("hhea", "numOfLongHorMetrics");
+    $numGlyphs           = $font->getData("maxp", "numGlyphs");
+
+    $font->seek($offset);
+
+    $data = array();
+    $metrics = $font->readUInt16Many($numOfLongHorMetrics * 2);
+    for ($gid = 0, $mid = 0; $gid < $numOfLongHorMetrics; $gid++) {
+      $advanceWidth    = isset($metrics[$mid]) ? $metrics[$mid] : 0;
+      $mid += 1;
+      $leftSideBearing = isset($metrics[$mid]) ? $metrics[$mid] : 0;
+      $mid += 1;
+      $data[$gid]      = array($advanceWidth, $leftSideBearing);
+    }
+
+    if ($numOfLongHorMetrics < $numGlyphs) {
+      $lastWidth = end($data);
+      $data      = array_pad($data, $numGlyphs, $lastWidth);
+    }
+
+    $this->data = $data;
+  }
+
+  protected function _encode() {
+    $font   = $this->getFont();
+    $subset = $font->getSubset();
+    $data   = $this->data;
+
+    $length = 0;
+
+    foreach ($subset as $gid) {
+      $length += $font->writeUInt16($data[$gid][0]);
+      $length += $font->writeUInt16($data[$gid][1]);
+    }
+
+    return $length;
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/Table/Type/kern.php b/notifications/dompdf/lib/php-font-lib/Table/Type/kern.php
new file mode 100644
index 00000000..98759464
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/Type/kern.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `kern` font table.
+ *
+ * @package php-font-lib
+ */
+class kern extends Table {
+  protected function _parse() {
+    $font = $this->getFont();
+
+    $data = $font->unpack(array(
+      "version"         => self::uint16,
+      "nTables"         => self::uint16,
+
+      // only the first subtable will be parsed
+      "subtableVersion" => self::uint16,
+      "length"          => self::uint16,
+      "coverage"        => self::uint16,
+    ));
+
+    $data["format"] = ($data["coverage"] >> 8);
+
+    $subtable = array();
+
+    switch ($data["format"]) {
+      case 0:
+        $subtable = $font->unpack(array(
+          "nPairs"        => self::uint16,
+          "searchRange"   => self::uint16,
+          "entrySelector" => self::uint16,
+          "rangeShift"    => self::uint16,
+        ));
+
+        $pairs = array();
+        $tree  = array();
+
+        $values = $font->readUInt16Many($subtable["nPairs"] * 3);
+        for ($i = 0, $idx = 0; $i < $subtable["nPairs"]; $i++) {
+          $left  = $values[$idx++];
+          $right = $values[$idx++];
+          $value = $values[$idx++];
+
+          if ($value >= 0x8000) {
+            $value -= 0x10000;
+          }
+
+          $pairs[] = array(
+            "left"  => $left,
+            "right" => $right,
+            "value" => $value,
+          );
+
+          $tree[$left][$right] = $value;
+        }
+
+        //$subtable["pairs"] = $pairs;
+        $subtable["tree"] = $tree;
+        break;
+
+      case 1:
+      case 2:
+      case 3:
+        break;
+    }
+
+    $data["subtable"] = $subtable;
+
+    $this->data = $data;
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/Table/Type/loca.php b/notifications/dompdf/lib/php-font-lib/Table/Type/loca.php
new file mode 100644
index 00000000..cbc2a200
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/Type/loca.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `loca` font table.
+ *
+ * @package php-font-lib
+ */
+class loca extends Table {
+  protected function _parse() {
+    $font   = $this->getFont();
+    $offset = $font->pos();
+
+    $indexToLocFormat = $font->getData("head", "indexToLocFormat");
+    $numGlyphs        = $font->getData("maxp", "numGlyphs");
+
+    $font->seek($offset);
+
+    $data = array();
+
+    // 2 bytes
+    if ($indexToLocFormat == 0) {
+      $d   = $font->read(($numGlyphs + 1) * 2);
+      $loc = unpack("n*", $d);
+
+      for ($i = 0; $i <= $numGlyphs; $i++) {
+        $data[] = isset($loc[$i + 1]) ?  $loc[$i + 1] * 2 : 0;
+      }
+    }
+
+    // 4 bytes
+    else {
+      if ($indexToLocFormat == 1) {
+        $d   = $font->read(($numGlyphs + 1) * 4);
+        $loc = unpack("N*", $d);
+
+        for ($i = 0; $i <= $numGlyphs; $i++) {
+          $data[] = isset($loc[$i + 1]) ? $loc[$i + 1] : 0;
+        }
+      }
+    }
+
+    $this->data = $data;
+  }
+
+  function _encode() {
+    $font = $this->getFont();
+    $data = $this->data;
+
+    $indexToLocFormat = $font->getData("head", "indexToLocFormat");
+    $numGlyphs        = $font->getData("maxp", "numGlyphs");
+    $length           = 0;
+
+    // 2 bytes
+    if ($indexToLocFormat == 0) {
+      for ($i = 0; $i <= $numGlyphs; $i++) {
+        $length += $font->writeUInt16($data[$i] / 2);
+      }
+    }
+
+    // 4 bytes
+    else {
+      if ($indexToLocFormat == 1) {
+        for ($i = 0; $i <= $numGlyphs; $i++) {
+          $length += $font->writeUInt32($data[$i]);
+        }
+      }
+    }
+
+    return $length;
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/Table/Type/maxp.php b/notifications/dompdf/lib/php-font-lib/Table/Type/maxp.php
new file mode 100644
index 00000000..b4ebae03
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/Type/maxp.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `maxp` font table.
+ *
+ * @package php-font-lib
+ */
+class maxp extends Table {
+  protected $def = array(
+    "version"               => self::Fixed,
+    "numGlyphs"             => self::uint16,
+    "maxPoints"             => self::uint16,
+    "maxContours"           => self::uint16,
+    "maxComponentPoints"    => self::uint16,
+    "maxComponentContours"  => self::uint16,
+    "maxZones"              => self::uint16,
+    "maxTwilightPoints"     => self::uint16,
+    "maxStorage"            => self::uint16,
+    "maxFunctionDefs"       => self::uint16,
+    "maxInstructionDefs"    => self::uint16,
+    "maxStackElements"      => self::uint16,
+    "maxSizeOfInstructions" => self::uint16,
+    "maxComponentElements"  => self::uint16,
+    "maxComponentDepth"     => self::uint16,
+  );
+
+  function _encode() {
+    $font                    = $this->getFont();
+    $this->data["numGlyphs"] = count($font->getSubset());
+
+    return parent::_encode();
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/Table/Type/name.php b/notifications/dompdf/lib/php-font-lib/Table/Type/name.php
new file mode 100644
index 00000000..794824d3
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/Type/name.php
@@ -0,0 +1,193 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+
+use FontLib\Table\Table;
+use FontLib\Font;
+
+/**
+ * `name` font table.
+ *
+ * @package php-font-lib
+ */
+class name extends Table {
+  private static $header_format = array(
+    "format"       => self::uint16,
+    "count"        => self::uint16,
+    "stringOffset" => self::uint16,
+  );
+
+  const NAME_COPYRIGHT          = 0;
+  const NAME_NAME               = 1;
+  const NAME_SUBFAMILY          = 2;
+  const NAME_SUBFAMILY_ID       = 3;
+  const NAME_FULL_NAME          = 4;
+  const NAME_VERSION            = 5;
+  const NAME_POSTSCRIPT_NAME    = 6;
+  const NAME_TRADEMARK          = 7;
+  const NAME_MANUFACTURER       = 8;
+  const NAME_DESIGNER           = 9;
+  const NAME_DESCRIPTION        = 10;
+  const NAME_VENDOR_URL         = 11;
+  const NAME_DESIGNER_URL       = 12;
+  const NAME_LICENSE            = 13;
+  const NAME_LICENSE_URL        = 14;
+  const NAME_PREFERRE_FAMILY    = 16;
+  const NAME_PREFERRE_SUBFAMILY = 17;
+  const NAME_COMPAT_FULL_NAME   = 18;
+  const NAME_SAMPLE_TEXT        = 19;
+
+  static $nameIdCodes = array(
+    0  => "Copyright",
+    1  => "FontName",
+    2  => "FontSubfamily",
+    3  => "UniqueID",
+    4  => "FullName",
+    5  => "Version",
+    6  => "PostScriptName",
+    7  => "Trademark",
+    8  => "Manufacturer",
+    9  => "Designer",
+    10 => "Description",
+    11 => "FontVendorURL",
+    12 => "FontDesignerURL",
+    13 => "LicenseDescription",
+    14 => "LicenseURL",
+    // 15
+    16 => "PreferredFamily",
+    17 => "PreferredSubfamily",
+    18 => "CompatibleFullName",
+    19 => "SampleText",
+  );
+
+  static $platforms = array(
+    0 => "Unicode",
+    1 => "Macintosh",
+    // 2 =>  Reserved
+    3 => "Microsoft",
+  );
+
+  static $platformSpecific = array(
+    // Unicode
+    0 => array(
+      0 => "Default semantics",
+      1 => "Version 1.1 semantics",
+      2 => "ISO 10646 1993 semantics (deprecated)",
+      3 => "Unicode 2.0 or later semantics",
+    ),
+
+    // Macintosh
+    1 => array(
+      0  => "Roman",
+      1  => "Japanese",
+      2  => "Traditional Chinese",
+      3  => "Korean",
+      4  => "Arabic",
+      5  => "Hebrew",
+      6  => "Greek",
+      7  => "Russian",
+      8  => "RSymbol",
+      9  => "Devanagari",
+      10 => "Gurmukhi",
+      11 => "Gujarati",
+      12 => "Oriya",
+      13 => "Bengali",
+      14 => "Tamil",
+      15 => "Telugu",
+      16 => "Kannada",
+      17 => "Malayalam",
+      18 => "Sinhalese",
+      19 => "Burmese",
+      20 => "Khmer",
+      21 => "Thai",
+      22 => "Laotian",
+      23 => "Georgian",
+      24 => "Armenian",
+      25 => "Simplified Chinese",
+      26 => "Tibetan",
+      27 => "Mongolian",
+      28 => "Geez",
+      29 => "Slavic",
+      30 => "Vietnamese",
+      31 => "Sindhi",
+    ),
+
+    // Microsoft
+    3 => array(
+      0  => "Symbol",
+      1  => "Unicode BMP (UCS-2)",
+      2  => "ShiftJIS",
+      3  => "PRC",
+      4  => "Big5",
+      5  => "Wansung",
+      6  => "Johab",
+      //  7 => Reserved
+      //  8 => Reserved
+      //  9 => Reserved
+      10 => "Unicode UCS-4",
+    ),
+  );
+
+  protected function _parse() {
+    $font = $this->getFont();
+
+    $tableOffset = $font->pos();
+
+    $data = $font->unpack(self::$header_format);
+
+    $records = array();
+    for ($i = 0; $i < $data["count"]; $i++) {
+      $record      = new nameRecord();
+      $record_data = $font->unpack(nameRecord::$format);
+      $record->map($record_data);
+
+      $records[] = $record;
+    }
+
+    $names = array();
+    foreach ($records as $record) {
+      $font->seek($tableOffset + $data["stringOffset"] + $record->offset);
+      $s                      = $font->read($record->length);
+      $record->string         = Font::UTF16ToUTF8($s);
+      $names[$record->nameID] = $record;
+    }
+
+    $data["records"] = $names;
+
+    $this->data = $data;
+  }
+
+  protected function _encode() {
+    $font = $this->getFont();
+
+    /** @var nameRecord[] $records */
+    $records       = $this->data["records"];
+    $count_records = count($records);
+
+    $this->data["count"]        = $count_records;
+    $this->data["stringOffset"] = 6 + $count_records * 12; // 6 => uint16 * 3, 12 => sizeof self::$record_format
+
+    $length = $font->pack(self::$header_format, $this->data);
+
+    $offset = 0;
+    foreach ($records as $record) {
+      $record->length = mb_strlen($record->getUTF16(), "8bit");
+      $record->offset = $offset;
+      $offset += $record->length;
+      $length += $font->pack(nameRecord::$format, (array)$record);
+    }
+
+    foreach ($records as $record) {
+      $str = $record->getUTF16();
+      $length += $font->write($str, mb_strlen($str, "8bit"));
+    }
+
+    return $length;
+  }
+}
diff --git a/notifications/dompdf/lib/php-font-lib/Table/Type/nameRecord.php b/notifications/dompdf/lib/php-font-lib/Table/Type/nameRecord.php
new file mode 100644
index 00000000..2073c20d
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/Type/nameRecord.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+namespace FontLib\Table\Type;
+
+use FontLib\Font;
+use FontLib\BinaryStream;
+
+/**
+ * Font table name record.
+ *
+ * @package php-font-lib
+ */
+class nameRecord extends BinaryStream {
+  public $platformID;
+  public $platformSpecificID;
+  public $languageID;
+  public $nameID;
+  public $length;
+  public $offset;
+  public $string;
+
+  public static $format = array(
+    "platformID"         => self::uint16,
+    "platformSpecificID" => self::uint16,
+    "languageID"         => self::uint16,
+    "nameID"             => self::uint16,
+    "length"             => self::uint16,
+    "offset"             => self::uint16,
+  );
+
+  public function map($data) {
+    foreach ($data as $key => $value) {
+      $this->$key = $value;
+    }
+  }
+
+  public function getUTF8() {
+    return $this->string;
+  }
+
+  public function getUTF16() {
+    return Font::UTF8ToUTF16($this->string);
+  }
+
+  function __toString() {
+    return $this->string;
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/Table/Type/os2.php b/notifications/dompdf/lib/php-font-lib/Table/Type/os2.php
new file mode 100644
index 00000000..19a3e21f
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/Type/os2.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `OS/2` font table.
+ *
+ * @package php-font-lib
+ */
+class os2 extends Table {
+  protected $def = array(
+    "version"             => self::uint16,
+    "xAvgCharWidth"       => self::int16,
+    "usWeightClass"       => self::uint16,
+    "usWidthClass"        => self::uint16,
+    "fsType"              => self::int16,
+    "ySubscriptXSize"     => self::int16,
+    "ySubscriptYSize"     => self::int16,
+    "ySubscriptXOffset"   => self::int16,
+    "ySubscriptYOffset"   => self::int16,
+    "ySuperscriptXSize"   => self::int16,
+    "ySuperscriptYSize"   => self::int16,
+    "ySuperscriptXOffset" => self::int16,
+    "ySuperscriptYOffset" => self::int16,
+    "yStrikeoutSize"      => self::int16,
+    "yStrikeoutPosition"  => self::int16,
+    "sFamilyClass"        => self::int16,
+    "panose"              => array(self::uint8, 10),
+    "ulCharRange"         => array(self::uint32, 4),
+    "achVendID"           => array(self::char, 4),
+    "fsSelection"         => self::uint16,
+    "fsFirstCharIndex"    => self::uint16,
+    "fsLastCharIndex"     => self::uint16,
+    "typoAscender"        => self::int16,
+    "typoDescender"       => self::int16,
+    "typoLineGap"         => self::int16,
+    "winAscent"           => self::int16,
+    "winDescent"          => self::int16,
+  );
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/Table/Type/post.php b/notifications/dompdf/lib/php-font-lib/Table/Type/post.php
new file mode 100644
index 00000000..030a942e
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/Table/Type/post.php
@@ -0,0 +1,143 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+use FontLib\TrueType\File;
+
+/**
+ * `post` font table.
+ *
+ * @package php-font-lib
+ */
+class post extends Table {
+  protected $def = array(
+    "format"             => self::Fixed,
+    "italicAngle"        => self::Fixed,
+    "underlinePosition"  => self::FWord,
+    "underlineThickness" => self::FWord,
+    "isFixedPitch"       => self::uint32,
+    "minMemType42"       => self::uint32,
+    "maxMemType42"       => self::uint32,
+    "minMemType1"        => self::uint32,
+    "maxMemType1"        => self::uint32,
+  );
+
+  protected function _parse() {
+    $font = $this->getFont();
+    $data = $font->unpack($this->def);
+
+    $names = array();
+
+    switch ($data["format"]) {
+      case 1:
+        $names = File::$macCharNames;
+        break;
+
+      case 2:
+        $data["numberOfGlyphs"] = $font->readUInt16();
+
+        $glyphNameIndex = $font->readUInt16Many($data["numberOfGlyphs"]);
+
+        $data["glyphNameIndex"] = $glyphNameIndex;
+
+        $namesPascal = array();
+        for ($i = 0; $i < $data["numberOfGlyphs"]; $i++) {
+          $len           = $font->readUInt8();
+          $namesPascal[] = $font->read($len);
+        }
+
+        foreach ($glyphNameIndex as $g => $index) {
+          if ($index < 258) {
+            $names[$g] = File::$macCharNames[$index];
+          }
+          else {
+            if (array_key_exists($index - 258, $namesPascal)) {
+              $names[$g] = $namesPascal[$index - 258];
+            }
+          }
+        }
+
+        break;
+
+      case 2.5:
+        // TODO
+        break;
+
+      case 3:
+        // nothing
+        break;
+
+      case 4:
+        // TODO
+        break;
+    }
+
+    $data["names"] = $names;
+
+    $this->data = $data;
+  }
+
+  function _encode() {
+    $font           = $this->getFont();
+    $data           = $this->data;
+    $data["format"] = 3;
+
+    $length = $font->pack($this->def, $data);
+
+    return $length;
+    /*
+    $subset = $font->getSubset();
+
+    switch($data["format"]) {
+      case 1:
+        // nothing to do
+      break;
+
+      case 2:
+        $old_names = $data["names"];
+
+        $glyphNameIndex = range(0, count($subset));
+
+        $names = array();
+        foreach($subset as $gid) {
+          $names[] = $data["names"][$data["glyphNameIndex"][$gid]];
+        }
+
+        $numberOfGlyphs = count($names);
+        $length += $font->writeUInt16($numberOfGlyphs);
+
+        foreach($glyphNameIndex as $gni) {
+          $length += $font->writeUInt16($gni);
+        }
+
+        //$names = array_slice($names, 257);
+        foreach($names as $name) {
+          $len = strlen($name);
+          $length += $font->writeUInt8($len);
+          $length += $font->write($name, $len);
+        }
+
+      break;
+
+      case 2.5:
+        // TODO
+      break;
+
+      case 3:
+        // nothing
+      break;
+
+      case 4:
+        // TODO
+      break;
+    }
+
+    return $length;*/
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/TrueType/Collection.php b/notifications/dompdf/lib/php-font-lib/TrueType/Collection.php
new file mode 100644
index 00000000..460ef4da
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/TrueType/Collection.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\TrueType;
+
+use Countable;
+use FontLib\BinaryStream;
+use Iterator;
+use OutOfBoundsException;
+
+/**
+ * TrueType collection font file.
+ *
+ * @package php-font-lib
+ */
+class Collection extends BinaryStream implements Iterator, Countable {
+  /**
+   * Current iterator position.
+   *
+   * @var integer
+   */
+  private $position = 0;
+
+  protected $collectionOffsets = array();
+  protected $collection = array();
+  protected $version;
+  protected $numFonts;
+
+  function parse() {
+    if (isset($this->numFonts)) {
+      return;
+    }
+
+    $this->read(4); // tag name
+
+    $this->version  = $this->readFixed();
+    $this->numFonts = $this->readUInt32();
+
+    for ($i = 0; $i < $this->numFonts; $i++) {
+      $this->collectionOffsets[] = $this->readUInt32();
+    }
+  }
+
+  /**
+   * @param int $fontId
+   *
+   * @throws OutOfBoundsException
+   * @return File
+   */
+  function getFont($fontId) {
+    $this->parse();
+
+    if (!isset($this->collectionOffsets[$fontId])) {
+      throw new OutOfBoundsException();
+    }
+
+    if (isset($this->collection[$fontId])) {
+      return $this->collection[$fontId];
+    }
+
+    $font    = new File();
+    $font->f = $this->f;
+    $font->setTableOffset($this->collectionOffsets[$fontId]);
+
+    return $this->collection[$fontId] = $font;
+  }
+
+  function current() {
+    return $this->getFont($this->position);
+  }
+
+  function key() {
+    return $this->position;
+  }
+
+  function next() {
+    return ++$this->position;
+  }
+
+  function rewind() {
+    $this->position = 0;
+  }
+
+  function valid() {
+    $this->parse();
+
+    return isset($this->collectionOffsets[$this->position]);
+  }
+
+  function count() {
+    $this->parse();
+
+    return $this->numFonts;
+  }
+}
diff --git a/notifications/dompdf/lib/php-font-lib/TrueType/File.php b/notifications/dompdf/lib/php-font-lib/TrueType/File.php
new file mode 100644
index 00000000..3594479a
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/TrueType/File.php
@@ -0,0 +1,471 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\TrueType;
+
+use FontLib\AdobeFontMetrics;
+use FontLib\Font;
+use FontLib\BinaryStream;
+use FontLib\Table\Table;
+use FontLib\Table\DirectoryEntry;
+use FontLib\Table\Type\glyf;
+use FontLib\Table\Type\name;
+use FontLib\Table\Type\nameRecord;
+
+/**
+ * TrueType font file.
+ *
+ * @package php-font-lib
+ */
+class File extends BinaryStream {
+  /**
+   * @var Header
+   */
+  public $header = array();
+
+  private $tableOffset = 0; // Used for TTC
+
+  private static $raw = false;
+
+  protected $directory = array();
+  protected $data = array();
+
+  protected $glyph_subset = array();
+
+  public $glyph_all = array();
+
+  static $macCharNames = array(
+    ".notdef", ".null", "CR",
+    "space", "exclam", "quotedbl", "numbersign",
+    "dollar", "percent", "ampersand", "quotesingle",
+    "parenleft", "parenright", "asterisk", "plus",
+    "comma", "hyphen", "period", "slash",
+    "zero", "one", "two", "three",
+    "four", "five", "six", "seven",
+    "eight", "nine", "colon", "semicolon",
+    "less", "equal", "greater", "question",
+    "at", "A", "B", "C", "D", "E", "F", "G",
+    "H", "I", "J", "K", "L", "M", "N", "O",
+    "P", "Q", "R", "S", "T", "U", "V", "W",
+    "X", "Y", "Z", "bracketleft",
+    "backslash", "bracketright", "asciicircum", "underscore",
+    "grave", "a", "b", "c", "d", "e", "f", "g",
+    "h", "i", "j", "k", "l", "m", "n", "o",
+    "p", "q", "r", "s", "t", "u", "v", "w",
+    "x", "y", "z", "braceleft",
+    "bar", "braceright", "asciitilde", "Adieresis",
+    "Aring", "Ccedilla", "Eacute", "Ntilde",
+    "Odieresis", "Udieresis", "aacute", "agrave",
+    "acircumflex", "adieresis", "atilde", "aring",
+    "ccedilla", "eacute", "egrave", "ecircumflex",
+    "edieresis", "iacute", "igrave", "icircumflex",
+    "idieresis", "ntilde", "oacute", "ograve",
+    "ocircumflex", "odieresis", "otilde", "uacute",
+    "ugrave", "ucircumflex", "udieresis", "dagger",
+    "degree", "cent", "sterling", "section",
+    "bullet", "paragraph", "germandbls", "registered",
+    "copyright", "trademark", "acute", "dieresis",
+    "notequal", "AE", "Oslash", "infinity",
+    "plusminus", "lessequal", "greaterequal", "yen",
+    "mu", "partialdiff", "summation", "product",
+    "pi", "integral", "ordfeminine", "ordmasculine",
+    "Omega", "ae", "oslash", "questiondown",
+    "exclamdown", "logicalnot", "radical", "florin",
+    "approxequal", "increment", "guillemotleft", "guillemotright",
+    "ellipsis", "nbspace", "Agrave", "Atilde",
+    "Otilde", "OE", "oe", "endash",
+    "emdash", "quotedblleft", "quotedblright", "quoteleft",
+    "quoteright", "divide", "lozenge", "ydieresis",
+    "Ydieresis", "fraction", "currency", "guilsinglleft",
+    "guilsinglright", "fi", "fl", "daggerdbl",
+    "periodcentered", "quotesinglbase", "quotedblbase", "perthousand",
+    "Acircumflex", "Ecircumflex", "Aacute", "Edieresis",
+    "Egrave", "Iacute", "Icircumflex", "Idieresis",
+    "Igrave", "Oacute", "Ocircumflex", "applelogo",
+    "Ograve", "Uacute", "Ucircumflex", "Ugrave",
+    "dotlessi", "circumflex", "tilde", "macron",
+    "breve", "dotaccent", "ring", "cedilla",
+    "hungarumlaut", "ogonek", "caron", "Lslash",
+    "lslash", "Scaron", "scaron", "Zcaron",
+    "zcaron", "brokenbar", "Eth", "eth",
+    "Yacute", "yacute", "Thorn", "thorn",
+    "minus", "multiply", "onesuperior", "twosuperior",
+    "threesuperior", "onehalf", "onequarter", "threequarters",
+    "franc", "Gbreve", "gbreve", "Idot",
+    "Scedilla", "scedilla", "Cacute", "cacute",
+    "Ccaron", "ccaron", "dmacron"
+  );
+
+  function getTable() {
+    $this->parseTableEntries();
+
+    return $this->directory;
+  }
+
+  function setTableOffset($offset) {
+    $this->tableOffset = $offset;
+  }
+
+  function parse() {
+    $this->parseTableEntries();
+
+    $this->data = array();
+
+    foreach ($this->directory as $tag => $table) {
+      if (empty($this->data[$tag])) {
+        $this->readTable($tag);
+      }
+    }
+  }
+
+  function utf8toUnicode($str) {
+    $len = mb_strlen($str, '8bit');
+    $out = array();
+
+    for ($i = 0; $i < $len; $i++) {
+      $uni = -1;
+      $h   = ord($str[$i]);
+
+      if ($h <= 0x7F) {
+        $uni = $h;
+      }
+      elseif ($h >= 0xC2) {
+        if (($h <= 0xDF) && ($i < $len - 1)) {
+          $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);
+        }
+        elseif (($h <= 0xEF) && ($i < $len - 2)) {
+          $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
+        }
+        elseif (($h <= 0xF4) && ($i < $len - 3)) {
+          $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
+        }
+      }
+
+      if ($uni >= 0) {
+        $out[] = $uni;
+      }
+    }
+
+    return $out;
+  }
+
+  function getUnicodeCharMap() {
+    $subtable = null;
+    foreach ($this->getData("cmap", "subtables") as $_subtable) {
+      if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
+        $subtable = $_subtable;
+        break;
+      }
+    }
+
+    if ($subtable) {
+      return $subtable["glyphIndexArray"];
+    }
+
+    return null;
+  }
+
+  function setSubset($subset) {
+    if (!is_array($subset)) {
+      $subset = $this->utf8toUnicode($subset);
+    }
+
+    $subset = array_unique($subset);
+
+    $glyphIndexArray = $this->getUnicodeCharMap();
+
+    if (!$glyphIndexArray) {
+      return;
+    }
+
+    $gids = array(
+      0, // .notdef
+      1, // .null
+    );
+
+    foreach ($subset as $code) {
+      if (!isset($glyphIndexArray[$code])) {
+        continue;
+      }
+
+      $gid        = $glyphIndexArray[$code];
+      $gids[$gid] = $gid;
+    }
+
+    /** @var glyf $glyf */
+    $glyf = $this->getTableObject("glyf");
+    $gids = $glyf->getGlyphIDs($gids);
+
+    sort($gids);
+
+    $this->glyph_subset = $gids;
+    $this->glyph_all    = array_values($glyphIndexArray); // FIXME
+  }
+
+  function getSubset() {
+    if (empty($this->glyph_subset)) {
+      return $this->glyph_all;
+    }
+
+    return $this->glyph_subset;
+  }
+
+  function encode($tags = array()) {
+    if (!self::$raw) {
+      $tags = array_merge(array("head", "hhea", "cmap", "hmtx", "maxp", "glyf", "loca", "name", "post"), $tags);
+    }
+    else {
+      $tags = array_keys($this->directory);
+    }
+
+    $num_tables = count($tags);
+    $n          = 16; // @todo
+
+    Font::d("Tables : " . implode(", ", $tags));
+
+    /** @var DirectoryEntry[] $entries */
+    $entries = array();
+    foreach ($tags as $tag) {
+      if (!isset($this->directory[$tag])) {
+        Font::d("  >> '$tag' table doesn't exist");
+        continue;
+      }
+
+      $entries[$tag] = $this->directory[$tag];
+    }
+
+    $this->header->data["numTables"] = $num_tables;
+    $this->header->encode();
+
+    $directory_offset = $this->pos();
+    $offset           = $directory_offset + $num_tables * $n;
+    $this->seek($offset);
+
+    $i = 0;
+    foreach ($entries as $entry) {
+      $entry->encode($directory_offset + $i * $n);
+      $i++;
+    }
+  }
+
+  function parseHeader() {
+    if (!empty($this->header)) {
+      return;
+    }
+
+    $this->seek($this->tableOffset);
+
+    $this->header = new Header($this);
+    $this->header->parse();
+  }
+
+  function getFontType(){
+    $class_parts = explode("\\", get_class($this));
+    return $class_parts[1];
+  }
+
+  function parseTableEntries() {
+    $this->parseHeader();
+
+    if (!empty($this->directory)) {
+      return;
+    }
+
+    if (empty($this->header->data["numTables"])) {
+      return;
+    }
+
+
+    $type = $this->getFontType();
+    $class = "FontLib\\$type\\TableDirectoryEntry";
+
+    for ($i = 0; $i < $this->header->data["numTables"]; $i++) {
+      /** @var TableDirectoryEntry $entry */
+      $entry = new $class($this);
+      $entry->parse();
+
+      $this->directory[$entry->tag] = $entry;
+    }
+  }
+
+  function normalizeFUnit($value, $base = 1000) {
+    return round($value * ($base / $this->getData("head", "unitsPerEm")));
+  }
+
+  protected function readTable($tag) {
+    $this->parseTableEntries();
+
+    if (!self::$raw) {
+      $name_canon = preg_replace("/[^a-z0-9]/", "", strtolower($tag));
+
+      $class = "FontLib\\Table\\Type\\$name_canon";
+
+      if (!isset($this->directory[$tag]) || !@class_exists($class)) {
+        return;
+      }
+    }
+    else {
+      $class = "FontLib\\Table\\Table";
+    }
+
+    /** @var Table $table */
+    $table = new $class($this->directory[$tag]);
+    $table->parse();
+
+    $this->data[$tag] = $table;
+  }
+
+  /**
+   * @param $name
+   *
+   * @return Table
+   */
+  public function getTableObject($name) {
+    return $this->data[$name];
+  }
+
+  public function setTableObject($name, Table $data) {
+    $this->data[$name] = $data;
+  }
+
+  public function getData($name, $key = null) {
+    $this->parseTableEntries();
+
+    if (empty($this->data[$name])) {
+      $this->readTable($name);
+    }
+
+    if (!isset($this->data[$name])) {
+      return null;
+    }
+
+    if (!$key) {
+      return $this->data[$name]->data;
+    }
+    else {
+      return $this->data[$name]->data[$key];
+    }
+  }
+
+  function addDirectoryEntry(DirectoryEntry $entry) {
+    $this->directory[$entry->tag] = $entry;
+  }
+
+  function saveAdobeFontMetrics($file, $encoding = null) {
+    $afm = new AdobeFontMetrics($this);
+    $afm->write($file, $encoding);
+  }
+
+  /**
+   * Get a specific name table string value from its ID
+   *
+   * @param int $nameID The name ID
+   *
+   * @return string|null
+   */
+  function getNameTableString($nameID) {
+    /** @var nameRecord[] $records */
+    $records = $this->getData("name", "records");
+
+    if (!isset($records[$nameID])) {
+      return null;
+    }
+
+    return $records[$nameID]->string;
+  }
+
+  /**
+   * Get font copyright
+   *
+   * @return string|null
+   */
+  function getFontCopyright() {
+    return $this->getNameTableString(name::NAME_COPYRIGHT);
+  }
+
+  /**
+   * Get font name
+   *
+   * @return string|null
+   */
+  function getFontName() {
+    return $this->getNameTableString(name::NAME_NAME);
+  }
+
+  /**
+   * Get font subfamily
+   *
+   * @return string|null
+   */
+  function getFontSubfamily() {
+    return $this->getNameTableString(name::NAME_SUBFAMILY);
+  }
+
+  /**
+   * Get font subfamily ID
+   *
+   * @return string|null
+   */
+  function getFontSubfamilyID() {
+    return $this->getNameTableString(name::NAME_SUBFAMILY_ID);
+  }
+
+  /**
+   * Get font full name
+   *
+   * @return string|null
+   */
+  function getFontFullName() {
+    return $this->getNameTableString(name::NAME_FULL_NAME);
+  }
+
+  /**
+   * Get font version
+   *
+   * @return string|null
+   */
+  function getFontVersion() {
+    return $this->getNameTableString(name::NAME_VERSION);
+  }
+
+  /**
+   * Get font weight
+   *
+   * @return string|null
+   */
+  function getFontWeight() {
+    return $this->getTableObject("OS/2")->data["usWeightClass"];
+  }
+
+  /**
+   * Get font Postscript name
+   *
+   * @return string|null
+   */
+  function getFontPostscriptName() {
+    return $this->getNameTableString(name::NAME_POSTSCRIPT_NAME);
+  }
+
+  function reduce() {
+    $names_to_keep = array(
+      name::NAME_COPYRIGHT,
+      name::NAME_NAME,
+      name::NAME_SUBFAMILY,
+      name::NAME_SUBFAMILY_ID,
+      name::NAME_FULL_NAME,
+      name::NAME_VERSION,
+      name::NAME_POSTSCRIPT_NAME,
+    );
+
+    foreach ($this->data["name"]->data["records"] as $id => $rec) {
+      if (!in_array($id, $names_to_keep)) {
+        unset($this->data["name"]->data["records"][$id]);
+      }
+    }
+  }
+}
diff --git a/notifications/dompdf/lib/php-font-lib/TrueType/Header.php b/notifications/dompdf/lib/php-font-lib/TrueType/Header.php
new file mode 100644
index 00000000..7ff79cce
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/TrueType/Header.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\TrueType;
+
+/**
+ * TrueType font file header.
+ *
+ * @package php-font-lib
+ */
+class Header extends \FontLib\Header {
+  protected $def = array(
+    "format"        => self::uint32,
+    "numTables"     => self::uint16,
+    "searchRange"   => self::uint16,
+    "entrySelector" => self::uint16,
+    "rangeShift"    => self::uint16,
+  );
+
+  public function parse() {
+    parent::parse();
+
+    $format                   = $this->data["format"];
+    $this->data["formatText"] = $this->convertUInt32ToStr($format);
+  }
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/TrueType/TableDirectoryEntry.php b/notifications/dompdf/lib/php-font-lib/TrueType/TableDirectoryEntry.php
new file mode 100644
index 00000000..fc4fe555
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/TrueType/TableDirectoryEntry.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\TrueType;
+
+use FontLib\Table\DirectoryEntry;
+
+/**
+ * TrueType table directory entry.
+ *
+ * @package php-font-lib
+ */
+class TableDirectoryEntry extends DirectoryEntry {
+  function __construct(File $font) {
+    parent::__construct($font);
+  }
+
+  function parse() {
+    parent::parse();
+
+    $font           = $this->font;
+    $this->checksum = $font->readUInt32();
+    $this->offset   = $font->readUInt32();
+    $this->length   = $font->readUInt32();
+    $this->entryLength += 12;
+  }
+}
+
diff --git a/notifications/dompdf/lib/php-font-lib/WOFF/File.php b/notifications/dompdf/lib/php-font-lib/WOFF/File.php
new file mode 100644
index 00000000..4668c239
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/WOFF/File.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\WOFF;
+
+use FontLib\Table\DirectoryEntry;
+
+/**
+ * WOFF font file.
+ *
+ * @package php-font-lib
+ *
+ * @property TableDirectoryEntry[] $directory
+ */
+class File extends \FontLib\TrueType\File {
+  function parseHeader() {
+    if (!empty($this->header)) {
+      return;
+    }
+
+    $this->header = new Header($this);
+    $this->header->parse();
+  }
+
+  public function load($file) {
+    parent::load($file);
+
+    $this->parseTableEntries();
+    $dataOffset = $this->pos() + count($this->directory) * 20;
+
+    $fw = $this->getTempFile(false);
+    $fr = $this->f;
+
+    $this->f = $fw;
+    $offset  = $this->header->encode();
+
+    foreach ($this->directory as $entry) {
+      // Read ...
+      $this->f = $fr;
+      $this->seek($entry->offset);
+      $data = $this->read($entry->length);
+
+      if ($entry->length < $entry->origLength) {
+        $data = (string) gzuncompress($data);
+      }
+
+      // Prepare data ...
+      $length        = mb_strlen($data, '8bit');
+      $entry->length = $entry->origLength = $length;
+      $entry->offset = $dataOffset;
+
+      // Write ...
+      $this->f = $fw;
+
+      // Woff Entry
+      $this->seek($offset);
+      $offset += $this->write($entry->tag, 4); // tag
+      $offset += $this->writeUInt32($dataOffset); // offset
+      $offset += $this->writeUInt32($length); // length
+      $offset += $this->writeUInt32($length); // origLength
+      $offset += $this->writeUInt32(DirectoryEntry::computeChecksum($data)); // checksum
+
+      // Data
+      $this->seek($dataOffset);
+      $dataOffset += $this->write($data, $length);
+    }
+
+    $this->f = $fw;
+    $this->seek(0);
+
+    // Need to re-parse this, don't know why
+    $this->header    = null;
+    $this->directory = array();
+    $this->parseTableEntries();
+  }
+}
diff --git a/notifications/dompdf/lib/php-font-lib/WOFF/Header.php b/notifications/dompdf/lib/php-font-lib/WOFF/Header.php
new file mode 100644
index 00000000..65a6f147
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/WOFF/Header.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\WOFF;
+
+/**
+ * WOFF font file header.
+ *
+ * @package php-font-lib
+ */
+class Header extends \FontLib\TrueType\Header {
+  protected $def = array(
+    "format"         => self::uint32,
+    "flavor"         => self::uint32,
+    "length"         => self::uint32,
+    "numTables"      => self::uint16,
+    self::uint16,
+    "totalSfntSize"  => self::uint32,
+    "majorVersion"   => self::uint16,
+    "minorVersion"   => self::uint16,
+    "metaOffset"     => self::uint32,
+    "metaLength"     => self::uint32,
+    "metaOrigLength" => self::uint32,
+    "privOffset"     => self::uint32,
+    "privLength"     => self::uint32,
+  );
+}
\ No newline at end of file
diff --git a/notifications/dompdf/lib/php-font-lib/WOFF/TableDirectoryEntry.php b/notifications/dompdf/lib/php-font-lib/WOFF/TableDirectoryEntry.php
new file mode 100644
index 00000000..eb67c9c4
--- /dev/null
+++ b/notifications/dompdf/lib/php-font-lib/WOFF/TableDirectoryEntry.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\WOFF;
+
+use FontLib\Table\DirectoryEntry;
+
+/**
+ * WOFF font file table directory entry.
+ *
+ * @package php-font-lib
+ */
+class TableDirectoryEntry extends DirectoryEntry {
+  public $origLength;
+
+  function __construct(File $font) {
+    parent::__construct($font);
+  }
+
+  function parse() {
+    parent::parse();
+
+    $font             = $this->font;
+    $this->offset     = $font->readUInt32();
+    $this->length     = $font->readUInt32();
+    $this->origLength = $font->readUInt32();
+    $this->checksum   = $font->readUInt32();
+  }
+}
diff --git a/notifications/processNotificationBatchDetails.php b/notifications/processNotificationBatchDetails.php
index 586f8ce6..da2f7ac4 100644
--- a/notifications/processNotificationBatchDetails.php
+++ b/notifications/processNotificationBatchDetails.php
@@ -1,6 +1,6 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
  * Copyright (C) 2007 - Liran Tal <liran@enginx.com> All Rights Reserved.
  *
@@ -13,135 +13,108 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
-	//include the dompdf class
-	require_once("dompdf/dompdf_config.inc.php");
 
-	//include the Pear Mail classes for sending out emails
-	@require_once('Mail.php');
-	@require_once('Mail/mime.php');
-	
-	$base = dirname(__FILE__);
-	
-	/**
-	 * createBatchDetailsNotification()
-	 * wrapper-function to create the notification to the customer
-	 * 
-	 * @param		array			customer information array
-	 */
-	function createBatchDetailsNotification($customerInfo) {
+    // prevent this file to be directly accessed
+    if (strpos($_SERVER['PHP_SELF'], '/notifications/processNotificationBatchDetails.php') !== false) {
+        header("Location: ../index.php");
+        exit;
+    }
 
-		global $base;
-		
-		$html = prepareNotificationTemplate($customerInfo);
-		$pdfDocument = createPDF($html);
-		
-		return $pdfDocument;
-		
-	}
+    // common notification functions
+    include("common.php");
+    
+    $base = dirname(__FILE__);
+    $base_path = "$base/templates/";
+    
+    /**
+     * createBatchDetailsNotification()
+     * wrapper-function to create the notification to the customer
+     * 
+     * @param        array        customer information array
+     */
+    function createBatchDetailsNotification($customerInfo) {
+        global $base_path;
+        
+        $html = prepareNotificationTemplate($customerInfo);
+        return createPDF($html, $base_path);
+    }
 
-	
-	/**
-	 * emailNotification()
-	 * creates an email message with the pdf and sends it
-	 * 
-	 * @param		string			pdf binary/string stream
-	 * @param		array			customer information array
-	 * @param		array			smtp server information
-	 * @param		string			from email address of the sender identity
-	 */
-	function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
+    
+    /**
+     * emailNotification()
+     * creates an email message with the pdf and sends it
+     * 
+     * @param        string        pdf binary/string stream
+     * @param        array         customer information array
+     * @param        array         smtp server information
+     * @param        string        from email address of the sender identity
+     */
+    function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
+        $sendTo = $customerInfo['business_email'];
+        $subject = "Business Pre-Paid Batch Information";
+        $body = "Notification letter of service";
+        $attachmentName = "notification.pdf";
+        
+        return send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName);
+    }
+    
+    
+    /**
+     * prepareNotificationTemplate()
+     * reads the notification (html) template and returns the formatted (html) notification
+     * 
+     * @param         array         Array of notification information
+     * @return        string        HTML notification
+     */
+    function prepareNotificationTemplate($customerInfo) {
+        global $base_path;
+        
+        // the HTML template
+        $notification_template = $base_path . "/batch_details.html";
+        
+        $notification_html_template = file_get_contents($notification_template);
+        
+        $date = date("Y-m-d");
+        
+        $business_name = $customerInfo['business_name'];
+        $business_owner_name = $customerInfo['business_owner_name']; 
+        $business_address = $customerInfo['business_address'];
+        $business_phone = $customerInfo['business_phone'];
+        $business_email = $customerInfo['business_email'];
+        $business_web = $customerInfo['business_web'];
+        $batch_details = $customerInfo['batch_details'];
+        $batch_active_users = $customerInfo['batch_active_users'];
+        $service_plan_info = $customerInfo['service_plan_info'];
+    
+        $tagname_value_mappings = array(
+                                            "__INVOICE_CREATION_DATE__" => $date,
+                                            "__BUSINESS_NAME__" => $business_name,
+                                            "__BUSINESS_OWNER_NAME__" => $business_owner_name,
+                                            "__BUSINESS_ADDRESS__" => $business_address,
+                                            "__BUSINESS_PHONE__" => $business_phone,
+                                            "__BUSINESS_EMAIL__" => $business_email,
+                                            "__BUSINESS_WEB__" => $business_web,
+                                            "__BATCH_DETAILS__" => $batch_details,
+                                            "__BATCH_ACTIVE_USERS__" => $batch_active_users,
+                                            "__SERVICE_PLAN_INFO__" => $service_plan_info,
+                                       );
+    
+        foreach ($tagname_value_mappings as $tagname => $value) {
+            $tag = sprintf("####%s####", $tagname);
+            $notification_html_template = str_replace($tag, $value, $notification_html_template);
+        }
 
-		global $base;
-		
-		if (empty($customerInfo['business_email']))
-			return;
-		
-		$headers = array(	"From"	=>	$from, 
-							"Subject"	=>	"Business Pre-Paid Batch Information",
-							"Reply-To"=> $from
-					);
-				
-		$mime = new Mail_mime();
-		$mime->setTXTBody("Notification letter of service"); 
-		$mime->addAttachment($pdfDocument, "application/pdf", "notification.pdf", false, 'base64');
-		$body = $mime->get();
-		$headers = $mime->headers($headers);
-		$mail =& Mail::factory("smtp", $smtpInfo);
-		$mail->send($customerInfo['business_email'], $headers, $body);
-	
-	}
-	
-	
-	/**
-	 * prepareNotificationTemplate()
-	 * reads the notification (html) template and returns the formatted (html) notification
-	 * 
-	 * @param		array			Array of notification information
-	 * @return		string			HTML notification
-	 */
-	function prepareNotificationTemplate($customerInfo) {
-	
-		global $base;
-		
-		// the HTML template
-		$notification_template = "$base/templates/batch_details.html";
-		$notification_html_template = file_get_contents($notification_template);
-	
-		$date = date("Y-m-d");
-		
-		$business_name = $customerInfo['business_name'];
-		$business_owner_name = $customerInfo['business_owner_name']; 
-		$business_address = $customerInfo['business_address'];
-		$business_phone = $customerInfo['business_phone'];
-		$business_email = $customerInfo['business_email'];
-		$business_web = $customerInfo['business_web'];
-		$batch_details = $customerInfo['batch_details'];
-		$batch_active_users = $customerInfo['batch_active_users'];
-		$service_plan_info = $customerInfo['service_plan_info'];
-	
-		$notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
-		
-		$notification_html_template = str_replace("####__BUSINESS_NAME__####", $business_name, $notification_html_template);
-		$notification_html_template = str_replace("####__BUSINESS_OWNER_NAME__####", $business_owner_name, $notification_html_template);
-		$notification_html_template = str_replace("####__BUSINESS_ADDRESS__####", $business_address, $notification_html_template);
-		$notification_html_template = str_replace("####__BUSINESS_PHONE__####", $business_phone, $notification_html_template);
-		$notification_html_template = str_replace("####__BUSINESS_EMAIL__####", $business_email, $notification_html_template);
-		$notification_html_template = str_replace("####__BUSINESS_WEB__####", $business_web, $notification_html_template);
-		$notification_html_template = str_replace("####__BATCH_DETAILS__####", $batch_details, $notification_html_template);
-		$notification_html_template = str_replace("####__BATCH_ACTIVE_USERS__####", $batch_active_users, $notification_html_template);
-		$notification_html_template = str_replace("####__SERVICE_PLAN_INFO__####", $service_plan_info, $notification_html_template);
-		
+        // this fix has been set in place according to this thread:
+        // https://stackoverflow.com/questions/37521775/dompdf-error-no-block-level-parent-found-not-good
+        return str_replace("\n", "", $notification_html_template);
+    }
 
-		return $notification_html_template;
-	}
-	
-	/**
-	 * createPDF()
-	 * creates a PDF document for a given html file
-	 * 
-	 * @param 		string			the html file to convert to pdf			
-	 * @return 		string			returns the pdf in binary/string stream
-	 */
-	function createPDF($html) {
-	
-		global $base;
-		
-		// instansiate the pdf document
-		$dompdf = new DOMPDF();
-		$dompdf->set_base_path("$base/templates/");
-		$dompdf->load_html($html);
-		$dompdf->render();
-	
-		$notification_pdf = $dompdf->output();
-		
-		return $notification_pdf;
-
-	}
 
 ?>
diff --git a/notifications/processNotificationUserDetailsInvoice.php b/notifications/processNotificationUserDetailsInvoice.php
index f58bec1f..ec5b42c6 100644
--- a/notifications/processNotificationUserDetailsInvoice.php
+++ b/notifications/processNotificationUserDetailsInvoice.php
@@ -1,6 +1,6 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
  * Copyright (C) 2007 - Liran Tal <liran@enginx.com> All Rights Reserved.
  *
@@ -13,40 +13,39 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
-	//include the dompdf class
-	require_once("dompdf/dompdf_config.inc.php");
 
-	//include the Pear Mail classes for sending out emails
-	@require_once('Mail.php');
-	@require_once('Mail/mime.php');
-	
-	$base = dirname(__FILE__);
-	
+    // prevent this file to be directly accessed
+    if (strpos($_SERVER['PHP_SELF'], '/notifications/processNotificationUserDetailsInvoice.php') !== false) {
+        header("Location: ../index.php");
+        exit;
+    }
+    
+    // common notification functions
+    include("common.php");
+    
+    $base = dirname(__FILE__);
+	$base_path = "$base/templates/";
+    
 	/**
 	 * createUserDetailsInvoiceNotification()
 	 * wrapper-function to create the notification to the customer
 	 * 
 	 * @param		array			customer information array
 	 */
 	function createUserDetailsInvoiceNotification($customerInfo) {
-
-		global $base;
-		
-		$html = prepareNotificationTemplate($customerInfo);
-		$pdfDocument = createPDF($html);
-
-		file_put_contents("$base/out4.pdf", $pdfDocument);
-		
-		return $pdfDocument;
-		
+		global $base_path;
+        
+        $html = prepareNotificationTemplate($customerInfo);
+		return createPDF($html, $base_path);
 	}
 
 	
 	/**
 	 * emailNotification()
@@ -56,29 +55,16 @@
 	 * @param		array			customer information array
 	 * @param		array			smtp server information
 	 * @param		string			from email address of the sender identity
 	 */
 	function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
-
-		global $base;
-		
-		if (empty($customerInfo['business_email']))
-			return;
-		
-		$headers = array(	"From"	=>	$from, 
-							"Subject"	=>	"User Invoice Notification",
-							"Reply-To"=> $from
-					);
-				
-		$mime = new Mail_mime();
-		$mime->setTXTBody("Notification letter of service"); 
-		$mime->addAttachment($pdfDocument, "application/pdf", "invoice.pdf", false, 'base64');
-		$body = $mime->get();
-		$headers = $mime->headers($headers);
-		$mail =& Mail::factory("smtp", $smtpInfo);
-		$mail->send($customerInfo['business_email'], $headers, $body);
-	
+        $sendTo = $customerInfo['business_email'];
+        $subject = "User Invoice Notification";
+        $body = "Notification letter of service";
+        $attachmentName = "invoice.pdf";
+        
+        return send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName);
 	}
 	
 	
 	/**
 	 * prepareNotificationTemplate()
@@ -86,15 +72,14 @@
 	 * 
 	 * @param		array			Array of notification information
 	 * @return		string			HTML notification
 	 */
 	function prepareNotificationTemplate($customerInfo) {
-	
-		global $base;
+		global $base_path;
 		
 		// the HTML template
-		$notification_template = "$base/templates/user_invoice_details.html";
+		$notification_template = $base_path . "/user_invoice_details.html";
 		$notification_html_template = file_get_contents($notification_template);
 	
 		$date = date("Y-m-d");
 	
 		$business_name = $customerInfo['business_name']; 
@@ -109,33 +94,9 @@
 		$notification_html_template = str_replace("####__BUSINESS_ADDRESS__####", $business_address, $notification_html_template);
 		$notification_html_template = str_replace("####__BUSINESS_PHONE__####", $business_phone, $notification_html_template);
 		$notification_html_template = str_replace("####__BUSINESS_EMAIL__####", $business_email, $notification_html_template);
 		$notification_html_template = str_replace("####__SERVICE_PLAN_INFO__####", $service_plan_info, $notification_html_template);
 		
-
 		return $notification_html_template;
 	}
-	
-	/**
-	 * createPDF()
-	 * creates a PDF document for a given html file
-	 * 
-	 * @param 		string			the html file to convert to pdf			
-	 * @return 		string			returns the pdf in binary/string stream
-	 */
-	function createPDF($html) {
-	
-		global $base;
-		
-		// instansiate the pdf document
-		$dompdf = new DOMPDF();
-		$dompdf->set_base_path("$base/templates/");
-		$dompdf->load_html($html);
-		$dompdf->render();
-	
-		$notification_pdf = $dompdf->output();
-		
-		return $notification_pdf;
-
-	}
 
 ?>
diff --git a/notifications/processNotificationUserInvoice.php b/notifications/processNotificationUserInvoice.php
index 1e7be81d..30403b4a 100644
--- a/notifications/processNotificationUserInvoice.php
+++ b/notifications/processNotificationUserInvoice.php
@@ -13,211 +13,173 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
- *          Miguel GarcÃ­a <miguelvisgarcia@gmail.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Miguel GarcÃ­a <miguelvisgarcia@gmail.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
-	//include the dompdf class
-	require_once("dompdf/dompdf_config.inc.php");
 
-	//include the Pear Mail classes for sending out emails
-	@require_once('Mail.php');
-	@require_once('Mail/mime.php');
-	
-	$base = dirname(__FILE__);
-	
-	/**
-	 * createNotification()
-	 * wrapper-function to create the notification to the customer
-	 * 
-	 * @param		array			customer information array
-	 * @param		boolean			true for HTML output, false for PDF output
-	 * @return 		string			returns the HTML or PDF output
-	 */
-	function createNotification($customerInfo, $asHTML = false) {
+    // common notification functions
+    include("common.php");
+    
+    $base = dirname(__FILE__);
+    $base_path = "$base/templates/";
+    
+    /**
+     * createNotification()
+     * wrapper-function to create the notification to the customer
+     * 
+     * @param         array          customer information array
+     * @param         boolean        true for HTML output, false for PDF output
+     * 
+     * @return        string         returns the HTML or PDF output
+     */
+    function createNotification($customerInfo, $asHTML = false) {
+        global $base_path;
+        
+        $html = prepareNotificationTemplate($customerInfo);
+        return (($asHTML) ? $html : createPDF($html, $base_path));
+    }
 
-		global $base;
-		
-		$html = prepareNotificationTemplate($customerInfo);
-		
-		if($asHTML) {
-			$document = $html;
-		}
-		else {
-			$document = createPDF($html);
-		}
+    
+    /**
+     * emailNotification()
+     * creates an email message with the pdf and sends it
+     * 
+     * @param        string        pdf binary/string stream
+     * @param        array         customer information array
+     * @param        array         smtp server information
+     * @param        string        from email address of the sender identity
+     */
+    function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
+        $sendTo = $customerInfo['business_email'];
+        $subject = "Invoice Information";
+        $body = "Notification letter of service";
+        $attachmentName = "notification.pdf";
+        
+        return send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName);
+    }
+    
+    
+    /**
+     * prepareNotificationTemplate()
+     * reads the notification (html) template and returns the formatted (html) notification
+     * 
+     * @param         array         Array of notification information
+     * 
+     * @return        string        HTML notification
+     */
+    function prepareNotificationTemplate($customerInfo) {
+        global $base_path, $configValues;
+        
+        $templates_base = $base_path;
+        
+        // the default HTML template - OLD STYLE
+        $notification_template = $templates_base . "user_invoice.html";
+        $notification_item_template = null;
+        
+        // the default HTML template - NEW STYLE
+        if(isset($configValues['CONFIG_INVOICE_TEMPLATE'])) {
+            $notification_template = $templates_base . $configValues['CONFIG_INVOICE_TEMPLATE'];
+            
+            if(isset($configValues['CONFIG_INVOICE_ITEM_TEMPLATE'])) {
+                $notification_item_template = $templates_base . $configValues['CONFIG_INVOICE_ITEM_TEMPLATE'];
+            }
+        }
+        
+        // the default HTML template for current location - NEW STYLE
+        if ((isset($_SESSION['location_name'])) && ($_SESSION['location_name'] != "default")) {
+            $location_name = $_SESSION['location_name'];
+            $location = $configValues['CONFIG_LOCATIONS'][$location_name];
+        
+            if (isset($location['CONFIG_INVOICE_TEMPLATE'])) {
+                $notification_template = $templates_base . $location['CONFIG_INVOICE_TEMPLATE'];
+                
+                if (isset($location['CONFIG_INVOICE_ITEM_TEMPLATE'])) {
+                    $notification_item_template = $templates_base . $location['CONFIG_INVOICE_ITEM_TEMPLATE'];
+                }
+            }
+        }
 
-		return $document;
-	}
+        // load template for invoice
+        $notification_html_template = file_get_contents($notification_template);
+        
+        // load template for each invoice item
+        if ($notification_item_template !== null) {
+            $notification_item_html_template = file_get_contents($notification_item_template);
+        }
 
-	
-	/**
-	 * emailNotification()
-	 * creates an email message with the pdf and sends it
-	 * 
-	 * @param		string			pdf binary/string stream
-	 * @param		array			customer information array
-	 * @param		array			smtp server information
-	 * @param		string			from email address of the sender identity
-	 */
-	function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
+        $date = date("Y-m-d");
+        
+        $name = $customerInfo['customer_name'];
+        $address = $customerInfo['customer_address'];
+        $phone = $customerInfo['customer_phone'];
+        $email = $customerInfo['customer_email'];
+        
+        $invoice_details = $customerInfo['invoice_details'];
+        $invoice_items = $customerInfo['invoice_items'];
+    
+        // notification date
+        $notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
+        
+        // user details
+        $notification_html_template = str_replace("####__CUSTOMER_NAME__####", $name, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_ADDRESS__####", $address, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_PHONE__####", $phone, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_EMAIL__####", $email, $notification_html_template);
+        
+        // invoice information
+        $notification_html_template = str_replace("####__INVOICE_DETAILS__####", $invoice_details, $notification_html_template);
+        $notification_html_template = str_replace("####__INVOICE_ITEMS__####", $invoice_items, $notification_html_template);
+        
+        // customer details - NEW STYLE            
+        $notification_html_template = str_replace("[CustomerId]", $customerInfo['customerId'], $notification_html_template);
+        $notification_html_template = str_replace("[CustomerName]", $customerInfo['customerName'], $notification_html_template);
+        $notification_html_template = str_replace("[CustomerAddress]", $customerInfo['customerAddress'], $notification_html_template);
+        $notification_html_template = str_replace("[CustomerAddress2]", $customerInfo['customerAddress2'], $notification_html_template);
+        $notification_html_template = str_replace("[CustomerPhone]", $customerInfo['customerPhone'], $notification_html_template);
+        $notification_html_template = str_replace("[CustomerEmail]", $customerInfo['customerEmail'], $notification_html_template);
+        $notification_html_template = str_replace("[CustomerContact]", $customerInfo['customerContact'], $notification_html_template);
+        
+        // invoice details - NEW STYLE
+        $notification_html_template = str_replace("[InvoiceNumber]", $customerInfo['invoiceNumber'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoiceDate]", $customerInfo['invoiceDate'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoiceStatus]", $customerInfo['invoiceStatus'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoiceTotalBilled]", $customerInfo['invoiceTotalBilled'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoicePaid]", $customerInfo['invoicePaid'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoiceDue]", $customerInfo['invoiceDue'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoiceNotes]", $customerInfo['invoiceNotes'], $notification_html_template);
+        
+        // invoice items - NEW STYLE
+        if($notification_item_template !== null) {
+            $invoiceItems = '';
+            
+            foreach($customerInfo['invoiceItems'] as $invoiceItem) {
+                $invoiceItemTemplate = $notification_item_html_template;
+        
+                $invoiceItemTemplate = str_replace("[InvoiceItemNumber]", $invoiceItem['invoiceItemNumber'], $invoiceItemTemplate);
+                $invoiceItemTemplate = str_replace("[InvoiceItemPlan]", $invoiceItem['invoiceItemPlan'], $invoiceItemTemplate);
+                $invoiceItemTemplate = str_replace("[InvoiceItemNotes]", $invoiceItem['invoiceItemNotes'], $invoiceItemTemplate);
+                $invoiceItemTemplate = str_replace("[InvoiceItemAmount]", $invoiceItem['invoiceItemAmount'], $invoiceItemTemplate);
+                $invoiceItemTemplate = str_replace("[InvoiceItemTaxAmount]", $invoiceItem['invoiceItemTaxAmount'], $invoiceItemTemplate);
+                $invoiceItemTemplate = str_replace("[InvoiceItemTotalAmount]", $invoiceItem['invoiceItemTotalAmount'], $invoiceItemTemplate);
+                
+                $invoiceItems .= $invoiceItemTemplate;
+            }
 
-		global $base;
-		
-		if (empty($customerInfo['customer_email']))
-			return;
-		
-		$headers = array (
-			"From" => $from, 
-			"Subject" => "Invoice Information",
-			"Reply-To" => $from
-		);
-				
-		$mime = new Mail_mime();
-		$mime->setTXTBody("Notification letter of service"); 
-		$mime->addAttachment($pdfDocument, "application/pdf", "notification.pdf", false, 'base64');
-		$body = $mime->get();
-		$headers = $mime->headers($headers);
-		$mail =& Mail::factory("smtp", $smtpInfo);
-		$mail->send($customerInfo['customer_email'], $headers, $body);
-	}
-	
-	
-	/**
-	 * prepareNotificationTemplate()
-	 * reads the notification (html) template and returns the formatted (html) notification
-	 * 
-	 * @param		array			Array of notification information
-	 * @return		string			HTML notification
-	 */
-	function prepareNotificationTemplate($customerInfo) {
-	
-		global $base;
-		global $configValues;
-		
-		// the default HTML template - OLD STYLE
-		$notification_template = "$base/templates/user_invoice.html";
-		$notification_item_template = null;
-		
-		// the default HTML template - NEW STYLE
-		if(isset($configValues['CONFIG_INVOICE_TEMPLATE'])) {
-			$notification_template = "$base/templates/" . $configValues['CONFIG_INVOICE_TEMPLATE'];
-			
-			if(isset($configValues['CONFIG_INVOICE_ITEM_TEMPLATE'])) {
-				$notification_item_template = "$base/templates/" . $configValues['CONFIG_INVOICE_ITEM_TEMPLATE'];
-			}
-		}
-		
-		// the default HTML template for current location - NEW STYLE
-		if((isset($_SESSION['location_name'])) && ($_SESSION['location_name'] != "default")) {
-			if(isset($configValues['CONFIG_LOCATIONS'][$_SESSION['location_name']]['CONFIG_INVOICE_TEMPLATE'])) {
-				$notification_template = "$base/templates/" . $configValues['CONFIG_LOCATIONS'][$_SESSION['location_name']]['CONFIG_INVOICE_TEMPLATE'];
-				
-				if(isset($configValues['CONFIG_LOCATIONS'][$_SESSION['location_name']]['CONFIG_INVOICE_ITEM_TEMPLATE'])) {
-					$notification_item_template = "$base/templates/" . $configValues['CONFIG_LOCATIONS'][$_SESSION['location_name']]['CONFIG_INVOICE_ITEM_TEMPLATE'];
-				}
-			}
-		}
-
-		// load template for invoice
-		$notification_html_template = file_get_contents($notification_template);
-		
-		// load template for each invoice item
-		if($notification_item_template !== null) {
-			$notification_item_html_template = file_get_contents($notification_item_template);
-		}
-
-		$date = date("Y-m-d");
-		
-		$name = $customerInfo['customer_name'];
-		$address = $customerInfo['customer_address'];
-		$phone = $customerInfo['customer_phone'];
-		$email = $customerInfo['customer_email'];
-		
-		$invoice_details = $customerInfo['invoice_details'];
-		$invoice_items = $customerInfo['invoice_items'];
-	
-		// notification date
-		$notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
-		
-		// user details
-		$notification_html_template = str_replace("####__CUSTOMER_NAME__####", $name, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_ADDRESS__####", $address, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_PHONE__####", $phone, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_EMAIL__####", $email, $notification_html_template);
-		
-		// invoice information
-		$notification_html_template = str_replace("####__INVOICE_DETAILS__####", $invoice_details, $notification_html_template);
-		$notification_html_template = str_replace("####__INVOICE_ITEMS__####", $invoice_items, $notification_html_template);
-		
-		// customer details - NEW STYLE			
-		$notification_html_template = str_replace("[CustomerId]", $customerInfo['customerId'], $notification_html_template);
-		$notification_html_template = str_replace("[CustomerName]", $customerInfo['customerName'], $notification_html_template);
-		$notification_html_template = str_replace("[CustomerAddress]", $customerInfo['customerAddress'], $notification_html_template);
-		$notification_html_template = str_replace("[CustomerAddress2]", $customerInfo['customerAddress2'], $notification_html_template);
-		$notification_html_template = str_replace("[CustomerPhone]", $customerInfo['customerPhone'], $notification_html_template);
-		$notification_html_template = str_replace("[CustomerEmail]", $customerInfo['customerEmail'], $notification_html_template);
-		$notification_html_template = str_replace("[CustomerContact]", $customerInfo['customerContact'], $notification_html_template);
-		
-		// invoice details - NEW STYLE
-		$notification_html_template = str_replace("[InvoiceNumber]", $customerInfo['invoiceNumber'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoiceDate]", $customerInfo['invoiceDate'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoiceStatus]", $customerInfo['invoiceStatus'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoiceTotalBilled]", $customerInfo['invoiceTotalBilled'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoicePaid]", $customerInfo['invoicePaid'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoiceDue]", $customerInfo['invoiceDue'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoiceNotes]", $customerInfo['invoiceNotes'], $notification_html_template);
-		
-		// invoice items - NEW STYLE
-		if($notification_item_template !== null) {
-			$invoiceItems = '';
-			
-			foreach($customerInfo['invoiceItems'] as $invoiceItem) {
-				$invoiceItemTemplate = $notification_item_html_template;
-		
-				$invoiceItemTemplate = str_replace("[InvoiceItemNumber]", $invoiceItem['invoiceItemNumber'], $invoiceItemTemplate);
-				$invoiceItemTemplate = str_replace("[InvoiceItemPlan]", $invoiceItem['invoiceItemPlan'], $invoiceItemTemplate);
-				$invoiceItemTemplate = str_replace("[InvoiceItemNotes]", $invoiceItem['invoiceItemNotes'], $invoiceItemTemplate);
-				$invoiceItemTemplate = str_replace("[InvoiceItemAmount]", $invoiceItem['invoiceItemAmount'], $invoiceItemTemplate);
-				$invoiceItemTemplate = str_replace("[InvoiceItemTaxAmount]", $invoiceItem['invoiceItemTaxAmount'], $invoiceItemTemplate);
-				$invoiceItemTemplate = str_replace("[InvoiceItemTotalAmount]", $invoiceItem['invoiceItemTotalAmount'], $invoiceItemTemplate);
-				
-				$invoiceItems .= $invoiceItemTemplate;
-			}
-
-			$notification_html_template = str_replace("[InvoiceItems]", $invoiceItems, $notification_html_template);
-		}
-		
-		// more invoice details - NEW STYLE
-		$notification_html_template = str_replace("[InvoiceTotalAmount]", $customerInfo['invoiceTotalAmount'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoiceTotalTax]", $customerInfo['invoiceTotalTax'], $notification_html_template);
-		
-		return $notification_html_template;
-	}
-	
-	/**
-	 * createPDF()
-	 * creates a PDF document for a given html file
-	 * 
-	 * @param 		string			the html file to convert to pdf			
-	 * @return 		string			returns the pdf in binary/string stream
-	 */
-	function createPDF($html) {
-	
-		global $base;
-		
-		// instansiate the pdf document
-		$dompdf = new DOMPDF();
-		$dompdf->set_base_path("$base/templates/");
-		$dompdf->load_html($html);
-		$dompdf->render();
-	
-		$notification_pdf = $dompdf->output();
-		
-		return $notification_pdf;
-	}
+            $notification_html_template = str_replace("[InvoiceItems]", $invoiceItems, $notification_html_template);
+        }
+        
+        // more invoice details - NEW STYLE
+        $notification_html_template = str_replace("[InvoiceTotalAmount]", $customerInfo['invoiceTotalAmount'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoiceTotalTax]", $customerInfo['invoiceTotalTax'], $notification_html_template);
+        
+        // this fix has been set in place according to this thread:
+        // https://stackoverflow.com/questions/37521775/dompdf-error-no-block-level-parent-found-not-good
+        return str_replace("\n", "", $notification_html_template);
+    }
 
 ?>
diff --git a/notifications/processNotificationWelcome.php b/notifications/processNotificationWelcome.php
index 3a858b75..6ec0e998 100644
--- a/notifications/processNotificationWelcome.php
+++ b/notifications/processNotificationWelcome.php
@@ -1,6 +1,6 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
  * Copyright (C) 2007 - Liran Tal <liran@enginx.com> All Rights Reserved.
  *
@@ -13,107 +13,72 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
-	//include the dompdf class
-	require_once("dompdf/dompdf_config.inc.php");
 
-	//include the Pear Mail classes for sending out emails
-	@require_once('Mail.php');
-	@require_once('Mail/mime.php');
-	
-	$base = dirname(__FILE__);
-	
-	/**
-	 * sendWelcomeNotification()
-	 * wrapper-function to send notification to the customer
-	 * 
-	 * @param		array			customer information array
-	 * @param		array			smtp server information
-	 * @param		string			from email address of the sender identity
-	 */
-	function sendWelcomeNotification($customerInfo, $smtpInfo, $from) {
+    // prevent this file to be directly accessed
+    if (strpos($_SERVER['PHP_SELF'], '/notifications/processNotificationWelcome.php') !== false) {
+        header("Location: ../index.php");
+        exit;
+    }
+    
+    // common notification functions
+    include("common.php");
+    
+    $base = dirname(__FILE__);
+    $base_path = "$base/templates/";
+    
+    /**
+     * sendWelcomeNotification()
+     * wrapper-function to send notification to the customer
+     * 
+     * @param        array         customer information array
+     * @param        array         smtp server information
+     * @param        string        from email address of the sender identity
+     */
+    function sendWelcomeNotification($customerInfo, $smtpInfo, $from) {
+        $sendTo = $customerInfo['business_email'];
+        $subject = "Welcome new customer!";
+        $body = "Notification letter of service";
+        $attachmentName = "notification.pdf";
+        
+        return send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName);
+    }
 
-		global $base;
-		
-		if (empty($customerInfo['customer_email']))
-			return;
-		
-		$headers = array(	"From"	=>	$from, 
-							"Subject"	=>	"Welcome new customer!",
-							"Reply-To"=> $from
-					);
-		
-		$html = prepareNotificationTemplate($customerInfo);
-		$pdfDocument = createPDF($html);
-		
-		$mime = new Mail_mime(); 
-		$mime->setTXTBody("Notification letter of service"); 
-		$mime->addAttachment($pdfDocument, "application/pdf", "notification.pdf", false, 'base64');
-		$body = $mime->get();
-		$headers = $mime->headers($headers);
-		$mail =& Mail::factory("smtp", $smtpInfo);
-		$mail->send($customerInfo['customer_email'], $headers, $body);		
-		
-	}
+    
+    /**
+     * prepareNotificationTemplate()
+     * reads the notification (html) template and returns the formatted (html) notification
+     * 
+     * @param         array         Array of notification information
+     * @return        string        HTML notification
+     */
+    function prepareNotificationTemplate($customerInfo) {
+        global $base_path;
+        
+        // the HTML template
+        $notification_template = $base_path . "welcome.html";
+        $notification_html_template = file_get_contents($notification_template);
+    
+        $date = date("Y-m-d");
+    
+        $customer_name = $customerInfo['customer_name'];
+        $customer_address = $customerInfo['customer_address']; 
+        $customer_phone = $customerInfo['customer_phone'];
+        $customer_email = $customerInfo['customer_email'];
+    
+        $notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_NAME__####", $customer_name, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_ADDRESS__####", $customer_address, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_PHONE__####", $customer_phone, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_EMAIL__####", $customer_email, $notification_html_template);
 
-	
-	/**
-	 * prepareNotificationTemplate()
-	 * reads the notification (html) template and returns the formatted (html) notification
-	 * 
-	 * @param		array			Array of notification information
-	 * @return		string			HTML notification
-	 */
-	function prepareNotificationTemplate($customerInfo) {
-	
-		global $base;
-		
-		// the HTML template
-		$notification_template = "$base/templates/welcome.html";
-		$notification_html_template = file_get_contents($notification_template);
-	
-		$date = date("Y-m-d");
-	
-		$customer_name = $customerInfo['customer_name'];
-		$customer_address = $customerInfo['customer_address']; 
-		$customer_phone = $customerInfo['customer_phone'];
-		$customer_email = $customerInfo['customer_email'];
-	
-		$notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_NAME__####", $customer_name, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_ADDRESS__####", $customer_address, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_PHONE__####", $customer_phone, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_EMAIL__####", $customer_email, $notification_html_template);
-
-		return $notification_html_template;
-	}
-	
-	/**
-	 * createPDF()
-	 * creates a PDF document for a given html file
-	 * 
-	 * @param 		string			the html file to convert to pdf			
-	 * @return 		string			returns the pdf in binary/string stream
-	 */
-	function createPDF($html) {
-	
-		global $base;
-		
-		// instansiate the pdf document
-		$dompdf = new DOMPDF();
-		$dompdf->set_base_path("$base/templates/");
-		$dompdf->load_html($html);
-		$dompdf->render();
-	
-		$notification_pdf = $dompdf->output();
-		
-		return $notification_pdf;
-
-	}
+        return $notification_html_template;
+    }
 
 ?>
diff --git a/rep-batch-details.php b/rep-batch-details.php
index ab2cdfb7..f979a7dc 100644
--- a/rep-batch-details.php
+++ b/rep-batch-details.php
@@ -14,10 +14,11 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
  *********************************************************************************************************
  *
  * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
 
     include("library/checklogin.php");
@@ -37,10 +38,13 @@
 
     $log = "visited page: ";
     $logQuery = "performed query for batch [$batch_name] on page: ";
     $logDebugSQL = "";
 
+    // set session's page variable
+    $_SESSION['PREV_LIST_PAGE'] = $_SERVER['REQUEST_URI'];
+
     include_once("lang/main.php");
     
     // first table
     $cols1 = array(
                     t('all','BatchName'),
@@ -80,27 +84,18 @@
     
     include("library/layout.php");
 
     // print HTML prologue   
     $title = t('Intro','repbatchdetails.php');
+    $help = t('helpPage','repbatchdetails');
     
     print_html_prologue($title, $langCode);
 
     include ("menu-reports-batch.php");
-      
-?>    
-
-        <div id="contentnorightbar">
-            <h2 id="Intro">
-                <a href="#"  onclick="javascript:toggleShowDiv('helpPage')">
-                    <?= t('Intro','repbatchdetails.php'); ?><h144>&#x2754;</h144>
-                </a>
-            </h2>
-
-            <div id="helpPage" style="display:none;visibility:visible"><?= t('helpPage','repbatchdetails') ?><br></div>
-
-<?php
+    
+    echo '<div id="contentnorightbar">';
+    print_title_and_help($title, $help);
 
     include('library/opendb.php');
     include('include/management/pages_common.php');
     
     // get $batch_id
@@ -146,11 +141,11 @@
     <table border="0" class="table1">
         <thead>
             <tr style="background-color: white">
                 <td style="text-align: right" colspan="<?= $colspan1 ?>">
                     <input class="button" type="button" value="Download Invoice"
-                        onclick="location.href='include/common/notificationsBatchDetails.php?batch_name=<?= urlencode($batch_name_enc) ?>&destination=download'">
+                        onclick="window.open('include/common/notificationsBatchDetails.php?batch_name=<?= urlencode($batch_name_enc) ?>&destination=download')">
                     <input class="button" type="button" value="Email Invoice to Business/Hotspot"
                         onclick="location.href='include/common/notificationsBatchDetails.php?batch_name=<?= urlencode($batch_name_enc) ?>&destination=email'">
                     <input class="button" type="button" value="CSV Export"
                         onclick="location.href='include/management/fileExport.php?reportFormat=csv&reportType=reportsBatchTotalUsers'">
                 </td>
@@ -316,28 +311,16 @@
         include_once("include/management/actionMessages.php");
     }
     
     include('library/closedb.php');
 
-?>
-
-</div><!-- #contentnorightbar -->
-        
-        <div id="footer">
-<?php
     include('include/config/logging.php');
-    include('page-footer.php');
+    
+    $inline_extra_js = "
+var tooltipObj = new DHTMLgoodies_formTooltip();
+tooltipObj.setTooltipPosition('right');
+tooltipObj.setPageBgColor('#EEEEEE');
+tooltipObj.setTooltipCornerSize(15);
+tooltipObj.initFormFieldTooltip()";
+    
+    print_footer_and_html_epilogue($inline_extra_js);
 ?>
-        </div><!-- #footer -->
-    </div>
-</div>
-
-<script>
-    var tooltipObj = new DHTMLgoodies_formTooltip();
-    tooltipObj.setTooltipPosition('right');
-    tooltipObj.setPageBgColor('#EEEEEE');
-    tooltipObj.setTooltipCornerSize(15);
-    tooltipObj.initFormFieldTooltip();
-</script>
-
-</body>
-</html>
diff --git a/rep-online.php b/rep-online.php
index fe4b5522..a613489b 100644
--- a/rep-online.php
+++ b/rep-online.php
@@ -187,11 +187,11 @@
         
         // this can be passed as form attribute and 
         // printTableFormControls function parameter
         $action = "mng-del.php";
 ?>
-<form name="listall" method="GET" action="<?= $action ?>">
+<form name="listall" method="POST" action="<?= $action ?>">
     <table border="0" class="table1">
         <thead>
             <tr style="background-color: white">
 <?php
         // page numbers are shown only if needed
@@ -299,10 +299,13 @@
         $links = setupLinks_str($pageNum, $maxPage, $orderBy, $orderType, $partial_query_string);
         printTableFoot($per_page_numrows, $numrows, $colspan, $drawNumberLinks, $links);
 ?>
 
     </table>
+    
+    <input name="csrf_token" type="hidden" value="<?= dalo_csrf_token() ?>">
+    
 </form>    
 
 <?php
     } else {
         $failureMsg = "Nothing to display";
diff --git a/rep-username.php b/rep-username.php
index 29945027..805b4fac 100644
--- a/rep-username.php
+++ b/rep-username.php
@@ -118,21 +118,32 @@
         </tr>
     </thead>
 
     <tbody>
 <?php
+            $csrf_token = dalo_csrf_token();
+
             while ($row = $res->fetchRow()) {
                 $rowlen = count($row);
                 
                 echo "<tr>";
                 for ($i = 0; $i < $rowlen; $i++) {
                     printf("<td>%s</td>", htmlspecialchars($row[$i], ENT_QUOTES, 'UTF-8'));
                 }
                 
                 $this_username = htmlspecialchars($row[1], ENT_QUOTES, 'UTF-8');
-                printf('<td><a href="mng-edit.php?username=%s">%s</a>&nbsp;<a href="mng-del.php?username=%s">%s</a></td>',
-                       urlencode($this_username), t('all','edit'), urlencode($this_username), t('all','del'));
+                echo '<td>';
+                $formId = $this_username . "-form-del";
+                printf('<form id="%s" style="display: none" method="POST" action="mng-del.php">', $formId);
+                printf('<input type="hidden" name="username[]" value="%s">', $this_username);
+                printf('<input type="hidden" name="csrf_token" value="%s">', $csrf_token);
+                echo '</form>';
+                
+                $onclick = sprintf("document.getElementById('%s').submit()", $formId);
+                printf('<a href="mng-edit.php?username=%s">%s</a>&nbsp;<a href="#" onclick="%s">%s</a>',
+                       urlencode($this_username), t('all','edit'), $onclick, t('all','del'));
+                echo '</td>';
                 echo "</tr>";
             }
 ?>
     </tbody>
 </table>

Vulnerable code: ?>
                
        </div><!-- #contentnorightbar -->
        
        <div id="footer">
<?php
    include('page-footer.php');
        </div><!-- #footer -->
    </div>
</div>

<script>
    var tooltipObj = new DHTMLgoodies_formTooltip();
    tooltipObj.setTooltipPosition('right');
    tooltipObj.setPageBgColor('#EEEEEE');
    tooltipObj.setTooltipCornerSize(15);
    tooltipObj.initFormFieldTooltip();
</script>

</body>
</html>
    $passwordType = (array_key_exists('passwordType', $_POST) && isset($_POST['passwordType']) &&
                         in_array($_POST['passwordType'], $valid_passwordTypes)) ? $_POST['passwordType'] : "";
<?php
 * Authors:	Liran Tal <liran@enginx.com>
  include ("library/checklogin.php");
  $operator = $_SESSION['operator_user'];

	require_once(dirname(__FILE__)."/../../notifications/processNotificationBatchDetails.php");
	require_once(dirname(__FILE__)."/../../library/config_read.php");

	isset($_GET['batch_name']) ? $batch_name = $_GET['batch_name'] : $batch_name = "";
	isset($_GET['destination']) ? $destination = $_GET['destination'] : $destination = "download";

	if ($batch_name != "") {
		$customerInfo = @getBatchDetails($batch_name);

		$smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
		$smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
		$smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
		$from = $configValues['CONFIG_MAIL_SMTPFROM'];

		$pdfDocument = @createBatchDetailsNotification($customerInfo);

		if ($destination == "download") {

			header("Content-type: application/pdf");
			header("Content-Disposition: attachment; filename=batch_notification_invoice_" . date("Ymd") . ".pdf; size=" . strlen($pdfDocument));
			print $pdfDocument;

		} else if ($destination == "email") {

			@emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
			header("Location: ".$_SERVER['HTTP_REFERER']);
		}

	}


	function getBatchDetails($batch_name = NULL) {

		require(dirname(__FILE__)."/../../library/opendb.php");
		require_once(dirname(__FILE__)."/../../lang/main.php");

		global $configValues;

		if ($batch_name == NULL || empty($batch_name))
			exit;



		$tableTags = "width='580px' ";
		$tableTrTags = "bgcolor='#ECE5B6'";

		$customerInfo = array();

		$sql = "SELECT ".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id,".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name,".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_description,".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_status,".

			"COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id)) as total_users,".
			"COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_RADACCT'].".username)) as active_users,".
			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname,".
			$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancost,".
			$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancurrency,".
			$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".name as HotspotName,".


			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationdate,".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationby,".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updatedate,".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updateby ".
			" FROM ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].
			" ON ".
			"(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id = ".
			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id) ".

			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
			" ON ".
			"(".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".planname = ".
			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname) ".

			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
			" ON ".
			"(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".hotspot_id = ".
			$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".id) ".

			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_RADACCT'].
			" ON ".
			"(".$configValues['CONFIG_DB_TBL_RADACCT'].".username = ".
			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username) ".

			" WHERE ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name = '".$dbSocket->escapeSimple($batch_name)."' ".
			" GROUP by ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ";

		$res = $dbSocket->query($sql);

		$batch_details = "";

		$batch_details .= "<table $tableTags><tr $tableTrTags>
					<td>
			".t('all','BatchName')."
			</td>

			<td>
			".t('all','HotSpot')."
			</td>

			<td>
			".t('all','BatchStatus')."
			</td>

			<td>
			".t('all','TotalUsers')."
			</td>

			<td>
			".t('all','ActiveUsers')."
			</td>

			<td>
			".t('all','PlanName')."
			</td>

			<td>
			".t('all','PlanCost')."
			</td>

			<td>
			".t('all','BatchCost')."
			</td>

			<td>
			".t('all','CreationDate')."
			</td>

			<td>
			".t('all','CreationBy')."
			</td>

			</tr>";

		$active_users_per = 0;
		$total_users = 0;
		$active_users = 0;
		$batch_cost = 0;

		$hotspot_name = "";
		$batch_id = "";
		$planname = "";

		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {

			$batch_id = $row['id'];
			$hotspot_name = $row['HotspotName'];
			$batch_status = $row['batch_status'];
			$plancost = $row['plancost'];
			$total_users = $row['total_users'];
			$active_users = $row['active_users'];
			$batch_cost = ($active_users * $plancost);
			$plan_currency = $row['plancurrency'];
			$planname = $row['planname'];


			$batch_details .= "
					<tr>

					<td>".$row['batch_name']."

					</td>

					<td>".$hotspot_name."

					</td>

					<td>".$batch_status."

					</td>

					<td>".$total_users."

					</td>

					<td>".$active_users."

					</td>

					<td>".
						$row['planname']."
					</td>

					<td>".$plancost."
					</td>

					<td>".$batch_cost."
					</td>

					<td>".
						$row['creationdate']."
					</td>

					<td>".
						$row['creationby']."
					</td>

				</tr>
			";



		}

		$batch_details .= "</table>";

		$customerInfo['batch_details'] = $batch_details;










		$sql = "SELECT planId, planName, planRecurringPeriod, planCost, planSetupCost, planTax, planCurrency FROM ".
				$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
				" WHERE planName = '".$planname."'";
		$res = $dbSocket->query($sql);
		$row = $res->fetchRow(DB_FETCHMODE_ASSOC);

		$service_plan_info = "";
		$service_plan_info = "<table $tableTags>";

		foreach($row as $rowName => $rowValue) {

			$service_plan_info .= "<tr $tableTrTags'>
						<td>$rowName</td>
						<td>$rowValue</td>
						</tr>";

		}

		$service_plan_info .= "</table>";
		$customerInfo['service_plan_info'] = $service_plan_info;


		$sql = "SELECT id, name, owner, address, companyphone, companyemail, companywebsite FROM ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
					" WHERE name='".$hotspot_name."'";
		$res = $dbSocket->query($sql);
		$row = $res->fetchRow(DB_FETCHMODE_ASSOC);

		$customerInfo['business_name'] = $row['name'];
		$customerInfo['business_owner_name'] = $row['owner'];
		$customerInfo['business_address'] = $row['address'];
		$customerInfo['business_phone'] = $row['companyphone'];
		$customerInfo['business_email'] = $row['companyemail'];
		$customerInfo['business_web'] = $row['companywebsite'];






		$batch_active_users = "";

		$sql = "SELECT ".
				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id,".
				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username,".
				$configValues['CONFIG_DB_TBL_RADACCT'].".acctstarttime,".
				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ".

				" FROM ".
				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].",".
				$configValues['CONFIG_DB_TBL_RADACCT'].",".
				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].

				" WHERE ".
				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = ".
				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id".
				" AND ".
				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = '$batch_id' ".
				" AND ".
				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username = ".
				$configValues['CONFIG_DB_TBL_RADACCT'].".username".

				" GROUP by ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username ".
				" ORDER BY id ,".$configValues['CONFIG_DB_TBL_RADACCT'].".radacctid ASC ";

		$res = $dbSocket->query($sql);

		$batch_active_users = "<table $tableTags><tr $tableTrTags'>
			<td>
			".t('all','BatchName')."
			</td>

			<td>
			".t('all','Username')."
			</td>

			<td>
			".t('all','StartTime')."
			</td>

			</tr>";


		$active_users_per = 0;
		$total_users = 0;
		$active_users = 0;
		$batch_cost = 0;
		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {

			$username = $row['username'];
			$acctstarttime = $row['acctstarttime'];
			$batch_name = $row['batch_name'];

			$batch_active_users .= "
					<tr>

					<td>".$batch_name."
					</td>

					<td>".$username."
					</td>

					<td>".$acctstarttime."
					</td>

				</tr>
			";

		}


		$batch_active_users .= "</table>";
		$customerInfo['batch_active_users'] = $batch_active_users;


		require(dirname(__FILE__)."/../../library/closedb.php");

		return $customerInfo;


	}
<?php
 * Authors:	Liran Tal <liran@enginx.com>
	require_once(dirname(__FILE__)."/../../notifications/processNotificationUserDetailsInvoice.php");
	//require_once(dirname(__FILE__)."/../../library/config_read.php");
	/*
	isset($_GET['batch_name']) ? $batch_name = $_GET['batch_name'] : $batch_name = "";
	isset($_GET['destination']) ? $destination = $_GET['destination'] : $destination = "download";
	
	//test
	$batch_name = "batch_test6"; 
	
	if ($batch_name != "") {
		$customerInfo = @getBatchDetails($batch_name);
		
		$smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
		$smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
		$smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
		$from = $configValues['CONFIG_MAIL_SMTPFROM'];
		
		$pdfDocument = @createBatchDetailsNotification($customerInfo);
		
		if ($destination == "download") {
			
			header("Content-type: application/pdf");
			header("Content-Disposition: attachment; filename=batch_notification_invoice_" . date("Ymd") . ".pdf; size=" . strlen($pdfDocument));
			print $pdfDocument;
			
		} else if ($destination == "email") {
			
			@emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
			header("Location: ".$_SERVER['HTTP_REFERER']);
		}
		
	}
	*/
	
	function getCustomerInfo($row) {
	
		//global $configValues;
		
		$customerInfo = array();
		
		getCustomerInfo_customer_info($row, $customerInfo);
		getCustomerInfo_service_plan($row, $customerInfo);
		
		return $customerInfo;
		
	}
	
	
	
	function getCustomerInfo_customer_info($row, &$customerInfo) {
				
		global $configValues;
		require(dirname(__FILE__)."/../../lang/main.php");
		
		$tableTags = "width='580px' ";
		$tableTrTags = "bgcolor='#ECE5B6'";
		
		
		if (!empty($row['email1']))
			$invoice_email = $row['email1'];
		else if (!empty($row['email2']))
			$invoice_email = $row['email2'];
		else if (!empty($row['email3']))
			$invoice_email = $row['email3'];
		else
			$invoice_email = "";
		
		if (!empty($row['mobilephone']))
			$invoice_phone = $row['mobilephone'];
		else if (!empty($row['workphone']))
			$invoice_phone = $row['mobilephone'];
		else if (!empty($row['homephone']))
			$invoice_phone = $row['homephone'];
		else
			$invoice_phone = "Unavailable";
			
		$invoice_address = "";
		if (!empty($row['address']))
			$invoice_address = $row['address'];
		
		if (!empty($row['city']))
			$invoice_address .= ", ".$row['city'];
		
		if (!empty($row['state']))
			$invoice_address .= "<br/>".$row['state'];
		
		if (!empty($row['zip']))
			$invoice_address .= " ".$row['zip'];
		
		if (empty($invoice_address))
			$invoice_address = "Unavailable";
		
		$customerInfo['business_name'] = $row['firstname']. " " .$row['lastname'];
		$customerInfo['business_address'] = $invoice_address;
		$customerInfo['business_phone'] = $invoice_phone;
		$customerInfo['business_email'] = $invoice_email;
	}
	
	
	function getCustomerInfo_service_plan($row, &$customerInfo) {
				
		global $configValues;
		require(dirname(__FILE__)."/../../lang/main.php");
		
		$tableTags = "width='580px' ";
		$tableTrTags = "bgcolor='#ECE5B6'";
		
		$service_plan_info = "";
		$service_plan_info = "<table $tableTags>";
		
		$service_plan_info .= "".
					"<tr $tableTrTags'>
					<td>".t('all','Username')."</td>
					<td>".$row['username']."</td>
					</tr>".
		
					"<tr $tableTrTags'>
					<td>".t('all','PlanName')."</td>
					<td>".$row['planname']."</td>
					</tr>".
					"<tr $tableTrTags'>
					<td>".t('all','PlanRecurring')."</td>
					<td>".$row['planRecurring']."</td>
					</tr>".
					"<tr $tableTrTags'>
					<td>".t('all','PlanRecurringPeriod')."</td>
					<td>".$row['planRecurringPeriod']."</td>
					</tr>".
					"<tr $tableTrTags'>
					<td>".t('all','PlanCost')."</td>
					<td>".$row['planCost']."</td>
					</tr>".
					"<tr $tableTrTags'>
					<td>".t('all','NextBill')."</td>
					<td>".$row['nextbill']."</td>
					</tr>".
					"<tr $tableTrTags'>
					<td>".t('all','BillDue')."</td>
					<td>".$row['billdue']."</td>
					</tr>".
					"";
		
		$service_plan_info .= "</table>";
		$customerInfo['service_plan_info'] = $service_plan_info;	
		
	}
	
	
	/*
	function getBatchDetails($batch_name = NULL) {
		
		require(dirname(__FILE__)."/../../library/opendb.php");
		require_once(dirname(__FILE__)."/../../lang/main.php");
		
		global $configValues;
		
		if ($batch_name == NULL || empty($batch_name))
			exit;
			
			
		$tableTags = "width='580px' ";
		$tableTrTags = "bgcolor='#ECE5B6'";
		
		$customerInfo = array();
		
		$sql = "SELECT ".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id,".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name,".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_description,".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_status,".
			
			"COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id)) as total_users,".
			"COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_RADACCT'].".username)) as active_users,".
			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname,".
			$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancost,".
			$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancurrency,".
			$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".name as HotspotName,".
			
			
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationdate,".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationby,".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updatedate,".
			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updateby ".
			" FROM ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].
			" ON ".
			"(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id = ".
			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id) ".
			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
			" ON ".
			"(".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".planname = ".
			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname) ".
			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
			" ON ".
			"(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".hotspot_id = ".
			$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".id) ".
			
			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_RADACCT'].
			" ON ".
			"(".$configValues['CONFIG_DB_TBL_RADACCT'].".username = ".
			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username) ".
			
			" WHERE ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name = '$batch_name' ".
			" GROUP by ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ";
			
		$res = $dbSocket->query($sql);
		$batch_details = "";
		
		$batch_details .= "<table $tableTags><tr $tableTrTags>
					<td> 
			".t('all','BatchName')."
			</td>
	
			<td> 
			".t('all','HotSpot')."
			</td>
	
			<td> 
			".t('all','BatchStatus')."
			</td>
			
			<td> 
			".t('all','TotalUsers')."
			</td>
	
			<td> 
			".t('all','ActiveUsers')."
			</td>
	
			<td> 
			".t('all','PlanName')."
			</td>
	
			<td> 
			".t('all','PlanCost')."
			</td>
	
			<td> 
			".t('all','BatchCost')."
			</td>
	
			<td> 
			".t('all','CreationDate')."
			</td>
	
			<td> 
			".t('all','CreationBy')."
			</td>
	
			</tr>";
		
		$active_users_per = 0;
		$total_users = 0;
		$active_users = 0;
		$batch_cost = 0;
		
		$hotspot_name = "";
		$batch_id = "";
		$planname = "";
		
		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
			
			$batch_id = $row['id'];
			$hotspot_name = $row['HotspotName'];
			$batch_status = $row['batch_status'];
			$plancost = $row['plancost'];
			$total_users = $row['total_users'];
			$active_users = $row['active_users'];
			$batch_cost = ($active_users * $plancost);
			$plan_currency = $row['plancurrency'];
			$planname = $row['planname'];
			
	
			$batch_details .= "
					<tr>
					
					<td>".$row['batch_name']."
						
					</td>
					
					<td>".$hotspot_name."
						
					</td>
			
					<td>".$batch_status."
						
					</td>
					
					<td>".$total_users."
						
					</td>
	
					<td>".$active_users."
						
					</td>
	
					<td>".
						$row['planname']."
					</td>
	
					<td>".$plancost."
					</td>
	
					<td>".$batch_cost."
					</td>
					
					<td>".
						$row['creationdate']."
					</td>
	
					<td>".
						$row['creationby']."
					</td>
	
				</tr>
			";
			
			
		}
		
		$batch_details .= "</table>";
		
		$customerInfo['batch_details'] = $batch_details;
		
		
		
		
		
		
		
		
		
		
		$sql = "SELECT planId, planName, planRecurringPeriod, planCost, planSetupCost, planTax, planCurrency FROM ".
				$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
				" WHERE planName = '".$planname."'";
		$res = $dbSocket->query($sql);
		$row = $res->fetchRow(DB_FETCHMODE_ASSOC);
		
		$service_plan_info = "";
		$service_plan_info = "<table $tableTags>";
		
		foreach($row as $rowName => $rowValue) {
		
			$service_plan_info .= "<tr $tableTrTags'>
						<td>$rowName</td>
						<td>$rowValue</td>
						</tr>";
		
		}
		
		$service_plan_info .= "</table>";
		$customerInfo['service_plan_info'] = $service_plan_info;
		
		
		$sql = "SELECT id, name, owner, address, companyphone, companyemail, companywebsite FROM ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
					" WHERE name='".$hotspot_name."'";
		$res = $dbSocket->query($sql);
		$row = $res->fetchRow(DB_FETCHMODE_ASSOC);
		
		$customerInfo['business_name'] = $row['name'];
		$customerInfo['business_owner_name'] = $row['owner'];
		$customerInfo['business_address'] = $row['address'];
		$customerInfo['business_phone'] = $row['companyphone'];
		$customerInfo['business_email'] = $row['companyemail'];
		$customerInfo['business_web'] = $row['companywebsite'];
				
		
		
		
		
		
		$batch_active_users = "";
		
		$sql = "SELECT ".
				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id,".
				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username,".
				$configValues['CONFIG_DB_TBL_RADACCT'].".acctstarttime,".
				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ".
				
				" FROM ".
				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].",".
				$configValues['CONFIG_DB_TBL_RADACCT'].",".
				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
				
				" WHERE ".
				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = ".
				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id".
				" AND ".
				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = '$batch_id' ".
				" AND ".
				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username = ".
				$configValues['CONFIG_DB_TBL_RADACCT'].".username".
	
				" GROUP by ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username ".
				" ORDER BY id ,".$configValues['CONFIG_DB_TBL_RADACCT'].".radacctid ASC ";
				
		$res = $dbSocket->query($sql);
		
		$batch_active_users = "<table $tableTags><tr $tableTrTags'>
			<td> 
			".t('all','BatchName')."
			</td>
	
			<td> 
			".t('all','Username')."
			</td>
	
			<td> 
			".t('all','StartTime')."
			</td>
	
			</tr>";
			
		$active_users_per = 0;
		$total_users = 0;
		$active_users = 0;
		$batch_cost = 0;
		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
	
			$username = $row['username'];
			$acctstarttime = $row['acctstarttime'];
			$batch_name = $row['batch_name'];
			
			$batch_active_users .= "
					<tr>
					
					<td>".$batch_name."
					</td>
	
					<td>".$username."
					</td>
	
					<td>".$acctstarttime."
					</td>
	
				</tr>
			";
		}
		
		$batch_active_users .= "</table>";
		$customerInfo['batch_active_users'] = $batch_active_users;
		
		
		require(dirname(__FILE__)."/../../library/closedb.php");
		
		return $customerInfo;
		
		
	}
	*/
?>
<?php
 * Authors:	Liran Tal <liran@enginx.com>
	require_once(dirname(__FILE__)."/../../notifications/processNotificationUserInvoice.php");
	require_once(dirname(__FILE__)."/../../library/config_read.php");
	
	isset($_GET['invoice_id']) ? $invoice_id = $_GET['invoice_id'] : $invoice_id = "";
	isset($_GET['destination']) ? $destination = $_GET['destination'] : $destination = "preview";
	
	if ($invoice_id != "") {
		$customerInfo = @getInvoiceDetails($invoice_id);
		$document = @createNotification($customerInfo, $destination == "preview");
		
		if ($destination == "download") {
			
			$pdfDocument = $document;
			
			header("Content-type: application/pdf");
			header("Content-Disposition: attachment; filename=notification_user_invoice_" . date("Ymd") . ".pdf; size=" . strlen($pdfDocument));
			print $pdfDocument;
			
		} else if ($destination == "email") {
			
			$pdfDocument = $document;
			
			$smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
			$smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
			$smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
			$from = $configValues['CONFIG_MAIL_SMTPFROM'];
		
			@emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
			header("Location: ".$_SERVER['HTTP_REFERER']);
			
		} else if ($destination == 'preview') {
			$htmlDocument = $document;
			$result = file_put_contents(dirname(__FILE__).'/../../notifications/templates/invoice_preview.html', $htmlDocument);
			header('Location: ../../notifications/templates/invoice_preview.html');
		}
	}
	
	
	function getInvoiceDetails($invoice_id = NULL) {
		
		require(dirname(__FILE__)."/../../library/opendb.php");
		require_once(dirname(__FILE__)."/../../lang/main.php");
		
		global $configValues;
		
		if ($invoice_id == NULL || empty($invoice_id))
			exit;
			
		$tableTags = "width='580px' ";
		$tableTrTags = "bgcolor='#ECE5B6'";
		
		// get invoice details
		$sql = "SELECT a.id, a.date, a.status_id, a.type_id, a.user_id, a.notes, b.contactperson, b.username, b.company, ".
				" b.city, b.state, b.country, b.zip, b.address, b.email, b.emailinvoice, b.phone, f.value as type, ".
				" c.value AS status, COALESCE(e2.totalpayed, 0) as totalpayed, COALESCE(d2.totalbilled, 0) as totalbilled ".
				" FROM ".$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICE']." AS a".
				" INNER JOIN ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO']." AS b ON (a.user_id = b.id) ".
				" INNER JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICESTATUS']." AS c ON (a.status_id = c.id) ".
				" INNER JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICETYPE']." AS f ON (a.type_id = f.id) ".
				" LEFT JOIN (SELECT SUM(d.amount + d.tax_amount) ".
					" as totalbilled, invoice_id, amount, tax_amount, notes, plan_id FROM ".$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICEITEMS']." AS d ".
					" GROUP BY d.invoice_id) AS d2 ON (d2.invoice_id = a.id) ".
				" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS']." AS bp2 ON (bp2.id = d2.plan_id) ".
				" LEFT JOIN (SELECT SUM(e.amount) as totalpayed, invoice_id FROM ". 
				$configValues['CONFIG_DB_TBL_DALOPAYMENTS']." AS e GROUP BY e.invoice_id) AS e2 ON (e2.invoice_id = a.id) ".
				" WHERE a.id = '".$dbSocket->escapeSimple($invoice_id)."'".
				" GROUP BY a.id ";
		$res = $dbSocket->query($sql);	
		$invoiceDetails = $res->fetchRow(DB_FETCHMODE_ASSOC);
		if (empty($invoiceDetails['email']))
			$customer_email = $invoiceDetails['emailinvoice'];
		else
			$customer_email = $invoiceDetails['email'];
			
		// populate user contact informatin
		$customerInfo['customer_name'] = $invoiceDetails['contactperson'];
		$customerInfo['customer_address'] = $invoiceDetails['address']. " " . $invoiceDetails['city']. " " . $invoiceDetails['state'];
		$customerInfo['customer_email'] = $customer_email;
		$customerInfo['customer_phone'] = $invoiceDetails['phone'];
		
		// populate user invoice details
		$balance = (float) ($invoiceDetails['totalpayed'] - $invoiceDetails['totalbilled']);
		$invoice_details = "";
		$invoice_details .= "".
		"<b>".t('all','ClientName')."</b>: ".$invoiceDetails['contactperson']."<br/>".
		"<b>".t('all','Invoice')."</b>: ".$invoice_id."<br/>".
		"<b>".t('all','Date')."</b>: ".$invoiceDetails['date']."<br/>".
		"<b>".t('all','TotalBilled')."</b>: ".$invoiceDetails['totalbilled']."<br/>".
		"<b>".t('all','TotalPayed')."</b>: ".$invoiceDetails['totalpayed']."<br/>".
		"<b>".t('all','Balance')."</b>: ".$balance."<br/>".
		"<b>".t('all','Status')."</b>: ".$invoiceDetails['status']."<br/>".
		"<b>".t('ContactInfo','Notes')."</b>: ".$invoiceDetails['notes']."<br/><br/><br/>";
		
		$customerInfo['invoice_details'] = $invoice_details;
		
		// populate customer data - NEW STYLE
		$customerInfo['customerId'] = $invoiceDetails['user_id'];
		$customerInfo['customerName'] = (isset($invoiceDetails['company']) ? $invoiceDetails['company'] : $invoiceDetails['contactperson']);
		$customerInfo['customerAddress'] = $invoiceDetails['address'];
		$customerInfo['customerAddress2'] = $invoiceDetails['zip'] . ' '. $invoiceDetails['city'] . ' ' .
		                                    $invoiceDetails['state'] . ' ' . $invoiceDetails['country'];
		$customerInfo['customerEmail'] = $invoiceDetails['email'];
		$customerInfo['customerPhone'] = $invoiceDetails['phone'];
		$customerInfo['customerContact'] = $invoiceDetails['contactperson'];
		
		$customerInfo['invoiceNumber'] = $invoice_id;
		$customerInfo['invoiceDate'] = date('Y-m-d', strtotime($invoiceDetails['date']));
		$customerInfo['invoiceStatus'] = strtoupper($invoiceDetails['status']);
		$customerInfo['invoiceTotalBilled'] = $invoiceDetails['totalbilled'];
		$customerInfo['invoicePaid'] = $invoiceDetails['totalpayed'];
		$customerInfo['invoiceDue'] = $balance;
		$customerInfo['invoiceNotes'] = $invoiceDetails['notes'];
		
		// populate user invoice items
		$invoice_items = "";
		$invoice_items .= "<table $tableTags><tr $tableTrTags>
			<th>Plan</th>
			<th>Item Amount</th>
			<th>Item Tax</th>
			<th>Notes</th>
			</tr>
			";
		// get all invoice items
		$sql = 'SELECT a.id, a.plan_id, a.amount, a.tax_amount, a.notes, b.planName '.
				' FROM '.$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICEITEMS'].' a '.
				' LEFT JOIN '.$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].' b ON a.plan_id = b.id '.
				' WHERE a.invoice_id = '.$invoice_id.' ORDER BY a.id ASC';
		$res = $dbSocket->query($sql);
		$logDebugSQL .= $sql . "\n";
		
		// initialize invoice items - NEW STYLE
		$invoiceItems = array();
		$invoiceItemsNumber = 1;
		$invoiceItemsTotalAmount = 0;
		$invoiceItemsTotalTax = 0;
		
		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
			$invoice_items .= "". 
				"<tr>".
					"<td>".$row['planName']."</td>".
					"<td>".$row['amount']."</td>".
					"<td>".$row['tax_amount']."</td>".
					"<td>".$row['notes']."</td>".
				"</tr>";
				
			// populate invoice items - NEW STYLE
			$invoiceItem = array();
			
			$invoiceItem['invoiceItemNumber'] = sprintf('%02d', $invoiceItemsNumber);
			$invoiceItem['invoiceItemPlan'] = $row['planName'];
			$invoiceItem['invoiceItemNotes'] = $row['notes'];
			$invoiceItem['invoiceItemAmount'] = $row['amount'];
			$invoiceItem['invoiceItemTaxAmount'] = $row['tax_amount'];
			$invoiceItem['invoiceItemTotalAmount'] = $row['amount'] + $row['tax_amount'];
			
			$invoiceItems[] = $invoiceItem;
			$invoiceItemsTotalAmount += $row['amount'];
			$invoiceItemsTotalTax += $row['tax_amount'];
			
			++$invoiceItemsNumber;
		}
		$invoice_items .= "</table>";
		
		$customerInfo['invoice_items'] = $invoice_items;
		
		// populate invoice items - NEW STYLE
		$customerInfo['invoiceItems'] = $invoiceItems;
		$customerInfo['invoiceTotalAmount'] = $invoiceItemsTotalAmount;
		$customerInfo['invoiceTotalTax'] = $invoiceItemsTotalTax;
		
		require(dirname(__FILE__)."/../../library/closedb.php");
		return $customerInfo;
	}
	
<?php
 * Authors:	Liran Tal <liran@enginx.com>
	require_once(dirname(__FILE__)."/../../notifications/processNotificationWelcome.php");
	
	if (!empty($email))
		$invoice_email = $email;
	else if (!empty($bi_emailinvoice))
		$invoice_email = $bi_emailinvoice;
	else if (!empty($bi_email))
		$invoice_email = $bi_email;
	else
		$invoice_email = "";
	
	if (!empty($mobilephone))
		$invoice_phone = $mobilephone;
	else if (!empty($workphone))
		$invoice_phone = $workphone;
	else if (!empty($homephone))
		$invoice_phone = $homephone;
	else
		$invoice_phone = "Unavailable";
		
	$invoice_address = "";
	if (!empty($ui_address))
		$invoice_address = $ui_address;
	
	if (!empty($ui_city))
		$invoice_address .= ", ".$ui_city;
	
	if (!empty($ui_state))
		$invoice_address .= "<br/>".$ui_state;
	
	if (!empty($ui_zip))
		$invoice_address .= " ".$ui_zip;
	
	if (empty($invoice_address))
		$invoice_address = "Unavailable";
	
	$customerInfo = array();
	$customerInfo['customer_name'] = $firstname ." ".$lastname;
	$customerInfo['customer_address'] = $invoice_address;
	
	$customerInfo['customer_phone'] = $invoice_phone;
	$customerInfo['customer_email'] = $invoice_email;
	$customerInfo['service_plan_name'] = $planName;
	
	$smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
	$smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
	$smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
	$from = $configValues['CONFIG_MAIL_SMTPFROM'];
	
	@sendWelcomeNotification($customerInfo, $smtpInfo, $from);

?>
    $ticketInformation = "Information: To use this card, please connect your device to the nearest ssid."
                       . "<br>"
                $ticketInformation = htmlspecialchars(trim($_POST['ticketInformation']), ENT_QUOTES, 'UTF-8');
                  
            } else {
                $ticketCost = $ticketTime = $ticketCurrency = "(n/a)";
        <title>untitled</title>
    height: 18mm;
    width: 100%;
    font-size: 9pt;
.card-foot {
    height: 24mm;
    padding: 2mm;
    font-size: 7pt;

               . '<table>'
               . '<tr>'
               . '<th>Login</th>';
            printf('<td>%s</td>', htmlspecialchars($username, ENT_QUOTES, 'UTF-8'));
            echo '</tr>'
               . '<tr>'
               . '<th>Password</th>';
            printf('<td>%s</td>', htmlspecialchars($password, ENT_QUOTES, 'UTF-8'));
            echo '</tr>'
               . '<tr>'
               . '<th>Validity</th>';
            printf('<td>%s</td>', htmlspecialchars($ticketTime, ENT_QUOTES, 'UTF-8'));
            echo '</tr>'
               . '<tr>'
               . '<th>Price</th>';
            printf('<td>%s</td>', htmlspecialchars($ticketCost, ENT_QUOTES, 'UTF-8'));
            echo '</tr>'
               . '</table>'
    header('Location: /login.php');
                    $exportForm .= sprintf('<form target="_blank" style="display: none" id="%s" ', $form_id)
                                 . sprintf('<input type="hidden" name="batch_name" value="%s">',
                                 . '<input type="hidden" name="type" value="batch">';
                        $exportForm .= sprintf('<input type="hidden" name="accounts[%d][0]" value="%s">', $i, htmlspecialchars($u, ENT_QUOTES, 'UTF-8'))
                                     . sprintf('<input type="hidden" name="accounts[%d][1]" value="%s">', $i, htmlspecialchars($p, ENT_QUOTES, 'UTF-8'));
                    $exportForm .= '</form>';
                    $exportForm .= sprintf('<input type="button" onclick="%s" value="CSV Download">', $onclick);
                    $exportForm .= sprintf('<input type="button" onclick="%s" value="Printable Tickets">', $onclick);
                            // if batch_history record was created successfuly
                            //~ if ($sql_batch_id != 0) {
                                //~ // remove the last || chars to sanitize it for proper format
                                //~ $exportCSV = substr($exportCSV, 0, -2);
                                //~ $successMsg = "Exported Usernames - ".
                                                    //~ "<a href='include/common/fileExportCSV.php?csv_output=$exportCSV'>download</a><br/>".
                                                //~ "Printable Tickets - ".
                                                    //~ "<a href='include/common/printTickets.php?type=batch&plan=$planName&accounts=$exportCSV'>view</a><br/>".
                                                //~ "Added to database new user(s): <b> $actionMsgGoodUsernames </b><br/>";
                            //~ }
                
                
    isset($_GET['username']) ? $username = $_GET['username'] : $username = "";
    isset($_GET['attribute']) ? $attribute = $_GET['attribute'] : $attribute = "";
    isset($_GET['tablename']) ? $tablename = $_GET['tablename'] : $tablename = "";
    isset($_GET['delradacct']) ? $delradacct = $_GET['delradacct'] : $delradacct = "";
    isset($_GET['clearSessionsUsers']) ? $clearSessionsUsers = $_GET['clearSessionsUsers'] : $clearSessionsUsers = "";

    $logAction = "";
    $logDebugSQL = "";

    $showRemoveDiv = "block";

    if ( (isset($_GET['username'])) && (!(isset($_GET['attribute']))) && (!(isset($_GET['tablename']))) ) {

        $allUsernames = "";
        $isSuccessful = 0;

        /* since the foreach loop will report an error/notice of undefined variable $value because
           it is possible that the $username is not an array, but rather a simple GET request
           with just some value, in this case we check if it's not an array and convert it to one with
           a NULL 2nd element
        */
        if (!is_array($username))
            $username = array($username, NULL);

        foreach ($username as $variable=>$value) {

            if (trim($variable) != "") {

                $username = $value;
                $allUsernames .= $username . ", ";

                include 'library/opendb.php';

                // setting table-related parameters first
                switch($configValues['FREERADIUS_VERSION']) {
                    case '1' :
                        $tableSetting['postauth']['user'] = 'user';
                        $tableSetting['postauth']['date'] = 'date';
                        break;
                    case '2' :
                        // down
                    case '3' :
                        // down
                    default  :
                        $tableSetting['postauth']['user'] = 'username';
                        $tableSetting['postauth']['date'] = 'authdate';
                        break;

                // delete all attributes associated with a username
                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADCHECK']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
                $res = $dbSocket->query($sql);
                $logDebugSQL .= $sql . "\n";

                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADREPLY']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
                $res = $dbSocket->query($sql);
                $logDebugSQL .= $sql . "\n";

                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_DALOUSERINFO']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
                $res = $dbSocket->query($sql);
                $logDebugSQL .= $sql . "\n";

                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
                $res = $dbSocket->query($sql);
                $logDebugSQL .= $sql . "\n";

                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADUSERGROUP']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
                $res = $dbSocket->query($sql);
                $logDebugSQL .= $sql . "\n";

                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADPOSTAUTH']." WHERE ".
                    $tableSetting['postauth']['user']."='".$dbSocket->escapeSimple($username)."'";
                $res = $dbSocket->query($sql);
                $logDebugSQL .= $sql . "\n";

                if (strtolower($delradacct) == "yes") {
                    $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADACCT']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
                    $res = $dbSocket->query($sql);
                    $logDebugSQL .= $sql . "\n";

                $successMsg = "Deleted user(s): <b> $allUsernames </b>";
                $logAction .= "Successfully deleted user(s) [$allUsernames] on page: ";

                include 'library/closedb.php';

            }  else {
                $failureMsg = "no user was entered, please specify a username to remove from database";
                $logAction .= "Failed deleting user(s) [$allUsernames] on page: ";


        $showRemoveDiv = "none";

        } //foreach


    } else     if ( (isset($_GET['username'])) && (isset($_GET['attribute'])) && (isset($_GET['tablename'])) ) {

        /* this section of the deletion process only deletes the username record with the specified attribute
         * variable from $tablename, this is in order to support just removing a single attribute for the user
         */

        include 'library/opendb.php';

        if (isset($attribute)) {
            if (preg_match('/__/', $attribute))
                list($columnId, $attribute) = explode("__", $attribute);
            else
                $attribute = $attribute;

        $sql = "DELETE FROM ".$dbSocket->escapeSimple($tablename)." WHERE Username='".$dbSocket->escapeSimple($username)."' ".
                " AND Attribute='".$dbSocket->escapeSimple($attribute)."' AND id=".$dbSocket->escapeSimple($columnId);
        $res = $dbSocket->query($sql);
        $logDebugSQL .= $sql . "\n";

        $successMsg = "Deleted attribute: <b> $attribute </b> for user(s): <b> $username </b> from database";
        $logAction .= "Successfully deleted attribute [$attribute] for user [$username] on page: ";

        include 'library/closedb.php';

        $showRemoveDiv = "none";

    } else if ( (isset($clearSessionsUsers)) && ($clearSessionsUsers != "") ) {

        /* this is used to remove stale user sessions from the accounting table
        */
        $allUsernames = "";

        if (!is_array($clearSessionsUsers))
            $clearSessionsUsers = array($clearSessionsUsers, NULL);

            foreach ($clearSessionsUsers as $variable=>$value) {

                if (trim($value) != "") {

                    list($userSessions,$acctStartTime) = preg_split('/\\|\\|/', $value);

                    $allUsernames .= $userSessions . ", ";

                    include 'library/opendb.php';

                    $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADACCT'].
                        " WHERE Username='$userSessions' AND AcctStartTime='$acctStartTime' ".
                        " AND (AcctStopTime='0000-00-00 00:00:00' OR AcctStopTime IS NULL)";
                    $res = $dbSocket->query($sql);
                    $logDebugSQL .= $sql . "\n";

                    $successMsg = "Deleted stale accounting sessions for user: <b> $allUsernames </b> from database";
                    $logAction .= "Successfully deleted stale accounting sessions for user [$allUsernames] on page: ";

                    include 'library/closedb.php';
            } // if trim

        } // foreach

        $showRemoveDiv = "none";

    <div id="removeDiv" style="display:<?php echo $showRemoveDiv ?>;visibility:visible" >
    <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="get">

        <h302> <?php echo t('title','AccountRemoval') ?> </h302>
        <br/>

        <label for='username' class='form'><?php echo t('all','Username')?></label>
        <input name='username[]' type='text' id='username' value='<?php echo $username ?>' tabindex=100 />
        <br />

        <label for='delradacct' class='form'><?php echo t('all','RemoveRadacctRecords')?></label>
        <select class='form' tabindex=102 name='delradacct' tabindex=101>
            <option value='no'>no</option>
            <option value='yes'>yes</option>
        </select>
        <br />

        <br/><br/>
        <hr><br/>
        <input type="submit" name="submit" value="<?php echo t('buttons','apply') ?>" tabindex=1000
            class='button' />

    </form>
    </div>
<?php
    
    include_once("include/management/autocomplete.php");
    
    if ($autoComplete) {
         $inline_extra_js = "
autoComEdit = new DHTMLSuite.autoComplete();
autoComEdit.add('username','include/management/dynamicAutocomplete.php','_small','getAjaxAutocompleteUsernames');";
    } else {
        $inline_extra_js = "";
    }
    
    print_footer_and_html_epilogue($inline_extra_js);

            printf('<a class="tablenovisit" href="mng-del.php?username=%s&attribute=%s&tablename=radcheck">',
                   urlencode($username_enc), urlencode($id__attribute));
                $divId = sprintf("%s-Tooltip-%d-check", $row[0], $editCounter);
    
            printf('<a class="tablenovisit" href="mng-del.php?username=%s&attribute=%s&tablename=radreply">',
                   urlencode($username_enc), urlencode($id__attribute));
                $divId = sprintf("%s-Tooltip-%d-reply", $row[0], $editCounter);
<form name="listall" method="GET" action="<?= $action ?>">
    var tooltipObj = new DHTMLgoodies_formTooltip();
    tooltipObj.setTooltipPosition('right');
    tooltipObj.setPageBgColor('#EEEEEE');
    tooltipObj.setTooltipCornerSize(15);
    tooltipObj.initFormFieldTooltip();";
<form name="searchusers" method="GET" action="mng-del.php">
        printTableFormControls('username[]', 'mng-del.php', 'searchusers');
?>

        </div><!-- #contentnorightbar -->

        <div id="footer">

<?php
    $log = "visited page: ";
    $logQuery = "performed query for ";
    if (!empty($username)) {
         $logQuery .= "username(s) starting with [$username] ";
    } else {
        $logQuery .= "all usernames ";
    }
    $logQuery .= "on page: ";
    include('page-footer.php');
?>


        </div><!-- #footer -->
    </div>
</div>

<script>
    var tooltipObj = new DHTMLgoodies_formTooltip();
    tooltipObj.setTooltipPosition('right');
    tooltipObj.setPageBgColor('#EEEEEE');
    tooltipObj.setTooltipCornerSize(15);
    tooltipObj.initFormFieldTooltip();
</script>
</body>
</html>
  require_once(DOMPDF_LIB_DIR . "/php-font-lib/classes/Font.php");
<?php
 * Authors:	Liran Tal <liran@enginx.com>
	//include the dompdf class
	require_once("dompdf/dompdf_config.inc.php");
	//include the Pear Mail classes for sending out emails
	@require_once('Mail.php');
	@require_once('Mail/mime.php');
	
	$base = dirname(__FILE__);
	
	/**
	 * createBatchDetailsNotification()
	 * wrapper-function to create the notification to the customer
	 * 
	 * @param		array			customer information array
	 */
	function createBatchDetailsNotification($customerInfo) {
		global $base;
		
		$html = prepareNotificationTemplate($customerInfo);
		$pdfDocument = createPDF($html);
		
		return $pdfDocument;
		
	}
	
	/**
	 * emailNotification()
	 * creates an email message with the pdf and sends it
	 * 
	 * @param		string			pdf binary/string stream
	 * @param		array			customer information array
	 * @param		array			smtp server information
	 * @param		string			from email address of the sender identity
	 */
	function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
		global $base;
		
		if (empty($customerInfo['business_email']))
			return;
		
		$headers = array(	"From"	=>	$from, 
							"Subject"	=>	"Business Pre-Paid Batch Information",
							"Reply-To"=> $from
					);
				
		$mime = new Mail_mime();
		$mime->setTXTBody("Notification letter of service"); 
		$mime->addAttachment($pdfDocument, "application/pdf", "notification.pdf", false, 'base64');
		$body = $mime->get();
		$headers = $mime->headers($headers);
		$mail =& Mail::factory("smtp", $smtpInfo);
		$mail->send($customerInfo['business_email'], $headers, $body);
	
	}
	
	
	/**
	 * prepareNotificationTemplate()
	 * reads the notification (html) template and returns the formatted (html) notification
	 * 
	 * @param		array			Array of notification information
	 * @return		string			HTML notification
	 */
	function prepareNotificationTemplate($customerInfo) {
	
		global $base;
		
		// the HTML template
		$notification_template = "$base/templates/batch_details.html";
		$notification_html_template = file_get_contents($notification_template);
	
		$date = date("Y-m-d");
		
		$business_name = $customerInfo['business_name'];
		$business_owner_name = $customerInfo['business_owner_name']; 
		$business_address = $customerInfo['business_address'];
		$business_phone = $customerInfo['business_phone'];
		$business_email = $customerInfo['business_email'];
		$business_web = $customerInfo['business_web'];
		$batch_details = $customerInfo['batch_details'];
		$batch_active_users = $customerInfo['batch_active_users'];
		$service_plan_info = $customerInfo['service_plan_info'];
	
		$notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
		
		$notification_html_template = str_replace("####__BUSINESS_NAME__####", $business_name, $notification_html_template);
		$notification_html_template = str_replace("####__BUSINESS_OWNER_NAME__####", $business_owner_name, $notification_html_template);
		$notification_html_template = str_replace("####__BUSINESS_ADDRESS__####", $business_address, $notification_html_template);
		$notification_html_template = str_replace("####__BUSINESS_PHONE__####", $business_phone, $notification_html_template);
		$notification_html_template = str_replace("####__BUSINESS_EMAIL__####", $business_email, $notification_html_template);
		$notification_html_template = str_replace("####__BUSINESS_WEB__####", $business_web, $notification_html_template);
		$notification_html_template = str_replace("####__BATCH_DETAILS__####", $batch_details, $notification_html_template);
		$notification_html_template = str_replace("####__BATCH_ACTIVE_USERS__####", $batch_active_users, $notification_html_template);
		$notification_html_template = str_replace("####__SERVICE_PLAN_INFO__####", $service_plan_info, $notification_html_template);
		
		return $notification_html_template;
	}
	
	/**
	 * createPDF()
	 * creates a PDF document for a given html file
	 * 
	 * @param 		string			the html file to convert to pdf			
	 * @return 		string			returns the pdf in binary/string stream
	 */
	function createPDF($html) {
	
		global $base;
		
		// instansiate the pdf document
		$dompdf = new DOMPDF();
		$dompdf->set_base_path("$base/templates/");
		$dompdf->load_html($html);
		$dompdf->render();
	
		$notification_pdf = $dompdf->output();
		
		return $notification_pdf;

	}
<?php
 * Authors:	Liran Tal <liran@enginx.com>
	//include the dompdf class
	require_once("dompdf/dompdf_config.inc.php");
	//include the Pear Mail classes for sending out emails
	@require_once('Mail.php');
	@require_once('Mail/mime.php');
	
	$base = dirname(__FILE__);
	

		global $base;
		
		$html = prepareNotificationTemplate($customerInfo);
		$pdfDocument = createPDF($html);

		file_put_contents("$base/out4.pdf", $pdfDocument);
		
		return $pdfDocument;
		

		global $base;
		
		if (empty($customerInfo['business_email']))
			return;
		
		$headers = array(	"From"	=>	$from, 
							"Subject"	=>	"User Invoice Notification",
							"Reply-To"=> $from
					);
				
		$mime = new Mail_mime();
		$mime->setTXTBody("Notification letter of service"); 
		$mime->addAttachment($pdfDocument, "application/pdf", "invoice.pdf", false, 'base64');
		$body = $mime->get();
		$headers = $mime->headers($headers);
		$mail =& Mail::factory("smtp", $smtpInfo);
		$mail->send($customerInfo['business_email'], $headers, $body);
	
	
		global $base;
		$notification_template = "$base/templates/user_invoice_details.html";

	
	/**
	 * createPDF()
	 * creates a PDF document for a given html file
	 * 
	 * @param 		string			the html file to convert to pdf			
	 * @return 		string			returns the pdf in binary/string stream
	 */
	function createPDF($html) {
	
		global $base;
		
		// instansiate the pdf document
		$dompdf = new DOMPDF();
		$dompdf->set_base_path("$base/templates/");
		$dompdf->load_html($html);
		$dompdf->render();
	
		$notification_pdf = $dompdf->output();
		
		return $notification_pdf;

	}
 * Authors:	Liran Tal <liran@enginx.com>
 *          Miguel GarcÃ­a <miguelvisgarcia@gmail.com>
	//include the dompdf class
	require_once("dompdf/dompdf_config.inc.php");
	//include the Pear Mail classes for sending out emails
	@require_once('Mail.php');
	@require_once('Mail/mime.php');
	
	$base = dirname(__FILE__);
	
	/**
	 * createNotification()
	 * wrapper-function to create the notification to the customer
	 * 
	 * @param		array			customer information array
	 * @param		boolean			true for HTML output, false for PDF output
	 * @return 		string			returns the HTML or PDF output
	 */
	function createNotification($customerInfo, $asHTML = false) {
		global $base;
		
		$html = prepareNotificationTemplate($customerInfo);
		
		if($asHTML) {
			$document = $html;
		}
		else {
			$document = createPDF($html);
		}
		return $document;
	}
	
	/**
	 * emailNotification()
	 * creates an email message with the pdf and sends it
	 * 
	 * @param		string			pdf binary/string stream
	 * @param		array			customer information array
	 * @param		array			smtp server information
	 * @param		string			from email address of the sender identity
	 */
	function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
		global $base;
		
		if (empty($customerInfo['customer_email']))
			return;
		
		$headers = array (
			"From" => $from, 
			"Subject" => "Invoice Information",
			"Reply-To" => $from
		);
				
		$mime = new Mail_mime();
		$mime->setTXTBody("Notification letter of service"); 
		$mime->addAttachment($pdfDocument, "application/pdf", "notification.pdf", false, 'base64');
		$body = $mime->get();
		$headers = $mime->headers($headers);
		$mail =& Mail::factory("smtp", $smtpInfo);
		$mail->send($customerInfo['customer_email'], $headers, $body);
	}
	
	
	/**
	 * prepareNotificationTemplate()
	 * reads the notification (html) template and returns the formatted (html) notification
	 * 
	 * @param		array			Array of notification information
	 * @return		string			HTML notification
	 */
	function prepareNotificationTemplate($customerInfo) {
	
		global $base;
		global $configValues;
		
		// the default HTML template - OLD STYLE
		$notification_template = "$base/templates/user_invoice.html";
		$notification_item_template = null;
		
		// the default HTML template - NEW STYLE
		if(isset($configValues['CONFIG_INVOICE_TEMPLATE'])) {
			$notification_template = "$base/templates/" . $configValues['CONFIG_INVOICE_TEMPLATE'];
			
			if(isset($configValues['CONFIG_INVOICE_ITEM_TEMPLATE'])) {
				$notification_item_template = "$base/templates/" . $configValues['CONFIG_INVOICE_ITEM_TEMPLATE'];
			}
		}
		
		// the default HTML template for current location - NEW STYLE
		if((isset($_SESSION['location_name'])) && ($_SESSION['location_name'] != "default")) {
			if(isset($configValues['CONFIG_LOCATIONS'][$_SESSION['location_name']]['CONFIG_INVOICE_TEMPLATE'])) {
				$notification_template = "$base/templates/" . $configValues['CONFIG_LOCATIONS'][$_SESSION['location_name']]['CONFIG_INVOICE_TEMPLATE'];
				
				if(isset($configValues['CONFIG_LOCATIONS'][$_SESSION['location_name']]['CONFIG_INVOICE_ITEM_TEMPLATE'])) {
					$notification_item_template = "$base/templates/" . $configValues['CONFIG_LOCATIONS'][$_SESSION['location_name']]['CONFIG_INVOICE_ITEM_TEMPLATE'];
				}
			}
		}

		// load template for invoice
		$notification_html_template = file_get_contents($notification_template);
		
		// load template for each invoice item
		if($notification_item_template !== null) {
			$notification_item_html_template = file_get_contents($notification_item_template);
		}

		$date = date("Y-m-d");
		
		$name = $customerInfo['customer_name'];
		$address = $customerInfo['customer_address'];
		$phone = $customerInfo['customer_phone'];
		$email = $customerInfo['customer_email'];
		
		$invoice_details = $customerInfo['invoice_details'];
		$invoice_items = $customerInfo['invoice_items'];
	
		// notification date
		$notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
		
		// user details
		$notification_html_template = str_replace("####__CUSTOMER_NAME__####", $name, $notification_html_template);
		$notification_html_template = str_replace("####__CUSTOMER_ADDRESS__####", $address, $notification_html_template);
		$notification_html_template = str_replace("####__CUSTOMER_PHONE__####", $phone, $notification_html_template);
		$notification_html_template = str_replace("####__CUSTOMER_EMAIL__####", $email, $notification_html_template);
		
		// invoice information
		$notification_html_template = str_replace("####__INVOICE_DETAILS__####", $invoice_details, $notification_html_template);
		$notification_html_template = str_replace("####__INVOICE_ITEMS__####", $invoice_items, $notification_html_template);
		
		// customer details - NEW STYLE			
		$notification_html_template = str_replace("[CustomerId]", $customerInfo['customerId'], $notification_html_template);
		$notification_html_template = str_replace("[CustomerName]", $customerInfo['customerName'], $notification_html_template);
		$notification_html_template = str_replace("[CustomerAddress]", $customerInfo['customerAddress'], $notification_html_template);
		$notification_html_template = str_replace("[CustomerAddress2]", $customerInfo['customerAddress2'], $notification_html_template);
		$notification_html_template = str_replace("[CustomerPhone]", $customerInfo['customerPhone'], $notification_html_template);
		$notification_html_template = str_replace("[CustomerEmail]", $customerInfo['customerEmail'], $notification_html_template);
		$notification_html_template = str_replace("[CustomerContact]", $customerInfo['customerContact'], $notification_html_template);
		
		// invoice details - NEW STYLE
		$notification_html_template = str_replace("[InvoiceNumber]", $customerInfo['invoiceNumber'], $notification_html_template);
		$notification_html_template = str_replace("[InvoiceDate]", $customerInfo['invoiceDate'], $notification_html_template);
		$notification_html_template = str_replace("[InvoiceStatus]", $customerInfo['invoiceStatus'], $notification_html_template);
		$notification_html_template = str_replace("[InvoiceTotalBilled]", $customerInfo['invoiceTotalBilled'], $notification_html_template);
		$notification_html_template = str_replace("[InvoicePaid]", $customerInfo['invoicePaid'], $notification_html_template);
		$notification_html_template = str_replace("[InvoiceDue]", $customerInfo['invoiceDue'], $notification_html_template);
		$notification_html_template = str_replace("[InvoiceNotes]", $customerInfo['invoiceNotes'], $notification_html_template);
		
		// invoice items - NEW STYLE
		if($notification_item_template !== null) {
			$invoiceItems = '';
			
			foreach($customerInfo['invoiceItems'] as $invoiceItem) {
				$invoiceItemTemplate = $notification_item_html_template;
		
				$invoiceItemTemplate = str_replace("[InvoiceItemNumber]", $invoiceItem['invoiceItemNumber'], $invoiceItemTemplate);
				$invoiceItemTemplate = str_replace("[InvoiceItemPlan]", $invoiceItem['invoiceItemPlan'], $invoiceItemTemplate);
				$invoiceItemTemplate = str_replace("[InvoiceItemNotes]", $invoiceItem['invoiceItemNotes'], $invoiceItemTemplate);
				$invoiceItemTemplate = str_replace("[InvoiceItemAmount]", $invoiceItem['invoiceItemAmount'], $invoiceItemTemplate);
				$invoiceItemTemplate = str_replace("[InvoiceItemTaxAmount]", $invoiceItem['invoiceItemTaxAmount'], $invoiceItemTemplate);
				$invoiceItemTemplate = str_replace("[InvoiceItemTotalAmount]", $invoiceItem['invoiceItemTotalAmount'], $invoiceItemTemplate);
				
				$invoiceItems .= $invoiceItemTemplate;
			}

			$notification_html_template = str_replace("[InvoiceItems]", $invoiceItems, $notification_html_template);
		}
		
		// more invoice details - NEW STYLE
		$notification_html_template = str_replace("[InvoiceTotalAmount]", $customerInfo['invoiceTotalAmount'], $notification_html_template);
		$notification_html_template = str_replace("[InvoiceTotalTax]", $customerInfo['invoiceTotalTax'], $notification_html_template);
		
		return $notification_html_template;
	}
	
	/**
	 * createPDF()
	 * creates a PDF document for a given html file
	 * 
	 * @param 		string			the html file to convert to pdf			
	 * @return 		string			returns the pdf in binary/string stream
	 */
	function createPDF($html) {
	
		global $base;
		
		// instansiate the pdf document
		$dompdf = new DOMPDF();
		$dompdf->set_base_path("$base/templates/");
		$dompdf->load_html($html);
		$dompdf->render();
	
		$notification_pdf = $dompdf->output();
		
		return $notification_pdf;
	}
<?php
 * Authors:	Liran Tal <liran@enginx.com>
	//include the dompdf class
	require_once("dompdf/dompdf_config.inc.php");
	//include the Pear Mail classes for sending out emails
	@require_once('Mail.php');
	@require_once('Mail/mime.php');
	
	$base = dirname(__FILE__);
	
	/**
	 * sendWelcomeNotification()
	 * wrapper-function to send notification to the customer
	 * 
	 * @param		array			customer information array
	 * @param		array			smtp server information
	 * @param		string			from email address of the sender identity
	 */
	function sendWelcomeNotification($customerInfo, $smtpInfo, $from) {
		global $base;
		
		if (empty($customerInfo['customer_email']))
			return;
		
		$headers = array(	"From"	=>	$from, 
							"Subject"	=>	"Welcome new customer!",
							"Reply-To"=> $from
					);
		
		$html = prepareNotificationTemplate($customerInfo);
		$pdfDocument = createPDF($html);
		
		$mime = new Mail_mime(); 
		$mime->setTXTBody("Notification letter of service"); 
		$mime->addAttachment($pdfDocument, "application/pdf", "notification.pdf", false, 'base64');
		$body = $mime->get();
		$headers = $mime->headers($headers);
		$mail =& Mail::factory("smtp", $smtpInfo);
		$mail->send($customerInfo['customer_email'], $headers, $body);		
		
	}
	
	/**
	 * prepareNotificationTemplate()
	 * reads the notification (html) template and returns the formatted (html) notification
	 * 
	 * @param		array			Array of notification information
	 * @return		string			HTML notification
	 */
	function prepareNotificationTemplate($customerInfo) {
	
		global $base;
		
		// the HTML template
		$notification_template = "$base/templates/welcome.html";
		$notification_html_template = file_get_contents($notification_template);
	
		$date = date("Y-m-d");
	
		$customer_name = $customerInfo['customer_name'];
		$customer_address = $customerInfo['customer_address']; 
		$customer_phone = $customerInfo['customer_phone'];
		$customer_email = $customerInfo['customer_email'];
	
		$notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
		$notification_html_template = str_replace("####__CUSTOMER_NAME__####", $customer_name, $notification_html_template);
		$notification_html_template = str_replace("####__CUSTOMER_ADDRESS__####", $customer_address, $notification_html_template);
		$notification_html_template = str_replace("####__CUSTOMER_PHONE__####", $customer_phone, $notification_html_template);
		$notification_html_template = str_replace("####__CUSTOMER_EMAIL__####", $customer_email, $notification_html_template);

		return $notification_html_template;
	}
	
	/**
	 * createPDF()
	 * creates a PDF document for a given html file
	 * 
	 * @param 		string			the html file to convert to pdf			
	 * @return 		string			returns the pdf in binary/string stream
	 */
	function createPDF($html) {
	
		global $base;
		
		// instansiate the pdf document
		$dompdf = new DOMPDF();
		$dompdf->set_base_path("$base/templates/");
		$dompdf->load_html($html);
		$dompdf->render();
	
		$notification_pdf = $dompdf->output();
		
		return $notification_pdf;

	}
      
?>    

        <div id="contentnorightbar">
            <h2 id="Intro">
                <a href="#"  onclick="javascript:toggleShowDiv('helpPage')">
                    <?= t('Intro','repbatchdetails.php'); ?><h144>&#x2754;</h144>
                </a>
            </h2>

            <div id="helpPage" style="display:none;visibility:visible"><?= t('helpPage','repbatchdetails') ?><br></div>

<?php
                        onclick="location.href='include/common/notificationsBatchDetails.php?batch_name=<?= urlencode($batch_name_enc) ?>&destination=download'">
?>

</div><!-- #contentnorightbar -->
        
        <div id="footer">
<?php
    include('page-footer.php');
        </div><!-- #footer -->
    </div>
</div>

<script>
    var tooltipObj = new DHTMLgoodies_formTooltip();
    tooltipObj.setTooltipPosition('right');
    tooltipObj.setPageBgColor('#EEEEEE');
    tooltipObj.setTooltipCornerSize(15);
    tooltipObj.initFormFieldTooltip();
</script>

</body>
</html>
<form name="listall" method="GET" action="<?= $action ?>">
                printf('<td><a href="mng-edit.php?username=%s">%s</a>&nbsp;<a href="mng-del.php?username=%s">%s</a></td>',
                       urlencode($this_username), t('all','edit'), urlencode($this_username), t('all','del'));
Fixed code:     
    $inline_extra_js = "
var tooltipObj = new DHTMLgoodies_formTooltip();
tooltipObj.setTooltipPosition('right');
tooltipObj.setPageBgColor('#EEEEEE');
tooltipObj.setTooltipCornerSize(15);
tooltipObj.initFormFieldTooltip()";
    
    print_footer_and_html_epilogue($inline_extra_js);
    <input type="hidden" name="csrf_token" value="<?= dalo_csrf_token() ?>">

    $passwordType = (array_key_exists('passwordType', $_POST) && isset($_POST['passwordType']) &&
                     in_array($_POST['passwordType'], $valid_passwordTypes)) ? $_POST['passwordType'] : "";

<?php 
 * Authors:    Liran Tal <liran@enginx.com>
 *             Filippo Lauria <filippo.lauria@iit.cnr.it>
    include("../../library/checklogin.php");
    $operator = $_SESSION['operator_user'];

    $batch_name = (array_key_exists('batch_name', $_GET) && !empty(str_replace("%", "", trim($_GET['batch_name']))))
                ? str_replace("%", "", trim($_GET['batch_name'])) : "";

    $destination = (array_key_exists('destination', $_GET) && !empty(trim($_GET['destination'])) &&
                    in_array(strtolower(trim($_GET['destination'])), array( "download", "email" )))
                 ? strtolower($_GET['destination']) : "download";

    if (empty($batch_name)) {
        die("you should provide a valid batch name");
    }
    
    include_once("../../notifications/processNotificationBatchDetails.php");
    include_once("../../library/config_read.php");
    
    
    function getBatchDetails($batch_name = NULL) {
        global $configValues;

        if ($batch_name == NULL || empty(trim($batch_name))) {
            return false;
        }

        include("../../library/opendb.php");
        include_once("../../lang/main.php");

        $tableTags = 'style="width: 580px"';
        $tableTrTags = 'style="background-color: #ECE5B6"';

        $customerInfo = array();

        $ths = array(
                        t('all','BatchName'),
                        t('all','HotSpot'),
                        t('all','BatchStatus'),
                        t('all','TotalUsers'),
                        t('all','ActiveUsers'),
                        t('all','PlanName'),
                        t('all','PlanCost'),
                        t('all','BatchCost'),
                        t('all','CreationDate'),
                        t('all','CreationBy'),
                    );

        // start filling in batch details
        $batch_details = "<table $tableTags><tr $tableTrTags>";
        
        foreach ($ths as $th) {
            $batch_details .= sprintf("<th>%s</th>", $th);
        }
        
        $batch_details .= "</tr>";

        $sql = sprintf("SELECT dbh.id AS batch_id, dbh.batch_name, dbh.batch_description, dbh.batch_status,
                               COUNT(DISTINCT(ubi.id)) AS total_users, COUNT(DISTINCT(ra.username)) AS active_users,
                               ubi.planname, dbp.plancost, dbp.plancurrency, dhs.name AS hotspot_name,
                               dbh.creationdate, dbh.creationby, dbh.updatedate, dbh.updateby
                          FROM %s AS dbh LEFT JOIN %s AS ubi ON dbh.id=ubi.batch_id
                                        LEFT JOIN %s AS dbp ON dbp.planname=ubi.planname
                                        LEFT JOIN %s AS dhs ON dbh.hotspot_id=dhs.id
                                        LEFT JOIN %s AS ra ON ra.username=ubi.username
                         WHERE dbh.batch_name='%s'
                         GROUP BY dbh.batch_name", $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'],
                                                   $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'],
                                                   $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'],
                                                   $configValues['CONFIG_DB_TBL_DALOHOTSPOTS'],
                                                   $configValues['CONFIG_DB_TBL_RADACCT'],
                                                   $dbSocket->escapeSimple($batch_name));
        $res = $dbSocket->query($sql);

        
        $active_users_per = 0;
        $total_users = 0;
        $active_users = 0;
        $batch_cost = 0;

        $hotspot_name = "";
        $batch_id = "";
        $planname = "";

        while($row = $res->fetchRow()) {

            foreach ($row as $i => $value) {
                $row[$i] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
            }

            list(
                    $batch_id, $batch_name, $batch_description, $batch_status, $total_users, $active_users, $planname,
                    $plancost, $plancurrency, $hotspot_name, $creationdate, $creationby, $updatedate, $updateby
                ) = $row;

            
            $batch_cost = (intval($active_users) * intval($plancost));

            $tds = array(
                            $batch_name,
                            $hotspot_name,
                            $batch_status,
                            $total_users,
                            $active_users,
                            $planname,
                            $plancost,
                            $batch_cost,
                            $creationdate,
                            $creationby
                        );
                        
            $batch_details .= "<tr>";
            foreach ($tds as $td) {
                $batch_details .= sprintf("<td>%s</td>", $td);
            }
            $batch_details .= "</tr>";

        }

        $batch_details .= "</table>";

        $customerInfo['batch_details'] = $batch_details;


        // filling in plan info
        if (!empty($planname)) {
        
            $sql = sprintf("SELECT planId, planName, planRecurringPeriod, planCost, planSetupCost, planTax, planCurrency
                              FROM %s WHERE planName='%s'", $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'],
                                                            $dbSocket->escapeSimple($planname));
            $res = $dbSocket->query($sql);
            $row = $res->fetchRow(DB_FETCHMODE_ASSOC);
            
            echo $sql;
            echo $row;
            exit;

            $service_plan_info = "<table $tableTags>";

            foreach ($row as $rowName => $rowValue) {
                $rowName = htmlspecialchars($rowName, ENT_QUOTES, 'UTF-8');
                $rowValue = htmlspecialchars($rowValue, ENT_QUOTES, 'UTF-8');

                $service_plan_info .= "<tr $tableTrTags>"
                                    . sprintf("<th>%s</th>", $rowName)
                                    . sprintf("<td>%s</td>", $rowValue)
                                    . "</tr>";
            }

            $service_plan_info .= "</table>";
            $customerInfo['service_plan_info'] = $service_plan_info;
        }

        // filling in business info
        if (!empty($hotspot_name)) {
            $sql = sprintf("SELECT id, name, owner, address, companyphone, companyemail, companywebsite
                              FROM %s WHERE name='%s'", $configValues['CONFIG_DB_TBL_DALOHOTSPOTS'],
                                                        $dbSocket->escapeSimple($hotspot_name));
            $res = $dbSocket->query($sql);
            $row = $res->fetchRow(DB_FETCHMODE_ASSOC);

            $customerInfo['business_name'] = $row['name'];
            $customerInfo['business_owner_name'] = $row['owner'];
            $customerInfo['business_address'] = $row['address'];
            $customerInfo['business_phone'] = $row['companyphone'];
            $customerInfo['business_email'] = $row['companyemail'];
            $customerInfo['business_web'] = $row['companywebsite'];
        }

        // active users details
        $sql = sprintf("SELECT ubi.id, ubi.username, ra.acctstarttime, dbh.batch_name
                          FROM %s AS ubi, %s AS ra, %s AS dbh
                         WHERE ubi.batch_id=dbh.id
                           AND ubi.batch_id='%s'
                           AND ubi.username=ra.username
                         GROUP BY ubi.username
                         ORDER BY id, ra.radacctid ASC", $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'],
                                                         $configValues['CONFIG_DB_TBL_RADACCT'],
                                                         $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'],
                                                         $dbSocket->escapeSimple($batch_id));
        $res = $dbSocket->query($sql);

        $ths = array(
                        t('all','BatchName'),
                        t('all','Username'),
                        t('all','StartTime'),
                    );

        $batch_active_users = "<table $tableTags><tr $tableTrTags>";
        foreach ($ths as $th) {
            $batch_active_users .= sprintf("<th>%s</th>", $th);
        }
        $batch_active_users .= "</tr>";
        
        $active_users_per = 0;
        $total_users = 0;
        $active_users = 0;
        $batch_cost = 0;
        while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
            foreach ($row as $i => $value) {
                $row[$i] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
            }

            list($id, $username, $acctstarttime, $batch_name) = $row;

            $tds = array( $username, $acctstarttime, $batch_name );

            $batch_active_users .= "<tr>";
            foreach ($tds as $td) {
                $batch_active_users .= sprintf("<td>%s</td>", $td);
            }
            $batch_active_users .= "</tr>";
        }
        
        $batch_active_users .= "</table>";
        $customerInfo['batch_active_users'] = $batch_active_users;


        require("../../library/closedb.php");

        return $customerInfo;
    }
    
    $customerInfo = getBatchDetails($batch_name);
    
    if ($customerInfo === false) {
        die("error when loading batch details");
    }

    $smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
    $smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
    $smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
    $from = $configValues['CONFIG_MAIL_SMTPFROM'];

    $pdfDocument = @createBatchDetailsNotification($customerInfo);

    if ($destination == "download") {
        $filename = sprintf('batch_notification_invoice_%s.pdf', date("Ymd"));
        $size = strlen($pdfDocument);
        
        header("Content-type: application/pdf");
        header(sprintf("Content-Disposition: attachment; filename=%s; size=%d", $filename, $size));
        print $pdfDocument;
        
    } else if ($destination == "email") {
        
        @emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
        
        $redirect = (array_key_exists('PREV_LIST_PAGE', $_SESSION) && !empty(trim($_SESSION['PREV_LIST_PAGE'])))
                  ? trim($_SESSION['PREV_LIST_PAGE']) : "/mng-batch.php";
        header("Location: " . $redirect);
        
    }
    
<?php 
 * Authors:    Liran Tal <liran@enginx.com>
 *             Filippo Lauria <filippo.lauria@iit.cnr.it>
    // prevent this file to be directly accessed
    if (strpos($_SERVER['PHP_SELF'], '/include/common/notificationsUserDetailsInvoice.php') !== false) {
        header("Location: ../../index.php");
        exit;
    }
    require_once(dirname(__FILE__)."/../../notifications/processNotificationUserDetailsInvoice.php");
    //require_once(dirname(__FILE__)."/../../library/config_read.php");
    /*
    isset($_GET['batch_name']) ? $batch_name = $_GET['batch_name'] : $batch_name = "";
    isset($_GET['destination']) ? $destination = $_GET['destination'] : $destination = "download";
    
    //test
    $batch_name = "batch_test6"; 
    
    if ($batch_name != "") {
        $customerInfo = @getBatchDetails($batch_name);
        
        $smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
        $smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
        $smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
        $from = $configValues['CONFIG_MAIL_SMTPFROM'];
        
        $pdfDocument = @createBatchDetailsNotification($customerInfo);
        
        if ($destination == "download") {
            
            header("Content-type: application/pdf");
            header("Content-Disposition: attachment; filename=batch_notification_invoice_" . date("Ymd") . ".pdf; size=" . strlen($pdfDocument));
            print $pdfDocument;
            
        } else if ($destination == "email") {
            
            @emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
            header("Location: ".$_SERVER['HTTP_REFERER']);
        }
        
    }
    */
    
    function getCustomerInfo($row) {
    
        //global $configValues;
        
        $customerInfo = array();
        
        getCustomerInfo_customer_info($row, $customerInfo);
        getCustomerInfo_service_plan($row, $customerInfo);
        
        return $customerInfo;
        
    }
    
    
    
    function getCustomerInfo_customer_info($row, &$customerInfo) {
                
        global $configValues;
        require(dirname(__FILE__)."/../../lang/main.php");
        
        $tableTags = "width='580px' ";
        $tableTrTags = "bgcolor='#ECE5B6'";
        
        
        if (!empty($row['email1']))
            $invoice_email = $row['email1'];
        else if (!empty($row['email2']))
            $invoice_email = $row['email2'];
        else if (!empty($row['email3']))
            $invoice_email = $row['email3'];
        else
            $invoice_email = "";
        
        if (!empty($row['mobilephone']))
            $invoice_phone = $row['mobilephone'];
        else if (!empty($row['workphone']))
            $invoice_phone = $row['mobilephone'];
        else if (!empty($row['homephone']))
            $invoice_phone = $row['homephone'];
        else
            $invoice_phone = "Unavailable";
            
        $invoice_address = "";
        if (!empty($row['address']))
            $invoice_address = $row['address'];
        
        if (!empty($row['city']))
            $invoice_address .= ", ".$row['city'];
        
        if (!empty($row['state']))
            $invoice_address .= "<br/>".$row['state'];
        
        if (!empty($row['zip']))
            $invoice_address .= " ".$row['zip'];
        
        if (empty($invoice_address))
            $invoice_address = "Unavailable";
        
        $customerInfo['business_name'] = $row['firstname']. " " .$row['lastname'];
        $customerInfo['business_address'] = $invoice_address;
        $customerInfo['business_phone'] = $invoice_phone;
        $customerInfo['business_email'] = $invoice_email;
    }
    
    
    function getCustomerInfo_service_plan($row, &$customerInfo) {
                
        global $configValues;
        require(dirname(__FILE__)."/../../lang/main.php");
        
        $tableTags = "width='580px' ";
        $tableTrTags = "bgcolor='#ECE5B6'";
        
        $service_plan_info = "";
        $service_plan_info = "<table $tableTags>";
        
        $service_plan_info .= "".
                    "<tr $tableTrTags'>
                    <td>".t('all','Username')."</td>
                    <td>".$row['username']."</td>
                    </tr>".
        
                    "<tr $tableTrTags'>
                    <td>".t('all','PlanName')."</td>
                    <td>".$row['planname']."</td>
                    </tr>".
                    "<tr $tableTrTags'>
                    <td>".t('all','PlanRecurring')."</td>
                    <td>".$row['planRecurring']."</td>
                    </tr>".
                    "<tr $tableTrTags'>
                    <td>".t('all','PlanRecurringPeriod')."</td>
                    <td>".$row['planRecurringPeriod']."</td>
                    </tr>".
                    "<tr $tableTrTags'>
                    <td>".t('all','PlanCost')."</td>
                    <td>".$row['planCost']."</td>
                    </tr>".
                    "<tr $tableTrTags'>
                    <td>".t('all','NextBill')."</td>
                    <td>".$row['nextbill']."</td>
                    </tr>".
                    "<tr $tableTrTags'>
                    <td>".t('all','BillDue')."</td>
                    <td>".$row['billdue']."</td>
                    </tr>".
                    "";
        
        $service_plan_info .= "</table>";
        $customerInfo['service_plan_info'] = $service_plan_info;    
        
    }
    
    
    /*
    function getBatchDetails($batch_name = NULL) {
        
        require(dirname(__FILE__)."/../../library/opendb.php");
        require_once(dirname(__FILE__)."/../../lang/main.php");
        
        global $configValues;
        
        if ($batch_name == NULL || empty($batch_name))
            exit;
            
            
        $tableTags = "width='580px' ";
        $tableTrTags = "bgcolor='#ECE5B6'";
        
        $customerInfo = array();
        
        $sql = "SELECT ".
            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id,".
            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name,".
            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_description,".
            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_status,".
            
            "COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id)) as total_users,".
            "COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_RADACCT'].".username)) as active_users,".
            $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname,".
            $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancost,".
            $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancurrency,".
            $configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".name as HotspotName,".
            
            
            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationdate,".
            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationby,".
            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updatedate,".
            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updateby ".
            " FROM ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
            " LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].
            " ON ".
            "(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id = ".
            $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id) ".
            " LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
            " ON ".
            "(".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".planname = ".
            $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname) ".
            " LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
            " ON ".
            "(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".hotspot_id = ".
            $configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".id) ".
            
            " LEFT JOIN ".$configValues['CONFIG_DB_TBL_RADACCT'].
            " ON ".
            "(".$configValues['CONFIG_DB_TBL_RADACCT'].".username = ".
            $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username) ".
            
            " WHERE ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name = '$batch_name' ".
            " GROUP by ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ";
            
        $res = $dbSocket->query($sql);
        $batch_details = "";
        
        $batch_details .= "<table $tableTags><tr $tableTrTags>
                    <td> 
            ".t('all','BatchName')."
            </td>
    
            <td> 
            ".t('all','HotSpot')."
            </td>
    
            <td> 
            ".t('all','BatchStatus')."
            </td>
            
            <td> 
            ".t('all','TotalUsers')."
            </td>
    
            <td> 
            ".t('all','ActiveUsers')."
            </td>
    
            <td> 
            ".t('all','PlanName')."
            </td>
    
            <td> 
            ".t('all','PlanCost')."
            </td>
    
            <td> 
            ".t('all','BatchCost')."
            </td>
    
            <td> 
            ".t('all','CreationDate')."
            </td>
    
            <td> 
            ".t('all','CreationBy')."
            </td>
    
            </tr>";
        
        $active_users_per = 0;
        $total_users = 0;
        $active_users = 0;
        $batch_cost = 0;
        
        $hotspot_name = "";
        $batch_id = "";
        $planname = "";
        
        while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
            
            $batch_id = $row['id'];
            $hotspot_name = $row['HotspotName'];
            $batch_status = $row['batch_status'];
            $plancost = $row['plancost'];
            $total_users = $row['total_users'];
            $active_users = $row['active_users'];
            $batch_cost = ($active_users * $plancost);
            $plan_currency = $row['plancurrency'];
            $planname = $row['planname'];
            
    
            $batch_details .= "
                    <tr>
                    
                    <td>".$row['batch_name']."
                        
                    </td>
                    
                    <td>".$hotspot_name."
                        
                    </td>
            
                    <td>".$batch_status."
                        
                    </td>
                    
                    <td>".$total_users."
                        
                    </td>
    
                    <td>".$active_users."
                        
                    </td>
    
                    <td>".
                        $row['planname']."
                    </td>
    
                    <td>".$plancost."
                    </td>
    
                    <td>".$batch_cost."
                    </td>
                    
                    <td>".
                        $row['creationdate']."
                    </td>
    
                    <td>".
                        $row['creationby']."
                    </td>
    
                </tr>
            ";
            
            
        }
        
        $batch_details .= "</table>";
        
        $customerInfo['batch_details'] = $batch_details;
        
        
        
        
        
        
        
        
        
        
        $sql = "SELECT planId, planName, planRecurringPeriod, planCost, planSetupCost, planTax, planCurrency FROM ".
                $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
                " WHERE planName = '".$planname."'";
        $res = $dbSocket->query($sql);
        $row = $res->fetchRow(DB_FETCHMODE_ASSOC);
        
        $service_plan_info = "";
        $service_plan_info = "<table $tableTags>";
        
        foreach($row as $rowName => $rowValue) {
        
            $service_plan_info .= "<tr $tableTrTags'>
                        <td>$rowName</td>
                        <td>$rowValue</td>
                        </tr>";
        
        }
        
        $service_plan_info .= "</table>";
        $customerInfo['service_plan_info'] = $service_plan_info;
        
        
        $sql = "SELECT id, name, owner, address, companyphone, companyemail, companywebsite FROM ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
                    " WHERE name='".$hotspot_name."'";
        $res = $dbSocket->query($sql);
        $row = $res->fetchRow(DB_FETCHMODE_ASSOC);
        
        $customerInfo['business_name'] = $row['name'];
        $customerInfo['business_owner_name'] = $row['owner'];
        $customerInfo['business_address'] = $row['address'];
        $customerInfo['business_phone'] = $row['companyphone'];
        $customerInfo['business_email'] = $row['companyemail'];
        $customerInfo['business_web'] = $row['companywebsite'];
                
        
        
        
        
        
        $batch_active_users = "";
        
        $sql = "SELECT ".
                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id,".
                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username,".
                $configValues['CONFIG_DB_TBL_RADACCT'].".acctstarttime,".
                $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ".
                
                " FROM ".
                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].",".
                $configValues['CONFIG_DB_TBL_RADACCT'].",".
                $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
                
                " WHERE ".
                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = ".
                $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id".
                " AND ".
                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = '$batch_id' ".
                " AND ".
                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username = ".
                $configValues['CONFIG_DB_TBL_RADACCT'].".username".
    
                " GROUP by ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username ".
                " ORDER BY id ,".$configValues['CONFIG_DB_TBL_RADACCT'].".radacctid ASC ";
                
        $res = $dbSocket->query($sql);
        
        $batch_active_users = "<table $tableTags><tr $tableTrTags'>
            <td> 
            ".t('all','BatchName')."
            </td>
    
            <td> 
            ".t('all','Username')."
            </td>
    
            <td> 
            ".t('all','StartTime')."
            </td>
    
            </tr>";
            
        $active_users_per = 0;
        $total_users = 0;
        $active_users = 0;
        $batch_cost = 0;
        while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
    
            $username = $row['username'];
            $acctstarttime = $row['acctstarttime'];
            $batch_name = $row['batch_name'];
            
            $batch_active_users .= "
                    <tr>
                    
                    <td>".$batch_name."
                    </td>
    
                    <td>".$username."
                    </td>
    
                    <td>".$acctstarttime."
                    </td>
    
                </tr>
            ";
        }
        

        $batch_active_users .= "</table>";
        $customerInfo['batch_active_users'] = $batch_active_users;
        
        
        require(dirname(__FILE__)."/../../library/closedb.php");
        
        return $customerInfo;
        
        
    }
    */
?>
<?php 
 * Authors:    Liran Tal <liran@enginx.com>
 *             Filippo Lauria <filippo.lauria@iit.cnr.it>
    include("../../library/checklogin.php");
    $operator = $_SESSION['operator_user'];
    $invoice_id = (array_key_exists('invoice_id', $_GET) && !empty(str_replace("%", "", trim($_GET['invoice_id']))))
                ? str_replace("%", "", trim($_GET['invoice_id'])) : "";
    $destination = (array_key_exists('destination', $_GET) && !empty(trim($_GET['destination'])) &&
                    in_array(strtolower(trim($_GET['destination'])), array( "download", "email", "preview" )))
                 ? strtolower($_GET['destination']) : "preview";
                 
    if (empty($invoice_id)) {
        die("you should provide a valid invoice id");
    }
    require_once("../../notifications/processNotificationUserInvoice.php");
    require_once("../../library/config_read.php");
    
    $customerInfo = @getInvoiceDetails($invoice_id);
    
    if ($customerInfo === false) {
        die("error when loading invoice");
    }
    
    $document = @createNotification($customerInfo, $destination == "preview");
        
    if ($destination == "download") {
        $filename = sprintf('notification_invoice_%s.pdf', date("Ymd"));
        $size = strlen($document);
        
        header("Content-type: application/pdf");
        header(sprintf("Content-Disposition: attachment; filename=%s; size=%d", $filename, $size));
            
        print $document;
    } else if ($destination == "email") {
        $smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
        $smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
        $smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
        
        $from = $configValues['CONFIG_MAIL_SMTPFROM'];
        
        @emailNotification($document, $customerInfo, $smtpInfo, $from);
        $redirect = (array_key_exists('PREV_LIST_PAGE', $_SESSION) && !empty(trim($_SESSION['PREV_LIST_PAGE'])))
                  ? trim($_SESSION['PREV_LIST_PAGE']) : "/bill-invoice.php";
        header("Location: " . $redirect);
            
    } else /*if ($destination == "preview")*/ {
        print $document;
        //~ $result = file_put_contents(dirname(__FILE__).'/../../notifications/templates/invoice_preview.html', $htmlDocument);
        //~ header('Location: ../../notifications/templates/invoice_preview.html');
    }
    
    
    
    function getInvoiceDetails($invoice_id = NULL) {
        global $configValues;
        
        if ($invoice_id == NULL || empty($invoice_id)) {
            exit;
        }
        
        include("../../library/opendb.php");
        include_once("../../lang/main.php");
            
        $tableTags = 'style="width: 580px"';
        $tableTrTags = 'style="background-color: #ECE5B6"';
        
        // get invoice details
        $sql = sprintf("SELECT a.id, a.date, a.status_id, a.type_id, a.user_id, a.notes, b.contactperson, b.username,
                               b.company, b.city, b.state, b.country, b.zip, b.address, b.email, b.emailinvoice, b.phone,
                               f.value AS type, c.value AS status, COALESCE(e2.totalpayed, 0) AS totalpayed,
                               COALESCE(d2.totalbilled, 0) AS totalbilled
                          FROM %s AS a INNER JOIN %s AS b ON a.user_id=b.id
                                       INNER JOIN %s AS c ON a.status_id=c.id
                                       INNER JOIN %s AS f ON a.type_id=f.id
                                       LEFT JOIN (
                                                    SELECT SUM(d.amount + d.tax_amount) AS totalbilled, invoice_id,
                                                           amount, tax_amount, notes, plan_id
                                                      FROM %s AS d
                                                     GROUP BY d.invoice_id
                                                 ) AS d2 ON d2.invoice_id=a.id
                                       LEFT JOIN %s AS bp2 ON bp2.id=d2.plan_id
                                       LEFT JOIN (
                                                    SELECT SUM(e.amount) AS totalpayed, invoice_id
                                                      FROM %s AS e
                                                     GROUP BY e.invoice_id
                                                 ) AS e2 ON e2.invoice_id=a.id
                         WHERE a.id=%d
                         GROUP BY a.id", $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICE'],
                                         $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'],
                                         $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICESTATUS'],
                                         $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICETYPE'],
                                         $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICEITEMS'],
                                         $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'],
                                         $configValues['CONFIG_DB_TBL_DALOPAYMENTS'],
                                         $dbSocket->escapeSimple($invoice_id));
        $res = $dbSocket->query($sql);    
        $invoiceDetails = $res->fetchRow(DB_FETCHMODE_ASSOC);
        $customer_email = (empty($invoiceDetails['email'])) ? $invoiceDetails['emailinvoice'] : $invoiceDetails['email'];
            
        // populate user contact informatin
        $customerInfo['customer_name'] = $invoiceDetails['contactperson'];
        $customerInfo['customer_address'] = $invoiceDetails['address'] . " " . $invoiceDetails['city'] . " " . $invoiceDetails['state'];
        $customerInfo['customer_email'] = $customer_email;
        $customerInfo['customer_phone'] = $invoiceDetails['phone'];
        
        // populate user invoice details
        $balance = floatval($invoiceDetails['totalpayed'] - $invoiceDetails['totalbilled']);
        
        $details = array(
                            array( t('all','ClientName'), $invoiceDetails['contactperson'] ),
                            array( t('all','Invoice'), $invoice_id ), 
                            array( t('all','Date'), $invoiceDetails['date'] ), 
                            array( t('all','TotalBilled'), $invoiceDetails['totalbilled'] ), 
                            array( t('all','TotalPayed'), $invoiceDetails['totalpayed'] ), 
                            array( t('all','Balance'), $balance ), 
                            array( t('all','Status'), $invoiceDetails['status'] ), 
                            array( t('ContactInfo','Notes'), $invoiceDetails['notes'] ), 
                        );
        
        $invoice_details = "";
        foreach ($details as $detail) {
            list( $caption, $data ) = $detail;
            $invoice_details .= sprintf("<b>%s</b>; %s<br>", $caption, $data);
        }
        $invoice_details .= "<br><br>";
        $customerInfo['invoice_details'] = $invoice_details;
        
        // populate customer data - NEW STYLE
        $customerInfo['customerId'] = $invoiceDetails['user_id'];
        $customerInfo['customerName'] = (isset($invoiceDetails['company']) ? $invoiceDetails['company'] : $invoiceDetails['contactperson']);
        $customerInfo['customerAddress'] = $invoiceDetails['address'];
        $customerInfo['customerAddress2'] = $invoiceDetails['zip'] . ' '. $invoiceDetails['city'] . ' ' .
                                            $invoiceDetails['state'] . ' ' . $invoiceDetails['country'];
        $customerInfo['customerEmail'] = $invoiceDetails['email'];
        $customerInfo['customerPhone'] = $invoiceDetails['phone'];
        $customerInfo['customerContact'] = $invoiceDetails['contactperson'];
        
        $customerInfo['invoiceNumber'] = $invoice_id;
        $customerInfo['invoiceDate'] = date('Y-m-d', strtotime($invoiceDetails['date']));
        $customerInfo['invoiceStatus'] = strtoupper($invoiceDetails['status']);
        $customerInfo['invoiceTotalBilled'] = $invoiceDetails['totalbilled'];
        $customerInfo['invoicePaid'] = $invoiceDetails['totalpayed'];
        $customerInfo['invoiceDue'] = $balance;
        $customerInfo['invoiceNotes'] = $invoiceDetails['notes'];
        $ths = array(
                        t('title','Plan']),
                        t('all','Tax']),
                        t('all','Amount']),
                        t('ContactInfo','Notes']),
                    );
        // populate user invoice items
        $invoice_items = "<table $tableTags><tr $tableTrTags>";
        foreach ($ths as $th) {
            $invoice_items .= sprintf("<th>%s</th>", $th);
        }
        $invoice_items .= "</tr>";
        
        // get all invoice items
        $sql = sprintf("SELECT a.id, a.plan_id, a.amount, a.tax_amount, a.notes, b.planName
                          FROM %s a LEFT JOIN %s b ON a.plan_id=b.id
                         WHERE a.invoice_id=%d
                         ORDER BY a.id ASC", $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICEITEMS'],
                                             $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'],
                                             $dbSocket->escapeSimple($invoice_id));
        $res = $dbSocket->query($sql);
        
        // initialize invoice items - NEW STYLE
        $invoiceItems = array();
        $invoiceItemsNumber = 1;
        $invoiceItemsTotalAmount = 0;
        $invoiceItemsTotalTax = 0;
        
        while ($row = $res->fetchRow()) {
            foreach ($row as $i => $value) {
                $row[$i] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
            }

            list($id, $plan_id, $amount, $tax_amount, $notes, $planName) = $row;

            $tds = array( $planName, $amount, $tax_amount, $notes );
            
            $invoice_items .= "<tr>";
            foreach ($tds as $td) {
                $invoice_items .= sprintf("<td>%s</td>", $td);
            }
            $invoice_items .= "</tr>";

            // populate invoice items - NEW STYLE
            $invoiceItem = array();
            
            $invoiceItem['invoiceItemNumber'] = sprintf('%02d', $invoiceItemsNumber);
            $invoiceItem['invoiceItemPlan'] = $planName;
            $invoiceItem['invoiceItemNotes'] = $notes;
            $invoiceItem['invoiceItemAmount'] = $amount;
            $invoiceItem['invoiceItemTaxAmount'] = $tax_amount;
            $invoiceItem['invoiceItemTotalAmount'] = intval($amount) + intval($tax_amount);
            
            $invoiceItems[] = $invoiceItem;
            $invoiceItemsTotalAmount += intval($amount);
            $invoiceItemsTotalTax += intval($tax_amount);
            
            ++$invoiceItemsNumber;
        }

        $invoice_items .= "</table>";
        
        $customerInfo['invoice_items'] = $invoice_items;
        
        // populate invoice items - NEW STYLE
        $customerInfo['invoiceItems'] = $invoiceItems;
        $customerInfo['invoiceTotalAmount'] = $invoiceItemsTotalAmount;
        $customerInfo['invoiceTotalTax'] = $invoiceItemsTotalTax;
        
        include("../../library/closedb.php");

        return $customerInfo;
    }
    
<?php 
 * Authors:    Liran Tal <liran@enginx.com>
 *             Filippo Lauria <filippo.lauria@iit.cnr.it>
 
    // prevent this file to be directly accessed
    if (strpos($_SERVER['PHP_SELF'], '/include/common/notificationWelcome.php') !== false) {
        header("Location: ../../index.php");
        exit;
    }
 
    include_once("notifications/processNotificationWelcome.php");
    
    // we (try to) init the email
    if (isset($email) && !empty($email)) {
        $invoice_email = $email;
    } else if (isset($bi_emailinvoice) && !empty($bi_emailinvoice)) {
        $invoice_email = $bi_emailinvoice;
    } else if (isset($bi_email) && !empty($bi_email)) {
        $invoice_email = $bi_email;
    } else {
        $invoice_email = "";
    }
    
    
    // we (try to) init the phone
    if (isset($mobilephone) && !empty($mobilephone)) {
        $invoice_phone = $mobilephone;
    } else if (isset($workphone) && !empty($workphone)) {
        $invoice_phone = $workphone;
    } else if (isset($homephone) && !empty($homephone)) {
        $invoice_phone = $homephone;
    } else {
        $invoice_phone = "(n/a)";
    }
    
    // we also (try to) init the address
    $invoice_address = "";
    if (isset($ui_address) &&!empty($ui_address)) {
        $invoice_address = $ui_address;
    }
    
    if (isset($ui_city) && !empty($ui_city)) {
        $invoice_address .= ", " . $ui_city;
    }
    
    if (isset($ui_state) && !empty($ui_state)) {
        $invoice_address .= "<br>" . $ui_state;
    }
    
    if (isset($ui_zip) && !empty($ui_zip)) {
        $invoice_address .= " " . $ui_zip;
    }
    
    if (empty($invoice_address)) {
        $invoice_address = "(n/a)";
    }
    
    // set SMTP server info
    $smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
    $smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
    $smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
    
    // set the from field
    $from = $configValues['CONFIG_MAIL_SMTPFROM'];
    
    // set customer info
    $customerInfo = array();
    $customerInfo['customer_name'] = sprintf("%s %s", $firstname, $lastname);
    $customerInfo['customer_address'] = $invoice_address;
    $customerInfo['customer_phone'] = $invoice_phone;
    $customerInfo['customer_email'] = $invoice_email;
    
    $customerInfo['service_plan_name'] = $planName;
    
    @sendWelcomeNotification($customerInfo, $smtpInfo, $from);
?>
    $ticketInformation = "<strong>Information</strong>:<br>to use this card, please connect your device to the nearest ssid."
            $batch_name = (array_key_exists('batch_name', $_POST) && !empty(trim($_POST['batch_name'])))
                        ? htmlspecialchars(trim($_POST['batch_name']), ENT_QUOTES, 'UTF-8') : "";
            
                $ticketInformation = "<strong>Information</strong>:<br>" . htmlspecialchars(trim($_POST['ticketInformation']), ENT_QUOTES, 'UTF-8');
            
            $ticketCost = "";
            $ticketTime = "";
            
            }
            
            $card_body_height = 10;
            $card_foot_height = 30;
            if (!empty($ticketCost)) {
                $card_foot_height -= 5;
                $card_body_height += 5;
            }
            if (!empty($ticketTime)) {
                $card_foot_height -= 5;
                $card_body_height += 5;
        <title><?= (!empty($batch_name)) ? $batch_name : "user cards" ?></title>
@page {
    size: 21cm 29.7cm;
    margin: 0;
}

body {
    font-family: Tahoma;
    padding: 1cm;
}

    height: <?= $card_body_height ?>mm;
    width: 42mm;
    font-size: 8pt;
.card-body table tr {
    border: 0;
    margin: 0;
    height: 5mm;
    padding: 0;
}

.card-body table th {
    text-align: right;
}

.card-body table td {
    text-align: left;
}

.card-foot {
    height: <?= $card_foot_height ?>mm;
    width: 42mm;
    padding: 1mm;
    font-size: 8pt;
            $trs = array(
                            "User" => $username,
                            "Pass" => $password
                        );
            
            if (!empty($ticketTime)) {
                $trs["Validity"] = $ticketTime;
            }
            
            if (!empty($ticketCost)) {
                $trs["Price"] = $ticketCost;
            }
            
               . '<table>';

            foreach ($trs as $label => $value) {
                printf('<tr><th>%s:</th><td>%s</td></tr>', $label, htmlspecialchars($value, ENT_QUOTES, 'UTF-8'));
            }

            echo '</table>'


    // implement a sort of "dynamic redirect finder" based on the number of "/" in the PHP_SELF value
    $count = substr_count($_SERVER['PHP_SELF'], "/", 1);
    $location = str_repeat("../", $count) . "login.php";

    header("Location: " . $location);
                    $exportForm .= sprintf('<form target="_blank" id="%s" ', $form_id)
                                 . sprintf('<input style="display: none" type="hidden" name="batch_name" value="%s">',
                                 . '<input style="display: none" type="hidden" name="type" value="batch">';
                        $exportForm .= sprintf('<input style="display: none" type="hidden" name="accounts[%d][0]" value="%s">', $i, htmlspecialchars($u, ENT_QUOTES, 'UTF-8'))
                                     . sprintf('<input style="display: none" type="hidden" name="accounts[%d][1]" value="%s">', $i, htmlspecialchars($p, ENT_QUOTES, 'UTF-8'));
                    
                    
                    $exportForm .= '<fieldset><label for="ticketInformation" class="form">Description</label>'
                                 . '<textarea class="form" id="ticketInformation" name="ticketInformation">'
                                 . 'to use this card, please connect your device to the nearest ssid.' . "\n"
                                 . 'Open your web browser and enter each needed field.</textarea>';
                    $exportForm .= '</fieldset></form>';
                    $exportForm .= sprintf('<input style="margin: 10px" type="button" onclick="%s" value="CSV Download">', $onclick);
                    $exportForm .= sprintf('<input style="margin: 10px" type="button" onclick="%s" value="Printable Tickets">', $onclick);

    $valid_tablenames = array(
                                $configValues['CONFIG_DB_TBL_RADCHECK'],
                                $configValues['CONFIG_DB_TBL_RADREPLY'],
                                $configValues['CONFIG_DB_TBL_RADGROUPREPLY'],
                                $configValues['CONFIG_DB_TBL_RADGROUPCHECK']
                             );

    include('library/opendb.php');

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (array_key_exists('csrf_token', $_POST) && isset($_POST['csrf_token']) && dalo_check_csrf_token($_POST['csrf_token'])) {
                
            $id__attribute = (array_key_exists('attribute', $_POST) && !empty(trim($_POST['attribute'])) &&
                              preg_match("/__/", trim($_POST['attribute'])) !== false) ? trim($_POST['attribute']) : "";
            
            $tablename = (!empty($id__attribute) && array_key_exists('tablename', $_POST) &&
                          !empty(trim($_POST['tablename'])) && in_array(trim($_POST['tablename']), $valid_tablenames))
                       ? trim($_POST['tablename']) : "";

            $delradacct = (array_key_exists('delradacct', $_POST) && strtolower(trim($_POST['delradacct'])) == 'yes');


            // validate values
            $usernames = array();
            
            if (array_key_exists('username', $_POST) && !empty($_POST['username'])) {
                
                $tmp = (!is_array($_POST['username'])) ? array($_POST['username']) : $_POST['username'];
                foreach ($tmp as $value) {
                    
                    $value = trim(str_replace("%", "", $value));
                    
                    if (!in_array($value, $usernames)) {
                        $usernames[] = $value;
                    }
                
                if (count($usernames) > 0) {
                
                    if (!empty($id__attribute) && !empty($tablename)) {
                        
                        $sql = sprintf("SELECT COUNT(id) FROM %s WHERE username='%s'",
                                       $configValues['CONFIG_DB_TBL_RADCHECK'], $dbSocket->escapeSimple($usernames[0]));
                        $res = $dbSocket->query($sql);
                        $logDebugSQL .= "$sql;\n";
                        
                        $check_attr_count = intval($res->fetchrow()[0]);
                        
                        $sql = sprintf("SELECT COUNT(id) FROM %s WHERE username='%s' AND attribute='Auth-Type' OR attribute LIKE '%%-Password'",
                                       $configValues['CONFIG_DB_TBL_RADCHECK'], $dbSocket->escapeSimple($usernames[0]));
                        $res = $dbSocket->query($sql);
                        $logDebugSQL .= "$sql;\n";
                        
                        $check_auth_attr_count = intval($res->fetchrow()[0]);
                        
                        list($columnId, $attribute) = explode("__", $id__attribute);
                        $attribute = trim($attribute);
                        $columnId = intval(trim($columnId));
                        
                        if ($tablename == 'radcheck' && $check_attr_count == 1 ||
                            (($attribute == 'Auth-Type' || preg_match("/-Password$/", $attribute) !== false) && $check_auth_attr_count == 1)) {
                            // if operator wants to remove the last check attribute
                            // or the last "password-like" check attribute
                            // they should delete all user related info stored in the db
                            
                            $format = "Cannot delete the last check (password like?) attribute for the selected user (<strong>%s</strong>)";
                            $failureMsg = sprintf($format,
                                                  htmlspecialchars($usernames[0], ENT_QUOTES, 'UTF-8'));
                            $logAction = sprintf("$format on page: ", $username[0]);
                        } else {
                        
                            $sql = sprintf("DELETE FROM %s WHERE username='%s' AND attribute='%s' AND id=%s",
                                           $dbSocket->escapeSimple($tablename), $dbSocket->escapeSimple($usernames[0]),
                                           $dbSocket->escapeSimple($attribute), $dbSocket->escapeSimple($columnId));
                            $res = $dbSocket->query($sql);
                            $logDebugSQL .= "$sql;\n";
                            
                            $format = "Deleted attribute <strong>%s</strong> for user <strong>%s</strong>";
                            $successMsg = sprintf($format, htmlspecialchars($attribute, ENT_QUOTES, 'UTF-8'),
                                                           htmlspecialchars($usernames[0], ENT_QUOTES, 'UTF-8'));
                            $logAction = sprintf("$format on page: ", $attribute, $usernames[0]);
                        }
                    } else {
                        $dbusers = array();
                        
                        foreach ($usernames as $u) {
                            if (!empty($dbSocket->escapeSimple($u))) {
                                $dbusers[] = $dbSocket->escapeSimple($u);
                            }
                        }
                        
                        $dbusersLen = count($dbusers);
                        if ($dbusersLen > 0) {
                            // setting table-related parameters first                
                            switch($configValues['FREERADIUS_VERSION']) {
                                case '1' :
                                    $tableSetting['postauth']['user'] = 'user';
                                    $tableSetting['postauth']['date'] = 'date';
                                    break;
                                case '2' :
                                    // down
                                case '3' :
                                    // down
                                default  :
                                    $tableSetting['postauth']['user'] = 'username';
                                    $tableSetting['postauth']['date'] = 'authdate';
                                    break;
                            }
                            
                            $sql_format = "DELETE FROM %s WHERE %s IN ('" . implode("', '", $dbusers) . "')";
                            
                            $sql = sprintf($sql_format, $configValues['CONFIG_DB_TBL_RADPOSTAUTH'],
                                                        $tableSetting['postauth']['user']);
                            $res = $dbSocket->query($sql);
                            $logDebugSQL .= "$sql;\n";
                            
                            $tables = array(
                                                $configValues['CONFIG_DB_TBL_RADCHECK'],
                                                $configValues['CONFIG_DB_TBL_RADREPLY'],
                                                $configValues['CONFIG_DB_TBL_DALOUSERINFO'],
                                                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'],
                                                $configValues['CONFIG_DB_TBL_RADUSERGROUP'],
                                           );

                            if ($delradacct) {
                                $tables[] = $configValues['CONFIG_DB_TBL_RADACCT'];
                            }
                            
                            foreach ($tables as $table) {
                                $sql = sprintf($sql_format, $table, 'username');
                                $res = $dbSocket->query($sql);
                                $logDebugSQL .= "$sql;\n";
                            }
                            
                            $format = "<strong>%d user(s)</strong> have been deleted";
                            $successMsg = sprintf($format, $dbusersLen);
                            $logAction = sprintf("$format on page: ", $dbusersLen);
                            
                        } else {
                            $failureMsg = "You have provided an empty or invalid username list";
                            $logAction = "Provided an empty or invalid username list (user(s) deletion) on page: ";
                        }
                    }
                } else {
                    $failureMsg = "You have provided an empty or invalid username list";
                    $logAction = "Provided an empty or invalid username list (user(s) deletion) on page: ";
                }
            } else if (array_key_exists('clearSessionsUsers', $_POST) && !empty($_POST['clearSessionsUsers'])) {
                
                $username__starttimes = array();
                
                $tmp = (!is_array($_POST['clearSessionsUsers'])) ? array($_POST['clearSessionsUsers']) : $_POST['clearSessionsUsers'];
                foreach ($tmp as $value) {
                    
                    $value = trim(str_replace("%", "", $value));
                    
                    if (!in_array($value, $username__starttimes)) {
                        $username__starttimes[] = $value;
                    }
                }
                
                
                $userstimesLen = count($username__starttimes);
                if ($userstimesLen > 0) {
                    
                    foreach ($username__starttimes as $username__starttime) {
                        list($username, $starttime) = explode('||', $username__starttime);
                        $sql = sprintf("DELETE FROM %s
                                         WHERE username='%s' AND AcctStartTime='%s'
                                           AND (AcctStopTime='0000-00-00 00:00:00' OR AcctStopTime IS NULL)",
                                       $configValues['CONFIG_DB_TBL_RADACCT'], $dbSocket->escapeSimple($username),
                                       $dbSocket->escapeSimple($starttime));
                        $res = $dbSocket->query($sql);
                        $logDebugSQL .= "$sql;\n";
                    }
                    
                    $format = "<strong>%d user' session(s)</strong> have been cleaned";
                    $successMsg = sprintf($format, $userstimesLen);
                    $logAction = sprintf("$format on page: ", $userstimesLen);
                    
                } else {
                    $failureMsg = "You have provided an empty or invalid username list";
                    $logAction = "Provided an empty or invalid username list (session cleaning) on page: ";
        } else {
            $failureMsg = sprintf("CSRF token error");
            $logAction .= sprintf("CSRF token error on page: ");
    include('library/closedb.php');

    include('library/opendb.php');
    
    $sql = sprintf("SELECT DISTINCT(username) FROM %s", $configValues['CONFIG_DB_TBL_RADCHECK']);
    $res = $dbSocket->query($sql);
    $logDebugSQL .= "$sql;\n";
    
    $options = array();
    while ($row = $res->fetchrow()) {
        $options[] = $row[0];
    }
    
    include('library/closedb.php');

    $input_descriptors1 = array();

    $input_descriptors1[] = array(
                                'name' => 'username[]',
                                'id' => 'username',
                                'type' => 'select',
                                'caption' => t('all','Username'),
                                'options' => $options,
                                'multiple' => true,
                                'size' => 5
                             );

    $input_descriptors1[] = array(
                                'name' => 'delradacct',
                                'type' => 'select',
                                'caption' => t('all','RemoveRadacctRecords'),
                                'options' => array("", "yes", "no"),
                             );

    $input_descriptors1[] = array(
                                    "name" => "csrf_token",
                                    "type" => "hidden",
                                    "value" => dalo_csrf_token(),
                                 );

    $input_descriptors1[] = array(
                                    'type' => 'submit',
                                    'name' => 'submit',
                                    'value' => t('buttons','apply')
                                 );
<form method="POST">
        <h302><?= t('title','AccountRemoval') ?></h302>
        <ul style="margin: 10px auto">
<?php
        foreach ($input_descriptors1 as $input_descriptor) {
            print_form_component($input_descriptor);
        }
?>
        </ul>
</form>
<?php
    if (array_key_exists('PREV_LIST_PAGE', $_SESSION) && !empty(trim($_SESSION['PREV_LIST_PAGE']))) {
        echo '<div style="float: right; text-align: right; margin: 0; font-size: small">';
        printf('<a href="%s" title="Back to Previous Page">Back to Previous Page</a>', trim($_SESSION['PREV_LIST_PAGE']));
        echo '</div>';
        
        unset($_SESSION['PREV_LIST_PAGE']);
    }
    print_footer_and_html_epilogue();
        $input_descriptors1[] = array(
                                        "type" => "hidden",
                                        "value" => dalo_csrf_token(),
                                        "name" => "csrf_token"
                                     );
        
            $onclick = sprintf("document.getElementById('form-%d-radcheck').submit()", $editCounter);
            
            printf('<a class="tablenovisit" href="#" onclick="%s">', $onclick);
                $divId = sprintf("%s-Tooltip-%d-radcheck", $row[0], $editCounter);
            $onclick = sprintf("document.getElementById('form-%d-radreply').submit()", $editCounter);
            
            printf('<a class="tablenovisit" href="#" onclick="%s">', $onclick);
                $divId = sprintf("%s-Tooltip-%d-radreply", $row[0], $editCounter);
<?php
    include('library/opendb.php');

    $tables = array(
                        'radcheck' => $configValues['CONFIG_DB_TBL_RADCHECK'],
                        'radreply' => $configValues['CONFIG_DB_TBL_RADREPLY']
                   );

    $csrf_token = dalo_csrf_token();

    foreach ($tables as $table_value => $table) {

        $sql = sprintf("SELECT id, attribute, value FROM %s WHERE username='%s'",
                       $table, $dbSocket->escapeSimple($username));
        $res = $dbSocket->query($sql);
        $logDebugSQL .= "$sql;\n";

        if ($res->numRows() > 0) {
            $counter = 0;
            while ($row = $res->fetchrow()) {
                list($id, $attribute, $value) = $row;
                
                $formId = sprintf("form-%d-%s", $counter, $table_value);
                $id__attribute = sprintf("%d__%s", intval($id), htmlspecialchars($attribute, ENT_QUOTES, 'UTF-8'));
                
                printf('<form id="%s" style="display: none" method="POST" action="mng-del.php">', $formId);
                printf('<input type="hidden" name="username" value="%s">', $username_enc);
                printf('<input type="hidden" name="attribute" value="%s">', $id__attribute);
                printf('<input type="hidden" name="csrf_token" value="%s">', $csrf_token);
                printf('<input type="hidden" name="tablename" value="%s">', $table_value);
                echo '</form>';
                
                $counter++;
            }
        }
    }
    include('library/closedb.php');
?>
<form name="listall" method="POST" action="<?= $action ?>">

    <input type="hidden" name="csrf_token" value="<?= dalo_csrf_token() ?>">
    
var tooltipObj = new DHTMLgoodies_formTooltip();
tooltipObj.setTooltipPosition('right');
tooltipObj.setPageBgColor('#EEEEEE');
tooltipObj.setTooltipCornerSize(15);
tooltipObj.initFormFieldTooltip();";
    // init logging variables
    $log = "visited page: ";
    $logQuery = "performed query for ";
    if (!empty($username)) {
         $logQuery .= "username(s) starting with [$username] ";
    } else {
        $logQuery .= "all usernames ";
    }
    $logQuery .= "on page: ";

        
        // this can be passed as form attribute and 
        // printTableFormControls function parameter
        $action = "mng-del.php";
<form name="searchusers" method="POST" action="<?= $action ?>">
        printTableFormControls('username[]', $action, 'searchusers');
    
    <input type="hidden" name="csrf_token" value="<?= dalo_csrf_token() ?>">
    
    
    $inline_extra_js = "
var tooltipObj = new DHTMLgoodies_formTooltip();
tooltipObj.setTooltipPosition('right');
tooltipObj.setPageBgColor('#EEEEEE');
tooltipObj.setTooltipCornerSize(15);
tooltipObj.initFormFieldTooltip()";
    print_footer_and_html_epilogue($inline_extra_js);
?>
<?php 
/*
 *********************************************************************************************************
 * daloRADIUS - RADIUS Web Platform
 * Copyright (C) 2007 - Liran Tal <liran@enginx.com> All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *********************************************************************************************************
 *
 * Authors:    Filippo Lauria <filippo.lauria@iit.cnr.it>
 *
 *********************************************************************************************************
 */

// prevent this file to be directly accessed
if (strpos($_SERVER['PHP_SELF'], '/notifications/common.php') !== false) {
    header("Location: ../index.php");
    exit;
}

//include the dompdf class
require_once("dompdf/dompdf_config.inc.php");

//include the Pear Mail classes for sending out emails
@require_once('Mail.php');
@require_once('Mail/mime.php');

/**
 * createPDF()
 * creates a PDF document for a given html file
 * 
 * @param         string        the html file to convert to pdf
 * 
 * @return        string        returns the pdf in binary/string stream
 */
function createPDF($html, $base_path) {
    // this fix has been set in place according to this thread:
    // https://stackoverflow.com/questions/37521775/dompdf-error-no-block-level-parent-found-not-good
    $html = str_replace("\n", "", $html);
    
    // instansiate the pdf document
    $dompdf = new DOMPDF();
    $dompdf->set_base_path($base_path);
    $dompdf->load_html($html);
    $dompdf->render();
    return $dompdf->output();
}


/**
 * send_notification_via_email()
 * creates an email message with the pdf and sends it
 * 
 * @param        string        pdf binary/string stream
 * @param        array         smtp server information
 * @param        string        destination email address
 * @param        string        from email address of the sender identity
 * @param        string        email subject
 * @param        string        email body
 * @param        string        pdf attachment name
 * 
 */
function send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName) {
    if (filter_var($sendTo, FILTER_VALIDATE_EMAIL) === false) {
        return false;
    }
    
    $headers = array(
                       "From"     => $from, 
                       "Subject"  => $subject,
                       "Reply-To" => $from
                    );

    $mime = new Mail_mime();
    $mime->setTXTBody($body); 
    $mime->addAttachment($pdfDocument, "application/pdf", $attachmentName, false, 'base64');
    $body = $mime->get();
    $headers = $mime->headers($headers);
    $mail =& Mail::factory("smtp", $smtpInfo);
    $mail->send($sendTo, $headers, $body);

    return true;
}
?>
  require_once(DOMPDF_LIB_DIR . "/php-font-lib/Font.php");
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib;

use FontLib\Table\Type\name;
use FontLib\TrueType\File;

/**
 * Adobe Font Metrics file creation utility class.
 *
 * @package php-font-lib
 */
class AdobeFontMetrics {
  private $f;

  /**
   * @var File
   */
  private $font;

  function __construct(File $font) {
    $this->font = $font;
  }

  function write($file, $encoding = null) {
    $map_data = array();

    if ($encoding) {
      $encoding = preg_replace("/[^a-z0-9-_]/", "", $encoding);
      $map_file = dirname(__FILE__) . "/../maps/$encoding.map";
      if (!file_exists($map_file)) {
        throw new \Exception("Unknown encoding ($encoding)");
      }

      $map      = new EncodingMap($map_file);
      $map_data = $map->parse();
    }

    $this->f = fopen($file, "w+");

    $font = $this->font;

    $this->startSection("FontMetrics", 4.1);
    $this->addPair("Notice", "Converted by PHP-font-lib");
    $this->addPair("Comment", "https://github.com/PhenX/php-font-lib");

    $encoding_scheme = ($encoding ? $encoding : "FontSpecific");
    $this->addPair("EncodingScheme", $encoding_scheme);

    $records = $font->getData("name", "records");
    foreach ($records as $id => $record) {
      if (!isset(name::$nameIdCodes[$id]) || preg_match("/[\r\n]/", $record->string)) {
        continue;
      }

      $this->addPair(name::$nameIdCodes[$id], $record->string);
    }

    $os2 = $font->getData("OS/2");
    $this->addPair("Weight", ($os2["usWeightClass"] > 400 ? "Bold" : "Medium"));

    $post = $font->getData("post");
    $this->addPair("ItalicAngle", $post["italicAngle"]);
    $this->addPair("IsFixedPitch", ($post["isFixedPitch"] ? "true" : "false"));
    $this->addPair("UnderlineThickness", $font->normalizeFUnit($post["underlineThickness"]));
    $this->addPair("UnderlinePosition", $font->normalizeFUnit($post["underlinePosition"]));

    $hhea = $font->getData("hhea");

    if (isset($hhea["ascent"])) {
      $this->addPair("FontHeightOffset", $font->normalizeFUnit($hhea["lineGap"]));
      $this->addPair("Ascender", $font->normalizeFUnit($hhea["ascent"]));
      $this->addPair("Descender", $font->normalizeFUnit($hhea["descent"]));
    }
    else {
      $this->addPair("FontHeightOffset", $font->normalizeFUnit($os2["typoLineGap"]));
      $this->addPair("Ascender", $font->normalizeFUnit($os2["typoAscender"]));
      $this->addPair("Descender", -abs($font->normalizeFUnit($os2["typoDescender"])));
    }

    $head = $font->getData("head");
    $this->addArray("FontBBox", array(
      $font->normalizeFUnit($head["xMin"]),
      $font->normalizeFUnit($head["yMin"]),
      $font->normalizeFUnit($head["xMax"]),
      $font->normalizeFUnit($head["yMax"]),
    ));

    $glyphIndexArray = $font->getUnicodeCharMap();

    if ($glyphIndexArray) {
      $hmtx  = $font->getData("hmtx");
      $names = $font->getData("post", "names");

      $this->startSection("CharMetrics", count($hmtx));

      if ($encoding) {
        foreach ($map_data as $code => $value) {
          list($c, $name) = $value;

          if (!isset($glyphIndexArray[$c])) {
            continue;
          }

          $g = $glyphIndexArray[$c];

          if (!isset($hmtx[$g])) {
            $hmtx[$g] = $hmtx[0];
          }

          $this->addMetric(array(
            "C"  => ($code > 255 ? -1 : $code),
            "WX" => $font->normalizeFUnit($hmtx[$g][0]),
            "N"  => $name,
          ));
        }
      }
      else {
        foreach ($glyphIndexArray as $c => $g) {
          if (!isset($hmtx[$g])) {
            $hmtx[$g] = $hmtx[0];
          }

          $this->addMetric(array(
            "U"  => $c,
            "WX" => $font->normalizeFUnit($hmtx[$g][0]),
            "N"  => (isset($names[$g]) ? $names[$g] : sprintf("uni%04x", $c)),
            "G"  => $g,
          ));
        }
      }

      $this->endSection("CharMetrics");

      $kern = $font->getData("kern", "subtable");
      $tree = is_array($kern) ? $kern["tree"] : null;

      if (!$encoding && is_array($tree)) {
        $this->startSection("KernData");
        $this->startSection("KernPairs", count($tree, COUNT_RECURSIVE) - count($tree));

        foreach ($tree as $left => $values) {
          if (!is_array($values)) {
            continue;
          }
          if (!isset($glyphIndexArray[$left])) {
            continue;
          }

          $left_gid = $glyphIndexArray[$left];

          if (!isset($names[$left_gid])) {
            continue;
          }

          $left_name = $names[$left_gid];

          $this->addLine("");

          foreach ($values as $right => $value) {
            if (!isset($glyphIndexArray[$right])) {
              continue;
            }

            $right_gid = $glyphIndexArray[$right];

            if (!isset($names[$right_gid])) {
              continue;
            }

            $right_name = $names[$right_gid];
            $this->addPair("KPX", "$left_name $right_name $value");
          }
        }

        $this->endSection("KernPairs");
        $this->endSection("KernData");
      }
    }

    $this->endSection("FontMetrics");
  }

  function addLine($line) {
    fwrite($this->f, "$line\n");
  }

  function addPair($key, $value) {
    $this->addLine("$key $value");
  }

  function addArray($key, $array) {
    $this->addLine("$key " . implode(" ", $array));
  }

  function addMetric($data) {
    $array = array();
    foreach ($data as $key => $value) {
      $array[] = "$key $value";
    }
    $this->addLine(implode(" ; ", $array));
  }

  function startSection($name, $value = "") {
    $this->addLine("Start$name $value");
  }

  function endSection($name) {
    $this->addLine("End$name");
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien Ménager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib;

/**
 * Autoloads FontLib classes
 *
 * @package php-font-lib
 */
class Autoloader {
  const PREFIX = 'FontLib';

  /**
   * Register the autoloader
   */
  public static function register() {
    spl_autoload_register(array(new self, 'autoload'));
  }

  /**
   * Autoloader
   *
   * @param string
   */
  public static function autoload($class) {
    $prefixLength = strlen(self::PREFIX);
    if (0 === strncmp(self::PREFIX, $class, $prefixLength)) {
      $file = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, $prefixLength));
      $file = realpath(__DIR__ . (empty($file) ? '' : DIRECTORY_SEPARATOR) . $file . '.php');
      if (file_exists($file)) {
        require_once $file;
      }
    }
  }
}

Autoloader::register();
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib;

/**
 * Generic font file binary stream.
 *
 * @package php-font-lib
 */
class BinaryStream {
  /**
   * @var resource The file pointer
   */
  protected $f;

  const uint8        = 1;
  const  int8        = 2;
  const uint16       = 3;
  const  int16       = 4;
  const uint32       = 5;
  const  int32       = 6;
  const shortFrac    = 7;
  const Fixed        = 8;
  const  FWord       = 9;
  const uFWord       = 10;
  const F2Dot14      = 11;
  const longDateTime = 12;
  const char         = 13;

  const modeRead      = "rb";
  const modeWrite     = "wb";
  const modeReadWrite = "rb+";

  static function backtrace() {
    var_dump(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
  }

  /**
   * Open a font file in read mode
   *
   * @param string $filename The file name of the font to open
   *
   * @return bool
   */
  public function load($filename) {
    return $this->open($filename, self::modeRead);
  }

  /**
   * Open a font file in a chosen mode
   *
   * @param string $filename The file name of the font to open
   * @param string $mode     The opening mode
   *
   * @throws \Exception
   * @return bool
   */
  public function open($filename, $mode = self::modeRead) {
    if (!in_array($mode, array(self::modeRead, self::modeWrite, self::modeReadWrite))) {
      throw new \Exception("Unknown file open mode");
    }

    $this->f = fopen($filename, $mode);

    return $this->f != false;
  }

  /**
   * Close the internal file pointer
   */
  public function close() {
    return fclose($this->f) != false;
  }

  /**
   * Change the internal file pointer
   *
   * @param resource $fp
   *
   * @throws \Exception
   */
  public function setFile($fp) {
    if (!is_resource($fp)) {
      throw new \Exception('$fp is not a valid resource');
    }

    $this->f = $fp;
  }

  /**
   * Create a temporary file in write mode
   *
   * @param bool $allow_memory Allow in-memory files
   *
   * @return resource the temporary file pointer resource
   */
  public static function getTempFile($allow_memory = true) {
    $f = null;

    if ($allow_memory) {
      $f = fopen("php://temp", "rb+");
    }
    else {
      $f = fopen(tempnam(sys_get_temp_dir(), "fnt"), "rb+");
    }

    return $f;
  }

  /**
   * Move the internal file pinter to $offset bytes
   *
   * @param int $offset
   *
   * @return bool True if the $offset position exists in the file
   */
  public function seek($offset) {
    return fseek($this->f, $offset, SEEK_SET) == 0;
  }

  /**
   * Gives the current position in the file
   *
   * @return int The current position
   */
  public function pos() {
    return ftell($this->f);
  }

  public function skip($n) {
    fseek($this->f, $n, SEEK_CUR);
  }

  /**
   * @param int $n The number of bytes to read
   *
   * @return string
   */
  public function read($n) {
    if ($n < 1) {
      return "";
    }

    return (string) fread($this->f, $n);
  }

  public function write($data, $length = null) {
    if ($data === null || $data === "" || $data === false) {
      return 0;
    }

    return fwrite($this->f, $data, $length);
  }

  public function readUInt8() {
    return ord($this->read(1));
  }

  public function readUInt8Many($count) {
    return array_values(unpack("C*", $this->read($count)));
  }

  public function writeUInt8($data) {
    return $this->write(chr($data), 1);
  }

  public function readInt8() {
    $v = $this->readUInt8();

    if ($v >= 0x80) {
      $v -= 0x100;
    }

    return $v;
  }

  public function readInt8Many($count) {
    return array_values(unpack("c*", $this->read($count)));
  }

  public function writeInt8($data) {
    if ($data < 0) {
      $data += 0x100;
    }

    return $this->writeUInt8($data);
  }

  public function readUInt16() {
    $a = unpack("nn", $this->read(2));

    return $a["n"];
  }

  public function readUInt16Many($count) {
    return array_values(unpack("n*", $this->read($count * 2)));
  }

  public function readUFWord() {
    return $this->readUInt16();
  }

  public function writeUInt16($data) {
    return $this->write(pack("n", $data), 2);
  }

  public function writeUFWord($data) {
    return $this->writeUInt16($data);
  }

  public function readInt16() {
    $a = unpack("nn", $this->read(2));
    $v = $a["n"];

    if ($v >= 0x8000) {
      $v -= 0x10000;
    }

    return $v;
  }

  public function readInt16Many($count) {
    $vals = array_values(unpack("n*", $this->read($count * 2)));
    foreach ($vals as &$v) {
      if ($v >= 0x8000) {
        $v -= 0x10000;
      }
    }

    return $vals;
  }

  public function readFWord() {
    return $this->readInt16();
  }

  public function writeInt16($data) {
    if ($data < 0) {
      $data += 0x10000;
    }

    return $this->writeUInt16($data);
  }

  public function writeFWord($data) {
    return $this->writeInt16($data);
  }

  public function readUInt32() {
    $a = unpack("NN", $this->read(4));

    return $a["N"];
  }

  public function writeUInt32($data) {
    return $this->write(pack("N", $data), 4);
  }

  public function readFixed() {
    $d  = $this->readInt16();
    $d2 = $this->readUInt16();

    return round($d + $d2 / 0x10000, 4);
  }

  public function writeFixed($data) {
    $left  = floor($data);
    $right = ($data - $left) * 0x10000;

    return $this->writeInt16($left) + $this->writeUInt16($right);
  }

  public function readLongDateTime() {
    $this->readUInt32(); // ignored
    $date = $this->readUInt32() - 2082844800;
    
    # PHP_INT_MIN isn't defined in PHP < 7.0
    $php_int_min = defined("PHP_INT_MIN") ? PHP_INT_MIN : ~PHP_INT_MAX;

    if (is_string($date) || $date > PHP_INT_MAX || $date < $php_int_min) {
      $date = 0;
    }

    return date("Y-m-d H:i:s", $date);
  }

  public function writeLongDateTime($data) {
    $date = strtotime($data);
    $date += 2082844800;

    return $this->writeUInt32(0) + $this->writeUInt32($date);
  }

  public function unpack($def) {
    $d = array();
    foreach ($def as $name => $type) {
      $d[$name] = $this->r($type);
    }

    return $d;
  }

  public function pack($def, $data) {
    $bytes = 0;
    foreach ($def as $name => $type) {
      $bytes += $this->w($type, $data[$name]);
    }

    return $bytes;
  }

  /**
   * Read a data of type $type in the file from the current position
   *
   * @param mixed $type The data type to read
   *
   * @return mixed The data that was read
   */
  public function r($type) {
    switch ($type) {
      case self::uint8:
        return $this->readUInt8();
      case self::int8:
        return $this->readInt8();
      case self::uint16:
        return $this->readUInt16();
      case self::int16:
        return $this->readInt16();
      case self::uint32:
        return $this->readUInt32();
      case self::int32:
        return $this->readUInt32();
      case self::shortFrac:
        return $this->readFixed();
      case self::Fixed:
        return $this->readFixed();
      case self::FWord:
        return $this->readInt16();
      case self::uFWord:
        return $this->readUInt16();
      case self::F2Dot14:
        return $this->readInt16();
      case self::longDateTime:
        return $this->readLongDateTime();
      case self::char:
        return $this->read(1);
      default:
        if (is_array($type)) {
          if ($type[0] == self::char) {
            return $this->read($type[1]);
          }
          if ($type[0] == self::uint16) {
            return $this->readUInt16Many($type[1]);
          }
          if ($type[0] == self::int16) {
            return $this->readInt16Many($type[1]);
          }
          if ($type[0] == self::uint8) {
            return $this->readUInt8Many($type[1]);
          }
          if ($type[0] == self::int8) {
            return $this->readInt8Many($type[1]);
          }

          $ret = array();
          for ($i = 0; $i < $type[1]; $i++) {
            $ret[] = $this->r($type[0]);
          }

          return $ret;
        }

        return null;
    }
  }

  /**
   * Write $data of type $type in the file from the current position
   *
   * @param mixed $type The data type to write
   * @param mixed $data The data to write
   *
   * @return int The number of bytes read
   */
  public function w($type, $data) {
    switch ($type) {
      case self::uint8:
        return $this->writeUInt8($data);
      case self::int8:
        return $this->writeInt8($data);
      case self::uint16:
        return $this->writeUInt16($data);
      case self::int16:
        return $this->writeInt16($data);
      case self::uint32:
        return $this->writeUInt32($data);
      case self::int32:
        return $this->writeUInt32($data);
      case self::shortFrac:
        return $this->writeFixed($data);
      case self::Fixed:
        return $this->writeFixed($data);
      case self::FWord:
        return $this->writeInt16($data);
      case self::uFWord:
        return $this->writeUInt16($data);
      case self::F2Dot14:
        return $this->writeInt16($data);
      case self::longDateTime:
        return $this->writeLongDateTime($data);
      case self::char:
        return $this->write($data, 1);
      default:
        if (is_array($type)) {
          if ($type[0] == self::char) {
            return $this->write($data, $type[1]);
          }

          $ret = 0;
          for ($i = 0; $i < $type[1]; $i++) {
            if (isset($data[$i])) {
              $ret += $this->w($type[0], $data[$i]);
            }
          }

          return $ret;
        }

        return null;
    }
  }

  /**
   * Converts a Uint32 value to string
   *
   * @param int $uint32
   *
   * @return string The string
   */
  public function convertUInt32ToStr($uint32) {
    return chr(($uint32 >> 24) & 0xFF) . chr(($uint32 >> 16) & 0xFF) . chr(($uint32 >> 8) & 0xFF) . chr($uint32 & 0xFF);
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\EOT;

/**
 * EOT font file.
 *
 * @package php-font-lib
 */
class File extends \FontLib\TrueType\File {
  const TTEMBED_SUBSET                   = 0x00000001;
  const TTEMBED_TTCOMPRESSED             = 0x00000004;
  const TTEMBED_FAILIFVARIATIONSIMULATED = 0x00000010;
  const TTMBED_EMBEDEUDC                 = 0x00000020;
  const TTEMBED_VALIDATIONTESTS          = 0x00000040; // Deprecated
  const TTEMBED_WEBOBJECT      = 0x00000080;
  const TTEMBED_XORENCRYPTDATA = 0x10000000;

  /**
   * @var Header
   */
  public $header;

  function parseHeader() {
    if (!empty($this->header)) {
      return;
    }

    $this->header = new Header($this);
    $this->header->parse();
  }

  function parse() {
    $this->parseHeader();

    $flags = $this->header->data["Flags"];

    if ($flags & self::TTEMBED_TTCOMPRESSED) {
      $mtx_version    = $this->readUInt8();
      $mtx_copy_limit = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8();
      $mtx_offset_1   = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8();
      $mtx_offset_2   = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8();
      /*
      var_dump("$mtx_version $mtx_copy_limit $mtx_offset_1 $mtx_offset_2");

      $pos = $this->pos();
      $size = $mtx_offset_1 - $pos;
      var_dump("pos: $pos");
      var_dump("size: $size");*/
    }

    if ($flags & self::TTEMBED_XORENCRYPTDATA) {
      // Process XOR
    }
    // TODO Read font data ...
  }

  /**
   * Little endian version of the read method
   *
   * @param int $n The number of bytes to read
   *
   * @return string
   */
  public function read($n) {
    if ($n < 1) {
      return "";
    }

    $string = (string) fread($this->f, $n);
    $chunks = mb_str_split($string, 2, '8bit');
    $chunks = array_map("strrev", $chunks);
    return implode("", $chunks);
  }

  public function readUInt32() {
    $uint32 = parent::readUInt32();

    return $uint32 >> 16 & 0x0000FFFF | $uint32 << 16 & 0xFFFF0000;
  }

  /**
   * Get font copyright
   *
   * @return string|null
   */
  function getFontCopyright() {
    return null;
  }

  /**
   * Get font name
   *
   * @return string|null
   */
  function getFontName() {
    return $this->header->data["FamilyName"];
  }

  /**
   * Get font subfamily
   *
   * @return string|null
   */
  function getFontSubfamily() {
    return $this->header->data["StyleName"];
  }

  /**
   * Get font subfamily ID
   *
   * @return string|null
   */
  function getFontSubfamilyID() {
    return $this->header->data["StyleName"];
  }

  /**
   * Get font full name
   *
   * @return string|null
   */
  function getFontFullName() {
    return $this->header->data["FullName"];
  }

  /**
   * Get font version
   *
   * @return string|null
   */
  function getFontVersion() {
    return $this->header->data["VersionName"];
  }

  /**
   * Get font weight
   *
   * @return string|null
   */
  function getFontWeight() {
    return $this->header->data["Weight"];
  }

  /**
   * Get font Postscript name
   *
   * @return string|null
   */
  function getFontPostscriptName() {
    return null;
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\EOT;

use Exception;
use FontLib\Font;

/**
 * TrueType font file header.
 *
 * @package php-font-lib
 *
 * @property File $font
 */
class Header extends \FontLib\Header {
  protected $def = array(
    "format"        => self::uint32,
    "numTables"     => self::uint16,
    "searchRange"   => self::uint16,
    "entrySelector" => self::uint16,
    "rangeShift"    => self::uint16,
  );

  public function parse() {
    $font = $this->font;

    $this->data = $font->unpack(array(
      "EOTSize"            => self::uint32,
      "FontDataSize"       => self::uint32,
      "Version"            => self::uint32,
      "Flags"              => self::uint32,
      "FontPANOSE"         => array(self::uint8, 10),
      "Charset"            => self::uint8,
      "Italic"             => self::uint8,
      "Weight"             => self::uint32,
      "fsType"             => self::uint16,
      "MagicNumber"        => self::uint16,
      "UnicodeRange1"      => self::uint32,
      "UnicodeRange2"      => self::uint32,
      "UnicodeRange3"      => self::uint32,
      "UnicodeRange4"      => self::uint32,
      "CodePageRange1"     => self::uint32,
      "CodePageRange2"     => self::uint32,
      "CheckSumAdjustment" => self::uint32,
      "Reserved1"          => self::uint32,
      "Reserved2"          => self::uint32,
      "Reserved3"          => self::uint32,
      "Reserved4"          => self::uint32,
    ));

    $this->data["Padding1"] = $font->readUInt16();
    $this->readString("FamilyName");

    $this->data["Padding2"] = $font->readUInt16();
    $this->readString("StyleName");

    $this->data["Padding3"] = $font->readUInt16();
    $this->readString("VersionName");

    $this->data["Padding4"] = $font->readUInt16();
    $this->readString("FullName");

    switch ($this->data["Version"]) {
      default:
        throw new Exception("Unknown EOT version " . $this->data["Version"]);

      case 0x00010000:
        // Nothing to do more
        break;

      case 0x00020001:
        $this->data["Padding5"] = $font->readUInt16();
        $this->readString("RootString");
        break;

      case 0x00020002:
        $this->data["Padding5"] = $font->readUInt16();
        $this->readString("RootString");

        $this->data["RootStringCheckSum"] = $font->readUInt32();
        $this->data["EUDCCodePage"]       = $font->readUInt32();

        $this->data["Padding6"] = $font->readUInt16();
        $this->readString("Signature");

        $this->data["EUDCFlags"]    = $font->readUInt32();
        $this->data["EUDCFontSize"] = $font->readUInt32();
        break;
    }

    if (!empty($this->data["RootString"])) {
      $this->data["RootString"] = explode("\0", $this->data["RootString"]);
    }
  }

  private function readString($name) {
    $font = $this->font;
    $size = $font->readUInt16();

    $this->data["{$name}Size"] = $size;
    $this->data[$name]         = Font::UTF16ToUTF8($font->read($size));
  }

  public function encode() {
    //return $this->font->pack($this->def, $this->data);
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib;

/**
 * Encoding map used to map a code point to a Unicode char.
 *
 * @package php-font-lib
 */
class EncodingMap {
  private $f;

  function __construct($file) {
    $this->f = fopen($file, "r");
  }

  function parse() {
    $map = array();

    while ($line = fgets($this->f)) {
      if (preg_match('/^[\!\=]([0-9A-F]{2,})\s+U\+([0-9A-F]{2})([0-9A-F]{2})\s+([^\s]+)/', $line, $matches)) {
        $unicode = (hexdec($matches[2]) << 8) + hexdec($matches[3]);
        $map[hexdec($matches[1])] = array($unicode, $matches[4]);
      }
    }

    ksort($map);

    return $map;
  }
}
<?php

namespace FontLib\Exception;

class FontNotFoundException extends \Exception
{
    public function __construct($fontPath)
    {
        $this->message = 'Font not found in: ' . $fontPath;
    }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib;

use FontLib\Exception\FontNotFoundException;

/**
 * Generic font file.
 *
 * @package php-font-lib
 */
class Font {
  static $debug = false;

  /**
   * @param string $file The font file
   *
   * @return TrueType\File|null $file
   */
  public static function load($file) {
      if(!file_exists($file)){
          throw new FontNotFoundException($file);
      }

    $header = file_get_contents($file, false, null, 0, 4);
    $class  = null;

    switch ($header) {
      case "\x00\x01\x00\x00":
      case "true":
      case "typ1":
        $class = "TrueType\\File";
        break;

      case "OTTO":
        $class = "OpenType\\File";
        break;

      case "wOFF":
        $class = "WOFF\\File";
        break;

      case "ttcf":
        $class = "TrueType\\Collection";
        break;

      // Unknown type or EOT
      default:
        $magicNumber = file_get_contents($file, false, null, 34, 2);

        if ($magicNumber === "LP") {
          $class = "EOT\\File";
        }
    }

    if ($class) {
      $class = "FontLib\\$class";

      /** @var TrueType\File $obj */
      $obj = new $class;
      $obj->load($file);

      return $obj;
    }

    return null;
  }

  static function d($str) {
    if (!self::$debug) {
      return;
    }
    echo "$str\n";
  }

  static function UTF16ToUTF8($str) {
    return mb_convert_encoding($str, "utf-8", "utf-16");
  }

  static function UTF8ToUTF16($str) {
    return mb_convert_encoding($str, "utf-16", "utf-8");
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 * @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
 */
namespace FontLib\Glyph;

use FontLib\Table\Type\glyf;
use FontLib\TrueType\File;
use FontLib\BinaryStream;

/**
 * `glyf` font table.
 *
 * @package php-font-lib
 */
class Outline extends BinaryStream {
  /**
   * @var \FontLib\Table\Type\glyf
   */
  protected $table;

  protected $offset;
  protected $size;

  // Data
  public $numberOfContours;
  public $xMin;
  public $yMin;
  public $xMax;
  public $yMax;

  /**
   * @var string|null
   */
  public $raw;

  /**
   * @param glyf $table
   * @param                 $offset
   * @param                 $size
   *
   * @return Outline
   */
  static function init(glyf $table, $offset, $size, BinaryStream $font) {
    $font->seek($offset);

    if ($font->readInt16() > -1) {
      /** @var OutlineSimple $glyph */
      $glyph = new OutlineSimple($table, $offset, $size);
    }
    else {
      /** @var OutlineComposite $glyph */
      $glyph = new OutlineComposite($table, $offset, $size);
    }

    $glyph->parse($font);

    return $glyph;
  }

  /**
   * @return File
   */
  function getFont() {
    return $this->table->getFont();
  }

  function __construct(glyf $table, $offset = null, $size = null) {
    $this->table  = $table;
    $this->offset = $offset;
    $this->size   = $size;
  }

  function parse(BinaryStream $font) {
    $font->seek($this->offset);

      $this->raw = $font->read($this->size);
  }

  function parseData() {
    $font = $this->getFont();
    $font->seek($this->offset);

    $this->numberOfContours = $font->readInt16();
    $this->xMin             = $font->readFWord();
    $this->yMin             = $font->readFWord();
    $this->xMax             = $font->readFWord();
    $this->yMax             = $font->readFWord();
  }

  function encode() {
    $font = $this->getFont();

    return $font->write($this->raw, mb_strlen((string) $this->raw, '8bit'));
  }

  function getSVGContours() {
    // Inherit
  }

  function getGlyphIDs() {
    return array();
  }
}

<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 * @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
 */

namespace FontLib\Glyph;
/**
 * Glyph outline component
 *
 * @package php-font-lib
 */
class OutlineComponent {
  public $flags;
  public $glyphIndex;
  public $a, $b, $c, $d, $e, $f;
  public $point_compound;
  public $point_component;
  public $instructions;

  function getMatrix() {
    return array(
      $this->a, $this->b,
      $this->c, $this->d,
      $this->e, $this->f,
    );
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 * @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
 */

namespace FontLib\Glyph;

/**
 * Composite glyph outline
 *
 * @package php-font-lib
 */
class OutlineComposite extends Outline {
  const ARG_1_AND_2_ARE_WORDS    = 0x0001;
  const ARGS_ARE_XY_VALUES       = 0x0002;
  const ROUND_XY_TO_GRID         = 0x0004;
  const WE_HAVE_A_SCALE          = 0x0008;
  const MORE_COMPONENTS          = 0x0020;
  const WE_HAVE_AN_X_AND_Y_SCALE = 0x0040;
  const WE_HAVE_A_TWO_BY_TWO     = 0x0080;
  const WE_HAVE_INSTRUCTIONS     = 0x0100;
  const USE_MY_METRICS           = 0x0200;
  const OVERLAP_COMPOUND         = 0x0400;

  /**
   * @var OutlineComponent[]
   */
  public $components = array();

  function getGlyphIDs() {
    if (empty($this->components)) {
      $this->parseData();
    }

    $glyphIDs = array();
    foreach ($this->components as $_component) {
      $glyphIDs[] = $_component->glyphIndex;

      $_glyph   = $this->table->data[$_component->glyphIndex];

      if ($_glyph !== $this) {
        $glyphIDs = array_merge($glyphIDs, $_glyph->getGlyphIDs());
      }
    }

    return $glyphIDs;
  }

  /*function parse() {
    //$this->parseData();
  }*/

  function parseData() {
    parent::parseData();

    $font = $this->getFont();

    do {
      $flags      = $font->readUInt16();
      $glyphIndex = $font->readUInt16();

      $a = 1.0;
      $b = 0.0;
      $c = 0.0;
      $d = 1.0;
      $e = 0.0;
      $f = 0.0;

      $point_compound  = null;
      $point_component = null;

      $instructions = null;

      if ($flags & self::ARG_1_AND_2_ARE_WORDS) {
        if ($flags & self::ARGS_ARE_XY_VALUES) {
          $e = $font->readInt16();
          $f = $font->readInt16();
        }
        else {
          $point_compound  = $font->readUInt16();
          $point_component = $font->readUInt16();
        }
      }
      else {
        if ($flags & self::ARGS_ARE_XY_VALUES) {
          $e = $font->readInt8();
          $f = $font->readInt8();
        }
        else {
          $point_compound  = $font->readUInt8();
          $point_component = $font->readUInt8();
        }
      }

      if ($flags & self::WE_HAVE_A_SCALE) {
        $a = $d = $font->readInt16();
      }
      elseif ($flags & self::WE_HAVE_AN_X_AND_Y_SCALE) {
        $a = $font->readInt16();
        $d = $font->readInt16();
      }
      elseif ($flags & self::WE_HAVE_A_TWO_BY_TWO) {
        $a = $font->readInt16();
        $b = $font->readInt16();
        $c = $font->readInt16();
        $d = $font->readInt16();
      }

      //if ($flags & self::WE_HAVE_INSTRUCTIONS) {
      //
      //}

      $component                  = new OutlineComponent();
      $component->flags           = $flags;
      $component->glyphIndex      = $glyphIndex;
      $component->a               = $a;
      $component->b               = $b;
      $component->c               = $c;
      $component->d               = $d;
      $component->e               = $e;
      $component->f               = $f;
      $component->point_compound  = $point_compound;
      $component->point_component = $point_component;
      $component->instructions    = $instructions;

      $this->components[] = $component;
    } while ($flags & self::MORE_COMPONENTS);
  }

  function encode() {
    $font = $this->getFont();

    $gids = $font->getSubset();

    $size = $font->writeInt16(-1);
    $size += $font->writeFWord($this->xMin);
    $size += $font->writeFWord($this->yMin);
    $size += $font->writeFWord($this->xMax);
    $size += $font->writeFWord($this->yMax);

    foreach ($this->components as $_i => $_component) {
      $flags = 0;
      if ($_component->point_component === null && $_component->point_compound === null) {
        $flags |= self::ARGS_ARE_XY_VALUES;

        if (abs($_component->e) > 0x7F || abs($_component->f) > 0x7F) {
          $flags |= self::ARG_1_AND_2_ARE_WORDS;
        }
      }
      elseif ($_component->point_component > 0xFF || $_component->point_compound > 0xFF) {
        $flags |= self::ARG_1_AND_2_ARE_WORDS;
      }

      if ($_component->b == 0 && $_component->c == 0) {
        if ($_component->a == $_component->d) {
          if ($_component->a != 1.0) {
            $flags |= self::WE_HAVE_A_SCALE;
          }
        }
        else {
          $flags |= self::WE_HAVE_AN_X_AND_Y_SCALE;
        }
      }
      else {
        $flags |= self::WE_HAVE_A_TWO_BY_TWO;
      }

      if ($_i < count($this->components) - 1) {
        $flags |= self::MORE_COMPONENTS;
      }

      $size += $font->writeUInt16($flags);

      $new_gid = array_search($_component->glyphIndex, $gids);
      $size += $font->writeUInt16($new_gid);

      if ($flags & self::ARG_1_AND_2_ARE_WORDS) {
        if ($flags & self::ARGS_ARE_XY_VALUES) {
          $size += $font->writeInt16($_component->e);
          $size += $font->writeInt16($_component->f);
        }
        else {
          $size += $font->writeUInt16($_component->point_compound);
          $size += $font->writeUInt16($_component->point_component);
        }
      }
      else {
        if ($flags & self::ARGS_ARE_XY_VALUES) {
          $size += $font->writeInt8($_component->e);
          $size += $font->writeInt8($_component->f);
        }
        else {
          $size += $font->writeUInt8($_component->point_compound);
          $size += $font->writeUInt8($_component->point_component);
        }
      }

      if ($flags & self::WE_HAVE_A_SCALE) {
        $size += $font->writeInt16($_component->a);
      }
      elseif ($flags & self::WE_HAVE_AN_X_AND_Y_SCALE) {
        $size += $font->writeInt16($_component->a);
        $size += $font->writeInt16($_component->d);
      }
      elseif ($flags & self::WE_HAVE_A_TWO_BY_TWO) {
        $size += $font->writeInt16($_component->a);
        $size += $font->writeInt16($_component->b);
        $size += $font->writeInt16($_component->c);
        $size += $font->writeInt16($_component->d);
      }
    }

    return $size;
  }

  public function getSVGContours() {
    $contours = array();

    /** @var \FontLib\Table\Type\glyf $glyph_data */
    $glyph_data = $this->getFont()->getTableObject("glyf");

    /** @var Outline[] $glyphs */
    $glyphs = $glyph_data->data;

    foreach ($this->components as $component) {
      $_glyph = $glyphs[$component->glyphIndex];

      if ($_glyph !== $this) {
        $contours[] = array(
          "contours"  => $_glyph->getSVGContours(),
          "transform" => $component->getMatrix(),
        );
      }
    }

    return $contours;
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 * @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
 */

namespace FontLib\Glyph;

/**
 * `glyf` font table.
 *
 * @package php-font-lib
 */
class OutlineSimple extends Outline {
  const ON_CURVE       = 0x01;
  const X_SHORT_VECTOR = 0x02;
  const Y_SHORT_VECTOR = 0x04;
  const REPEAT         = 0x08;
  const THIS_X_IS_SAME = 0x10;
  const THIS_Y_IS_SAME = 0x20;

  public $instructions;
  public $points;

  function parseData() {
    parent::parseData();

    if (!$this->size) {
      return;
    }

    $font = $this->getFont();

    $noc = $this->numberOfContours;

    if ($noc == 0) {
      return;
    }

    $endPtsOfContours = $font->r(array(self::uint16, $noc));

    $instructionLength  = $font->readUInt16();
    $this->instructions = $font->r(array(self::uint8, $instructionLength));

    $count = $endPtsOfContours[$noc - 1] + 1;

    // Flags
    $flags = array();
    for ($index = 0; $index < $count; $index++) {
      $flags[$index] = $font->readUInt8();

      if ($flags[$index] & self::REPEAT) {
        $repeats = $font->readUInt8();

        for ($i = 1; $i <= $repeats; $i++) {
          $flags[$index + $i] = $flags[$index];
        }

        $index += $repeats;
      }
    }

    $points = array();
    foreach ($flags as $i => $flag) {
      $points[$i]["onCurve"]      = $flag & self::ON_CURVE;
      $points[$i]["endOfContour"] = in_array($i, $endPtsOfContours);
    }

    // X Coords
    $x = 0;
    for ($i = 0; $i < $count; $i++) {
      $flag = $flags[$i];

      if ($flag & self::THIS_X_IS_SAME) {
        if ($flag & self::X_SHORT_VECTOR) {
          $x += $font->readUInt8();
        }
      }
      else {
        if ($flag & self::X_SHORT_VECTOR) {
          $x -= $font->readUInt8();
        }
        else {
          $x += $font->readInt16();
        }
      }

      $points[$i]["x"] = $x;
    }

    // Y Coords
    $y = 0;
    for ($i = 0; $i < $count; $i++) {
      $flag = $flags[$i];

      if ($flag & self::THIS_Y_IS_SAME) {
        if ($flag & self::Y_SHORT_VECTOR) {
          $y += $font->readUInt8();
        }
      }
      else {
        if ($flag & self::Y_SHORT_VECTOR) {
          $y -= $font->readUInt8();
        }
        else {
          $y += $font->readInt16();
        }
      }

      $points[$i]["y"] = $y;
    }

    $this->points = $points;
  }

  public function splitSVGPath($path) {
    preg_match_all('/([a-z])|(-?\d+(?:\.\d+)?)/i', $path, $matches, PREG_PATTERN_ORDER);

    return $matches[0];
  }

  public function makePoints($path) {
    $path = $this->splitSVGPath($path);
    $l    = count($path);
    $i    = 0;

    $points = array();

    while ($i < $l) {
      switch ($path[$i]) {
        // moveTo
        case "M":
          $points[] = array(
            "onCurve"      => true,
            "x"            => $path[++$i],
            "y"            => $path[++$i],
            "endOfContour" => false,
          );
          break;

        // lineTo
        case "L":
          $points[] = array(
            "onCurve"      => true,
            "x"            => $path[++$i],
            "y"            => $path[++$i],
            "endOfContour" => false,
          );
          break;

        // quadraticCurveTo
        case "Q":
          $points[] = array(
            "onCurve"      => false,
            "x"            => $path[++$i],
            "y"            => $path[++$i],
            "endOfContour" => false,
          );
          $points[] = array(
            "onCurve"      => true,
            "x"            => $path[++$i],
            "y"            => $path[++$i],
            "endOfContour" => false,
          );
          break;

        // closePath
        /** @noinspection PhpMissingBreakStatementInspection */
        case "z":
          $points[count($points) - 1]["endOfContour"] = true;

        default:
          $i++;
          break;
      }
    }

    return $points;
  }

  function encode() {
    if (empty($this->points)) {
      return parent::encode();
    }

    return $this->size = $this->encodePoints($this->points);
  }

  public function encodePoints($points) {
    $endPtsOfContours = array();
    $flags            = array();
    $coords_x         = array();
    $coords_y         = array();

    $last_x = 0;
    $last_y = 0;
    $xMin   = $yMin = 0xFFFF;
    $xMax   = $yMax = -0xFFFF;
    foreach ($points as $i => $point) {
      $flag = 0;
      if ($point["onCurve"]) {
        $flag |= self::ON_CURVE;
      }

      if ($point["endOfContour"]) {
        $endPtsOfContours[] = $i;
      }

      // Simplified, we could do some optimizations
      if ($point["x"] == $last_x) {
        $flag |= self::THIS_X_IS_SAME;
      }
      else {
        $x          = intval($point["x"]);
        $xMin       = min($x, $xMin);
        $xMax       = max($x, $xMax);
        $coords_x[] = $x - $last_x; // int16
      }

      // Simplified, we could do some optimizations
      if ($point["y"] == $last_y) {
        $flag |= self::THIS_Y_IS_SAME;
      }
      else {
        $y          = intval($point["y"]);
        $yMin       = min($y, $yMin);
        $yMax       = max($y, $yMax);
        $coords_y[] = $y - $last_y; // int16
      }

      $flags[] = $flag;
      $last_x  = $point["x"];
      $last_y  = $point["y"];
    }

    $font = $this->getFont();

    $l = 0;
    $l += $font->writeInt16(count($endPtsOfContours)); // endPtsOfContours
    $l += $font->writeFWord(isset($this->xMin) ? $this->xMin : $xMin); // xMin
    $l += $font->writeFWord(isset($this->yMin) ? $this->yMin : $yMin); // yMin
    $l += $font->writeFWord(isset($this->xMax) ? $this->xMax : $xMax); // xMax
    $l += $font->writeFWord(isset($this->yMax) ? $this->yMax : $yMax); // yMax

    // Simple glyf
    $l += $font->w(array(self::uint16, count($endPtsOfContours)), $endPtsOfContours); // endPtsOfContours
    $l += $font->writeUInt16(0); // instructionLength
    $l += $font->w(array(self::uint8, count($flags)), $flags); // flags
    $l += $font->w(array(self::int16, count($coords_x)), $coords_x); // xCoordinates
    $l += $font->w(array(self::int16, count($coords_y)), $coords_y); // yCoordinates
    return $l;
  }

  public function getSVGContours($points = null) {
    $path = "";

    if (!$points) {
      if (empty($this->points)) {
        $this->parseData();
      }

      $points = $this->points;
    }

    $length     = (empty($points) ? 0 : count($points));
    $firstIndex = 0;
    $count      = 0;

    for ($i = 0; $i < $length; $i++) {
      $count++;

      if ($points[$i]["endOfContour"]) {
        $path .= $this->getSVGPath($points, $firstIndex, $count);
        $firstIndex = $i + 1;
        $count      = 0;
      }
    }

    return $path;
  }

  protected function getSVGPath($points, $startIndex, $count) {
    $offset = 0;
    $path   = "";

    while ($offset < $count) {
      $point    = $points[$startIndex + $offset % $count];
      $point_p1 = $points[$startIndex + ($offset + 1) % $count];

      if ($offset == 0) {
        $path .= "M{$point['x']},{$point['y']} ";
      }

      if ($point["onCurve"]) {
        if ($point_p1["onCurve"]) {
          $path .= "L{$point_p1['x']},{$point_p1['y']} ";
          $offset++;
        }
        else {
          $point_p2 = $points[$startIndex + ($offset + 2) % $count];

          if ($point_p2["onCurve"]) {
            $path .= "Q{$point_p1['x']},{$point_p1['y']},{$point_p2['x']},{$point_p2['y']} ";
          }
          else {
            $path .= "Q{$point_p1['x']},{$point_p1['y']}," . $this->midValue($point_p1['x'], $point_p2['x']) . "," . $this->midValue($point_p1['y'], $point_p2['y']) . " ";
          }

          $offset += 2;
        }
      }
      else {
        if ($point_p1["onCurve"]) {
          $path .= "Q{$point['x']},{$point['y']},{$point_p1['x']},{$point_p1['y']} ";
        }
        else {
          $path .= "Q{$point['x']},{$point['y']}," . $this->midValue($point['x'], $point_p1['x']) . "," . $this->midValue($point['y'], $point_p1['y']) . " ";
        }

        $offset++;
      }
    }

    $path .= "z ";

    return $path;
  }

  function midValue($a, $b) {
    return $a + ($b - $a) / 2;
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */
namespace FontLib;

use FontLib\TrueType\File;

/**
 * Font header container.
 *
 * @package php-font-lib
 */
abstract class Header extends BinaryStream {
  /**
   * @var File
   */
  protected $font;
  protected $def = array();

  public $data;

  public function __construct(File $font) {
    $this->font = $font;
  }

  public function encode() {
    return $this->font->pack($this->def, $this->data);
  }

  public function parse() {
    $this->data = $this->font->unpack($this->def);
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\OpenType;

/**
 * Open Type font, the same as a TrueType one.
 *
 * @package php-font-lib
 */
class File extends \FontLib\TrueType\File {
  //
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\OpenType;

/**
 * Open Type Table directory entry, the same as a TrueType one.
 *
 * @package php-font-lib
 */
class TableDirectoryEntry extends \FontLib\TrueType\TableDirectoryEntry {

}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */
namespace FontLib\Table;

use FontLib\TrueType\File;
use FontLib\Font;
use FontLib\BinaryStream;

/**
 * Generic Font table directory entry.
 *
 * @package php-font-lib
 */
class DirectoryEntry extends BinaryStream {
  /**
   * @var File
   */
  protected $font;

  /**
   * @var Table
   */
  protected $font_table;

  public $entryLength = 4;

  public $tag;
  public $checksum;
  public $offset;
  public $length;

  protected $origF;

  /**
   * @param string $data
   *
   * @return int
   */
  static function computeChecksum($data) {
    $len = mb_strlen($data, '8bit');
    $mod = $len % 4;

    if ($mod) {
      $data = str_pad($data, $len + (4 - $mod), "\0");
    }

    $len = mb_strlen($data, '8bit');

    $hi = 0x0000;
    $lo = 0x0000;

    for ($i = 0; $i < $len; $i += 4) {
      $hi += (ord($data[$i]) << 8) + ord($data[$i + 1]);
      $lo += (ord($data[$i + 2]) << 8) + ord($data[$i + 3]);
      $hi += $lo >> 16;
      $lo = $lo & 0xFFFF;
      $hi = $hi & 0xFFFF;
    }

    return ($hi << 8) + $lo;
  }

  function __construct(File $font) {
    $this->font = $font;
    $this->f    = $font->f;
  }

  function parse() {
    $this->tag = $this->font->read(4);
  }

  function open($filename, $mode = self::modeRead) {
    // void
  }

  function setTable(Table $font_table) {
    $this->font_table = $font_table;
  }

  function encode($entry_offset) {
    Font::d("\n==== $this->tag ====");
    //Font::d("Entry offset  = $entry_offset");

    $data = $this->font_table;
    $font = $this->font;

    $table_offset = $font->pos();
    $this->offset = $table_offset;
    $table_length = $data->encode();

    $font->seek($table_offset);
    $table_data = $font->read($table_length);

    $font->seek($entry_offset);

    $font->write($this->tag, 4);
    $font->writeUInt32(self::computeChecksum($table_data));
    $font->writeUInt32($table_offset);
    $font->writeUInt32($table_length);

    Font::d("Bytes written = $table_length");

    $font->seek($table_offset + $table_length);
  }

  /**
   * @return File
   */
  function getFont() {
    return $this->font;
  }

  function startRead() {
    $this->font->seek($this->offset);
  }

  function endRead() {
    //
  }

  function startWrite() {
    $this->font->seek($this->offset);
  }

  function endWrite() {
    //
  }
}

<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */
namespace FontLib\Table;

use FontLib\TrueType\File;
use FontLib\Font;
use FontLib\BinaryStream;

/**
 * Generic font table.
 *
 * @package php-font-lib
 */
class Table extends BinaryStream {
  /**
   * @var DirectoryEntry
   */
  protected $entry;
  protected $def = array();

  public $data;

  final public function __construct(DirectoryEntry $entry) {
    $this->entry = $entry;
    $entry->setTable($this);
  }

  /**
   * @return File
   */
  public function getFont() {
    return $this->entry->getFont();
  }

  protected function _encode() {
    if (empty($this->data)) {
      Font::d("  >> Table is empty");

      return 0;
    }

    return $this->getFont()->pack($this->def, $this->data);
  }

  protected function _parse() {
    $this->data = $this->getFont()->unpack($this->def);
  }

  protected function _parseRaw() {
    $this->data = $this->getFont()->read($this->entry->length);
  }

  protected function _encodeRaw() {
    return $this->getFont()->write($this->data, $this->entry->length);
  }

  public function toHTML() {
    return "<pre>" . var_export($this->data, true) . "</pre>";
  }

  final public function encode() {
    $this->entry->startWrite();

    if (false && empty($this->def)) {
      $length = $this->_encodeRaw();
    }
    else {
      $length = $this->_encode();
    }

    $this->entry->endWrite();

    return $length;
  }

  final public function parse() {
    $this->entry->startRead();

    if (false && empty($this->def)) {
      $this->_parseRaw();
    }
    else {
      $this->_parse();
    }

    $this->entry->endRead();
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\Table\Type;
use FontLib\Table\Table;

/**
 * `cmap` font table.
 *
 * @package php-font-lib
 */
class cmap extends Table {
  private static $header_format = array(
    "version"         => self::uint16,
    "numberSubtables" => self::uint16,
  );

  private static $subtable_header_format = array(
    "platformID"         => self::uint16,
    "platformSpecificID" => self::uint16,
    "offset"             => self::uint32,
  );

  private static $subtable_v4_format = array(
    "length"        => self::uint16,
    "language"      => self::uint16,
    "segCountX2"    => self::uint16,
    "searchRange"   => self::uint16,
    "entrySelector" => self::uint16,
    "rangeShift"    => self::uint16,
  );

  private static $subtable_v12_format = array(
    "length"        => self::uint32,
    "language"      => self::uint32,
    "ngroups"    => self::uint32
  );

  protected function _parse() {
    $font = $this->getFont();

    $cmap_offset = $font->pos();

    $data = $font->unpack(self::$header_format);

    $subtables = array();
    for ($i = 0; $i < $data["numberSubtables"]; $i++) {
      $subtables[] = $font->unpack(self::$subtable_header_format);
    }

    $data["subtables"] = $subtables;

    foreach ($data["subtables"] as $i => &$subtable) {
      $font->seek($cmap_offset + $subtable["offset"]);

      $subtable["format"] = $font->readUInt16();

      // @todo Only CMAP version 4 and 12
      if (($subtable["format"] != 4) && ($subtable["format"] != 12)) {
        unset($data["subtables"][$i]);
        $data["numberSubtables"]--;
        continue;
      }

      if ($subtable["format"] == 12) {

        $font->readUInt16();

        $subtable += $font->unpack(self::$subtable_v12_format);

        $glyphIndexArray = array();
        $endCodes = array();
        $startCodes = array();

        for ($p = 0; $p < $subtable['ngroups']; $p++) {

          $startCode = $startCodes[] = $font->readUInt32();
          $endCode = $endCodes[] = $font->readUInt32();
          $startGlyphCode = $font->readUInt32();

          for ($c = $startCode; $c <= $endCode; $c++) {
            $glyphIndexArray[$c] = $startGlyphCode;
            $startGlyphCode++;
          }
        }

        $subtable += array(
          "startCode" => $startCodes,
          "endCode" => $endCodes,
          "glyphIndexArray" => $glyphIndexArray,
        );

      }
      else if ($subtable["format"] == 4) {

        $subtable += $font->unpack(self::$subtable_v4_format);

        $segCount             = $subtable["segCountX2"] / 2;
        $subtable["segCount"] = $segCount;

        $endCode = $font->readUInt16Many($segCount);

        $font->readUInt16(); // reservedPad

        $startCode = $font->readUInt16Many($segCount);
        $idDelta   = $font->readInt16Many($segCount);

        $ro_start      = $font->pos();
        $idRangeOffset = $font->readUInt16Many($segCount);

        $glyphIndexArray = array();
        for ($i = 0; $i < $segCount; $i++) {
          $c1 = $startCode[$i];
          $c2 = $endCode[$i];
          $d  = $idDelta[$i];
          $ro = $idRangeOffset[$i];

          if ($ro > 0) {
            $font->seek($subtable["offset"] + 2 * $i + $ro);
          }

          for ($c = $c1; $c <= $c2; $c++) {
            if ($ro == 0) {
              $gid = ($c + $d) & 0xFFFF;
            }
            else {
              $offset = ($c - $c1) * 2 + $ro;
              $offset = $ro_start + 2 * $i + $offset;

              $font->seek($offset);
              $gid = $font->readUInt16();

              if ($gid != 0) {
                $gid = ($gid + $d) & 0xFFFF;
              }
            }

            if ($gid > 0) {
              $glyphIndexArray[$c] = $gid;
            }
          }
        }

        $subtable += array(
          "endCode"         => $endCode,
          "startCode"       => $startCode,
          "idDelta"         => $idDelta,
          "idRangeOffset"   => $idRangeOffset,
          "glyphIndexArray" => $glyphIndexArray,
        );
      }
    }

    $this->data = $data;
  }

  function _encode() {
    $font = $this->getFont();

    $subset          = $font->getSubset();
    $glyphIndexArray = $font->getUnicodeCharMap();

    $newGlyphIndexArray = array();
    foreach ($glyphIndexArray as $code => $gid) {
      $new_gid = array_search($gid, $subset);
      if ($new_gid !== false) {
        $newGlyphIndexArray[$code] = $new_gid;
      }
    }

    ksort($newGlyphIndexArray); // Sort by char code

    $segments = array();

    $i        = -1;
    $prevCode = 0xFFFF;
    $prevGid  = 0xFFFF;

    foreach ($newGlyphIndexArray as $code => $gid) {
      if (
        $prevCode + 1 != $code ||
        $prevGid + 1 != $gid
      ) {
        $i++;
        $segments[$i] = array();
      }

      $segments[$i][] = array($code, $gid);

      $prevCode = $code;
      $prevGid  = $gid;
    }

    $segments[][] = array(0xFFFF, 0xFFFF);

    $startCode = array();
    $endCode   = array();
    $idDelta   = array();

    foreach ($segments as $codes) {
      $start = reset($codes);
      $end   = end($codes);

      $startCode[] = $start[0];
      $endCode[]   = $end[0];
      $idDelta[]   = $start[1] - $start[0];
    }

    $segCount      = count($startCode);
    $idRangeOffset = array_fill(0, $segCount, 0);

    $searchRange   = 1;
    $entrySelector = 0;
    while ($searchRange * 2 <= $segCount) {
      $searchRange *= 2;
      $entrySelector++;
    }
    $searchRange *= 2;
    $rangeShift = $segCount * 2 - $searchRange;

    $subtables = array(
      array(
        // header
        "platformID"         => 3, // Unicode
        "platformSpecificID" => 1,
        "offset"             => null,

        // subtable
        "format"             => 4,
        "length"             => null,
        "language"           => 0,
        "segCount"           => $segCount,
        "segCountX2"         => $segCount * 2,
        "searchRange"        => $searchRange,
        "entrySelector"      => $entrySelector,
        "rangeShift"         => $rangeShift,
        "startCode"          => $startCode,
        "endCode"            => $endCode,
        "idDelta"            => $idDelta,
        "idRangeOffset"      => $idRangeOffset,
        "glyphIndexArray"    => $newGlyphIndexArray,
      )
    );

    $data = array(
      "version"         => 0,
      "numberSubtables" => count($subtables),
      "subtables"       => $subtables,
    );

    $length = $font->pack(self::$header_format, $data);

    $subtable_headers_size   = $data["numberSubtables"] * 8; // size of self::$subtable_header_format
    $subtable_headers_offset = $font->pos();

    $length += $font->write(str_repeat("\0", $subtable_headers_size), $subtable_headers_size);

    // write subtables data
    foreach ($data["subtables"] as $i => $subtable) {
      $length_before                   = $length;
      $data["subtables"][$i]["offset"] = $length;

      $length += $font->writeUInt16($subtable["format"]);

      $before_subheader = $font->pos();
      $length += $font->pack(self::$subtable_v4_format, $subtable);

      $segCount = $subtable["segCount"];
      $length += $font->w(array(self::uint16, $segCount), $subtable["endCode"]);
      $length += $font->writeUInt16(0); // reservedPad
      $length += $font->w(array(self::uint16, $segCount), $subtable["startCode"]);
      $length += $font->w(array(self::int16, $segCount), $subtable["idDelta"]);
      $length += $font->w(array(self::uint16, $segCount), $subtable["idRangeOffset"]);
      $length += $font->w(array(self::uint16, $segCount), array_values($subtable["glyphIndexArray"]));

      $after_subtable = $font->pos();

      $subtable["length"] = $length - $length_before;
      $font->seek($before_subheader);
      $length += $font->pack(self::$subtable_v4_format, $subtable);

      $font->seek($after_subtable);
    }

    // write subtables headers
    $font->seek($subtable_headers_offset);
    foreach ($data["subtables"] as $subtable) {
      $font->pack(self::$subtable_header_format, $subtable);
    }

    return $length;
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\Table\Type;

use FontLib\Table\Table;
use FontLib\Glyph\Outline;
use FontLib\Glyph\OutlineSimple;

/**
 * `glyf` font table.
 *
 * @package php-font-lib
 * @property Outline[] $data
 */
class glyf extends Table {
  protected function _parse() {
    $font   = $this->getFont();
    $offset = $font->pos();

    $loca      = $font->getData("loca");
    $real_loca = array_slice($loca, 0, -1); // Not the last dummy loca entry

    $data = array();

    foreach ($real_loca as $gid => $location) {
      $_offset    = $offset + $loca[$gid];
      $_size      = $loca[$gid + 1] - $loca[$gid];
      $data[$gid] = Outline::init($this, $_offset, $_size, $font);
    }

    $this->data = $data;
  }

  public function getGlyphIDs($gids = array()) {
    $glyphIDs = array();

    foreach ($gids as $_gid) {
      $_glyph   = $this->data[$_gid];
      $glyphIDs = array_merge($glyphIDs, $_glyph->getGlyphIDs());
    }

    return array_unique(array_merge($gids, $glyphIDs));
  }

  public function toHTML() {
    $max  = 160;
    $font = $this->getFont();

    $head      = $font->getData("head");
    $head_json = json_encode($head);

    $os2      = $font->getData("OS/2");
    $os2_json = json_encode($os2);

    $hmtx      = $font->getData("hmtx");
    $hmtx_json = json_encode($hmtx);

    $names           = $font->getData("post", "names");
    $glyphIndexArray = array_flip($font->getUnicodeCharMap());

    $width  = (abs($head["xMin"]) + $head["xMax"]);
    $height = (abs($head["yMin"]) + $head["yMax"]);

    $ratio = 1;
    if ($width > $max || $height > $max) {
      $ratio  = max($width, $height) / $max;
      $width  = round($width / $ratio);
      $height = round($height / $ratio);
    }

    $n = 500;

    $s = "<h3>" . "Only the first $n simple glyphs are shown (" . count($this->data) . " total)
    <div class='glyph-view simple'>Simple glyph</div>
    <div class='glyph-view composite'>Composite glyph</div>
    Zoom: <input type='range' value='100' max='400' onchange='Glyph.resize(this.value)' />
    </h3>
    <script>
      Glyph.ratio  = $ratio;
      Glyph.head   = $head_json;
      Glyph.os2    = $os2_json;
      Glyph.hmtx   = $hmtx_json;
      Glyph.width  = $width;
      Glyph.height = $height;
    </script>";

    foreach ($this->data as $g => $glyph) {
      if ($n-- <= 0) {
        break;
      }

      $glyph->parseData();

      $shape      = array(
        "SVGContours" => $glyph->getSVGContours(),
        "xMin"        => $glyph->xMin,
        "yMin"        => $glyph->yMin,
        "xMax"        => $glyph->xMax,
        "yMax"        => $glyph->yMax,
      );
      $shape_json = json_encode($shape);

      $type = ($glyph instanceof OutlineSimple ? "simple" : "composite");
      $char = isset($glyphIndexArray[$g]) ? $glyphIndexArray[$g] : 0;
      $name = isset($names[$g]) ? $names[$g] : sprintf("uni%04x", $char);
      $char = $char ? "&#{$glyphIndexArray[$g]};" : "";

      $s .= "<div class='glyph-view $type' id='glyph-$g'>
              <span class='glyph-id'>$g</span>
              <span class='char'>$char</span>
              <span class='char-name'>$name</span>
              ";

      if ($type == "composite") {
        foreach ($glyph->getGlyphIDs() as $_id) {
          $s .= "<a href='#glyph-$_id' class='glyph-component-id'>$_id</a> ";
        }
      }

      $s .= "<br />
            <canvas width='$width' height='$height' id='glyph-canvas-$g'></canvas>
            </div>
            <script>Glyph.glyphs.push([$g,$shape_json]);</script>";
    }

    return $s;
  }


  protected function _encode() {
    $font   = $this->getFont();
    $subset = $font->getSubset();
    $data   = $this->data;

    $loca = array();

    $length = 0;
    foreach ($subset as $gid) {
      $loca[] = $length;
      $length += $data[$gid]->encode();
    }

    $loca[]                             = $length; // dummy loca
    $font->getTableObject("loca")->data = $loca;

    return $length;
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\Table\Type;
use FontLib\Table\Table;
use Exception;

/**
 * `head` font table.
 *
 * @package php-font-lib
 */
class head extends Table {
  protected $def = array(
    "tableVersion"       => self::Fixed,
    "fontRevision"       => self::Fixed,
    "checkSumAdjustment" => self::uint32,
    "magicNumber"        => self::uint32,
    "flags"              => self::uint16,
    "unitsPerEm"         => self::uint16,
    "created"            => self::longDateTime,
    "modified"           => self::longDateTime,
    "xMin"               => self::FWord,
    "yMin"               => self::FWord,
    "xMax"               => self::FWord,
    "yMax"               => self::FWord,
    "macStyle"           => self::uint16,
    "lowestRecPPEM"      => self::uint16,
    "fontDirectionHint"  => self::int16,
    "indexToLocFormat"   => self::int16,
    "glyphDataFormat"    => self::int16,
  );

  protected function _parse() {
    parent::_parse();

    if ($this->data["magicNumber"] != 0x5F0F3CF5) {
      throw new Exception("Incorrect magic number (" . dechex($this->data["magicNumber"]) . ")");
    }
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\Table\Type;
use FontLib\Table\Table;

/**
 * `hhea` font table.
 *
 * @package php-font-lib
 */
class hhea extends Table {
  protected $def = array(
    "version"             => self::Fixed,
    "ascent"              => self::FWord,
    "descent"             => self::FWord,
    "lineGap"             => self::FWord,
    "advanceWidthMax"     => self::uFWord,
    "minLeftSideBearing"  => self::FWord,
    "minRightSideBearing" => self::FWord,
    "xMaxExtent"          => self::FWord,
    "caretSlopeRise"      => self::int16,
    "caretSlopeRun"       => self::int16,
    "caretOffset"         => self::FWord,
    self::int16,
    self::int16,
    self::int16,
    self::int16,
    "metricDataFormat"    => self::int16,
    "numOfLongHorMetrics" => self::uint16,
  );

  function _encode() {
    $font                              = $this->getFont();
    $this->data["numOfLongHorMetrics"] = count($font->getSubset());

    return parent::_encode();
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\Table\Type;
use FontLib\Table\Table;

/**
 * `hmtx` font table.
 *
 * @package php-font-lib
 */
class hmtx extends Table {
  protected function _parse() {
    $font   = $this->getFont();
    $offset = $font->pos();

    $numOfLongHorMetrics = $font->getData("hhea", "numOfLongHorMetrics");
    $numGlyphs           = $font->getData("maxp", "numGlyphs");

    $font->seek($offset);

    $data = array();
    $metrics = $font->readUInt16Many($numOfLongHorMetrics * 2);
    for ($gid = 0, $mid = 0; $gid < $numOfLongHorMetrics; $gid++) {
      $advanceWidth    = isset($metrics[$mid]) ? $metrics[$mid] : 0;
      $mid += 1;
      $leftSideBearing = isset($metrics[$mid]) ? $metrics[$mid] : 0;
      $mid += 1;
      $data[$gid]      = array($advanceWidth, $leftSideBearing);
    }

    if ($numOfLongHorMetrics < $numGlyphs) {
      $lastWidth = end($data);
      $data      = array_pad($data, $numGlyphs, $lastWidth);
    }

    $this->data = $data;
  }

  protected function _encode() {
    $font   = $this->getFont();
    $subset = $font->getSubset();
    $data   = $this->data;

    $length = 0;

    foreach ($subset as $gid) {
      $length += $font->writeUInt16($data[$gid][0]);
      $length += $font->writeUInt16($data[$gid][1]);
    }

    return $length;
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\Table\Type;
use FontLib\Table\Table;

/**
 * `kern` font table.
 *
 * @package php-font-lib
 */
class kern extends Table {
  protected function _parse() {
    $font = $this->getFont();

    $data = $font->unpack(array(
      "version"         => self::uint16,
      "nTables"         => self::uint16,

      // only the first subtable will be parsed
      "subtableVersion" => self::uint16,
      "length"          => self::uint16,
      "coverage"        => self::uint16,
    ));

    $data["format"] = ($data["coverage"] >> 8);

    $subtable = array();

    switch ($data["format"]) {
      case 0:
        $subtable = $font->unpack(array(
          "nPairs"        => self::uint16,
          "searchRange"   => self::uint16,
          "entrySelector" => self::uint16,
          "rangeShift"    => self::uint16,
        ));

        $pairs = array();
        $tree  = array();

        $values = $font->readUInt16Many($subtable["nPairs"] * 3);
        for ($i = 0, $idx = 0; $i < $subtable["nPairs"]; $i++) {
          $left  = $values[$idx++];
          $right = $values[$idx++];
          $value = $values[$idx++];

          if ($value >= 0x8000) {
            $value -= 0x10000;
          }

          $pairs[] = array(
            "left"  => $left,
            "right" => $right,
            "value" => $value,
          );

          $tree[$left][$right] = $value;
        }

        //$subtable["pairs"] = $pairs;
        $subtable["tree"] = $tree;
        break;

      case 1:
      case 2:
      case 3:
        break;
    }

    $data["subtable"] = $subtable;

    $this->data = $data;
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\Table\Type;
use FontLib\Table\Table;

/**
 * `loca` font table.
 *
 * @package php-font-lib
 */
class loca extends Table {
  protected function _parse() {
    $font   = $this->getFont();
    $offset = $font->pos();

    $indexToLocFormat = $font->getData("head", "indexToLocFormat");
    $numGlyphs        = $font->getData("maxp", "numGlyphs");

    $font->seek($offset);

    $data = array();

    // 2 bytes
    if ($indexToLocFormat == 0) {
      $d   = $font->read(($numGlyphs + 1) * 2);
      $loc = unpack("n*", $d);

      for ($i = 0; $i <= $numGlyphs; $i++) {
        $data[] = isset($loc[$i + 1]) ?  $loc[$i + 1] * 2 : 0;
      }
    }

    // 4 bytes
    else {
      if ($indexToLocFormat == 1) {
        $d   = $font->read(($numGlyphs + 1) * 4);
        $loc = unpack("N*", $d);

        for ($i = 0; $i <= $numGlyphs; $i++) {
          $data[] = isset($loc[$i + 1]) ? $loc[$i + 1] : 0;
        }
      }
    }

    $this->data = $data;
  }

  function _encode() {
    $font = $this->getFont();
    $data = $this->data;

    $indexToLocFormat = $font->getData("head", "indexToLocFormat");
    $numGlyphs        = $font->getData("maxp", "numGlyphs");
    $length           = 0;

    // 2 bytes
    if ($indexToLocFormat == 0) {
      for ($i = 0; $i <= $numGlyphs; $i++) {
        $length += $font->writeUInt16($data[$i] / 2);
      }
    }

    // 4 bytes
    else {
      if ($indexToLocFormat == 1) {
        for ($i = 0; $i <= $numGlyphs; $i++) {
          $length += $font->writeUInt32($data[$i]);
        }
      }
    }

    return $length;
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\Table\Type;
use FontLib\Table\Table;

/**
 * `maxp` font table.
 *
 * @package php-font-lib
 */
class maxp extends Table {
  protected $def = array(
    "version"               => self::Fixed,
    "numGlyphs"             => self::uint16,
    "maxPoints"             => self::uint16,
    "maxContours"           => self::uint16,
    "maxComponentPoints"    => self::uint16,
    "maxComponentContours"  => self::uint16,
    "maxZones"              => self::uint16,
    "maxTwilightPoints"     => self::uint16,
    "maxStorage"            => self::uint16,
    "maxFunctionDefs"       => self::uint16,
    "maxInstructionDefs"    => self::uint16,
    "maxStackElements"      => self::uint16,
    "maxSizeOfInstructions" => self::uint16,
    "maxComponentElements"  => self::uint16,
    "maxComponentDepth"     => self::uint16,
  );

  function _encode() {
    $font                    = $this->getFont();
    $this->data["numGlyphs"] = count($font->getSubset());

    return parent::_encode();
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\Table\Type;

use FontLib\Table\Table;
use FontLib\Font;

/**
 * `name` font table.
 *
 * @package php-font-lib
 */
class name extends Table {
  private static $header_format = array(
    "format"       => self::uint16,
    "count"        => self::uint16,
    "stringOffset" => self::uint16,
  );

  const NAME_COPYRIGHT          = 0;
  const NAME_NAME               = 1;
  const NAME_SUBFAMILY          = 2;
  const NAME_SUBFAMILY_ID       = 3;
  const NAME_FULL_NAME          = 4;
  const NAME_VERSION            = 5;
  const NAME_POSTSCRIPT_NAME    = 6;
  const NAME_TRADEMARK          = 7;
  const NAME_MANUFACTURER       = 8;
  const NAME_DESIGNER           = 9;
  const NAME_DESCRIPTION        = 10;
  const NAME_VENDOR_URL         = 11;
  const NAME_DESIGNER_URL       = 12;
  const NAME_LICENSE            = 13;
  const NAME_LICENSE_URL        = 14;
  const NAME_PREFERRE_FAMILY    = 16;
  const NAME_PREFERRE_SUBFAMILY = 17;
  const NAME_COMPAT_FULL_NAME   = 18;
  const NAME_SAMPLE_TEXT        = 19;

  static $nameIdCodes = array(
    0  => "Copyright",
    1  => "FontName",
    2  => "FontSubfamily",
    3  => "UniqueID",
    4  => "FullName",
    5  => "Version",
    6  => "PostScriptName",
    7  => "Trademark",
    8  => "Manufacturer",
    9  => "Designer",
    10 => "Description",
    11 => "FontVendorURL",
    12 => "FontDesignerURL",
    13 => "LicenseDescription",
    14 => "LicenseURL",
    // 15
    16 => "PreferredFamily",
    17 => "PreferredSubfamily",
    18 => "CompatibleFullName",
    19 => "SampleText",
  );

  static $platforms = array(
    0 => "Unicode",
    1 => "Macintosh",
    // 2 =>  Reserved
    3 => "Microsoft",
  );

  static $platformSpecific = array(
    // Unicode
    0 => array(
      0 => "Default semantics",
      1 => "Version 1.1 semantics",
      2 => "ISO 10646 1993 semantics (deprecated)",
      3 => "Unicode 2.0 or later semantics",
    ),

    // Macintosh
    1 => array(
      0  => "Roman",
      1  => "Japanese",
      2  => "Traditional Chinese",
      3  => "Korean",
      4  => "Arabic",
      5  => "Hebrew",
      6  => "Greek",
      7  => "Russian",
      8  => "RSymbol",
      9  => "Devanagari",
      10 => "Gurmukhi",
      11 => "Gujarati",
      12 => "Oriya",
      13 => "Bengali",
      14 => "Tamil",
      15 => "Telugu",
      16 => "Kannada",
      17 => "Malayalam",
      18 => "Sinhalese",
      19 => "Burmese",
      20 => "Khmer",
      21 => "Thai",
      22 => "Laotian",
      23 => "Georgian",
      24 => "Armenian",
      25 => "Simplified Chinese",
      26 => "Tibetan",
      27 => "Mongolian",
      28 => "Geez",
      29 => "Slavic",
      30 => "Vietnamese",
      31 => "Sindhi",
    ),

    // Microsoft
    3 => array(
      0  => "Symbol",
      1  => "Unicode BMP (UCS-2)",
      2  => "ShiftJIS",
      3  => "PRC",
      4  => "Big5",
      5  => "Wansung",
      6  => "Johab",
      //  7 => Reserved
      //  8 => Reserved
      //  9 => Reserved
      10 => "Unicode UCS-4",
    ),
  );

  protected function _parse() {
    $font = $this->getFont();

    $tableOffset = $font->pos();

    $data = $font->unpack(self::$header_format);

    $records = array();
    for ($i = 0; $i < $data["count"]; $i++) {
      $record      = new nameRecord();
      $record_data = $font->unpack(nameRecord::$format);
      $record->map($record_data);

      $records[] = $record;
    }

    $names = array();
    foreach ($records as $record) {
      $font->seek($tableOffset + $data["stringOffset"] + $record->offset);
      $s                      = $font->read($record->length);
      $record->string         = Font::UTF16ToUTF8($s);
      $names[$record->nameID] = $record;
    }

    $data["records"] = $names;

    $this->data = $data;
  }

  protected function _encode() {
    $font = $this->getFont();

    /** @var nameRecord[] $records */
    $records       = $this->data["records"];
    $count_records = count($records);

    $this->data["count"]        = $count_records;
    $this->data["stringOffset"] = 6 + $count_records * 12; // 6 => uint16 * 3, 12 => sizeof self::$record_format

    $length = $font->pack(self::$header_format, $this->data);

    $offset = 0;
    foreach ($records as $record) {
      $record->length = mb_strlen($record->getUTF16(), "8bit");
      $record->offset = $offset;
      $offset += $record->length;
      $length += $font->pack(nameRecord::$format, (array)$record);
    }

    foreach ($records as $record) {
      $str = $record->getUTF16();
      $length += $font->write($str, mb_strlen($str, "8bit"));
    }

    return $length;
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */
namespace FontLib\Table\Type;

use FontLib\Font;
use FontLib\BinaryStream;

/**
 * Font table name record.
 *
 * @package php-font-lib
 */
class nameRecord extends BinaryStream {
  public $platformID;
  public $platformSpecificID;
  public $languageID;
  public $nameID;
  public $length;
  public $offset;
  public $string;

  public static $format = array(
    "platformID"         => self::uint16,
    "platformSpecificID" => self::uint16,
    "languageID"         => self::uint16,
    "nameID"             => self::uint16,
    "length"             => self::uint16,
    "offset"             => self::uint16,
  );

  public function map($data) {
    foreach ($data as $key => $value) {
      $this->$key = $value;
    }
  }

  public function getUTF8() {
    return $this->string;
  }

  public function getUTF16() {
    return Font::UTF8ToUTF16($this->string);
  }

  function __toString() {
    return $this->string;
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\Table\Type;
use FontLib\Table\Table;

/**
 * `OS/2` font table.
 *
 * @package php-font-lib
 */
class os2 extends Table {
  protected $def = array(
    "version"             => self::uint16,
    "xAvgCharWidth"       => self::int16,
    "usWeightClass"       => self::uint16,
    "usWidthClass"        => self::uint16,
    "fsType"              => self::int16,
    "ySubscriptXSize"     => self::int16,
    "ySubscriptYSize"     => self::int16,
    "ySubscriptXOffset"   => self::int16,
    "ySubscriptYOffset"   => self::int16,
    "ySuperscriptXSize"   => self::int16,
    "ySuperscriptYSize"   => self::int16,
    "ySuperscriptXOffset" => self::int16,
    "ySuperscriptYOffset" => self::int16,
    "yStrikeoutSize"      => self::int16,
    "yStrikeoutPosition"  => self::int16,
    "sFamilyClass"        => self::int16,
    "panose"              => array(self::uint8, 10),
    "ulCharRange"         => array(self::uint32, 4),
    "achVendID"           => array(self::char, 4),
    "fsSelection"         => self::uint16,
    "fsFirstCharIndex"    => self::uint16,
    "fsLastCharIndex"     => self::uint16,
    "typoAscender"        => self::int16,
    "typoDescender"       => self::int16,
    "typoLineGap"         => self::int16,
    "winAscent"           => self::int16,
    "winDescent"          => self::int16,
  );
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\Table\Type;
use FontLib\Table\Table;
use FontLib\TrueType\File;

/**
 * `post` font table.
 *
 * @package php-font-lib
 */
class post extends Table {
  protected $def = array(
    "format"             => self::Fixed,
    "italicAngle"        => self::Fixed,
    "underlinePosition"  => self::FWord,
    "underlineThickness" => self::FWord,
    "isFixedPitch"       => self::uint32,
    "minMemType42"       => self::uint32,
    "maxMemType42"       => self::uint32,
    "minMemType1"        => self::uint32,
    "maxMemType1"        => self::uint32,
  );

  protected function _parse() {
    $font = $this->getFont();
    $data = $font->unpack($this->def);

    $names = array();

    switch ($data["format"]) {
      case 1:
        $names = File::$macCharNames;
        break;

      case 2:
        $data["numberOfGlyphs"] = $font->readUInt16();

        $glyphNameIndex = $font->readUInt16Many($data["numberOfGlyphs"]);

        $data["glyphNameIndex"] = $glyphNameIndex;

        $namesPascal = array();
        for ($i = 0; $i < $data["numberOfGlyphs"]; $i++) {
          $len           = $font->readUInt8();
          $namesPascal[] = $font->read($len);
        }

        foreach ($glyphNameIndex as $g => $index) {
          if ($index < 258) {
            $names[$g] = File::$macCharNames[$index];
          }
          else {
            if (array_key_exists($index - 258, $namesPascal)) {
              $names[$g] = $namesPascal[$index - 258];
            }
          }
        }

        break;

      case 2.5:
        // TODO
        break;

      case 3:
        // nothing
        break;

      case 4:
        // TODO
        break;
    }

    $data["names"] = $names;

    $this->data = $data;
  }

  function _encode() {
    $font           = $this->getFont();
    $data           = $this->data;
    $data["format"] = 3;

    $length = $font->pack($this->def, $data);

    return $length;
    /*
    $subset = $font->getSubset();

    switch($data["format"]) {
      case 1:
        // nothing to do
      break;

      case 2:
        $old_names = $data["names"];

        $glyphNameIndex = range(0, count($subset));

        $names = array();
        foreach($subset as $gid) {
          $names[] = $data["names"][$data["glyphNameIndex"][$gid]];
        }

        $numberOfGlyphs = count($names);
        $length += $font->writeUInt16($numberOfGlyphs);

        foreach($glyphNameIndex as $gni) {
          $length += $font->writeUInt16($gni);
        }

        //$names = array_slice($names, 257);
        foreach($names as $name) {
          $len = strlen($name);
          $length += $font->writeUInt8($len);
          $length += $font->write($name, $len);
        }

      break;

      case 2.5:
        // TODO
      break;

      case 3:
        // nothing
      break;

      case 4:
        // TODO
      break;
    }

    return $length;*/
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\TrueType;

use Countable;
use FontLib\BinaryStream;
use Iterator;
use OutOfBoundsException;

/**
 * TrueType collection font file.
 *
 * @package php-font-lib
 */
class Collection extends BinaryStream implements Iterator, Countable {
  /**
   * Current iterator position.
   *
   * @var integer
   */
  private $position = 0;

  protected $collectionOffsets = array();
  protected $collection = array();
  protected $version;
  protected $numFonts;

  function parse() {
    if (isset($this->numFonts)) {
      return;
    }

    $this->read(4); // tag name

    $this->version  = $this->readFixed();
    $this->numFonts = $this->readUInt32();

    for ($i = 0; $i < $this->numFonts; $i++) {
      $this->collectionOffsets[] = $this->readUInt32();
    }
  }

  /**
   * @param int $fontId
   *
   * @throws OutOfBoundsException
   * @return File
   */
  function getFont($fontId) {
    $this->parse();

    if (!isset($this->collectionOffsets[$fontId])) {
      throw new OutOfBoundsException();
    }

    if (isset($this->collection[$fontId])) {
      return $this->collection[$fontId];
    }

    $font    = new File();
    $font->f = $this->f;
    $font->setTableOffset($this->collectionOffsets[$fontId]);

    return $this->collection[$fontId] = $font;
  }

  function current() {
    return $this->getFont($this->position);
  }

  function key() {
    return $this->position;
  }

  function next() {
    return ++$this->position;
  }

  function rewind() {
    $this->position = 0;
  }

  function valid() {
    $this->parse();

    return isset($this->collectionOffsets[$this->position]);
  }

  function count() {
    $this->parse();

    return $this->numFonts;
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\TrueType;

use FontLib\AdobeFontMetrics;
use FontLib\Font;
use FontLib\BinaryStream;
use FontLib\Table\Table;
use FontLib\Table\DirectoryEntry;
use FontLib\Table\Type\glyf;
use FontLib\Table\Type\name;
use FontLib\Table\Type\nameRecord;

/**
 * TrueType font file.
 *
 * @package php-font-lib
 */
class File extends BinaryStream {
  /**
   * @var Header
   */
  public $header = array();

  private $tableOffset = 0; // Used for TTC

  private static $raw = false;

  protected $directory = array();
  protected $data = array();

  protected $glyph_subset = array();

  public $glyph_all = array();

  static $macCharNames = array(
    ".notdef", ".null", "CR",
    "space", "exclam", "quotedbl", "numbersign",
    "dollar", "percent", "ampersand", "quotesingle",
    "parenleft", "parenright", "asterisk", "plus",
    "comma", "hyphen", "period", "slash",
    "zero", "one", "two", "three",
    "four", "five", "six", "seven",
    "eight", "nine", "colon", "semicolon",
    "less", "equal", "greater", "question",
    "at", "A", "B", "C", "D", "E", "F", "G",
    "H", "I", "J", "K", "L", "M", "N", "O",
    "P", "Q", "R", "S", "T", "U", "V", "W",
    "X", "Y", "Z", "bracketleft",
    "backslash", "bracketright", "asciicircum", "underscore",
    "grave", "a", "b", "c", "d", "e", "f", "g",
    "h", "i", "j", "k", "l", "m", "n", "o",
    "p", "q", "r", "s", "t", "u", "v", "w",
    "x", "y", "z", "braceleft",
    "bar", "braceright", "asciitilde", "Adieresis",
    "Aring", "Ccedilla", "Eacute", "Ntilde",
    "Odieresis", "Udieresis", "aacute", "agrave",
    "acircumflex", "adieresis", "atilde", "aring",
    "ccedilla", "eacute", "egrave", "ecircumflex",
    "edieresis", "iacute", "igrave", "icircumflex",
    "idieresis", "ntilde", "oacute", "ograve",
    "ocircumflex", "odieresis", "otilde", "uacute",
    "ugrave", "ucircumflex", "udieresis", "dagger",
    "degree", "cent", "sterling", "section",
    "bullet", "paragraph", "germandbls", "registered",
    "copyright", "trademark", "acute", "dieresis",
    "notequal", "AE", "Oslash", "infinity",
    "plusminus", "lessequal", "greaterequal", "yen",
    "mu", "partialdiff", "summation", "product",
    "pi", "integral", "ordfeminine", "ordmasculine",
    "Omega", "ae", "oslash", "questiondown",
    "exclamdown", "logicalnot", "radical", "florin",
    "approxequal", "increment", "guillemotleft", "guillemotright",
    "ellipsis", "nbspace", "Agrave", "Atilde",
    "Otilde", "OE", "oe", "endash",
    "emdash", "quotedblleft", "quotedblright", "quoteleft",
    "quoteright", "divide", "lozenge", "ydieresis",
    "Ydieresis", "fraction", "currency", "guilsinglleft",
    "guilsinglright", "fi", "fl", "daggerdbl",
    "periodcentered", "quotesinglbase", "quotedblbase", "perthousand",
    "Acircumflex", "Ecircumflex", "Aacute", "Edieresis",
    "Egrave", "Iacute", "Icircumflex", "Idieresis",
    "Igrave", "Oacute", "Ocircumflex", "applelogo",
    "Ograve", "Uacute", "Ucircumflex", "Ugrave",
    "dotlessi", "circumflex", "tilde", "macron",
    "breve", "dotaccent", "ring", "cedilla",
    "hungarumlaut", "ogonek", "caron", "Lslash",
    "lslash", "Scaron", "scaron", "Zcaron",
    "zcaron", "brokenbar", "Eth", "eth",
    "Yacute", "yacute", "Thorn", "thorn",
    "minus", "multiply", "onesuperior", "twosuperior",
    "threesuperior", "onehalf", "onequarter", "threequarters",
    "franc", "Gbreve", "gbreve", "Idot",
    "Scedilla", "scedilla", "Cacute", "cacute",
    "Ccaron", "ccaron", "dmacron"
  );

  function getTable() {
    $this->parseTableEntries();

    return $this->directory;
  }

  function setTableOffset($offset) {
    $this->tableOffset = $offset;
  }

  function parse() {
    $this->parseTableEntries();

    $this->data = array();

    foreach ($this->directory as $tag => $table) {
      if (empty($this->data[$tag])) {
        $this->readTable($tag);
      }
    }
  }

  function utf8toUnicode($str) {
    $len = mb_strlen($str, '8bit');
    $out = array();

    for ($i = 0; $i < $len; $i++) {
      $uni = -1;
      $h   = ord($str[$i]);

      if ($h <= 0x7F) {
        $uni = $h;
      }
      elseif ($h >= 0xC2) {
        if (($h <= 0xDF) && ($i < $len - 1)) {
          $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);
        }
        elseif (($h <= 0xEF) && ($i < $len - 2)) {
          $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
        }
        elseif (($h <= 0xF4) && ($i < $len - 3)) {
          $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
        }
      }

      if ($uni >= 0) {
        $out[] = $uni;
      }
    }

    return $out;
  }

  function getUnicodeCharMap() {
    $subtable = null;
    foreach ($this->getData("cmap", "subtables") as $_subtable) {
      if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
        $subtable = $_subtable;
        break;
      }
    }

    if ($subtable) {
      return $subtable["glyphIndexArray"];
    }

    return null;
  }

  function setSubset($subset) {
    if (!is_array($subset)) {
      $subset = $this->utf8toUnicode($subset);
    }

    $subset = array_unique($subset);

    $glyphIndexArray = $this->getUnicodeCharMap();

    if (!$glyphIndexArray) {
      return;
    }

    $gids = array(
      0, // .notdef
      1, // .null
    );

    foreach ($subset as $code) {
      if (!isset($glyphIndexArray[$code])) {
        continue;
      }

      $gid        = $glyphIndexArray[$code];
      $gids[$gid] = $gid;
    }

    /** @var glyf $glyf */
    $glyf = $this->getTableObject("glyf");
    $gids = $glyf->getGlyphIDs($gids);

    sort($gids);

    $this->glyph_subset = $gids;
    $this->glyph_all    = array_values($glyphIndexArray); // FIXME
  }

  function getSubset() {
    if (empty($this->glyph_subset)) {
      return $this->glyph_all;
    }

    return $this->glyph_subset;
  }

  function encode($tags = array()) {
    if (!self::$raw) {
      $tags = array_merge(array("head", "hhea", "cmap", "hmtx", "maxp", "glyf", "loca", "name", "post"), $tags);
    }
    else {
      $tags = array_keys($this->directory);
    }

    $num_tables = count($tags);
    $n          = 16; // @todo

    Font::d("Tables : " . implode(", ", $tags));

    /** @var DirectoryEntry[] $entries */
    $entries = array();
    foreach ($tags as $tag) {
      if (!isset($this->directory[$tag])) {
        Font::d("  >> '$tag' table doesn't exist");
        continue;
      }

      $entries[$tag] = $this->directory[$tag];
    }

    $this->header->data["numTables"] = $num_tables;
    $this->header->encode();

    $directory_offset = $this->pos();
    $offset           = $directory_offset + $num_tables * $n;
    $this->seek($offset);

    $i = 0;
    foreach ($entries as $entry) {
      $entry->encode($directory_offset + $i * $n);
      $i++;
    }
  }

  function parseHeader() {
    if (!empty($this->header)) {
      return;
    }

    $this->seek($this->tableOffset);

    $this->header = new Header($this);
    $this->header->parse();
  }

  function getFontType(){
    $class_parts = explode("\\", get_class($this));
    return $class_parts[1];
  }

  function parseTableEntries() {
    $this->parseHeader();

    if (!empty($this->directory)) {
      return;
    }

    if (empty($this->header->data["numTables"])) {
      return;
    }


    $type = $this->getFontType();
    $class = "FontLib\\$type\\TableDirectoryEntry";

    for ($i = 0; $i < $this->header->data["numTables"]; $i++) {
      /** @var TableDirectoryEntry $entry */
      $entry = new $class($this);
      $entry->parse();

      $this->directory[$entry->tag] = $entry;
    }
  }

  function normalizeFUnit($value, $base = 1000) {
    return round($value * ($base / $this->getData("head", "unitsPerEm")));
  }

  protected function readTable($tag) {
    $this->parseTableEntries();

    if (!self::$raw) {
      $name_canon = preg_replace("/[^a-z0-9]/", "", strtolower($tag));

      $class = "FontLib\\Table\\Type\\$name_canon";

      if (!isset($this->directory[$tag]) || !@class_exists($class)) {
        return;
      }
    }
    else {
      $class = "FontLib\\Table\\Table";
    }

    /** @var Table $table */
    $table = new $class($this->directory[$tag]);
    $table->parse();

    $this->data[$tag] = $table;
  }

  /**
   * @param $name
   *
   * @return Table
   */
  public function getTableObject($name) {
    return $this->data[$name];
  }

  public function setTableObject($name, Table $data) {
    $this->data[$name] = $data;
  }

  public function getData($name, $key = null) {
    $this->parseTableEntries();

    if (empty($this->data[$name])) {
      $this->readTable($name);
    }

    if (!isset($this->data[$name])) {
      return null;
    }

    if (!$key) {
      return $this->data[$name]->data;
    }
    else {
      return $this->data[$name]->data[$key];
    }
  }

  function addDirectoryEntry(DirectoryEntry $entry) {
    $this->directory[$entry->tag] = $entry;
  }

  function saveAdobeFontMetrics($file, $encoding = null) {
    $afm = new AdobeFontMetrics($this);
    $afm->write($file, $encoding);
  }

  /**
   * Get a specific name table string value from its ID
   *
   * @param int $nameID The name ID
   *
   * @return string|null
   */
  function getNameTableString($nameID) {
    /** @var nameRecord[] $records */
    $records = $this->getData("name", "records");

    if (!isset($records[$nameID])) {
      return null;
    }

    return $records[$nameID]->string;
  }

  /**
   * Get font copyright
   *
   * @return string|null
   */
  function getFontCopyright() {
    return $this->getNameTableString(name::NAME_COPYRIGHT);
  }

  /**
   * Get font name
   *
   * @return string|null
   */
  function getFontName() {
    return $this->getNameTableString(name::NAME_NAME);
  }

  /**
   * Get font subfamily
   *
   * @return string|null
   */
  function getFontSubfamily() {
    return $this->getNameTableString(name::NAME_SUBFAMILY);
  }

  /**
   * Get font subfamily ID
   *
   * @return string|null
   */
  function getFontSubfamilyID() {
    return $this->getNameTableString(name::NAME_SUBFAMILY_ID);
  }

  /**
   * Get font full name
   *
   * @return string|null
   */
  function getFontFullName() {
    return $this->getNameTableString(name::NAME_FULL_NAME);
  }

  /**
   * Get font version
   *
   * @return string|null
   */
  function getFontVersion() {
    return $this->getNameTableString(name::NAME_VERSION);
  }

  /**
   * Get font weight
   *
   * @return string|null
   */
  function getFontWeight() {
    return $this->getTableObject("OS/2")->data["usWeightClass"];
  }

  /**
   * Get font Postscript name
   *
   * @return string|null
   */
  function getFontPostscriptName() {
    return $this->getNameTableString(name::NAME_POSTSCRIPT_NAME);
  }

  function reduce() {
    $names_to_keep = array(
      name::NAME_COPYRIGHT,
      name::NAME_NAME,
      name::NAME_SUBFAMILY,
      name::NAME_SUBFAMILY_ID,
      name::NAME_FULL_NAME,
      name::NAME_VERSION,
      name::NAME_POSTSCRIPT_NAME,
    );

    foreach ($this->data["name"]->data["records"] as $id => $rec) {
      if (!in_array($id, $names_to_keep)) {
        unset($this->data["name"]->data["records"][$id]);
      }
    }
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\TrueType;

/**
 * TrueType font file header.
 *
 * @package php-font-lib
 */
class Header extends \FontLib\Header {
  protected $def = array(
    "format"        => self::uint32,
    "numTables"     => self::uint16,
    "searchRange"   => self::uint16,
    "entrySelector" => self::uint16,
    "rangeShift"    => self::uint16,
  );

  public function parse() {
    parent::parse();

    $format                   = $this->data["format"];
    $this->data["formatText"] = $this->convertUInt32ToStr($format);
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\TrueType;

use FontLib\Table\DirectoryEntry;

/**
 * TrueType table directory entry.
 *
 * @package php-font-lib
 */
class TableDirectoryEntry extends DirectoryEntry {
  function __construct(File $font) {
    parent::__construct($font);
  }

  function parse() {
    parent::parse();

    $font           = $this->font;
    $this->checksum = $font->readUInt32();
    $this->offset   = $font->readUInt32();
    $this->length   = $font->readUInt32();
    $this->entryLength += 12;
  }
}

<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\WOFF;

use FontLib\Table\DirectoryEntry;

/**
 * WOFF font file.
 *
 * @package php-font-lib
 *
 * @property TableDirectoryEntry[] $directory
 */
class File extends \FontLib\TrueType\File {
  function parseHeader() {
    if (!empty($this->header)) {
      return;
    }

    $this->header = new Header($this);
    $this->header->parse();
  }

  public function load($file) {
    parent::load($file);

    $this->parseTableEntries();
    $dataOffset = $this->pos() + count($this->directory) * 20;

    $fw = $this->getTempFile(false);
    $fr = $this->f;

    $this->f = $fw;
    $offset  = $this->header->encode();

    foreach ($this->directory as $entry) {
      // Read ...
      $this->f = $fr;
      $this->seek($entry->offset);
      $data = $this->read($entry->length);

      if ($entry->length < $entry->origLength) {
        $data = (string) gzuncompress($data);
      }

      // Prepare data ...
      $length        = mb_strlen($data, '8bit');
      $entry->length = $entry->origLength = $length;
      $entry->offset = $dataOffset;

      // Write ...
      $this->f = $fw;

      // Woff Entry
      $this->seek($offset);
      $offset += $this->write($entry->tag, 4); // tag
      $offset += $this->writeUInt32($dataOffset); // offset
      $offset += $this->writeUInt32($length); // length
      $offset += $this->writeUInt32($length); // origLength
      $offset += $this->writeUInt32(DirectoryEntry::computeChecksum($data)); // checksum

      // Data
      $this->seek($dataOffset);
      $dataOffset += $this->write($data, $length);
    }

    $this->f = $fw;
    $this->seek(0);

    // Need to re-parse this, don't know why
    $this->header    = null;
    $this->directory = array();
    $this->parseTableEntries();
  }
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\WOFF;

/**
 * WOFF font file header.
 *
 * @package php-font-lib
 */
class Header extends \FontLib\TrueType\Header {
  protected $def = array(
    "format"         => self::uint32,
    "flavor"         => self::uint32,
    "length"         => self::uint32,
    "numTables"      => self::uint16,
    self::uint16,
    "totalSfntSize"  => self::uint32,
    "majorVersion"   => self::uint16,
    "minorVersion"   => self::uint16,
    "metaOffset"     => self::uint32,
    "metaLength"     => self::uint32,
    "metaOrigLength" => self::uint32,
    "privOffset"     => self::uint32,
    "privLength"     => self::uint32,
  );
}
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/PhenX/php-font-lib
 * @author  Fabien MÃ©nager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */

namespace FontLib\WOFF;

use FontLib\Table\DirectoryEntry;

/**
 * WOFF font file table directory entry.
 *
 * @package php-font-lib
 */
class TableDirectoryEntry extends DirectoryEntry {
  public $origLength;

  function __construct(File $font) {
    parent::__construct($font);
  }

  function parse() {
    parent::parse();

    $font             = $this->font;
    $this->offset     = $font->readUInt32();
    $this->length     = $font->readUInt32();
    $this->origLength = $font->readUInt32();
    $this->checksum   = $font->readUInt32();
  }
}
<?php 
 * Authors:    Liran Tal <liran@enginx.com>
 *             Filippo Lauria <filippo.lauria@iit.cnr.it>
    // prevent this file to be directly accessed
    if (strpos($_SERVER['PHP_SELF'], '/notifications/processNotificationBatchDetails.php') !== false) {
        header("Location: ../index.php");
        exit;
    }
    // common notification functions
    include("common.php");
    
    $base = dirname(__FILE__);
    $base_path = "$base/templates/";
    
    /**
     * createBatchDetailsNotification()
     * wrapper-function to create the notification to the customer
     * 
     * @param        array        customer information array
     */
    function createBatchDetailsNotification($customerInfo) {
        global $base_path;
        
        $html = prepareNotificationTemplate($customerInfo);
        return createPDF($html, $base_path);
    }
    
    /**
     * emailNotification()
     * creates an email message with the pdf and sends it
     * 
     * @param        string        pdf binary/string stream
     * @param        array         customer information array
     * @param        array         smtp server information
     * @param        string        from email address of the sender identity
     */
    function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
        $sendTo = $customerInfo['business_email'];
        $subject = "Business Pre-Paid Batch Information";
        $body = "Notification letter of service";
        $attachmentName = "notification.pdf";
        
        return send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName);
    }
    
    
    /**
     * prepareNotificationTemplate()
     * reads the notification (html) template and returns the formatted (html) notification
     * 
     * @param         array         Array of notification information
     * @return        string        HTML notification
     */
    function prepareNotificationTemplate($customerInfo) {
        global $base_path;
        
        // the HTML template
        $notification_template = $base_path . "/batch_details.html";
        
        $notification_html_template = file_get_contents($notification_template);
        
        $date = date("Y-m-d");
        
        $business_name = $customerInfo['business_name'];
        $business_owner_name = $customerInfo['business_owner_name']; 
        $business_address = $customerInfo['business_address'];
        $business_phone = $customerInfo['business_phone'];
        $business_email = $customerInfo['business_email'];
        $business_web = $customerInfo['business_web'];
        $batch_details = $customerInfo['batch_details'];
        $batch_active_users = $customerInfo['batch_active_users'];
        $service_plan_info = $customerInfo['service_plan_info'];
    
        $tagname_value_mappings = array(
                                            "__INVOICE_CREATION_DATE__" => $date,
                                            "__BUSINESS_NAME__" => $business_name,
                                            "__BUSINESS_OWNER_NAME__" => $business_owner_name,
                                            "__BUSINESS_ADDRESS__" => $business_address,
                                            "__BUSINESS_PHONE__" => $business_phone,
                                            "__BUSINESS_EMAIL__" => $business_email,
                                            "__BUSINESS_WEB__" => $business_web,
                                            "__BATCH_DETAILS__" => $batch_details,
                                            "__BATCH_ACTIVE_USERS__" => $batch_active_users,
                                            "__SERVICE_PLAN_INFO__" => $service_plan_info,
                                       );
    
        foreach ($tagname_value_mappings as $tagname => $value) {
            $tag = sprintf("####%s####", $tagname);
            $notification_html_template = str_replace($tag, $value, $notification_html_template);
        }
        // this fix has been set in place according to this thread:
        // https://stackoverflow.com/questions/37521775/dompdf-error-no-block-level-parent-found-not-good
        return str_replace("\n", "", $notification_html_template);
    }
<?php 
 * Authors:    Liran Tal <liran@enginx.com>
 *             Filippo Lauria <filippo.lauria@iit.cnr.it>
    // prevent this file to be directly accessed
    if (strpos($_SERVER['PHP_SELF'], '/notifications/processNotificationUserDetailsInvoice.php') !== false) {
        header("Location: ../index.php");
        exit;
    }
    
    // common notification functions
    include("common.php");
    
    $base = dirname(__FILE__);
	$base_path = "$base/templates/";
    
		global $base_path;
        
        $html = prepareNotificationTemplate($customerInfo);
		return createPDF($html, $base_path);
        $sendTo = $customerInfo['business_email'];
        $subject = "User Invoice Notification";
        $body = "Notification letter of service";
        $attachmentName = "invoice.pdf";
        
        return send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName);
		global $base_path;
		$notification_template = $base_path . "/user_invoice_details.html";
 * Authors:    Liran Tal <liran@enginx.com>
 *             Miguel GarcÃ­a <miguelvisgarcia@gmail.com>
 *             Filippo Lauria <filippo.lauria@iit.cnr.it>
    // common notification functions
    include("common.php");
    
    $base = dirname(__FILE__);
    $base_path = "$base/templates/";
    
    /**
     * createNotification()
     * wrapper-function to create the notification to the customer
     * 
     * @param         array          customer information array
     * @param         boolean        true for HTML output, false for PDF output
     * 
     * @return        string         returns the HTML or PDF output
     */
    function createNotification($customerInfo, $asHTML = false) {
        global $base_path;
        
        $html = prepareNotificationTemplate($customerInfo);
        return (($asHTML) ? $html : createPDF($html, $base_path));
    }
    
    /**
     * emailNotification()
     * creates an email message with the pdf and sends it
     * 
     * @param        string        pdf binary/string stream
     * @param        array         customer information array
     * @param        array         smtp server information
     * @param        string        from email address of the sender identity
     */
    function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
        $sendTo = $customerInfo['business_email'];
        $subject = "Invoice Information";
        $body = "Notification letter of service";
        $attachmentName = "notification.pdf";
        
        return send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName);
    }
    
    
    /**
     * prepareNotificationTemplate()
     * reads the notification (html) template and returns the formatted (html) notification
     * 
     * @param         array         Array of notification information
     * 
     * @return        string        HTML notification
     */
    function prepareNotificationTemplate($customerInfo) {
        global $base_path, $configValues;
        
        $templates_base = $base_path;
        
        // the default HTML template - OLD STYLE
        $notification_template = $templates_base . "user_invoice.html";
        $notification_item_template = null;
        
        // the default HTML template - NEW STYLE
        if(isset($configValues['CONFIG_INVOICE_TEMPLATE'])) {
            $notification_template = $templates_base . $configValues['CONFIG_INVOICE_TEMPLATE'];
            
            if(isset($configValues['CONFIG_INVOICE_ITEM_TEMPLATE'])) {
                $notification_item_template = $templates_base . $configValues['CONFIG_INVOICE_ITEM_TEMPLATE'];
            }
        }
        
        // the default HTML template for current location - NEW STYLE
        if ((isset($_SESSION['location_name'])) && ($_SESSION['location_name'] != "default")) {
            $location_name = $_SESSION['location_name'];
            $location = $configValues['CONFIG_LOCATIONS'][$location_name];
        
            if (isset($location['CONFIG_INVOICE_TEMPLATE'])) {
                $notification_template = $templates_base . $location['CONFIG_INVOICE_TEMPLATE'];
                
                if (isset($location['CONFIG_INVOICE_ITEM_TEMPLATE'])) {
                    $notification_item_template = $templates_base . $location['CONFIG_INVOICE_ITEM_TEMPLATE'];
                }
            }
        }
        // load template for invoice
        $notification_html_template = file_get_contents($notification_template);
        
        // load template for each invoice item
        if ($notification_item_template !== null) {
            $notification_item_html_template = file_get_contents($notification_item_template);
        }
        $date = date("Y-m-d");
        
        $name = $customerInfo['customer_name'];
        $address = $customerInfo['customer_address'];
        $phone = $customerInfo['customer_phone'];
        $email = $customerInfo['customer_email'];
        
        $invoice_details = $customerInfo['invoice_details'];
        $invoice_items = $customerInfo['invoice_items'];
    
        // notification date
        $notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
        
        // user details
        $notification_html_template = str_replace("####__CUSTOMER_NAME__####", $name, $notification_html_template);
        $notification_html_template = str_replace("####__CUSTOMER_ADDRESS__####", $address, $notification_html_template);
        $notification_html_template = str_replace("####__CUSTOMER_PHONE__####", $phone, $notification_html_template);
        $notification_html_template = str_replace("####__CUSTOMER_EMAIL__####", $email, $notification_html_template);
        
        // invoice information
        $notification_html_template = str_replace("####__INVOICE_DETAILS__####", $invoice_details, $notification_html_template);
        $notification_html_template = str_replace("####__INVOICE_ITEMS__####", $invoice_items, $notification_html_template);
        
        // customer details - NEW STYLE            
        $notification_html_template = str_replace("[CustomerId]", $customerInfo['customerId'], $notification_html_template);
        $notification_html_template = str_replace("[CustomerName]", $customerInfo['customerName'], $notification_html_template);
        $notification_html_template = str_replace("[CustomerAddress]", $customerInfo['customerAddress'], $notification_html_template);
        $notification_html_template = str_replace("[CustomerAddress2]", $customerInfo['customerAddress2'], $notification_html_template);
        $notification_html_template = str_replace("[CustomerPhone]", $customerInfo['customerPhone'], $notification_html_template);
        $notification_html_template = str_replace("[CustomerEmail]", $customerInfo['customerEmail'], $notification_html_template);
        $notification_html_template = str_replace("[CustomerContact]", $customerInfo['customerContact'], $notification_html_template);
        
        // invoice details - NEW STYLE
        $notification_html_template = str_replace("[InvoiceNumber]", $customerInfo['invoiceNumber'], $notification_html_template);
        $notification_html_template = str_replace("[InvoiceDate]", $customerInfo['invoiceDate'], $notification_html_template);
        $notification_html_template = str_replace("[InvoiceStatus]", $customerInfo['invoiceStatus'], $notification_html_template);
        $notification_html_template = str_replace("[InvoiceTotalBilled]", $customerInfo['invoiceTotalBilled'], $notification_html_template);
        $notification_html_template = str_replace("[InvoicePaid]", $customerInfo['invoicePaid'], $notification_html_template);
        $notification_html_template = str_replace("[InvoiceDue]", $customerInfo['invoiceDue'], $notification_html_template);
        $notification_html_template = str_replace("[InvoiceNotes]", $customerInfo['invoiceNotes'], $notification_html_template);
        
        // invoice items - NEW STYLE
        if($notification_item_template !== null) {
            $invoiceItems = '';
            
            foreach($customerInfo['invoiceItems'] as $invoiceItem) {
                $invoiceItemTemplate = $notification_item_html_template;
        
                $invoiceItemTemplate = str_replace("[InvoiceItemNumber]", $invoiceItem['invoiceItemNumber'], $invoiceItemTemplate);
                $invoiceItemTemplate = str_replace("[InvoiceItemPlan]", $invoiceItem['invoiceItemPlan'], $invoiceItemTemplate);
                $invoiceItemTemplate = str_replace("[InvoiceItemNotes]", $invoiceItem['invoiceItemNotes'], $invoiceItemTemplate);
                $invoiceItemTemplate = str_replace("[InvoiceItemAmount]", $invoiceItem['invoiceItemAmount'], $invoiceItemTemplate);
                $invoiceItemTemplate = str_replace("[InvoiceItemTaxAmount]", $invoiceItem['invoiceItemTaxAmount'], $invoiceItemTemplate);
                $invoiceItemTemplate = str_replace("[InvoiceItemTotalAmount]", $invoiceItem['invoiceItemTotalAmount'], $invoiceItemTemplate);
                
                $invoiceItems .= $invoiceItemTemplate;
            }
            $notification_html_template = str_replace("[InvoiceItems]", $invoiceItems, $notification_html_template);
        }
        
        // more invoice details - NEW STYLE
        $notification_html_template = str_replace("[InvoiceTotalAmount]", $customerInfo['invoiceTotalAmount'], $notification_html_template);
        $notification_html_template = str_replace("[InvoiceTotalTax]", $customerInfo['invoiceTotalTax'], $notification_html_template);
        
        // this fix has been set in place according to this thread:
        // https://stackoverflow.com/questions/37521775/dompdf-error-no-block-level-parent-found-not-good
        return str_replace("\n", "", $notification_html_template);
    }
<?php 
 * Authors:    Liran Tal <liran@enginx.com>
 *             Filippo Lauria <filippo.lauria@iit.cnr.it>
    // prevent this file to be directly accessed
    if (strpos($_SERVER['PHP_SELF'], '/notifications/processNotificationWelcome.php') !== false) {
        header("Location: ../index.php");
        exit;
    }
    
    // common notification functions
    include("common.php");
    
    $base = dirname(__FILE__);
    $base_path = "$base/templates/";
    
    /**
     * sendWelcomeNotification()
     * wrapper-function to send notification to the customer
     * 
     * @param        array         customer information array
     * @param        array         smtp server information
     * @param        string        from email address of the sender identity
     */
    function sendWelcomeNotification($customerInfo, $smtpInfo, $from) {
        $sendTo = $customerInfo['business_email'];
        $subject = "Welcome new customer!";
        $body = "Notification letter of service";
        $attachmentName = "notification.pdf";
        
        return send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName);
    }
    
    /**
     * prepareNotificationTemplate()
     * reads the notification (html) template and returns the formatted (html) notification
     * 
     * @param         array         Array of notification information
     * @return        string        HTML notification
     */
    function prepareNotificationTemplate($customerInfo) {
        global $base_path;
        
        // the HTML template
        $notification_template = $base_path . "welcome.html";
        $notification_html_template = file_get_contents($notification_template);
    
        $date = date("Y-m-d");
    
        $customer_name = $customerInfo['customer_name'];
        $customer_address = $customerInfo['customer_address']; 
        $customer_phone = $customerInfo['customer_phone'];
        $customer_email = $customerInfo['customer_email'];
    
        $notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
        $notification_html_template = str_replace("####__CUSTOMER_NAME__####", $customer_name, $notification_html_template);
        $notification_html_template = str_replace("####__CUSTOMER_ADDRESS__####", $customer_address, $notification_html_template);
        $notification_html_template = str_replace("####__CUSTOMER_PHONE__####", $customer_phone, $notification_html_template);
        $notification_html_template = str_replace("####__CUSTOMER_EMAIL__####", $customer_email, $notification_html_template);
        return $notification_html_template;
    }
 *             Filippo Lauria <filippo.lauria@iit.cnr.it>
    // set session's page variable
    $_SESSION['PREV_LIST_PAGE'] = $_SERVER['REQUEST_URI'];

    $help = t('helpPage','repbatchdetails');
    
    echo '<div id="contentnorightbar">';
    print_title_and_help($title, $help);
                        onclick="window.open('include/common/notificationsBatchDetails.php?batch_name=<?= urlencode($batch_name_enc) ?>&destination=download')">
    
    $inline_extra_js = "
var tooltipObj = new DHTMLgoodies_formTooltip();
tooltipObj.setTooltipPosition('right');
tooltipObj.setPageBgColor('#EEEEEE');
tooltipObj.setTooltipCornerSize(15);
tooltipObj.initFormFieldTooltip()";
    
    print_footer_and_html_epilogue($inline_extra_js);
<form name="listall" method="POST" action="<?= $action ?>">
    
    <input name="csrf_token" type="hidden" value="<?= dalo_csrf_token() ?>">
    
            $csrf_token = dalo_csrf_token();

                echo '<td>';
                $formId = $this_username . "-form-del";
                printf('<form id="%s" style="display: none" method="POST" action="mng-del.php">', $formId);
                printf('<input type="hidden" name="username[]" value="%s">', $this_username);
                printf('<input type="hidden" name="csrf_token" value="%s">', $csrf_token);
                echo '</form>';
                
                $onclick = sprintf("document.getElementById('%s').submit()", $formId);
                printf('<a href="mng-edit.php?username=%s">%s</a>&nbsp;<a href="#" onclick="%s">%s</a>',
                       urlencode($this_username), t('all','edit'), $onclick, t('all','del'));
                echo '</td>';